use super::channel::{Channel, ChannelInit, EpochOutput};
use super::LoopConfig;
use crate::iq::{Cf64, IqError, IqSource, SampleSpec};
const CHUNK: usize = 1 << 16;
#[derive(Debug)]
pub struct TrackingBank {
spec: SampleSpec,
channels: Vec<Channel>,
}
impl TrackingBank {
pub fn new(spec: SampleSpec, channels: &[(ChannelInit, LoopConfig)]) -> Result<Self, String> {
let channels = channels
.iter()
.map(|(init, cfg)| Channel::new(&spec, init, cfg))
.collect::<Result<Vec<_>, _>>()?;
Ok(Self { spec, channels })
}
pub fn spec(&self) -> SampleSpec {
self.spec
}
pub fn channels(&self) -> &[Channel] {
&self.channels
}
pub fn process(&mut self, samples: &[Cf64], out: &mut Vec<Vec<EpochOutput>>) {
out.resize_with(self.channels.len(), Vec::new);
for (ch, o) in self.channels.iter_mut().zip(out.iter_mut()) {
ch.process(samples, o);
}
}
pub fn run(
&mut self,
src: &mut dyn IqSource,
max_samples: Option<u64>,
) -> Result<Vec<Vec<EpochOutput>>, IqError> {
let mut out = Vec::new();
out.resize_with(self.channels.len(), Vec::new);
let mut buf = vec![Cf64::default(); CHUNK];
let mut done = 0u64;
loop {
let want = match max_samples {
Some(m) => (m.saturating_sub(done)).min(CHUNK as u64) as usize,
None => CHUNK,
};
if want == 0 {
break;
}
let n = src.read(&mut buf[..want])?;
if n == 0 {
break;
}
self.process(&buf[..n], &mut out);
done += n as u64;
}
Ok(out)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ReplayResult {
pub config: LoopConfig,
pub channels: Vec<Vec<EpochOutput>>,
}
pub fn replay(
src: &mut dyn IqSource,
inits: &[ChannelInit],
configs: &[LoopConfig],
max_samples: Option<u64>,
) -> Result<Vec<ReplayResult>, IqError> {
let pairs: Vec<(ChannelInit, LoopConfig)> = configs
.iter()
.flat_map(|c| inits.iter().map(move |i| (i.clone(), c.clone())))
.collect();
let mut bank = TrackingBank::new(src.spec(), &pairs).map_err(IqError::Format)?;
let mut out = bank.run(src, max_samples)?.into_iter();
Ok(configs
.iter()
.map(|c| ReplayResult {
config: c.clone(),
channels: out.by_ref().take(inits.len()).collect(),
})
.collect())
}