====== std.geodesy.frames — geodätisch, ECEF, ENU, NED ======
''import std.geodesy.frames;''
Die Umrechnung zwischen den vier Bezugsrahmen: geodätische Koordinaten (Breite, Länge, Höhe), erdfestes ECEF und die beiden örtlichen Rahmen ENU und NED.
→ [[lyx_-_programmiersprache:units:geodesy|std.geodesy Übersicht]] · [[lyx_-_programmiersprache:units:geodesy:ellipsoid|std.geodesy.ellipsoid]] · [[lyx_-_programmiersprache:guides:kurs-im-raum|Guide: Kurs und Lage im 3D-Raum]]
----
===== 1. Geodätische Koordinaten =====
pub type Geodetic = struct { latRad: f64; lonRad: f64; hEll: f64; }
^ Signatur ^ Zweck ^
| ''GeodNew(latRad, lonRad, hEll): Geodetic'' | aus **Bogenmaß** |
| ''GeodFromDeg(latDeg, lonDeg, hEll): Geodetic'' | aus **Grad** — der übliche Einstieg |
| ''GeodLatDeg(g: Geodetic): f64'' · ''GeodLonDeg(g: Geodetic): f64'' | zurück in Grad |
''hEll'' ist die Höhe über dem **Ellipsoid**, nicht über dem Meeresspiegel. Der Unterschied (Geoidundulation) beträgt in Mitteleuropa rund 45 m — wer eine GPS-Höhe unbesehen als Höhe über NN nimmt, liegt genau um diesen Betrag daneben. Ein Geoidmodell bringt diese Unit **nicht** mit.
----
===== 2. Geodätisch ↔ ECEF =====
^ Signatur ^ Zweck ^
| ''GeodToEcef(e: Ellipsoid, g: Geodetic): V3'' | nach erdfest-kartesisch |
| ''EcefToGeod(e: Ellipsoid, p: V3): Geodetic'' | zurück |
Die Rückrechnung ist der schwierigere Weg — die Breite steht in einer Gleichung, die sich nicht geschlossen auflösen lässt. Die Umrechnung hin und zurück trifft im Test wieder denselben Wert (''53.630400'' → ''53.630400'').
==== Drei Proben, die man nachrechnen kann ====
45N/45O: 3194419.145 | 3194419.145 | 4487348.409
Aequator: 6378137.000 | 0.000 | 0.000
Nordpol: 0.000 | 0.000 | 6356752.314
* **45° N / 45° O** stimmt bis auf drei Nachkommastellen mit dem Literaturwert überein — die übliche Kontrollstelle, weil dort x und y gleich groß sein **müssen**.
* **Am Äquator ist x genau die große Halbachse** (''EllWgs84().a'' = 6378137.000), y und z sind null.
* **Am Pol ist z genau die kleine Halbachse** (''EllB(e)'' = 6356752.314).
Diese drei Fälle sind die schnellste Probe, ob ein Ellipsoid richtig eingesetzt und die Breite nicht versehentlich in Grad statt Bogenmaß übergeben wurde: schon ein vertauschtes Winkelmaß verschiebt den Äquatorpunkt um Tausende Kilometer.
**ECEF ist die Drehscheibe** dieser Unit: alles andere führt über diesen Rahmen.
----
===== 3. Örtliche Rahmen =====
^ Signatur ^ Zweck ^
| ''EcefToEnu(e: Ellipsoid, origin: Geodetic, p: V3): V3'' | ECEF → **Ost, Nord, Oben** |
| ''EnuToEcef(e: Ellipsoid, origin: Geodetic, enu: V3): V3'' | zurück |
| ''EcefToNed(e: Ellipsoid, origin: Geodetic, p: V3): V3'' | ECEF → **Nord, Ost, Unten** |
| ''NedToEcef(e: Ellipsoid, origin: Geodetic, ned: V3): V3'' | zurück |
| ''EnuToNed(enu: V3): V3'' · ''NedToEnu(ned: V3): V3'' | direkt umtauschen |
**ENU und NED unterscheiden sich in zwei Dingen**: die ersten beiden Achsen sind vertauscht, und **die dritte hat das umgekehrte Vorzeichen**. ''EnuToNed'' rechnet ''(e, n, u) → (n, e, −u)''.
Das Vorzeichen ist die Falle: wer ENU-Werte als NED liest, bekommt eine Höhe, die unter der Erde liegt — und in einer Regelschleife eine Steuergröße mit falschem Vorzeichen. Luftfahrt und Regelungstechnik rechnen in **NED**, Vermessung und Antennentechnik in **ENU**.
----
===== 4. Sichtlinie =====
^ Signatur ^ Zweck ^
| ''EnuAzimuth(enu: V3): f64'' | Azimut in Bogenmaß, von Nord über Ost |
| ''EnuElevation(enu: V3): f64'' | Erhebungswinkel über der Horizontebene |
| ''EnuSlantRange(enu: V3): f64'' | **Schrägentfernung** — die Luftlinie, nicht die Grundstrecke |
----
===== 5. Beispiel =====
unit main;
import std.coordinates.vec3;
import std.coordinates.transform;
import std.geodesy.ellipsoid;
import std.geodesy.frames;
import std.io;
import std.string;
fn main(): int64 {
var e: Ellipsoid := EllWgs84();
var hh: Geodetic := GeodFromDeg(53.6304, 9.9882, 0.0);
var zz: Geodetic := GeodFromDeg(53.5511, 9.9937, 0.0);
var enu: V3 := EcefToEnu(e, hh, GeodToEcef(e, zz));
PrintLn(StrConcat("Ost (m): ", FloatToStr(enu.x, 1)));
PrintLn(StrConcat("Nord (m): ", FloatToStr(enu.y, 1)));
PrintLn(StrConcat("Azimut (Grad):", FloatToStr(RadToDeg(EnuAzimuth(enu)), 2)));
PrintLn(StrConcat("Entfernung: ", FloatToStr(EnuSlantRange(enu), 1)));
return 0;
}
Ost (m): 363.4
Nord (m): -8825.9
Azimut (Grad):177.63
Entfernung: 8833.4
**Nord ist negativ**, weil das Ziel südlich liegt — und der Azimut 177,6° sagt dasselbe noch einmal. Beides zusammen ist die Probe, ob der Rahmen richtig herum steht.
----
Letzte Aktualisierung: 2026-09-07 — Abschnitt „Drei Proben" ergänzt: 45°N/45°O gegen den Literaturwert, Äquator gegen die große und Pol gegen die kleine Halbachse; nachgemessen mit lyxc 1.2.2D.
Vorherige letzte Aktualisierung: 2026-09-06 — Seite neu angelegt; erhoben aus ''std/geodesy/frames.lyx'', Beispiel gegen lyxc 1.2.2D übersetzt und ausgeführt.