[fix](trx-backend-soapysdr): add stereo diff high-pass and headroom
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -34,7 +34,11 @@ const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.015;
|
|||||||
const STEREO_SEPARATION_GAIN: f32 = 1.006;
|
const STEREO_SEPARATION_GAIN: f32 = 1.006;
|
||||||
/// Extra headroom in the stereo matrix to reduce stereo-only clipping/IMD on
|
/// Extra headroom in the stereo matrix to reduce stereo-only clipping/IMD on
|
||||||
/// strong program material. This keeps bass excursions from flattening treble.
|
/// strong program material. This keeps bass excursions from flattening treble.
|
||||||
const STEREO_MATRIX_GAIN: f32 = 0.42;
|
const STEREO_MATRIX_GAIN: f32 = 0.30;
|
||||||
|
/// Gentle high-pass memory for the stereo L-R path.
|
||||||
|
/// This trims only very low-frequency difference energy that can eat headroom
|
||||||
|
/// and modulate higher-frequency stereo detail.
|
||||||
|
const STEREO_DIFF_DC_R: f32 = 0.9995;
|
||||||
/// Fractional-resampler FIR taps for WFM audio reconstruction.
|
/// Fractional-resampler FIR taps for WFM audio reconstruction.
|
||||||
const WFM_RESAMP_TAPS: usize = 6;
|
const WFM_RESAMP_TAPS: usize = 6;
|
||||||
/// Polyphase slots for the WFM fractional FIR resampler.
|
/// Polyphase slots for the WFM fractional FIR resampler.
|
||||||
@@ -419,6 +423,9 @@ pub struct WfmStereoDecoder {
|
|||||||
/// Quadrature companion of the L-R path used for phase trim / crosstalk adjustment.
|
/// Quadrature companion of the L-R path used for phase trim / crosstalk adjustment.
|
||||||
diff_q_lpf1: BiquadLowPass,
|
diff_q_lpf1: BiquadLowPass,
|
||||||
diff_q_lpf2: BiquadLowPass,
|
diff_q_lpf2: BiquadLowPass,
|
||||||
|
/// Gentle high-pass on the stereo-difference path to reduce bass-driven IMD.
|
||||||
|
diff_dc: DcBlocker,
|
||||||
|
diff_q_dc: DcBlocker,
|
||||||
/// DC blockers on audio outputs — remove carrier-offset DC from the FM discriminator.
|
/// DC blockers on audio outputs — remove carrier-offset DC from the FM discriminator.
|
||||||
dc_m: DcBlocker,
|
dc_m: DcBlocker,
|
||||||
dc_l: DcBlocker,
|
dc_l: DcBlocker,
|
||||||
@@ -487,6 +494,8 @@ impl WfmStereoDecoder {
|
|||||||
diff_lpf2: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q2),
|
diff_lpf2: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q2),
|
||||||
diff_q_lpf1: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q1),
|
diff_q_lpf1: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q1),
|
||||||
diff_q_lpf2: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q2),
|
diff_q_lpf2: BiquadLowPass::new(composite_rate_f, STEREO_DIFF_BW_HZ, BW4_Q2),
|
||||||
|
diff_dc: DcBlocker::new(STEREO_DIFF_DC_R),
|
||||||
|
diff_q_dc: DcBlocker::new(STEREO_DIFF_DC_R),
|
||||||
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),
|
||||||
@@ -567,10 +576,12 @@ impl WfmStereoDecoder {
|
|||||||
// --- L-R (diff): 38 kHz demod + 4th-order Butterworth (unblended) ---
|
// --- L-R (diff): 38 kHz demod + 4th-order Butterworth (unblended) ---
|
||||||
// Blend is applied per-band at audio rate in the emit step below.
|
// Blend is applied per-band at audio rate in the emit step below.
|
||||||
let (sin_2p, cos_2p) = (2.0 * pilot_phase_est).sin_cos();
|
let (sin_2p, cos_2p) = (2.0 * pilot_phase_est).sin_cos();
|
||||||
let diff_i = self.diff_lpf2.process(self.diff_lpf1.process(x * (cos_2p * 2.0)));
|
let diff_i = self
|
||||||
|
.diff_dc
|
||||||
|
.process(self.diff_lpf2.process(self.diff_lpf1.process(x * (cos_2p * 2.0))));
|
||||||
let diff_q = self
|
let diff_q = self
|
||||||
.diff_q_lpf2
|
.diff_q_dc
|
||||||
.process(self.diff_q_lpf1.process(x * (-sin_2p * 2.0)));
|
.process(self.diff_q_lpf2.process(self.diff_q_lpf1.process(x * (-sin_2p * 2.0))));
|
||||||
|
|
||||||
// --- Polyphase FIR fractional resampling ---
|
// --- Polyphase FIR fractional resampling ---
|
||||||
// This uses a short windowed-sinc bank instead of cubic interpolation
|
// This uses a short windowed-sinc bank instead of cubic interpolation
|
||||||
|
|||||||
Reference in New Issue
Block a user