use num_complex::Complex64;
#[derive(Debug, Clone)]
pub struct WienerFilter {
coefficients: Vec<f64>,
order: usize,
}
impl WienerFilter {
pub fn from_power_spectra(signal_power: &[f64], noise_power: &[f64]) -> Self {
assert_eq!(
signal_power.len(),
noise_power.len(),
"Signal and noise spectra must have same length"
);
let order = signal_power.len();
let coefficients = signal_power
.iter()
.zip(noise_power.iter())
.map(|(s, n)| if s + n > 1e-10 { s / (s + n) } else { 0.0 })
.collect();
Self {
coefficients,
order,
}
}
pub fn from_snr(order: usize, snr: f64) -> Self {
let coefficients = vec![snr / (snr + 1.0); order];
Self {
coefficients,
order,
}
}
pub fn apply_frequency(&self, spectrum: &[Complex64]) -> Vec<Complex64> {
assert_eq!(
spectrum.len(),
self.order,
"Spectrum length must match filter order"
);
spectrum
.iter()
.zip(self.coefficients.iter())
.map(|(s, c)| s * c)
.collect()
}
pub fn apply(&self, signal: &[f64]) -> Vec<f64> {
use rustfft::num_complex::Complex;
use rustfft::FftPlanner;
let n = signal.len();
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(n);
let ifft = planner.plan_fft_inverse(n);
let mut buffer: Vec<Complex<f64>> = signal.iter().map(|&x| Complex::new(x, 0.0)).collect();
fft.process(&mut buffer);
for (i, coef) in self.coefficients.iter().enumerate().take(n.min(self.order)) {
buffer[i] *= coef;
}
ifft.process(&mut buffer);
buffer.iter().map(|c| c.re / n as f64).collect()
}
pub fn coefficients(&self) -> &[f64] {
&self.coefficients
}
pub fn order(&self) -> usize {
self.order
}
}
pub fn estimate_power_spectra(
signal: &[f64],
segment_length: usize,
overlap: usize,
) -> (Vec<f64>, Vec<f64>) {
use rustfft::{num_complex::Complex, FftPlanner};
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(segment_length);
let step = segment_length - overlap;
let num_segments = (signal.len() - overlap) / step;
let mut power_sum = vec![0.0; segment_length];
for i in 0..num_segments {
let start = i * step;
let end = start + segment_length;
if end > signal.len() {
break;
}
let segment = &signal[start..end];
let mut buffer: Vec<Complex<f64>> = segment.iter().map(|&x| Complex::new(x, 0.0)).collect();
fft.process(&mut buffer);
for (j, c) in buffer.iter().enumerate() {
power_sum[j] += (c.re * c.re + c.im * c.im) / segment_length as f64;
}
}
let power: Vec<f64> = power_sum.iter().map(|p| p / num_segments as f64).collect();
let mut sorted = power.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
let noise_floor = sorted[sorted.len() / 10];
let signal_power = power.iter().map(|p| (p - noise_floor).max(0.0)).collect();
let noise_power = vec![noise_floor; segment_length];
(signal_power, noise_power)
}
#[cfg(test)]
mod tests {
use super::*;
use rand::Rng;
#[test]
fn test_wiener_snr() {
let wf = WienerFilter::from_snr(128, 10.0);
assert!((wf.coefficients()[0] - 10.0 / 11.0).abs() < 1e-6);
}
#[test]
fn test_wiener_apply() {
let mut rng = rand::thread_rng();
let signal: Vec<f64> = (0..128).map(|i| (i as f64 * 0.1).sin()).collect();
let noisy: Vec<f64> = signal.iter().map(|&s| s + rng.gen::<f64>() * 0.1).collect();
let wf = WienerFilter::from_snr(128, 5.0);
let filtered = wf.apply(&noisy);
assert_eq!(filtered.len(), noisy.len());
}
}