Eine Route ist eine Kette von Punkten auf einer Kugel. Was dazwischen zu rechnen ist: Kurs, Strecke, wo bin ich relativ zur Linie — und was der Wind daraus macht. Dieser Guide verbindet std.geo.sphere (Geometrie auf der Erde) mit std.aero.perf (Wind und Kurven) und std.aero.fplan (Vorhersage über die Kette).
→ Guides · Standardatmosphäre · Flugführungsrechner bauen · Kurs und Lage im 3D-Raum
import std.geo.sphere; // Großkreis, Kurse, Zielpunkt, Abweichungen, Sichtverbindung
import std.aero.perf; // Winddreieck, Kurvenvorhalt
import std.aero.fplan; // Vorhersage entlang der Kette
Es gibt zwei Geo-Units, und die Wahl entscheidet über die Richtigkeit des Ergebnisses:
| Unit | Rechnet | Wofür |
|---|---|---|
| std.geo | ganzzahlige Mikrograd, flache Näherung | Rechtecke, Bounding-Boxen, Umkreissuche im Nahbereich |
| std.geo.sphere | f64 auf der Kugel | Navigation, Kurse, Strecken über Kontinente, Polnähe, Datumsgrenze |
std.geo.sphere. Die flachen Näherungen aus std.geo stimmen auf 95 km bis auf 0,6 %, auf 6000 km fehlen 6,5 %, auf der Südhalbkugel 64 %, und an der Datumsgrenze verrechnen sie sich um den Faktor 180 (#1888).
Die Grenze von std.geo.sphere ist die Kugelform selbst: gegen das WGS84-Ellipsoid weicht sie um bis zu 0,5 % ab — rund 20 km auf einer Äquatorlänge, wenige Meter auf 100 km. Für Navigation, Funkstrecken und Reichweiten genügt das; für Vermessung nicht.
Ein Punkt ist GeoPos mit lat und lon in Grad (nicht Bogenmaß, nicht Mikrograd):
var eddh: GeoPos := GeoPosNew(53.6304, 9.9882); // Breite, Länge
var ausGms: f64 := GeoFromDms(53.0, 37.0, 49.4, 1); // 53°37'49.4" N
unit main;
import std.geo.sphere;
import std.io;
import std.string;
fn main(): int64 {
var eddh: GeoPos := GeoPosNew(53.6304, 9.9882); // Hamburg
var lepa: GeoPos := GeoPosNew(39.5517, 2.7388); // Palma
PrintLn(StrConcat("Grosskreis: ", StrConcat(FloatToStr(GeoDistanceNm(eddh, lepa), 1), " NM")));
PrintLn(StrConcat("Anfangskurs: ", StrConcat(FloatToStr(GeoBearing(eddh, lepa), 1), " Grad")));
PrintLn(StrConcat("Endkurs: ", StrConcat(FloatToStr(GeoFinalBearing(eddh, lepa), 1), " Grad")));
PrintLn(StrConcat("Loxodromkurs: ", StrConcat(FloatToStr(GeoRhumbBearing(eddh, lepa), 1), " Grad")));
PrintLn(StrConcat("Loxodrom: ", StrConcat(FloatToStr(GeoRhumbDistance(eddh, lepa) / 1852.0, 1), " NM")));
var m: GeoPos := GeoMidpoint(eddh, lepa);
PrintLn(StrConcat("Mitte: ",
StrConcat(FloatToStr(m.lat, 3), StrConcat(" / ", FloatToStr(m.lon, 3)))));
return 0;
}
Grosskreis: 895.5 NM
Anfangskurs: 202.2 Grad
Endkurs: 196.9 Grad
Loxodromkurs: 199.3 Grad
Loxodrom: 895.8 NM
Mitte: 46.647 / 5.890
Drei Dinge stehen in diesen Zahlen:
GeoDistanceNm liefert Seemeilen, GeoDistance Meter, GeoDistanceKm Kilometer. Die Einheit steht im Namen; GeoRhumbDistance liefert Meter und wird oben von Hand auf NM gebracht.Der eigentliche Kern: für jeden Abschnitt Kurs und Strecke aus der Geometrie, Grundgeschwindigkeit und Vorhaltewinkel aus dem Wind, Zeit und Treibstoff in die Vorhersage.
unit main;
import std.geo.sphere;
import std.aero.perf;
import std.aero.fplan;
import std.io;
import std.string;
fn abschnitt(name: pchar, von: GeoPos, nach: GeoPos, tasKt: f64,
windVon: f64, windKt: f64, ref v: AvVorhersage): void {
var kurs: f64 := GeoBearing(von, nach);
var strecke: f64 := GeoDistanceNm(von, nach);
var gs: f64 := AvGrundgeschwindigkeitKt(kurs, tasKt, windVon, windKt);
var wca: f64 := AvVorhaltewinkelGrad(kurs, tasKt, windVon, windKt);
AvVorhersageAbschnitt(v, strecke, gs, 2400.0);
var s: pchar := StrConcat(name, " Kurs=");
s := StrConcat(s, FloatToStr(kurs, 0));
s := StrConcat(s, " d=");
s := StrConcat(s, FloatToStr(strecke, 0));
s := StrConcat(s, " NM GS=");
s := StrConcat(s, FloatToStr(gs, 0));
s := StrConcat(s, " WCA=");
s := StrConcat(s, FloatToStr(wca, 1));
s := StrConcat(s, " ETA=");
s := StrConcat(s, FloatToStr(AvVorhersageZeitMin(v), 0));
s := StrConcat(s, " min EFOB=");
PrintLn(StrConcat(s, StrConcat(FloatToStr(AvVorhersageEfobKg(v), 0), " kg")));
}
fn main(): int64 {
var eddh: GeoPos := GeoPosNew(53.6304, 9.9882);
var frd: GeoPos := GeoPosNew(50.0500, 8.5000);
var dij: GeoPos := GeoPosNew(47.2690, 5.0900);
var lepa: GeoPos := GeoPosNew(39.5517, 2.7388);
var v: AvVorhersage := AvVorhersageStart(9000.0);
abschnitt("EDDH->FRD ", eddh, frd, 440.0, 270.0, 40.0, v);
abschnitt("FRD ->DIJ ", frd, dij, 440.0, 300.0, 60.0, v);
abschnitt("DIJ ->LEPA", dij, lepa, 440.0, 340.0, 30.0, v);
return 0;
}
EDDH->FRD Kurs=195 d=222 NM GS=428 WCA=5.0 ETA=31 min EFOB=7755 kg
FRD ->DIJ Kurs=220 d=215 NM GS=425 WCA=7.7 ETA=61 min EFOB=6543 kg
DIJ ->LEPA Kurs=193 d=475 NM GS=465 WCA=2.1 ETA=123 min EFOB=4093 kg
Die Grundgeschwindigkeit ist auf jedem Abschnitt eine andere — 425 bis 465 kt bei gleichbleibenden 440 kt TAS, weil der Wind mal quer und mal von hinten kommt. Genau deshalb schreibt std.aero.fplan den Zustand je Abschnitt fort, statt eine Formel über die Gesamtstrecke anzubieten: eine Mittelung wäre bei Gegenwind auf der zweiten Hälfte gutmütig falsch.
Windrichtung ist die Richtung, aus der es weht — meteorologische Konvention, wie in jeder Wettermeldung. AvWindkomponenteKt liefert positiv bei Rückenwind, AvSeitenwindKt immer den Betrag.
Die zwei Zahlen, die auf jeder Navigationsanzeige stehen: Querabweichung (wie weit neben dem Kurs) und Längsabweichung (wie weit entlang der Strecke).
unit main;
import std.geo.sphere;
import std.aero.perf;
import std.io;
import std.string;
fn main(): int64 {
var von: GeoPos := GeoPosNew(53.6304, 9.9882);
var nach: GeoPos := GeoPosNew(50.0500, 8.5000);
var ist: GeoPos := GeoPosNew(52.0000, 9.1000); // Ist-Position
var xtk: f64 := GeoCrossTrack(ist, von, nach) / 1852.0;
var atk: f64 := GeoAlongTrack(ist, von, nach) / 1852.0;
var rest: f64 := GeoDistanceNm(von, nach) - atk;
PrintLn(StrConcat("Querabweichung: ", StrConcat(FloatToStr(xtk, 2), " NM")));
PrintLn(StrConcat("zurueckgelegt: ", StrConcat(FloatToStr(atk, 1), " NM")));
PrintLn(StrConcat("Rest bis WPT: ", StrConcat(FloatToStr(rest, 1), " NM")));
PrintLn(StrConcat("Direktkurs: ", StrConcat(FloatToStr(GeoBearing(ist, nach), 1), " Grad")));
var neu: GeoPos := GeoDestination(von, 195.0, 25.0 * 1852.0); // 25 NM auf 195°
PrintLn(StrConcat("neuer WPT: ",
StrConcat(FloatToStr(neu.lat, 4), StrConcat(" / ", FloatToStr(neu.lon, 4)))));
PrintLn(StrConcat("Kursaenderung 40 Grad -> Vorhalt ",
StrConcat(FloatToStr(AvKurvenvorhaltNm(440.0, 25.0, 40.0), 2), " NM")));
PrintLn(StrConcat("Streckengewinn: ",
StrConcat(FloatToStr(AvKurvenabkuerzungNm(440.0, 25.0, 40.0), 2), " NM")));
PrintLn(StrConcat("Kompasssektor: ", IntToStr(GeoCompassSector(GeoBearing(von, nach)))));
return 0;
}
Querabweichung: 6.45 NM
zurueckgelegt: 102.9 NM
Rest bis WPT: 119.1 NM
Direktkurs: 191.2 Grad
neuer WPT: 53.2281 / 9.8082
Kursaenderung 40 Grad -> Vorhalt 2.20 NM
Streckengewinn: 0.18 NM
Kompasssektor: 9
Dasselbe bei der Längsabweichung: negativ heißt „noch vor dem Startpunkt„ — der Fußpunkt liegt hinter einem. Wer den Rest als Strecke − ATK rechnet, bekommt dann mehr als die Gesamtstrecke, und das ist richtig so.
GeoDestination konstruiert einen Wegpunkt aus Kurs und Strecke — die Umkehrung von GeoBearing/GeoDistance. Damit entstehen Zwischenpunkte, Ausweichpunkte und Warteschleifen-Fixes, die in keiner Datenbank stehen. Achtung auf die Einheit: die Strecke ist in Metern anzugeben.
GeoCompassSector gibt die Richtung als Sektor 0…15 (0 = N, 4 = O, 8 = S, 12 = W) — hier 9 = SSW für Kurs 195°.
Ein Flugzeug fliegt keine Ecken. Es beginnt die Kurve vor dem Wegpunkt und ist danach wieder auf der Linie — bei 440 kt und 25° Querneigung sind das 2,2 NM Vorhalt und 0,18 NM Streckengewinn je 40°-Knick. Über eine Route mit zwanzig Wegpunkten summiert sich das auf mehrere Meilen und eine knappe Minute.
Ab 180° Kursänderung liefert AvKurvenvorhaltNm den Fehlerwert: eine Kehrtwende lässt sich nicht abkürzen, und eine sehr große Zahl sähe aus wie ein Ergebnis.
Ob ein Funkfeuer empfangen wird, ist eine Frage der Erdkrümmung — VOR und DME arbeiten auf Sichtlinie:
unit main;
import std.geo.sphere;
import std.aero.atmos;
import std.io;
import std.string;
fn main(): int64 {
var vor: GeoPos := GeoPosNew(50.0500, 8.5000); // Funkfeuer auf 300 m
var ac: GeoPos := GeoPosNew(47.2690, 5.0900); // Flugzeug in FL350
var hAc: f64 := AvFtToM(35000.0);
PrintLn(StrConcat("Horizont Flugzeug: ",
StrConcat(FloatToStr(GeoHorizon(hAc) / 1852.0, 0), " NM")));
PrintLn(StrConcat("Horizont Funkfeuer: ",
StrConcat(FloatToStr(GeoHorizon(300.0) / 1852.0, 0), " NM")));
PrintLn(StrConcat("Entfernung: ",
StrConcat(FloatToStr(GeoDistanceNm(vor, ac), 0), " NM")));
if (GeoLineOfSight(vor, 300.0, ac, hAc)) {
PrintLn("Sichtverbindung: ja -- Empfang moeglich");
} else {
PrintLn("Sichtverbindung: nein -- ausser Reichweite");
}
return 0;
}
Horizont Flugzeug: 199 NM
Horizont Funkfeuer: 33 NM
Entfernung: 215 NM
Sichtverbindung: ja -- Empfang moeglich
Die Summe beider Horizonte (232 NM) übersteigt die Entfernung — also besteht Sicht. Das ist die geometrische Obergrenze; Senderleistung, Antennencharakteristik und Geländeabschattung stehen hier nicht drin. GeoLineOfSight beantwortet „kann es überhaupt gehen“, nicht „geht es„.
| Fehlt | Folge für den Aufrufer |
|---|---|
| Missweisung (magnetisch ↔ rechtweisend) | Alle Kurse sind rechtweisend. Wer magnetische Kurse braucht, bringt das Modell (WMM/IGRF) oder die Kartenangabe selbst mit |
| Ellipsoid (WGS84, Vincenty) | Bis 0,5 % Abweichung. Für Vermessung ungeeignet |
| Höhe in der Streckenrechnung | GeoDistance ist die Strecke auf der Kugeloberfläche, nicht die geflogene Schrägstrecke. Bei FL350 über 100 NM ist der Unterschied unter 0,1 % — bei kurzen Strecken mit großem Höhenunterschied nicht |
| Wegpunkt-Datenbank | Namen, Kennungen, Luftstraßen und Verfahren kommen vom Aufrufer; die Units rechnen mit Koordinaten |
| Geländedaten | Für Freigängigkeit und Abschattung braucht es ein Höhenmodell |
std.geo.sphere — std.geo ist für Rechtecke im Nahbereich, nicht für Kurse.GeoDistance* in der Einheit, die im Namen steht — GeoDestination und GeoCrossTrack arbeiten dagegen in Metern.GeoIsError, GeoPosIsError) statt einer plausiblen Zahl.Weiter: Flugführungsrechner bauen setzt die Route in ein senkrechtes Profil um — Steig- und Sinkflug, T/C und T/D, Reserven. Für Lage und Drehungen im Raum (Quaternionen, Bezugsrahmen): Kurs und Lage im 3D-Raum.
Letzte Aktualisierung: 2026-09-07 — Beispiele und Fließtext auf die englischen Namen von std.geo.sphere gezogen (GeoEntfernungSm → GeoDistanceNm, GeoKurs → GeoBearing, GeoQuerabweichung → GeoCrossTrack …); alle vier Vollprogramme neu übersetzt und ausgeführt.
Vorherige letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; alle Beispiele gegen lyxc 1.1.18A übersetzt und ausgeführt, die Ausgaben stammen aus dem Lauf.
Prüflauf 2026-09-05 mit doku-pruefer.py über den gesamten Aero-Bereich (Repo-Compiler lyxc 1.2.2A): 13 Vollprogramme, 13 übersetzen, 0 echte Fehler; 193 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen.