[fix](trx-backend-soapysdr): preserve RDS subcarrier with narrow WFM bandwidth

The IQ prefilter cutoff was audio_bandwidth_hz/2, so any setting below
~120 kHz would cut off the 57 kHz RDS subcarrier before FM demod.

- Clamp IQ prefilter cutoff to >= 60 kHz for WFM in both new() and
  rebuild_filters() — audio quality is unaffected since WfmStereoDecoder
  applies its own 18 kHz lowpass internally
- Ensure pipeline target rate >= 120 kHz for WFM so the decimated IQ
  sample rate can represent the 60 kHz cutoff

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-26 23:31:54 +01:00
parent 1994534b4d
commit 32e090f927
@@ -328,7 +328,11 @@ impl ChannelDsp {
}
let target_rate = match mode {
RigMode::WFM => audio_bandwidth_hz.max(audio_sample_rate.saturating_mul(4)),
// Ensure composite rate is at least 120 kHz so the IQ filter can
// pass the 57 kHz RDS subcarrier regardless of the user's audio BW.
RigMode::WFM => audio_bandwidth_hz
.max(audio_sample_rate.saturating_mul(4))
.max(120_000),
RigMode::VDES => audio_sample_rate.max(96_000),
_ => audio_sample_rate.max(1),
};
@@ -346,10 +350,20 @@ 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;
let cutoff_hz = {
let raw = self
.audio_bandwidth_hz
.min(channel_sample_rate.saturating_sub(1)) as f32
/ 2.0;
// For WFM, always pass at least the 57 kHz RDS subcarrier.
// Audio bandwidth is handled inside WfmStereoDecoder, so widening
// the IQ prefilter here does not affect output audio quality.
if self.mode == RigMode::WFM {
raw.max(60_000.0)
} else {
raw
}
};
let cutoff_norm = if self.sdr_sample_rate == 0 {
0.1
} else {
@@ -424,7 +438,14 @@ impl ChannelDsp {
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;
let cutoff_hz = {
let raw = audio_bandwidth_hz.min(channel_sample_rate.saturating_sub(1)) as f32 / 2.0;
if *mode == RigMode::WFM {
raw.max(60_000.0)
} else {
raw
}
};
let cutoff_norm = if sdr_sample_rate == 0 {
0.1
} else {