LALSimBurst.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 J. Creighton, K. Cannon
00003  *
00004  * This program is free software; you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License as published by the
00006  * Free Software Foundation; either version 2 of the License, or (at your
00007  * option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License along
00015  * with with program; see the file COPYING. If not, write to the Free
00016  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00017  * 02111-1307  USA
00018  */
00019 
00020 
00021 /*
00022  * ============================================================================
00023  *
00024  *                                  Preamble
00025  *
00026  * ============================================================================
00027  */
00028 
00029 
00030 #include <math.h>
00031 #include <gsl/gsl_rng.h>
00032 #include <gsl/gsl_randist.h>
00033 #include <lal/LALSimBurst.h>
00034 #include <lal/LALConstants.h>
00035 #include <lal/FrequencySeries.h>
00036 #include <lal/Sequence.h>
00037 #include <lal/TimeFreqFFT.h>
00038 #include <lal/TimeSeries.h>
00039 #include <lal/RealFFT.h>
00040 #include <lal/Units.h>
00041 #include <lal/Date.h>
00042 #include "check_series_macros.h"
00043 
00044 
00045 #include <lal/LALRCSID.h>
00046 NRCSID(LALSIMBURSTC, "$Id:");
00047 
00048 
00049 /*
00050  * ============================================================================
00051  *
00052  *          Fill a time series with stationary white Gaussian noise
00053  *
00054  * ============================================================================
00055  */
00056 
00057 
00058 static void gaussian_noise(REAL8TimeSeries * series, REAL8 rms, gsl_rng * rng)
00059 {
00060         unsigned i;
00061 
00062         for(i = 0; i < series->data->length; i++)
00063                 series->data->data[i] = gsl_ran_gaussian(rng, rms);
00064 }
00065 
00066 
00067 /*
00068  * ============================================================================
00069  *
00070  *                                 Utilities
00071  *
00072  * ============================================================================
00073  */
00074 
00075 
00076 /**
00077  * Returns the strain of the sample with the largest magnitude.
00078  */
00079 
00080 
00081 REAL8 XLALMeasureHPeak(const REAL8TimeSeries *series)
00082 {
00083         static const char func[] = "XLALMeasureHPeak";
00084         double hpeak;
00085         unsigned i;
00086 
00087         if(!series->data->length)
00088                 XLAL_ERROR_REAL8(func, XLAL_EBADLEN);
00089 
00090         hpeak = series->data->data[0];
00091         for(i = 1; i < series->data->length; i++)
00092                 if(fabs(series->data->data[i]) > fabs(hpeak))
00093                         hpeak = series->data->data[i];
00094 
00095         return hpeak;
00096 }
00097 
00098 
00099 /**
00100  * From two time series, s1 and s2, computes and returns
00101  *
00102  * \int s1(t) s2(t) \diff t.
00103  */
00104 
00105 
00106 REAL8 XLALMeasureIntS1S2DT(const REAL8TimeSeries *s1, const REAL8TimeSeries *s2)
00107 {
00108         static const char func[] = "XLALMeasureIntS1S2DT";
00109         double e = 0.0;
00110         double sum = 0.0;
00111         unsigned i;
00112 
00113         /* FIXME:  this is overly strict, this function could be smarter */
00114 
00115         LAL_CHECK_CONSISTENT_TIME_SERIES(s1, s2, XLAL_REAL8_FAIL_NAN);
00116 
00117         /* Kahans's compensated summation algorithm */
00118 
00119         for(i = 0; i < s1->data->length; i++) {
00120                 double tmp = sum;
00121                 /* what we want to add = s1 * s2 + "error from last
00122                  * iteration" */
00123                 double x = s1->data->data[i] * s2->data->data[i] + e;
00124                 /* add */
00125                 sum += x;
00126                 /* negative of what was actually added */
00127                 e = tmp - sum;
00128                 /* what didn't get added, add next time */
00129                 e += x;
00130         }
00131 
00132         return sum * s1->deltaT;
00133 }
00134 
00135 
00136 /**
00137  * Returns what people call the "root-sum-square strain".  Infact, this is
00138  *
00139  * \sqrt{\sum (h_{+}^{2} + h_{x}^{2}) \Delta t},
00140  *
00141  * which is an approximation of
00142  *
00143  * \sqrt{\int (h_{+}^{2} + h_{x}^{2}) \diff t}.
00144  */
00145 
00146 
00147 REAL8 XLALMeasureHrss(
00148         const REAL8TimeSeries *hplus,
00149         const REAL8TimeSeries *hcross
00150 )
00151 {
00152         return sqrt(XLALMeasureIntS1S2DT(hplus, hplus) + XLALMeasureIntS1S2DT(hcross, hcross));
00153 }
00154 
00155 
00156 /**
00157  * Given the Fourier transform of a real-valued function h(t), compute and
00158  * return the integral of the square of its derivative:
00159  *
00160  * \int \dot{h}^{2} \diff t.
00161  *
00162  * The normalization factors in this function assume that
00163  * XLALREAL8FreqTimeFFT() will be used to convert the frequency series to
00164  * the time domain.
00165  */
00166 
00167 
00168 REAL8 XLALMeasureIntHDotSquaredDT(const COMPLEX16FrequencySeries *fseries)
00169 {
00170         unsigned i;
00171         double e = 0.0;
00172         double sum = 0.0;
00173 
00174         /* Kahan's compensated summation algorithm. The summation is done
00175          * from lowest to highest frequency under the assumption that high
00176          * frequency components tend to add more to the magnitude of the
00177          * derivative.  Note that because only half the components of the
00178          * Fourier transform are stored a factor of 2 is added after the
00179          * sum.  The DC component should only count once, but it does not
00180          * contribute anything to the sum so no special case is required to
00181          * handle it. */
00182 
00183         for(i = 0; i < fseries->data->length; i++) {
00184                 double tmp = sum;
00185                 /* what we want to add = f^{2} |\tilde{s}(f)|^{2} + "error
00186                  * from last iteration" */
00187                 double x = (fseries->f0 + i * fseries->deltaF) * (fseries->f0 + i * fseries->deltaF) * (fseries->data->data[i].re * fseries->data->data[i].re + fseries->data->data[i].im * fseries->data->data[i].im) + e;
00188                 /* add */
00189                 sum += x;
00190                 /* negative of what was actually added */
00191                 e = tmp - sum;
00192                 /* what didn't get added, add next time */
00193                 e += x;
00194         }
00195 
00196         /* because we've only summed the positive frequency components */
00197 
00198         sum *= 2;
00199 
00200         /* 4 \pi^{2} \delta f */
00201 
00202         sum *= LAL_TWOPI * LAL_TWOPI * fseries->deltaF;
00203 
00204         return sum;
00205 }
00206 
00207 
00208 /**
00209  * Given h+ and hx in the waveframe, compute and return E/r^2.  The return
00210  * value is in LAL's native units, computed by evaluating
00211  *
00212  * \int [ \dot{h}_{+}^{2} + \dot{h}_{\cross}^{2} ] \diff t
00213  *
00214  * and multiplying by LAL_C_SI^{3} / (4 LAL_G_SI).
00215  */
00216 
00217 
00218 REAL8 XLALMeasureEoverRsquared(REAL8TimeSeries *hplus, REAL8TimeSeries *hcross)
00219 {
00220         static const char func[] = "XLALMeasureEoverRsquared";
00221         REAL8FFTPlan *plan;
00222         COMPLEX16FrequencySeries *tilde_hplus, *tilde_hcross;
00223         double e_over_rsquared;
00224         unsigned i;
00225 
00226         /* FIXME:  this is overly strict, this function could be smarter */
00227 
00228         LAL_CHECK_CONSISTENT_TIME_SERIES(hplus, hcross, XLAL_REAL8_FAIL_NAN);
00229 
00230         /* transform to the frequency domain */
00231 
00232         plan = XLALCreateForwardREAL8FFTPlan(hplus->data->length, 0);
00233         tilde_hplus = XLALCreateCOMPLEX16FrequencySeries(NULL, &hplus->epoch, 0.0, 0.0, &lalDimensionlessUnit, hplus->data->length / 2 + 1);
00234         tilde_hcross = XLALCreateCOMPLEX16FrequencySeries(NULL, &hcross->epoch, 0.0, 0.0, &lalDimensionlessUnit, hcross->data->length / 2 + 1);
00235         if(!plan || !tilde_hplus || !tilde_hcross) {
00236                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00237                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00238                 XLALDestroyREAL8FFTPlan(plan);
00239                 XLAL_ERROR(func, XLAL_EFUNC);
00240         }
00241         i = XLALREAL8TimeFreqFFT(tilde_hplus, hplus, plan);
00242         i |= XLALREAL8TimeFreqFFT(tilde_hcross, hcross, plan);
00243         XLALDestroyREAL8FFTPlan(plan);
00244         if(i) {
00245                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00246                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00247                 XLAL_ERROR(func, XLAL_EFUNC);
00248         }
00249 
00250         /* measure E / r^2 */
00251 
00252         e_over_rsquared = (double) LAL_C_SI * LAL_C_SI * LAL_C_SI / (4 * LAL_G_SI) * (XLALMeasureIntHDotSquaredDT(tilde_hplus) + XLALMeasureIntHDotSquaredDT(tilde_hcross));
00253 
00254         /* done */
00255 
00256         XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00257         XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00258 
00259         return e_over_rsquared;
00260 }
00261 
00262 
00263 /*
00264  * ============================================================================
00265  *
00266  *            Construct a Band- and Time-Limited White Noise Burst
00267  *
00268  * ============================================================================
00269  */
00270 
00271 
00272 /**
00273  * Parameters:
00274  *
00275  * duration
00276  *      time domain Gaussian envelope is \propto \exp ( -\frac{1}{2} t^{2}
00277  *      / duration^{2} ) where t and duration are in seconds.
00278  * frequency
00279  * bandwidth
00280  *      frequency domain Gaussian envelope is \propto \exp ( -\frac{1}{2}
00281  *      (f - f_{0})^{2} / bandwidth^{2} ) where f and bandwidth are in
00282  *      Hertz.
00283  * int_hdot_squared
00284  *      waveform is normalized so that \int (\dot{h}_{+}^{2} +
00285  *      \dot{h}_{\times}^{2}) \diff t equals this
00286  * delta_t
00287  *      the sample rate of the time series to construct
00288  * rng
00289  *      a GSL random number generator to be used to produce Gaussian random
00290  *      variables
00291  *
00292  * Output:
00293  *
00294  * Two time series containing h+(t) and hx(t), with the time-domain
00295  * Gaussian envelope's peak located at t = 0 (as defined by the epoch and
00296  * deltaT).  The + and x time series are two independent injections.
00297  *
00298  * Note:  because the injection is constructed with a random number
00299  * generator, any changes to this function that change how random numbers
00300  * are chosen will indirectly have the effect of altering the relationship
00301  * between injection waveform and random number seed.  For example,
00302  * increasing the length of the time series will change the injection
00303  * waveforms.  There's nothing wrong with this, the waveforms are still
00304  * correct, but if there is a need to reproduce a waveform exactly then it
00305  * will be necessary to tag the code before making such changes.
00306  */
00307 
00308 
00309 int XLALGenerateBandAndTimeLimitedWhiteNoiseBurst(
00310         REAL8TimeSeries **hplus,
00311         REAL8TimeSeries **hcross,
00312         REAL8 duration,
00313         REAL8 frequency,
00314         REAL8 bandwidth,
00315         REAL8 int_hdot_squared,
00316         REAL8 delta_t,
00317         gsl_rng *rng
00318 )
00319 {
00320         static const char func[] = "XLALGenerateBandAndTimeLimitedWhiteNoiseBurst";
00321         int length;
00322         LIGOTimeGPS epoch;
00323         COMPLEX16FrequencySeries *tilde_hplus, *tilde_hcross;
00324         REAL8Window *window;
00325         REAL8FFTPlan *plan;
00326         REAL8 norm_factor;
00327         /* compensate the width of the time-domain window's envelope for
00328          * the broadening will be induced by the subsequent application of
00329          * the frequency-domain envelope */
00330         REAL8 sigma_t_squared = duration * duration / 4.0 - 1.0 / (LAL_PI * LAL_PI * bandwidth * bandwidth);
00331         unsigned i;
00332 
00333         /* check input.  checking if sigma_t_squared < 0 is equivalent to
00334          * checking if duration * bandwidth < LAL_2_PI */
00335 
00336         if(duration < 0 || bandwidth < 0 || sigma_t_squared < 0 || int_hdot_squared < 0 || delta_t <= 0) {
00337                 *hplus = *hcross = NULL;
00338                 XLAL_ERROR(func, XLAL_EINVAL);
00339         }
00340 
00341         /* length of the injection time series is 30 * duration, rounded to
00342          * the nearest odd integer */
00343 
00344         length = (int) floor(30.0 * duration / delta_t / 2.0);
00345         length = 2 * length + 1;
00346 
00347         /* the middle sample is t = 0 */
00348 
00349         XLALGPSSetREAL8(&epoch, -(length - 1) / 2 * delta_t);
00350 
00351         /* allocate the time series */
00352 
00353         *hplus = XLALCreateREAL8TimeSeries("BTLWNB +", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00354         *hcross = XLALCreateREAL8TimeSeries("BTLWNB x", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00355         if(!*hplus || !*hcross) {
00356                 XLALDestroyREAL8TimeSeries(*hplus);
00357                 XLALDestroyREAL8TimeSeries(*hcross);
00358                 *hplus = *hcross = NULL;
00359                 XLAL_ERROR(func, XLAL_EFUNC);
00360         }
00361 
00362         /* fill with independent zero-mean unit variance Gaussian random
00363          * numbers (any non-zero amplitude is OK, it will be adjusted
00364          * later) */
00365 
00366         gaussian_noise(*hplus, 1, rng);
00367         gaussian_noise(*hcross, 1, rng);
00368 
00369         /* apply the time-domain Gaussian window.  the window function's
00370          * shape parameter is ((length - 1) * delta_t / 2) / \sigma_{t} where
00371          * \sigma_{t} is the compensated time-domain window duration */
00372 
00373         window = XLALCreateGaussREAL8Window((*hplus)->data->length, (((*hplus)->data->length - 1) * delta_t / 2) / sqrt(sigma_t_squared));
00374         if(!window) {
00375                 XLALDestroyREAL8TimeSeries(*hplus);
00376                 XLALDestroyREAL8TimeSeries(*hcross);
00377                 *hplus = *hcross = NULL;
00378                 XLAL_ERROR(func, XLAL_EFUNC);
00379         }
00380         for(i = 0; i < window->data->length; i++) {
00381                 (*hplus)->data->data[i] *= window->data->data[i];
00382                 (*hcross)->data->data[i] *= window->data->data[i];
00383         }
00384         XLALDestroyREAL8Window(window);
00385 
00386         /* transform to the frequency domain */
00387 
00388         plan = XLALCreateForwardREAL8FFTPlan((*hplus)->data->length, 0);
00389         tilde_hplus = XLALCreateCOMPLEX16FrequencySeries(NULL, &epoch, 0.0, 0.0, &lalDimensionlessUnit, (*hplus)->data->length / 2 + 1);
00390         tilde_hcross = XLALCreateCOMPLEX16FrequencySeries(NULL, &epoch, 0.0, 0.0, &lalDimensionlessUnit, (*hcross)->data->length / 2 + 1);
00391         if(!plan || !tilde_hplus || !tilde_hcross) {
00392                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00393                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00394                 XLALDestroyREAL8FFTPlan(plan);
00395                 XLALDestroyREAL8TimeSeries(*hplus);
00396                 XLALDestroyREAL8TimeSeries(*hcross);
00397                 *hplus = *hcross = NULL;
00398                 XLAL_ERROR(func, XLAL_EFUNC);
00399         }
00400         i = XLALREAL8TimeFreqFFT(tilde_hplus, *hplus, plan);
00401         i |= XLALREAL8TimeFreqFFT(tilde_hcross, *hcross, plan);
00402         XLALDestroyREAL8FFTPlan(plan);
00403         if(i) {
00404                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00405                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00406                 XLALDestroyREAL8TimeSeries(*hplus);
00407                 XLALDestroyREAL8TimeSeries(*hcross);
00408                 *hplus = *hcross = NULL;
00409                 XLAL_ERROR(func, XLAL_EFUNC);
00410         }
00411 
00412         /* apply the frequency-domain Gaussian window.  the window
00413          * function's shape parameter is computed similarly to that of the
00414          * time-domain window, with \sigma_{f} = \Delta f / 2.  the window
00415          * is created with its peak on the middle sample, which we need to
00416          * shift to the sample corresponding to the injection's centre
00417          * frequency. */
00418 
00419         window = XLALCreateGaussREAL8Window(2 * tilde_hplus->data->length + 1, (tilde_hplus->data->length * tilde_hplus->deltaF) / (bandwidth / 2.0));
00420         if(!window) {
00421                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00422                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00423                 XLALDestroyREAL8TimeSeries(*hplus);
00424                 XLALDestroyREAL8TimeSeries(*hcross);
00425                 *hplus = *hcross = NULL;
00426                 XLAL_ERROR(func, XLAL_EFUNC);
00427         }
00428         XLALResizeREAL8Sequence(window->data, tilde_hplus->data->length - (unsigned) floor(frequency / tilde_hplus->deltaF + 0.5), tilde_hplus->data->length);
00429         for(i = 0; i < window->data->length; i++) {
00430                 tilde_hplus->data->data[i].re *= window->data->data[i];
00431                 tilde_hplus->data->data[i].im *= window->data->data[i];
00432                 tilde_hcross->data->data[i].re *= window->data->data[i];
00433                 tilde_hcross->data->data[i].im *= window->data->data[i];
00434         }
00435         XLALDestroyREAL8Window(window);
00436 
00437         /* normalize the waveform to achieve the desired \int
00438          * (\dot{h}_{+}^{2} + \dot{h}_{\times}^{2}) dt */
00439 
00440         norm_factor = sqrt(int_hdot_squared / (XLALMeasureIntHDotSquaredDT(tilde_hplus) + XLALMeasureIntHDotSquaredDT(tilde_hcross)));
00441         for(i = 0; i < tilde_hplus->data->length; i++) {
00442                 tilde_hplus->data->data[i].re *= norm_factor;
00443                 tilde_hplus->data->data[i].im *= norm_factor;
00444                 tilde_hcross->data->data[i].re *= norm_factor;
00445                 tilde_hcross->data->data[i].im *= norm_factor;
00446         }
00447 
00448         /* transform to the time domain */
00449 
00450         plan = XLALCreateReverseREAL8FFTPlan((*hplus)->data->length, 0);
00451         if(!plan) {
00452                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00453                 XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00454                 XLALDestroyREAL8TimeSeries(*hplus);
00455                 XLALDestroyREAL8TimeSeries(*hcross);
00456                 *hplus = *hcross = NULL;
00457                 XLAL_ERROR(func, XLAL_EFUNC);
00458         }
00459         i = XLALREAL8FreqTimeFFT(*hplus, tilde_hplus, plan);
00460         i |= XLALREAL8FreqTimeFFT(*hcross, tilde_hcross, plan);
00461         XLALDestroyREAL8FFTPlan(plan);
00462         XLALDestroyCOMPLEX16FrequencySeries(tilde_hplus);
00463         XLALDestroyCOMPLEX16FrequencySeries(tilde_hcross);
00464         if(i) {
00465                 XLALDestroyREAL8TimeSeries(*hplus);
00466                 XLALDestroyREAL8TimeSeries(*hcross);
00467                 *hplus = *hcross = NULL;
00468                 XLAL_ERROR(func, XLAL_EFUNC);
00469         }
00470 
00471         /* force the sample rate incase round-off has shifted it a bit */
00472 
00473         (*hplus)->deltaT = (*hcross)->deltaT = delta_t;
00474 
00475         /* apply a Tukey window for continuity at the start and end of the
00476          * injection.  the window's shape parameter sets what fraction of
00477          * the window is used by the tapers */
00478 
00479         window = XLALCreateTukeyREAL8Window((*hplus)->data->length, 0.5);
00480         if(!window) {
00481                 XLALDestroyREAL8TimeSeries(*hplus);
00482                 XLALDestroyREAL8TimeSeries(*hcross);
00483                 *hplus = *hcross = NULL;
00484                 XLAL_ERROR(func, XLAL_EFUNC);
00485         }
00486         for(i = 0; i < window->data->length; i++) {
00487                 (*hplus)->data->data[i] *= window->data->data[i];
00488                 (*hcross)->data->data[i] *= window->data->data[i];
00489         }
00490         XLALDestroyREAL8Window(window);
00491 
00492         /* done */
00493 
00494         return 0;
00495 }
00496 
00497 
00498 /*
00499  * ============================================================================
00500  *
00501  *                         Sine-Gaussian and Friends
00502  *
00503  * ============================================================================
00504  */
00505 
00506 
00507 /**
00508  * Input:
00509  *
00510  * Q:  the "Q" of the waveform.  The Gaussian envelope is exp(-1/2 t^{2} /
00511  * sigma_{t}^{2}) where sigma_{t} = Q / (2 \pi f).  High Q --> long
00512  * duration.
00513  *
00514  * centre_frequency:   the frequency of the sinusoidal oscillations that
00515  * get multiplied by the Gaussian envelope.
00516  *
00517  * hrss:  the root-sum-squares strain of the waveform (summed over both
00518  * polarizations).
00519  *
00520  * eccentricity:  0 --> circularly polarized, 1 --> linearly polarized.
00521  *
00522  * polarization:  the angle from the + axis to the major axis of the
00523  * waveform ellipsoid.  with the eccentricity set to 1 (output is linearly
00524  * polarized), 0 --> output contains + polarization only, pi/2 --> output
00525  * contains x polarization only.  with the eccentricity set to 0 (output is
00526  * circularly polarized), the polarization parameter is irrelevant.
00527  *
00528  * Output:
00529  *
00530  * h+ and hx time series containing a cosine-Gaussian in the + polarization
00531  * and a sine-Gaussian in the x polarization.  The Gaussian envelope peaks
00532  * in both at t = 0 as defined by epoch and deltaT.  Note that a Tukey
00533  * window with tapers covering 50% of the time series is applied to make
00534  * the waveform go to 0 smoothly at the start and end.
00535  */
00536 
00537 
00538 int XLALSimBurstSineGaussian(
00539         REAL8TimeSeries **hplus,
00540         REAL8TimeSeries **hcross,
00541         REAL8 Q,
00542         REAL8 centre_frequency,
00543         REAL8 hrss,
00544         REAL8 eccentricity,
00545         REAL8 polarization,
00546         REAL8 delta_t
00547 )
00548 {
00549         static const char func[] = "XLALSimBurstSineGaussian";
00550         REAL8Window *window;
00551         /* semimajor and semiminor axes of waveform ellipsoid */
00552         const double a = 1.0 / sqrt(2.0 - eccentricity * eccentricity);
00553         const double b = a * sqrt(1.0 - eccentricity * eccentricity);
00554         /* rss of plus and cross polarizations */
00555         const double hplusrss  = hrss * (a * cos(polarization) - b * sin(polarization));
00556         const double hcrossrss = hrss * (b * cos(polarization) + a * sin(polarization));
00557         /* rss of unit amplitude cosine- and sine-gaussian waveforms.  see
00558          * K. Riles, LIGO-T040055-00.pdf */
00559         const double cgrss = sqrt((Q / (4.0 * centre_frequency * sqrt(LAL_PI))) * (1.0 + exp(-Q * Q)));
00560         const double sgrss = sqrt((Q / (4.0 * centre_frequency * sqrt(LAL_PI))) * (1.0 - exp(-Q * Q)));
00561         /* "peak" amplitudes of plus and cross */
00562         const double h0plus  = hplusrss / cgrss;
00563         const double h0cross = hcrossrss / sgrss;
00564         LIGOTimeGPS epoch;
00565         int length;
00566         unsigned i;
00567 
00568         /* length of the injection time series is 30 * the width of the
00569          * Gaussian envelope (sigma_t in the comments above), rounded to
00570          * the nearest odd integer */
00571 
00572         length = (int) floor(30.0 * Q / (LAL_TWOPI * centre_frequency) / delta_t / 2.0);
00573         length = 2 * length + 1;
00574 
00575         /* the middle sample is t = 0 */
00576 
00577         XLALGPSSetREAL8(&epoch, -(length - 1) / 2 * delta_t);
00578 
00579         /* allocate the time series */
00580 
00581         *hplus = XLALCreateREAL8TimeSeries("sine-Gaussian +", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00582         *hcross = XLALCreateREAL8TimeSeries("sine-Gaussian x", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00583         if(!*hplus || !*hcross) {
00584                 XLALDestroyREAL8TimeSeries(*hplus);
00585                 XLALDestroyREAL8TimeSeries(*hcross);
00586                 *hplus = *hcross = NULL;
00587                 XLAL_ERROR(func, XLAL_EFUNC);
00588         }
00589 
00590         /* populate */
00591 
00592         for(i = 0; i < (*hplus)->data->length; i++) {
00593                 const double t = ((int) i - (length - 1) / 2) * delta_t;
00594                 const double phi = LAL_TWOPI * centre_frequency * t;
00595                 const double fac = exp(-0.5 * phi * phi / (Q * Q));
00596                 (*hplus)->data->data[i]  = h0plus * fac * cos(phi);
00597                 (*hcross)->data->data[i] = h0cross * fac * sin(phi);
00598         }
00599 
00600         /* apply a Tukey window for continuity at the start and end of the
00601          * injection.  the window's shape parameter sets what fraction of
00602          * the window is used by the tapers */
00603 
00604         window = XLALCreateTukeyREAL8Window((*hplus)->data->length, 0.5);
00605         if(!window) {
00606                 XLALDestroyREAL8TimeSeries(*hplus);
00607                 XLALDestroyREAL8TimeSeries(*hcross);
00608                 *hplus = *hcross = NULL;
00609                 XLAL_ERROR(func, XLAL_EFUNC);
00610         }
00611         for(i = 0; i < window->data->length; i++) {
00612                 (*hplus)->data->data[i] *= window->data->data[i];
00613                 (*hcross)->data->data[i] *= window->data->data[i];
00614         }
00615         XLALDestroyREAL8Window(window);
00616 
00617         return 0;
00618 }
00619 
00620 
00621 /*
00622  * ============================================================================
00623  *
00624  *                                String Cusp
00625  *
00626  * ============================================================================
00627  */
00628 
00629 
00630 /**
00631  * Input:
00632  *      amplitude = waveform's amplitude parameter
00633  *      f_high = high frequency cutoff
00634  *      delta_t = sample period of output time series
00635  *
00636  * Output:
00637  *      h+(t) and hx(t), where the cusp waveform has been placed entirely
00638  *      in the + polarization (the x polarization is zeroed), and the
00639  *      waveform peaks at t = 0 (as defined by the epoch and deltaT).
00640  *
00641  * The low frequency cut-off is fixed at 1 Hz;  there's nothing special
00642  * about 1 Hz except that it is low compared to the frequency at which we
00643  * should be high-passing the data
00644  */
00645 
00646 
00647 int XLALGenerateStringCusp(
00648         REAL8TimeSeries **hplus,
00649         REAL8TimeSeries **hcross,
00650         REAL8 amplitude,
00651         REAL8 f_high,
00652         REAL8 delta_t
00653 )
00654 {
00655         static const char func[] = "XLALGenerateStringCusp";
00656         COMPLEX16FrequencySeries *tilde_h;
00657         REAL8FFTPlan *plan;
00658         LIGOTimeGPS epoch;
00659         int length;
00660         int i;
00661         /* low frequency cut-off in Hertz */
00662         const double f_low = 1.0;
00663 
00664         /* check input */
00665 
00666         if(amplitude < 0 || f_high < f_low || delta_t <= 0) {
00667                 *hplus = *hcross = NULL;
00668                 XLAL_ERROR(func, XLAL_EINVAL);
00669         }
00670 
00671         /* length of the injection time series is 15 / f_low, rounded to
00672          * the nearest odd integer */
00673 
00674         length = (int) (15 / f_low / delta_t / 2.0);
00675         length = 2 * length + 1;
00676 
00677         /* the middle sample is t = 0 */
00678 
00679         XLALGPSSetREAL8(&epoch, -(length - 1) / 2 * delta_t);
00680 
00681         /* allocate time and frequency series and FFT plan */
00682 
00683         *hplus = XLALCreateREAL8TimeSeries("string cusp +", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00684         *hcross = XLALCreateREAL8TimeSeries("string cusp x", &epoch, 0.0, delta_t, &lalStrainUnit, length);
00685         tilde_h = XLALCreateCOMPLEX16FrequencySeries("string cusp +", &epoch, 0.0, 1.0 / (length * delta_t), &lalDimensionlessUnit, length / 2 + 1);
00686         plan = XLALCreateReverseREAL8FFTPlan(length, 0);
00687         if(!*hplus || !*hcross || !tilde_h || !plan) {
00688                 XLALDestroyREAL8TimeSeries(*hplus);
00689                 XLALDestroyREAL8TimeSeries(*hcross);
00690                 XLALDestroyCOMPLEX16FrequencySeries(tilde_h);
00691                 XLALDestroyREAL8FFTPlan(plan);
00692                 *hplus = *hcross = NULL;
00693                 XLAL_ERROR(func, XLAL_EFUNC);
00694         }
00695         XLALUnitMultiply(&tilde_h->sampleUnits, &(*hplus)->sampleUnits, &lalSecondUnit);
00696 
00697         /* zero the x time series, injection is done in + only */
00698 
00699         memset((*hcross)->data->data, 0, (*hcross)->data->length * sizeof(*(*hcross)->data->data));
00700 
00701         /* construct the waveform in the frequency domain */
00702 
00703         for(i = 0; (unsigned) i < tilde_h->data->length; i++) {
00704                 double f = tilde_h->f0 + i * tilde_h->deltaF;
00705 
00706                 /* frequency-domain wave form */
00707 
00708                 tilde_h->data->data[i].re = amplitude * pow((sqrt(1 + f_low * f_low / (f * f))), -8) * pow(f, -4.0 / 3.0);
00709                 if(f > f_high)
00710                         tilde_h->data->data[i].re *= exp(1 - f / f_high);
00711                 tilde_h->data->data[i].im = tilde_h->data->data[i].re;
00712 
00713                 /* phase shift to put waveform's peak on the middle sample
00714                  * of the time series */
00715 
00716                 tilde_h->data->data[i].im *= sin(-LAL_PI * i * (length - 1) / length);
00717                 tilde_h->data->data[i].re *= cos(-LAL_PI * i * (length - 1) / length);
00718         }
00719 
00720         /* set DC to zero */
00721 
00722         tilde_h->data->data[0].re = 0;
00723         tilde_h->data->data[0].im = 0;
00724 
00725         /* set Nyquist to zero */
00726 
00727         tilde_h->data->data[tilde_h->data->length - 1].re = 0;
00728         tilde_h->data->data[tilde_h->data->length - 1].im = 0;
00729 
00730         /* transform to time domain */
00731 
00732         i = XLALREAL8FreqTimeFFT(*hplus, tilde_h, plan);
00733         XLALDestroyCOMPLEX16FrequencySeries(tilde_h);
00734         XLALDestroyREAL8FFTPlan(plan);
00735         if(i) {
00736                 XLALDestroyREAL8TimeSeries(*hplus);
00737                 XLALDestroyREAL8TimeSeries(*hcross);
00738                 *hplus = *hcross = NULL;
00739                 XLAL_ERROR(func, XLAL_EFUNC);
00740         }
00741 
00742         /* force the sample rate incase round-off has shifted it a bit */
00743 
00744         (*hplus)->deltaT = (*hcross)->deltaT = delta_t;
00745 
00746         /* apodize the time series */
00747         /* FIXME:  use a Tukey window? */
00748 
00749         for(i = (*hplus)->data->length - 1; i >= 0; i--)
00750                 (*hplus)->data->data[i] -= (*hplus)->data->data[0];
00751 
00752         /* done */
00753 
00754         return 0;
00755 }

Generated on Sun Sep 7 03:06:57 2008 for LAL by  doxygen 1.5.2