use crate::error::{FFTError, FFTResult};
use std::f64::consts::PI;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WindowType {
Rectangular,
Hann,
Hamming,
Blackman,
BlackmanHarris,
FlatTop,
Bartlett,
BartlettHann,
Tukey(f64), Kaiser(f64), Gaussian(f64), }
#[allow(dead_code)]
pub fn create_window(windowtype: WindowType, length: usize) -> FFTResult<Vec<f64>> {
if length == 0 {
return Err(FFTError::ValueError("Window length cannot be zero".into()));
}
let mut window = vec![0.0; length];
let n = length as f64;
match windowtype {
WindowType::Rectangular => {
window.iter_mut().for_each(|w| *w = 1.0);
}
WindowType::Hann => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.5 * (1.0 - (2.0 * PI * x).cos());
}
}
WindowType::Hamming => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.54 - 0.46 * (2.0 * PI * x).cos();
}
}
WindowType::Blackman => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.42 - 0.5 * (2.0 * PI * x).cos() + 0.08 * (4.0 * PI * x).cos();
}
}
WindowType::BlackmanHarris => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.35875 - 0.48829 * (2.0 * PI * x).cos() + 0.14128 * (4.0 * PI * x).cos()
- 0.01168 * (6.0 * PI * x).cos();
}
}
WindowType::FlatTop => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.21557895 - 0.41663158 * (2.0 * PI * x).cos()
+ 0.277263158 * (4.0 * PI * x).cos()
- 0.083578947 * (6.0 * PI * x).cos()
+ 0.006947368 * (8.0 * PI * x).cos();
}
}
WindowType::Bartlett => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 1.0 - (2.0 * x - 1.0).abs();
}
}
WindowType::BartlettHann => {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.62 - 0.48 * (x - 0.5).abs() - 0.38 * (2.0 * PI * x).cos();
}
}
WindowType::Tukey(alpha) => {
if alpha <= 0.0 {
window.iter_mut().for_each(|w| *w = 1.0);
} else if alpha >= 1.0 {
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 / (n - 1.0);
*w = 0.5 * (1.0 - (2.0 * PI * x).cos());
}
} else {
let alpha_n = alpha * (n - 1.0) / 2.0;
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64;
if x < alpha_n {
*w = 0.5 * (1.0 - (PI * x / alpha_n).cos());
} else if x <= (n - 1.0) - alpha_n {
*w = 1.0;
} else {
*w = 0.5 * (1.0 - (PI * (x - (n - 1.0) + alpha_n) / alpha_n).cos());
}
}
}
}
WindowType::Kaiser(beta) => {
let bessel_i0 = |x: f64| -> f64 {
let mut sum = 1.0;
let mut term = 1.0;
for k in 1..20 {
let k_f = k as f64;
term *= (x / 2.0).powi(2) / (k_f * k_f);
sum += term;
if term < 1e-12 * sum {
break;
}
}
sum
};
let beta_i0 = bessel_i0(beta);
for (i, w) in window.iter_mut().enumerate() {
let x = 2.0 * i as f64 / (n - 1.0) - 1.0;
let arg = beta * (1.0 - x * x).sqrt();
*w = bessel_i0(arg) / beta_i0;
}
}
WindowType::Gaussian(sigma) => {
let center = (n - 1.0) / 2.0;
for (i, w) in window.iter_mut().enumerate() {
let x = i as f64 - center;
*w = (-0.5 * (x / (sigma * center)).powi(2)).exp();
}
}
}
Ok(window)
}
#[allow(dead_code)]
pub fn apply_window(signal: &[f64], window: &[f64]) -> FFTResult<Vec<f64>> {
if signal.len() != window.len() {
return Err(FFTError::ValueError(
"Signal and window lengths must match".into(),
));
}
let mut windowed_signal = vec![0.0; signal.len()];
for (i, w) in windowed_signal.iter_mut().enumerate() {
*w = signal[i] * window[i];
}
Ok(windowed_signal)
}
#[allow(dead_code)]
pub fn window_properties(window: &[f64]) -> WindowProperties {
let n = window.len();
let mut sum = 0.0;
let mut sum_squared = 0.0;
let mut coherent_gain = 0.0;
for &w in window {
sum += w;
sum_squared += w * w;
coherent_gain += w;
}
coherent_gain /= n as f64;
let processing_gain = coherent_gain.powi(2) / (sum_squared / n as f64);
let equivalent_noise_bandwidth = n as f64 * sum_squared / (sum * sum);
WindowProperties {
coherent_gain,
processing_gain,
equivalent_noise_bandwidth,
}
}
#[derive(Debug, Clone, Copy)]
pub struct WindowProperties {
pub coherent_gain: f64,
pub processing_gain: f64,
pub equivalent_noise_bandwidth: f64,
}