sc_neurocore_engine/analysis/
spectral.rs1use rustfft::{num_complex::Complex64, FftPlanner};
10
11pub fn power_spectrum(binary_train: &[i32], dt: f64) -> (Vec<f64>, Vec<f64>) {
16 let n = binary_train.len();
17 if n < 2 {
18 return (vec![], vec![]);
19 }
20 let mean: f64 = binary_train.iter().map(|&s| s as f64).sum::<f64>() / n as f64;
21 let mut buf: Vec<Complex64> = binary_train
22 .iter()
23 .map(|&s| Complex64::new(s as f64 - mean, 0.0))
24 .collect();
25
26 let mut planner = FftPlanner::new();
27 let fft = planner.plan_fft_forward(n);
28 fft.process(&mut buf);
29
30 let n_rfft = n / 2 + 1;
31 let nf = n as f64;
32 let psd: Vec<f64> = buf[..n_rfft]
33 .iter()
34 .map(|c| (c.re * c.re + c.im * c.im) / nf)
35 .collect();
36 let freqs: Vec<f64> = (0..n_rfft).map(|k| k as f64 / (nf * dt)).collect();
37 (psd, freqs)
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn test_power_spectrum_basic() {
46 let train = vec![1, 0, 1, 0, 1, 0, 1, 0, 1, 0];
47 let (psd, freqs) = power_spectrum(&train, 0.001);
48 assert_eq!(psd.len(), 6);
49 assert_eq!(freqs.len(), 6);
50 assert!((freqs[0]).abs() < 1e-12);
51 assert!((freqs[5] - 500.0).abs() < 1e-6);
53 }
54
55 #[test]
56 fn test_power_spectrum_empty() {
57 let (psd, freqs) = power_spectrum(&[1], 0.001);
58 assert!(psd.is_empty());
59 assert!(freqs.is_empty());
60 }
61
62 #[test]
63 fn test_power_spectrum_silence() {
64 let train = vec![0; 100];
65 let (psd, _) = power_spectrum(&train, 0.001);
66 assert!(psd.iter().all(|&v| v.abs() < 1e-12));
68 }
69
70 #[test]
71 fn test_power_spectrum_dc_removed() {
72 let train = vec![1; 64];
74 let (psd, _) = power_spectrum(&train, 0.001);
75 assert!(psd.iter().all(|&v| v.abs() < 1e-12));
76 }
77
78 #[test]
79 fn test_power_spectrum_peak_at_periodic() {
80 let mut train = vec![0i32; 20];
82 for i in (0..20).step_by(4) {
83 train[i] = 1;
84 }
85 let (psd, _) = power_spectrum(&train, 0.001);
86 let peak_idx = psd
88 .iter()
89 .enumerate()
90 .skip(1) .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
92 .unwrap()
93 .0;
94 assert!(
99 peak_idx == 5 || peak_idx == 10,
100 "Expected peak at index 5 or 10, got {peak_idx}"
101 );
102 }
103
104 #[test]
105 fn test_power_spectrum_length() {
106 let train = vec![0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1];
107 let (psd, freqs) = power_spectrum(&train, 0.001);
108 assert_eq!(psd.len(), 13 / 2 + 1);
109 assert_eq!(psd.len(), freqs.len());
110 }
111}