Files
trx-rs/trx-rs.toml.example
sjg c10b5faef4
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / reuse (push) Canceled after 0s
[feat](trx-rs): redesign SDR noise blanker with tuning profiles
The old IQ noise blanker tracked a fast running RMS and, on a
threshold crossing, replaced the sample with the last clean one. That
hard sample-and-hold is a step discontinuity: it splatters energy back
across the wideband passband, so after the narrow channel filter it
often sounded worse than the noise it removed — especially on SSB, CW
and digital. It also had no look-ahead (the impulse leading edge leaked
through before the fast RMS reacted), blanked only single samples, and
used a fixed 1/128 time constant that did not scale with capture rate.

Redesign the blanker around accepted wideband-NB practice and add
profiles matched to the interference source:

- Noise-floor tracker updated only from clean samples and frozen while
  blanking, so a burst cannot desensitise detection.
- Detection on instantaneous power vs threshold² × noise floor.
- Look-ahead delay line so the gate closes *before* the impulse reaches
  the output, removing the leading edge.
- Raised-cosine tapered gate (ramp 1→0→1) instead of a hard hold, which
  minimises blanker splatter.
- Windowed blanking that re-arms on every detected sample to cover the
  full width of a burst.
- All timings expressed in real time and converted to samples at the
  capture rate, so behaviour is consistent across SDR sample rates.

Profiles (`NoiseBlankerProfile`, default `spike`): spike, ignition,
powerline, broadband — each selects the blank window, look-ahead, taper
and floor time constant. `threshold` stays orthogonal as the
sensitivity knob.

Core/protocol:
- New `NoiseBlankerProfile` in trx-core (serde/parse/u8/TS), re-exported
  at the crate root; `RigFilterState.sdr_nb_profile` for state sync.
- `profile` added to `RigCommand`/`ClientCommand::SetSdrNoiseBlanker`,
  the trait method, and the command mapping.

Config: `[rig.sdr.noise_blanker] profile = "spike"` (regenerated
trx-rs.toml.example).

SDR backend: `NoiseBlanker` rewritten in the channel DSP; profile wired
through `SoapySdrConfig`, the runtime setter, and `filter_state()`.

Frontend: an "NB profile" selector in the SDR advanced controls
(POST /set_sdr_noise_blanker&profile=…), reflecting server state; the
profile rides along with the enable/threshold quick toggle so it is
preserved. Regenerated generated.ts and app.js.

Tests: profile u8/parse round-trips (trx-core); DSP tests for impulse
suppression with no leading-edge leak, strong-steady-signal
pass-through, and wider-profile-blanks-longer.

Docs: User-Manual NB section rewritten for the new algorithm and
profiles.

Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-16 21:42:14 +02:00

258 lines
6.2 KiB
TOML

