Commit Graph

90 Commits

Author SHA1 Message Date
sjg 0a7195c93c fix(trx-server): add shutdown signal to audio capture/playback threads
Pass `watch::Receiver<bool>` into `run_capture` and `run_playback` so
both threads check for shutdown at the top of their outer recovery loop
and inner monitoring loop. Without this, restarting the process (e.g.
via systemd after an ALSA error) left the old threads stalled forever
while new ones were created alongside them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 01:15:57 +01:00
sjg 600257a7c4 [perf](trx-backend-soapysdr): replace FIR with FFT overlap-save via rustfft
Replace the per-sample ring-buffer FIR convolution with block-level
overlap-save convolution using rustfft. For a block of M samples and
N taps the old approach costs O(N·M); the new one costs O(M log M),
with rustfft using SIMD (AVX2/SSE4) internally.

Key changes:
- Add rustfft = "6" dependency
- Add BlockFirFilter: overlap-save filter with pre-computed H(f) and
  a single forward+inverse FFT pair per block (no per-sample multiply)
- ChannelDsp.process_block() now:
  1. Batch-mixes entire block to baseband in one vectorisable loop
  2. Applies BlockFirFilter to I and Q (one FFT pair each)
  3. Decimates and demodulates as before
- Keep the old FirFilter for unit tests (sample-by-sample interface)
- Add BlockFirFilter unit tests (DC passthrough, length preservation)
- IQ_BLOCK_SIZE promoted to pub const for use in filter sizing

For the default config (4096-sample blocks, 64 taps, decim=40):
  Old: ~262144 multiply-adds per FIR × 2 components = ~524k per block
  New: ~2 × (3 × 8192 × log2(8192)) ops, all SIMD-vectorised by rustfft

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 01:05:49 +01:00
sjg efd2c53754 [fix](trx-server): auto-generate rig IDs when not specified in config
When rigs are configured without explicit IDs in TOML, they now
receive auto-generated IDs based on model name and index (e.g.,
"ft817_0", "soapysdr_1"). Previously, the default empty ID prevented
auto-generation from triggering.

Changes:
- Set RigInstanceConfig default id to empty string (was "default")
- This allows resolved_rigs() auto-generation to trigger for all rigs
- Legacy single-rig path still gets explicit "default" ID
- Auto-generated IDs now appear in HTTP API /rigs endpoint
- Rig picker now displays auto-IDs correctly

The fix ensures that rigs without explicit ids get proper identifiers
that appear in the protocol and frontend selectors.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:24:15 +01:00
sjg 12d967154c [fix](trx-backend-soapysdr): improve device initialization diagnostics
Enhance error handling and messaging for SoapySDR device initialization:
- Add fallback logic to try empty args if device-specific args fail
- Provide detailed multi-line error messages with troubleshooting guidance
- Include suggestions to check SoapySDR plugins and run SoapySDRUtil --probe
- Clarify device lifetime management in struct
- Document why actual streaming not yet implemented (soapysdr 0.3 limitations)
- Note that sdr 0.3 requires FFI or upgrade for streaming support

This helps users diagnose device initialization failures and understand
the current architectural limitations.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-27 00:18:07 +01:00
sjg 6ed3f96155 [refactor](trx-server): access soapysdr through trx-backend facade
Remove direct dependency on trx-backend-soapysdr from server Cargo.toml.
Re-export SoapySdrRig from trx-backend instead so server accesses it
through the proper facade. This keeps sub-backends internal to trx-backend
and maintains proper module organization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:12:38 +01:00
sjg c3ea605924 [refactor](trx-backend-soapysdr): remove feature gating, require real hardware
Drop optional feature gating - SoapySDR hardware support is now required.
Make soapysdr a required dependency in Cargo.toml instead of optional.
Update server to always enable soapysdr backend and its dependencies.
Simplify initialization to always use RealIqSource instead of conditional
fallback to MockIqSource.

