diff --git a/src/trx-server/trx-backend/src/dummy.rs b/src/trx-server/trx-backend/src/dummy.rs index 1c75849..8c78817 100644 --- a/src/trx-server/trx-backend/src/dummy.rs +++ b/src/trx-server/trx-backend/src/dummy.rs @@ -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 diff --git a/src/trx-server/trx-backend/trx-backend-ft450d/src/lib.rs b/src/trx-server/trx-backend/trx-backend-ft450d/src/lib.rs index c454504..9b694a8 100644 --- a/src/trx-server/trx-backend/trx-backend-ft450d/src/lib.rs +++ b/src/trx-server/trx-backend/trx-backend-ft450d/src/lib.rs @@ -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(), diff --git a/src/trx-server/trx-backend/trx-backend-ft817/src/lib.rs b/src/trx-server/trx-backend/trx-backend-ft817/src/lib.rs index c1f2bed..3b4c5fe 100644 --- a/src/trx-server/trx-backend/trx-backend-ft817/src/lib.rs +++ b/src/trx-server/trx-backend/trx-backend-ft817/src/lib.rs @@ -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(), diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs index 3196db7..c6e5bf1 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs @@ -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::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> + 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> + Send + 'a>> { + Box::pin(async move { + tracing::debug!("SoapySdrRig: set_fir_taps -> {}", taps); + self.fir_taps = taps; + Ok(()) + }) + } + + fn filter_state(&self) -> Option { + 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)