SimulateInspiral.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Teviet Creighton
00003 *
00004 *  This program is free software; you can redistribute it and/or modify
00005 *  it under the terms of the GNU General Public License as published by
00006 *  the Free Software Foundation; either version 2 of the License, or
00007 *  (at your option) any later version.
00008 *
00009 *  This program is distributed in the hope that it will be useful,
00010 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 *  GNU General Public License for more details.
00013 *
00014 *  You should have received a copy of the GNU General Public License
00015 *  along with with program; see the file COPYING. If not, write to the
00016 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00017 *  MA  02111-1307  USA
00018 */
00019 
00020 /***************************** <lalVerbatim file="SimulateInspiralCV">
00021 Author: Creighton, T. D.
00022 $Id: SimulateInspiral.c,v 1.3 2007/06/08 14:41:47 bema Exp $
00023 **************************************************** </lalVerbatim> */
00024 
00025 /********************************************************** <lalLaTeX>
00026 
00027 \subsection{Module \texttt{SimulateInspiral.c}}
00028 \label{ss:SimulateInspiral.c}
00029 
00030 Injects inspiral waveforms into detector output.
00031 
00032 \subsubsection*{Prototypes}
00033 \vspace{0.1in}
00034 \input{SimulateInspiralCP}
00035 \idx{LALSimulateInspiral()}
00036 
00037 \subsubsection*{Description}
00038 
00039 This function generates a binary inspiral signal using the parameters
00040 in \verb@*params@, simulates an instrument's response to that signal
00041 using the instrument transfer function \verb@*transfer@, and injects
00042 the resulting waveform into detector output stored in \verb@*output@.
00043 
00044 The \verb@*output@ time series should have all of its fields set to
00045 their desired values, and should have a data sequence already
00046 allocated; the function \verb@LALSimulateInspiral()@ simply adds the
00047 inspiral waveform on top of the existing data. The \verb@epoch@ and
00048 \verb@deltaT@ fields must be set, as they are used to determine the
00049 sample rate and time positioning of the injected signal.  The
00050 \verb@sampleUnits@ field must be set to \verb@lalADCCountUnit@ for
00051 consistency.
00052 
00053 The \verb@*transfer@ frequency series should define the complex
00054 frequency response function $T(f)$ taking the differential strain
00055 signal $\tilde{h}(f)$ to detector response
00056 $\tilde{o}(f)=T(f)\tilde{h}(f)$, and should have units of ADC counts
00057 per strain.  It is treated as zero outside its frequency domain, and
00058 is linearly interpolated between its frequency samples.
00059 
00060 The \verb@*params@ structure represents the parameters of an inspiral
00061 signal to be injected (if \verb@params->next@=\verb@NULL@), or the
00062 head of a linked list of parameter structures for multiple injections.
00063 For each structure, if the \verb@signalAmplitude@ field is $\geq0$,
00064 the injected waveform will be scaled to give it the correct
00065 characteristic detection amplitude, and the \verb@effDist@ field is
00066 set appropriately.  If \verb@signalAmplitude@$<0$ and
00067 \verb@effDist@$>0$, the waveform is injected with that effective
00068 distance, and the \verb@signalAmplitude@ field is set appropriately.
00069 If \verb@signalAmplitude@$<0$ and \verb@effDist@$\leq0$, an error is
00070 returned (that and all subsequent injections are skipped).
00071 
00072 An error is also returned (and no injections performed) if any of the
00073 fields of \verb@*output@ and \verb@*transfer@ are not set to usable
00074 values, including such things as wrong units or bad sampling
00075 intervals.
00076 
00077 \subsubsection*{Usage}
00078 
00079 One of the most useful applications of this routine is to generate
00080 simulated noise containing a signal.  The following code snippet
00081 generates white Gaussian noise with rms amplitude \verb@SIGMA@, and
00082 injects a signal with intrinsic signal-to-noise ratio
00083 $\sqrt{(h|h)}=$\verb@SNR@ into it, coalescing at a time \verb@DT@
00084 seconds from the start of the time series, with a wave phase
00085 \verb@PHI@ at coalescence.  The \verb@REAL4TimeSeries output@ and
00086 \verb@COMPLEX8FrequencySeries transfer@ structures are assumed to be
00087 defined and allocated outside of this block.
00088 
00089 ******************************************************* </lalLaTeX> */
00090 #if 0
00091 /* <lalVerbatim> */
00092 {
00093   UINT4 i;
00094   SimulateInspiralParamStruc inspParams;
00095   RandomParams *randParams = NULL;
00096 
00097   /* Generate white Gaussian noise. */
00098   LALCreateRandomParams( status->statusPtr, &randParams, 0 );
00099   LALNormalDeviates( status->statusPtr, output.data, randParams );
00100   for ( i = 0; i < output.data->length; i++ )
00101     output.data->data[i] *= SIGMA;
00102   LALDestroyRandomParams( status->statusPtr, &randParams );
00103 
00104   /* Inject signal. */
00105   inspParams.timeC = output.epoch;
00106   inspParams.timeC.gpsSeconds += DT;
00107   inspParams.phiC = PHI; inspParams.mass1 = M1; inspParams.mass2 = M2;
00108   inspParams.signalAmplitude = SNR*SIGMA;
00109   inspParams.next = NULL;
00110   LALSimulateInspiral( status->statusPtr, &output, &transfer, &inspParams );
00111 }
00112 /* </lalVerbatim> */
00113 #endif
00114 /********************************************************** <lalLaTeX>
00115 
00116 \subsubsection*{Algorithm}
00117 
00118 The default mode of operation, when one specifies the desired
00119 amplitude, is as follows:
00120 
00121 First, \verb@LALGeneratePPNInspiral()@ is called to generate the
00122 signal, placing the source at a distance of 1Mpc with optimal
00123 orientation.  For lack of anything better, the amplitude and phase
00124 functions are sampled at the full sampling interval as the output data
00125 stream.  This function call also returns the time at coalescence.
00126 
00127 Second, the waveform produced by the signal in the detector output
00128 stream is calculated.  The basic algorithm is the same as that in
00129 \verb@LALSimulateCoherentGW()@, but we can simplify it significantly
00130 because we ignore polarization responses, time delays, and
00131 interpolation between time samples.  Thus we have only to compute the
00132 effect of the frequency transfer function ${\cal T}(f)$.  As stated in
00133 the \verb@SimulateCoherentGW.h@ header, for quasiperiodic waveforms
00134 $h(t)=\mathrm{Re}[{\cal H}(t)e^{i\phi(t)}]$ we can approximate the
00135 instrument response (in the absence of noise) as:
00136 $$
00137 o(t) \approx \mathrm{Re}[{\cal T}\{f(t)\}{\cal H}(t)e^{i\phi(t)}] \;.
00138 $$
00139 In our case we are only sensitive to a single polarization (let's say
00140 $h_+$), so we take ${\cal H}(t)=A_+(t)$, where the phase of $\cal H$
00141 is absorbed into the coalescence phase of $\phi$.  Then we can write
00142 the instrument response as:
00143 $$
00144 o(t) \approx A_+(t)[ T_\mathrm{re}\{f(t)\}\cos\phi(t)
00145         - T_\mathrm{im}\{f(t)\}\sin\phi(t) ] \;.
00146 $$
00147 This calculation can be done in place and stored in one of the arrays
00148 for the amplitude, phase, or frequency functions, since they are
00149 already sampled at the correct rate and have the correct length.
00150 
00151 Third, the characteristic detection amplitude is computed, and the
00152 whole waveform is scaled so that it has the correct value.
00153 Simultaneously, the effective distance is set to \mbox{1Mpc/(the scale
00154 factor)}.  The epoch is also adjusted to give the waveform the correct
00155 coalescence time.
00156 
00157 Finally, \verb@LALSSInjectTimeSeries()@ is called to inject the
00158 waveform into the output time series.  The whole procedure is repeated
00159 for any other nodes in the linked list of parameters.
00160 
00161 If any parameter structure specifies the effective distance in place
00162 of the characteristic detection amplitude, then the signal is injected
00163 with that effective distance and is not rescaled.  The characteristic
00164 detection amplitude field is set to the measured value.
00165 
00166 \subsubsection*{Uses}
00167 \begin{verbatim}
00168 LALWarning()
00169 LALDDestroyVector()           LALFree()
00170 LALSDestroyVector()           LALSDestroyVectorSequence()
00171 LALGeneratePPNInspiral()      LALSSInjectTimeSeries()
00172 \end{verbatim}
00173 
00174 \subsubsection*{Notes}
00175 
00176 \vfill{\footnotesize\input{SimulateInspiralCV}}
00177 
00178 ******************************************************* </lalLaTeX> */
00179 
00180 #include <math.h>
00181 #include <lal/LALStdio.h>
00182 #include <lal/LALStdlib.h>
00183 #include <lal/LALError.h>
00184 #include <lal/AVFactories.h>
00185 #include <lal/SeqFactories.h>
00186 #include <lal/Units.h>
00187 #include <lal/Inject.h>
00188 #include <lal/SimulateCoherentGW.h>
00189 #include <lal/GeneratePPNInspiral.h>
00190 #include <lal/SimulateInspiral.h>
00191 
00192 /* The lower cutoff frequency, if not specified as an input paramster,
00193    is defined as the point where the sensitivity is reduced by a
00194    factor of SIMULATEINSPIRALC_CUTOFF: */
00195 #define SIMULATEINSPIRALC_CUTOFF (0.000001)
00196 
00197 NRCSID( SIMULATEINSPIRALC, "$Id: SimulateInspiral.c,v 1.3 2007/06/08 14:41:47 bema Exp $" );
00198 
00199 /* <lalVerbatim file="SimulateInspiralCP"> */
00200 void
00201 LALSimulateInspiral( LALStatus                  *stat,
00202                      REAL4TimeSeries            *output,
00203                      COMPLEX8FrequencySeries    *transfer,
00204                      SimulateInspiralParamStruc *params )
00205 { /* </lalVerbatim> */
00206   CHAR name[LALNameLength]; /* name of output time series */
00207   UINT4 i;                  /* an index */
00208   COMPLEX8 *tData;          /* pointer to transfer function data */
00209   PPNParamStruc ppnParams;  /* the parameters of the inspiral */
00210   CoherentGW signal;        /* the signal generated */
00211 
00212   /* Frequency interpolation constants.  By linear interpolation, the
00213      transfer function at any frequency f is given by:
00214 
00215                             f - f_k                  f_{k+1} - f
00216      T(f) = T(f_{k+1}) * -------------  +  T(f_k) * -------------
00217                          f_{k+1} - f_k              f_{k+1} - f_k
00218 
00219           = x*T_{k+1}  +  ( 1 - x )*T_k
00220 
00221      where:
00222 
00223      y = ( f - f0 )/deltaF = fOffset + dfInv*f
00224      k = (int)( y )
00225      x = y - k
00226 
00227   */
00228   REAL8 dfInv, fOffset;
00229 
00230   INITSTATUS( stat, "LALSimulateInspiral", SIMULATEINSPIRALC );
00231   ATTATCHSTATUSPTR( stat );
00232 
00233   /* Make sure argument structures and their fields exist. */
00234   ASSERT( output, stat, SIMULATEINSPIRALH_ENUL,
00235           SIMULATEINSPIRALH_MSGENUL );
00236   ASSERT( output->data, stat, SIMULATEINSPIRALH_ENUL,
00237           SIMULATEINSPIRALH_MSGENUL );
00238   ASSERT( output->data->data, stat, SIMULATEINSPIRALH_ENUL,
00239           SIMULATEINSPIRALH_MSGENUL );
00240   ASSERT( transfer, stat, SIMULATEINSPIRALH_ENUL,
00241           SIMULATEINSPIRALH_MSGENUL );
00242   ASSERT( transfer->data, stat, SIMULATEINSPIRALH_ENUL,
00243           SIMULATEINSPIRALH_MSGENUL );
00244   ASSERT( transfer->data->data, stat, SIMULATEINSPIRALH_ENUL,
00245           SIMULATEINSPIRALH_MSGENUL );
00246 
00247   /* Set up interpolation constants for the transfer function. */
00248   ASSERT( transfer->deltaF != 0.0, stat, SIMULATEINSPIRALH_EDF,
00249           SIMULATEINSPIRALH_MSGEDF );
00250   dfInv = 1.0/transfer->deltaF;
00251   fOffset = -dfInv*transfer->f0;
00252 
00253   /* If the low-frequency cutoff is not specified, find the point
00254      where the amplitude of the response drops to
00255      SIMULATEINSPIRALC_CUTOFF times its maximum. */
00256   memset( &ppnParams, 0, sizeof(PPNParamStruc) );
00257   if ( params->fStart > 0.0 )
00258     ppnParams.fStartIn = params->fStart;
00259   else {
00260     REAL4 xferMax = 0.0; /* maximum amplitude of transfer function */
00261     tData = transfer->data->data;
00262     i = transfer->data->length;
00263     while ( i-- ) {
00264       REAL4 xfer = fabs( tData->re ) + fabs( tData->im );
00265       if ( xfer > xferMax )
00266         xferMax = xfer;
00267       tData++;
00268     }
00269     xferMax *= SIMULATEINSPIRALC_CUTOFF;
00270     tData = transfer->data->data;
00271     i = 0;
00272     while ( fabs( tData->re ) + fabs( tData->im ) < xferMax ) {
00273       tData++;
00274       i++;
00275     }
00276     ppnParams.fStartIn = transfer->f0 + i*transfer->deltaF;
00277   }
00278 
00279   /* Set up other parameters that won't change between injections. */
00280   memset( &signal, 0, sizeof(CoherentGW) );
00281   ppnParams.deltaT = output->deltaT;
00282   tData = transfer->data->data;
00283   strncpy( name, output->name, LALNameLength );
00284 
00285   /* Inject for each node in the list of parameters. */
00286   while ( params ) {
00287     BOOLEAN fFlag = 0; /* whether we stepped outside transfer domain */
00288     INT8 tc;           /* coalescence time, in GPS nanoseconds */
00289     REAL4 amp2 = 0.0;  /* characteristic detection amplitude squared */
00290     REAL4 *aData;      /* pointer to amplitude data */
00291     REAL4 *fData;      /* pointer to frequency data */
00292     REAL8 *phiData;    /* pointer to phase data */
00293 
00294     /* First, generate the inspiral amplitude and phase as functions
00295        of time. */
00296     ppnParams.mTot = params->mass1 + params->mass2;
00297     ppnParams.eta = params->mass1*params->mass2
00298       /ppnParams.mTot/ppnParams.mTot;
00299     ppnParams.phi = params->phiC;
00300     ppnParams.d = 1000000.0*LAL_PC_SI;
00301     if ( params->signalAmplitude < 0.0 ) {
00302       if ( params->effDist <= 0.0 ) {
00303         ABORT( stat, SIMULATEINSPIRALH_EBAD, SIMULATEINSPIRALH_MSGEBAD );
00304       }
00305       ppnParams.d *= params->effDist;
00306     }
00307     TRY( LALGeneratePPNInspiral( stat->statusPtr, &signal,
00308                                  &ppnParams ), stat );
00309 
00310     /* Next, simulate the instrument response.  To save memory, this
00311        will be done in place, overwriting the frequency function
00312        signal.f since it has the right type and length.  Also compute
00313        characteristic detection amplitude. */
00314     aData = signal.a->data->data;
00315     fData = signal.f->data->data;
00316     phiData = signal.phi->data->data;
00317     for ( i = 0; i < signal.f->data->length; i++ ) {
00318       REAL8 y = fOffset + *fData*dfInv;
00319       if ( y < 0.0 || y >= transfer->data->length ) {
00320         *fData = 0.0;
00321         fFlag = 1;
00322       } else {
00323         UINT4 k = (UINT4)( y );
00324         REAL8 x = y - k;
00325         REAL4 tRe = x*tData[k+1].re + ( 1.0 - x )*tData[k].re;
00326         REAL4 tIm = x*tData[k+1].im + ( 1.0 - x )*tData[k].im;
00327         *fData = *aData*( tRe*cos( *phiData ) - tIm*sin( *phiData ) );
00328         amp2 += (*fData)*(*fData);
00329       }
00330       aData+=2;
00331       fData++;
00332       phiData++;
00333     }
00334     signal.f->sampleUnits = lalADCCountUnit;
00335 
00336     /* Warn if we ever stepped outside of the frequency domain of the
00337        transfer function. */
00338     if ( fFlag )
00339       LALWarning( stat, "Signal passed outside of the frequency domain"
00340                   " of the transfer function (transfer function is"
00341                   " treated as zero outside its specified domain)" );
00342 
00343     /* Rescale either amplitude or distance, and shift time. */
00344     if ( params->signalAmplitude < 0.0 )
00345       params->signalAmplitude = sqrt( amp2 );
00346     else {
00347       amp2 = params->signalAmplitude / sqrt( amp2 );
00348       fData = signal.f->data->data;
00349       i = signal.f->data->length;
00350       while ( i-- ) {
00351         *fData *= amp2;
00352         fData++;
00353       }
00354       if ( amp2 == 0.0 )
00355         params->effDist = LAL_REAL4_MAX;
00356       else
00357         params->effDist = 1.0 / amp2;
00358     }
00359     tc = params->timeC.gpsSeconds;
00360     tc *= 1000000000L;
00361     tc += params->timeC.gpsNanoSeconds;
00362     tc -= (INT8)( 1000000000.0L*ppnParams.tc );
00363     signal.f->epoch.gpsSeconds = tc / 1000000000L;
00364     signal.f->epoch.gpsNanoSeconds = tc % 1000000000L;
00365 
00366     /* Inject the waveform into the output data, and reset everything
00367        for the next injection. */
00368     TRY( LALSDestroyVectorSequence( stat->statusPtr, &(signal.a->data) ),
00369          stat );
00370     TRY( LALDDestroyVector( stat->statusPtr, &(signal.phi->data) ),
00371          stat );
00372     LALFree( signal.a ); signal.a = NULL;
00373     LALFree( signal.phi ); signal.phi = NULL;
00374     LALSSInjectTimeSeries( stat->statusPtr, output, signal.f );
00375     BEGINFAIL( stat ) {
00376       TRY( LALSDestroyVector( stat->statusPtr, &(signal.f->data) ),
00377            stat );
00378       LALFree( signal.f ); signal.f = NULL;
00379     } ENDFAIL( stat );
00380     TRY( LALSDestroyVector( stat->statusPtr, &(signal.f->data) ),
00381          stat );
00382     LALFree( signal.f ); signal.f = NULL;
00383     strncpy( output->name, name, LALNameLength );
00384     params = params->next;
00385   }
00386 
00387   /* End of parameter list; no further cleanup should be necessary. */
00388   DETATCHSTATUSPTR( stat );
00389   RETURN( stat );
00390 }

Generated on Sun Sep 7 03:07:14 2008 for LAL by  doxygen 1.5.2