This assumes SoapySDR library is installed on the system.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 00:09:10 +01:00
sjg 1de15cba7e [feat](trx-server): auto-generate rig IDs from model name if not specified
Allow rigs to have empty IDs in config; auto-generate from backend model
name with numeric suffix (e.g., 'ft817_0', 'ft817_1', 'soapysdr_0').
This makes config more concise when using multiple instances of the same
model without explicit ID assignment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:59:49 +01:00
sjg 5a6cac470a [feat](trx-backend-soapysdr): implement real hardware IQ source with feature gating
Add RealIqSource that connects to actual SoapySDR devices when soapysdr-sys
feature is enabled. Implements proper device initialization, frequency/bandwidth
configuration, gain control, and RX stream management.

When soapysdr-sys feature is disabled (default), falls back to MockIqSource
for testing. Update feature flags in Cargo.toml dependencies to support both
real hardware and mock operation.

- Device initialization with proper error handling
- RX frequency, bandwidth, and gain configuration
- IQ sample streaming via broadcast channel
- Proper resource cleanup via Drop trait
- Throttled MockIqSource to prevent 100% CPU

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:58:12 +01:00
sjg a337c0ccea [fix](trx-backend-soapysdr): throttle MockIqSource to avoid 100% CPU load
Add sleep proportional to block duration in iq_read_loop to simulate
real hardware timing. MockIqSource immediately returns samples without
any delay, causing busy-looping. Throttling prevents excessive CPU usage
when using the mock source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:48:41 +01:00
sjg 8297af8302 [feat](trx-server): add configurable display name for rigs
Add optional `name` field to RigInstanceConfig to allow long-form rig names
(e.g., "HF Transceiver"). The name defaults to rig id if not configured.
Add display_name() method to get display name with fallback to id.
Propagate display_name through RigHandle to listener for GetRigs response.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:46:02 +01:00
sjg 68944f7d78 [fix](trx-backend): fix clippy warnings and improve code style
Fix useless vec! allocation in demod tests, improve loop iterator usage,
and reformat code for better readability across dummy and soapysdr
backends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:33:40 +01:00
sjg f8a683b312 [style](trx-server): improve code formatting and remove unused imports
Reformat assertions, multi-line function calls, and error handling for
better readability. Remove unused soapysdr feature import in main.rs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:33:37 +01:00
sjg 30ac7aab1e [feat](trx-server): add soapysdr as default feature
Enable SoapySDR backend by default to make multi-device setups work out
of the box without requiring explicit feature flags.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:33:34 +01:00
sjg 47cff3f927 feat(multi-rig): auto-discover per-rig audio ports via GetRigs 2026-02-25 23:31:38 +01:00
sjg 3b98c8b7b5 [feat](trx-server): dispatch SetBandwidth and SetFirTaps in rig_task
Handle SetBandwidth(hz) and SetFirTaps(taps) in process_command:
call the RigCat methods, update filter state in-place, broadcast
the updated state, and return the new snapshot.

Fix missing capability fields in listener.rs test helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-25 20:25:00 +01:00
sjg dc33d1e841 [feat](trx-backend): set capability flags; add SDR filter state; add UC-08 tests
Set tx/tx_limit/vfo_switch/filter_controls/signal_meter on all backends:
- FT-817, FT-450D, dummy: tx=true, tx_limit=true, vfo_switch=true,
  filter_controls=false, signal_meter=true
- SoapySDR: tx=false, tx_limit=false, vfo_switch=false,
  filter_controls=true, signal_meter=true

SoapySDR backend now stores bandwidth_hz and fir_taps fields; overrides
set_bandwidth, set_fir_taps, and filter_state on RigCat to expose live
DSP state in the snapshot.

Add UC-08 unit tests on dummy backend asserting tx capabilities present
and filter_controls absent, and that filter_state returns None.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-25 20:24:55 +01:00
sjg 0b8c408c17 [feat](trx-server): implement multi-rig support
Enable N simultaneous rig backends in one server process:

