1use std::f64::consts::PI;
13
14pub trait Float: Copy + Clone + std::fmt::Debug +
16 std::ops::Add<Output = Self> +
17 std::ops::Sub<Output = Self> +
18 std::ops::Mul<Output = Self> +
19 std::ops::Div<Output = Self> +
20 std::cmp::PartialOrd {
21 const PI: Self;
22 const ZERO: Self;
23 const ONE: Self;
24 const TWO: Self; fn sin(self) -> Self;
25 fn cos(self) -> Self;
26 fn atan2(self, other: Self) -> Self;
27 fn sqrt(self) -> Self;
28 fn exp(self) -> Self;
29 fn powf(self, n: Self) -> Self;
30 fn abs(self) -> Self;
31 fn from_usize(n: usize) -> Self;
32 fn from_f64(f: f64) -> Self;
33 fn to_f64(self) -> f64;
34 fn is_nan(self) -> bool;
35 fn is_infinite(self) -> bool;
36}
37
38impl Float for f64 {
39 const PI: Self = std::f64::consts::PI;
40 const ZERO: Self = 0.0;
41 const ONE: Self = 1.0;
42 const TWO: Self = 2.0;
43
44 #[inline]
45 fn sin(self) -> Self { self.sin() }
46 #[inline]
47 fn cos(self) -> Self { self.cos() }
48 #[inline]
49 fn atan2(self, other: Self) -> Self { self.atan2(other) }
50 #[inline]
51 fn sqrt(self) -> Self { self.sqrt() }
52 #[inline]
53 fn exp(self) -> Self { self.exp() }
54 #[inline]
55 fn powf(self, n: Self) -> Self { self.powf(n) }
56 #[inline]
57 fn abs(self) -> Self { self.abs() }
58 #[inline]
59 fn from_usize(n: usize) -> Self { n as f64 }
60 #[inline]
61 fn from_f64(f: f64) -> Self { f }
62 #[inline]
63 fn to_f64(self) -> f64 { self }
64 #[inline]
65 fn is_nan(self) -> bool { self.is_nan() }
66 #[inline]
67 fn is_infinite(self) -> bool { self.is_infinite() }
68}
69
70impl Float for f32 {
71 const PI: Self = std::f32::consts::PI;
72 const ZERO: Self = 0.0;
73 const ONE: Self = 1.0;
74 const TWO: Self = 2.0;
75
76 #[inline]
77 fn sin(self) -> Self { self.sin() }
78 #[inline]
79 fn cos(self) -> Self { self.cos() }
80 #[inline]
81 fn atan2(self, other: Self) -> Self { self.atan2(other) }
82 #[inline]
83 fn sqrt(self) -> Self { self.sqrt() }
84 #[inline]
85 fn exp(self) -> Self { self.exp() }
86 #[inline]
87 fn powf(self, n: Self) -> Self { self.powf(n) }
88 #[inline]
89 fn abs(self) -> Self { self.abs() }
90 #[inline]
91 fn from_usize(n: usize) -> Self { n as f32 }
92 #[inline]
93 fn from_f64(f: f64) -> Self { f as f32 }
94 #[inline]
95 fn to_f64(self) -> f64 { self as f64 }
96 #[inline]
97 fn is_nan(self) -> bool { self.is_nan() }
98 #[inline]
99 fn is_infinite(self) -> bool { self.is_infinite() }
100}
101
102#[derive(Clone, Copy, Debug)]
104pub struct Complex<T: Float> {
105 pub re: T,
106 pub im: T,
107}
108
109impl<T: Float> Complex<T> {
110 #[inline]
111 pub fn new(re: T, im: T) -> Self {
112 Self { re, im }
113 }
114
115 #[inline]
116 pub fn zero() -> Self {
117 Self { re: T::ZERO, im: T::ZERO }
118 }
119
120 #[inline]
121 pub fn one() -> Self {
122 Self { re: T::ONE, im: T::ZERO }
123 }
124
125 #[inline]
126 pub fn i() -> Self {
127 Self { re: T::ZERO, im: T::ONE }
128 }
129
130 #[inline]
132 pub fn norm(&self) -> T {
133 (self.re * self.re + self.im * self.im).sqrt()
134 }
135
136 #[inline]
138 pub fn norm_sqr(&self) -> T {
139 self.re * self.re + self.im * self.im
140 }
141
142 #[inline]
144 pub fn conj(&self) -> Self {
145 Self {
146 re: self.re,
147 im: T::ZERO - self.im,
148 }
149 }
150
151 #[inline]
153 pub fn arg(&self) -> T {
154 self.im.atan2(self.re)
155 }
156
157 #[inline]
159 pub fn exp(&self) -> Self {
160 let r = self.re.exp();
161 Self {
162 re: r * self.im.cos(),
163 im: r * self.im.sin(),
164 }
165 }
166
167 #[inline]
169 pub fn powf(&self, n: T) -> Self {
170 let r = self.norm();
171 let theta = self.arg();
172 let r_n = r.powf(n);
173 let n_theta = n * theta;
174 Self {
175 re: r_n * n_theta.cos(),
176 im: r_n * n_theta.sin(),
177 }
178 }
179
180 #[inline]
182 pub fn is_nan(&self) -> bool {
183 self.re.is_nan() || self.im.is_nan()
184 }
185
186 #[inline]
188 pub fn is_infinite(&self) -> bool {
189 self.re.is_infinite() || self.im.is_infinite()
190 }
191
192 #[inline]
194 pub fn is_finite(&self) -> bool {
195 !self.is_nan() && !self.is_infinite()
196 }
197}
198
199impl<T: Float> std::ops::Add for Complex<T> {
200 type Output = Self;
201 #[inline]
202 fn add(self, rhs: Self) -> Self {
203 Self {
204 re: self.re + rhs.re,
205 im: self.im + rhs.im,
206 }
207 }
208}
209
210impl<T: Float> std::ops::Sub for Complex<T> {
211 type Output = Self;
212 #[inline]
213 fn sub(self, rhs: Self) -> Self {
214 Self {
215 re: self.re - rhs.re,
216 im: self.im - rhs.im,
217 }
218 }
219}
220
221impl<T: Float> std::ops::Mul for Complex<T> {
222 type Output = Self;
223 #[inline]
224 fn mul(self, rhs: Self) -> Self {
225 Self {
226 re: self.re * rhs.re - self.im * rhs.im,
227 im: self.re * rhs.im + self.im * rhs.re,
228 }
229 }
230}
231
232impl<T: Float> std::ops::Mul for &Complex<T> {
233 type Output = Complex<T>;
234 #[inline]
235 fn mul(self, rhs: Self) -> Complex<T> {
236 Complex {
237 re: self.re * rhs.re - self.im * rhs.im,
238 im: self.re * rhs.im + self.im * rhs.re,
239 }
240 }
241}
242
243impl<T: Float> std::ops::Div for Complex<T> {
244 type Output = Self;
245 #[inline]
246 fn div(self, rhs: Self) -> Self {
247 let denom = rhs.re * rhs.re + rhs.im * rhs.im;
248 Self {
249 re: (self.re * rhs.re + self.im * rhs.im) / denom,
250 im: (self.im * rhs.re - self.re * rhs.im) / denom,
251 }
252 }
253}
254
255impl<T: Float> std::ops::Div<T> for Complex<T> {
256 type Output = Self;
257 #[inline]
258 fn div(self, rhs: T) -> Self {
259 Self {
260 re: self.re / rhs,
261 im: self.im / rhs,
262 }
263 }
264}
265
266impl<T: Float> std::ops::Mul<T> for Complex<T> {
267 type Output = Self;
268 #[inline]
269 fn mul(self, rhs: T) -> Self {
270 Self {
271 re: self.re * rhs,
272 im: self.im * rhs,
273 }
274 }
275}
276
277impl<T: Float> std::ops::AddAssign for Complex<T> {
278 #[inline]
279 fn add_assign(&mut self, rhs: Self) {
280 self.re = self.re + rhs.re;
281 self.im = self.im + rhs.im;
282 }
283}
284
285impl<T: Float> std::ops::SubAssign for Complex<T> {
286 #[inline]
287 fn sub_assign(&mut self, rhs: Self) {
288 self.re = self.re - rhs.re;
289 self.im = self.im - rhs.im;
290 }
291}
292
293impl<T: Float> std::ops::MulAssign<T> for Complex<T> {
294 #[inline]
295 fn mul_assign(&mut self, rhs: T) {
296 self.re = self.re * rhs;
297 self.im = self.im * rhs;
298 }
299}
300
301impl<T: Float> std::iter::Sum for Complex<T> {
302 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
303 iter.fold(Complex::zero(), |acc, x| acc + x)
304 }
305}
306
307impl<'a, T: Float> std::iter::Sum<&'a Complex<T>> for Complex<T> {
308 fn sum<I: Iterator<Item = &'a Complex<T>>>(iter: I) -> Self {
309 iter.fold(Complex::zero(), |acc, x| acc + *x)
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq)]
315pub enum FftError {
316 InvalidSize,
318 ContainsNaN,
320 ContainsInfinity,
322 EmptyInput,
324}
325
326impl std::fmt::Display for FftError {
327 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
328 match self {
329 FftError::InvalidSize => write!(f, "Tamanho deve ser potência de 2"),
330 FftError::ContainsNaN => write!(f, "Input contém NaN"),
331 FftError::ContainsInfinity => write!(f, "Input contém infinito"),
332 FftError::EmptyInput => write!(f, "Input vazio"),
333 }
334 }
335}
336
337impl std::error::Error for FftError {}
338
339pub type Result<T> = std::result::Result<T, FftError>;
340
341#[inline]
343fn is_power_of_two(n: usize) -> bool {
344 n > 0 && (n & (n - 1)) == 0
345}
346
347#[inline]
349fn log2(mut n: usize) -> usize {
350 let mut log = 0;
351 while n > 1 {
352 n >>= 1;
353 log += 1;
354 }
355 log
356}
357
358#[inline]
361fn bit_reverse_permutation<T: Float>(data: &mut [Complex<T>]) {
362 let n = data.len();
363 let log_n = log2(n);
364
365 for i in 0..n {
366 let j = reverse_bits(i, log_n);
367 if i < j {
368 data.swap(i, j);
369 }
370 }
371}
372
373#[inline]
375fn reverse_bits(mut x: usize, bits: usize) -> usize {
376 let mut result = 0;
377 for _ in 0..bits {
378 result = (result << 1) | (x & 1);
379 x >>= 1;
380 }
381 result
382}
383
384pub struct FftPlanner<T: Float> {
386 size: usize,
387 twiddles: Vec<Complex<T>>,
388 inverse: bool,
389}
390
391impl<T: Float> FftPlanner<T> {
392 pub fn new(size: usize, inverse: bool) -> Result<Self> {
394 if size == 0 {
395 return Err(FftError::EmptyInput);
396 }
397 if !is_power_of_two(size) {
398 return Err(FftError::InvalidSize);
399 }
400
401 let twiddles = Self::precompute_twiddles(size, inverse);
402 Ok(Self { size, twiddles, inverse })
403 }
404
405 fn precompute_twiddles(n: usize, inverse: bool) -> Vec<Complex<T>> {
407 let sign = if inverse { T::ONE } else { T::ZERO - T::ONE };
408 let mut twiddles = Vec::with_capacity(n / 2);
409
410 for k in 0..n/2 {
411 let angle = sign * T::TWO * T::PI * T::from_usize(k) / T::from_usize(n);
412 twiddles.push(Complex::new(angle.cos(), angle.sin()));
413 }
414
415 twiddles
416 }
417
418 pub fn process_in_place(&self, data: &mut [Complex<T>]) -> Result<()> {
420 if data.len() != self.size {
421 return Err(FftError::InvalidSize);
422 }
423
424 for c in data.iter() {
426 if c.is_nan() {
427 return Err(FftError::ContainsNaN);
428 }
429 if c.is_infinite() {
430 return Err(FftError::ContainsInfinity);
431 }
432 }
433
434 bit_reverse_permutation(data);
436
437 let n = data.len();
439 let mut size = 2;
440
441 while size <= n {
442 let half_size = size / 2;
443 let step = n / size;
444
445 for i in (0..n).step_by(size) {
446 for j in 0..half_size {
447 let k = j * step;
448 let twiddle = self.twiddles[k];
449
450 let t = twiddle * data[i + j + half_size];
451 let u = data[i + j];
452
453 data[i + j] = u + t;
454 data[i + j + half_size] = u - t;
455 }
456 }
457
458 size *= 2;
459 }
460
461 if self.inverse {
463 let scale = T::ONE / T::from_usize(n);
464 for c in data.iter_mut() {
465 *c *= scale;
466 }
467 }
468
469 Ok(())
470 }
471
472 pub fn process(&self, input: &[Complex<T>]) -> Result<Vec<Complex<T>>> {
474 let mut output = input.to_vec();
475 self.process_in_place(&mut output)?;
476 Ok(output)
477 }
478}
479
480pub struct Fft {
481 size: usize,
482}
483
484impl Fft {
485 pub fn new(size: usize) -> Self {
486 Self { size }
487 }
488
489 pub fn process(&self, input: &[Complex<f64>]) -> Vec<Complex<f64>> {
490 if input.len() != self.size {
491 panic!("Input size mismatch");
492 }
493 fft_recursive(input)
494 }
495
496 pub fn process_with_scratch(&self, input: &mut [Complex<f64>], _scratch: &mut [Complex<f64>]) {
497 let result = self.process(input);
498 input.copy_from_slice(&result);
499 }
500}
501
502fn fft_recursive(x: &[Complex<f64>]) -> Vec<Complex<f64>> {
503 let n = x.len();
504
505 if n <= 1 {
506 return x.to_vec();
507 }
508
509 let mut even = Vec::with_capacity(n / 2);
511 let mut odd = Vec::with_capacity(n / 2);
512
513 for (i, &val) in x.iter().enumerate() {
514 if i % 2 == 0 {
515 even.push(val);
516 } else {
517 odd.push(val);
518 }
519 }
520
521 let even_fft = fft_recursive(&even);
523 let odd_fft = fft_recursive(&odd);
524
525 let mut result = vec![Complex::zero(); n];
527
528 for k in 0..n / 2 {
529 let angle = -2.0 * PI * (k as f64) / (n as f64);
530 let twiddle = Complex::new(angle.cos(), angle.sin());
531 let t = twiddle * odd_fft[k];
532
533 result[k] = even_fft[k] + t;
534 result[k + n / 2] = even_fft[k] - t;
535 }
536
537 result
538}
539
540pub fn fft(input: &[Complex<f64>]) -> Vec<Complex<f64>> {
542 fft_recursive(input)
543}
544
545pub fn fft_generic<T: Float>(input: &[Complex<T>]) -> Result<Vec<Complex<T>>> {
547 let planner = FftPlanner::new(input.len(), false)?;
548 planner.process(input)
549}
550
551pub fn ifft(input: &[Complex<f64>]) -> Vec<Complex<f64>> {
553 let n = input.len();
554
555 let conjugated: Vec<Complex<f64>> = input
557 .iter()
558 .map(|c| Complex::new(c.re, -c.im))
559 .collect();
560
561 let result = fft_recursive(&conjugated);
563
564 result
566 .iter()
567 .map(|c| Complex::new(c.re / n as f64, -c.im / n as f64))
568 .collect()
569}
570
571pub fn ifft_generic<T: Float>(input: &[Complex<T>]) -> Result<Vec<Complex<T>>> {
573 let planner = FftPlanner::new(input.len(), true)?;
574 planner.process(input)
575}
576
577pub fn rfft<T: Float>(input: &[T]) -> Result<Vec<Complex<T>>> {
580 let n = input.len();
581 if !is_power_of_two(n) {
582 return Err(FftError::InvalidSize);
583 }
584
585 let complex_input: Vec<Complex<T>> = input
587 .iter()
588 .map(|&x| Complex::new(x, T::ZERO))
589 .collect();
590
591 let full_fft = fft_generic(&complex_input)?;
593
594 Ok(full_fft[..n/2 + 1].to_vec())
596}
597
598pub fn irfft<T: Float>(input: &[Complex<T>], n: usize) -> Result<Vec<T>> {
600 if !is_power_of_two(n) {
601 return Err(FftError::InvalidSize);
602 }
603
604 let mut full_spectrum = vec![Complex::zero(); n];
606
607 for (i, &val) in input.iter().enumerate() {
609 full_spectrum[i] = val;
610 }
611
612 for i in 1..n/2 {
614 full_spectrum[n - i] = full_spectrum[i].conj();
615 }
616
617 let complex_output = ifft_generic(&full_spectrum)?;
619
620 Ok(complex_output.iter().map(|c| c.re).collect())
622}
623
624pub mod window {
626 use super::Float;
627
628 pub fn hamming<T: Float>(n: usize) -> Vec<T> {
630 let n_f = T::from_usize(n);
631 (0..n)
632 .map(|i| {
633 let i_f = T::from_usize(i);
634 let a0 = T::from_usize(54) / T::from_usize(100);
635 let a1 = T::from_usize(46) / T::from_usize(100);
636 let arg = T::TWO * T::PI * i_f / (n_f - T::ONE);
637 a0 - a1 * arg.cos()
638 })
639 .collect()
640 }
641
642 pub fn hann<T: Float>(n: usize) -> Vec<T> {
644 let n_f = T::from_usize(n);
645 (0..n)
646 .map(|i| {
647 let i_f = T::from_usize(i);
648 let arg = T::TWO * T::PI * i_f / (n_f - T::ONE);
649 (T::ONE - arg.cos()) / T::TWO
650 })
651 .collect()
652 }
653
654 pub fn blackman<T: Float>(n: usize) -> Vec<T> {
656 let n_f = T::from_usize(n);
657 (0..n)
658 .map(|i| {
659 let i_f = T::from_usize(i);
660 let a0 = T::from_usize(42) / T::from_usize(100);
661 let a1 = T::from_usize(50) / T::from_usize(100);
662 let a2 = T::from_usize(8) / T::from_usize(100);
663 let arg = T::TWO * T::PI * i_f / (n_f - T::ONE);
664 a0 - a1 * arg.cos() + a2 * (T::TWO * arg).cos()
665 })
666 .collect()
667 }
668
669 pub fn blackman_harris<T: Float>(n: usize) -> Vec<T> {
671 let n_f = T::from_usize(n);
672 (0..n)
673 .map(|i| {
674 let i_f = T::from_usize(i);
675 let a0 = T::from_usize(35875) / T::from_usize(100000);
676 let a1 = T::from_usize(48829) / T::from_usize(100000);
677 let a2 = T::from_usize(14128) / T::from_usize(100000);
678 let a3 = T::from_usize(1168) / T::from_usize(100000);
679
680 let arg = T::TWO * T::PI * i_f / (n_f - T::ONE);
681 a0
682 - a1 * arg.cos()
683 + a2 * (T::TWO * arg).cos()
684 - a3 * (T::TWO * T::from_usize(3) * arg / T::TWO).cos()
685 })
686 .collect()
687 }
688
689 pub fn bartlett<T: Float>(n: usize) -> Vec<T> {
691 let n_f = T::from_usize(n);
692 (0..n)
693 .map(|i| {
694 let i_f = T::from_usize(i);
695 let x = T::TWO * i_f / (n_f - T::ONE) - T::ONE;
696 T::ONE - x.abs()
697 })
698 .collect()
699 }
700
701 pub fn rectangular<T: Float>(n: usize) -> Vec<T> {
703 vec![T::ONE; n]
704 }
705
706 pub fn apply<T: Float>(signal: &[T], window: &[T]) -> Vec<T> {
708 signal.iter()
709 .zip(window.iter())
710 .map(|(&s, &w)| s * w)
711 .collect()
712 }
713}
714
715pub mod fft2d {
717 use super::*;
718
719 #[derive(Clone, Debug)]
721 pub struct Image2D<T: Float> {
722 pub width: usize,
723 pub height: usize,
724 pub data: Vec<Complex<T>>,
725 }
726
727 impl<T: Float> Image2D<T> {
728 pub fn new(width: usize, height: usize) -> Self {
730 Self {
731 width,
732 height,
733 data: vec![Complex::zero(); width * height],
734 }
735 }
736
737 pub fn from_real(width: usize, height: usize, data: Vec<T>) -> Result<Self> {
739 if data.len() != width * height {
740 return Err(FftError::InvalidSize);
741 }
742 Ok(Self {
743 width,
744 height,
745 data: data.iter().map(|&x| Complex::new(x, T::ZERO)).collect(),
746 })
747 }
748
749 pub fn from_complex(width: usize, height: usize, data: Vec<Complex<T>>) -> Result<Self> {
751 if data.len() != width * height {
752 return Err(FftError::InvalidSize);
753 }
754 Ok(Self { width, height, data })
755 }
756
757 #[inline]
759 pub fn get(&self, x: usize, y: usize) -> Complex<T> {
760 self.data[y * self.width + x]
761 }
762
763 #[inline]
765 pub fn set(&mut self, x: usize, y: usize, value: Complex<T>) {
766 self.data[y * self.width + x] = value;
767 }
768
769 pub fn get_row(&self, y: usize) -> Vec<Complex<T>> {
771 let start = y * self.width;
772 self.data[start..start + self.width].to_vec()
773 }
774
775 pub fn set_row(&mut self, y: usize, row: &[Complex<T>]) {
777 let start = y * self.width;
778 self.data[start..start + self.width].copy_from_slice(row);
779 }
780
781 pub fn get_column(&self, x: usize) -> Vec<Complex<T>> {
783 (0..self.height)
784 .map(|y| self.get(x, y))
785 .collect()
786 }
787
788 pub fn set_column(&mut self, x: usize, column: &[Complex<T>]) {
790 for (y, &value) in column.iter().enumerate() {
791 self.set(x, y, value);
792 }
793 }
794
795 pub fn magnitude(&self) -> Vec<T> {
797 self.data.iter().map(|c| c.norm()).collect()
798 }
799
800 pub fn power_spectrum(&self) -> Vec<T> {
802 self.data.iter().map(|c| c.norm_sqr()).collect()
803 }
804
805 pub fn fftshift(&mut self) {
807 let half_h = self.height / 2;
808 let half_w = self.width / 2;
809
810 for y in 0..half_h {
812 for x in 0..half_w {
813 let temp = self.get(x, y);
815 self.set(x, y, self.get(x + half_w, y + half_h));
816 self.set(x + half_w, y + half_h, temp);
817
818 let temp = self.get(x + half_w, y);
820 self.set(x + half_w, y, self.get(x, y + half_h));
821 self.set(x, y + half_h, temp);
822 }
823 }
824 }
825 }
826
827 pub struct Fft2DPlanner<T: Float> {
829 width: usize,
830 height: usize,
831 row_planner: FftPlanner<T>,
832 col_planner: FftPlanner<T>,
833 #[allow(dead_code)]
834 inverse: bool,
835 }
836
837 impl<T: Float> Fft2DPlanner<T> {
838 pub fn new(width: usize, height: usize, inverse: bool) -> Result<Self> {
840 let row_planner = FftPlanner::new(width, inverse)?;
841 let col_planner = FftPlanner::new(height, inverse)?;
842
843 Ok(Self {
844 width,
845 height,
846 row_planner,
847 col_planner,
848 inverse,
849 })
850 }
851
852 pub fn process(&self, image: &Image2D<T>) -> Result<Image2D<T>> {
856 if image.width != self.width || image.height != self.height {
857 return Err(FftError::InvalidSize);
858 }
859
860 let mut result = image.clone();
861
862 for y in 0..self.height {
864 let row = result.get_row(y);
865 let transformed = self.row_planner.process(&row)?;
866 result.set_row(y, &transformed);
867 }
868
869 for x in 0..self.width {
871 let col = result.get_column(x);
872 let transformed = self.col_planner.process(&col)?;
873 result.set_column(x, &transformed);
874 }
875
876 Ok(result)
877 }
878
879 pub fn process_in_place(&self, image: &mut Image2D<T>) -> Result<()> {
881 if image.width != self.width || image.height != self.height {
882 return Err(FftError::InvalidSize);
883 }
884
885 for y in 0..self.height {
887 let mut row = image.get_row(y);
888 self.row_planner.process_in_place(&mut row)?;
889 image.set_row(y, &row);
890 }
891
892 for x in 0..self.width {
894 let mut col = image.get_column(x);
895 self.col_planner.process_in_place(&mut col)?;
896 image.set_column(x, &col);
897 }
898
899 Ok(())
900 }
901 }
902
903 pub fn fft2d<T: Float>(image: &Image2D<T>) -> Result<Image2D<T>> {
905 let planner = Fft2DPlanner::new(image.width, image.height, false)?;
906 planner.process(image)
907 }
908
909 pub fn ifft2d<T: Float>(image: &Image2D<T>) -> Result<Image2D<T>> {
911 let planner = Fft2DPlanner::new(image.width, image.height, true)?;
912 planner.process(image)
913 }
914}
915
916pub mod filters {
918 use super::*;
919 use super::fft2d::*;
920
921 #[derive(Debug, Clone, Copy)]
923 pub enum FilterType {
924 LowPass,
925 HighPass,
926 BandPass,
927 BandReject,
928 }
929
930 pub struct IdealFilter<T: Float> {
932 width: usize,
933 height: usize,
934 filter_type: FilterType,
935 cutoff: T,
936 cutoff2: Option<T>, }
938
939 impl<T: Float> IdealFilter<T> {
940 pub fn new(width: usize, height: usize, filter_type: FilterType, cutoff: T) -> Self {
942 Self {
943 width,
944 height,
945 filter_type,
946 cutoff,
947 cutoff2: None,
948 }
949 }
950
951 pub fn new_band(
953 width: usize,
954 height: usize,
955 filter_type: FilterType,
956 cutoff1: T,
957 cutoff2: T,
958 ) -> Self {
959 Self {
960 width,
961 height,
962 filter_type,
963 cutoff: cutoff1,
964 cutoff2: Some(cutoff2),
965 }
966 }
967
968 #[inline]
970 fn distance(&self, x: usize, y: usize) -> T {
971 let cx = T::from_usize(self.width) / T::TWO;
972 let cy = T::from_usize(self.height) / T::TWO;
973 let dx = T::from_usize(x) - cx;
974 let dy = T::from_usize(y) - cy;
975 (dx * dx + dy * dy).sqrt()
976 }
977
978 pub fn apply(&self, freq_image: &mut Image2D<T>) {
980 for y in 0..self.height {
981 for x in 0..self.width {
982 let dist = self.distance(x, y);
983 let factor = self.filter_factor(dist);
984 let pixel = freq_image.get(x, y);
985 freq_image.set(x, y, pixel * factor);
986 }
987 }
988 }
989
990 fn filter_factor(&self, distance: T) -> T {
992 match self.filter_type {
993 FilterType::LowPass => {
994 if distance <= self.cutoff {
995 T::ONE
996 } else {
997 T::ZERO
998 }
999 }
1000 FilterType::HighPass => {
1001 if distance > self.cutoff {
1002 T::ONE
1003 } else {
1004 T::ZERO
1005 }
1006 }
1007 FilterType::BandPass => {
1008 let cutoff2 = self.cutoff2.unwrap_or(self.cutoff);
1009 if distance >= self.cutoff && distance <= cutoff2 {
1010 T::ONE
1011 } else {
1012 T::ZERO
1013 }
1014 }
1015 FilterType::BandReject => {
1016 let cutoff2 = self.cutoff2.unwrap_or(self.cutoff);
1017 if distance < self.cutoff || distance > cutoff2 {
1018 T::ONE
1019 } else {
1020 T::ZERO
1021 }
1022 }
1023 }
1024 }
1025 }
1026
1027 pub struct GaussianFilter<T: Float> {
1029 width: usize,
1030 height: usize,
1031 sigma: T,
1032 high_pass: bool,
1033 }
1034
1035 impl<T: Float> GaussianFilter<T> {
1036 pub fn new(width: usize, height: usize, sigma: T, high_pass: bool) -> Self {
1038 Self {
1039 width,
1040 height,
1041 sigma,
1042 high_pass,
1043 }
1044 }
1045
1046 #[inline]
1048 fn distance(&self, x: usize, y: usize) -> T {
1049 let cx = T::from_usize(self.width) / T::TWO;
1050 let cy = T::from_usize(self.height) / T::TWO;
1051 let dx = T::from_usize(x) - cx;
1052 let dy = T::from_usize(y) - cy;
1053 (dx * dx + dy * dy).sqrt()
1054 }
1055
1056 pub fn apply(&self, freq_image: &mut Image2D<T>) {
1058 for y in 0..self.height {
1059 for x in 0..self.width {
1060 let dist = self.distance(x, y);
1061 let dist_sqr = dist * dist;
1062 let sigma_sqr = self.sigma * self.sigma;
1063
1064 let factor = (T::ZERO - dist_sqr / (T::TWO * sigma_sqr)).exp();
1066
1067 let final_factor = if self.high_pass {
1068 T::ONE - factor } else {
1070 factor
1071 };
1072
1073 let pixel = freq_image.get(x, y);
1074 freq_image.set(x, y, pixel * final_factor);
1075 }
1076 }
1077 }
1078 }
1079
1080 pub fn convolve2d<T: Float>(
1082 image: &Image2D<T>,
1083 kernel: &Image2D<T>,
1084 ) -> Result<Image2D<T>> {
1085 if image.width != kernel.width || image.height != kernel.height {
1087 return Err(FftError::InvalidSize);
1088 }
1089
1090 let freq_image = fft2d(image)?;
1092 let freq_kernel = fft2d(kernel)?;
1093
1094 let mut result = freq_image.clone();
1096 for i in 0..result.data.len() {
1097 result.data[i] = freq_image.data[i] * freq_kernel.data[i];
1098 }
1099
1100 ifft2d(&result)
1102 }
1103}
1104
1105pub mod timefreq {
1107 use super::*;
1108
1109 #[derive(Debug, Clone, Copy)]
1111 pub struct OverlapConfig {
1112 pub window_size: usize,
1114 pub hop_size: usize,
1116 }
1117
1118 impl OverlapConfig {
1119 pub fn overlap_50(window_size: usize) -> Self {
1121 Self {
1122 window_size,
1123 hop_size: window_size / 2,
1124 }
1125 }
1126
1127 pub fn overlap_75(window_size: usize) -> Self {
1129 Self {
1130 window_size,
1131 hop_size: window_size / 4,
1132 }
1133 }
1134
1135 pub fn new(window_size: usize, hop_size: usize) -> Result<Self> {
1137 if hop_size == 0 || hop_size > window_size {
1138 return Err(FftError::InvalidSize);
1139 }
1140 Ok(Self { window_size, hop_size })
1141 }
1142
1143 pub fn num_frames(&self, signal_len: usize) -> usize {
1145 if signal_len < self.window_size {
1146 return 0;
1147 }
1148 (signal_len - self.window_size) / self.hop_size + 1
1149 }
1150
1151 pub fn overlap_percent(&self) -> f64 {
1153 100.0 * (1.0 - self.hop_size as f64 / self.window_size as f64)
1154 }
1155 }
1156
1157 #[derive(Clone, Debug)]
1159 pub struct Spectrogram<T: Float> {
1160 pub data: Vec<Vec<Complex<T>>>,
1162 pub num_frames: usize,
1164 pub num_freqs: usize,
1166 pub sample_rate: T,
1168 pub config: OverlapConfig,
1170 }
1171
1172 impl<T: Float> Spectrogram<T> {
1173 pub fn new(
1175 num_frames: usize,
1176 num_freqs: usize,
1177 sample_rate: T,
1178 config: OverlapConfig,
1179 ) -> Self {
1180 let data = vec![vec![Complex::zero(); num_frames]; num_freqs];
1181 Self {
1182 data,
1183 num_frames,
1184 num_freqs,
1185 sample_rate,
1186 config,
1187 }
1188 }
1189
1190 #[inline]
1192 pub fn get(&self, freq_idx: usize, time_idx: usize) -> Complex<T> {
1193 self.data[freq_idx][time_idx]
1194 }
1195
1196 #[inline]
1198 pub fn set(&mut self, freq_idx: usize, time_idx: usize, value: Complex<T>) {
1199 self.data[freq_idx][time_idx] = value;
1200 }
1201
1202 pub fn magnitude(&self) -> Vec<Vec<T>> {
1204 self.data
1205 .iter()
1206 .map(|freq_row| freq_row.iter().map(|c| c.norm()).collect())
1207 .collect()
1208 }
1209
1210 pub fn power(&self) -> Vec<Vec<T>> {
1212 self.data
1213 .iter()
1214 .map(|freq_row| freq_row.iter().map(|c| c.norm_sqr()).collect())
1215 .collect()
1216 }
1217
1218 pub fn phase(&self) -> Vec<Vec<T>> {
1220 self.data
1221 .iter()
1222 .map(|freq_row| freq_row.iter().map(|c| c.arg()).collect())
1223 .collect()
1224 }
1225
1226 pub fn magnitude_db(&self) -> Vec<Vec<T>> {
1228 let mag = self.magnitude();
1229 let twenty = T::from_usize(20);
1230 let epsilon = T::from_usize(1) / T::from_usize(1000000);
1231 mag.iter()
1232 .map(|row| {
1233 row.iter()
1234 .map(|&m| {
1235 let m_safe = if m > T::ZERO { m } else { epsilon };
1237 let log_val = T::from_f64(m_safe.to_f64().log10());
1238 twenty * log_val
1239 })
1240 .collect()
1241 })
1242 .collect()
1243 }
1244
1245 pub fn frequencies(&self) -> Vec<T> {
1247 (0..self.num_freqs)
1248 .map(|k| {
1249 T::from_usize(k) * self.sample_rate / T::from_usize(self.config.window_size)
1250 })
1251 .collect()
1252 }
1253
1254 pub fn times(&self) -> Vec<T> {
1256 (0..self.num_frames)
1257 .map(|frame| {
1258 T::from_usize(frame * self.config.hop_size) / self.sample_rate
1259 })
1260 .collect()
1261 }
1262
1263 pub fn spectral_centroid(&self) -> Vec<T> {
1265 let freqs = self.frequencies();
1266 let mag = self.magnitude();
1267
1268 (0..self.num_frames)
1269 .map(|frame| {
1270 let mut weighted_sum = T::ZERO;
1271 let mut total_mag = T::ZERO;
1272
1273 for freq_idx in 0..self.num_freqs {
1274 let m = mag[freq_idx][frame];
1275 weighted_sum = weighted_sum + freqs[freq_idx] * m;
1276 total_mag = total_mag + m;
1277 }
1278
1279 if total_mag > T::ZERO {
1280 weighted_sum / total_mag
1281 } else {
1282 T::ZERO
1283 }
1284 })
1285 .collect()
1286 }
1287
1288 pub fn spectral_bandwidth(&self) -> Vec<T> {
1290 let freqs = self.frequencies();
1291 let mag = self.magnitude();
1292 let centroid = self.spectral_centroid();
1293
1294 (0..self.num_frames)
1295 .map(|frame| {
1296 let mut variance = T::ZERO;
1297 let mut total_mag = T::ZERO;
1298
1299 for freq_idx in 0..self.num_freqs {
1300 let m = mag[freq_idx][frame];
1301 let diff = freqs[freq_idx] - centroid[frame];
1302 variance = variance + (diff * diff) * m;
1303 total_mag = total_mag + m;
1304 }
1305
1306 if total_mag > T::ZERO {
1307 (variance / total_mag).sqrt()
1308 } else {
1309 T::ZERO
1310 }
1311 })
1312 .collect()
1313 }
1314
1315 pub fn spectral_flatness(&self) -> Vec<T> {
1318 let mag = self.magnitude();
1319
1320 (0..self.num_frames)
1321 .map(|frame| {
1322 let mut geometric_mean = T::ONE;
1323 let mut arithmetic_mean = T::ZERO;
1324 let n = T::from_usize(self.num_freqs);
1325
1326 for freq_idx in 0..self.num_freqs {
1327 let m = mag[freq_idx][frame];
1328 let m_safe = if m > T::ZERO { m } else { T::from_usize(1) / T::from_usize(1000000) };
1329 geometric_mean = geometric_mean * m_safe;
1330 arithmetic_mean = arithmetic_mean + m;
1331 }
1332
1333 geometric_mean = geometric_mean.powf(T::ONE / n);
1334 arithmetic_mean = arithmetic_mean / n;
1335
1336 if arithmetic_mean > T::ZERO {
1337 geometric_mean / arithmetic_mean
1338 } else {
1339 T::ZERO
1340 }
1341 })
1342 .collect()
1343 }
1344
1345 pub fn spectral_rolloff(&self, threshold_percent: T) -> Vec<T> {
1347 let freqs = self.frequencies();
1348 let power = self.power();
1349
1350 (0..self.num_frames)
1351 .map(|frame| {
1352 let mut total_energy = T::ZERO;
1354 for freq_idx in 0..self.num_freqs {
1355 total_energy = total_energy + power[freq_idx][frame];
1356 }
1357
1358 let threshold = total_energy * threshold_percent / T::from_usize(100);
1359 let mut cumulative = T::ZERO;
1360
1361 for freq_idx in 0..self.num_freqs {
1363 cumulative = cumulative + power[freq_idx][frame];
1364 if cumulative >= threshold {
1365 return freqs[freq_idx];
1366 }
1367 }
1368
1369 freqs[self.num_freqs - 1]
1370 })
1371 .collect()
1372 }
1373 }
1374
1375 pub struct StftProcessor<T: Float> {
1377 config: OverlapConfig,
1378 window: Vec<T>,
1379 planner: FftPlanner<T>,
1380 }
1381
1382 impl<T: Float> StftProcessor<T> {
1383 pub fn new(config: OverlapConfig, window_type: WindowType) -> Result<Self> {
1385 if !is_power_of_two(config.window_size) {
1386 return Err(FftError::InvalidSize);
1387 }
1388
1389 let window = match window_type {
1390 WindowType::Hann => window::hann(config.window_size),
1391 WindowType::Hamming => window::hamming(config.window_size),
1392 WindowType::Blackman => window::blackman(config.window_size),
1393 WindowType::BlackmanHarris => window::blackman_harris(config.window_size),
1394 WindowType::Rectangle => window::rectangular(config.window_size),
1395 };
1396
1397 let planner = FftPlanner::new(config.window_size, false)?;
1398
1399 Ok(Self {
1400 config,
1401 window,
1402 planner,
1403 })
1404 }
1405
1406 pub fn process(&self, signal: &[T], sample_rate: T) -> Result<Spectrogram<T>> {
1408 let num_frames = self.config.num_frames(signal.len());
1409 if num_frames == 0 {
1410 return Err(FftError::InvalidSize);
1411 }
1412
1413 let num_freqs = self.config.window_size / 2 + 1; let mut spec = Spectrogram::new(num_frames, num_freqs, sample_rate, self.config);
1415
1416 for frame_idx in 0..num_frames {
1418 let start = frame_idx * self.config.hop_size;
1419 let end = start + self.config.window_size;
1420
1421 let mut frame: Vec<Complex<T>> = signal[start..end]
1423 .iter()
1424 .zip(self.window.iter())
1425 .map(|(&s, &w)| Complex::new(s * w, T::ZERO))
1426 .collect();
1427
1428 self.planner.process_in_place(&mut frame)?;
1430
1431 for freq_idx in 0..num_freqs {
1433 spec.set(freq_idx, frame_idx, frame[freq_idx]);
1434 }
1435 }
1436
1437 Ok(spec)
1438 }
1439
1440 pub fn inverse(&self, spec: &Spectrogram<T>) -> Result<Vec<T>> {
1442 let signal_len = (spec.num_frames - 1) * self.config.hop_size + self.config.window_size;
1443 let mut signal = vec![T::ZERO; signal_len];
1444 let mut window_sum = vec![T::ZERO; signal_len];
1445
1446 let ifft_planner = FftPlanner::new(self.config.window_size, true)?;
1447
1448 for frame_idx in 0..spec.num_frames {
1449 let mut full_spectrum = vec![Complex::zero(); self.config.window_size];
1451
1452 for freq_idx in 0..spec.num_freqs {
1453 full_spectrum[freq_idx] = spec.get(freq_idx, frame_idx);
1454 }
1455
1456 for freq_idx in 1..spec.num_freqs - 1 {
1458 full_spectrum[self.config.window_size - freq_idx] =
1459 full_spectrum[freq_idx].conj();
1460 }
1461
1462 ifft_planner.process_in_place(&mut full_spectrum)?;
1464
1465 let start = frame_idx * self.config.hop_size;
1467 for i in 0..self.config.window_size {
1468 let pos = start + i;
1469 signal[pos] = signal[pos] + full_spectrum[i].re * self.window[i];
1470 window_sum[pos] = window_sum[pos] + self.window[i] * self.window[i];
1471 }
1472 }
1473
1474 for i in 0..signal_len {
1476 if window_sum[i] > T::ZERO {
1477 signal[i] = signal[i] / window_sum[i];
1478 }
1479 }
1480
1481 Ok(signal)
1482 }
1483 }
1484
1485 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1487 pub enum WindowType {
1488 Hann,
1489 Hamming,
1490 Blackman,
1491 BlackmanHarris,
1492 Rectangle,
1493 }
1494}
1495
1496pub mod num_complex {
1498 pub use super::Complex;
1499 pub type Complex64 = Complex<f64>;
1500 pub type Complex32 = Complex<f32>;
1501}
1502
1503#[cfg(test)]
1504mod tests {
1505 use super::*;
1506
1507 const EPSILON_F64: f64 = 1e-10;
1508 const EPSILON_F32: f32 = 1e-5;
1509
1510 #[test]
1511 fn test_is_power_of_two() {
1512 assert!(is_power_of_two(1));
1513 assert!(is_power_of_two(2));
1514 assert!(is_power_of_two(4));
1515 assert!(is_power_of_two(1024));
1516 assert!(!is_power_of_two(0));
1517 assert!(!is_power_of_two(3));
1518 assert!(!is_power_of_two(100));
1519 }
1520
1521 #[test]
1522 fn test_bit_reverse() {
1523 assert_eq!(reverse_bits(0b000, 3), 0b000);
1524 assert_eq!(reverse_bits(0b001, 3), 0b100);
1525 assert_eq!(reverse_bits(0b010, 3), 0b010);
1526 assert_eq!(reverse_bits(0b011, 3), 0b110);
1527 assert_eq!(reverse_bits(0b100, 3), 0b001);
1528 }
1529
1530 #[test]
1531 fn test_fft_simple() {
1532 let input = vec![
1533 Complex::new(1.0, 0.0),
1534 Complex::new(0.0, 0.0),
1535 Complex::new(1.0, 0.0),
1536 Complex::new(0.0, 0.0),
1537 ];
1538
1539 let output = fft(&input);
1540 assert_eq!(output.len(), 4);
1541 }
1542
1543 #[test]
1544 fn test_fft_planner() {
1545 let input = vec![
1546 Complex::new(1.0, 0.0),
1547 Complex::new(2.0, 0.0),
1548 Complex::new(3.0, 0.0),
1549 Complex::new(4.0, 0.0),
1550 ];
1551
1552 let planner = FftPlanner::new(4, false).unwrap();
1553 let output = planner.process(&input).unwrap();
1554 assert_eq!(output.len(), 4);
1555 }
1556
1557 #[test]
1558 fn test_ifft_roundtrip() {
1559 let input = vec![
1560 Complex::new(1.0, 0.0),
1561 Complex::new(2.0, 0.0),
1562 Complex::new(3.0, 0.0),
1563 Complex::new(4.0, 0.0),
1564 ];
1565
1566 let freq = fft(&input);
1567 let recovered = ifft(&freq);
1568
1569 for (orig, rec) in input.iter().zip(recovered.iter()) {
1570 assert!((orig.re - rec.re).abs() < EPSILON_F64);
1571 assert!((orig.im - rec.im).abs() < EPSILON_F64);
1572 }
1573 }
1574
1575 #[test]
1576 fn test_fft_planner_roundtrip() {
1577 let input = vec![
1578 Complex::new(1.0, 0.5),
1579 Complex::new(2.0, -0.3),
1580 Complex::new(3.0, 0.7),
1581 Complex::new(4.0, -0.9),
1582 ];
1583
1584 let fft_planner = FftPlanner::new(4, false).unwrap();
1585 let ifft_planner = FftPlanner::new(4, true).unwrap();
1586
1587 let freq = fft_planner.process(&input).unwrap();
1588 let recovered = ifft_planner.process(&freq).unwrap();
1589
1590 for (orig, rec) in input.iter().zip(recovered.iter()) {
1591 assert!((orig.re - rec.re).abs() < EPSILON_F64,
1592 "re: {} vs {}", orig.re, rec.re);
1593 assert!((orig.im - rec.im).abs() < EPSILON_F64,
1594 "im: {} vs {}", orig.im, rec.im);
1595 }
1596 }
1597
1598 #[test]
1599 fn test_fft_impulse() {
1600 let mut input = vec![Complex::zero(); 8];
1602 input[0] = Complex::new(1.0, 0.0);
1603
1604 let output = fft(&input);
1605
1606 for c in output.iter() {
1608 assert!((c.norm() - 1.0).abs() < EPSILON_F64);
1609 }
1610 }
1611
1612 #[test]
1613 fn test_fft_dc_signal() {
1614 let input = vec![Complex::new(1.0, 0.0); 8];
1616 let output = fft(&input);
1617
1618 assert!((output[0].re - 8.0).abs() < EPSILON_F64);
1620 assert!(output[0].im.abs() < EPSILON_F64);
1621
1622 for i in 1..8 {
1624 assert!(output[i].norm() < EPSILON_F64);
1625 }
1626 }
1627
1628 #[test]
1629 fn test_fft_sine_wave() {
1630 let n = 16;
1632 let k = 2; let input: Vec<Complex<f64>> = (0..n)
1634 .map(|i| {
1635 let angle = 2.0 * PI * (k as f64) * (i as f64) / (n as f64);
1636 Complex::new(angle.sin(), 0.0)
1637 })
1638 .collect();
1639
1640 let output = fft(&input);
1641
1642 assert!(output[k].norm() > 5.0); assert!(output[n - k].norm() > 5.0);
1645
1646 for i in 0..n {
1648 if i != k && i != n - k {
1649 assert!(output[i].norm() < 0.1);
1650 }
1651 }
1652 }
1653
1654 #[test]
1655 fn test_parseval_theorem() {
1656 let input = vec![
1658 Complex::new(1.0, 0.5),
1659 Complex::new(2.0, -0.3),
1660 Complex::new(3.0, 0.7),
1661 Complex::new(4.0, -0.9),
1662 ];
1663
1664 let energy_time: f64 = input.iter().map(|c| c.norm_sqr()).sum();
1665
1666 let output = fft(&input);
1667 let energy_freq: f64 = output.iter().map(|c| c.norm_sqr()).sum::<f64>() / (input.len() as f64);
1668
1669 assert!((energy_time - energy_freq).abs() < EPSILON_F64);
1670 }
1671
1672 #[test]
1673 fn test_linearity() {
1674 let x = vec![
1676 Complex::new(1.0, 0.0),
1677 Complex::new(2.0, 0.0),
1678 Complex::new(3.0, 0.0),
1679 Complex::new(4.0, 0.0),
1680 ];
1681
1682 let y = vec![
1683 Complex::new(0.5, 0.5),
1684 Complex::new(1.0, -0.5),
1685 Complex::new(1.5, 0.3),
1686 Complex::new(2.0, -0.7),
1687 ];
1688
1689 let a = 2.0;
1690 let b = 3.0;
1691
1692 let combined: Vec<Complex<f64>> = x.iter()
1694 .zip(y.iter())
1695 .map(|(&xi, &yi)| xi * a + yi * b)
1696 .collect();
1697
1698 let fft_combined = fft(&combined);
1699
1700 let fft_x = fft(&x);
1702 let fft_y = fft(&y);
1703 let expected: Vec<Complex<f64>> = fft_x.iter()
1704 .zip(fft_y.iter())
1705 .map(|(&fx, &fy)| fx * a + fy * b)
1706 .collect();
1707
1708 for (c, e) in fft_combined.iter().zip(expected.iter()) {
1709 assert!((c.re - e.re).abs() < EPSILON_F64);
1710 assert!((c.im - e.im).abs() < EPSILON_F64);
1711 }
1712 }
1713
1714 #[test]
1715 fn test_rfft_real_signal() {
1716 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
1717 let output = rfft(&input).unwrap();
1718
1719 assert_eq!(output.len(), 5);
1721
1722 assert!(output[0].im.abs() < EPSILON_F64);
1724 }
1725
1726 #[test]
1727 fn test_rfft_roundtrip() {
1728 let input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
1729 let spectrum = rfft(&input).unwrap();
1730 let recovered = irfft(&spectrum, input.len()).unwrap();
1731
1732 for (orig, rec) in input.iter().zip(recovered.iter()) {
1733 assert!((orig - rec).abs() < EPSILON_F64);
1734 }
1735 }
1736
1737 #[test]
1738 fn test_hamming_window() {
1739 let window = window::hamming::<f64>(8);
1740 assert_eq!(window.len(), 8);
1741
1742 assert!((window[0] - 0.08).abs() < 0.01);
1744 assert!((window[7] - 0.08).abs() < 0.01);
1745
1746 let max = window.iter().cloned().fold(0.0, f64::max);
1748 assert!(max > 0.9);
1749 }
1750
1751 #[test]
1752 fn test_hann_window() {
1753 let window = window::hann::<f64>(8);
1754 assert_eq!(window.len(), 8);
1755
1756 assert!(window[0].abs() < EPSILON_F64);
1758 assert!(window[7].abs() < EPSILON_F64);
1759 }
1760
1761 #[test]
1762 fn test_window_apply() {
1763 let signal = vec![1.0, 2.0, 3.0, 4.0];
1764 let window = window::rectangular::<f64>(4);
1765 let windowed = window::apply(&signal, &window);
1766
1767 for (s, w) in signal.iter().zip(windowed.iter()) {
1769 assert!((s - w).abs() < EPSILON_F64);
1770 }
1771 }
1772
1773 #[test]
1774 fn test_f32_support() {
1775 let input = vec![
1776 Complex::new(1.0f32, 0.0f32),
1777 Complex::new(2.0f32, 0.0f32),
1778 Complex::new(3.0f32, 0.0f32),
1779 Complex::new(4.0f32, 0.0f32),
1780 ];
1781
1782 let planner = FftPlanner::new(4, false).unwrap();
1783 let output = planner.process(&input).unwrap();
1784 assert_eq!(output.len(), 4);
1785 }
1786
1787 #[test]
1788 fn test_error_handling() {
1789 let input = vec![Complex::new(1.0, 0.0); 3];
1791 assert!(fft_generic(&input).is_err());
1792
1793 assert!(FftPlanner::<f64>::new(0, false).is_err());
1795
1796 assert!(FftPlanner::<f64>::new(5, false).is_err());
1798 }
1799
1800 #[test]
1801 fn test_nan_detection() {
1802 let mut input = vec![Complex::new(1.0, 0.0); 4];
1803 input[2] = Complex::new(f64::NAN, 0.0);
1804
1805 let planner = FftPlanner::new(4, false).unwrap();
1806 assert!(planner.process(&input).is_err());
1807 }
1808
1809 #[test]
1810 fn test_infinity_detection() {
1811 let mut input = vec![Complex::new(1.0, 0.0); 4];
1812 input[1] = Complex::new(f64::INFINITY, 0.0);
1813
1814 let planner = FftPlanner::new(4, false).unwrap();
1815 assert!(planner.process(&input).is_err());
1816 }
1817
1818 mod test_fft2d {
1820 use super::*;
1821 use crate::fft2d::*;
1822
1823 #[test]
1824 fn test_fft2d_basic() {
1825 let data = vec![1.0; 16];
1827 let image = Image2D::from_real(4, 4, data).unwrap();
1828
1829 let freq = fft2d(&image).unwrap();
1830 assert_eq!(freq.data.len(), 16);
1831 }
1832
1833 #[test]
1834 fn test_fft2d_roundtrip() {
1835 let data: Vec<f64> = (0..64)
1837 .map(|i| (i as f64) * 0.1)
1838 .collect();
1839 let image = Image2D::from_real(8, 8, data).unwrap();
1840
1841 let freq = fft2d(&image).unwrap();
1843 let recovered = ifft2d(&freq).unwrap();
1844
1845 for i in 0..64 {
1847 let diff = (image.data[i].re - recovered.data[i].re).abs();
1848 assert!(diff < EPSILON_F64, "Erro em pixel {}: {}", i, diff);
1849 }
1850 }
1851
1852 #[test]
1853 fn test_fft2d_separability() {
1854 let data = vec![1.0, 2.0, 3.0, 4.0];
1857 let image = Image2D::from_real(2, 2, data).unwrap();
1858
1859 let result = fft2d(&image).unwrap();
1860
1861 let dc = result.get(0, 0);
1863 assert!((dc.re - 10.0).abs() < EPSILON_F64);
1864 }
1865
1866 #[test]
1867 fn test_fft2d_parseval() {
1868 let data: Vec<f64> = (0..64)
1870 .map(|i| ((i as f64) * 0.1).sin())
1871 .collect();
1872 let image = Image2D::from_real(8, 8, data).unwrap();
1873
1874 let energy_spatial: f64 = image.data.iter()
1875 .map(|c| c.norm_sqr())
1876 .sum();
1877
1878 let freq = fft2d(&image).unwrap();
1879 let energy_freq: f64 = freq.data.iter()
1880 .map(|c| c.norm_sqr())
1881 .sum::<f64>() / 64.0;
1882
1883 let diff = (energy_spatial - energy_freq).abs();
1884 assert!(diff < EPSILON_F64 * 100.0, "Parseval: {}", diff);
1885 }
1886
1887 #[test]
1888 fn test_image2d_fftshift() {
1889 let mut image = Image2D::new(4, 4);
1890
1891 image.set(0, 0, Complex::new(1.0, 0.0));
1893 image.set(3, 3, Complex::new(2.0, 0.0));
1894
1895 image.fftshift();
1896
1897 assert_eq!(image.get(2, 2).re, 1.0);
1899 assert_eq!(image.get(1, 1).re, 2.0);
1900 }
1901
1902 #[test]
1903 fn test_gaussian_filter() {
1904 use crate::filters::*;
1905
1906 let data = vec![1.0; 64];
1907 let image = Image2D::from_real(8, 8, data).unwrap();
1908
1909 let mut freq = fft2d(&image).unwrap();
1910
1911 let filter = GaussianFilter::new(8, 8, 2.0, false);
1912 filter.apply(&mut freq);
1913
1914 let filtered = ifft2d(&freq).unwrap();
1915
1916 let original_max = 1.0;
1918 let filtered_max = filtered.magnitude().iter()
1919 .cloned()
1920 .fold(0.0, f64::max);
1921
1922 assert!(filtered_max <= original_max);
1923 }
1924
1925 #[test]
1926 fn test_ideal_lowpass_filter() {
1927 use crate::filters::*;
1928
1929 let data = vec![1.0; 64];
1930 let image = Image2D::from_real(8, 8, data).unwrap();
1931
1932 let mut freq = fft2d(&image).unwrap();
1933
1934 let filter = IdealFilter::new(8, 8, FilterType::LowPass, 2.0);
1935 filter.apply(&mut freq);
1936
1937 let result = ifft2d(&freq).unwrap();
1939 assert_eq!(result.data.len(), 64);
1940 }
1941
1942 #[test]
1943 fn test_convolve2d() {
1944 use crate::filters::convolve2d;
1945
1946 let img_data = vec![
1948 1.0, 0.0, 0.0, 0.0,
1949 0.0, 0.0, 0.0, 0.0,
1950 0.0, 0.0, 0.0, 0.0,
1951 0.0, 0.0, 0.0, 0.0,
1952 ];
1953 let image = Image2D::from_real(4, 4, img_data).unwrap();
1954
1955 let kernel_data = vec![
1957 0.0, 0.0, 0.0, 0.0,
1958 0.0, 1.0, 0.0, 0.0,
1959 0.0, 0.0, 0.0, 0.0,
1960 0.0, 0.0, 0.0, 0.0,
1961 ];
1962 let kernel = Image2D::from_real(4, 4, kernel_data).unwrap();
1963
1964 let result = convolve2d(&image, &kernel).unwrap();
1965 assert_eq!(result.data.len(), 16);
1966 }
1967 }
1968}
1969
1970#[cfg(test)]
1972mod bench {
1973 use super::*;
1974 use std::time::Instant;
1975
1976 fn benchmark_fft(size: usize, iterations: usize) -> f64 {
1977 let input: Vec<Complex<f64>> = (0..size)
1978 .map(|i| Complex::new((i as f64).sin(), (i as f64).cos()))
1979 .collect();
1980
1981 let planner = FftPlanner::new(size, false).unwrap();
1982
1983 let start = Instant::now();
1984 for _ in 0..iterations {
1985 let _ = planner.process(&input).unwrap();
1986 }
1987 let duration = start.elapsed();
1988
1989 duration.as_secs_f64() / (iterations as f64)
1990 }
1991
1992 #[test]
1993 #[ignore] fn bench_fft_sizes() {
1995 println!("\n=== Benchmark FFT (tempo médio por operação) ===");
1996
1997 for &size in &[64, 128, 256, 512, 1024, 2048, 4096] {
1998 let iterations = 1000000 / size; let avg_time = benchmark_fft(size, iterations);
2000 let throughput = (size as f64) / avg_time / 1e6; println!("N={:5} : {:.3} µs/op ({:.1} Msamples/s)",
2003 size, avg_time * 1e6, throughput);
2004 }
2005 }
2006
2007 #[test]
2008 #[ignore]
2009 fn bench_comparison_recursive_vs_iterative() {
2010 let size = 1024;
2011 let iterations = 1000;
2012
2013 let input: Vec<Complex<f64>> = (0..size)
2014 .map(|i| Complex::new(i as f64, 0.0))
2015 .collect();
2016
2017 let start = Instant::now();
2019 for _ in 0..iterations {
2020 let _ = fft(&input);
2021 }
2022 let recursive_time = start.elapsed().as_secs_f64() / (iterations as f64);
2023
2024 let planner = FftPlanner::new(size, false).unwrap();
2026 let start = Instant::now();
2027 for _ in 0..iterations {
2028 let _ = planner.process(&input).unwrap();
2029 }
2030 let iterative_time = start.elapsed().as_secs_f64() / (iterations as f64);
2031
2032 println!("\n=== Comparação Recursiva vs Iterativa (N={}) ===", size);
2033 println!("Recursiva : {:.3} µs", recursive_time * 1e6);
2034 println!("Iterativa : {:.3} µs", iterative_time * 1e6);
2035 println!("Speedup : {:.2}x", recursive_time / iterative_time);
2036 }
2037
2038 mod test_stft {
2040 use super::*;
2041 use crate::timefreq::*;
2042
2043 #[test]
2044 fn test_overlap_config() {
2045 let config = OverlapConfig::overlap_50(512);
2046 assert_eq!(config.window_size, 512);
2047 assert_eq!(config.hop_size, 256);
2048 assert_eq!(config.overlap_percent(), 50.0);
2049
2050 let config = OverlapConfig::overlap_75(512);
2051 assert_eq!(config.hop_size, 128);
2052 assert_eq!(config.overlap_percent(), 75.0);
2053 }
2054
2055 #[test]
2056 fn test_num_frames() {
2057 let config = OverlapConfig::overlap_50(512);
2058
2059 assert_eq!(config.num_frames(1536), 5);
2061
2062 assert_eq!(config.num_frames(256), 0);
2064 }
2065
2066 #[test]
2067 fn test_stft_chirp() {
2068 let sample_rate = 4096.0;
2071 let duration = 2.0; let n_samples = (sample_rate * duration) as usize;
2073
2074 let f0 = 100.0; let f1 = 800.0; let chirp_rate = (f1 - f0) / duration;
2077
2078 let signal: Vec<f64> = (0..n_samples)
2079 .map(|i| {
2080 let t = i as f64 / sample_rate;
2081 let freq = f0 + chirp_rate * t;
2082 (2.0 * std::f64::consts::PI * freq * t).sin()
2083 })
2084 .collect();
2085
2086 let config = OverlapConfig::overlap_75(512);
2088 let processor = StftProcessor::new(config, WindowType::Hann).unwrap();
2089 let spec = processor.process(&signal, sample_rate).unwrap();
2090
2091 assert_eq!(spec.num_freqs, 257); assert!(spec.num_frames > 0);
2094
2095 let freqs = spec.frequencies();
2097 let mag = spec.magnitude();
2098
2099 let mut dominant_freqs = Vec::new();
2100 for frame in 0..spec.num_frames {
2101 let mut max_mag = 0.0;
2102 let mut max_idx = 0;
2103
2104 for freq_idx in 0..spec.num_freqs {
2105 if mag[freq_idx][frame] > max_mag {
2106 max_mag = mag[freq_idx][frame];
2107 max_idx = freq_idx;
2108 }
2109 }
2110 dominant_freqs.push(freqs[max_idx]);
2111 }
2112
2113 for i in 1..dominant_freqs.len() {
2115 assert!(
2116 dominant_freqs[i] >= dominant_freqs[i - 1] - 50.0,
2117 "Frequência dominante deve aumentar: {} -> {}",
2118 dominant_freqs[i - 1], dominant_freqs[i]
2119 );
2120 }
2121 }
2122
2123 #[test]
2124 fn test_stft_reversibility() {
2125 let sample_rate = 8192.0;
2127 let duration = 1.0;
2128 let n_samples = (sample_rate * duration) as usize;
2129
2130 let signal: Vec<f64> = (0..n_samples)
2131 .map(|i| {
2132 let t = i as f64 / sample_rate;
2133 (2.0 * std::f64::consts::PI * 200.0 * t).sin()
2135 + 0.5 * (2.0 * std::f64::consts::PI * 400.0 * t).sin()
2136 + 0.3 * (2.0 * std::f64::consts::PI * 800.0 * t).sin()
2137 })
2138 .collect();
2139
2140 let config = OverlapConfig::overlap_75(512);
2142 let processor = StftProcessor::new(config, WindowType::Hann).unwrap();
2143
2144 let spec = processor.process(&signal, sample_rate).unwrap();
2145 let reconstructed = processor.inverse(&spec).unwrap();
2146
2147 let margin = 512; let mut max_error: f64 = 0.0;
2150 let mut rms_error = 0.0;
2151
2152 for i in margin..(n_samples - margin) {
2153 if i < reconstructed.len() {
2154 let error = (signal[i] - reconstructed[i]).abs();
2155 max_error = max_error.max(error);
2156 rms_error += error * error;
2157 }
2158 }
2159
2160 let n_samples_checked = n_samples - 2 * margin;
2161 rms_error = (rms_error / (n_samples_checked as f64)).sqrt();
2162
2163 println!("STFT Reversibilidade - Max error: {:.3e}, RMS error: {:.3e}", max_error, rms_error);
2164
2165 assert!(rms_error < 0.01, "RMS error muito alto: {}", rms_error);
2167 }
2168
2169 #[test]
2170 fn test_spectral_features() {
2171 let sample_rate = 8192.0;
2173 let n_samples = 8192;
2174
2175 let signal: Vec<f64> = (0..n_samples)
2176 .map(|i| {
2177 let t = i as f64 / sample_rate;
2178 (2.0 * std::f64::consts::PI * 440.0 * t).sin()
2180 })
2181 .collect();
2182
2183 let config = OverlapConfig::overlap_50(512);
2184 let processor = StftProcessor::new(config, WindowType::Hann).unwrap();
2185 let spec = processor.process(&signal, sample_rate).unwrap();
2186
2187 let centroids = spec.spectral_centroid();
2189 assert!(centroids.len() > 0);
2190
2191 let avg_centroid: f64 = centroids.iter().sum::<f64>() / (centroids.len() as f64);
2193 println!("Centróide espectral médio: {:.1} Hz (esperado ~440 Hz)", avg_centroid);
2194 assert!((avg_centroid - 440.0).abs() < 100.0);
2195
2196 let bandwidths = spec.spectral_bandwidth();
2198 assert!(bandwidths.len() > 0);
2199
2200 let avg_bandwidth: f64 = bandwidths.iter().sum::<f64>() / (bandwidths.len() as f64);
2202 println!("Largura de banda média: {:.1} Hz", avg_bandwidth);
2203 assert!(avg_bandwidth < 200.0);
2204
2205 let flatness = spec.spectral_flatness();
2207 let avg_flatness: f64 = flatness.iter().sum::<f64>() / (flatness.len() as f64);
2208 println!("Flatness média: {:.4} (esperado próximo de 0 para tom puro)", avg_flatness);
2209
2210 assert!(avg_flatness < 0.3);
2212
2213 let rolloff = spec.spectral_rolloff(85.0); assert!(rolloff.len() > 0);
2216 let avg_rolloff: f64 = rolloff.iter().sum::<f64>() / (rolloff.len() as f64);
2217 println!("Rolloff 85% médio: {:.1} Hz", avg_rolloff);
2218 }
2219
2220 #[test]
2221 fn test_spectrogram_magnitude_db() {
2222 let sample_rate = 8192.0;
2223 let n_samples = 4096;
2224
2225 let signal: Vec<f64> = (0..n_samples)
2226 .map(|i| {
2227 let t = i as f64 / sample_rate;
2228 (2.0 * std::f64::consts::PI * 440.0 * t).sin()
2229 })
2230 .collect();
2231
2232 let config = OverlapConfig::overlap_50(256);
2233 let processor = StftProcessor::new(config, WindowType::Hann).unwrap();
2234 let spec = processor.process(&signal, sample_rate).unwrap();
2235
2236 let mag = spec.magnitude();
2237 let mag_db = spec.magnitude_db();
2238
2239 assert_eq!(mag.len(), mag_db.len());
2241 assert_eq!(mag[0].len(), mag_db[0].len());
2242
2243 let mut max_db = -1000.0;
2245 for freq_row in &mag_db {
2246 for &db_val in freq_row {
2247 if db_val > max_db {
2248 max_db = db_val;
2249 }
2250 }
2251 }
2252
2253 println!("Pico em dB: {:.1} dB", max_db);
2254 assert!(max_db > -20.0);
2255 }
2256
2257 #[test]
2258 fn test_window_comparison() {
2259 let sample_rate = 8192.0;
2261 let n_samples = 4096;
2262
2263 let signal: Vec<f64> = (0..n_samples)
2264 .map(|i| {
2265 let t = i as f64 / sample_rate;
2266 (2.0 * std::f64::consts::PI * 440.0 * t).sin()
2268 + (2.0 * std::f64::consts::PI * 480.0 * t).sin()
2269 })
2270 .collect();
2271
2272 let config = OverlapConfig::overlap_50(512);
2273
2274 for window_type in [WindowType::Rectangle, WindowType::Hann,
2275 WindowType::Hamming, WindowType::Blackman] {
2276 let processor = StftProcessor::new(config, window_type).unwrap();
2277 let spec = processor.process(&signal, sample_rate).unwrap();
2278
2279 let freqs = spec.frequencies();
2280 let mag = spec.magnitude();
2281
2282 let mut peaks = Vec::new();
2284 for frame in 0..spec.num_frames.min(5) {
2285 let mut local_max = 0.0;
2286 let mut local_idx = 0;
2287
2288 for freq_idx in 10..spec.num_freqs - 10 {
2289 if mag[freq_idx][frame] > local_max {
2290 local_max = mag[freq_idx][frame];
2291 local_idx = freq_idx;
2292 }
2293 }
2294 peaks.push(freqs[local_idx]);
2295 }
2296
2297 println!("Janela {:?}: picos em ~{:.0} Hz", window_type, peaks[0]);
2298 }
2299 }
2300 }
2301}
2302
2303pub mod parallel;
2305
2306pub mod streaming;
2308
2309pub mod simd;
2311
2312pub mod cache;
2314
2315pub mod advanced;