[fix](trx-backend-soapysdr,trx-frontend-http): reset stereo state and soften wfm top end
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -964,11 +964,19 @@ function resetRdsDisplay() {
|
||||
updateRdsPsOverlay(null);
|
||||
}
|
||||
|
||||
function resetWfmStereoIndicator() {
|
||||
if (!wfmStFlagEl) return;
|
||||
wfmStFlagEl.textContent = "MO";
|
||||
wfmStFlagEl.classList.remove("wfm-st-flag-stereo");
|
||||
wfmStFlagEl.classList.add("wfm-st-flag-mono");
|
||||
}
|
||||
|
||||
function applyLocalTunedFrequency(hz, forceDisplay = false) {
|
||||
if (!Number.isFinite(hz)) return;
|
||||
const freqChanged = lastFreqHz !== hz;
|
||||
if (freqChanged) {
|
||||
resetRdsDisplay();
|
||||
resetWfmStereoIndicator();
|
||||
}
|
||||
lastFreqHz = hz;
|
||||
updateDocumentTitle(lastSpectrumData?.rds ?? null);
|
||||
|
||||
@@ -23,8 +23,6 @@ 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 = 20.0;
|
||||
/// Fixed phase trim on the recovered L-R channel to compensate pilot-path delay.
|
||||
@@ -362,9 +360,6 @@ 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,
|
||||
@@ -431,8 +426,6 @@ 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),
|
||||
@@ -553,11 +546,11 @@ impl WfmStereoDecoder {
|
||||
let right_corr = (sum_i - diff) * 0.5;
|
||||
let left = self
|
||||
.dc_l
|
||||
.process(self.pilot_notch_l.process(self.deemph_l.process(left_corr)))
|
||||
.process(self.deemph_l.process(left_corr))
|
||||
.clamp(-1.0, 1.0);
|
||||
let right = self
|
||||
.dc_r
|
||||
.process(self.pilot_notch_r.process(self.deemph_r.process(right_corr)))
|
||||
.process(self.deemph_r.process(right_corr))
|
||||
.clamp(-1.0, 1.0);
|
||||
output.push(left);
|
||||
output.push(right);
|
||||
@@ -591,6 +584,11 @@ impl WfmStereoDecoder {
|
||||
self.rds_decoder.reset();
|
||||
}
|
||||
|
||||
pub fn reset_stereo_detect(&mut self) {
|
||||
self.stereo_detect_level = 0.0;
|
||||
self.stereo_detected = false;
|
||||
}
|
||||
|
||||
pub fn stereo_detected(&self) -> bool {
|
||||
self.stereo_detected
|
||||
}
|
||||
|
||||
@@ -568,6 +568,13 @@ impl ChannelDsp {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_wfm_state(&mut self) {
|
||||
if let Some(decoder) = &mut self.wfm_decoder {
|
||||
decoder.reset_rds();
|
||||
decoder.reset_stereo_detect();
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a block of raw IQ samples through the full DSP chain.
|
||||
///
|
||||
/// 1. **Batch mixer**: compute the full LO signal for the block at once,
|
||||
@@ -990,7 +997,7 @@ mod tests {
|
||||
fn channel_dsp_processes_silence() {
|
||||
let (pcm_tx, _pcm_rx) = broadcast::channel::<Vec<f32>>(8);
|
||||
let mut dsp =
|
||||
ChannelDsp::new(0.0, &RigMode::USB, 48_000, 8_000, 1, 20, 3000, 75, true, true, 31, pcm_tx);
|
||||
ChannelDsp::new(0.0, &RigMode::USB, 48_000, 8_000, 1, 20, 3000, 75, true, 31, pcm_tx);
|
||||
let block = vec![Complex::new(0.0_f32, 0.0_f32); 4096];
|
||||
dsp.process_block(&block);
|
||||
}
|
||||
@@ -999,7 +1006,7 @@ mod tests {
|
||||
fn channel_dsp_set_mode() {
|
||||
let (pcm_tx, _) = broadcast::channel::<Vec<f32>>(8);
|
||||
let mut dsp =
|
||||
ChannelDsp::new(0.0, &RigMode::USB, 48_000, 8_000, 1, 20, 3000, 75, true, true, 31, pcm_tx);
|
||||
ChannelDsp::new(0.0, &RigMode::USB, 48_000, 8_000, 1, 20, 3000, 75, true, 31, pcm_tx);
|
||||
assert_eq!(dsp.demodulator, Demodulator::Usb);
|
||||
dsp.set_mode(&RigMode::FM);
|
||||
assert_eq!(dsp.demodulator, Demodulator::Fm);
|
||||
@@ -1015,7 +1022,6 @@ mod tests {
|
||||
20,
|
||||
75,
|
||||
true,
|
||||
true,
|
||||
&[(200_000.0, RigMode::USB, 3000, 64)],
|
||||
);
|
||||
assert_eq!(pipeline.pcm_senders.len(), 1);
|
||||
@@ -1024,7 +1030,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pipeline_empty_channels() {
|
||||
let pipeline = SdrPipeline::start(Box::new(MockIqSource), 1_920_000, 48_000, 1, 20, 75, true, true, &[]);
|
||||
let pipeline = SdrPipeline::start(Box::new(MockIqSource), 1_920_000, 48_000, 1, 20, 75, true, &[]);
|
||||
assert_eq!(pipeline.pcm_senders.len(), 0);
|
||||
assert_eq!(pipeline.channel_dsps.len(), 0);
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ impl RigCat for SoapySdrRig {
|
||||
let mut dsp = dsp_arc.lock().unwrap();
|
||||
dsp.set_channel_if_hz(channel_if_hz);
|
||||
if freq_changed {
|
||||
dsp.reset_rds();
|
||||
dsp.reset_wfm_state();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user