[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:
@@ -23,8 +23,10 @@ const BW4_Q1: f32 = 0.5412;
|
|||||||
const BW4_Q2: f32 = 1.3066;
|
const BW4_Q2: f32 = 1.3066;
|
||||||
/// Q for the 19 kHz pilot notch (~3.8 kHz 3 dB bandwidth).
|
/// Q for the 19 kHz pilot notch (~3.8 kHz 3 dB bandwidth).
|
||||||
const PILOT_NOTCH_Q: f32 = 5.0;
|
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.
|
/// 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.
|
/// Fixed phase trim on the recovered L-R channel to compensate pilot-path delay.
|
||||||
const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.0;
|
const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.0;
|
||||||
/// Fixed gain trim on the recovered L-R channel.
|
/// Fixed gain trim on the recovered L-R channel.
|
||||||
@@ -393,6 +395,9 @@ pub struct WfmStereoDecoder {
|
|||||||
dc_m: DcBlocker,
|
dc_m: DcBlocker,
|
||||||
dc_l: DcBlocker,
|
dc_l: DcBlocker,
|
||||||
dc_r: DcBlocker,
|
dc_r: DcBlocker,
|
||||||
|
/// Post-matrix pilot notches for stereo outputs.
|
||||||
|
pilot_notch_l: BiquadNotch,
|
||||||
|
pilot_notch_r: BiquadNotch,
|
||||||
deemph_m: Deemphasis,
|
deemph_m: Deemphasis,
|
||||||
deemph_l: Deemphasis,
|
deemph_l: Deemphasis,
|
||||||
deemph_r: Deemphasis,
|
deemph_r: Deemphasis,
|
||||||
@@ -459,6 +464,8 @@ impl WfmStereoDecoder {
|
|||||||
dc_m: DcBlocker::new(0.9999),
|
dc_m: DcBlocker::new(0.9999),
|
||||||
dc_l: DcBlocker::new(0.9999),
|
dc_l: DcBlocker::new(0.9999),
|
||||||
dc_r: 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_m: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
|
||||||
deemph_l: 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),
|
deemph_r: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
|
||||||
@@ -493,8 +500,8 @@ impl WfmStereoDecoder {
|
|||||||
|
|
||||||
// --- Pilot phase estimator ---
|
// --- Pilot phase estimator ---
|
||||||
let (sin_p, cos_p) = self.pilot_phase.sin_cos();
|
let (sin_p, cos_p) = self.pilot_phase.sin_cos();
|
||||||
let i = self.pilot_i_lp.process(x * cos_p);
|
let i = self.pilot_i_lp.process(pilot_tone * cos_p);
|
||||||
let q = self.pilot_q_lp.process(x * -sin_p);
|
let q = self.pilot_q_lp.process(pilot_tone * -sin_p);
|
||||||
let phase_err = q.atan2(i);
|
let phase_err = q.atan2(i);
|
||||||
let pilot_phase_est = self.pilot_phase + phase_err;
|
let pilot_phase_est = self.pilot_phase + phase_err;
|
||||||
self.pilot_phase += self.pilot_freq;
|
self.pilot_phase += self.pilot_freq;
|
||||||
@@ -567,11 +574,13 @@ impl WfmStereoDecoder {
|
|||||||
// Single-band: uniform blend across all frequencies.
|
// Single-band: uniform blend across all frequencies.
|
||||||
diff_i * blend_i
|
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
|
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);
|
.clamp(-1.0, 1.0);
|
||||||
let right = self.dc_r
|
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);
|
.clamp(-1.0, 1.0);
|
||||||
output.push(left);
|
output.push(left);
|
||||||
output.push(right);
|
output.push(right);
|
||||||
|
|||||||
Reference in New Issue
Block a user