The WEFAX decoder decoded content but chopped a single chart into many short PNG "chunks". Two heuristics fought each other: in State::Receiving the carrier-loss watchdog finalized the image after only 30 low-correlation scan lines (~15 s at 120 LPM), and the Idle variance auto-start then re-triggered on the still-present carrier ~3 s later, starting a fresh image. Ordinary HF fading (QSB) of 5-20 s therefore split every chart into a stream of tiny images. Reference decoders (fldigi) keep one continuous image per APT cycle up to a large line cap and only stop on the APT stop tone or genuine signal loss. Align with that model: - Raise the end-of-transmission watchdog to 120 low-correlation lines (~60 s at 120 LPM) so normal fades ride through within one image. - Gate the variance-based auto-start to fire at most once per session (auto_start_used). After the first image a new one starts only on an APT start tone or an explicit reset, so trailing carrier/noise can no longer spawn a second image. reset() re-arms it. - Cap a single image at 3000 lines to bound memory on an open carrier. - Require a 2 s (was 1 s) APT tone sustain, cutting false Stop detections on busy image content that momentarily hits ~450 transitions/s. Also make line slicing drift-free: 120 LPM at 11025 Hz is 5512.5 samples per line, and slicing on the rounded integer accumulated a fractional-sample error every line (slow horizontal slant). Boundaries are now derived from the exact fractional line length (samples_per_line_f64) so the error never accumulates. Adds regression tests for the single-auto-start invariant and zero slicer drift over 1000 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UiK871ht2uPFBHtMbxy3wD Signed-off-by: Stan Grams <sjg@haxx.space>
trx-rs splits radio hardware access from user-facing interfaces so you can run
rig control, SDR DSP, decoding, audio streaming, and web access as separate,
composable pieces.
| Backends | Yaesu FT-817, Yaesu FT-450D, SoapySDR |
| Frontends | Web UI, rigctl-compatible TCP, JSON-over-TCP |
| Decoders | AIS, APRS, CW, FT8, RDS, VDES, WSPR |
| Audio | Opus streaming between server, client, and browser |
Quick Start
1. Install dependencies
Debian / Ubuntu
sudo apt install build-essential pkg-config cmake libopus-dev libasound2-dev
# Optional — SDR support
sudo apt install libsoapysdr-dev
Fedora
sudo dnf install gcc pkg-config cmake opus-devel alsa-lib-devel
# Optional — SDR support
sudo dnf install SoapySDR-devel
Arch Linux
sudo pacman -S base-devel pkgconf cmake opus alsa-lib
# Optional — SDR support
sudo pacman -S soapysdr
macOS (Homebrew)
brew install cmake opus
# Optional — SDR support
brew install soapysdr
See Build Requirements in the wiki for details on each library.
Note:
cmakeis required even when a system Opus library is installed. Theaudiopus_syscrate probes for Opus viapkg-config; if it is not found (orpkg-configis unavailable), it falls back to compiling a vendored copy of Opus with CMake. A missingcmaketherefore fails the build withis cmake not installed?rather than a missing-Opus error.
2. Build
cargo build --release
Build without SDR support: cargo build --release --no-default-features
3. Configure
Run the interactive setup wizard to generate config files for your station:
./target/release/trx-configurator
The wizard walks you through rig selection, serial port detection, audio
settings, and frontend options, then writes trx-server.toml and
trx-client.toml.
Alternatively, copy trx-rs.toml.example — a commented example covering every
setting — and edit it by hand:
cp trx-rs.toml.example trx-rs.toml
./target/release/trx-server --check-config --config trx-rs.toml
--check-config reports everything wrong with a config without starting
anything. --print-config prints the same settings without comments.
4. Run
./target/release/trx-server --config trx-server.toml
./target/release/trx-client --config trx-client.toml
A single trx-rs.toml can configure both: the server reads its [trx-server]
section and the client reads [trx-client].
Open the configured HTTP frontend address in a browser (default http://localhost:8080).
How It Works
graph TD
SDR1["SDR #1"] & SDR2["SDR #2"] <-->|USB| S1["trx-server A"]
SDR3["SDR #3"] & FT817["FT-817"] <-->|USB / serial| S2["trx-server B"]
S1 <-->|"JSON-TCP :4530"| C1["trx-client"]
S1 -->|"Opus-TCP per rig"| C1
S2 <-->|"JSON-TCP :4530"| C1
S2 -->|"Opus-TCP per rig"| C1
C1 <-->|internal channels| F1["Web UI :8080"]
C1 <-->|internal channels| F2["rigctl :4532"]
Each trx-server owns one or more rigs and runs DSP, decoding, and audio capture locally.
A trx-client connects to any number of servers over TCP and exposes them through
a unified set of frontends.
Documentation
| Resource | Description |
|---|---|
| User Manual | Configuration, features, and usage |
| Architecture | System design, crate layout, data flow, and internals |
| Optimization Guidelines | Performance guidelines for the real-time DSP pipeline |
| Planned Features | Roadmap and design notes |
| Contributing | Commit conventions, workflow, and code style |
License
GPL-2.0-or-later. See LICENSES for the full license text and
bundled third-party license files. Bundled third-party components retain their
original licenses: Leaflet is BSD-2-Clause, DSEG is OFL-1.1, and opus-decoder
is MIT. The APRS symbol sprites come from
hessu/aprs-symbols; their per-symbol
copyright status is catalogued in
LICENSES/LicenseRef-APRS-Symbols.txt.
