This page explains the radio quantities an operator sets and reads on SatLink, and how they are constrained by the hardware. The modem's internals are covered in Signal Processing.
Sample rate, samples per symbol and symbol rate#
The symbol rate on the air is not a free number. It is a division:
symbol_rate = ad9361_sample_rate / (sps × interp)| Term | Range | Why |
|---|---|---|
sps | 4, fixed | Every pulse-shaping kernel in the PL (the RRC matched filter, the BPSK, QPSK and GMSK modulators) is built for 4 samples per symbol when the bitstream is synthesised. A profile with any other samples_per_symbol is refused |
interp | 1 to 32 | The range the DUC decodes (the DDC decimates by the same factor) |
| AD9361 sample rate | 2 083 333 to 61 440 000 Sa/s | The part's own limits |
| Modem sample rate | up to 13 MSa/s | Above that the chain cannot fill every DAC beat and the DAC is fed zeros |
So only a fixed ladder of symbol rates can be produced. phy.symbol_rate_baud in a profile
must be one of the available rungs; GET /api/v1/system/capabilities lists them all.
| Symbol rate (baud) | AD9361 rate | interp | Available |
|---|---|---|---|
| 5 000 000 | 20 MSa/s | 1 | No — above the 13 MSa/s the modem sustains |
| 2 500 000 | 10 MSa/s | 1 | Yes |
| 1 250 000 | 5 MSa/s | 1 | Yes |
| 625 000 | 2.5 MSa/s | 1 | Yes |
| 312 500 | 2.5 MSa/s | 2 | Yes |
| 156 250 | 2.5 MSa/s | 4 | Yes |
| 78 125 | 2.5 MSa/s | 8 | Yes |
| 39 062.5 | 2.5 MSa/s | 16 | Yes |
| 19 531.25 | 2.5 MSa/s | 32 | Yes |
| 9 765.625 | — | 64 | No — needs interp 64, or a part rate below its floor |
The bottom six rungs all run the AD9361 at 2.5 MSa/s, so moving between them changes only the DUC/DDC factor and does not retune the part (retuning invalidates the digital interface calibration, see Chain Conditioning).
A profile apply writes interp into the DUC and DDC. It does not set the AD9361's sample rate:
that belongs to the tuning step of the chain conditioning, and the chain's rate condition
catches the two disagreeing.
Bandwidth#
The transmit pulse and the receive matched filter are root-raised-cosine filters with roll-off
α = 0.35, so the occupied bandwidth is about Rs × 1.35: 26 kHz at 19 531.25 baud, 53 kHz at
39 062.5 baud, 3.4 MHz at 2.5 Mbaud.
The AD9361's analog filters are sized on every profile apply from the symbol rate:
Rs × 1.35 × 1.2, clamped to the part's 200 kHz floor and 40 MHz ceiling. Below about
127 kbaud the 200 kHz floor decides. The applied values are readable in /api/v1/radio/status
(rx_bandwidth_hz, tx_bandwidth_hz).
Tuning: LO plus NCO#
The AD9361 leaks a little of its local oscillator at the LO frequency. If the LO sat exactly on the channel centre, that leakage would land in the middle of the signal. SatLink therefore tunes in two stages:
- the LO (coarse, analog) is placed below the requested centre by an offset;
- the NCO (fine, digital, in the DUC and DDC) shifts the signal back up by the same offset.
The offset is 1.25 × Rs × 1.35 / 2, a quarter past the band edge, so the leakage falls just
outside the occupied band. For a 437.2 MHz profile at 19 531.25 baud this places the LOs about
16.5 kHz below 437.2 MHz with a +16.5 kHz NCO offset. If the signal is too wide for the sampled
band to fit the offset, the tuning refuses rather than picking a bad value.
A profile apply performs this tuning and reports it in one sentence, including the centre actually reached (the LO synthesiser has a finite step, typically a few hertz off).
You can also move the NCO by hand, per side, within ±fs/2:
satlinkctl radio nco --rx-hz=-2500 --tx-hz=0 # attached form for negative values
satlinkctl radio freq 437200000 --channel both # the LOs (rx, tx or both)A manual change of frequency, gain or register after a profile apply marks the active profile
dirty: GET /api/v1/profiles/active then reports dirty: true and what made it so.
Gains and attenuation#
There are two kinds of gain knob, and they must not be confused:
| Domain | Knob | Unit | Where |
|---|---|---|---|
rf (default) | AD9361 RX gain | dB of amplification | radio.rx_gain_db, satlinkctl radio gain --rx-db |
rf (default) | AD9361 TX gain | dB of attenuation, 0 to −89.75 | radio.tx_power_dbm (negative values), satlinkctl radio gain --tx-db |
digital | RX ingress scalar | Q8.8 fixed point | satlinkctl radio gain --domain digital |
digital | TX DUC gain | Q15 fixed point (0x7FFF = unity) | satlinkctl radio gain --domain digital |
On this part the TX gain is an attenuation: 0 dB is full drive and values go negative. A profile's
tx_power_dbm is therefore applied only when it is zero or negative; a positive value (a power
at an antenna, behind an external amplifier) is refused with an explanation. When a profile sets
rx_gain_db, the AD9361's own AGC is switched to manual, because the part refuses gain writes
while its AGC owns the setting.
/api/v1/radio/status carries both families: rx_gain_db/tx_gain_db are the PL's digital
scalars; rx_rf_gain_db/tx_rf_gain_db are the part's gains in dB. Read the one you set.
Output level#
On the reference bench the board's output at the TX1 connector was calibrated as:
P_board ≈ att + 15.8 dBm (att = TX attenuation, −30 ≤ att ≤ −10 dB measured)This is a calibration of one board at 437 MHz, not a datasheet value. Linearity below −30 dB of attenuation has not been measured.
Reading the receive level#
Several indicators describe the received signal. They measure different things:
| Indicator | What it is | How to read it |
|---|---|---|
part_rssi_db | The AD9361's own RSSI (in_voltage0_rssi), dB below full scale, referred to the input (the receive gain is already compensated) | Smaller is stronger. On the reference bench, ~75 means no signal |
rssi_dbm | The PL's RSSI meter, downstream of the ADC and the DDC | Tracks the analog gain 1 dB per dB until it saturates. Its absolute offset is not calibrated (off by about 13 dB) |
agc_gain | The PL AGC's linear gain (Q4.12, maximum just under 16.0) | Near 16.0 while traffic flows means the receiver is starved: raise rx_gain_db. Near 16.0 with nothing transmitted is normal |
part_rx_gain_db | The AD9361's RX gain, read back from the part | The independent variable of a gain sweep |
Duplex and the ENSM#
The AD9361's Enable State Machine (ENSM) must be in an emitting state for the transmitter to
put anything on the connector. On-air conditioning sets it to fdd (frequency-division duplex:
transmit and receive at once). The RF kill switch sets it to alert. The profile field
radio.duplex is descriptive and does not drive the ENSM.
Time on the board#
The board has no real-time clock and reads 1970 at power-on. Every timestamp it emits
(ts_ms on the link, frame times on /api/v1/frames, the run clock) counts from boot or from
daemon start, and says so in a field beside it. Stamp arrival on your own host when you need to
correlate with anything else.