Loading [MathJax]/extensions/TeX/AMSsymbols.js
LAL 7.7.0.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
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 {
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 )
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 */
422static 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}
428static 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 */
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
1467LALPSDRegressor *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 */
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{
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)
REAL4FrequencySeries * XLALCutREAL4FrequencySeries(const REAL4FrequencySeries *series, size_t first, size_t length)
REAL8FrequencySeries * XLALCutREAL8FrequencySeries(const REAL8FrequencySeries *series, size_t first, size_t length)
void XLALDestroyREAL4FrequencySeries(REAL4FrequencySeries *series)
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)
COMPLEX16Sequence * XLALCreateCOMPLEX16Sequence(size_t length)
void XLALDestroyREAL8Sequence(REAL8Sequence *sequence)
void XLALDestroyREAL4Sequence(REAL4Sequence *sequence)
void XLALDestroyCOMPLEX16Sequence(COMPLEX16Sequence *sequence)
REAL8Sequence * XLALCopyREAL8Sequence(REAL8Sequence *sequence)
REAL8Sequence * XLALCreateREAL8Sequence(size_t length)
REAL8Sequence * XLALCutREAL8Sequence(REAL8Sequence *sequence, size_t first, size_t length)
unsigned XLALPSDRegressorGetAverageSamples(const LALPSDRegressor *r)
Return the average_samples of a LALPSDRegressor object.
REAL8Sequence * XLALREAL8WindowTwoPointSpectralCorrelation(const REAL8Window *window, const REAL8FFTPlan *plan)
Compute the two-point spectral correlation function for a whitened frequency series from the window a...
void XLALPSDRegressorReset(LALPSDRegressor *r)
Reset a LALPSDRegressor object to the newly-allocated state.
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.
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.
COMPLEX8FrequencySeries * XLALWhitenCOMPLEX8FrequencySeries(COMPLEX8FrequencySeries *fseries, const REAL4FrequencySeries *psd)
Normalize a COMPLEX8 frequency series to a REAL4 average PSD.
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...
REAL8FrequencySeries * XLALPSDRegressorGetPSD(const LALPSDRegressor *r)
Retrieve a copy of the current PSD estimate.
int XLALPSDRegressorSetMedianSamples(LALPSDRegressor *r, unsigned median_samples)
Change the median_samples of a LALPSDRegressor object.
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.
COMPLEX16FrequencySeries * XLALWhitenCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *fseries, const REAL8FrequencySeries *psd)
Double-precision version of XLALWhitenCOMPLEX8FrequencySeries().
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.
LALPSDRegressor * XLALPSDRegressorNew(unsigned average_samples, unsigned median_samples)
Allocate and initialize a LALPSDRegressor object.
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 * XLALUnitSqrt(LALUnit *output, const LALUnit *input)
Produces the square-root of a LALUnit structure.
Definition: UnitRaise.c:133
LALUnit * XLALUnitSquare(LALUnit *output, const LALUnit *input)
Produces the square of a LALUnit structure.
Definition: UnitRaise.c:120
REAL4Vector * XLALCreateREAL4Vector(UINT4 length)
void XLALDestroyREAL4Vector(REAL4Vector *vector)
COMPLEX8Vector * XLALCreateCOMPLEX8Vector(UINT4 length)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
COMPLEX16Vector * XLALCreateCOMPLEX16Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
void XLALDestroyCOMPLEX16Vector(COMPLEX16Vector *vector)
void XLALDestroyCOMPLEX8Vector(COMPLEX8Vector *vector)
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
REAL4Sequence * XLALUnitaryWindowREAL4Sequence(REAL4Sequence *sequence, const REAL4Window *window)
Single-precision version of XLALUnitaryWindowREAL8Sequence().
Definition: Window.c:304
#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