use pictorus_block_data::{BlockData as OldBlockData, FromPass};
use pictorus_traits::{HasIc, Pass, ProcessBlock};
use crate::traits::CopyInto;
pub struct DelayBlock<T: Pass + Default + Copy, const N: usize>
where
pictorus_block_data::BlockData: FromPass<T>,
{
samples: [T; N],
sample_index: usize,
initial_accumulation: bool,
output: T,
pub data: OldBlockData,
}
impl<T: Pass + Default + Copy + CopyInto<T>, const N: usize> HasIc for DelayBlock<T, N>
where
pictorus_block_data::BlockData: FromPass<T>,
{
fn new(parameters: &Self::Parameters) -> Self {
let mut output = Self::default();
T::copy_into(parameters.ic.as_by(), &mut output.output);
output.data = OldBlockData::from_pass(parameters.ic.as_by());
output
}
}
impl<T: Pass + Default + Copy, const N: usize> Default for DelayBlock<T, N>
where
pictorus_block_data::BlockData: FromPass<T>,
{
fn default() -> Self {
Self {
samples: [T::default(); N],
initial_accumulation: true,
output: T::default(),
sample_index: 0,
data: <OldBlockData as FromPass<T>>::from_pass(T::default().as_by()),
}
}
}
impl<T: Pass + Default + Copy + CopyInto<T>, const N: usize> ProcessBlock for DelayBlock<T, N>
where
pictorus_block_data::BlockData: FromPass<T>,
{
type Inputs = T;
type Output = T;
type Parameters = Parameters<T>;
fn process<'b>(
&'b mut self,
parameters: &Self::Parameters,
_context: &dyn pictorus_traits::Context,
inputs: pictorus_traits::PassBy<'_, Self::Inputs>,
) -> pictorus_traits::PassBy<'b, Self::Output> {
let effective_delay = if parameters.is_delayed {
N.saturating_sub(1)
} else {
N
};
if self.initial_accumulation {
self.output = parameters.ic;
} else if effective_delay == 0 {
T::copy_into(inputs, &mut self.output);
} else {
let output_index =
(self.sample_index + self.samples.len() - effective_delay) % self.samples.len();
self.output = self.samples[output_index];
}
T::copy_into(inputs, &mut self.samples[self.sample_index]);
self.sample_index += 1;
if self.sample_index >= self.samples.len() {
self.initial_accumulation = false;
self.sample_index = 0;
}
self.data = OldBlockData::from_pass(self.output.as_by());
self.output.as_by()
}
}
pub struct Parameters<T: Pass + Default + Copy> {
ic: T,
is_delayed: bool,
}
impl<T: Pass + Default + Copy + CopyInto<T>> Parameters<T> {
pub fn new(ic: T, is_delayed: bool) -> Self {
Self { ic, is_delayed }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::StubContext;
use pictorus_traits::Matrix;
#[test]
fn test_delay_block_scalar() {
let mut block = DelayBlock::<f64, 3>::default();
let parameters = Parameters {
ic: 0.0,
is_delayed: false,
};
let context = StubContext::default();
assert_eq!(block.process(¶meters, &context, 1.0), 0.0);
assert_eq!(block.process(¶meters, &context, 2.0), 0.0);
assert_eq!(block.process(¶meters, &context, 3.0), 0.0);
assert_eq!(block.process(¶meters, &context, 4.0), 1.0);
assert_eq!(block.process(¶meters, &context, 5.0), 2.0);
assert_eq!(block.process(¶meters, &context, 6.0), 3.0);
assert_eq!(block.process(¶meters, &context, 7.0), 4.0);
assert_eq!(block.process(¶meters, &context, 8.0), 5.0);
assert_eq!(block.process(¶meters, &context, 9.0), 6.0);
assert_eq!(block.process(¶meters, &context, 10.0), 7.0);
assert_eq!(block.process(¶meters, &context, 11.0), 8.0);
assert_eq!(block.process(¶meters, &context, 12.0), 9.0);
assert_eq!(block.process(¶meters, &context, 13.0), 10.0);
assert_eq!(block.process(¶meters, &context, 14.0), 11.0);
assert_eq!(block.process(¶meters, &context, 15.0), 12.0);
assert_eq!(block.process(¶meters, &context, 16.0), 13.0);
assert_eq!(block.process(¶meters, &context, 17.0), 14.0);
assert_eq!(block.process(¶meters, &context, 18.0), 15.0);
assert_eq!(block.process(¶meters, &context, 19.0), 16.0);
assert_eq!(block.process(¶meters, &context, 20.0), 17.0);
}
#[test]
fn test_delay_block_with_delayed_input() {
let mut block = DelayBlock::<f64, 3>::default();
let parameters = Parameters {
ic: 0.0,
is_delayed: true,
};
let context = StubContext::default();
assert_eq!(block.process(¶meters, &context, 1.0), 0.0);
assert_eq!(block.process(¶meters, &context, 2.0), 0.0);
assert_eq!(block.process(¶meters, &context, 3.0), 0.0);
assert_eq!(block.process(¶meters, &context, 4.0), 2.0);
assert_eq!(block.process(¶meters, &context, 5.0), 3.0);
assert_eq!(block.process(¶meters, &context, 6.0), 4.0);
}
#[test]
fn test_delay_block_matrix() {
let mut block = DelayBlock::<Matrix<2, 2, f64>, 3>::default();
let parameters = Parameters {
ic: Matrix {
data: [[0.0, 0.0], [0.0, 0.0]],
},
is_delayed: false,
};
let context = StubContext::default();
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[1.0, 2.0], [3.0, 4.0]]
}
),
&Matrix {
data: [[0.0, 0.0], [0.0, 0.0]]
}
);
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[5.0, 6.0], [7.0, 8.0]]
}
),
&Matrix {
data: [[0.0, 0.0], [0.0, 0.0]]
}
);
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[9.0, 10.0], [11.0, 12.0]]
}
),
&Matrix {
data: [[0.0, 0.0], [0.0, 0.0]]
}
);
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[13.0, 14.0], [15.0, 16.0]]
}
),
&Matrix {
data: [[1.0, 2.0], [3.0, 4.0]]
}
);
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[17.0, 18.0], [19.0, 20.0]]
}
),
&Matrix {
data: [[5.0, 6.0], [7.0, 8.0]]
}
);
assert_eq!(
block.process(
¶meters,
&context,
&Matrix {
data: [[21.0, 22.0], [23.0, 24.0]]
}
),
&Matrix {
data: [[9.0, 10.0], [11.0, 12.0]]
}
);
}
#[test]
fn test_delay_block_scalar_ics() {
let mut block = DelayBlock::<f64, 6>::default();
let parameters = Parameters {
ic: 42.0,
is_delayed: false,
};
let context = StubContext::default();
assert_eq!(block.process(¶meters, &context, 1.0), 42.0);
assert_eq!(block.process(¶meters, &context, 2.0), 42.0);
assert_eq!(block.process(¶meters, &context, 3.0), 42.0);
let parameters = Parameters {
ic: 12.0,
is_delayed: false,
};
assert_eq!(block.process(¶meters, &context, 4.0), 12.0);
assert_eq!(block.process(¶meters, &context, 5.0), 12.0);
assert_eq!(block.process(¶meters, &context, 6.0), 12.0);
assert_eq!(block.process(¶meters, &context, 7.0), 1.0);
assert_eq!(block.process(¶meters, &context, 8.0), 2.0);
assert_eq!(block.process(¶meters, &context, 9.0), 3.0);
assert_eq!(block.process(¶meters, &context, 10.0), 4.0);
assert_eq!(block.process(¶meters, &context, 11.0), 5.0);
}
}