use crate::error::{Error, Result};
use crate::dotprod::DotProd;
use crate::filter::FirPfbFilter;
use crate::buffer::Window;
use num_complex::ComplexFloat;
#[derive(Clone, Debug)]
pub struct Fdelay<T, Coeff = T> {
nmax: usize,
m: usize,
npfb: usize,
delay: f32,
w: Window<T>,
pfb: FirPfbFilter<T, Coeff>,
w_index: usize,
f_index: usize,
}
impl<T, Coeff> Fdelay<T, Coeff>
where
Coeff: Clone + Copy + ComplexFloat<Real = f32> + From<f32>,
T: Clone + Copy + ComplexFloat<Real = f32> + std::ops::Mul<Coeff, Output = T> + Default,
[Coeff]: DotProd<T, Output = T>,
{
pub fn new(nmax: usize, m: usize, npfb: usize) -> Result<Self> {
if nmax == 0 {
return Err(Error::Config("maximum delay must be greater than zero".into()));
}
if m == 0 {
return Err(Error::Config("filter semi-length must be greater than zero".into()));
}
if npfb == 0 {
return Err(Error::Config("number of filters must be greater than zero".into()));
}
let w = Window::new(nmax + 1)?;
let pfb = FirPfbFilter::default(npfb, m)?;
let mut q = Self {
nmax,
m,
npfb,
delay: 0.0,
w,
pfb,
w_index: nmax - 1,
f_index: 0,
};
q.reset();
Ok(q)
}
pub fn new_default(nmax: usize) -> Result<Self> {
Self::new(nmax, 8, 64)
}
pub fn reset(&mut self) {
self.delay = 0.0;
self.w_index = self.nmax - 1;
self.f_index = 0;
self.w.reset();
self.pfb.reset();
}
pub fn get_delay(&self) -> f32 {
self.delay
}
pub fn set_delay(&mut self, delay: f32) -> Result<()> {
if delay < 0.0 {
return Err(Error::Config("delay cannot be negative".into()));
}
if delay > self.nmax as f32 {
return Err(Error::Config(format!("delay ({}) cannot exceed maximum ({})",
delay, self.nmax)));
}
let offset = self.nmax as f32 - delay;
let intpart = offset.floor() as i32;
let fracpart = offset - intpart as f32;
self.w_index = intpart as usize;
self.f_index = (self.npfb as f32 * fracpart).round() as usize;
while self.f_index >= self.npfb {
self.w_index += 1;
self.f_index -= self.npfb;
}
if self.w_index > self.nmax {
return Err(Error::Internal("window index exceeds maximum".into()));
}
self.delay = delay;
Ok(())
}
pub fn adjust_delay(&mut self, delta: f32) -> Result<()> {
self.set_delay(self.delay + delta)
}
pub fn get_nmax(&self) -> usize {
self.nmax
}
pub fn get_m(&self) -> usize {
self.m
}
pub fn get_npfb(&self) -> usize {
self.npfb
}
pub fn push(&mut self, x: T) -> () {
self.w.push(x);
self.pfb.push(self.w.index(self.w_index).unwrap());
}
pub fn write(&mut self, x: &[T]) -> () {
for &xi in x {
self.push(xi);
}
}
pub fn execute(&mut self) -> Result<T> {
self.pfb.execute(self.f_index)
}
pub fn execute_block(&mut self, x: &[T], y: &mut [T]) -> Result<()> {
for (&xi, yi) in x.iter().zip(y.iter_mut()) {
self.push(xi);
*yi = self.execute()?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
use num_complex::Complex32;
use approx::assert_relative_eq;
fn testbench_fdelay_rrrf(nmax: usize, m: usize, npfb: usize, delay: f32) {
let tol = 0.01f32;
let num_samples = nmax + 2 * m;
let mut q = Fdelay::<f32, f32>::new(nmax, m, npfb).unwrap();
q.set_delay(delay * 0.7).unwrap();
q.adjust_delay(delay * 0.3).unwrap();
assert_eq!(q.get_nmax(), nmax);
assert_eq!(q.get_m(), m);
assert_eq!(q.get_npfb(), npfb);
assert_relative_eq!(q.get_delay(), delay, epsilon = 1e-6);
let mut x = vec![0.0f32; num_samples];
let mut y = vec![0.0f32; num_samples];
x[0] = 1.0;
q.execute_block(&x, &mut y).unwrap();
let fc = 0.1 / num_samples as f32; let mut v = Complex32::new(0.0, 0.0);
for i in 0..num_samples {
v += y[i] * Complex32::from_polar(1.0, 2.0 * std::f32::consts::PI * fc * i as f32);
}
let delay_est = v.arg() / (2.0 * std::f32::consts::PI * fc) - m as f32;
assert_relative_eq!(delay_est, delay, epsilon = tol);
}
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_0)]
fn test_fdelay_rrrf_0() { testbench_fdelay_rrrf(200, 12, 64, 0.0 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_1)]
fn test_fdelay_rrrf_1() { testbench_fdelay_rrrf(200, 12, 64, 0.0001 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_2)]
fn test_fdelay_rrrf_2() { testbench_fdelay_rrrf(200, 12, 64, 0.1 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_3)]
fn test_fdelay_rrrf_3() { testbench_fdelay_rrrf(200, 12, 64, 0.9 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_4)]
fn test_fdelay_rrrf_4() { testbench_fdelay_rrrf(200, 12, 64, 0.9999 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_5)]
fn test_fdelay_rrrf_5() { testbench_fdelay_rrrf(200, 12, 64, 16.99 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_6)]
fn test_fdelay_rrrf_6() { testbench_fdelay_rrrf(200, 12, 64, 17.00 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_7)]
fn test_fdelay_rrrf_7() { testbench_fdelay_rrrf(200, 12, 64, 17.01 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_8)]
fn test_fdelay_rrrf_8() { testbench_fdelay_rrrf(200, 12, 64, 199.9 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_9)]
fn test_fdelay_rrrf_9() { testbench_fdelay_rrrf(200, 12, 64, 200.0 ); }
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_config)]
fn test_fdelay_rrrf_config() {
let nmax: usize = 200;
let m: usize = 12;
let npfb: usize = 64;
assert!(Fdelay::<f32, f32>::new(0, m, npfb).is_err());
assert!(Fdelay::<f32, f32>::new(nmax, 0, npfb).is_err());
assert!(Fdelay::<f32, f32>::new(nmax, m, 0).is_err());
assert!(Fdelay::<f32, f32>::new_default(0).is_err());
let mut q = Fdelay::<f32, f32>::new_default(nmax).unwrap();
assert!(q.set_delay(-1.0).is_err());
assert!(q.set_delay(nmax as f32 + 1.0).is_err());
assert!(q.adjust_delay(-1.0).is_err());
}
#[test]
#[autotest_annotate(autotest_fdelay_rrrf_push_write)]
fn test_fdelay_rrrf_push_write() {
let mut q0 = Fdelay::<f32, f32>::new_default(200).unwrap();
let mut q1 = Fdelay::<f32, f32>::new_default(200).unwrap();
q0.set_delay(7.2280).unwrap();
q1.set_delay(7.2280).unwrap();
let buf = [-1.0, 3.0, 5.0, -3.0, 5.0, 1.0, -3.0, -4.0];
for trial in 0..20 {
let n = trial % 8;
for i in 0..n {
q0.push(buf[i]);
}
q1.write(&buf[..n]);
let v0 = q0.execute();
let v1 = q1.execute();
assert_eq!(v0, v1);
}
}
}