Einen Raumfahrzeug-Computersimulator mit Claude Code bauen

Ich wollte verstehen, wie Computer auf Raumfahrzeugen funktionieren. Die Echtzeit-Planung, die Strahlungshärtung, die verzögerungstolerante Vernetzung, die Mars-Rover mit der Erde in Verbindung hält. Mit Artemis II am Horizont (die erste bemannte Mondmission seit über 50 Jahren) schien es der richtige Zeitpunkt zu sein, tiefer einzutauchen. Also baute ich einen Simulator. Von Grund auf. Mit Claude Code als meinem Pair-Programmer.

Keine physische Hardware. Alles läuft in QEMU und Docker auf einem Laptop. Code auf GitHub.

flowchart LR
    QEMU["QEMU (Cortex-M3)"] -->|UART socket| Bridge[uart_bridge.py]
    Bridge -->|bpsendfile| SC[Spacecraft Node]
    SC -->|LTP + 5s delay| GS[Ground Station]

    subgraph Docker
        SC
        GS
    end
flowchart TB
    QEMU["QEMU (Cortex-M3)"] -->|UART socket| Bridge[uart_bridge.py]
    Bridge -->|bpsendfile| SC["Spacecraft Node (Docker)"]
    SC -->|LTP + 5s delay| GS["Ground Station (Docker)"]

Warum Dieses Projekt

Ich wollte etwas wirklich Schwieriges lernen. Etwas, bei dem die Konzepte unbekannt und das Werkzeug unnachgiebig sind. C-Compiler, die auf ARM abzielen. Linker-Skripte. Speicherabgebildete Ein-/Ausgabe. Interrupt-Vektortabellen.

Claude Code hat das möglich gemacht. Nicht weil es den gesamten Code schrieb, sondern weil es erklärte, was ein Linker-Skript tut, während wir eines schrieben, sodass die Erklärung im eigentlichen Problem verankert war, das ich löste. Dasselbe gilt für volatile, FreeRTOS-Prioritäts-Preemption und LTP-Neuübertragungstimer.

Der Fahrplan

Ich teilte das Projekt in vier Phasen auf, jede auf der letzten aufbauend:

PhaseWasSchlüsselkonzepte
Bare MetalARM-Kreuzkompilierung, UART-Ausgabe, Interrupt-HandlerSpeicherabgebildete E/A, SysTick-Timer, Start-Assembly
FreeRTOSTasks, Warteschlangen, Watchdogs, PrioritätsinversionDeterministisches Scheduling, Mutex-Protokolle, Preemption
DTNZwei-Knoten-Netzwerk, degradierte Verbindungen, CFDP, Kontaktgraph-RoutingBundle-Protokoll, Store-and-Forward, LTP-Zuverlässigkeit
IntegrationVollständige Telemetrie-Pipeline mit Mars-EntfernungsverzögerungenUART-Brücke, tc netem, synchronisiertes ION OWLT

Jede Phase hat automatisierte Tests. Jeder Meilenstein ist ein PR mit bestandenem CI.

Phase 1: Bare Metal

Die erste Herausforderung war, überhaupt etwas zum Laufen zu bringen. ARM-Kreuzkompilierung, die auf einen Cortex-M3 (MPS2-AN385) in QEMU abzielt. Kein Betriebssystem, keine Standardbibliothek, kein printf.

Claude half mir, die Startsequenz zu verstehen: die Vektortabelle, den Reset-Handler, das Kopieren von .data von Flash in RAM, das Nullen von .bss. Dinge, die passieren, bevor main() überhaupt ausgeführt wird.

Der erste Erfolg war ein einzelnes Zeichen auf einer UART-Konsole:

#define UART0_DR  (*(volatile uint32_t *)0x40004000)

void uart_putc(char c) {
    UART0_DR = c;
}

Zwei Zeilen C. Keine Bibliotheken. Nur ein Byte an eine Speicheradresse schreiben, die zufällig mit einem seriellen Port verdrahtet ist. Es fühlte sich an wie direkt mit der Maschine zu sprechen.

Danach: SysTick-Timer-Interrupts, Interrupt-Handler, strukturierte Ausgabe. Die SysTick-Demo konfiguriert einen Hardware-Timer, der bei 2 Hz feuert und 10 Ticks zählt:

SysTick interrupt demo
======================
Ticking at 2 Hz for 5 seconds (10 ticks)...

  tick 1
  tick 2
  tick 3
  tick 4
  tick 5
  tick 6
  tick 7
  tick 8
  tick 9
  tick 10