- rig_handle.rs: new RigHandle { rig_id, rig_tx, state_rx } thin struct
- config.rs: RigInstanceConfig, rigs: Vec<RigInstanceConfig> in ServerConfig,
  resolved_rigs() (synthesises legacy flat fields as id="default"),
  validate() checks unique rig IDs and audio ports; MR-08 config tests
- audio.rs: replace four OnceLock<Mutex<VecDeque>> statics with
  DecoderHistories { aprs, ft8, wspr } Arc struct; decoder/listener
  functions now take Arc<DecoderHistories> for per-rig isolation
- rig_task.rs: add rig_id + histories: Arc<DecoderHistories> to
  RigTaskConfig; clear_*_history calls use ctx.histories instance methods
- listener.rs: run_listener takes Arc<HashMap<String, RigHandle>> +
  default_rig_id; routes envelope.rig_id to correct rig; GetRigs fast
  path aggregates all rig states; all responses include rig_id field
- main.rs: loop over resolved_rigs(); spawn_rig_audio_stack() helper;
  builds Arc<HashMap<String, RigHandle>> passed to run_listener

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-25 08:04:19 +01:00
sjg 319e935d97 [feat](trx-server): wire SoapySDR backend and AudioSource into server startup
- Call validate_sdr() at startup and abort on errors (SDR.md §11)
- Build SoapySdrRig with full [[sdr.channels]] config when access_type is
  "sdr"; subscribe to its primary-channel PCM sender before handing the
  pre-built rig to the rig task via RigTaskConfig.prebuilt_rig
- Skip cpal capture when an SDR audio source is available; bridge the
  SdrPipeline PCM broadcast into pcm_tx so all decoder tasks are unchanged
- Add trx-backend-soapysdr optional dep and soapysdr feature to trx-server
- Add prebuilt_rig field to RigTaskConfig so rig_task skips the registry
  factory when a pre-built rig is supplied by main

