Commit Graph
29 Commits
Author SHA1 Message Date
ClaudeandStan Grams 5d6b9a4d94 [fix](trx-rds): reduce false decodes with OSD cost ceiling and PI consistency
Add OSD_MAX_FLIP_COST (0.45) to reject OSD corrections where the flipped
bits had high confidence — a strong false-decode indicator. Genuine errors
at 9-10 dB SNR have cost ≲0.3; noise matches cost 0.6-1.2.

Add PI consistency gate in process_group: reject groups whose Block A PI
differs from the candidate's established PI, preventing noise from
polluting accumulated PS/RT/PTYN text fields.

Raise PI_ACC_THRESHOLD from 2 to 3 for stronger PI voting.

Extend noise rejection test from 0.5s to 2s. Add 9 dB SNR sensitivity
test (all 16 tests pass).

https://claude.ai/code/session_01GYax4BQ9ZV9ZZfMjmmzgbh
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 07:20:22 +01:00
ClaudeandStan Grams 54b1f20ea4 [fix](trx-wspr): reduce false positives in WSPR decoder
The WSPR decoder was producing many false positive decodes due to
several overly permissive thresholds that allowed noise to reach the
Fano sequential decoder, which could then converge on random data:

- Raise normalized sync score threshold from 0.10 to 0.20 to reject
  noise candidates before attempting expensive Fano decoding
- Add minimum SNR gate (-20 dB) to skip candidates where the signal
  is indistinguishable from noise
- Return and check the Fano decoder's cumulative path metric, rejecting
  low-confidence decodes (metric < 20) that are likely noise artifacts
- Raise RMS threshold from 0.0005 to 0.005 to reject near-silent audio
- Add near-frequency deduplication to prevent the same signal decoded
  at slightly different (freq, dt) offsets from appearing multiple times
- Add noise-only regression test to verify no false positives on random
  input

https://claude.ai/code/session_01HTBoEsD1hp99TiYMSaHMVG
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 02:06:56 +01:00
ClaudeandStan Grams 06796342e7 [fix](trx-rds): apply RRC pulse shaping in test signal generator
The `chips_to_rds_signal` test helper was generating rectangular chip
pulses, but the receiver expects RRC-shaped transmit pulses so that
RRC(tx) × RRC(rx) = raised cosine with zero ISI. The rectangular
pulses caused ISI that drifted the symbol clock sampling point,
consistently skipping PS segment 2 in the end-to-end test.

Replace rectangular pulses with an impulse train convolved with the
same RRC taps used by the receiver. All 15 tests now pass including
`end_to_end_clean_signal_decodes_ps`.

https://claude.ai/code/session_01N2UcGaLDzYiM3gNrZ6kFBj
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-27 01:53:29 +01:00
ClaudeandStan Grams 8e6623b39e [fix](trx-rs): use per-message rig_id for map marker tagging
The map module was tagging all decode markers (APRS, AIS, VDES,
FT8/FT4/FT2/WSPR locators) with the global rig picker's active rig
instead of the actual source rig. This made the map's own rig filter
dropdown ineffective in multi-rig setups.

- Add rig_id field to all decode message structs (AisMessage,
  VdesMessage, AprsPacket, CwEvent, Ft8Message, WsprMessage)
- Set rig_id on messages in audio_client before broadcasting, using
  the actual rig connection identifier
- Update history collector to prefer message rig_id over the global
  active rig fallback
- Pass rig_id through plugin normalize functions (AIS, APRS, VDES,
  HF-APRS) so it reaches the map add functions
- Update all map marker functions (aprsMapAddStation, aisMapAddVessel,
  vdesMapAddPoint, mapAddLocator) to use the message's rig_id with
  fallback to the global picker for backward compatibility

https://claude.ai/code/session_015gC7axHk2jmp7HbFPdbivN
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 16:29:53 +01:00
ClaudeandStan Grams a63f27971d fix(trx-server): remove unused RigSdr import in rig_task.rs
Also run cargo fmt to fix formatting issues across trx-server,
trx-frontend-http, and trx-configurator.

