[fix](trx-backend-soapysdr): widen RDS bandpass Q from 5.0 to 3.5 to reduce in-band distortion

The 8th-order (4×biquad) RDS bandpass at Q=5 per stage produced a
composite −3 dB bandwidth of ±2480 Hz, but the steep 8th-order roll-off
tapered the RDS signal edges (±1544 Hz at α=0.30) by −1.2 dB.  This
distorted the RRC matched filter's expected flat spectrum, causing ISI
and degrading soft-decision confidence — directly hurting PS/RT decode
on weak signals.

Q=3.5 widens the composite passband to ±3560 Hz, reducing band-edge
attenuation to −0.59 dB while still providing ≈−4 dB rejection at the
stereo difference signal edge (53 kHz) and steep 8th-order far-out
roll-off.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>

https://claude.ai/code/session_01Sw9esAuic8KHP1t8nZgvH2
Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-03-27 15:22:24 +00:00
committed by Stan Grams
parent dd1af5aa94
commit a387047464
@@ -298,9 +298,15 @@ impl BiquadNotch {
// ---------------------------------------------------------------------------
/// Four cascaded biquad bandpass sections forming an effective 8th-order BPF.
/// Q=5 per section gives ≈ ±2480 Hz passband at 57 kHz — wide enough to pass
/// the full RDS DSB signal while providing much steeper adjacent-channel
/// rejection than the previous single-stage (2nd-order) filter.
/// Q=3.5 per section gives ≈ ±3560 Hz composite passband at 57 kHz.
///
/// At α=0.30 the RDS signal extends ±1544 Hz from center. The previous
/// Q=5 gave only ±2480 Hz composite bandwidth, which tapered the RDS band
/// edges by 1.2 dB — enough to distort the RRC matched filter's expected
/// pulse shape, cause ISI, and degrade soft-decision confidence values.
/// Q=3.5 reduces edge attenuation to 0.59 dB while still providing
/// ≈−4 dB rejection at the stereo difference signal edge (53 kHz) and
/// steep 8th-order roll-off beyond.
#[derive(Debug, Clone)]
struct Iir8BandPass {
stages: [BiquadBandPass; 4],
@@ -308,7 +314,7 @@ struct Iir8BandPass {
impl Iir8BandPass {
fn new(sample_rate: f32, center_hz: f32) -> Self {
const Q: f32 = 5.0;
const Q: f32 = 3.5;
Self {
stages: std::array::from_fn(|_| BiquadBandPass::new(sample_rate, center_hz, Q)),
}