# trx-rs example configuration
#
# Generated from the config structs; regenerate with:
# cargo run -p trx-config --example generate_example
#
# Both sections are optional: trx-server reads [trx-server], trx-client reads
# [trx-client], and either may live in its own file with the section header
# omitted. Any string may use ${ENV_VAR}, and credentials may be moved out of
# this file with the matching *_file keys.
#
# Check a config without starting anything:
# trx-server --check-config --config trx-rs.toml
# trx-client --check-config --config trx-rs.toml
# Server: drives the radio hardware.
[trx-server]
rigs = []
# Station identity. Coordinates feed PSKReporter and the map.
[trx-server.general]
callsign = "N0CALL"
log_level = "info"
latitude = 52.2297
longitude = 21.0122
# Single-rig layout. For several radios, delete this and use [[rigs]].
[trx-server.rig]
model = "ft817"
initial_freq_hz = 144300000
initial_mode = "USB"
# How to reach the radio: serial, tcp, or sdr.
[trx-server.rig.access]
type = "serial"
port = "/dev/ttyUSB0"
baud = 9600
# CAT polling and retry behaviour.
[trx-server.behavior]
poll_interval_ms = 500
poll_interval_tx_ms = 100
max_retries = 3
retry_base_delay_ms = 100
vfo_prime = true
# JSON control listener that trx-client connects to.
[trx-server.listen]
enabled = true
listen = "127.0.0.1"
port = 4530
# Tokens clients must present. Empty means no authentication.
# Use tokens_file = "/etc/trx-rs/tokens" to keep them out of this file.
[trx-server.listen.auth]
tokens = []
# Opus audio stream for trx-client.
[trx-server.audio]
enabled = true
listen = "127.0.0.1"
port = 4531
rx_enabled = true
tx_enabled = true
sample_rate = 48000
channels = 2
frame_duration_ms = 20
bitrate_bps = 256000
# Report FT8/FT4/WSPR spots to pskreporter.info.
[trx-server.pskreporter]
enabled = false
host = "report.pskreporter.info"
port = 4739
# Forward received APRS frames to APRS-IS.
[trx-server.aprsfi]
enabled = false
host = "rotate.aprs.net"
port = 14580
passcode = -1
beacon = false
beacon_interval_secs = 1200
beacon_symbol_table = "/"
beacon_symbol_code = "-"
# Write decodes to JSON Lines files.
[trx-server.decode_logs]
enabled = false
dir = "/var/lib/trx-rs/decoders"
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"
wefax_file = "TRXRS-WEFAX-%YYYY%-%MM%-%DD%.log"
# Which decoders run. Trimming this list saves real CPU on small boxes.
# Valid names: aprs, aprs_hf, ais, cw, ft2, ft4, ft8, lrpt, sstv, vdes, wefax, wspr.
# output_dir sets where sstv/wefax/lrpt write images (default: user cache dir).
[trx-server.decoders]
enabled = [
"aprs",
"aprs_hf",
"ais",
"cw",
"ft2",
"ft4",
"ft8",
"lrpt",
"sstv",
"vdes",
"wefax",
"wspr",
]
# SoapySDR pipeline; used when [rig.access] type = "sdr".
[trx-server.sdr]
sample_rate = 1920000
bandwidth = 1500000
wfm_deemphasis_us = 50
center_offset_hz = 100000
dig_sideband = "auto"
channels = []
max_virtual_channels = 4
spectrum_fft_size = 1024
spectrum_interval_ms = 50
# "auto" for hardware AGC, or "manual".
[trx-server.sdr.gain]
mode = "auto"
value = 30.0
# Software squelch on demodulated audio.
[trx-server.sdr.squelch]
enabled = false
threshold_db = -65.0
hysteresis_db = 3.0
tail_ms = 180
# Impulse-noise suppression on the IQ stream.
[trx-server.sdr.noise_blanker]
enabled = false
threshold = 10.0
profile = "spike"
# Timeout and buffer tuning. The defaults suit most setups.
[trx-server.timeouts]
command_exec_timeout_ms = 10000
poll_refresh_timeout_ms = 8000
io_timeout_ms = 10000
request_timeout_ms = 12000
rig_task_channel_buffer = 32
# Client: exposes the radio to users.
[trx-client]
# Labels shown in the web UI.
[trx-client.general]
callsign = "N0CALL"
website_url = "https://haxx.space"
website_name = "haxx.space"
ais_vessel_url_base = "https://www.vesselfinder.com/?mmsi="
log_level = "info"
# Legacy single-remote form; prefer [[remotes]] below.
[trx-client.remote]
poll_interval_ms = 750
spectrum_interval_ms = 50
[trx-client.remote.auth]
[[trx-client.remotes]]
name = "home-hf"
url = "192.168.1.100:4530"
rig_id = "hf"
poll_interval_ms = 750
spectrum_interval_ms = 50
[trx-client.remotes.auth]
token = "my-token"
[[trx-client.remotes]]
name = "home-vhf"
url = "192.168.1.100:4530"
rig_id = "vhf"
poll_interval_ms = 750
spectrum_interval_ms = 50
[trx-client.remotes.auth]
token = "my-token"
# Web UI. default_rig_name and the per-rig maps are keyed by the
# [[remotes]] name, not the server-side rig id.
[trx-client.frontends.http]
enabled = true
listen = "127.0.0.1"
port = 8080
default_rig_name = "home-hf"
initial_map_zoom = 10
spectrum_coverage_margin_hz = 50000
spectrum_usable_span_ratio = 0.9200000166893005
show_sdr_gain_control = true
bandplan_enabled = true
bandplan_region = "iaru_r1"
decode_history_retention_min = 1440
[trx-client.frontends.http.decode_history_retention_min_by_rig]
# Optional user/password ACL for the web UI. Administrators manage
# accounts stored in users_file.
[trx-client.frontends.http.auth]
enabled = false
users_file = "trx-http-users.json"
bootstrap_admin_username = "admin"
bootstrap_admin_password = "change-this-password"
bootstrap_read_enabled = true
bootstrap_read_username = "guest"
bootstrap_read_password = "guest"
session_ttl_min = 480
cookie_secure = false
cookie_same_site = "Lax"
# Hamlib-compatible TCP interface, one listener per rig.
[trx-client.frontends.rigctl]
enabled = false
listen = "127.0.0.1"
port = 4532
[trx-client.frontends.rigctl.rig_ports]
# JSON-over-TCP control interface.
[trx-client.frontends.http_json]
enabled = true
listen = "127.0.0.1"
port = 0
[trx-client.frontends.http_json.auth]
tokens = []
# Where to fetch the audio stream from.
[trx-client.frontends.audio]
enabled = true
server_port = 4531
[trx-client.frontends.audio.rig_urls]
[trx-client.frontends.audio.rig_ports]
# Play RX audio on a local sound device and capture TX from one.
[trx-client.frontends.audio.bridge]
enabled = false
bitrate_bps = 192000
rx_gain = 1.0
tx_gain = 1.0
# Station log of contacts. path defaults to logbook.jsonl in the user's
# data directory; set it to keep the log on a backed-up volume.
[trx-client.logbook]