[fix](trx-backend-soapysdr): improve wfm stereo separation

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-02-28 21:34:31 +01:00
parent a456864957
commit 73fe9d52ff
@@ -23,8 +23,10 @@ const BW4_Q1: f32 = 0.5412;
const BW4_Q2: f32 = 1.3066;
/// Q for the 19 kHz pilot notch (~3.8 kHz 3 dB bandwidth).
const PILOT_NOTCH_Q: f32 = 5.0;
/// Tighter post-matrix stereo notch for suppressing residual pilot leakage.
const STEREO_PILOT_NOTCH_Q: f32 = 12.0;
/// Narrow 19 kHz band-pass used to derive zero-crossings for switching stereo demod.
const PILOT_BPF_Q: f32 = 10.0;
const PILOT_BPF_Q: f32 = 20.0;
/// Fixed phase trim on the recovered L-R channel to compensate pilot-path delay.
const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.0;
/// Fixed gain trim on the recovered L-R channel.
@@ -393,6 +395,9 @@ pub struct WfmStereoDecoder {
dc_m: DcBlocker,
dc_l: DcBlocker,
dc_r: DcBlocker,
/// Post-matrix pilot notches for stereo outputs.
pilot_notch_l: BiquadNotch,
pilot_notch_r: BiquadNotch,
deemph_m: Deemphasis,
deemph_l: Deemphasis,
deemph_r: Deemphasis,
@@ -459,6 +464,8 @@ impl WfmStereoDecoder {
dc_m: DcBlocker::new(0.9999),
dc_l: DcBlocker::new(0.9999),
dc_r: DcBlocker::new(0.9999),
pilot_notch_l: BiquadNotch::new(audio_rate.max(1) as f32, PILOT_HZ, STEREO_PILOT_NOTCH_Q),
pilot_notch_r: BiquadNotch::new(audio_rate.max(1) as f32, PILOT_HZ, STEREO_PILOT_NOTCH_Q),
deemph_m: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
deemph_l: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
deemph_r: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
@@ -493,8 +500,8 @@ impl WfmStereoDecoder {
// --- Pilot phase estimator ---
let (sin_p, cos_p) = self.pilot_phase.sin_cos();
let i = self.pilot_i_lp.process(x * cos_p);
let q = self.pilot_q_lp.process(x * -sin_p);
let i = self.pilot_i_lp.process(pilot_tone * cos_p);
let q = self.pilot_q_lp.process(pilot_tone * -sin_p);
let phase_err = q.atan2(i);
let pilot_phase_est = self.pilot_phase + phase_err;
self.pilot_phase += self.pilot_freq;
@@ -567,11 +574,13 @@ impl WfmStereoDecoder {
// Single-band: uniform blend across all frequencies.
diff_i * blend_i
};
let left_corr = (sum_i + diff_denoised) * 0.5;
let right_corr = (sum_i - diff_denoised) * 0.5;
let left = self.dc_l
.process(self.deemph_l.process((sum_i + diff_denoised) * 0.5))
.process(self.pilot_notch_l.process(self.deemph_l.process(left_corr)))
.clamp(-1.0, 1.0);
let right = self.dc_r
.process(self.deemph_r.process((sum_i - diff_denoised) * 0.5))
.process(self.pilot_notch_r.process(self.deemph_r.process(right_corr)))
.clamp(-1.0, 1.0);
output.push(left);
output.push(right);