Marks SDR-08 complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 22:27:15 +01:00
sjg dc3fd63a83 [test](trx-server): add SDR config validation unit tests
Adds 12 unit tests for ServerConfig::validate_sdr() covering: minimal
valid config, non-SDR skip, empty/missing args, zero sample_rate,
channel IF out-of-range (positive, negative, exactly at Nyquist), dual
stream_opus, tx_enabled with SDR backend, duplicate decoder, and
multiple simultaneous errors. Marks SDR-11 complete in SDR.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 22:12:27 +01:00
sjg 1be5b3d4c2 [feat](trx-backend-soapysdr): implement SoapySdrRig with AudioSource and DSP wiring
Replace the stub SoapySdrRig with a full implementation: wire up SdrPipeline
from dsp.rs, implement AudioSource::subscribe_pcm on the primary channel,
add gain control (manual/auto with fallback warning), and track primary
channel freq/mode so set_freq/set_mode update the live DSP pipeline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 21:57:39 +01:00
sjg 1353e2a29b [test](trx-backend-soapysdr): add demodulator unit tests
Add 9 unit tests covering all demodulators in demod.rs: USB/LSB/Passthrough
real-part extraction, AM DC removal and varying-envelope, FM tone frequency
and silence, CW peak normalisation, mode mapping, and empty-input safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 21:51:48 +01:00
sjg 5090ab71b3 [feat](trx-backend-soapysdr): implement IQ DSP pipeline (mixer/FIR/decimate/demod)
Add dsp.rs with IqSource trait abstraction, MockIqSource, windowed-sinc
FIR low-pass filter, ChannelDsp (mixer/decimate/demod/frame-accumulator),
and SdrPipeline which spawns a dedicated IQ read thread. No soapysdr crate
dependency; the real device will be wired in SDR-07.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 21:34:09 +01:00
sjg db6dff304e [feat](trx-backend-soapysdr): implement demodulators (USB/LSB/AM/FM/CW)
Add demod.rs with Demodulator enum and per-mode demodulate() implementations:
USB/Passthrough (real part), LSB (real part, IF negated upstream), AM
(envelope, DC removal, peak normalisation), FM/WFM (quadrature discriminator
scaled by 1/π), CW (magnitude envelope, peak normalised). Marks SDR-05 done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 21:29:27 +01:00
sjg 2ee217a1a1 [feat](trx-backend-soapysdr): add crate scaffold with RigCat stub
Introduces the trx-backend-soapysdr crate with a compilable SoapySdrRig
struct that satisfies the Rig + RigCat trait bounds.  RX methods
(get_status, set_freq, set_mode, get_signal_strength) are implemented;
TX-only methods return RigError::not_supported.  as_audio_source returns
None for now (overridden in SDR-07).  Wires the crate into the workspace
and trx-backend (feature "soapysdr"), and fixes the non-exhaustive match
on RigAccess::Sdr in trx-server main.rs and rig_task.rs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 19:51:52 +01:00
sjg b00f054bd4 [feat](trx-server): add SdrConfig structs and SDR access config validation
Add SdrConfig, SdrGainConfig, and SdrChannelConfig structs to config.rs,
extend AccessConfig with an args field for the "sdr" access type, wire
SdrConfig into ServerConfig, and implement validate_sdr() with all
startup validation rules from SDR.md §11. Marks SDR-03 complete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 19:16:27 +01:00
sjg bf19ffb38d [feat](trx-backend): add RigAccess::Sdr variant and soapysdr feature stub
Add `RigAccess::Sdr { args: String }` to the backend access enum, register
a feature-gated `soapysdr` factory stub in `register_builtin_backends_on`,
handle the new variant in all existing `match` arms, and add the `soapysdr`
feature flag (no dep yet; implementation lands in SDR-04). Mark SDR-02 done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-24 19:07:00 +01:00
sjg b9005acffd [refactor](decoders): extract decoder logging into trx-decode-log crate
Move DecoderLoggers and DecodeLogsConfig out of trx-server into a
dedicated src/decoders/trx-decode-log crate, giving file logging the
same standalone crate treatment as the four decoder crates.

- src/decoders/trx-decode-log/ (new — DecodeLogsConfig + DecoderLoggers)
- trx-server/config.rs: re-exports DecodeLogsConfig from trx-decode-log
  so ServerConfig field references and all tests compile unchanged
- trx-server: drop decode_logs module, use trx_decode_log directly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:36:04 +01:00
sjg f4b92a0f20 [refactor](decoders): extract CW decoder into trx-cw crate
Move the Goertzel-based CW decoder out of trx-server::decode::cw into
a dedicated src/decoders/trx-cw crate, matching the layout of trx-aprs,
trx-ft8, and trx-wspr. The decode module is now empty and removed.

- src/decoders/trx-cw/ (new — Goertzel + Morse decoder)
- trx-server: drop decode module entirely, use trx_cw::CwDecoder
- CwEvent stays in trx-core (mirrors AprsPacket / Ft8Message / WsprMessage)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:33:02 +01:00
sjg 3ebd185a7e [refactor](decoders): consolidate decoder crates under src/decoders/
Move trx-ft8 and trx-wspr into src/decoders/ alongside a new trx-aprs
crate that extracts the Bell 202/AX.25 decoder from trx-server, giving
all three modems a consistent crate-per-decoder layout.

