use futuredsp::FirFilter;
use futuredsp::prelude::*;
use futuresdr::prelude::*;
use std::cmp::min;
#[derive(Block)]
pub struct PfbArbResampler<
I: CpuBufferReader<Item = Complex32> = DefaultCpuReader<Complex32>,
O: CpuBufferWriter<Item = Complex32> = DefaultCpuWriter<Complex32>,
> {
rate: f32,
num_filters: usize,
n_taps_per_filter: usize,
fir_filters: Vec<FirFilter<Complex32, Complex32, Vec<f32>>>,
diff_filters: Vec<FirFilter<Complex32, Complex32, Vec<f32>>>,
filter_index: usize,
dec_rate: usize,
accum: f32, flt_rate: f32,
buff: [Complex32; 2],
#[input]
input: I,
#[output]
output: O,
}
impl<I, O> PfbArbResampler<I, O>
where
I: CpuBufferReader<Item = Complex32>,
O: CpuBufferWriter<Item = Complex32>,
{
fn taps_per_filter(num_taps: usize, num_filts: usize) -> usize {
(num_taps as f32 / num_filts as f32).ceil() as usize
}
fn create_diff_taps(taps: &[f32]) -> Vec<f32> {
let diff_filter: [f32; 2] = [-1., 1.];
let mut diff_taps: Vec<f32> = vec![0.; taps.len()];
for i in 0..taps.len() - 1 {
for j in 0..2 {
diff_taps[i] += diff_filter[j] * taps[i + j];
}
}
diff_taps[taps.len() - 1] = 0.;
diff_taps
}
fn create_taps(
taps: &[f32],
num_filters: usize,
) -> Vec<FirFilter<Complex32, Complex32, Vec<f32>>> {
let taps_per_filter = Self::taps_per_filter(taps.len(), num_filters);
let mut fir_filters = vec![];
for i in 0..num_filters {
let mut taps_tmp: Vec<f32> = taps[i..].iter().step_by(num_filters).copied().collect();
if taps_tmp.len() < taps_per_filter {
taps_tmp.push(0.);
}
fir_filters.push(FirFilter::<Complex32, Complex32, _>::new(taps_tmp));
}
fir_filters
}
#[allow(clippy::type_complexity)]
fn build_filterbank(
taps: &[f32],
num_filters: usize,
) -> (
Vec<FirFilter<Complex32, Complex32, Vec<f32>>>,
Vec<FirFilter<Complex32, Complex32, Vec<f32>>>,
) {
let diff_taps = Self::create_diff_taps(taps);
let filters = Self::create_taps(taps, num_filters);
let diff_filters = Self::create_taps(&diff_taps, num_filters);
(filters, diff_filters)
}
pub fn new(rate: f32, taps: &[f32], num_filters: usize) -> Self {
let (filters, diff_filters) = Self::build_filterbank(taps, num_filters);
let n_taps_per_filter = Self::taps_per_filter(taps.len(), num_filters);
let dec_rate: f32 = (num_filters as f32 / rate).floor();
let flt_rate = (num_filters as f32 / rate) - dec_rate;
let starting_filter = (taps.len() / 2) % num_filters;
Self {
rate,
num_filters,
n_taps_per_filter,
fir_filters: filters,
diff_filters,
filter_index: starting_filter,
dec_rate: dec_rate as usize,
accum: 0.0,
flt_rate,
buff: [Complex32::new(0., 0.); 2],
input: I::default(),
output: O::default(),
}
}
}
#[doc(hidden)]
impl Kernel for PfbArbResampler {
async fn work(
&mut self,
io: &mut WorkIo,
_mio: &mut MessageOutputs,
_b: &mut BlockMeta,
) -> Result<()> {
let input = self.input.slice();
let ninput_items = input
.len()
.saturating_sub(self.rate as usize + self.n_taps_per_filter - 1);
let out = self.output.slice();
let noutput_items = out.len();
let nitem_to_process = min(ninput_items, (noutput_items as f32 / self.rate) as usize);
if nitem_to_process > 0 {
let mut i_in: usize = 0;
let mut i_out: usize = 0;
while i_in < nitem_to_process {
while self.filter_index < self.num_filters {
self.fir_filters[self.filter_index].filter(
&input[i_in..i_in + self.n_taps_per_filter],
&mut self.buff[0..1],
);
self.diff_filters[self.filter_index].filter(
&input[i_in..i_in + self.n_taps_per_filter],
&mut self.buff[1..2],
);
out[i_out] = self.buff[0] + self.buff[1] * self.accum; i_out += 1;
self.accum += self.flt_rate;
self.filter_index += self.dec_rate + self.accum.floor() as usize;
self.accum %= 1.0;
}
i_in += self.filter_index / self.num_filters;
self.filter_index %= self.num_filters;
}
self.input.consume(i_in);
self.output.produce(i_out);
}
if ninput_items - nitem_to_process < self.n_taps_per_filter && self.input.finished() {
io.finished = true;
}
Ok(())
}
}