bpmprocess/fit_fft.c

Go to the documentation of this file.
00001 
00005 #include <stdio.h>
00006 #include <bpm/bpm_alloc.h>
00007 #include <bpm/bpm_nr.h>
00008 #include <bpm/bpm_units.h>
00009 #include <bpm/bpm_messages.h>
00010 #include <bpm/bpm_process.h>
00011 
00012 #define FIT_MAX_ITER     5000  // max iteration
00013 #define FIT_WINDOW_FACTOR   3  // number of sigma to fit withing (integer)
00014 
00015 /*
00016   p[0] = amplitude
00017   p[1] = freq
00018   p[2] = tdecay
00019   p[3] = offset
00020 */
00021 
00022 void fcnlorjac( double *p, double *ljac, int np, int ns, void *a ) {
00023 
00024   int i,j;
00025   double denom;
00026   double f;
00027 
00028   for ( i=j=0; i<ns; i++ ) {
00029     // convert sample nr to freq
00030     f = (((double*)a)[0] + (double)i + 0.5 ) / ((double*)a)[1] * ((double*)a)[2];
00031     denom = SQR( f - p[1] ) + SQR( p[2] / 2. );
00032 
00033     if ( denom == 0. ) {
00034       ljac[j++] = 0.;
00035       ljac[j++] = 0.;
00036       ljac[j++] = 0.;
00037       ljac[j++] = 1.;
00038     } else {
00039       ljac[j++] =  1. / denom;
00040       ljac[j++] =  2. * p[0] * (f-p[1]) / SQR( denom );
00041       ljac[j++] = -0.5 * p[0] * p[2] / SQR( denom );
00042       ljac[j++] =  1.;
00043     }
00044 
00045   }
00046 
00047   return;
00048 }
00049 
00050 void fcnlor( double *p, double *lor, int np, int ns, void *a ) {
00051 
00052   int i;
00053   double f;
00062   for ( i=0; i<ns; i++ ) {
00063     // convert sample nr to freq
00064     f = (((double*)a)[0] + (double) i + 0.5 ) / ((double*)a)[1] * ((double*)a)[2];
00065     lor[i] = p[0] / ( SQR( f - p[1] ) + SQR( p[2] / 2. ) ) + p[3];
00066   }
00067 
00068   return;
00069 }
00070 
00071 // ----------------------------------------------------------------------------------
00072 
00077 int fit_fft_prepare( double **fft, int ns, double fs, int *n1, int *n2,
00078                      double *amp, double *freq, double *fwhm ) {
00079 
00080   int i, imax=0;
00081   double  pw, tmp;
00082 
00083   if ( ! fft || ! amp ) {
00084     bpm_error( "Invalid pointers in fit_fft_prepare(...)",
00085                __FILE__, __LINE__ );
00086     return BPM_FAILURE;
00087   }
00088 
00089   // some vary basic initial values
00090   *amp    = 0; // need this to be 0 to find max !
00091   *freq   = 20.0*MHz;
00092   *fwhm   = 10.0*MHz; 
00093   *n1     =  20;
00094   *n2     = 100;
00095 
00096   // get maximum peak position in spectrum, only up to nyquist freq (ns/2)
00097   for ( i=0; i<ns/2; i++ ) {
00098     pw = SQR( fft[i][Re] ) + SQR( fft[i][Im]);
00099     if ( pw > *amp ) {
00100       *amp = pw; imax = i;
00101     }
00102   }
00103   *freq = (double) imax / (double) ns * fs;
00104 
00105   // run left and right to get the FWHM initial value
00106   for ( i=imax; i>0; i-- ) {
00107     pw = SQR( fft[i][Re] ) + SQR( fft[i][Im]);
00108     if ( pw <= *amp/2. ) break;
00109   }
00110   *n1 = i;
00111 
00112   for ( i=imax; i<ns/2 ; i++ ) {
00113     pw = SQR( fft[i][Re] ) + SQR( fft[i][Im]);
00114     if ( pw <= *amp/2. ) break;
00115   }
00116   *n2 = i;
00117 
00118   // set the FWHM initial decay cte
00119   *fwhm = ( (double)( *n2 - *n1 ) / (double)ns * fs );
00120 
00121   // now let's take 2 x FWHM as fit range...
00122   *n1 = imax - FIT_WINDOW_FACTOR * ABS(imax - *n1);
00123   *n2 = imax + FIT_WINDOW_FACTOR * ABS(imax - *n2);
00124 
00125   // safety check...
00126   if ( *n1 < 0 ) *n1 = 0;
00127   if ( *n2 > ns/2) *n2=ns/2;
00128 
00129   if ( *n2 <= *n1 ) {
00130     bpm_error( "Error in fit range ( n2 <= n1 ) in fit_fft_prepare(...)",
00131                __FILE__, __LINE__ );
00132     return BPM_FAILURE;
00133   }
00134   
00135   if ( *n2 - *n1 < 5 ) {
00136     bpm_error( "Error, too few number of samples in fit_fft_prepare(...)",
00137                __FILE__, __LINE__ );
00138     return BPM_FAILURE;
00139   }
00140 
00141 
00142 #if 0
00143   printf( "fftfit prepare : n1=%d, n2=%d, imax=%d, amp=%f, freq=%f, fwhm=%f\n",
00144           *n1, *n2, imax, *amp, *freq, *fwhm );
00145   fflush( stdout );
00146 #endif
00147 
00148   return BPM_SUCCESS;
00149 }
00150 
00151 // ----------------------------------------------------------------------------------
00152 
00153 /*
00154   Fits the power spectrum of the FT of a waveform frequency and decay time.
00155   the fitting range is set by i=n1;i<=n2
00156   
00157   @param **fft the fft
00158   @param  ns   the number of samples
00159   @param  fs   the sampling frequency
00160   
00161   @param  freq      the returned frequency (p1)
00162   @param  tdecay    the returned tdecay (p2)
00163   @param  a         p0 of the fit ( can be NULL if not interested )
00164   @param  c         p3 of the fit ( can be NULL if not interested )
00165   
00166   @return BPM_SUCCESS upon success, BPM_FAILURE upon failure
00167 */
00168 int fit_fft( double **fft, int ns, double fs, double *freq, double *tdecay, 
00169              double *A, double *C ) {
00170 
00171   const int npar = 4; // number of parameters in fit
00172   double *pspec, par[npar];
00173   double opt[4], info[LM_INFO_SZ];
00174   double a[3], i_amp, i_freq, i_fwhm;
00175   int i, nfit, n1, n2;
00176 
00177 #if 0
00178   double *err;
00179 #endif
00180 
00181   opt[0]=LM_INIT_MU; opt[1]=1E-15; opt[2]=1E-15; opt[3]=1E-20;
00182 
00183   // init
00184   *freq   = 0.;
00185   *tdecay = 0.;
00186 
00187   if ( ! fft ) {
00188     bpm_error( "Invalid pointer in fit_fft(...)",
00189                __FILE__, __LINE__ );
00190     return BPM_FAILURE;
00191   }
00192   
00193   if ( fit_fft_prepare( fft, ns, fs, &n1, &n2, &i_amp, &i_freq, &i_fwhm ) == BPM_FAILURE ) {
00194     bpm_error( "Error in preparing fft fit, givin up in fit_fft(...)", __FILE__, __LINE__  );
00195     return BPM_FAILURE;
00196   }
00197 
00198   // prepare the fit stuff
00199   nfit = n2 - n1 + 1;
00200   pspec = alloc_simple_wave_double( nfit );
00201   for ( i=0; i<nfit; i++ ) {
00202     pspec[i] = SQR( fft[n1+i][Re] ) + SQR( fft[n1+i][Im] );
00203   }
00204 
00205   // intial pars
00206   par[0] = i_amp;
00207   par[1] = i_freq;
00208   par[2] = i_fwhm;
00209   par[3] = 0.;
00210 
00211   // extra data
00212   a[0] = (double) n1;
00213   a[1] = (double) ns;
00214   a[2] = fs;
00215 
00216   // test jacobian
00217 #if 0
00218   err = alloc_simple_wave_double( nfit );
00219   nr_lmchkjac( fcnlor, fcnlorjac, par, npar, nfit, a, err );
00220   for (i=0;i<nfit;i++) printf( "err[%d] = %f\n", i, err[i] ); fflush(stdout);
00221   free(err);
00222 #endif
00223 
00224   // here we go
00225   nr_lmder( fcnlor, fcnlorjac, par, pspec, npar, nfit, FIT_MAX_ITER, opt, info,
00226                     NULL, NULL, a );
00227 
00228   *freq = par[1];
00229   // we fitted the FWHM, this is the inverse of the decay time and lacks a 
00230   // factor of pi : http://mathworld.wolfram.com/FourierTransformLorentzianFunction.html
00231   if ( par[2] != 0. ) *tdecay = 1. / par[2] / PI;
00232 
00233   // return the other parameters as well if needed
00234   if ( A ) *A = par[0];
00235   if ( C ) *C = par[3];
00236   
00237   free_simple_wave_double( pspec );
00238 
00239   return BPM_SUCCESS;
00240 }
00241 
00242 #undef FIT_MAX_ITER

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