<WRAP tip>
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.
</WRAP>
→ Für den atmosphärischen Flug (Standardatmosphäre, Flugleistung, Flugführung): std.aero.
std.astro ist die Schicht oberhalb der Vektor- und Quaternionen-Algebra: Bahnenergie, Transfermanöver, Einfang, Atmosphäreneintritt, Startauslegung. Der Zuschnitt entstand als 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.
→ Kurs und Lage im 3D-Raum · std.quat · std.matrix · Mathematik-Units
| Unit | Inhalt | Seite |
|---|---|---|
std.astro.state | Zustandsvektoren, Vektorrechnung, Rahmenkennungen, Frenet-Dreibein, NaN als Fehlerwert | std.astro.state |
std.astro.orbit | Vis-Viva, Energie, Halbachse, Umlaufzeit, Bahnelemente aus einem Zustand, Hohmann, Fluchtbahnen, Ebenenänderung | std.astro.orbit |
std.astro.insertion | Einfang, Zirkularisieren, Perizentrum anheben, Deorbit, Korrekturschübe aus zwei Zuständen | std.astro.insertion |
std.astro.entry | Atmosphären (Erde, Mars, Venus, Titan), Allen-Eggers, Sutton-Graves, Eintrittskorridor | std.astro.entry |
std.astro.launch | Ziolkowski, Stufung, Schub-Gewicht, Aufstiegsverluste, Startazimut und Inklination | std.astro.launch |
std.astro.attitude | Lagefehler (Quaternion, Winkel, Achse, Fehlervektor), Winkelgeschwindigkeit, Propagation, Schwenkzeiten und Ratengrenzen | std.astro.attitude |
std.astro.guidance | Abstand, Annäherungsrate, dichteste Annäherung, Quer- und Längsabweichung, Restflugzeit, Sichtlinienrate, Proportional Navigation | std.astro.guidance |
std.astro.groundtrack | Bodenspur: Greenwich-Stundenwinkel, ECI↔ECEF, Unterpunkt in Breite, Länge und Höhe | std.astro.groundtrack |
std.astro.cr3bp | eingeschränktes Dreikörperproblem: die fünf Librationspunkte, Hill-Radius, Jacobi-Konstante | std.astro.cr3bp |
Dazu vier Datenseiten ohne eigene Unit:
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.
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 mitf64-Rückgabe liefert derzeit das Bitmuster statt des Werts, sobald das Ergebnis in eine Variable oder einen Ausdruck geht (#1887). Genau daran ist der erste Entwurf gescheitert:Halbachse()lieferte0.0statt6678000.0, währendEnergie()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.
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.
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.
pos_eci gegen pos_ecef — der Compiler kann die beiden nicht unterscheiden, weder über dim/utype (beide sind Längen) noch über eigene Structs (#1886: Struct-Typen werden nicht geprüft). Siehe 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.QuatIsUnit als Prüfung.
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 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.