use crate::numbers::*;
pub trait WindowFunction<T>: Sync
where
T: RealNumber,
{
fn is_symmetric(&self) -> bool;
fn window(&self, n: usize, length: usize) -> T;
}
pub struct TriangularWindow;
impl<T> WindowFunction<T> for TriangularWindow
where
T: RealNumber,
{
fn is_symmetric(&self) -> bool {
true
}
fn window(&self, n: usize, length: usize) -> T {
let one = T::one();
let two = T::from(2.0).unwrap();
let n = T::from(n).unwrap();
let length = T::from(length).unwrap();
one - ((n - (length - one) / two) / (length / two)).abs()
}
}
pub struct HammingWindow<T>
where
T: RealNumber,
{
alpha: T,
beta: T,
}
impl<T> HammingWindow<T>
where
T: RealNumber,
{
pub fn new(alpha: T) -> Self {
HammingWindow {
alpha,
beta: (T::one() - alpha),
}
}
pub fn default() -> Self {
Self::new(T::from(0.54).unwrap())
}
}
impl<T> WindowFunction<T> for HammingWindow<T>
where
T: RealNumber,
{
fn is_symmetric(&self) -> bool {
true
}
fn window(&self, n: usize, length: usize) -> T {
let one = T::one();
let two = T::from(2.0).unwrap();
let pi = T::PI();
let n = T::from(n).unwrap();
let length = T::from(length).unwrap();
self.alpha - self.beta * (two * pi * n / (length - one)).cos()
}
}
pub struct BlackmanHarrisWindow;
impl<T> WindowFunction<T> for BlackmanHarrisWindow
where
T: RealNumber,
{
fn is_symmetric(&self) -> bool {
true
}
fn window(&self, n: usize, length: usize) -> T {
let one = T::one();
let two = T::from(2.0).unwrap();
let four = T::from(4.0).unwrap();
let six = T::from(6.0).unwrap();
let pi = T::PI();
let a_naught = T::from(0.35875).unwrap();
let a_one = T::from(0.48829).unwrap();
let a_two = T::from(0.14128).unwrap();
let a_three = T::from(0.01168).unwrap();
let n = T::from(n).unwrap();
let length = T::from(length).unwrap();
a_naught - a_one * (two * pi * n / (length - one)).cos()
+ a_two * (four * pi * n / (length - one)).cos()
- a_three * (six * pi * n / (length - one)).cos()
}
}
pub struct RectangularWindow;
impl<T> WindowFunction<T> for RectangularWindow
where
T: RealNumber,
{
fn is_symmetric(&self) -> bool {
true
}
fn window(&self, _n: usize, _length: usize) -> T {
let one = T::one();
one
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Debug;
fn window_test<T, W>(window: W, expected: &[T])
where
T: RealNumber + Debug,
W: WindowFunction<T>,
{
let mut result = vec![T::zero(); expected.len()];
for i in 0..result.len() {
result[i] = window.window(i, result.len());
}
for i in 0..result.len() {
if (result[i] - expected[i]).abs() > T::from(1e-4).unwrap() {
panic!("assertion failed: {:?} != {:?}", result, expected);
}
}
}
#[test]
fn triangular_window32_test() {
let window = TriangularWindow;
let expected = [0.2, 0.6, 1.0, 0.6, 0.2];
window_test(window, &expected);
}
#[test]
fn hamming_window32_test() {
let hamming = HammingWindow::<f32>::default();
let expected = [0.08, 0.54, 1.0, 0.54, 0.08];
window_test(hamming, &expected);
}
#[test]
fn blackmanharris_window32_test() {
let blackmanharris = BlackmanHarrisWindow;
let expected = [0.0001, 0.2175, 1.0000, 0.2175, 0.0001];
window_test(blackmanharris, &expected);
}
#[test]
fn rectangular_window32_test() {
let rectangular = RectangularWindow;
let expected = [1.0, 1.0, 1.0, 1.0, 1.0];
window_test(rectangular, &expected);
}
}