Inhaltsverzeichnis

Navigation und Wegpunkte

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


1. Erst die richtige Unit wählen

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
 
Für Navigation immer 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


2. Kurs und Strecke: Großkreis gegen Loxodrome

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:


3. Eine Route abarbeiten

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.


4. Wo bin ich relativ zur Linie?

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

 
Das Vorzeichen der Querabweichung ist die halbe Aussage. Positiv heißt rechts des Kurses, negativ links. Ohne Vorzeichen weiß man, wie weit man abgekommen ist, aber nicht, wohin man steuern muss.

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

Kurvenvorhalt: warum die Route kürzer ist als die Summe der Abschnitte

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.


5. Funkreichweite und Sicht

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


6. Was diese Units nicht können

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

7. Merksätze


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 (GeoEntfernungSmGeoDistanceNm, GeoKursGeoBearing, GeoQuerabweichungGeoCrossTrack …); 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.