[feat](trx-backend-soapysdr): adapt wfm stereo separation gain
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -25,9 +25,11 @@ const PILOT_NOTCH_Q: f32 = 5.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 = 20.0;
|
const PILOT_BPF_Q: f32 = 20.0;
|
||||||
/// Fixed phase trim on the recovered L-R channel.
|
/// Fixed phase trim on the recovered L-R channel.
|
||||||
const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.001;
|
const STEREO_SEPARATION_PHASE_TRIM: f32 = 0.434;
|
||||||
/// Fixed gain trim on the recovered L-R channel.
|
/// Lower bound for dynamic gain trim on the recovered L-R channel.
|
||||||
const STEREO_SEPARATION_GAIN: f32 = 1.00;
|
const STEREO_SEPARATION_GAIN_MIN: f32 = 0.92;
|
||||||
|
/// Upper bound for dynamic gain trim on the recovered L-R channel.
|
||||||
|
const STEREO_SEPARATION_GAIN_MAX: f32 = 1.08;
|
||||||
/// Extra headroom in the stereo matrix.
|
/// Extra headroom in the stereo matrix.
|
||||||
const STEREO_MATRIX_GAIN: f32 = 1.20;
|
const STEREO_MATRIX_GAIN: f32 = 1.20;
|
||||||
/// Stereo detection runs every N composite samples.
|
/// Stereo detection runs every N composite samples.
|
||||||
@@ -461,6 +463,8 @@ pub struct WfmStereoDecoder {
|
|||||||
deemph_r: Deemphasis,
|
deemph_r: Deemphasis,
|
||||||
stereo_detect_level: f32,
|
stereo_detect_level: f32,
|
||||||
stereo_detected: bool,
|
stereo_detected: bool,
|
||||||
|
pilot_lock_level: f32,
|
||||||
|
stereo_separation_gain: f32,
|
||||||
detect_counter: u32,
|
detect_counter: u32,
|
||||||
detect_pilot_mag_acc: f32,
|
detect_pilot_mag_acc: f32,
|
||||||
detect_pilot_abs_acc: f32,
|
detect_pilot_abs_acc: f32,
|
||||||
@@ -521,6 +525,8 @@ impl WfmStereoDecoder {
|
|||||||
deemph_r: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
|
deemph_r: Deemphasis::new(audio_rate.max(1) as f32, deemphasis_us),
|
||||||
stereo_detect_level: 0.0,
|
stereo_detect_level: 0.0,
|
||||||
stereo_detected: false,
|
stereo_detected: false,
|
||||||
|
pilot_lock_level: 0.0,
|
||||||
|
stereo_separation_gain: 1.0,
|
||||||
detect_counter: 0,
|
detect_counter: 0,
|
||||||
detect_pilot_mag_acc: 0.0,
|
detect_pilot_mag_acc: 0.0,
|
||||||
detect_pilot_abs_acc: 0.0,
|
detect_pilot_abs_acc: 0.0,
|
||||||
@@ -585,6 +591,7 @@ impl WfmStereoDecoder {
|
|||||||
let avg_abs = self.detect_pilot_abs_acc * inv_n;
|
let avg_abs = self.detect_pilot_abs_acc * inv_n;
|
||||||
let pilot_coherence = (avg_mag / (avg_abs + 1e-4)).clamp(0.0, 1.0);
|
let pilot_coherence = (avg_mag / (avg_abs + 1e-4)).clamp(0.0, 1.0);
|
||||||
let pilot_lock = ((pilot_coherence - 0.4) / 0.2).clamp(0.0, 1.0);
|
let pilot_lock = ((pilot_coherence - 0.4) / 0.2).clamp(0.0, 1.0);
|
||||||
|
self.pilot_lock_level += 0.12 * (pilot_lock - self.pilot_lock_level);
|
||||||
let stereo_drive = (avg_mag * pilot_lock * 120.0).clamp(0.0, 1.0);
|
let stereo_drive = (avg_mag * pilot_lock * 120.0).clamp(0.0, 1.0);
|
||||||
let detect_coeff = if stereo_drive > self.stereo_detect_level {
|
let detect_coeff = if stereo_drive > self.stereo_detect_level {
|
||||||
0.0008 * STEREO_DETECT_DECIMATION as f32
|
0.0008 * STEREO_DETECT_DECIMATION as f32
|
||||||
@@ -651,7 +658,14 @@ impl WfmStereoDecoder {
|
|||||||
let blend_i =
|
let blend_i =
|
||||||
(self.prev_blend + frac * (stereo_blend_target - self.prev_blend)).clamp(0.0, 1.0);
|
(self.prev_blend + frac * (stereo_blend_target - self.prev_blend)).clamp(0.0, 1.0);
|
||||||
self.prev_blend = stereo_blend_target;
|
self.prev_blend = stereo_blend_target;
|
||||||
let diff_i = (diff_i * trim_cos + diff_q * trim_sin) * STEREO_SEPARATION_GAIN;
|
let separation_drive =
|
||||||
|
(self.pilot_lock_level * 0.65 + self.stereo_detect_level * 0.35).clamp(0.0, 1.0);
|
||||||
|
let separation_target = STEREO_SEPARATION_GAIN_MIN
|
||||||
|
+ (STEREO_SEPARATION_GAIN_MAX - STEREO_SEPARATION_GAIN_MIN) * separation_drive;
|
||||||
|
self.stereo_separation_gain +=
|
||||||
|
0.015 * (separation_target - self.stereo_separation_gain);
|
||||||
|
let diff_i =
|
||||||
|
(diff_i * trim_cos + diff_q * trim_sin) * self.stereo_separation_gain;
|
||||||
let diff_i = self.denoise.process(sum_i, diff_i, diff_q);
|
let diff_i = self.denoise.process(sum_i, diff_i, diff_q);
|
||||||
|
|
||||||
if self.output_channels >= 2 && self.stereo_enabled {
|
if self.output_channels >= 2 && self.stereo_enabled {
|
||||||
@@ -735,6 +749,8 @@ impl WfmStereoDecoder {
|
|||||||
self.deemph_r.reset();
|
self.deemph_r.reset();
|
||||||
self.stereo_detect_level = 0.0;
|
self.stereo_detect_level = 0.0;
|
||||||
self.stereo_detected = false;
|
self.stereo_detected = false;
|
||||||
|
self.pilot_lock_level = 0.0;
|
||||||
|
self.stereo_separation_gain = 1.0;
|
||||||
self.detect_counter = 0;
|
self.detect_counter = 0;
|
||||||
self.detect_pilot_mag_acc = 0.0;
|
self.detect_pilot_mag_acc = 0.0;
|
||||||
self.detect_pilot_abs_acc = 0.0;
|
self.detect_pilot_abs_acc = 0.0;
|
||||||
|
|||||||
Reference in New Issue
Block a user