====== 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.