bpmdsp/discrete_fourier_transforms.c

Go to the documentation of this file.
00001 
00006 #include "bpm/bpm_wf.h"
00007 #include "bpm/bpm_dsp.h"
00008 
00009 // prototype the two functions from fftsg.c that we're going to use 
00010 // in this code.
00011 void cdft(int, int, double *, int *, double *);
00012 void rdft(int, int, double *, int *, double *);
00013 
00014 
00015 // define the fft buffers
00016 static int    *_fft_work_area        = NULL;  // bitreversal work area
00017 static int     _fft_work_area_length = 0;     // ... buffer length 
00018 static double *_fft_sc_table         = NULL;  // sin/cos value table
00019 static int     _fft_sc_table_length  = 0;     // ... buffer length
00020 static double *_fft_data             = NULL;  // fft input data array
00021 static int     _fft_data_length      = 0;     // ... buffer length
00022 
00023 // ----------------------------------------------------------------------------
00024 
00025 int _is_pow2( int n ) {
00026   int p = 0, r = 0;
00027   do {
00028     r = n % 2;  n /= 2;  p++;
00029   } while ( r == 0 && n > 1 );
00030   if( r != 0 ) return FALSE;
00031   return p;
00032 }
00033 
00034 // ----------------------------------------------------------------------------
00035 
00036 int _check_fft_buffers( int ns ) {
00037 
00038   // recommended work area buffer length
00039   int nw = 2+(1<<(int)(log(ns/2+0.5)/log(2))/2);
00040   int nt = ns/2;
00041 
00042   // check the work area buffer
00043   if ( _fft_work_area ) {
00044     if ( _fft_work_area_length < nw ) {
00045       bpm_warning( "FFT work buffer to small, increasing size...", __FILE__, __LINE__ );
00046       free( _fft_work_area );
00047       _fft_work_area = (int*) calloc( nw, sizeof(int) );
00048       if ( ! _fft_work_area ) {
00049         bpm_error( "Cannot allocate memory for FFT work buffer", __FILE__, __LINE__ );
00050         return BPM_FAILURE;
00051       }
00052       _fft_work_area_length = nw;
00053     }
00054   } else {
00055     bpm_warning( "Allocating FFT work buffer, no fft_initialise() found", __FILE__, __LINE__ );
00056     _fft_work_area = (int*) calloc( nw, sizeof(int) );
00057     if ( ! _fft_work_area ) {
00058       bpm_error( "Cannot allocate memory for FFT work buffer", __FILE__, __LINE__ );
00059       return BPM_FAILURE;
00060     }
00061     _fft_work_area_length = nw;
00062   }
00063   
00064   // check the sin/cos table buffer length
00065   if ( _fft_sc_table ) {
00066     if ( _fft_sc_table_length < nt ) {
00067       bpm_warning( "FFT sin/cos table too small, increasing size...", __FILE__, __LINE__ );
00068       free( _fft_sc_table );
00069       _fft_sc_table = (double*) calloc( nt, sizeof(double) );
00070       if ( ! _fft_sc_table ) {
00071         bpm_error( "Cannot allocate memory for FFT sin/cos table", __FILE__, __LINE__ );
00072         return BPM_FAILURE;
00073       }
00074       _fft_sc_table_length = nt;
00075     }
00076   } else {
00077     bpm_warning( "Allocating FFT sin/cos table buffer, no fft_initialise() found",
00078                  __FILE__, __LINE__ );
00079     _fft_sc_table = (double*) calloc( nt, sizeof(double) );
00080     if ( ! _fft_sc_table ) {
00081       bpm_error( "Cannot allocate memory for FFT sin/cos table", __FILE__, __LINE__ );
00082       return BPM_FAILURE;
00083     }
00084     _fft_sc_table_length = nt;
00085   }
00086 
00087   // check the fft data array buffer length
00088   if ( _fft_data ) {
00089     if ( _fft_data_length < 2*ns ) {
00090       bpm_warning( "FFT data buffer length too small, increasing size...", __FILE__, __LINE__ );
00091       free( _fft_data );
00092       _fft_data = (double*) calloc( 2*ns, sizeof(double) );
00093       if ( ! _fft_data ) {
00094         bpm_error( "Cannot allocate memory for FFT data buffer", __FILE__, __LINE__ );
00095         return BPM_FAILURE;
00096       }
00097       _fft_data_length = 2*ns;
00098     }
00099   } else {
00100     bpm_warning( "Allocating FFT data buffer, no fft_initialise() found",
00101                  __FILE__, __LINE__ );
00102     _fft_data = (double*) calloc( 2*ns, sizeof(double) );
00103     if ( ! _fft_data ) {
00104       bpm_error( "Cannot allocate memory for FFT data buffer", __FILE__, __LINE__ );
00105       return BPM_FAILURE;
00106     }
00107     _fft_data_length = 2*ns;
00108   }
00109 
00110   return BPM_SUCCESS;
00111 }
00112 
00113 // ----------------------------------------------------------------------------
00114 
00115 
00116 int  fft_gen_tables( void ) {
00117 
00118   if ( _fft_work_area ) {
00119     _fft_work_area[0] = 0;
00120   } else {
00121     bpm_error( "FFT work buffer not allocated, cannot regenerate tables", __FILE__, __LINE__ );
00122     return BPM_FAILURE;
00123   }
00124 
00125   return BPM_SUCCESS;
00126 }
00127 
00128 // ----------------------------------------------------------------------------
00129 
00130 int fft_initialise( int ns ) {
00131   
00132   // recommended work area buffer length
00133   int nw = 2+(1<<(int)(log(ns/2+0.5)/log(2))/2);
00134   int nt = ns/2;
00135 
00136   if ( _fft_work_area || _fft_sc_table || _fft_data ) {
00137     bpm_error( "FFT buffers alread initialised, please cleanup first with fft_cleanup()",
00138                __FILE__, __LINE__ );
00139     return BPM_FAILURE;
00140   }
00141 
00142   _fft_work_area = (int*) calloc( nw, sizeof(int) );
00143   _fft_sc_table  = (double*) calloc( nt, sizeof(double) );
00144   _fft_data      = (double*) calloc( 2*ns, sizeof(double) );
00145 
00146   if ( ! _fft_work_area || ! _fft_sc_table || ! _fft_data ) {
00147     bpm_error( "Failed to allocate memory for the FFT buffers in fft_initialise",
00148                __FILE__, __LINE__ );
00149     return BPM_FAILURE;
00150   }
00151 
00152   _fft_work_area_length = nw;
00153   _fft_sc_table_length  = nt;
00154   _fft_data_length      = 2*ns;
00155   // setting the first bit to 0 to tell the fft to 
00156   // generate the sin/cos table
00157 
00158   return fft_gen_tables();
00159 }
00160 
00161 // ----------------------------------------------------------------------------
00162 
00163 void fft_cleanup( void ) {
00164 
00165   if( _fft_work_area ) free( _fft_work_area );
00166   if( _fft_sc_table )  free( _fft_sc_table );
00167   if( _fft_data )      free( _fft_data );
00168 
00169   _fft_work_area_length = 0;
00170   _fft_sc_table_length  = 0;
00171   _fft_data_length      = 0;
00172 
00173   return;
00174 }
00175 
00176 // ----------------------------------------------------------------------------
00177 
00178 int complexfft( complexwf_t *z, int fft_mode ) {
00179 
00180   // in place fft on complex wf z 
00181 
00182   int i = 0;
00183 
00184   if ( ! _is_pow2(z->ns) ) {
00185     bpm_warning( "Number of samples is not of the form 2^n, may run into trouble with FFT !",
00186                  __FILE__, __LINE__ );
00187   }
00188 
00189   if ( _check_fft_buffers( z->ns ) == BPM_FAILURE ) {
00190     bpm_error( "Error checking FFT buffers in complexfft()", __FILE__, __LINE__ );
00191     return BPM_FAILURE;
00192   }
00193 
00194   // fill FFT data buffer
00195   for ( i=0; i<z->ns; i++ ) {
00196     _fft_data[2*i]   = z->wf[i].re;
00197     _fft_data[2*i+1] = z->wf[i].im; 
00198   }
00199 
00200   // execute the cdft
00201   switch ( fft_mode ) {
00202     case FFT_FORWARD:
00203       cdft( 2*z->ns, 1, _fft_data, _fft_work_area, _fft_sc_table );
00204       break;
00205     case FFT_BACKWARD:
00206       cdft( 2*z->ns, -1, _fft_data, _fft_work_area, _fft_sc_table );
00207       break;
00208     default:
00209       bpm_error( "Unknown FFT mode in complexfft()", __FILE__, __LINE__ );
00210       return BPM_FAILURE;
00211       break;
00212   }
00213   
00214   // reset the data
00215   for ( i=0; i<z->ns; i++ ) {
00216     z->wf[i].re = _fft_data[2*i];
00217     z->wf[i].im = _fft_data[2*i+1];
00218   }
00219 
00220   return BPM_SUCCESS;
00221 }
00222 
00223 // ----------------------------------------------------------------------------
00224 
00225 int realfft( doublewf_t *y, int fft_mode, complexwf_t *z ) {
00226 
00227   // FFT_FORWARD : real wf of ns samples y -> complex wf of ns/2 samples z
00228   // FFT_BACKWARD: complex wf of ns/2 samples in z -> real wf of ns samples in y
00229 
00230   int i = 0;
00231 
00232   if ( ! _is_pow2(z->ns) ) {
00233     bpm_warning( "Number of samples is not of the form 2^n, may run into trouble with FFT !",
00234                  __FILE__, __LINE__ );
00235   }
00236 
00237   if ( _check_fft_buffers( z->ns ) == BPM_FAILURE ) {
00238     bpm_error( "Error checking FFT buffers in complexfft()", __FILE__, __LINE__ );
00239     return BPM_FAILURE;
00240   }
00241 
00242 
00243   // execute the cdft
00244   switch ( fft_mode ) {
00245     case FFT_FORWARD:
00246       // fill FFT data buffer
00247       for ( i=0; i<z->ns; i++ ) _fft_data[i] = y->wf[i];
00248 
00249       rdft( z->ns, 1, _fft_data, _fft_work_area, _fft_sc_table );
00250 
00251       // refill the data
00252       for ( i=0; i<z->ns/2; i++ ) {
00253         z->wf[i].re = z->wf[z->ns-1-i].re = _fft_data[2*i];
00254         z->wf[i].im = z->wf[z->ns-1-i].im = _fft_data[2*i+1];
00255       }
00256 
00257       break;
00258     case FFT_BACKWARD:
00259       // fill FFT data buffer
00260       for ( i=0; i<z->ns/2; i++ ) {
00261         _fft_data[2*i]   = z->wf[i].re;
00262         _fft_data[2*i+1] = z->wf[i].im;
00263       }
00264 
00265       rdft( z->ns, -1, _fft_data, _fft_work_area, _fft_sc_table );
00266       
00267       // refill the data
00268       for ( i=0; i<z->ns; i++ ) y->wf[i] = _fft_data[i];
00269       break;
00270 
00271     default:
00272       bpm_error( "Unknown FFT mode in complexfft()", __FILE__, __LINE__ );
00273       return BPM_FAILURE;
00274       break;
00275   }
00276   
00277 
00278   return BPM_SUCCESS;
00279 }

Generated on Thu Jan 10 10:18:04 2008 for libbpm by  doxygen 1.5.1