Skip to main content

rescale_core/fft/
transform.rs

1//! Algoritma Fast Fourier Transform (FFT) Cooley-Tukey Radix-2 (In-Place & Zero Allocation).
2
3use super::complex::Complex;
4use std::f32::consts::PI;
5
6pub struct Fft;
7
8impl Fft {
9    /// Menghitung Forward FFT (Time domain -> Frequency domain) in-place.
10    /// Catatan: Panjang buffer `buffer.len()` WAJIB berupa perpangkatan 2 (Power of Two: 64, 128, 256, 512, 1024, dst).
11    ///
12    /// # Panics
13    ///
14    /// Panik jika `buffer` kosong atau panjangnya bukan perpangkatan dua.
15    pub fn forward(buffer: &mut [Complex]) {
16        let n = buffer.len();
17        assert!(
18            n > 0 && (n & (n - 1)) == 0,
19            "FFT size must be a power of two"
20        );
21
22        // 1. Permutasi bit-reversal
23        Self::bit_reverse_permutation(buffer);
24
25        // 2. Komputasi Butterfly Cooley-Tukey
26        let mut len = 2;
27        while len <= n {
28            let half_len = len / 2;
29            let angle_step = -2.0 * PI / (len as f32);
30            let w_step = Complex::from_polar(1.0, angle_step);
31
32            let mut i = 0;
33            while i < n {
34                let mut w = Complex::new(1.0, 0.0);
35                for j in 0..half_len {
36                    let u = buffer[i + j];
37                    let v = buffer[i + j + half_len] * w;
38
39                    buffer[i + j] = u + v;
40                    buffer[i + j + half_len] = u - v;
41
42                    w = w * w_step;
43                }
44                i += len;
45            }
46            len *= 2;
47        }
48    }
49
50    /// Menghitung Inverse FFT (Frequency domain -> Time domain) in-place.
51    ///
52    /// # Panics
53    ///
54    /// Panik jika `buffer` kosong atau panjangnya bukan perpangkatan dua.
55    pub fn inverse(buffer: &mut [Complex]) {
56        let n = buffer.len();
57        assert!(
58            n > 0 && (n & (n - 1)) == 0,
59            "IFFT size must be a power of two"
60        );
61
62        // 1. Konjugat kompleks
63        for item in buffer.iter_mut() {
64            *item = item.conjugate();
65        }
66
67        // 2. Forward FFT
68        Self::forward(buffer);
69
70        // 3. Konjugat kompleks & skala 1/N
71        let inv_n = 1.0 / (n as f32);
72        for item in buffer.iter_mut() {
73            *item = item.conjugate() * inv_n;
74        }
75    }
76
77    /// Menghitung Power Spectrum Magnitude dari hasil FFT.
78    ///
79    /// Mengembalikan vektor kosong bila `buffer` kosong.
80    pub fn compute_magnitude_spectrum(buffer: &[Complex]) -> Vec<f32> {
81        if buffer.is_empty() {
82            return Vec::new();
83        }
84        let half = buffer.len() / 2 + 1; // Nyquist limit
85        let mut mags = Vec::with_capacity(half);
86        for item in &buffer[0..half] {
87            mags.push(item.magnitude());
88        }
89        mags
90    }
91
92    /// Menghitung frekuensi dominan (Peak Frequency Bin).
93    ///
94    /// Mengembalikan `(0.0, 0.0)` bila `buffer` kosong (tidak ada bin yang dominan).
95    pub fn find_dominant_frequency(buffer: &[Complex], sample_rate: f32) -> (f32, f32) {
96        if buffer.is_empty() {
97            return (0.0, 0.0);
98        }
99        let half = buffer.len() / 2 + 1;
100        let mut max_mag = 0.0_f32;
101        let mut max_bin = 0;
102
103        for (i, item) in buffer[0..half].iter().enumerate() {
104            let mag = item.magnitude();
105            if mag > max_mag {
106                max_mag = mag;
107                max_bin = i;
108            }
109        }
110
111        let freq_hz = (max_bin as f32) * sample_rate / (buffer.len() as f32);
112        (freq_hz, max_mag)
113    }
114
115    /// Penyusunan ulang indeks bit-reversal
116    fn bit_reverse_permutation(buffer: &mut [Complex]) {
117        let n = buffer.len();
118        let mut j = 0;
119        for i in 0..n {
120            if i < j {
121                buffer.swap(i, j);
122            }
123            let mut bit = n >> 1;
124            while j & bit != 0 {
125                j ^= bit;
126                bit >>= 1;
127            }
128            j ^= bit;
129        }
130    }
131}