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.
FM downlink mode
Section titled “FM downlink mode”During pre-launch checkout and certain test configurations, the Apollo USB system switches from PM mode to wideband FM. In this mode, the PCM and voice subcarriers are replaced by 9 Subcarrier Oscillators (SCOs) that encode analog sensor voltages as FM tones:
graph TD
A["Sensor 1\n0-5V DC"] -->|"SCO 1\n14.5 kHz"| G["Composite\nSCO Signal"]
B["Sensor 5\n0-5V DC"] -->|"SCO 5\n52.5 kHz"| G
C["Sensor 9\n0-5V DC"] -->|"SCO 9\n165 kHz"| G
G -->|"FM\n500 kHz dev"| H["2287.5 MHz\nRF Carrier"]
H --> I["Transmitted\nFM Downlink"]
style A fill:#2d5016,stroke:#4a8c2a
style B fill:#2d5016,stroke:#4a8c2a
style C fill:#2d5016,stroke:#4a8c2a
style H fill:#1a3a5c,stroke:#3a7abd
style I fill:#1a3a5c,stroke:#3a7abd
The key differences from PM mode:
| Property | PM Mode | FM Mode |
|---|---|---|
| Carrier modulation | Phase (0.133 rad peak) | Frequency (wideband) |
| Data format | Digital (PCM frames) | Analog (voltage-to-frequency) |
| Subcarriers | 1.024 MHz BPSK + 1.25 MHz FM | 9 SCO tones (14.5-165 kHz) |
| Demodulation | Phase extraction | Frequency extraction |
gr-apollo FM block decomposition
Section titled “gr-apollo FM block decomposition”The FM mode blocks mirror the PM mode three-layer architecture:
graph LR
subgraph "Layer 1: Carrier"
A["fm_mod"] --> B["fm_demod"]
end
subgraph "Layer 2: Subcarrier"
C["sco_mod\n(per channel)"] --> D["sco_demod\n(per channel)"]
end
subgraph "Layer 3: Convenience"
E["fm_signal_source"] --> F["fm_downlink_receiver"]
end
style A fill:#1a3a5c,stroke:#3a7abd,color:#fff
style B fill:#1a3a5c,stroke:#3a7abd,color:#fff
style C fill:#5c3a1a,stroke:#bd7a3a,color:#fff
style D fill:#5c3a1a,stroke:#bd7a3a,color:#fff
style E fill:#2d5016,stroke:#4a8c2a,color:#fff
style F fill:#2d5016,stroke:#4a8c2a,color:#fff
The fm_signal_source and fm_downlink_receiver convenience blocks wire the full chain together, just as usb_signal_source and usb_downlink_receiver do for PM mode. The FM receiver uses streaming float outputs (one per SCO channel) rather than PDU messages, since SCO telemetry is continuous analog data.
The uplink signal
Section titled “The uplink signal”The downlink carries telemetry from spacecraft to ground. The uplink does the reverse — delivering commands from Mission Control to the spacecraft on 2106.40625 MHz.
The modulation scheme is the same family as the downlink (PM carrier with FM subcarriers), but the parameters are dramatically different. Where the downlink tiptoes along at 0.133 rad peak deviation, the uplink runs at 1.0 rad. This asymmetry is not a design inconsistency — it reflects a fundamental physical reality. The ground station has megawatt-class transmitters and 26-meter dish antennas. The spacecraft has a 20-watt traveling-wave tube and a dish measured in inches. The ground can afford the power overhead of higher modulation index; the spacecraft cannot waste a single fraction of a watt.
The uplink carries two information streams:
- Command data — DSKY commands encoded as 15-bit AGC words, transmitted at 2 kbps NRZ on a 70 kHz FM subcarrier with +/-4 kHz deviation
- Voice (optional) — Crew-directed audio on a 30 kHz FM subcarrier with +/-7.5 kHz deviation
graph LR
subgraph "TX — Ground Station"
A["DSKY Commands<br/>15-bit AGC words"] --> B["Word Serializer<br/>15 bits → NRZ<br/>2 kbps"]
B --> C["NRZ Encoder<br/>0/1 → +1/-1"]
C --> D["FM Mod<br/>70 kHz<br/>±4 kHz dev"]
D --> E["Σ"]
V1["Voice Audio<br/>300-3000 Hz"] -->|"FM<br/>30 kHz<br/>±7.5 kHz dev"| E
E --> F["PM Mod<br/>1.0 rad peak"]
F --> G["2106.4 MHz<br/>RF Carrier"]
end
G -->|"240/221<br/>turnaround"| H
subgraph "RX — Spacecraft"
H["RF Input<br/>2106.4 MHz"] --> I["PM Demod<br/>Carrier PLL"]
I --> J["Extract<br/>70 kHz"]
J --> K["FM Demod"]
K --> L["Slicer<br/>2 kbps"]
L --> M["Word<br/>Deserializer"]
M --> N["AGC<br/>Commands"]
end
style A fill:#2d5016,stroke:#4a8c2a
style V1 fill:#2d5016,stroke:#4a8c2a
style G fill:#1a3a5c,stroke:#3a7abd
style H fill:#1a3a5c,stroke:#3a7abd
style F fill:#3a1a5c,stroke:#7a3abd
style I fill:#3a1a5c,stroke:#7a3abd
style N fill:#5c3a1a,stroke:#bd7a3a
The uplink transmit chain assembles commands into the modulated carrier:
- uplink_encoder — Accepts DSKY command PDUs and formats them as 15-bit AGC uplink words. The word format matches the Apollo AGC’s INLINK channel (channel 45) protocol.
- uplink_word_serializer — Serializes 15-bit words into a continuous NRZ bit stream at 2 kbps. Each bit is held for the full bit period.
- nrz_encoder — Converts the 0/1 byte stream to +1.0/-1.0 floats, identical to the downlink NRZ encoder.
- FM modulator — Frequency-modulates the NRZ data onto a 70 kHz subcarrier with +/-4 kHz deviation.
- PM modulator — Phase-modulates the composite subcarrier signal onto the carrier at 1.0 rad peak deviation.
The usb_uplink_source hierarchical block wires this chain together.
The uplink receiver disassembles the signal in reverse order:
- PM demodulator — Same carrier PLL approach as the downlink, but configured for the wider 1.0 rad deviation. The larger modulation index means the small-angle approximation no longer holds (15.9% error at 1.0 rad), but FM demodulation of the subcarrier is tolerant of this distortion.
- 70 kHz extraction — Bandpass filter centered on the command subcarrier.
- FM demodulator — Recovers the NRZ data stream from the 70 kHz subcarrier.
- Slicer and deserializer — Threshold detection at 2 kbps, then reassembly of 15-bit AGC words from the serial bit stream.
The usb_uplink_receiver hierarchical block wires this chain together.
PRN ranging
Section titled “PRN ranging”Voice and telemetry tell the ground station what the spacecraft is doing. Ranging tells them where it is. The Apollo USB system measures Earth-to-spacecraft distance by timing how long a known signal takes to make the round trip.
The ranging signal is a composite pseudo-random noise (PRN) code, transmitted by the ground station and transponded (retransmitted) by the spacecraft. The ground station correlates the received code against its own reference copy, and the time offset between them is the two-way light-time delay.
graph LR
A["PRN Generator<br/>~994 kchip/s"] --> B["NRZ Encode<br/>0/1 → +1/-1"]
B --> C["PM Mod onto<br/>Uplink Carrier"]
C --> D["Spacecraft<br/>Transponder"]
D --> E["PM Mod onto<br/>Downlink Carrier"]
E --> F["Ground RX<br/>PRN Correlator"]
F --> G["Range<br/>Measurement"]
A --> H["Reference Copy<br/>(delayed)"]
H --> F
style A fill:#5c3a1a,stroke:#bd7a3a
style D fill:#1a3a5c,stroke:#3a7abd
style F fill:#3a1a5c,stroke:#7a3abd
style G fill:#2d5016,stroke:#4a8c2a
The composite code
Section titled “The composite code”The PRN code is not a single sequence — it is five component codes combined with Boolean logic:
| Code | Length (chips) | Type |
|---|---|---|
| CL | 2 | Clock (alternating 0,1) |
| X | 11 | Fixed pattern |
| A | 31 | Maximal-length LFSR |
| B | 63 | Maximal-length LFSR |
| C | 127 | Maximal-length LFSR |
The combination logic produces one output chip per clock:
output = (NOT(X) AND majority(A, B, C)) XOR CLwhere majority(A, B, C) outputs 1 when two or more inputs are 1. The combined code length is the product of the component lengths: 2 x 11 x 31 x 63 x 127 = 5,456,682 chips. At the chip rate of approximately 994 kchip/s, one full code period takes about 5.49 seconds.
The maximal-length LFSR sequences (A, B, C) give the composite code excellent autocorrelation properties — a sharp peak when aligned, low sidelobes everywhere else. This is what makes unambiguous range measurement possible even at the signal-to-noise ratios encountered at lunar distance.
Sequential acquisition
Section titled “Sequential acquisition”Correlating against the full 5.4-million-chip code in one pass would be computationally ruinous, even by modern standards. The Apollo system uses a sequential strategy that exploits the composite structure:
- Correlate CL first (length 2) — resolves range to within half the CL period
- Correlate X (length 11) — refines within the CL ambiguity window
- Correlate A (length 31) — further refinement
- Correlate B (length 63) — further refinement
- Correlate C (length 127) — final resolution
Each stage resolves the ambiguity left by the previous one. The total number of trial positions searched is 2 + 11 + 31 + 63 + 127 = 234, rather than 5,456,682. This makes acquisition practical in seconds rather than hours.
Range resolution
Section titled “Range resolution”The two-way range resolution is determined by the chip rate:
resolution = c / (2 x chip_rate) = 299,792,458 / (2 x 994,000) ~ 150.8 metersThis is the minimum distinguishable range difference. The actual measurement precision is much better than this, because the correlator interpolates between chips. NASA routinely achieved ranging precision on the order of 15 meters to the Moon — about one-tenth of a chip.
gr-apollo ranging blocks
Section titled “gr-apollo ranging blocks”- ranging_source — Generates the composite PRN chip stream by clocking the five component code generators and combining their outputs. Configurable chip rate (default ~994 kchip/s). Output is one byte per chip (0 or 1).
- ranging_mod — NRZ-encodes the chip stream (+1/-1) and scales it for injection into the PM modulator’s composite baseband signal.
- ranging_demod — Performs FFT-based cross-correlation between the received ranging signal and a locally generated reference. Sequential acquisition is implemented as a state machine that steps through the CL, X, A, B, C stages. Outputs range measurement PDUs containing the measured delay in chips, the equivalent range in meters, and a confidence metric.