https://claude.ai/code/session_01RsHUyVz2wjQjsEsxJo5owt
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 14:01:44 +01:00
ClaudeandStan Grams c8f33b8939 [refactor](trx-rs): remove shared-library plugin system
Drop the plugin loading infrastructure (libloading-based dynamic .so/.dylib/.dll
loading) from both trx-server and trx-client. The feature was unused and posed an
unnecessary security risk by executing arbitrary native code from disk.

Removed:
- src/trx-app/src/plugins.rs (plugin discovery, validation, FFI registration)
- examples/trx-plugin-example/ (cdylib example plugin)
- libloading dependency from trx-app
- load_backend_plugins / load_frontend_plugins calls from server and client
- Plugin documentation from README.md and CLAUDE.md

https://claude.ai/code/session_01DTEUpz3XPUeWmz74NeaFgb
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 12:44:56 +01:00
ClaudeandStan Grams 9692e31c8c [docs](trx-rs): update Improvement Areas wiki — mark resolved items
Update the Improvement Areas audit document to reflect all fixes
implemented across PRs #58, #59, and #60. 22 items now marked as
resolved; 5 remaining items reorganized by priority.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 11:42:25 +01:00
ClaudeandStan Grams 3e0169b91f [refactor](trx-backend-soapysdr): implement RigSdr trait for SoapySdrRig
Move 13 SDR-specific methods from impl RigCat into a new impl RigSdr
block. Add as_sdr()/as_sdr_ref() overrides returning Some(self) so the
SDR extension is accessible via the RigCat trait object.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 08:47:17 +01:00
ClaudeandStan Grams 748d26f47d [refactor](trx-server): route SDR commands through RigCat::as_sdr()
Update all SDR command handlers in rig_task to access SDR methods via
ctx.rig.as_sdr() instead of calling them directly on RigCat. Query-only
SDR operations (filter_state, get_spectrum, get_vchan_rds) use
as_sdr_ref(). Non-SDR rigs now get proper not_supported errors.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 08:47:17 +01:00
ClaudeandStan Grams 74749bc6de [refactor](trx-core): split RigCat into base trait + RigSdr extension
Extract 13 SDR-specific methods (set_center_freq, set_bandwidth,
set_sdr_gain/lna/agc/squelch/nb, set_wfm_*, filter_state, get_spectrum,
get_vchan_rds) into a new RigSdr trait. RigCat retains core CAT
operations and gains as_sdr()/as_sdr_ref() for optional SDR access.

