Konstrui Kosmoŝipan Komputadan Simulilon kun Claude Code

Mi volis kompreni kiel funkcias kosmoŝipaj komputiloj. La realtempan planadون, la radiado-hardadon, la prokrasto-tolereman rettadon kiu konservas la Marsmarsejojn komunikantaj kun la Tero. Kun Artemis II ĉe la horizonto (la unua pilotata luna misio en pli ol 50 jaroj) ŝajnis la ĝusta momento por enplonĝi. Do mi konstruis simulilon. Ekde la komenco. Kun Claude Code kiel mia parprogramisto.

Neniu fizika aparataro. Ĉio funkcias en QEMU kaj Docker sur portkomputilo. Kodo sur 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)"]

Kial Ĉi Tiu Projekto

Mi volis lerni ion vere malfacilan. Ion kie la konceptoj estas nekonataj kaj la iloj estas senkompatemaj. C-kompililoj celantaj ARM. Ligilaj skriptoj. Memoro-mapita Enigo/Eligo. Interrompo-vektora tablo.

Claude Code ebligis tion. Ne ĉar ĝi verkis la tutan kodon, sed ĉar ĝi klarigis kion faras ligila skripto dum ni verkis unu, do la klarigo estis enradikigita en la reala problemo kiun mi solvis. Same por volatile, FreeRTOS prioritat-antaŭpreno kaj LTP-retransmisiaj kronoj.

La Vojmapo

Mi dividis la projekton en kvar fazojn, ĉiu konstruita sur la antaŭa:

FazoKioĈefaj Konceptoj
Nuda MetaloARM-kruckompilado, UART-eligo, interrompo-prizorgantojMemoro-mapita E/E, SysTick-krono, ekfunkcio-asembleo
FreeRTOSTaskoj, vicoj, gardokronoj, prioritat-inversiĝoDeterminisma planado, mutex-protokoloj, antaŭpreno
DTNDu-nodа reto, degraĵitaj ligiloj, CFDP, kontakta-grafea vojigadoBundle Protocol, stoki-kaj-plusendi, LTP-fidindeco
IntegradoPlena telemetria konvejero kun Mars-distancaj prokrastojUART-ponto, tc netem, sinkronigita ION OWLT

Ĉiu fazo havas aŭtomatigitajn testojn. Ĉiu ŝtono estas PR kun CI-pasado.

Fazo 1: Nuda Metalo

La unua defio estis igi ion funkcii. ARM-kruckompilado celanta Cortex-M3 (MPS2-AN385) en QEMU. Neniu OS, neniu norma biblioteko, neniu printf.

Claude helpis min kompreni la ekfunkcian sekvencon: la vektora tabelo, la restartiĝa prizorganto, kopiado de .data el fulmo al RAM, nuligo de .bss. Aĵoj kiuj okazas antaŭ ol main() eĉ kuras.

La unua venko estis ununura signo aperanta sur UART-konzolo:

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

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

Du linioj de C. Neniuj bibliotekoj. Nur skribi bajton al memor-adreso kiu hazarde estas kabeita al seria pordo. Ĝi sentis kiel rekte paroli al la maŝino.

De tie: SysTick-krona interrompoj, interrompo-prizorgantoj, strukturita eligo. La SysTick-demo agordigas aparatan kronon por ekfunkcii ĉe 2 Hz kaj kalkulas 10 taktojn:

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.

Neniuj perditaj taktoj. Neniaj ekstraaj taktoj. La CPU dormas inter interrompoj kaj la aparataro vekas ĝin precize en la ĝusta momento. Determinisma ekzekutado el nuda metalo.

Fazo 2: FreeRTOS

Kun nuda metalo funkcianta, mi aldonis FreeRTOS, realtempan operaciumon kiu funkcias sur ĉio de medicinaj aparatoj ĝis satelitoj.

La ekzercoj estis konstruitaj progresive:

  1. Du taskoj ĉe malsamaj rapidecoj. Baza pluritasking.
  2. Vic-bazita komunikado. Sekure kunhavi datumojn inter taskoj.
  3. Gardokrona krono. Detekti kaj rekovri de blokitaj taskoj.
  4. Sensor-konvejero. Kvar sensiloj ĉe malsamaj rapidecoj manĝigantaj procezan ĉenon.
  5. Prioritat-inversiĝo. Eligi kaj solvi la klasikan RTOS-cimon.

Jen la sensor-konvejero ekfunkcianta. Rimarkigu la 10 Hz giroskopo dominas la fluon, kun la 1 Hz temperatur-legado premita ĉe takto 1001:

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

Tio estas prioritat-bazita antaŭpreno en ago. La giroskopo funkcias ĉe prioritato 4, la temperatur-sensilo ĉe prioritato 1. La temperatur-legado nur trairas kiam la giroskopo ne okupas la CPU.

