[fix](trx-backend-soapysdr): restore wfm agc and cutoffs
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -11,13 +11,14 @@ const RDS_BPF_Q: f32 = 10.0;
|
|||||||
/// Pilot tone frequency (Hz).
|
/// Pilot tone frequency (Hz).
|
||||||
const PILOT_HZ: f32 = 19_000.0;
|
const PILOT_HZ: f32 = 19_000.0;
|
||||||
/// Audio bandwidth for WFM (Hz).
|
/// Audio bandwidth for WFM (Hz).
|
||||||
/// 17 kHz preserves more top-end while still leaving guard band below the
|
/// 15.8 kHz leaves more guard band below the 19 kHz pilot and reduces
|
||||||
/// 19 kHz pilot.
|
/// top-end artifacts on strong signals while still preserving the useful
|
||||||
const AUDIO_BW_HZ: f32 = 17_000.0;
|
/// broadcast audio range.
|
||||||
|
const AUDIO_BW_HZ: f32 = 15_800.0;
|
||||||
/// Stereo L-R subchannel bandwidth for WFM (Hz).
|
/// Stereo L-R subchannel bandwidth for WFM (Hz).
|
||||||
/// Keep this a bit lower than the mono path because the recovered difference
|
/// Keep this a bit lower than the mono path because the recovered difference
|
||||||
/// signal is noisier and more prone to high-frequency artifacts.
|
/// signal is noisier and more prone to high-frequency artifacts.
|
||||||
const STEREO_DIFF_BW_HZ: f32 = 15_800.0;
|
const STEREO_DIFF_BW_HZ: f32 = 14_500.0;
|
||||||
/// Q values for a proper 4th-order Butterworth cascade (two 2nd-order stages).
|
/// Q values for a proper 4th-order Butterworth cascade (two 2nd-order stages).
|
||||||
/// Stage 1: Q = 1 / (2 cos(π/8))
|
/// Stage 1: Q = 1 / (2 cos(π/8))
|
||||||
const BW4_Q1: f32 = 0.5412;
|
const BW4_Q1: f32 = 0.5412;
|
||||||
@@ -220,6 +221,14 @@ impl SoftAgc {
|
|||||||
(x * gain).clamp(-1.0, 1.0)
|
(x * gain).clamp(-1.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn process_pair(&mut self, left: f32, right: f32) -> (f32, f32) {
|
||||||
|
let gain = self.update_gain(left.abs().max(right.abs()));
|
||||||
|
(
|
||||||
|
(left * gain).clamp(-1.0, 1.0),
|
||||||
|
(right * gain).clamp(-1.0, 1.0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn process_complex(&mut self, x: Complex<f32>) -> Complex<f32> {
|
pub(crate) fn process_complex(&mut self, x: Complex<f32>) -> Complex<f32> {
|
||||||
let gain = self.update_gain((x.re * x.re + x.im * x.im).sqrt());
|
let gain = self.update_gain((x.re * x.re + x.im * x.im).sqrt());
|
||||||
let mut y = x * gain;
|
let mut y = x * gain;
|
||||||
|
|||||||
@@ -690,12 +690,29 @@ impl ChannelDsp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. Demodulate + post-process -----------------------------------
|
// --- 4. Demodulate + post-process -----------------------------------
|
||||||
// WFM: full composite decoder (handles its own DC blocks + deemphasis).
|
// WFM: full composite decoder (handles its own DC blocks + deemphasis),
|
||||||
// All other modes: stateless demodulator → DC blocker (where enabled) → AGC.
|
// then apply post-audio AGC on the decoded PCM. Other modes use the
|
||||||
// WFM bypasses post-audio AGC so the deemphasized stereo path is
|
// normal stateless demodulator → DC blocker (where enabled) → AGC path.
|
||||||
// heard directly; all other modes use the normal post-demod AGC path.
|
|
||||||
let audio = if let Some(decoder) = self.wfm_decoder.as_mut() {
|
let audio = if let Some(decoder) = self.wfm_decoder.as_mut() {
|
||||||
decoder.process_iq(&decimated)
|
let mut out = decoder.process_iq(&decimated);
|
||||||
|
if !self.wfm_stereo && self.output_channels >= 2 {
|
||||||
|
for pair in out.chunks_exact_mut(2) {
|
||||||
|
let mono = self.audio_agc.process(pair[0]);
|
||||||
|
pair[0] = mono;
|
||||||
|
pair[1] = mono;
|
||||||
|
}
|
||||||
|
} else if self.wfm_stereo && self.output_channels >= 2 {
|
||||||
|
for pair in out.chunks_exact_mut(2) {
|
||||||
|
let (left, right) = self.audio_agc.process_pair(pair[0], pair[1]);
|
||||||
|
pair[0] = left;
|
||||||
|
pair[1] = right;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for sample in &mut out {
|
||||||
|
*sample = self.audio_agc.process(*sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out
|
||||||
} else {
|
} else {
|
||||||
let mut raw = self.demodulator.demodulate(&decimated);
|
let mut raw = self.demodulator.demodulate(&decimated);
|
||||||
for s in &mut raw {
|
for s in &mut raw {
|
||||||
|
|||||||
Reference in New Issue
Block a user