Done — 5 seconds counted by interrupt.

Keine verlorenen Ticks. Keine extra Ticks. Die CPU schläft zwischen Interrupts und die Hardware weckt sie genau im richtigen Moment auf. Deterministische Ausführung aus dem Bare Metal.

Phase 2: FreeRTOS

Mit funktionierendem Bare Metal fügte ich FreeRTOS hinzu, ein Echtzeit-Betriebssystem, das auf allem läuft, von medizinischen Geräten bis hin zu Satelliten.

Die Übungen wurden schrittweise aufgebaut:

  1. Zwei Tasks mit unterschiedlichen Raten. Grundlegendes Multitasking.
  2. Warteschlangen-basierte Kommunikation. Daten sicher zwischen Tasks teilen.
  3. Watchdog-Timer. Hängende Tasks erkennen und davon erholen.
  4. Sensor-Pipeline. Vier Sensoren mit unterschiedlichen Raten, die eine Verarbeitungskette speisen.
  5. Prioritätsinversion. Den klassischen RTOS-Bug auslösen und beheben.

Hier startet die Sensor-Pipeline. Beachten Sie, wie der 10-Hz-Gyroskop den Datenstrom dominiert, mit dem 1-Hz-Temperaturlesegerät, das bei Tick 1001 eingeschoben wird:

FreeRTOS Sensor Pipeline Demo
=============================

[GYRO]  Sensor online (10 Hz, priority 4)
[PROC]  Processor online (priority 3)
[TELEM] Telemetry online (priority 2)
[TEMP]  Sensor online (1 Hz, priority 1)
[TELEM] #000 GYRO: 300 at tick 100
[TELEM] #001 GYRO: 300 at tick 200
  ...
[TELEM] #009 GYRO: 300 at tick 1000
[TELEM] #010 TEMP: 1991 at tick 1001
[TELEM] #011 GYRO: 300 at tick 1100

Das ist prioritätsbasierte Preemption in Aktion. Das Gyroskop läuft mit Priorität 4, der Temperatursensor mit Priorität 1. Die Temperaturmessung kommt nur durch, wenn das Gyroskop die CPU nicht belegt.

Die Prioritätsinversionsübung war die lehrreichste. Ich erstellte drei Tasks, bei denen ein niedrigprioritärer Task einen Mutex hält, den ein hochprioritärer Task benötigt, und ein mittelprioritärer Task beide aushungert. Die Lösung: Prioritätsvererbung, bei der FreeRTOS den niedrigprioritären Task vorübergehend anhebt, damit er den Mutex schneller freigeben kann.

Das ist der Bug, der die Mars-Pathfinder-Mission 1997 fast getötet hätte. Es selbst zu bauen ließ die Lehrbucherklärung einrasten.

Phase 3: Verzögerungstolerante Vernetzung

TCP/IP funktioniert nicht im Weltraum. Hin-und-Rückzeiten zum Mars reichen von 6 bis 44 Minuten. Verbindungen fallen stundenlang aus, wenn Planeten das Signal verdecken. TCPs Annahme einer kontinuierlichen Verbindung mit niedriger Latenz bricht vollständig zusammen.

NASA JPLs Antwort ist DTN, oder Verzögerungstolerante Vernetzung. Das Bundle-Protokoll speichert Daten lokal und leitet sie Hop für Hop weiter, wenn Verbindungen verfügbar werden. Es ist genau für die Bedingungen konzipiert, die das Internet kaputt machen.

Ich baute NASAs ION-Implementierung in Docker und führte zunehmend schwierigere Tests durch:

Der intermittierende Verbindungstest erzählt die ganze DTN-Geschichte in ein paar Ausgabezeilen:

Test: intermittent link (send during outage, deliver on recovery)...
    qdisc: qdisc netem root refcnt 2 limit 1000 loss 100%
    confirmed: bundle queued (link is down)
    restoring link...
  PASS: bundle held during outage
  PASS: bundle delivered after link recovery

Das ist Store-and-Forward in Aktion. Das Bundle wird gesendet, während die Verbindung vollständig tot ist, 100% Paketverlust. Es sitzt in der Warteschlange des lokalen Knotens. In dem Moment, in dem wir die netem-Regel löschen und die Konnektivität wiederherstellen, überträgt LTP neu und das Bundle kommt am anderen Ende an. TCP hätte längst aufgegeben.

