Signal Architecture
The Apollo Unified S-Band (USB) system multiplexes voice, telemetry, and ranging onto a single 2287.5 MHz carrier using nested modulation layers. Understanding these layers — and the reasons behind each design choice — is the key to understanding what gr-apollo does and why its blocks are structured the way they are.
All parameters in this section are from the NAA Telecommunication Systems Study Guide (Course A-624).
The downlink signal, inside out
Section titled “The downlink signal, inside out”The spacecraft transmitter begins with a stable carrier at 2287.5 MHz. Multiple information streams are combined onto this carrier through a two-level modulation scheme: subcarriers are first individually modulated with their data, then the composite subcarrier signal phase-modulates the RF carrier.
graph TD
A["PCM Telemetry<br/>51.2 kbps NRZ"] -->|BPSK| B["1.024 MHz<br/>Subcarrier"]
C["Voice Audio<br/>300-3000 Hz"] -->|FM<br/>+/-29 kHz dev| D["1.25 MHz<br/>Subcarrier"]
E["Ranging Code<br/>PRN sequence"] --> F["Ranging<br/>Subcarrier"]
B --> G["Composite<br/>Baseband Signal"]
D --> G
F --> G
G -->|PM<br/>0.133 rad peak| H["2287.5 MHz<br/>RF Carrier"]
H --> I["Transmitted<br/>Downlink"]
style A fill:#2d5016,stroke:#4a8c2a
style C fill:#2d5016,stroke:#4a8c2a
style E fill:#2d5016,stroke:#4a8c2a
style H fill:#1a3a5c,stroke:#3a7abd
style I fill:#1a3a5c,stroke:#3a7abd
This is a frequency-division multiplexing system. The PCM subcarrier sits at 1.024 MHz, voice at 1.25 MHz, and ranging at its own frequency. Because these subcarriers are well-separated in frequency, the receiver can extract each one independently with bandpass filters. The entire system uses a single RF carrier — hence “Unified” S-Band.
Why 0.133 radians?
Section titled “Why 0.133 radians?”The PM peak deviation of 0.133 rad (7.6 degrees) is one of the most important numbers in the system. It seems absurdly small — barely a wiggle on the carrier phase. That is the point.
At small modulation indices, the relationship between the modulating signal and the recovered phase is very nearly linear. The small-angle approximation sin(theta) ~= theta holds to within 0.3% at 0.133 rad:
| Angle (rad) | sin(angle) | Error |
|---|---|---|
| 0.133 | 0.1326 | 0.29% |
| 0.5 | 0.4794 | 4.1% |
| 1.0 | 0.8415 | 15.9% |
This linearity means the PM demodulator output is a faithful reproduction of the composite subcarrier signal. There is no need for pre-distortion or nonlinear correction — the receiver just extracts the phase and gets the subcarriers back, ready for filtering and demodulation.
The tradeoff is signal power. Most of the transmitted power remains in the carrier rather than the sidebands. The spacecraft burns roughly 20 watts of RF power (via the traveling-wave tube amplifier), but only a fraction of a watt ends up in each subcarrier. The Deep Space Network’s 26-meter dishes made up the difference with raw antenna gain.
The timing hierarchy
Section titled “The timing hierarchy”Every timing parameter in the Apollo PCM system derives from a single 512 kHz master clock in the Central Timing Equipment (CTE). This is not a coincidence — it is the entire design philosophy. When all frequencies share a common root, the receiver can exploit integer relationships for synchronization.
graph TD
M["512 kHz<br/>Master Clock"] --> A["51.2 kbps bit clock<br/>divide by 10"]
M --> B["1.6 kbps bit clock<br/>divide by 320"]
A --> C["6,400 words/sec<br/>8 bits/word"]
B --> D["200 words/sec<br/>8 bits/word"]
C --> E["50 frames/sec<br/>128 words/frame"]
D --> F["1 frame/sec<br/>200 words/frame"]
E --> G["1 subframe/sec<br/>50 frames"]
M --> H["1.024 MHz subcarrier<br/>multiply by 2"]
style M fill:#5c3a1a,stroke:#bd7a3a
style H fill:#1a3a5c,stroke:#3a7abd
At high rate: 512,000 / 10 = 51,200 bits/sec. Divide by 8 bits/word = 6,400 words/sec. Divide by 128 words/frame = 50 frames/sec. Multiply 50 frames by the frame period and you get exactly 1 second per subframe.
The 1.024 MHz PCM subcarrier is 512 kHz times 2. This means exactly 20 subcarrier cycles fit in one bit period (1,024,000 / 51,200 = 20). The BPSK demodulator can use this integer relationship for more stable symbol timing recovery.
Why 5.12 MHz sample rate?
Section titled “Why 5.12 MHz sample rate?”gr-apollo’s default baseband sample rate is 5.12 MHz — exactly 10 times the master clock. This choice is deliberate:
| Relationship | Value | Integer? |
|---|---|---|
| Samples per master clock cycle | 5,120,000 / 512,000 = 10 | Yes |
| Samples per PCM subcarrier cycle | 5,120,000 / 1,024,000 = 5 | Yes |
| Samples per bit (high rate) | 5,120,000 / 51,200 = 100 | Yes |
| Samples per voice subcarrier cycle | 5,120,000 / 1,250,000 = 4.096 | No |
Every PCM-related rate divides evenly into the sample rate. This eliminates fractional sample offsets in the bit timing recovery loop, making synchronization faster and more reliable. The voice subcarrier does not divide evenly, but voice is FM — it tolerates timing imprecision far better than digital data.
The gr-apollo block decomposition
Section titled “The gr-apollo block decomposition”The receiver follows the signal structure in reverse. Each modulation layer gets its own block, and the blocks chain together in the same order the signal was built:
graph LR
A["Complex<br/>Baseband<br/>5.12 MHz"] --> B["pm_demod"]
B --> C["subcarrier_extract<br/>1.024 MHz"]
C --> D["bpsk_demod"]
D --> E["pcm_frame_sync"]
E --> F["pcm_demux"]
B --> G["voice_subcarrier_demod<br/>1.25 MHz"]
F -->|"frames PDU"| H["AGC Data"]
F -->|"telemetry PDU"| I["Telemetry Words"]
F -->|"agc_data PDU"| J["downlink_decoder"]
J -->|"downlink PDU"| K["AGC Bridge"]
G --> L["Audio Out<br/>8 kHz"]
style A fill:#1a3a5c,stroke:#3a7abd
style B fill:#3a1a5c,stroke:#7a3abd
style C fill:#3a1a5c,stroke:#7a3abd
style D fill:#3a1a5c,stroke:#7a3abd
style E fill:#5c3a1a,stroke:#bd7a3a
style F fill:#5c3a1a,stroke:#bd7a3a
style G fill:#2d5016,stroke:#4a8c2a
The first four blocks form a streaming signal processing chain, each transforming the signal’s representation:
- pm_demod — Carrier PLL locks to the residual carrier, then
atan2(Im, Re)extracts instantaneous phase. Output is a float representing the composite modulating signal. - subcarrier_extract — Frequency-translating FIR filter shifts the 1.024 MHz subcarrier to DC and applies a 150 kHz bandpass (matching the spec’s 949-1099 kHz range). Output is complex baseband.
- bpsk_demod — Costas loop resolves phase, Mueller & Muller TED recovers symbol timing, binary slicer makes hard decisions. Output is one byte per bit (0 or 1).
- pcm_frame_sync — Sliding-window correlator finds the 32-bit sync word. Transitions through SEARCH, VERIFY, and LOCKED states. Emits complete frames as PDU messages.
After frame sync, the data path switches from streaming samples to asynchronous PDU messages:
- pcm_demux — Receives frame PDUs, separates the 4-byte sync word from the 124 data words, identifies AGC channel data at word positions 34, 35, and 57, applies A/D voltage scaling.
- downlink_decoder — Collects channel 34/35 byte pairs, reassembles 15-bit AGC words, buffers 400 words into complete downlink list snapshots.
- agc_bridge — TCP client connecting to the Virtual AGC emulator (yaAGC). Bidirectional: sends uplink commands, receives AGC I/O channel updates.
The voice path branches off after the PM demodulator and uses its own subcarrier extraction:
- voice_subcarrier_demod — Extracts the 1.25 MHz subcarrier (58 kHz bandwidth), applies quadrature FM demodulation, bandpasses to 300-3000 Hz, and resamples to 8 kHz audio output.
The voice path is completely independent of the PCM path. Both operate simultaneously on the same PM demodulator output.
The usb_downlink_receiver hierarchical block wires the PCM chain together as a convenience — connect complex baseband in, get telemetry PDUs out. For finer control over PLL bandwidths or to tap intermediate signals, the individual blocks can be used directly.
Phase ambiguity and frame sync
Section titled “Phase ambiguity and frame sync”The Costas loop in bpsk_demod has a fundamental 180-degree phase ambiguity. A 2nd-order Costas loop locks to one of two stable equilibria that are 180 degrees apart. If it locks to the wrong one, every recovered bit is inverted.
The system resolves this at the frame sync layer rather than at the Costas loop. The 32-bit sync word has a known pattern: [5-bit A][15-bit core][6-bit B][6-bit frame ID]. The A, B, and core fields are fixed (the core complements on odd frames, but this is deterministic). If the correlator finds the complement of the expected pattern everywhere, it knows the Costas loop locked to the wrong phase, and can invert the bit stream.
This is a well-known technique in BPSK systems. It is more reliable than trying to resolve the ambiguity in the Costas loop itself, because the frame sync pattern provides a definitive answer rather than a probabilistic one.
Coherent frequency plan
Section titled “Coherent frequency plan”The Apollo USB system maintains a coherent frequency relationship between uplink and downlink. The spacecraft’s receiver locks to the uplink at 2106.40625 MHz, and the transmitter generates its downlink carrier by multiplying the received frequency by 240/221:
2106.40625 MHz x 240/221 = 2287.5 MHzThis coherent turnaround allows the ground station to measure the two-way Doppler shift with extreme precision — the ratio is exact, so any frequency difference between transmitted uplink and received downlink is entirely due to spacecraft velocity. This is how NASA tracked the spacecraft’s range rate to centimeter-per-second precision using 1960s technology.
gr-apollo does not implement the coherent turnaround (it is a receiver, not a transponder), but the frequency plan explains why the numbers are what they are. The 2287.5 MHz downlink frequency is not arbitrary — it is locked to the uplink via a ratio that was carefully chosen to avoid ambiguities in the Doppler measurement.
The transmit chain
Section titled “The transmit chain”gr-apollo’s TX blocks mirror the receive path. Where the receiver disassembles the signal layer by layer, the transmitter builds it up. Each TX block has a direct RX counterpart, and the internal architecture reflects this symmetry:
graph LR
A["PCM Frame Source<br/>32-bit sync<br/>128 words/frame"] --> B["NRZ Encoder<br/>0/1 → +1/-1<br/>100 samp/bit"]
B --> C["BPSK Mod<br/>× cos(1.024 MHz)"]
C --> D["Σ"]
E["Voice Mod<br/>FM → 1.25 MHz<br/>±29 kHz dev"] --> F["× 0.764"]
F --> D
D --> G["PM Mod<br/>exp(j·φ)"]
G --> H["Complex<br/>Baseband<br/>5.12 MHz"]
style A fill:#2d5016,stroke:#4a8c2a
style B fill:#5c3a1a,stroke:#bd7a3a
style C fill:#3a1a5c,stroke:#7a3abd
style E fill:#2d5016,stroke:#4a8c2a
style G fill:#1a3a5c,stroke:#3a7abd
style H fill:#1a3a5c,stroke:#3a7abd
The PCM transmit path generates telemetry frames and modulates them onto the BPSK subcarrier:
- pcm_frame_source — Generates 128-word PCM frames with 32-bit sync words. Frame IDs cycle 1 through 50 (one subframe per second). Odd frames get a complemented sync core automatically. Dynamic payloads can be injected via the
frame_datamessage port. - nrz_encoder — Converts the bit stream (byte values 0/1) to a float NRZ waveform (+1.0/-1.0). Each bit is repeated for 100 samples at the default 5.12 MHz rate.
- bpsk_subcarrier_mod — Multiplies the NRZ data by a 1.024 MHz cosine carrier. This BPSK modulation flips the subcarrier phase 180 degrees on each bit transition — the inverse of the Costas loop recovery in
bpsk_demod.
The voice path produces an FM subcarrier that is summed with the PCM subcarrier:
- fm_voice_subcarrier_mod — Two modes: an internal sine test tone (for testing) or external audio input (for real mission audio). The audio is FM-modulated at baseband and upconverted to 1.25 MHz. The voice level is scaled by 1.68/2.2 (approximately 0.764) relative to the PCM subcarrier, matching the spacecraft’s Pre-Modulation Processor power allocation.
The voice and PCM subcarriers occupy non-overlapping frequency bands (1.024 MHz vs 1.25 MHz), so they can be linearly summed before phase modulation without interference.
After summing the subcarriers, the composite signal is phase-modulated onto a complex carrier:
- pm_mod — Applies
exp(j * dev * m(t))wheredevis 0.133 rad andm(t)is the composite subcarrier signal. The output is a constant-envelope complex baseband signal. The small deviation ensures the modulation is linear (same 0.3% approximation that makes the receiver work). - Optional AWGN — When
snr_dbis set, Gaussian noise is added to the complex output for realistic channel simulation.
The usb_signal_source hierarchical block wires the entire TX chain together as a convenience — the transmit-side counterpart to usb_downlink_receiver. For scenarios that need finer control (like injecting external audio), the individual blocks can be assembled manually. The full_downlink_demo.py example shows this approach.