LALSimulation  5.4.0.1-fe68b98
LALSimulation.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 J. Creighton, T. Creighton
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with with program; see the file COPYING. If not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301 USA
18  */
19 
20 /*
21  * ============================================================================
22  *
23  * Preamble
24  *
25  * ============================================================================
26  */
27 
28 
29 #include <math.h>
30 #include <gsl/gsl_sf_expint.h>
31 #include <lal/LALSimulation.h>
32 #include <lal/LALDetectors.h>
33 #include <lal/DetResponse.h>
34 #include <lal/Date.h>
35 #include <lal/Units.h>
36 #include <lal/TimeDelay.h>
37 #include <lal/SkyCoordinates.h>
38 #include <lal/TimeSeries.h>
39 #include <lal/TimeSeriesInterp.h>
40 #include <lal/FrequencySeries.h>
41 #include <lal/TimeFreqFFT.h>
42 #include <lal/Window.h>
43 #include "check_series_macros.h"
44 
45 /*
46  * ============================================================================
47  *
48  * Utilities
49  *
50  * ============================================================================
51  */
52 
53 
54 /*
55  * Note: this function will round really really large numbers "up" to 0.
56  */
57 
58 
59 static unsigned long round_up_to_power_of_two(unsigned long x)
60 {
61  unsigned n;
62 
63  x--;
64  /* if x shares bits with x + 1, then x + 1 is not a power of 2 */
65  for(n = 1; n && (x & (x + 1)); n *= 2)
66  x |= x >> n;
67 
68  return x + 1;
69 }
70 
71 
72 /**
73  * @brief Turn a detector prefix string into a LALDetector structure.
74  * @details
75  * The first two characters of the input string are used as the instrument
76  * name, which allows channel names in the form `H1:LSC-STRAIN` to be used.
77  * The return value is a pointer into the lalCachedDetectors array, so
78  * modifications to the contents are global. Make a copy of the structure if
79  * you want to modify it safely.
80  * @param[in] string The detector prefix string.
81  * @returns
82  * A cached LALDetector structure corresponding to the supplied prefix.
83  * @retval NULL Unrecognized prefix string.
84  */
86  const char *string
87 )
88 {
89  int i;
90 
91  for(i = 0; i < LAL_NUM_DETECTORS; i++)
92  if(!strncmp(string, lalCachedDetectors[i].frDetector.prefix, 2))
93  return &lalCachedDetectors[i];
94 
95  XLALPrintError("%s(): error: can't identify instrument from string \"%s\"\n", __func__, string);
97 }
98 
99 
100 /*
101  * ============================================================================
102  *
103  * Injection Machinery
104  *
105  * ============================================================================
106  */
107 
108 
109 #if 0
110 REAL8TimeSeries * XLALSimQuasiPeriodicInjectionREAL8TimeSeries( REAL8TimeSeries *aplus, REAL8TimeSeries *across, REAL8TimeSeries *frequency, REAL8TimeSeries *phi, REAL8TimeSeries *psi, SkyPosition *position, LALDetector *detector, EphemerisData *ephemerides, LIGOTimeGPS *start, REAL8 deltaT, UINT4 length, COMPLEX16FrequencySeries *response )
111 {
112  REAL8 slowDeltaT = 600.0; /* 10 minutes */
113  UINT4 slowLength;
114 
115  /* sanity checks */
116 
117  /* SET UP LOOK-UP TABLES */
118 
119  /* construct time-dependent time-delay look-up table */
120  /* uses ephemeris */
121  /* sampled slowDeltaT */
122  /* starts 8 min before injection start, ends 8 min after injection end */
123  slowLength = floor(0.5 + (length*deltaT + 16.0)/slowDeltaT);
124 
125  /* construct time-dependent polarization response look-up table */
126  /* sampled at slowDeltaT */
127  /* starts 8 min before injection start, ends 8 min after injection end */
128  fplus;
129  fcross;
130 
131 
132 
133  /* apply time-dependent time-delay */
134  /* apply time-dependent polarization response */
135  /* apply frequency-dependent detector response */
136  for ( j = 0; j < injection->length; ++j ) {
137  REAL8 dt;
138  REAL8 fpl;
139  REAL8 fcr;
140  REAL8 apl;
141  REAL8 acr;
142  REAL8 freq;
143  REAL8 phase;
144  REAL8 pol;
145 
146  /* interpolate to get dt, fpl, fcr */
147 
148  /* interpolate to get apl, acr, freq, phase, pol */
149  /* but not at detector time: detector time plus dt */
150 
151  /* rotate fpl and fcr using pol */
152 
153  /* adjust apl, acr, and phase by detector response at freq */
154 
155  injection->data->data[j] = fpl*apl*cos(phase) + fcr*acr*sin(phase); /* or something like this */
156 
157  }
158 
159 
160 
161 }
162 #endif
163 
164 
166  double welch_factor;
167  double T; /*armlen/(c*deltaT)*/
168  double armcos;
169 };
170 
171 
172 /**
173  * This kernel function incorporates beyond-long-wavelength effects.
174  * The derivation of the formula is summarized in
175  * Soichiro Morisaki, "Kernel function for injection of signals with short
176  * wavelength", LIGO-T1800394.
177  */
178 static void highfreq_kernel(double *cached_kernel, int kernel_length, double residual, void *data)
179 {
180  struct highfreq_kernel_data *kernel_data = data;
181  double welch_factor = kernel_data->welch_factor;
182  double T = kernel_data->T;
183  double armcos = kernel_data->armcos;
184  double *stop = cached_kernel + kernel_length;
185  int i;
186 
187  for(i = -(kernel_length - 1) / 2; cached_kernel < stop; i++) {
188  double x = i + residual;
189  double y = welch_factor * x;
190  if(fabs(y) < 1.) {
191  double Si1 = gsl_sf_Si(LAL_PI * (x + T * armcos));
192  double Si2 = gsl_sf_Si(LAL_PI * (x + T));
193  double Si3 = gsl_sf_Si(LAL_PI * (x - T));
194  *cached_kernel++ = ((Si2 - Si1) / (T * (1. - armcos)) + (Si1 - Si3) / (T * (1. + armcos))) * (1. - y * y) / LAL_TWOPI;
195  } else
196  *cached_kernel++ = 0.;
197  }
198 };
199 
200 
201 /**
202  * @brief Transforms the waveform polarizations into a detector strain
203  * @details
204  * This routine takes the plus and cross waveform polarizations, along
205  * with the sky position, polarization angle, and detector structure,
206  * and computes the external strain on the detector.
207  *
208  * The input time series should have their epochs set to the start of
209  * those time series at the geocetre (for simplicity the epochs must be
210  * the same, and they must have the same length and sample rates)
211  *
212  * @param[in] hplus Pointer to a REAL8TimeSeries containing the plus polarization waveform
213  * @param[in] hcross Pointer to a REAL8TimeSeries containing the cross polarization waveform
214  * @param[in] right_ascension The right ascension of the source in radians
215  * @param[in] declination The declination of the source in radians
216  * @param[in] psi The polarization angle giving the orientation of the wave co-ordinate system in radians
217  * @param[in] detector Pointer to a LALDetector structure for the detector into which the injection is destined to be injected
218  *
219  * @returns
220  * The strain time series as seen in the detector, with the epoch set to
221  * the start of the time series at that detector. The output time series
222  * units are the same as the two input time series (which must both have
223  * the same sample units).
224  *
225  * @retval NULL Failure
226  *
227  * @note
228  * A 19-sample Welch-windowed sinc kernel is used for sub-sample
229  * interpolation. See XLALREAL8TimeSeriesInterpEval() for more
230  * information, and consider the frequency response of this kernel when
231  * using this function with injections whose frequency content approaches
232  * the Nyquist frequency.
233  * @n@n
234  * The geometric delay and antenna response are only recalculated every 250
235  * ms --- the Earth's rotation is modelled as discontinuous jumps occurring
236  * at a rate of 4 Hz. The Earth rotates at 7e-5 rad/s, therefore given a
237  * radius of 6e6 m and c=3e8 m/s, the maximum geometric speed for points on
238  * the surface is about 1.5 us/s. Updating the detector response and
239  * geometric delay every 250 ms means the antenna response is accurate to
240  * about +/- 20 urad and the geometric delay to about +/- 300 ns (about
241  * 0.01 sample at 32 kHz). Because we use UTC (instead of UT1) sidereal
242  * time is only accurate to +/- 900 ms, so assuming the Earth's orientation
243  * to be fixed for 250 ms at a time is not the dominant source of Earth
244  * orientation error in these calculations, but one should be aware of the
245  * periodic nature of the updates if extreme phase stability is required.
246  * @n@n
247  * The output time series is padded to capture the interpolation kernel
248  * structure resulting from possible sharp edges at the start or end of the
249  * input time series data. Neglecting the padding for the interpolation
250  * kernel's impulse response, the output time series is, in general, not
251  * the same duration as the input time series due to Doppler compression or
252  * resulting from Earth rotation.
253  */
255  const REAL8TimeSeries *hplus,
256  const REAL8TimeSeries *hcross,
257  REAL8 right_ascension,
258  REAL8 declination,
259  REAL8 psi,
260  const LALDetector *detector
261 )
262 {
263  /* mean arm length in samples */
264  const double arm_length_samples = (detector->frDetector.xArmMidpoint + detector->frDetector.yArmMidpoint) / (LAL_C_SI * hplus->deltaT);
265  /* kernel length in samples. increase by 28 times the arm length
266  * to accomodate the additional signal delay. */
267  const int kernel_length = 67 + 48 * lround(2.0 * arm_length_samples);
268  /* 0.25 s or 1 sample whichever is larger */
269  const unsigned det_resp_interval = round(0.25 / hplus->deltaT) < 1 ? 1 : round(0.25 / hplus->deltaT);
270  REAL8TimeSeries *xsignal = NULL;
271  REAL8TimeSeries *ysignal = NULL;
272  LALREAL8TimeSeriesInterp *xinterp = NULL;
273  LALREAL8TimeSeriesInterp *yinterp = NULL;
274  struct highfreq_kernel_data xdata;
275  struct highfreq_kernel_data ydata;
276  double fxplus = XLAL_REAL8_FAIL_NAN;
277  double fxcross = XLAL_REAL8_FAIL_NAN;
278  double fyplus = XLAL_REAL8_FAIL_NAN;
279  double fycross = XLAL_REAL8_FAIL_NAN;
280  double geometric_delay = XLAL_REAL8_FAIL_NAN;
281  LIGOTimeGPS t; /* a time */
282  double dt; /* an offset */
283  char *name;
284  REAL8TimeSeries *h = NULL;
285  unsigned i;
286 
287  /* check input */
288 
289  LAL_CHECK_VALID_SERIES(hplus, NULL);
290  LAL_CHECK_VALID_SERIES(hcross, NULL);
291  LAL_CHECK_CONSISTENT_TIME_SERIES(hplus, hcross, NULL);
292  /* test that the input's length can be treated as a signed valued
293  * without overflow, and that adding the kernel length plus an
294  * Earth diameter's worth of samples won't overflow */
295  if((int) hplus->data->length < 0 || (int) (hplus->data->length + kernel_length + 2.0 * LAL_REARTH_SI / LAL_C_SI / hplus->deltaT) < 0) {
296  XLALPrintError("%s(): error: input series too long\n", __func__);
298  }
299 
300  /* generate name */
301 
302  name = XLALMalloc(strlen(detector->frDetector.prefix) + 11);
303  if(!name)
304  goto error;
305  sprintf(name, "%s injection", detector->frDetector.prefix);
306 
307  /* allocate output time series. the time series' duration is
308  * adjusted to account for Doppler-induced dilation of the
309  * waveform, and is padded to accomodate ringing of the
310  * interpolation kernel. the sign of dt follows from the
311  * observation that time stamps in the output time series are
312  * mapped to time stamps in the input time series by adding the
313  * output of XLALTimeDelayFromEarthCenter(), so if that number is
314  * larger at the start of the waveform than at the end then the
315  * output time series must be longer than the input. (the Earth's
316  * rotation is not super-luminal so we don't have to account for
317  * time reversals in the mapping) */
318 
319  /* time (at geocentre) of end of waveform */
320  t = hplus->epoch;
321  if(!XLALGPSAdd(&t, hplus->data->length * hplus->deltaT)) {
322  XLALFree(name);
323  goto error;
324  }
325  /* change in geometric delay from start to end */
326  dt = XLALTimeDelayFromEarthCenter(detector->location, right_ascension, declination, &hplus->epoch) - XLALTimeDelayFromEarthCenter(detector->location, right_ascension, declination, &t);
327  /* allocate, lengthen sequence to incorporate time delay caused by
328  * beyond-long-wavelength effect */
329  h = XLALCreateREAL8TimeSeries(name, &hplus->epoch, hplus->f0, hplus->deltaT, &hplus->sampleUnits, (int) hplus->data->length + kernel_length - 1 + ceil(dt / hplus->deltaT) + lround(4.0 * arm_length_samples));
330  XLALFree(name);
331  if(!h)
332  goto error;
333 
334  /* shift the epoch so that the start of the input time series
335  * passes through this detector at the time of the sample at offset
336  * (kernel_length-1)/2 we assume the kernel is sufficiently short
337  * that it doesn't matter whether we compute the geometric delay at
338  * the start or middle of the kernel. */
339 
340  geometric_delay = XLALTimeDelayFromEarthCenter(detector->location, right_ascension, declination, &h->epoch);
341  if(XLAL_IS_REAL8_FAIL_NAN(geometric_delay))
342  goto error;
343  if(!XLALGPSAdd(&h->epoch, geometric_delay - (kernel_length - 1) / 2 * h->deltaT))
344  goto error;
345 
346  /* round epoch to an integer sample boundary so that
347  * XLALSimAddInjectionREAL8TimeSeries() can use no-op code path.
348  * note: we assume a sample boundary occurs on the integer second.
349  * if this isn't the case (e.g, time-shifted injections or some GEO
350  * data) that's OK, but we might end up paying for a second
351  * sub-sample time shift when adding the the time series into the
352  * target data stream in XLALSimAddInjectionREAL8TimeSeries().
353  * don't bother checking for errors, this is changing the timestamp
354  * by less than 1 sample, if we're that close to overflowing it'll
355  * be caught in the loop below. */
356 
357  dt = XLALGPSModf(&dt, &h->epoch);
358  XLALGPSAdd(&h->epoch, round(dt / h->deltaT) * h->deltaT - dt);
359 
360  /* Compute signals at the times of samples in hplus in advance.
361  * It reduces the computational cost for interpolation */
362 
363  xsignal = XLALCreateREAL8TimeSeries("xsignal", &hplus->epoch, hplus->f0, hplus->deltaT, &hplus->sampleUnits, (int) hplus->data->length);
364  ysignal = XLALCreateREAL8TimeSeries("ysignal", &hplus->epoch, hplus->f0, hplus->deltaT, &hplus->sampleUnits, (int) hplus->data->length);
365  for(i = 0; i < hplus->data->length; i++) {
366  t = hplus->epoch;
367  if(!XLALGPSAdd(&t, i * hplus->deltaT))
368  goto error;
369  /* Compute detector's response. Here the geometric delay
370  * from geocenter is neglected since it is small compared
371  * to the rotational period of the Earth */
372  if(!(i % det_resp_interval)) {
373  double armlen = XLAL_REAL8_FAIL_NAN;
374  double xcos = XLAL_REAL8_FAIL_NAN;
375  double ycos = XLAL_REAL8_FAIL_NAN;
376  XLALComputeDetAMResponseParts(&armlen, &xcos, &ycos, &fxplus, &fyplus, &fxcross, &fycross, detector, right_ascension, declination, psi, XLALGreenwichMeanSiderealTime(&t));
377  }
378  xsignal->data->data[i] = fxplus * hplus->data->data[i] + fxcross * hcross->data->data[i];
379  ysignal->data->data[i] = fyplus * hplus->data->data[i] + fycross * hcross->data->data[i];
380  if(XLAL_IS_REAL8_FAIL_NAN(xsignal->data->data[i]) || XLAL_IS_REAL8_FAIL_NAN(ysignal->data->data[i]))
381  goto error;
382  }
383 
384  /* initialize interpolators. */
385 
386  /* use filtering interpolators. see TimeSeriesInterp.c for
387  * meaning of welch_factor */
388  xdata.welch_factor = ydata.welch_factor = 1.0 / ((kernel_length - 1.) / 2. + 1.);
389  xinterp = XLALREAL8TimeSeriesInterpCreate(xsignal, kernel_length, highfreq_kernel, &xdata);
390  yinterp = XLALREAL8TimeSeriesInterpCreate(ysignal, kernel_length, highfreq_kernel, &ydata);
391  if(!xinterp || !yinterp)
392  goto error;
393 
394  /* compute output sample by sample */
395  /* FIXME: Now xdata and ydata are not renewed until geometric delay
396  * changes significantly. This can cause systematic errors. For
397  * example, if the detector is on the North pole, xdata and ydata
398  * are never renewed although armcos can be changing. */
399 
400  for(i = 0; i < h->data->length; i++) {
401  /* time of sample in detector */
402  t = h->epoch;
403  if(!XLALGPSAdd(&t, i * h->deltaT))
404  goto error;
405 
406  /* geometric delay from geocentre and highfreq_kernel_data */
407  if(!(i % det_resp_interval)) {
408  geometric_delay = -XLALTimeDelayFromEarthCenter(detector->location, right_ascension, declination, &t);
409  /* Compute highfreq_kernel_data */
410  double armlen = XLAL_REAL8_FAIL_NAN;
411  XLALComputeDetAMResponseParts(&armlen, &xdata.armcos, &ydata.armcos, &fxplus, &fyplus, &fxcross, &fycross, detector, right_ascension, declination, psi, XLALGreenwichMeanSiderealTime(&t));
412  armlen /= LAL_C_SI * h->deltaT;
413  xdata.T = armlen;
414  ydata.T = armlen;
415  }
416  if(XLAL_IS_REAL8_FAIL_NAN(geometric_delay))
417  goto error;
419  goto error;
420 
421  /* time of sample at geocentre */
422  if(!XLALGPSAdd(&t, geometric_delay))
423  goto error;
424 
425  /* evaluate linear combination of interpolators */
426  h->data->data[i] = XLALREAL8TimeSeriesInterpEval(xinterp, &t, 0) + XLALREAL8TimeSeriesInterpEval(yinterp, &t, 0);
427 
429  goto error;
430  }
431 
432  /* done */
437  return h;
438 
439 error:
446 }
447 
448 
449 /**
450  * @brief Adds a detector strain time series to detector data.
451  * @details
452  * Essentially a wrapper for XLALAddREAL8TimeSeries(), but performs
453  * sub-sample re-interpolation to adjust the source time series epoch to
454  * lie on an integer sample boundary in the target time series. This
455  * transformation is done in the frequency domain, so it is convenient to
456  * allow a response function to be applied at the same time. Passing NULL
457  * for the response function turns this feature off (i.e., uses a unit
458  * response).
459  *
460  * This function accepts source and target time series whose units are not
461  * the same, and allows the two time series to be herterodyned (although it
462  * currently requires them to have the same heterodyne frequency).
463  *
464  * @param[in,out] target Pointer to the time series into which the strain will
465  * be added
466  *
467  * @param[in,out] h Pointer to the time series containing the detector strain
468  * (the strain data is modified by this routine)
469  *
470  * @param[in] response Pointer to the response function transforming strain to
471  * detector output units, or NULL for unit response.
472  *
473  * @retval 0 Success
474  * @retval <0 Failure
475  *
476  * @attention
477  * The source time series is modified in place by this function!
478  */
480  REAL8TimeSeries *target,
481  REAL8TimeSeries *h,
482  const COMPLEX16FrequencySeries *response
483 )
484 {
485  /* 1 ns is about 10^-5 samples at 16384 Hz */
486  const double noop_threshold = 1e-4; /* samples */
487  /* the source time series is padded with at least this many 0's at
488  * the start and end before re-interpolation in an attempt to
489  * suppress aperiodicity artifacts, and 1/2 this many samples is
490  * clipped from the start and end afterwards */
491  const unsigned aperiodicity_suppression_buffer = 32768;
492  double start_sample_int;
493  double start_sample_frac;
494 
495  /* check input */
496 
497  /* FIXME: since we route the source time series through the
498  * frequency domain, it might not be hard to adjust the heterodyne
499  * frequency and sample rate to match the target time series
500  * instead of making it an error if they don't match. */
501 
502  if(h->deltaT != target->deltaT || h->f0 != target->f0) {
503  XLALPrintError("%s(): error: input sample rates or heterodyne frequencies do not match\n", __func__);
505  }
506 
507  /* compute the integer and fractional parts of the sample index in
508  * the target time series on which the source time series begins.
509  * modf() returns integer and fractional parts that have the same
510  * sign, e.g. -3.9 --> -3 + -0.9. we adjust these so that the
511  * magnitude of the fractional part is not greater than 0.5, e.g.
512  * -3.9 --> -4 + 0.1, so that we never do more than 1/2 a sample of
513  * re-interpolation. I don't know if really makes any difference,
514  * though */
515 
516  start_sample_frac = modf(XLALGPSDiff(&h->epoch, &target->epoch) / target->deltaT, &start_sample_int);
517  if(start_sample_frac < -0.5) {
518  start_sample_frac += 1.0;
519  start_sample_int -= 1.0;
520  } else if(start_sample_frac > +0.5) {
521  start_sample_frac -= 1.0;
522  start_sample_int += 1.0;
523  }
524 
525  /* perform sub-sample interpolation if needed */
526 
527  if(fabs(start_sample_frac) > noop_threshold || response) {
528  COMPLEX16FrequencySeries *tilde_h;
529  REAL8FFTPlan *plan;
530  REAL8Window *window;
531  unsigned i;
532 
533  /* extend the source time series by adding the
534  * "aperiodicity padding" to the start and end. for
535  * efficiency's sake, make sure the new length is a power
536  * of two, and don't forget to adjust the start index in
537  * the target time series. */
538 
539  i = round_up_to_power_of_two(h->data->length + 2 * aperiodicity_suppression_buffer);
540  if(i < h->data->length) {
541  /* integer overflow */
542  XLALPrintError("%s(): error: source time series too long\n", __func__);
544  }
545  start_sample_int -= (i - h->data->length) / 2;
546  if(!XLALResizeREAL8TimeSeries(h, -(int) (i - h->data->length) / 2, i))
548 
549  /* transform source time series to frequency domain. the
550  * FFT function populates the frequency series' metadata
551  * with the appropriate values. */
552 
553  tilde_h = XLALCreateCOMPLEX16FrequencySeries(NULL, &h->epoch, 0, 0, &lalDimensionlessUnit, h->data->length / 2 + 1);
555  if(!tilde_h || !plan) {
559  }
560  i = XLALREAL8TimeFreqFFT(tilde_h, h, plan);
562  if(i) {
565  }
566 
567  /* apply sub-sample time correction and optional response
568  * function */
569 
570  for(i = 0; i < tilde_h->data->length; i++) {
571  const double f = tilde_h->f0 + i * tilde_h->deltaF;
572  COMPLEX16 fac;
573 
574  /* phase for sub-sample time correction */
575 
576  fac = cexp(-I * LAL_TWOPI * f * start_sample_frac * target->deltaT);
577 
578  /* divide the source by the response function. if
579  * a frequency is required that lies outside the
580  * domain of definition of the response function,
581  * then the response is assumed equal to its value
582  * at the nearest edge of the domain of definition.
583  * within the domain of definition, frequencies are
584  * rounded to the nearest bin. if the response
585  * function is zero in some bin, then the source
586  * data is zeroed in that bin (instead of dividing
587  * by 0). */
588 
589  /* FIXME: should we use GSL to construct an
590  * interpolator for the modulus and phase as
591  * functions of frequency, and use that to evaluate
592  * the response? instead of rounding to nearest
593  * bin? */
594 
595  if(response) {
596  int j = floor((f - response->f0) / response->deltaF + 0.5);
597  if(j < 0)
598  j = 0;
599  else if((unsigned) j > response->data->length - 1)
600  j = response->data->length - 1;
601  if(response->data->data[j] == 0.0)
602  fac = 0.0;
603  else
604  fac /= response->data->data[j];
605  }
606 
607  /* apply factor */
608 
609  tilde_h->data->data[i] *= fac;
610  }
611 
612  /* adjust DC and Nyquist components. the DC component must
613  * always be real-valued. because we have adjusted the
614  * source time series to have a length that is an even
615  * integer (we've made it a power of 2) the Nyquist
616  * component must also be real valued. */
617 
618  if(response) {
619  /* a response function has been provided. zero the
620  * DC and Nyquist components */
621  if(tilde_h->f0 == 0.0)
622  tilde_h->data->data[0] = 0.0;
623  tilde_h->data->data[tilde_h->data->length - 1] = 0.0;
624  } else {
625  /* no response has been provided. set the phase of
626  * the DC component to 0, set the imaginary
627  * component of the Nyquist to 0 */
628  if(tilde_h->f0 == 0.0)
629  tilde_h->data->data[0] = cabs(tilde_h->data->data[0]);
630  tilde_h->data->data[tilde_h->data->length - 1] = creal(tilde_h->data->data[tilde_h->data->length - 1]);
631  }
632 
633  /* return to time domain */
634 
636  if(!plan) {
639  }
640  i = XLALREAL8FreqTimeFFT(h, tilde_h, plan);
643  if(i)
645 
646  /* the deltaT can get "corrupted" by floating point
647  * round-off during its trip through the frequency domain.
648  * since this function starts by confirming that the sample
649  * rate of the source matches that of the target time
650  * series, we can use the target series' sample rate to
651  * reset the source's sample rate to its original value.
652  * but we do a check to make sure we're not masking a real
653  * bug */
654 
655  if(fabs(h->deltaT - target->deltaT) / target->deltaT > 1e-12) {
656  XLALPrintError("%s(): error: oops, internal sample rate mismatch\n", __func__);
658  }
659  h->deltaT = target->deltaT;
660 
661  /* set source epoch from target epoch and integer sample
662  * offset */
663 
664  h->epoch = target->epoch;
665  XLALGPSAdd(&h->epoch, start_sample_int * target->deltaT);
666 
667  /* clip half of the "aperiodicity padding" from the start
668  * and end of the source time series in a continuing effort
669  * to suppress aperiodicity artifacts. */
670 
671  if(!XLALResizeREAL8TimeSeries(h, aperiodicity_suppression_buffer / 2, h->data->length - aperiodicity_suppression_buffer))
673 
674  /* apply a Tukey window whose tapers lie within the
675  * remaining aperiodicity padding. leaving one sample of
676  * the aperiodicty padding untouched on each side of the
677  * original time series because the data might have been
678  * shifted into it */
679 
680  window = XLALCreateTukeyREAL8Window(h->data->length, (double) (aperiodicity_suppression_buffer - 2) / h->data->length);
681  if(!window)
683  for(i = 0; i < h->data->length; i++)
684  h->data->data[i] *= window->data->data[i];
685  XLALDestroyREAL8Window(window);
686  }
687 
688  /* add source time series to target time series */
689 
690  if(!XLALAddREAL8TimeSeries(target, h))
692 
693  /* done */
694 
695  return 0;
696 }
697 
698 
699 /**
700  * @brief Adds a detector strain time series to detector data.
701  * @details
702  * Essentially a wrapper for XLALAddREAL4TimeSeries(), but performs
703  * sub-sample re-interpolation to adjust the source time series epoch to
704  * lie on an integer sample boundary in the target time series. This
705  * transformation is done in the frequency domain, so it is convenient to
706  * allow a response function to be applied at the same time. Passing NULL
707  * for the response function turns this feature off (i.e., uses a unit
708  * response).
709  *
710  * This function accepts source and target time series whose units are not
711  * the same, and allows the two time series to be herterodyned (although it
712  * currently requires them to have the same heterodyne frequency).
713  *
714  * @param[in,out] target Pointer to the time series into which the strain will
715  * be added
716  *
717  * @param[in,out] h Pointer to the time series containing the detector strain
718  * (the strain data is modified by this routine)
719  *
720  * @param[in] response Pointer to the response function transforming strain to
721  * detector output units, or NULL for unit response.
722  *
723  * @retval 0 Success
724  * @retval <0 Failure
725  *
726  * @attention
727  * The source time series is modified in place by this function!
728  */
730  REAL4TimeSeries *target,
731  REAL4TimeSeries *h,
732  const COMPLEX8FrequencySeries *response
733 )
734 {
735  /* 1 ns is about 10^-5 samples at 16384 Hz */
736  const double noop_threshold = 1e-4; /* samples */
737  /* the source time series is padded with at least this many 0's at
738  * the start and end before re-interpolation in an attempt to
739  * suppress aperiodicity artifacts, and 1/2 this many samples is
740  * clipped from the start and end afterwards */
741  const unsigned aperiodicity_suppression_buffer = 32768;
742  double start_sample_int;
743  double start_sample_frac;
744 
745  /* check input */
746 
747  /* FIXME: since we route the source time series through the
748  * frequency domain, it might not be hard to adjust the heterodyne
749  * frequency and sample rate to match the target time series
750  * instead of making it an error if they don't match. */
751 
752  if(h->deltaT != target->deltaT || h->f0 != target->f0) {
753  XLALPrintError("%s(): error: input sample rates or heterodyne frequencies do not match\n", __func__);
755  }
756 
757  /* compute the integer and fractional parts of the sample index in
758  * the target time series on which the source time series begins.
759  * modf() returns integer and fractional parts that have the same
760  * sign, e.g. -3.9 --> -3 + -0.9. we adjust these so that the
761  * magnitude of the fractional part is not greater than 0.5, e.g.
762  * -3.9 --> -4 + 0.1, so that we never do more than 1/2 a sample of
763  * re-interpolation. I don't know if really makes any difference,
764  * though */
765 
766  start_sample_frac = modf(XLALGPSDiff(&h->epoch, &target->epoch) / target->deltaT, &start_sample_int);
767  if(start_sample_frac < -0.5) {
768  start_sample_frac += 1.0;
769  start_sample_int -= 1.0;
770  } else if(start_sample_frac > +0.5) {
771  start_sample_frac -= 1.0;
772  start_sample_int += 1.0;
773  }
774 
775  /* perform sub-sample interpolation if needed */
776 
777  if(fabs(start_sample_frac) > noop_threshold || response) {
778  COMPLEX8FrequencySeries *tilde_h;
779  REAL4FFTPlan *plan;
780  REAL4Window *window;
781  unsigned i;
782 
783  /* extend the source time series by adding the "aperiodicity
784  * padding" to the start and end. for efficiency's sake, make sure
785  * the new length is a power of two, and don't forget to adjust the
786  * start index in the target time series. */
787 
788  i = round_up_to_power_of_two(h->data->length + 2 * aperiodicity_suppression_buffer);
789  if(i < h->data->length) {
790  /* integer overflow */
791  XLALPrintError("%s(): error: source time series too long\n", __func__);
793  }
794  start_sample_int -= (i - h->data->length) / 2;
795  if(!XLALResizeREAL4TimeSeries(h, -(int) (i - h->data->length) / 2, i))
797 
798  /* transform source time series to frequency domain. the FFT
799  * function populates the frequency series' metadata with the
800  * appropriate values. */
801 
802  tilde_h = XLALCreateCOMPLEX8FrequencySeries(NULL, &h->epoch, 0, 0, &lalDimensionlessUnit, h->data->length / 2 + 1);
804  if(!tilde_h || !plan) {
808  }
809  i = XLALREAL4TimeFreqFFT(tilde_h, h, plan);
811  if(i) {
814  }
815 
816  /* apply sub-sample time correction and optional response function
817  * */
818 
819  for(i = 0; i < tilde_h->data->length; i++) {
820  const double f = tilde_h->f0 + i * tilde_h->deltaF;
821  COMPLEX8 fac;
822 
823  /* phase for sub-sample time correction */
824 
825  fac = cexp(-I * LAL_TWOPI * f * start_sample_frac * target->deltaT);
826 
827  /* divide the source by the response function. if a
828  * frequency is required that lies outside the domain of
829  * definition of the response function, then the response
830  * is assumed equal to its value at the nearest edge of the
831  * domain of definition. within the domain of definition,
832  * frequencies are rounded to the nearest bin. if the
833  * response function is zero in some bin, then the source
834  * data is zeroed in that bin (instead of dividing by 0).
835  * */
836 
837  /* FIXME: should we use GSL to construct an interpolator
838  * for the modulus and phase as functions of frequency, and
839  * use that to evaluate the response? instead of rounding
840  * to nearest bin? */
841 
842  if(response) {
843  int j = floor((f - response->f0) / response->deltaF + 0.5);
844  if(j < 0)
845  j = 0;
846  else if((unsigned) j > response->data->length - 1)
847  j = response->data->length - 1;
848  if(response->data->data[j] == 0.0)
849  fac = 0.0;
850  else
851  fac /= response->data->data[j];
852  }
853 
854  /* apply factor */
855 
856  tilde_h->data->data[i] *= fac;
857  }
858 
859  /* adjust DC and Nyquist components. the DC component must always
860  * be real-valued. because we have adjusted the source time series
861  * to have a length that is an even integer (we've made it a power
862  * of 2) the Nyquist component must also be real valued. */
863 
864  if(response) {
865  /* a response function has been provided. zero the DC and
866  * Nyquist components */
867  if(tilde_h->f0 == 0.0)
868  tilde_h->data->data[0] = 0.0;
869  tilde_h->data->data[tilde_h->data->length - 1] = 0.0;
870  } else {
871  /* no response has been provided. set the phase of the DC
872  * component to 0, set the imaginary component of the
873  * Nyquist to 0 */
874  if(tilde_h->f0 == 0.0)
875  tilde_h->data->data[0] = cabsf(tilde_h->data->data[0]);
876  tilde_h->data->data[tilde_h->data->length - 1] = crealf(tilde_h->data->data[tilde_h->data->length - 1]);
877  }
878 
879  /* return to time domain */
880 
882  if(!plan) {
885  }
886  i = XLALREAL4FreqTimeFFT(h, tilde_h, plan);
889  if(i)
891 
892  /* the deltaT can get "corrupted" by floating point round-off
893  * during its trip through the frequency domain. since this
894  * function starts by confirming that the sample rate of the source
895  * matches that of the target time series, we can use the target
896  * series' sample rate to reset the source's sample rate to its
897  * original value. but we do a check to make sure we're not
898  * masking a real bug */
899 
900  if(fabs(h->deltaT - target->deltaT) / target->deltaT > 1e-12) {
901  XLALPrintError("%s(): error: oops, internal sample rate mismatch\n", __func__);
903  }
904  h->deltaT = target->deltaT;
905 
906  /* set source epoch from target epoch and integer sample offset */
907 
908  h->epoch = target->epoch;
909  XLALGPSAdd(&h->epoch, start_sample_int * target->deltaT);
910 
911  /* clip half of the "aperiodicity padding" from the start and end
912  * of the source time series in a continuing effort to suppress
913  * aperiodicity artifacts. */
914 
915  if(!XLALResizeREAL4TimeSeries(h, aperiodicity_suppression_buffer / 2, h->data->length - aperiodicity_suppression_buffer))
917 
918  /* apply a Tukey window whose tapers lie within the remaining
919  * aperiodicity padding. leaving one sample of the aperiodicty
920  * padding untouched on each side of the original time series
921  * because the data might have been shifted into it */
922 
923  window = XLALCreateTukeyREAL4Window(h->data->length, (double) (aperiodicity_suppression_buffer - 2) / h->data->length);
924  if(!window)
926  for(i = 0; i < h->data->length; i++)
927  h->data->data[i] *= window->data->data[i];
928  XLALDestroyREAL4Window(window);
929  }
930 
931  /* add source time series to target time series */
932 
933  if(!XLALAddREAL4TimeSeries(target, h))
935 
936  /* done */
937 
938  return 0;
939 }
940 
941 
942 
943 
944 /* Helper routine that computes a segment of strain data with a single
945  * time delay and beam pattern applied to the whole segment. The duration
946  * of the segment must therefore be reasonably short or else the movement
947  * of the earth will invalidate the use of a single time shift and beam
948  * pattern for the entire segment. */
950  REAL8TimeSeries *segment,
951  const REAL8TimeSeries *hplus,
952  const REAL8TimeSeries *hcross,
955  REAL8FFTPlan *fwdplan,
956  REAL8FFTPlan *revplan,
957  REAL8Window *window,
958  double ra,
959  double dec,
960  double psi,
962  const COMPLEX16FrequencySeries *response
963 )
964 {
965  LIGOTimeGPS t;
966  double gmst;
967  double xcos;
968  double ycos;
969  double fxplus;
970  double fyplus;
971  double fxcross;
972  double fycross;
973  double armlen;
974  double deltaT;
975  double offint;
976  double offrac;
977  int offset;
978  int j;
979  size_t k;
980 
981  /* this routine assumes the segment has a length of N points where N is
982  * a power of two and that the workspace frequency series and the FFT
983  * plans are compatible with the size N; these assumptions are not
984  * checked: the calling routine must ensure that they are true */
985 
986  /* compute fplus, fcross, and time delay from earth's center at the
987  * time corresponding to the middle of the segment */
988 
989  t = segment->epoch;
990  XLALGPSAdd(&t, 0.5 * segment->data->length * segment->deltaT);
992  XLALComputeDetAMResponseParts(&armlen, &xcos, &ycos, &fxplus, &fyplus,
993  &fxcross, &fycross, detector, ra, dec, psi, gmst);
994  deltaT = XLALTimeDelayFromEarthCenter(detector->location, ra, dec, &t);
995 
996  /* add to the geometric delay the difference in time between the
997  * beginning of the injection timeseries and the beginning of the
998  * segment */
999 
1000  deltaT += XLALGPSDiff(&hplus->epoch, &segment->epoch);
1001 
1002  /* compute the integer and fractional parts of the sample index in the
1003  * segment on which the hplus and hcross time series begins: modf()
1004  * returns integer and fractional parts that have the same sign, e.g.,
1005  * -3.9 --> -3 + -0.9, and we adjust these so that magnitude of the
1006  * fractional part is not greater than 0.5, e.g., -3.9 --> -4.0 + 0.1,
1007  * so that we never do more than 1/2 a sample of re-interpolation */
1008 
1009  offrac = modf(deltaT / segment->deltaT, &offint);
1010  if (offrac < -0.5) {
1011  offrac += 1.0;
1012  offint -= 1.0;
1013  } else if (offrac > 0.5) {
1014  offrac -= 1.0;
1015  offint += 1.0;
1016  }
1017  offset = offint;
1018 
1019  /* now compute the sub-sample time shift that must be applied to the
1020  * segment data */
1021 
1022  deltaT = offrac * segment->deltaT;
1023 
1024  /* window the date and put it in frequency domain */
1025 
1026  for (j = 0; j < (int)segment->data->length; ++j)
1027  if (j >= offset && j < (int)hplus->data->length + offset) {
1028  segment->data->data[j] = window->data->data[j]
1029  * hplus->data->data[j - offset];
1030  } else
1031  segment->data->data[j] = 0.0;
1032 
1033  if (XLALREAL8TimeFreqFFT(work1, segment, fwdplan) < 0)
1035 
1036  for (j = 0; j < (int)segment->data->length; ++j)
1037  if (j >= offset && j < (int)hcross->data->length + offset) {
1038  segment->data->data[j] = window->data->data[j]
1039  * hcross->data->data[j - offset];
1040  } else
1041  segment->data->data[j] = 0.0;
1042 
1043  if (XLALREAL8TimeFreqFFT(work2, segment, fwdplan) < 0)
1045 
1046  /* apply sub-sample time shift in frequency domain */
1047 
1048  for (k = 0; k < work1->data->length; ++k) {
1049  double f = work1->f0 + k * work1->deltaF;
1050  double beta = f * armlen / LAL_C_SI;
1051  COMPLEX16 Tx, Ty; /* x- and y-arm transfer functions */
1052  COMPLEX16 gplus, gcross;
1053  COMPLEX16 fac;
1054 
1055  /* phase for sub-sample time correction */
1056  fac = cexp(-I * LAL_TWOPI * f * deltaT);
1057  if (response)
1058  fac /= response->data->data[k];
1059 
1062  gplus = Tx * fxplus + Ty * fyplus;
1063  gcross = Tx * fxcross + Ty * fycross;
1064 
1065  work1->data->data[k] *= gplus;
1066  work1->data->data[k] += gcross * work2->data->data[k];
1067  work1->data->data[k] *= fac;
1068  }
1069 
1070  /* adjust DC and Nyquist components: the DC component must always be
1071  * real-valued; because the calling routine has made the time series
1072  * have an even length, the Nyquist component must also be real-valued;
1073  * also this routine makes the assumption that both the DC and the
1074  * Nyquist components are zero */
1075 
1076  work1->data->data[0] = cabs(work1->data->data[0]);
1077  work1->data->data[work1->data->length - 1] =
1078  creal(work1->data->data[work1->data->length - 1]);
1079 
1080  /* return data to time domain */
1081 
1082  if (XLALREAL8FreqTimeFFT(segment, work1, revplan) < 0)
1084 
1085  return 0;
1086 }
1087 
1088 /* Helper routine that computes a segment of strain data with a single
1089  * time delay and beam pattern applied to the whole segment. The duration
1090  * of the segment must therefore be reasonably short or else the movement
1091  * of the earth will invalidate the use of a single time shift and beam
1092  * pattern for the entire segment. */
1094  REAL4TimeSeries *segment,
1095  const REAL4TimeSeries *hplus,
1096  const REAL4TimeSeries *hcross,
1097  COMPLEX8FrequencySeries *work1,
1098  COMPLEX8FrequencySeries *work2,
1099  REAL4FFTPlan *fwdplan,
1100  REAL4FFTPlan *revplan,
1101  REAL4Window *window,
1102  double ra,
1103  double dec,
1104  double psi,
1106  const COMPLEX8FrequencySeries *response
1107 )
1108 {
1109  LIGOTimeGPS t;
1110  double gmst;
1111  double xcos;
1112  double ycos;
1113  double fxplus;
1114  double fyplus;
1115  double fxcross;
1116  double fycross;
1117  double armlen;
1118  double deltaT;
1119  double offint;
1120  double offrac;
1121  int offset;
1122  int j;
1123  size_t k;
1124 
1125  /* this routine assumes the segment has a length of N points where N is
1126  * a power of two and that the workspace frequency series and the FFT
1127  * plans are compatible with the size N; these assumptions are not
1128  * checked: the calling routine must ensure that they are true */
1129 
1130  /* compute fplus, fcross, and time delay from earth's center at the
1131  * time corresponding to the middle of the segment */
1132 
1133  t = segment->epoch;
1134  XLALGPSAdd(&t, 0.5 * segment->data->length * segment->deltaT);
1135  gmst = XLALGreenwichMeanSiderealTime(&t);
1136  XLALComputeDetAMResponseParts(&armlen, &xcos, &ycos, &fxplus, &fyplus,
1137  &fxcross, &fycross, detector, ra, dec, psi, gmst);
1138  deltaT = XLALTimeDelayFromEarthCenter(detector->location, ra, dec, &t);
1139 
1140  /* add to the geometric delay the difference in time between the
1141  * beginning of the injection timeseries and the beginning of the
1142  * segment */
1143 
1144  deltaT += XLALGPSDiff(&hplus->epoch, &segment->epoch);
1145 
1146  /* compute the integer and fractional parts of the sample index in the
1147  * segment on which the hplus and hcross time series begins: modf()
1148  * returns integer and fractional parts that have the same sign, e.g.,
1149  * -3.9 --> -3 + -0.9, and we adjust these so that magnitude of the
1150  * fractional part is not greater than 0.5, e.g., -3.9 --> -4.0 + 0.1,
1151  * so that we never do more than 1/2 a sample of re-interpolation */
1152 
1153  offrac = modf(deltaT / segment->deltaT, &offint);
1154  if (offrac < -0.5) {
1155  offrac += 1.0;
1156  offint -= 1.0;
1157  } else if (offrac > 0.5) {
1158  offrac -= 1.0;
1159  offint += 1.0;
1160  }
1161  offset = offint;
1162 
1163  /* now compute the sub-sample time shift that must be applied to the
1164  * segment data */
1165 
1166  deltaT = offrac * segment->deltaT;
1167 
1168  /* window the date and put it in frequency domain */
1169 
1170  for (j = 0; j < (int)segment->data->length; ++j)
1171  if (j >= offset && j < (int)hplus->data->length + offset) {
1172  segment->data->data[j] = window->data->data[j]
1173  * hplus->data->data[j - offset];
1174  } else
1175  segment->data->data[j] = 0.0;
1176 
1177  if (XLALREAL4TimeFreqFFT(work1, segment, fwdplan) < 0)
1179 
1180  for (j = 0; j < (int)segment->data->length; ++j)
1181  if (j >= offset && j < (int)hcross->data->length + offset) {
1182  segment->data->data[j] = window->data->data[j]
1183  * hcross->data->data[j - offset];
1184  } else
1185  segment->data->data[j] = 0.0;
1186 
1187  if (XLALREAL4TimeFreqFFT(work2, segment, fwdplan) < 0)
1189 
1190  /* apply sub-sample time shift in frequency domain */
1191 
1192  for (k = 0; k < work1->data->length; ++k) {
1193  double f = work1->f0 + k * work1->deltaF;
1194  double beta = f * armlen / LAL_C_SI;
1195  COMPLEX16 Tx, Ty; /* x- and y-arm transfer functions */
1196  COMPLEX16 gplus, gcross;
1197  COMPLEX16 fac;
1198 
1199  /* phase for sub-sample time correction */
1200  fac = cexp(-I * LAL_TWOPI * f * deltaT);
1201  if (response)
1202  fac /= response->data->data[k];
1203 
1206  gplus = Tx * fxplus + Ty * fyplus;
1207  gcross = Tx * fxcross + Ty * fycross;
1208 
1209  work1->data->data[k] *= gplus;
1210  work1->data->data[k] += gcross * work2->data->data[k];
1211  work1->data->data[k] *= fac;
1212  }
1213 
1214  /* adjust DC and Nyquist components: the DC component must always be
1215  * real-valued; because the calling routine has made the time series
1216  * have an even length, the Nyquist component must also be real-valued;
1217  * also this routine makes the assumption that both the DC and the
1218  * Nyquist components are zero */
1219 
1220  work1->data->data[0] = cabs(work1->data->data[0]);
1221  work1->data->data[work1->data->length - 1] =
1222  creal(work1->data->data[work1->data->length - 1]);
1223 
1224  /* return data to time domain */
1225 
1226  if (XLALREAL4FreqTimeFFT(segment, work1, revplan) < 0)
1228 
1229  return 0;
1230 }
1231 
1232 /**
1233  * @brief Computes strain for a detector and injects into target time series.
1234  * @details This routine takes care of the time-changing time delay from
1235  * the Earth's center and the time-changing antenna response pattern; it
1236  * also accounts for deviations from the long-wavelength limit at high
1237  * frequencies. An optional calibration response function can be provided
1238  * if the output time series is not in strain units.
1239  * @param[in,out] target Time series to inject strain into.
1240  * @param[in] hplus Time series with plus-polarization gravitational waveform.
1241  * @param[in] hcross Time series with cross-polarization gravitational waveform.
1242  * @param[in] ra Right ascension of the source (radians).
1243  * @param[in] dec Declination of the source (radians).
1244  * @param[in] psi Polarization angle of the source (radians).
1245  * @param[in] detector Detector to use when computing strain.
1246  * @param[in] response Response function to use, or NULL if none.
1247  * @retval 0 Success.
1248  * @retval <0 Failure.
1249  */
1251  REAL8TimeSeries *target,
1252  const REAL8TimeSeries *hplus,
1253  const REAL8TimeSeries *hcross,
1254  double ra,
1255  double dec,
1256  double psi,
1258  const COMPLEX16FrequencySeries *response
1259 )
1260 {
1261  const double nominal_segdur = 2.0; /* nominal segment duration = 2s */
1262  const double max_time_delay = 0.1; /* generous allowed time delay */
1263  const size_t strides_per_segment = 2; /* 2 strides in one segment */
1264  LIGOTimeGPS t0;
1265  LIGOTimeGPS t1;
1266  size_t length; /* length in samples of interval t0 - t1 */
1267  size_t seglen; /* length of segment in samples */
1268  size_t padlen; /* padding at beginning and end of segment */
1269  size_t ovrlap; /* overlapping data length */
1270  size_t stride; /* stride of each step */
1271  size_t nsteps; /* number of steps to take */
1272  REAL8TimeSeries *h = NULL; /* strain timeseries to inject into target */
1273  REAL8TimeSeries *segment = NULL;
1274  COMPLEX16FrequencySeries *work1 = NULL;
1275  COMPLEX16FrequencySeries *work2 = NULL;
1276  REAL8FFTPlan *fwdplan = NULL;
1277  REAL8FFTPlan *revplan = NULL;
1278  REAL8Window *window = NULL;
1279  size_t step;
1280  size_t j;
1281  int errnum = 0;
1282 
1283  /* check validity and compatibility of time series */
1284 
1289  if (response == NULL) {
1291  } else {
1292  /* units do no need to agree, but sample interval and
1293  * start frequency do */
1294  if (fabs(target->deltaT - hplus->deltaT ) > LAL_REAL8_EPS)
1296  if (fabs(target->f0 - hplus->f0) > LAL_REAL8_EPS)
1298  }
1299 
1300 
1301  /* constants describing the data segmentation: the length of the
1302  * segment must be a power of two and the segment duration is at least
1303  * nominal_segdur */
1304 
1305  seglen = round_up_to_power_of_two(nominal_segdur / target->deltaT);
1306  stride = seglen / strides_per_segment;
1307  padlen = max_time_delay / target->deltaT;
1308  ovrlap = seglen;
1309  ovrlap -= 2 * padlen;
1310  ovrlap -= stride;
1311 
1312  /* determine start and end time: the start time is the later of the
1313  * start of the hplus/hcross time series and the target time series;
1314  * the end time is the earlier of the end of the hplus/hcross time
1315  * series and the target time series */
1316 
1317  t0 = hplus->epoch;
1318  t1 = target->epoch;
1319  XLALGPSAdd(&t0, hplus->data->length * hplus->deltaT);
1320  XLALGPSAdd(&t1, target->data->length * target->deltaT);
1321  t1 = XLALGPSCmp(&t1, &t0) < 0 ? t1 : t0;
1322  t0 = hplus->epoch;
1323  t0 = XLALGPSCmp(&t0, &target->epoch) > 0 ? t0 : target->epoch;
1324 
1325  /* add padding of 1 stride before and after these start and end times */
1326 
1327  XLALGPSAdd(&t0, -1.0 * stride * target->deltaT);
1328  XLALGPSAdd(&t1, stride * target->deltaT);
1329 
1330  /* determine if this is a disjoint set: if so, there is nothing to do */
1331 
1332  if (XLALGPSCmp(&t1, &t0) <= 0)
1333  return 0;
1334 
1335  /* create a segment that is seglen samples long */
1336 
1337  segment = XLALCreateREAL8TimeSeries(NULL, &t0, target->f0,
1338  target->deltaT, &target->sampleUnits, seglen);
1339  if (!segment) {
1340  errnum = XLAL_EFUNC;
1341  goto freereturn;
1342  }
1343 
1344  /* create a time series to hold the strain to inject into the target */
1345 
1346  length = XLALGPSDiff(&t1, &t0) / target->deltaT;
1347  h = XLALCreateREAL8TimeSeries(NULL, &t0, target->f0, target->deltaT,
1348  &target->sampleUnits, length);
1349  if (!h) {
1350  errnum = XLAL_EFUNC;
1351  goto freereturn;
1352  }
1353  memset(h->data->data, 0, h->data->length * sizeof(*h->data->data));
1354 
1355  /* determine number of steps it takes to go from t0 to t1 */
1356 
1357  nsteps = ((length%stride) ? (1 + length/stride) : (length/stride));
1358 
1359 
1360  /* create frequency-domain workspace; note that the FFT function
1361  * populates the frequency series' metadata with the appropriate
1362  * values */
1363 
1364  work1 = XLALCreateCOMPLEX16FrequencySeries(NULL, &t0, 0, 0,
1365  &lalDimensionlessUnit, seglen / 2 + 1);
1366  work2 = XLALCreateCOMPLEX16FrequencySeries(NULL, &t0, 0, 0,
1367  &lalDimensionlessUnit, seglen / 2 + 1);
1368  if (!work1 || !work2) {
1369  errnum = XLAL_EFUNC;
1370  goto freereturn;
1371  }
1372 
1373  /* create forward and reverse FFT plans */
1374 
1375  fwdplan = XLALCreateForwardREAL8FFTPlan(seglen, 0);
1376  revplan = XLALCreateReverseREAL8FFTPlan(seglen, 0);
1377  if (!fwdplan || !revplan) {
1378  errnum = XLAL_EFUNC;
1379  goto freereturn;
1380  }
1381 
1382  /* create a Tukey window with tapers entirely within the padding */
1383 
1384  window = XLALCreateTukeyREAL8Window(seglen, (double)padlen / seglen);
1385 
1386 
1387  /* loop over steps, adding data from the current step to the strain */
1388 
1389  for (step = 0; step < nsteps; ++step) {
1390 
1391  int status;
1392  size_t offset;
1393 
1394  /* compute one segment of strain with time appropriate beam
1395  * pattern functions and time delays from earth's center */
1396 
1397  status = XLALSimComputeStrainSegmentREAL8TimeSeries(segment, hplus, hcross, work1, work2, fwdplan, revplan, window,
1398  ra, dec, psi, detector, response);
1399  if (status < 0) {
1400  errnum = XLAL_EFUNC;
1401  goto freereturn;
1402  }
1403 
1404  /* compute the offset of this segment relative to the strain series */
1405 
1406  offset = XLALGPSDiff(&segment->epoch, &h->epoch) / h->deltaT;
1407  for (j = padlen; j < seglen - padlen; ++j)
1408  if ((j + offset) < h->data->length) {
1409  if (step && j - padlen < ovrlap) {
1410  /* feather overlapping data */
1411  double x = (double)(j - padlen) / ovrlap;
1412  h->data->data[j + offset] = x * segment->data->data[j]
1413  + (1.0 - x) * h->data->data[j + offset];
1414  } else /* no feathering of remaining data */
1415  h->data->data[j + offset] = segment->data->data[j];
1416  }
1417 
1418  /* advance segment start time the next step */
1419 
1420  XLALGPSAdd(&segment->epoch, stride * segment->deltaT);
1421  }
1422 
1423  /* apply window to beginning and end of time series to reduce ringing */
1424 
1425  for (j = 0; j < stride - padlen; ++j)
1426  h->data->data[j] = h->data->data[h->data->length - 1 - j] = 0.0;
1427  for ( ; j < stride; ++j) {
1428  double fac = window->data->data[j - (stride - padlen)];
1429  h->data->data[j] *= fac;
1430  h->data->data[h->data->length - 1 - j] *= fac;
1431  }
1432 
1433 
1434  /* add computed strain to target time series */
1435 
1436  XLALAddREAL8TimeSeries(target, h);
1437 
1438 freereturn:
1439 
1440  /* free all memory and return */
1441 
1442  XLALDestroyREAL8Window(window);
1443  XLALDestroyREAL8FFTPlan(revplan);
1444  XLALDestroyREAL8FFTPlan(fwdplan);
1448  XLALDestroyREAL8TimeSeries(segment);
1449 
1450  if (errnum)
1451  XLAL_ERROR(errnum);
1452  return 0;
1453 }
1454 
1455 /**
1456  * @brief Computes strain for a detector and injects into target time series.
1457  * @details This routine takes care of the time-changing time delay from
1458  * the Earth's center and the time-changing antenna response pattern; it
1459  * also accounts for deviations from the long-wavelength limit at high
1460  * frequencies. An optional calibration response function can be provided
1461  * if the output time series is not in strain units.
1462  * @param[in,out] target Time series to inject strain into.
1463  * @param[in] hplus Time series with plus-polarization gravitational waveform.
1464  * @param[in] hcross Time series with cross-polarization gravitational waveform.
1465  * @param[in] ra Right ascension of the source (radians).
1466  * @param[in] dec Declination of the source (radians).
1467  * @param[in] psi Polarization angle of the source (radians).
1468  * @param[in] detector Detector to use when computing strain.
1469  * @param[in] response Response function to use, or NULL if none.
1470  * @retval 0 Success.
1471  * @retval <0 Failure.
1472  */
1474  REAL4TimeSeries *target,
1475  const REAL4TimeSeries *hplus,
1476  const REAL4TimeSeries *hcross,
1477  double ra,
1478  double dec,
1479  double psi,
1481  const COMPLEX8FrequencySeries *response
1482 )
1483 {
1484  const double nominal_segdur = 2.0; /* nominal segment duration = 2s */
1485  const double max_time_delay = 0.1; /* generous allowed time delay */
1486  const size_t strides_per_segment = 2; /* 2 strides in one segment */
1487  LIGOTimeGPS t0;
1488  LIGOTimeGPS t1;
1489  size_t length; /* length in samples of interval t0 - t1 */
1490  size_t seglen; /* length of segment in samples */
1491  size_t padlen; /* padding at beginning and end of segment */
1492  size_t ovrlap; /* overlapping data length */
1493  size_t stride; /* stride of each step */
1494  size_t nsteps; /* number of steps to take */
1495  REAL4TimeSeries *h = NULL; /* strain timeseries to inject into target */
1496  REAL4TimeSeries *segment = NULL;
1497  COMPLEX8FrequencySeries *work1 = NULL;
1498  COMPLEX8FrequencySeries *work2 = NULL;
1499  REAL4FFTPlan *fwdplan = NULL;
1500  REAL4FFTPlan *revplan = NULL;
1501  REAL4Window *window = NULL;
1502  size_t step;
1503  size_t j;
1504  int errnum = 0;
1505 
1506  /* check validity and compatibility of time series */
1507 
1512  if (response == NULL) {
1514  } else {
1515  /* units do no need to agree, but sample interval and
1516  * start frequency do */
1517  if (fabs(target->deltaT - hplus->deltaT ) > LAL_REAL8_EPS)
1519  if (fabs(target->f0 - hplus->f0) > LAL_REAL8_EPS)
1521  }
1522 
1523 
1524  /* constants describing the data segmentation: the length of the
1525  * segment must be a power of two and the segment duration is at least
1526  * nominal_segdur */
1527 
1528  seglen = round_up_to_power_of_two(nominal_segdur / target->deltaT);
1529  stride = seglen / strides_per_segment;
1530  padlen = max_time_delay / target->deltaT;
1531  ovrlap = seglen;
1532  ovrlap -= 2 * padlen;
1533  ovrlap -= stride;
1534 
1535  /* determine start and end time: the start time is the later of the
1536  * start of the hplus/hcross time series and the target time series;
1537  * the end time is the earlier of the end of the hplus/hcross time
1538  * series and the target time series */
1539 
1540  t0 = hplus->epoch;
1541  t1 = target->epoch;
1542  XLALGPSAdd(&t0, hplus->data->length * hplus->deltaT);
1543  XLALGPSAdd(&t1, target->data->length * target->deltaT);
1544  t1 = XLALGPSCmp(&t1, &t0) < 0 ? t1 : t0;
1545  t0 = hplus->epoch;
1546  t0 = XLALGPSCmp(&t0, &target->epoch) > 0 ? t0 : target->epoch;
1547 
1548  /* add padding of 1 stride before and after these start and end times */
1549 
1550  XLALGPSAdd(&t0, -1.0 * stride * target->deltaT);
1551  XLALGPSAdd(&t1, stride * target->deltaT);
1552 
1553  /* determine if this is a disjoint set: if so, there is nothing to do */
1554 
1555  if (XLALGPSCmp(&t1, &t0) <= 0)
1556  return 0;
1557 
1558  /* create a segment that is seglen samples long */
1559 
1560  segment = XLALCreateREAL4TimeSeries(NULL, &t0, target->f0,
1561  target->deltaT, &target->sampleUnits, seglen);
1562  if (!segment) {
1563  errnum = XLAL_EFUNC;
1564  goto freereturn;
1565  }
1566 
1567  /* create a time series to hold the strain to inject into the target */
1568 
1569  length = XLALGPSDiff(&t1, &t0) / target->deltaT;
1570  h = XLALCreateREAL4TimeSeries(NULL, &t0, target->f0, target->deltaT,
1571  &target->sampleUnits, length);
1572  if (!h) {
1573  errnum = XLAL_EFUNC;
1574  goto freereturn;
1575  }
1576  memset(h->data->data, 0, h->data->length * sizeof(*h->data->data));
1577 
1578  /* determine number of steps it takes to go from t0 to t1 */
1579 
1580  nsteps = ((length%stride) ? (1 + length/stride) : (length/stride));
1581 
1582 
1583  /* create frequency-domain workspace; note that the FFT function
1584  * populates the frequency series' metadata with the appropriate
1585  * values */
1586 
1587  work1 = XLALCreateCOMPLEX8FrequencySeries(NULL, &t0, 0, 0,
1588  &lalDimensionlessUnit, seglen / 2 + 1);
1589  work2 = XLALCreateCOMPLEX8FrequencySeries(NULL, &t0, 0, 0,
1590  &lalDimensionlessUnit, seglen / 2 + 1);
1591  if (!work1 || !work2) {
1592  errnum = XLAL_EFUNC;
1593  goto freereturn;
1594  }
1595 
1596  /* create forward and reverse FFT plans */
1597 
1598  fwdplan = XLALCreateForwardREAL4FFTPlan(seglen, 0);
1599  revplan = XLALCreateReverseREAL4FFTPlan(seglen, 0);
1600  if (!fwdplan || !revplan) {
1601  errnum = XLAL_EFUNC;
1602  goto freereturn;
1603  }
1604 
1605  /* create a Tukey window with tapers entirely within the padding */
1606 
1607  window = XLALCreateTukeyREAL4Window(seglen, (double)padlen / seglen);
1608 
1609 
1610  /* loop over steps, adding data from the current step to the strain */
1611 
1612  for (step = 0; step < nsteps; ++step) {
1613 
1614  int status;
1615  size_t offset;
1616 
1617  /* compute one segment of strain with time appropriate beam
1618  * pattern functions and time delays from earth's center */
1619 
1620  status = XLALSimComputeStrainSegmentREAL4TimeSeries(segment, hplus, hcross, work1, work2, fwdplan, revplan, window,
1621  ra, dec, psi, detector, response);
1622  if (status < 0) {
1623  errnum = XLAL_EFUNC;
1624  goto freereturn;
1625  }
1626 
1627  /* compute the offset of this segment relative to the strain series */
1628 
1629  offset = XLALGPSDiff(&segment->epoch, &h->epoch) / h->deltaT;
1630  for (j = padlen; j < seglen - padlen; ++j)
1631  if ((j + offset) < h->data->length) {
1632  if (step && j - padlen < ovrlap) {
1633  /* feather overlapping data */
1634  double x = (double)(j - padlen) / ovrlap;
1635  h->data->data[j + offset] = x * segment->data->data[j]
1636  + (1.0 - x) * h->data->data[j + offset];
1637  } else /* no feathering of remaining data */
1638  h->data->data[j + offset] = segment->data->data[j];
1639  }
1640 
1641  /* advance segment start time the next step */
1642 
1643  XLALGPSAdd(&segment->epoch, stride * segment->deltaT);
1644  }
1645 
1646  /* apply window to beginning and end of time series to reduce ringing */
1647 
1648  for (j = 0; j < stride - padlen; ++j)
1649  h->data->data[j] = h->data->data[h->data->length - 1 - j] = 0.0;
1650  for ( ; j < stride; ++j) {
1651  double fac = window->data->data[j - (stride - padlen)];
1652  h->data->data[j] *= fac;
1653  h->data->data[h->data->length - 1 - j] *= fac;
1654  }
1655 
1656 
1657  /* add computed strain to target time series */
1658 
1659  XLALAddREAL4TimeSeries(target, h);
1660 
1661 freereturn:
1662 
1663  /* free all memory and return */
1664 
1665  XLALDestroyREAL4Window(window);
1666  XLALDestroyREAL4FFTPlan(revplan);
1667  XLALDestroyREAL4FFTPlan(fwdplan);
1671  XLALDestroyREAL4TimeSeries(segment);
1672 
1673  if (errnum)
1674  XLAL_ERROR(errnum);
1675  return 0;
1676 }
1677 
1678 
1679 /*
1680  * The following routines are more computationally efficient but they
1681  * assume the long-wavelength limit is valid.
1682  */
1683 
1684 
1685 /* Helper routine that computes a segment of strain data with a single
1686  * time delay and beam pattern applied to the whole segment. The duration
1687  * of the segment must therefore be reasonably short or else the movement
1688  * of the earth will invalidate the use of a single time shift and beam
1689  * pattern for the entire segment. */
1691  REAL8TimeSeries *segment,
1692  const REAL8TimeSeries *hplus,
1693  const REAL8TimeSeries *hcross,
1695  REAL8FFTPlan *fwdplan,
1696  REAL8FFTPlan *revplan,
1697  REAL8Window *window,
1698  double ra,
1699  double dec,
1700  double psi,
1702  const COMPLEX16FrequencySeries *response
1703 )
1704 {
1705  LIGOTimeGPS t;
1706  double gmst;
1707  double fplus;
1708  double fcross;
1709  double deltaT;
1710  double offint;
1711  double offrac;
1712  int offset;
1713  int j;
1714  size_t k;
1715 
1716  /* this routine assumes the segment has a length of N points where N is
1717  * a power of two and that the workspace frequency series and the FFT
1718  * plans are compatible with the size N; these assumptions are not
1719  * checked: the calling routine must ensure that they are true */
1720 
1721  /* compute fplus, fcross, and time delay from earth's center at the
1722  * time corresponding to the middle of the segment */
1723 
1724  t = segment->epoch;
1725  XLALGPSAdd(&t, 0.5 * segment->data->length * segment->deltaT);
1726  gmst = XLALGreenwichMeanSiderealTime(&t);
1727  XLALComputeDetAMResponse(&fplus, &fcross,
1728  (const REAL4(*)[3])(uintptr_t)detector->response, ra, dec, psi, gmst);
1729  deltaT = XLALTimeDelayFromEarthCenter(detector->location, ra, dec, &t);
1730 
1731  /* add to the geometric delay the difference in time between the
1732  * beginning of the injection timeseries and the beginning of the
1733  * segment */
1734 
1735  deltaT += XLALGPSDiff(&hplus->epoch, &segment->epoch);
1736 
1737  /* compute the integer and fractional parts of the sample index in the
1738  * segment on which the hplus and hcross time series begins: modf()
1739  * returns integer and fractional parts that have the same sign, e.g.,
1740  * -3.9 --> -3 + -0.9, and we adjust these so that magnitude of the
1741  * fractional part is not greater than 0.5, e.g., -3.9 --> -4.0 + 0.1,
1742  * so that we never do more than 1/2 a sample of re-interpolation */
1743 
1744  offrac = modf(deltaT / segment->deltaT, &offint);
1745  if (offrac < -0.5) {
1746  offrac += 1.0;
1747  offint -= 1.0;
1748  } else if (offrac > 0.5) {
1749  offrac -= 1.0;
1750  offint += 1.0;
1751  }
1752  offset = offint;
1753 
1754  /* now compute the sub-sample time shift that must be applied to the
1755  * segment data */
1756 
1757  deltaT = offrac * segment->deltaT;
1758 
1759  /* compute the strain from hplus and hcross and populate the segment
1760  * with this windowed data; apply appropriate integer offset */
1761 
1762  for (j = 0; j < (int)segment->data->length; ++j)
1763  if (j >= offset && j < (int)hplus->data->length + offset)
1764  segment->data->data[j] = window->data->data[j]
1765  * ( fplus * hplus->data->data[j - offset]
1766  + fcross * hcross->data->data[j - offset] );
1767  else
1768  segment->data->data[j] = 0.0;
1769 
1770 
1771  /* put segment in frequency domain */
1772 
1773  if (XLALREAL8TimeFreqFFT(work, segment, fwdplan) < 0)
1775 
1776  /* apply sub-sample time shift in frequency domain */
1777 
1778  for (k = 0; k < work->data->length; ++k) {
1779  double f = work->f0 + k * work->deltaF;
1780  COMPLEX16 fac;
1781 
1782  /* phase for sub-sample time correction */
1783  fac = cexp(-I * LAL_TWOPI * f * deltaT);
1784  if (response)
1785  fac /= response->data->data[k];
1786 
1787  work->data->data[k] *= fac;
1788  }
1789 
1790  /* adjust DC and Nyquist components: the DC component must always be
1791  * real-valued; because the calling routine has made the time series
1792  * have an even length, the Nyquist component must also be real-valued;
1793  * also this routine makes the assumption that both the DC and the
1794  * Nyquist components are zero */
1795 
1796  work->data->data[0] = cabs(work->data->data[0]);
1797  work->data->data[work->data->length - 1] =
1798  creal(work->data->data[work->data->length - 1]);
1799 
1800  /* return data to time domain */
1801 
1802  if (XLALREAL8FreqTimeFFT(segment, work, revplan) < 0)
1804 
1805  return 0;
1806 }
1807 
1808 
1809 /* Helper routine that computes a segment of strain data with a single
1810  * time delay and beam pattern applied to the whole segment. The duration
1811  * of the segment must therefore be reasonably short or else the movement
1812  * of the earth will invalidate the use of a single time shift and beam
1813  * pattern for the entire segment. */
1815  REAL4TimeSeries *segment,
1816  const REAL4TimeSeries *hplus,
1817  const REAL4TimeSeries *hcross,
1819  REAL4FFTPlan *fwdplan,
1820  REAL4FFTPlan *revplan,
1821  REAL4Window *window,
1822  double ra,
1823  double dec,
1824  double psi,
1826  const COMPLEX8FrequencySeries *response
1827 )
1828 {
1829  LIGOTimeGPS t;
1830  double gmst;
1831  double fplus;
1832  double fcross;
1833  double deltaT;
1834  double offint;
1835  double offrac;
1836  int offset;
1837  int j;
1838  size_t k;
1839 
1840  /* this routine assumes the segment has a length of N points where N is
1841  * a power of two and that the workspace frequency series and the FFT
1842  * plans are compatible with the size N; these assumptions are not
1843  * checked: the calling routine must ensure that they are true */
1844 
1845  /* compute fplus, fcross, and time delay from earth's center at the
1846  * time corresponding to the middle of the segment */
1847 
1848  t = segment->epoch;
1849  XLALGPSAdd(&t, 0.5 * segment->data->length * segment->deltaT);
1850  gmst = XLALGreenwichMeanSiderealTime(&t);
1851  XLALComputeDetAMResponse(&fplus, &fcross,
1852  (const REAL4(*)[3])(uintptr_t)detector->response, ra, dec, psi, gmst);
1853  deltaT = XLALTimeDelayFromEarthCenter(detector->location, ra, dec, &t);
1854 
1855  /* add to the geometric delay the difference in time between the
1856  * beginning of the injection timeseries and the beginning of the
1857  * segment */
1858 
1859  deltaT += XLALGPSDiff(&hplus->epoch, &segment->epoch);
1860 
1861  /* compute the integer and fractional parts of the sample index in the
1862  * segment on which the hplus and hcross time series begins: modf()
1863  * returns integer and fractional parts that have the same sign, e.g.,
1864  * -3.9 --> -3 + -0.9, and we adjust these so that magnitude of the
1865  * fractional part is not greater than 0.5, e.g., -3.9 --> -4.0 + 0.1,
1866  * so that we never do more than 1/2 a sample of re-interpolation */
1867 
1868  offrac = modf(deltaT / segment->deltaT, &offint);
1869  if (offrac < -0.5) {
1870  offrac += 1.0;
1871  offint -= 1.0;
1872  } else if (offrac > 0.5) {
1873  offrac -= 1.0;
1874  offint += 1.0;
1875  }
1876  offset = offint;
1877 
1878  /* now compute the sub-sample time shift that must be applied to the
1879  * segment data */
1880 
1881  deltaT = offrac * segment->deltaT;
1882 
1883  /* compute the strain from hplus and hcross and populate the segment
1884  * with this windowed data; apply appropriate integer offset */
1885 
1886  for (j = 0; j < (int)segment->data->length; ++j)
1887  if (j >= offset && j < (int)hplus->data->length + offset)
1888  segment->data->data[j] = window->data->data[j]
1889  * ( fplus * hplus->data->data[j - offset]
1890  + fcross * hcross->data->data[j - offset] );
1891  else
1892  segment->data->data[j] = 0.0;
1893 
1894 
1895  /* put segment in frequency domain */
1896 
1897  if (XLALREAL4TimeFreqFFT(work, segment, fwdplan) < 0)
1899 
1900  /* apply sub-sample time shift in frequency domain */
1901 
1902  for (k = 0; k < work->data->length; ++k) {
1903  double f = work->f0 + k * work->deltaF;
1904  COMPLEX8 fac;
1905 
1906  /* phase for sub-sample time correction */
1907  fac = cexp(-I * LAL_TWOPI * f * deltaT);
1908  if (response)
1909  fac /= response->data->data[k];
1910 
1911  work->data->data[k] *= fac;
1912  }
1913 
1914  /* adjust DC and Nyquist components: the DC component must always be
1915  * real-valued; because the calling routine has made the time series
1916  * have an even length, the Nyquist component must also be real-valued;
1917  * also this routine makes the assumption that both the DC and the
1918  * Nyquist components are zero */
1919 
1920  work->data->data[0] = cabs(work->data->data[0]);
1921  work->data->data[work->data->length - 1] =
1922  creal(work->data->data[work->data->length - 1]);
1923 
1924  /* return data to time domain */
1925 
1926  if (XLALREAL4FreqTimeFFT(segment, work, revplan) < 0)
1928 
1929  return 0;
1930 }
1931 
1932 
1933 /**
1934  * @brief Computes strain for a detector and injects into target time series.
1935  * @details This routine takes care of the time-changing time delay from
1936  * the Earth's center and the time-changing antenna response pattern; it
1937  * does NOT account for deviations from the long-wavelength limit at high
1938  * frequencies. An optional calibration response function can be provided
1939  * if the output time series is not in strain units.
1940  * @param[in,out] target Time series to inject strain into.
1941  * @param[in] hplus Time series with plus-polarization gravitational waveform.
1942  * @param[in] hcross Time series with cross-polarization gravitational waveform.
1943  * @param[in] ra Right ascension of the source (radians).
1944  * @param[in] dec Declination of the source (radians).
1945  * @param[in] psi Polarization angle of the source (radians).
1946  * @param[in] detector Detector to use when computing strain.
1947  * @param[in] response Response function to use, or NULL if none.
1948  * @retval 0 Success.
1949  * @retval <0 Failure.
1950  * @warning This routine assumes the long-wavelength limit (LWL) is valid
1951  * when computing the detector strain; at high frequencies near the free
1952  * spectral range of an interferometric detector this approximation becomes
1953  * invalid.
1954  */
1956  REAL8TimeSeries *target,
1957  const REAL8TimeSeries *hplus,
1958  const REAL8TimeSeries *hcross,
1959  double ra,
1960  double dec,
1961  double psi,
1963  const COMPLEX16FrequencySeries *response
1964 )
1965 {
1966  const double nominal_segdur = 2.0; /* nominal segment duration = 2s */
1967  const double max_time_delay = 0.1; /* generous allowed time delay */
1968  const size_t strides_per_segment = 2; /* 2 strides in one segment */
1969  LIGOTimeGPS t0;
1970  LIGOTimeGPS t1;
1971  size_t length; /* length in samples of interval t0 - t1 */
1972  size_t seglen; /* length of segment in samples */
1973  size_t padlen; /* padding at beginning and end of segment */
1974  size_t ovrlap; /* overlapping data length */
1975  size_t stride; /* stride of each step */
1976  size_t nsteps; /* number of steps to take */
1977  REAL8TimeSeries *h = NULL; /* strain timeseries to inject into target */
1978  REAL8TimeSeries *segment = NULL;
1979  COMPLEX16FrequencySeries *work = NULL;
1980  REAL8FFTPlan *fwdplan = NULL;
1981  REAL8FFTPlan *revplan = NULL;
1982  REAL8Window *window = NULL;
1983  size_t step;
1984  size_t j;
1985  int errnum = 0;
1986 
1987  /* check validity and compatibility of time series */
1988 
1993  if (response == NULL) {
1995  } else {
1996  /* units do no need to agree, but sample interval and
1997  * start frequency do */
1998  if (fabs(target->deltaT - hplus->deltaT ) > LAL_REAL8_EPS)
2000  if (fabs(target->f0 - hplus->f0) > LAL_REAL8_EPS)
2002  }
2003 
2004 
2005  /* constants describing the data segmentation: the length of the
2006  * segment must be a power of two and the segment duration is at least
2007  * nominal_segdur */
2008 
2009  seglen = round_up_to_power_of_two(nominal_segdur / target->deltaT);
2010  stride = seglen / strides_per_segment;
2011  padlen = max_time_delay / target->deltaT;
2012  ovrlap = seglen;
2013  ovrlap -= 2 * padlen;
2014  ovrlap -= stride;
2015 
2016  /* determine start and end time: the start time is the later of the
2017  * start of the hplus/hcross time series and the target time series;
2018  * the end time is the earlier of the end of the hplus/hcross time
2019  * series and the target time series */
2020 
2021  t0 = hplus->epoch;
2022  t1 = target->epoch;
2023  XLALGPSAdd(&t0, hplus->data->length * hplus->deltaT);
2024  XLALGPSAdd(&t1, target->data->length * target->deltaT);
2025  t1 = XLALGPSCmp(&t1, &t0) < 0 ? t1 : t0;
2026  t0 = hplus->epoch;
2027  t0 = XLALGPSCmp(&t0, &target->epoch) > 0 ? t0 : target->epoch;
2028 
2029  /* add padding of 1 stride before and after these start and end times */
2030 
2031  XLALGPSAdd(&t0, -1.0 * stride * target->deltaT);
2032  XLALGPSAdd(&t1, stride * target->deltaT);
2033 
2034  /* determine if this is a disjoint set: if so, there is nothing to do */
2035 
2036  if (XLALGPSCmp(&t1, &t0) <= 0)
2037  return 0;
2038 
2039  /* create a segment that is seglen samples long */
2040 
2041  segment = XLALCreateREAL8TimeSeries(NULL, &t0, target->f0,
2042  target->deltaT, &target->sampleUnits, seglen);
2043  if (!segment) {
2044  errnum = XLAL_EFUNC;
2045  goto freereturn;
2046  }
2047 
2048  /* create a time series to hold the strain to inject into the target */
2049 
2050  length = XLALGPSDiff(&t1, &t0) / target->deltaT;
2051  h = XLALCreateREAL8TimeSeries(NULL, &t0, target->f0, target->deltaT,
2052  &target->sampleUnits, length);
2053  if (!h) {
2054  errnum = XLAL_EFUNC;
2055  goto freereturn;
2056  }
2057  memset(h->data->data, 0, h->data->length * sizeof(*h->data->data));
2058 
2059  /* determine number of steps it takes to go from t0 to t1 */
2060 
2061  nsteps = ((length%stride) ? (1 + length/stride) : (length/stride));
2062 
2063 
2064  /* create frequency-domain workspace; note that the FFT function
2065  * populates the frequency series' metadata with the appropriate
2066  * values */
2067 
2068  work = XLALCreateCOMPLEX16FrequencySeries(NULL, &t0, 0, 0,
2069  &lalDimensionlessUnit, seglen / 2 + 1);
2070  if (!work) {
2071  errnum = XLAL_EFUNC;
2072  goto freereturn;
2073  }
2074 
2075  /* create forward and reverse FFT plans */
2076 
2077  fwdplan = XLALCreateForwardREAL8FFTPlan(seglen, 0);
2078  revplan = XLALCreateReverseREAL8FFTPlan(seglen, 0);
2079  if (!fwdplan || !revplan) {
2080  errnum = XLAL_EFUNC;
2081  goto freereturn;
2082  }
2083 
2084  /* create a Tukey window with tapers entirely within the padding */
2085 
2086  window = XLALCreateTukeyREAL8Window(seglen, (double)padlen / seglen);
2087 
2088 
2089  /* loop over steps, adding data from the current step to the strain */
2090 
2091  for (step = 0; step < nsteps; ++step) {
2092 
2093  int status;
2094  size_t offset;
2095 
2096  /* compute one segment of strain with time appropriate beam
2097  * pattern functions and time delays from earth's center */
2098 
2100  hplus, hcross, work, fwdplan, revplan, window, ra, dec,
2101  psi, detector, response);
2102  if (status < 0) {
2103  errnum = XLAL_EFUNC;
2104  goto freereturn;
2105  }
2106 
2107  /* compute the offset of this segment relative to the strain series */
2108 
2109  offset = XLALGPSDiff(&segment->epoch, &h->epoch) / h->deltaT;
2110  for (j = padlen; j < seglen - padlen; ++j)
2111  if ((j + offset) < h->data->length) {
2112  if (step && j - padlen < ovrlap) {
2113  /* feather overlapping data */
2114  double x = (double)(j - padlen) / ovrlap;
2115  h->data->data[j + offset] = x * segment->data->data[j]
2116  + (1.0 - x) * h->data->data[j + offset];
2117  } else /* no feathering of remaining data */
2118  h->data->data[j + offset] = segment->data->data[j];
2119  }
2120 
2121  /* advance segment start time the next step */
2122 
2123  XLALGPSAdd(&segment->epoch, stride * segment->deltaT);
2124  }
2125 
2126  /* apply window to beginning and end of time series to reduce ringing */
2127 
2128  for (j = 0; j < stride - padlen; ++j)
2129  h->data->data[j] = h->data->data[h->data->length - 1 - j] = 0.0;
2130  for ( ; j < stride; ++j) {
2131  double fac = window->data->data[j - (stride - padlen)];
2132  h->data->data[j] *= fac;
2133  h->data->data[h->data->length - 1 - j] *= fac;
2134  }
2135 
2136 
2137  /* add computed strain to target time series */
2138 
2139  XLALAddREAL8TimeSeries(target, h);
2140 
2141 freereturn:
2142 
2143  /* free all memory and return */
2144 
2145  XLALDestroyREAL8Window(window);
2146  XLALDestroyREAL8FFTPlan(revplan);
2147  XLALDestroyREAL8FFTPlan(fwdplan);
2150  XLALDestroyREAL8TimeSeries(segment);
2151 
2152  if (errnum)
2153  XLAL_ERROR(errnum);
2154  return 0;
2155 }
2156 
2157 
2158 /**
2159  * @brief Computes strain for a detector and injects into target time series.
2160  * @details This routine takes care of the time-changing time delay from
2161  * the Earth's center and the time-changing antenna response pattern; it
2162  * does NOT account for deviations from the long-wavelength limit at high
2163  * frequencies. An optional calibration response function can be provided
2164  * if the output time series is not in strain units.
2165  * @param[in,out] target Time series to inject strain into.
2166  * @param[in] hplus Time series with plus-polarization gravitational waveform.
2167  * @param[in] hcross Time series with cross-polarization gravitational waveform.
2168  * @param[in] ra Right ascension of the source (radians).
2169  * @param[in] dec Declination of the source (radians).
2170  * @param[in] psi Polarization angle of the source (radians).
2171  * @param[in] detector Detector to use when computing strain.
2172  * @param[in] response Response function to use, or NULL if none.
2173  * @retval 0 Success.
2174  * @retval <0 Failure.
2175  * @warning This routine assumes the long-wavelength limit (LWL) is valid
2176  * when computing the detector strain; at high frequencies near the free
2177  * spectral range of an interferometric detector this approximation becomes
2178  * invalid.
2179  */
2181  REAL4TimeSeries *target,
2182  const REAL4TimeSeries *hplus,
2183  const REAL4TimeSeries *hcross,
2184  double ra,
2185  double dec,
2186  double psi,
2188  const COMPLEX8FrequencySeries *response
2189 )
2190 {
2191  const double nominal_segdur = 2.0; /* nominal segment duration = 2s */
2192  const double max_time_delay = 0.1; /* generous allowed time delay */
2193  const size_t strides_per_segment = 2; /* 2 strides in one segment */
2194  LIGOTimeGPS t0;
2195  LIGOTimeGPS t1;
2196  size_t length; /* length in samples of interval t0 - t1 */
2197  size_t seglen; /* length of segment in samples */
2198  size_t padlen; /* padding at beginning and end of segment */
2199  size_t ovrlap; /* overlapping data length */
2200  size_t stride; /* stride of each step */
2201  size_t nsteps; /* number of steps to take */
2202  REAL4TimeSeries *h = NULL; /* strain timeseries to inject into target */
2203  REAL4TimeSeries *segment = NULL;
2204  COMPLEX8FrequencySeries *work = NULL;
2205  REAL4FFTPlan *fwdplan = NULL;
2206  REAL4FFTPlan *revplan = NULL;
2207  REAL4Window *window = NULL;
2208  size_t step;
2209  size_t j;
2210  int errnum = 0;
2211 
2212  /* check validity and compatibility of time series */
2213 
2218  if (response == NULL) {
2220  } else {
2221  /* units do no need to agree, but sample interval and
2222  * start frequency do */
2223  if (fabs(target->deltaT - hplus->deltaT ) > LAL_REAL8_EPS)
2225  if (fabs(target->f0 - hplus->f0) > LAL_REAL8_EPS)
2227  }
2228 
2229 
2230  /* constants describing the data segmentation: the length of the
2231  * segment must be a power of two and the segment duration is at least
2232  * nominal_segdur */
2233 
2234  seglen = round_up_to_power_of_two(nominal_segdur / target->deltaT);
2235  stride = seglen / strides_per_segment;
2236  padlen = max_time_delay / target->deltaT;
2237  ovrlap = seglen;
2238  ovrlap -= 2 * padlen;
2239  ovrlap -= stride;
2240 
2241  /* determine start and end time: the start time is the later of the
2242  * start of the hplus/hcross time series and the target time series;
2243  * the end time is the earlier of the end of the hplus/hcross time
2244  * series and the target time series */
2245 
2246  t0 = hplus->epoch;
2247  t1 = target->epoch;
2248  XLALGPSAdd(&t0, hplus->data->length * hplus->deltaT);
2249  XLALGPSAdd(&t1, target->data->length * target->deltaT);
2250  t1 = XLALGPSCmp(&t1, &t0) < 0 ? t1 : t0;
2251  t0 = hplus->epoch;
2252  t0 = XLALGPSCmp(&t0, &target->epoch) > 0 ? t0 : target->epoch;
2253 
2254  /* add padding of 1 stride before and after these start and end times */
2255 
2256  XLALGPSAdd(&t0, -1.0 * stride * target->deltaT);
2257  XLALGPSAdd(&t1, stride * target->deltaT);
2258 
2259  /* determine if this is a disjoint set: if so, there is nothing to do */
2260 
2261  if (XLALGPSCmp(&t1, &t0) <= 0)
2262  return 0;
2263 
2264  /* create a segment that is seglen samples long */
2265 
2266  segment = XLALCreateREAL4TimeSeries(NULL, &t0, target->f0,
2267  target->deltaT, &target->sampleUnits, seglen);
2268  if (!segment) {
2269  errnum = XLAL_EFUNC;
2270  goto freereturn;
2271  }
2272 
2273  /* create a time series to hold the strain to inject into the target */
2274 
2275  length = XLALGPSDiff(&t1, &t0) / target->deltaT;
2276  h = XLALCreateREAL4TimeSeries(NULL, &t0, target->f0, target->deltaT,
2277  &target->sampleUnits, length);
2278  if (!h) {
2279  errnum = XLAL_EFUNC;
2280  goto freereturn;
2281  }
2282  memset(h->data->data, 0, h->data->length * sizeof(*h->data->data));
2283 
2284  /* determine number of steps it takes to go from t0 to t1 */
2285 
2286  nsteps = ((length%stride) ? (1 + length/stride) : (length/stride));
2287 
2288 
2289  /* create frequency-domain workspace; note that the FFT function
2290  * populates the frequency series' metadata with the appropriate
2291  * values */
2292 
2293  work = XLALCreateCOMPLEX8FrequencySeries(NULL, &t0, 0, 0,
2294  &lalDimensionlessUnit, seglen / 2 + 1);
2295  if (!work) {
2296  errnum = XLAL_EFUNC;
2297  goto freereturn;
2298  }
2299 
2300  /* create forward and reverse FFT plans */
2301 
2302  fwdplan = XLALCreateForwardREAL4FFTPlan(seglen, 0);
2303  revplan = XLALCreateReverseREAL4FFTPlan(seglen, 0);
2304  if (!fwdplan || !revplan) {
2305  errnum = XLAL_EFUNC;
2306  goto freereturn;
2307  }
2308 
2309  /* create a Tukey window with tapers entirely within the padding */
2310 
2311  window = XLALCreateTukeyREAL4Window(seglen, (double)padlen / seglen);
2312 
2313 
2314  /* loop over steps, adding data from the current step to the strain */
2315 
2316  for (step = 0; step < nsteps; ++step) {
2317 
2318  int status;
2319  size_t offset;
2320 
2321  /* compute one segment of strain with time appropriate beam
2322  * pattern functions and time delays from earth's center */
2323 
2325  hplus, hcross, work, fwdplan, revplan, window, ra, dec,
2326  psi, detector, response);
2327  if (status < 0) {
2328  errnum = XLAL_EFUNC;
2329  goto freereturn;
2330  }
2331 
2332  /* compute the offset of this segment relative to the strain series */
2333 
2334  offset = XLALGPSDiff(&segment->epoch, &h->epoch) / h->deltaT;
2335  for (j = padlen; j < seglen - padlen; ++j)
2336  if ((j + offset) < h->data->length) {
2337  if (step && j - padlen < ovrlap) {
2338  /* feather overlapping data */
2339  double x = (double)(j - padlen) / ovrlap;
2340  h->data->data[j + offset] = x * segment->data->data[j]
2341  + (1.0 - x) * h->data->data[j + offset];
2342  } else /* no feathering of remaining data */
2343  h->data->data[j + offset] = segment->data->data[j];
2344  }
2345 
2346  /* advance segment start time the next step */
2347 
2348  XLALGPSAdd(&segment->epoch, stride * segment->deltaT);
2349  }
2350 
2351  /* apply window to beginning and end of time series to reduce ringing */
2352 
2353  for (j = 0; j < stride - padlen; ++j)
2354  h->data->data[j] = h->data->data[h->data->length - 1 - j] = 0.0;
2355  for ( ; j < stride; ++j) {
2356  double fac = window->data->data[j - (stride - padlen)];
2357  h->data->data[j] *= fac;
2358  h->data->data[h->data->length - 1 - j] *= fac;
2359  }
2360 
2361 
2362  /* add computed strain to target time series */
2363 
2364  XLALAddREAL4TimeSeries(target, h);
2365 
2366 freereturn:
2367 
2368  /* free all memory and return */
2369 
2370  XLALDestroyREAL4Window(window);
2371  XLALDestroyREAL4FFTPlan(revplan);
2372  XLALDestroyREAL4FFTPlan(fwdplan);
2375  XLALDestroyREAL4TimeSeries(segment);
2376 
2377  if (errnum)
2378  XLAL_ERROR(errnum);
2379  return 0;
2380 }
static double beta(const double a, const double b, const sysq *system)
Internal function that computes the spin-orbit couplings.
const char * name
static int XLALSimComputeStrainSegmentREAL8TimeSeries(REAL8TimeSeries *segment, const REAL8TimeSeries *hplus, const REAL8TimeSeries *hcross, COMPLEX16FrequencySeries *work1, COMPLEX16FrequencySeries *work2, REAL8FFTPlan *fwdplan, REAL8FFTPlan *revplan, REAL8Window *window, double ra, double dec, double psi, LALDetector *detector, const COMPLEX16FrequencySeries *response)
static unsigned long round_up_to_power_of_two(unsigned long x)
Definition: LALSimulation.c:59
static int XLALSimComputeLWLStrainSegmentREAL8TimeSeries(REAL8TimeSeries *segment, const REAL8TimeSeries *hplus, const REAL8TimeSeries *hcross, COMPLEX16FrequencySeries *work, REAL8FFTPlan *fwdplan, REAL8FFTPlan *revplan, REAL8Window *window, double ra, double dec, double psi, LALDetector *detector, const COMPLEX16FrequencySeries *response)
static int XLALSimComputeStrainSegmentREAL4TimeSeries(REAL4TimeSeries *segment, const REAL4TimeSeries *hplus, const REAL4TimeSeries *hcross, COMPLEX8FrequencySeries *work1, COMPLEX8FrequencySeries *work2, REAL4FFTPlan *fwdplan, REAL4FFTPlan *revplan, REAL4Window *window, double ra, double dec, double psi, LALDetector *detector, const COMPLEX8FrequencySeries *response)
static int XLALSimComputeLWLStrainSegmentREAL4TimeSeries(REAL4TimeSeries *segment, const REAL4TimeSeries *hplus, const REAL4TimeSeries *hcross, COMPLEX8FrequencySeries *work, REAL4FFTPlan *fwdplan, REAL4FFTPlan *revplan, REAL4Window *window, double ra, double dec, double psi, LALDetector *detector, const COMPLEX8FrequencySeries *response)
static void highfreq_kernel(double *cached_kernel, int kernel_length, double residual, void *data)
This kernel function incorporates beyond-long-wavelength effects.
static REAL8TimeSeries * error(const REAL8TimeSeries *s1, const REAL8TimeSeries *s0)
LALREAL8TimeSeriesInterp * XLALREAL8TimeSeriesInterpCreate(const REAL8TimeSeries *series, int kernel_length, void(*kernel)(double *, int, double, void *), void *kernel_data)
void XLALREAL8TimeSeriesInterpDestroy(LALREAL8TimeSeriesInterp *interp)
REAL8 XLALREAL8TimeSeriesInterpEval(LALREAL8TimeSeriesInterp *interp, const LIGOTimeGPS *t, int bounds_check)
double dt
Definition: bh_ringdown.c:113
double i
Definition: bh_ringdown.c:118
double e
Definition: bh_ringdown.c:117
#define LAL_CHECK_VALID_SERIES(s, val)
#define LAL_CHECK_CONSISTENT_TIME_SERIES(s1, s2, val)
#define LAL_CHECK_COMPATIBLE_TIME_SERIES(s1, s2, val)
const char * detector
sigmaKerr data[0]
const LALDetector lalCachedDetectors[LAL_NUM_DETECTORS]
void XLALComputeDetAMResponseParts(double *armlen, double *xcos, double *ycos, double *fxplus, double *fyplus, double *fxcross, double *fycross, const LALDetector *detector, double ra, double dec, double psi, double gmst)
void XLALComputeDetAMResponse(double *fplus, double *fcross, const REAL4 D[3][3], const double ra, const double dec, const double psi, const double gmst)
COMPLEX16 XLALComputeDetArmTransferFunction(double beta, double mu)
COMPLEX8FrequencySeries * XLALCreateCOMPLEX8FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
COMPLEX16FrequencySeries * XLALCreateCOMPLEX16FrequencySeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaF, const LALUnit *sampleUnits, size_t length)
void XLALDestroyCOMPLEX8FrequencySeries(COMPLEX8FrequencySeries *series)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define LAL_C_SI
#define LAL_REARTH_SI
#define LAL_REAL8_EPS
#define LAL_PI
#define LAL_TWOPI
double complex COMPLEX16
double REAL8
uint32_t UINT4
float complex COMPLEX8
float REAL4
LAL_NUM_DETECTORS
void * XLALMalloc(size_t n)
void XLALFree(void *p)
REAL8TimeSeries * XLALSimDetectorStrainREAL8TimeSeries(const REAL8TimeSeries *hplus, const REAL8TimeSeries *hcross, REAL8 right_ascension, REAL8 declination, REAL8 psi, const LALDetector *detector)
Transforms the waveform polarizations into a detector strain.
int XLALSimInjectLWLDetectorStrainREAL4TimeSeries(REAL4TimeSeries *target, const REAL4TimeSeries *hplus, const REAL4TimeSeries *hcross, double ra, double dec, double psi, LALDetector *detector, const COMPLEX8FrequencySeries *response)
Computes strain for a detector and injects into target time series.
int XLALSimInjectDetectorStrainREAL8TimeSeries(REAL8TimeSeries *target, const REAL8TimeSeries *hplus, const REAL8TimeSeries *hcross, double ra, double dec, double psi, LALDetector *detector, const COMPLEX16FrequencySeries *response)
Computes strain for a detector and injects into target time series.
int XLALSimInjectLWLDetectorStrainREAL8TimeSeries(REAL8TimeSeries *target, const REAL8TimeSeries *hplus, const REAL8TimeSeries *hcross, double ra, double dec, double psi, LALDetector *detector, const COMPLEX16FrequencySeries *response)
Computes strain for a detector and injects into target time series.
int XLALSimInjectDetectorStrainREAL4TimeSeries(REAL4TimeSeries *target, const REAL4TimeSeries *hplus, const REAL4TimeSeries *hcross, double ra, double dec, double psi, LALDetector *detector, const COMPLEX8FrequencySeries *response)
Computes strain for a detector and injects into target time series.
int XLALSimAddInjectionREAL4TimeSeries(REAL4TimeSeries *target, REAL4TimeSeries *h, const COMPLEX8FrequencySeries *response)
Adds a detector strain time series to detector data.
const LALDetector * XLALDetectorPrefixToLALDetector(const char *string)
Turn a detector prefix string into a LALDetector structure.
Definition: LALSimulation.c:85
int XLALSimAddInjectionREAL8TimeSeries(REAL8TimeSeries *target, REAL8TimeSeries *h, const COMPLEX16FrequencySeries *response)
Adds a detector strain time series to detector data.
REAL4FFTPlan * XLALCreateReverseREAL4FFTPlan(UINT4 size, int measurelvl)
void XLALDestroyREAL8FFTPlan(REAL8FFTPlan *plan)
REAL4FFTPlan * XLALCreateForwardREAL4FFTPlan(UINT4 size, int measurelvl)
REAL8FFTPlan * XLALCreateReverseREAL8FFTPlan(UINT4 size, int measurelvl)
void XLALDestroyREAL4FFTPlan(REAL4FFTPlan *plan)
REAL8FFTPlan * XLALCreateForwardREAL8FFTPlan(UINT4 size, int measurelvl)
REAL8 XLALTimeDelayFromEarthCenter(const double detector_earthfixed_xyz_metres[3], double source_right_ascension_radians, double source_declination_radians, const LIGOTimeGPS *gpstime)
int XLALREAL8TimeFreqFFT(COMPLEX16FrequencySeries *freq, const REAL8TimeSeries *tser, const REAL8FFTPlan *plan)
int XLALREAL4FreqTimeFFT(REAL4TimeSeries *tser, const COMPLEX8FrequencySeries *freq, const REAL4FFTPlan *plan)
int XLALREAL8FreqTimeFFT(REAL8TimeSeries *tser, const COMPLEX16FrequencySeries *freq, const REAL8FFTPlan *plan)
int XLALREAL4TimeFreqFFT(COMPLEX8FrequencySeries *freq, const REAL4TimeSeries *tser, const REAL4FFTPlan *plan)
REAL8TimeSeries * XLALResizeREAL8TimeSeries(REAL8TimeSeries *series, int first, size_t length)
REAL4TimeSeries * XLALResizeREAL4TimeSeries(REAL4TimeSeries *series, int first, size_t length)
REAL8TimeSeries * XLALCreateREAL8TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
REAL8TimeSeries * XLALAddREAL8TimeSeries(REAL8TimeSeries *arg1, const REAL8TimeSeries *arg2)
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
void XLALDestroyREAL4TimeSeries(REAL4TimeSeries *series)
REAL4TimeSeries * XLALAddREAL4TimeSeries(REAL4TimeSeries *arg1, const REAL4TimeSeries *arg2)
REAL4TimeSeries * XLALCreateREAL4TimeSeries(const CHAR *name, const LIGOTimeGPS *epoch, REAL8 f0, REAL8 deltaT, const LALUnit *sampleUnits, size_t length)
const LALUnit lalDimensionlessUnit
REAL4Window * XLALCreateTukeyREAL4Window(UINT4 length, REAL4 beta)
void XLALDestroyREAL8Window(REAL8Window *window)
void XLALDestroyREAL4Window(REAL4Window *window)
REAL8Window * XLALCreateTukeyREAL8Window(UINT4 length, REAL8 beta)
#define XLAL_ERROR_NULL(...)
#define XLAL_ERROR(...)
#define XLAL_REAL8_FAIL_NAN
int XLALPrintError(const char *fmt,...) _LAL_GCC_PRINTF_FORMAT_(1
#define XLAL_IS_REAL8_FAIL_NAN(val)
XLAL_EBADLEN
XLAL_EFREQ
XLAL_EDATA
XLAL_EFUNC
XLAL_EERR
XLAL_EINVAL
XLAL_FAILURE
XLAL_ETIME
REAL8 XLALGreenwichMeanSiderealTime(const LIGOTimeGPS *gpstime)
LIGOTimeGPS * XLALGPSAdd(LIGOTimeGPS *epoch, REAL8 dt)
REAL8 XLALGPSModf(REAL8 *iptr, const LIGOTimeGPS *epoch)
int XLALGPSCmp(const LIGOTimeGPS *t0, const LIGOTimeGPS *t1)
REAL8 XLALGPSDiff(const LIGOTimeGPS *t1, const LIGOTimeGPS *t0)
list x
list y
string status
COMPLEX16Sequence * data
COMPLEX16 * data
COMPLEX8Sequence * data
COMPLEX8 * data
REAL4Sequence * data
LALUnit sampleUnits
LIGOTimeGPS epoch
REAL4 * data
REAL4Sequence * data
REAL8Sequence * data
LALUnit sampleUnits
LIGOTimeGPS epoch
REAL8 * data
REAL8Sequence * data
double deltaT
Definition: unicorn.c:24