[feat](trx-rs): add CCI/ACI bars to WFM panel with RDS mitigation
Estimate Co-Channel Interference (CCI) from pilot tone quadrature leakage and coherence degradation. Estimate Adjacent Channel Interference (ACI) from CMA equalizer tap deviation from identity. Both metrics (0-100 scale) are surfaced through RigFilterState and displayed as colour-coded bars in the WFM control panel. The RDS decoder quality parameter is now adaptively penalised when CCI/ACI levels are elevated, reducing block-error rate under interference conditions. https://claude.ai/code/session_016EKzep42RCvE4GxvvRaCwu Signed-off-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1701,6 +1701,7 @@ function resetDecoderStateOnRigSwitch() {
|
|||||||
vchanRdsById = new Map();
|
vchanRdsById = new Map();
|
||||||
resetRdsDisplay();
|
resetRdsDisplay();
|
||||||
resetWfmStereoIndicator();
|
resetWfmStereoIndicator();
|
||||||
|
resetIntfBars();
|
||||||
|
|
||||||
// Spectrum — clear stale data from previous rig's SDR
|
// Spectrum — clear stale data from previous rig's SDR
|
||||||
lastSpectrumData = null;
|
lastSpectrumData = null;
|
||||||
@@ -1728,6 +1729,23 @@ function resetWfmStereoIndicator() {
|
|||||||
wfmStFlagEl.classList.add("wfm-st-flag-mono");
|
wfmStFlagEl.classList.add("wfm-st-flag-mono");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateIntfBar(fillEl, valEl, level) {
|
||||||
|
if (!fillEl || !valEl) return;
|
||||||
|
const v = Math.round(Math.min(Math.max(level, 0), 100));
|
||||||
|
valEl.textContent = String(v);
|
||||||
|
fillEl.style.width = v + "%";
|
||||||
|
fillEl.classList.toggle("wfm-intf-warn", v >= 35 && v < 65);
|
||||||
|
fillEl.classList.toggle("wfm-intf-high", v >= 65);
|
||||||
|
if (v < 35) {
|
||||||
|
fillEl.classList.remove("wfm-intf-warn", "wfm-intf-high");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetIntfBars() {
|
||||||
|
updateIntfBar(wfmCciFillEl, wfmCciValEl, 0);
|
||||||
|
updateIntfBar(wfmAciFillEl, wfmAciValEl, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Fast CSS-based frequency/BW marker positioning ──────────────────────────
|
// ── Fast CSS-based frequency/BW marker positioning ──────────────────────────
|
||||||
// These lightweight DOM elements reposition via `transform: translateX()`
|
// These lightweight DOM elements reposition via `transform: translateX()`
|
||||||
// which is GPU-composited — zero layout/paint cost. The full WebGL overlay
|
// which is GPU-composited — zero layout/paint cost. The full WebGL overlay
|
||||||
@@ -1800,6 +1818,7 @@ function applyLocalTunedFrequency(hz, forceDisplay = false) {
|
|||||||
primaryRds = null;
|
primaryRds = null;
|
||||||
resetRdsDisplay();
|
resetRdsDisplay();
|
||||||
resetWfmStereoIndicator();
|
resetWfmStereoIndicator();
|
||||||
|
resetIntfBars();
|
||||||
}
|
}
|
||||||
lastFreqHz = hz;
|
lastFreqHz = hz;
|
||||||
window.lastFreqHz = lastFreqHz;
|
window.lastFreqHz = lastFreqHz;
|
||||||
@@ -2955,6 +2974,8 @@ function render(update) {
|
|||||||
wfmStFlagEl.classList.toggle("wfm-st-flag-stereo", detected);
|
wfmStFlagEl.classList.toggle("wfm-st-flag-stereo", detected);
|
||||||
wfmStFlagEl.classList.toggle("wfm-st-flag-mono", !detected);
|
wfmStFlagEl.classList.toggle("wfm-st-flag-mono", !detected);
|
||||||
}
|
}
|
||||||
|
if (typeof update.filter.wfm_cci === "number") updateIntfBar(wfmCciFillEl, wfmCciValEl, update.filter.wfm_cci);
|
||||||
|
if (typeof update.filter.wfm_aci === "number") updateIntfBar(wfmAciFillEl, wfmAciValEl, update.filter.wfm_aci);
|
||||||
if (samStereoWidthEl && typeof update.filter.sam_stereo_width === "number") {
|
if (samStereoWidthEl && typeof update.filter.sam_stereo_width === "number") {
|
||||||
samStereoWidthEl.value = String(Math.round(update.filter.sam_stereo_width * 100));
|
samStereoWidthEl.value = String(Math.round(update.filter.sam_stereo_width * 100));
|
||||||
}
|
}
|
||||||
@@ -7494,6 +7515,10 @@ const sdrLnaGainEl = document.getElementById("sdr-lna-gain-db");
|
|||||||
const sdrLnaGainSetBtn = document.getElementById("sdr-lna-gain-set");
|
const sdrLnaGainSetBtn = document.getElementById("sdr-lna-gain-set");
|
||||||
const sdrAgcEl = document.getElementById("sdr-agc-enabled");
|
const sdrAgcEl = document.getElementById("sdr-agc-enabled");
|
||||||
const wfmStFlagEl = document.getElementById("wfm-st-flag");
|
const wfmStFlagEl = document.getElementById("wfm-st-flag");
|
||||||
|
const wfmCciFillEl = document.getElementById("wfm-cci-fill");
|
||||||
|
const wfmCciValEl = document.getElementById("wfm-cci-val");
|
||||||
|
const wfmAciFillEl = document.getElementById("wfm-aci-fill");
|
||||||
|
const wfmAciValEl = document.getElementById("wfm-aci-val");
|
||||||
const samControlsCol = document.getElementById("sam-controls-col");
|
const samControlsCol = document.getElementById("sam-controls-col");
|
||||||
const samStereoWidthEl = document.getElementById("sam-stereo-width");
|
const samStereoWidthEl = document.getElementById("sam-stereo-width");
|
||||||
const samCarrierSyncEl = document.getElementById("sam-carrier-sync");
|
const samCarrierSyncEl = document.getElementById("sam-carrier-sync");
|
||||||
|
|||||||
@@ -250,6 +250,14 @@
|
|||||||
<span class="wfm-control-label">Pilot</span>
|
<span class="wfm-control-label">Pilot</span>
|
||||||
<span id="wfm-st-flag" class="wfm-st-flag wfm-st-flag-mono">MO</span>
|
<span id="wfm-st-flag" class="wfm-st-flag wfm-st-flag-mono">MO</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="wfm-control wfm-intf-bar-wrap" aria-label="Co-Channel Interference">
|
||||||
|
<span class="wfm-control-label">CCI</span>
|
||||||
|
<span class="wfm-intf-bar"><span id="wfm-cci-fill" class="wfm-intf-fill"></span><span id="wfm-cci-val" class="wfm-intf-val">0</span></span>
|
||||||
|
</label>
|
||||||
|
<label class="wfm-control wfm-intf-bar-wrap" aria-label="Adjacent Channel Interference">
|
||||||
|
<span class="wfm-control-label">ACI</span>
|
||||||
|
<span class="wfm-intf-bar"><span id="wfm-aci-fill" class="wfm-intf-fill"></span><span id="wfm-aci-val" class="wfm-intf-val">0</span></span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="label"><span>WFM</span></div>
|
<div class="label"><span>WFM</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -265,6 +265,47 @@ input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem;
|
|||||||
border-color: var(--border-light);
|
border-color: var(--border-light);
|
||||||
background: color-mix(in srgb, var(--input-bg) 92%, var(--panel-2));
|
background: color-mix(in srgb, var(--input-bg) 92%, var(--panel-2));
|
||||||
}
|
}
|
||||||
|
.wfm-intf-bar-wrap {
|
||||||
|
min-width: 3.6rem;
|
||||||
|
}
|
||||||
|
.wfm-intf-bar {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 3.6rem;
|
||||||
|
min-height: 2.1rem;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid var(--border-light);
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--input-bg);
|
||||||
|
}
|
||||||
|
.wfm-intf-fill {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 0%;
|
||||||
|
border-radius: 5px 0 0 5px;
|
||||||
|
background: color-mix(in srgb, #4fc3f7 45%, transparent);
|
||||||
|
transition: width 0.25s ease, background 0.35s ease;
|
||||||
|
}
|
||||||
|
.wfm-intf-fill.wfm-intf-warn {
|
||||||
|
background: color-mix(in srgb, #ffa726 55%, transparent);
|
||||||
|
}
|
||||||
|
.wfm-intf-fill.wfm-intf-high {
|
||||||
|
background: color-mix(in srgb, #ef5350 55%, transparent);
|
||||||
|
}
|
||||||
|
.wfm-intf-val {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
.controls-col-center::after {
|
.controls-col-center::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
|
|||||||
@@ -338,6 +338,12 @@ pub struct RigFilterState {
|
|||||||
pub wfm_stereo_detected: bool,
|
pub wfm_stereo_detected: bool,
|
||||||
#[serde(default = "default_wfm_denoise_level")]
|
#[serde(default = "default_wfm_denoise_level")]
|
||||||
pub wfm_denoise: WfmDenoiseLevel,
|
pub wfm_denoise: WfmDenoiseLevel,
|
||||||
|
/// Co-Channel Interference level (0–100 scale).
|
||||||
|
#[serde(default)]
|
||||||
|
pub wfm_cci: u8,
|
||||||
|
/// Adjacent Channel Interference level (0–100 scale).
|
||||||
|
#[serde(default)]
|
||||||
|
pub wfm_aci: u8,
|
||||||
/// SAM stereo width (0.0 = mono, 1.0 = full stereo).
|
/// SAM stereo width (0.0 = mono, 1.0 = full stereo).
|
||||||
#[serde(default = "default_sam_stereo_width")]
|
#[serde(default = "default_sam_stereo_width")]
|
||||||
pub sam_stereo_width: f32,
|
pub sam_stereo_width: f32,
|
||||||
|
|||||||
@@ -326,6 +326,8 @@ mod tests {
|
|||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
wfm_stereo_detected: false,
|
wfm_stereo_detected: false,
|
||||||
wfm_denoise: trx_core::WfmDenoiseLevel::Auto,
|
wfm_denoise: trx_core::WfmDenoiseLevel::Auto,
|
||||||
|
wfm_cci: 0,
|
||||||
|
wfm_aci: 0,
|
||||||
sam_stereo_width: 1.0,
|
sam_stereo_width: 1.0,
|
||||||
sam_carrier_sync: true,
|
sam_carrier_sync: true,
|
||||||
}),
|
}),
|
||||||
@@ -372,6 +374,8 @@ mod tests {
|
|||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
wfm_stereo_detected: true,
|
wfm_stereo_detected: true,
|
||||||
wfm_denoise: trx_core::WfmDenoiseLevel::Auto,
|
wfm_denoise: trx_core::WfmDenoiseLevel::Auto,
|
||||||
|
wfm_cci: 12,
|
||||||
|
wfm_aci: 45,
|
||||||
sam_stereo_width: 0.5,
|
sam_stereo_width: 0.5,
|
||||||
sam_carrier_sync: false,
|
sam_carrier_sync: false,
|
||||||
}),
|
}),
|
||||||
@@ -384,6 +388,8 @@ mod tests {
|
|||||||
assert_eq!(f.sdr_gain_db, Some(18.0));
|
assert_eq!(f.sdr_gain_db, Some(18.0));
|
||||||
assert_eq!(f.wfm_deemphasis_us, 50);
|
assert_eq!(f.wfm_deemphasis_us, 50);
|
||||||
assert!(f.wfm_stereo_detected);
|
assert!(f.wfm_stereo_detected);
|
||||||
|
assert_eq!(f.wfm_cci, 12);
|
||||||
|
assert_eq!(f.wfm_aci, 45);
|
||||||
assert_eq!(f.sam_stereo_width, 0.5);
|
assert_eq!(f.sam_stereo_width, 0.5);
|
||||||
assert!(!f.sam_carrier_sync);
|
assert!(!f.sam_carrier_sync);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -606,6 +606,10 @@ pub struct WfmStereoDecoder {
|
|||||||
prev_blend: f32,
|
prev_blend: f32,
|
||||||
output_phase_inc: f64,
|
output_phase_inc: f64,
|
||||||
output_phase: f64,
|
output_phase: f64,
|
||||||
|
/// Smoothed CCI (Co-Channel Interference) estimate, 0–100 scale.
|
||||||
|
cci_level: f32,
|
||||||
|
/// Smoothed ACI (Adjacent Channel Interference) estimate, 0–100 scale.
|
||||||
|
aci_level: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WfmStereoDecoder {
|
impl WfmStereoDecoder {
|
||||||
@@ -674,6 +678,8 @@ impl WfmStereoDecoder {
|
|||||||
prev_blend: 0.0,
|
prev_blend: 0.0,
|
||||||
output_phase_inc,
|
output_phase_inc,
|
||||||
output_phase: 0.0,
|
output_phase: 0.0,
|
||||||
|
cci_level: 0.0,
|
||||||
|
aci_level: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +692,28 @@ impl WfmStereoDecoder {
|
|||||||
// The constant-modulus property of FM drives tap adaptation without a
|
// The constant-modulus property of FM drives tap adaptation without a
|
||||||
// training sequence, suppressing adjacent-channel interference.
|
// training sequence, suppressing adjacent-channel interference.
|
||||||
let equalized: Vec<Complex<f32>> = samples.iter().map(|&s| self.cma.process(s)).collect();
|
let equalized: Vec<Complex<f32>> = samples.iter().map(|&s| self.cma.process(s)).collect();
|
||||||
|
|
||||||
|
// ACI estimation: measure CMA tap deviation from identity.
|
||||||
|
// When adjacent-channel interference is present the equalizer drives its
|
||||||
|
// taps away from the centre-tap-only identity configuration.
|
||||||
|
{
|
||||||
|
let mut tap_dev = 0.0_f32;
|
||||||
|
for (k, &tap) in self.cma.taps.iter().enumerate() {
|
||||||
|
if k == CMA_N_TAPS / 2 {
|
||||||
|
// Centre tap: deviation from (1+0j).
|
||||||
|
tap_dev += (tap - Complex::new(1.0, 0.0)).norm_sqr();
|
||||||
|
} else {
|
||||||
|
tap_dev += tap.norm_sqr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Map deviation to 0–100 scale. Empirically, deviation > 0.5 is
|
||||||
|
// heavy ACI; scale linearly with a sqrt compressor for readability.
|
||||||
|
let raw_aci = (tap_dev.sqrt() * 100.0 / 0.7).clamp(0.0, 100.0);
|
||||||
|
// Smooth with ~200 ms time constant at block rate.
|
||||||
|
let alpha = 0.08_f32;
|
||||||
|
self.aci_level += alpha * (raw_aci - self.aci_level);
|
||||||
|
}
|
||||||
|
|
||||||
let disc = demod_fm_with_prev(&equalized, &mut self.prev_iq);
|
let disc = demod_fm_with_prev(&equalized, &mut self.prev_iq);
|
||||||
let mut output = Vec::with_capacity(
|
let mut output = Vec::with_capacity(
|
||||||
((samples.len() as f64 * self.output_phase_inc).ceil() as usize + 1)
|
((samples.len() as f64 * self.output_phase_inc).ceil() as usize + 1)
|
||||||
@@ -745,6 +773,23 @@ impl WfmStereoDecoder {
|
|||||||
} else if self.stereo_detect_level > 0.6 {
|
} else if self.stereo_detect_level > 0.6 {
|
||||||
self.stereo_detected = true;
|
self.stereo_detected = true;
|
||||||
}
|
}
|
||||||
|
// CCI estimation: pilot quadrature leakage indicates co-channel
|
||||||
|
// interference. A clean pilot has all energy in I; CCI adds
|
||||||
|
// incoherent 19 kHz energy that leaks into Q.
|
||||||
|
let q_abs = (self.pilot_q_lp.y).abs();
|
||||||
|
let i_abs = (self.pilot_i_lp.y).abs();
|
||||||
|
let cci_ratio = if i_abs > 1e-8 {
|
||||||
|
(q_abs / i_abs).clamp(0.0, 1.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
// Also factor in coherence drop: low coherence at moderate
|
||||||
|
// pilot amplitude implies multipath / co-channel.
|
||||||
|
let coherence_penalty = (1.0 - pilot_coherence).clamp(0.0, 1.0);
|
||||||
|
let raw_cci = ((cci_ratio * 0.6 + coherence_penalty * 0.4) * 100.0).clamp(0.0, 100.0);
|
||||||
|
let cci_alpha = 0.08_f32;
|
||||||
|
self.cci_level += cci_alpha * (raw_cci - self.cci_level);
|
||||||
|
|
||||||
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;
|
||||||
@@ -770,7 +815,13 @@ impl WfmStereoDecoder {
|
|||||||
self.rds_decoder.clear_pilot_ref();
|
self.rds_decoder.clear_pilot_ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
let rds_quality = (0.35 + pilot_mag * 20.0).clamp(0.35, 1.0);
|
// Adaptive RDS quality: base metric from pilot strength, then
|
||||||
|
// penalise for CCI and ACI so the decoder weights bits lower when
|
||||||
|
// interference is present (reduces block-error rate).
|
||||||
|
let rds_base = (0.35 + pilot_mag * 20.0).clamp(0.35, 1.0);
|
||||||
|
let cci_penalty = 1.0 - (self.cci_level * 0.006).clamp(0.0, 0.45);
|
||||||
|
let aci_penalty = 1.0 - (self.aci_level * 0.004).clamp(0.0, 0.30);
|
||||||
|
let rds_quality = (rds_base * cci_penalty * aci_penalty).clamp(0.10, 1.0);
|
||||||
let rds_clean = self.rds_dc.process(self.rds_bpf.process(x));
|
let rds_clean = self.rds_dc.process(self.rds_bpf.process(x));
|
||||||
let _ = self.rds_decoder.process_sample(rds_clean, rds_quality);
|
let _ = self.rds_decoder.process_sample(rds_clean, rds_quality);
|
||||||
|
|
||||||
@@ -916,6 +967,8 @@ impl WfmStereoDecoder {
|
|||||||
self.denoise.reset();
|
self.denoise.reset();
|
||||||
self.prev_blend = 0.0;
|
self.prev_blend = 0.0;
|
||||||
self.output_phase = 0.0;
|
self.output_phase = 0.0;
|
||||||
|
self.cci_level = 0.0;
|
||||||
|
self.aci_level = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_denoise_level(&mut self, level: WfmDenoiseLevel) {
|
pub fn set_denoise_level(&mut self, level: WfmDenoiseLevel) {
|
||||||
@@ -925,6 +978,16 @@ impl WfmStereoDecoder {
|
|||||||
pub fn stereo_detected(&self) -> bool {
|
pub fn stereo_detected(&self) -> bool {
|
||||||
self.stereo_detected
|
self.stereo_detected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Current CCI (Co-Channel Interference) level, 0–100 scale.
|
||||||
|
pub fn cci_level(&self) -> u8 {
|
||||||
|
self.cci_level.round().clamp(0.0, 100.0) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Current ACI (Adjacent Channel Interference) level, 0–100 scale.
|
||||||
|
pub fn aci_level(&self) -> u8 {
|
||||||
|
self.aci_level.round().clamp(0.0, 100.0) as u8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -601,6 +601,20 @@ impl ChannelDsp {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn wfm_cci(&self) -> u8 {
|
||||||
|
self.wfm_decoder
|
||||||
|
.as_ref()
|
||||||
|
.map(WfmStereoDecoder::cci_level)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wfm_aci(&self) -> u8 {
|
||||||
|
self.wfm_decoder
|
||||||
|
.as_ref()
|
||||||
|
.map(WfmStereoDecoder::aci_level)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset_rds(&mut self) {
|
pub fn reset_rds(&mut self) {
|
||||||
if let Some(decoder) = &mut self.wfm_decoder {
|
if let Some(decoder) = &mut self.wfm_decoder {
|
||||||
decoder.reset_rds();
|
decoder.reset_rds();
|
||||||
|
|||||||
@@ -895,14 +895,20 @@ impl RigSdr for SoapySdrRig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn filter_state(&self) -> Option<RigFilterState> {
|
fn filter_state(&self) -> Option<RigFilterState> {
|
||||||
let wfm_stereo_detected = self
|
let (wfm_stereo_detected, wfm_cci, wfm_aci) = {
|
||||||
.pipeline
|
let dsps = self.pipeline.channel_dsps.read().unwrap();
|
||||||
.channel_dsps
|
let dsp = dsps.get(self.primary_channel_idx);
|
||||||
.read()
|
let stereo = dsp
|
||||||
.unwrap()
|
.and_then(|d| d.lock().ok().map(|d| d.wfm_stereo_detected()))
|
||||||
.get(self.primary_channel_idx)
|
|
||||||
.and_then(|dsp| dsp.lock().ok().map(|d| d.wfm_stereo_detected()))
|
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
let cci = dsp
|
||||||
|
.and_then(|d| d.lock().ok().map(|d| d.wfm_cci()))
|
||||||
|
.unwrap_or(0);
|
||||||
|
let aci = dsp
|
||||||
|
.and_then(|d| d.lock().ok().map(|d| d.wfm_aci()))
|
||||||
|
.unwrap_or(0);
|
||||||
|
(stereo, cci, aci)
|
||||||
|
};
|
||||||
Some(RigFilterState {
|
Some(RigFilterState {
|
||||||
bandwidth_hz: self.bandwidth_hz,
|
bandwidth_hz: self.bandwidth_hz,
|
||||||
cw_center_hz: 700,
|
cw_center_hz: 700,
|
||||||
@@ -921,6 +927,8 @@ impl RigSdr for SoapySdrRig {
|
|||||||
wfm_stereo: self.wfm_stereo,
|
wfm_stereo: self.wfm_stereo,
|
||||||
wfm_stereo_detected,
|
wfm_stereo_detected,
|
||||||
wfm_denoise: self.wfm_denoise,
|
wfm_denoise: self.wfm_denoise,
|
||||||
|
wfm_cci,
|
||||||
|
wfm_aci,
|
||||||
sam_stereo_width: self.sam_stereo_width,
|
sam_stereo_width: self.sam_stereo_width,
|
||||||
sam_carrier_sync: self.sam_carrier_sync,
|
sam_carrier_sync: self.sam_carrier_sync,
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user