Commit Graph
100 Commits
Author SHA1 Message Date
sjgandClaude Opus 4.6 a5e66ed287 [fix](trx-backend-soapysdr): revert wfm resampler taps back to 16
32 taps caused audio silence on real signals. Revert to 16 taps
which works correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 02:20:42 +01:00
sjgandClaude Opus 4.6 edb456e843 [fix](trx-backend-soapysdr): replace avx2 atan2 with high-precision polynomial
Replace the low-accuracy 0.273 linear atan approximation with a
7th-order minimax polynomial (max error ~2.4e-7 rad vs ~0.004 rad).
Use branchless |y|>|x| argument reduction instead of y/x division
with quadrant fixup, avoiding division-by-zero and NaN branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 02:10:02 +01:00
sjgandClaude Opus 4.6 cb4b81ca94 [fix](trx-backend-soapysdr): use precise atan2 in wfm discriminator and pll
Replace fast_atan2 polynomial approximation with f32::atan2 in the WFM
stereo decoder's FM discriminator and pilot PLL. The approximation
introduced harmonic distortion (~0.22 deg error) that manifested as
treble artifacts on strong/overdeviated broadcast signals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 02:04:06 +01:00
sjgandClaude Opus 4.6 8f6a5af4ec [fix](trx-backend-soapysdr): hard-limit iq magnitude before wfm discriminator
Normalize IQ samples exceeding unit magnitude before the FM
discriminator. The discriminator only uses phase, so clamping
amplitude prevents overdeviated signals from producing clipped
composite baseband without losing frequency information.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:58:16 +01:00
sjgandClaude Opus 4.6 13de90d664 [fix](trx-backend-soapysdr): increase wfm resampler taps from 16 to 32
Doubles the polyphase FIR length for the composite-to-audio rate
converter, improving stopband rejection from ~25 dB to ~50 dB.
This reduces treble distortion from imaging artifacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:55:59 +01:00
sjgandClaude Opus 4.6 8d0dc17317 [chore](trx-backend-soapysdr): remove unused smoothstep01
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:49:27 +01:00
sjgandClaude Opus 4.6 815efdb1ae [fix](trx-backend-soapysdr): match sum/diff filters and simplify stereo blend
- Set STEREO_DIFF_BW_HZ = AUDIO_BW_HZ so both filter paths have
  identical group delay (improves multitone separation by ~10 dB).
- Zero out STEREO_SEPARATION_PHASE_TRIM (unnecessary with matched filters).
- Replace gradual blend ramp with binary blend: full stereo at pilot
  lock, mono when unlocked. The hysteresis thresholds already handle
  noisy signals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:48:16 +01:00
