fixed_fft/
lib.rs

1//! Fixed-point Fast Fourier Transform
2//!
3//! This crate is intended for use with cores without an FPU and thus
4//! can perform a fixed point FFT more quickly. The FFT code uses
5//! a signed 16 bit number format, which is interpreted as a Q15
6//! format (i.e. one sign bit, 15 fractional bits).
7//!
8//! The code was written under the assumption that a Count Leading Zeros (CLZ)
9//! instruction is available on the target architecture.
10//!
11//! # Examples
12//!
13//! ## FFT with complex input data
14//!
15//! ```
16//! use fixed_fft::{Direction, fft_radix2_q15};
17//! use num_complex::Complex;
18//!
19//! let mut samples = [Complex::new(1000, 0); 4];
20//! fft_radix2_q15(&mut samples, Direction::Forward).unwrap();
21//! assert_eq!(samples[0], Complex::new(4000, 0));
22//! assert_eq!(samples[1], Complex::new(0, 0));
23//! assert_eq!(samples[2], Complex::new(0, 0));
24//! assert_eq!(samples[3], Complex::new(0, 0));
25//! ```
26//!
27//! ## FFT with real input data
28//!
29//! Note that the length of the output data is N / 2 + 1, where N is the FFT length.
30//!
31//! ```
32//! use fixed_fft::fft_radix2_real_q15;
33//! use num_complex::Complex;
34//!
35//! let mut samples = [1000; 4];
36//! let mut result = [Complex::new(0, 0); 3];
37//! fft_radix2_real_q15(&mut samples, &mut result, false).unwrap();
38//! assert_eq!(result[0], Complex::new(4000, 0));
39//! assert_eq!(result[1], Complex::new(0, 0));
40//! assert_eq!(result[2], Complex::new(0, 0));
41//! ```
42//!
43
44#![no_std]
45
46use core::slice;
47use num_complex::Complex;
48
49type Q15 = i16;
50
51pub enum Direction {
52    /// Normal forward FFT
53    Forward,
54
55    /// Forward FFT with output values scaled by 1/N
56    ///
57    /// This variant can be used to avoid numerical overflows.
58    ForwardScaled,
59
60    /// Inverse FFT
61    Inverse,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq)]
65pub enum FFTError {
66    InvalidFFTSize,
67    InvalidDataSize,
68}
69
70const SINETAB_LD_N: usize = 10;
71const SINETAB_N: usize = 1 << SINETAB_LD_N;
72
73/// Sine table used to calculate twiddle factors
74static SINETAB: [i16; SINETAB_N - SINETAB_N / 4] = [
75    0, 201, 402, 603, 804, 1005, 1206, 1407, 1608, 1809, 2009, 2210, 2410, 2611, 2811, 3012, 3212,
76    3412, 3612, 3811, 4011, 4210, 4410, 4609, 4808, 5007, 5205, 5404, 5602, 5800, 5998, 6195, 6393,
77    6590, 6786, 6983, 7179, 7375, 7571, 7767, 7962, 8157, 8351, 8545, 8739, 8933, 9126, 9319, 9512,
78    9704, 9896, 10087, 10278, 10469, 10659, 10849, 11039, 11228, 11417, 11605, 11793, 11980, 12167,
79    12353, 12539, 12725, 12910, 13094, 13279, 13462, 13645, 13828, 14010, 14191, 14372, 14553,
80    14732, 14912, 15090, 15269, 15446, 15623, 15800, 15976, 16151, 16325, 16499, 16673, 16846,
81    17018, 17189, 17360, 17530, 17700, 17869, 18037, 18204, 18371, 18537, 18703, 18868, 19032,
82    19195, 19357, 19519, 19680, 19841, 20000, 20159, 20317, 20475, 20631, 20787, 20942, 21096,
83    21250, 21403, 21554, 21705, 21856, 22005, 22154, 22301, 22448, 22594, 22739, 22884, 23027,
84    23170, 23311, 23452, 23592, 23731, 23870, 24007, 24143, 24279, 24413, 24547, 24680, 24811,
85    24942, 25072, 25201, 25329, 25456, 25582, 25708, 25832, 25955, 26077, 26198, 26319, 26438,
86    26556, 26674, 26790, 26905, 27019, 27133, 27245, 27356, 27466, 27575, 27683, 27790, 27896,
87    28001, 28105, 28208, 28310, 28411, 28510, 28609, 28706, 28803, 28898, 28992, 29085, 29177,
88    29268, 29358, 29447, 29534, 29621, 29706, 29791, 29874, 29956, 30037, 30117, 30195, 30273,
89    30349, 30424, 30498, 30571, 30643, 30714, 30783, 30852, 30919, 30985, 31050, 31113, 31176,
90    31237, 31297, 31356, 31414, 31470, 31526, 31580, 31633, 31685, 31736, 31785, 31833, 31880,
91    31926, 31971, 32014, 32057, 32098, 32137, 32176, 32213, 32250, 32285, 32318, 32351, 32382,
92    32412, 32441, 32469, 32495, 32521, 32545, 32567, 32589, 32609, 32628, 32646, 32663, 32678,
93    32692, 32705, 32717, 32728, 32737, 32745, 32752, 32757, 32761, 32765, 32766, 32767, 32766,
94    32765, 32761, 32757, 32752, 32745, 32737, 32728, 32717, 32705, 32692, 32678, 32663, 32646,
95    32628, 32609, 32589, 32567, 32545, 32521, 32495, 32469, 32441, 32412, 32382, 32351, 32318,
96    32285, 32250, 32213, 32176, 32137, 32098, 32057, 32014, 31971, 31926, 31880, 31833, 31785,
97    31736, 31685, 31633, 31580, 31526, 31470, 31414, 31356, 31297, 31237, 31176, 31113, 31050,
98    30985, 30919, 30852, 30783, 30714, 30643, 30571, 30498, 30424, 30349, 30273, 30195, 30117,
99    30037, 29956, 29874, 29791, 29706, 29621, 29534, 29447, 29358, 29268, 29177, 29085, 28992,
100    28898, 28803, 28706, 28609, 28510, 28411, 28310, 28208, 28105, 28001, 27896, 27790, 27683,
101    27575, 27466, 27356, 27245, 27133, 27019, 26905, 26790, 26674, 26556, 26438, 26319, 26198,
102    26077, 25955, 25832, 25708, 25582, 25456, 25329, 25201, 25072, 24942, 24811, 24680, 24547,
103    24413, 24279, 24143, 24007, 23870, 23731, 23592, 23452, 23311, 23170, 23027, 22884, 22739,
104    22594, 22448, 22301, 22154, 22005, 21856, 21705, 21554, 21403, 21250, 21096, 20942, 20787,
105    20631, 20475, 20317, 20159, 20000, 19841, 19680, 19519, 19357, 19195, 19032, 18868, 18703,
106    18537, 18371, 18204, 18037, 17869, 17700, 17530, 17360, 17189, 17018, 16846, 16673, 16499,
107    16325, 16151, 15976, 15800, 15623, 15446, 15269, 15090, 14912, 14732, 14553, 14372, 14191,
108    14010, 13828, 13645, 13462, 13279, 13094, 12910, 12725, 12539, 12353, 12167, 11980, 11793,
109    11605, 11417, 11228, 11039, 10849, 10659, 10469, 10278, 10087, 9896, 9704, 9512, 9319, 9126,
110    8933, 8739, 8545, 8351, 8157, 7962, 7767, 7571, 7375, 7179, 6983, 6786, 6590, 6393, 6195, 5998,
111    5800, 5602, 5404, 5205, 5007, 4808, 4609, 4410, 4210, 4011, 3811, 3612, 3412, 3212, 3012, 2811,
112    2611, 2410, 2210, 2009, 1809, 1608, 1407, 1206, 1005, 804, 603, 402, 201, 0, -201, -402, -603,
113    -804, -1005, -1206, -1407, -1608, -1809, -2009, -2210, -2410, -2611, -2811, -3012, -3212,
114    -3412, -3612, -3811, -4011, -4210, -4410, -4609, -4808, -5007, -5205, -5404, -5602, -5800,
115    -5998, -6195, -6393, -6590, -6786, -6983, -7179, -7375, -7571, -7767, -7962, -8157, -8351,
116    -8545, -8739, -8933, -9126, -9319, -9512, -9704, -9896, -10087, -10278, -10469, -10659, -10849,
117    -11039, -11228, -11417, -11605, -11793, -11980, -12167, -12353, -12539, -12725, -12910, -13094,
118    -13279, -13462, -13645, -13828, -14010, -14191, -14372, -14553, -14732, -14912, -15090, -15269,
119    -15446, -15623, -15800, -15976, -16151, -16325, -16499, -16673, -16846, -17018, -17189, -17360,
120    -17530, -17700, -17869, -18037, -18204, -18371, -18537, -18703, -18868, -19032, -19195, -19357,
121    -19519, -19680, -19841, -20000, -20159, -20317, -20475, -20631, -20787, -20942, -21096, -21250,
122    -21403, -21554, -21705, -21856, -22005, -22154, -22301, -22448, -22594, -22739, -22884, -23027,
123    -23170, -23311, -23452, -23592, -23731, -23870, -24007, -24143, -24279, -24413, -24547, -24680,
124    -24811, -24942, -25072, -25201, -25329, -25456, -25582, -25708, -25832, -25955, -26077, -26198,
125    -26319, -26438, -26556, -26674, -26790, -26905, -27019, -27133, -27245, -27356, -27466, -27575,
126    -27683, -27790, -27896, -28001, -28105, -28208, -28310, -28411, -28510, -28609, -28706, -28803,
127    -28898, -28992, -29085, -29177, -29268, -29358, -29447, -29534, -29621, -29706, -29791, -29874,
128    -29956, -30037, -30117, -30195, -30273, -30349, -30424, -30498, -30571, -30643, -30714, -30783,
129    -30852, -30919, -30985, -31050, -31113, -31176, -31237, -31297, -31356, -31414, -31470, -31526,
130    -31580, -31633, -31685, -31736, -31785, -31833, -31880, -31926, -31971, -32014, -32057, -32098,
131    -32137, -32176, -32213, -32250, -32285, -32318, -32351, -32382, -32412, -32441, -32469, -32495,
132    -32521, -32545, -32567, -32589, -32609, -32628, -32646, -32663, -32678, -32692, -32705, -32717,
133    -32728, -32737, -32745, -32752, -32757, -32761, -32765, -32766,
134];
135
136/// complex multiplication in Q15 format
137fn q15_cmul(a: Complex<Q15>, b: Complex<Q15>) -> Complex<Q15> {
138    let rnd: i32 = 1 << 14; // constant for rounding
139    Complex::new(
140        ((a.re as i32 * b.re as i32 - a.im as i32 * b.im as i32 + rnd) >> 15) as i16,
141        ((a.re as i32 * b.im as i32 + a.im as i32 * b.re as i32 + rnd) >> 15) as i16,
142    )
143}
144
145fn check_size(size: usize) -> Result<(), FFTError> {
146    if !(2..=SINETAB_N).contains(&size) {
147        return Err(FFTError::InvalidFFTSize);
148    }
149    const MSB: usize = (usize::MAX >> 1) + 1;
150    let allowed_size = MSB >> (size.leading_zeros());
151    if allowed_size != size {
152        return Err(FFTError::InvalidFFTSize);
153    }
154    Ok(())
155}
156
157/// Radix-2 in-place FFT
158///
159/// Perform forward/inverse fast Fourier transform using the radix-2 algorithm.
160/// The the length of slice```data``` corresponds to the FFT size and must be a
161/// power of 2. The maximum FFT size is 1024.
162///
163/// Returns `FFTError::InvalidFFTSize` if the requested FFT size is not supported.
164pub fn fft_radix2_q15(data: &mut [Complex<Q15>], dir: Direction) -> Result<(), FFTError> {
165    let fft_size = data.len();
166    check_size(fft_size)?;
167    let (inverse, shift) = match dir {
168        Direction::Forward => (false, 0),
169        Direction::ForwardScaled => (false, 1),
170        Direction::Inverse => (true, 1),
171    };
172
173    // re-order data
174    let mut mr = 0usize; // bit reverse counter
175    for m in 1..fft_size {
176        const MSB: usize = (usize::MAX >> 1) + 1;
177        let l = MSB >> (fft_size - 1 - mr).leading_zeros();
178        mr = (mr & (l - 1)) + l;
179        if mr > m {
180            data.swap(m, mr);
181        }
182    }
183
184    let mut stage = 1;
185    let mut sine_step = (SINETAB_LD_N - 1) as isize;
186    // loop over stages
187    while stage < fft_size {
188        // loop over groups with the same twiddle factor
189        let twiddle_step = stage << 1;
190        for group in 0..stage {
191            // calculate twiddle factor
192            let sin_ndx = group << sine_step;
193            let wr = SINETAB[sin_ndx + SINETAB_N / 4] >> shift;
194            let wi = match inverse {
195                false => -SINETAB[sin_ndx],
196                true => SINETAB[sin_ndx],
197            } >> shift;
198            let w = Complex::new(wr, wi);
199            // butterfly operations
200            let mut i = group;
201            while i < fft_size {
202                let j = i + stage;
203                let temp = q15_cmul(w, data[j]);
204
205                let q = Complex::new(data[i].re >> shift, data[i].im >> shift);
206                data[i] = q + temp;
207                data[j] = q - temp;
208                i += twiddle_step;
209            }
210        }
211        sine_step -= 1;
212        stage = twiddle_step;
213    }
214    Ok(())
215}
216
217/// Real value FFT using a complex valued FFT
218///
219/// The `input` data will be used as a buffer for an in-place CFFT and will thus be modified by
220/// this function. The `output` slice will hold the result without redundant data.
221/// The length of the slices must be as follows: `output.len() >= input.len() / 2 + 1`
222/// The data will be scaled by 1/input.len() if `scale == true`.
223pub fn fft_radix2_real_q15(
224    input: &mut [i16],
225    output: &mut [Complex<Q15>],
226    scale: bool,
227) -> Result<(), FFTError> {
228    let fft_len = input.len();
229    check_size(fft_len)?;
230    let half_fft_len = fft_len / 2;
231    if output.len() < half_fft_len + 1 {
232        return Err(FFTError::InvalidDataSize);
233    }
234
235    // Assume that Complex<T> is a exactly a sequence of two values { re: T, im: T }
236    // This is checked for T == Q15 by the test module below
237    let ptr = input.as_mut_ptr() as *mut Complex<Q15>;
238    let fft_out = unsafe { slice::from_raw_parts_mut(ptr, half_fft_len) };
239
240    // Do half size CFFT
241    fft_radix2_q15(
242        &mut fft_out[0..half_fft_len],
243        if scale {
244            Direction::ForwardScaled
245        } else {
246            Direction::Forward
247        },
248    )?;
249
250    let shift = scale as usize;
251    // Post processing that yields fft_len / 2 + 1 complex values
252    output[0] = Complex::new((fft_out[0].im + fft_out[0].re) >> shift, 0);
253    for k in 1..half_fft_len {
254        // calculate the twiddle factor w
255        let sin_ndx = k * SINETAB_N / fft_len;
256        let w = Complex::new(SINETAB[sin_ndx + SINETAB_N / 4], -SINETAB[sin_ndx]);
257
258        // do one post processing calculation
259        let zo = Complex::new(
260            (fft_out[half_fft_len - k].im + fft_out[k].im) >> (shift + 1),
261            (fft_out[half_fft_len - k].re - fft_out[k].re) >> (shift + 1),
262        );
263        let ze = Complex::new(
264            (fft_out[k].re + fft_out[half_fft_len - k].re) >> (shift + 1),
265            (fft_out[k].im - fft_out[half_fft_len - k].im) >> (shift + 1),
266        );
267        let r = ze + q15_cmul(zo, w);
268        output[k] = r;
269    }
270    output[half_fft_len] = Complex::new((fft_out[0].re - fft_out[0].im) >> shift, 0);
271
272    Ok(())
273}
274
275#[cfg(test)]
276mod tests {
277
278    extern crate alloc;
279    use super::{check_size, FFTError, Q15, SINETAB, SINETAB_LD_N, SINETAB_N};
280    use alloc::vec::Vec;
281    use core::f64;
282    use core::mem::size_of;
283    use num_complex::Complex;
284
285    /// check sine table
286    #[test]
287    fn verify_sinewave() {
288        for (i, v) in SINETAB.iter().enumerate() {
289            let fval = 32767.0 * f64::sin(2.0 * f64::consts::PI * (i as f64) / (SINETAB_N as f64));
290            let rval = fval.round() as i16;
291            assert_eq!(*v, rval);
292        }
293    }
294
295    /// Check size of Complex<Q15> to make sure that the unsafe type cast in the real FFT function
296    /// works correctly
297    #[test]
298    fn check_complex_lenth() {
299        assert_eq!(size_of::<Complex<Q15>>(), 2 * size_of::<Q15>());
300    }
301
302    #[test]
303    fn check_size_test() {
304        let valid = (1..=SINETAB_LD_N).map(|k| 1 << k).collect::<Vec<usize>>();
305        for s in 0..=SINETAB_N * 2 {
306            let result = check_size(s);
307            if valid.contains(&s) {
308                assert_eq!(result, Ok(()));
309            } else {
310                assert_eq!(result, Err(FFTError::InvalidFFTSize))
311            }
312        }
313    }
314}