All docsStellar LinkRF bench · by Stellar Systems v0.1.0

Tools & Utilities

Measurement Tools

Byte-exact loopback measurements and how to read them, characterising the RF path, level and gain sweeps, throughput, and the bring-up scripts.

This page covers the instruments used to answer "does the link work, and how well". Every one of them has a way to be wrong; each section says what the instrument proves and what it does not.

The byte-exact loopback#

The one measurement that proves bytes cross the modem correctly: feed a known payload, capture what comes back, compare.

Shell
head -c 65536 /dev/urandom > p.bin             # a raw le:u8/32 payload (see below)
satlinkctl chain up --target pl --profile pl_loopback
satlinkctl datapath disable                    # loopback claims the modem itself
satlinkctl loopback --payload p.bin --blocks 4 --warmup 1 -o rx.bin

loopback arms the reader first, feeds the payload continuously, collects --blocks blocks in a row after discarding --warmup blocks, and compares. The same comparison runs offline:

Shell
satlinkctl compare rx.bin p.bin

The payload file is in the raw DMA encoding: only the low byte of each 32-bit little-endian word is used. A file of random bytes is fine (three bytes in four are ignored); to send a specific byte sequence, place each byte in the low lane of a 32-bit word.

Reading the result#

The comparison is reported per block, never as a single figure: each block is aligned to the payload independently, because consecutive DMA blocks need not share an alignment.

VerdictMeaning
BytePerfectEvery block matched entirely
DegradedEvery block correlates with the payload, some bytes differ
NoCorrelationAt least one block does not correlate at all: not a degraded link, no link

A slip is flagged separately when the alignment between consecutive blocks does not advance by exactly one block (a byte lost or inserted at a block boundary). It is not a demodulation error.

How to interpret errors:

  • Errors only in the first 16 bytes of a block are the DMA block-boundary artefact: the block boundary does not align with a frame. Not a modem defect.
  • Errors concentrated in block 0 and thinning out are an acquisition transient. Use --warmup 1; its effect varies a lot from run to run.
  • A short comparison window over-weights boundary errors (the same capture has scored 98.8 % over 1024 bytes and 99.71 % over 4096). Compare whole captures.

Make the measurement able to fail#

A green loopback is the easiest thing to fake. Two controls make it mean something:

  1. Check the path. On an RF measurement, confirm the PL loopback mux is off (satlinkctl chain status shows pl-mux 0, device-loopback open, dac-source 2). A profile with radio.loopback: true applied after the mode switch silently turns an "RF" loopback into a PL one.
  2. Turn it red once. Collapse the RF level and nothing else (for example TX attenuation from −20 to −89 dB): the capture must stop (504, no blocks). Restore it and it must come back.

Frame counters as instruments#

tx.framer.frame_cnt counts what really crossed the PL, whatever userspace believes it sent. It is the reliable throughput instrument: read it over a timestamped interval during a continuous feed. Do not measure throughput with dd on the IIO devices: the buffer absorbs several blocks before anything crosses, and one dd count=1 per block only ever sends the first block.

rx_demod.deframer.frame_cnt moving at zero errors proves frame sync, not correct bytes: the marker is inserted by the PL, downstream of the payload, so a receiver can lock on it while the payload is wrong. Use a byte comparison or a transfer frame's FECF for payload correctness.

Characterising the RF path#

Before trusting any absolute level, measure the loss between TX1 and your instruments:

Shell
python3 pl/top/bringup/rf_path_gain.py --rx1 connected   # or --rx1 open

The script sets the analyser's reference offset to zero, sweeps the TX attenuation, and derives the path loss from the board's known output (P_board ≈ att + 15.8 dBm). It checks three criteria before setting any offset: the loss must be constant across the sweep, the loss beyond the pad must be a plausible splitter (3 to 7 dB), and 1 dB of commanded attenuation must move the output by 1 dB. If any fails, it refuses to set an offset. --rx1 is required because terminating the RX1 arm changes the splitter's loss (about 0.4 dB on the reference bench).

Re-run it whenever a connector, pad or splitter changes, or the carrier moves far: a pad's loss is frequency dependent. The script needs the analyser on the network and a board with the raw_tx profile available.

Sweeps as scenarios#

A sweep is a measurement: run it as a scenario so every point shares the run's clock, traffic and telemetry, rather than as a shell loop.

ScenarioMeasures
bringup/rf_rx_gain_response.yamlThe receive chain's response to the AD9361 RX gain, 0 to 70 dB, under a constant transmitter
bringup/rf_rx_port_sweep.yamlWhich AD9361 RX input carries the signal, with a no-signal window first
bringup/rf_level_sweep.yamlThe receiver across transmit levels
bringup/tone_sweep_reference.yamlPure tones at 0.1, 0.25 and 0.5 of DAC full scale, then a lower-sideband tone, for analyser calibration
impairments/channel_operations_sweep_v1.yamlThe five channel operations with windowed assertions

pl/top/bringup/read_port_sweep.py <board> <run-id> and read_gain_response.py read such runs, taking every window boundary from the run's own events.

When reading a sweep, prefer slopes and internal states (a 1:1 response, an AGC at its ceiling, a port at its no-signal floor) over absolute levels: they do not depend on a calibration you may not have.

satlink-bench (ps/satlink-bench) measures the sustained PS↔PL throughput on the board: one IIO descriptor kept open, one timestamp per write, and the PL counters read in the same process at the same instants. It reports a distribution, because the TX chain runs in bursts. --duplex runs a reader alongside, armed first; feeding TX alone back-pressures the RX chain within milliseconds.

Other bring-up instruments#

ToolPurpose
satlinkctl doctorBring-up health check by register name, including repeated reads of the same address
satlink-checkHost-side cold-deploy check: daemon, services, PL registers, profiles and scenarios
pl/top/bringup/power_meter_check.pyThe PL power meter's response across amplitudes (input and output taps read in successive windows, so about 1 dB of run-to-run spread)
pl/top/bringup/run-campaign.shLaunch a long shell campaign under a transient systemd unit, logging to both the journal and a file
ILA capturesIntegrated logic analyser builds for signals no register exposes (see Building the Bitstream)

Before instrumenting a block, read the debug registers it already exposes: ADI's axi_dmac, for example, publishes its reset-manager state in its DBG0 register (0x774 = all domains released).

Stellar Link · v0.1.0

↑↓ to moveEnter to open