Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALPulsar 7.1.1.1-5e288d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
GeneratePulsarSignal.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2004, 2005 Reinhard Prix
3 * Copyright (C) 2004, 2005 Greg Mendell
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/* NOTES: */
22/* 07/14/04 gam; add functions LALComputeSkyAndZeroPsiAMResponse and LALFastGeneratePulsarSFTs */
23/* 10/08/04 gam; fix indexing into trig lookup tables (LUTs) by having table go from -2*pi to 2*pi */
24/* 10/12/04 gam; When computing fCross and fPlus need to use 2.0*psi. */
25/* 09/07/05 gam; Add Dterms parameter to LALFastGeneratePulsarSFTs; use this to fill in SFT bins with fake data as per LALDemod else fill in bin with zero */
26
27#include <math.h>
28#include <gsl/gsl_math.h>
29
30#include <lal/AVFactories.h>
31#include <lal/TimeSeries.h>
32#include <lal/SeqFactories.h>
33#include <lal/RealFFT.h>
34#include <lal/SFTfileIO.h>
35#include <lal/Window.h>
36#include <lal/Random.h>
37
38#include <lal/GeneratePulsarSignal.h>
39
40/*----------------------------------------------------------------------*/
41/* Internal helper functions */
43static int XLALcheckNoiseSFTs( const SFTVector *sfts, REAL8 f0, REAL8 f1, REAL8 deltaF );
44int XLALcorrect_phase( SFTtype *sft, LIGOTimeGPS tHeterodyne );
45
46/*----------------------------------------------------------------------*/
47
48static REAL8 eps = 1.e-14; /* maximal REAL8 roundoff-error (used for determining if some REAL8 frequency corresponds to an integer "bin-index" */
50
51/* ----- DEFINES ----- */
52
53/*---------- Global variables ----------*/
54
55/// \addtogroup GeneratePulsarSignal_h
56/// @{
57
58/**
59 * Generate a time-series at the detector for a given pulsar.
60 */
63 )
64{
65 XLAL_CHECK_NULL( params != NULL, XLAL_EINVAL, "Invalid NULL input 'params'\n" );
66
67 int ret;
68 /*----------------------------------------------------------------------
69 *
70 * First call GenerateSpinOrbitCW() to generate the source-signal
71 *
72 *----------------------------------------------------------------------*/
74 sourceParams.psi = params->pulsar.psi;
75 sourceParams.aPlus = params->pulsar.aPlus;
76 sourceParams.aCross = params->pulsar.aCross;
77 sourceParams.phi0 = params->pulsar.phi0;
78 sourceParams.f0 = params->pulsar.f0;
79
80 sourceParams.position = params->pulsar.position;
81 /* set source position: make sure it's "normalized", i.e. [0<=alpha<2pi]x[-pi/2<=delta<=pi/2] */
82 XLALNormalizeSkyPosition( &( sourceParams.position.longitude ), &( sourceParams.position.latitude ) );
83
84 /* if pulsar is in binary-orbit, set binary parameters */
85 if ( params->orbit.asini > 0 ) {
86 /*------------------------------------------------------------ */
87 /* temporary fix for comparison with Chris' code */
88 /*
89 TRY (XLALConvertGPS2SSB (status->statusPtr, &tmpTime, params->orbit->orbitEpoch, params), status);
90 sourceParams.orbitEpoch = tmpTime;
91 */
92 sourceParams.orbitEpoch = params->orbit.tp;
93 sourceParams.omega = params->orbit.argp;
94 /* ------- here we do conversion to Teviets preferred variables -------*/
95 sourceParams.rPeriNorm = params->orbit.asini * ( 1.0 - params->orbit.ecc );
96 sourceParams.oneMinusEcc = 1.0 - params->orbit.ecc;
97 sourceParams.angularSpeed = ( LAL_TWOPI / params->orbit.period ) * sqrt( ( 1.0 + params->orbit.ecc ) / pow( ( 1.0 - params->orbit.ecc ), 3.0 ) );
98 } else {
99 sourceParams.rPeriNorm = 0.0; /* this defines an isolated pulsar */
100 }
101
102 if ( params->pulsar.refTime.gpsSeconds != 0 ) {
103 sourceParams.spinEpoch = params->pulsar.refTime; /* pulsar reference-time in SSB frame (TDB) */
104 } else { /* if not given: use startTime converted to SSB as tRef ! */
105 LIGOTimeGPS tmpTime;
106 ret = XLALConvertGPS2SSB( &tmpTime, params->startTimeGPS, params );
108 sourceParams.spinEpoch = tmpTime;
109 }
110
111 if ( params->sourceDeltaT == 0 ) { // backwards-compatible treatment for absence of this parameter
112 /* sampling-timestep and length for source-parameters */
113 /* in seconds; hardcoded; was 60s in makefakedata_v2,
114 * but for fast binaries (e.g. SCO-X1) we need faster sampling
115 * This does not seem to affect performance a lot (~4% in makefakedata),
116 * but we'll nevertheless make this sampling faster for binaries and slower
117 * for isolated pulsars */
118 if ( params->orbit.asini > 0 ) {
119 sourceParams.deltaT = 5; /* for binaries */
120 } else {
121 sourceParams.deltaT = 60; /* for isolated pulsars */
122 }
123 } else { // use the user-defined sampling
124 sourceParams.deltaT = params->sourceDeltaT;
125 }
126
127 /* start-time in SSB time */
129 ret = XLALConvertGPS2SSB( &t0, params->startTimeGPS, params );
131
132 t0.gpsSeconds -= ( UINT4 )sourceParams.deltaT; /* start one time-step earlier to be safe */
133
134 /* end time in SSB */
135 LIGOTimeGPS t1 = params->startTimeGPS;
137 ret = XLALConvertGPS2SSB( &t1, t1, params );
139
140 /* get duration of source-signal */
141 REAL8 SSBduration = XLALGPSDiff( &t1, &t0 );
142 SSBduration += 2.0 * sourceParams.deltaT; /* add two time-steps to be safe */
143
144 sourceParams.epoch = t0;
145 sourceParams.length = ( UINT4 ) ceil( SSBduration / sourceParams.deltaT );
146
147 /* we use frequency-spindowns, but GenerateSpinOrbitCW wants f_k = fkdot / (f0 * k!) */
148 if ( params->pulsar.spindown ) {
149 UINT4 numSpindowns = params->pulsar.spindown->length;
150 sourceParams.f = XLALCreateREAL8Vector( numSpindowns );
151 XLAL_CHECK_NULL( sourceParams.f != NULL, XLAL_EFUNC, "XLALCreateREAL8Vector(%d) failed.", numSpindowns );
152
153 UINT4 kFact = 1;
154 for ( UINT4 i = 0; i < numSpindowns; i++ ) {
155 sourceParams.f->data[i] = params->pulsar.spindown->data[i] / ( kFact * params->pulsar.f0 );
156 kFact *= i + 2;
157 } // i < numSpindowns
158 } // if pulsar.spindown
159
160 /* finally, call the function to generate the source waveform */
161 PulsarCoherentGW XLAL_INIT_DECL( sourceSignal );
162
163 XLAL_CHECK_NULL( XLALGenerateSpinOrbitCW( &sourceSignal, &sourceParams ) == XLAL_SUCCESS, XLAL_EFUNC );
164
165 /* free spindown-vector right away, so we don't forget */
166 if ( sourceParams.f ) {
167 XLALDestroyREAL8Vector( sourceParams.f );
168 }
169
170 /* check that sampling interval was short enough */
171 XLAL_CHECK_NULL( sourceParams.dfdt <= 2.0, XLAL_ETOL, "GenerateSpinOrbitCW() returned df*dt = %f > 2.0\n", sourceParams.dfdt );
172
173 /*----------------------------------------------------------------------
174 *
175 * Now call the function to translate the source-signal into a (heterodyned)
176 * signal at the detector
177 *
178 *----------------------------------------------------------------------*/
179 /* first set up the detector-response */
181 detector.transfer = params->transfer;
182 detector.site = params->site;
183 detector.ephemerides = params->ephemerides;
184
185 /* *contrary* to makefakedata_v2, we use the GPS start-time of the timeseries as
186 * the heterodyning epoch, in order to make sure that the global phase of the
187 * SFTs it correct: this is necessary to allow correct parameter-estimation on the
188 * pulsar signal-phase in heterodyned SFTs
189 */
190 detector.heterodyneEpoch = params->startTimeGPS;
191
192 /* NOTE: a timeseries of length N*dT has no timestep at N*dT !! (convention) */
193 UINT4 numSteps = ( UINT4 ) ceil( params->samplingRate * params->duration );
194 REAL8 dt = 1.0 / params->samplingRate;
195 REAL8 fHet = params->fHeterodyne;
196
197 /* ok, we need to prepare the output time-series */
198 REAL4TimeSeries *output = XLALCreateREAL4TimeSeries( "", &( params->startTimeGPS ), fHet, dt, &emptyLALUnit, numSteps );
199 XLAL_CHECK_NULL( output != NULL, XLAL_EFUNC, "XLALCreateREAL4TimeSeries() failed with xlalErrno = %d\n", xlalErrno );
200
201 // internal interpolation parameters for LALPulsarSimulateCoherentGW()
202 sourceSignal.dtDelayBy2 = params->dtDelayBy2;
203 sourceSignal.dtPolBy2 = params->dtPolBy2;
204
206
207 /* set 'name'-field of timeseries to contain the right "channel prefix" for the detector */
208 CHAR *name = XLALGetChannelPrefix( params->site->frDetector.name );
209 XLAL_CHECK_NULL( name != NULL, XLAL_EFUNC );
210 strcpy( output->name, name );
211 XLALFree( name );
212
213 /*----------------------------------------------------------------------*/
214 /* Free all allocated memory that is not returned */
215 XLALDestroyREAL4VectorSequence( sourceSignal.a->data );
216 XLALFree( sourceSignal.a );
217 XLALDestroyREAL4TimeSeries( sourceSignal.f );
218 XLALDestroyREAL8TimeSeries( sourceSignal.phi );
219
220 return output;
221
222} /* XLALGeneratePulsarSignal() */
223
224/**
225 * \deprecated Use XLALGeneratePulsarSignal() instead.
226 */
227void
228LALGeneratePulsarSignal( LALStatus *status, /**< pointer to LALStatus structure */
229 REAL4TimeSeries **signalvec, /**< output time-series */
230 const PulsarSignalParams *params ) /**< input params */
231{
233
235 if ( output == NULL ) {
236 XLALPrintError( "XLALGeneratePulsarSignal() failed with xlalErrno = %d\n", xlalErrno );
237 ABORTXLAL( status );
238 }
239
240 ( *signalvec ) = output;
241
242 RETURN( status );
243
244} /* LALGeneratePulsarSignal() */
245
246/**
247 * Turn the given time-series into SFTs and add noise if given.
248 */
249SFTVector *
250XLALSignalToSFTs( const REAL4TimeSeries *signalvec, /**< input time-series */
251 const SFTParams *params /**< params for output-SFTs */
252 )
253{
254 XLAL_CHECK_NULL( signalvec != NULL, XLAL_EINVAL, "Invalid NULL input 'signalvec'\n" );
255 XLAL_CHECK_NULL( params != NULL, XLAL_EINVAL, "Invalid NULL input 'params'\n" );
256
257 REAL8 f0 = signalvec->f0; /* lowest frequency */
258 REAL8 dt = signalvec->deltaT; /* timeseries timestep */
259 REAL8 Band = 1.0 / ( 2.0 * dt ); /* NOTE: frequency-band is determined by sampling-rate! */
260 REAL8 deltaF = 1.0 / params->Tsft; /* frequency-resolution */
261
262 int ret;
263 /* if noiseSFTs are given: check they are consistent with signal! */
264 if ( params->noiseSFTs ) {
265 ret = XLALcheckNoiseSFTs( params->noiseSFTs, f0, f0 + Band, deltaF );
267 }
268
269 /* make sure that number of timesamples/SFT is an integer (up to possible rounding errors) */
270 REAL8 REALnumTimesteps = params->Tsft / dt; /* this is a float!*/
271 UINT4 numTimesteps = lround( REALnumTimesteps ); /* number of time-samples in an Tsft, round to closest int */
272 XLAL_CHECK_NULL( fabs( REALnumTimesteps - numTimesteps ) / REALnumTimesteps < eps, XLAL_ETOL,
273 "Inconsistent sampling-step (dt=%g) and Tsft=%g: must be integer multiple Tsft/dt = %g >= %g\n",
274 dt, params->Tsft, REALnumTimesteps, eps );
275
276 /* Prepare FFT: compute plan for FFTW */
277 RealFFTPlan *pfwd = XLALCreateForwardREAL4FFTPlan( numTimesteps, 0 );
278 XLAL_CHECK_NULL( pfwd != NULL, XLAL_EFUNC, "XLALCreateForwardREAL4FFTPlan(%d,0) failed.\n", numTimesteps );
279
280 /* get some info about time-series */
281 LIGOTimeGPS tStart = signalvec->epoch; /* start-time of time-series */
282
283 /* get last possible start-time for an SFT */
284 REAL8 duration = round( 1.0 * signalvec->data->length * dt ); /* total duration rounded to seconds */
285 LIGOTimeGPS tLast = tStart;
286 XLALGPSAdd( &tLast, duration - params->Tsft );
288
289 /* for simplicity we _always_ work with timestamps.
290 * Therefore, we have to generate them now if none have been provided by the user. */
292 LIGOTimeGPSVector *localTimestamps = NULL;
293 if ( params->timestamps == NULL ) {
294 REAL8 Toverlap = 0;
295 XLAL_CHECK_NULL( ( localTimestamps = XLALMakeTimestamps( tStart, duration, params->Tsft, Toverlap ) ) != NULL, XLAL_EFUNC );
296 /* see if the last timestamp is valid (can fit a full SFT in?), if not, drop it */
297 LIGOTimeGPS lastTs = localTimestamps->data [ localTimestamps->length - 1 ];
298 if ( XLALGPSDiff( &lastTs, &tLast ) > 0 ) { // if lastTs > tLast
299 localTimestamps->length --;
300 }
301 timestamps = localTimestamps;
302 } else { /* if given, use those, and check they are valid */
303 timestamps = params->timestamps;
304 }
305
306 /* check that all timestamps lie within [tStart, tLast] */
308
309 UINT4 numSFTs = timestamps->length; /* number of SFTs to produce */
310 /* check that we have the right number of noise-SFTs */
311 if ( params->noiseSFTs ) {
312 XLAL_CHECK_NULL( params->noiseSFTs->length == numSFTs, XLAL_EDOM, "Inconsistent number of SFTs in timestamps (%d) and noise-SFTs (%d)\n",
313 numSFTs, params->noiseSFTs->length );
314 }
315
316 /* check that if the user gave a window then the length should be correct */
317 if ( params->window ) {
318 XLAL_CHECK_NULL( numTimesteps == params->window->data->length, XLAL_EDOM, "Inconsistent window-length =%d, differs from numTimesteps=%d\n",
319 params->window->data->length, numTimesteps );
320 }
321
322 /* prepare SFT-vector for return */
323 UINT4 numBins = ( UINT4 )( numTimesteps / 2 ) + 1; /* number of frequency-bins per SFT */
324
326 XLAL_CHECK_NULL( sftvect != NULL, XLAL_EFUNC, "XLALCreateSFTVector(numSFTs=%d, numBins=%d) failed.\n", numSFTs, numBins );
327
328 LIGOTimeGPS tPrev = tStart; /* initialize */
329 UINT4 totalIndex = 0; /* timestep-index to start next FFT from */
330
331 /* Assign memory to timeStretchCopy */
332 REAL4Vector *timeStretchCopy = XLALCreateREAL4Vector( numTimesteps );
333 XLAL_CHECK_NULL( timeStretchCopy != NULL, XLAL_EFUNC, "XLALCreateREAL4Vector(%d) failed.\n", numTimesteps );
334
335 /* main loop: apply FFT the requested time-stretches */
336 for ( UINT4 iSFT = 0; iSFT < numSFTs; iSFT++ ) {
337 SFTtype *thisSFT = &( sftvect->data[iSFT] ); /* point to current SFT-slot */
338
339 /* find the start-bin for this SFT in the time-series */
340 REAL8 delay = XLALGPSDiff( &( timestamps->data[iSFT] ), &tPrev );
341
342 /* round properly: picks *closest* timestep (==> "nudging") !! */
343 INT4 relIndexShift = lround( delay / signalvec->deltaT );
344 totalIndex += relIndexShift;
345
346 REAL4Vector timeStretch;
347 timeStretch.length = numTimesteps;
348 timeStretch.data = signalvec->data->data + totalIndex; /* point to the right sample-bin */
349 memcpy( timeStretchCopy->data, timeStretch.data, numTimesteps * sizeof( *timeStretch.data ) );
350
351 /* fill the header of the i'th output SFT */
352 REAL8 realDelay = ( REAL4 )( relIndexShift * signalvec->deltaT ); /* cast to REAL4 to avoid rounding-errors*/
353 LIGOTimeGPS tmpTime = tPrev;
354 XLALGPSAdd( &tmpTime, realDelay );
355
356 strcpy( thisSFT->name, signalvec->name );
357 /* set the ACTUAL timestamp! (can be different from requested one ==> "nudging") */
358 thisSFT->epoch = tmpTime;
359 thisSFT->f0 = signalvec->f0; /* minimum frequency */
360 thisSFT->deltaF = 1.0 / params->Tsft; /* frequency-spacing */
361
362 tPrev = tmpTime; /* prepare next loop */
363
364 /* ok, issue at least a warning if we have "nudged" an SFT-timestamp */
365 if ( lalDebugLevel > 0 ) {
366 REAL8 diff = XLALGPSDiff( &( timestamps->data[iSFT] ), &tmpTime );
367 if ( diff != 0 ) {
368 XLALPrintError( "Warning: timestamp %d had to be 'nudged' by %e s to fit with time-series\n", iSFT, diff );
369 /* double check if magnitude of nudging seems reasonable .. */
370 XLAL_CHECK_NULL( fabs( diff ) < signalvec->deltaT, XLAL_ETOL, "Nudged by more (%g) than deltaT=%g ... this sounds wrong! (We better stop)\n",
371 fabs( diff ), signalvec->deltaT );
372 } // if nudging
373 } /* if lalDebugLevel */
374
375 /* Now window the current time series stretch, if necessary */
376 if ( params->window ) {
377 // the SFT normalization in case of windowing follows the conventions detailed in \cite SFT-spec
378 const float inv_sigma_win = 1.0 / sqrt( params->window->sumofsquares / params->window->data->length );
379 for ( UINT4 idatabin = 0; idatabin < timeStretchCopy->length; idatabin++ ) {
380 timeStretchCopy->data[idatabin] *= inv_sigma_win * params->window->data->data[idatabin];
381 }
382 } // if window
383
384 /* the central step: FFT the ith time-stretch into an SFT-slot */
385 ret = XLALREAL4ForwardFFT( thisSFT->data, timeStretchCopy, pfwd );
386 XLAL_CHECK_NULL( ret == XLAL_SUCCESS, XLAL_EFUNC, "XLALREAL4ForwardFFT() failed.\n" );
387
388 /* normalize DFT-data to conform to SFT specification ==> multiply DFT by dt */
389 COMPLEX8 *data = thisSFT->data->data;
390 for ( UINT4 i = 0; i < numBins ; i ++ ) {
391 *( data ) *= ( ( REAL4 ) dt );
392 data ++;
393 } /* for i < numBins */
394
395 /* correct heterodyning-phase, IF NECESSARY */
396 if ( ( ( INT4 )signalvec->f0 != signalvec->f0 ) || ( signalvec->epoch.gpsNanoSeconds != 0 ) || ( thisSFT->epoch.gpsNanoSeconds != 0 ) ) {
397 /* theterodyne = signalvec->epoch!*/
398 ret = XLALcorrect_phase( thisSFT, signalvec->epoch );
399 XLAL_CHECK_NULL( ret == XLAL_SUCCESS, XLAL_EFUNC, "XLALcorrect_phase() failed.\n" );
400 } /* if phase-correction necessary */
401
402 /* Now add the noise-SFTs if given */
403 if ( params->noiseSFTs ) {
404 SFTtype *thisNoiseSFT = &( params->noiseSFTs->data[iSFT] );
405 UINT4 index0n = round( ( thisSFT->f0 - thisNoiseSFT->f0 ) / thisSFT->deltaF );
406
407 data = thisSFT->data->data;
408 COMPLEX8 *noise = &( thisNoiseSFT->data->data[index0n] );
409 for ( UINT4 j = 0; j < numBins; j++ ) {
410 *( data ) += *noise;
411 data++;
412 noise++;
413 } /* for j < numBins */
414
415 } /* if noiseSFTs */
416
417 } /* for iSFT < numSFTs */
418
419 /* free stuff */
421 XLALDestroyREAL4Vector( timeStretchCopy );
422
423 /* did we create timestamps ourselves? */
424 if ( localTimestamps != NULL ) {
425 XLALDestroyTimestampVector( localTimestamps ); // if yes, free them
426 }
427
428 return sftvect;
429
430} /* XLALSignalToSFTs() */
431
432/**
433 * \deprecated Use XLALSignalToSFTs() instead
434 */
435void
436LALSignalToSFTs( LALStatus *status, /**< pointer to LALStatus structure */
437 SFTVector **outputSFTs, /**< [out] SFT-vector */
438 const REAL4TimeSeries *signalvec, /**< input time-series */
439 const SFTParams *params ) /**< params for output-SFTs */
440{
442
443 if ( outputSFTs == NULL ) {
444 ABORT( status, XLAL_EINVAL, "Invalid NULL input 'outputSFTs'" );
445 }
446 if ( ( *outputSFTs ) != NULL ) {
447 ABORT( status, XLAL_EINVAL, "Input-pointer (*outputSFTs) must be NULL" );
448 }
449
450 SFTVector *out = XLALSignalToSFTs( signalvec, params );
451 if ( out == NULL ) {
452 XLALPrintError( "XLALSignalToSFTs() failed with xlalErrno = %d\n", xlalErrno );
453 ABORTXLAL( status );
454 }
455
456 ( *outputSFTs ) = out;
457
458 RETURN( status );
459
460} /* LALSignalToSFTs() */
461
462
463/* 07/14/04 gam */
464/** /deprecated Move to attic?
465 * Wrapper for LALComputeSky() and LALComputeDetAMResponse() that finds the sky
466 * constants and \f$ F_+ \f$ and \f$ F_\times \f$ for use with LALFastGeneratePulsarSFTs().
467 * Uses the output of LALComputeSkyAndZeroPsiAMResponse() and the same inputs as
468 * LALGeneratePulsarSignal() and LALSignalToSFTs().
469 * This function used LALComputeSkyBinary() if params->pSigParams->orbit is not
470 * NULL, else it uses LALComputeSky() to find the skyConsts.
471 * NOTE THAT THIS FUNCTION COMPUTES \f$ F_+ \f$ and \f$ F_x \f$ for ZERO Psi!!!
472 * LALFastGeneratePulsarSFTs() used these to find \f$ F_+ \f$ and \f$ F_x \f$ for NONZERO Psi.
473 */
474void
475LALComputeSkyAndZeroPsiAMResponse( LALStatus *status, /**< pointer to LALStatus structure */
476 SkyConstAndZeroPsiAMResponse *output, /**< output */
477 const SFTandSignalParams *params ) /**< params */
478{
479 INT4 i;
480 INT4 numSFTs; /* number of SFTs */
481 BarycenterInput baryinput; /* Stores detector location and other barycentering data */
482 CSParams *csParams = NULL; /* ComputeSky parameters */
483 SkyPosition tmp;
484 EarthState earth;
485 EmissionTime emit;
486 LALDetAMResponse response; /* output of LALComputeDetAMResponse */
487 LALDetAndSource *das; /* input for LALComputeDetAMResponse */
488 REAL8 halfTsft; /* half the time of one SFT */
489 LIGOTimeGPS midTS; /* midpoint time for an SFT */
490
493
494 numSFTs = params->pSFTParams->timestamps->length; /* number of SFTs */
495 halfTsft = 0.5 * params->pSFTParams->Tsft; /* half the time of one SFT */
496
497 /* setup baryinput for LALComputeSky */
498 baryinput.site = *( params->pSigParams->site );
499 /* account for a quirk in XLALBarycenter(): -> see documentation of type BarycenterInput */
500 baryinput.site.location[0] /= LAL_C_SI;
501 baryinput.site.location[1] /= LAL_C_SI;
502 baryinput.site.location[2] /= LAL_C_SI;
503 if ( params->pSigParams->pulsar.position.system != COORDINATESYSTEM_EQUATORIAL ) {
504 ABORT( status, GENERATEPULSARSIGNALH_EBADCOORDS, GENERATEPULSARSIGNALH_MSGEBADCOORDS );
505 }
506 TRY( LALNormalizeSkyPosition( status->statusPtr, &tmp, &( params->pSigParams->pulsar.position ) ), status );
507 baryinput.alpha = tmp.longitude;
508 baryinput.delta = tmp.latitude;
509 baryinput.dInv = 0.e0; /* following makefakedata_v2 */
510
511 if ( params->pSigParams->orbit.asini > 0 ) {
512 XLALPrintError( "Sorry, binary orbits (asini>0) not supported.\n" );
513 ABORT( status, GENERATEPULSARSIGNALH_EINPUT, GENERATEPULSARSIGNALH_MSGEINPUT );
514 } else {
515 /* LALComputeSky parameters */
516 csParams = ( CSParams * )LALMalloc( sizeof( CSParams ) );
517 csParams->tGPS = params->pSFTParams->timestamps->data;
518 csParams->skyPos = ( REAL8 * )LALMalloc( 2 * sizeof( REAL8 ) );
519 csParams->mObsSFT = numSFTs;
520 csParams->tSFT = params->pSFTParams->Tsft;
521 csParams->edat = params->pSigParams->ephemerides;
522 csParams->baryinput = &baryinput;
523 if ( params->pSigParams->pulsar.spindown ) {
524 csParams->spinDwnOrder = params->pSigParams->pulsar.spindown->length;
525 } else {
526 csParams->spinDwnOrder = 0;
527 }
528 csParams->skyPos[0] = params->pSigParams->pulsar.position.longitude;
529 csParams->skyPos[1] = params->pSigParams->pulsar.position.latitude;
530 csParams->earth = &earth;
531 csParams->emit = &emit;
532
533 /* Call LALComputeSky */
534 TRY( LALComputeSky( status->statusPtr, output->skyConst, 0, csParams ), status );
535 LALFree( csParams->skyPos );
536 LALFree( csParams );
537 }
538
539 /* Set up das, the Detector and Source info */
540 das = ( LALDetAndSource * )LALMalloc( sizeof( LALDetAndSource ) );
541 das->pSource = ( LALSource * )LALMalloc( sizeof( LALSource ) );
542 das->pDetector = params->pSigParams->site;
543 das->pSource->equatorialCoords.latitude = params->pSigParams->pulsar.position.latitude;
544 das->pSource->equatorialCoords.longitude = params->pSigParams->pulsar.position.longitude;
545 das->pSource->orientation = 0.0; /* NOTE THIS FUNCTION COMPUTE F_+ and F_x for ZERO Psi!!! */
546 das->pSource->equatorialCoords.system = params->pSigParams->pulsar.position.system;
547
548 /* loop that calls LALComputeDetAMResponse to find F_+ and F_x at the midpoint of each SFT for ZERO Psi */
549 for ( i = 0; i < numSFTs; i++ ) {
550 /* Find mid point from timestamp, half way through SFT. */
551 midTS = params->pSFTParams->timestamps->data[i];
552 XLALGPSAdd( &midTS, halfTsft );
553 TRY( LALComputeDetAMResponse( status->statusPtr, &response, das, &midTS ), status );
554 output->fPlusZeroPsi[i] = response.plus;
555 output->fCrossZeroPsi[i] = response.cross;
556 }
557 LALFree( das->pSource );
558 LALFree( das );
559
561 RETURN( status );
562} /* LALComputeSkyAndZeroPsiAMResponse */
563
564/* 07/14/04 gam */
565/**
566 * Fast generation of Fake SFTs for a pure pulsar signal.
567 * Uses the output of LALComputeSkyAndZeroPsiAMResponse and the same inputs
568 * as LALGeneratePulsarSignal and LALSignalToSFTs. The fake signal is
569 * Taylor expanded to first order about the midpoint time of each SFT.
570 * Analytic expressions are used to find each SFT
571 */
572void
574 SFTVector **outputSFTs,
575 const SkyConstAndZeroPsiAMResponse *input,
577{
578 INT4 numSFTs; /* number of SFTs */
579 REAL4 N; /* N = number of time-samples that would have been used to generate SFTs directly */
580 INT4 iSFT; /* index that gives which SFT in an SFTVector */
581 INT4 SFTlen; /* number of frequency bins in an SFT */
582 REAL8 tSFT, f0, band, f0Signal, deltaF;
583 REAL4 fPlus, fCross, psi, phi0Signal;
584 /* REAL4 halfAPlus, halfACross, cosPsi, sinPsi; */ /* 10/12/04 gam */
585 REAL4 halfAPlus, halfACross, cos2Psi, sin2Psi;
586 REAL8 realA, imagA, xSum, ySum, xTmp, yTmp; /* xSum, ySum and xTmp are the same as xSum, ySum, and x in LALDemod; yTmp is -y from LALDemod plus phi0Signal */
587 REAL8 realQcc, imagQcc, realPcc, imagPcc, realTmp, imagTmp; /* Pcc is the complex conjugate of P in LALDemod; Qcc is the complex conjugate of Q in LALDemod times exp(i*phi0Signal) */
588 REAL8 kappa; /* kappa = index of freq at midpoint of SFT which is usually not an integer */
589 REAL8 real8TwoPi = ( REAL8 )LAL_TWOPI;
590 REAL8 sin2PiKappa, oneMinusCos2PiKappa;
591 SFTtype *thisSFT, *thisNoiseSFT; /* SFT-pointers */
592 SFTVector *sftvect = NULL; /* return value. For better readability */
593 INT4 j, k, k0, s, spOrder, tmpInt, index0n;
594 INT4 jStart, jEnd, k1;
595 BOOLEAN setToZero = 0; /* 09/07/05 gam; flag that set whether to zero bins not within the Dterms loop */
596 REAL8 smallX = 0.000000001;
597 /* Next are for LUT for trig calls */
598 INT4 indexTrig;
599 REAL8 halfResTrig = ( ( REAL8 )params->resTrig ) / 2.0; /* 10/08/04 gam; fix indexing into trig lookup tables (LUTs) by having table go from -2*pi to 2*pi */
600 REAL8 varTmp, dTmp, dTmp2, sinTmp, cosTmp;
601
604
605 /* fprintf(stdout,"\n Hello from LALFastGeneratePulsarSFTs \n");
606 fflush(stdout); */
607
608 ASSERT( outputSFTs != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL );
609 /* ASSERT (*outputSFTs == NULL, status, GENERATEPULSARSIGNALH_ENONULL, GENERATEPULSARSIGNALH_MSGENONULL); */
610 ASSERT( params != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL );
611 ASSERT( input != NULL, status, GENERATEPULSARSIGNALH_ENULL, GENERATEPULSARSIGNALH_MSGENULL );
612
613 if ( params->pSFTParams->timestamps && params->pSFTParams->noiseSFTs ) {
614 ASSERT( params->pSFTParams->timestamps->length == params->pSFTParams->noiseSFTs->length, status,
615 GENERATEPULSARSIGNALH_ENUMSFTS, GENERATEPULSARSIGNALH_MSGENUMSFTS );
616 }
617
618 /* 10/08/04 gam; fix indexing into trig lookup tables (LUTs) by having table go from -2*pi to 2*pi */
619 if ( params->resTrig > 0 ) {
620 ASSERT( fabs( ( params->trigArg[0] + ( ( REAL8 )LAL_TWOPI ) ) / ( ( REAL8 )LAL_TWOPI ) ) < ( 2.0e-6 * ( ( REAL8 )LAL_TWOPI ) / params->resTrig ), status,
621 GENERATEPULSARSIGNALH_ELUTS, GENERATEPULSARSIGNALH_MSGELUTS );
622 ASSERT( fabs( ( params->trigArg[params->resTrig] - ( ( REAL8 )LAL_TWOPI ) ) / ( ( REAL8 )LAL_TWOPI ) ) < ( 2.0e-6 * ( ( REAL8 )LAL_TWOPI ) / params->resTrig ), status,
623 GENERATEPULSARSIGNALH_ELUTS, GENERATEPULSARSIGNALH_MSGELUTS );
624 }
625
626 /* SFT parameters */
627 tSFT = params->pSFTParams->Tsft; /* SFT duration */
628 deltaF = 1.0 / tSFT; /* frequency resolution */
629 f0 = params->pSigParams->fHeterodyne; /* start frequency */
630 k0 = lround( f0 * tSFT ); /* index of start frequency */
631 band = 0.5 * params->pSigParams->samplingRate; /* frequency band */
632 SFTlen = lround( band * tSFT ); /* number of frequency-bins */
633 numSFTs = params->pSFTParams->timestamps->length; /* number of SFTs */
634
635 if ( ( params->Dterms < 1 ) || ( params->Dterms > SFTlen ) ) {
636 ABORT( status, GENERATEPULSARSIGNALH_EDTERMS, GENERATEPULSARSIGNALH_MSGEDTERMS );
637 }
638
639 /* prepare SFT-vector for return */
640 if ( *outputSFTs == NULL ) {
641 XLAL_CHECK_LAL( status, ( sftvect = XLALCreateSFTVector( numSFTs, SFTlen ) ) != NULL, XLAL_EFUNC );
642 setToZero = 1; /* 09/07/05 gam; allocated memory for the output SFTs, zero bins not within the Dterms loop */
643 } else {
644 sftvect = *outputSFTs; /* Assume memory already allocated for SFTs */
645 setToZero = 0; /* 09/07/05 gam; it's up to the user in this case to initialize bin to zero */
646 }
647
648 /* if noiseSFTs are given: check they are consistent with signal! */
649 if ( params->pSFTParams->noiseSFTs ) {
650 int ret = XLALcheckNoiseSFTs( params->pSFTParams->noiseSFTs, f0, f0 + band, deltaF );
651 if ( ret != XLAL_SUCCESS ) {
652 ABORT( status, XLAL_EFAILED, "XLALcheckNoiseSFTs() failed" );
653 }
654 }
655
656 /* Signal parameters */
657 N = ( REAL4 )( 2 * params->nSamples ); /* N = number of time-samples that would have been used to generate SFTs directly */
658 halfAPlus = 0.5 * N * params->pSigParams->pulsar.aPlus;
659 halfACross = 0.5 * N * params->pSigParams->pulsar.aCross;
660 psi = params->pSigParams->pulsar.psi;
661 /* cosPsi = (REAL4)cos(psi);
662 sinPsi = (REAL4)sin(psi); */ /* 10/12/04 gam */
663 cos2Psi = ( REAL4 )cos( 2.0 * psi );
664 sin2Psi = ( REAL4 )sin( 2.0 * psi );
665 f0Signal = params->pSigParams->pulsar.f0;
666 phi0Signal = params->pSigParams->pulsar.phi0;
667 if ( params->pSigParams->pulsar.spindown ) {
668 spOrder = params->pSigParams->pulsar.spindown->length;
669 } else {
670 spOrder = 0;
671 }
672
673 /* loop that generates each SFT */
674 for ( iSFT = 0; iSFT < numSFTs; iSFT++ ) {
675
676 thisSFT = &( sftvect->data[iSFT] ); /* select the SFT to work on */
677
678 /* find fPlus, fCross, and the real and imaginary parts of the modulated amplitude, realA and imagA */
679 /* fPlus = input->fPlusZeroPsi[iSFT]*cosPsi + input->fCrossZeroPsi[iSFT]*sinPsi;
680 fCross = input->fCrossZeroPsi[iSFT]*cosPsi - input->fPlusZeroPsi[iSFT]*sinPsi; */ /* 10/12/04 gam */
681 fPlus = input->fPlusZeroPsi[iSFT] * cos2Psi + input->fCrossZeroPsi[iSFT] * sin2Psi;
682 fCross = input->fCrossZeroPsi[iSFT] * cos2Psi - input->fPlusZeroPsi[iSFT] * sin2Psi;
683 realA = ( REAL8 )( halfAPlus * fPlus );
684 imagA = ( REAL8 )( halfACross * fCross );
685
686 /* Compute sums used to find the phase at the beginning of each SFT and kappa associated with fOneHalf*/
687 /* xSum and ySum are the same as xSum and ySum in LALDemod */
688 tmpInt = 2 * iSFT * ( spOrder + 1 ) + 1;
689 xSum = 0.0;
690 ySum = 0.0;
691 for ( s = 0; s < spOrder; s++ ) {
692 xSum += params->pSigParams->pulsar.spindown->data[s] * input->skyConst[tmpInt + 2 + 2 * s];
693 ySum += params->pSigParams->pulsar.spindown->data[s] * input->skyConst[tmpInt + 1 + 2 * s];
694 }
695
696 /* find kappa associated with fOneHalf */
697 /* fOneHalf = (f0Signal*input->skyConst[tmpInt] + xSum)/tSFT; */
698 /* Do not need to actually compute this, just kappa */
699 /* kappa = REAL8 index associated with fOneHalf; usually not an integer */
700 kappa = f0Signal * input->skyConst[tmpInt] + xSum;
701
702 if ( params->resTrig > 0 ) {
703 /* if (params->resTrig > 0) use LUT for trig calls to find sin and cos, else will use standard sin and cos */
704
705 /* Compute phase at the beginning of each SFT, called yTmp */
706 /* yTmp is -y from LALDemod plus phi0Signal */
707 /* Qcc is the complex conjugate of Q in LALDemod times exp(i*phi0Signal) */
708 /* Using LUT to find cos(yTmp) and sin(yTmp) */
709 yTmp = phi0Signal / real8TwoPi + f0Signal * input->skyConst[tmpInt - 1] + ySum;
710 varTmp = yTmp - ( INT4 )yTmp;
711 /* indexTrig=(INT4)(varTmp*params->resTrig+0.5); */ /* 10/08/04 gam */
712 indexTrig = lround( ( varTmp + 1.0 ) * halfResTrig );
713 dTmp = real8TwoPi * varTmp - params->trigArg[indexTrig];
714 dTmp2 = 0.5 * dTmp * dTmp;
715 sinTmp = params->sinVal[indexTrig];
716 cosTmp = params->cosVal[indexTrig];
717 imagQcc = sinTmp + dTmp * cosTmp - dTmp2 * sinTmp;
718 realQcc = cosTmp - dTmp * sinTmp - dTmp2 * cosTmp;
719
720 /* Find sin(2*pi*kappa) and 1 - cos(2*pi*kappa) */
721 /* Using LUT to find sin(2*pi*kappa) and 1 - cos(2*pi*kappa) */
722 varTmp = kappa - ( INT4 )kappa;
723 /* indexTrig=(INT4)(varTmp*params->resTrig+0.5); */
724 indexTrig = lround( ( varTmp + 1.0 ) * halfResTrig ); /* 10/08/04 gam */
725 dTmp = real8TwoPi * varTmp - params->trigArg[indexTrig];
726 dTmp2 = 0.5 * dTmp * dTmp;
727 sinTmp = params->sinVal[indexTrig];
728 cosTmp = params->cosVal[indexTrig];
729 sin2PiKappa = sinTmp + dTmp * cosTmp - dTmp2 * sinTmp;
730 oneMinusCos2PiKappa = 1.0 - cosTmp + dTmp * sinTmp + dTmp2 * cosTmp;
731
732 } else {
733 /* if (params->resTrig > 0) use LUT for trig calls to find sin and cos, else will use standard sin and cos */
734
735 /* Compute phase at the beginning of each SFT, called yTmp */
736 /* yTmp is -y from LALDemod plus phi0Signal */
737 /* Qcc is the complex conjugate of Q in LALDemod times exp(i*phi0Signal) */
738 yTmp = phi0Signal + real8TwoPi * ( f0Signal * input->skyConst[tmpInt - 1] + ySum );
739 realQcc = cos( yTmp );
740 imagQcc = sin( yTmp );
741
742 /* Find sin(2*pi*kappa) and 1 - cos(2*pi*kappa) */
743 /* use xTmp as temp storage for 2\pi\kappa; note xTmp = 2\pi(\kappa -k) is used in loop below */
744 xTmp = real8TwoPi * kappa;
745 sin2PiKappa = sin( xTmp );
746 oneMinusCos2PiKappa = 1.0 - cos( xTmp );
747
748 } /* END if (params->resTrig > 0) else ... */
749
750 /* 09/07/05 gam; use Dterms to fill in SFT bins with fake data as per LALDemod else fill in bin with zero */
751 k1 = ( INT4 )kappa - params->Dterms + 1; /* This is the same as k1 in LALDemod */
752 jStart = k1 - k0;
753 if ( jStart < 0 ) {
754 jStart = 0;
755 }
756 jEnd = k1 + 2 * params->Dterms - k0;
757 if ( jEnd > SFTlen ) {
758 jEnd = SFTlen;
759 }
760
761 /* fill in the data */
762 if ( setToZero ) {
763 for ( j = 0; j < jStart; j++ ) {
764 thisSFT->data->data[j] = 0.0;
765 }
766 }
767 /* This is the same as the inner most loop over k in LALDemod */
768 for ( j = jStart; j < jEnd; j++ ) {
769 k = k0 + j; /* k is the index of the frequency associated with index j */
770 /* xTmp is the same as x in LALDemod */
771 xTmp = real8TwoPi * ( kappa - ( ( REAL8 )k ) );
772 /* Pcc is the complex conjugate of P in LALDemod */
773 if ( fabs( xTmp ) < smallX ) {
774 /* If xTmp is small we need correct xTmp->0 limit */
775 realPcc = 1.0;
776 imagPcc = 0.0;
777 } else {
778 realPcc = sin2PiKappa / xTmp;
779 imagPcc = oneMinusCos2PiKappa / xTmp;
780 }
781 realTmp = realQcc * realPcc - imagQcc * imagPcc;
782 imagTmp = realQcc * imagPcc + imagQcc * realPcc;
783 thisSFT->data->data[j] = crectf( ( REAL4 )( realTmp * realA - imagTmp * imagA ), ( REAL4 )( realTmp * imagA + imagTmp * realA ) );
784 } /* END for (j=jStart; j<jEnd; j++) */
785 if ( setToZero ) {
786 for ( j = jEnd; j < SFTlen; j++ ) {
787 thisSFT->data->data[j] = 0.0;
788 }
789 }
790 /* fill in SFT metadata */
791 thisSFT->epoch = params->pSFTParams->timestamps->data[iSFT];
792 thisSFT->f0 = f0; /* start frequency */
793 thisSFT->deltaF = deltaF; /* frequency resolution */
794
795 /* Now add the noise-SFTs if given */
796 if ( params->pSFTParams->noiseSFTs ) {
797 thisNoiseSFT = &( params->pSFTParams->noiseSFTs->data[iSFT] );
798 index0n = lround( ( thisSFT->f0 - thisNoiseSFT->f0 ) * tSFT );
799 for ( j = 0; j < SFTlen; j++ ) {
800 thisSFT->data->data[j] += thisNoiseSFT->data->data[index0n + j];
801 } /* for j < SFTlen */
802 }
803 } /* for iSFT < numSFTs */
804
805 /* prepare SFT-vector for return */
806 if ( *outputSFTs == NULL ) {
807 *outputSFTs = sftvect;
808 } /* else sftvect already points to same memory as *outputSFTs */
809
811 RETURN( status );
812} /* LALFastGeneratePulsarSFTs () */
813
814
815
816/*--------------- some useful helper-functions ---------------*/
817
818/**
819 * Convert earth-frame GPS time into barycentric-frame SSB time for given source.
820 * \note The only fields used in params are: \a site, \a pulsar.position
821 * and \a ephemerides.
822 */
823int
824XLALConvertGPS2SSB( LIGOTimeGPS *SSBout, /**< [out] arrival-time in SSB */
825 LIGOTimeGPS GPSin, /**< [in] GPS-arrival time at detector */
826 const PulsarSignalParams *params /**< define source-location and detector */
827 )
828{
829 XLAL_CHECK( SSBout != NULL, XLAL_EINVAL, "Invalid NULL input 'SSBout'\n" );
830 XLAL_CHECK( params != NULL, XLAL_EINVAL, "Invalid NULL input 'params'\n" );
831
832 BarycenterInput XLAL_INIT_DECL( baryinput );
833 baryinput.site = *( params->site );
834 /* account for a quirk in XLALBarycenter(): -> see documentation of type BarycenterInput */
835 baryinput.site.location[0] /= LAL_C_SI;
836 baryinput.site.location[1] /= LAL_C_SI;
837 baryinput.site.location[2] /= LAL_C_SI;
838 XLAL_CHECK( params->pulsar.position.system == COORDINATESYSTEM_EQUATORIAL, XLAL_EDOM, "Non-equatorial coords not implemented yet\n" );
839
840 SkyPosition tmp = params->pulsar.position;
841 XLALNormalizeSkyPosition( &( tmp.longitude ), &( tmp.latitude ) );
842 baryinput.alpha = tmp.longitude;
843 baryinput.delta = tmp.latitude;
844 baryinput.dInv = 0.e0; /* following makefakedata_v2 */
845
846 baryinput.tgps = GPSin;
847
848
849 EarthState earth;
850 EmissionTime emit;
851
852 int ret = XLALBarycenterEarth( &earth, &GPSin, params->ephemerides );
854
855 ret = XLALBarycenter( &emit, &baryinput, &earth );
857
858 ( *SSBout ) = emit.te;
859
860 return XLAL_SUCCESS;
861
862} /* XLALConvertGPS2SSB() */
863
864/**
865 * Convert barycentric frame SSB time into earth-frame GPS time
866 *
867 * NOTE: this uses simply the inversion-routine used in the original
868 * makefakedata_v2
869 */
870int XLALConvertSSB2GPS( LIGOTimeGPS *GPSout, /**< [out] GPS-arrival-time at detector */
871 LIGOTimeGPS SSBin, /**< [in] input: signal arrival time at SSB */
872 const PulsarSignalParams *params /**< params defining source-location and detector */
873 )
874{
875 XLAL_CHECK( GPSout != NULL, XLAL_EINVAL, "Invalid NULL output-pointer 'GPSout'\n" );
876 XLAL_CHECK( params != NULL, XLAL_EINVAL, "Invalid NULL input 'params'\n" );
877
878 /*
879 * To start root finding, use SSBpulsarparams as guess
880 * (not off by more than 400 secs!
881 */
882 LIGOTimeGPS GPSguess = SSBin;
883 UINT4 flip_flop_counter = 0;
884 INT8 delta;
885
886 /* now find GPS time corresponding to SSBin by iterations */
888 for ( iterations = 0; iterations < 100; iterations++ ) {
889 LIGOTimeGPS SSBofguess;
890
891 /* find SSB time of guess */
892 int ret = XLALConvertGPS2SSB( &SSBofguess, GPSguess, params );
894
895 /* compute difference between that and what we want */
896 delta = XLALGPSToINT8NS( &SSBin ) - XLALGPSToINT8NS( &SSBofguess );
897
898 /* if we are within 1ns of the result increment the flip-flop counter */
899 /* cast delta to "long long" to ensure expected type for llabs() */
900 if ( llabs( ( long long )delta ) == 1 ) {
901 flip_flop_counter ++;
902 }
903
904 /* break if we've converged: let's be strict to < 1 ns ! */
905 /* also break if the flip-flop counter has reached 3 */
906 if ( ( delta == 0 ) || ( flip_flop_counter >= 3 ) ) {
907 break;
908 }
909
910 /* use delta to make next guess */
911 INT8 guess = XLALGPSToINT8NS( &GPSguess );
912 guess += delta;
913
914 XLALINT8NSToGPS( &GPSguess, guess );
915
916 } /* for iterations < 100 */
917
918 /* check for convergence of root finder */
919 if ( iterations == 100 ) {
920 XLAL_ERROR( XLAL_EFAILED, "SSB->GPS iterative conversion failed to converge to <= 1ns within 100 iterations: delta = %"LAL_INT8_FORMAT" ns\n", delta );
921 }
922
923 /* if we exited because of flip-flop and final delta was +1 then round up to the higher value */
924 /* otherwise we are already at the higher value and we do nothing */
925 if ( ( flip_flop_counter == 3 ) && ( delta == +1 ) ) {
926 XLALINT8NSToGPS( &GPSguess, XLALGPSToINT8NS( &GPSguess ) + 1 );
927 }
928
929 /* Now that we've found the GPS time that corresponds to the given SSB time */
930 ( *GPSout ) = GPSguess;
931
932 return XLAL_SUCCESS;
933
934} /* XLALConvertSSB2GPS() */
935
936/**
937 * Generate a REAL4TimeSeries containing a sinusoid with
938 * amplitude 'h0', frequency 'Freq-fHeterodyne' and initial phase 'phi0'.
939 */
942{
944
945 /* set 'name'-field of timeseries to contain the right "channel prefix" for the detector */
946 char *name;
947 XLAL_CHECK_NULL( ( name = XLALGetChannelPrefix( params->site->frDetector.name ) ) != NULL, XLAL_EFUNC );
948
949 /* NOTE: a timeseries of length N*dT has no timestep at N*dT !! (convention) */
950 UINT4 length = ( UINT4 ) ceil( params->samplingRate * params->duration );
951 REAL8 deltaT = 1.0 / params->samplingRate;
952 REAL8 tStart = XLALGPSGetREAL8( &params->startTimeGPS );
953
954 REAL4TimeSeries *ret;
955 XLAL_CHECK_NULL( ( ret = XLALCreateREAL4TimeSeries( name, &( params->startTimeGPS ), params->fHeterodyne, deltaT, &emptyLALUnit, length ) ) != NULL, XLAL_EFUNC );
956 XLALFree( name );
957
958 REAL8 h0 = params->pulsar.aPlus + sqrt( pow( params->pulsar.aPlus, 2 ) - pow( params->pulsar.aCross, 2 ) );
959 REAL8 omH = LAL_TWOPI * ( params->pulsar.f0 - params->fHeterodyne );
960
961 for ( UINT4 i = 0; i < length; i++ ) {
962 REAL8 ti = tStart + i * deltaT;
963 ret->data->data[i] = h0 * sin( omH * ti + params->pulsar.phi0 );
964 }
965
966 /* return final timeseries */
967 return ret;
968
969} /* XLALGenerateLineFeature() */
970
971
972/**
973 * Generate Gaussian noise with standard-deviation sigma, add it to inSeries.
974 *
975 * \note if seed==0, then an INT4 from /dev/urandom is read and used as random-seed, if
976 * this can't be opened or read, an error is returned in this case.
977 *
978 */
979int
981{
982 XLAL_CHECK( inSeries != NULL, XLAL_EINVAL );
983
984 UINT4 numPoints = inSeries->data->length;
985
986 REAL4Vector *v1;
987 XLAL_CHECK( ( v1 = XLALCreateREAL4Vector( numPoints ) ) != NULL, XLAL_EFUNC );
988
989 /*
990 * If seed=0, XLALCreateRandomParams() would resort to using time() with seconds-resolution
991 * to generate a seed, which is unsafe and could easily end up producing identical time-series
992 * on repeated or parallel calls on a cluster.
993 * Therefore we try to read an INT4 from /dev/urandom and use that as our seed.
994 * Note: /dev/random can be slow after the first few accesses, which is why we're using urandom instead.
995 * [Cryptographic safety isn't a concern here at all]
996 */
997 if ( seed == 0 ) {
998 FILE *devrandom;
999 XLAL_CHECK( ( devrandom = fopen( "/dev/urandom", "rb" ) ) != NULL, XLAL_EIO );
1000 if ( fread( ( void * )&seed, sizeof( INT4 ), 1, devrandom ) != 1 ) {
1001 fclose( devrandom );
1002 XLAL_ERROR( XLAL_EIO, "Failed to read 4-byte seed from '/dev/urandom'\n\n" );
1003 }
1004 fclose( devrandom );
1005 } // if seed==0
1006
1007 RandomParams *randpar;
1008 XLAL_CHECK( ( randpar = XLALCreateRandomParams( seed ) ) != NULL, XLAL_EFUNC );
1009
1011
1012 for ( UINT4 i = 0; i < numPoints; i++ ) {
1013 inSeries->data->data[i] += sigma * v1->data[i];
1014 }
1015
1016 /* destroy randpar*/
1017 XLALDestroyRandomParams( randpar );
1018
1019 /* destroy v1 */
1021
1022 return XLAL_SUCCESS;
1023
1024} /* XLALAddGaussianNoise() */
1025
1026
1027
1028/**
1029 * Destroy a MultiREAL4TimeSeries, NULL-robust
1030 */
1031void
1033{
1034 if ( !multiTS ) {
1035 return;
1036 }
1037
1038 UINT4 numDet = multiTS->length;
1039 for ( UINT4 X = 0; X < numDet; X ++ ) {
1041 }
1042
1043 XLALFree( multiTS->data );
1044 XLALFree( multiTS );
1045
1046 return;
1047
1048} // XLALDestroyMultiREAL4TimeSeries()
1049
1050/**
1051 * Destroy a MultiREAL8TimeSeries, NULL-robust
1052 */
1053void
1055{
1056 if ( !multiTS ) {
1057 return;
1058 }
1059
1060 UINT4 numDet = multiTS->length;
1061 for ( UINT4 X = 0; X < numDet; X ++ ) {
1063 }
1064
1065 XLALFree( multiTS->data );
1066 XLALFree( multiTS );
1067
1068 return;
1069
1070} // XLALDestroyMultiREAL8TimeSeries()
1071
1072
1073/* ***********************************************************************
1074 * the following are INTERNAL FUNCTIONS not to be called outside of this
1075 * module
1076 ************************************************************************/
1077
1078/**
1079 * Check that all timestamps given lie within the range [t0, t1]
1080 *
1081 * return: 0 if ok, ERROR if not
1082 */
1083int
1085{
1086 XLAL_CHECK( timestamps != NULL, XLAL_EINVAL, "Invalid NULL input 'timestamps'\n" );
1087 UINT4 numTimestamps = timestamps->length;
1088 XLAL_CHECK( numTimestamps > 0, XLAL_EDOM, "Invalid zero-length vector 'timestamps'\n" );
1089 REAL8 t0R = XLALGPSGetREAL8( &t0 );
1090 REAL8 t1R = XLALGPSGetREAL8( &t1 );
1091 XLAL_CHECK( t1R >= t0R, XLAL_EDOM, "Invalid negative time range: t0=%f must be <= t1=%f]\n", t0R, t1R );
1092
1093 for ( UINT4 i = 0; i < numTimestamps; i ++ ) {
1094 LIGOTimeGPS *ti = &( timestamps->data[i] );
1095
1096 REAL8 tiR = XLALGPSGetREAL8( ti );
1097
1098 REAL8 diff0 = XLALGPSDiff( ti, &t0 );
1099 XLAL_CHECK( diff0 >= 0, XLAL_EDOM, "Timestamp i=%d outside of bounds: t_i = %f < [%f,%f]\n", i, tiR, t0R, t1R );
1100
1101 REAL8 diff1 = XLALGPSDiff( &t1, ti );
1102 XLAL_CHECK( diff1 >= 0, XLAL_EDOM, "Timestamp i=%d outside of bounds: t_i = %f > [%f,%f]\n", i, tiR, t0R, t1R );
1103
1104 } /* for i < numTimestamps */
1105
1106 return XLAL_SUCCESS;
1107
1108} /* XLALcheck_timestamp_bounds() */
1109
1110/**
1111 * Check if frequency-range and resolution of noiseSFTs is consistent with signal-band [f0, f1]
1112 * \note All frequencies f are required to correspond to integer *bins* f/dFreq, ABORT if not
1113 *
1114 * return XLAL_SUCCESS if everything fine, error-code otherwise
1115 */
1116static int
1118{
1119 XLAL_CHECK( sfts != NULL, XLAL_EINVAL, "Invalid NULL input 'sfts'\n" );
1120 XLAL_CHECK( ( f0 >= 0 ) && ( f1 > 0 ) && ( deltaF > 0 ), XLAL_EDOM, "Invalid non-positive frequency input: f0 = %g, f1 = %g, deltaF = %g\n", f0, f1, deltaF );
1121
1122 int ret;
1123
1124 for ( UINT4 i = 0; i < sfts->length; i++ ) {
1125 SFTtype *thisSFT = &( sfts->data[i] );
1126 REAL8 deltaFn = thisSFT->deltaF;
1127 REAL8 fn0 = thisSFT->f0;
1128 REAL8 fn1 = f0 + thisSFT->data->length * deltaFn;
1129
1130 ret = gsl_fcmp( deltaFn, deltaF, eps );
1131 XLAL_CHECK( ret == 0, XLAL_ETOL, "Time-base of noise-SFTs Tsft_n=%f differs from signal-SFTs Tsft=%f\n", 1.0 / deltaFn, 1.0 / deltaF );
1132
1133 XLAL_CHECK( ( f0 >= fn0 ) && ( f1 <= fn1 ), XLAL_EDOM, "Signal frequency-band [%f,%f] is not contained in noise SFTs [%f,%f]\n", f0, f1, fn0, fn1 );
1134
1135 /* all frequencies here must correspond to exact integer frequency-bins (wrt dFreq = 1/TSFT) */
1136 REAL8 binReal = f0 / deltaF;
1137 REAL8 binRounded = round( binReal );
1138 ret = gsl_fcmp( binReal, binRounded, eps );
1139 XLAL_CHECK( ret == 0, XLAL_ETOL, "Signal-band frequency f0/deltaF = %.16g differs from integer bin by more than %g relative deviation.\n", binReal, eps );
1140
1141 binReal = f1 / deltaF;
1142 binRounded = round( binReal );
1143 ret = gsl_fcmp( binReal, binRounded, eps );
1144 XLAL_CHECK( ret == 0, XLAL_ETOL, "Signal-band frequency f1/deltaF = %.16g differs from integer bin by more than %g relative deviation.\n", binReal, eps );
1145
1146 binReal = fn0 / deltaF;
1147 binRounded = round( binReal );
1148 ret = gsl_fcmp( binReal, binRounded, eps );
1149 XLAL_CHECK( ret == 0, XLAL_ETOL, "Noise-SFT start frequency fn0/deltaF = %.16g differs from integer bin by more than %g relative deviation.\n", binReal, eps );
1150
1151 } /* for i < numSFTs */
1152
1153 return XLAL_SUCCESS;
1154
1155} /* XLALcheckNoiseSFTs() */
1156
1157
1158
1159/**
1160 * Yousuke's phase-correction function, taken from makefakedata_v2
1161 */
1162int
1164{
1165 XLAL_CHECK( sft != NULL, XLAL_EINVAL, "Invalid NULL input 'sft'\n" );
1166
1167 REAL8 deltaT = XLALGPSDiff( &( sft->epoch ), &tHeterodyne );
1168 REAL8 deltaFT = deltaT * sft->f0; // freq * time
1169
1170 /* check if we really need to do anything here? (i.e. is deltaT an integer?) */
1171 if ( fabs( deltaFT - ( INT4 ) deltaFT ) > eps ) {
1172 XLALPrintInfo( "XLALcorrect_phase(): we DO need to apply heterodyning phase-correction\n" );
1173
1174 REAL8 deltaPhase = deltaFT * LAL_TWOPI; // 'phase' = freq * time * 2pi
1175
1176 REAL8 cosx = cos( deltaPhase );
1177 REAL8 sinx = sin( deltaPhase );
1178
1179 for ( UINT4 i = 0; i < sft->data->length; i++ ) {
1180 COMPLEX8 fvec1 = sft->data->data[i];
1181 sft->data->data[i] = crectf( crealf( fvec1 ) * cosx - cimagf( fvec1 ) * sinx, cimagf( fvec1 ) * cosx + crealf( fvec1 ) * sinx );
1182 } /* for i < length */
1183
1184 } /* if deltaFT not integer */
1185
1186 return XLAL_SUCCESS;
1187
1188} /* XLALcorrect_phase() */
1189
1190/// @}
void LALComputeSky(LALStatus *status, REAL8 *skyConst, INT8 iSkyCoh, CSParams *params)
Given an input index which refers to the sky patch under consideration, this routine returns the phas...
Definition: ComputeSky.c:65
static LALUnit emptyLALUnit
static REAL8 eps
int j
int k
#define LALMalloc(n)
#define LALFree(p)
static double double delta
#define ABORT(statusptr, code, mesg)
#define XLAL_CHECK_LAL(sp, assertion,...)
#define TRY(func, statusptr)
#define ATTATCHSTATUSPTR(statusptr)
#define ASSERT(assertion, statusptr, code, mesg)
#define DETATCHSTATUSPTR(statusptr)
#define INITSTATUS(statusptr)
#define RETURN(statusptr)
#define ABORTXLAL(sp)
const char * name
Definition: SearchTiming.c:93
LIGOTimeGPSVector * timestamps
coeffs k0
coeffs k1
void XLALDestroyREAL4VectorSequence(REAL4VectorSequence *vecseq)
void LALComputeDetAMResponse(LALStatus *status, LALDetAMResponse *pResponse, const LALDetAndSource *pDetAndSrc, const LIGOTimeGPS *gps)
void XLALDestroyMultiREAL4TimeSeries(MultiREAL4TimeSeries *multiTS)
Destroy a MultiREAL4TimeSeries, NULL-robust.
#define GENERATEPULSARSIGNALH_EDTERMS
Dterms must be greater than zero and less than or equal to half the number of SFT bins.
int XLALConvertGPS2SSB(LIGOTimeGPS *SSBout, LIGOTimeGPS GPSin, const PulsarSignalParams *params)
Convert earth-frame GPS time into barycentric-frame SSB time for given source.
REAL4TimeSeries * XLALGeneratePulsarSignal(const PulsarSignalParams *params)
Generate a time-series at the detector for a given pulsar.
#define GENERATEPULSARSIGNALH_EINPUT
Invalid input-arguments to function.
int XLALConvertSSB2GPS(LIGOTimeGPS *GPSout, LIGOTimeGPS SSBin, const PulsarSignalParams *params)
Convert barycentric frame SSB time into earth-frame GPS time.
void LALGeneratePulsarSignal(LALStatus *status, REAL4TimeSeries **signalvec, const PulsarSignalParams *params)
#define GENERATEPULSARSIGNALH_EBADCOORDS
Current code requires sky position in equatorial coordinates.
REAL4TimeSeries * XLALGenerateLineFeature(const PulsarSignalParams *params)
Generate a REAL4TimeSeries containing a sinusoid with amplitude 'h0', frequency 'Freq-fHeterodyne' an...
SFTVector * XLALSignalToSFTs(const REAL4TimeSeries *signalvec, const SFTParams *params)
Turn the given time-series into SFTs and add noise if given.
#define GENERATEPULSARSIGNALH_ENULL
Arguments contained an unexpected null pointer.
static int XLALcheckNoiseSFTs(const SFTVector *sfts, REAL8 f0, REAL8 f1, REAL8 deltaF)
Check if frequency-range and resolution of noiseSFTs is consistent with signal-band [f0,...
void XLALDestroyMultiREAL8TimeSeries(MultiREAL8TimeSeries *multiTS)
Destroy a MultiREAL8TimeSeries, NULL-robust.
void LALFastGeneratePulsarSFTs(LALStatus *status, SFTVector **outputSFTs, const SkyConstAndZeroPsiAMResponse *input, const SFTandSignalParams *params)
Fast generation of Fake SFTs for a pure pulsar signal.
#define GENERATEPULSARSIGNALH_ELUTS
Lookup tables (LUTs) for trig functions must be defined on domain -2pi to 2pi inclusive.
void LALSignalToSFTs(LALStatus *status, SFTVector **outputSFTs, const REAL4TimeSeries *signalvec, const SFTParams *params)
void LALComputeSkyAndZeroPsiAMResponse(LALStatus *status, SkyConstAndZeroPsiAMResponse *output, const SFTandSignalParams *params)
/deprecated Move to attic? Wrapper for LALComputeSky() and LALComputeDetAMResponse() that finds the s...
static int XLALcheck_timestamp_bounds(const LIGOTimeGPSVector *timestamps, LIGOTimeGPS t0, LIGOTimeGPS t1)
Check that all timestamps given lie within the range [t0, t1].
#define GENERATEPULSARSIGNALH_ENUMSFTS
Inconsistent number of SFTs in timestamps and noise-SFTs.
int XLALAddGaussianNoise(REAL4TimeSeries *inSeries, REAL4 sigma, INT4 seed)
Generate Gaussian noise with standard-deviation sigma, add it to inSeries.
int XLALcorrect_phase(SFTtype *sft, LIGOTimeGPS tHeterodyne)
Yousuke's phase-correction function, taken from makefakedata_v2.
int XLALGenerateSpinOrbitCW(PulsarCoherentGW *sourceSignal, SpinOrbitCWParamStruc *sourceParams)
FIXME: Temporary XLAL-wapper to LAL-function LALGenerateSpinOrbitCW()
int XLALBarycenterEarth(EarthState *earth, const LIGOTimeGPS *tGPS, const EphemerisData *edat)
Computes the position and orientation of the Earth, at some arrival time , specified LIGOTimeGPS inp...
Definition: LALBarycenter.c:78
int XLALBarycenter(EmissionTime *emit, const BarycenterInput *baryinput, const EarthState *earth)
Transforms from detector arrival time in GPS (as specified in the LIGOTimeGPS structure) to pulse em...
#define LAL_TWOPI
unsigned char BOOLEAN
double REAL8
#define XLAL_INIT_DECL(var,...)
int64_t INT8
#define crectf(re, im)
char CHAR
uint32_t UINT4
float complex COMPLEX8
int32_t INT4
float REAL4
#define lalDebugLevel
void XLALFree(void *p)
#define LAL_INT8_FORMAT
int XLALPulsarSimulateCoherentGW(REAL4TimeSeries *output, PulsarCoherentGW *CWsignal, PulsarDetectorResponse *detector)
FIXME: Temporary XLAL-wapper to LAL-function LALPulsarSimulateCoherentGW()
RandomParams * XLALCreateRandomParams(INT4 seed)
void XLALDestroyRandomParams(RandomParams *params)
int XLALNormalDeviates(REAL4Vector *deviates, RandomParams *params)
#define RealFFTPlan
REAL4FFTPlan * XLALCreateForwardREAL4FFTPlan(UINT4 size, int measurelvl)
void XLALDestroyREAL4FFTPlan(REAL4FFTPlan *plan)
int XLALREAL4ForwardFFT(COMPLEX8Vector *output, const REAL4Vector *input, const REAL4FFTPlan *plan)
LIGOTimeGPSVector * XLALMakeTimestamps(LIGOTimeGPS tStart, REAL8 Tspan, REAL8 Tsft, REAL8 Toverlap)
Given a start-time, Tspan, Tsft and Toverlap, returns a list of timestamps covering this time-stretch...
CHAR * XLALGetChannelPrefix(const CHAR *name)
Find the valid CW detector prefix.
Definition: SFTnaming.c:203
SFTVector * XLALCreateSFTVector(UINT4 numSFTs, UINT4 numBins)
XLAL function to create an SFTVector of numSFT SFTs with SFTlen frequency-bins (which will be allocat...
Definition: SFTtypes.c:230
void XLALDestroyTimestampVector(LIGOTimeGPSVector *vect)
De-allocate a LIGOTimeGPSVector.
Definition: SFTtimestamps.c:69
void XLALNormalizeSkyPosition(double *restrict longitude, double *restrict latitude)
void LALNormalizeSkyPosition(LALStatus *stat, SkyPosition *posOut, const SkyPosition *posIn)
COORDINATESYSTEM_EQUATORIAL
REAL4TimeSeries * XLALCreateREAL4TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
void XLALDestroyREAL4TimeSeries(REAL4TimeSeries *series)
REAL4Vector * XLALCreateREAL4Vector(UINT4 length)
void XLALDestroyREAL4Vector(REAL4Vector *vector)
REAL8Vector * XLALCreateREAL8Vector(UINT4 length)
void XLALDestroyREAL8Vector(REAL8Vector *vector)
#define xlalErrno
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_SUCCESS
XLAL_EFUNC
XLAL_ETOL
XLAL_EDOM
XLAL_EIO
XLAL_EINVAL
XLAL_EFAILED
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
REAL8 XLALGPSGetREAL8(const LIGOTimeGPS *epoch)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
LIGOTimeGPS * XLALINT8NSToGPS(LIGOTimeGPS *epoch, INT8 ns)
INT8 XLALGPSToINT8NS(const LIGOTimeGPS *epoch)
float data[BLOCKSIZE]
Definition: hwinject.c:360
out
int deltaF
int N
double t0
Basic input structure to LALBarycenter.c.
REAL8 alpha
Source right ascension in ICRS J2000 coords (radians).
REAL8 dInv
1/(distance to source), in 1/sec.
REAL8 delta
Source declination in ICRS J2000 coords (radians)
LALDetector site
detector site info.
CHAR name[LALNameLength]
COMPLEX8Sequence * data
A vector of COMPLEX8FrequencySeries.
COMPLEX8FrequencySeries * data
Pointer to the data array.
UINT4 length
Number of elements in array.
COMPLEX8 * data
This structure contains the parameters for the LALComputeSky() routine.
Definition: ComputeSky.h:131
EmissionTime * emit
TO BE DOCUMENTED.
Definition: ComputeSky.h:138
INT8 spinDwnOrder
The maximal number of spindown parameters per spindown parameter set.
Definition: ComputeSky.h:132
LIGOTimeGPS * tGPS
An array containing the GPS times of the first datum from each SFT.
Definition: ComputeSky.h:135
REAL8 tSFT
The timescale of one SFT.
Definition: ComputeSky.h:134
EarthState * earth
TO BE DOCUMENTED.
Definition: ComputeSky.h:139
BarycenterInput * baryinput
A switch which turns modulation on/off.
Definition: ComputeSky.h:137
REAL8 * skyPos
The array containing the sky patch coordinates.
Definition: ComputeSky.h:136
const EphemerisData * edat
ephemeris data
Definition: ComputeSky.h:140
INT8 mObsSFT
The number of SFTs in the observation time.
Definition: ComputeSky.h:133
Basic output structure of LALBarycenterEarth.c.
Basic output structure produced by LALBarycenter.c.
LIGOTimeGPS te
pulse emission time (TDB); also sometimes called `‘arrival time (TDB) of same wavefront at SSB’'
LALSource * pSource
const LALDetector * pDetector
REAL8 location[3]
SkyPosition equatorialCoords
REAL8 orientation
INT4 gpsNanoSeconds
A vector of 'timestamps' of type LIGOTimeGPS.
Definition: SFTfileIO.h:188
LIGOTimeGPS * data
array of timestamps
Definition: SFTfileIO.h:193
UINT4 length
number of timestamps
Definition: SFTfileIO.h:192
A collection of (multi-IFO) REAL4 time-series.
A collection of (multi-IFO) REAL8 time-series.
This structure stores a representation of a plane gravitational wave propagating from a particular po...
This structure contains information required to determine the response of a detector to a gravitation...
Input parameters to GeneratePulsarSignal(), defining the source and the time-series.
CHAR name[LALNameLength]
REAL4Sequence * data
LIGOTimeGPS epoch
REAL4 * data
Parameters defining the SFTs to be returned from LALSignalToSFTs().
Parameters defining the pulsar signal and SFTs used by LALFastGeneratePulsarSFTs().
Sky Constants and beam pattern response functions used by LALFastGeneratePulsarSFTs().
REAL8 * skyConst
vector of A and B sky constants
REAL4 * fPlusZeroPsi
vector of Fplus values for psi = 0 at midpoint of each SFT
REAL4 * fCrossZeroPsi
vector of Fcross values for psi = 0 at midpoint of each SFT
REAL8 longitude
REAL8 latitude
CoordinateSystem system
This structure stores the parameters for constructing a gravitational waveform with both a Taylor-pol...
double duration
double psi
double deltaT