====== std.astro — Bahnmechanik, Lage und Guidance ====== **Seit PR #1890 (2026-08-30) auf ''develop''.** Fünf Units unter ''std/astro/'', ausgeliefert nach ''/usr/include/lyx/units/std/astro/'' (''.lyx'' und ''.lyu'' synchron), abgesichert durch 189 Prüfungen in ''tests/astro_{orbit,insertion,entry,launch}_test.lyx''. Alle Beispiele dieser Seiten sind mit ''lyxc 1.1.15B'' gebaut und ausgeführt. → Für den **atmosphärischen** Flug (Standardatmosphäre, Flugleistung, Flugführung): [[lyx_-_programmiersprache:units:aero|std.aero]]. ''std.astro'' ist die Schicht oberhalb der Vektor- und Quaternionen-Algebra: Bahnenergie, Transfermanöver, Einfang, Atmosphäreneintritt, Startauslegung. Der Zuschnitt entstand als [[https://github.com/SEOLizer/LyX-Compiler/issues/1900|Issue #1900]] und ist mit PR #1890 umgesetzt. **Vollständig seit 2026-08-31:** Lageregelung und Guidance sind mit ''std.astro.attitude'' und ''std.astro.guidance'' dazugekommen (#1900, 83 Prüfungen). Der Weg über ''std.quat'' und ''std.matrix'' in Abschnitt 3 bleibt als Hintergrund stehen — gebraucht wird er dafür nicht mehr. → [[lyx_-_programmiersprache:guides:kurs-im-raum|Kurs und Lage im 3D-Raum]] · [[lyx_-_programmiersprache:units:quat|std.quat]] · [[lyx_-_programmiersprache:units:matrix|std.matrix]] · [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] ---- ===== 1. Die fünf Units ===== ^ Unit ^ Inhalt ^ Seite ^ | ''std.astro.state'' | Zustandsvektoren, Vektorrechnung, Rahmenkennungen, Frenet-Dreibein, NaN als Fehlerwert | [[lyx_-_programmiersprache:units:astro:state|std.astro.state]] | | ''std.astro.orbit'' | Vis-Viva, Energie, Halbachse, Umlaufzeit, Bahnelemente aus einem Zustand, Hohmann, Fluchtbahnen, Ebenenänderung | [[lyx_-_programmiersprache:units:astro:orbit|std.astro.orbit]] | | ''std.astro.insertion'' | Einfang, Zirkularisieren, Perizentrum anheben, Deorbit, Korrekturschübe aus zwei Zuständen | [[lyx_-_programmiersprache:units:astro:insertion|std.astro.insertion]] | | ''std.astro.entry'' | Atmosphären (Erde, Mars, Venus, Titan), Allen-Eggers, Sutton-Graves, Eintrittskorridor | [[lyx_-_programmiersprache:units:astro:entry|std.astro.entry]] | | ''std.astro.launch'' | Ziolkowski, Stufung, Schub-Gewicht, Aufstiegsverluste, Startazimut und Inklination | [[lyx_-_programmiersprache:units:astro:launch|std.astro.launch]] | | ''std.astro.attitude'' | Lagefehler (Quaternion, Winkel, Achse, Fehlervektor), Winkelgeschwindigkeit, Propagation, Schwenkzeiten und Ratengrenzen | [[lyx_-_programmiersprache:units:astro:attitude|std.astro.attitude]] | | ''std.astro.guidance'' | Abstand, Annäherungsrate, dichteste Annäherung, Quer- und Längsabweichung, Restflugzeit, Sichtlinienrate, Proportional Navigation | [[lyx_-_programmiersprache:units:astro:guidance|std.astro.guidance]] | | ''std.astro.groundtrack'' | Bodenspur: Greenwich-Stundenwinkel, ECI↔ECEF, Unterpunkt in Breite, Länge und Höhe | [[lyx_-_programmiersprache:units:astro:groundtrack|std.astro.groundtrack]] | | ''std.astro.cr3bp'' | eingeschränktes Dreikörperproblem: die fünf Librationspunkte, Hill-Radius, Jacobi-Konstante | [[lyx_-_programmiersprache:units:astro:cr3bp|std.astro.cr3bp]] | Dazu vier Datenseiten ohne eigene Unit: * [[lyx_-_programmiersprache:units:astro:startplaetze|Startplätze]] — Breiten der aktiven Rampen (SpaceX-Rampen, Kourou, Baikonur, Andøya, die asiatischen Plätze, Wostotschny, Plessezk) mit Wikidata-Quelle, dazu Drehgewinn, Aufstiegs-dv und Ebenenstrafe je Platz. * [[lyx_-_programmiersprache:units:astro:traegerraketen|Trägerraketen]] — fünf Referenzsysteme (Spectrum, Falcon 9, Ariane 64, Angara A5, Long March 5) mit gerechnetem Schub-Gewicht-Verhältnis, Nutzlastanteil, Austrittsgeschwindigkeit und Brenndauer. * [[lyx_-_programmiersprache:units:astro:orbitmatrix|Orbit-Matrix]] — welcher Träger von welcher Rampe welchen Orbit erreicht: dv-Bedarf für 16 Rampen und sechs Zielbahnen (darunter ISS, Tiangong, Hubble) sowie die modellierte Nutzlast je Träger und Ziel. * [[lyx_-_programmiersprache:units:astro:bahntypen|Bahntypen]] — Katalog der Umlaufbahnen (LEO, polar, SSO, MEO, GTO, GEO, Molniya, Tundra) mit gerechneten Kennwerten und den Bahnfamilien, die ohne J2 nicht auslegbar sind. Die Abhängigkeiten laufen nur nach unten: ''state'' steht allein, ''orbit'' baut darauf, ''insertion''/''entry''/''launch'' auf beiden; ''attitude'' und ''guidance'' brauchen nur ''state'' (und ''std.quat''). ''groundtrack'' und ''cr3bp'' stehen daneben: die eine bringt eine Zeitachse ins Spiel, die andere verlässt das Zweikörperproblem. ===== 1b. Zwei Entwurfsentscheidungen, die überall durchgehalten sind ===== **NaN statt Ersatzwert.** Jede Funktion gibt bei unmöglicher Eingabe ''AError()'' zurück, geprüft mit ''AIsError''. Bei Bahnrechnungen sehen ''0'' und ''1'' beide wie gültige Ergebnisse aus, und ein stiller Ersatzwert wandert unbemerkt in die nächste Rechnung. Beispiele, an denen das trägt: ''AHyperbolicExcess'' unterhalb der Fluchtgeschwindigkeit (0 sähe aus wie eine gerade erreichte Flucht), ''ARaan'' bei äquatorialer Bahn (0 wäre ein gültiger Winkel), ''AUnitVec'' beim Nullvektor, ''ATrueAnomaly'' auf der Kreisbahn. **SI durchgehend**, Meter und Sekunden, Winkel in Radiant. Die Units mischen nirgends — ein Zahlenwert ohne Einheit ist in der Bahnmechanik wertlos. > **Freie Funktionen, keine Methoden.** So ist es umgesetzt — der Rechenkern steht als freie Funktion mit dem Zustand als Parameter. Zwei Gründe: > > * Sachlich: viele Größen brauchen **zwei** Zustände (Annäherung, Relativbewegung) oder Zustand plus Zielbahn — als Methode an einem der beiden wäre die Zuordnung willkürlich. > * Technisch: eine Methode mit ''f64''-Rückgabe liefert derzeit das **Bitmuster** statt des Werts, sobald das Ergebnis in eine Variable oder einen Ausdruck geht ([[https://github.com/SEOLizer/LyX-Compiler/issues/1887|#1887]]). Genau daran ist der erste Entwurf gescheitert: ''Halbachse()'' lieferte ''0.0'' statt ''6678000.0'', während ''Energie()'' richtig rechnete. Der Quelltext hält ausdrücklich fest, dass der Zuschnitt auch nach einem Fix richtig bleibt — die sachliche Begründung ist von dem Fehler unabhängig. ---- ===== 2. Beispiel: Bahnmechanik von Hand (ohne die Units) ===== Das folgende Programm rechnet dieselben Größen **ohne** ''std.astro'' — es zeigt, was die Units abnehmen, und dient als Gegenprobe zu ihren Ergebnissen. import std.quat; import std.matrix; import std.math; import std.io; type Zustand = struct { pos: Vec3; vel: Vec3; lage: Quat; t_ns: int64; } con MU_ERDE: f64 := 398600441800000.0; // m^3/s^2 fn Energie(z: Zustand, mu: f64): f64 { return Vec3LengthSq(z.vel) / 2.0 - mu / Vec3Length(z.pos); } fn Halbachse(z: Zustand, mu: f64): f64 { return 0.0 - mu / (2.0 * Energie(z, mu)); } fn Umlaufzeit(a: f64, mu: f64): f64 { return 2.0 * 3.14159265358979 * SqrtF64(a * a * a / mu); } fn VisViva(mu: f64, r: f64, a: f64): f64 { return SqrtF64(mu * (2.0 / r - 1.0 / a)); } fn HohmannDeltaV(mu: f64, r1: f64, r2: f64): f64 { var at: f64 := (r1 + r2) / 2.0; return AbsF64(VisViva(mu, r1, at) - SqrtF64(mu / r1)) + AbsF64(SqrtF64(mu / r2) - VisViva(mu, r2, at)); } fn HohmannZeit(mu: f64, r1: f64, r2: f64): f64 { var at: f64 := (r1 + r2) / 2.0; return 3.14159265358979 * SqrtF64(at * at * at / mu); } fn InklinationDeltaV(v: f64, winkelRad: f64): f64 { return 2.0 * v * SinF64(winkelRad / 2.0); } fn Raketengleichung(m0: f64, dv: f64, isp: f64): f64 { return m0 * (1.0 - ExpF64(0.0 - dv / (isp * 9.80665))); } fn main(): int64 { var leo: f64 := 6678000.0; // 300 km Hoehe var geo: f64 := 42164000.0; var z: Zustand; z.pos := Vec3New(leo, 0.0, 0.0); z.vel := Vec3New(0.0, SqrtF64(MU_ERDE / leo), 0.0); Print("spez. Energie : "); PrintLn(FloatToStr(Energie(z, MU_ERDE), 1)); Print("grosse Halbachse: "); PrintLn(FloatToStr(Halbachse(z, MU_ERDE), 1)); Print("Umlaufzeit min : "); PrintLn(FloatToStr(Umlaufzeit(Halbachse(z, MU_ERDE), MU_ERDE) / 60.0, 2)); Print("Hohmann dv m/s : "); PrintLn(FloatToStr(HohmannDeltaV(MU_ERDE, leo, geo), 1)); Print("Hohmann t h : "); PrintLn(FloatToStr(HohmannZeit(MU_ERDE, leo, geo) / 3600.0, 2)); Print("Inkl 28.5 Grad : "); PrintLn(FloatToStr(InklinationDeltaV(3074.7, 28.5 * 3.14159265358979 / 180.0), 1)); Print("Treibstoff kg : "); PrintLn(FloatToStr(Raketengleichung(1000.0, 3892.6, 320.0), 1)); return 0; } spez. Energie : -29844297.8 grosse Halbachse: 6678000.0 Umlaufzeit min : 90.52 Hohmann dv m/s : 3892.6 Hohmann t h : 5.28 Inkl 28.5 Grad : 1513.7 Treibstoff kg : 710.7 Jede dieser Zahlen ist gegen bekannte Werte prüfbar — und genau deshalb gehört die Bahnmechanik in eine eigene Unit: 90,5 Minuten Umlaufzeit in 300 km Höhe, 3,9 km/s für LEO→GEO, 5,3 Stunden Transferzeit, 1,5 km/s für 28,5 Grad Inklinationsänderung aus dem Geostationärorbit. Wer die Unit ändert, merkt an diesen Zahlen sofort, wenn etwas nicht mehr stimmt. ---- ===== 3. Beispiel: Lage und Guidance von Hand ===== Lageregelung und Guidance sind in ''std.astro'' noch **nicht** enthalten (Fehlerquaternion, CPA, Sichtlinienrate); der folgende Weg über ''std.quat'' und ''std.matrix'' bleibt dafür der aktuelle. import std.quat; import std.matrix; import std.math; import std.io; fn Grad(rad: f64): f64 { return rad * 180.0 / 3.14159265358979; } fn LageFehler(ist: Quat, ziel: Quat): Quat { return QuatMul(ziel, QuatInverse(ist)); } // Zeit bis zur groessten Annaeherung: t = -(r.v) / (v.v) fn ZeitBisCpa(rRel: Vec3, vRel: Vec3): f64 { var vv: f64 := Vec3Dot(vRel, vRel); if (vv < 0.000000001) { return 0.0; } // gleiche Geschwindigkeit: nie naeher return 0.0 - Vec3Dot(rRel, vRel) / vv; } fn AbstandBeiCpa(rRel: Vec3, vRel: Vec3): f64 { var t: f64 := ZeitBisCpa(rRel, vRel); if (t < 0.0) { return Vec3Length(rRel); } // entfernt sich bereits return Vec3Length(Vec3Add(rRel, Vec3Scale(vRel, t))); } // Querabweichung von der Sollbahn durch a in Richtung d (Einheitsvektor) fn Querabweichung(p: Vec3, a: Vec3, d: Vec3): f64 { var w: Vec3 := Vec3Sub(p, a); return Vec3Length(Vec3Sub(w, Vec3Scale(d, Vec3Dot(w, d)))); } // Sichtlinien-Drehrate fuer Proportional Navigation: omega = (r x v) / (r.r) fn Sichtlinienrate(rRel: Vec3, vRel: Vec3): Vec3 { var rr: f64 := Vec3Dot(rRel, rRel); if (rr < 0.000000001) { return Vec3Zero(); } return Vec3Scale(Vec3Cross(rRel, vRel), 1.0 / rr); } fn main(): int64 { var ist: Quat := QuatIdentity(); var ziel: Quat := QuatFromAxisAngle(0.0, 0.0, 1.0, 30.0 * 3.14159265358979 / 180.0); Print("Lagefehler Grad : "); PrintLn(FloatToStr(Grad(QuatAngle(LageFehler(ist, ziel))), 2)); // Objekt 1000 m voraus, 50 m seitlich versetzt, kommt mit 10 m/s entgegen var rRel: Vec3 := Vec3New(1000.0, 50.0, 0.0); var vRel: Vec3 := Vec3New(0.0 - 10.0, 0.0, 0.0); Print("Zeit bis CPA s : "); PrintLn(FloatToStr(ZeitBisCpa(rRel, vRel), 1)); Print("Abstand bei CPA m : "); PrintLn(FloatToStr(AbstandBeiCpa(rRel, vRel), 1)); Print("Querabweichung m : "); PrintLn(FloatToStr(Querabweichung(Vec3New(500.0, 30.0, 0.0), Vec3Zero(), Vec3UnitX()), 1)); var w: Vec3 := Sichtlinienrate(rRel, vRel); Print("Sichtlinienrate z : "); PrintLn(FloatToStr(w.z, 6)); return 0; } Lagefehler Grad : 30.00 Zeit bis CPA s : 100.0 Abstand bei CPA m : 50.0 Querabweichung m : 30.0 Sichtlinienrate z : 0.000499 Auch hier ist jedes Ergebnis von Hand nachrechenbar: 1000 m Vorsprung bei 10 m/s Annäherung sind 100 Sekunden, und der seitliche Versatz von 50 m bleibt — das ist der Abstand bei größter Annäherung. Die Sichtlinienrate ist die Größe, auf die Proportional Navigation regelt: bleibt sie null, ist der Kurs ein Kollisionskurs. ---- ===== 4. Was beim Bau zu beachten ist ===== * **Bezugsrahmen in den Namen.** ''pos_eci'' gegen ''pos_ecef'' — der Compiler kann die beiden nicht unterscheiden, weder über ''dim''/''utype'' (beide sind Längen) noch über eigene Structs ([[https://github.com/SEOLizer/LyX-Compiler/issues/1886|#1886]]: Struct-Typen werden nicht geprüft). Siehe [[lyx_-_programmiersprache:guides:kurs-im-raum|Kurs und Lage im 3D-Raum]], Abschnitt 1. * **''acos'' klemmen.** ''Vec3Dot'' zweier Einheitsvektoren kann ''1.0000000002'' ergeben; ''AcosF64'' liefert daraus NaN, und NaN wandert stumm durch die ganze Rechnung. * **Division durch fast null abfangen.** Zwei Objekte mit derselben Geschwindigkeit haben keine Annäherungszeit; ohne Prüfung kommt Unendlich heraus statt einer Aussage. * **Quaternionen normalisieren.** Nach jeder Verkettung; ''QuatIsUnit'' als Prüfung. * **SI-Einheiten durchgängig**, Meter und Sekunden. Kilometer sind der klassische Weg zu einem Faktor 1000 an der falschen Stelle. ---- Letzte Aktualisierung: 2026-09-01 — ''std.astro.attitude'' und ''std.astro.guidance'' aufgenommen; die Lücke „Lageregelung und Guidance fehlen" ist damit geschlossen. Zuvor 2026-08-31 — Datenseite [[lyx_-_programmiersprache:units:astro:startplaetze|Startplätze]] aufgenommen (SpaceX-Rampen mit Wikidata-Quellen, alle Kennzahlen gerechnet). Zuvor 2026-08-31 — Seite auf den ausgelieferten Stand gebracht (PR #1890): fünf Unit-Seiten verlinkt, Zuschnitt-Tabelle durch die tatsächlichen Units ersetzt. Zuvor 2026-08-31 — Seite neu angelegt, als der Zuschnitt noch ein Vorschlag war.