sjgandClaude Opus 4.6 13576f73aa [test](trx-backend-soapysdr): add multi-tone wfm stereo separation test
Test L-only and R-only signals with tones at 400, 2000, 8000 and
14000 Hz to catch frequency-dependent group delay and phase trim
issues that the single 1 kHz test misses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:42:39 +01:00
sjg 33b218fce3 [perf](trx-backend-soapysdr): reduce frame emission overhead
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:41:34 +01:00
sjgandClaude Opus 4.6 2ed4aaf46d [fix](trx-backend-soapysdr): tune wfm stereo filter bw and matrix gain
Restore AUDIO_BW_HZ to 15.8 kHz for cleaner mono path, widen
STEREO_DIFF_BW_HZ to 18 kHz for better high-frequency stereo detail,
and raise STEREO_MATRIX_GAIN from 0.30 to 0.50 (mathematically correct
unity gain for the L=(S+D)/2 stereo matrix).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:37:15 +01:00
sjg e7997f8492 [perf](trx-backend-soapysdr): use ring history for wfm resampler
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:34:50 +01:00
sjgandClaude Opus 4.6 a6c3a85c8d [fix](trx-backend-soapysdr): match wfm sum/diff filter cutoffs at 18 kHz
Raise both AUDIO_BW_HZ and STEREO_DIFF_BW_HZ to 18 kHz so the L+R and
L-R filter paths have identical group delay across the full audio band.
The previous mismatch (15.8 vs 14.5 kHz) caused frequency-dependent
phase errors in the stereo matrix that degraded real-world separation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:31:34 +01:00
sjg f9691fee1e [perf](trx-backend-soapysdr): vectorize wfm discriminator angle
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:30:41 +01:00
sjg 72219956d9 [perf](trx-backend-soapysdr): trim wfm resampler taps
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:21:50 +01:00
sjgandClaude Opus 4.6 b2ce62646c [fix](trx-backend-soapysdr): improve wfm stereo blend threshold
Raise the stereo blend floor from 0.55 to 0.75 at pilot lock and lower
the full-blend ceiling from stereo_detect_level 0.92 to 0.70.  This
gives real-world signals with moderate pilot strength much better L/R
separation (~17 dB immediately at lock vs ~5 dB before) and reaches
full unity blend sooner.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:20:38 +01:00
sjg 926a02e043 [perf](trx-backend-soapysdr): streamline wfm decode loop
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:19:47 +01:00
sjg b42e557a25 [perf](trx-backend-soapysdr): pack iq fir filtering
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:17:16 +01:00
sjg 198efa2092 [perf](trx-backend-soapysdr): update fir overlap in place
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:13:10 +01:00
sjg 7a6467320b [perf](trx-backend-soapysdr): reuse fir scratch buffers
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:10:34 +01:00
sjg ca0f236f4a [perf](trx-backend-soapysdr): reduce mixer and buffer overhead
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:07:19 +01:00
sjg bec3fc72d3 [fix](trx-backend-soapysdr): widen wfm stereo image
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 01:03:05 +01:00
sjg 6f37f9c66b [fix](trx-backend-soapysdr): refine wfm stereo tracking
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:58:20 +01:00
sjg e84a0599fa [feat](trx-backend-soapysdr): add avx2 wfm hot paths
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:52:53 +01:00
sjg 3e88c3f5d9 [feat](trx-backend-soapysdr): speed up fir and widen wfm resampler
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:50:04 +01:00
sjg 5411607b63 [fix](trx-backend-soapysdr): honor frontend wfm bandwidth
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:46:08 +01:00
sjg 17c70f89e5 [fix](trx-backend-soapysdr): fully reset wfm decoder on tune
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:41:28 +01:00
sjg fdcc732c83 [fix](trx-backend-soapysdr): retune wfm fir window
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:38:44 +01:00
sjg b0d8ce8e29 [fix](trx-backend-soapysdr): restore wfm agc and cutoffs
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:35:19 +01:00
sjgandOpenAI Codex a6847742d9 [fix](trx-backend-soapysdr): keep wfm discriminator state across blocks
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:31:41 +01:00
sjgandOpenAI Codex a8ed9e8fa4 [fix](trx-backend-soapysdr): lower wfm internal rate ceiling
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:24:33 +01:00
sjgandOpenAI Codex a25182ea3f [fix](trx-backend-soapysdr): drop wfm limiter and widen cutoffs
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:20:41 +01:00
sjgandOpenAI Codex 3b277da243 [fix](trx-backend-soapysdr): make wfm limiter bass-insensitive
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:10:54 +01:00
sjgandOpenAI Codex 206696acc5 [feat](trx-backend-soapysdr): add adaptive wfm stereo blend
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:07:13 +01:00
sjgandOpenAI Codex 5060522541 [fix](trx-backend-soapysdr): add wfm limiter and restore cutoffs
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:02:19 +01:00
sjgandOpenAI Codex b6f0a9967d [fix](trx-backend-soapysdr): improve wfm stereo separation trim
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-03-01 00:00:31 +01:00
sjgandOpenAI Codex 32822effda [fix](trx-backend-soapysdr): retune wfm sum and diff cutoffs
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:55:17 +01:00
sjgandOpenAI Codex 635d2b39bb [fix](trx-backend-soapysdr): add stereo diff high-pass and headroom
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:51:26 +01:00
sjgandOpenAI Codex 420e98de31 [feat](trx-client,trx-frontend-http): add default rig and hideable rf gain
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:45:17 +01:00
sjgandOpenAI Codex 9453c90dac [fix](trx-backend-soapysdr): retune wfm stereo difference path
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:41:07 +01:00
sjgandOpenAI Codex 0dd91a952b [fix](trx-backend-soapysdr): add headroom to wfm stereo matrix
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:38:18 +01:00
sjgandOpenAI Codex 6d466f44d2 [fix](trx-backend-soapysdr): narrow wfm stereo difference band
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:37:08 +01:00
sjgandOpenAI Codex 9e49bfbcc8 [fix](trx-backend-soapysdr): latch full stereo blend after lock
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:34:43 +01:00
sjgandOpenAI Codex 617255cd32 [feat](trx-rs): expose live sdr gain control
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:30:08 +01:00
sjgandOpenAI Codex 93e507606e [feat](trx-server,trx-backend-soapysdr): add sdr gain ceiling
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:23:41 +01:00
sjgandOpenAI Codex dbeec9fb39 [feat](trx-backend-soapysdr): use fir resampler for wfm audio
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:17:15 +01:00
sjgandOpenAI Codex 96da7ca471 [style](trx-frontend-http): lower rds overlay badge
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:12:38 +01:00
sjgandOpenAI Codex 5a6ff1427a [fix](trx-client,trx-backend-soapysdr): improve rig switching and fm agc
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 23:06:53 +01:00
sjgandOpenAI Codex 8440fe164b [fix](trx-backend-soapysdr): reduce wfm audio cutoff
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:54:03 +01:00
sjgandOpenAI Codex 735c9a7f07 [fix](trx-backend-soapysdr): bypass agc for wfm audio
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:38:54 +01:00
sjgandOpenAI Codex 3ccda5fb5a [fix](trx-backend-soapysdr,trx-frontend-http): reset stereo state and soften wfm top end
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:34:54 +01:00
sjgandOpenAI Codex 3a75b7ac5b [fix](trx-frontend-http): satisfy audio copyTo arguments
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:28:35 +01:00
sjgandOpenAI Codex 85ad204974 [fix](trx-frontend-http): handle stereo audio frame layouts
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:26:56 +01:00
sjgandOpenAI Codex f06dbc921a [fix](trx-frontend-http): reconfigure audio stream on rig change
Co-authored-by: OpenAI Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:23:03 +01:00
sjgandCodex b6053729a4 [fix](trx-backend-soapysdr): emit proper stereo pcm frames
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:07:34 +01:00
sjgandCodex df1bbf8f5b [fix](trx-rs): remove wfm denoise and default stereo audio
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 22:01:17 +01:00
sjgandCodex d6c89bcc6b [fix](trx-backend-soapysdr): tighten wfm pilot detect and stereo agc
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 21:51:36 +01:00
sjgandCodex b722787ada [feat](trx-rs): show live wfm stereo detect state
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 21:44:32 +01:00
sjgandCodex 862f0200bb [fix](trx-backend-soapysdr): tune wfm stereo separation trim
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 21:39:35 +01:00
sjgandCodex 73fe9d52ff [fix](trx-backend-soapysdr): improve wfm stereo separation
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 21:34:31 +01:00
sjgandCodex a456864957 [fix](trx-backend-soapysdr): restore wfm stereo separation
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 21:26:32 +01:00
sjgandCodex 1cc60c8a10 [style](trx-frontend-http): align wfm control button sizing
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:51:27 +01:00
sjgandClaude Opus 4.6 eb740dbb43 [fix](trx-backend-soapysdr): add missing wfm_denoise arg in dsp tests
Fix pre-existing compilation failures in four test call sites that were
missing the wfm_denoise: bool argument added to ChannelDsp::new() and
SdrPipeline::start().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:43:10 +01:00
sjgandClaude Opus 4.6 bbc7e857e5 [fix](trx-backend-soapysdr): improve WFM stereo demodulation quality
- Raise audio LPF cutoff from 15 kHz to 17 kHz to pass full FM stereo
  audio bandwidth without excessive HF rolloff
