Inmarsat-C STD-C / EGC — native decoder
The L-band maritime store-and-forward messaging and SafetyNET/MSI broadcast downlink: BPSK at 1200 sym/s through a CCSDS r=1/2 K=7 Viterbi decoder, a 15-bit LFSR descrambler, a 64-column block de-interleaver and a 24-bit unique-word frame sync, with an EGC (Enhanced Group Call) message layer on top.
Rafe project · app/radio/inmarsat.py, test_inmarsat.py · RX (+ reference encoder for self-test) · reuses the CCSDS Viterbi and BPSK receiver from app/radio/sat/ · frame/scrambler/interleaver/UW constants validated self-consistently; exact on-air interop is the next step
Abstract
Inmarsat-C is the low-rate geostationary satellite service that carries, among other traffic, EGC SafetyNET — the satellite counterpart of NAVTEX: navigational warnings, weather, and distress relays broadcast to ships beyond MF/VHF range. The downlink TDM carrier is BPSK at 1200 symbols/s in the 1.5 GHz band, protected by the same convolutional code the space agencies use (rate 1/2, constraint length 7), block interleaving against burst fades, and an additive scrambler for spectral conditioning.
Rafe's decoder is deliberately layered on the satellite DSP already in the repo: the BPSK carrier/timing recovery and the K=7 Viterbi come from app/radio/sat/ (shared with LRPT/CCSDS), and this module adds the four STD-C-specific stages — descrambler, de-interleaver, unique-word frame sync, and the EGC message layer. This document pins every constant of those stages, the exact encode/decode order, and the honest interop status: the chain round-trips through noise, carrier offset and raw IQ in the test suite, but the frame constants (notably the unique word) still need validation against a live off-air STD-C capture.
1. Background
1.1 The system
An Inmarsat-C terminal is a telex-grade store-and-forward messager: ~600 bit/s usable, no voice. Traffic flows ship↔︎shore through Land Earth Stations (LES); the shore-to-ship direction rides continuous TDM carriers from each satellite. EGC is the broadcast service multiplexed into those carriers — SafetyNET (maritime safety, structured by NAV/MET area) and FleetNET (commercial group messaging). A receive-only EGC watch is exactly what this decoder targets: lock the TDM downlink, recover frames, and parse the safety messages.
1.2 The coding chain
The downlink protection stack, outermost first:
info bits → scramble (LFSR x^15+x^14+1) → conv encode r=1/2 K=7
→ block interleave (64 columns) → [UW ‖ payload] → BPSK
Note the order: scrambling is applied to the info bits before encoding (and therefore descrambling happens after Viterbi decoding). The interleaver sits between the encoder and the channel so a fade is dispersed into isolated soft-bit hits, which the Viterbi absorbs — the standard concatenation logic (maths guide §12.1).
2. The exact constants
2.1 Physical layer
| Parameter | Value | Code |
|---|---|---|
| Modulation | BPSK | sat.qpsk_rx.demod_bpsk |
| Symbol rate | 1200 sym/s | SYM_RATE = 1200 |
| Pulse shaping | RRC, α = 0.5 | demod_bpsk(..., alpha=0.5) |
| Receiver chain | AGC → RRC matched filter → Gardner timing → BPSK Costas | sat/qpsk_rx.py |
| Bit mapping | symbol real part: +1 → bit 0, −1 → bit 1 | soft = (1 − real)/2 |
2.2 FEC — CCSDS convolutional code (shared with sat/)
| Parameter | Value |
|---|---|
| Rate, constraint length | 1/2, K = 7 (64 states) |
| Generators | G1 = 0o171, G2 = 0o133, G2 output inverted (CCSDS convention) |
| Decoder | soft-input Viterbi (sat/viterbi.py), inputs 0.0–1.0 |
| Flush | 6 zero bits; coded length = 2·(n_info + 6) |
2.3 Scrambler
15-bit additive LFSR, polynomial \(x^{15}+x^{14}+1\), seed 0x7FFF:
output bit = reg & 1
feedback = (reg >> 14) ^ (reg >> 13) (bits 14 ⊕ 13)
reg = ((reg << 1) | feedback) & 0x7FFF
The PN sequence is XORed onto the bits; the operation is involutive (descramble = scramble). This is the same polynomial family as DVB-S's energy-dispersal scrambler (see the Commonalities table).
2.4 Interleaver
Block interleaver, 64 columns: write the coded bits row by row (zero-pad the last row), transmit column by column. De-interleaving reshapes column-major and reads back row-major, preserving soft values so the Viterbi loses nothing.
2.5 Frame sync — the unique word
24-bit UW prepended to each interleaved block:
1 0 1 1 0 0 0 1 1 1 0 0 1 0 1 1 0 1 0 0 1 1 1 0 = 0xB1CB4E
The receiver hard-slices the soft stream at 0.5 and slides a correlator, accepting the first position with ≤ 3 bit errors against the UW. The UW also resolves BPSK's 180° phase ambiguity: decode_iq simply tries both polarities and keeps whichever one both finds a UW and Viterbi-decodes (cf. LRPT's four-rotation ASM hunt, same idea).
Interop caveat (deliberate): this UW value is flagged in the source for confirmation against the STD-C TDM spec. The decode chain is self-consistent — the encoder and decoder agree — but a live capture must confirm the word (and the interleaver geometry) before off-air claims.
2.6 Frame assembly
stdc_encode(info): TX = UW ‖ interleave( conv_encode( scramble(info) ) )
stdc_decode(soft): find UW (≤3 err) → deinterleave 2·(n_info+6) soft bits
→ Viterbi → descramble → info
The decoder needs n_info (the expected payload size) to size the de-interleave — frame-length signalling from the real TDM structure is not yet implemented (§6).
3. The EGC message layer
build_egc / parse_egc serialise a SafetyNET-style message into the frame payload. Layout (all single bytes unless noted):
| Offset | Field | Value |
|---|---|---|
| 0 | magic | 0x7C |
| 1 | priority | 0 routine · 1 safety · 2 urgency · 3 distress (2 LSBs) |
| 2 | service code | default 0x31 |
| 3 | LES id | originating Land Earth Station |
| 4–5 | sequence number | 16-bit big-endian |
| 6 | area code | NAV/MET area or address group |
| 7 | text length n | ≤ 255 |
| 8 … 8+n−1 | text | ASCII |
| last | checksum | two's complement of the byte sum: sum(body + checksum) ≡ 0 (mod 256) |
parse_egc rejects a payload that is short, lacks the magic, or fails the checksum, and otherwise returns priority (named), service, LES, sequence, area, and the text.
Scope note: this is Rafe's compact serialisation of the EGC semantics (the fields a SafetyNET watchkeeper needs), not a byte-exact transcription of the on-air EGC packet header, which carries additional repetition/presentation codes inside the TDM packet structure. Off-air decoding will extend this layer once the TDM framing (§6) is in.
4. Decoding raw IQ, end to end
decode_iq(iq, fs, n_info):
demod_bpsk— AGC, RRC (α = 0.5) matched filter, Gardner symbol timing, BPSK Costas loop → complex symbols.- Normalise amplitude by the mean |real part|.
- For each polarity ±1: map to soft bits
clip((1 − real·scale·pol)/2, 0, 1), runstdc_decode; return the first success.
The IQ round-trip test drives this with 5 Hz carrier offset, fractional-sample timing offset, 0.8 rad phase, and additive noise — decoded to the exact EGC text.
5. Validation
| Test | Asserts |
|---|---|
test_scramble_involutive |
descramble(scramble(x)) = x |
test_interleave_roundtrip |
64-column interleave/de-interleave is exact (incl. padding) |
test_egc_build_parse |
priority/LES/seq/text recovered |
test_egc_checksum_rejects |
one flipped byte → {"error": "checksum"} |
test_stdc_chain_clean |
encode → decode identity on the full frame |
test_stdc_chain_noisy_offset |
19-bit stream offset + 4 % random bit flips → exact text |
test_stdc_bpsk_iq |
raw BPSK IQ with frequency/timing/phase offsets + noise → exact text (both BPSK polarities handled) |
6. Limitations and interop caveats
- TDM frame structure not yet implemented. A real STD-C carrier is a continuous sequence of fixed-length TDM frames (8.64 s, with frame counters and packet descriptors delimiting the multiplexed packets). This module decodes one UW-framed block of known payload size; carrier-following and packet demultiplexing are the next layer.
- UW / interleaver constants need on-air confirmation (§2.5). The scrambler polynomial and the convolutional code are standard and shared with the validated CCSDS chain.
- EGC layer is semantic, not byte-exact (§3).
- Uplink (ship-to-shore) signalling is out of scope — this is a shore-to-ship watch receiver.
Related: LRPT (the same Viterbi core and BPSK/QPSK receiver lineage), NAVTEX (the MF sibling service EGC SafetyNET extends to satellite coverage), and the maths guide on convolutional codes and LFSR scrambling.