sci_rs/signal/
resample.rs1use nalgebra::Complex;
2use num_traits::{Float, Zero};
3use rustfft::FftNum;
4
5pub fn resample<F: Float + FftNum>(x: &[F], n: usize) -> Vec<F> {
20 let mut fft_planner = rustfft::FftPlanner::<F>::new();
31 let fft = fft_planner.plan_fft_forward(x.len());
32 let ifft = fft_planner.plan_fft_inverse(n);
33
34 let scratch_length = std::cmp::max(
35 fft.get_inplace_scratch_len(),
36 ifft.get_inplace_scratch_len(),
37 );
38 let mut scratch = vec![Complex::zero(); scratch_length];
39 let mut x = x
40 .into_iter()
41 .map(|x| Complex::new(*x, F::zero()))
42 .collect::<Vec<_>>();
43 fft.process_with_scratch(&mut x, &mut scratch);
44
45 let mut y = vec![Complex::zero(); n];
47 let bins = std::cmp::min(x.len(), n);
48 let half_spectrum = bins / 2;
49 y[..half_spectrum].copy_from_slice(&x[..half_spectrum]);
50 y[n - half_spectrum..].copy_from_slice(&x[x.len() - half_spectrum..]);
51
52 ifft.process_with_scratch(&mut y, &mut scratch);
54
55 let scale_factor = F::from(1.0 / x.len() as f64).unwrap();
57 let y = y.iter().map(|x| x.re * scale_factor).collect::<Vec<_>>();
58
59 y
60}
61
62#[cfg(test)]
63mod tests {
64 use approx::assert_relative_eq;
65 use rand::Rng;
66
67 use super::*;
68
69 #[test]
70 #[should_panic]
71 fn can_resample_like_scipy() {
72 let x = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.];
73 let y = resample(&x, 5);
74 let expected = vec![3., 2.18649851, 5.01849831, 5.98150169, 8.81350149];
75 assert_eq!(y.len(), expected.len());
76
77 println!("y: {:?}, scipy: {:?}", y, expected);
79 for (y, expected) in y.iter().zip(expected.iter()) {
80 assert_relative_eq!(y, expected, epsilon = 1e-10);
81 }
82 }
83
84 #[test]
85 fn can_resample_to_exact_number() {
86 let mut rng = rand::thread_rng();
91 for i in (0..100) {
92 let len = rng.gen_range((10..50));
93 let x = (0..len)
94 .map(|_| rng.gen_range((-100.0..100.)))
95 .collect::<Vec<_>>();
96 let y = resample(&x, 100);
97 assert_eq!(y.len(), 100);
98 }
99
100 for i in (0..50) {
101 let len = rng.gen_range((200..10000));
102 let target_len = rng.gen_range((50..50000));
103 let x = (0..len)
104 .map(|_| rng.gen_range((-100.0..100.)))
105 .collect::<Vec<_>>();
106 let y = resample(&x, target_len);
107 assert_eq!(y.len(), target_len);
108 }
109 }
110}