import std.astro.state;
Ort und Geschwindigkeit in einem Inertialsystem, dazu Vektorrechnung und das begleitende Dreibein. Die Unit ist die Grundlage der übrigen std.astro-Units; sie rechnet selbst keine Bahnmechanik.
Einheiten durchgehend SI: Meter, Meter je Sekunde, Sekunden, m³/s².
→ std.astro (Übersicht) · std.astro.orbit · Guide: Kurs und Lage im 3D-Raum
pub type AVec3 = struct { x: f64; y: f64; z: f64; }
pub type AState = struct {
rx: f64; ry: f64; rz: f64; // Ort [m]
vx: f64; vy: f64; vTarget: f64; // Geschwindigkeit [m/s]
}
Das dritte Geschwindigkeitsfeld heißt wiedervz(#1902, nachgemessen mit lyxc 1.2.2B). Frühere Fassungen der Unit trugen dortvTarget— ein Benennungsfehler in der öffentlichen Schnittstelle, der auchAMakeStatebetraf. Heute gilts.vz, undAMakeState(rx, ry, rz, vx, vy, vz)heißt so, wie es aussieht.
Sie rechnen nichts. Sie erlauben, eine Verwechslung auszusprechen, statt sie im Kommentar zu lassen.
| Konstante | Wert | Bedeutung |
|---|---|---|
AFRAME_UNKNOWN | 0 | unbekannt — AFrameMatches meldet damit nie Übereinstimmung |
AFRAME_ECI | 1 | erdzentriert inertial |
AFRAME_ECEF | 2 | erdzentriert mitrotierend |
AFRAME_LVLH | 3 | lokal horizontal, lokal vertikal |
AFRAME_RIC | 4 | radial / in-track / cross-track |
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
AError() | f64 | NaN — der Fehlerwert der gesamten std.astro-Familie |
AIsError(x: f64) | bool | ob x NaN ist (NaN ist der einzige Wert, der sich selbst ungleich ist) |
AVecIsError(a: AVec3) | bool | ob eine Komponente NaN ist |
Warum NaN und nicht 0 oder −1. Bei Bahnrechnungen sehen0und1beide wie gültige Ergebnisse aus, und ein stiller Ersatzwert wandert unbemerkt in die nächste Rechnung. NaN pflanzt sich fort und lässt sich am Ende einmal prüfen — genau dafür ist er da. Jede Funktion dieser Unit-Familie gibt bei unmöglicher Eingabe NaN zurück.
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
AVec(x, y, z) | AVec3 | Vektor bauen |
AAdd(a, b) · ASub(a, b) | AVec3 | Summe, Differenz |
AScale(a, s: f64) | AVec3 | mit einem Skalar strecken |
ADot(a, b) | f64 | Skalarprodukt |
ACross(a, b) | AVec3 | Kreuzprodukt |
AMagnitude(a) | f64 | Betrag |
AUnitVec(a) | AVec3 | Einheitsvektor; beim Nullvektor NaN in allen Komponenten — er hat keine Richtung, und (0,0,0) wäre ein gültiger Vektor, der unbemerkt weiterwandert |
AAngle(a, b) | f64 [rad] | Winkel 0…π; NaN, wenn einer der beiden der Nullvektor ist |
AAnglerechnet überatan2(|a×b|, a·b), nicht überacos. Dieacos-Form verliert bei fast parallelen Vektoren jede Genauigkeit, weil der Kosinus dort flach ist — und genau dieser Fall (zwei fast gleiche Lagevektoren) ist in der Bahnmechanik der häufige.
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
AMakeState(rx, ry, rz, vx, vy, vTarget) | AState | Zustand bauen |
APosition(s) · AVelocity(s) | AVec3 | Ort, Geschwindigkeit als Vektor |
ARadius(s) · ASpeed(s) | f64 | Betrag von Ort und Geschwindigkeit |
AStateValid(s) | bool | NaN in einer Komponente oder Ort im Ursprung ⇒ false |
AFrameMatches(a: int64, b: int64) | bool | gleiche Rahmenkennung; AFRAME_UNKNOWN ergibt immer false |
AAngularMomentum(s) | AVec3 [m²/s] | spezifischer Drehimpuls r × v |
ARadialSpeed(s) | f64 | radiale Geschwindigkeit, positiv beim Entfernen |
Der Ursprung ist kein Randfall, den man mitrechnen könnte: dort ist die Gravitationsbeschleunigung nicht definiert, und jede Bahnrechnung teilt durch den Abstand.
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
ATangent(s) | AVec3 | Einheitsvektor in Bewegungsrichtung |
ABinormal(s) | AVec3 | senkrecht auf der Bahnebene, entlang des Drehimpulses |
ANormal(s) | AVec3 | B × T — vervollständigt das Rechtssystem |
Bei einer geraden Bahn gibt es keine Bahnebene. Sind Ort und Geschwindigkeit parallel (senkrechter Auf- oder Abstieg), istBnicht bestimmt, und alle drei Funktionen liefern NaN. Eine gewählte Ersatzrichtung wäre hier besonders gefährlich — sie sieht aus wie eine Bahnebene.
| Name | Bedeutung | Einheit | Gültiger Bereich |
|---|---|---|---|
rx, ry, rz | Ort im Inertialsystem, vom Mittelpunkt des Zentralkörpers aus — nicht von der Oberfläche | m | Betrag > 0; der Ursprung ist ungültig |
vx, vy, vTarget | Geschwindigkeit im selben Rahmen wie der Ort (#1902: das dritte Feld sollte vz heißen) | m/s | beliebig, auch 0 |
s | ein AState; die meisten Funktionen prüfen ihn nicht selbst — AStateValid ist die Stelle dafür | — | — |
a, b | Vektoren; bei AAngle und AUnitVec darf keiner der Nullvektor sein | je nach Zusammenhang | — |
s: f64 bei AScale | Streckfaktor | dimensionslos | beliebig, auch negativ |
Die Höhe ist nicht der Radius. Ein Zustand in „400 km Höhe„ hatrx = ARadiusEarth() + 400000.0— also rund 6 778 137 m. Wer die Höhe direkt einsetzt, rechnet mit einer Bahn im Erdinneren; alle Formeln liefern dann Zahlen, nur eben falsche. Das ist der häufigste Fehler beim Einstieg, und keine Funktion kann ihn erkennen.
Die Radiusfunktionen dafür stehen in std.astro.orbit:ARadiusEarth(),ARadiusMoon(),ARadiusMars().
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 BI(b: bool): int64 { if (b) { return 1; } return 0; }
fn main(): int64 {
var mu: f64 := AMuEarth();
var r: f64 := 6678000.0; // 300 km Hoehe
var vc: f64 := ACircularSpeed(mu, r);
var inc: f64 := 51.6 * Pi() / 180.0; // Neigung wie die ISS
var s: AState := AMakeState(r, 0.0, 0.0, 0.0, vc * CosF64(inc), vc * SinF64(inc));
Print("Zustand gueltig : "); PrintLn(IntToStr(BI(AStateValid(s))));
zeig("Radius : ", ARadius(s), 1);
zeig("Tempo : ", ASpeed(s), 2);
zeig("Radialtempo : ", ARadialSpeed(s), 6);
zeig("|h| : ", AMagnitude(AAngularMomentum(s)), 1);
var t: AVec3 := ATangent(s);
var b: AVec3 := ABinormal(s);
var n: AVec3 := ANormal(s);
zeig("T*B (soll 0) : ", ADot(t, b), 12);
zeig("|N| (soll 1) : ", AMagnitude(n), 12);
Print("Rahmen ECI==ECEF : ");
PrintLn(IntToStr(BI(AFrameMatches(AFRAME_ECI, AFRAME_ECEF))));
Print("Rahmen UNKNOWN : ");
PrintLn(IntToStr(BI(AFrameMatches(AFRAME_UNKNOWN, AFRAME_UNKNOWN))));
return 0;
}
Zustand gueltig : 1
Radius : 6678000.0
Tempo : 7725.84
Radialtempo : 0.000000
|h| : 51593156041.7
T*B (soll 0) : 0.000000000000
|N| (soll 1) : 1.000000000000
Rahmen ECI==ECEF : 0
Rahmen UNKNOWN : 0
Die Unit gibt bei unmöglicher Eingabe NaN zurück — hier drei Fälle, in denen das trägt:
import std.astro.state;
import std.io;
fn BI(b: bool): int64 { if (b) { return 1; } return 0; }
fn zeigVec(n: pchar, v: AVec3): void {
Print(n);
if (AVecIsError(v)) { PrintLn("NaN (nicht bestimmt)"); return; }
Print("("); Print(FloatToStr(v.x, 3)); Print(", ");
Print(FloatToStr(v.y, 3)); Print(", "); Print(FloatToStr(v.z, 3)); PrintLn(")");
}
fn main(): int64 {
// 1. Senkrechter Aufstieg: Ort und Geschwindigkeit parallel -> keine Bahnebene
var senk: AState := AMakeState(6678000.0, 0.0, 0.0, 1200.0, 0.0, 0.0);
Print("Zustand gueltig : "); PrintLn(IntToStr(BI(AStateValid(senk))));
zeigVec("Tangente : ", ATangent(senk));
zeigVec("Binormale : ", ABinormal(senk));
zeigVec("Normale : ", ANormal(senk));
Print("Radialtempo : "); PrintLn(FloatToStr(ARadialSpeed(senk), 1));
// 2. Ort im Ursprung: dort ist die Schwerkraft nicht definiert
var nullpunkt: AState := AMakeState(0.0, 0.0, 0.0, 0.0, 7700.0, 0.0);
Print("Ursprung gueltig : "); PrintLn(IntToStr(BI(AStateValid(nullpunkt))));
// 3. NaN pflanzt sich fort und wird EINMAL geprueft
var x: f64 := AAngle(AVec(0.0, 0.0, 0.0), AVec(1.0, 0.0, 0.0));
var y: f64 := x * 2.0 + 100.0;
Print("Kette NaN geblieben: "); PrintLn(IntToStr(BI(AIsError(y))));
return 0;
}
Zustand gueltig : 1
Tangente : (1.000, 0.000, 0.000)
Binormale : NaN (nicht bestimmt)
Normale : NaN (nicht bestimmt)
Radialtempo : 1200.0
Ursprung gueltig : 0
Kette NaN geblieben: 1
Drei Dinge stehen darin:
AStateValid meldet 1, die Tangente zeigt nach außen, das Radialtempo ist die volle Geschwindigkeit. Nur eine Bahnebene gibt es nicht, und deshalb sind Binormale und Normale NaN. Eine gewählte Ersatzrichtung sähe hier aus wie eine echte Bahnebene.AStateValid abgefangen, bevor irgendeine Formel durch den Abstand teilt.x * 2.0 + 100.0 ist immer noch NaN. Deshalb genügt eine Prüfung am Ende einer Kette statt einer nach jedem Schritt.
Letzte Aktualisierung: 2026-09-05 (#1902, nachgemessen mit lyxc 1.2.2B) — das Feld heißt wieder vz; der Kasten zum Benennungsfehler ist auf den behobenen Stand gezogen.
Vorherige letzte Aktualisierung: 2026-08-31 — Parametertabelle samt Höhe-gegen-Radius-Kasten und ein zweites Beispiel zu den Fehlerfällen ergänzt; beide Beispiele mit lyxc 1.1.15B gebaut und ausgeführt.
Zuvor 2026-08-31 — Seite neu angelegt aus std/astro/state.lyx (PR #1890).