[fix](trx-backend-soapysdr,trx-frontend-http): make wfm mono mode real
Co-authored-by: Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -462,6 +462,16 @@ async fn process_command(
|
||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||
return snapshot_from(ctx.state);
|
||||
}
|
||||
RigCommand::SetWfmStereo(enabled) => {
|
||||
if let Err(e) = ctx.rig.set_wfm_stereo(enabled).await {
|
||||
return Err(RigError::communication(format!("set_wfm_stereo: {e}")));
|
||||
}
|
||||
if let Some(f) = ctx.state.filter.as_mut() {
|
||||
f.wfm_stereo = enabled;
|
||||
}
|
||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||
return snapshot_from(ctx.state);
|
||||
}
|
||||
RigCommand::SetWfmDenoise(enabled) => {
|
||||
if let Err(e) = ctx.rig.set_wfm_denoise(enabled).await {
|
||||
return Err(RigError::communication(format!("set_wfm_denoise: {e}")));
|
||||
|
||||
@@ -345,6 +345,7 @@ impl Deemphasis {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WfmStereoDecoder {
|
||||
output_channels: usize,
|
||||
stereo_enabled: bool,
|
||||
rds_decoder: RdsDecoder,
|
||||
rds_bpf: BiquadBandPass,
|
||||
rds_dc: DcBlocker,
|
||||
@@ -393,6 +394,7 @@ impl WfmStereoDecoder {
|
||||
composite_rate: u32,
|
||||
audio_rate: u32,
|
||||
output_channels: usize,
|
||||
stereo_enabled: bool,
|
||||
deemphasis_us: u32,
|
||||
denoise_enabled: bool,
|
||||
) -> Self {
|
||||
@@ -401,6 +403,7 @@ impl WfmStereoDecoder {
|
||||
let deemphasis_us = deemphasis_us as f32;
|
||||
Self {
|
||||
output_channels: output_channels.max(1),
|
||||
stereo_enabled,
|
||||
rds_decoder: RdsDecoder::new(composite_rate),
|
||||
rds_bpf: BiquadBandPass::new(composite_rate_f, RDS_SUBCARRIER_HZ, RDS_BPF_Q),
|
||||
rds_dc: DcBlocker::new(0.995),
|
||||
@@ -499,7 +502,7 @@ impl WfmStereoDecoder {
|
||||
let blend_i = prev_blend + frac * (stereo_blend - prev_blend);
|
||||
|
||||
// --- Deemphasis + DC block + output ---
|
||||
if self.output_channels >= 2 {
|
||||
if self.output_channels >= 2 && self.stereo_enabled {
|
||||
// Apply multiband or single-band stereo blend at audio rate.
|
||||
let diff_denoised = if self.denoise_enabled {
|
||||
// Multiband: attenuates high-frequency diff more aggressively
|
||||
@@ -521,11 +524,14 @@ impl WfmStereoDecoder {
|
||||
// Mono path: apply the pilot notch here so the 19 kHz pilot tone
|
||||
// does not leak into mono audio. Phase matching with diff is not
|
||||
// a concern for mono, so the notch can sit anywhere in the chain.
|
||||
output.push(
|
||||
self.dc_m
|
||||
.process(self.deemph_m.process(self.sum_notch.process(sum_i)))
|
||||
.clamp(-1.0, 1.0),
|
||||
);
|
||||
let mono = self
|
||||
.dc_m
|
||||
.process(self.deemph_m.process(self.sum_notch.process(sum_i)))
|
||||
.clamp(-1.0, 1.0);
|
||||
output.push(mono);
|
||||
if self.output_channels >= 2 {
|
||||
output.push(mono);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,6 +542,10 @@ impl WfmStereoDecoder {
|
||||
self.denoise_enabled = enabled;
|
||||
}
|
||||
|
||||
pub fn set_stereo_enabled(&mut self, enabled: bool) {
|
||||
self.stereo_enabled = enabled;
|
||||
}
|
||||
|
||||
pub fn rds_data(&self) -> Option<RdsData> {
|
||||
self.rds_decoder.snapshot()
|
||||
}
|
||||
|
||||
@@ -309,6 +309,8 @@ pub struct ChannelDsp {
|
||||
fir_taps: usize,
|
||||
/// WFM deemphasis time constant in microseconds.
|
||||
wfm_deemphasis_us: u32,
|
||||
/// Whether WFM stereo decoding is enabled.
|
||||
wfm_stereo: bool,
|
||||
/// Whether multiband stereo denoising is enabled for WFM.
|
||||
wfm_denoise: bool,
|
||||
/// Decimation factor: `sdr_sample_rate / audio_sample_rate`.
|
||||
@@ -410,6 +412,7 @@ impl ChannelDsp {
|
||||
channel_sample_rate,
|
||||
self.audio_sample_rate,
|
||||
self.output_channels,
|
||||
self.wfm_stereo,
|
||||
self.wfm_deemphasis_us,
|
||||
self.wfm_denoise,
|
||||
));
|
||||
@@ -432,6 +435,7 @@ impl ChannelDsp {
|
||||
frame_duration_ms: u16,
|
||||
audio_bandwidth_hz: u32,
|
||||
wfm_deemphasis_us: u32,
|
||||
wfm_stereo: bool,
|
||||
wfm_denoise: bool,
|
||||
fir_taps: usize,
|
||||
pcm_tx: broadcast::Sender<Vec<f32>>,
|
||||
@@ -477,6 +481,7 @@ impl ChannelDsp {
|
||||
audio_bandwidth_hz,
|
||||
fir_taps: taps,
|
||||
wfm_deemphasis_us,
|
||||
wfm_stereo,
|
||||
wfm_denoise,
|
||||
decim_factor,
|
||||
output_channels,
|
||||
@@ -497,6 +502,7 @@ impl ChannelDsp {
|
||||
channel_sample_rate,
|
||||
audio_sample_rate,
|
||||
output_channels,
|
||||
wfm_stereo,
|
||||
wfm_deemphasis_us,
|
||||
wfm_denoise,
|
||||
))
|
||||
@@ -544,6 +550,13 @@ impl ChannelDsp {
|
||||
self.rebuild_filters(true);
|
||||
}
|
||||
|
||||
pub fn set_wfm_stereo(&mut self, enabled: bool) {
|
||||
self.wfm_stereo = enabled;
|
||||
if let Some(decoder) = &mut self.wfm_decoder {
|
||||
decoder.set_stereo_enabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_wfm_denoise(&mut self, enabled: bool) {
|
||||
self.wfm_denoise = enabled;
|
||||
if let Some(decoder) = &mut self.wfm_decoder {
|
||||
@@ -689,6 +702,7 @@ impl SdrPipeline {
|
||||
output_channels: usize,
|
||||
frame_duration_ms: u16,
|
||||
wfm_deemphasis_us: u32,
|
||||
wfm_stereo: bool,
|
||||
wfm_denoise: bool,
|
||||
channels: &[(f64, RigMode, u32, usize)],
|
||||
) -> Self {
|
||||
@@ -711,6 +725,7 @@ impl SdrPipeline {
|
||||
frame_duration_ms,
|
||||
audio_bandwidth_hz,
|
||||
wfm_deemphasis_us,
|
||||
wfm_stereo,
|
||||
wfm_denoise,
|
||||
fir_taps,
|
||||
pcm_tx.clone(),
|
||||
|
||||
@@ -39,6 +39,8 @@ pub struct SoapySdrRig {
|
||||
retune_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
||||
/// Current WFM deemphasis setting in microseconds.
|
||||
wfm_deemphasis_us: u32,
|
||||
/// Whether WFM stereo decode is enabled.
|
||||
wfm_stereo: bool,
|
||||
/// Whether multiband WFM stereo denoising is enabled.
|
||||
wfm_denoise: bool,
|
||||
}
|
||||
@@ -117,6 +119,7 @@ impl SoapySdrRig {
|
||||
audio_channels,
|
||||
frame_duration_ms,
|
||||
wfm_deemphasis_us,
|
||||
true, // wfm_stereo: enabled by default
|
||||
true, // wfm_denoise: enabled by default
|
||||
channels,
|
||||
);
|
||||
@@ -185,6 +188,7 @@ impl SoapySdrRig {
|
||||
center_hz: hardware_center_hz,
|
||||
retune_cmd,
|
||||
wfm_deemphasis_us,
|
||||
wfm_stereo: true,
|
||||
wfm_denoise: true,
|
||||
})
|
||||
}
|
||||
@@ -464,6 +468,19 @@ impl RigCat for SoapySdrRig {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_wfm_stereo<'a>(
|
||||
&'a mut self,
|
||||
enabled: bool,
|
||||
) -> Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
self.wfm_stereo = enabled;
|
||||
if let Some(dsp_arc) = self.pipeline.channel_dsps.get(self.primary_channel_idx) {
|
||||
dsp_arc.lock().unwrap().set_wfm_stereo(enabled);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_wfm_denoise<'a>(
|
||||
&'a mut self,
|
||||
enabled: bool,
|
||||
@@ -483,6 +500,7 @@ impl RigCat for SoapySdrRig {
|
||||
fir_taps: self.fir_taps,
|
||||
cw_center_hz: 700,
|
||||
wfm_deemphasis_us: self.wfm_deemphasis_us,
|
||||
wfm_stereo: self.wfm_stereo,
|
||||
wfm_denoise: self.wfm_denoise,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user