- src/decoders/trx-ft8/  (moved from src/trx-ft8/)
- src/decoders/trx-wspr/ (moved from src/trx-wspr/)
- src/decoders/trx-aprs/ (new — Bell 202 AFSK + AX.25/APRS decoder)
- trx-ft8/build.rs: fix external/ft8_lib relative path after move
- trx-server: drop decode::aprs module, use trx_aprs::AprsDecoder
- AprsPacket stays in trx-core (mirrors Ft8Message / WsprMessage)
- Workspace Cargo.toml updated with new member paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:30:04 +01:00
sjg 0d6a35a933 [feat](trx-server): add APRS-IS IGate uplink (aprs.fi integration)
Forwards CRC-valid RF APRS packets to APRS-IS via plain TCP using the
TNC2 line format, making them visible on aprs.fi and other APRS-IS
consumers. Mirrors the pskreporter module in structure.

- New aprsfi.rs: IGate task with reconnect loop (exponential backoff
  1s→60s), login/logresp, 60s keepalive, 60s stats, passcode
  auto-computation from callsign (standard APRS hash algorithm)
- config.rs: AprsFiConfig struct with enabled/host/port/passcode fields
  and validation; default host rotate.aprs.net:14580
- main.rs: mod aprsfi; spawn task inside audio block when aprsfi.enabled
- trx-server.toml.example, CONFIGURATION.md: document [aprsfi] section
- Remove APRSFI_IMPLEMENTATION.rs planning artifact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:23:35 +01:00
sjg e12a3dfa4f [fix](trx-server): re-enumerate audio device on each stream recovery cycle
After ALSA POLLERR the existing cpal Device handle can be stale, causing
repeated stream build failures with no path to recovery. Re-acquire host
and device inside the outer recreation loop so each attempt gets a fresh
handle. Device-not-found is now a warn+retry rather than a fatal error,
allowing recovery from transient USB audio device disappearances.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:59:29 +01:00
sjg 1a282f5ec6 [fix](trx-server): prevent audio thread crash on ALSA EPIPE
CPAL error callbacks can fire millions of times per second on ALSA
EPIPE (errno -32). Previously each invocation did a string allocation
and mutex lock, saturating CPU and eventually crashing the server.

- Use atomic swap in both input and output error callbacks so only the
  first error fires the expensive log+notify path; all subsequent hits
  cost a single atomic op and return immediately.
- Replace stream.play()? with explicit error handling in run_playback
  so a play failure triggers stream recreation instead of permanently
  terminating the playback thread.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <stanislawgrams@gmail.com>
2026-02-23 13:24:55 +01:00
sjg 66baa73fa9 [test](trx-server): update port assertions for 4530/4531 defaults
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <stanislawgrams@gmail.com>
2026-02-23 12:39:49 +01:00
sjg 5dc2b923d1 [chore](trx-server): update default ports to 4530/4531
Change JSON TCP listener default from 4532 to 4530 and audio
streaming default from 4533 to 4531.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <stanislawgrams@gmail.com>
2026-02-23 12:12:04 +01:00
sjg 1d0db79ca3 [feat](trx-rs): show server/client build info in HTTP footer
Add compile-time build dates for trx-server and trx-frontend-http, propagate server build metadata through rig state/snapshot, and render both versions + build dates in the HTTP footer.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 11:06:13 +01:00
sjg 8a4e9f5ed3 [feat](trx-server): add optional per-decoder log files
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 10:43:52 +01:00
sjg 86ca1a60fb [fix](trx-rs): harden hamlib rigctl compatibility
Improve rigctl interoperability with hamlib/WSJT-X and stabilize FT-817 PTT handling.\n\n- support extended '+' command replies\n- accept decimal and MHz-style frequency inputs\n- retry set_freq rounded to 10 Hz on CAT alignment errors\n- add compatibility handling for get_level probes\n- broaden PTT command parsing and aliases\n- derive PTT capability dynamically from snapshot data\n- improve dump_state/dump_caps compatibility behavior\n- move temporary rigctl diagnostics to debug level\n- make FT-817 set_ptt more reliable with unlock/clear and double-send\n\nCo-authored-by: OpenAI Codex <codex@openai.com>

Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 01:30:04 +01:00
sjg 81890b15a8 [fix](trx-core): expose rig min tuning step and align UI tuning
Add min_freq_step_hz to RigCapabilities, set backend values, and make HTTP frontend parse suffix-less frequency input using the selected unit while snapping set/jog frequencies to rig step granularity.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 00:17:16 +01:00
sjg a7719bd7a9 [fix](trx-server): make ALSA stream recovery reliable
Signal backend stream callback failures directly to capture/playback loops and strengthen cross-thread failure flag visibility so recurring POLLERR conditions consistently trigger stream recreation.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 00:12:12 +01:00
sjg a1d56dc1d0 [fix](trx-server): make PSK Reporter activation diagnosable
Require a receiver locator source when PSK Reporter is enabled,
show inactive reason in status text, and add periodic uplink runtime
counters (received/sent/skipped/errors).

