This page describes what happens to your bytes between the moment they enter the modem and the moment they come out of the receiver, and which profile fields control each step. The register level detail is in Transmitter Chain and Receiver Chain.
Modulations#
phy.modulation | Status | Notes |
|---|---|---|
BPSK | Implemented | One bit per symbol |
QPSK | Implemented | Two bits per symbol; the reference configuration of the bench |
GMSK | Implemented, approximated | phy.bt is required and must be 0.5. See the note below |
OQPSK | Refused | No half-symbol offset path exists in the fabric; it would transmit plain QPSK |
MSK | Refused | Would transmit GMSK (BT 0.5) instead |
π/4-DQPSK | Refused | Neither the differential encoding nor the π/4 rotation exists |
FSK, GFSK | Refused | No FSK path; phy.freq_deviation_hz reaches no register |
A refused modulation fails the profile apply with a message saying why. All modulations run at 4 samples per symbol. BPSK and QPSK use root-raised-cosine shaping (α = 0.35) and Gray mapping; the receiver is coherent (Costas carrier loop, Gardner timing recovery).
The transmit and receive chains#
TX: bytes -> framer -> fec_tx -> scrambler -> asm_insert -> tx_fill -> modulator -> DUC
RX: DDC -> AGC -> matched filter -> timing -> carrier -> slicer -> demod
-> deframer (find ASM, un-rotate, descramble) -> fec_rx -> bytesThe two chains are not mirror images, and the placement follows CCSDS 131.0-B:
- the attached sync marker is added after coding, so the receiver can find frames before it decodes them;
- the scrambler works on the coded stream, just before the marker, and the marker itself is never scrambled;
- on receive, the deframer sits before the FEC decoder: it delimits frames and descrambles on the coded stream.
Frames and frame lengths#
The framer cuts the transmit byte stream into frames of FRAME_LEN bytes, taken from
framing.mtu_bytes (default 256 when absent). Because every code expands the frame and the
deframer counts coded bytes, the two ends are programmed with different lengths:
framing.fec.type | Framer FRAME_LEN | Deframer FRAME_LEN | Constraint |
|---|---|---|---|
none | mtu_bytes | mtu_bytes | — |
conv (K=7, r=1/2) | mtu_bytes | 2 × mtu_bytes | — |
rs (RS(255,223)) | 223 | 255 | mtu_bytes must be 223 |
rs+conv | 223 | 510 | mtu_bytes must be 223 |
The profile manager computes both values; you only set mtu_bytes.
Packet boundaries do not survive by default. The framer pads a short frame only when the DMA
signals the end of a block, which happens once per 4096-byte block, not per packet. Otherwise it
closes frames on the byte count alone, so what comes back is your byte stream re-cut at
FRAME_LEN. To keep one packet per frame, send packets in fixed slots of FRAME_LEN bytes
(see Datapath and Transmission). Slots only
stay aligned when FRAME_LEN divides 4096: 1024, 512, 256, 128, 64 or 16 work, 223 does not,
so slot framing is not available with Reed-Solomon.
Attached sync marker (ASM)#
framing.sync_word.value_hex sets the 32-bit marker (the CCSDS value is 1ACFFC1D). The same
value is written to the transmitter and the receiver.
- No
sync_word, no marker. A profile withoutsync_wordtransmits raw frames with no marker, and the receiver cannot lock onto anything: such a profile is transmit-only (theraw_txprofile is one, used to put a known signal on a spectrum analyser). - Phase ambiguity is resolved in the receiver. A QPSK carrier loop can lock on any of four
phases (BPSK and GMSK: two). The deframer searches the marker under every rotation the
modulation allows, latches the one that matched, and un-rotates the payload. The matched
rotation is readable in
rx_demod.deframer.statusbits [4:3] (0, 90, 180 or 270 degrees). - One wrong bit is tolerated. A marker with exactly one wrong bit out of 32 is accepted and
the frame delivered; these soft matches are counted separately (
ERR_CNT) and give a pre-FEC bit error estimate (ber_asm). A marker with two wrong bits is not matched and the frame is lost.
Scrambler#
The CCSDS pseudo-randomiser, polynomial x^8+x^7+x^5+x^3+1, byte-wise, applied to the coded
stream before the marker.
framing:
scrambler:
enabled: true
seed: 0xFF # optional, 8 bits, default 0xFF (all ones, the CCSDS preset)The same register value (enable in bit 0, seed in bits [15:8]) is written to both ends, so the
transmitter and receiver cannot disagree. scrambler.polynomial is descriptive: the polynomial
is fixed in the fabric.
Forward error correction#
framing:
fec:
type: "rs+conv" # none | conv | rs | rs+conv
rate: "223/255" # required when type is not none; informativetype | Encoder (TX) | Decoder (RX) |
|---|---|---|
none | bypassed | bypassed |
conv | convolutional K=7, r=1/2, G1=171₈, G2=133₈ (bit-exact CCSDS) | Viterbi |
rs | Reed-Solomon (255,223), GF(2⁸) polynomial 0x187 | Berlekamp-Massey, Chien/Forney |
rs+conv | RS outer, convolutional inner (the CCSDS 131.0-B concatenation) | Viterbi, then RS |
ldpc, turbo, polar | not implemented — the chain is configured as uncoded | — |
Points to know:
- The Reed-Solomon implementation uses the conventional basis, not the CCSDS dual basis. Its correcting power is the same, but interoperability with a third-party CCSDS RS decoder or encoder is not guaranteed.
fec.ratemust be present when coding is enabled, but it does not select anything: the code type does.framing.interleaveris descriptive; there is no interleaver in the datapath.- The receiver counts corrected bytes and uncorrectable codewords (
fec_rx.CORR_CNT,FAIL_CNT). An uncorrectable codeword is still delivered, errors included.
Preamble and inter-frame fill#
After a silence, the receiver's AGC, timing loop and carrier loop start cold; between frames, a
modulator with nothing to send would simply stop and let the loops drift. The tx_fill block
solves both:
- a preamble of
pre_symbolssymbols is sent before a frame that follows a silence; - up to
post_symbolssymbols of fill are sent between frames.
Fill is inserted only at frame boundaries, after the marker insertion, so it is never scrambled or coded and never appears inside a frame.
framing:
preamble:
type: "fill" # "fill" (built-in pattern) or "pattern" (your own value_hex)
# value_hex: "727252D02A1FEDD8" # optional for fill, required for pattern
pre_symbols: 512 # default 512
post_symbols: 2048 # default 2048; 0 is refused| Field | Meaning |
|---|---|
type | fill uses the built-in 64-bit pattern 0x727252D02A1FEDD8 unless value_hex is given; pattern requires value_hex |
value_hex | 64-bit pattern, 16 hex digits read MSB first; 8 digits are repeated to fill 64 bits |
pre_symbols | Preamble length before a frame that follows silence. The measured threshold on the PL loopback is 161 to 192 symbols; 512 gives margin (about 26 ms at 19 531 baud) |
post_symbols | Maximum fill between two frames. It must be bounded: the profile sequencer commits only after 64 clocks with no TX sample, so an endless fill would block every later profile apply and channel change |
Every pattern is checked before it is written: it must stay at least 8 bits away from every
image of the sync word the deframer searches (the deframer accepts 1 wrong bit), and its mean
must stay below a quarter of full scale (a biased pattern is a carrier). A profile without
framing.preamble disables the fill.
CCSDS transfer frames#
framing.transfer_frame adds a CCSDS Space Data Link layer on the PS side. It has no register
behind it: satlinkd's link service wraps each message in a transfer frame before handing the
octets to the modem, and locates frames again in the received stream.
framing:
mtu_bytes: 1024 # the transfer frame IS the PL frame
transfer_frame:
type: "tm" # tm (132.0-B), aos (732.0-B) or uslp (732.1-B)
scid: 0x0AB # 10 bits for TM, 8 for AOS, 16 for USLP
vcid: 1 # 3 bits for TM, 6 for AOS and USLP
fecf: true # CRC-16 Frame Error Control Field, default trueRules checked at apply:
mtu_bytesis required and must divide 4096 (1024, 512, 256, 128, 64 work; the common mission length 1115 does not);- the SCID and VCID must fit the field widths of the chosen frame type;
- transfer frames cannot be combined with
rsorrs+conv(223 does not divide 4096). Usenoneorconvand let the FECF carry the error detection.
Received frames are published on the link service with framing: "transfer_frame" and a
quality verdict: good (FECF checks), erred (FECF fails; the payload is still delivered) or
undetermined (no FECF, or no verdict possible). A message larger than one frame is refused, not
segmented. The CCSDS layers left out (SDLS, COP-1, LDPC and turbo codes, AOS header error
control) are listed in Known Limitations.
Encoding and other descriptive fields#
phy.encoding (nrz, nrzi, …) is descriptive: line coding is not implemented, and the
receiver resolves phase ambiguity by searching marker rotations instead of by differential
coding. See RF Profiles for the complete list of fields and which
of them reach the hardware.