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 }
1.5.2