root / Ports / examples / glcdSpectrum / fix_fft.cpp
History | View | Annotate | Download (7 KB)
| 1 | //>>> The latest version of this code can be found at https://github.com/jcw/ !!
|
|---|---|
| 2 | |
| 3 | // copied from http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1286718155
|
| 4 | |
| 5 | #include <avr/pgmspace.h> |
| 6 | // #include "fix_fft.h"
|
| 7 | #include <WProgram.h> |
| 8 | |
| 9 | /* fix_fft.c - Fixed-point in-place Fast Fourier Transform */
|
| 10 | /*
|
| 11 | All data are fixed-point short integers, in which -32768 |
| 12 | to +32768 represent -1.0 to +1.0 respectively. Integer |
| 13 | arithmetic is used for speed, instead of the more natural |
| 14 | floating-point. |
| 15 | |
| 16 | For the forward FFT (time -> freq), fixed scaling is |
| 17 | performed to prevent arithmetic overflow, and to map a 0dB |
| 18 | sine/cosine wave (i.e. amplitude = 32767) to two -6dB freq |
| 19 | coefficients. The return value is always 0. |
| 20 | |
| 21 | For the inverse FFT (freq -> time), fixed scaling cannot be |
| 22 | done, as two 0dB coefficients would sum to a peak amplitude |
| 23 | of 64K, overflowing the 32k range of the fixed-point integers. |
| 24 | Thus, the fix_fft() routine performs variable scaling, and |
| 25 | returns a value which is the number of bits LEFT by which |
| 26 | the output must be shifted to get the actual amplitude |
| 27 | (i.e. if fix_fft() returns 3, each value of fr[] and fi[] |
| 28 | must be multiplied by 8 (2**3) for proper scaling. |
| 29 | Clearly, this cannot be done within fixed-point short |
| 30 | integers. In practice, if the result is to be used as a |
| 31 | filter, the scale_shift can usually be ignored, as the |
| 32 | result will be approximately correctly normalized as is. |
| 33 | |
| 34 | Written by: Tom Roberts 11/8/89 |
| 35 | Made portable: Malcolm Slaney 12/15/94 malcolm@interval.com |
| 36 | Enhanced: Dimitrios P. Bouras 14 Jun 2006 dbouras@ieee.org |
| 37 | Modified for 8bit values David Keller 10.10.2010 |
| 38 | */ |
| 39 | |
| 40 | |
| 41 | #define N_WAVE 256 /* full length of Sinewave[] */ |
| 42 | #define LOG2_N_WAVE 8 /* log2(N_WAVE) */ |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | /*
|
| 48 | Since we only use 3/4 of N_WAVE, we define only |
| 49 | this many samples, in order to conserve data space. |
| 50 | */ |
| 51 | |
| 52 | |
| 53 | |
| 54 | const prog_int8_t Sinewave[N_WAVE-N_WAVE/4] PROGMEM = { |
| 55 | 0, 3, 6, 9, 12, 15, 18, 21, |
| 56 | 24, 28, 31, 34, 37, 40, 43, 46, |
| 57 | 48, 51, 54, 57, 60, 63, 65, 68, |
| 58 | 71, 73, 76, 78, 81, 83, 85, 88, |
| 59 | 90, 92, 94, 96, 98, 100, 102, 104, |
| 60 | 106, 108, 109, 111, 112, 114, 115, 117, |
| 61 | 118, 119, 120, 121, 122, 123, 124, 124, |
| 62 | 125, 126, 126, 127, 127, 127, 127, 127, |
| 63 | |
| 64 | 127, 127, 127, 127, 127, 127, 126, 126, |
| 65 | 125, 124, 124, 123, 122, 121, 120, 119, |
| 66 | 118, 117, 115, 114, 112, 111, 109, 108, |
| 67 | 106, 104, 102, 100, 98, 96, 94, 92, |
| 68 | 90, 88, 85, 83, 81, 78, 76, 73, |
| 69 | 71, 68, 65, 63, 60, 57, 54, 51, |
| 70 | 48, 46, 43, 40, 37, 34, 31, 28, |
| 71 | 24, 21, 18, 15, 12, 9, 6, 3, |
| 72 | |
| 73 | 0, -3, -6, -9, -12, -15, -18, -21, |
| 74 | -24, -28, -31, -34, -37, -40, -43, -46, |
| 75 | -48, -51, -54, -57, -60, -63, -65, -68, |
| 76 | -71, -73, -76, -78, -81, -83, -85, -88, |
| 77 | -90, -92, -94, -96, -98, -100, -102, -104, |
| 78 | -106, -108, -109, -111, -112, -114, -115, -117, |
| 79 | -118, -119, -120, -121, -122, -123, -124, -124, |
| 80 | -125, -126, -126, -127, -127, -127, -127, -127, |
| 81 | |
| 82 | /*-127, -127, -127, -127, -127, -127, -126, -126,
|
| 83 | -125, -124, -124, -123, -122, -121, -120, -119, |
| 84 | -118, -117, -115, -114, -112, -111, -109, -108, |
| 85 | -106, -104, -102, -100, -98, -96, -94, -92, |
| 86 | -90, -88, -85, -83, -81, -78, -76, -73, |
| 87 | -71, -68, -65, -63, -60, -57, -54, -51, |
| 88 | -48, -46, -43, -40, -37, -34, -31, -28, |
| 89 | -24, -21, -18, -15, -12, -9, -6, -3, */ |
| 90 | }; |
| 91 | |
| 92 | |
| 93 | |
| 94 | |
| 95 | |
| 96 | |
| 97 | /*
|
| 98 | FIX_MPY() - fixed-point multiplication & scaling. |
| 99 | Substitute inline assembly for hardware-specific |
| 100 | optimization suited to a particluar DSP processor. |
| 101 | Scaling ensures that result remains 16-bit. |
| 102 | */ |
| 103 | inline char FIX_MPY(char a, char b) |
| 104 | {
|
| 105 | |
| 106 | //Serial.println(a);
|
| 107 | //Serial.println(b);
|
| 108 | |
| 109 | |
| 110 | /* shift right one less bit (i.e. 15-1) */
|
| 111 | int c = ((int)a * (int)b) >> 6; |
| 112 | /* last bit shifted out = rounding-bit */
|
| 113 | b = c & 0x01;
|
| 114 | /* last shift + rounding bit */
|
| 115 | a = (c >> 1) + b;
|
| 116 | |
| 117 | /*
|
| 118 | Serial.println(Sinewave[3]); |
| 119 | Serial.println(c); |
| 120 | Serial.println(a); |
| 121 | while(1);*/ |
| 122 | |
| 123 | return a;
|
| 124 | } |
| 125 | |
| 126 | /*
|
| 127 | fix_fft() - perform forward/inverse fast Fourier transform. |
| 128 | fr[n],fi[n] are real and imaginary arrays, both INPUT AND |
| 129 | RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to |
| 130 | 0 for forward transform (FFT), or 1 for iFFT. |
| 131 | */ |
| 132 | int fix_fft(char fr[], char fi[], int m, int inverse) |
| 133 | {
|
| 134 | int mr, nn, i, j, l, k, istep, n, scale, shift;
|
| 135 | char qr, qi, tr, ti, wr, wi;
|
| 136 | |
| 137 | n = 1 << m;
|
| 138 | |
| 139 | /* max FFT size = N_WAVE */
|
| 140 | if (n > N_WAVE)
|
| 141 | return -1; |
| 142 | |
| 143 | mr = 0;
|
| 144 | nn = n - 1;
|
| 145 | scale = 0;
|
| 146 | |
| 147 | /* decimation in time - re-order data */
|
| 148 | for (m=1; m<=nn; ++m) { |
| 149 | l = n; |
| 150 | do {
|
| 151 | l >>= 1;
|
| 152 | } while (mr+l > nn);
|
| 153 | mr = (mr & (l-1)) + l;
|
| 154 | |
| 155 | if (mr <= m)
|
| 156 | continue;
|
| 157 | tr = fr[m]; |
| 158 | fr[m] = fr[mr]; |
| 159 | fr[mr] = tr; |
| 160 | ti = fi[m]; |
| 161 | fi[m] = fi[mr]; |
| 162 | fi[mr] = ti; |
| 163 | } |
| 164 | |
| 165 | l = 1;
|
| 166 | k = LOG2_N_WAVE-1;
|
| 167 | while (l < n) {
|
| 168 | if (inverse) {
|
| 169 | /* variable scaling, depending upon data */
|
| 170 | shift = 0;
|
| 171 | for (i=0; i<n; ++i) { |
| 172 | j = fr[i]; |
| 173 | if (j < 0) |
| 174 | j = -j; |
| 175 | m = fi[i]; |
| 176 | if (m < 0) |
| 177 | m = -m; |
| 178 | if (j > 16383 || m > 16383) { |
| 179 | shift = 1;
|
| 180 | break;
|
| 181 | } |
| 182 | } |
| 183 | if (shift)
|
| 184 | ++scale; |
| 185 | } else {
|
| 186 | /*
|
| 187 | fixed scaling, for proper normalization -- |
| 188 | there will be log2(n) passes, so this results |
| 189 | in an overall factor of 1/n, distributed to |
| 190 | maximize arithmetic accuracy. |
| 191 | */ |
| 192 | shift = 1;
|
| 193 | } |
| 194 | /*
|
| 195 | it may not be obvious, but the shift will be |
| 196 | performed on each data point exactly once, |
| 197 | during this pass. |
| 198 | */ |
| 199 | istep = l << 1;
|
| 200 | for (m=0; m<l; ++m) { |
| 201 | j = m << k; |
| 202 | /* 0 <= j < N_WAVE/2 */
|
| 203 | wr = pgm_read_word_near(Sinewave + j+N_WAVE/4);
|
| 204 | |
| 205 | /*Serial.println("asdfasdf");
|
| 206 | Serial.println(wr); |
| 207 | Serial.println(j+N_WAVE/4); |
| 208 | Serial.println(Sinewave[256]); |
| 209 | |
| 210 | Serial.println("");*/
|
| 211 | |
| 212 | |
| 213 | wi = -pgm_read_word_near(Sinewave + j); |
| 214 | if (inverse)
|
| 215 | wi = -wi; |
| 216 | if (shift) {
|
| 217 | wr >>= 1;
|
| 218 | wi >>= 1;
|
| 219 | } |
| 220 | for (i=m; i<n; i+=istep) {
|
| 221 | j = i + l; |
| 222 | tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]); |
| 223 | ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]); |
| 224 | qr = fr[i]; |
| 225 | qi = fi[i]; |
| 226 | if (shift) {
|
| 227 | qr >>= 1;
|
| 228 | qi >>= 1;
|
| 229 | } |
| 230 | fr[j] = qr - tr; |
| 231 | fi[j] = qi - ti; |
| 232 | fr[i] = qr + tr; |
| 233 | fi[i] = qi + ti; |
| 234 | } |
| 235 | } |
| 236 | --k; |
| 237 | l = istep; |
| 238 | } |
| 239 | return scale;
|
| 240 | } |
| 241 | |
| 242 | /*
|
| 243 | fix_fftr() - forward/inverse FFT on array of real numbers. |
| 244 | Real FFT/iFFT using half-size complex FFT by distributing |
| 245 | even/odd samples into real/imaginary arrays respectively. |
| 246 | In order to save data space (i.e. to avoid two arrays, one |
| 247 | for real, one for imaginary samples), we proceed in the |
| 248 | following two steps: a) samples are rearranged in the real |
| 249 | array so that all even samples are in places 0-(N/2-1) and |
| 250 | all imaginary samples in places (N/2)-(N-1), and b) fix_fft |
| 251 | is called with fr and fi pointing to index 0 and index N/2 |
| 252 | respectively in the original array. The above guarantees |
| 253 | that fix_fft "sees" consecutive real samples as alternating |
| 254 | real and imaginary samples in the complex array. |
| 255 | */ |
| 256 | int fix_fftr(char f[], int m, int inverse) |
| 257 | {
|
| 258 | int i, N = 1<<(m-1), scale = 0; |
| 259 | char tt, *fr=f, *fi=&f[N];
|
| 260 | |
| 261 | if (inverse)
|
| 262 | scale = fix_fft(fi, fr, m-1, inverse);
|
| 263 | for (i=1; i<N; i+=2) { |
| 264 | tt = f[N+i-1];
|
| 265 | f[N+i-1] = f[i];
|
| 266 | f[i] = tt; |
| 267 | } |
| 268 | if (! inverse)
|
| 269 | scale = fix_fft(fi, fr, m-1, inverse);
|
| 270 | return scale;
|
| 271 | } |