- Replace 2-point linear interpolation resampler with 4-point Hermite
  cubic spline for a much flatter passband up to 17 kHz
- Add FM discriminator gain normalization (fm_gain = fs / 150000) so
  ±75 kHz deviation maps to ±1.0 regardless of composite sample rate,
  stabilizing stereo carrier amplitude reconstruction
- Double pilot PLL proportional (0.0015→0.003) and integral
  (0.00002→0.00005) gains for faster lock and better tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:43:01 +01:00
sjgandCodex 3d8fd32488 [fix](trx-backend-soapysdr): prevent double agc on mono wfm
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:30:12 +01:00
sjgandCodex 244f91d97b [fix](trx-backend-soapysdr,trx-frontend-http): make wfm mono mode real
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:25:04 +01:00
sjgandClaude Sonnet 4.6 b7c1da138b [fix](trx-backend-soapysdr): fix WFM stereo separation
The 19 kHz pilot notch was applied only to the L+R sum path, introducing
~22° of phase shift at 15 kHz relative to the L-R diff path.  This phase
mismatch caused interchannel crosstalk (≈ −14 dB separation at 15 kHz).

Fix: remove the notch from the sum processing chain so both sum and diff
pass through identical 4th-order Butterworth LPFs, giving phase-coherent
demodulation across the full audio band.  The notch is relocated to the
mono output branch where phase alignment with the diff channel is not
required.  Pilot rejection on the stereo L/R outputs is still adequate
(~28 dB) from the combined LPF + deemphasis response at 19 kHz.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 20:11:15 +01:00
sjgandClaude Sonnet 4.6 95716a0fc3 [feat](trx-rs): expose WFM stereo denoise toggle
Add a server-side toggle for the multiband stereo denoiser so it can be
enabled or disabled at runtime without restarting the server.

