Run the Demos
gr-apollo ships with four demo scripts that exercise different parts of the TX/RX chain. They progress from a self-contained streaming loopback to a full mission simulation with crew voice, and finally to live integration with the Virtual AGC emulator.
| Demo | Requires | What It Does |
|---|---|---|
loopback_demo.py | GNU Radio | Streaming TX to RX round-trip |
voice_subcarrier_demo.py | GNU Radio, scipy | Real audio through 1.25 MHz FM |
full_downlink_demo.py | GNU Radio, scipy | PCM telemetry + crew voice on one carrier |
agc_loopback_demo.py | yaAGC (no GR) | Live AGC telemetry over TCP |
Prerequisites
Section titled “Prerequisites”Loopback
Section titled “Loopback”Script: examples/loopback_demo.py
Requires: GNU Radio
The loopback demo connects usb_signal_source directly to usb_downlink_receiver through the GNU Radio scheduler. It transmits PCM frames, receives them back, and displays sync word analysis for each recovered frame.
graph LR
A["usb_signal_source\n(TX chain)"]:::rf --> B["head\n(sample limiter)"]:::timing --> C["usb_downlink_receiver\n(RX chain)"]:::rf
C -->|"frames (PDU)"| D["message_debug\n(store)"]:::data
classDef data fill:#2d5016,stroke:#4a8c2a,color:#fff
classDef rf fill:#1a3a5c,stroke:#3a7abd,color:#fff
classDef timing fill:#5c3a1a,stroke:#bd7a3a,color:#fff
uv run python examples/loopback_demo.pyuv run python examples/loopback_demo.py --voice # include voice subcarrieruv run python examples/loopback_demo.py --snr 20 # add noise at 20 dB SNRuv run python examples/loopback_demo.py --frames 20 # generate 20 framesArguments
Section titled “Arguments”| Argument | Default | Description |
|---|---|---|
--frames | 10 | Number of PCM frames to transmit |
--snr | None | SNR in dB (None = clean, no noise) |
--voice | off | Enable the 1.25 MHz FM voice subcarrier with 1 kHz test tone |
Expected Output
Section titled “Expected Output”============================================================Apollo USB Loopback Demo============================================================ Frames to transmit: 10 Samples per frame: 102,400 Total samples: 1,024,000 Duration: 0.200 s SNR: clean (no noise) Voice subcarrier: disabled
Building flowgraph...Running flowgraph (TX -> RX)...
Recovered 8 frames from 10 transmitted
------------------------------------------------------------ Frame 1: ID= 3 (odd ), sync=0xAB31D403, 124 words [00 00 00 00 00 00 00 00 ...] Frame 2: ID= 4 (even), sync=0xABCED404, 124 words [00 00 00 00 00 00 00 00 ...] ...------------------------------------------------------------
Recovery rate: 8/10 (80%)Voice Subcarrier
Section titled “Voice Subcarrier”Script: examples/voice_subcarrier_demo.py
Requires: GNU Radio, scipy
This demo takes a real audio file (such as actual Apollo 11 crew recordings), modulates it onto the 1.25 MHz FM voice subcarrier with +/-29 kHz deviation, then demodulates it back to audio. The round-trip exercises the same signal path the spacecraft and ground station used.
graph LR
A["WAV file\n(any rate)"]:::data --> B["resample\nto 8 kHz"]:::timing --> C["upsample\nto 5.12 MHz"]:::timing
C --> D["fm_voice_subcarrier_mod\n(audio_input=True)"]:::rf
D --> E["voice_subcarrier_demod\n(8 kHz output)"]:::rf
E --> F["recovered\nWAV file"]:::data
classDef data fill:#2d5016,stroke:#4a8c2a,color:#fff
classDef rf fill:#1a3a5c,stroke:#3a7abd,color:#fff
classDef timing fill:#5c3a1a,stroke:#bd7a3a,color:#fff
uv run python examples/voice_subcarrier_demo.py examples/audio/apollo11_crew.wavuv run python examples/voice_subcarrier_demo.py input.wav --output recovered.wavuv run python examples/voice_subcarrier_demo.py input.wav --playArguments
Section titled “Arguments”| Argument | Default | Description |
|---|---|---|
input (positional) | — | Input WAV file (any sample rate) |
--output, -o | <input>_recovered.wav | Output WAV file path |
--play | off | Play recovered audio with aplay after processing |
--sample-rate | 5,120,000 | Baseband sample rate in Hz |
Expected Output
Section titled “Expected Output”============================================================Apollo Voice Subcarrier Demo============================================================
Input: examples/audio/apollo11_crew.wav Sample rate: 48000 Hz Duration: 5.23 s Samples: 251,136
Resampled to 8000 Hz: 41,856 samples Upsampling 8000 Hz -> 5.12 MHz (ratio 640:1)... Upsampled: 26,787,840 samples (2.1s)
Building flowgraph: FM mod (1.25 MHz) -> FM demod...Running flowgraph... Processed in 3.4s Recovered: 41,790 samples at 8000 Hz Duration: 5.22 s
Saved: examples/audio/apollo11_crew_recovered.wav Peak amplitude: 0.8234
Play with: aplay examples/audio/apollo11_crew_recovered.wav
Done.Full Downlink
Section titled “Full Downlink”Script: examples/full_downlink_demo.py
Requires: GNU Radio, scipy
The full downlink demo reconstructs the complete Apollo USB downlink: PCM telemetry frames on the 1.024 MHz BPSK subcarrier AND crew voice on the 1.25 MHz FM subcarrier, both phase-modulated onto a single complex carrier. The receiver splits the signal back into decoded frames and audio.
graph TB
subgraph TX ["TX (spacecraft)"]
direction LR
A["pcm_frame_source\n→ nrz → bpsk_mod"]:::data --> D["add_ff"]:::rf
B["crew audio\n→ fm_voice_mod"]:::data --> C["× 0.764"]:::rf --> D
D --> E["pm_mod"]:::rf
end
subgraph RX ["RX (ground station)"]
direction LR
F["pm_demod"]:::rf --> G["bpsk_demod\n→ frame_sync"]:::rf
F --> H["voice_demod"]:::rf
G --> I["PCM frames"]:::data
H --> J["crew audio"]:::data
end
E --> F
classDef data fill:#2d5016,stroke:#4a8c2a,color:#fff
classDef rf fill:#1a3a5c,stroke:#3a7abd,color:#fff
This demo builds the TX chain manually (not using usb_signal_source) so it can inject external audio into the voice channel. It then runs the RX chain twice: once for PCM frame recovery, once for voice demodulation.
uv run python examples/full_downlink_demo.py examples/audio/apollo11_crew.wavuv run python examples/full_downlink_demo.py input.wav --snr 25uv run python examples/full_downlink_demo.py input.wav --playArguments
Section titled “Arguments”| Argument | Default | Description |
|---|---|---|
audio (positional) | — | Input crew voice WAV file |
--output, -o | <input>_fullchain.wav | Output WAV path for recovered voice |
--snr | None | Add AWGN noise at this SNR in dB |
--play | off | Play recovered voice with aplay |
Expected Output
Section titled “Expected Output”============================================================Apollo Full Downlink Demo PCM telemetry (1.024 MHz BPSK) + crew voice (1.25 MHz FM)============================================================
Loading crew voice audio... Source: examples/audio/apollo11_crew.wav (5.23s) Upsampled: 26,787,840 samples at 5.12 MHz
PCM frames: ~263 at 50 fps Signal: 26,787,840 samples (5.23s) SNR: clean
TX: Building combined PCM + voice signal... Generated 26,787,840 complex samples (4.2s) PM envelope std: 0.000001 (should be ~0 for clean)
RX: Decoding PCM telemetry frames... Recovered 260 PCM frames (6.1s)
Frame 1: ID= 3 (odd), 124 data words Frame 2: ID= 4 (even), 124 data words Frame 3: ID= 5 (odd), 124 data words Frame 4: ID= 6 (even), 124 data words Frame 5: ID= 7 (odd), 124 data words ... (255 more frames)
RX: Demodulating crew voice (1.25 MHz FM)... Recovered 41,790 audio samples (3.8s) Duration: 5.22s at 8000 Hz Saved: examples/audio/apollo11_crew_fullchain.wav
============================================================ TX: 5.23s of combined PCM + voice RX: 260 PCM frames + 5.22s crew voice SNR: clean============================================================
Play voice: aplay examples/audio/apollo11_crew_fullchain.wavAGC Integration
Section titled “AGC Integration”Script: examples/agc_loopback_demo.py
Requires: yaAGC emulator (no GNU Radio needed)
This demo connects directly to a running Virtual AGC emulator over TCP, receives DNTM1/DNTM2 telemetry packets, decodes them into downlink list snapshots, and optionally sends DSKY commands.
graph LR
A["yaAGC\n(Luminary099)"]:::timing -->|"TCP :19697"| B["AGCBridgeClient"]:::rf
B --> C["DownlinkEngine\n(reassemble words)"]:::data
C --> D["telemetry\nsnapshots"]:::data
E["UplinkEncoder\n(V16N36E)"]:::data -->|"INLINK ch 045"| B
classDef data fill:#2d5016,stroke:#4a8c2a,color:#fff
classDef rf fill:#1a3a5c,stroke:#3a7abd,color:#fff
classDef timing fill:#5c3a1a,stroke:#bd7a3a,color:#fff
Prerequisites
Section titled “Prerequisites”-
Install Virtual AGC from the project website. The key binary is
yaAGC. -
Start the AGC emulator with a mission flight software image:
Terminal window yaAGC --core=Luminary099.bin --port=19697 -
Optionally start yaDSKY2 for a visual DSKY display:
Terminal window yaDSKY2 --port=19698
uv run python examples/agc_loopback_demo.pyuv run python examples/agc_loopback_demo.py --host 192.168.1.100uv run python examples/agc_loopback_demo.py --send-v16n36uv run python examples/agc_loopback_demo.py --duration 30Arguments
Section titled “Arguments”| Argument | Default | Description |
|---|---|---|
--host | localhost | yaAGC hostname or IP |
--port | 19697 | yaAGC TCP port |
--duration | 10.0 | Collection duration in seconds |
--send-v16n36 | off | Send V16N36E (display mission elapsed time) to the AGC |
Expected Output
Section titled “Expected Output”============================================================Apollo AGC Integration Demo============================================================ Target: localhost:19697 Duration: 10.0 seconds
Connecting to yaAGC at localhost:19697... Connection: connecting Connection: connected
Sending V16N36E (display time)... Sent 7 uplink words
Collecting telemetry for 10.0 seconds...------------------------------------------------------------ Telemetry snapshot: CM Coast/Alignment (type 2), 400 words [000] = 00002 (2) [001] = 00000 (0) [002] = 77777 (32767) [003] = 00000 (0) [004] = 00000 (0) ... (395 more words)------------------------------------------------------------
Summary: Total packets received: 1247 Telemetry words: 834 Telemetry snapshots: 2 Duration: 10.1 seconds
Done.Which Demo to Start With
Section titled “Which Demo to Start With”If you are new to gr-apollo:
-
Start with the loopback demo. It has no external dependencies beyond GNU Radio and exercises the complete TX/RX round-trip in a self-contained flowgraph.
-
Try the voice demo with an Apollo crew recording to hear the signal processing in action. Audio files in
examples/audio/are ready to use. -
Run the full downlink to see both PCM and voice working together on one carrier — the way it worked on the actual spacecraft.
-
Connect to yaAGC when you are ready to interact with a running Apollo Guidance Computer.