LALPulsar  6.1.0.1-89842e6
LFTandTSutils.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 Reinhard Prix
3  * Copyright (C) 2009 Chris Messenger, Reinhard Prix, Pinkesh Patel, Xavier Siemens, Holger Pletsch
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with with program; see the file COPYING. If not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  * MA 02110-1301 USA
19  */
20 
21 #include "config.h"
22 
23 /* System includes */
24 #include <stdio.h>
25 
26 /* LAL-includes */
27 #include <lal/LFTandTSutils.h>
28 #include <lal/SFTfileIO.h>
29 #include <lal/LogPrintf.h>
30 #include <lal/TimeSeries.h>
31 #include <lal/ComplexFFT.h>
32 #include <lal/ComputeFstat.h>
33 #include <lal/SinCosLUT.h>
34 #include <lal/Factorial.h>
35 #include <lal/Window.h>
36 
37 /*---------- DEFINES ----------*/
38 #define MYMAX(x,y) ( (x) > (y) ? (x) : (y) )
39 #define MYMIN(x,y) ( (x) < (y) ? (x) : (y) )
40 #define SQ(x) ((x)*(x))
41 
42 #define FINITE_OR_ONE(x) (((x) != 0) ? (x) : 1.0)
43 #define cRELERR(x,y) ( cabsf( (x) - (y) ) / FINITE_OR_ONE( 0.5 * (cabsf(x) + cabsf(y)) ) )
44 #define fRELERR(x,y) ( fabsf( (x) - (y) ) / FINITE_OR_ONE( 0.5 * (fabsf(x) + fabsf(y)) ) )
45 #define OOTWOPI (1.0 / LAL_TWOPI) // 1/2pi
46 #define OOPI (1.0 / LAL_PI) // 1/pi
47 #define LD_SMALL4 (2.0e-4) // "small" number for REAL4: taken from Demod()
48 /*---------- Global variables ----------*/
50 
51 /* ---------- local prototypes ---------- */
52 
53 /*---------- empty initializers ---------- */
54 
55 /* ---------- function definitions ---------- */
56 
57 /// \addtogroup LFTandTSutils_h
58 /// @{
59 
60 /**
61  * Turn the given multi-IFO SFTvectors into one long Fourier transform (LFT) over the total observation time
62  */
63 SFTtype *
64 XLALSFTVectorToLFT( SFTVector *sfts, /**< input SFT vector (gets modified!) */
65  REAL8 upsampling /**< upsampling factor >= 1 */
66  )
67 {
68  XLAL_CHECK_NULL( ( sfts != NULL ) && ( sfts->length > 0 ), XLAL_EINVAL );
69  XLAL_CHECK_NULL( upsampling >= 1, XLAL_EDOM, "Upsampling factor (%f) must be >= 1 \n", upsampling );
70 
71  // ----- some useful SFT infos
72  SFTtype *firstSFT = &( sfts->data[0] );
73  UINT4 numBinsSFT = firstSFT->data->length;
74  REAL8 dfSFT = firstSFT->deltaF;
75  REAL8 Tsft = 1.0 / dfSFT;
76  REAL8 f0SFT = firstSFT->f0;
77 
78  // ----- turn input SFTs into a complex (heterodyned) timeseries
79  COMPLEX8TimeSeries *lTS;
80  XLAL_CHECK_NULL( ( lTS = XLALSFTVectorToCOMPLEX8TimeSeries( sfts ) ) != NULL, XLAL_EFUNC );
81  REAL8 dt = lTS->deltaT;
82  UINT4 numSamples0 = lTS->data->length;
83  REAL8 Tspan0 = numSamples0 * dt;
84 
85  // ---------- determine time-span of upsampled time-series
86  /* NOTE: Tspan MUST be an integer multiple of Tsft,
87  * in order for the frequency bins of the final FFT
88  * to be commensurate with the SFT bins.
89  * This is required so that fHet is an exact
90  * frequency-bin in both cases
91  */
92  UINT4 numSFTsFit = lround( ( Tspan0 * upsampling ) / Tsft );
93  REAL8 Tspan = numSFTsFit * Tsft;
94  UINT4 numSamples = lround( Tspan / dt );
95 
96  // ----- enlarge TimeSeries container for zero-padding if neccessary
97  if ( numSamples > numSamples0 ) {
98  XLAL_CHECK_NULL( ( lTS->data->data = XLALRealloc( lTS->data->data, numSamples * sizeof( lTS->data->data[0] ) ) ) != NULL, XLAL_ENOMEM );
99  lTS->data->length = numSamples;
100  memset( lTS->data->data + numSamples0, 0, ( numSamples - numSamples0 ) * sizeof( lTS->data->data[0] ) ); /* set all new time-samples to zero */
101  }
102 
103  /* translate this back into fmin for the LFT (counting down from DC==fHet) */
104  /* fHet = DC of our internal DFTs */
105  UINT4 NnegSFT = NhalfNeg( numBinsSFT );
106  REAL8 fHet = f0SFT + 1.0 * NnegSFT * dfSFT;
107 
108  UINT4 NnegLFT = NhalfNeg( numSamples );
109  REAL8 f0LFT = fHet - NnegLFT / Tspan;
110 
111  // ----- prepare output LFT ----------
112  SFTtype *outputLFT;
113  XLAL_CHECK_NULL( ( outputLFT = XLALCreateSFT( numSamples ) ) != NULL, XLAL_EFUNC );
114 
115  // prepare LFT header
116  strcpy( outputLFT->name, firstSFT->name );
117  strncat( outputLFT->name, ":long Fourier transform", sizeof( outputLFT->name ) - 1 - strlen( outputLFT->name ) );
118  outputLFT->epoch = firstSFT->epoch;
119  outputLFT->f0 = f0LFT;
120  outputLFT->deltaF = 1.0 / Tspan;
121  outputLFT->sampleUnits = firstSFT->sampleUnits;
122 
123  // ---------- FFT the long timeseries ----------
124  COMPLEX8FFTPlan *LFTplan;
125  XLAL_CHECK_NULL( ( LFTplan = XLALCreateForwardCOMPLEX8FFTPlan( numSamples, 0 ) ) != NULL, XLAL_EFUNC );
126  XLAL_CHECK_NULL( XLALCOMPLEX8VectorFFT( outputLFT->data, lTS->data, LFTplan ) == XLAL_SUCCESS, XLAL_EFUNC );
128  // apply proper normalization 'dt'
129  for ( UINT4 k = 0; k < outputLFT->data->length; k ++ ) {
130  outputLFT->data->data[k] *= dt;
131  }
132 
133  /* cleanup memory */
135  XLALDestroyCOMPLEX8FFTPlan( LFTplan );
136 
137  return outputLFT;
138 
139 } // XLALSFTVectorToLFT()
140 
141 
142 /**
143  * Turn the given SFTvector into one long time-series, properly dealing with gaps.
144  */
146 XLALSFTVectorToCOMPLEX8TimeSeries( const SFTVector *sftsIn /**< [in] SFT vector */
147  )
148 {
149  // check input sanity
150  XLAL_CHECK_NULL( ( sftsIn != NULL ) && ( sftsIn->length > 0 ), XLAL_EINVAL );
151 
152  // create a local copy of the input SFTs, as they will be locally modified!
153  SFTVector *sfts;
154  XLAL_CHECK_NULL( ( sfts = XLALDuplicateSFTVector( sftsIn ) ) != NULL, XLAL_EFUNC );
155 
156  /* define some useful shorthands */
157  UINT4 numSFTs = sfts->length;
158  SFTtype *firstSFT = &( sfts->data[0] );
159  SFTtype *lastSFT = &( sfts->data[numSFTs - 1] );
160  UINT4 numFreqBinsSFT = firstSFT->data->length;
161  REAL8 dfSFT = firstSFT->deltaF;
162  REAL8 Tsft = 1.0 / dfSFT;
163  REAL8 deltaT = Tsft / numFreqBinsSFT; // complex FFT: numSamplesSFT = numFreqBinsSFT
164  REAL8 f0SFT = firstSFT->f0;
165 
166  /* if the start and end input pointers are NOT NULL then determine start and time-span of the final long time-series */
167  LIGOTimeGPS start = firstSFT->epoch;
168  LIGOTimeGPS end = lastSFT->epoch;
169  XLALGPSAdd( &end, Tsft );
170 
171  /* determine output time span */
172  REAL8 Tspan;
174 
175  UINT4 numSamples = lround( Tspan / deltaT );
176 
177  /* determine the heterodyning frequency */
178  /* fHet = DC of our internal DFTs */
179  UINT4 NnegSFT = NhalfNeg( numFreqBinsSFT );
180  REAL8 fHet = f0SFT + 1.0 * NnegSFT * dfSFT;
181 
182  /* ----- Prepare invFFT of SFTs: compute plan for FFTW */
183  COMPLEX8FFTPlan *SFTplan;
184  XLAL_CHECK_NULL( ( SFTplan = XLALCreateReverseCOMPLEX8FFTPlan( numFreqBinsSFT, 0 ) ) != NULL, XLAL_EFUNC );
185 
186  /* ----- Prepare short time-series holding ONE invFFT of a single SFT */
188  COMPLEX8TimeSeries *sTS;
189  XLAL_CHECK_NULL( ( sTS = XLALCreateCOMPLEX8TimeSeries( "short timeseries", &epoch, 0, deltaT, &emptyLALUnit, numFreqBinsSFT ) ) != NULL, XLAL_EFUNC );
190 
191  /* ----- prepare long TimeSeries container ---------- */
192  COMPLEX8TimeSeries *lTS;
193  XLAL_CHECK_NULL( ( lTS = XLALCreateCOMPLEX8TimeSeries( firstSFT->name, &start, fHet, deltaT, &emptyLALUnit, numSamples ) ) != NULL, XLAL_EFUNC );
194  memset( lTS->data->data, 0, numSamples * sizeof( *lTS->data->data ) ); /* set all time-samples to zero (in case there are gaps) */
195 
196  /* ---------- loop over all SFTs and inverse-FFT them ---------- */
197  for ( UINT4 n = 0; n < numSFTs; n ++ ) {
198  SFTtype *thisSFT = &( sfts->data[n] );
199 
200  /* find bin in long timeseries corresponding to starttime of *this* SFT */
201  REAL8 offset_n = XLALGPSDiff( &( thisSFT->epoch ), &start );
202  UINT4 bin0_n = lround( offset_n / deltaT ); /* round to closest bin */
203 
204  REAL8 nudge_n = bin0_n * deltaT - offset_n; /* rounding error */
205  nudge_n = 1e-9 * round( nudge_n * 1e9 ); /* round to closest nanosecond */
206  /* nudge SFT into integer timestep bin if necessary */
207  XLAL_CHECK_NULL( XLALTimeShiftSFT( thisSFT, nudge_n ) == XLAL_SUCCESS, XLAL_EFUNC );
208 
209  /* determine heterodyning phase-correction for this SFT */
210  REAL8 offset = XLALGPSDiff( &thisSFT->epoch, &start ); // updated value after time-shift
211  // fHet * Tsft is an integer by construction, because fHet was chosen as a frequency-bin of the input SFTs
212  // therefore we only need the remainder (offset % Tsft)
213  REAL8 offsetEff = fmod( offset, Tsft );
214  REAL8 hetCycles = fmod( fHet * offsetEff, 1 ); // heterodyning phase-correction for this SFT
215 
216  if ( nudge_n != 0 ) {
217  XLALPrintInfo( "n = %d, offset_n = %g, nudge_n = %g, offset = %g, offsetEff = %g, hetCycles = %g\n",
218  n, offset_n, nudge_n, offset, offsetEff, hetCycles );
219  }
220 
221  REAL4 hetCorrection_re, hetCorrection_im;
222  XLAL_CHECK_NULL( XLALSinCos2PiLUT( &hetCorrection_im, &hetCorrection_re, -hetCycles ) == XLAL_SUCCESS, XLAL_EFUNC );
223  COMPLEX8 hetCorrection = crectf( hetCorrection_re, hetCorrection_im );
224 
225  /* Note: we also bundle the overall normalization of 'df' into the het-correction.
226  * This ensures that the resulting timeseries will have the correct normalization, according to
227  * x_l = invFT[sft]_l = df * sum_{k=0}^{N-1} xt_k * e^(i 2pi k l / N )
228  * where x_l is the l-th timestamp, and xt_k is the k-th frequency bin of the SFT.
229  * See the LAL-conventions on FFTs: http://www.ligo.caltech.edu/docs/T/T010095-00.pdf
230  * (the FFTw convention does not contain the factor of 'df', which is why we need to
231  * apply it ourselves)
232  *
233  */
234  hetCorrection *= dfSFT;
235 
237  XLAL_CHECK_NULL( XLALCOMPLEX8VectorFFT( sTS->data, thisSFT->data, SFTplan ) == XLAL_SUCCESS, XLAL_EFUNC );
238 
239  for ( UINT4 j = 0; j < sTS->data->length; j++ ) {
240  sTS->data->data[j] *= hetCorrection;
241  } // for j < numFreqBinsSFT
242 
243  // copy the short (shifted) heterodyned timeseries into correct location within long timeseries
244  UINT4 binsLeft = numSamples - bin0_n;
245  UINT4 copyLen = MYMIN( numFreqBinsSFT, binsLeft ); /* make sure not to write past the end of the long TS */
246  memcpy( &lTS->data->data[bin0_n], sTS->data->data, copyLen * sizeof( lTS->data->data[0] ) );
247 
248  } /* for n < numSFTs */
249 
250  // cleanup memory
251  XLALDestroySFTVector( sfts );
253  XLALDestroyCOMPLEX8FFTPlan( SFTplan );
254 
255  return lTS;
256 
257 } // XLALSFTVectorToCOMPLEX8TimeSeries()
258 
259 
260 /**
261  * Turn the given multiSFTvector into multiple long COMPLEX8TimeSeries, properly dealing with gaps.
262  * Memory allocation for the output MultiCOMPLEX8TimeSeries is done within this function.
263  *
264  */
266 XLALMultiSFTVectorToCOMPLEX8TimeSeries( const MultiSFTVector *multisfts /**< [in] multi SFT vector */
267  )
268 {
269  // check input sanity
270  XLAL_CHECK_NULL( ( multisfts != NULL ) && ( multisfts->length > 0 ), XLAL_EINVAL );
271  UINT4 numDetectors = multisfts->length;
272 
273  /* allocate memory for the output structure */
275  XLAL_CHECK_NULL( ( out = XLALMalloc( sizeof( MultiCOMPLEX8TimeSeries ) ) ) != NULL, XLAL_ENOMEM );
276  XLAL_CHECK_NULL( ( out->data = XLALMalloc( numDetectors * sizeof( COMPLEX8TimeSeries * ) ) ) != NULL, XLAL_ENOMEM );
277  out->length = numDetectors;
278 
279  /* loop over detectors */
280  for ( UINT4 X = 0; X < numDetectors; X++ ) {
281  XLAL_CHECK_NULL( ( out->data[X] = XLALSFTVectorToCOMPLEX8TimeSeries( multisfts->data[X] ) ) != NULL, XLAL_EFUNC );
282  }
283 
284  return out;
285 
286 } // XLALMultiSFTVectorToCOMPLEX8TimeSeries()
287 
288 
289 
290 /**
291  * Change frequency-bin order from fftw-convention to a 'SFT'
292  * ie. from FFTW: f[0], f[1],...f[N/2], f[-(N-1)/2], ... f[-2], f[-1]
293  * to: f[-(N-1)/2], ... f[-1], f[0], f[1], .... f[N/2]
294  */
295 int
297 {
298  XLAL_CHECK( ( X != NULL ) && ( X->length > 0 ), XLAL_EINVAL );
299 
300  UINT4 N = X -> length;
301  UINT4 Npos_and_DC = NhalfPosDC( N );
302  UINT4 Nneg = NhalfNeg( N );
303 
304  /* allocate temporary storage for swap */
305  COMPLEX8 *tmp;
306  XLAL_CHECK( ( tmp = XLALMalloc( N * sizeof( *tmp ) ) ) != NULL, XLAL_ENOMEM );
307  memcpy( tmp, X->data, N * sizeof( *tmp ) );
308 
309  /* Copy second half from FFTW: 'negative' frequencies */
310  memcpy( X->data, tmp + Npos_and_DC, Nneg * sizeof( *tmp ) );
311 
312  /* Copy first half from FFTW: 'DC + positive' frequencies */
313  memcpy( X->data + Nneg, tmp, Npos_and_DC * sizeof( *tmp ) );
314 
315  XLALFree( tmp );
316 
317  return XLAL_SUCCESS;
318 
319 } // XLALReorderFFTWtoSFT()
320 
321 /**
322  * Change frequency-bin order from 'SFT' to fftw-convention
323  * ie. from f[-(N-1)/2], ... f[-1], f[0], f[1], .... f[N/2]
324  * to FFTW: f[0], f[1],...f[N/2], f[-(N-1)/2], ... f[-2], f[-1]
325  */
326 int
328 {
329  XLAL_CHECK( ( X != NULL ) && ( X->length > 0 ), XLAL_EINVAL );
330 
331  UINT4 N = X->length;
332  UINT4 Npos_and_DC = NhalfPosDC( N );
333  UINT4 Nneg = NhalfNeg( N );
334 
335  /* allocate temporary storage for swap */
336  COMPLEX8 *tmp;
337  XLAL_CHECK( ( tmp = XLALMalloc( N * sizeof( *tmp ) ) ) != NULL, XLAL_ENOMEM );
338 
339  memcpy( tmp, X->data, N * sizeof( *tmp ) );
340 
341  /* Copy second half of FFTW: 'negative' frequencies */
342  memcpy( X->data + Npos_and_DC, tmp, Nneg * sizeof( *tmp ) );
343 
344  /* Copy first half of FFTW: 'DC + positive' frequencies */
345  memcpy( X->data, tmp + Nneg, Npos_and_DC * sizeof( *tmp ) );
346 
347  XLALFree( tmp );
348 
349  return XLAL_SUCCESS;
350 
351 } // XLALReorderSFTtoFFTW()
352 
353 /**
354  * Time-shift the given SFT by an amount of 'shift' seconds,
355  * using the frequency-domain expression
356  * \f$ \widetilde{y}(f) = \widetilde{x}(f) \, e^{i 2\pi\,f\,\tau} \f$ ,
357  * which shifts \f$ x(t) \f$ into \f$ y(t) = x(t + \tau) \f$
358  *
359  * NOTE: this <b>modifies</b> the SFT in place
360  */
361 int
362 XLALTimeShiftSFT( SFTtype *sft, /**< [in/out] SFT to time-shift */
363  REAL8 shift /**< time-shift \f$ \tau \f$ in seconds */
364  )
365 {
366  XLAL_CHECK( ( sft != NULL ) && ( sft->data != NULL ), XLAL_EINVAL );
367 
368  if ( shift == 0 ) {
369  return XLAL_SUCCESS;
370  }
371 
372  for ( UINT4 k = 0; k < sft->data->length; k++ ) {
373  REAL8 fk = sft->f0 + k * sft->deltaF; /* frequency of k-th bin */
374  REAL8 shiftCyles = shift * fk;
375  REAL4 fact_re, fact_im; /* complex phase-shift factor e^(-2pi f tau) */
376  XLAL_CHECK( XLALSinCos2PiLUT( &fact_im, &fact_re, shiftCyles ) == XLAL_SUCCESS, XLAL_EFUNC );
377 
378  COMPLEX8 fact = crectf( fact_re, fact_im );
379  sft->data->data[k] *= fact;
380 
381  } /* for k < numBins */
382 
383  /* adjust SFTs epoch to the shift */
384  XLALGPSAdd( &sft->epoch, shift );
385 
386  return XLAL_SUCCESS;
387 
388 } // XLALTimeShiftSFT()
389 
390 /**
391  * Multi-detector wrapper for XLALFrequencyShiftCOMPLEX8TimeSeries
392  * NOTE: this <b>modifies</b> the MultiCOMPLEX8Timeseries in place
393  */
394 int
395 XLALFrequencyShiftMultiCOMPLEX8TimeSeries( MultiCOMPLEX8TimeSeries *x, /**< [in/out] timeseries to time-shift */
396  const REAL8 shift /**< [in] freq-shift in Hz */
397  )
398 {
399  XLAL_CHECK( ( x != NULL ) && ( x->data != NULL ) && ( x->length > 0 ), XLAL_EINVAL );
400 
401  if ( shift == 0 ) {
402  return XLAL_SUCCESS;
403  }
404 
405  /* loop over detectors */
406  UINT4 numDetectors = x->length;
407  for ( UINT4 X = 0; X < numDetectors; X++ ) {
408  /* shift the frequency of each detector's data */
410  }
411 
412  return XLAL_SUCCESS;
413 
414 } /* XLALFrequencyShiftMultiCOMPLEX8TimeSeries() */
415 
416 
417 /**
418  * Freq-shift the given COMPLEX8Timeseries by an amount of 'shift' Hz,
419  * using the time-domain expression y(t) = x(t) * e^(-i 2pi df t),
420  * which shifts x(f) into y(f) = x(f + df)
421  *
422  * NOTE: this <b>modifies</b> the COMPLEX8TimeSeries in place
423  */
424 int
425 XLALFrequencyShiftCOMPLEX8TimeSeries( COMPLEX8TimeSeries *x, /**< [in/out] timeseries to time-shift */
426  const REAL8 shift /**< [in] freq-shift in Hz */
427  )
428 {
429  XLAL_CHECK( ( x != NULL ) && ( x->data != NULL ), XLAL_EINVAL );
430 
431  if ( shift == 0 ) {
432  return XLAL_SUCCESS;
433  }
434 
435  /* get timeseries time-step */
436  REAL8 deltat = x->deltaT;
437 
438  /* loop over COMPLEX8TimeSeries elements */
439  for ( UINT4 k = 0; k < x->data->length; k++ ) {
440  REAL8 tk = k * deltat; /* time of k-th bin */
441  REAL8 shiftCycles = shift * tk;
442  REAL4 fact_re, fact_im; /* complex phase-shift factor e^(-2pi f tau) */
443 
444  /* use a sin/cos look-up-table for speed */
445  XLAL_CHECK( XLALSinCos2PiLUT( &fact_im, &fact_re, shiftCycles ) == XLAL_SUCCESS, XLAL_EFUNC );
446  COMPLEX8 fact = crectf( fact_re, fact_im );
447 
448  x->data->data[k] *= fact;
449 
450  } /* for k < numBins */
451 
452  /* adjust timeseries heterodyne frequency to the shift */
453  x->f0 -= shift;
454 
455  return XLAL_SUCCESS;
456 
457 } /* XLALFrequencyShiftCOMPLEX8TimeSeries() */
458 
459 
460 /**
461  * Apply a spin-down correction to the complex8 timeseries
462  * using the time-domain expression y(t) = x(t) * e^(-i 2pi sum f_k * (t-tref)^(k+1)),
463  *
464  * NOTE: this <b>modifies</b> the input COMPLEX8TimeSeries in place
465  */
466 int
467 XLALSpinDownCorrectionMultiTS( MultiCOMPLEX8TimeSeries *multiTimeSeries, /**< [in/out] timeseries to time-shift */
468  const PulsarDopplerParams *doppler /**< parameter-space point to correct for */
469  )
470 {
471  // check input sanity
472  XLAL_CHECK( ( multiTimeSeries != NULL ) && ( multiTimeSeries->data != NULL ) && ( multiTimeSeries->length > 0 ), XLAL_EINVAL );
473  XLAL_CHECK( doppler != NULL, XLAL_EINVAL );
474 
475  UINT4 numDetectors = multiTimeSeries->length;
476 
477  LIGOTimeGPS *epoch = &( multiTimeSeries->data[0]->epoch );
478  UINT4 numSamples = multiTimeSeries->data[0]->data->length;
479 
480  REAL8 dt = multiTimeSeries->data[0]->deltaT;
481 
482  /* determine number of spin down's and check if sensible */
483  UINT4 nspins = PULSAR_MAX_SPINS - 1;
484  while ( ( nspins > 0 ) && ( doppler->fkdot[nspins] == 0 ) ) {
485  nspins--;
486  }
487 
488  /* apply spin derivitive correction to resampled timeseries */
489  REAL8 tk = XLALGPSDiff( epoch, &( doppler->refTime ) );
490  for ( UINT4 k = 0; k < numSamples; k++ ) {
491  REAL8 tk_pow_jp1 = tk;
492  REAL8 cycles_k = 0;
493  for ( UINT4 j = 1; j <= nspins; j++ ) {
494  tk_pow_jp1 *= tk;
495  /* compute fractional number of cycles the spin-derivitive has added since the reftime */
496  cycles_k += LAL_FACT_INV[j + 1] * doppler->fkdot[j] * tk_pow_jp1;
497  } // for j < nspins
498 
499  REAL4 cosphase, sinphase;
500  XLAL_CHECK( XLALSinCos2PiLUT( &sinphase, &cosphase, -cycles_k ) == XLAL_SUCCESS, XLAL_EFUNC );
501  COMPLEX8 em2piphase = crectf( cosphase, sinphase );
502 
503  /* loop over detectors */
504  for ( UINT4 X = 0; X < numDetectors; X++ ) {
505  multiTimeSeries->data[X]->data->data[k] *= em2piphase;
506  } // for X < numDetectors
507 
508  tk += dt;
509 
510  } // for k < numSamples
511 
512  return XLAL_SUCCESS;
513 
514 } // XLALSpinDownCorrectionMultiTS()
515 
516 
517 /* ===== Object creation/destruction functions ===== */
518 
519 /**
520  * Destroy a MultiCOMPLEX8TimeSeries structure.
521  * Note, this is "NULL-robust" in the sense that it will not crash
522  * on NULL-entries anywhere in this struct, so it can be used
523  * for failure-cleanup even on incomplete structs
524  */
525 void
527 {
528  if ( ! multiTimes ) {
529  return;
530  }
531  if ( multiTimes->data != NULL ) {
532  UINT4 numDetectors = multiTimes->length;
533  for ( UINT4 X = 0; X < numDetectors; X ++ ) {
534  XLALDestroyCOMPLEX8TimeSeries( multiTimes->data[X] );
535  } // for X < numDetectors
536  XLALFree( multiTimes->data );
537  } // if multiTimes->data
538 
539  XLALFree( multiTimes );
540 
541  return;
542 
543 } // XLALDestroyMultiCOMPLEX8TimeSeries()
544 
545 
546 /**
547  * Duplicates a MultiCOMPLEX8TimeSeries structure.
548  * Allocates memory and copies contents.
549  */
552 {
553  XLAL_CHECK_NULL( multiTimes != NULL, XLAL_EINVAL );
554  XLAL_CHECK_NULL( multiTimes->length > 0, XLAL_EINVAL );
555 
556  UINT4 numDetectors = multiTimes->length;
557 
558  // ----- prepare memory for multicomplex8timeseries container
560  XLAL_CHECK_NULL( ( out = XLALCalloc( 1, sizeof( *out ) ) ) != NULL, XLAL_ENOMEM );
561  out->length = numDetectors;
562  XLAL_CHECK_NULL( ( out->data = XLALCalloc( numDetectors, sizeof( *out->data ) ) ) != NULL, XLAL_ENOMEM );
563 
564  // ----- copy each of the numDet complex8timeseries contents
565  for ( UINT4 X = 0; X < numDetectors; X ++ ) {
566  XLAL_CHECK_NULL( ( out->data[X] = XLALDuplicateCOMPLEX8TimeSeries( multiTimes->data[X] ) ) != NULL, XLAL_EFUNC );
567  }
568 
569  return out;
570 
571 } // XLALDuplicateMultiCOMPLEX8TimeSeries()
572 
573 /**
574  * Duplicates a COMPLEX8TimeSeries structure.
575  * Allocates memory and copies contents.
576  */
579 {
580  XLAL_CHECK_NULL( ttimes != NULL, XLAL_EINVAL );
581  XLAL_CHECK_NULL( ( ttimes->data != NULL ) && ( ttimes->data->length > 0 ) && ( ttimes->data->data != NULL ), XLAL_EINVAL );
582 
584  XLAL_CHECK_NULL( ( out = XLALCalloc( 1, sizeof( *out ) ) ) != NULL, XLAL_ENOMEM );
585 
586  // copy header info [including data-pointer, will be reset]
587  memcpy( out, ttimes, sizeof( *ttimes ) );
588 
589  UINT4 numBins = ttimes->data->length;
591 
592  // copy contents of COMPLEX8 vector
593  memcpy( out->data->data, ttimes->data->data, numBins * sizeof( ttimes->data->data[0] ) );
594 
595  return out;
596 
597 } // XLALDuplicateCOMPLEX8TimeSeries()
598 
599 /**
600  * Copies a MultiCOMPLEX8TimeSeries structure, output must be allocated of identical size as input!
601  */
602 int
604  MultiCOMPLEX8TimeSeries *multiTimesIn
605  )
606 {
607  XLAL_CHECK( multiTimesIn != NULL, XLAL_EINVAL );
608  XLAL_CHECK( multiTimesOut != NULL, XLAL_EINVAL );
609 
610  UINT4 numDetectors = multiTimesIn->length;
611  XLAL_CHECK( multiTimesOut->length == numDetectors, XLAL_EINVAL );
612 
613  // ----- copy each of the numDet complex8timeseries contents
614  for ( UINT4 X = 0; X < numDetectors; X ++ ) {
615  XLAL_CHECK( XLALCopyCOMPLEX8TimeSeries( multiTimesOut->data[X], multiTimesIn->data[X] ) == XLAL_SUCCESS, XLAL_EFUNC );
616  }
617 
618  return XLAL_SUCCESS;
619 
620 } // XLALCopyMultiCOMPLEX8TimeSeries()
621 
622 /**
623  * Copies a COMPLEX8TimeSeries structure, output must be allocated of identical size as input!
624  */
625 int
627  COMPLEX8TimeSeries *ts_in
628  )
629 {
630  XLAL_CHECK( ts_out != NULL, XLAL_EINVAL );
631  XLAL_CHECK( ts_in != NULL, XLAL_EINVAL );
632 
633  UINT4 numSamples = ts_in->data->length;
634  XLAL_CHECK( ts_out->data->length == numSamples, XLAL_EINVAL );
635 
636  COMPLEX8Vector *tmp = ts_out->data;
637 
638  // copy header info [including data-pointer, will be restored]
639  ( *ts_out ) = ( *ts_in );
640  ts_out->data = tmp;
641 
642  // copy contents of COMPLEX8 vector
643  memcpy( ts_out->data->data, ts_in->data->data, numSamples * sizeof( ts_out->data->data[0] ) );
644 
645  return XLAL_SUCCESS;
646 
647 } // XLALCopyCOMPLEX8TimeSeries()
648 
649 
650 /** Compare two COMPLEX8 vectors using various different comparison metrics
651  */
652 int
653 XLALCompareCOMPLEX8Vectors( VectorComparison *result, ///< [out] return comparison results
654  const COMPLEX8Vector *x, ///< [in] first input vector
655  const COMPLEX8Vector *y, ///< [in] second input vector
656  const VectorComparison *tol ///< [in] accepted tolerances on comparisons, or NULL for no check
657  )
658 {
659  XLAL_CHECK( result != NULL, XLAL_EINVAL );
660  XLAL_CHECK( x != NULL, XLAL_EINVAL );
661  XLAL_CHECK( y != NULL, XLAL_EINVAL );
662  XLAL_CHECK( x->data != NULL, XLAL_EINVAL );
663  XLAL_CHECK( y->data != NULL, XLAL_EINVAL );
664  XLAL_CHECK( x->length > 0, XLAL_EINVAL );
665  XLAL_CHECK( x->length == y->length, XLAL_EINVAL );
666 
667  REAL8 x_L1 = 0, x_L2 = 0;
668  REAL8 x_L2sq = 0;
669  REAL8 y_L1 = 0, y_L2 = 0;
670  REAL8 y_L2sq = 0;
671  REAL8 diff_L1 = 0, diff_L2 = 0;
672  COMPLEX16 scalar = 0;
673 
674  REAL8 maxAbsx = 0, maxAbsy = 0;
675  COMPLEX8 x_atMaxAbsx = 0, y_atMaxAbsx = 0;
676  COMPLEX8 x_atMaxAbsy = 0, y_atMaxAbsy = 0;
677 
678 
679  UINT4 numSamples = x->length;
680  for ( UINT4 i = 0; i < numSamples; i ++ ) {
681  COMPLEX16 x_i = x->data[i];
682  COMPLEX16 y_i = y->data[i];
683  REAL8 xAbs2_i = x_i * conj( x_i );
684  REAL8 yAbs2_i = y_i * conj( y_i );
685  REAL8 xAbs_i = sqrt( xAbs2_i );
686  REAL8 yAbs_i = sqrt( yAbs2_i );
687  XLAL_CHECK( isfinite( xAbs_i ), XLAL_EFPINVAL, "non-finite element: x(%d) = %g + I %g\n", i, creal( x_i ), cimag( x_i ) );
688  XLAL_CHECK( isfinite( yAbs_i ), XLAL_EFPINVAL, "non-finite element: y(%d) = %g + I %g\n", i, creal( y_i ), cimag( y_i ) );
689 
690  REAL8 absdiff = cabs( x_i - y_i );
691  diff_L1 += absdiff;
692  diff_L2 += SQ( absdiff );
693 
694  x_L1 += xAbs_i;
695  y_L1 += yAbs_i;
696  x_L2sq += xAbs2_i;
697  y_L2sq += yAbs2_i;
698 
699  scalar += x_i * conj( y_i );
700 
701  if ( xAbs_i > maxAbsx ) {
702  maxAbsx = xAbs_i;
703  x_atMaxAbsx = x_i;
704  y_atMaxAbsx = y_i;
705  }
706  if ( yAbs_i > maxAbsy ) {
707  maxAbsy = yAbs_i;
708  x_atMaxAbsy = x_i;
709  y_atMaxAbsy = y_i;
710  }
711 
712  } // for i < numSamples
713 
714  // complete L2 norms by taking sqrt
715  x_L2 = sqrt( x_L2sq );
716  y_L2 = sqrt( y_L2sq );
717  diff_L2 = sqrt( diff_L2 );
718  REAL8 sinAngle2 = ( x_L2sq * y_L2sq - scalar * conj( scalar ) ) / FINITE_OR_ONE( x_L2sq * y_L2sq );
719  REAL8 angle = asin( sqrt( fabs( sinAngle2 ) ) );
720 
721  // compute and return comparison results
722  result->relErr_L1 = diff_L1 / FINITE_OR_ONE( 0.5 * ( x_L1 + y_L1 ) );
723  result->relErr_L2 = diff_L2 / FINITE_OR_ONE( 0.5 * ( x_L2 + y_L2 ) );
724  result->angleV = angle;
725 
726  result->relErr_atMaxAbsx = cRELERR( x_atMaxAbsx, y_atMaxAbsx );
727  result->relErr_atMaxAbsy = cRELERR( x_atMaxAbsy, y_atMaxAbsy );;
728 
730 
731  return XLAL_SUCCESS;
732 
733 } // XLALCompareCOMPLEX8Vectors()
734 
735 /** Compare two REAL4 vectors using various different comparison metrics
736  */
737 int
738 XLALCompareREAL4Vectors( VectorComparison *result, ///< [out] return comparison results
739  const REAL4Vector *x, ///< [in] first input vector
740  const REAL4Vector *y, ///< [in] second input vector
741  const VectorComparison *tol ///< [in] accepted tolerances on comparisons, or NULL for no check
742  )
743 {
744  XLAL_CHECK( result != NULL, XLAL_EINVAL );
745  XLAL_CHECK( x != NULL, XLAL_EINVAL );
746  XLAL_CHECK( y != NULL, XLAL_EINVAL );
747  XLAL_CHECK( x->data != NULL, XLAL_EINVAL );
748  XLAL_CHECK( y->data != NULL, XLAL_EINVAL );
749  XLAL_CHECK( x->length > 0, XLAL_EINVAL );
750  XLAL_CHECK( x->length == y->length, XLAL_EINVAL );
751 
752  REAL8 x_L1 = 0, x_L2 = 0;
753  REAL8 x_L2sq = 0;
754  REAL8 y_L1 = 0, y_L2 = 0;
755  REAL8 y_L2sq = 0;
756  REAL8 diff_L1 = 0, diff_L2 = 0;
757  REAL8 scalar = 0;
758 
759  REAL4 maxAbsx = 0, maxAbsy = 0;
760  REAL4 x_atMaxAbsx = 0, y_atMaxAbsx = 0;
761  REAL4 x_atMaxAbsy = 0, y_atMaxAbsy = 0;
762 
763  UINT4 numSamples = x->length;
764  for ( UINT4 i = 0; i < numSamples; i ++ ) {
765  REAL8 x_i = x->data[i];
766  REAL8 y_i = y->data[i];
767  XLAL_CHECK( isfinite( x_i ), XLAL_EFPINVAL, "non-finite element: x(%d) = %g\n", i, x_i );
768  XLAL_CHECK( isfinite( y_i ), XLAL_EFPINVAL, "non-finite element: y(%d) = %g\n", i, y_i );
769  REAL8 xAbs_i = fabs( x_i );
770  REAL8 yAbs_i = fabs( y_i );
771  REAL8 xAbs2_i = SQ( x_i );
772  REAL8 yAbs2_i = SQ( y_i );
773 
774  REAL8 absdiff = fabs( x_i - y_i );
775  diff_L1 += absdiff;
776  diff_L2 += SQ( absdiff );
777 
778  x_L1 += xAbs_i;
779  y_L1 += yAbs_i;
780  x_L2sq += xAbs2_i;
781  y_L2sq += yAbs2_i;
782 
783  scalar += x_i * y_i;
784 
785  if ( xAbs_i > maxAbsx ) {
786  maxAbsx = xAbs_i;
787  x_atMaxAbsx = x_i;
788  y_atMaxAbsx = y_i;
789  }
790  if ( yAbs_i > maxAbsy ) {
791  maxAbsy = yAbs_i;
792  x_atMaxAbsy = x_i;
793  y_atMaxAbsy = y_i;
794  }
795 
796  } // for i < numSamples
797 
798  // complete L2 norms by taking sqrt
799  x_L2 = sqrt( x_L2sq );
800  y_L2 = sqrt( y_L2sq );
801  diff_L2 = sqrt( diff_L2 );
802 
803  // compute and return comparison results
804  result->relErr_L1 = diff_L1 / FINITE_OR_ONE( 0.5 * ( x_L1 + y_L1 ) );
805  result->relErr_L2 = diff_L2 / FINITE_OR_ONE( 0.5 * ( x_L2 + y_L2 ) );
806  REAL8 sinAngle2 = ( x_L2sq * y_L2sq - SQ( scalar ) ) / FINITE_OR_ONE( x_L2sq * y_L2sq );
807  result->angleV = asin( sqrt( fabs( sinAngle2 ) ) );
808  result->relErr_atMaxAbsx = fRELERR( x_atMaxAbsx, y_atMaxAbsx );
809  result->relErr_atMaxAbsy = fRELERR( x_atMaxAbsy, y_atMaxAbsy );;
810 
812 
813  return XLAL_SUCCESS;
814 
815 } // XLALCompareREAL4Vectors()
816 
817 /** Check VectorComparison result against specified tolerances,
818  * to allow for standardized comparison and reporting.
819  */
820 int
821 XLALCheckVectorComparisonTolerances( const VectorComparison *result, ///< [in] comparison result from XLALCompareREAL4Vectors() or XLALCompareCOMPLEX8Vectors()
822  const VectorComparison *tol ///< [in] accepted tolerances on comparisons: if NULL=> always accept the result
823  )
824 {
825  XLAL_CHECK( result != NULL, XLAL_EINVAL );
826 
827  VectorComparison XLAL_INIT_DECL( localTol );
828  if ( tol != NULL ) {
829  localTol = ( *tol );
830  }
831 
832  const char *names[5] = { "relErr_L1", "relErr_L2", "angleV", "relErr_atMaxAbsx", "relErr_atMaxAbsy" };
833  const REAL4 results[5] = { result->relErr_L1, result->relErr_L2, result->angleV, result->relErr_atMaxAbsx, result->relErr_atMaxAbsy };
834  const REAL4 tols[5] = { localTol.relErr_L1, localTol.relErr_L2, localTol.angleV, localTol.relErr_atMaxAbsx, localTol.relErr_atMaxAbsy };
835 
836  BOOLEAN failed = 0;
837 
838  for ( size_t i = 0; i < XLAL_NUM_ELEM( names ); ++i ) {
839 
840  if ( results[i] > tols[i] ) {
841  failed = 1;
842  }
843 
844  if ( lalDebugLevel & LALINFO ) {
845  XLALPrintError( "%-16s = %.1e (%.1e): %s\n", names[i], results[i], tols[i],
846  ( results[i] > tols[i] ) ? "FAILED. Exceeded tolerance." : "OK." );
847  }
848 
849  }
850 
851  if ( failed ) {
852  XLAL_ERROR( XLAL_ETOL, "FAILED. Exceeded at least one tolerance level.\n" );
853  }
854 
855  XLALPrintInfo( "OK.\n" );
856 
857  return XLAL_SUCCESS;
858 
859 } // XLALCheckVectorComparisonTolerances()
860 
861 /** Interpolate a given regularly-spaced COMPLEX8 timeseries 'ts_in = x_in(j * dt)' onto new samples
862  * 'y_out(t_out)' using *windowed* Shannon sinc interpolation, windowed to (2*Dterms+1) terms, namely
863  * \f[
864  * \newcommand{\Dterms}{\mathrm{Dterms}}
865  * \f]
866  *
867  * \f{equation}{
868  * x(t) = \sum_{j = j^* - \Dterms}^{j^* + \Dterms} x_j \, w_j \, \frac{\sin(\pi\delta_j)}{\pi\delta_j}\,,\quad\text{with}\quad
869  * \delta_j \equiv \frac{t - t_j}{\Delta t}\,,
870  * \f}
871  * where \f$ j^* \equiv \mathrm{round}(t / \Delta t) \f$ , and
872  * where \f$ w_j \f$ is the window used (here: Hamming)
873  *
874  * In order to implement this more efficiently, we observe that \f$ \sin(\pi\delta_j) = (-1)^{(j-j0)}\sin(\pi\delta_{j0}) \f$ for integer \f$ j \f$ ,
875  * and therefore
876  *
877  * \f{equation}{
878  * x(t) = \frac{\sin(\pi\,\delta_{j0})}{\pi} \, \sum_{j = j^* - \Dterms}^{j^* + \Dterms} (-1)^{(j-j0)}\frac{x_j \, w_j}{\delta_j}\,,
879  * \f}
880  *
881  * NOTE: Using Dterms=0 corresponds to closest-bin interpolation
882  *
883  * NOTE2: samples *outside* the original timespan are returned as 0
884  *
885  * NOTE3: we're using a Hamming window of length L = 2*Dterms + 1, which has a pass-band wiggle of delta_p ~ 0.0022,
886  * and a transition bandwidth of (4/L) * Bandwidth.
887  * You need to make sure to include sufficient effective sidebands to the input timeseries, so that the transition band can
888  * be safely ignored or 'cut out' at the end
889  */
890 int
891 XLALSincInterpolateCOMPLEX8TimeSeries( COMPLEX8Vector *y_out, ///< [out] output series of interpolated y-values [must be same size as t_out]
892  const REAL8Vector *t_out, ///< [in] output time-steps to interpolate input to
893  const COMPLEX8TimeSeries *ts_in,///< [in] regularly-spaced input timeseries
894  UINT4 Dterms ///< [in] window sinc kernel sum to +-Dterms around max
895  )
896 {
897  XLAL_CHECK( y_out != NULL, XLAL_EINVAL );
898  XLAL_CHECK( t_out != NULL, XLAL_EINVAL );
899  XLAL_CHECK( ts_in != NULL, XLAL_EINVAL );
900  XLAL_CHECK( y_out->length == t_out->length, XLAL_EINVAL );
901 
902  UINT4 numSamplesOut = t_out->length;
903  UINT4 numSamplesIn = ts_in->data->length;
904  REAL8 dt = ts_in->deltaT;
905  REAL8 tmin = XLALGPSGetREAL8( &( ts_in->epoch ) ); // time of first bin in input timeseries
906 
907  REAL8Window *win;
908  UINT4 winLen = 2 * Dterms + 1;
909  XLAL_CHECK( ( win = XLALCreateHammingREAL8Window( winLen ) ) != NULL, XLAL_EFUNC );
910 
911  const REAL8 oodt = 1.0 / dt;
912 
913  for ( UINT4 l = 0; l < numSamplesOut; l ++ ) {
914  REAL8 t = t_out->data[l] - tmin; // measure time since start of input timeseries
915 
916  // samples outside of input timeseries are returned as 0
917  if ( ( t < 0 ) || ( t > ( numSamplesIn - 1 )*dt ) ) { // avoid any extrapolations!
918  y_out->data[l] = 0;
919  continue;
920  }
921 
922  REAL8 t_by_dt = t * oodt;
923  INT8 jstar = lround( t_by_dt ); // bin closest to 't', guaranteed to be in [0, numSamples-1]
924 
925  if ( fabs( t_by_dt - jstar ) < LD_SMALL4 ) { // avoid numerical problems near peak
926  y_out->data[l] = ts_in->data->data[jstar]; // known analytic solution for exact bin
927  continue;
928  }
929 
930  INT4 jStart0 = jstar - Dterms;
931  UINT4 jEnd0 = jstar + Dterms;
932  UINT4 jStart = MYMAX( jStart0, 0 );
933  UINT4 jEnd = MYMIN( jEnd0, numSamplesIn - 1 );
934 
935  REAL4 delta_jStart = ( t_by_dt - jStart );
936  REAL4 sin0, cos0;
937  XLALSinCosLUT( &sin0, &cos0, LAL_PI * delta_jStart );
938  REAL4 sin0oopi = sin0 * OOPI;
939 
940  COMPLEX8 y_l = 0;
941  REAL8 delta_j = delta_jStart;
942  for ( UINT8 j = jStart; j <= jEnd; j ++ ) {
943  COMPLEX8 Cj = win->data->data[j - jStart0] * sin0oopi / delta_j;
944 
945  y_l += Cj * ts_in->data->data[j];
946 
947  sin0oopi = -sin0oopi; // sin-term flips sign every step
948  delta_j --;
949  } // for j in [j* - Dterms, ... ,j* + Dterms]
950 
951  y_out->data[l] = y_l;
952 
953  } // for l < numSamplesOut
954 
956 
957  return XLAL_SUCCESS;
958 
959 } // XLALSincInterpolateCOMPLEX8TimeSeries()
960 
961 /** Interpolate a given regularly-spaced COMPLEX8 frequency-series 'fs_in = x_in( k * df)' onto new samples
962  * 'y_out(f_out)' using (complex) Sinc interpolation (obtained from Dirichlet kernel in large-N limit), truncated to (2*Dterms+1) terms, namely
963  *
964  * \f{equation}{
965  * \widetilde{x}(f) = \sum_{k = k^* - \Delta k}^{k^* + \Delta k} \widetilde{x}_k \, \frac{ 1 - e^{-i 2\pi\,\delta_k} }{ i 2\pi\,\delta_k}\,,
966  * \f}
967  * where \f$ k^* \equiv \mathrm{round}(f / \Delta f) \f$ , and \f$ \delta_k \equiv \frac{f - f_k}{\Delta f} \f$ .
968  *
969  * In order to implement this more efficiently, we observe that \f$ e^{-i 2\pi\,\delta_k} = e^{-i 2\pi\,\delta_{k*}} \f$ for integer \f$ k \f$ , and therefore
970  * \f{equation}{
971  * \widetilde{x}(f) = \frac{\sin(2\pi\delta_{k*}) + i(\cos(2\pi\delta_{k*}) - 1)}{2\pi} \sum_{k = k^* - \Delta k}^{k^* + \Delta k} \frac{\widetilde{x}_k}{\delta_k}\,,
972  * \f}
973  *
974  * NOTE: Using Dterms=0 corresponds to closest-bin interpolation
975  *
976  * NOTE2: frequencies *outside* the original frequency band are returned as 0
977  */
978 int
979 XLALSincInterpolateCOMPLEX8FrequencySeries( COMPLEX8Vector *y_out, ///< [out] output series of interpolated y-values [must be alloc'ed already]
980  const REAL8Vector *f_out, ///< [in] output frequency-values to interpolate input to
981  const COMPLEX8FrequencySeries *fs_in, ///< [in] regularly-spaced input frequency-series
982  UINT4 Dterms ///< [in] truncate interpolation kernel sum to +-Dterms around max
983  )
984 {
985  XLAL_CHECK( y_out != NULL, XLAL_EINVAL );
986  XLAL_CHECK( f_out != NULL, XLAL_EINVAL );
987  XLAL_CHECK( fs_in != NULL, XLAL_EINVAL );
988 
989  UINT4 numBinsOut = f_out->length;
990  XLAL_CHECK( y_out->length == numBinsOut, XLAL_EINVAL );
991 
992  UINT4 numBinsIn = fs_in->data->length;
993  REAL8 df = fs_in->deltaF;
994  REAL8 fMin = fs_in->f0; // frequency of first input bin
995 
996  const REAL8 oodf = 1.0 / df;
997 
998  for ( UINT4 l = 0; l < numBinsOut; l ++ ) {
999  REAL8 f = f_out->data[l] - fMin; // measure frequency from lowest input bin
1000 
1001  // bins outside of input frequency-series are returned as 0
1002  if ( ( f < 0 ) || ( f > ( numBinsIn - 1 )*df ) ) { // avoid any extrapolations!
1003  y_out->data[l] = 0;
1004  continue;
1005  }
1006 
1007  INT8 kstar = lround( f * oodf ); // bin closest to 't', guaranteed to be in [0, numBins-1]
1008 
1009  if ( fabs( f - kstar * df ) < 1e-6 ) { // avoid numerical problems near peak
1010  y_out->data[l] = fs_in->data->data[kstar]; // known analytic solution for exact bin
1011  continue;
1012  }
1013 
1014  UINT8 k0 = MYMAX( kstar - Dterms, 0 );
1015  UINT8 k1 = MYMIN( kstar + Dterms, numBinsIn - 1 );
1016 
1017  REAL4 delta_kstar = ( f - kstar * df ) * oodf; // in [-0.5, 0.5]
1018  REAL4 sin2pidelta, cos2pidelta;
1019  XLALSinCos2PiLUTtrimmed( &sin2pidelta, &cos2pidelta, delta_kstar + 1.0f );
1020 
1021  COMPLEX8 prefact = OOTWOPI * ( sin2pidelta + I * ( cos2pidelta - 1.0f ) );
1022 
1023  COMPLEX8 y_l = 0;
1024  REAL4 delta_k = delta_kstar + Dterms;
1025  for ( UINT8 k = k0; k <= k1; k ++ ) {
1026  y_l += fs_in->data->data[k] / delta_k;
1027  delta_k --;
1028  } // for k in [k* - Dterms, ... ,k* + Dterms]
1029 
1030  y_out->data[l] = prefact * y_l;
1031 
1032  } // for l < numBinsOut
1033 
1034  return XLAL_SUCCESS;
1035 
1036 } // XLALSincInterpolateCOMPLEX8FrequencySeries()
1037 
1038 
1039 /** (Complex)Sinc-interpolate an input SFT to an output SFT.
1040  * This is a simple convenience wrapper to XLALSincInterpolateCOMPLEX8FrequencySeries()
1041  * for the special case of interpolating onto new SFT frequency bins
1042  */
1043 SFTtype *
1044 XLALSincInterpolateSFT( const SFTtype *sft_in, ///< [in] input SFT
1045  REAL8 f0Out, ///< [in] new start frequency
1046  REAL8 dfOut, ///< [in] new frequency step-size
1047  UINT4 numBinsOut, ///< [in] new number of bins
1048  UINT4 Dterms ///< [in] truncate interpolation kernel sum to +-Dterms around max
1049  )
1050 {
1051  XLAL_CHECK_NULL( sft_in != NULL, XLAL_EINVAL );
1052  XLAL_CHECK_NULL( dfOut > 0, XLAL_EINVAL );
1053  XLAL_CHECK_NULL( numBinsOut > 0, XLAL_EINVAL );
1054 
1055  // setup frequency vector
1056  REAL8Vector *f_out;
1057  XLAL_CHECK_NULL( ( f_out = XLALCreateREAL8Vector( numBinsOut ) ) != NULL, XLAL_EFUNC );
1058  for ( UINT4 k = 0; k < numBinsOut; k ++ ) {
1059  f_out->data[k] = f0Out + k * dfOut;
1060  } // for k < numBinsOut
1061 
1062  SFTtype *out;
1063  XLAL_CHECK_NULL( ( out = XLALCalloc( 1, sizeof( *out ) ) ) != NULL, XLAL_EFUNC );
1064  ( *out ) = ( *sft_in ); // copy header
1065  out->f0 = f0Out;
1066  out->deltaF = dfOut;
1067  XLAL_CHECK_NULL( ( out->data = XLALCreateCOMPLEX8Vector( numBinsOut ) ) != NULL, XLAL_EFUNC );
1068 
1070 
1071  XLALDestroyREAL8Vector( f_out );
1072 
1073  return out;
1074 
1075 } // XLALSincInterpolateSFT()
1076 
1077 
1078 /**
1079  * Interpolate frequency-series to newLen frequency-bins.
1080  * This is using DFT-interpolation (derived from zero-padding).
1081  */
1084  UINT4 refineby,
1085  UINT4 Dterms
1086  )
1087 {
1088  UINT4 newLen, oldLen, l;
1089  COMPLEX8Vector *ret = NULL;
1090 
1091  if ( !in ) {
1092  return NULL;
1093  }
1094 
1095  oldLen = in->length;
1096  newLen = oldLen * refineby;
1097 
1098  /* the following are used to speed things up in the innermost loop */
1099  if ( ( ret = XLALCreateCOMPLEX8Vector( newLen ) ) == NULL ) {
1100  return NULL;
1101  }
1102 
1103  for ( l = 0; l < newLen; l++ ) {
1104 
1105  REAL8 kappa_l_k;
1106  REAL8 remain, kstarREAL;
1107  UINT4 kstar, kmin, kmax, k;
1108  REAL8 sink, coskm1;
1109  REAL8 Yk_re, Yk_im, Xd_re, Xd_im;
1110 
1111  kstarREAL = 1.0 * l / refineby;
1112  kstar = lround( kstarREAL ); /* round to closest bin */
1113  kstar = MYMIN( kstar, oldLen - 1 ); /* stay within the old SFT index-bounds */
1114  remain = kstarREAL - kstar;
1115 
1116  /* boundaries for innermost loop */
1117  kmin = MYMAX( 0, ( INT4 )kstar - ( INT4 )Dterms );
1118  kmax = MYMIN( oldLen, kstar + Dterms );
1119 
1120  Yk_re = Yk_im = 0;
1121  if ( fabs( remain ) > 1e-5 ) { /* denominater doens't vanish */
1122  /* Optimization: sin(2pi*kappa(l,k)) = sin(2pi*kappa(l,0) and idem for cos */
1123  sink = sin( LAL_TWOPI * remain );
1124  coskm1 = cos( LAL_TWOPI * remain ) - 1.0;
1125 
1126  /* ---------- innermost loop: k over 2*Dterms around kstar ---------- */
1127  for ( k = kmin; k < kmax; k++ ) {
1128  REAL8 Plk_re, Plk_im;
1129 
1130  Xd_re = crealf( in->data[k] );
1131  Xd_im = cimagf( in->data[k] );
1132 
1133  kappa_l_k = kstarREAL - k;
1134 
1135  Plk_re = sink / kappa_l_k;
1136  Plk_im = coskm1 / kappa_l_k;
1137 
1138  Yk_re += Plk_re * Xd_re - Plk_im * Xd_im;
1139  Yk_im += Plk_re * Xd_im + Plk_im * Xd_re;
1140 
1141  } /* hotloop over Dterms */
1142  } else { /* kappa -> 0: Plk = 2pi delta(k, l) */
1143  Yk_re = LAL_TWOPI * crealf( in->data[kstar] );
1144  Yk_im = LAL_TWOPI * cimagf( in->data[kstar] );
1145  }
1146 
1147  ret->data[l] = crectf( OOTWOPI * Yk_re, OOTWOPI * Yk_im );
1148 
1149  } /* for l < newlen */
1150 
1151  return ret;
1152 
1153 } /* XLALrefineCOMPLEX8Vector() */
1154 
1155 /// @}
int j
int k
#define MYMIN(x, y)
Definition: LFTandTSutils.c:39
#define LD_SMALL4
Definition: LFTandTSutils.c:47
static LALUnit emptyLALUnit
Definition: LFTandTSutils.c:49
#define OOPI
Definition: LFTandTSutils.c:46
#define cRELERR(x, y)
Definition: LFTandTSutils.c:43
#define MYMAX(x, y)
Definition: LFTandTSutils.c:38
#define SQ(x)
Definition: LFTandTSutils.c:40
#define fRELERR(x, y)
Definition: LFTandTSutils.c:44
#define FINITE_OR_ONE(x)
Definition: LFTandTSutils.c:42
#define OOTWOPI
Definition: LFTandTSutils.c:45
REAL8 tol
const char * names[]
int l
double e
coeffs k0
coeffs k1
const double deltaT
COMPLEX8FFTPlan * XLALCreateReverseCOMPLEX8FFTPlan(UINT4 size, int measurelvl)
int XLALCOMPLEX8VectorFFT(COMPLEX8Vector *_LAL_RESTRICT_ output, const COMPLEX8Vector *_LAL_RESTRICT_ input, const COMPLEX8FFTPlan *plan)
COMPLEX8FFTPlan * XLALCreateForwardCOMPLEX8FFTPlan(UINT4 size, int measurelvl)
void XLALDestroyCOMPLEX8FFTPlan(COMPLEX8FFTPlan *plan)
static const REAL8 LAL_FACT_INV[]
#define LAL_PI
#define LAL_TWOPI
unsigned char BOOLEAN
#define XLAL_NUM_ELEM(x)
uint64_t UINT8
double complex COMPLEX16
double REAL8
#define XLAL_INIT_DECL(var,...)
int64_t INT8
#define crectf(re, im)
uint32_t UINT4
float complex COMPLEX8
int32_t INT4
float REAL4
#define lalDebugLevel
LALINFO
void * XLALCalloc(size_t m, size_t n)
void * XLALMalloc(size_t n)
void * XLALRealloc(void *p, size_t n)
void XLALFree(void *p)
SFTtype * XLALSFTVectorToLFT(SFTVector *sfts, REAL8 upsampling)
Turn the given multi-IFO SFTvectors into one long Fourier transform (LFT) over the total observation ...
Definition: LFTandTSutils.c:64
int XLALReorderFFTWtoSFT(COMPLEX8Vector *X)
Change frequency-bin order from fftw-convention to a 'SFT' ie.
MultiCOMPLEX8TimeSeries * XLALMultiSFTVectorToCOMPLEX8TimeSeries(const MultiSFTVector *multisfts)
Turn the given multiSFTvector into multiple long COMPLEX8TimeSeries, properly dealing with gaps.
int XLALSincInterpolateCOMPLEX8TimeSeries(COMPLEX8Vector *y_out, const REAL8Vector *t_out, const COMPLEX8TimeSeries *ts_in, UINT4 Dterms)
Interpolate a given regularly-spaced COMPLEX8 timeseries 'ts_in = x_in(j * dt)' onto new samples 'y_o...
#define NhalfNeg(N)
Definition: LFTandTSutils.h:47
int XLALFrequencyShiftMultiCOMPLEX8TimeSeries(MultiCOMPLEX8TimeSeries *x, const REAL8 shift)
Multi-detector wrapper for XLALFrequencyShiftCOMPLEX8TimeSeries NOTE: this modifies the MultiCOMPLEX8...
int XLALTimeShiftSFT(SFTtype *sft, REAL8 shift)
Time-shift the given SFT by an amount of 'shift' seconds, using the frequency-domain expression ,...
int XLALFrequencyShiftCOMPLEX8TimeSeries(COMPLEX8TimeSeries *x, const REAL8 shift)
Freq-shift the given COMPLEX8Timeseries by an amount of 'shift' Hz, using the time-domain expression ...
COMPLEX8TimeSeries * XLALSFTVectorToCOMPLEX8TimeSeries(const SFTVector *sftsIn)
Turn the given SFTvector into one long time-series, properly dealing with gaps.
int XLALCopyMultiCOMPLEX8TimeSeries(MultiCOMPLEX8TimeSeries *multiTimesOut, MultiCOMPLEX8TimeSeries *multiTimesIn)
Copies a MultiCOMPLEX8TimeSeries structure, output must be allocated of identical size as input!
int XLALCheckVectorComparisonTolerances(const VectorComparison *result, const VectorComparison *tol)
Check VectorComparison result against specified tolerances, to allow for standardized comparison and ...
int XLALReorderSFTtoFFTW(COMPLEX8Vector *X)
Change frequency-bin order from 'SFT' to fftw-convention ie.
int XLALCompareREAL4Vectors(VectorComparison *result, const REAL4Vector *x, const REAL4Vector *y, const VectorComparison *tol)
Compare two REAL4 vectors using various different comparison metrics.
int XLALCompareCOMPLEX8Vectors(VectorComparison *result, const COMPLEX8Vector *x, const COMPLEX8Vector *y, const VectorComparison *tol)
Compare two COMPLEX8 vectors using various different comparison metrics.
COMPLEX8TimeSeries * XLALDuplicateCOMPLEX8TimeSeries(COMPLEX8TimeSeries *ttimes)
Duplicates a COMPLEX8TimeSeries structure.
int XLALSpinDownCorrectionMultiTS(MultiCOMPLEX8TimeSeries *multiTimeSeries, const PulsarDopplerParams *doppler)
Apply a spin-down correction to the complex8 timeseries using the time-domain expression y(t) = x(t) ...
MultiCOMPLEX8TimeSeries * XLALDuplicateMultiCOMPLEX8TimeSeries(MultiCOMPLEX8TimeSeries *multiTimes)
Duplicates a MultiCOMPLEX8TimeSeries structure.
#define NhalfPosDC(N)
Definition: LFTandTSutils.h:46
int XLALCopyCOMPLEX8TimeSeries(COMPLEX8TimeSeries *ts_out, COMPLEX8TimeSeries *ts_in)
Copies a COMPLEX8TimeSeries structure, output must be allocated of identical size as input!
SFTtype * XLALSincInterpolateSFT(const SFTtype *sft_in, REAL8 f0Out, REAL8 dfOut, UINT4 numBinsOut, UINT4 Dterms)
(Complex)Sinc-interpolate an input SFT to an output SFT.
int XLALSincInterpolateCOMPLEX8FrequencySeries(COMPLEX8Vector *y_out, const REAL8Vector *f_out, const COMPLEX8FrequencySeries *fs_in, UINT4 Dterms)
Interpolate a given regularly-spaced COMPLEX8 frequency-series 'fs_in = x_in( k * df)' onto new sampl...
COMPLEX8Vector * XLALrefineCOMPLEX8Vector(const COMPLEX8Vector *in, UINT4 refineby, UINT4 Dterms)
Interpolate frequency-series to newLen frequency-bins.
void XLALDestroyMultiCOMPLEX8TimeSeries(MultiCOMPLEX8TimeSeries *multiTimes)
Destroy a MultiCOMPLEX8TimeSeries structure.
#define PULSAR_MAX_SPINS
maximal number of spin-parameters (Freq + spindowns) we can handle
SFTtype * XLALCreateSFT(UINT4 numBins)
XLAL function to create one SFT-struct.
Definition: SFTtypes.c:152
SFTVector * XLALDuplicateSFTVector(const SFTVector *sftsIn)
Create a complete copy of an SFT vector.
Definition: SFTtypes.c:328
void XLALDestroySFTVector(SFTVector *vect)
XLAL interface to destroy an SFTVector.
Definition: SFTtypes.c:300
int XLALSinCosLUT(REAL4 *sinx, REAL4 *cosx, REAL8 x)
Calculate sin(x) and cos(x) to roughly 1e-7 precision using a lookup-table and Taylor-expansion.
Definition: SinCosLUT.c:83
int XLALSinCos2PiLUT(REAL4 *sin2pix, REAL4 *cos2pix, REAL8 x)
Calculate sin(2*pi*x) and cos(2*pi*x) to roughly 1e-7 precision using a lookup-table and Taylor-expan...
Definition: SinCosLUT.c:97
int XLALSinCos2PiLUTtrimmed(REAL4 *s, REAL4 *c, REAL8 x)
A function that uses the lookup tables to evaluate sin and cos values of 2*Pi*x, but relies on x bein...
Definition: SinCosLUT.c:112
COMPLEX8TimeSeries * XLALCreateCOMPLEX8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX8TimeSeries(COMPLEX8TimeSeries *series)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
COMPLEX8Vector * XLALCreateCOMPLEX8Vector(UINT4 length)
void XLALDestroyREAL8Window(REAL8Window *window)
REAL8Window * XLALCreateHammingREAL8Window(UINT4 length)
int int int XLALPrintInfo(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_ERROR(...)
#define XLAL_CHECK(assertion,...)
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_CHECK_NULL(assertion,...)
XLAL_ENOMEM
XLAL_SUCCESS
XLAL_EFPINVAL
XLAL_EFUNC
XLAL_ETOL
XLAL_EDOM
XLAL_EINVAL
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
list y
end
n
out
int N
size_t numDetectors
CHAR name[LALNameLength]
COMPLEX8Sequence * data
A vector of COMPLEX8FrequencySeries.
COMPLEX8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
LIGOTimeGPS epoch
COMPLEX8Sequence * data
COMPLEX8 * data
Multi-IFO container for COMPLEX8 resampled timeseries.
Definition: LFTandTSutils.h:52
COMPLEX8TimeSeries ** data
array of COMPLEX8TimeSeries (pointers)
Definition: LFTandTSutils.h:54
UINT4 length
number of IFOs
Definition: LFTandTSutils.h:53
A collection of SFT vectors – one for each IFO in a multi-IFO search.
Definition: SFTfileIO.h:179
UINT4 length
number of ifos
Definition: SFTfileIO.h:183
SFTVector ** data
sftvector for each ifo
Definition: SFTfileIO.h:184
Type containing the 'Doppler-parameters' affecting the time-evolution of the phase.
PulsarSpins fkdot
Intrinsic spins: [Freq, f1dot, f2dot, ...
LIGOTimeGPS refTime
Reference time of pulsar parameters (in SSB!)
REAL8 * data
Struct holding the results of comparing two floating-point vectors (real-valued or complex),...
Definition: LFTandTSutils.h:61
REAL4 relErr_L2
relative error between vectors using L2 norm
Definition: LFTandTSutils.h:63
REAL4 angleV
angle between the two vectors, according to
Definition: LFTandTSutils.h:64
REAL4 relErr_L1
relative error between vectors using L1 norm
Definition: LFTandTSutils.h:62
REAL4 relErr_atMaxAbsy
single-sample relative error at maximum |sample-value| of second vector 'x'
Definition: LFTandTSutils.h:66
REAL4 relErr_atMaxAbsx
single-sample relative error at maximum |sample-value| of first vector 'x'
Definition: LFTandTSutils.h:65
enum @1 epoch
double df