Backend (trx-backend-soapysdr):
- Add `denoise_enabled: bool` to `WfmStereoDecoder`; gate multiband
  blend behind it (falls back to uniform single-band blend when off)
- Add `set_denoise_enabled()` method on `WfmStereoDecoder`
- Propagate `wfm_denoise: bool` through `ChannelDsp`, `SdrPipeline`,
  and `SoapySdrRig`; add `set_wfm_denoise()` at each layer
- Include `wfm_denoise` in `filter_state()` so it flows into snapshots

Protocol / core (trx-core, trx-protocol, trx-server):
- Add `SetWfmDenoise(bool)` to `RigCommand` and `ClientCommand`
- Add default `set_wfm_denoise()` trait method to `RigCat`
- Handle `SetWfmDenoise` in `rig_task.rs` and update `RigFilterState`
- Add `wfm_denoise: bool` (default `true`) to `RigFilterState`

Frontend (trx-frontend-http):
- Add `POST /toggle_wfm_denoise` endpoint
- Add "Denoise On/Off" button next to the stereo/mono audio picker
- Sync button state from SSE filter snapshot (`update.filter.wfm_denoise`)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 19:47:36 +01:00
sjgandClaude Sonnet 4.6 ffdc193671 [feat](trx-backend-soapysdr): add multiband stereo denoising for WFM
Split the L-R diff channel into three frequency bands at audio rate and
apply SNR-weighted blending per band driven by pilot magnitude:

  - 0–2 kHz:   blend¹  (most stereo — low frequencies have best SNR)
  - 2–8 kHz:   blend²  (moderate noise reduction)
  - 8–15 kHz:  blend⁴  (aggressive noise reduction — hiss-prone range)