Phase 4: Integration

Die letzte Phase verbindet alles. FreeRTOS-Firmware generiert Telemetrie in QEMU. Ein Python-Brücken-Skript liest die UART-Ausgabe und injiziert sie als DTN-Bundles. Die Bundles durchqueren ein verzögertes Netzwerk, um eine Bodenstation zu erreichen.

Die Firmware startet und beginnt sofort zu streamen:

# Spacecraft Telemetry Firmware v1.0
# ===================================
# GYRO sensor online (10 Hz, priority 4)
# Processor online (priority 3)
# Telemetry online (priority 2)
# TEMP sensor online (1 Hz, priority 1)
# BATT sensor online (0.5 Hz, priority 1)
# SUN sensor online (2 Hz, priority 1)
$TELEM,0000,GYRO,300,100
$TELEM,0001,GYRO,300,200
  ...
$TELEM,0005,SUN,801,501
  ...
$TELEM,0011,TEMP,1991,1001
$TELEM,0012,SUN,801,1001

Vier Sensoren mit vier verschiedenen Raten. Man kann sehen, wie das 10-Hz-Gyroskop die meisten Messwerte produziert, mit dem 2-Hz-Sonnensensor, der 1-Hz-Temperatur und der 0,5-Hz-Batterie dazwischen. Die Brücke fasst diese alle 2 Sekunden in DTN-Bundles zusammen.

Der Mars-Verzögerungstest beweist, dass die gesamte Pipeline unter realistischen Bedingungen funktioniert:

Running Mars-delay end-to-end tests...
  PASS: tc netem delay 5s applied on spacecraft
  PASS: bridge exited cleanly
  PASS: bridge read telemetry lines
  PASS: bridge sent >= 1 bundle (got 12)
  PASS: ground station received files (got 8)
  PASS: received telemetry lines (got 224)
  PASS: telemetry CSV format valid (5 fields)
  INFO: delay observable (8 delivered at bridge exit < 12 sent)
7 passed, 0 failed

12 Bundles gesendet, aber nur 8 geliefert, als die Brücke beendete. Der Rest war noch unterwegs durch die 5-Sekunden-Verzögerung. Das ist die Verzögerung, die real ist, nicht in Software simuliert.

Die Mars-Verzögerungssimulation verwendet zwei synchronisierte Mechanismen:

  1. tc netem fügt 5 Sekunden echter Netzwerklatenz zu beiden Containern hinzu
  2. ION-Bereichstabellen setzen die gleiche 5-Sekunden-Einweglichtlaufzeit, damit LTP-Neuübertragungstimer korrekt sind

Beide müssen übereinstimmen. Wenn die tatsächliche Verzögerung 5 Sekunden beträgt, ION aber glaubt, es sei 1 Sekunde, überträgt LTP aggressiv neu und überflutet die Verbindung. Das richtig hinzubekommen lehrte mich mehr über Protokolldesign als jedes Lehrbuchkapitel über Zuverlässigkeit.

Mit Claude Code arbeiten

Dieses Projekt hätte mich ohne Claude Monate gekostet. Nicht weil der Code komplex ist (die meisten Dateien haben unter 300 Zeilen), sondern weil die Lernkurve für jedes Gebiet steil ist.

Was gut funktioniert hat:

Was Sorgfalt erforderte:

Die Zahlen

MetrikWert
Gesamte Tests50+ Assertions in 7 Test-Suites
SprachenC, Python, Bash
HardwareKeine (QEMU + Docker)
Zeilen C~1.200 (Firmware + Bare Metal)
Zeilen Python~1.500 (Tests + Brücke + DTN-Skripte)
ION DTN-Konfiguration~350 Zeilen in 6 .rc-Dateien

Ausprobieren

Alles ist Open Source und läuft auf jedem Linux-Rechner mit QEMU und Docker:

sudo apt install gcc-arm-none-eabi qemu-system-arm build-essential
git clone https://github.com/granda/spacecraft-computing-sim
cd spacecraft-computing-sim

make -C bare-metal run          # bare metal UART output
make -C freertos run            # FreeRTOS sensor pipeline
make -C dtn test                # two-node DTN network
make -C integration test-bridge # full telemetry pipeline
make -C integration test-mars-delay  # Mars-distance delays

Keine physische Hardware. Keine Cloud-Dienste. Nur ein Laptop und Neugier darüber, wie Computer auf Raumfahrzeugen funktionieren.