This makes missing-spot issues visible instead of silently dropping
all decode events.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-13 00:05:54 +01:00
sjg d085f96838 [fix](trx-server): auto-recover audio streams and harden frontend reconnect
Implement ALSA/CPAL stream auto-recovery by recreating input/output
streams after backend callback failures with bounded retry delay.

Also improve HTTP frontend resilience by polling /status on reconnect
and after SSE errors to refresh snapshot state after broken pipes.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 23:59:39 +01:00
sjg 273283708e [fix](trx-server): preserve PSK Reporter status in snapshots
Pass pskreporter_status through RigTaskConfig and apply it to rig_task
state initialization so snapshot updates keep the About-tab value.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 23:50:41 +01:00
sjg e243f0e4cc [fix](trx-server): include legacy home config path and coords in print-config
Fix server config discovery to include ~/.trx-server.toml and update
example/print-config output to explicitly include general latitude and
longitude fields.

Also update config docs and add a test for the legacy search path.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 23:47:36 +01:00
sjg 28dab2d00f [feat](trx-server): expose PSK Reporter status in About
Add pskreporter_status to shared rig snapshots and display it in the
HTTP frontend About tab.

Also include audio stream error log throttling to avoid repetitive ALSA
error flooding in backend logs.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 23:37:15 +01:00
sjg db792cc9d8 [feat](trx-server): add PSK Reporter uplink for FT8/WSPR
Add a PSK Reporter uploader task that subscribes to decoded FT8/WSPR
messages and sends spots over UDP when [pskreporter].enabled is true.

Include new [pskreporter] server config options and example config docs,
and hardcode software identification as 'trx-server v<version> by SP2SJG'.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 23:31:04 +01:00
sjg 091810c6cb [feat](trx-server): integrate wsprd-based WSPR decoding
Add a new trx-wspr crate that wraps wsprd slot decoding and parsed
results, wire it into the server audio pipeline, and emit WSPR decode
events to clients.

Also add frontend event routing for WSPR decode messages and temporary
rendering in the FT8 table until a dedicated WSPR panel is introduced.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 22:47:37 +01:00
sjg 63ba6882cd [feat](trx-core): add WSPR decode plumbing across stack
Introduce WSPR command/state/protocol/transport wiring and integrate lifecycle, history, and frontend API paths mirroring the FT8 architecture.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 22:38:05 +01:00
sjg 4b34a39745 [refactor](workspace): complete remaining architecture phases
Bundle all pending repository updates, including plugin context de-globalization, runtime hardening, config validation, boundary tests, and supporting docs/scripts.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 22:27:36 +01:00
sjg 144afbae8e [fix](trx-server): validate config semantics at startup
Add semantic validate() checks for server/client config models and fail fast on invalid ranges, field combinations, and auth token values before runtime startup.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 22:05:54 +01:00
sjg 3cc36d9c24 [fix](trx-client): harden json tcp parsing and io limits
Add typed remote endpoint parsing (including bracketed IPv6), bounded JSON line reads, and read/write/request timeouts across client/server JSON-TCP paths.

Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
2026-02-12 21:57:24 +01:00