====== std.astro.orbit — Bahnmechanik ======
''import std.astro.orbit;''
Vis-Viva und alles, was daraus folgt: Geschwindigkeiten, Energie, Bahnelemente aus einem Zustand, Hohmann-Transfer, Fluchtbahnen und Ebenenänderungen. Alle Winkel in Radiant, alle Längen in Metern.
→ [[lyx_-_programmiersprache:units:astro|std.astro (Übersicht)]] · [[lyx_-_programmiersprache:units:astro:state|std.astro.state]] · [[lyx_-_programmiersprache:units:astro:insertion|std.astro.insertion]]
----
===== 1. Zentralkörper =====
Die Werte stehen als **Funktionen**, nicht als Konstanten — so sind sie in Ausdrücken überall verwendbar.
^ Funktion ^ Wert ^ Einheit ^
| ''AMuEarth()'' | 3.986004418e14 | m³/s² |
| ''AMuSun()'' | 1.32712440018e20 | m³/s² |
| ''AMuMoon()'' | 4.902800118e12 | m³/s² |
| ''AMuMars()'' | 4.2828372e13 | m³/s² |
| ''ARadiusEarth()'' | 6378137.0 | m (Äquator) |
| ''ARadiusMoon()'' | 1737400.0 | m |
| ''ARadiusMars()'' | 3396200.0 | m |
| ''AGravConstant()'' | 6.6743e−11 | m³/(kg·s²) |
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AMuFromMass(m)'' | ''f64'' | ''G · m'' für einen Körper, der oben nicht steht |
| ''ASurfaceGravity(mu, R)'' | ''f64'' [m/s²] | ''mu/R²'' — für die Erde 9.7983 (am Äquator, ohne Fliehkraft) |
===== 2. Geschwindigkeiten =====
^ Funktion ^ Rückgabe ^ Bedeutung ^ NaN wenn ^
| ''AVisViva(mu, r, a)'' | ''f64'' [m/s] | ''v² = mu·(2/r − 1/a)'' — die Gleichung, aus der fast alles folgt | ''mu≤0'', ''r≤0'', ''a=0'' oder Radikand < 0 (Ort liegt gar nicht auf der Bahn) |
| ''ACircularSpeed(mu, r)'' | ''f64'' | Kreisbahn, der Sonderfall ''a = r'' | ''mu≤0'' oder ''r≤0'' |
| ''AEscapeSpeed(mu, r)'' | ''f64'' | Bahnenergie wird 0; immer ''√2'' mal Kreisbahntempo | dito |
===== 3. Flucht und Hyperbelbahn =====
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AHyperbolicExcess(mu, r, v)'' | ''f64'' [m/s] | ''v_∞'' — was vom Tempo unendlich weit draußen übrig bleibt. **NaN, wenn ''v'' unter der Fluchtgeschwindigkeit liegt** |
| ''AC3(mu, r, v)'' | ''f64'' [m²/s²] | charakteristische Energie ''= v_∞² = 2E''; anders als ''v_∞'' auch für gebundene Bahnen definiert und dann **negativ** |
| ''AInjectionSpeed(mu, r, vInf)'' | ''f64'' | welches Tempo am Ort ''r'' ein gewünschtes ''v_∞'' ergibt: ''√(v_∞² + 2mu/r)'' |
| ''AEscapeDv(mu, r, vInf)'' | ''f64'' | Schub aus der **Kreisbahn** auf dieses ''v_∞''; ''vInf = 0'' ergibt ''(√2−1)·v_kreis'' |
| ''ATurnAngle(e)'' | ''f64'' [rad] | Ablenkung einer Vorbeiflugbahn: ''sin(δ/2) = 1/e''. NaN für ''e ≤ 1'' |
| ''AImpactParameter(a, e)'' | ''f64'' [m] | ''b = |a|·√(e²−1)'' — Abstand, in dem der Körper **ohne** Schwerkraft vorbeigeflogen wäre |
| ''AFlybyPeriapsis(mu, vInf, b)'' | ''f64'' [m] | nächster Abstand eines Vorbeiflugs. Ob er unter dem Körperradius liegt (Einschlag), prüft der Aufrufer |
> **Drei Verwechslungen, die die Unit im Quelltext ausdrücklich benennt:** Die Fluchtgeschwindigkeit ist ein **Betrag ohne Richtung**; sie gilt für **diesen** Zentralkörper (wer die Erde mit 11.2 km/s verlässt, steht danach in einer Sonnenbahn); und sie gilt für den **Stoß** — ein Antrieb mit kleinem Schub über Stunden braucht mehr.
>
> ''AInjectionSpeed'' ist zugleich die Formel hinter dem **Oberth-Effekt**: tief im Schwerefeld ist der Zuschlag für dasselbe ''v_∞'' viel kleiner, weil unter der Wurzel der große Term ''2mu/r'' steht.
===== 4. Energie, Halbachse, Umlaufzeit =====
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AEnergy(mu, r, v)'' | ''f64'' [J/kg] | ''v²/2 − mu/r''. Negativ = gebunden, 0 = Parabel, positiv = Hyperbel |
| ''ASemiMajorAxis(mu, r, v)'' | ''f64'' [m] | ''−mu/(2E)''; **NaN bei ''E = 0''** — die Parabel hat keine endliche Halbachse |
| ''APeriod(mu, a)'' | ''f64'' [s] | ''2π√(a³/mu)'', drittes Keplersches Gesetz; NaN für ''a ≤ 0'' |
| ''ASemiMajorFromPeriod(mu, T)'' | ''f64'' [m] | Umkehrung — für sonnensynchrone und geostationäre Auslegung |
===== 5. Bahnelemente aus einem Zustand =====
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AEccentricityVec(mu, s)'' | ''AVec3'' | Laplace-Runge-Lenz-Vektor; zeigt zum Perizentrum |
| ''AEccentricity(mu, s)'' | ''f64'' | dessen Betrag |
| ''AInclination(s)'' | ''f64'' [rad] | 0…π; über π/2 heißt rückläufig. NaN bei gerader Bahn |
| ''ARaan(s)'' | ''f64'' [rad] | Rektaszension des aufsteigenden Knotens, 0…2π. **NaN bei äquatorialer Bahn** — dort gibt es keinen Knoten, und 0 wäre ein gültiger Winkel |
| ''ACircularThreshold()'' | ''f64'' | 1e−12 |
| ''AIsCircular(mu, s)'' | ''bool'' | Exzentrizität unter der Schwelle |
| ''ATrueAnomaly(mu, s)'' | ''f64'' [rad] | Winkel vom Perizentrum zum Ort, 0…2π; das Vorzeichen entscheidet die Radialgeschwindigkeit. **NaN auf der Kreisbahn** |
| ''APeriapsis(a, e)'' · ''AApoapsis(a, e)'' | ''f64'' [m] | ''a(1∓e)''; NaN für ''e ≥ 1'' |
> **Warum es eine Kreisbahn-Schwelle gibt.** Eine rechnerisch exakte Kreisbahn existiert nicht: der Exzentrizitätsvektor einer aus dem Kreistempo gebauten Bahn hat den Betrag ~1.2e−16 — Rundungsrauschen, aber nicht 0, und seine **Richtung ist dabei völlig beliebig**. Wer daraus eine wahre Anomalie rechnet, bekommt eine Zahl ohne Bedeutung. 1e−12 trennt sauber: Rundung liegt vier Größenordnungen darunter, die kleinste praktisch gemeinte Exzentrizität darüber.
===== 6. Hohmann-Transfer =====
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AHohmannDv1(mu, r1, r2)'' | ''f64'' | erster Schub (im Ausgangsorbit), **vorzeichenbehaftet** |
| ''AHohmannDv2(mu, r1, r2)'' | ''f64'' | zweiter Schub (im Zielorbit), vorzeichenbehaftet |
| ''AHohmannDv(mu, r1, r2)'' | ''f64'' | Summe der **Beträge** |
| ''AHohmannTime(mu, r1, r2)'' | ''f64'' [s] | halbe Umlaufzeit der Transferellipse |
| ''AHohmannPhaseAngle(mu, r1, r2)'' | ''f64'' [rad] | Voraushaltewinkel: wie weit das Ziel beim Start vorausstehen muss |
> **Zwei Grenzen, die die Unit selbst benennt:**
>
> * Beim **Absenken** sind beide Einzelwerte negativ. Wer sie vorzeichenbehaftet addiert, bekommt den Treibstoffbedarf mit falschem Vorzeichen — deshalb summiert ''AHohmannDv'' die Beträge.
> * Für ''r2/r1 > 11.94'' ist der **bielliptische** Transfer günstiger. Diese Unit rechnet ihn nicht; sie liefert dann einen richtigen, aber nicht den besten Wert.
===== 7. Ebenenänderung =====
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''APlaneChangeDv(v, dInc)'' | ''f64'' | ''2v·sin(Δi/2)'' — reine Inklinationsänderung |
| ''ACombinedDv(v1, v2, dInc)'' | ''f64'' | Tempo **und** Ebene in einem Schub (Kosinussatz); nie teurer als beide nacheinander |
Bei 7.7 km/s kostet **ein Grad rund 135 m/s**. Deshalb wird die Ebene im Apozentrum gedreht, wo das Tempo am kleinsten ist.
===== 8. Abplattung: Knotendrift, Apsidendrift, sonnensynchrone Bahnen =====
Die Unit rechnet sonst mit einem kugelsymmetrischen Zentralkörper. Dieser Abschnitt ist die einzige Ausnahme: er berücksichtigt die **Abplattung** über den Koeffizienten ''J2'' — den Term, ohne den sich sonnensynchrone Bahnen, Molniya und Tundra nicht erklären lassen ([[https://github.com/SEOLizer/LyX-Compiler/issues/1988|#1988]]).
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''AJ2Earth()'', ''AJ2Mars()'', ''AJ2Moon()'' | ''f64'' | zweiter zonaler Koeffizient; Erde 1.08263·10⁻³ |
| ''ANodalDrift(mu, j2, rBody, a, e, inc)'' | ''f64'' | Knotendrift in **rad/s**; negativ bei prograden Bahnen (Knoten wandert nach Westen) |
| ''AApsidalDrift(mu, j2, rBody, a, e, inc)'' | ''f64'' | Drehung der Apsidenlinie in **rad/s** |
| ''ACriticalInclination()'' | ''f64'' | 63.4349° in Radiant — dort verschwindet der Apsidendrift |
| ''ACriticalInclinationRetro()'' | ''f64'' | die zweite Lösung, 116.5651° |
| ''ASunSyncDrift()'' | ''f64'' | Solldrift einer sonnensynchronen Bahn: 360° je **tropischem** Jahr |
| ''ASunSyncInclination(mu, j2, rBody, a, e)'' | ''f64'' | Neigung, die diese Drift erzeugt; Fehlerwert, wenn es bei dieser Höhe keine gibt |
Drei Punkte, an denen die Formeln anders sind, als man sie im Kopf hat:
* **In der Driftformel steht der Halbparameter ''p = a(1−e²)'', nicht die große Halbachse.** Bei einer Kreisbahn ist das dasselbe, bei Molniya mit ''e = 0.74'' unterscheidet es sich um mehr als das Doppelte.
* **Sonnensynchron heißt tropisches Jahr** (365.2422 Tage), also synchron mit der **Sonne** und nicht mit den Fixsternen. Die Bahn heißt nach dem, womit sie mitläuft.
* **Eine sonnensynchrone Bahn ist zwangsläufig rückläufig.** Die verlangte Drift ist positiv, also muss der Kosinus der Neigung negativ sein. Deshalb liegen alle SSO-Neigungen über 90°.
Ein Zahlenbeispiel steht auf der Seite [[lyx_-_programmiersprache:units:astro:bahntypen|Bahntypen]]: 700 km Bahnhöhe verlangen 98.188°, und die ISS-Bahnebene wandert 5.0023 Grad pro Tag nach Westen.
Auf demselben Term bauen drei weitere Funktionsgruppen auf:
^ Funktion ^ Rückgabe ^ Bedeutung ^
| ''ANodalPeriod(mu, j2, rBody, a, e, inc)'' | ''f64'' | Umlaufzeit von **Knoten zu Knoten** — nicht dasselbe wie ''APeriod'' |
| ''ARepeatCycleFromSma(mu, j2, rBody, omegaBody, a, e, inc)'' | ''f64'' | Umläufe pro Tag einer gegebenen Bahn |
| ''ASmaFromRepeatCycle(mu, j2, rBody, omegaBody, k, m, e, inc)'' | ''f64'' | Halbachse für //k// Umläufe in //m// Tagen |
| ''ADecayPerRev(rho, ballisticCoeff, a)'' | ''f64'' | Höhenverlust je Umlauf durch Luftwiderstand |
| ''ADecayRate(mu, rho, ballisticCoeff, a)'' | ''f64'' | derselbe Verlust als Rate |
| ''AJ3Earth()'' | ''f64'' | dritter zonaler Koeffizient, −2.532·10⁻⁶ |
| ''AFrozenArgPerigee()'' | ''f64'' | 90° — die einzige Lage, in der ein Perigäum stehenbleibt |
| ''AFrozenEccentricity(j2, j3, rBody, a, inc)'' | ''f64'' | Exzentrizität, bei der J2 und J3 sich aufheben |
| ''AIsFrozen(j2, j3, rBody, a, e, inc, argP, toleranz)'' | ''bool'' | Prüfung einer gegebenen Bahn |
* **Wiederholbahnen** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1993|#1993]]) schließen sich, wenn die Nodalperiode, die Erddrehung und die Knotendrift zusammenpassen. ''ASmaFromRepeatCycle'' liefert für den Landsat-Zyklus 233/16 eine Bahnhöhe von 700.3 km.
* **Bahnverfall** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1994|#1994]]) verbindet die Luftdichte mit dem ballistischen Koeffizienten aus [[lyx_-_programmiersprache:units:astro:entry|std.astro.entry]]. Die Dichte ist ein **Parameter**, kein Modell — sie kommt vom Aufrufer.
* **Frozen Orbits** ([[https://github.com/SEOLizer/LyX-Compiler/issues/1996|#1996]]) brauchen J3 zusätzlich zu J2: J2 dreht das Perigäum, J3 zieht es zurück, und bei genau einer Exzentrizität heben sich beide auf. Für 800 km bei 98.6° sind das 0.0010274.
Zeitabhängige Rechnungen stehen bewusst **nicht** hier, sondern in [[lyx_-_programmiersprache:units:astro:groundtrack|std.astro.groundtrack]]: alles in dieser Unit ist zeitlos, eine Bahn hat eine Periode, aber keinen Zeitpunkt.
===== 9. Die Parameter im Einzelnen =====
^ Name ^ Bedeutung ^ Einheit ^ Zu beachten ^
| ''mu'' | Gravitationsparameter ''G·M'' des **Zentralkörpers** | m³/s² | ''AMuEarth()'' & Co.; für einen anderen Körper ''AMuFromMass(m)''. Muss > 0 sein |
| ''r'' | Abstand vom **Mittelpunkt**, nicht Höhe über der Oberfläche | m | ''ARadiusEarth() + hoehe''. Muss > 0 sein |
| ''a'' | große Halbachse; bei der Kreisbahn ''a = r'', bei der Hyperbel **negativ** | m | ''a = 0'' ist ungültig (Parabel) |
| ''e'' | numerische Exzentrizität: 0 Kreis, <1 Ellipse, 1 Parabel, >1 Hyperbel | dimensionslos | ''APeriapsis''/''AApoapsis'' verlangen ''e < 1'', ''ATurnAngle''/''AImpactParameter'' verlangen ''e > 1'' |
| ''v'' | Bahngeschwindigkeit am Ort ''r'' | m/s | |
| ''vInf'' | hyperbolische Überschussgeschwindigkeit „im Unendlichen" | m/s | 0 ist gültig (Parabel) |
| ''r1'', ''r2'' | Radien der Ausgangs- und der Zielkreisbahn beim Hohmann-Transfer | m | ''r2 < r1'' ist erlaubt: das Absenken |
| ''dInc'' | Änderung der Bahnneigung | **Radiant** | Grad × ''Pi()/180.0'' |
| ''T'' | Umlaufzeit bei ''ASemiMajorFromPeriod'' | s | für geostationär die **siderische** 86164 s, nicht 86400 |
| ''b'' | Stoßparameter eines Vorbeiflugs | m | |
| ''s'' | Zustand aus [[lyx_-_programmiersprache:units:astro:state|std.astro.state]] | — | muss ''AStateValid'' bestehen |
> **Drei Verwechslungen, die Zahlen liefern statt Fehler:**
>
> * **Höhe statt Radius.** ''ACircularSpeed(mu, 400000.0)'' rechnet eine Bahn 400 km vom Erdmittelpunkt — tief im Inneren. Das Ergebnis (31.6 km/s) sieht aus wie eine Geschwindigkeit.
> * **Grad statt Radiant.** ''APlaneChangeDv(7725.8, 30.0)'' liest 30 als Radiant, also knapp fünf volle Umdrehungen. Alle Winkel dieser Unit sind Radiant.
> * **86400 statt 86164 s.** Der geostationäre Radius folgt aus dem **Sterntag**; mit dem Sonnentag kommen rund 42 240 km statt 42 164 km heraus — 76 km daneben.
===== 10. Beispiel: Kreisbahn und Transfer =====
import std.astro.state;
import std.astro.orbit;
import std.io;
fn zeig(n: pchar, v: f64, d: int64): void { Print(n); PrintLn(FloatToStr(v, d)); }
fn BI(b: bool): int64 { if (b) { return 1; } return 0; }
fn main(): int64 {
var mu: f64 := AMuEarth();
var leo: f64 := 6678000.0; // 300 km Hoehe
var geo: f64 := 42164000.0;
zeig("g0 Oberflaeche : ", ASurfaceGravity(mu, ARadiusEarth()), 4);
zeig("v_kreis LEO : ", ACircularSpeed(mu, leo), 2);
zeig("v_flucht LEO : ", AEscapeSpeed(mu, leo), 2);
zeig("Energie LEO : ", AEnergy(mu, leo, ACircularSpeed(mu, leo)), 1);
zeig("Umlaufzeit min : ", APeriod(mu, leo) / 60.0, 2);
zeig("a aus T (siderisch): ", ASemiMajorFromPeriod(mu, 86164.0), 1);
zeig("Hohmann dv1 : ", AHohmannDv1(mu, leo, geo), 1);
zeig("Hohmann dv2 : ", AHohmannDv2(mu, leo, geo), 1);
zeig("Hohmann dv gesamt : ", AHohmannDv(mu, leo, geo), 1);
zeig("Hohmann t Stunden : ", AHohmannTime(mu, leo, geo) / 3600.0, 2);
zeig("Ebenenaend. 1 Grad : ", APlaneChangeDv(7725.8, 0.017453292519943295), 1);
zeig("v_inf bei 11 km/s : ", AHyperbolicExcess(mu, leo, 11000.0), 1);
Print("v_inf zu langsam : ");
PrintLn(IntToStr(BI(AIsError(AHyperbolicExcess(mu, leo, 7000.0)))));
return 0;
}
g0 Oberflaeche : 9.7983
v_kreis LEO : 7725.84
v_flucht LEO : 10925.99
Energie LEO : -29844297.8
Umlaufzeit min : 90.52
a aus T (siderisch): 42164138.8
Hohmann dv1 : 2425.8
Hohmann dv2 : 1466.8
Hohmann dv gesamt : 3892.6
Hohmann t Stunden : 5.28
Ebenenaend. 1 Grad : 134.8
v_inf bei 11 km/s : 1273.9
v_inf zu langsam : 1
Die letzte Zeile ist der wichtigste Teil des Beispiels: 7 km/s reichen in 300 km Höhe nicht zur Flucht, und die Funktion sagt das mit NaN — statt 0 zurückzugeben, was wie eine gerade noch erreichte Flucht aussähe.
===== 11. Beispiel: Bahnelemente einer Ellipse =====
Aus einem einzigen Zustand im Perigäum lässt sich die ganze Bahn ablesen — hier eine Molniya-artige Bahn (Perigäum 600 km, Apogäum 39 900 km, 63.4 Grad Neigung):
import std.astro.state;
import std.astro.orbit;
import std.math;
import std.io;
fn zeig(n: pchar, v: f64, d: int64): void { Print(n); PrintLn(FloatToStr(v, d)); }
fn main(): int64 {
var mu: f64 := AMuEarth();
var rad: f64 := Pi() / 180.0;
var rp: f64 := ARadiusEarth() + 600000.0;
var ra: f64 := ARadiusEarth() + 39900000.0;
var a: f64 := (rp + ra) / 2.0;
var vp: f64 := AVisViva(mu, rp, a); // Tempo im Perigaeum
var inc: f64 := 63.4 * rad;
// Im Perigaeum steht die Geschwindigkeit senkrecht auf dem Ort
var s: AState := AMakeState(rp, 0.0, 0.0, 0.0, vp * CosF64(inc), vp * SinF64(inc));
zeig("Tempo Perigaeum : ", ASpeed(s), 1);
zeig("Halbachse km : ", ASemiMajorAxis(mu, ARadius(s), ASpeed(s)) / 1000.0, 1);
zeig("Exzentrizitaet : ", AEccentricity(mu, s), 5);
zeig("Inklination Grad : ", AInclination(s) / rad, 2);
zeig("Umlaufzeit h : ", APeriod(mu, a) / 3600.0, 3);
zeig("Perigaeum km : ", APeriapsis(a, AEccentricity(mu, s)) / 1000.0, 1);
zeig("Apogaeum km : ", AApoapsis(a, AEccentricity(mu, s)) / 1000.0, 1);
zeig("wahre Anomalie Grad: ", ATrueAnomaly(mu, s) / rad, 2);
zeig("Tempo Apogaeum : ", AVisViva(mu, ra, a), 1);
return 0;
}
Tempo Perigaeum : 9963.6
Halbachse km : 26628.1
Exzentrizitaet : 0.73794
Inklination Grad : 63.40
Umlaufzeit h : 12.012
Perigaeum km : 6978.1
Apogaeum km : 46278.1
wahre Anomalie Grad: 0.00
Tempo Apogaeum : 1502.4
Zwei Proben, die zeigen, dass die Rechnung geschlossen ist: die zurückgerechneten Peri- und Apozentrumsabstände treffen die Eingabe wieder (6978 km = 6378 + 600, 46278 km = 6378 + 39900), und die **wahre Anomalie ist 0** — der Zustand steht ja im Perigäum.
Die Umlaufzeit von 12.012 h ist der Grund für diese Bahnfamilie: zwei Umläufe je Sterntag, mit langem Verweilen über der Nordhalbkugel. Das Tempo unterscheidet sich dabei um den Faktor 6.6 zwischen Perigäum (9964 m/s) und Apogäum (1502 m/s) — deshalb sind Manöver im Apogäum billig.
----
Letzte Aktualisierung: 2026-08-31 — Parametertabelle mit den drei häufigen Verwechslungen (Höhe/Radius, Grad/Radiant, Sonnentag/Sterntag) und ein zweites Beispiel zu den Bahnelementen ergänzt; beide Beispiele mit ''lyxc 1.1.15B'' gebaut und ausgeführt.
Zuvor 2026-08-31 — Seite neu angelegt aus ''std/astro/orbit.lyx'' (PR #1890).