Move blend from composite rate to audio rate so the crossover filters
(2nd-order Butterworth at 2 kHz and 8 kHz) operate at 48 kHz and the
pilot blend is linearly interpolated per audio sample for smooth
transitions. Unblended diff is now stored in prev_diff; prev_blend
tracks the blend value for the same interpolation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 17:16:46 +01:00
sjgandCodex ec1518facc [fix](trx-frontend-http): serve safari-friendly favicon
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 17:14:20 +01:00
sjgandCodex e26b8dd6a1 [feat](trx-frontend-http): refresh favicon from logo
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 17:09:51 +01:00
sjgandClaude Sonnet 4.6 332ad4448b [fix](trx-backend-soapysdr): fix AM demodulation quality
Three bugs made the AM path sound wrong:

1. AGC attack too fast (5 ms).  The slowest audio a broadcast AM station
   can transmit is ~50 Hz (20 ms period).  A 5 ms attack lets the AGC track
   individual audio cycles, which causes severe pumping and amplitude
   distortion.  Change to 500 ms attack / 5 s release so the AGC only
   responds to slow carrier-amplitude fading, not the audio modulation itself.

2. Bandwidth too narrow.  The IQ filter cutoff is audio_bandwidth_hz / 2,
   so the previous 6 000 Hz setting gave only 3 kHz audio bandwidth.
   AM broadcast sidebands extend to ±4.5–5 kHz; raise the default to
   12 000 (cutoff 6 kHz) to cover the full audio band.

3. DC blocker rate inconsistent.  For AM the demodulated magnitude is
   always ≥ 0 and the DC component equals the carrier amplitude; only true
   DC needs removing.  Unify all non-WFM modes to r = 0.9999 (corner
   ≈ 0.76 Hz @ 48 kHz), which strips carrier DC without touching any
   audible bass content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 17:00:32 +01:00
sjgandCodex 1f3bdb988a [fix](trx-frontend-http): avoid rerendering unchanged rds af list
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:46:55 +01:00
sjgandCodex ce25751c5d [feat](trx-rds,trx-frontend-http): add af tuning and dynamic page title
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:41:25 +01:00
sjgandCodex a18ef33ee2 [feat](trx-frontend-http): enrich and align rds details
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:31:45 +01:00
sjgandClaude Sonnet 4.6 1f9c09668d [fix](trx-backend-soapysdr): demodulate CW as USB (real part, not envelope)
CW signals in SDR are centred at an audio offset (e.g. 700 Hz) by the
upstream FIR filter, so demodulating as USB (taking the real part) produces
the correct side-tone.  The previous magnitude/envelope approach produced a
DC pulse per key press with no audible tone.

Re-enable the DC blocker for CW/CWR (r = 0.9999): the output is now audio
that can carry a DC offset from BFO frequency error, identical to USB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:25:53 +01:00
sjgandCodex eed5673f95 [feat](trx-frontend-http): smooth spectrum waveform
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:13:10 +01:00
sjgandClaude Sonnet 4.6 ba9881ff62 [feat](trx-backend-soapysdr): add AGC and DC blocking to all demod modes
Add SoftAgc — a fast-attack/slow-release envelope AGC with a max-gain cap
— to all demodulated audio paths so that switching between modes (WFM, AM,
SSB, CW, FM) no longer produces large volume jumps.  AGC is applied after
every demodulator, including WFM, with a shared target level of 0.5.

Add per-mode DC blocking (DcBlocker) for USB/LSB/AM/FM/DIG paths to remove
carrier frequency-offset DC from the FM discriminator and LO bleedthrough in
SSB.  CW is excluded because high-passing a non-negative envelope creates
negative-going artifacts on each key release; WFM already has internal DC
blockers on each output channel.

AGC time constants are tuned per mode:
  CW/CWR  – 1 ms attack / 50 ms release  (follows individual dots/dashes)
  AM      – 5 ms attack / 200 ms release  (tracks fading carriers)
  all else– 5 ms attack / 500 ms release  (suits voice and data)

Simplify demod_am and demod_cw: remove per-block peak normalisation and DC
removal that caused block-boundary level discontinuities ("pumping").  Both
now return raw magnitudes and rely on the downstream DC blocker and AGC for
normalisation.

