1+ /* *
2+ * Hunter Adams ([email protected] ) 3+ * Reproduced and modified with explicit permission
4+ *
5+ * Original code in action:
6+ * https://www.youtube.com/watch?v=8aibPy4yzCk
7+ *
8+ */
9+ #include " fixed_fft.hpp"
10+ #include < algorithm>
11+
12+ // Adapted from https://github.com/raspberrypi/pico-sdk/blob/master/src/host/pico_bit_ops/bit_ops.c
13+ uint16_t __always_inline __revs (uint16_t v) {
14+ v = ((v & 0x5555u ) << 1u ) | ((v >> 1u ) & 0x5555u );
15+ v = ((v & 0x3333u ) << 2u ) | ((v >> 2u ) & 0x3333u );
16+ v = ((v & 0x0f0fu ) << 4u ) | ((v >> 4u ) & 0x0f0fu );
17+ return ((v >> 8u ) & 0x00ffu ) | ((v & 0x00ffu ) << 8u );
18+ }
19+
20+ FIX_FFT::~FIX_FFT () {
21+ }
22+
23+ int FIX_FFT::get_scaled (unsigned int i, unsigned int scale) {
24+ return fix15_to_int (multiply_fix15 (fr[i], int_to_fix15 (scale)));
25+ }
26+
27+ void FIX_FFT::init () {
28+
29+ // Populate Filter and Sine tables
30+ for (auto ii = 0u ; ii < SAMPLE_COUNT; ii++) {
31+ // Full sine wave with period NUM_SAMPLES
32+ // Wolfram Alpha: Plot[(sin(2 * pi * (x / 1.0))), {x, 0, 1}]
33+ sine_table[ii] = float_to_fix15 (0 .5f * sin ((M_PI * 2 .0f ) * ((float ) ii) / (float )SAMPLE_COUNT));
34+
35+ // This is a crude approximation of a Lanczos window.
36+ // Wolfram Alpha Comparison: Plot[0.5 * (1.0 - cos(2 * pi * (x / 1.0))), {x, 0, 1}], Plot[LanczosWindow[x - 0.5], {x, 0, 1}]
37+ filter_window[ii] = float_to_fix15 (0 .5f * (1 .0f - cos ((M_PI * 2 .0f ) * ((float ) ii) / ((float )SAMPLE_COUNT))));
38+ }
39+ }
40+
41+ void FIX_FFT::update () {
42+ float max_freq = 0 ;
43+
44+ // Copy/window elements into a fixed-point array
45+ for (auto i = 0u ; i < SAMPLE_COUNT; i++) {
46+ fr[i] = multiply_fix15 (int_to_fix15 ((int )sample_array[i]), filter_window[i]);
47+ fi[i] = (fix15)0 ;
48+ }
49+
50+ // Compute the FFT
51+ FFT ();
52+
53+ // Find the magnitudes
54+ for (auto i = 0u ; i < (SAMPLE_COUNT / 2u ); i++) {
55+ // get the approx magnitude
56+ fr[i] = abs (fr[i]); // >>9
57+ fi[i] = abs (fi[i]);
58+ // reuse fr to hold magnitude
59+ fr[i] = std::max (fr[i], fi[i]) +
60+ multiply_fix15 (std::min (fr[i], fi[i]), float_to_fix15 (0 .4f ));
61+
62+ // Keep track of maximum
63+ if (fr[i] > max_freq && i >= 5u ) {
64+ max_freq = FIX_FFT::fr[i];
65+ max_freq_dex = i;
66+ }
67+ }
68+ }
69+
70+ float FIX_FFT::max_frequency () {
71+ return max_freq_dex * (sample_rate / SAMPLE_COUNT);
72+ }
73+
74+ void FIX_FFT::FFT () {
75+ // Bit Reversal Permutation
76+ // Bit reversal code below originally based on that found here:
77+ // https://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious
78+ // https://en.wikipedia.org/wiki/Bit-reversal_permutation
79+ // Detail here: https://vanhunteradams.com/FFT/FFT.html#Single-point-transforms-(reordering)
80+ //
81+ // PH: Converted to stdlib functions and __revs so it doesn't hurt my eyes
82+ for (auto m = 1u ; m < SAMPLE_COUNT - 1u ; m++) {
83+ unsigned int mr = __revs (m) >> shift_amount;
84+ // don't swap that which has already been swapped
85+ if (mr <= m) continue ;
86+ // swap the bit-reveresed indices
87+ std::swap (fr[m], fr[mr]);
88+ std::swap (fi[m], fi[mr]);
89+ }
90+
91+ // Danielson-Lanczos
92+ // Adapted from code by:
93+ // Tom Roberts 11/8/89 and Malcolm Slaney 12/15/94 [email protected] 94+ // Detail here: https://vanhunteradams.com/FFT/FFT.html#Two-point-transforms
95+ // Length of the FFT's being combined (starts at 1)
96+ //
97+ // PH: Moved variable declarations to first-use so types are visually explicit.
98+ // PH: Removed div 2 on sine table values, have computed the sine table pre-divided.
99+ unsigned int L = 1 ;
100+ int k = log2_samples - 1 ;
101+
102+ // While the length of the FFT's being combined is less than the number of gathered samples
103+ while (L < SAMPLE_COUNT) {
104+ // Determine the length of the FFT which will result from combining two FFT's
105+ int istep = L << 1 ;
106+ // For each element in the FFT's that are being combined
107+ for (auto m = 0u ; m < L; ++m) {
108+ // Lookup the trig values for that element
109+ int j = m << k; // index into sine_table
110+ fix15 wr = sine_table[j + SAMPLE_COUNT / 4 ];
111+ fix15 wi = -sine_table[j];
112+ // i gets the index of one of the FFT elements being combined
113+ for (auto i = m; i < SAMPLE_COUNT; i += istep) {
114+ // j gets the index of the FFT element being combined with i
115+ int j = i + L;
116+ // compute the trig terms (bottom half of the above matrix)
117+ fix15 tr = multiply_fix15 (wr, fr[j]) - multiply_fix15 (wi, fi[j]);
118+ fix15 ti = multiply_fix15 (wr, fi[j]) + multiply_fix15 (wi, fr[j]);
119+ // divide ith index elements by two (top half of above matrix)
120+ fix15 qr = fr[i] >> 1 ;
121+ fix15 qi = fi[i] >> 1 ;
122+ // compute the new values at each index
123+ fr[j] = qr - tr;
124+ fi[j] = qi - ti;
125+ fr[i] = qr + tr;
126+ fi[i] = qi + ti;
127+ }
128+ }
129+ --k;
130+ L = istep;
131+ }
132+ }
0 commit comments