1mod welch;
4
5pub use welch::{Detrend, Welch, Window};
6
7use std::f64::consts::PI;
8
9use modewise_method::{MethodDescriptor, MethodError};
10use modewise_signals::MultichannelSeries;
11use modewise_spectral::{TimeFrequencyMap, TimeFrequencyTransform};
12use ndarray::Array3;
13use rustfft::FftPlanner;
14use rustfft::num_complex::Complex64;
15
16#[derive(Debug, Clone)]
19pub struct Stft {
20 window_len: usize,
21 hop: usize,
22}
23
24impl Stft {
25 pub fn new(window_len: usize, hop: usize) -> Result<Self, MethodError> {
26 if window_len < 2 {
27 return Err(MethodError::invalid("window_len", "must be at least 2"));
28 }
29 if hop == 0 {
30 return Err(MethodError::invalid("hop", "must be at least 1"));
31 }
32 Ok(Self { window_len, hop })
33 }
34
35 fn window(&self) -> Vec<f64> {
36 let n = self.window_len as f64;
37 (0..self.window_len)
38 .map(|i| 0.5 - 0.5 * (2.0 * PI * i as f64 / n).cos())
39 .collect()
40 }
41}
42
43impl TimeFrequencyTransform for Stft {
44 fn descriptor(&self) -> MethodDescriptor {
45 MethodDescriptor {
46 name: "stft",
47 version: env!("CARGO_PKG_VERSION"),
48 multivariate: false,
49 }
50 }
51
52 fn transform(&self, signal: &MultichannelSeries) -> Result<TimeFrequencyMap, MethodError> {
53 let (n, hop) = (self.window_len, self.hop);
54 if signal.n_samples() < n {
55 return Err(MethodError::SignalTooShort {
56 method: "stft",
57 needed: n,
58 actual: signal.n_samples(),
59 });
60 }
61 let n_frames = (signal.n_samples() - n) / hop + 1;
62 let n_bins = n / 2 + 1;
63 let window = self.window();
64 let fft = FftPlanner::new().plan_fft_forward(n);
65
66 let mut power = Array3::zeros((signal.n_channels(), n_bins, n_frames));
67 for (c, channel) in signal.data().rows().into_iter().enumerate() {
68 for frame in 0..n_frames {
69 let start = frame * hop;
70 let mut buffer: Vec<Complex64> = (0..n)
71 .map(|i| Complex64::new(channel[start + i] * window[i], 0.0))
72 .collect();
73 fft.process(&mut buffer);
74 for (bin, value) in buffer.iter().take(n_bins).enumerate() {
75 power[[c, bin, frame]] = value.norm_sqr();
76 }
77 }
78 }
79
80 let fs = signal.sampling_rate().hz();
81 let frequencies_hz = (0..n_bins).map(|k| k as f64 * fs / n as f64).collect();
82 let times_s = (0..n_frames)
83 .map(|f| (f * hop) as f64 / fs + n as f64 / (2.0 * fs))
84 .collect();
85 TimeFrequencyMap::new(power, frequencies_hz, times_s, signal.labels().to_vec())
86 }
87}