use seify::ChannelControls;
use seify::Range;
use std::collections::HashMap;
use crate::runtime::Error;
use crate::runtime::Pmt;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SourceCapabilities {
pub chan: usize,
pub antenna: Option<Vec<String>>,
pub bandwidth: Option<Range>,
pub freq: Option<Range>,
pub gain: Option<Range>,
pub sample_rate: Option<Range>,
}
impl SourceCapabilities {
pub(crate) fn from_channel_controls(chan: usize, controls: &ChannelControls) -> Self {
Self {
chan,
antenna: controls.antennas.clone(),
bandwidth: controls.bandwidth_range.clone(),
freq: controls.frequency_range.clone(),
gain: controls.gain_range.clone(),
sample_rate: controls.sample_rate_range.clone(),
}
}
pub fn to_serializable_pmt(&self) -> Pmt {
let mut map = HashMap::from([("chan".to_string(), Pmt::U64(self.chan as u64))]);
if let Some(antenna) = &self.antenna {
map.insert(
"antenna".to_string(),
Pmt::VecPmt(antenna.iter().cloned().map(Pmt::String).collect()),
);
}
if let Some(bandwidth) = &self.bandwidth {
map.insert("bandwidth".to_string(), bandwidth.into());
}
if let Some(freq) = &self.freq {
map.insert("freq".to_string(), freq.into());
}
if let Some(gain) = &self.gain {
map.insert("gain".to_string(), gain.into());
}
if let Some(sample_rate) = &self.sample_rate {
map.insert("sample_rate".to_string(), sample_rate.into());
}
Pmt::MapStrPmt(map)
}
}
pub(super) fn configured_channel_id(value: &Pmt) -> Option<usize> {
match value {
Pmt::Null | Pmt::Ok => Some(0),
Pmt::U32(chan) => Some(*chan as usize),
Pmt::U64(chan) => Some(*chan as usize),
Pmt::Usize(chan) => Some(*chan),
_ => None,
}
}
impl TryFrom<Pmt> for SourceCapabilities {
type Error = Error;
fn try_from(value: Pmt) -> Result<Self, Self::Error> {
let Pmt::MapStrPmt(mut map) = value else {
return Err(Error::PmtConversionError);
};
let chan = match map.remove("chan") {
Some(Pmt::U32(chan)) => chan as usize,
Some(Pmt::U64(chan)) => chan as usize,
Some(Pmt::Usize(chan)) => chan,
_ => return Err(Error::PmtConversionError),
};
let antenna = match map.remove("antenna") {
Some(Pmt::VecPmt(values)) => Some(
values
.into_iter()
.map(|value| match value {
Pmt::String(value) => Ok(value),
_ => Err(Error::PmtConversionError),
})
.collect::<Result<Vec<_>, _>>()?,
),
None => None,
_ => return Err(Error::PmtConversionError),
};
Ok(Self {
chan,
antenna,
bandwidth: optional_range(map.remove("bandwidth"))?,
freq: optional_range(map.remove("freq"))?,
gain: optional_range(map.remove("gain"))?,
sample_rate: optional_range(map.remove("sample_rate"))?,
})
}
}
fn optional_range(value: Option<Pmt>) -> Result<Option<Range>, Error> {
value
.map(|value| Range::try_from(value).map_err(|_| Error::PmtConversionError))
.transpose()
}
#[cfg(test)]
mod tests {
use super::*;
use seify::RangeItem;
#[test]
fn serializable_pmt_round_trip() {
let capabilities = SourceCapabilities {
chan: 1,
antenna: Some(vec!["RX".to_string(), "RX2".to_string()]),
freq: Some(Range::new(vec![RangeItem::Interval(1e6, 6e9)])),
gain: Some(Range::new(vec![RangeItem::Step(0.0, 40.0, 8.0)])),
sample_rate: Some(Range::new(vec![RangeItem::Value(10e6)])),
..Default::default()
};
let decoded = SourceCapabilities::try_from(capabilities.to_serializable_pmt()).unwrap();
assert_eq!(decoded, capabilities);
}
}