#![allow(dead_code)]
use crate::kernel::{Complex, Float};
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterType {
Flat,
Dirichlet,
Gaussian,
BlackmanHarris,
}
#[derive(Debug, Clone)]
pub struct AliasingFilter<T: Float> {
pub coeffs: Vec<Complex<T>>,
pub filter_type: FilterType,
pub width: usize,
pub n: usize,
}
impl<T: Float> AliasingFilter<T> {
pub fn flat(n: usize, width: usize) -> Self {
let mut coeffs = vec![Complex::<T>::zero(); n];
let half_width = width / 2;
for i in 0..half_width {
coeffs[i] = Complex::new(T::ONE, T::ZERO);
}
for i in (n - half_width)..n {
coeffs[i] = Complex::new(T::ONE, T::ZERO);
}
Self {
coeffs,
filter_type: FilterType::Flat,
width,
n,
}
}
pub fn dirichlet(n: usize, width: usize) -> Self {
let mut coeffs = vec![Complex::<T>::zero(); n];
let m = width as f64;
let n_f = n as f64;
for i in 0..n {
let x = 2.0 * core::f64::consts::PI * (i as f64) / n_f;
let val = if x.abs() < 1e-10 {
m } else {
(m * x / 2.0).sin() / (x / 2.0).sin()
};
let normalized = val / m;
coeffs[i] = Complex::new(T::from_f64(normalized.abs()), T::ZERO);
}
Self {
coeffs,
filter_type: FilterType::Dirichlet,
width,
n,
}
}
pub fn gaussian(n: usize, sigma: f64) -> Self {
let mut coeffs = vec![Complex::<T>::zero(); n];
let n_f = n as f64;
for i in 0..n {
let freq = if i <= n / 2 { i as f64 } else { i as f64 - n_f };
let val = (-freq * freq / (2.0 * sigma * sigma)).exp();
coeffs[i] = Complex::new(T::from_f64(val), T::ZERO);
}
Self {
coeffs,
filter_type: FilterType::Gaussian,
width: (4.0 * sigma).ceil() as usize,
n,
}
}
pub fn blackman_harris(n: usize, width: usize) -> Self {
let mut coeffs = vec![Complex::<T>::zero(); n];
let width_f = width as f64;
let a0 = 0.355_768;
let a1 = 0.487_396;
let a2 = 0.144_232;
let a3 = 0.012_604;
let half_width = width / 2;
for i in 0..width {
let x = (i as f64) / width_f;
let two_pi = 2.0 * core::f64::consts::PI;
let val = a0 - a1 * (two_pi * x).cos() + a2 * (2.0 * two_pi * x).cos()
- a3 * (3.0 * two_pi * x).cos();
let idx = if i < half_width { i } else { n - width + i };
if idx < n {
coeffs[idx] = Complex::new(T::from_f64(val), T::ZERO);
}
}
Self {
coeffs,
filter_type: FilterType::BlackmanHarris,
width,
n,
}
}
pub fn apply(&self, signal_fft: &[Complex<T>]) -> Vec<Complex<T>> {
debug_assert_eq!(signal_fft.len(), self.n);
signal_fft
.iter()
.zip(self.coeffs.iter())
.map(|(&s, &f)| s * f)
.collect()
}
pub fn apply_time_domain(&self, signal: &[Complex<T>]) -> Vec<Complex<T>> {
self.apply(signal)
}
pub fn response_at(&self, freq_idx: usize) -> Complex<T> {
if freq_idx < self.n {
self.coeffs[freq_idx]
} else {
Complex::<T>::zero()
}
}
pub fn bandwidth_3db(&self) -> usize {
let half_power = T::from_f64(0.5); let mut count = 0;
for coeff in &self.coeffs {
if coeff.norm_sqr() >= half_power {
count += 1;
}
}
count
}
}
pub fn create_optimal_filter<T: Float>(
n: usize,
k: usize,
num_buckets: usize,
) -> AliasingFilter<T> {
let width = n / num_buckets;
if k < n / 100 {
AliasingFilter::gaussian(n, (width as f64) / 2.0)
} else if k < n / 20 {
AliasingFilter::dirichlet(n, width)
} else {
AliasingFilter::flat(n, width)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_flat_filter() {
let filter: AliasingFilter<f64> = AliasingFilter::flat(64, 16);
assert_eq!(filter.coeffs.len(), 64);
assert_eq!(filter.filter_type, FilterType::Flat);
assert_eq!(filter.response_at(0).re, 1.0);
}
#[test]
fn test_gaussian_filter() {
let filter: AliasingFilter<f64> = AliasingFilter::gaussian(64, 8.0);
assert_eq!(filter.coeffs.len(), 64);
assert_eq!(filter.filter_type, FilterType::Gaussian);
assert!(filter.response_at(0).re > 0.9);
}
#[test]
fn test_dirichlet_filter() {
let filter: AliasingFilter<f64> = AliasingFilter::dirichlet(64, 8);
assert_eq!(filter.coeffs.len(), 64);
assert_eq!(filter.filter_type, FilterType::Dirichlet);
}
#[test]
fn test_filter_apply() {
let filter: AliasingFilter<f64> = AliasingFilter::flat(8, 4);
let signal = vec![Complex::new(1.0_f64, 0.0); 8];
let filtered = filter.apply(&signal);
assert_eq!(filtered.len(), 8);
}
#[test]
fn test_optimal_filter() {
let filter1: AliasingFilter<f64> = create_optimal_filter(1024, 5, 32);
assert_eq!(filter1.filter_type, FilterType::Gaussian);
let filter2: AliasingFilter<f64> = create_optimal_filter(1024, 30, 32);
assert_eq!(filter2.filter_type, FilterType::Dirichlet);
let filter3: AliasingFilter<f64> = create_optimal_filter(1024, 100, 32);
assert_eq!(filter3.filter_type, FilterType::Flat);
}
}