AverageSpectrum.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Bernd Machenschalk, Jolien Creighton, Kipp Cannon
00003 *
00004 *  This program is free software; you can redistribute it and/or modify
00005 *  it under the terms of the GNU General Public License as published by
00006 *  the Free Software Foundation; either version 2 of the License, or
00007 *  (at your option) any later version.
00008 *
00009 *  This program is distributed in the hope that it will be useful,
00010 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 *  GNU General Public License for more details.
00013 *
00014 *  You should have received a copy of the GNU General Public License
00015 *  along with with program; see the file COPYING. If not, write to the
00016 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00017 *  MA  02111-1307  USA
00018 */
00019 
00020 #include <math.h>
00021 #include <string.h>
00022 #include <lal/LALComplex.h>
00023 #include <lal/FrequencySeries.h>
00024 #include <lal/LALStdlib.h>
00025 #include <lal/LALConstants.h>
00026 #include <lal/AVFactories.h>
00027 #include <lal/Sequence.h>
00028 #include <lal/TimeFreqFFT.h>
00029 #include <lal/Units.h>
00030 #include <lal/Window.h>
00031 #include <lal/Date.h>
00032 
00033 #include <lal/LALRCSID.h>
00034 NRCSID (AVERAGESPECTRUMC,"$Id: AverageSpectrum.c,v 1.26 2008/09/30 00:52:56 kipp Exp $");
00035 
00036 
00037 
00038 
00039 /**
00040  *
00041  * Compute a "modified periodogram," i.e., the power spectrum of a windowed
00042  * time series.
00043  *
00044  */
00045 int XLALREAL4ModifiedPeriodogram(
00046     REAL4FrequencySeries        *periodogram,
00047     const REAL4TimeSeries       *tseries,
00048     const REAL4Window           *window,
00049     const REAL4FFTPlan          *plan
00050     )
00051 {
00052   static const char *func = "XLALREAL4ModifiedPeriodogram";
00053   REAL4Sequence *work;
00054   REAL4 normfac;
00055   UINT4 k;
00056   int result;
00057 
00058   if ( ! periodogram || ! tseries || ! plan )
00059       XLAL_ERROR( func, XLAL_EFAULT );
00060   if ( ! periodogram->data || ! tseries->data )
00061       XLAL_ERROR( func, XLAL_EINVAL );
00062   if ( tseries->deltaT <= 0.0 )
00063       XLAL_ERROR( func, XLAL_EINVAL );
00064   if ( periodogram->data->length != tseries->data->length/2 + 1 )
00065       XLAL_ERROR( func, XLAL_EBADLEN );
00066   if ( window )
00067   {
00068     if ( ! window->data )
00069       XLAL_ERROR( func, XLAL_EINVAL );
00070     if ( window->sumofsquares <= 0.0 )
00071       XLAL_ERROR( func, XLAL_EINVAL );
00072     if ( window->data->length != tseries->data->length )
00073       XLAL_ERROR( func, XLAL_EBADLEN );
00074   }
00075 
00076   /* if the window has been specified, apply it to data */
00077   if ( window )
00078   {
00079     UINT4 j;
00080 
00081     /* make a working copy */
00082     work = XLALCutREAL4Sequence( tseries->data, 0, tseries->data->length );
00083     if ( ! work )
00084       XLAL_ERROR( func, XLAL_EFUNC );
00085 
00086     /* apply windowing to data */
00087     for ( j = 0; j < tseries->data->length; ++j )
00088       work->data[j] *= window->data->data[j];
00089   }
00090   else
00091     /* point to original data */
00092     work = tseries->data;
00093 
00094   /* compute the power spectrum of the (windowed) timeseries */
00095   /* CHECKME: are DC and Nyquist right? */
00096   result = XLALREAL4PowerSpectrum( periodogram->data, work, plan );
00097   /* destroy the workspace if it was created */
00098   if ( window )
00099     XLALDestroyREAL4Sequence( work );
00100   /* check for errors from the PowerSpectrum call */
00101   if ( result == XLAL_FAILURE )
00102     XLAL_ERROR( func, XLAL_EFUNC );
00103 
00104   /* normalize power spectrum to give correct units */
00105   /* CHECKME: is this the right factor? */
00106   if ( window )
00107     normfac = tseries->deltaT / window->sumofsquares;
00108   else
00109     normfac = tseries->deltaT / tseries->data->length;
00110   for ( k = 0; k < periodogram->data->length; ++k )
00111     periodogram->data->data[k] *= normfac;
00112 
00113   /* now set rest of metadata */
00114   periodogram->epoch  = tseries->epoch;
00115   periodogram->f0     = tseries->f0; /* FIXME: is this right? */
00116   periodogram->deltaF = 1.0 / ( tseries->data->length * tseries->deltaT );
00117 
00118   /* compute units */
00119   if ( ! XLALUnitSquare( &periodogram->sampleUnits, &tseries->sampleUnits ) )
00120     XLAL_ERROR( func, XLAL_EFUNC );
00121   if ( ! XLALUnitMultiply( &periodogram->sampleUnits,
00122                            &periodogram->sampleUnits, &lalSecondUnit ) )
00123     XLAL_ERROR( func, XLAL_EFUNC );
00124 
00125   return 0;
00126 }
00127 
00128 /**
00129  *
00130  * Compute a "modified periodogram," i.e., the power spectrum of a windowed
00131  * time series.
00132  *
00133  */
00134 int XLALREAL8ModifiedPeriodogram(
00135     REAL8FrequencySeries        *periodogram,
00136     const REAL8TimeSeries       *tseries,
00137     const REAL8Window           *window,
00138     const REAL8FFTPlan          *plan
00139     )
00140 {
00141   static const char *func = "XLALREAL8ModifiedPeriodogram";
00142   REAL8Sequence *work;
00143   REAL8 normfac;
00144   UINT4 k;
00145   int result;
00146 
00147   if ( ! periodogram || ! tseries || ! plan )
00148       XLAL_ERROR( func, XLAL_EFAULT );
00149   if ( ! periodogram->data || ! tseries->data )
00150       XLAL_ERROR( func, XLAL_EINVAL );
00151   if ( tseries->deltaT <= 0.0 )
00152       XLAL_ERROR( func, XLAL_EINVAL );
00153   if ( periodogram->data->length != tseries->data->length/2 + 1 )
00154       XLAL_ERROR( func, XLAL_EBADLEN );
00155   if ( window )
00156   {
00157     if ( ! window->data )
00158       XLAL_ERROR( func, XLAL_EINVAL );
00159     if ( window->sumofsquares <= 0.0 )
00160       XLAL_ERROR( func, XLAL_EINVAL );
00161     if ( window->data->length != tseries->data->length )
00162       XLAL_ERROR( func, XLAL_EBADLEN );
00163   }
00164 
00165   /* if the window has been specified, apply it to data */
00166   if ( window )
00167   {
00168     UINT4 j;
00169 
00170     /* make a working copy */
00171     work = XLALCutREAL8Sequence( tseries->data, 0, tseries->data->length );
00172     if ( ! work )
00173       XLAL_ERROR( func, XLAL_EFUNC );
00174 
00175     /* apply windowing to data */
00176     for ( j = 0; j < tseries->data->length; ++j )
00177       work->data[j] *= window->data->data[j];
00178   }
00179   else
00180     /* point to original data */
00181     work = tseries->data;
00182 
00183   /* compute the power spectrum of the (windowed) timeseries */
00184   /* CHECKME: are DC and Nyquist right? */
00185   result = XLALREAL8PowerSpectrum( periodogram->data, work, plan );
00186   /* destroy the workspace if it was created */
00187   if ( window )
00188     XLALDestroyREAL8Sequence( work );
00189   /* check for errors from the PowerSpectrum call */
00190   if ( result == XLAL_FAILURE )
00191     XLAL_ERROR( func, XLAL_EFUNC );
00192 
00193   /* normalize power spectrum to give correct units */
00194   /* CHECKME: is this the right factor? */
00195   if ( window )
00196     normfac = tseries->deltaT / window->sumofsquares;
00197   else
00198     normfac = tseries->deltaT / tseries->data->length;
00199   for ( k = 0; k < periodogram->data->length; ++k )
00200     periodogram->data->data[k] *= normfac;
00201 
00202   /* now set rest of metadata */
00203   periodogram->epoch  = tseries->epoch;
00204   periodogram->f0     = tseries->f0; /* FIXME: is this right? */
00205   periodogram->deltaF = 1.0 / ( tseries->data->length * tseries->deltaT );
00206 
00207   /* compute units */
00208   if ( ! XLALUnitSquare( &periodogram->sampleUnits, &tseries->sampleUnits ) )
00209     XLAL_ERROR( func, XLAL_EFUNC );
00210   if ( ! XLALUnitMultiply( &periodogram->sampleUnits,
00211                            &periodogram->sampleUnits, &lalSecondUnit ) )
00212     XLAL_ERROR( func, XLAL_EFUNC );
00213 
00214   return 0;
00215 }
00216 
00217 
00218 /**
00219  *
00220  * Use Welch's method to compute the average power spectrum of a time series.
00221  *
00222  * See: Peter D. Welch "The Use of Fast Fourier Transform for the Estimation
00223  * of Power Spectra: A Method Based on Time Averaging Over Short, Modified
00224  * Periodograms"  IEEE Transactions on Audio and Electroacoustics,
00225  * Vol. AU-15, No. 2, June 1967.
00226  *
00227  */
00228 int XLALREAL4AverageSpectrumWelch(
00229     REAL4FrequencySeries        *spectrum,
00230     const REAL4TimeSeries       *tseries,
00231     UINT4                        seglen,
00232     UINT4                        stride,
00233     const REAL4Window           *window,
00234     const REAL4FFTPlan          *plan
00235     )
00236 {
00237   static const char *func = "XLALREAL4AverageSpectrumWelch";
00238   REAL4FrequencySeries *work; /* workspace */
00239   REAL4Sequence sequence; /* working copy of input time series data */
00240   REAL4TimeSeries tseriescopy; /* working copy of input time series */
00241   UINT4 numseg;
00242   UINT4 seg;
00243   UINT4 k;
00244 
00245   if ( ! spectrum || ! tseries || ! plan )
00246       XLAL_ERROR( func, XLAL_EFAULT );
00247   if ( ! spectrum->data || ! tseries->data )
00248       XLAL_ERROR( func, XLAL_EINVAL );
00249   if ( tseries->deltaT <= 0.0 )
00250       XLAL_ERROR( func, XLAL_EINVAL );
00251 
00252   /* construct local copy of time series */
00253   sequence = *tseries->data;
00254   tseriescopy = *tseries;
00255   tseriescopy.data = &sequence;
00256   tseriescopy.data->length = seglen;
00257 
00258   numseg = 1 + (tseries->data->length - seglen)/stride;
00259 
00260   /* consistency check for lengths: make sure that the segments cover the
00261    * data record completely */
00262   if ( (numseg - 1)*stride + seglen != tseries->data->length )
00263     XLAL_ERROR( func, XLAL_EBADLEN );
00264   if ( spectrum->data->length != seglen/2 + 1 )
00265     XLAL_ERROR( func, XLAL_EBADLEN );
00266 
00267   /* make sure window, if present, is appropriate */
00268   if ( window )
00269   {
00270     if ( ! window->data )
00271       XLAL_ERROR( func, XLAL_EINVAL );
00272     if ( window->sumofsquares <= 0.0 )
00273       XLAL_ERROR( func, XLAL_EINVAL );
00274     if ( window->data->length != seglen )
00275       XLAL_ERROR( func, XLAL_EBADLEN );
00276   }
00277 
00278   /* clear spectrum data */
00279   memset( spectrum->data->data, 0,
00280       spectrum->data->length * sizeof( *spectrum->data->data ) );
00281 
00282   /* create frequency series data workspace */
00283   work = XLALCutREAL4FrequencySeries( spectrum, 0, spectrum->data->length );
00284   if( ! work )
00285     XLAL_ERROR( func, XLAL_EFUNC );
00286 
00287   for ( seg = 0; seg < numseg; seg++, tseriescopy.data->data += stride )
00288   {
00289     /* compute the modified periodogram; clean up and exit on failure */
00290     if ( XLALREAL4ModifiedPeriodogram( work, &tseriescopy, window, plan ) == XLAL_FAILURE )
00291     {
00292       XLALDestroyREAL4FrequencySeries( work );
00293       XLAL_ERROR( func, XLAL_EFUNC );
00294     }
00295 
00296     /* add the periodogram to the running sum */
00297     for ( k = 0; k < spectrum->data->length; ++k )
00298       spectrum->data->data[k] += work->data->data[k];
00299   }
00300 
00301   /* set metadata */
00302   spectrum->epoch       = work->epoch;
00303   spectrum->f0          = work->f0;
00304   spectrum->deltaF      = work->deltaF;
00305   spectrum->sampleUnits = work->sampleUnits;
00306 
00307   /* divide spectrum data by the number of segments in average */
00308   for ( k = 0; k < spectrum->data->length; ++k )
00309     spectrum->data->data[k] /= numseg;
00310 
00311   /* clean up */
00312   XLALDestroyREAL4FrequencySeries( work );
00313 
00314   return 0;
00315 }
00316 
00317 /**
00318  *
00319  * Use Welch's method to compute the average power spectrum of a time series.
00320  *
00321  * See: Peter D. Welch "The Use of Fast Fourier Transform for the Estimation
00322  * of Power Spectra: A Method Based on Time Averaging Over Short, Modified
00323  * Periodograms"  IEEE Transactions on Audio and Electroacoustics,
00324  * Vol. AU-15, No. 2, June 1967.
00325  *
00326  */
00327 int XLALREAL8AverageSpectrumWelch(
00328     REAL8FrequencySeries        *spectrum,
00329     const REAL8TimeSeries       *tseries,
00330     UINT4                        seglen,
00331     UINT4                        stride,
00332     const REAL8Window           *window,
00333     const REAL8FFTPlan          *plan
00334     )
00335 {
00336   static const char *func = "XLALREAL8AverageSpectrumWelch";
00337   REAL8FrequencySeries *work; /* workspace */
00338   REAL8Sequence sequence; /* working copy of input time series data */
00339   REAL8TimeSeries tseriescopy; /* working copy of input time series */
00340   UINT4 numseg;
00341   UINT4 seg;
00342   UINT4 k;
00343 
00344   if ( ! spectrum || ! tseries || ! plan )
00345       XLAL_ERROR( func, XLAL_EFAULT );
00346   if ( ! spectrum->data || ! tseries->data )
00347       XLAL_ERROR( func, XLAL_EINVAL );
00348   if ( tseries->deltaT <= 0.0 )
00349       XLAL_ERROR( func, XLAL_EINVAL );
00350 
00351   /* construct local copy of time series */
00352   sequence = *tseries->data;
00353   tseriescopy = *tseries;
00354   tseriescopy.data = &sequence;
00355   tseriescopy.data->length = seglen;
00356 
00357   numseg = 1 + (tseries->data->length - seglen)/stride;
00358 
00359   /* consistency check for lengths: make sure that the segments cover the
00360    * data record completely */
00361   if ( (numseg - 1)*stride + seglen != tseries->data->length )
00362     XLAL_ERROR( func, XLAL_EBADLEN );
00363   if ( spectrum->data->length != seglen/2 + 1 )
00364     XLAL_ERROR( func, XLAL_EBADLEN );
00365 
00366   /* make sure window, if present, is appropriate */
00367   if ( window )
00368   {
00369     if ( ! window->data )
00370       XLAL_ERROR( func, XLAL_EINVAL );
00371     if ( window->sumofsquares <= 0.0 )
00372       XLAL_ERROR( func, XLAL_EINVAL );
00373     if ( window->data->length != seglen )
00374       XLAL_ERROR( func, XLAL_EBADLEN );
00375   }
00376 
00377   /* clear spectrum data */
00378   memset( spectrum->data->data, 0,
00379       spectrum->data->length * sizeof( *spectrum->data->data ) );
00380 
00381   /* create frequency series data workspace */
00382   work = XLALCutREAL8FrequencySeries( spectrum, 0, spectrum->data->length );
00383   if( ! work )
00384     XLAL_ERROR( func, XLAL_EFUNC );
00385 
00386   for ( seg = 0; seg < numseg; seg++, tseriescopy.data->data += stride )
00387   {
00388     /* compute the modified periodogram; clean up and exit on failure */
00389     if ( XLALREAL8ModifiedPeriodogram( work, &tseriescopy, window, plan ) == XLAL_FAILURE )
00390     {
00391       XLALDestroyREAL8FrequencySeries( work );
00392       XLAL_ERROR( func, XLAL_EFUNC );
00393     }
00394 
00395     /* add the periodogram to the running sum */
00396     for ( k = 0; k < spectrum->data->length; ++k )
00397       spectrum->data->data[k] += work->data->data[k];
00398   }
00399 
00400   /* set metadata */
00401   spectrum->epoch       = work->epoch;
00402   spectrum->f0          = work->f0;
00403   spectrum->deltaF      = work->deltaF;
00404   spectrum->sampleUnits = work->sampleUnits;
00405 
00406   /* divide spectrum data by the number of segments in average */
00407   for ( k = 0; k < spectrum->data->length; ++k )
00408     spectrum->data->data[k] /= numseg;
00409 
00410   /* clean up */
00411   XLALDestroyREAL8FrequencySeries( work );
00412 
00413   return 0;
00414 }
00415 
00416 
00417 /* 
00418  *
00419  * Median Method: use median average rather than mean.
00420  *
00421  */
00422 
00423 /* compute the median bias */
00424 REAL8 XLALMedianBias( UINT4 nn )
00425 {
00426   const UINT4 nmax = 1000;
00427   REAL8 ans = 1;
00428   UINT4 n = (nn - 1)/2;
00429   UINT4 i;
00430 
00431   if ( nn >= nmax )
00432     return LAL_LN2;
00433 
00434   for ( i = 1; i <= n; ++i )
00435   {
00436     ans -= 1.0/(2*i);
00437     ans += 1.0/(2*i + 1);
00438   }
00439 
00440   return ans;
00441 }
00442 
00443 /* cleanup temporary workspace... ignore xlal errors */
00444 static void median_cleanup_REAL4( REAL4FrequencySeries *work, UINT4 n )
00445 {
00446   int saveErrno = xlalErrno;
00447   UINT4 i;
00448   for ( i = 0; i < n; ++i )
00449     if ( work[i].data )
00450       XLALDestroyREAL4Vector( work[i].data );
00451   XLALFree( work );
00452   xlalErrno = saveErrno;
00453   return;
00454 }
00455 static void median_cleanup_REAL8( REAL8FrequencySeries *work, UINT4 n )
00456 {
00457   int saveErrno = xlalErrno;
00458   UINT4 i;
00459   for ( i = 0; i < n; ++i )
00460     if ( work[i].data )
00461       XLALDestroyREAL8Vector( work[i].data );
00462   XLALFree( work );
00463   xlalErrno = saveErrno;
00464   return;
00465 }
00466 
00467 /* comparison for floating point numbers */
00468 static int compare_REAL4( const void *p1, const void *p2 )
00469 {
00470   REAL4 x1 = *(const REAL4 *)p1;
00471   REAL4 x2 = *(const REAL4 *)p2;
00472   return (x1 > x2) - (x1 < x2);
00473 }
00474 static int compare_REAL8( const void *p1, const void *p2 )
00475 {
00476   REAL8 x1 = *(const REAL8 *)p1;
00477   REAL8 x2 = *(const REAL8 *)p2;
00478   return (x1 > x2) - (x1 < x2);
00479 }
00480 
00481 
00482 /**
00483  *
00484  * Median Method: use median average rather than mean.  Note: this will
00485  * cause a bias if the segments overlap, i.e., if the stride is less than
00486  * the segment length -- even though the median bias for Gaussian noise
00487  * is accounted for -- because the segments are not independent and their
00488  * correlation is non-zero.
00489  *
00490  */
00491 int XLALREAL4AverageSpectrumMedian(
00492     REAL4FrequencySeries        *spectrum,
00493     const REAL4TimeSeries       *tseries,
00494     UINT4                        seglen,
00495     UINT4                        stride,
00496     const REAL4Window           *window,
00497     const REAL4FFTPlan          *plan
00498     )
00499 {
00500   static const char *func = "XLALREAL4AverageSpectrumMedian";
00501   REAL4FrequencySeries *work; /* array of frequency series */
00502   REAL4 *bin; /* array of bin values */
00503   REAL4 biasfac; /* median bias factor */
00504   REAL4 normfac; /* normalization factor */
00505   UINT4 reclen; /* length of entire data record */
00506   UINT4 numseg;
00507   UINT4 seg;
00508   UINT4 k;
00509 
00510   if ( ! spectrum || ! tseries || ! plan )
00511       XLAL_ERROR( func, XLAL_EFAULT );
00512   if ( ! spectrum->data || ! tseries->data )
00513       XLAL_ERROR( func, XLAL_EINVAL );
00514   if ( tseries->deltaT <= 0.0 )
00515       XLAL_ERROR( func, XLAL_EINVAL );
00516 
00517   reclen = tseries->data->length;
00518   numseg = 1 + (reclen - seglen)/stride;
00519 
00520   /* consistency check for lengths: make sure that the segments cover the
00521    * data record completely */
00522   if ( (numseg - 1)*stride + seglen != reclen )
00523     XLAL_ERROR( func, XLAL_EBADLEN );
00524   if ( spectrum->data->length != seglen/2 + 1 )
00525     XLAL_ERROR( func, XLAL_EBADLEN );
00526 
00527   /* make sure window, if present, is appropriate */
00528   if ( window )
00529   {
00530     if ( ! window->data )
00531       XLAL_ERROR( func, XLAL_EINVAL );
00532     if ( window->sumofsquares <= 0.0 )
00533       XLAL_ERROR( func, XLAL_EINVAL );
00534     if ( window->data->length != seglen )
00535       XLAL_ERROR( func, XLAL_EBADLEN );
00536   }
00537 
00538   /* create frequency series data workspaces */
00539   work = XLALCalloc( numseg, sizeof( *work ) );
00540   if ( ! work )
00541     XLAL_ERROR( func, XLAL_ENOMEM );
00542   for ( seg = 0; seg < numseg; ++seg )
00543   {
00544     work[seg].data = XLALCreateREAL4Vector( spectrum->data->length );
00545     if ( ! work[seg].data )
00546     {
00547       median_cleanup_REAL4( work, numseg ); /* cleanup */
00548       XLAL_ERROR( func, XLAL_EFUNC );
00549     }
00550   }
00551 
00552   for ( seg = 0; seg < numseg; ++seg )
00553   {
00554     REAL4Vector savevec; /* save the time series data vector */
00555     int code;
00556 
00557     /* save the time series data vector */
00558     savevec = *tseries->data;
00559 
00560     /* set the data vector to be appropriate for the even segment */
00561     tseries->data->length  = seglen;
00562     tseries->data->data   += seg * stride;
00563 
00564     /* compute the modified periodogram for the even segment */
00565     code = XLALREAL4ModifiedPeriodogram( work + seg, tseries, window, plan );
00566     
00567     /* restore the time series data vector to its original state */
00568     *tseries->data = savevec;
00569 
00570     /* now check for failure of the XLAL routine */
00571     if ( code == XLAL_FAILURE )
00572     {
00573       median_cleanup_REAL4( work, numseg ); /* cleanup */
00574       XLAL_ERROR( func, XLAL_EFUNC );
00575     }
00576   }
00577 
00578   /* create array to hold a particular frequency bin data */
00579   bin = XLALMalloc( numseg * sizeof( *bin ) );
00580   if ( ! bin )
00581   {
00582     median_cleanup_REAL4( work, numseg ); /* cleanup */
00583     XLAL_ERROR( func, XLAL_ENOMEM );
00584   }
00585 
00586   /* compute median bias factor */
00587   biasfac = XLALMedianBias( numseg );
00588 
00589   /* normaliztion takes into account bias */
00590   normfac = 1.0 / biasfac;
00591 
00592   /* now loop over frequency bins and compute the median-mean */
00593   for ( k = 0; k < spectrum->data->length; ++k )
00594   {
00595     /* assign array of even segment values to bin array for this freq bin */
00596     for ( seg = 0; seg < numseg; ++seg )
00597       bin[seg] = work[seg].data->data[k];
00598 
00599     /* sort them and find median */
00600     qsort( bin, numseg, sizeof( *bin ), compare_REAL4 );
00601     if ( numseg % 2 ) /* odd number of evens */
00602       spectrum->data->data[k] = bin[numseg/2];
00603     else /* even number... take average */
00604       spectrum->data->data[k] = 0.5*(bin[numseg/2-1] + bin[numseg/2]);
00605 
00606     /* remove median bias */
00607     spectrum->data->data[k] *= normfac;
00608   }
00609 
00610   /* set metadata */
00611   spectrum->epoch       = work->epoch;
00612   spectrum->f0          = work->f0;
00613   spectrum->deltaF      = work->deltaF;
00614   spectrum->sampleUnits = work->sampleUnits;
00615 
00616   /* free the workspace data */
00617   XLALFree( bin );
00618   median_cleanup_REAL4( work, numseg );
00619 
00620   return 0;
00621 }
00622 
00623 /**
00624  *
00625  * Median Method: use median average rather than mean.  Note: this will
00626  * cause a bias if the segments overlap, i.e., if the stride is less than
00627  * the segment length -- even though the median bias for Gaussian noise
00628  * is accounted for -- because the segments are not independent and their
00629  * correlation is non-zero.
00630  *
00631  */
00632 int XLALREAL8AverageSpectrumMedian(
00633     REAL8FrequencySeries        *spectrum,
00634     const REAL8TimeSeries       *tseries,
00635     UINT4                        seglen,
00636     UINT4                        stride,
00637     const REAL8Window           *window,
00638     const REAL8FFTPlan          *plan
00639     )
00640 {
00641   static const char *func = "XLALREAL8AverageSpectrumMedian";
00642   REAL8FrequencySeries *work; /* array of frequency series */
00643   REAL8 *bin; /* array of bin values */
00644   REAL8 biasfac; /* median bias factor */
00645   REAL8 normfac; /* normalization factor */
00646   UINT4 reclen; /* length of entire data record */
00647   UINT4 numseg;
00648   UINT4 seg;
00649   UINT4 k;
00650 
00651   if ( ! spectrum || ! tseries || ! plan )
00652       XLAL_ERROR( func, XLAL_EFAULT );
00653   if ( ! spectrum->data || ! tseries->data )
00654       XLAL_ERROR( func, XLAL_EINVAL );
00655   if ( tseries->deltaT <= 0.0 )
00656       XLAL_ERROR( func, XLAL_EINVAL );
00657 
00658   reclen = tseries->data->length;
00659   numseg = 1 + (reclen - seglen)/stride;
00660 
00661   /* consistency check for lengths: make sure that the segments cover the
00662    * data record completely */
00663   if ( (numseg - 1)*stride + seglen != reclen )
00664     XLAL_ERROR( func, XLAL_EBADLEN );
00665   if ( spectrum->data->length != seglen/2 + 1 )
00666     XLAL_ERROR( func, XLAL_EBADLEN );
00667 
00668   /* make sure window, if present, is appropriate */
00669   if ( window )
00670   {
00671     if ( ! window->data )
00672       XLAL_ERROR( func, XLAL_EINVAL );
00673     if ( window->sumofsquares <= 0.0 )
00674       XLAL_ERROR( func, XLAL_EINVAL );
00675     if ( window->data->length != seglen )
00676       XLAL_ERROR( func, XLAL_EBADLEN );
00677   }
00678 
00679   /* create frequency series data workspaces */
00680   work = XLALCalloc( numseg, sizeof( *work ) );
00681   if ( ! work )
00682     XLAL_ERROR( func, XLAL_ENOMEM );
00683   for ( seg = 0; seg < numseg; ++seg )
00684   {
00685     work[seg].data = XLALCreateREAL8Vector( spectrum->data->length );
00686     if ( ! work[seg].data )
00687     {
00688       median_cleanup_REAL8( work, numseg ); /* cleanup */
00689       XLAL_ERROR( func, XLAL_EFUNC );
00690     }
00691   }
00692 
00693   for ( seg = 0; seg < numseg; ++seg )
00694   {
00695     REAL8Vector savevec; /* save the time series data vector */
00696     int code;
00697 
00698     /* save the time series data vector */
00699     savevec = *tseries->data;
00700 
00701     /* set the data vector to be appropriate for the even segment */
00702     tseries->data->length  = seglen;
00703     tseries->data->data   += seg * stride;
00704 
00705     /* compute the modified periodogram for the even segment */
00706     code = XLALREAL8ModifiedPeriodogram( work + seg, tseries, window, plan );
00707     
00708     /* restore the time series data vector to its original state */
00709     *tseries->data = savevec;
00710 
00711     /* now check for failure of the XLAL routine */
00712     if ( code == XLAL_FAILURE )
00713     {
00714       median_cleanup_REAL8( work, numseg ); /* cleanup */
00715       XLAL_ERROR( func, XLAL_EFUNC );
00716     }
00717   }
00718 
00719   /* create array to hold a particular frequency bin data */
00720   bin = XLALMalloc( numseg * sizeof( *bin ) );
00721   if ( ! bin )
00722   {
00723     median_cleanup_REAL8( work, numseg ); /* cleanup */
00724     XLAL_ERROR( func, XLAL_ENOMEM );
00725   }
00726 
00727   /* compute median bias factor */
00728   biasfac = XLALMedianBias( numseg );
00729 
00730   /* normaliztion takes into account bias */
00731   normfac = 1.0 / biasfac;
00732 
00733   /* now loop over frequency bins and compute the median-mean */
00734   for ( k = 0; k < spectrum->data->length; ++k )
00735   {
00736     /* assign array of even segment values to bin array for this freq bin */
00737     for ( seg = 0; seg < numseg; ++seg )
00738       bin[seg] = work[seg].data->data[k];
00739 
00740     /* sort them and find median */
00741     qsort( bin, numseg, sizeof( *bin ), compare_REAL8 );
00742     if ( numseg % 2 ) /* odd number of evens */
00743       spectrum->data->data[k] = bin[numseg/2];
00744     else /* even number... take average */
00745       spectrum->data->data[k] = 0.5*(bin[numseg/2-1] + bin[numseg/2]);
00746 
00747     /* remove median bias */
00748     spectrum->data->data[k] *= normfac;
00749   }
00750 
00751   /* set metadata */
00752   spectrum->epoch       = work->epoch;
00753   spectrum->f0          = work->f0;
00754   spectrum->deltaF      = work->deltaF;
00755   spectrum->sampleUnits = work->sampleUnits;
00756 
00757   /* free the workspace data */
00758   XLALFree( bin );
00759   median_cleanup_REAL8( work, numseg );
00760 
00761   return 0;
00762 }
00763 
00764 
00765 /* 
00766  *
00767  * Median-Mean Method
00768  *
00769  */
00770 
00771 
00772 /* cleanup temporary workspace... ignore xlal errors */
00773 static void median_mean_cleanup_REAL4( REAL4FrequencySeries *even, REAL4FrequencySeries *odd, UINT4 n )
00774 {
00775   int saveErrno = xlalErrno;
00776   UINT4 i;
00777   for ( i = 0; i < n; ++i )
00778   {
00779     if ( even[i].data )
00780       XLALDestroyREAL4Vector( even[i].data );
00781     if ( odd[i].data )
00782       XLALDestroyREAL4Vector( odd[i].data );
00783   }
00784   XLALFree( even );
00785   XLALFree( odd );
00786   xlalErrno = saveErrno;
00787   return;
00788 }
00789 static void median_mean_cleanup_REAL8( REAL8FrequencySeries *even, REAL8FrequencySeries *odd, UINT4 n )
00790 {
00791   int saveErrno = xlalErrno;
00792   UINT4 i;
00793   for ( i = 0; i < n; ++i )
00794   {
00795     if ( even[i].data )
00796       XLALDestroyREAL8Vector( even[i].data );
00797     if ( odd[i].data )
00798       XLALDestroyREAL8Vector( odd[i].data );
00799   }
00800   XLALFree( even );
00801