LAL  7.5.0.1-ec27e42
AverageSpectrum.c
Go to the documentation of this file.
1 /*
2 * Copyright (C) 2007 Bernd Machenschalk, Jolien Creighton, Kipp Cannon, Drew Keppel
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with with program; see the file COPYING. If not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 */
19 
20 #include <complex.h>
21 #include <math.h>
22 #include <string.h>
23 #include <gsl/gsl_sf_gamma.h>
24 #include <lal/FrequencySeries.h>
25 #include <lal/LALAtomicDatatypes.h>
26 #include <lal/LALConstants.h>
27 #include <lal/AVFactories.h>
28 #include <lal/Sequence.h>
29 #include <lal/TimeFreqFFT.h>
30 #include <lal/Units.h>
31 #include <lal/Window.h>
32 #include <lal/Date.h>
33 
35 {
36  double x = creal(z);
37  double y = cimag(z);
38  return x * x + y * y;
39 }
40 
41 /**
42  * Compute a "modified periodogram," i.e., the power spectrum of a windowed
43  * time series.
44  *
45  */
47  REAL4FrequencySeries *periodogram,
48  const REAL4TimeSeries *tseries,
49  const REAL4Window *window,
50  const REAL4FFTPlan *plan
51  )
52 {
53  REAL4Sequence *work;
54  REAL4 normfac;
55  UINT4 k;
56  int result;
57 
58  if ( ! periodogram || ! tseries || ! plan )
60  if ( ! periodogram->data || ! tseries->data )
62  if ( tseries->deltaT <= 0.0 )
64  if ( periodogram->data->length != tseries->data->length/2 + 1 )
66 
67  /* if the window has been specified, apply it to data */
68  if ( window )
69  {
70  /* make a working copy */
71  work = XLALCutREAL4Sequence( tseries->data, 0, tseries->data->length );
72  if ( ! work )
74 
75  /* apply windowing to data */
76  if ( ! XLALUnitaryWindowREAL4Sequence( work, window ) )
77  {
80  }
81  }
82  else
83  /* point to original data */
84  work = tseries->data;
85 
86  /* compute the power spectrum of the (windowed) timeseries */
87  /* CHECKME: are DC and Nyquist right? */
88  result = XLALREAL4PowerSpectrum( periodogram->data, work, plan );
89  /* destroy the workspace if it was created */
90  if ( window )
92  /* check for errors from the PowerSpectrum call */
93  if ( result == XLAL_FAILURE )
95 
96  /* normalize power spectrum to give correct units */
97  /* CHECKME: is this the right factor? */
98  normfac = tseries->deltaT / tseries->data->length;
99  for ( k = 0; k < periodogram->data->length; ++k )
100  periodogram->data->data[k] *= normfac;
101 
102  /* now set rest of metadata */
103  periodogram->epoch = tseries->epoch;
104  periodogram->f0 = tseries->f0; /* FIXME: is this right? */
105  periodogram->deltaF = 1.0 / ( tseries->data->length * tseries->deltaT );
106 
107  /* compute units */
108  if ( ! XLALUnitSquare( &periodogram->sampleUnits, &tseries->sampleUnits ) )
110  if ( ! XLALUnitMultiply( &periodogram->sampleUnits,
111  &periodogram->sampleUnits, &lalSecondUnit ) )
113 
114  return 0;
115 }
116 
117 /**
118  * Compute a "modified periodogram," i.e., the power spectrum of a windowed
119  * time series.
120  *
121  */
123  REAL8FrequencySeries *periodogram,
124  const REAL8TimeSeries *tseries,
125  const REAL8Window *window,
126  const REAL8FFTPlan *plan
127  )
128 {
129  REAL8Sequence *work;
130  REAL8 normfac;
131  UINT4 k;
132  int result;
133 
134  if ( ! periodogram || ! tseries || ! plan )
136  if ( ! periodogram->data || ! tseries->data )
138  if ( tseries->deltaT <= 0.0 )
140  if ( periodogram->data->length != tseries->data->length/2 + 1 )
142 
143  /* if the window has been specified, apply it to data */
144  if ( window )
145  {
146  /* make a working copy */
147  work = XLALCutREAL8Sequence( tseries->data, 0, tseries->data->length );
148  if ( ! work )
150 
151  /* apply windowing to data */
152  if ( ! XLALUnitaryWindowREAL8Sequence( work, window ) )
153  {
154  XLALDestroyREAL8Sequence( work );
156  }
157  }
158  else
159  /* point to original data */
160  work = tseries->data;
161 
162  /* compute the power spectrum of the (windowed) timeseries */
163  /* CHECKME: are DC and Nyquist right? */
164  result = XLALREAL8PowerSpectrum( periodogram->data, work, plan );
165  /* destroy the workspace if it was created */
166  if ( window )
167  XLALDestroyREAL8Sequence( work );
168  /* check for errors from the PowerSpectrum call */
169  if ( result == XLAL_FAILURE )
171 
172  /* normalize power spectrum to give correct units */
173  /* CHECKME: is this the right factor? */
174  normfac = tseries->deltaT / tseries->data->length;
175  for ( k = 0; k < periodogram->data->length; ++k )
176  periodogram->data->data[k] *= normfac;
177 
178  /* now set rest of metadata */
179  periodogram->epoch = tseries->epoch;
180  periodogram->f0 = tseries->f0; /* FIXME: is this right? */
181  periodogram->deltaF = 1.0 / ( tseries->data->length * tseries->deltaT );
182 
183  /* compute units */
184  if ( ! XLALUnitSquare( &periodogram->sampleUnits, &tseries->sampleUnits ) )
186  if ( ! XLALUnitMultiply( &periodogram->sampleUnits,
187  &periodogram->sampleUnits, &lalSecondUnit ) )
189 
190  return 0;
191 }
192 
193 
194 /**
195  * Use Welch's method to compute the average power spectrum of a time series.
196  *
197  * See: Peter D. Welch "The Use of Fast Fourier Transform for the Estimation
198  * of Power Spectra: A Method Based on Time Averaging Over Short, Modified
199  * Periodograms" IEEE Transactions on Audio and Electroacoustics,
200  * Vol. AU-15, No. 2, June 1967.
201  *
202  */
204  REAL4FrequencySeries *spectrum,
205  const REAL4TimeSeries *tseries,
206  UINT4 seglen,
207  UINT4 stride,
208  const REAL4Window *window,
209  const REAL4FFTPlan *plan
210  )
211 {
212  REAL4FrequencySeries *work; /* workspace */
213  REAL4Sequence sequence; /* working copy of input time series data */
214  REAL4TimeSeries tseriescopy; /* working copy of input time series */
215  UINT4 numseg;
216  UINT4 seg;
217  UINT4 k;
218 
219  if ( ! spectrum || ! tseries || ! plan )
221  if ( ! spectrum->data || ! tseries->data )
223  if ( tseries->deltaT <= 0.0 )
225 
226  /* construct local copy of time series */
227  sequence = *tseries->data;
228  tseriescopy = *tseries;
229  tseriescopy.data = &sequence;
230  tseriescopy.data->length = seglen;
231 
232  numseg = 1 + (tseries->data->length - seglen)/stride;
233 
234  /* consistency check for lengths: make sure that the segments cover the
235  * data record completely */
236  if ( (numseg - 1)*stride + seglen != tseries->data->length )
238  if ( spectrum->data->length != seglen/2 + 1 )
240 
241  /* clear spectrum data */
242  memset( spectrum->data->data, 0,
243  spectrum->data->length * sizeof( *spectrum->data->data ) );
244 
245  /* create frequency series data workspace */
246  work = XLALCutREAL4FrequencySeries( spectrum, 0, spectrum->data->length );
247  if( ! work )
249 
250  for ( seg = 0; seg < numseg; seg++, tseriescopy.data->data += stride )
251  {
252  /* compute the modified periodogram; clean up and exit on failure */
253  if ( XLALREAL4ModifiedPeriodogram( work, &tseriescopy, window, plan ) == XLAL_FAILURE )
254  {
257  }
258 
259  /* add the periodogram to the running sum */
260  for ( k = 0; k < spectrum->data->length; ++k )
261  spectrum->data->data[k] += work->data->data[k];
262  }
263 
264  /* set metadata */
265  spectrum->epoch = work->epoch;
266  spectrum->f0 = work->f0;
267  spectrum->deltaF = work->deltaF;
268  spectrum->sampleUnits = work->sampleUnits;
269 
270  /* divide spectrum data by the number of segments in average */
271  for ( k = 0; k < spectrum->data->length; ++k )
272  spectrum->data->data[k] /= numseg;
273 
274  /* clean up */
276 
277  return 0;
278 }
279 
280 /**
281  * Use Welch's method to compute the average power spectrum of a time series.
282  *
283  * See: Peter D. Welch "The Use of Fast Fourier Transform for the Estimation
284  * of Power Spectra: A Method Based on Time Averaging Over Short, Modified
285  * Periodograms" IEEE Transactions on Audio and Electroacoustics,
286  * Vol. AU-15, No. 2, June 1967.
287  *
288  */
290  REAL8FrequencySeries *spectrum,
291  const REAL8TimeSeries *tseries,
292  UINT4 seglen,
293  UINT4 stride,
294  const REAL8Window *window,
295  const REAL8FFTPlan *plan
296  )
297 {
298  REAL8FrequencySeries *work; /* workspace */
299  REAL8Sequence sequence; /* working copy of input time series data */
300  REAL8TimeSeries tseriescopy; /* working copy of input time series */
301  UINT4 numseg;
302  UINT4 seg;
303  UINT4 k;
304 
305  if ( ! spectrum || ! tseries || ! plan )
307  if ( ! spectrum->data || ! tseries->data )
309  if ( tseries->deltaT <= 0.0 )
311 
312  /* construct local copy of time series */
313  sequence = *tseries->data;
314  tseriescopy = *tseries;
315  tseriescopy.data = &sequence;
316  tseriescopy.data->length = seglen;
317 
318  numseg = 1 + (tseries->data->length - seglen)/stride;
319 
320  /* consistency check for lengths: make sure that the segments cover the
321  * data record completely */
322  if ( (numseg - 1)*stride + seglen != tseries->data->length )
324  if ( spectrum->data->length != seglen/2 + 1 )
326 
327  /* clear spectrum data */
328  memset( spectrum->data->data, 0,
329  spectrum->data->length * sizeof( *spectrum->data->data ) );
330 
331  /* create frequency series data workspace */
332  work = XLALCutREAL8FrequencySeries( spectrum, 0, spectrum->data->length );
333  if( ! work )
335 
336  for ( seg = 0; seg < numseg; seg++, tseriescopy.data->data += stride )
337  {
338  /* compute the modified periodogram; clean up and exit on failure */
339  if ( XLALREAL8ModifiedPeriodogram( work, &tseriescopy, window, plan ) == XLAL_FAILURE )
340  {
343  }
344 
345  /* add the periodogram to the running sum */
346  for ( k = 0; k < spectrum->data->length; ++k )
347  spectrum->data->data[k] += work->data->data[k];
348  }
349 
350  /* set metadata */
351  spectrum->epoch = work->epoch;
352  spectrum->f0 = work->f0;
353  spectrum->deltaF = work->deltaF;
354  spectrum->sampleUnits = work->sampleUnits;
355 
356  /* divide spectrum data by the number of segments in average */
357  for ( k = 0; k < spectrum->data->length; ++k )
358  spectrum->data->data[k] /= numseg;
359 
360  /* clean up */
362 
363  return 0;
364 }
365 
366 
367 /*
368  *
369  * Median Method: use median average rather than mean.
370  *
371  */
372 
373 /**
374  * compute the median bias *
375  * See arXiv: gr-qc/0509116 appendix B for details
376  */
377 
379 {
380  const UINT4 nmax = 1000;
381  REAL8 ans = 1;
382  UINT4 n = (nn - 1)/2;
383  UINT4 i;
384 
385  if ( nn >= nmax )
386  return LAL_LN2;
387 
388  for ( i = 1; i <= n; ++i )
389  {
390  ans -= 1.0/(2*i);
391  ans += 1.0/(2*i + 1);
392  }
393 
394  return ans;
395 }
396 
397 /* cleanup temporary workspace... ignore xlal errors */
399 {
400  int saveErrno = xlalErrno;
401  UINT4 i;
402  for ( i = 0; i < n; ++i )
403  if ( work[i].data )
404  XLALDestroyREAL4Vector( work[i].data );
405  XLALFree( work );
406  xlalErrno = saveErrno;
407  return;
408 }
410 {
411  int saveErrno = xlalErrno;
412  UINT4 i;
413  for ( i = 0; i < n; ++i )
414  if ( work[i].data )
415  XLALDestroyREAL8Vector( work[i].data );
416  XLALFree( work );
417  xlalErrno = saveErrno;
418  return;
419 }
420 
421 /* comparison for floating point numbers */
422 static int compare_REAL4( const void *p1, const void *p2 )
423 {
424  REAL4 x1 = *(const REAL4 *)p1;
425  REAL4 x2 = *(const REAL4 *)p2;
426  return (x1 > x2) - (x1 < x2);
427 }
428 static int compare_REAL8( const void *p1, const void *p2 )
429 {
430  REAL8 x1 = *(const REAL8 *)p1;
431  REAL8 x2 = *(const REAL8 *)p2;
432  return (x1 > x2) - (x1 < x2);
433 }
434 
435 
436 /**
437  * Median Method: use median average rather than mean. Note: this will
438  * cause a bias if the segments overlap, i.e., if the stride is less than
439  * the segment length -- even though the median bias for Gaussian noise
440  * is accounted for -- because the segments are not independent and their
441  * correlation is non-zero.
442  *
443  */
445  REAL4FrequencySeries *spectrum,
446  const REAL4TimeSeries *tseries,
447  UINT4 seglen,
448  UINT4 stride,
449  const REAL4Window *window,
450  const REAL4FFTPlan *plan
451  )
452 {
453  REAL4FrequencySeries *work; /* array of frequency series */
454  REAL4 *bin; /* array of bin values */
455  REAL4 biasfac; /* median bias factor */
456  REAL4 normfac; /* normalization factor */
457  UINT4 reclen; /* length of entire data record */
458  UINT4 numseg;
459  UINT4 seg;
460  UINT4 k;
461 
462  if ( ! spectrum || ! tseries || ! plan )
464  if ( ! spectrum->data || ! tseries->data )
466  if ( tseries->deltaT <= 0.0 )
468 
469  reclen = tseries->data->length;
470  numseg = 1 + (reclen - seglen)/stride;
471 
472  /* consistency check for lengths: make sure that the segments cover the
473  * data record completely */
474  if ( (numseg - 1)*stride + seglen != reclen )
476  if ( spectrum->data->length != seglen/2 + 1 )
478 
479  /* create frequency series data workspaces */
480  work = XLALCalloc( numseg, sizeof( *work ) );
481  if ( ! work )
483  for ( seg = 0; seg < numseg; ++seg )
484  {
485  work[seg].data = XLALCreateREAL4Vector( spectrum->data->length );
486  if ( ! work[seg].data )
487  {
488  median_cleanup_REAL4( work, numseg ); /* cleanup */
490  }
491  }
492 
493  for ( seg = 0; seg < numseg; ++seg )
494  {
495  REAL4Vector savevec; /* save the time series data vector */
496  int code;
497 
498  /* save the time series data vector */
499  savevec = *tseries->data;
500 
501  /* set the data vector to be appropriate for the even segment */
502  tseries->data->length = seglen;
503  tseries->data->data += seg * stride;
504 
505  /* compute the modified periodogram for the even segment */
506  code = XLALREAL4ModifiedPeriodogram( work + seg, tseries, window, plan );
507 
508  /* restore the time series data vector to its original state */
509  *tseries->data = savevec;
510 
511  /* now check for failure of the XLAL routine */
512  if ( code == XLAL_FAILURE )
513  {
514  median_cleanup_REAL4( work, numseg ); /* cleanup */
516  }
517  }
518 
519  /* create array to hold a particular frequency bin data */
520  bin = XLALMalloc( numseg * sizeof( *bin ) );
521  if ( ! bin )
522  {
523  median_cleanup_REAL4( work, numseg ); /* cleanup */
525  }
526 
527  /* compute median bias factor */
528  biasfac = XLALMedianBias( numseg );
529 
530  /* normaliztion takes into account bias */
531  normfac = 1.0 / biasfac;
532 
533  /* now loop over frequency bins and compute the median-mean */
534  for ( k = 0; k < spectrum->data->length; ++k )
535  {
536  /* assign array of even segment values to bin array for this freq bin */
537  for ( seg = 0; seg < numseg; ++seg )
538  bin[seg] = work[seg].data->data[k];
539 
540  /* sort them and find median */
541  qsort( bin, numseg, sizeof( *bin ), compare_REAL4 );
542  if ( numseg % 2 ) /* odd number of evens */
543  spectrum->data->data[k] = bin[numseg/2];
544  else /* even number... take average */
545  spectrum->data->data[k] = 0.5*(bin[numseg/2-1] + bin[numseg/2]);
546 
547  /* remove median bias */
548  spectrum->data->data[k] *= normfac;
549  }
550 
551  /* set metadata */
552  spectrum->epoch = work->epoch;
553  spectrum->f0 = work->f0;
554  spectrum->deltaF = work->deltaF;
555  spectrum->sampleUnits = work->sampleUnits;
556 
557  /* free the workspace data */
558  XLALFree( bin );
559  median_cleanup_REAL4( work, numseg );
560 
561  return 0;
562 }
563 
564 /**
565  * Median Method: use median average rather than mean. Note: this will
566  * cause a bias if the segments overlap, i.e., if the stride is less than
567  * the segment length -- even though the median bias for Gaussian noise
568  * is accounted for -- because the segments are not independent and their
569  * correlation is non-zero.
570  *
571  */
573  REAL8FrequencySeries *spectrum,
574  const REAL8TimeSeries *tseries,
575  UINT4 seglen,
576  UINT4 stride,
577  const REAL8Window *window,
578  const REAL8FFTPlan *plan
579  )
580 {
581  REAL8FrequencySeries *work; /* array of frequency series */
582  REAL8 *bin; /* array of bin values */
583  REAL8 biasfac; /* median bias factor */
584  REAL8 normfac; /* normalization factor */
585  UINT4 reclen; /* length of entire data record */
586  UINT4 numseg;
587  UINT4 seg;
588  UINT4 k;
589 
590  if ( ! spectrum || ! tseries || ! plan )
592  if ( ! spectrum->data || ! tseries->data )
594  if ( tseries->deltaT <= 0.0 )
596 
597  reclen = tseries->data->length;
598  numseg = 1 + (reclen - seglen)/stride;
599 
600  /* consistency check for lengths: make sure that the segments cover the
601  * data record completely */
602  if ( (numseg - 1)*stride + seglen != reclen )
604  if ( spectrum->data->length != seglen/2 + 1 )
606 
607  /* create frequency series data workspaces */
608  work = XLALCalloc( numseg, sizeof( *work ) );
609  if ( ! work )
611  for ( seg = 0; seg < numseg; ++seg )
612  {
613  work[seg].data = XLALCreateREAL8Vector( spectrum->data->length );
614  if ( ! work[seg].data )
615  {
616  median_cleanup_REAL8( work, numseg ); /* cleanup */
618  }
619  }
620 
621  for ( seg = 0; seg < numseg; ++seg )
622  {
623  REAL8Vector savevec; /* save the time series data vector */
624  int code;
625 
626  /* save the time series data vector */
627  savevec = *tseries->data;
628 
629  /* set the data vector to be appropriate for the even segment */
630  tseries->data->length = seglen;
631  tseries->data->data += seg * stride;
632 
633  /* compute the modified periodogram for the even segment */
634  code = XLALREAL8ModifiedPeriodogram( work + seg, tseries, window, plan );
635 
636  /* restore the time series data vector to its original state */
637  *tseries->data = savevec;
638 
639  /* now check for failure of the XLAL routine */
640  if ( code == XLAL_FAILURE )
641  {
642  median_cleanup_REAL8( work, numseg ); /* cleanup */
644  }
645  }
646 
647  /* create array to hold a particular frequency bin data */
648  bin = XLALMalloc( numseg * sizeof( *bin ) );
649  if ( ! bin )
650  {
651  median_cleanup_REAL8( work, numseg ); /* cleanup */
653  }
654 
655  /* compute median bias factor */
656  biasfac = XLALMedianBias( numseg );
657 
658  /* normaliztion takes into account bias */
659  normfac = 1.0 / biasfac;
660 
661  /* now loop over frequency bins and compute the median-mean */
662  for ( k = 0; k < spectrum->data->length; ++k )
663  {
664  /* assign array of even segment values to bin array for this freq bin */
665  for ( seg = 0; seg < numseg; ++seg )
666  bin[seg] = work[seg].data->data[k];
667 
668  /* sort them and find median */
669  qsort( bin, numseg, sizeof( *bin ), compare_REAL8 );
670  if ( numseg % 2 ) /* odd number of evens */
671  spectrum->data->data[k] = bin[numseg/2];
672  else /* even number... take average */
673  spectrum->data->data[k] = 0.5*(bin[numseg/2-1] + bin[numseg/2]);
674 
675  /* remove median bias */
676  spectrum->data->data[k] *= normfac;
677  }
678 
679  /* set metadata */
680  spectrum->epoch = work->epoch;
681  spectrum->f0 = work->f0;
682  spectrum->deltaF = work->deltaF;
683  spectrum->sampleUnits = work->sampleUnits;
684 
685  /* free the workspace data */
686  XLALFree( bin );
687  median_cleanup_REAL8( work, numseg );
688 
689  return 0;
690 }
691 
692 
693 /*
694  *
695  * Median-Mean Method
696  *
697  */
698 
699 
700 /* cleanup temporary workspace... ignore xlal errors */
702 {
703  int saveErrno = xlalErrno;
704  UINT4 i;
705  for ( i = 0; i < n; ++i )
706  {
707  if ( even[i].data )
708  XLALDestroyREAL4Vector( even[i].data );
709  if ( odd[i].data )
710  XLALDestroyREAL4Vector( odd[i].data );
711  }
712  XLALFree( even );
713  XLALFree( odd );
714  xlalErrno = saveErrno;
715  return;
716 }
718 {
719  int saveErrno = xlalErrno;
720  UINT4 i;
721  for ( i = 0; i < n; ++i )
722  {
723  if ( even[i].data )
724  XLALDestroyREAL8Vector( even[i].data );
725  if ( odd[i].data )
726  XLALDestroyREAL8Vector( odd[i].data );
727  }
728  XLALFree( even );
729  XLALFree( odd );
730  xlalErrno = saveErrno;
731  return;
732 }
733 
734 /**
735  * Median-Mean Method: divide overlapping segments into "even" and "odd"
736  * segments; compute the bin-by-bin median of the "even" segments and the
737  * "odd" segments, and then take the bin-by-bin average of these two median
738  * averages.
739  *
740  */
742  REAL4FrequencySeries *spectrum,
743  const REAL4TimeSeries *tseries,
744  UINT4 seglen,
745  UINT4 stride,
746  const REAL4Window *window,
747  const REAL4FFTPlan *plan
748  )
749 {
750  REAL4FrequencySeries *even; /* array of even frequency series */
751  REAL4FrequencySeries *odd; /* array of odd frequency series */
752  REAL4 *bin; /* array of bin values */
753  REAL4 biasfac; /* median bias factor */
754  REAL4 normfac; /* normalization factor */
755  UINT4 reclen; /* length of entire data record */
756  UINT4 numseg;
757  UINT4 halfnumseg;
758  UINT4 seg;
759  UINT4 k;
760 
761  if ( ! spectrum || ! tseries || ! plan )
763  if ( ! spectrum->data || ! tseries->data )
765  if ( tseries->deltaT <= 0.0 )
767 
768  reclen = tseries->data->length;
769  numseg = 1 + (reclen - seglen)/stride;
770 
771  /* consistency check for lengths: make sure that the segments cover the
772  * data record completely */
773  if ( (numseg - 1)*stride + seglen != reclen )
775  if ( spectrum->data->length != seglen/2 + 1 )
777 
778  /* for median-mean to work, the number of segments must be even and
779  * the stride must be greater-than or equal-to half of the seglen */
780  halfnumseg = numseg/2;
781  if ( numseg%2 || stride < seglen/2 )
783 
784  /* create frequency series data workspaces */
785  even = XLALCalloc( halfnumseg, sizeof( *even ) );
786  if ( ! even )
788  odd = XLALCalloc( halfnumseg, sizeof( *odd ) );
789  if ( ! odd )
791  for ( seg = 0; seg < halfnumseg; ++seg )
792  {
793  even[seg].data = XLALCreateREAL4Vector( spectrum->data->length );
794  odd[seg].data = XLALCreateREAL4Vector( spectrum->data->length );
795  if ( ! even[seg].data || ! odd[seg].data )
796  {
797  median_mean_cleanup_REAL4( even, odd, halfnumseg ); /* cleanup */
799  }
800  }
801 
802  for ( seg = 0; seg < halfnumseg; ++seg )
803  {
804  REAL4Vector savevec; /* save the time series data vector */
805  int code;
806 
807  /* save the time series data vector */
808  savevec = *tseries->data;
809 
810  /* set the data vector to be appropriate for the even segment */
811  tseries->data->length = seglen;
812  tseries->data->data += 2 * seg * stride;
813 
814  /* compute the modified periodogram for the even segment */
815  code = XLALREAL4ModifiedPeriodogram( even + seg, tseries, window, plan );
816 
817  /* now check for failure of the XLAL routine */
818  if ( code == XLAL_FAILURE )
819  {
820  *tseries->data = savevec;
821  median_mean_cleanup_REAL4( even, odd, halfnumseg ); /* cleanup */
823  }
824 
825  /* set the data vector to be appropriate for the odd segment */
826  tseries->data->data += stride;
827 
828  /* compute the modified periodogram for the odd segment */
829  code = XLALREAL4ModifiedPeriodogram( odd + seg, tseries, window, plan );
830 
831  /* now check for failure of the XLAL routine */
832  if ( code == XLAL_FAILURE )
833  {
834  *tseries->data = savevec;
835  median_mean_cleanup_REAL4( even, odd, halfnumseg ); /* cleanup */
837  }
838 
839  /* restore the time series data vector to its original state */
840  *tseries->data = savevec;
841  }
842 
843  /* create array to hold a particular frequency bin data */
844  bin = XLALMalloc( halfnumseg * sizeof( *bin ) );
845  if ( ! bin )
846  {
847  median_mean_cleanup_REAL4( even, odd, halfnumseg ); /* cleanup */
849  }
850 
851  /* compute median bias factor */
852  biasfac = XLALMedianBias( halfnumseg );
853 
854  /* normaliztion takes into account bias and a factor of two from averaging
855  * the even and the odd */
856  normfac = 1.0 / ( 2.0 * biasfac );
857 
858  /* now loop over frequency bins and compute the median-mean */
859  for ( k = 0; k < spectrum->data->length; ++k )
860  {
861  REAL4 evenmedian;
862  REAL4 oddmedian;
863 
864  /* assign array of even segment values to bin array for this freq bin */
865  for ( seg = 0; seg < halfnumseg; ++seg )
866  bin[seg] = even[seg].data->data[k];
867 
868  /* sort them and find median */
869  qsort( bin, halfnumseg, sizeof( *bin ), compare_REAL4 );
870  if ( halfnumseg % 2 ) /* odd number of evens */
871  evenmedian = bin[halfnumseg/2];
872  else /* even number... take average */
873  evenmedian = 0.5*(bin[halfnumseg/2-1] + bin[halfnumseg/2]);
874 
875  /* assign array of odd segment values to bin array for this freq bin */
876  for ( seg = 0; seg < halfnumseg; ++seg )
877  bin[seg] = odd[seg].data->data[k];
878 
879  /* sort them and find median */
880  qsort( bin, halfnumseg, sizeof( *bin ), compare_REAL4 );
881  if ( halfnumseg % 2 ) /* odd number of odds */
882  oddmedian = bin[halfnumseg/2];
883  else /* even number... take average */
884  oddmedian = 0.5*(bin[halfnumseg/2-1] + bin[halfnumseg/2]);
885 
886  /* spectrum for this bin is the mean of the medians */
887  spectrum->data->data[k] = normfac * (evenmedian + oddmedian);
888  }
889 
890  /* set metadata */
891  spectrum->epoch = even->epoch;
892  spectrum->f0 = even->f0;
893  spectrum->deltaF = even->deltaF;
894  spectrum->sampleUnits = even->sampleUnits;
895 
896  /* free the workspace data */
897  XLALFree( bin );
898  median_mean_cleanup_REAL4( even, odd, halfnumseg );
899 
900  return 0;
901 }
902 
903 /**
904  * Median-Mean Method: divide overlapping segments into "even" and "odd"
905  * segments; compute the bin-by-bin median of the "even" segments and the
906  * "odd" segments, and then take the bin-by-bin average of these two median
907  * averages.
908  *
909  */
911  REAL8FrequencySeries *spectrum,
912  const REAL8TimeSeries *tseries,
913  UINT4 seglen,
914  UINT4 stride,
915  const REAL8Window *window,
916  const REAL8FFTPlan *plan
917  )
918 {
919  REAL8FrequencySeries *even; /* array of even frequency series */
920  REAL8FrequencySeries *odd; /* array of odd frequency series */
921  REAL8 *bin; /* array of bin values */
922  REAL8 biasfac; /* median bias factor */
923  REAL8 normfac; /* normalization factor */
924  UINT4 reclen; /* length of entire data record */
925  UINT4 numseg;
926  UINT4 halfnumseg;
927  UINT4 seg;
928  UINT4 k;
929 
930  if ( ! spectrum || ! tseries || ! plan )
932  if ( ! spectrum->data || ! tseries->data )
934  if ( tseries->deltaT <= 0.0 )
936 
937  reclen = tseries->data->length;
938  numseg = 1 + (reclen - seglen)/stride;
939 
940  /* consistency check for lengths: make sure that the segments cover the
941  * data record completely */
942  if ( (numseg - 1)*stride + seglen != reclen )
944  if ( spectrum->data->length != seglen/2 + 1 )
946 
947  /* for median-mean to work, the number of segments must be even and
948  * the stride must be greater-than or equal-to half of the seglen */
949  halfnumseg = numseg/2;
950  if ( numseg%2 || stride < seglen/2 )
952 
953  /* create frequency series data workspaces */
954  even = XLALCalloc( halfnumseg, sizeof( *even ) );
955  if ( ! even )
957  odd = XLALCalloc( halfnumseg, sizeof( *odd ) );
958  if ( ! odd )
960  for ( seg = 0; seg < halfnumseg; ++seg )
961  {
962  even[seg].data = XLALCreateREAL8Vector( spectrum->data->length );
963  odd[seg].data = XLALCreateREAL8Vector( spectrum->data->length );
964  if ( ! even[seg].data || ! odd[seg].data )
965  {
966  median_mean_cleanup_REAL8( even, odd, halfnumseg ); /* cleanup */
968  }
969  }
970 
971  for ( seg = 0; seg < halfnumseg; ++seg )
972  {
973  REAL8Vector savevec; /* save the time series data vector */
974  int code;
975 
976  /* save the time series data vector */
977  savevec = *tseries->data;
978 
979  /* set the data vector to be appropriate for the even segment */
980  tseries->data->length = seglen;
981  tseries->data->data += 2 * seg * stride;
982 
983  /* compute the modified periodogram for the even segment */
984  code = XLALREAL8ModifiedPeriodogram( even + seg, tseries, window, plan );
985 
986  /* now check for failure of the XLAL routine */
987  if ( code == XLAL_FAILURE )
988  {
989  *tseries->data = savevec;
990  median_mean_cleanup_REAL8( even, odd, halfnumseg ); /* cleanup */
992  }
993 
994  /* set the data vector to be appropriate for the odd segment */
995  tseries->data->data += stride;
996 
997  /* compute the modified periodogram for the odd segment */
998  code = XLALREAL8ModifiedPeriodogram( odd + seg, tseries, window, plan );
999 
1000  /* now check for failure of the XLAL routine */
1001  if ( code == XLAL_FAILURE )
1002  {
1003  *tseries->data = savevec;
1004  median_mean_cleanup_REAL8( even, odd, halfnumseg ); /* cleanup */
1006  }
1007 
1008  /* restore the time series data vector to its original state */
1009  *tseries->data = savevec;
1010  }
1011 
1012  /* create array to hold a particular frequency bin data */
1013  bin = XLALMalloc( halfnumseg * sizeof( *bin ) );
1014  if ( ! bin )
1015  {
1016  median_mean_cleanup_REAL8( even, odd, halfnumseg ); /* cleanup */
1018  }
1019 
1020  /* compute median bias factor */
1021  biasfac = XLALMedianBias( halfnumseg );
1022 
1023  /* normaliztion takes into account bias and a factor of two from averaging
1024  * the even and the odd */
1025  normfac = 1.0 / ( 2.0 * biasfac );
1026 
1027  /* now loop over frequency bins and compute the median-mean */
1028  for ( k = 0; k < spectrum->data->length; ++k )
1029  {
1030  REAL8 evenmedian;
1031  REAL8 oddmedian;
1032 
1033  /* assign array of even segment values to bin array for this freq bin */
1034  for ( seg = 0; seg < halfnumseg; ++seg )
1035  bin[seg] = even[seg].data->data[k];
1036 
1037  /* sort them and find median */
1038  qsort( bin, halfnumseg, sizeof( *bin ), compare_REAL8 );
1039  if ( halfnumseg % 2 ) /* odd number of evens */
1040  evenmedian = bin[halfnumseg/2];
1041  else /* even number... take average */
1042  evenmedian = 0.5*(bin[halfnumseg/2-1] + bin[halfnumseg/2]);
1043 
1044  /* assign array of odd segment values to bin array for this freq bin */
1045  for ( seg = 0; seg < halfnumseg; ++seg )
1046  bin[seg] = odd[seg].data->data[k];
1047 
1048  /* sort them and find median */
1049  qsort( bin, halfnumseg, sizeof( *bin ), compare_REAL8 );
1050  if ( halfnumseg % 2 ) /* odd number of odds */
1051  oddmedian = bin[halfnumseg/2];
1052  else /* even number... take average */
1053  oddmedian = 0.5*(bin[halfnumseg/2-1] + bin[halfnumseg/2]);
1054 
1055  /* spectrum for this bin is the mean of the medians */
1056  spectrum->data->data[k] = normfac * (evenmedian + oddmedian);
1057  }
1058 
1059  /* set metadata */
1060  spectrum->epoch = even->epoch;
1061  spectrum->f0 = even->f0;
1062  spectrum->deltaF = even->deltaF;
1063  spectrum->sampleUnits = even->sampleUnits;
1064 
1065  /* free the workspace data */
1066  XLALFree( bin );
1067  median_mean_cleanup_REAL8( even, odd, halfnumseg );
1068 
1069  return 0;
1070 }
1071 
1072 /** UNDOCUMENTED */
1074  REAL4FrequencySeries *spectrum,
1075  REAL4 lowfreq,
1076  UINT4 seglen,
1077  UINT4 trunclen,
1078  REAL4FFTPlan *fwdplan,
1079  REAL4FFTPlan *revplan
1080  )
1081 {
1082  UINT4 cut;
1083  UINT4 k;
1084 
1085  if ( ! spectrum )
1087  if ( ! spectrum->data )
1089  if ( spectrum->deltaF <= 0.0 )
1091  if ( lowfreq < 0 )
1093  if ( ! seglen || spectrum->data->length != seglen/2 + 1 )
1095  if ( trunclen && ! revplan )
1097 
1098  cut = lowfreq / spectrum->deltaF;
1099  if ( cut < 1 ) /* need to get rid of DC at least */
1100  cut = 1;
1101 
1102  if ( trunclen ) /* truncate while inverting */
1103  {
1104  COMPLEX8Vector *vtilde; /* frequency-domain vector workspace */
1105  REAL4Vector *vector; /* time-domain vector workspace */
1106  REAL4 normfac;
1107 
1108  vector = XLALCreateREAL4Vector( seglen );
1109  vtilde = XLALCreateCOMPLEX8Vector( seglen / 2 + 1 );
1110 
1111  /* clear the low frequency components */
1112  memset( vtilde->data, 0, cut * sizeof( *vtilde->data ) );
1113 
1114  /* stick the root-inv-spectrum into the complex frequency-domain vector */
1115  for ( k = cut; k < vtilde->length - 1; ++k )
1116  {
1117  if ( spectrum->data->data[k] )
1118  vtilde->data[k] = 1.0 / sqrt( spectrum->data->data[k] ); /* purely real */
1119  else
1120  vtilde->data[k] = 0.0;
1121  }
1122 
1123  /* no Nyquist */
1124  vtilde->data[vtilde->length - 1] = 0.0;
1125 
1126  /* construct time-domain version of root-inv-spectrum */
1127  XLALREAL4ReverseFFT( vector, vtilde, revplan );
1128 
1129  /* now truncate it: zero time from trunclen/2 to length - trunclen/2 */
1130  memset( vector->data + trunclen/2, 0,
1131  (vector->length - trunclen) * sizeof( *vector->data ) );
1132 
1133  /* reconstruct spectrum */
1134  XLALREAL4PowerSpectrum( spectrum->data, vector, fwdplan );
1135 
1136  /* clear the low frequency components... again, and Nyquist too */
1137  memset( spectrum->data->data, 0, cut * sizeof( *spectrum->data->data ) );
1138  spectrum->data->data[spectrum->data->length - 1] = 0.0;
1139 
1140  /* rescale spectrum: 0.5 to undo factor of two from REAL4PowerSpectrum */
1141  /* seglen^2 for the reverse fft (squared because power spectrum) */
1142  /* Note: cast seglen to real so that seglen*seglen is a real rather */
1143  /* than a four-byte integer which might overflow... */
1144  normfac = 0.5 / ( ((REAL4)seglen) * ((REAL4)seglen) );
1145  for ( k = cut; k < spectrum->data->length - 1; ++k )
1146  spectrum->data->data[k] *= normfac;
1147 
1148  /* cleanup workspace */
1149  XLALDestroyCOMPLEX8Vector( vtilde );
1150  XLALDestroyREAL4Vector( vector );
1151  }
1152  else /* otherwise just invert the spectrum */
1153  {
1154  /* clear the low frequency components */
1155  memset( spectrum->data->data, 0, cut * sizeof( *spectrum->data->data ) );
1156 
1157  /* invert the high frequency (non-Nyquist) components */
1158  for ( k = cut; k < spectrum->data->length - 1; ++k )
1159  {
1160  if ( spectrum->data->data[k] )
1161  spectrum->data->data[k] = 1.0 / spectrum->data->data[k];
1162  else
1163  spectrum->data->data[k] = 0;
1164  }
1165 
1166  /* zero Nyquist */
1167  spectrum->data->data[spectrum->data->length - 1] = 0.0;
1168  }
1169 
1170  /* now correct the units */
1171  XLALUnitRaiseINT2( &spectrum->sampleUnits, &spectrum->sampleUnits, -1 );
1172 
1173  return 0;
1174 }
1175 
1176 /** UNDOCUMENTED */
1178  REAL8FrequencySeries *spectrum,
1179  REAL8 lowfreq,
1180  UINT4 seglen,
1181  UINT4 trunclen,
1182  REAL8FFTPlan *fwdplan,
1183  REAL8FFTPlan *revplan
1184  )
1185 {
1186  UINT4 cut;
1187  UINT4 k;
1188 
1189  if ( ! spectrum )
1191  if ( ! spectrum->data )
1193  if ( spectrum->deltaF <= 0.0 )
1195  if ( lowfreq < 0 )
1197  if ( ! seglen || spectrum->data->length != seglen/2 + 1 )
1199  if ( trunclen && ! revplan )
1201 
1202  cut = lowfreq / spectrum->deltaF;
1203  if ( cut < 1 ) /* need to get rid of DC at least */
1204  cut = 1;
1205 
1206  if ( trunclen ) /* truncate while inverting */
1207  {
1208  COMPLEX16Vector *vtilde; /* frequency-domain vector workspace */
1209  REAL8Vector *vector; /* time-domain vector workspace */
1210  REAL8 normfac;
1211 
1212  vector = XLALCreateREAL8Vector( seglen );
1213  vtilde = XLALCreateCOMPLEX16Vector( seglen / 2 + 1 );
1214 
1215  /* clear the low frequency components */
1216  memset( vtilde->data, 0, cut * sizeof( *vtilde->data ) );
1217 
1218  /* stick the root-inv-spectrum into the complex frequency-domain vector */
1219  for ( k = cut; k < vtilde->length - 1; ++k )
1220  {
1221  if ( spectrum->data->data[k] )
1222  vtilde->data[k] = 1.0 / sqrt( spectrum->data->data[k] ); /* purely real */
1223  else
1224  vtilde->data[k] = 0.0;
1225  }
1226 
1227  /* no Nyquist */
1228  vtilde->data[vtilde->length - 1] = 0.0;
1229 
1230  /* construct time-domain version of root-inv-spectrum */
1231  XLALREAL8ReverseFFT( vector, vtilde, revplan );
1232 
1233  /* now truncate it: zero time from trunclen/2 to length - trunclen/2 */
1234  memset( vector->data + trunclen/2, 0,
1235  (vector->length - trunclen) * sizeof( *vector->data ) );
1236 
1237  /* reconstruct spectrum */
1238  XLALREAL8PowerSpectrum( spectrum->data, vector, fwdplan );
1239 
1240  /* clear the low frequency components... again, and Nyquist too */
1241  memset( spectrum->data->data, 0, cut * sizeof( *spectrum->data->data ) );
1242  spectrum->data->data[spectrum->data->length - 1] = 0.0;
1243 
1244  /* rescale spectrum: 0.5 to undo factor of two from REAL8PowerSpectrum */
1245  /* seglen^2 for the reverse fft (squared because power spectrum) */
1246  /* Note: cast seglen to real so that seglen*seglen is a real rather */
1247  /* than a four-byte integer which might overflow... */
1248  normfac = 0.5 / ( ((REAL8)seglen) * ((REAL8)seglen) );
1249  for ( k = cut; k < spectrum->data->length - 1; ++k )
1250  spectrum->data->data[k] *= normfac;
1251 
1252  /* cleanup workspace */
1253  XLALDestroyCOMPLEX16Vector( vtilde );
1254  XLALDestroyREAL8Vector( vector );
1255  }
1256  else /* otherwise just invert the spectrum */
1257  {
1258  /* clear the low frequency components */
1259  memset( spectrum->data->data, 0, cut * sizeof( *spectrum->data->data ) );
1260 
1261  /* invert the high frequency (non-Nyquist) components */
1262  for ( k = cut; k < spectrum->data->length - 1; ++k )
1263  {
1264  if ( spectrum->data->data[k] )
1265  spectrum->data->data[k] = 1.0 / spectrum->data->data[k];
1266  else
1267  spectrum->data->data[k] = 0.0;
1268  }
1269 
1270  /* zero Nyquist */
1271  spectrum->data->data[spectrum->data->length - 1] = 0.0;
1272  }
1273 
1274  /* now correct the units */
1275  XLALUnitRaiseINT2( &spectrum->sampleUnits, &spectrum->sampleUnits, -1 );
1276 
1277  return 0;
1278 }
1279 
1280 
1281 /**
1282  * Normalize a COMPLEX8 frequency series to a REAL4 average PSD. If the
1283  * frequency series is the Fourier transform of (coloured) Gaussian random
1284  * noise, and the PSD is of the same noise, and both have been computed
1285  * according to the LAL technical specifications (LIGO-T010095-00-Z), then
1286  * the output frequency series' bins will be complex Gaussian random
1287  * variables with mean squares of 1 (the real and imaginary components,
1288  * individually, have variances of 1/2). Fourier transforms computed by
1289  * XLALREAL4ForwardFFT(), and PSDs computed by
1290  * XLALREAL4AverageSpectrumMedian() and friends conform to the LAL
1291  * technical specifications.
1292  *
1293  * PSDs computed from high- or low-passed data can include 0s at one or the
1294  * other end of the spectrum, and these would normally result in
1295  * divide-by-zero errors. This routine avoids PSD divide-by-zero errors by
1296  * ignoring those frequency bins, setting them to zero in the frequency
1297  * series. The justification is that if the PSD is 0 then there must be
1298  * nothing in that frequency bin to normalize anyway.
1299  *
1300  * The input PSD is allowed to span a larger frequency band than the input
1301  * frequency series, but the frequency resolutions of the two must be the
1302  * same. The frequency series is modified in place. The return value is
1303  * the input frequency series' address on success, or NULL on error. When
1304  * an error occurs, the contents of the input frequency series are
1305  * undefined.
1306  *
1307  * Reviewed: f16747afde9f92ede1c25b0b537d21160b0389a3 on 2014-08-10 by J. Creighton, B. Sathyaprakash, K. Cannon.
1308  */
1310 {
1311  COMPLEX8 *fdata = fseries->data->data;
1312  REAL4 *pdata = psd->data->data;
1313  double norm = 2 * psd->deltaF;
1314  LALUnit unit; /* scratch space */
1315  unsigned i; /* fseries index */
1316  unsigned j; /* psd index */
1317 
1318  if((psd->deltaF != fseries->deltaF) ||
1319  (fseries->f0 < psd->f0))
1320  /* resolution mismatch, or PSD does not span fseries at low end */
1322 
1323  j = (fseries->f0 - psd->f0) / psd->deltaF;
1324  if(j * psd->deltaF + psd->f0 != fseries->f0)
1325  /* fseries does not start on an integer PSD sample */
1327  if(j + fseries->data->length > psd->data->length)
1328  /* PSD does not span fseries at high end */
1330 
1331  for(i = 0; i < fseries->data->length; i++, j++)
1332  {
1333  if(pdata[j])
1334  fdata[i] *= sqrt(norm / pdata[j]);
1335  else
1336  /* PSD has a 0 in it, treat as a zero in the filter */
1337  fdata[i] = 0.0;
1338  }
1339 
1340  /* zero the DC and Nyquist components for safety */
1341  if(fseries->data->length)
1342  {
1343  if(fseries->f0 == 0)
1344  fdata[0] = 0.0;
1345  fdata[fseries->data->length - 1] = 0.0;
1346  }
1347 
1348  /* update the units of fseries. norm has units of Hz */
1349  XLALUnitDivide(&unit, &psd->sampleUnits, &lalHertzUnit);
1350  XLALUnitSqrt(&unit, &unit);
1351  XLALUnitDivide(&fseries->sampleUnits, &fseries->sampleUnits, &unit);
1352 
1353  return fseries;
1354 }
1355 
1356 
1357 /**
1358  * Double-precision version of XLALWhitenCOMPLEX8FrequencySeries().
1359  *
1360  * Reviewed: f16747afde9f92ede1c25b0b537d21160b0389a3 on 2014-08-10 by J. Creighton, B. Sathyaprakash, K. Cannon.
1361  */
1363 {
1364  COMPLEX16 *fdata = fseries->data->data;
1365  REAL8 *pdata = psd->data->data;
1366  double norm = 2 * psd->deltaF;
1367  LALUnit unit; /* scratch space */
1368  unsigned i; /* fseries index */
1369  unsigned j; /* psd index */
1370 
1371  if((psd->deltaF != fseries->deltaF) ||
1372  (fseries->f0 < psd->f0))
1373  /* resolution mismatch, or PSD does not span fseries at low end */
1375 
1376  j = (fseries->f0 - psd->f0) / psd->deltaF;
1377  if(j * psd->deltaF + psd->f0 != fseries->f0)
1378  /* fseries does not start on an integer PSD sample */
1380  if(j + fseries->data->length > psd->data->length)
1381  /* PSD does not span fseries at high end */
1383 
1384  for(i = 0; i < fseries->data->length; i++, j++)
1385  {
1386  if(pdata[j])
1387  fdata[i] *= sqrt(norm / pdata[j]);
1388  else
1389  /* PSD has a 0 in it, treat as a zero in the filter */
1390  fdata[i] = 0.0;
1391  }
1392 
1393  /* zero the DC and Nyquist components for safety */
1394  if(fseries->data->length)
1395  {
1396  if(fseries->f0 == 0)
1397  fdata[0] = 0.0;
1398  fdata[fseries->data->length - 1] = 0.0;
1399  }
1400 
1401  /* update the units of fseries. norm has units of Hz */
1402  XLALUnitDivide(&unit, &psd->sampleUnits, &lalHertzUnit);
1403  XLALUnitSqrt(&unit, &unit);
1404  XLALUnitDivide(&fseries->sampleUnits, &fseries->sampleUnits, &unit);
1405 
1406  return fseries;
1407 }
1408 
1409 
1410 /*
1411  * PSD regression functions.
1412  */
1413 
1414 
1415 /**
1416  * Geometric mean median bias factor.
1417  *
1418  * For a sample of nn 2-degree of freedom \f$\chi^{2}\f$-distributed random
1419  * variables, compute and return the natural logarithm of the ratio of the
1420  * sample median to the geometric mean. See Section III.A. of
1421  * LIGO-T0900462 for the derivation.
1422  */
1424 {
1425  return log(XLALMedianBias(nn)) - nn * (gsl_sf_lngamma(1.0 / nn) - log(nn));
1426 }
1427 
1428 /**
1429  * Allocate and initialize a LALPSDRegressor object.
1430  *
1431  * The LALPSDRegressor object implements an algorithm for iteratively
1432  * estimating a moving mean power spectral density from a sequence of
1433  * frequency series objects containing Fourier-transformed snippets of a
1434  * time series. The algorithm is designed to converge quickly even with
1435  * high dynamic range data and to be stable against periods of
1436  * non-Gaussianity ("glitches"). These things are achieved, respectively,
1437  * by using a running geometric mean instead of an arithmetic mean and
1438  * updating the running mean from a median of recent frequency series
1439  * objects. Obtaining a correctly normalized PSD from these steps requires
1440  * the application of certain correction factors that have been derived
1441  * from the assumption that the underlying statistics are Gaussian. If the
1442  * noise is not Gaussian then the resulting PSD estimate will be biased ---
1443  * it will differ from the true arithmetic mean of the spectrum by some
1444  * unknown factor.
1445  *
1446  * Two parameters control the behaviour of the LALPSDRegressor object: the
1447  * average_samples and median_samples. When new data is supplied, the
1448  * median of the most recent median_samples frequency series objects is
1449  * used to update the moving average. For each update, the new PSD
1450  * estimate is computed from 1 part the new data and (average_samples - 1)
1451  * parts the old data. This makes the regressor implement a moving
1452  * exponentially-distributed weighted average, with average_samples setting
1453  * the scale of the distribution.
1454  *
1455  * Changes to the PSD must persist for approximately 1/2 the number of
1456  * median samples before they begin to be reflected in the running average,
1457  * and so the median_samples parameter controls the robustness of the PSD
1458  * estimate to glitches. Increasing the median_samples also increases the
1459  * time required for the PSD to converge.
1460  *
1461  * Increasing the average_samples parameter decreases the sample noise in
1462  * the resulting PSD by averaging over more frequency series samples but
1463  * increases the time required to converge to a new PSD should the spectrum
1464  * changes.
1465  */
1466 
1467 LALPSDRegressor *XLALPSDRegressorNew(unsigned average_samples, unsigned median_samples)
1468 {
1469  LALPSDRegressor *new;
1470  REAL8Sequence **history;
1471 
1472  /* require the number of samples used for the average and the number of
1473  * snapshots used for the median to both be positive, and the number of
1474  * snapshots used for the median to be odd */
1475  if(average_samples < 1 || median_samples < 1 || !(median_samples & 1))
1477 
1478  new = XLALMalloc(sizeof(*new));
1479  history = XLALCalloc(median_samples, sizeof(*history));
1480  if(!new || !history)
1481  {
1482  XLALFree(new);
1483  XLALFree(history);
1485  }
1486 
1487  new->average_samples = average_samples;
1488  new->median_samples = median_samples;
1489  new->n_samples = 0;
1490  new->history = history;
1491  new->mean_square = NULL;
1492 
1493  return new;
1494 }
1495 
1496 /**
1497  * Reset a LALPSDRegressor object to the newly-allocated state. This
1498  * resets the internal frequency series parameters.
1499  */
1501 {
1502  if(r->history)
1503  {
1504  unsigned i;
1505  for(i = 0; i < r->median_samples; i++)
1506  if(r->history[i])
1507  {
1508  XLALDestroyREAL8Sequence(r->history[i]);
1509  r->history[i] = NULL;
1510  }
1511  }
1512  XLALDestroyREAL8FrequencySeries(r->mean_square);
1513  r->mean_square = NULL;
1514  r->n_samples = 0;
1515 }
1516 
1517 /**
1518  * Free all memory associated with a LALPSDRegressor object. The object
1519  * must not be used again after calling this function.
1520  */
1522 {
1523  if(r)
1524  {
1526  XLALFree(r->history);
1527  r->history = NULL;
1528  }
1529  free(r);
1530 }
1531 
1532 /**
1533  * Change the average_samples of a LALPSDRegressor object.
1534  */
1535 int XLALPSDRegressorSetAverageSamples(LALPSDRegressor *r, unsigned average_samples)
1536 {
1537  /* require the number of samples used for the average to be positive */
1538  if(average_samples < 1)
1540  r->average_samples = average_samples;
1541  return 0;
1542 }
1543 
1544 /**
1545  * Return the average_samples of a LALPSDRegressor object.
1546  */
1548 {
1549  return r->average_samples;
1550 }
1551 
1552 /**
1553  * Return the number of frequency series samples over which the running
1554  * average has been computed. This initially counts the number of
1555  * frequency series updates the LALPSDRegressor has received, until the
1556  * count reaches average_samples and then it stops increasing.
1557  */
1559 {
1560  return r->n_samples;
1561 }
1562 
1563 /**
1564  * Change the median_samples of a LALPSDRegressor object. Any existing
1565  * median history is preserved to the extent possible: if median_samples
1566  * is being reduced then the most recent samples are preserved.
1567  */
1569 {
1570  unsigned i;
1571  REAL8Sequence **history;
1572 
1573  /* require the number of samples used for median to be positive and odd */
1574  if(median_samples < 1 || !(median_samples & 1))
1576 
1577  /* if the history buffer is being shrunk, delete discarded samples before
1578  * resize */
1579  for(i = median_samples; i < r->median_samples; i++)
1580  {
1581  XLALDestroyREAL8Sequence(r->history[i]);
1582  r->history[i] = NULL;
1583  }
1584 
1585  /* resize history buffer */
1586  history = XLALRealloc(r->history, median_samples * sizeof(*history));
1587  if(!history)
1589  r->history = history;
1590 
1591  /* if there is already at least one sample in the buffer, and the history
1592  * is being enlarged, allocate space for the new samples and initialize
1593  * them to the value of the oldest sample in the history */
1594  if(r->history[0])
1595  {
1596  for(i = r->median_samples; i < median_samples; i++)
1597  {
1598  r->history[i] = XLALCopyREAL8Sequence(r->history[r->median_samples - 1]);
1599  if(!r->history[i])
1601  }
1602  }
1603 
1604  r->median_samples = median_samples;
1605 
1606  return 0;
1607 }
1608 
1609 /**
1610  * Return the median_samples of a LALPSDRegressor object.
1611  */
1613 {
1614  return r->median_samples;
1615 }
1616 
1617 /**
1618  * Update a LALPSDRegressor object from a frequency series object. The
1619  * frequency series is assumed to have been computed from real-valued data
1620  * and to contain only non-negative frequency components. The contents of
1621  * the frequency series object are digested, and this code does not take
1622  * ownership of if; the calling code is responsible for freeing the
1623  * frequency series object when it is done with it.
1624  *
1625  * The properties of the first frequency series object added to the
1626  * regressor set certain internal parameters like the frequency resolution
1627  * and Nyquist frequency of the PSD. Once those parameters have been set,
1628  * the only mechanism by which they can be changed is to call
1629  * XLALPSDRegressorReset() and reset the regressor to the newly-allocated
1630  * state.
1631  */
1633 {
1634  double *bin_history;
1635  unsigned history_length;
1636  double median_bias;
1637  unsigned i;
1638 
1639  /* is this the first sample? */
1640 
1641  if(!r->n_samples)
1642  {
1643  /* create space for mean square series */
1644 
1645  XLALDestroyREAL8FrequencySeries(r->mean_square);
1646  r->mean_square = XLALCreateREAL8FrequencySeries(sample->name, &sample->epoch, sample->f0, sample->deltaF, &sample->sampleUnits, sample->data->length);
1647  if(!r->mean_square)
1649 
1650  /* create space for median history samples */
1651 
1652  for(i = 0; i < r->median_samples; i++)
1653  {
1654  XLALDestroyREAL8Sequence(r->history[i]);
1655  r->history[i] = XLALCreateREAL8Sequence(sample->data->length);
1656  if(!r->history[i])
1657  {
1658  while(i--)
1659  {
1660  XLALDestroyREAL8Sequence(r->history[i]);
1661  r->history[i] = NULL;
1662  }
1663  XLALDestroyREAL8FrequencySeries(r->mean_square);
1664  r->mean_square = NULL;
1666  }
1667  }
1668 
1669  /* store the square magnitudes in the first history series, and the
1670  * logarithms of those in the mean square series */
1671 
1672  XLALUnitSquare(&r->mean_square->sampleUnits, &r->mean_square->sampleUnits);
1673  for(i = 0; i < sample->data->length; i++)
1674  r->mean_square->data->data[i] = log(r->history[0]->data[i] = cabs2(sample->data->data[i]));
1675 
1676  /* set n_samples to 1 */
1677 
1678  r->n_samples = 1;
1679 
1680  /* done */
1681 
1682  return 0;
1683  }
1684 
1685  /* FIXME: also check units */
1686  if((sample->f0 != r->mean_square->f0) || (sample->deltaF != r->mean_square->deltaF) || (sample->data->length != r->mean_square->data->length))
1687  {
1688  XLALPrintError("%s(): input parameter mismatch", __func__);
1690  }
1691 
1692  /* if we get here, the regressor has already been initialized and the
1693  * input sample has the correct parameters. we need to update the
1694  * regressor */
1695 
1696  /* cyclically permute pointers in history buffer up one */
1697 
1698  {
1699  REAL8Sequence *oldest = r->history[r->median_samples - 1];
1700  memmove(r->history + 1, r->history, (r->median_samples - 1) * sizeof(*r->history));
1701  r->history[0] = oldest;
1702  }
1703 
1704  /* copy data from current sample into history buffer */
1705 
1706  for(i = 0; i < sample->data->length; i++)
1707  r->history[0]->data[i] = cabs2(sample->data->data[i]);
1708 
1709  /* bump the number of samples that have been recorded */
1710 
1711  if(r->n_samples < r->average_samples)
1712  r->n_samples++;
1713  else
1714  /* just in case */
1715  r->n_samples = r->average_samples;
1716 
1717  /* create array to hold one frequency bin's history */
1718 
1719  history_length = r->n_samples < r->median_samples ? r->n_samples : r->median_samples;
1720  bin_history = XLALMalloc(history_length * sizeof(*bin_history));
1721  if(!bin_history)
1723 
1724  /* compute the logarithm of the median bias factor */
1725 
1726  median_bias = XLALLogMedianBiasGeometric(history_length);
1727 
1728  /* update the geometric mean square using the median in each frequency
1729  * bin */
1730 
1731  for(i = 0; i < r->mean_square->data->length; i++)
1732  {
1733  unsigned j;
1734  double log_bin_median;
1735 
1736  /* retrieve the history for this bin */
1737 
1738  for(j = 0; j < history_length; j++)
1739  bin_history[j] = r->history[j]->data[i];
1740 
1741  /* sort (to find the median) */
1742 
1743  qsort(bin_history, history_length, sizeof(*bin_history), compare_REAL8);
1744  log_bin_median = log(bin_history[history_length / 2]);
1745 
1746  /* use logarithm of median to update geometric mean.
1747  *
1748  * the samples are proportional to a random variable that is
1749  * \chi^{2}-distributed with two-degrees of freedom. the median of
1750  * samples that are \chi^{2} distributed is not equal to the mean of
1751  * those samples. the median differs from the mean by a factor whose
1752  * logarithm is stored in median_bias, and we need to adjust the median
1753  * by that factor before using it to update the average. our samples
1754  * are not \chi^{2} distributed, but they are proportional to a
1755  * variable that is so the correction factor is the same.
1756  */
1757 
1758  if(isinf(log_bin_median) && log_bin_median < 0)
1759  r->mean_square->data->data[i] = r->mean_square->data->data[i] + log((r->n_samples - 1.0) / r->n_samples);
1760  else
1761  r->mean_square->data->data[i] = (r->mean_square->data->data[i] * (r->n_samples - 1) + log_bin_median - median_bias) / r->n_samples;
1762  }
1763 
1764  XLALFree(bin_history);
1765  return 0;
1766 }
1767 
1768 /**
1769  * Retrieve a copy of the current PSD estimate. The return value is a
1770  * newly-allocated frequency series object. The calling code is
1771  * responsible for freeing it when it no longer needs it.
1772  */
1774 {
1775  REAL8FrequencySeries *psd;
1776  REAL8 *pdata;
1777  /* arbitrary constant to make result comply with LAL definition of PSD */
1778  double lal_normalization_constant = 2 * r->mean_square->deltaF;
1779  unsigned i;
1780 
1781  /* initialized yet? */
1782 
1783  if(!r->n_samples) {
1784  XLALPrintError("%s: not initialized", __func__);
1786  }
1787 
1788  /* start with a copy of the mean square */
1789 
1790  /* FIXME: adjust the name */
1791  psd = XLALCutREAL8FrequencySeries(r->mean_square, 0, r->mean_square->data->length);
1792  if(!psd)
1794  pdata = psd->data->data;
1795 
1796  /*
1797  * PSD = variance * (arbitrary normalization), variance = <|z|^2> -
1798  * |<z>|^2, <z> = 0
1799  *
1800  * note that the mean_square array stores the logarithms of the geometric
1801  * means of the square magnitudes of the bins. exponentiating the values
1802  * in the array recovers the geometric mean of the square magnitudes, but
1803  * the PSD should be obtained from the arithmetic mean of the square
1804  * magnitudes. the geometric mean of samples that are \chi^{2}
1805  * distributed with 2 degrees of freedom is 2 / exp(gamma), while the
1806  * arithmetic mean is 2. therefore, in addition to the arbitrary LAL
1807  * normalization constant, we multiply the geometric mean by exp(gamma)
1808  * to recover an estimate of the arithmetic mean.
1809  */
1810 
1811  for(i = 0; i < psd->data->length; i++)
1812  pdata[i] = exp(pdata[i] + LAL_GAMMA) * lal_normalization_constant;
1813 
1814  /* normalization constant has units of Hz */
1815 
1817 
1818  /* done */
1819 
1820  return psd;
1821 }
1822 
1823 /**
1824  * Initialize the running average of a LALPSDRegressor to the given PSD,
1825  * and record it has having counted as weight samples of the running
1826  * history. The median history is erased, and repopulated with values
1827  * derived from the PSD. The PSD data is copied, this function does not
1828  * take ownership of the PSD object, the calling code is responsible for
1829  * freeing it when it no longer needs it.
1830  */
1832 {
1833  /* arbitrary constant to remove from LAL definition of PSD */
1834  double lal_normalization_constant = 2 * psd->deltaF;
1835  unsigned i;
1836 
1837  if(!r->n_samples)
1838  {
1839  /* initialize the mean square array to a copy of the PSD */
1840  XLALDestroyREAL8FrequencySeries(r->mean_square);
1841  r->mean_square = XLALCutREAL8FrequencySeries(psd, 0, psd->data->length);
1842  if(!r->mean_square)
1844 
1845  /* normalization constant to be removed has units of Hz */
1846  XLALUnitDivide(&r->mean_square->sampleUnits, &r->mean_square->sampleUnits, &lalHertzUnit);
1847 
1848  /* create median history buffer */
1849 
1850  for(i = 0; i < r->median_samples; i++)
1851  {
1852  XLALDestroyREAL8Sequence(r->history[i]);
1853  r->history[i] = XLALCreateREAL8Sequence(r->mean_square->data->length);
1854 
1855  /* failure? */
1856  if(!r->history[i])
1857  {
1858  while(--i)
1859  {
1860  XLALDestroyREAL8Sequence(r->history[i]);
1861  r->history[i] = NULL;
1862  }
1863  XLALDestroyREAL8FrequencySeries(r->mean_square);
1864  r->mean_square = NULL;
1866  }
1867  }
1868  }
1869  /* FIXME: also check units */
1870  else if((psd->f0 != r->mean_square->f0) || (psd->deltaF != r->mean_square->deltaF) || (psd->data->length != r->mean_square->data->length))
1871  {
1872  XLALPrintError("%s(): input parameter mismatch", __func__);
1874  }
1875  else
1876  {
1877  /* copy the PSD data into the mean square */
1878  memcpy(r->mean_square->data->data, psd->data->data, psd->data->length * sizeof(*psd->data->data));
1879  }
1880 
1881  /* remove the LAL normalization factor from the PSD, recovering the value
1882  * of the arithmetic mean square for each bin */
1883  for(i = 0; i < r->mean_square->data->length; i++)
1884  r->mean_square->data->data[i] /= lal_normalization_constant;
1885 
1886  /* copy the arithmetic mean square data into the median history buffer */
1887  for(i = 0; i < r->median_samples; i++)
1888  memcpy(r->history[i]->data, r->mean_square->data->data, r->mean_square->data->length * sizeof(*r->mean_square->data->data));
1889 
1890  /* store the logarithms of the geometric mean squares */
1891  for(i = 0; i < r->mean_square->data->length; i++)
1892  r->mean_square->data->data[i] = log(r->mean_square->data->data[i]) - LAL_GAMMA;
1893 
1894  /* set the n_samples paramter */
1895  r->n_samples = weight <= r->average_samples ? weight : r->average_samples;
1896 
1897  return 0;
1898 }
1899 
1900 
1901 /**
1902  * Compute the two-point spectral correlation function for a whitened
1903  * frequency series from the window applied to the original time series.
1904  *
1905  * If \f$x_{j}\f$ is a stationary process then the components of its
1906  * Fourier transform, \f$X_{k}\f$, are independent random variables, and
1907  * let their mean square be \f$\langle|X_{k}|^{2}\rangle = 1\f$. If
1908  * \f$x_{j}\f$ is multiplied by the window function \f$w_{j}\f$ then it is
1909  * no longer stationary and the components of its Fourier transform are no
1910  * longer independent. Their correlations are
1911  *
1912  * \f[\langle X_{k} X*_{k'}\rangle\f]
1913  *
1914  * and depend only on \f$|k - k'|\f$.
1915  *
1916  * Given the window function \f$w_{j}\f$, this function computes and
1917  * returns a sequence containing \f$\langle X_{k} X*_{k'}\rangle\f$. The
1918  * sequence's indices are \f$|k - k'|\f$. A straight-forward normalization
1919  * factor can be applied to convert this for use with a sequence
1920  * \f$x_{j}\f$ whose Fourier transform does not have bins with equal mean
1921  * square.
1922  *
1923  * The FFT plan argument must be a forward plan (time to frequency) whose
1924  * length equals that of the window. If the window has length N the return
1925  * value is the address of a newly-allocated sequence of length floor(N/2 +
1926  * 1), or NULL on error. This function assumes the window is symmetric
1927  * about its midpoint (is an even function of the sample index if the
1928  * midpoint is index 0), so that its Fourier transform is real-valued.
1929  */
1931  const REAL8Window *window, /**< window function used to prevent leakage when measuring PSD. see XLALCreateHannREAL8Window() and friends. */
1932  const REAL8FFTPlan *plan /**< forward FFT plan. see XLALCreateREAL8FFTPlan(). */
1933 )
1934 {
1935  REAL8Sequence *wsquared;
1936  COMPLEX16Sequence *tilde_wsquared;
1937  REAL8Sequence *correlation;
1938  unsigned i;
1939 
1940  if(window->sumofsquares <= 0)
1942 
1943  /*
1944  * Create a sequence to hold the normalized square of the window and its
1945  * Fourier transform.
1946  */
1947 
1948  wsquared = XLALCopyREAL8Sequence(window->data);
1949  tilde_wsquared = XLALCreateCOMPLEX16Sequence(window->data->length / 2 + 1);
1950  if(!wsquared || !tilde_wsquared)
1951  {
1952  XLALDestroyREAL8Sequence(wsquared);
1953  XLALDestroyCOMPLEX16Sequence(tilde_wsquared);
1955  }
1956 
1957  /*
1958  * Compute the normalized square of the window.
1959  */
1960 
1961  for(i = 0; i < wsquared->length; i++)
1962  wsquared->data[i] *= wsquared->data[i] / window->sumofsquares;
1963 
1964  /*
1965  * Fourier transform.
1966  */
1967 
1968  if(XLALREAL8ForwardFFT(tilde_wsquared, wsquared, plan))
1969  {
1970  XLALDestroyREAL8Sequence(wsquared);
1971  XLALDestroyCOMPLEX16Sequence(tilde_wsquared);
1973  }
1974  XLALDestroyREAL8Sequence(wsquared);
1975 
1976  /*
1977  * Create space to hold the two-point correlation function.
1978  */
1979 
1980  correlation = XLALCreateREAL8Sequence(tilde_wsquared->length);
1981  if(!correlation)
1982  {
1983  XLALDestroyCOMPLEX16Sequence(tilde_wsquared);
1985  }
1986 
1987  /*
1988  * Extract real components from Fourier transform.
1989  */
1990 
1991  for(i = 0; i < correlation->length; i++)
1992  correlation->data[i] = creal(tilde_wsquared->data[i]);
1993  XLALDestroyCOMPLEX16Sequence(tilde_wsquared);
1994 
1995  /*
1996  * Done.
1997  */
1998 
1999  return correlation;
2000 }
static COMPLEX16 cabs2(COMPLEX16 z)
static int compare_REAL8(const void *p1, const void *p2)
static void median_cleanup_REAL8(REAL8FrequencySeries *work, UINT4 n)
static void median_cleanup_REAL4(REAL4FrequencySeries *work, UINT4 n)
static int compare_REAL4(const void *p1, const void *p2)
static void median_mean_cleanup_REAL4(REAL4FrequencySeries *even, REAL4FrequencySeries *odd, UINT4 n)
static void median_mean_cleanup_REAL8(REAL8FrequencySeries *even, REAL8FrequencySeries *odd, UINT4 n)
int XLALREAL4PowerSpectrum(REAL4Vector *spec, const REAL4Vector *data, const REAL4FFTPlan *plan)
Definition: CudaRealFFT.c:301
int XLALPrintError(const char *fmt,...)
Definition: XLALError.c:68
REAL8FrequencySeries * XLALCreateREAL8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL4FrequencySeries(REAL4FrequencySeries *series)
REAL4FrequencySeries * XLALCutREAL4FrequencySeries(const REAL4FrequencySeries *series, size_t first, size_t length)
REAL8FrequencySeries * XLALCutREAL8FrequencySeries(const REAL8FrequencySeries *series, size_t first, size_t length)
void XLALDestroyREAL8FrequencySeries(REAL8FrequencySeries *series)
#define LAL_LN2
natural log of 2, ln(2)
Definition: LALConstants.h:172
#define LAL_GAMMA
Euler-Mascheroni constant, gamma.
Definition: LALConstants.h:176
double complex COMPLEX16
Double-precision floating-point complex number (16 bytes total)
double REAL8
Double precision real floating-point number (8 bytes).
uint32_t UINT4
Four-byte unsigned integer.
float complex COMPLEX8
Single-precision floating-point complex number (8 bytes total)
float REAL4
Single precision real floating-point number (4 bytes).
#define XLALMalloc(n)
Definition: LALMalloc.h:44
#define XLALCalloc(m, n)
Definition: LALMalloc.h:45
#define XLALFree(p)
Definition: LALMalloc.h:47
#define XLALRealloc(p, n)
Definition: LALMalloc.h:46
static const INT4 r
Definition: Random.c:82
int XLALREAL8ForwardFFT(COMPLEX16Vector *output, const REAL8Vector *input, const REAL8FFTPlan *plan)
Performs a forward FFT of REAL8 data.
Definition: CudaRealFFT.c:471
int XLALREAL8ReverseFFT(REAL8Vector *output, const COMPLEX16Vector *input, const REAL8FFTPlan *plan)
Performs a reverse FFT of REAL8 data.
Definition: CudaRealFFT.c:518
int XLALREAL8PowerSpectrum(REAL8Vector *spec, const REAL8Vector *data, const REAL8FFTPlan *plan)
Computes the power spectrum of REAL8 data.
Definition: CudaRealFFT.c:589
int XLALREAL4ReverseFFT(REAL4Vector *output, const COMPLEX8Vector *input, const REAL4FFTPlan *plan)
Performs a reverse FFT of REAL4 data.
Definition: CudaRealFFT.c:198
REAL4Sequence * XLALCutREAL4Sequence(REAL4Sequence *sequence, size_t first, size_t length)
REAL8Sequence * XLALCutREAL8Sequence(REAL8Sequence *sequence, size_t first, size_t length)
REAL8Sequence * XLALCopyREAL8Sequence(REAL8Sequence *sequence)
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
void XLALDestroyREAL4Sequence(REAL4Sequence *sequence)
void XLALDestroyCOMPLEX16Sequence(COMPLEX16Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
COMPLEX16Sequence * XLALCreateCOMPLEX16Sequence(size_t length)
unsigned XLALPSDRegressorGetAverageSamples(const LALPSDRegressor *r)
Return the average_samples of a LALPSDRegressor object.
void XLALPSDRegressorReset(LALPSDRegressor *r)
Reset a LALPSDRegressor object to the newly-allocated state.
LALPSDRegressor * XLALPSDRegressorNew(unsigned average_samples, unsigned median_samples)
Allocate and initialize a LALPSDRegressor object.
REAL8 XLALMedianBias(UINT4 nn)
compute the median bias * See arXiv: gr-qc/0509116 appendix B for details
int XLALREAL4ModifiedPeriodogram(REAL4FrequencySeries *periodogram, const REAL4TimeSeries *tseries, const REAL4Window *window, const REAL4FFTPlan *plan)
Compute a "modified periodogram," i.e., the power spectrum of a windowed time series.
int XLALPSDRegressorAdd(LALPSDRegressor *r, const COMPLEX16FrequencySeries *sample)
Update a LALPSDRegressor object from a frequency series object.
REAL8FrequencySeries * XLALPSDRegressorGetPSD(const LALPSDRegressor *r)
Retrieve a copy of the current PSD estimate.
REAL8Sequence * XLALREAL8WindowTwoPointSpectralCorrelation(const REAL8Window *window, const REAL8FFTPlan *plan)
Compute the two-point spectral correlation function for a whitened frequency series from the window a...
int XLALREAL8AverageSpectrumWelch(REAL8FrequencySeries *spectrum, const REAL8TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL8Window *window, const REAL8FFTPlan *plan)
Use Welch's method to compute the average power spectrum of a time series.
int XLALREAL4AverageSpectrumMedian(REAL4FrequencySeries *spectrum, const REAL4TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL4Window *window, const REAL4FFTPlan *plan)
Median Method: use median average rather than mean.
void XLALPSDRegressorFree(LALPSDRegressor *r)
Free all memory associated with a LALPSDRegressor object.
int XLALREAL8AverageSpectrumMedian(REAL8FrequencySeries *spectrum, const REAL8TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL8Window *window, const REAL8FFTPlan *plan)
Median Method: use median average rather than mean.
int XLALPSDRegressorSetAverageSamples(LALPSDRegressor *r, unsigned average_samples)
Change the average_samples of a LALPSDRegressor object.
unsigned XLALPSDRegressorGetMedianSamples(const LALPSDRegressor *r)
Return the median_samples of a LALPSDRegressor object.
int XLALPSDRegressorSetPSD(LALPSDRegressor *r, const REAL8FrequencySeries *psd, unsigned weight)
Initialize the running average of a LALPSDRegressor to the given PSD, and record it has having counte...
int XLALPSDRegressorSetMedianSamples(LALPSDRegressor *r, unsigned median_samples)
Change the median_samples of a LALPSDRegressor object.
COMPLEX16FrequencySeries * XLALWhitenCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *fseries, const REAL8FrequencySeries *psd)
Double-precision version of XLALWhitenCOMPLEX8FrequencySeries().
int XLALREAL8ModifiedPeriodogram(REAL8FrequencySeries *periodogram, const REAL8TimeSeries *tseries, const REAL8Window *window, const REAL8FFTPlan *plan)
Compute a "modified periodogram," i.e., the power spectrum of a windowed time series.
unsigned XLALPSDRegressorGetNSamples(const LALPSDRegressor *r)
Return the number of frequency series samples over which the running average has been computed.
REAL8 XLALLogMedianBiasGeometric(UINT4 nn)
Geometric mean median bias factor.
COMPLEX8FrequencySeries * XLALWhitenCOMPLEX8FrequencySeries(COMPLEX8FrequencySeries *fseries, const REAL4FrequencySeries *psd)
Normalize a COMPLEX8 frequency series to a REAL4 average PSD.
int XLALREAL8SpectrumInvertTruncate(REAL8FrequencySeries *spectrum, REAL8 lowfreq, UINT4 seglen, UINT4 trunclen, REAL8FFTPlan *fwdplan, REAL8FFTPlan *revplan)
UNDOCUMENTED.
int XLALREAL4AverageSpectrumWelch(REAL4FrequencySeries *spectrum, const REAL4TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL4Window *window, const REAL4FFTPlan *plan)
Use Welch's method to compute the average power spectrum of a time series.
int XLALREAL4AverageSpectrumMedianMean(REAL4FrequencySeries *spectrum, const REAL4TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL4Window *window, const REAL4FFTPlan *plan)
Median-Mean Method: divide overlapping segments into "even" and "odd" segments; compute the bin-by-bi...
int XLALREAL8AverageSpectrumMedianMean(REAL8FrequencySeries *spectrum, const REAL8TimeSeries *tseries, UINT4 seglen, UINT4 stride, const REAL8Window *window, const REAL8FFTPlan *plan)
Median-Mean Method: divide overlapping segments into "even" and "odd" segments; compute the bin-by-bi...
int XLALREAL4SpectrumInvertTruncate(REAL4FrequencySeries *spectrum, REAL4 lowfreq, UINT4 seglen, UINT4 trunclen, REAL4FFTPlan *fwdplan, REAL4FFTPlan *revplan)
UNDOCUMENTED.
const LALUnit lalSecondUnit
second [s]
Definition: UnitDefs.c:162
const LALUnit lalHertzUnit
Hertz [Hz].
Definition: UnitDefs.c:171
LALUnit * XLALUnitDivide(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
UNDOCUMENTED.
Definition: UnitMultiply.c:108
LALUnit * XLALUnitMultiply(LALUnit *output, const LALUnit *unit1, const LALUnit *unit2)
This function multiplies together the LALUnit structures *(input->unitOne) and *(input->unitTwo),...
Definition: UnitMultiply.c:64
LALUnit * XLALUnitRaiseINT2(LALUnit *output, const LALUnit *input, INT2 power)
Raises a LALUnit structure to an integer power power.
Definition: UnitRaise.c:106
LALUnit * XLALUnitSquare(LALUnit *output, const LALUnit *input)
Produces the square of a LALUnit structure.
Definition: UnitRaise.c:120
LALUnit * XLALUnitSqrt(LALUnit *output, const LALUnit *input)
Produces the square-root of a LALUnit structure.
Definition: UnitRaise.c:133
COMPLEX16Vector * XLALCreateCOMPLEX16Vector(UINT4 length)
void XLALDestroyREAL4Vector(REAL4Vector *vector)
REAL4Vector * XLALCreateREAL4Vector(UINT4 length)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
void XLALDestroyCOMPLEX16Vector(COMPLEX16Vector *vector)
COMPLEX8Vector * XLALCreateCOMPLEX8Vector(UINT4 length)
void XLALDestroyCOMPLEX8Vector(COMPLEX8Vector *vector)
REAL4Sequence * XLALUnitaryWindowREAL4Sequence(REAL4Sequence *sequence, const REAL4Window *window)
Single-precision version of XLALUnitaryWindowREAL8Sequence().
Definition: Window.c:304
REAL8Sequence * XLALUnitaryWindowREAL8Sequence(REAL8Sequence *sequence, const REAL8Window *window)
Multiply a REAL8Sequence in-place by a REAL8Window with a normalization that preserves the variance o...
Definition: Window.c:264
#define XLAL_ERROR_NULL(...)
Macro to invoke a failure from a XLAL routine returning a pointer.
Definition: XLALError.h:713
#define xlalErrno
Modifiable lvalue containing the XLAL error number.
Definition: XLALError.h:571
#define XLAL_ERROR(...)
Macro to invoke a failure from a XLAL routine returning an integer.
Definition: XLALError.h:700
@ XLAL_EBADLEN
Inconsistent or invalid length.
Definition: XLALError.h:419
@ XLAL_ENOMEM
Memory allocation error.
Definition: XLALError.h:407
@ XLAL_EDATA
Invalid data.
Definition: XLALError.h:427
@ XLAL_EFAULT
Invalid pointer.
Definition: XLALError.h:408
@ XLAL_EFUNC
Internal function call failed bit: "or" this with existing error number.
Definition: XLALError.h:462
@ XLAL_EDOM
Input domain error.
Definition: XLALError.h:410
@ XLAL_EINVAL
Invalid argument.
Definition: XLALError.h:409
@ XLAL_FAILURE
Failure return value (not an error number)
Definition: XLALError.h:402
See DATATYPE-FrequencySeries types for documentation.
Definition: LALDatatypes.h:909
CHAR name[LALNameLength]
Definition: LALDatatypes.h:910
COMPLEX16Sequence * data
Definition: LALDatatypes.h:915
Vector of type COMPLEX16, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:172
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:176
COMPLEX16 * data
Pointer to the data array.
Definition: LALDatatypes.h:177
See DATATYPE-FrequencySeries types for documentation.
Definition: LALDatatypes.h:899
COMPLEX8Sequence * data
Definition: LALDatatypes.h:905
Vector of type COMPLEX8, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:163
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:167
COMPLEX8 * data
Pointer to the data array.
Definition: LALDatatypes.h:168
UNDOCUMENTED.
Definition: TimeFreqFFT.h:180
This structure stores units in the mksA system (plus Kelvin, Strain, and ADC Count).
Definition: LALDatatypes.h:498
See DATATYPE-FrequencySeries types for documentation.
Definition: LALDatatypes.h:879
REAL4Sequence * data
Definition: LALDatatypes.h:885
Time series of REAL4 data, see DATATYPE-TimeSeries types for more details.
Definition: LALDatatypes.h:570
REAL4Sequence * data
The sequence of sampled data.
Definition: LALDatatypes.h:576
LALUnit sampleUnits
The physical units of the quantity being sampled.
Definition: LALDatatypes.h:575
REAL8 deltaT
The time step between samples of the time series in seconds.
Definition: LALDatatypes.h:573
LIGOTimeGPS epoch
The start time of the time series.
Definition: LALDatatypes.h:572
REAL8 f0
The heterodyning frequency, in Hertz (zero if not heterodyned).
Definition: LALDatatypes.h:574
Vector of type REAL4, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:145
REAL4 * data
Pointer to the data array.
Definition: LALDatatypes.h:150
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:149
Structure for storing REAL4 window function data, providing storage for a sequence of samples as well...
Definition: Window.h:243
See DATATYPE-FrequencySeries types for documentation.
Definition: LALDatatypes.h:889
REAL8Sequence * data
Definition: LALDatatypes.h:895
Time series of REAL8 data, see DATATYPE-TimeSeries types for more details.
Definition: LALDatatypes.h:580
REAL8Sequence * data
The sequence of sampled data.
Definition: LALDatatypes.h:586
LALUnit sampleUnits
The physical units of the quantity being sampled.
Definition: LALDatatypes.h:585
REAL8 f0
The heterodyning frequency, in Hertz (zero if not heterodyned).
Definition: LALDatatypes.h:584
REAL8 deltaT
The time step between samples of the time series in seconds.
Definition: LALDatatypes.h:583
LIGOTimeGPS epoch
The start time of the time series.
Definition: LALDatatypes.h:582
Vector of type REAL8, see DATATYPE-Vector types for more details.
Definition: LALDatatypes.h:154
REAL8 * data
Pointer to the data array.
Definition: LALDatatypes.h:159
UINT4 length
Number of elements in array.
Definition: LALDatatypes.h:158
Structure for storing REAL8 window function data, providing storage for a sequence of samples as well...
Definition: Window.h:254
REAL8Sequence * data
The window function samples.
Definition: Window.h:255
REAL8 sumofsquares
The sum of the squares of the window function samples.
Definition: Window.h:256