├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── fft.c ├── fft.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | main: 2 | gcc main.c fft.c -o main -lm -O3 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fft 2 | Inplace complex-valued fast fourier transform in C. 3 | 4 | Used to calculate shift of a shifted signal. 5 | -------------------------------------------------------------------------------- /fft.c: -------------------------------------------------------------------------------- 1 | #include "fft.h" 2 | 3 | #include 4 | #include 5 | 6 | static int fft_bitreverse(int x, int n){ 7 | int i, result = 0; 8 | for (i = 0; i < n; i++){ 9 | result = (result << 1) | ((x >> i) & 1); 10 | } 11 | return result; 12 | } 13 | 14 | static int fft_bitlength(int x){ 15 | int n = 0; 16 | for (; x; x >>= 1) n++; 17 | return n; 18 | } 19 | 20 | void fft_init(struct FFT *fft, int n){ 21 | fft->c = (float*)malloc(n*sizeof(*fft->c)); 22 | fft->s = (float*)malloc(n*sizeof(*fft->s)); 23 | fft->reversed = (int*)malloc(n*sizeof(*fft->reversed)); 24 | fft->n = n; 25 | 26 | const float pi = 3.14159265358979f; 27 | 28 | int i, log2_n = fft_bitlength(n) - 1; 29 | for (i = 0; i < n; i++){ 30 | float t = -2.0f*pi/n*i; 31 | fft->c[i] = cos(t); 32 | fft->s[i] = sin(t); 33 | fft->reversed[i] = fft_bitreverse(i, log2_n); 34 | } 35 | } 36 | 37 | void fft_free(struct FFT *fft){ 38 | free(fft->reversed); 39 | free(fft->c); 40 | free(fft->s); 41 | } 42 | 43 | static void fft_swap_and_first_pass(struct FFT *fft, float *re, float *im){ 44 | int i, n = fft->n; 45 | for (i = 0; i < n; i++){ 46 | int j = fft->reversed[i]; 47 | if (i < j){ 48 | float temp; 49 | 50 | temp = re[i]; 51 | re[i] = re[j]; 52 | re[j] = temp; 53 | 54 | temp = im[i]; 55 | im[i] = im[j]; 56 | im[j] = temp; 57 | } 58 | } 59 | 60 | if (n >= 2){ 61 | for (i = 0; i < n; i += 2){ 62 | float ar = re[i + 0]; 63 | float ai = im[i + 0]; 64 | float br = re[i + 1]; 65 | float bi = im[i + 1]; 66 | re[i + 0] = ar + br; 67 | im[i + 0] = ai + bi; 68 | re[i + 1] = ar - br; 69 | im[i + 1] = ai - bi; 70 | } 71 | } 72 | } 73 | 74 | void fft_fft(struct FFT *fft, float *re, float *im){ 75 | int block_size, block_offset, i, n = fft->n; 76 | float *c = fft->c; 77 | float *s = fft->s; 78 | 79 | fft_swap_and_first_pass(fft, re, im); 80 | 81 | if (n >= 4){ 82 | for (i = 0; i < n; i += 4){ 83 | float ar = re[i + 0]; 84 | float ai = im[i + 0]; 85 | float cr = re[i + 1]; 86 | float ci = im[i + 1]; 87 | float br = re[i + 2]; 88 | float bi = im[i + 2]; 89 | float di = re[i + 3]; 90 | float dr = im[i + 3]; 91 | re[i + 0] = ar + br; 92 | im[i + 0] = ai + bi; 93 | re[i + 1] = cr - dr; 94 | im[i + 1] = ci - di; 95 | re[i + 2] = ar - br; 96 | im[i + 2] = ai - bi; 97 | re[i + 3] = cr + dr; 98 | im[i + 3] = ci + di; 99 | } 100 | } 101 | 102 | for (block_size = 8; block_size <= n; block_size *= 2){ 103 | int cos_sin_stride = n/block_size; 104 | for (block_offset = 0; block_offset < n; block_offset += block_size){ 105 | for (i = 0; i < block_size/2; i++){ 106 | float ar = re[block_offset + i]; 107 | float ai = im[block_offset + i]; 108 | float br = re[block_offset + i + block_size/2]; 109 | float bi = im[block_offset + i + block_size/2]; 110 | float cr = c[i*cos_sin_stride]; 111 | float ci = s[i*cos_sin_stride]; 112 | float dr = br*cr - bi*ci; 113 | float di = br*ci + bi*cr; 114 | re[block_offset + i ] = ar + dr; 115 | im[block_offset + i ] = ai + di; 116 | re[block_offset + i + block_size/2] = ar - dr; 117 | im[block_offset + i + block_size/2] = ai - di; 118 | } 119 | } 120 | } 121 | } 122 | 123 | void fft_ifft(struct FFT *fft, float *re, float *im){ 124 | int block_size, block_offset, i, n = fft->n; 125 | float *c = fft->c; 126 | float *s = fft->s; 127 | 128 | fft_swap_and_first_pass(fft, re, im); 129 | 130 | if (n >= 4){ 131 | for (i = 0; i < n; i += 4){ 132 | float ar = re[i + 0]; 133 | float ai = im[i + 0]; 134 | float cr = re[i + 1]; 135 | float ci = im[i + 1]; 136 | float br = re[i + 2]; 137 | float bi = im[i + 2]; 138 | float di = -re[i + 3]; 139 | float dr = im[i + 3]; 140 | re[i + 0] = ar + br; 141 | im[i + 0] = ai + bi; 142 | re[i + 1] = cr - dr; 143 | im[i + 1] = ci - di; 144 | re[i + 2] = ar - br; 145 | im[i + 2] = ai - bi; 146 | re[i + 3] = cr + dr; 147 | im[i + 3] = ci + di; 148 | } 149 | } 150 | 151 | for (block_size = 8; block_size <= n; block_size *= 2){ 152 | int cos_sin_stride = n/block_size; 153 | for (block_offset = 0; block_offset < n; block_offset += block_size){ 154 | for (i = 0; i < block_size/2; i++){ 155 | float ar = re[block_offset + i]; 156 | float ai = im[block_offset + i]; 157 | float br = re[block_offset + i + block_size/2]; 158 | float bi = im[block_offset + i + block_size/2]; 159 | float cr = c[i*cos_sin_stride]; 160 | float ci = -s[i*cos_sin_stride]; 161 | float dr = br*cr - bi*ci; 162 | float di = br*ci + bi*cr; 163 | re[block_offset + i ] = ar + dr; 164 | im[block_offset + i ] = ai + di; 165 | re[block_offset + i + block_size/2] = ar - dr; 166 | im[block_offset + i + block_size/2] = ai - di; 167 | } 168 | } 169 | } 170 | 171 | float scale = 1.0f/n; 172 | for (i = 0; i < n; i++){ 173 | re[i] *= scale; 174 | im[i] *= scale; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /fft.h: -------------------------------------------------------------------------------- 1 | #ifndef FFT_INCLUDED 2 | #define FFT_INCLUDED 3 | 4 | struct FFT { 5 | int n, *reversed; 6 | float *c, *s; 7 | }; 8 | 9 | void fft_init(struct FFT *fft, int n); 10 | void fft_free(struct FFT *fft); 11 | void fft_fft(struct FFT *fft, float *re, float *im); 12 | void fft_ifft(struct FFT *fft, float *re, float *im); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "fft.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | float my_abs(float x){ 10 | return x < 0.0f ? -x : x; 11 | } 12 | 13 | float randf() { 14 | do { 15 | float x = rand()*2.0f/RAND_MAX - 1.0f; 16 | float y = rand()*2.0f/RAND_MAX - 1.0f; 17 | float r = x*x + y*y; 18 | if (r == 0.0f || r > 1.0f) continue; 19 | return x*sqrt(-2.0*log(r)/r); 20 | } while(1); 21 | } 22 | 23 | void reverse(float *a, int n){ 24 | for (int i = 0; i < n/2; i++){ 25 | int j = n - 1 - i; 26 | float temp = a[i]; 27 | a[i] = a[j]; 28 | a[j] = temp; 29 | } 30 | } 31 | 32 | int calculate_shift(float *re, float *im, float *re_shifted, float *im_shifted, int n){ 33 | struct FFT fft[1]; 34 | fft_init(fft, n); 35 | 36 | reverse(re_shifted, n/2); 37 | reverse(im_shifted, n/2); 38 | 39 | fft_fft(fft, re, im); 40 | fft_fft(fft, re_shifted, im_shifted); 41 | 42 | int i; 43 | for (i = 0; i < n; i++){ 44 | float real = re[i]*re_shifted[i] - im[i]*im_shifted[i]; 45 | float imag = im[i]*re_shifted[i] + re[i]*im_shifted[i]; 46 | 47 | re[i] = real; 48 | im[i] = imag; 49 | } 50 | 51 | fft_ifft(fft, re, im); 52 | 53 | int index_max = -1; 54 | float max_value = -1.0f; 55 | for (i = 0; i < n; i++){ 56 | float abs_value = my_abs(re[i]); 57 | 58 | if (abs_value > max_value){ 59 | max_value = abs_value; 60 | index_max = i; 61 | } 62 | } 63 | 64 | fft_free(fft); 65 | 66 | return (n - 1 - index_max)%(n/2); 67 | } 68 | 69 | void test_shift(int shift){ 70 | int i, n = 512; 71 | 72 | float *re = (float*)malloc(sizeof(*re)*n*2); 73 | float *im = (float*)malloc(sizeof(*im)*n*2); 74 | float *re_shifted = (float*)malloc(sizeof(*re)*n*2); 75 | float *im_shifted = (float*)malloc(sizeof(*im)*n*2); 76 | 77 | /* initialize an array with random values and padded zeros */ 78 | for (i = 0; i < n; i++){ 79 | re[i] = randf(); 80 | im[i] = 0.0f; 81 | 82 | re[i + n] = 0.0f; 83 | im[i + n] = 0.0f; 84 | } 85 | 86 | /* create a shifted signal */ 87 | for (i = 0; i < n; i++){ 88 | int j = (i - shift + n) % n; 89 | re_shifted[i] = re[j]; 90 | im_shifted[i] = im[j]; 91 | 92 | re_shifted[i + n] = 0.0f; 93 | im_shifted[i + n] = 0.0f; 94 | } 95 | 96 | int calculated_shift = calculate_shift(re, im, re_shifted, im_shifted, n*2); 97 | 98 | if (shift != calculated_shift){ 99 | printf("ERROR: Expected shift %i, but got %i\n", shift, calculated_shift); 100 | exit(-1); 101 | } 102 | 103 | free(re); 104 | free(im); 105 | free(re_shifted); 106 | free(im_shifted); 107 | } 108 | 109 | void test_frequency(int frequency, int n){ 110 | const float pi = 3.14159265358979f; 111 | 112 | float *re = (float*)malloc(sizeof(*re)*n); 113 | float *im = (float*)malloc(sizeof(*im)*n); 114 | 115 | int i; 116 | for (i = 0; i < n; i++){ 117 | re[i] = cos(2.0f*pi*frequency/n*i); 118 | im[i] = 0.0f; 119 | } 120 | 121 | struct FFT fft[1]; 122 | fft_init(fft, n); 123 | fft_fft(fft, re, im); 124 | 125 | /* The bin re[frequency] and re[n - frequency] should be n/2. */ 126 | /* All other bins should be 0. */ 127 | float max_error = 0.01f; 128 | 129 | for (i = 0; i < n; i++){ 130 | assert(my_abs(im[i]) < max_error); 131 | } 132 | 133 | for (i = 0; i < frequency; i++){ 134 | assert(my_abs(re[i]) < max_error); 135 | } 136 | 137 | assert(my_abs(re[frequency] - n/2) < max_error); 138 | 139 | for (i = frequency + 1; i < n - frequency; i++){ 140 | assert(my_abs(re[i]) < max_error); 141 | } 142 | 143 | assert(my_abs(re[n - frequency] - n/2) < max_error); 144 | 145 | for (i = n - frequency + 1; i < n; i++){ 146 | assert(my_abs(re[i]) < max_error); 147 | } 148 | 149 | fft_ifft(fft, re, im); 150 | 151 | /* check that x = fft(ifft(x)) */ 152 | 153 | for (i = 0; i < n; i++){ 154 | float x = cos(2.0f*pi*frequency/n*i); 155 | assert(my_abs(re[i] - x) < max_error); 156 | assert(my_abs(im[i]) < max_error); 157 | } 158 | 159 | fft_free(fft); 160 | free(re); 161 | free(im); 162 | } 163 | 164 | int main(){ 165 | 166 | for (int shift = 0; shift < 512; shift++){ 167 | test_shift(shift); 168 | } 169 | 170 | for (int n = 16; n <= 256; n *= 2){ 171 | for (int frequency = 1; frequency < n/2; frequency++){ 172 | test_frequency(frequency, n); 173 | } 174 | } 175 | 176 | return 0; 177 | } 178 | --------------------------------------------------------------------------------