DIG is already wired as Passthrough (identical to USB); no change needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:04:26 +01:00
sjgandCodex 5096e01a60 [feat](trx-client,trx-frontend-http): raise spectrum update rate
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 16:04:15 +01:00
sjgandCodex ea6830e343 [feat](trx-frontend-http): add rds raw json copy action
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:51:08 +01:00
sjgandClaude Sonnet 4.6 6dc26e48a3 [feat](trx-backend-soapysdr): improve WFM audio quality
Replace one-pole sum/diff filters with a proper 4th-order Butterworth
cascade (Q = 0.5412 / 1.3066) at 15 kHz.  This reduces pilot tone
leakage from −4 dB to −12 dB at 19 kHz and suppresses the 38 kHz DSB
carrier from −9 dB to −32 dB, significantly improving stereo crosstalk.

Add a biquad notch at 19 kHz on the L+R channel to eliminate the residual
pilot tone that would otherwise be audible after downsampling to 48 kHz.

Replace nearest-neighbor (sample-hold) resampling with linear interpolation
inside WfmStereoDecoder.  The output sample is now placed at the exact
fractional position between the two adjacent composite samples using the
phase accumulator state, removing timing jitter and harmonic distortion on
sustained tones.

Add DC blockers (pole at 0.9999, corner ≈ 0.75 Hz at 48 kHz) to all audio
outputs to remove carrier frequency-offset DC from the FM discriminator
without any audible bass roll-off.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:46:56 +01:00
sjgandCodex 42f61de502 [style](trx-frontend-http): tighten overview spacing
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:30:02 +01:00
sjgandCodex cf8d0743ce [feat](trx-rds,trx-frontend-http): expand rds metadata display
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:27:26 +01:00
sjgandCodex 8827131264 [fix](trx-frontend-http): copy formatted rds ps text
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:11:29 +01:00
sjgandCodex b1fab91e0e [fix](trx-frontend-http): shrink rds pi fallback display
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 15:08:53 +01:00
sjgandCodex 27c8018d89 [feat](trx-frontend-http): show rds pi before ps arrives
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:55:38 +01:00
sjgandCodex b172ace0ee [feat](trx-rds,trx-backend-soapysdr): condition weak-signal rds updates
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:52:31 +01:00
sjgandCodex e9fa27be3a [fix](trx-rds): restore prior recovery path
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:46:42 +01:00
sjgandCodex 5a27cb634d [feat](trx-backend-soapysdr): prefilter rds subcarrier
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:41:19 +01:00
sjgandCodex 2874c63dd1 [fix](trx-rds): relax weak-signal commit thresholds
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:36:46 +01:00
sjgandCodex a94fcd75af [feat](trx-rds): add early-late timing correction
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:32:23 +01:00
sjgandCodex 017b7be8a8 [fix](trx-rds,trx-backend-soapysdr,trx-frontend-http): relax rds filtering
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:26:11 +01:00
sjgandCodex 778e695941 [fix](trx-frontend-http): sanitize stale tune step options
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:21:39 +01:00
sjgandCodex 2ed68e4210 [feat](trx-rds,trx-backend-soapysdr): improve weak-signal rds recovery
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:19:36 +01:00
sjgandCodex 0329be6124 [fix](trx-frontend-http): clarify jog step controls
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:16:09 +01:00
sjgandCodex 8f7afed132 [fix](trx-backend-soapysdr,trx-frontend-http): keep rds state on bw changes
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:13:06 +01:00
sjgandCodex 976f66d383 [feat](trx-frontend-http): format copied rds overlay text
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:08:20 +01:00
sjgandCodex f48e2a6a81 [feat](trx-frontend-http): improve rds pty and ps display
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:03:46 +01:00
sjgandCodex adadcb4a77 [fix](trx-backend-soapysdr,trx-frontend-http): preserve raw rds ps text
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 12:01:27 +01:00
sjgandCodex abab89b89c [feat](trx-frontend-http): refine rds overlay interactions
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 11:58:12 +01:00
sjgandCodex 2cd33386ef [fix](trx-frontend-http): add rx audio jitter buffer
Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-02-28 11:53:50 +01:00