use seify::Device;
use seify::DeviceTrait;
use seify::Direction;
use std::collections::HashMap;
use crate::runtime::Error;
use crate::runtime::Pmt;
#[derive(Debug, Default, Clone)]
pub struct Config {
pub chan: Option<usize>,
pub antenna: Option<String>,
pub bandwidth: Option<f64>,
pub freq: Option<f64>,
pub gain: Option<f64>,
pub sample_rate: Option<f64>,
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn to_pmt(&self) -> Pmt {
Pmt::Any(Box::new(self.clone()))
}
pub fn to_serializable_pmt(&self) -> Pmt {
let mut m = HashMap::new();
if let Some(chan) = self.chan {
m.insert("chan".to_string(), Pmt::U64(chan as u64));
}
if let Some(antenna) = &self.antenna {
m.insert("antenna".to_string(), Pmt::String(antenna.clone()));
}
if let Some(bandwidth) = &self.bandwidth {
m.insert("bandwidth".to_string(), Pmt::F64(*bandwidth));
}
if let Some(freq) = &self.freq {
m.insert("freq".to_string(), Pmt::F64(*freq));
}
if let Some(gain) = &self.gain {
m.insert("gain".to_string(), Pmt::F64(*gain));
}
if let Some(sample_rate) = &self.sample_rate {
m.insert("sample_rate".to_string(), Pmt::F64(*sample_rate));
}
Pmt::MapStrPmt(m)
}
pub fn apply<D: DeviceTrait + Clone>(
&self,
dev: &Device<D>,
channels: &[usize],
dir: Direction,
) -> Result<(), Error> {
if let Some(chan) = self.selected_channel(channels)? {
self.apply_channel(dev, dir, chan)?;
} else {
for &chan in channels {
self.apply_channel(dev, dir, chan)?;
}
}
Ok(())
}
fn apply_channel<D: DeviceTrait + Clone>(
&self,
dev: &Device<D>,
dir: Direction,
chan: usize,
) -> Result<(), Error> {
if let Some(ref a) = self.antenna {
dev.set_antenna(dir, chan, a)?;
}
if let Some(b) = self.bandwidth {
dev.set_bandwidth(dir, chan, b)?;
}
if let Some(f) = self.freq {
dev.set_frequency(dir, chan, f)?;
}
if let Some(g) = self.gain {
dev.set_gain(dir, chan, g)?;
}
if let Some(s) = self.sample_rate {
dev.set_sample_rate(dir, chan, s)?;
}
Ok(())
}
fn selected_channel(&self, channels: &[usize]) -> Result<Option<usize>, Error> {
self.chan
.map(|idx| channels.get(idx).copied().ok_or(Error::InvalidParameter))
.transpose()
}
pub fn from<D: DeviceTrait + Clone>(
dev: &Device<D>,
dir: Direction,
channel: usize,
) -> Result<Self, Error> {
let inner = dev.impl_ref::<D>()?;
Ok(Config {
chan: None,
antenna: inner.antenna(dir, channel).ok(),
bandwidth: inner.bandwidth(dir, channel).ok(),
freq: inner.frequency(dir, channel).ok(),
gain: inner.gain(dir, channel).ok().flatten(),
sample_rate: inner.sample_rate(dir, channel).ok(),
})
}
}
impl TryFrom<Pmt> for Config {
type Error = Error;
fn try_from(pmt: Pmt) -> Result<Self, Self::Error> {
match pmt {
Pmt::Any(a) => {
if let Some(cfg) = a.downcast_ref::<Self>() {
Ok(cfg.clone())
} else {
Err(Error::PmtConversionError)
}
}
Pmt::MapStrPmt(mut m) => {
let mut cfg = Config::default();
for (n, v) in m.drain() {
match (n.as_str(), v) {
("chan", Pmt::U32(chan)) => {
cfg.chan = Some(chan as usize);
}
("chan", Pmt::U64(chan)) => {
cfg.chan = Some(chan as usize);
}
("chan", Pmt::Usize(chan)) => {
cfg.chan = Some(chan);
}
("antenna", Pmt::String(p)) => {
cfg.antenna = Some(p.to_owned());
}
("bandwidth", p) => {
cfg.bandwidth = Some(p.try_into()?);
}
("freq", p) => {
cfg.freq = Some(p.try_into()?);
}
("gain", p) => {
cfg.gain = Some(p.try_into()?);
}
("sample_rate", p) => {
cfg.sample_rate = Some(p.try_into()?);
}
_ => warn!("unrecognized key name: {}", n),
}
}
Ok(cfg)
}
_ => Err(Error::PmtConversionError),
}
}
}
#[cfg(test)]
mod tests {
use super::Config;
use crate::runtime::Error;
use crate::runtime::Pmt;
use std::collections::HashMap;
#[test]
fn serializable_pmt_includes_chan() {
let cfg = Config {
chan: Some(1),
freq: Some(102e6),
..Default::default()
};
let Pmt::MapStrPmt(map) = cfg.to_serializable_pmt() else {
panic!("expected map");
};
assert_eq!(map.get("chan"), Some(&Pmt::U64(1)));
assert_eq!(map.get("freq"), Some(&Pmt::F64(102e6)));
}
#[test]
fn map_pmt_parses_chan() {
let cfg = Config::try_from(Pmt::MapStrPmt(HashMap::from([(
"chan".to_string(),
Pmt::U32(2),
)])))
.unwrap();
assert_eq!(cfg.chan, Some(2));
}
#[test]
fn selected_channel_maps_configured_index() {
let cfg = Config {
chan: Some(1),
..Default::default()
};
assert_eq!(cfg.selected_channel(&[2, 4]).unwrap(), Some(4));
}
#[test]
fn selected_channel_rejects_out_of_range_index() {
let cfg = Config {
chan: Some(2),
..Default::default()
};
assert_eq!(cfg.selected_channel(&[2, 4]), Err(Error::InvalidParameter));
}
}