Estimate Co-Channel Interference (CCI) from pilot tone quadrature
leakage and coherence degradation. Estimate Adjacent Channel
Interference (ACI) from CMA equalizer tap deviation from identity.
Both metrics (0-100 scale) are surfaced through RigFilterState and
displayed as colour-coded bars in the WFM control panel.
The RDS decoder quality parameter is now adaptively penalised when
CCI/ACI levels are elevated, reducing block-error rate under
interference conditions.
https://claude.ai/code/session_016EKzep42RCvE4GxvvRaCwu
Signed-off-by: Claude <noreply@anthropic.com>
If the incumbent candidate has not produced a state update in 2 seconds,
clear its score advantage so any candidate can take over. This prevents
the decoder from "freezing" on stale data when the incumbent's timing or
carrier tracking degrades — particularly important for dynamic PS where
the station rotates program service text.
Signed-off-by: Claude <noreply@anthropic.com>
https://claude.ai/code/session_0136sPdLUpYgvskrzbi2Epkv
Signed-off-by: Claude <noreply@anthropic.com>
Fix Gardner TED loop structure bug (type-3 → type-2 PLL) and tune
gains for ζ=0.707 damping. Add adaptive Costas loop bandwidth that
narrows from ~22 Hz to ~5.5 Hz once carrier is locked, reducing phase
noise at low SNR. Narrow RRC matched filter (α=0.30, span=10 chips)
for ~0.6 dB noise BW gain. Add OSD(4) for locked-mode blocks after
first successful group, and increase PI accumulation threshold to 8.
TED bug details: the original code used `clock_inc += correction`
which added the full integrator value at every chip, creating an
extra integration (type-3 loop) that is unconditionally unstable.
Fixed to `clock_inc = nominal + correction` (standard type-2 PLL).
Gains retuned: Kp=4e-4, Ki=8e-8 for ζ≈0.707 and loop BW≈0.11 Hz.
Signed-off-by: Claude <noreply@anthropic.com>
https://claude.ai/code/session_0136sPdLUpYgvskrzbi2Epkv
Signed-off-by: Claude <noreply@anthropic.com>
The Auto button now toggles between Off and Auto states. Default is Off.
First click sets squelch to noise floor + 6 dB; second click resets to
Open (0%). Button shows active state with green highlight when engaged.
https://claude.ai/code/session_01TDQyrZiPKfWGATVWPsLmHT
Signed-off-by: Claude <noreply@anthropic.com>
Add an "Auto" button next to the SQL slider that sets the squelch
threshold to the current noise floor (estimated from spectrum bins)
plus a 6 dB margin. Uses the existing estimateNoiseFloorDb() heuristic.
https://claude.ai/code/session_01TDQyrZiPKfWGATVWPsLmHT
Signed-off-by: Claude <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>