From d5b1b6f0209f8fa94aec445667ff222953a3a585 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 28 Feb 2026 09:10:14 +0100 Subject: [PATCH] [fix](trx-backend-soapysdr): widen WFM IQ filter to preserve RDS subcarrier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FIR LPF cutoff was audio_bandwidth_hz/2; with wfm_bandwidth_hz=75000 this gave 37.5 kHz, stripping the 57 kHz RDS subcarrier before FM demodulation. Clamp the IQ filter bandwidth to at least 130 kHz (cutoff ≥ 65 kHz) for WFM so the RDS subcarrier always reaches the decoder, regardless of the configured audio bandwidth. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- .../trx-backend-soapysdr/src/dsp.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs index 9709a9a..ad82b32 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs @@ -323,10 +323,14 @@ impl ChannelDsp { self.audio_sample_rate, self.audio_bandwidth_hz, ); - let cutoff_hz = self - .audio_bandwidth_hz - .min(channel_sample_rate.saturating_sub(1)) as f32 - / 2.0; + // For WFM, widen the IQ filter enough to pass the RDS subcarrier at + // 57 kHz (requires cutoff ≥ 65 kHz → bandwidth ≥ 130 kHz). + let iq_filter_bw = if self.mode == RigMode::WFM { + self.audio_bandwidth_hz.max(130_000) + } else { + self.audio_bandwidth_hz + }; + let cutoff_hz = iq_filter_bw.min(channel_sample_rate.saturating_sub(1)) as f32 / 2.0; let cutoff_norm = if self.sdr_sample_rate == 0 { 0.1 } else { @@ -384,7 +388,14 @@ impl ChannelDsp { let taps = fir_taps.max(1); let (decim_factor, channel_sample_rate) = Self::pipeline_rates(mode, sdr_sample_rate, audio_sample_rate, audio_bandwidth_hz); - let cutoff_hz = audio_bandwidth_hz.min(channel_sample_rate.saturating_sub(1)) as f32 / 2.0; + // For WFM, widen the IQ filter enough to pass the RDS subcarrier at + // 57 kHz (requires cutoff ≥ 65 kHz → bandwidth ≥ 130 kHz). + let iq_filter_bw = if *mode == RigMode::WFM { + audio_bandwidth_hz.max(130_000) + } else { + audio_bandwidth_hz + }; + let cutoff_hz = iq_filter_bw.min(channel_sample_rate.saturating_sub(1)) as f32 / 2.0; let cutoff_norm = if sdr_sample_rate == 0 { 0.1 } else {