La prioritat-inversiĝa ekzerco estis la plej instrua. Mi kreis tri taskojn kie malalt-prioritata tasko tenas mutex kiu bezonas alt-prioritata tasko, kaj mez-prioritata tasko malfektas ambaŭ. La solvo: prioritat-heredo, kie FreeRTOS provizore plialtigas la malalt-prioritatan taskon por ke ĝi povu liberigi la mutex pli rapide.

Tio estas la cimo kiu preskaŭ mortigis la Mars Pathfinder-mision en 1997. Mem konstrui ĝin igis la lernolibran klarigon klaki.

Fazo 3: Prokrasto-Tolerema Rettado

TCP/IP ne funkcias en spaco. Reen-kaj-antaŭen-tempoj al Marso intervalas de 6 ĝis 44 minutoj. Ligiloj falas dum horoj kiam planedoj okzultas la signalon. La hipotezo de TCP pri daŭra, malalta-prokrasta konekto tute disfaliĝas.

La respondo de NASA JPL estas DTN, aŭ Prokrasto-Tolerema Rettado. La Bundle Protocol stokas datumojn loke kaj plusendas ilin salpet-post-salto kiam ligiloj fariĝas disponeblaj. Ĝi estas dezajnita por ĝuste la kondiĉoj kiuj rompiĝas la Interreton.

Mi konstruis la NASA ION-implementadon en Docker kaj plenumis progresive pli malfacilajn testojn:

La interrompa ligila testo rakontas la tutan DTN-historion en kelkaj linioj da eligo:

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

Tio estas stoki-kaj-plusendi en ago. La bundle estas sendita dum la ligilo estas tute morta, 100% paketo-perdo. Ĝi sidas en la vico de la loka nodo. La momento kiam ni forigas la netem-regulon kaj restartigas konektecon, LTP retransmitigas kaj la bundle alvenas ĉe la alia fino. TCP jam delonge rezignus.

Fazo 4: Integrado

La fina fazo konektas ĉion. FreeRTOS-firmvaro generas telemetron en QEMU. Python-ponta skripto legas la UART-eligon kaj injektas ĝin kiel DTN-bundlojn. La bundloj trairas prokrastitan reton por atingi terfunkcian stacion.

La firmvaro ekfunkcias kaj tuj komencas striumi:

# 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

Kvar sensiloj ĉe kvar malsamaj rapidecoj. Oni povas vidi la 10 Hz giroskopo produktanta la plej multajn legadojn, kun la 2 Hz sun-sensilo, 1 Hz temperaturo kaj 0,5 Hz baterio interplektitaj. La ponto grupas tiujn en DTN-bundlojn ĉiun 2 sekundojn.

La Mars-prokrasta testo pruvas ke la tuta konvejero funkcias sub realistaj kondiĉoj:

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 bundloj senditaj, sed nur 8 liveritaj kiam la ponto finiĝis. La resto ankoraŭ estis en flugo tra la 5-sekunda prokrasto. Tio estas la prokrasto estanta reala, ne simulita en programaro.

La Mars-prokrasta simulado uzas du sinkronigitajn mekanismojn:

  1. tc netem aldonas 5 sekundojn da reala rettada latenteco al ambaŭ ujooj
  2. ION-intervalo-tabeloj agordas la saman 5-sekundan unu-direksan lumovojan tempon por ke LTP-retransmisiaj kronoj estu ĝustaj

Ambaŭ devas konkordi. Se la reala prokrasto estas 5 sekundoj sed ION opinias ke ĝi estas 1 sekundo, LTP agreseme retransmitigas kaj inundas la ligilon. Ĝuste agordi tion instruis al mi pli pri protokola dezajno ol ajna lernolibra ĉapitro pri fidindeco.

Labori kun Claude Code

Ĉi tiu projekto estus kostinta al mi monatojn sen Claude. Ne ĉar la kodo estas kompleksa (plej multaj dosieroj havas malpli ol 300 liniojn) sed ĉar la lernkurvo por ĉiu fako estas kruta.

Kio bone funkciis:

Kio postulas zorgecon:

La Nombroj

MetrikoValoro
Totalaj testoj50+ asertoj en 7 test-aroj
LingvojC, Python, Bash
AparataroNeniu (QEMU + Docker)
Linioj de C~1.200 (firmvaro + nuda metalo)
Linioj de Python~1.500 (testoj + ponto + DTN-skriptoj)
ION DTN-ago~350 linioj en 6 .rc-dosieroj

Provu

Ĉio estas malfermitkoda kaj funkcias sur ajna Linux-maŝino kun QEMU kaj 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

Neniu fizika aparataro. Neniaj nubaj servoj. Nur portkomputilo kaj scivolo pri kiel funkcias kosmoŝipaj komputiloj.