Signals / SDR / Field Reference
Satellite Signal Decode: From IQ to Frames
raw iq → telemetry ccsds / dsn lineage The path from a recorded baseband file to decoded spacecraft telemetry is a fixed pipeline: demodulate the carrier, recover symbols, undo the channel coding, find the frames. Each stage has a small set of knobs, and a failure at one stage has a recognizable signature in the next. This is the working reference for that pipeline, with the Dwingeloo Tianwen-2 X-band capture as the running example.
The pipeline, end to end
Every coherent digital downlink decodes along the same spine. Read it top to bottom; each stage consumes the output of the one above. The failure mode that matters most is the silent one: a stage that locks while the next refuses to sync, which always means the break is downstream of where it looks.
Stage 1 · Know your IQ file
Before any DSP, read the container. A baseband recording is just interleaved samples, and getting the sample format wrong produces noise that looks plausibly like a weak signal. SigMF files carry the answer in a sidecar JSON; trust it over the filename. The three numbers you must have before touching anything: sample rate, datatype, and center frequency.
- Nyquist check: the recorded bandwidth is the sample rate. A 2 Msps file holds plus or minus 1 MHz around the center. If your signal is wider, it is clipped and no decode will be clean.
- Sanity FFT first: plot the power spectrum before anything else. You are looking for the carrier, the symmetric data sidebands, and where they sit relative to DC.
Stage 2 · Identify the modulation
The spectrum tells you most of it. A sharp spike with symmetric humps either side is residual-carrier PM, the deep-space default: most power parked in the carrier for tracking, data riding a subcarrier on the shoulders. No central spike, just a hump, means suppressed-carrier PSK where all power is in the data. The constellation after coarse lock confirms the order: two blobs is BPSK, four is QPSK.
| spectrum signature | likely modulation | recover with |
|---|---|---|
| sharp central spike + symmetric sidebands | residual-carrier PCM/PSK/PM | PLL on carrier, then subcarrier costas |
| single smooth hump, no spike | suppressed-carrier BPSK/QPSK | costas loop |
| two humps, constant envelope | FSK / GFSK | quadrature demod |
| flat-topped wideband block | OQPSK / high-rate, or oversampled | poly-phase clock sync |
Stage 3 · Recover carrier and symbols
Two loops, in order. First lock the carrier: an FFT-peak estimate de-rotates the bulk of the offset, a PLL or Costas loop tracks the rest. For residual-carrier signals you then mix down to the subcarrier and Costas-lock that. Second, recover symbol timing with a timing error detector (Gardner is the workhorse) so you sample at the eye center. BPSK Costas recovery costs you absolute phase, which is why a 180 degree ambiguity follows you into the decoder.
Stage 4 · Undo the inner code
Most coherent downlinks wrap the data in a convolutional code, decoded by a Viterbi decoder. The CCSDS baseline is rate 1/2, constraint length 7, polynomials 0o171 and 0o133. Viterbi will report a lock state and a bit error rate. A synced Viterbi at low BER means your demod is good. This is the most important diagnostic in the whole chain: if Viterbi locks but frames never appear, stop touching the demod. The fault is in framing or the outer code.
Stage 5 · Find the frames
Decoded bits are a continuous stream. Framing finds structure in it. A correlator slides along looking for the attached sync marker, the known 32-bit pattern 0x1ACFFC1D for CCSDS. Once locked, the stream is sliced into fixed-length frames, the pseudo-randomizer is reversed, and the Reed-Solomon outer code corrects residual errors. This is where Chinese-lineage probes bite you: they often use RS in the conventional basis, not the CCSDS-default dual basis, and they interleave several codewords per frame. A generic CCSDS deframer with the wrong basis or depth correlates the ASM fine and then fails every RS codeword, which presents as permanent deframer NOSYNC.
Stage 6 · Parse the telemetry
Corrected frames are transfer frames carrying space packets on virtual channels. Split by APID, watch lengths, and start charting channels over time. Oscillating values are often attitude or spin. A slow exponential decay is usually a thermal control loop. Float32 fields that are not unit-norm are probably not quaternions. Without a secondary header there is no per-packet timestamp, so the frame counter is your clock.
The NOSYNC decision tree
The single most common failure once you have signal. Work it in this order; each step is cheap to test and the order is by probability.
- Viterbi not syncing? The problem is upstream, in demod. Check subcarrier frequency, symbol rate, and that you are actually locked to the carrier.
- Viterbi synced, deframer NOSYNC? The demod is fine. Everything below is framing or outer FEC.
- Sweep RS interleave depth. Wrong depth means the deinterleaver scrambles every codeword. Try the documented depth, then 1, 4, 5.
- Toggle RS basis. Conventional vs dual. Chinese probes use conventional; generic decoders default to dual.
- Confirm frame length and ASM. A wrong frame size desyncs immediately after the first marker.
- Try both symbol polarities. The 180 degree BPSK ambiguity. Run normal and bit-inverted deframers in parallel.
- Toggle the derandomizer. If it is off when it should be on, RS sees garbage.
Worked example · Tianwen-2 X-band
The Dwingeloo 25 m dish recorded Tianwen-2 on its 8428.190 MHz downlink. Live SatDump showed Viterbi SYNCED at BER 0.0049 but the deframer stuck at NOSYNC. By the decision tree above, the demod was fine and the fault was framing. The fix was the RS configuration, confirmed against a published decode of the same recordings.
| parameter | value | note |
|---|---|---|
| center / rate | 8428.190 MHz / 2 Msps | ci16_le SigMF |
| modulation | residual-carrier PM, BPSK on subcarrier | 65536 Hz subcarrier |
| symbol rate | 16384 baud | |
| inner code | conv r=1/2, K=7 (0o171/0o133) | Viterbi |
| outer code | RS(255,223) conventional, interleave 4 | NOT dual basis |
| frame | 892 bytes, 1.000 s, AOS, VC1, SCID 0xED | 4 x 223 |
| scrambler | CCSDS pseudo-randomizer ON |
The two knobs that resolved NOSYNC were interleave depth 4 and conventional basis, exactly steps 3 and 4 of the tree.