Non-SDR backends no longer see SDR methods in their trait impl.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 08:47:17 +01:00
ClaudeandStan Grams aa3ed81786 [fix](trx-server): replace all history mutex .expect() with poison recovery
Replace 25 .expect("X history mutex poisoned") calls in DecoderHistories
with .unwrap_or_else(|e| e.into_inner()) to gracefully recover from
poisoned locks instead of crashing the server.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 08:47:17 +01:00
ClaudeandStan Grams 337cb72974 [refactor](trx-server): use state data constructors for pub(crate) fields
Migrate ready_data_from_state and transmitting_data_from_state to use
the new ReadyStateData::new() and TransmittingStateData::new()
constructors instead of direct struct field initialization.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams 8ce2e4ed08 [refactor](trx-core): restrict state data fields to pub(crate) with accessors
Make ReadyStateData and TransmittingStateData fields pub(crate) to
prevent external mutation that could bypass state machine invariants.
Add constructors and getter methods for external consumers.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams 71c23f0895 [refactor](trx-frontend-http): pre-allocate spectrum encoding output
Replace format! with pre-allocated String::with_capacity for spectrum
frame encoding, reducing allocation overhead in the hot SSE path.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams adf65ae56d [fix](trx-frontend-http): warn when auth enabled but cookie_secure is false
Log a startup warning when HTTP auth is active but cookie_secure remains
false, alerting operators that session cookies will be sent unencrypted.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams 9b6c845fa8 [fix](trx-frontend-http): recover from poisoned locks and document ordering
Replace all .unwrap() on RwLock/Mutex acquisitions with
.unwrap_or_else(|e| e.into_inner()) to gracefully recover from poisoned
locks instead of panicking. Add lock ordering documentation to the
module header to prevent deadlocks.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams c5320ca2fb [refactor](trx-server): add AtomicUsize counter and recover from poisoned locks
Add an AtomicUsize total_count field to DecoderHistories, maintained by
record/prune/clear methods, so estimated_total_count() avoids 9 separate
mutex acquisitions. Also replace audio ring buffer .unwrap() calls with
.unwrap_or_else(|e| e.into_inner()) to recover from poisoned locks.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:29:55 +01:00
ClaudeandStan Grams 74eb755858 [chore](trx-rs): update Cargo.lock
https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 22ad90b2ba [fix](trx-server): release mutex before serialization in flush_all
Clone history data out under the lock, then drop the guard before
calling save_key, so serialization never blocks concurrent readers.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams d7c8eed44f [feat](trx-frontend-http): add per-IP login rate limiting
Implement LoginRateLimiter that tracks failed login attempts per IP,
enforcing a cooldown (10 attempts per 60s window) to mitigate brute-
force attacks on the /auth/login endpoint.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams c299e9a2d2 [fix](trx-server): truncate raw JSON in error logs to 128 chars
Prevent potential information disclosure by truncating raw client input
in log messages instead of logging the full payload.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams edf16b63d6 [fix](trx-core): log invalid state machine transitions
Add debug-level tracing for rejected state transitions instead of
silently returning false, aiding debugging of unexpected rig behavior.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 9cf66fc04a [fix](trx-app): add plugin loading validation and disable toggle
Reject world-writable plugin files on Unix to prevent loading tampered
libraries. Add TRX_PLUGINS_DISABLED env var to disable plugin loading
entirely.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams cbe22bd7b6 [refactor](trx-frontend-http): replace string-level JSON splice with serde(flatten)
Use a StateWithMeta wrapper struct with #[serde(flatten)] for merging
rig state with frontend meta, replacing the manual string manipulation.
Also add Serialize derive and skip_serializing_if to FrontendMeta.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 635a1214d0 [refactor](trx-client): switch VChanAudioCmd to bounded channels (cap 256)
Replace unbounded_channel with channel(256) for VChanAudioCmd to prevent
unlimited memory accumulation under backpressure. Use try_send in
synchronous contexts.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams c3abc5ff5b [refactor](trx-frontend): add DecodeHistory type alias and use bounded channels
Introduce DecodeHistory<T> alias for the repeated
Arc<Mutex<VecDeque<(Instant, Option<String>, T)>>> pattern (9 fields).
Also switch VChanAudioCmd channel senders from UnboundedSender to Sender
to prevent unbounded memory growth under backpressure.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 449b877694 [refactor](trx-ftx): use HashSet for candidate deduplication
Replace Vec::contains() with HashSet::insert() for O(1) dedup lookups
instead of O(n), significantly reducing comparisons during decode.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 99d95c8eb6 [refactor](trx-frontend-rigctl): adapt to Cow-returning mode_to_string
Update rig_mode_to_str to call .into_owned() on the new Cow return.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00
ClaudeandStan Grams 019f12e3fc [refactor](trx-protocol): return Cow<'static, str> from mode_to_string
Eliminates per-call String allocations for standard modes by returning
borrowed static strings. Only the Other variant allocates.

https://claude.ai/code/session_01XzurkeuUmamBuhQwxVy7T4
Signed-off-by: Claude <noreply@anthropic.com>
2026-03-26 07:02:46 +01:00