[feat](trx-backend-soapysdr): add wfm denoise levels
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -1428,8 +1428,10 @@ function render(update) {
|
|||||||
saveSetting("wfmAudioMode", nextMode);
|
saveSetting("wfmAudioMode", nextMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (wfmDenoiseEl && typeof update.filter.wfm_denoise === "boolean") {
|
if (wfmDenoiseEl && (typeof update.filter.wfm_denoise === "string" || typeof update.filter.wfm_denoise === "boolean")) {
|
||||||
const nextDenoise = update.filter.wfm_denoise ? "on" : "off";
|
const nextDenoise = typeof update.filter.wfm_denoise === "string"
|
||||||
|
? normalizeWfmDenoiseLevel(update.filter.wfm_denoise)
|
||||||
|
: (update.filter.wfm_denoise ? "auto" : "low");
|
||||||
if (wfmDenoiseEl.value !== nextDenoise) {
|
if (wfmDenoiseEl.value !== nextDenoise) {
|
||||||
wfmDenoiseEl.value = nextDenoise;
|
wfmDenoiseEl.value = nextDenoise;
|
||||||
saveSetting("wfmDenoise", nextDenoise);
|
saveSetting("wfmDenoise", nextDenoise);
|
||||||
@@ -2990,6 +2992,13 @@ function levelFromChannels(channels, frameCount) {
|
|||||||
return Math.min(100, rms * 220);
|
return Math.min(100, rms * 220);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeWfmDenoiseLevel(value) {
|
||||||
|
const next = String(value ?? "").toLowerCase();
|
||||||
|
if (next === "auto" || next === "low" || next === "medium" || next === "high") return next;
|
||||||
|
if (next === "off") return "low";
|
||||||
|
return "auto";
|
||||||
|
}
|
||||||
|
|
||||||
if (wfmAudioModeEl) {
|
if (wfmAudioModeEl) {
|
||||||
wfmAudioModeEl.value = loadSetting("wfmAudioMode", "stereo");
|
wfmAudioModeEl.value = loadSetting("wfmAudioMode", "stereo");
|
||||||
wfmAudioModeEl.addEventListener("change", () => {
|
wfmAudioModeEl.addEventListener("change", () => {
|
||||||
@@ -2999,11 +3008,12 @@ if (wfmAudioModeEl) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (wfmDenoiseEl) {
|
if (wfmDenoiseEl) {
|
||||||
wfmDenoiseEl.value = loadSetting("wfmDenoise", "on");
|
wfmDenoiseEl.value = normalizeWfmDenoiseLevel(loadSetting("wfmDenoise", "auto"));
|
||||||
wfmDenoiseEl.addEventListener("change", () => {
|
wfmDenoiseEl.addEventListener("change", () => {
|
||||||
saveSetting("wfmDenoise", wfmDenoiseEl.value);
|
const level = normalizeWfmDenoiseLevel(wfmDenoiseEl.value);
|
||||||
const enabled = wfmDenoiseEl.value !== "off";
|
wfmDenoiseEl.value = level;
|
||||||
postPath(`/set_wfm_denoise?enabled=${enabled ? "true" : "false"}`).catch(() => {});
|
saveSetting("wfmDenoise", level);
|
||||||
|
postPath(`/set_wfm_denoise?level=${encodeURIComponent(level)}`).catch(() => {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (wfmDeemphasisEl) {
|
if (wfmDeemphasisEl) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use tokio::time::{self, Duration};
|
|||||||
use tokio_stream::wrappers::{IntervalStream, WatchStream};
|
use tokio_stream::wrappers::{IntervalStream, WatchStream};
|
||||||
|
|
||||||
use trx_core::radio::freq::Freq;
|
use trx_core::radio::freq::Freq;
|
||||||
|
use trx_core::rig::state::WfmDenoiseLevel;
|
||||||
use trx_core::rig::{RigAccessMethod, RigCapabilities, RigInfo};
|
use trx_core::rig::{RigAccessMethod, RigCapabilities, RigInfo};
|
||||||
use trx_core::{RigCommand, RigRequest, RigSnapshot, RigState};
|
use trx_core::{RigCommand, RigRequest, RigSnapshot, RigState};
|
||||||
use trx_frontend::{FrontendRuntimeContext, RemoteRigEntry};
|
use trx_frontend::{FrontendRuntimeContext, RemoteRigEntry};
|
||||||
@@ -551,7 +552,7 @@ pub async fn set_wfm_stereo(
|
|||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct WfmDenoiseQuery {
|
pub struct WfmDenoiseQuery {
|
||||||
pub enabled: bool,
|
pub level: WfmDenoiseLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/set_wfm_denoise")]
|
#[post("/set_wfm_denoise")]
|
||||||
@@ -559,7 +560,7 @@ pub async fn set_wfm_denoise(
|
|||||||
query: web::Query<WfmDenoiseQuery>,
|
query: web::Query<WfmDenoiseQuery>,
|
||||||
rig_tx: web::Data<mpsc::Sender<RigRequest>>,
|
rig_tx: web::Data<mpsc::Sender<RigRequest>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
send_command(&rig_tx, RigCommand::SetWfmDenoise(query.enabled)).await
|
send_command(&rig_tx, RigCommand::SetWfmDenoise(query.level)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/toggle_aprs_decode")]
|
#[post("/toggle_aprs_decode")]
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ pub type DynResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
|||||||
pub use rig::command::RigCommand;
|
pub use rig::command::RigCommand;
|
||||||
pub use rig::request::RigRequest;
|
pub use rig::request::RigRequest;
|
||||||
pub use rig::response::{RigError, RigResult};
|
pub use rig::response::{RigError, RigResult};
|
||||||
pub use rig::state::{RdsData, RigFilterState, RigMode, RigSnapshot, RigState};
|
pub use rig::state::{RdsData, RigFilterState, RigMode, RigSnapshot, RigState, WfmDenoiseLevel};
|
||||||
pub use rig::AudioSource;
|
pub use rig::AudioSource;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// SPDX-License-Identifier: BSD-2-Clause
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
use crate::radio::freq::Freq;
|
use crate::radio::freq::Freq;
|
||||||
|
use crate::rig::state::WfmDenoiseLevel;
|
||||||
use crate::RigMode;
|
use crate::RigMode;
|
||||||
|
|
||||||
/// Internal command handled by the rig task.
|
/// Internal command handled by the rig task.
|
||||||
@@ -36,6 +37,6 @@ pub enum RigCommand {
|
|||||||
SetSdrGain(f64),
|
SetSdrGain(f64),
|
||||||
SetWfmDeemphasis(u32),
|
SetWfmDeemphasis(u32),
|
||||||
SetWfmStereo(bool),
|
SetWfmStereo(bool),
|
||||||
SetWfmDenoise(bool),
|
SetWfmDenoise(WfmDenoiseLevel),
|
||||||
GetSpectrum,
|
GetSpectrum,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ pub trait RigCat: Rig + Send {
|
|||||||
|
|
||||||
fn set_wfm_denoise<'a>(
|
fn set_wfm_denoise<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
_enabled: bool,
|
_level: state::WfmDenoiseLevel,
|
||||||
) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
|
) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
|
||||||
Box::pin(std::future::ready(Err(
|
Box::pin(std::future::ready(Err(
|
||||||
Box::new(response::RigError::not_supported("set_wfm_denoise"))
|
Box::new(response::RigError::not_supported("set_wfm_denoise"))
|
||||||
|
|||||||
@@ -277,8 +277,17 @@ pub struct RigFilterState {
|
|||||||
pub wfm_stereo: bool,
|
pub wfm_stereo: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub wfm_stereo_detected: bool,
|
pub wfm_stereo_detected: bool,
|
||||||
#[serde(default = "default_wfm_denoise")]
|
#[serde(default = "default_wfm_denoise_level")]
|
||||||
pub wfm_denoise: bool,
|
pub wfm_denoise: WfmDenoiseLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum WfmDenoiseLevel {
|
||||||
|
Auto,
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_wfm_deemphasis_us() -> u32 {
|
fn default_wfm_deemphasis_us() -> u32 {
|
||||||
@@ -289,8 +298,8 @@ fn default_wfm_stereo() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_wfm_denoise() -> bool {
|
fn default_wfm_denoise_level() -> WfmDenoiseLevel {
|
||||||
true
|
WfmDenoiseLevel::Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spectrum data from SDR backends (FFT magnitude over the full capture bandwidth).
|
/// Spectrum data from SDR backends (FFT magnitude over the full capture bandwidth).
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ pub fn client_command_to_rig(cmd: ClientCommand) -> RigCommand {
|
|||||||
RigCommand::SetWfmDeemphasis(deemphasis_us)
|
RigCommand::SetWfmDeemphasis(deemphasis_us)
|
||||||
}
|
}
|
||||||
ClientCommand::SetWfmStereo { enabled } => RigCommand::SetWfmStereo(enabled),
|
ClientCommand::SetWfmStereo { enabled } => RigCommand::SetWfmStereo(enabled),
|
||||||
ClientCommand::SetWfmDenoise { enabled } => RigCommand::SetWfmDenoise(enabled),
|
ClientCommand::SetWfmDenoise { level } => RigCommand::SetWfmDenoise(level),
|
||||||
ClientCommand::GetSpectrum => RigCommand::GetSpectrum,
|
ClientCommand::GetSpectrum => RigCommand::GetSpectrum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ pub fn rig_command_to_client(cmd: RigCommand) -> ClientCommand {
|
|||||||
ClientCommand::SetWfmDeemphasis { deemphasis_us }
|
ClientCommand::SetWfmDeemphasis { deemphasis_us }
|
||||||
}
|
}
|
||||||
RigCommand::SetWfmStereo(enabled) => ClientCommand::SetWfmStereo { enabled },
|
RigCommand::SetWfmStereo(enabled) => ClientCommand::SetWfmStereo { enabled },
|
||||||
RigCommand::SetWfmDenoise(enabled) => ClientCommand::SetWfmDenoise { enabled },
|
RigCommand::SetWfmDenoise(level) => ClientCommand::SetWfmDenoise { level },
|
||||||
RigCommand::GetSpectrum => ClientCommand::GetSpectrum,
|
RigCommand::GetSpectrum => ClientCommand::GetSpectrum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use trx_core::rig::state::RigSnapshot;
|
use trx_core::rig::state::RigSnapshot;
|
||||||
|
use trx_core::WfmDenoiseLevel;
|
||||||
|
|
||||||
/// Command received from network clients (JSON).
|
/// Command received from network clients (JSON).
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -41,7 +42,7 @@ pub enum ClientCommand {
|
|||||||
SetSdrGain { gain_db: f64 },
|
SetSdrGain { gain_db: f64 },
|
||||||
SetWfmDeemphasis { deemphasis_us: u32 },
|
SetWfmDeemphasis { deemphasis_us: u32 },
|
||||||
SetWfmStereo { enabled: bool },
|
SetWfmStereo { enabled: bool },
|
||||||
SetWfmDenoise { enabled: bool },
|
SetWfmDenoise { level: WfmDenoiseLevel },
|
||||||
GetSpectrum,
|
GetSpectrum,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -480,12 +480,12 @@ async fn process_command(
|
|||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
}
|
}
|
||||||
RigCommand::SetWfmDenoise(enabled) => {
|
RigCommand::SetWfmDenoise(level) => {
|
||||||
if let Err(e) = ctx.rig.set_wfm_denoise(enabled).await {
|
if let Err(e) = ctx.rig.set_wfm_denoise(level).await {
|
||||||
return Err(RigError::communication(format!("set_wfm_denoise: {e}")));
|
return Err(RigError::communication(format!("set_wfm_denoise: {e}")));
|
||||||
}
|
}
|
||||||
if let Some(f) = ctx.state.filter.as_mut() {
|
if let Some(f) = ctx.state.filter.as_mut() {
|
||||||
f.wfm_denoise = enabled;
|
f.wfm_denoise = level;
|
||||||
}
|
}
|
||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// SPDX-License-Identifier: BSD-2-Clause
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
use num_complex::Complex;
|
use num_complex::Complex;
|
||||||
use trx_core::rig::state::RdsData;
|
use trx_core::rig::state::{RdsData, WfmDenoiseLevel};
|
||||||
use trx_rds::RdsDecoder;
|
use trx_rds::RdsDecoder;
|
||||||
|
|
||||||
use super::{math::demod_fm_with_prev, DcBlocker};
|
use super::{math::demod_fm_with_prev, DcBlocker};
|
||||||
@@ -387,7 +387,7 @@ impl DenoiseSubband {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct StereoDenoise {
|
struct StereoDenoise {
|
||||||
bands: [DenoiseSubband; DENOISE_BANDS],
|
bands: [DenoiseSubband; DENOISE_BANDS],
|
||||||
enabled: bool,
|
level: WfmDenoiseLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StereoDenoise {
|
impl StereoDenoise {
|
||||||
@@ -397,16 +397,12 @@ impl StereoDenoise {
|
|||||||
});
|
});
|
||||||
Self {
|
Self {
|
||||||
bands,
|
bands,
|
||||||
enabled: true,
|
level: WfmDenoiseLevel::Auto,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process(&mut self, sum: f32, diff_i: f32, diff_q: f32) -> f32 {
|
fn process(&mut self, sum: f32, diff_i: f32, diff_q: f32) -> f32 {
|
||||||
if !self.enabled {
|
|
||||||
return diff_i;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut gain_sum = 0.0_f32;
|
let mut gain_sum = 0.0_f32;
|
||||||
let mut weight_sum = 0.0_f32;
|
let mut weight_sum = 0.0_f32;
|
||||||
for band in &mut self.bands {
|
for band in &mut self.bands {
|
||||||
@@ -420,7 +416,16 @@ impl StereoDenoise {
|
|||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
diff_i * broadband_gain
|
let effective_gain = match self.level {
|
||||||
|
WfmDenoiseLevel::Auto => {
|
||||||
|
let strength = (0.3 + (1.0 - broadband_gain) * 0.7).clamp(0.3, 1.0);
|
||||||
|
1.0 - (1.0 - broadband_gain) * strength
|
||||||
|
}
|
||||||
|
WfmDenoiseLevel::Low => 1.0 - (1.0 - broadband_gain) * 0.35,
|
||||||
|
WfmDenoiseLevel::Medium => 1.0 - (1.0 - broadband_gain) * 0.65,
|
||||||
|
WfmDenoiseLevel::High => broadband_gain,
|
||||||
|
};
|
||||||
|
diff_i * effective_gain.clamp(0.0, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
@@ -489,6 +494,7 @@ impl WfmStereoDecoder {
|
|||||||
output_channels: usize,
|
output_channels: usize,
|
||||||
stereo_enabled: bool,
|
stereo_enabled: bool,
|
||||||
deemphasis_us: u32,
|
deemphasis_us: u32,
|
||||||
|
denoise_level: WfmDenoiseLevel,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let composite_rate_f = composite_rate.max(1) as f32;
|
let composite_rate_f = composite_rate.max(1) as f32;
|
||||||
let output_phase_inc = audio_rate.max(1) as f64 / composite_rate.max(1) as f64;
|
let output_phase_inc = audio_rate.max(1) as f64 / composite_rate.max(1) as f64;
|
||||||
@@ -538,7 +544,11 @@ impl WfmStereoDecoder {
|
|||||||
diff_hist: [0.0; WFM_RESAMP_TAPS],
|
diff_hist: [0.0; WFM_RESAMP_TAPS],
|
||||||
diff_q_hist: [0.0; WFM_RESAMP_TAPS],
|
diff_q_hist: [0.0; WFM_RESAMP_TAPS],
|
||||||
hist_pos: 0,
|
hist_pos: 0,
|
||||||
denoise: StereoDenoise::new(audio_rate.max(1) as f32),
|
denoise: {
|
||||||
|
let mut denoise = StereoDenoise::new(audio_rate.max(1) as f32);
|
||||||
|
denoise.level = denoise_level;
|
||||||
|
denoise
|
||||||
|
},
|
||||||
prev_blend: 0.0,
|
prev_blend: 0.0,
|
||||||
output_phase_inc,
|
output_phase_inc,
|
||||||
output_phase: 0.0,
|
output_phase: 0.0,
|
||||||
@@ -768,8 +778,8 @@ impl WfmStereoDecoder {
|
|||||||
self.output_phase = 0.0;
|
self.output_phase = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_denoise_enabled(&mut self, enabled: bool) {
|
pub fn set_denoise_level(&mut self, level: WfmDenoiseLevel) {
|
||||||
self.denoise.enabled = enabled;
|
self.denoise.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stereo_detected(&self) -> bool {
|
pub fn stereo_detected(&self) -> bool {
|
||||||
@@ -815,7 +825,8 @@ mod tests {
|
|||||||
iq.push(Complex::from_polar(1.0, phase));
|
iq.push(Complex::from_polar(1.0, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decoder = WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50);
|
let mut decoder =
|
||||||
|
WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50, WfmDenoiseLevel::Auto);
|
||||||
let output = decoder.process_iq(&iq);
|
let output = decoder.process_iq(&iq);
|
||||||
|
|
||||||
let skip_samples = (0.2 * audio_rate as f32) as usize;
|
let skip_samples = (0.2 * audio_rate as f32) as usize;
|
||||||
@@ -883,7 +894,14 @@ mod tests {
|
|||||||
iq.push(Complex::from_polar(1.0, phase));
|
iq.push(Complex::from_polar(1.0, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decoder = WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50);
|
let mut decoder = WfmStereoDecoder::new(
|
||||||
|
composite_rate,
|
||||||
|
audio_rate,
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
50,
|
||||||
|
WfmDenoiseLevel::Auto,
|
||||||
|
);
|
||||||
let output = decoder.process_iq(&iq);
|
let output = decoder.process_iq(&iq);
|
||||||
|
|
||||||
let skip_samples = (0.3 * audio_rate as f32) as usize;
|
let skip_samples = (0.3 * audio_rate as f32) as usize;
|
||||||
@@ -947,7 +965,8 @@ mod tests {
|
|||||||
iq.push(Complex::from_polar(1.0, phase));
|
iq.push(Complex::from_polar(1.0, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decoder = WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50);
|
let mut decoder =
|
||||||
|
WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50, WfmDenoiseLevel::Auto);
|
||||||
let output = decoder.process_iq(&iq);
|
let output = decoder.process_iq(&iq);
|
||||||
|
|
||||||
assert!(!decoder.stereo_detected());
|
assert!(!decoder.stereo_detected());
|
||||||
@@ -1031,12 +1050,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_denoise_bypass_when_disabled() {
|
fn test_denoise_low_preserves_more_than_high() {
|
||||||
let mut denoise = StereoDenoise::new(48_000.0);
|
let mut low = StereoDenoise::new(48_000.0);
|
||||||
denoise.enabled = false;
|
low.level = WfmDenoiseLevel::Low;
|
||||||
|
let mut high = StereoDenoise::new(48_000.0);
|
||||||
|
high.level = WfmDenoiseLevel::High;
|
||||||
|
|
||||||
for &value in &[0.0_f32, 0.5, -0.3, 1.0, -1.0, 0.001] {
|
for &value in &[0.0_f32, 0.5, -0.3, 1.0, -1.0, 0.001] {
|
||||||
assert_eq!(denoise.process(0.1, value, 0.2), value);
|
let low_out = low.process(0.1, value, 0.2).abs();
|
||||||
|
let high_out = high.process(0.1, value, 0.2).abs();
|
||||||
|
assert!(low_out + 0.000_001 >= high_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1115,7 +1138,8 @@ mod tests {
|
|||||||
iq.push(Complex::from_polar(1.0, phase));
|
iq.push(Complex::from_polar(1.0, phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut decoder = WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50);
|
let mut decoder =
|
||||||
|
WfmStereoDecoder::new(composite_rate, audio_rate, 2, true, 50, WfmDenoiseLevel::Auto);
|
||||||
let output = decoder.process_iq(&iq);
|
let output = decoder.process_iq(&iq);
|
||||||
|
|
||||||
let skip_samples = (0.2 * audio_rate as f32) as usize;
|
let skip_samples = (0.2 * audio_rate as f32) as usize;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use num_complex::Complex;
|
use num_complex::Complex;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use trx_core::rig::state::{RdsData, RigMode};
|
use trx_core::rig::state::{RdsData, RigMode, WfmDenoiseLevel};
|
||||||
|
|
||||||
use crate::demod::{DcBlocker, Demodulator, SoftAgc, WfmStereoDecoder};
|
use crate::demod::{DcBlocker, Demodulator, SoftAgc, WfmStereoDecoder};
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ pub struct ChannelDsp {
|
|||||||
fir_taps: usize,
|
fir_taps: usize,
|
||||||
wfm_deemphasis_us: u32,
|
wfm_deemphasis_us: u32,
|
||||||
wfm_stereo: bool,
|
wfm_stereo: bool,
|
||||||
wfm_denoise: bool,
|
wfm_denoise: WfmDenoiseLevel,
|
||||||
pub decim_factor: usize,
|
pub decim_factor: usize,
|
||||||
output_channels: usize,
|
output_channels: usize,
|
||||||
pub frame_buf: Vec<f32>,
|
pub frame_buf: Vec<f32>,
|
||||||
@@ -153,6 +153,7 @@ impl ChannelDsp {
|
|||||||
self.output_channels,
|
self.output_channels,
|
||||||
self.wfm_stereo,
|
self.wfm_stereo,
|
||||||
self.wfm_deemphasis_us,
|
self.wfm_deemphasis_us,
|
||||||
|
self.wfm_denoise,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -214,7 +215,7 @@ impl ChannelDsp {
|
|||||||
fir_taps: taps,
|
fir_taps: taps,
|
||||||
wfm_deemphasis_us,
|
wfm_deemphasis_us,
|
||||||
wfm_stereo,
|
wfm_stereo,
|
||||||
wfm_denoise: true,
|
wfm_denoise: WfmDenoiseLevel::Auto,
|
||||||
decim_factor,
|
decim_factor,
|
||||||
output_channels,
|
output_channels,
|
||||||
frame_buf: Vec::with_capacity(frame_size + output_channels),
|
frame_buf: Vec::with_capacity(frame_size + output_channels),
|
||||||
@@ -242,6 +243,7 @@ impl ChannelDsp {
|
|||||||
output_channels,
|
output_channels,
|
||||||
wfm_stereo,
|
wfm_stereo,
|
||||||
wfm_deemphasis_us,
|
wfm_deemphasis_us,
|
||||||
|
WfmDenoiseLevel::Auto,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -279,10 +281,10 @@ impl ChannelDsp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_wfm_denoise(&mut self, enabled: bool) {
|
pub fn set_wfm_denoise(&mut self, level: WfmDenoiseLevel) {
|
||||||
self.wfm_denoise = enabled;
|
self.wfm_denoise = level;
|
||||||
if let Some(decoder) = &mut self.wfm_decoder {
|
if let Some(decoder) = &mut self.wfm_decoder {
|
||||||
decoder.set_denoise_enabled(enabled);
|
decoder.set_denoise_level(level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
use trx_core::radio::freq::{Band, Freq};
|
use trx_core::radio::freq::{Band, Freq};
|
||||||
use trx_core::rig::response::RigError;
|
use trx_core::rig::response::RigError;
|
||||||
use trx_core::rig::state::{RigFilterState, SpectrumData};
|
use trx_core::rig::state::{RigFilterState, SpectrumData, WfmDenoiseLevel};
|
||||||
use trx_core::rig::{
|
use trx_core::rig::{
|
||||||
AudioSource, Rig, RigAccessMethod, RigCapabilities, RigCat, RigInfo, RigStatusFuture,
|
AudioSource, Rig, RigAccessMethod, RigCapabilities, RigCat, RigInfo, RigStatusFuture,
|
||||||
};
|
};
|
||||||
@@ -42,7 +42,7 @@ pub struct SoapySdrRig {
|
|||||||
/// Whether WFM stereo decode is enabled.
|
/// Whether WFM stereo decode is enabled.
|
||||||
wfm_stereo: bool,
|
wfm_stereo: bool,
|
||||||
/// Whether WFM stereo denoise is enabled.
|
/// Whether WFM stereo denoise is enabled.
|
||||||
wfm_denoise: bool,
|
wfm_denoise: WfmDenoiseLevel,
|
||||||
/// Requested hardware gain setting in dB.
|
/// Requested hardware gain setting in dB.
|
||||||
gain_db: f64,
|
gain_db: f64,
|
||||||
/// Optional hard ceiling for the applied hardware gain in dB.
|
/// Optional hard ceiling for the applied hardware gain in dB.
|
||||||
@@ -206,7 +206,7 @@ impl SoapySdrRig {
|
|||||||
retune_cmd,
|
retune_cmd,
|
||||||
wfm_deemphasis_us,
|
wfm_deemphasis_us,
|
||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
wfm_denoise: true,
|
wfm_denoise: WfmDenoiseLevel::Auto,
|
||||||
gain_db,
|
gain_db,
|
||||||
max_gain_db,
|
max_gain_db,
|
||||||
})
|
})
|
||||||
@@ -526,12 +526,12 @@ impl RigCat for SoapySdrRig {
|
|||||||
|
|
||||||
fn set_wfm_denoise<'a>(
|
fn set_wfm_denoise<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
enabled: bool,
|
level: WfmDenoiseLevel,
|
||||||
) -> Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
) -> Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
self.wfm_denoise = enabled;
|
self.wfm_denoise = level;
|
||||||
if let Some(dsp_arc) = self.pipeline.channel_dsps.get(self.primary_channel_idx) {
|
if let Some(dsp_arc) = self.pipeline.channel_dsps.get(self.primary_channel_idx) {
|
||||||
dsp_arc.lock().unwrap().set_wfm_denoise(enabled);
|
dsp_arc.lock().unwrap().set_wfm_denoise(level);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user