[feat](trx-backend): set capability flags; add SDR filter state; add UC-08 tests
Set tx/tx_limit/vfo_switch/filter_controls/signal_meter on all backends: - FT-817, FT-450D, dummy: tx=true, tx_limit=true, vfo_switch=true, filter_controls=false, signal_meter=true - SoapySDR: tx=false, tx_limit=false, vfo_switch=false, filter_controls=true, signal_meter=true SoapySDR backend now stores bandwidth_hz and fir_taps fields; overrides set_bandwidth, set_fir_taps, and filter_state on RigCat to expose live DSP state in the snapshot. Add UC-08 unit tests on dummy backend asserting tx capabilities present and filter_controls absent, and that filter_state returns None. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -104,6 +104,11 @@ impl DummyRig {
|
||||
rit: false,
|
||||
rpt: false,
|
||||
split: false,
|
||||
tx: true,
|
||||
tx_limit: true,
|
||||
vfo_switch: true,
|
||||
filter_controls: false,
|
||||
signal_meter: true,
|
||||
},
|
||||
access: RigAccessMethod::Serial {
|
||||
path: "/dev/null".to_string(),
|
||||
@@ -141,6 +146,35 @@ impl DummyRig {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use trx_core::rig::Rig;
|
||||
|
||||
#[test]
|
||||
fn dummy_has_tx_capabilities() {
|
||||
let rig = DummyRig::new();
|
||||
let caps = &rig.info().capabilities;
|
||||
assert!(caps.tx, "dummy rig should have tx capability");
|
||||
assert!(caps.tx_limit, "dummy rig should have tx_limit capability");
|
||||
assert!(caps.vfo_switch, "dummy rig should have vfo_switch capability");
|
||||
assert!(caps.signal_meter, "dummy rig should have signal_meter capability");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_has_no_filter_controls() {
|
||||
let rig = DummyRig::new();
|
||||
let caps = &rig.info().capabilities;
|
||||
assert!(!caps.filter_controls, "dummy rig should not have filter_controls");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dummy_filter_state_is_none() {
|
||||
let rig = DummyRig::new();
|
||||
assert!(rig.filter_state().is_none(), "dummy rig should return None for filter_state");
|
||||
}
|
||||
}
|
||||
|
||||
impl Rig for DummyRig {
|
||||
fn info(&self) -> &RigInfo {
|
||||
&self.info
|
||||
|
||||
@@ -176,6 +176,11 @@ impl Ft450d {
|
||||
rpt: false,
|
||||
split: false,
|
||||
lock: true,
|
||||
tx: true,
|
||||
tx_limit: true,
|
||||
vfo_switch: true,
|
||||
filter_controls: false,
|
||||
signal_meter: true,
|
||||
},
|
||||
access: RigAccessMethod::Serial {
|
||||
path: path.to_string(),
|
||||
|
||||
@@ -207,6 +207,11 @@ impl Ft817 {
|
||||
rpt: false,
|
||||
split: false,
|
||||
lock: true,
|
||||
tx: true,
|
||||
tx_limit: true,
|
||||
vfo_switch: true,
|
||||
filter_controls: false,
|
||||
signal_meter: true,
|
||||
},
|
||||
access: RigAccessMethod::Serial {
|
||||
path: path.to_string(),
|
||||
|
||||
@@ -12,6 +12,7 @@ use trx_core::rig::{
|
||||
AudioSource, Rig, RigAccessMethod, RigCapabilities, RigCat, RigInfo, RigStatusFuture,
|
||||
};
|
||||
use trx_core::rig::response::RigError;
|
||||
use trx_core::rig::state::RigFilterState;
|
||||
use trx_core::{DynResult, RigMode};
|
||||
|
||||
/// RX-only backend for any SoapySDR-compatible device.
|
||||
@@ -22,6 +23,9 @@ pub struct SoapySdrRig {
|
||||
pipeline: dsp::SdrPipeline,
|
||||
/// Index of the primary channel in `pipeline.channel_dsps`.
|
||||
primary_channel_idx: usize,
|
||||
/// Current filter state of the primary channel (for filter_controls support).
|
||||
bandwidth_hz: u32,
|
||||
fir_taps: u32,
|
||||
}
|
||||
|
||||
impl SoapySdrRig {
|
||||
@@ -108,6 +112,11 @@ impl SoapySdrRig {
|
||||
rit: false,
|
||||
rpt: false,
|
||||
split: false,
|
||||
tx: false,
|
||||
tx_limit: false,
|
||||
vfo_switch: false,
|
||||
filter_controls: true,
|
||||
signal_meter: true,
|
||||
},
|
||||
// No serial/TCP access for SDR devices; carry args in addr field.
|
||||
access: RigAccessMethod::Tcp {
|
||||
@@ -115,12 +124,20 @@ impl SoapySdrRig {
|
||||
},
|
||||
};
|
||||
|
||||
// Initialise filter state from primary channel config (index 0), or defaults.
|
||||
let (bandwidth_hz, fir_taps) = channels
|
||||
.first()
|
||||
.map(|&(_, _, bw, taps)| (bw, taps as u32))
|
||||
.unwrap_or((3000, 64));
|
||||
|
||||
Ok(Self {
|
||||
info,
|
||||
freq: initial_freq,
|
||||
mode: initial_mode,
|
||||
pipeline,
|
||||
primary_channel_idx: 0,
|
||||
bandwidth_hz,
|
||||
fir_taps,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -130,7 +147,7 @@ impl SoapySdrRig {
|
||||
pub fn new(args: &str) -> DynResult<Self> {
|
||||
Self::new_with_config(
|
||||
args,
|
||||
&[], // no channels — pipeline does nothing
|
||||
&[], // no channels — pipeline does nothing; filter defaults applied in new_with_config
|
||||
"auto",
|
||||
30.0,
|
||||
48_000,
|
||||
@@ -299,6 +316,36 @@ impl RigCat for SoapySdrRig {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_bandwidth<'a>(
|
||||
&'a mut self,
|
||||
bandwidth_hz: u32,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
tracing::debug!("SoapySdrRig: set_bandwidth -> {} Hz", bandwidth_hz);
|
||||
self.bandwidth_hz = bandwidth_hz;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn set_fir_taps<'a>(
|
||||
&'a mut self,
|
||||
taps: u32,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
tracing::debug!("SoapySdrRig: set_fir_taps -> {}", taps);
|
||||
self.fir_taps = taps;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn filter_state(&self) -> Option<RigFilterState> {
|
||||
Some(RigFilterState {
|
||||
bandwidth_hz: self.bandwidth_hz,
|
||||
fir_taps: self.fir_taps,
|
||||
cw_center_hz: 700,
|
||||
})
|
||||
}
|
||||
|
||||
/// Override: this backend provides demodulated PCM audio.
|
||||
fn as_audio_source(&self) -> Option<&dyn AudioSource> {
|
||||
Some(self)
|
||||
|
||||
Reference in New Issue
Block a user