DMA (bytes) -> framer -> fec_tx -> scrambler -> asm_insert -> tx_fill
-> modulator (BPSK | QPSK | GMSK, selected by MOD_SEL)
-> duc_core -> tx_egress -> AD9361 DAC
\-> loopback mux -> channel emulator -> RX chainAll blocks live in the TX window at 0x4003_0000, one 256-byte sub-window each. Register names
below are the catalogue names used by satlinkctl reg, scenario reg: steps and watched
registers.
framer#
Cuts the incoming byte stream into frames of FRAME_LEN bytes.
| Register | Meaning | Set by |
|---|---|---|
tx.framer.ctrl | [0] enable | Profile (always enabled) |
tx.framer.frame_len | Frame length in bytes (223 at reset) | framing.mtu_bytes (223 with Reed-Solomon) |
tx.framer.pad | Byte used to pad a short frame | Reset value 0x00 |
tx.framer.frame_cnt | Frames fully emitted | Read: the traffic proof |
A frame is closed on the byte count, or padded to FRAME_LEN when the stream's tlast arrives.
The TX DMA raises tlast once per 4096-byte block, not per packet, which is why packet
boundaries only survive with slot framing (see
Datapath and Transmission).
fec_tx#
CCSDS 131.0-B encoder: Reed-Solomon (255,223) outer code, convolutional K=7 r=1/2 inner code (G1 = 171₈, G2 = 133₈).
| Register | Meaning |
|---|---|
tx.fec_tx.ctrl | [0] enable, [1] bypass RS, [2] bypass convolutional |
tx.fec_tx.rate | Read-only: r = 1/2 |
tx.fec_tx.status | [0] busy, [1] RS active, [2] convolutional active |
framing.fec.type | ctrl |
|---|---|
none (and unimplemented types) | 0x7 (both bypassed) |
conv | 0x3 |
rs | 0x5 |
rs+conv | 0x1 |
The output is one symbol per byte: the convolutional path emits two coded bits per output byte (the modulators consume the low bits); the bypass path keeps the same packing. The RS encoder uses the conventional basis, not the CCSDS dual basis.
scrambler#
CCSDS pseudo-randomiser x^8+x^7+x^5+x^3+1 on the coded stream, before the marker.
| Register | Meaning |
|---|---|
tx.scrambler.ctrl | [0] enable, [15:8] seed (0 selects the CCSDS default 0xFF) |
tx.scrambler.status | [0] in frame, [15:8] live PN state, [19:16] PN bit index |
tx.scrambler.frame_cnt | Frames scrambled |
The PN sequence restarts on the first beat of every frame. The register layout is identical to the
receiver's rx_demod.deframer.scramble, and a profile writes the same value to both, so the two
ends cannot disagree.
asm_insert#
Attaches the 32-bit sync marker in front of every coded frame, in the same symbol packing as the data (16 bytes in QPSK, 32 in BPSK/GMSK, MSB first). The marker is not scrambled.
| Register | Meaning |
|---|---|
tx.asm_insert.ctrl | [0] enable. Disabled, the block is transparent |
tx.asm_insert.sync_word | The marker (0x1ACFFC1D) |
tx.asm_insert.status, tx.asm_insert.asm_cnt | Emitting flag, markers emitted |
A profile without framing.sync_word disables it: raw frames, no marker, and a receiver that
cannot frame.
tx_fill#
Inserts a preamble before a frame that follows a silence (64 clock cycles with no symbol), and up to a bounded amount of fill between frames, only at frame boundaries.
| Register | Meaning |
|---|---|
tx.fill.ctrl | [0] preamble enable, [1] inter-frame fill enable |
tx.fill.pre_len | Preamble length in symbols (16 bits) |
tx.fill.post_len | Maximum fill between frames in symbols (16 bits) |
tx.fill.pat_hi, tx.fill.pat_lo | The 64-bit pattern, read MSB first, 1 or 2 bits per symbol |
tx.fill.status, tx.fill.pre_cnt, tx.fill.fill_cnt | State and counters |
The default pattern 0x727252D02A1FEDD8 was searched for: at least 10 bits from every image of
the CCSDS marker the deframer tests, zero mean in QPSK and BPSK (no carrier line), and a flat
spectrum. See Modulation & Coding
for the profile fields and the checks applied to a custom pattern.
Modulators#
tx.chan_cfg.mod_sel selects which modulator feeds the DUC: 0 = BPSK, 1 = QPSK, 2 = GMSK. The
selected modulator must also be enabled (ctrl bit 0); a disabled one accepts symbols and
drops them silently, which the chain's modulator condition catches.
| Block | Registers | Notes |
|---|---|---|
mod_bpsk | ctrl, sps, coeff, status | RRC α = 0.35 shaping |
mod_qpsk | ctrl, sps, coeff, status | Gray mapping, constellation at 1/√2 of full scale (0x5A82), RRC α = 0.35. Reads bits [1:0] of each byte |
mod_gmsk | ctrl, bt, sps, coeff, status | MSK-style continuous phase, differentially coded, linear phase ramp; no Gaussian filter (bt and coeff unused) |
The coeff registers allow the RRC taps to be rewritten at runtime (tap index in the low 16
bits, Q15 coefficient in the high 16 bits); profiles do not use them.
tx.chan_cfg.freq_hz, baudrate and sps are descriptive: written by a profile as a record,
consumed by nothing (only mod_sel drives the modulator mux).
duc_core#
Interpolates by INTERP and shifts the signal with an NCO.
| Register | Meaning | Set by |
|---|---|---|
tx.duc.ctrl | [0] enable | Profile |
tx.duc.interp | Interpolation factor 1 to 32 | Profile, from the symbol rate |
tx.duc.gain | Q15 gain (0x7FFF = unity) | Profile; radio gain --domain digital |
tx.duc.nco_freq | Q32 phase increment | Tuning (the LO offset), radio nco --tx-hz |
tx.duc.filter_sel | Compensation FIR bank (0 = identity) | — |
The interpolator is a first-order linear interpolator; the compensation FIR is an identity
placeholder. A profile apply does not touch nco_freq: the offset belongs to the tuning (see
RF Fundamentals).
tx_egress#
The exit of the TX chain.
| Register | Meaning |
|---|---|
tx.egress.ctrl | [0] enable (the "TX egress"), [1] loopback enable: must stay 0 |
tx.egress.status | [0] busy, [1] overflow |
tx.egress.cnt | Samples out (clear on write) |
With the egress closed, blocks keep arriving from the DMA and are discarded: the framer counter
climbs while tx.egress.cnt stays still. A profile opens it when radio.tx_enabled is true; the
RF kill switch closes it.
Bit 1 is a second loopback switch inside the egress. The PL loopback is selected by the shell's
GLOBAL_CTRL[0] only; setting both would divert the samples away from the loopback path and
starve it.
After the egress#
Samples cross from the modem clock to the AD9361 clock through an asynchronous FIFO into the
axi_ad9361 DAC core. The DAC core's source selector (dac_data_sel) must be 2 (DMA) for the
modem's samples to be transmitted; other sources are the internal DDS (0, used by scenario
tone: steps), test patterns and PN sequences (used by the interface tuning). Chain conditioning
sets and checks it (see Chain Conditioning).
When GLOBAL_CTRL[0] is set, the loopback mux sends the stream through the channel emulator into
the RX chain instead (see Channel Models).