From 04f8fd019aec19a14c5eb67c4344cc734a1b3210 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Tue, 24 Feb 2026 21:24:41 +0100 Subject: [PATCH] [docs](trx-rs): document SDR backend configuration options Add [sdr], [sdr.gain], and [[sdr.channels]] sections to CONFIGURATION.md, extend [rig.access] with type = "sdr" / args, add CLI override note, and append a commented SoapySDR example block to trx-server.toml.example. Mark SDR-09 as complete in SDR.md. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- CONFIGURATION.md | 44 ++++++++++++++++++++++++++++++++++++----- SDR.md | 2 +- trx-server.toml.example | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 2cf434b..1520000 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -42,17 +42,20 @@ Notes: - `initial_mode` (`string`, default: `"USB"`): one of `LSB|USB|CW|CWR|AM|WFM|FM|DIG|PKT` ### `[rig.access]` -- `type` (`string`, default behavior: `serial` if omitted): `serial|tcp` +- `type` (`string`, default behavior: `serial` if omitted): `serial|tcp|sdr` - Serial mode: -- `port` (`string`) -- `baud` (`u32`) + - `port` (`string`) + - `baud` (`u32`) - TCP mode: -- `host` (`string`) -- `tcp_port` (`u16`) + - `host` (`string`) + - `tcp_port` (`u16`) +- SDR mode: + - `args` (`string`, required when `type = "sdr"`): SoapySDR device args string (e.g. `"driver=rtlsdr"` or `"driver=airspy,serial=00000001"`). Passed verbatim to `SoapySDR::Device::new()`. Notes: - For `serial`, both `port` and `baud` are required. - For `tcp`, both `host` and `tcp_port` are required. +- For `sdr`, `args` must be non-empty. The `port`, `baud`, `host`, and `tcp_port` fields are ignored. ### `[behavior]` - `poll_interval_ms` (`u64`, default: `500`, must be `> 0`) @@ -110,6 +113,36 @@ Notes: - The IGate reconnects automatically with exponential backoff (1 s → 2 s → … → 60 s) on TCP errors. - Requires `[audio].enabled = true` (APRS packets are decoded from audio). +### `[sdr]` +- `sample_rate` (`u32`, default: `1920000`, must be `> 0`): IQ capture rate in Hz. Must be supported by the device. +- `bandwidth` (`u32`, default: `1500000`): Hardware IF filter bandwidth in Hz. +- `center_offset_hz` (`i64`, default: `100000`): The SDR tunes this many Hz below the dial frequency to keep the signal off the DC spur. Negative values tune above. + +### `[sdr.gain]` +- `mode` (`string`, default: `"auto"`): `"auto"` enables hardware AGC (falls back to `"manual"` with a warning if the device does not support it); `"manual"` uses the fixed `value`. +- `value` (`f64`, default: `30.0`): Gain in dB. Used only when `mode = "manual"`. + +### `[[sdr.channels]]` + +Defines one virtual receiver channel within the wideband IQ stream. At least one channel is required when using the `soapysdr` backend. The **first** channel in the list is the *primary* channel: `set_freq` and `set_mode` from rig control apply to it, and `get_status` reads from it. + +- `id` (`string`, default: `""`): Human-readable label used in logs. +- `offset_hz` (`i64`, default: `0`): Frequency offset from the dial frequency in Hz. Primary channel should be `0`. +- `mode` (`string`, default: `"auto"`): Demodulation mode. `"auto"` follows the RigCat `set_mode` command; or a fixed mode string: `LSB`, `USB`, `CW`, `CWR`, `AM`, `WFM`, `FM`, `DIG`, `PKT`. +- `audio_bandwidth_hz` (`u32`, default: `3000`): One-sided bandwidth of the post-demodulation audio BPF in Hz. +- `fir_taps` (`usize`, default: `64`): FIR filter tap count. Higher values give sharper roll-off at the cost of latency. +- `cw_center_hz` (`u32`, default: `700`): CW tone centre frequency in the audio domain (Hz). +- `wfm_bandwidth_hz` (`u32`, default: `75000`): Pre-demodulation filter bandwidth for WFM only (Hz). +- `decoders` (`string[]`, default: `[]`): Decoder IDs that receive this channel's PCM audio. Valid values: `"ft8"`, `"wspr"`, `"aprs"`, `"cw"`. Each decoder ID may appear in at most one channel. +- `stream_opus` (`bool`, default: `false`): Encode this channel's audio as Opus and stream to clients over the TCP audio port. At most one channel may set this to `true`. + +Notes: +- Requires `libSoapySDR` installed (`brew install soapysdr` on macOS; `libsoapysdr-dev` on Debian/Ubuntu). +- The SDR backend is RX-only. `[audio] tx_enabled` must be `false`. +- Channel IF constraint: `|center_offset_hz + offset_hz| < sample_rate / 2` for every channel; violated channels cause a startup error. +- `[audio] sample_rate` must match the output audio rate of the SDR pipeline (48000 Hz recommended). +- Use `trx-server --print-config` to see all defaults. SDR fields appear only if the binary was built with `--features soapysdr`. + ### `[decode_logs]` - `enabled` (`bool`, default: `false`) - `dir` (`string`, default: `"$XDG_DATA_HOME/trx-rs/decoders"`; fallback: `"logs/decoders"`, must not be empty when enabled) @@ -180,6 +213,7 @@ Notes: - `--rig`, `--access`, positional `RIG_ADDR` - `--callsign` - `--listen`, `--port` (JSON listener) +- SDR backend: all SDR options are file-only (`[sdr]` and `[[sdr.channels]]`). ### `trx-client` - `--config`, `--print-config` diff --git a/SDR.md b/SDR.md index f9c0a18..0462d7c 100644 --- a/SDR.md +++ b/SDR.md @@ -34,7 +34,7 @@ This document specifies the requirements for a SoapySDR-based RX-only backend (` | ID | Status | Task | Touches | Needs | |----|--------|------|---------|-------| | SDR-08 | `[ ]` | `main.rs`: after building rig, if `as_audio_source()` is `Some` skip cpal, subscribe each decoder and the Opus encoder to the appropriate channel PCM senders; validate `stream_opus` count ≤ 1 | `src/trx-server/src/main.rs` | SDR-03, SDR-07 | -| SDR-09 | `[ ]` | Add `trx-backend-soapysdr` to workspace `Cargo.toml`; update `CONFIGURATION.md` with new `[sdr]` / `[[sdr.channels]]` options | `Cargo.toml`, `CONFIGURATION.md` | SDR-04 | +| SDR-09 | `[x]` | Add `trx-backend-soapysdr` to workspace `Cargo.toml`; update `CONFIGURATION.md` with new `[sdr]` / `[[sdr.channels]]` options | `Cargo.toml`, `CONFIGURATION.md` | SDR-04 | ### Validation & tests diff --git a/trx-server.toml.example b/trx-server.toml.example index 9a1f5fb..c5226b8 100644 --- a/trx-server.toml.example +++ b/trx-server.toml.example @@ -102,3 +102,40 @@ aprs_file = "TRXRS-APRS-%YYYY%-%MM%-%DD%.log" cw_file = "TRXRS-CW-%YYYY%-%MM%-%DD%.log" ft8_file = "TRXRS-FT8-%YYYY%-%MM%-%DD%.log" wspr_file = "TRXRS-WSPR-%YYYY%-%MM%-%DD%.log" + +# --- SoapySDR backend example --- +# To use an SDR device instead of a physical transceiver, set: +# +# [rig] +# model = "soapysdr" +# initial_freq_hz = 14074000 +# initial_mode = "USB" +# +# [rig.access] +# type = "sdr" +# args = "driver=rtlsdr" +# +# [sdr] +# sample_rate = 1920000 +# bandwidth = 1500000 +# center_offset_hz = 200000 +# +# [sdr.gain] +# mode = "auto" +# value = 30.0 +# +# [[sdr.channels]] +# id = "primary" +# offset_hz = 0 +# mode = "auto" +# audio_bandwidth_hz = 3000 +# decoders = ["ft8", "cw"] +# stream_opus = true +# +# [[sdr.channels]] +# id = "wspr" +# offset_hz = 21600 +# mode = "USB" +# audio_bandwidth_hz = 3000 +# decoders = ["wspr"] +# stream_opus = false