/** * \brief Provides an interface between code build from \ref lalinspiral_findchirp and * various simulation packages for injecting chirps into data. * \author Brown, D. A. and Creighton, T. D * * Injects the signals described * in the linked list of \c SimInspiralTable structures \c events * into the data \c chan. The response function \c resp should * contain the response function to use when injecting the signals into the data. */ void LALFindChirpInjectIMR ( LALStatus *status, REAL4TimeSeries *chan, SimInspiralTable *events, SimRingdownTable *ringdownevents, COMPLEX8FrequencySeries *resp, INT4 injectSignalType ) { UINT4 k; DetectorResponse detector; SimInspiralTable *thisEvent = NULL; SimRingdownTable *thisRingdownEvent = NULL; PPNParamStruc ppnParams; CoherentGW waveform, *wfm; INT8 waveformStartTime; REAL4TimeSeries signalvec; COMPLEX8Vector *unity = NULL; CHAR warnMsg[512]; #if 0 UINT4 n; UINT4 i; #endif INITSTATUS(status); ATTATCHSTATUSPTR( status ); ASSERT( chan, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( chan->data, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( chan->data->data, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( events, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( resp, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( resp->data, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); ASSERT( resp->data->data, status, FINDCHIRPH_ENULL, FINDCHIRPH_MSGENULL ); /* * * set up structures and parameters needed * */ /* fixed waveform injection parameters */ memset( &ppnParams, 0, sizeof(PPNParamStruc) ); ppnParams.deltaT = chan->deltaT; ppnParams.lengthIn = 0; ppnParams.ppn = NULL; /* * * compute the transfer function from the given response function * */ /* allocate memory and copy the parameters describing the freq series */ memset( &detector, 0, sizeof( DetectorResponse ) ); detector.transfer = (COMPLEX8FrequencySeries *) LALCalloc( 1, sizeof(COMPLEX8FrequencySeries) ); if ( ! detector.transfer ) { ABORT( status, FINDCHIRPH_EALOC, FINDCHIRPH_MSGEALOC ); } memcpy( &(detector.transfer->epoch), &(resp->epoch), sizeof(LIGOTimeGPS) ); detector.transfer->f0 = resp->f0; detector.transfer->deltaF = resp->deltaF; detector.site = (LALDetector *) LALMalloc( sizeof(LALDetector) ); /* set the detector site */ switch ( chan->name[0] ) { case 'H': *(detector.site) = lalCachedDetectors[LALDetectorIndexLHODIFF]; LALWarning( status, "computing waveform for Hanford." ); break; case 'L': *(detector.site) = lalCachedDetectors[LALDetectorIndexLLODIFF]; LALWarning( status, "computing waveform for Livingston." ); break; case 'G': *(detector.site) = lalCachedDetectors[LALDetectorIndexGEO600DIFF]; LALWarning( status, "computing waveform for GEO600." ); break; case 'T': *(detector.site) = lalCachedDetectors[LALDetectorIndexTAMA300DIFF]; LALWarning( status, "computing waveform for TAMA300." ); break; case 'V': *(detector.site) = lalCachedDetectors[LALDetectorIndexVIRGODIFF]; LALWarning( status, "computing waveform for Virgo." ); break; default: LALFree( detector.site ); detector.site = NULL; LALWarning( status, "Unknown detector site, computing plus mode " "waveform with no time delay" ); break; } /* set up units for the transfer function */ if (XLALUnitDivide( &(detector.transfer->sampleUnits), &lalADCCountUnit, &lalStrainUnit ) == NULL) { ABORTXLAL(status); } /* invert the response function to get the transfer function */ LALCCreateVector( status->statusPtr, &( detector.transfer->data ), resp->data->length ); CHECKSTATUSPTR( status ); LALCCreateVector( status->statusPtr, &unity, resp->data->length ); CHECKSTATUSPTR( status ); for ( k = 0; k < resp->data->length; ++k ) { unity->data[k] = 1.0; } LALCCVectorDivide( status->statusPtr, detector.transfer->data, unity, resp->data ); CHECKSTATUSPTR( status ); LALCDestroyVector( status->statusPtr, &unity ); CHECKSTATUSPTR( status ); thisRingdownEvent = ringdownevents; /* * * loop over the signals and inject them into the time series * */ for ( thisEvent = events; thisEvent; thisEvent = thisEvent->next) { /* * * generate waveform and inject it into the data * */ /* clear the waveform structure */ memset( &waveform, 0, sizeof(CoherentGW) ); LALGenerateInspiral(status->statusPtr, &waveform, thisEvent, &ppnParams); CHECKSTATUSPTR( status ); /* add the ringdown */ wfm = XLALGenerateInspRing( &waveform, thisEvent, thisRingdownEvent, injectSignalType ); if ( !wfm ) { fprintf( stderr, "Failed to generate the waveform \n" ); if (xlalErrno == XLAL_EFAILED) { fprintf( stderr, "Too much merger\n"); XLALDestroyREAL4TimeSeries( chan ); xlalErrno = XLAL_SUCCESS; return; } else exit ( 1 ); } waveform = *wfm; LALInfo( status, ppnParams.termDescription ); if ( thisEvent->geocent_end_time.gpsSeconds ) { /* get the gps start time of the signal to inject */ waveformStartTime = XLALGPSToINT8NS( &(thisEvent->geocent_end_time) ); waveformStartTime -= (INT8) ( 1000000000.0 * ppnParams.tc ); } else { LALInfo( status, "Waveform start time is zero: injecting waveform " "into center of data segment" ); /* center the waveform in the data segment */ waveformStartTime = XLALGPSToINT8NS( &(chan->epoch) ); waveformStartTime += (INT8) ( 1000000000.0 * ((REAL8) (chan->data->length - ppnParams.length) / 2.0) * chan->deltaT ); } snprintf( warnMsg, sizeof(warnMsg)/sizeof(*warnMsg), "Injected waveform timing:\n" "thisEvent->geocent_end_time.gpsSeconds = %d\n" "thisEvent->geocent_end_time.gpsNanoSeconds = %d\n" "ppnParams.tc = %e\n" "waveformStartTime = %" LAL_INT8_FORMAT "\n", thisEvent->geocent_end_time.gpsSeconds, thisEvent->geocent_end_time.gpsNanoSeconds, ppnParams.tc, waveformStartTime ); LALInfo( status, warnMsg ); /* clear the signal structure */ memset( &signalvec, 0, sizeof(REAL4TimeSeries) ); /* set the start times for injection */ XLALINT8NSToGPS( &(waveform.a->epoch), waveformStartTime ); memcpy( &(waveform.f->epoch), &(waveform.a->epoch), sizeof(LIGOTimeGPS) ); memcpy( &(waveform.phi->epoch), &(waveform.a->epoch), sizeof(LIGOTimeGPS) ); /* set the start time of the signal vector to the start time of the chan */ signalvec.epoch = chan->epoch; /* set the parameters for the signal time series */ signalvec.deltaT = chan->deltaT; if ( ( signalvec.f0 = chan->f0 ) != 0 ) { ABORT( status, FINDCHIRPH_EHETR, FINDCHIRPH_MSGEHETR ); } signalvec.sampleUnits = lalADCCountUnit; /* simulate the detectors response to the inspiral */ LALSCreateVector( status->statusPtr, &(signalvec.data), chan->data->length ); CHECKSTATUSPTR( status ); LALSimulateCoherentGW( status->statusPtr, &signalvec, &waveform, &detector ); CHECKSTATUSPTR( status ); /* *****************************************************************************/ #if 0 FILE *fp; char fname[512]; UINT4 jj, kplus, kcross; snprintf( fname, sizeof(fname) / sizeof(*fname), "waveform-%d-%d-%s.txt", thisEvent->geocent_end_time.gpsSeconds, thisEvent->geocent_end_time.gpsNanoSeconds, thisEvent->waveform ); fp = fopen( fname, "w" ); for( jj = 0, kplus = 0, kcross = 1; jj < waveform.phi->data->length; ++jj, kplus += 2, kcross +=2 ) { fprintf(fp, "%d %e %e %le %e\n", jj, waveform.a->data->data[kplus], waveform.a->data->data[kcross], waveform.phi->data->data[jj], waveform.f->data->data[jj]); } fclose( fp ); #endif /* ********************************************************************************/ #if 0 FILE *fp; char fname[512]; UINT4 jj; snprintf( fname, sizeof(fname) / sizeof(*fname), "waveform-%d-%d-%s.txt", thisEvent->geocent_end_time.gpsSeconds, thisEvent->geocent_end_time.gpsNanoSeconds, thisEvent->waveform ); fp = fopen( fname, "w" ); for( jj = 0; jj < signalvec.data->length; ++jj ) { fprintf(fp, "%d %e %e \n", jj, signalvec.data->data[jj]); } fclose( fp ); #endif /* ********************************************************************************/ /* inject the signal into the data channel */ LALSSInjectTimeSeries( status->statusPtr, chan, &signalvec ); CHECKSTATUSPTR( status ); /* allocate and go to next SimRingdownTable */ if( thisEvent->next ) thisRingdownEvent = thisRingdownEvent->next = (SimRingdownTable *) calloc( 1, sizeof(SimRingdownTable) ); else thisRingdownEvent->next = NULL; /* destroy the signal */ LALSDestroyVector( status->statusPtr, &(signalvec.data) ); CHECKSTATUSPTR( status ); LALSDestroyVectorSequence( status->statusPtr, &(waveform.a->data) ); CHECKSTATUSPTR( status ); LALSDestroyVector( status->statusPtr, &(waveform.f->data) ); CHECKSTATUSPTR( status ); LALDDestroyVector( status->statusPtr, &(waveform.phi->data) ); CHECKSTATUSPTR( status ); if ( waveform.shift ) { LALSDestroyVector( status->statusPtr, &(waveform.shift->data) ); CHECKSTATUSPTR( status ); } LALFree( waveform.a ); LALFree( waveform.f ); LALFree( waveform.phi ); if ( waveform.shift ) { LALFree( waveform.shift ); } } LALCDestroyVector( status->statusPtr, &( detector.transfer->data ) ); CHECKSTATUSPTR( status ); if ( detector.site ) LALFree( detector.site ); LALFree( detector.transfer ); DETATCHSTATUSPTR( status ); RETURN( status ); }
/** * \author Creighton, T. D. * * \brief Computes a continuous waveform with frequency drift and Doppler * modulation from a parabolic orbital trajectory. * * This function computes a quaiperiodic waveform using the spindown and * orbital parameters in <tt>*params</tt>, storing the result in * <tt>*output</tt>. * * In the <tt>*params</tt> structure, the routine uses all the "input" * fields specified in \ref GenerateSpinOrbitCW_h, and sets all of the * "output" fields. If <tt>params-\>f</tt>=\c NULL, no spindown * modulation is performed. If <tt>params-\>oneMinusEcc</tt>\f$\neq0\f$, or if * <tt>params-\>rPeriNorm</tt>\f$\times\f$<tt>params-\>angularSpeed</tt>\f$\geq1\f$ * (faster-than-light speed at periapsis), an error is returned. * * In the <tt>*output</tt> structure, the field <tt>output-\>h</tt> is * ignored, but all other pointer fields must be set to \c NULL. The * function will create and allocate space for <tt>output-\>a</tt>, * <tt>output-\>f</tt>, and <tt>output-\>phi</tt> as necessary. The * <tt>output-\>shift</tt> field will remain set to \c NULL. * * ### Algorithm ### * * For parabolic orbits, we combine \eqref{eq_spinorbit-tr}, * \eqref{eq_spinorbit-t}, and \eqref{eq_spinorbit-upsilon} to get \f$t_r\f$ * directly as a function of \f$E\f$: * \f{equation}{ * \label{eq_cubic-e} * t_r = t_p + \frac{r_p\sin i}{c} \left[ \cos\omega + * \left(\frac{1}{v_p} + \cos\omega\right)E - * \frac{\sin\omega}{4}E^2 + \frac{1}{12v_p}E^3\right] \;, * \f} * where \f$v_p=r_p\dot{\upsilon}_p\sin i/c\f$ is a normalized velocity at * periapsis. Following the prescription for the general analytic * solution to the real cubic equation, we substitute * \f$E=x+3v_p\sin\omega\f$ to obtain: * \f{equation}{ * \label{eq_cubic-x} * x^3 + px = q \;, * \f} * where: * \f{eqnarray}{ * \label{eq_cubic-p} * p & = & 12 + 12v_p\cos\omega - 3v_p^2\sin^2\omega \;, \\ * \label{eq_cubic-q} * q & = & 12v_p^2\sin\omega\cos\omega - 24v_p\sin\omega + * 2v_p^3\sin^3\omega + 12\dot{\upsilon}_p(t_r-t_p) \;. * \f} * We note that \f$p>0\f$ is guaranteed as long as \f$v_p<1\f$, so the right-hand * side of \eqref{eq_cubic-x} is monotonic in \f$x\f$ and has exactly one * root. However, \f$p\rightarrow0\f$ in the limit \f$v_p\rightarrow1\f$ and * \f$\omega=\pi\f$. This may cause some loss of precision in subsequent * calculations. But \f$v_p\sim1\f$ means that our solution will be * inaccurate anyway because we ignore significant relativistic effects. * * Since \f$p>0\f$, we can substitute \f$x=y\sqrt{3/4p}\f$ to obtain: * \f{equation}{ * 4y^3 + 3y = \frac{q}{2}\left(\frac{3}{p}\right)^{3/2} \equiv C \;. * \f} * Using the triple-angle hyperbolic identity * \f$\sinh(3\theta)=4\sinh^3\theta+3\sinh\theta\f$, we have * \f$y=\sinh\left(\frac{1}{3}\sinh^{-1}C\right)\f$. The solution to the * original cubic equation is then: * \f{equation}{ * E = 3v_p\sin\omega + 2\sqrt{\frac{p}{3}} * \sinh\left(\frac{1}{3}\sinh^{-1}C\right) \;. * \f} * To ease the calculation of \f$E\f$, we precompute the constant part * \f$E_0=3v_p\sin\omega\f$ and the coefficient \f$\Delta E=2\sqrt{p/3}\f$. * Similarly for \f$C\f$, we precompute a constant piece \f$C_0\f$ evaluated at * the epoch of the output time series, and a stepsize coefficient * \f$\Delta C=6(p/3)^{3/2}\dot{\upsilon}_p\Delta t\f$, where \f$\Delta t\f$ is * the step size in the (output) time series in \f$t_r\f$. Thus at any * timestep \f$i\f$, we obtain \f$C\f$ and hence \f$E\f$ via: * \f{eqnarray}{ * C & = & C_0 + i\Delta C \;,\\ * E & = & E_0 + \Delta E\times\left\{\begin{array}{l@{\qquad}c} * \sinh\left[\frac{1}{3}\ln\left( * C + \sqrt{C^2+1} \right) \right]\;, & C\geq0 \;,\\ \\ * \sinh\left[-\frac{1}{3}\ln\left( * -C + \sqrt{C^2+1} \right) \right]\;, & C\leq0 \;,\\ * \end{array}\right. * \f} * where we have explicitly written \f$\sinh^{-1}\f$ in terms of functions in * \c math.h. Once \f$E\f$ is found, we can compute * \f$t=E(12+E^2)/(12\dot{\upsilon}_p)\f$ (where again \f$1/12\dot{\upsilon}_p\f$ * can be precomputed), and hence \f$f\f$ and \f$\phi\f$ via * \eqref{eq_taylorcw-freq} and \eqref{eq_taylorcw-phi}. The * frequency \f$f\f$ must then be divided by the Doppler factor: * \f[ * 1 + \frac{\dot{R}}{c} = 1 + \frac{v_p}{4+E^2}\left( * 4\cos\omega - 2E\sin\omega \right) * \f] * (where once again \f$4\cos\omega\f$ and \f$2\sin\omega\f$ can be precomputed). * * This routine does not account for relativistic timing variations, and * issues warnings or errors based on the criterea of * \eqref{eq_relativistic-orbit} in GenerateEllipticSpinOrbitCW(). * The routine will also warn if * it seems likely that \c REAL8 precision may not be sufficient to * track the orbit accurately. We estimate that numerical errors could * cause the number of computed wave cycles to vary by * \f[ * \Delta N \lesssim f_0 T\epsilon\left[ * \sim6+\ln\left(|C|+\sqrt{|C|^2+1}\right)\right] \;, * \f] * where \f$|C|\f$ is the maximum magnitude of the variable \f$C\f$ over the * course of the computation, \f$f_0T\f$ is the approximate total number of * wave cycles over the computation, and \f$\epsilon\approx2\times10^{-16}\f$ * is the fractional precision of \c REAL8 arithmetic. If this * estimate exceeds 0.01 cycles, a warning is issued. */ void LALGenerateParabolicSpinOrbitCW( LALStatus *stat, PulsarCoherentGW *output, SpinOrbitCWParamStruc *params ) { UINT4 n, i; /* number of and index over samples */ UINT4 nSpin = 0, j; /* number of and index over spindown terms */ REAL8 t, dt, tPow; /* time, interval, and t raised to a power */ REAL8 phi0, f0, twopif0; /* initial phase, frequency, and 2*pi*f0 */ REAL8 f, fPrev; /* current and previous values of frequency */ REAL4 df = 0.0; /* maximum difference between f and fPrev */ REAL8 phi; /* current value of phase */ REAL8 vp; /* projected speed at periapsis */ REAL8 argument; /* argument of periapsis */ REAL8 fourCosOmega; /* four times the cosine of argument */ REAL8 twoSinOmega; /* two times the sine of argument */ REAL8 vpCosOmega; /* vp times cosine of argument */ REAL8 vpSinOmega; /* vp times sine of argument */ REAL8 vpSinOmega2; /* vpSinOmega squared */ REAL8 vDot6; /* 6 times angular speed at periapsis */ REAL8 oneBy12vDot; /* one over (12 times angular speed) */ REAL8 pBy3; /* constant sqrt(p/3) in cubic equation */ REAL8 p32; /* constant (p/3)^1.5 in cubic equation */ REAL8 c, c0, dc; /* C variable, offset, and step increment */ REAL8 e, e2, e0; /* E variable, E^2, and constant piece of E */ REAL8 de; /* coefficient of sinh() piece of E */ REAL8 tpOff; /* orbit epoch - time series epoch (s) */ REAL8 spinOff; /* spin epoch - orbit epoch (s) */ REAL8 *fSpin = NULL; /* pointer to Taylor coefficients */ REAL4 *fData; /* pointer to frequency data */ REAL8 *phiData; /* pointer to phase data */ INITSTATUS(stat); ATTATCHSTATUSPTR( stat ); /* Make sure parameter and output structures exist. */ ASSERT( params, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); ASSERT( output, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); /* Make sure output fields don't exist. */ ASSERT( !( output->a ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->f ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->phi ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->shift ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); /* If Taylor coeficients are specified, make sure they exist. */ if ( params->f ) { ASSERT( params->f->data, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); nSpin = params->f->length; fSpin = params->f->data; } /* Set up some constants (to avoid repeated calculation or dereferencing), and make sure they have acceptable values. */ vp = params->rPeriNorm*params->angularSpeed; vDot6 = 6.0*params->angularSpeed; n = params->length; dt = params->deltaT; f0 = fPrev = params->f0; if ( params->oneMinusEcc != 0.0 ) { ABORT( stat, GENERATESPINORBITCWH_EECC, GENERATESPINORBITCWH_MSGEECC ); } if ( vp >= 1.0 ) { ABORT( stat, GENERATESPINORBITCWH_EFTL, GENERATESPINORBITCWH_MSGEFTL ); } if ( vp <= 0.0 || dt <= 0.0 || f0 <= 0.0 || vDot6 <= 0.0 || n == 0 ) { ABORT( stat, GENERATESPINORBITCWH_ESGN, GENERATESPINORBITCWH_MSGESGN ); } #ifndef NDEBUG if ( lalDebugLevel & LALWARNING ) { if ( f0*n*dt*vp*vp > 0.5 ) LALWarning( stat, "Orbit may have significant relativistic" " effects that are not included" ); } #endif /* Compute offset between time series epoch and periapsis, and betweem periapsis and spindown reference epoch. */ tpOff = (REAL8)( params->orbitEpoch.gpsSeconds - params->epoch.gpsSeconds ); tpOff += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->epoch.gpsNanoSeconds ); spinOff = (REAL8)( params->orbitEpoch.gpsSeconds - params->spinEpoch.gpsSeconds ); spinOff += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->spinEpoch.gpsNanoSeconds ); /* Set up some other constants. */ twopif0 = f0*LAL_TWOPI; phi0 = params->phi0; argument = params->omega; oneBy12vDot = 0.5/vDot6; fourCosOmega = 4.0*cos( argument ); twoSinOmega = 2.0*sin( argument ); vpCosOmega = 0.25*vp*fourCosOmega; vpSinOmega = 0.5*vp*twoSinOmega; vpSinOmega2 = vpSinOmega*vpSinOmega; pBy3 = sqrt( 4.0*( 1.0 + vpCosOmega ) - vpSinOmega2 ); p32 = 1.0/( pBy3*pBy3*pBy3 ); c0 = p32*( vpSinOmega*( 6.0*vpCosOmega - 12.0 + vpSinOmega2 ) - tpOff*vDot6 ); dc = p32*vDot6*dt; e0 = 3.0*vpSinOmega; de = 2.0*pBy3; /* Check whether REAL8 precision is good enough. */ #ifndef NDEBUG if ( lalDebugLevel & LALWARNING ) { REAL8 x = fabs( c0 + n*dc ); /* a temporary computation variable */ if ( x < fabs( c0 ) ) x = fabs( c0 ); x = 6.0 + log( x + sqrt( x*x + 1.0 ) ); if ( LAL_REAL8_EPS*f0*dt*n*x > 0.01 ) LALWarning( stat, "REAL8 arithmetic may not have sufficient" " precision for this orbit" ); } #endif /* Allocate output structures. */ if ( ( output->a = (REAL4TimeVectorSeries *) LALMalloc( sizeof(REAL4TimeVectorSeries) ) ) == NULL ) { ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->a, 0, sizeof(REAL4TimeVectorSeries) ); if ( ( output->f = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->f, 0, sizeof(REAL4TimeSeries) ); if ( ( output->phi = (REAL8TimeSeries *) LALMalloc( sizeof(REAL8TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->phi, 0, sizeof(REAL8TimeSeries) ); /* Set output structure metadata fields. */ output->position = params->position; output->psi = params->psi; output->a->epoch = output->f->epoch = output->phi->epoch = params->epoch; output->a->deltaT = n*params->deltaT; output->f->deltaT = output->phi->deltaT = params->deltaT; output->a->sampleUnits = lalStrainUnit; output->f->sampleUnits = lalHertzUnit; output->phi->sampleUnits = lalDimensionlessUnit; snprintf( output->a->name, LALNameLength, "CW amplitudes" ); snprintf( output->f->name, LALNameLength, "CW frequency" ); snprintf( output->phi->name, LALNameLength, "CW phase" ); /* Allocate phase and frequency arrays. */ LALSCreateVector( stat->statusPtr, &( output->f->data ), n ); BEGINFAIL( stat ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); LALDCreateVector( stat->statusPtr, &( output->phi->data ), n ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); /* Allocate and fill amplitude array. */ { CreateVectorSequenceIn in; /* input to create output->a */ in.length = 2; in.vectorLength = 2; LALSCreateVectorSequence( stat->statusPtr, &(output->a->data), &in ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); TRY( LALDDestroyVector( stat->statusPtr, &( output->phi->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); output->a->data->data[0] = output->a->data->data[2] = params->aPlus; output->a->data->data[1] = output->a->data->data[3] = params->aCross; } /* Fill frequency and phase arrays. */ fData = output->f->data->data; phiData = output->phi->data->data; for ( i = 0; i < n; i++ ) { /* Compute emission time. */ c = c0 + dc*i; if ( c > 0 ) e = e0 + de*sinh( log( c + sqrt( c*c + 1.0 ) )/3.0 ); else e = e0 + de*sinh( -log( -c + sqrt( c*c + 1.0 ) )/3.0 ); e2 = e*e; phi = t = tPow = oneBy12vDot*e*( 12.0 + e2 ); /* Compute source emission phase and frequency. */ f = 1.0; for ( j = 0; j < nSpin; j++ ) { f += fSpin[j]*tPow; phi += fSpin[j]*( tPow*=t )/( j + 2.0 ); } /* Appy frequency Doppler shift. */ f *= f0 / ( 1.0 + vp*( fourCosOmega - e*twoSinOmega ) /( 4.0 + e2 ) ); phi *= twopif0; if ( fabs( f - fPrev ) > df ) df = fabs( f - fPrev ); *(fData++) = fPrev = f; *(phiData++) = phi + phi0; } /* Set output field and return. */ params->dfdt = df*dt; DETATCHSTATUSPTR( stat ); RETURN( stat ); }
void LALREAL4AverageSpectrum ( LALStatus *status, REAL4FrequencySeries *fSeries, REAL4TimeSeries *tSeries, AverageSpectrumParams *params ) { UINT4 i, j, k; /* seg, ts and freq counters */ UINT4 numSeg; /* number of segments in average */ UINT4 fLength; /* length of requested power spec */ UINT4 tLength; /* length of time series segments */ REAL4Vector *tSegment = NULL; /* dummy time series segment */ COMPLEX8Vector *fSegment = NULL; /* dummy freq series segment */ REAL4 *tSeriesPtr; /* pointer to the segment data */ REAL4 psdNorm = 0; /* factor to multiply windows data */ REAL4 fftRe, fftIm; /* real and imag parts of fft */ REAL4 *s; /* work space for computing mean */ REAL4 *psdSeg = NULL; /* storage for individual specta */ LALUnit unit; /* RAT4 negRootTwo = { -1, 1 }; */ INITSTATUS(status); XLAL_PRINT_DEPRECATION_WARNING("XLALREAL4AverageSpectrumWelch"); ATTATCHSTATUSPTR (status); /* check the input and output data pointers are non-null */ ASSERT( fSeries, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( fSeries->data, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( fSeries->data->data, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( tSeries, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( tSeries->data, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( tSeries->data->data, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); /* check the contents of the parameter structure */ ASSERT( params, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( params->window, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( params->window->data, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); ASSERT( params->window->data->length > 0, status, TIMEFREQFFTH_EZSEG, TIMEFREQFFTH_MSGEZSEG ); ASSERT( params->plan, status, TIMEFREQFFTH_ENULL, TIMEFREQFFTH_MSGENULL ); if ( ! ( params->method == useUnity || params->method == useMean || params->method == useMedian ) ) { ABORT( status, TIMEFREQFFTH_EUAVG, TIMEFREQFFTH_MSGEUAVG ); } /* check that the window length and fft storage lengths agree */ fLength = fSeries->data->length; tLength = params->window->data->length; if ( fLength != tLength / 2 + 1 ) { ABORT( status, TIMEFREQFFTH_EMISM, TIMEFREQFFTH_MSGEMISM ); } /* compute the number of segs, check that the length and overlap are valid */ numSeg = (tSeries->data->length - params->overlap) / (tLength - params->overlap); if ( (tSeries->data->length - params->overlap) % (tLength - params->overlap) ) { ABORT( status, TIMEFREQFFTH_EMISM, TIMEFREQFFTH_MSGEMISM ); } /* clear the output spectrum and the workspace frequency series */ memset( fSeries->data->data, 0, fLength * sizeof(REAL4) ); /* compute the parameters of the output frequency series data */ fSeries->epoch = tSeries->epoch; fSeries->f0 = tSeries->f0; fSeries->deltaF = 1.0 / ( (REAL8) tLength * tSeries->deltaT ); if ( XLALUnitMultiply( &unit, &(tSeries->sampleUnits), &(tSeries->sampleUnits) ) == NULL ) { ABORTXLAL(status); } if ( XLALUnitMultiply( &(fSeries->sampleUnits), &unit, &lalSecondUnit ) == NULL ) { ABORTXLAL(status); } /* if this is a unit spectrum, just set the conents to unity and return */ if ( params->method == useUnity ) { for ( k = 0; k < fLength; ++k ) { fSeries->data->data[k] = 1.0; } DETATCHSTATUSPTR( status ); RETURN( status ); } /* create temporary storage for the dummy time domain segment */ LALCreateVector( status->statusPtr, &tSegment, tLength ); CHECKSTATUSPTR( status ); /* create temporary storage for the individual ffts */ LALCCreateVector( status->statusPtr, &fSegment, fLength ); CHECKSTATUSPTR( status ); if ( params->method == useMedian ) { /* create enough storage for the indivdiual power spectra */ psdSeg = XLALCalloc( numSeg, fLength * sizeof(REAL4) ); } /* compute each of the power spectra used in the average */ for ( i = 0, tSeriesPtr = tSeries->data->data; i < (UINT4) numSeg; ++i ) { /* copy the time series data to the dummy segment */ memcpy( tSegment->data, tSeriesPtr, tLength * sizeof(REAL4) ); /* window the time series segment */ for ( j = 0; j < tLength; ++j ) { tSegment->data[j] *= params->window->data->data[j]; } /* compute the fft of the data segment */ LALForwardRealFFT( status->statusPtr, fSegment, tSegment, params->plan ); CHECKSTATUSPTR (status); /* advance the segment data pointer to the start of the next segment */ tSeriesPtr += tLength - params->overlap; /* compute the psd components */ if ( params->method == useMean ) { /* we can get away with less storage */ for ( k = 0; k < fLength; ++k ) { fftRe = crealf(fSegment->data[k]); fftIm = cimagf(fSegment->data[k]); fSeries->data->data[k] += fftRe * fftRe + fftIm * fftIm; } /* halve the DC and Nyquist components to be consistent with T010095 */ fSeries->data->data[0] /= 2; fSeries->data->data[fLength - 1] /= 2; } else if ( params->method == useMedian ) { /* we must store all the spectra */ for ( k = 0; k < fLength; ++k ) { fftRe = crealf(fSegment->data[k]); fftIm = cimagf(fSegment->data[k]); psdSeg[i * fLength + k] = fftRe * fftRe + fftIm * fftIm; } /* halve the DC and Nyquist components to be consistent with T010095 */ psdSeg[i * fLength] /= 2; psdSeg[i * fLength + fLength - 1] /= 2; } } /* destroy the dummy time series segment and the fft scratch space */ LALDestroyVector( status->statusPtr, &tSegment ); CHECKSTATUSPTR( status ); LALCDestroyVector( status->statusPtr, &fSegment ); CHECKSTATUSPTR( status ); /* compute the desired average of the spectra */ if ( params->method == useMean ) { /* normalization constant for the arithmentic mean */ psdNorm = ( 2.0 * tSeries->deltaT ) / ( (REAL4) numSeg * params->window->sumofsquares ); /* normalize the psd to it matches the conventions document */ for ( k = 0; k < fLength; ++k ) { fSeries->data->data[k] *= psdNorm; } } else if ( params->method == useMedian ) { REAL8 bias; /* determine the running median bias */ /* note: this is not the correct bias if the segments are overlapped */ if ( params->overlap ) { LALWarning( status, "Overlapping segments with median method causes a biased spectrum." ); } TRY( LALRngMedBias( status->statusPtr, &bias, numSeg ), status ); /* normalization constant for the median */ psdNorm = ( 2.0 * tSeries->deltaT ) / ( bias * params->window->sumofsquares ); /* allocate memory array for insert sort */ s = XLALMalloc( numSeg * sizeof(REAL4) ); if ( ! s ) { ABORT( status, TIMEFREQFFTH_EMALLOC, TIMEFREQFFTH_MSGEMALLOC ); } /* compute the median spectra and normalize to the conventions doc */ for ( k = 0; k < fLength; ++k ) { fSeries->data->data[k] = psdNorm * MedianSpec( psdSeg, s, k, fLength, numSeg ); } /* free memory used for sort array */ XLALFree( s ); /* destroy the storage for the individual spectra */ XLALFree( psdSeg ); } DETATCHSTATUSPTR( status ); RETURN( status ); }
/** * \author Creighton, T. D. * * \brief Computes the response of a detector to a coherent gravitational wave. * * This function takes a quasiperiodic gravitational waveform given in * <tt>*signal</tt>, and estimates the corresponding response of the * detector whose position, orientation, and transfer function are * specified in <tt>*detector</tt>. The result is stored in * <tt>*output</tt>. * * The fields <tt>output-\>epoch</tt>, <tt>output->deltaT</tt>, and * <tt>output-\>data</tt> must already be set, in order to specify the time * period and sampling rate for which the response is required. If * <tt>output-\>f0</tt> is nonzero, idealized heterodyning is performed (an * amount \f$2\pi f_0(t-t_0)\f$ is subtracted from the phase before computing * the sinusoid, where \f$t_0\f$ is the heterodyning epoch defined in * \c detector). For the input signal, <tt>signal-\>h</tt> is ignored, * and the signal is treated as zero at any time for which either * <tt>signal-\>a</tt> or <tt>signal-\>phi</tt> is not defined. * * This routine will convert <tt>signal-\>position</tt> to equatorial * coordinates, if necessary. * * ### Algorithm ### * * The routine first accounts for the time delay between the detector and * the solar system barycentre, based on the detector position * information stored in <tt>*detector</tt> and the propagation direction * specified in <tt>*signal</tt>. Values of the propagation delay are * precomuted at fixed intervals and stored in a table, with the * intervals \f$\Delta T_\mathrm{delay}\f$ chosen such that the value * interpolated from adjacent table entries will never differ from the * true value by more than some timing error \f$\sigma_T\f$. This implies * that: * \f[ * \Delta T_\mathrm{delay} \leq \sqrt{ * \frac{8\sigma_T}{\max\{a/c\}} } \; , * \f] * where \f$\max\{a/c\}=1.32\times10^{-10}\mathrm{s}^{-1}\f$ is the maximum * acceleration of an Earth-based detector in the barycentric frame. The * total propagation delay also includes Einstein and Shapiro delay, but * these are more slowly varying and thus do not constrain the table * spacing. At present, a 400s table spacing is hardwired into the code, * implying \f$\sigma_T\approx3\mu\f$s, comparable to the stated accuracy of * <tt>LALBarycenter()</tt>. * * Next, the polarization response functions of the detector * \f$F_{+,\times}(\alpha,\delta)\f$ are computed for every 10 minutes of the * signal's duration, using the position of the source in <tt>*signal</tt>, * the detector information in <tt>*detector</tt>, and the function * <tt>LALComputeDetAMResponseSeries()</tt>. Subsequently, the * polarization functions are estimated for each output sample by * interpolating these precomputed values. This guarantees that the * interpolated value is accurate to \f$\sim0.1\%\f$. * * Next, the frequency response of the detector is estimated in the * quasiperiodic limit as follows: * <ul> * <li> At each sample point in <tt>*output</tt>, the propagation delay is * computed and added to the sample time, and the instantaneous * amplitudes \f$A_1\f$, \f$A_2\f$, frequency \f$f\f$, phase \f$\phi\f$, and polarization * shift \f$\Phi\f$ are found by interpolating the nearest values in * <tt>signal-\>a</tt>, <tt>signal-\>f</tt>, <tt>signal-\>phi</tt>, and * <tt>signal-\>shift</tt>, respectively. If <tt>signal-\>f</tt> is not * defined at that point in time, then \f$f\f$ is estimated by differencing * the two nearest values of \f$\phi\f$, as \f$f\approx\Delta\phi/2\pi\Delta * t\f$. If <tt>signal-\>shift</tt> is not defined, then \f$\Phi\f$ is treated as * zero.</li> * <li> The complex transfer function of the detector the frequency \f$f\f$ * is found by interpolating <tt>detector-\>transfer</tt>. The amplitude of * the transfer function is multiplied with \f$A_1\f$ and \f$A_2\f$, and the * phase of the transfer function is added to \f$\phi\f$,</li> * <li> The plus and cross contributions \f$o_+\f$, \f$o_\times\f$ to the * detector output are computed as in \eqref{eq_quasiperiodic_hpluscross} * of \ref PulsarSimulateCoherentGW_h, but * using the response-adjusted amplitudes and phase.</li> * <li> The final detector response \f$o\f$ is computed as * \f$o=(o_+F_+)+(o_\times F_\times)\f$.</li> * </ul> * * ### A note on interpolation: ### * * Much of the computational work in this routine involves interpolating * various time series to find their values at specific output times. * The algorithm is summarized below. * * Let \f$A_j = A( t_A + j\Delta t_A )\f$ be a sampled time series, which we * want to resample at new (output) time intervals \f$t_k = t_0 + k\Delta * t\f$. We first precompute the following quantities: * \f{eqnarray}{ * t_\mathrm{off} & = & \frac{t_0-t_A}{\Delta t_A} \; , \\ * dt & = & \frac{\Delta t}{\Delta t_A} \; . * \f} * Then, for each output sample time \f$t_k\f$, we compute: * \f{eqnarray}{ * t & = & t_\mathrm{off} + k \times dt \; , \\ * j & = & \lfloor t \rfloor \; , \\ * f & = & t - j \; , * \f} * where \f$\lfloor x\rfloor\f$ is the "floor" function; i.e.\ the largest * integer \f$\leq x\f$. The time series sampled at the new time is then: * \f[ * A(t_k) = f \times A_{j+1} + (1-f) \times A_j \; . * \f] * * ### Notes ### * * The major computational hit in this routine comes from computing the * sine and cosine of the phase angle in * \eqref{eq_quasiperiodic_hpluscross} of * \ref PulsarSimulateCoherentGW_h. For better online performance, these can * be replaced by other (approximate) trig functions. Presently the code * uses the native \c libm functions by default, or the function * <tt>sincosp()</tt> in \c libsunmath \e if this function is * available \e and the constant \c ONLINE is defined. * Differences at the level of 0.01 begin to appear only for phase * arguments greater than \f$10^{14}\f$ or so (corresponding to over 500 * years between phase epoch and observation time for frequencies of * around 1kHz). * * To activate this feature, be sure that <tt>sunmath.h</tt> and * \c libsunmath are on your system, and add <tt>-DONLINE</tt> to the * <tt>--with-extra-cppflags</tt> configuration argument. In future this * flag may be used to turn on other efficient trig algorithms on other * (non-Solaris) platforms. * */ void LALPulsarSimulateCoherentGW( LALStatus *stat, REAL4TimeSeries *output, PulsarCoherentGW *CWsignal, PulsarDetectorResponse *detector ) { INT4 i, n; /* index over output->data, and its final value */ INT4 nMax; /* used to store limits on index ranges */ INT4 fInit, fFinal; /* index range for which CWsignal->f is defined */ INT4 shiftInit, shiftFinal; /* ditto for CWsignal->shift */ UINT4 dtDelayBy2; /* delay table half-interval (s) */ UINT4 dtPolBy2; /* polarization table half-interval (s) */ REAL4 *outData; /* pointer to output data */ REAL8 delayMin, delayMax; /* min and max values of time delay */ SkyPosition source; /* source sky position */ BOOLEAN transfer; /* 1 if transfer function is specified */ BOOLEAN fFlag = 0; /* 1 if frequency left detector->transfer range */ BOOLEAN pFlag = 0; /* 1 if frequency was estimated from phase */ /* get delay table and polaristion tables half intervals if defined (>0) in the PulsarCoherentGW structure otherwise default to 400s for dtDelatBy2 and 300s for dtPolBy2 */ dtDelayBy2 = CWsignal->dtDelayBy2 > 0 ? CWsignal->dtDelayBy2 : 400; dtPolBy2 = CWsignal->dtPolBy2 > 0 ? CWsignal->dtPolBy2 : 300; /* The amplitude, frequency, phase, polarization shift, polarization response, and propagation delay are stored in arrays that must be interpolated. For a quantity x, we define a pointer xData to the data array. At some time t measured in units of output->deltaT, the interpolation point in xData is given by ( xOff + t*xDt ), where xOff is an offset and xDt is a relative sampling rate. */ LALDetAMResponseSeries polResponse; REAL8Vector *delay = NULL; REAL4 *aData, *fData, *shiftData, *plusData, *crossData; REAL8 *phiData, *delayData; REAL8 aOff, fOff, phiOff, shiftOff, polOff, delayOff; REAL8 aDt, fDt, phiDt, shiftDt, polDt, delayDt; /* Frequencies in the detector transfer function are interpolated similarly, except everything is normalized with respect to detector->transfer->deltaF. */ REAL4Vector *aTransfer = NULL; REAL4Vector *phiTransfer = NULL; REAL4Vector *phiTemp = NULL; REAL4 *aTransData = NULL, *phiTransData = NULL; REAL8 f0 = 1.0; REAL8 phiFac = 1.0, fFac = 1.0; /* Heterodyning phase factor LAL_TWOPI*output->f0*output->deltaT, and phase offset at the start of the series LAL_TWOPI*output->f0*(time offset). */ REAL8 heteroFac, phi0; /* Variables required by the TCENTRE() macro, above. */ REAL8 realIndex; INT4 intIndex; REAL8 indexFrac; INITSTATUS(stat); ATTATCHSTATUSPTR( stat ); /* Make sure parameter structures and their fields exist. */ ASSERT( CWsignal, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); if ( !( CWsignal->a ) ) { ABORT( stat, SIMULATECOHERENTGWH_ESIG, SIMULATECOHERENTGWH_MSGESIG ); } ASSERT( CWsignal->a->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( CWsignal->a->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); if ( !( CWsignal->phi ) ) { ABORT( stat, SIMULATECOHERENTGWH_ESIG, SIMULATECOHERENTGWH_MSGESIG ); } ASSERT( CWsignal->phi->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( CWsignal->phi->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); if ( CWsignal->f ) { ASSERT( CWsignal->f->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( CWsignal->f->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); } if ( CWsignal->shift ) { ASSERT( CWsignal->shift->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( CWsignal->shift->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); } ASSERT( detector, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); if ( ( transfer = ( detector->transfer != NULL ) ) ) { ASSERT( detector->transfer->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( detector->transfer->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); } ASSERT( output, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( output->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); ASSERT( output->data->data, stat, SIMULATECOHERENTGWH_ENUL, SIMULATECOHERENTGWH_MSGENUL ); /* Check dimensions of amplitude array. */ ASSERT( CWsignal->a->data->vectorLength == 2, stat, SIMULATECOHERENTGWH_EDIM, SIMULATECOHERENTGWH_MSGEDIM ); /* Make sure we never divide by zero. */ ASSERT( CWsignal->a->deltaT != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); ASSERT( CWsignal->phi->deltaT != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); aDt = output->deltaT / CWsignal->a->deltaT; phiDt = output->deltaT / CWsignal->phi->deltaT; ASSERT( aDt != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); ASSERT( phiDt != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); if ( CWsignal->f ) { ASSERT( CWsignal->f->deltaT != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); fDt = output->deltaT / CWsignal->f->deltaT; ASSERT( fDt != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); } else fDt = 0.0; if ( CWsignal->shift ) { ASSERT( CWsignal->shift->deltaT != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); shiftDt = output->deltaT / CWsignal->shift->deltaT; ASSERT( shiftDt != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); } else shiftDt = 0.0; if ( transfer ) { ASSERT( detector->transfer->deltaF != 0.0, stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); fFac = 1.0 / detector->transfer->deltaF; phiFac = fFac / ( LAL_TWOPI*CWsignal->phi->deltaT ); f0 = detector->transfer->f0/detector->transfer->deltaF; } heteroFac = LAL_TWOPI*output->f0*output->deltaT; phi0 = (REAL8)( output->epoch.gpsSeconds - detector->heterodyneEpoch.gpsSeconds ); phi0 += 0.000000001*(REAL8)( output->epoch.gpsNanoSeconds - detector->heterodyneEpoch.gpsNanoSeconds ); phi0 *= LAL_TWOPI*output->f0; if ( phi0 > 1.0/LAL_REAL8_EPS ) { LALWarning( stat, "REAL8 arithmetic is not sufficient to maintain" " heterodyne phase to within a radian." ); } /* Check units on input, and set units on output. */ { ASSERT( XLALUnitCompare( &(CWsignal->f->sampleUnits), &lalHertzUnit ) == 0, stat, SIMULATECOHERENTGWH_EUNIT, SIMULATECOHERENTGWH_MSGEUNIT ); ASSERT( XLALUnitCompare( &(CWsignal->phi->sampleUnits), &lalDimensionlessUnit ) == 0, stat, SIMULATECOHERENTGWH_EUNIT, SIMULATECOHERENTGWH_MSGEUNIT ); if( CWsignal->shift ) { ASSERT( XLALUnitCompare( &(CWsignal->shift->sampleUnits), &lalDimensionlessUnit ) == 0, stat, SIMULATECOHERENTGWH_EUNIT, SIMULATECOHERENTGWH_MSGEUNIT ); } if ( transfer ) { if ( XLALUnitMultiply( &(output->sampleUnits), &(CWsignal->a->sampleUnits), &(detector->transfer->sampleUnits) ) == NULL ) { ABORT( stat, SIMULATECOHERENTGWH_EUNIT, SIMULATECOHERENTGWH_MSGEUNIT ); } } else { output->sampleUnits = CWsignal->a->sampleUnits; } snprintf( output->name, LALNameLength, "response to %s", CWsignal->a->name ); } /* Define temporary variables to access the data of CWsignal->a, CWsignal->f, and CWsignal->phi. */ aData = CWsignal->a->data->data; INT4 aLen = CWsignal->a->data->length * CWsignal->a->data->vectorLength; phiData = CWsignal->phi->data->data; INT4 phiLen = CWsignal->phi->data->length; outData = output->data->data; INT4 fLen=0, shiftLen=0; if ( CWsignal->f ) { fData = CWsignal->f->data->data; fLen = CWsignal->f->data->length; } else { fData = NULL; } if ( CWsignal->shift ) { shiftData = CWsignal->shift->data->data; shiftLen = CWsignal->shift->data->length; } else { shiftData = NULL; } /* Convert source position to equatorial coordinates, if required. */ if ( detector->site ) { source = CWsignal->position; if ( source.system != COORDINATESYSTEM_EQUATORIAL ) { ConvertSkyParams params; /* parameters for conversion */ EarthPosition location; /* location of detector */ params.gpsTime = &( output->epoch ); params.system = COORDINATESYSTEM_EQUATORIAL; if ( source.system == COORDINATESYSTEM_HORIZON ) { params.zenith = &( location.geodetic ); location.x = detector->site->location[0]; location.y = detector->site->location[1]; location.z = detector->site->location[2]; TRY( LALGeocentricToGeodetic( stat->statusPtr, &location ), stat ); } TRY( LALConvertSkyCoordinates( stat->statusPtr, &source, &source, ¶ms ), stat ); } } /* Generate the table of propagation delays. dtDelayBy2 = (UINT4)( 38924.9/sqrt( output->f0 + 1.0/output->deltaT ) ); */ delayDt = output->deltaT/( 2.0*dtDelayBy2 ); nMax = (UINT4)( output->data->length*delayDt ) + 3; TRY( LALDCreateVector( stat->statusPtr, &delay, nMax ), stat ); delayData = delay->data; /* Compute delay from solar system barycentre. */ if ( detector->site && detector->ephemerides ) { LIGOTimeGPS gpsTime; /* detector time when we compute delay */ EarthState state; /* Earth position info at that time */ BarycenterInput input; /* input structure to LALBarycenter() */ EmissionTime emit; /* output structure from LALBarycenter() */ /* Arrange nested pointers, and set initial values. */ gpsTime = input.tgps = output->epoch; gpsTime.gpsSeconds -= dtDelayBy2; input.tgps.gpsSeconds -= dtDelayBy2; input.site = *(detector->site); for ( i = 0; i < 3; i++ ) input.site.location[i] /= LAL_C_SI; input.alpha = source.longitude; input.delta = source.latitude; input.dInv = 0.0; delayMin = delayMax = 1.1*LAL_AU_SI/( LAL_C_SI*output->deltaT ); delayMax *= -1; /* Compute table. */ for ( i = 0; i < nMax; i++ ) { REAL8 tDelay; /* propagation time */ LALBarycenterEarth( stat->statusPtr, &state, &gpsTime, detector->ephemerides ); BEGINFAIL( stat ) TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); ENDFAIL( stat ); LALBarycenter( stat->statusPtr, &emit, &input, &state ); BEGINFAIL( stat ) TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); ENDFAIL( stat ); delayData[i] = tDelay = emit.deltaT/output->deltaT; if ( tDelay < delayMin ) delayMin = tDelay; if ( tDelay > delayMax ) delayMax = tDelay; gpsTime.gpsSeconds += 2*dtDelayBy2; input.tgps.gpsSeconds += 2*dtDelayBy2; } } /* No information from which to compute delays. */ else { LALInfo( stat, "Detector site and ephemerides absent; simulating hplus with no" " propagation delays" ); memset( delayData, 0, nMax*sizeof(REAL8) ); delayMin = delayMax = 0.0; } /* Generate the table of polarization response functions. */ polDt = output->deltaT/( 2.0*dtPolBy2 ); nMax = (UINT4)( output->data->length*polDt ) + 3; memset( &polResponse, 0, sizeof( LALDetAMResponseSeries ) ); polResponse.pPlus = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ); polResponse.pCross = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ); polResponse.pScalar = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ); if ( !polResponse.pPlus || !polResponse.pCross || !polResponse.pScalar ) { if ( polResponse.pPlus ) LALFree( polResponse.pPlus ); if ( polResponse.pCross ) LALFree( polResponse.pCross ); if ( polResponse.pScalar ) LALFree( polResponse.pScalar ); TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); ABORT( stat, SIMULATECOHERENTGWH_EMEM, SIMULATECOHERENTGWH_MSGEMEM ); } memset( polResponse.pPlus, 0, sizeof(REAL4TimeSeries) ); memset( polResponse.pCross, 0, sizeof(REAL4TimeSeries) ); memset( polResponse.pScalar, 0, sizeof(REAL4TimeSeries) ); LALSCreateVector( stat->statusPtr, &( polResponse.pPlus->data ), nMax ); BEGINFAIL( stat ) { LALFree( polResponse.pPlus ); LALFree( polResponse.pCross ); LALFree( polResponse.pScalar ); TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); } ENDFAIL( stat ); LALSCreateVector( stat->statusPtr, &( polResponse.pCross->data ), nMax ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pPlus->data ) ), stat ); LALFree( polResponse.pPlus ); LALFree( polResponse.pCross ); LALFree( polResponse.pScalar ); TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); } ENDFAIL( stat ); LALSCreateVector( stat->statusPtr, &( polResponse.pScalar->data ), nMax ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pPlus->data ) ), stat ); TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pCross->data ) ), stat ); LALFree( polResponse.pPlus ); LALFree( polResponse.pCross ); LALFree( polResponse.pScalar ); TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); } ENDFAIL( stat ); plusData = polResponse.pPlus->data->data; crossData = polResponse.pCross->data->data; INT4 plusLen = polResponse.pPlus->data->length; INT4 crossLen = polResponse.pCross->data->length; if ( plusLen != crossLen ) { XLALPrintError ("plusLen = %d != crossLen = %d\n", plusLen, crossLen ); ABORT ( stat, SIMULATECOHERENTGWH_EBAD, SIMULATECOHERENTGWH_MSGEBAD ); } if ( detector->site ) { LALSource polSource; /* position and polarization angle */ LALDetAndSource input; /* response input structure */ LALTimeIntervalAndNSample params; /* response parameter structure */ /* Arrange nested pointers, and set initial values. */ polSource.equatorialCoords = source; polSource.orientation = (REAL8)( CWsignal->psi ); input.pSource = &polSource; input.pDetector = detector->site; params.epoch = output->epoch; params.epoch.gpsSeconds -= dtPolBy2; params.deltaT = 2.0*dtPolBy2; params.nSample = nMax; /* Compute table of responses. */ LALComputeDetAMResponseSeries( stat->statusPtr, &polResponse, &input, ¶ms ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pPlus->data ) ), stat ); TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pCross->data ) ), stat ); TRY( LALSDestroyVector( stat->statusPtr, &( polResponse.pScalar->data ) ), stat ); LALFree( polResponse.pPlus ); LALFree( polResponse.pCross ); LALFree( polResponse.pScalar ); TRY( LALDDestroyVector( stat->statusPtr, &delay ), stat ); } ENDFAIL( stat ); } else {
void LALRingInjectSignals( LALStatus *stat, REAL4TimeSeries *series, SimRingdownTable *injections, COMPLEX8FrequencySeries *resp, INT4 calType ) { UINT4 k; INT4 injStartTime; INT4 injStopTime; DetectorResponse detector; COMPLEX8Vector *unity = NULL; CoherentGW waveform; RingParamStruc ringParam; REAL4TimeSeries signalvec; SimRingdownTable *simRingdown=NULL; LALDetector *tmpDetector=NULL /*,*nullDetector=NULL*/; COMPLEX8FrequencySeries *transfer = NULL; INITSTATUS(stat); ATTATCHSTATUSPTR( stat ); /* set up start and end of injection zone TODO: fix this hardwired 10 */ injStartTime = series->epoch.gpsSeconds - 10; injStopTime = series->epoch.gpsSeconds + 10 + (INT4)(series->data->length * series->deltaT); /* *compute the transfer function */ /* allocate memory and copy the parameters describing the freq series */ memset( &detector, 0, sizeof( DetectorResponse ) ); transfer = (COMPLEX8FrequencySeries *) LALCalloc( 1, sizeof(COMPLEX8FrequencySeries) ); if ( ! transfer ) { ABORT( stat, GENERATERINGH_EMEM, GENERATERINGH_MSGEMEM ); } memcpy( &(transfer->epoch), &(resp->epoch), sizeof(LIGOTimeGPS) ); transfer->f0 = resp->f0; transfer->deltaF = resp->deltaF; tmpDetector = detector.site = (LALDetector *) LALMalloc( sizeof(LALDetector) ); /* set the detector site */ switch ( series->name[0] ) { case 'H': *(detector.site) = lalCachedDetectors[LALDetectorIndexLHODIFF]; LALWarning( stat, "computing waveform for Hanford." ); break; case 'L': *(detector.site) = lalCachedDetectors[LALDetectorIndexLLODIFF]; LALWarning( stat, "computing waveform for Livingston." ); break; default: LALFree( detector.site ); detector.site = NULL; tmpDetector = NULL; LALWarning( stat, "Unknown detector site, computing plus mode " "waveform with no time delay" ); break; } /* set up units for the transfer function */ if (XLALUnitDivide( &(detector.transfer->sampleUnits), &lalADCCountUnit, &lalStrainUnit ) == NULL) { ABORTXLAL(stat); } /* invert the response function to get the transfer function */ LALCCreateVector( stat->statusPtr, &( transfer->data ), resp->data->length ); CHECKSTATUSPTR( stat ); LALCCreateVector( stat->statusPtr, &unity, resp->data->length ); CHECKSTATUSPTR( stat ); for ( k = 0; k < resp->data->length; ++k ) { unity->data[k] = 1.0; } LALCCVectorDivide( stat->statusPtr, transfer->data, unity, resp->data ); CHECKSTATUSPTR( stat ); LALCDestroyVector( stat->statusPtr, &unity ); CHECKSTATUSPTR( stat ); /* Set up a time series to hold signal in ADC counts */ signalvec.deltaT = series->deltaT; if ( ( signalvec.f0 = series->f0 ) != 0 ) { ABORT( stat, GENERATERINGH_EMEM, GENERATERINGH_MSGEMEM ); } signalvec.sampleUnits = lalADCCountUnit; signalvec.data=NULL; LALSCreateVector( stat->statusPtr, &(signalvec.data), series->data->length ); CHECKSTATUSPTR( stat ); /* loop over list of waveforms and inject into data stream */ simRingdown = injections; while ( simRingdown ) { /* only do the work if the ring is in injection zone */ if( (injStartTime - simRingdown->geocent_start_time.gpsSeconds) * (injStopTime - simRingdown->geocent_start_time.gpsSeconds) > 0 ) { simRingdown = simRingdown->next; continue; } /* set the ring params */ ringParam.deltaT = series->deltaT; if( !( strcmp( simRingdown->coordinates, "HORIZON" ) ) ) { ringParam.system = COORDINATESYSTEM_HORIZON; } else if ( !( strcmp( simRingdown->coordinates, "ZENITH" ) ) ) { /* set coordinate system for completeness */ ringParam.system = COORDINATESYSTEM_EQUATORIAL; detector.site = NULL; } else if ( !( strcmp( simRingdown->coordinates, "GEOGRAPHIC" ) ) ) { ringParam.system = COORDINATESYSTEM_GEOGRAPHIC; } else if ( !( strcmp( simRingdown->coordinates, "EQUATORIAL" ) ) ) { ringParam.system = COORDINATESYSTEM_EQUATORIAL; } else if ( !( strcmp( simRingdown->coordinates, "ECLIPTIC" ) ) ) { ringParam.system = COORDINATESYSTEM_ECLIPTIC; } else if ( !( strcmp( simRingdown->coordinates, "GALACTIC" ) ) ) { ringParam.system = COORDINATESYSTEM_GALACTIC; } else ringParam.system = COORDINATESYSTEM_EQUATORIAL; /* generate the ring */ memset( &waveform, 0, sizeof(CoherentGW) ); LALGenerateRing( stat->statusPtr, &waveform, series, simRingdown, &ringParam ); CHECKSTATUSPTR( stat ); /* print the waveform to a file */ if ( 0 ) { FILE *fp; char fname[512]; UINT4 jj, kplus, kcross; snprintf( fname, sizeof(fname) / sizeof(*fname), "waveform-%d-%d-%s.txt", simRingdown->geocent_start_time.gpsSeconds, simRingdown->geocent_start_time.gpsNanoSeconds, simRingdown->waveform ); fp = fopen( fname, "w" ); for( jj = 0, kplus = 0, kcross = 1; jj < waveform.phi->data->length; ++jj, kplus += 2, kcross +=2 ) { fprintf(fp, "%d %e %e %le %e\n", jj, waveform.a->data->data[kplus], waveform.a->data->data[kcross], waveform.phi->data->data[jj], waveform.f->data->data[jj]); } fclose( fp ); } /* end */ #if 0 fprintf( stderr, "a->epoch->gpsSeconds = %d\na->epoch->gpsNanoSeconds = %d\n", waveform.a->epoch.gpsSeconds, waveform.a->epoch.gpsNanoSeconds ); fprintf( stderr, "phi->epoch->gpsSeconds = %d\nphi->epoch->gpsNanoSeconds = %d\n", waveform.phi->epoch.gpsSeconds, waveform.phi->epoch.gpsNanoSeconds ); fprintf( stderr, "f->epoch->gpsSeconds = %d\nf->epoch->gpsNanoSeconds = %d\n", waveform.f->epoch.gpsSeconds, waveform.f->epoch.gpsNanoSeconds ); #endif /* must set the epoch of signal since it's used by coherent GW */ signalvec.epoch = series->epoch; memset( signalvec.data->data, 0, signalvec.data->length * sizeof(REAL4) ); /* decide which way to calibrate the data; defaul to old way */ if( calType ) detector.transfer=NULL; else detector.transfer=transfer; /* convert this into an ADC signal */ LALSimulateCoherentGW( stat->statusPtr, &signalvec, &waveform, &detector ); CHECKSTATUSPTR( stat ); /* print the waveform to a file */ if ( 0 ) { FILE *fp; char fname[512]; UINT4 jj; snprintf( fname, sizeof(fname) / sizeof(*fname), "signal-%d-%d-%s.txt", simRingdown->geocent_start_time.gpsSeconds, simRingdown->geocent_start_time.gpsNanoSeconds, simRingdown->waveform ); fp = fopen( fname, "w" ); for( jj = 0; jj < signalvec.data->length; ++jj ) { fprintf( fp, "%d %le\n", jj, signalvec.data->data[jj] ); } fclose( fp ); } /* end */ #if 0 fprintf( stderr, "series.epoch->gpsSeconds = %d\nseries.epoch->gpsNanoSeconds = %d\n", series->epoch.gpsSeconds, series->epoch.gpsNanoSeconds ); fprintf( stderr, "signalvec->epoch->gpsSeconds = %d\nsignalvec->epoch->gpsNanoSeconds = %d\n", signalvec.epoch.gpsSeconds, signalvec.epoch.gpsNanoSeconds ); #endif /* if calibration using RespFilt */ if( calType == 1 ) XLALRespFilt(&signalvec, transfer); /* inject the signal into the data channel */ LALSSInjectTimeSeries( stat->statusPtr, series, &signalvec ); CHECKSTATUSPTR( stat ); /* free memory in coherent GW structure. TODO: fix this */ LALSDestroyVectorSequence( stat->statusPtr, &( waveform.a->data ) ); CHECKSTATUSPTR( stat ); LALSDestroyVector( stat->statusPtr, &( waveform.f->data ) ); CHECKSTATUSPTR( stat ); LALDDestroyVector( stat->statusPtr, &( waveform.phi->data ) ); CHECKSTATUSPTR( stat ); LALFree( waveform.a ); waveform.a = NULL; LALFree( waveform.f ); waveform.f = NULL; LALFree( waveform.phi ); waveform.phi = NULL; /* reset the detector site information in case it changed */ detector.site = tmpDetector; /* move on to next one */ simRingdown = simRingdown->next; } /* destroy the signal */ LALSDestroyVector( stat->statusPtr, &(signalvec.data) ); CHECKSTATUSPTR( stat ); LALCDestroyVector( stat->statusPtr, &( transfer->data ) ); CHECKSTATUSPTR( stat ); if ( detector.site ) LALFree( detector.site ); LALFree( transfer ); DETATCHSTATUSPTR( stat ); RETURN( stat ); }
/* Define a function for parsing a string literal. */ static void LALLiteralToString( LALStatus *stat, CHAR *string, const CHAR *literal, UINT4 length ) { CHAR c; /* Current character being considered. */ UINT4 n = 0; /* Counter of number of characters written. */ INITSTATUS(stat); /* Find open quote. */ while ( ( c = *literal ) != '"' && c != '\n' && c != '\0' ) literal++; if ( *literal != '"' ) { LALWarning( stat, "No open quote found" ); RETURN( stat ); } literal++; /* Start parsing. */ while ( n < length - 1 ) { /* End of literal, either implicit or explicit. */ if ( ( c = *(literal++) ) == '\0' || c == '\n' ) { LALWarning( stat, "No close quote found" ); string[n] = '\0'; RETURN( stat ); } else if ( c == '"' ) { string[n] = '\0'; RETURN( stat ); } /* Escape sequence. */ else if ( c == '\\' ) { /* Do not allow actual end-of-line or end-of-string to be escaped. */ if ( ( c = *(literal++) ) == '\0' || c == '\n' ) { LALWarning( stat, "No close quote found" ); string[n] = '\0'; RETURN( stat ); } /* Other special escape characters. */ else if ( c == 'a' || c == 'A' ) string[n++] = '\a'; else if ( c == 'b' || c == 'B' ) string[n++] = '\b'; else if ( c == 'f' || c == 'F' ) string[n++] = '\f'; else if ( c == 'n' || c == 'N' ) string[n++] = '\n'; else if ( c == 'r' || c == 'R' ) string[n++] = '\r'; else if ( c == 't' || c == 'T' ) string[n++] = '\t'; else if ( c == 'v' || c == 'V' ) string[n++] = '\v'; /* Hexadecimal character code. */ else if ( c == 'x' || c == 'X' ) { c = *(literal++); /* first digit */ if ( isxdigit( c ) ) { UINT2 value; if ( isdigit( c ) ) value = c - '0'; else value = 10 + tolower( c ) - 'a'; c = *(literal++); /* second digit */ if ( isxdigit( c ) ) { value *= 16; if ( isdigit( c ) ) value += c - '0'; else value += 10 + tolower( c ) - 'a'; } else /* no second digit */ literal--; string[n++] = (CHAR)( value ); if ( value == 0 ) { LALWarning( stat, "Found explicit end-of-string \\0" ); RETURN( stat ); } } else { /* no first digit */ LALWarning( stat, "Treating empty hex cde as explicit" " end-of-string \\0" ); string[n] = '\0'; RETURN( stat ); } } /* Octal character code. */ else if ( c >= '0' && c < '8' ) { UINT2 value = c - '0'; c = *(literal++); /* second digit */ if ( c >= '0' && c < '8' ) { value *= 8; value += c - '0'; c = *(literal++); /* third digit */ if ( c >= '0' && c < '8' ) { value *= 8; value += c - '0'; } else /* no third digit */ literal--; } else /* no second digit */ literal--; if ( value > 255 ) LALWarning( stat, "Ignoring octal character code >= '\\400'" ); else string[n++] = (CHAR)( value ); if ( value == 0 ) { LALWarning( stat, "Found explicit end-of-string \\0" ); RETURN( stat ); } } /* Other escaped character. */ else { if ( c != '\\' && c != '?' && c != '\'' && c != '"' ) LALWarning( stat, "Dropping \\ from unrecognized escape" " sequence" ); string[n++] = c; } } /* Other character. */ else string[n++] = c; } if ( *literal != '"' ) LALWarning( stat, "Reached maximum length before reading close" " quote" ); string[n] = '\0'; RETURN( stat ); }
/** \see See \ref LALInspiralHybridHexagonalBank_c for documentation */ void LALInspiralCreatePNCoarseBankHybridHexa( LALStatus *status, InspiralTemplateList **list, INT4 *nlist, InspiralCoarseBankIn coarseIn ) { INT4 i; INT4 firstId = 0; REAL4 A0, A3; REAL4 piFl; InspiralBankParams bankPars; InspiralTemplate *tempPars; InspiralMomentsEtc moments; InspiralCell *cells = 0; HexaGridParam gridParam; CellEvolution cellEvolution; CellList *cellList = NULL; INITSTATUS(status); ATTATCHSTATUSPTR( status ); ASSERT( coarseIn.mMin > 0., status, LALINSPIRALBANKH_ESIZE, LALINSPIRALBANKH_MSGESIZE ); ASSERT( coarseIn.mMax > 0., status, LALINSPIRALBANKH_ESIZE, LALINSPIRALBANKH_MSGESIZE ); ASSERT( coarseIn.MMax >= 2.*coarseIn.mMin, status, LALINSPIRALBANKH_ESIZE, LALINSPIRALBANKH_MSGESIZE ); /* Set the elements of the metric and tempPars structures in */ /* conformity with the coarseIn structure */ if ( !(tempPars = (InspiralTemplate *) LALCalloc( 1, sizeof(InspiralTemplate)))) { LALFree(tempPars); LALFree(cells); ABORT( status, LALINSPIRALBANKH_EMEM, LALINSPIRALBANKH_MSGEMEM ); } LALInspiralSetParams( status->statusPtr, tempPars, coarseIn ); CHECKSTATUSPTR( status ); /* Identify the boundary of search and parameters for the */ /* first lattice point */ LALInspiralSetSearchLimits( status->statusPtr, &bankPars, coarseIn ); CHECKSTATUSPTR( status ); tempPars->totalMass = coarseIn.MMax; tempPars->eta = 0.25; tempPars->ieta = 1.L; tempPars->fLower = coarseIn.fLower; tempPars->massChoice = m1Andm2; tempPars->mass1 = coarseIn.mMin; tempPars->mass2 = coarseIn.mMax; LALInspiralParameterCalc( status->statusPtr, tempPars ); CHECKSTATUSPTR( status ); /* Get the moments of the PSD integrand and other parameters */ /* required in the computation of the metric once for all. */ LALGetInspiralMoments( status->statusPtr, &moments, &coarseIn.shf, tempPars ); CHECKSTATUSPTR( status ); /* Allocate memory for one cell */ cells = (InspiralCell*) LALCalloc(1, sizeof(InspiralCell) ); /*define gridParam*/ gridParam.mm = coarseIn.mmCoarse; gridParam.x0Min = bankPars.x0Min; gridParam.x0Max = bankPars.x0Max; gridParam.x1Min = bankPars.x1Min; gridParam.x1Max = bankPars.x1Max; gridParam.mMin = coarseIn.mMin; gridParam.mMax = coarseIn.mMax; gridParam.MMin = coarseIn.MMin; gridParam.MMax = coarseIn.MMax; gridParam.etaMin = coarseIn.etamin; gridParam.space = coarseIn.space; gridParam.massRange = coarseIn.massRange; gridParam.gridSpacing = coarseIn.gridSpacing; gridParam.fLower = coarseIn.fLower; cellEvolution.nTemplate = 1; cellEvolution.nTemplateMax = 1; cellEvolution.fertile = 0; /* initialise that first cell */ tempPars->massChoice = t03; cells[0].t0 = tempPars->t0; cells[0].t3 = tempPars->t3; /* some aliases */ piFl = LAL_PI * tempPars->fLower; A0 = 5. / pow(piFl, 8./3.) / 256.; A3 = LAL_PI / pow(piFl, 5./3.)/8.; /* Initialise the first template */ LALInitHexagonalBank( status->statusPtr, &cells, firstId, &moments, tempPars, &gridParam, &cellEvolution, &cellList); CHECKSTATUSPTR( status ); { INT4 k, kk; /*some indexes*/ INT4 *new_list = NULL; CellList *ptr = NULL; INT4 length = 1; /* default size of the bank when we start the bank generation. */ /* we re-allocate an array which size equals the * template bank size. */ if (! (new_list = LALMalloc(length*sizeof(INT4)))) { ABORT( status, LALINSPIRALBANKH_EMEM, LALINSPIRALBANKH_MSGEMEM ); } /* while there are cells/template which can propagate, we carry on the loop.*/ while (cellEvolution.fertile) { length = LALListLength(cellList); /*realloc some memory for the next template*/ if (! (new_list = LALRealloc(new_list, length*sizeof(INT4)))) { ABORT( status, LALINSPIRALBANKH_EMEM, LALINSPIRALBANKH_MSGEMEM ); /* freeing memory here ? */ } ptr = cellList; /* we extract the ids which might change within the LALPopulateCell * function. Indeed the bank might grow and then we will lost track * of ids/bank size and so on. */ for ( k = 0; k < length; k++) { new_list[k] = ptr->id; ptr = ptr->next; } /* look at all the template/ids in the current bank to search for fertile cells */ for (kk = 0; kk < length; kk++) { k = new_list[kk]; if ( cells[k].status == Fertile) { LALPopulateCell(status->statusPtr, &moments, &cells, k, tempPars, &gridParam, &cellEvolution, &cellList); CHECKSTATUSPTR( status ); /* now the bank might have grown, but we only look at the * template created before this for loop, when we entered * in the while loop * */ } } } LALFree(new_list); } #if 1 { INT4 edge1=0, edge2=0; gridParam.gridSpacing = Hexagonal; i=0; while (i<cellEvolution.nTemplate){ if (cells[i].position == Edge){ edge1 = i; cells[i].position = In; i=cellEvolution.nTemplate; } i++; } i=0; while (i<cellEvolution.nTemplate){ if (cells[i].position == Edge){ edge2=i; cells[i].position = In; i=cellEvolution.nTemplate; } i++; } if (cells[edge1].t0 > cells[edge2].t0){ LALPopulateNarrowEdge(status->statusPtr, &moments, &cells, edge1, tempPars, &gridParam, &cellEvolution, &cellList, 0); CHECKSTATUSPTR( status ); LALPopulateNarrowEdge(status->statusPtr, &moments, &cells, edge2, tempPars, &gridParam, &cellEvolution, &cellList, 1); CHECKSTATUSPTR( status ); } else { LALPopulateNarrowEdge(status->statusPtr, &moments, &cells, edge1, tempPars, &gridParam, &cellEvolution, &cellList, 1); CHECKSTATUSPTR( status ); LALPopulateNarrowEdge(status->statusPtr, &moments, &cells, edge2, tempPars, &gridParam, &cellEvolution, &cellList, 0); CHECKSTATUSPTR( status ); } } #endif if (cellList != NULL) ABORT(status, LALINSPIRALBANKH_EHEXAINIT,LALINSPIRALBANKH_MSGEHEXAINIT); /* Here is the current number of template generated. Now, we need * to clean some of them which might be redundant. * */ *nlist = cellEvolution.nTemplate; { INT4 k ; INT4 length; length = cellEvolution.nTemplate; for ( k = 0; k < length; k++) { REAL4 a; REAL4 b; REAL4 x0; REAL4 tempA3; SFindRootIn input; INT4 valid; PRIN prin; tempA3 = pow(A3, -5./2.)/pow(0.25,-1.5); tempPars->t0 = cells[k].t0; tempPars->t3 = cells[k].t3; /* if non physical parameter i.e below eta=0.25*/ if(cells[k].RectPosition[0] == Below ) { INT4 above=0, below=0, in=0, out=0; /*first, we define the line which is along the long semi-axis of the * ambiguity function, defined by the angle theta and the position of * the template. * */ a = tan(cells[k].metric.theta); b = cells[k].t3 - a * cells[k].t0; /* and call a function to search for a solution along eta=1/4 */ input.function = LALSPAF; input.xmin = cells[k].t3-1e-3; input.xmax = 1000; input.xacc = 1e-6; prin.ct = a * A0 * tempA3; prin.b = b; LALSBisectionFindRoot(status->statusPtr, &x0, &input, (void *)&prin); CHECKSTATUSPTR( status ); tempPars->t3 = x0 + 1e-3; /* to be sure it is physical */ tempPars->t0 = (tempPars->t3 - b)/a; if (tempPars->t0 > 0) { LALInspiralParameterCalc(status->statusPtr, tempPars); CHECKSTATUSPTR( status ); } else { LALWarning(status,"HybridHexagonal placement: nothing to be done since t0<=0\n"); } cells[k].t0 = tempPars->t0; cells[k].t3 = tempPars->t3; /* update its position values */ valid = 1; GetPositionRectangle(status->statusPtr, &cells, k, tempPars , &gridParam, &cellEvolution, &cellList, &valid); { switch (cells[k].RectPosition[1]){ case In: in +=1; break; case Below: below +=1; break; case Above: above +=1; break; case Out: out +=1; break; case Edge: break; } switch (cells[k].RectPosition[2]){ case In: in +=1; break; case Below: below +=1; break; case Above: above +=1; break; case Out: out +=1; break; case Edge: break; } switch (cells[k].RectPosition[3]){ case In: in +=1; break; case Below: below +=1; break; case Above: above +=1; break; case Out: out +=1; break; case Edge: break; } switch (cells[k].RectPosition[4]){ case In: in +=1; break; case Below: below +=1; break; case Above: above +=1; break; case Out: out +=1; break; case Edge: break; } } if (above == 2 && cells[k].position == In) { if (cells[k].child[0] >=0 ) { cells[cells[k].child[0]].position = Out; } else { /* nothing to be done, the child is not valid anyway*/ } } else { } } else { } } } for (i=0; i<cellEvolution.nTemplate; i++) { if (cells[i].position == In ) { *nlist = *nlist +1; } } /* allocate appropriate memory and fill the output bank */ *list = (InspiralTemplateList*) LALRealloc( *list, sizeof(InspiralTemplateList) * (*nlist+1) ); if ( ! *list ) { LALFree( tempPars ); ABORT( status, LALINSPIRALBANKH_EMEM, LALINSPIRALBANKH_MSGEMEM ); } memset( *list + *nlist, 0, sizeof(InspiralTemplateList) ); { *nlist = 0 ; for (i=0; i<cellEvolution.nTemplate; i++) { if ((cells[i].position == In) && (cells[i].t0 > 0)) { tempPars->t0 = cells[i].t0; tempPars->t3 = cells[i].t3; tempPars->massChoice = t03; tempPars->fLower = coarseIn.fLower; LALInspiralParameterCalc( status->statusPtr, tempPars ); CHECKSTATUSPTR( status ); (*list)[*nlist].ID = *nlist; (*list)[*nlist].params = *tempPars; (*list)[*nlist].metric = cells[i].metric; ++(*nlist); } } } LALFree( cells ); LALFree( tempPars ); DETATCHSTATUSPTR( status ); RETURN ( status ); }
void LALInspiralEccentricityForInjection( LALStatus *status, CoherentGW *waveform, InspiralTemplate *params, PPNParamStruc *ppnParams ) { INT4 count, i; REAL8 p, phiC; REAL4Vector a; /* pointers to generated amplitude data */ REAL4Vector ff; /* pointers to generated frequency data */ REAL8Vector phi; /* generated phase data */ CreateVectorSequenceIn in; CHAR message[256]; InspiralInit paramsInit; INITSTATUS(status); ATTATCHSTATUSPTR(status); /* Make sure parameter and waveform structures exist. */ ASSERT( params, status, LALINSPIRALH_ENULL, LALINSPIRALH_MSGENULL ); ASSERT(waveform, status, LALINSPIRALH_ENULL, LALINSPIRALH_MSGENULL); ASSERT( !( waveform->a ), status, LALINSPIRALH_ENULL, LALINSPIRALH_MSGENULL ); ASSERT( !( waveform->f ), status, LALINSPIRALH_ENULL, LALINSPIRALH_MSGENULL ); ASSERT( !( waveform->phi ), status, LALINSPIRALH_ENULL, LALINSPIRALH_MSGENULL ); /* Compute some parameters*/ LALInspiralInit(status->statusPtr, params, ¶msInit); CHECKSTATUSPTR(status); if (paramsInit.nbins == 0){ DETATCHSTATUSPTR(status); RETURN (status); } /* Now we can allocate memory and vector for coherentGW structure*/ ff.length = paramsInit.nbins; a.length = 2* paramsInit.nbins; phi.length = paramsInit.nbins; ff.data = (REAL4 *) LALCalloc(paramsInit.nbins, sizeof(REAL4)); a.data = (REAL4 *) LALCalloc(2 * paramsInit.nbins, sizeof(REAL4)); phi.data= (REAL8 *) LALCalloc(paramsInit.nbins, sizeof(REAL8)); /* Check momory allocation is okay */ if (!(ff.data) || !(a.data) || !(phi.data)) { if (ff.data) LALFree(ff.data); if (a.data) LALFree(a.data); if (phi.data) LALFree(phi.data); ABORT( status, LALINSPIRALH_EMEM, LALINSPIRALH_MSGEMEM ); } count = 0; /* Call the engine function */ LALInspiralEccentricityEngine(status->statusPtr, NULL, NULL, &a, &ff, &phi, &count, params); BEGINFAIL( status ) { LALFree(ff.data); LALFree(a.data); LALFree(phi.data); } ENDFAIL( status ); p = phi.data[count-1]; params->fFinal = ff.data[count-1]; sprintf(message, "cycles = %f", p/(double)LAL_TWOPI); LALInfo(status, message); if ( (INT4)(p/LAL_TWOPI) < 2 ){ sprintf(message, "The waveform has only %f cycles; we don't keep waveform with less than 2 cycles.", p/(double)LAL_TWOPI ); XLALPrintError("%s", message); LALWarning(status, message); } /*wrap the phase vector*/ phiC = phi.data[count-1] ; for (i = 0; i < count; i++) { phi.data[i] = phi.data[i] - phiC + ppnParams->phi; } /* Allocate the waveform structures. */ if ( ( waveform->a = (REAL4TimeVectorSeries *) LALCalloc(1, sizeof(REAL4TimeVectorSeries) ) ) == NULL ) { ABORT( status, LALINSPIRALH_EMEM, LALINSPIRALH_MSGEMEM ); } if ( ( waveform->f = (REAL4TimeSeries *) LALCalloc(1, sizeof(REAL4TimeSeries) ) ) == NULL ) { LALFree( waveform->a ); waveform->a = NULL; ABORT( status, LALINSPIRALH_EMEM, LALINSPIRALH_MSGEMEM ); } if ( ( waveform->phi = (REAL8TimeSeries *) LALCalloc(1, sizeof(REAL8TimeSeries) ) ) == NULL ) { LALFree( waveform->a ); waveform->a = NULL; LALFree( waveform->f ); waveform->f = NULL; ABORT( status, LALINSPIRALH_EMEM, LALINSPIRALH_MSGEMEM ); } in.length = (UINT4)(count); in.vectorLength = 2; LALSCreateVectorSequence( status->statusPtr, &( waveform->a->data ), &in ); CHECKSTATUSPTR(status); LALSCreateVector( status->statusPtr, &( waveform->f->data ), count); CHECKSTATUSPTR(status); LALDCreateVector( status->statusPtr, &( waveform->phi->data ), count ); CHECKSTATUSPTR(status); memcpy(waveform->f->data->data , ff.data, count*(sizeof(REAL4))); memcpy(waveform->a->data->data , a.data, 2*count*(sizeof(REAL4))); memcpy(waveform->phi->data->data ,phi.data, count*(sizeof(REAL8))); waveform->a->deltaT = waveform->f->deltaT = waveform->phi->deltaT = ppnParams->deltaT; waveform->a->sampleUnits = lalStrainUnit; waveform->f->sampleUnits = lalHertzUnit; waveform->phi->sampleUnits = lalDimensionlessUnit; waveform->position = ppnParams->position; waveform->psi = ppnParams->psi; snprintf( waveform->a->name, LALNameLength, "T1 inspiral amplitude" ); snprintf( waveform->f->name, LALNameLength, "T1 inspiral frequency" ); snprintf( waveform->phi->name, LALNameLength, "T1 inspiral phase" ); /* --- fill some output ---*/ ppnParams->tc = (double)(count-1) / params->tSampling ; ppnParams->length = count; ppnParams->dfdt = ((REAL4)(waveform->f->data->data[count-1] - waveform->f->data->data[count-2])) * ppnParams->deltaT; ppnParams->fStop = params->fFinal; ppnParams->termCode = GENERATEPPNINSPIRALH_EFSTOP; ppnParams->termDescription = GENERATEPPNINSPIRALH_MSGEFSTOP; ppnParams->fStart = ppnParams->fStartIn; /* --- free memory --- */ LALFree(ff.data); LALFree(a.data); LALFree(phi.data); DETATCHSTATUSPTR(status); RETURN (status); }
/** * Anand: 26 October 2006 * This function nudges the templates in the list to * the (max-total-mass = constant) line. * This is done only for those templates whose total * mass exceeds the desired max-total-mass value. The * templates are nudged along the metric eigen direction * until they lie on the said line. */ void LALNudgeTemplatesToConstantTotalMassLine( LALStatus *status, InspiralTemplateList **list, INT4 nlist, InspiralCoarseBankIn coarseIn ) { InspiralTemplate *tempPars=NULL; InspiralMetric *metric=NULL; InspiralMomentsEtc moments; INITSTATUS(status); ATTATCHSTATUSPTR( status ); /* If there are no templates, return now */ if ( nlist <= 0 ) { LALWarning( status, "number of templates is <= 0 ! " ); DETATCHSTATUSPTR(status); RETURN (status); } /* Allocate memory (only required to calculate noise moments) */ tempPars = (InspiralTemplate *) LALCalloc( 1, sizeof(InspiralTemplate) ); metric = (InspiralMetric *) LALCalloc( 1, sizeof(InspiralMetric) ); /* Init the tempPars */ LALInspiralSetParams( status->statusPtr, tempPars, coarseIn ); CHECKSTATUSPTR( status ); tempPars->totalMass = coarseIn.MMax; tempPars->eta = 0.25; tempPars->ieta = 1.L; tempPars->fLower = coarseIn.fLower; tempPars->massChoice = totalMassAndEta; LALInspiralParameterCalc( status->statusPtr, tempPars ); CHECKSTATUSPTR( status ); /* Get the moments of the PSD required in the computation of the metric */ LALGetInspiralMoments( status->statusPtr, &moments, &coarseIn.shf, tempPars ); CHECKSTATUSPTR( status ); /* Loop over template list and nudge the templates if required */ { INT4 i; REAL4 P, Q, M, C, ms; /*, t0, t3;*/ M = coarseIn.MMax*LAL_MTSUN_SI; P = (5./256.)*pow( (LAL_PI*coarseIn.fLower), -8./3. ) ; Q = (LAL_PI/8.)*pow( (LAL_PI*coarseIn.fLower), -5./3. ) ; for (i=0; i < nlist; i++) { /* If the totalMass of this template exceeds max-total-mass * then nudge along the metric eigen-direction. */ if ( (*list)[i].params.totalMass > coarseIn.MMax ) { ms = tan( LAL_PI/2. + (*list)[i].metric.theta ); C = (*list)[i].params.t3 - ms*((*list)[i].params.t0); /* Calculate the new co-ordinates in tau0-tau3 space */ (*list)[i].params.t3 = C / ( 1. - (ms*P/(M*Q)) ); (*list)[i].params.t0 = P*(*list)[i].params.t3/(M*Q); /* Calculate the other parameters */ LALInspiralParameterCalc( status->statusPtr, &(*list)[i].params ); CHECKSTATUSPTR( status ); /* Check that the new point has not gone down below the * equal mass line. If it has, set it to m1=m2=coarseIn.MMax/2.0 */ if ( (*list)[i].params.eta > 0.25L ) { InputMasses originalMassChoice = (*list)[i].params.massChoice; (*list)[i].params.totalMass = coarseIn.MMax ; (*list)[i].params.eta = 0.25L; (*list)[i].params.massChoice = totalMassAndEta; LALInspiralParameterCalc( status->statusPtr, &(*list)[i].params ); CHECKSTATUSPTR( status ); /* Reset the massChoice to whatever it was */ (*list)[i].params.massChoice = originalMassChoice; } /* Recalculate the metric at this new point */ LALInspiralComputeMetric( status->statusPtr, &((*list)[i].metric), &((*list)[i].params), &moments ); CHECKSTATUSPTR( status ); } }/* Loop over templates */ } /* Clean up */ LALFree( tempPars ); LALFree( metric ); /* Normal exit */ DETATCHSTATUSPTR(status); RETURN (status); }
/** * \author Creighton, T. D. * * \brief Computes a continuous waveform with frequency drift and Doppler * modulation from a hyperbolic orbital trajectory. * * This function computes a quaiperiodic waveform using the spindown and * orbital parameters in <tt>*params</tt>, storing the result in * <tt>*output</tt>. * * In the <tt>*params</tt> structure, the routine uses all the "input" * fields specified in \ref GenerateSpinOrbitCW_h, and sets all of the * "output" fields. If <tt>params-\>f</tt>=\c NULL, no spindown * modulation is performed. If <tt>params-\>oneMinusEcc</tt>\f$\not<0\f$ (a * non-hyperbolic orbit), or if * <tt>params-\>rPeriNorm</tt>\f$\times\f$<tt>params-\>angularSpeed</tt>\f$\geq1\f$ * (faster-than-light speed at periapsis), an error is returned. * * In the <tt>*output</tt> structure, the field <tt>output-\>h</tt> is * ignored, but all other pointer fields must be set to \c NULL. The * function will create and allocate space for <tt>output-\>a</tt>, * <tt>output-\>f</tt>, and <tt>output-\>phi</tt> as necessary. The * <tt>output-\>shift</tt> field will remain set to \c NULL. * * ### Algorithm ### * * For hyperbolic orbits, we combine \eqref{eq_spinorbit-tr}, * \eqref{eq_spinorbit-t}, and \eqref{eq_spinorbit-upsilon} to get \f$t_r\f$ * directly as a function of \f$E\f$: * \f{eqnarray}{ * \label{eq_tr-e3} * t_r = t_p & + & \left(\frac{r_p \sin i}{c}\right)\sin\omega\\ * & + & \frac{1}{n} \left( -E + * \left[v_p(e-1)\cos\omega + e\right]\sinh E * - \left[v_p\sqrt{\frac{e-1}{e+1}}\sin\omega\right] * [\cosh E - 1]\right) \;, * \f} * where \f$v_p=r_p\dot{\upsilon}_p\sin i/c\f$ is a normalized velocity at * periapsis and \f$n=\dot{\upsilon}_p\sqrt{(1-e)^3/(1+e)}\f$ is a normalized * angular speed for the orbit (the hyperbolic analogue of the mean * angular speed for closed orbits). For simplicity we write this as: * \f{equation}{ * \label{eq_tr-e4} * t_r = T_p + \frac{1}{n}\left( E + A\sinh E + B[\cosh E - 1] \right) \;, * \f} * * \figure{inject_hanomaly,eps,0.23,Function to be inverted to find eccentric anomaly} * * where \f$T_p\f$ is the \e observed time of periapsis passage. Thus * the key numerical procedure in this routine is to invert the * expression \f$x=E+A\sinh E+B(\cosh E - 1)\f$ to get \f$E(x)\f$. This function * is sketched to the right (solid line), along with an approximation * used for making an initial guess (dotted line), as described later. * * We note that \f$A^2-B^2<1\f$, although it approaches 1 when * \f$e\rightarrow1\f$, or when \f$v_p\rightarrow1\f$ and either \f$e=0\f$ or * \f$\omega=\pi\f$. Except in this limit, Newton-Raphson methods will * converge rapidly for any initial guess. In this limit, though, the * slope \f$dx/dE\f$ approaches zero at \f$E=0\f$, and an initial guess or * iteration landing near this point will send the next iteration off to * unacceptably large or small values. A hybrid root-finding strategy is * used to deal with this, and with the exponential behaviour of \f$x\f$ at * large \f$E\f$. * * First, we compute \f$x=x_{\pm1}\f$ at \f$E=\pm1\f$. If the desired \f$x\f$ lies * in this range, we use a straightforward Newton-Raphson root finder, * with the constraint that all guesses of \f$E\f$ are restricted to the * domain \f$[-1,1]\f$. This guarantees that the scheme will eventually find * itself on a uniformly-convergent trajectory. * * Second, for \f$E\f$ outside of this range, \f$x\f$ is dominated by the * exponential terms: \f$x\approx\frac{1}{2}(A+B)\exp(E)\f$ for \f$E\gg1\f$, and * \f$x\approx-\frac{1}{2}(A-B)\exp(-E)\f$ for \f$E\ll-1\f$. We therefore do an * \e approximate Newton-Raphson iteration on the function \f$\ln|x|\f$, * where the approximation is that we take \f$d\ln|x|/d|E|\approx1\f$. This * involves computing an extra logarithm inside the loop, but gives very * rapid convergence to high precision, since \f$\ln|x|\f$ is very nearly * linear in these regions. * * At the start of the algorithm, we use an initial guess of * \f$E=-\ln[-2(x-x_{-1})/(A-B)-\exp(1)]\f$ for \f$x<x_{-1}\f$, \f$E=x/x_{-1}\f$ for * \f$x_{-1}\leq x\leq0\f$, \f$E=x/x_{+1}\f$ for \f$0\leq x\leq x_{+1}\f$, or * \f$E=\ln[2(x-x_{+1})/(A+B)-\exp(1)]\f$ for \f$x>x_{+1}\f$. We refine this * guess until we get agreement to within 0.01 parts in part in * \f$N_\mathrm{cyc}\f$ (where \f$N_\mathrm{cyc}\f$ is the larger of the number * of wave cycles in a time \f$2\pi/n\f$, or the number of wave cycles in the * entire waveform being generated), or one part in \f$10^{15}\f$ (an order * of magnitude off the best precision possible with \c REAL8 * numbers). The latter case indicates that \c REAL8 precision may * fail to give accurate phasing, and one should consider modeling the * orbit as a set of Taylor frequency coefficients \'{a} la * <tt>LALGenerateTaylorCW()</tt>. On subsequent timesteps, we use the * previous timestep as an initial guess, which is good so long as the * timesteps are much smaller than \f$1/n\f$. * * Once a value of \f$E\f$ is found for a given timestep in the output * series, we compute the system time \f$t\f$ via \eqref{eq_spinorbit-t}, * and use it to determine the wave phase and (non-Doppler-shifted) * frequency via \eqref{eq_taylorcw-freq} * and \eqref{eq_taylorcw-phi}. The Doppler shift on the frequency is * then computed using \eqref{eq_spinorbit-upsilon} * and \eqref{eq_orbit-rdot}. We use \f$\upsilon\f$ as an intermediate in * the Doppler shift calculations, since expressing \f$\dot{R}\f$ directly in * terms of \f$E\f$ results in expression of the form \f$(e-1)/(e\cosh E-1)\f$, * which are difficult to simplify and face precision losses when * \f$E\sim0\f$ and \f$e\rightarrow1\f$. By contrast, solving for \f$\upsilon\f$ is * numerically stable provided that the system <tt>atan2()</tt> function is * well-designed. * * This routine does not account for relativistic timing variations, and * issues warnings or errors based on the criterea of * \eqref{eq_relativistic-orbit} in \ref LALGenerateEllipticSpinOrbitCW(). */ void LALGenerateHyperbolicSpinOrbitCW( LALStatus *stat, PulsarCoherentGW *output, SpinOrbitCWParamStruc *params ) { UINT4 n, i; /* number of and index over samples */ UINT4 nSpin = 0, j; /* number of and index over spindown terms */ REAL8 t, dt, tPow; /* time, interval, and t raised to a power */ REAL8 phi0, f0, twopif0; /* initial phase, frequency, and 2*pi*f0 */ REAL8 f, fPrev; /* current and previous values of frequency */ REAL4 df = 0.0; /* maximum difference between f and fPrev */ REAL8 phi; /* current value of phase */ REAL8 vDotAvg; /* nomalized orbital angular speed */ REAL8 vp; /* projected speed at periapsis */ REAL8 upsilon, argument; /* true anomaly, and argument of periapsis */ REAL8 eCosOmega; /* eccentricity * cosine of argument */ REAL8 tPeriObs; /* time of observed periapsis */ REAL8 spinOff; /* spin epoch - orbit epoch */ REAL8 x; /* observed ``mean anomaly'' */ REAL8 xPlus, xMinus; /* limits where exponentials dominate */ REAL8 dx, dxMax; /* current and target errors in x */ REAL8 a, b; /* constants in equation for x */ REAL8 ecc; /* orbital eccentricity */ REAL8 eccMinusOne, eccPlusOne; /* ecc - 1 and ecc + 1 */ REAL8 e; /* hyperbolic anomaly */ REAL8 sinhe, coshe; /* sinh of e, and cosh of e minus 1 */ REAL8 *fSpin = NULL; /* pointer to Taylor coefficients */ REAL4 *fData; /* pointer to frequency data */ REAL8 *phiData; /* pointer to phase data */ INITSTATUS(stat); ATTATCHSTATUSPTR( stat ); /* Make sure parameter and output structures exist. */ ASSERT( params, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); ASSERT( output, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); /* Make sure output fields don't exist. */ ASSERT( !( output->a ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->f ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->phi ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->shift ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); /* If Taylor coeficients are specified, make sure they exist. */ if ( params->f ) { ASSERT( params->f->data, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); nSpin = params->f->length; fSpin = params->f->data; } /* Set up some constants (to avoid repeated calculation or dereferencing), and make sure they have acceptable values. */ eccMinusOne = -params->oneMinusEcc; ecc = 1.0 + eccMinusOne; eccPlusOne = 2.0 + eccMinusOne; if ( eccMinusOne <= 0.0 ) { ABORT( stat, GENERATESPINORBITCWH_EECC, GENERATESPINORBITCWH_MSGEECC ); } vp = params->rPeriNorm*params->angularSpeed; vDotAvg = params->angularSpeed *sqrt( eccMinusOne*eccMinusOne*eccMinusOne/eccPlusOne ); n = params->length; dt = params->deltaT; f0 = fPrev = params->f0; if ( vp >= 1.0 ) { ABORT( stat, GENERATESPINORBITCWH_EFTL, GENERATESPINORBITCWH_MSGEFTL ); } if ( vp <= 0.0 || dt <= 0.0 || f0 <= 0.0 || vDotAvg <= 0.0 || n == 0 ) { ABORT( stat, GENERATESPINORBITCWH_ESGN, GENERATESPINORBITCWH_MSGESGN ); } /* Set up some other constants. */ twopif0 = f0*LAL_TWOPI; phi0 = params->phi0; argument = params->omega; a = vp*eccMinusOne*cos( argument ) + ecc; b = -vp*sqrt( eccMinusOne/eccPlusOne )*sin( argument ); eCosOmega = ecc*cos( argument ); if ( n*dt*vDotAvg > LAL_TWOPI ) dxMax = 0.01/( f0*n*dt ); else dxMax = 0.01/( f0*LAL_TWOPI/vDotAvg ); if ( dxMax < 1.0e-15 ) { dxMax = 1.0e-15; #ifndef NDEBUG LALWarning( stat, "REAL8 arithmetic may not have sufficient" " precision for this orbit" ); #endif } #ifndef NDEBUG if ( lalDebugLevel & LALWARNING ) { REAL8 tau = n*dt; if ( tau > LAL_TWOPI/vDotAvg ) tau = LAL_TWOPI/vDotAvg; if ( f0*tau*vp*vp*ecc/eccPlusOne > 0.25 ) LALWarning( stat, "Orbit may have significant relativistic" " effects that are not included" ); } #endif /* Compute offset between time series epoch and observed periapsis, and betweem true periapsis and spindown reference epoch. */ tPeriObs = (REAL8)( params->orbitEpoch.gpsSeconds - params->epoch.gpsSeconds ); tPeriObs += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->epoch.gpsNanoSeconds ); tPeriObs += params->rPeriNorm*sin( params->omega ); spinOff = (REAL8)( params->orbitEpoch.gpsSeconds - params->spinEpoch.gpsSeconds ); spinOff += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->spinEpoch.gpsNanoSeconds ); /* Determine bounds of hybrid root-finding algorithm, and initial guess for e. */ xMinus = 1.0 + a*sinh( -1.0 ) + b*cosh( -1.0 ) - b; xPlus = -1.0 + a*sinh( 1.0 ) + b*cosh( 1.0 ) - b; x = -vDotAvg*tPeriObs; if ( x < xMinus ) e = -log( -2.0*( x - xMinus )/( a - b ) - exp( 1.0 ) ); else if ( x <= 0 ) e = x/xMinus; else if ( x <= xPlus ) e = x/xPlus; else e = log( 2.0*( x - xPlus )/( a + b ) - exp( 1.0 ) ); sinhe = sinh( e ); coshe = cosh( e ) - 1.0; /* Allocate output structures. */ if ( ( output->a = (REAL4TimeVectorSeries *) LALMalloc( sizeof(REAL4TimeVectorSeries) ) ) == NULL ) { ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->a, 0, sizeof(REAL4TimeVectorSeries) ); if ( ( output->f = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->f, 0, sizeof(REAL4TimeSeries) ); if ( ( output->phi = (REAL8TimeSeries *) LALMalloc( sizeof(REAL8TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->phi, 0, sizeof(REAL8TimeSeries) ); /* Set output structure metadata fields. */ output->position = params->position; output->psi = params->psi; output->a->epoch = output->f->epoch = output->phi->epoch = params->epoch; output->a->deltaT = n*params->deltaT; output->f->deltaT = output->phi->deltaT = params->deltaT; output->a->sampleUnits = lalStrainUnit; output->f->sampleUnits = lalHertzUnit; output->phi->sampleUnits = lalDimensionlessUnit; snprintf( output->a->name, LALNameLength, "CW amplitudes" ); snprintf( output->f->name, LALNameLength, "CW frequency" ); snprintf( output->phi->name, LALNameLength, "CW phase" ); /* Allocate phase and frequency arrays. */ LALSCreateVector( stat->statusPtr, &( output->f->data ), n ); BEGINFAIL( stat ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); LALDCreateVector( stat->statusPtr, &( output->phi->data ), n ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); /* Allocate and fill amplitude array. */ { CreateVectorSequenceIn in; /* input to create output->a */ in.length = 2; in.vectorLength = 2; LALSCreateVectorSequence( stat->statusPtr, &(output->a->data), &in ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); TRY( LALDDestroyVector( stat->statusPtr, &( output->phi->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); output->a->data->data[0] = output->a->data->data[2] = params->aPlus; output->a->data->data[1] = output->a->data->data[3] = params->aCross; } /* Fill frequency and phase arrays. */ fData = output->f->data->data; phiData = output->phi->data->data; for ( i = 0; i < n; i++ ) { x = vDotAvg*( i*dt - tPeriObs ); /* Use approximate Newton-Raphson method on ln|x| if |x| > 1. */ if ( x < xMinus ) { x = log( -x ); while ( fabs( dx = log( e - a*sinhe - b*coshe ) - x ) > dxMax ) { e += dx; sinhe = sinh( e ); coshe = cosh( e ) - 1.0; } } else if ( x > xPlus ) { x = log( x ); while ( fabs( dx = log( -e + a*sinhe + b*coshe ) - x ) > dxMax ) { e -= dx; sinhe = sinh( e ); coshe = cosh( e ) - 1.0; } } /* Use ordinary Newton-Raphson method on x if |x| <= 1. */ else { while ( fabs( dx = -e + a*sinhe + b*coshe - x ) > dxMax ) { e -= dx/( -1.0 + a*coshe + a + b*sinhe ); if ( e < -1.0 ) e = -1.0; else if ( e > 1.0 ) e = 1.0; sinhe = sinh( e ); coshe = cosh( e ) - 1.0; } } /* Compute source emission time, phase, and frequency. */ phi = t = tPow = ( ecc*sinhe - e )/vDotAvg + spinOff; f = 1.0; for ( j = 0; j < nSpin; j++ ) { f += fSpin[j]*tPow; phi += fSpin[j]*( tPow*=t )/( j + 2.0 ); } /* Appy frequency Doppler shift. */ upsilon = 2.0*atan2( sqrt( eccPlusOne*coshe ), sqrt( eccMinusOne*( coshe + 2.0 ) ) ); f *= f0 / ( 1.0 + vp*( cos( argument + upsilon ) + eCosOmega ) /eccPlusOne ); phi *= twopif0; if ( fabs( f - fPrev ) > df ) df = fabs( f - fPrev ); *(fData++) = fPrev = f; *(phiData++) = phi + phi0; } /* Set output field and return. */ params->dfdt = df*dt; DETATCHSTATUSPTR( stat ); RETURN( stat ); }
/** UNDOCUMENTED */ void LALUpdateCalibration( LALStatus *status, CalibrationFunctions *output, CalibrationFunctions *input, CalibrationUpdateParams *params ) { const REAL4 tiny = 1e-6; COMPLEX8Vector *save; COMPLEX8 *R; COMPLEX8 *C; COMPLEX8 *R0; COMPLEX8 *C0; COMPLEX8 a; COMPLEX8 ab; REAL8 epoch; REAL8 first_cal; REAL8 duration; REAL4 dt; REAL4 i_r4; UINT4 n; UINT4 i; UINT4 length = 0; CHAR warnMsg[512]; INITSTATUS(status); ATTATCHSTATUSPTR( status ); /* check input */ ASSERT( input, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->sensingFunction, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->responseFunction, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->sensingFunction->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->responseFunction->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->sensingFunction->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( input->responseFunction->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); n = input->sensingFunction->data->length; ASSERT( (int)n > 0, status, CALIBRATIONH_ESIZE, CALIBRATIONH_MSGESIZE ); ASSERT( input->responseFunction->data->length == n, status, CALIBRATIONH_ESZMM, CALIBRATIONH_MSGESZMM ); /* check output */ ASSERT( output, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->sensingFunction, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->responseFunction, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->sensingFunction->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->responseFunction->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->sensingFunction->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->responseFunction->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( output->sensingFunction->data->length == n, status, CALIBRATIONH_ESZMM, CALIBRATIONH_MSGESZMM ); ASSERT( output->responseFunction->data->length == n, status, CALIBRATIONH_ESZMM, CALIBRATIONH_MSGESZMM ); /* check params */ ASSERT( params, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->sensingFactor, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->openLoopFactor, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->sensingFactor->deltaT, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->sensingFactor->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->openLoopFactor->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->sensingFactor->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->openLoopFactor->data->data, status, CALIBRATIONH_ENULL, CALIBRATIONH_MSGENULL ); ASSERT( params->openLoopFactor->data->length == params->sensingFactor->data->length, status, CALIBRATIONH_ESZMM, CALIBRATIONH_MSGESZMM ); R0 = input->responseFunction->data->data; C0 = input->sensingFunction->data->data; R = output->responseFunction->data->data; C = output->sensingFunction->data->data; save = output->responseFunction->data; output->responseFunction = input->responseFunction; output->responseFunction->data = save; output->responseFunction->epoch = params->epoch; save = output->sensingFunction->data; output->sensingFunction = input->sensingFunction; output->sensingFunction->data = save; output->sensingFunction->epoch = params->epoch; /* locate correct values of a and ab */ epoch = XLALGPSGetREAL8(&(params->epoch)); first_cal = XLALGPSGetREAL8(&(params->sensingFactor->epoch)); duration = XLALGPSGetREAL8(&(params->duration)); dt = epoch - first_cal; /* find the first point at or before the requested time */ if ( (i_r4 = floor( dt / params->sensingFactor->deltaT ) ) < 0 ) { ABORT( status, CALIBRATIONH_ETIME, CALIBRATIONH_MSGETIME ); } else { i = (UINT4) i_r4; } /* compute the sum of the calibration factors */ a = ab = 0; length = 0; do { COMPLEX8 this_a; COMPLEX8 this_ab; if ( i > params->sensingFactor->data->length - 1 ) { ABORT( status, CALIBRATIONH_ETIME, CALIBRATIONH_MSGETIME ); } this_a = params->sensingFactor->data->data[i]; this_ab = params->openLoopFactor->data->data[i]; /* JC: I CHANGED THE LOGIC HERE TO WHAT I THOUGHT IT SHOULD BE! */ if ( ( fabs( creal(this_a) ) < tiny && fabs( cimag(this_a) ) < tiny ) || ( fabs( creal(this_ab) ) < tiny && fabs( cimag(this_ab) ) < tiny ) ) { /* this is a hack for the broken S2 calibration frame data */ if ( (params->epoch.gpsSeconds >= CAL_S2START) && (params->epoch.gpsSeconds < CAL_S2END ) ) { /* if the zero is during S2 print a warning... */ snprintf( warnMsg, XLAL_NUM_ELEM(warnMsg), "Zero calibration factors found during S2 at GPS %10.9f", first_cal + (REAL8) i * params->sensingFactor->deltaT ); LALWarning( status, warnMsg ); } else { /* ...or abort if we are outside S2 */ snprintf( warnMsg, XLAL_NUM_ELEM(warnMsg), "Zero calibration factor found at GPS %10.9f", first_cal + (REAL8) i * params->sensingFactor->deltaT ); LALWarning( status, warnMsg ); ABORT( status, CALIBRATIONH_EZERO, CALIBRATIONH_MSGEZERO ); } } else { /* increment the count of factors if we are adding a non-zero value */ ++length; } /* add this value to the sum */ a += this_a; ab += this_ab; /* increment the calibration factor index */ ++i; } while ( (first_cal + (REAL8) i * params->sensingFactor->deltaT) < (epoch + duration) ); /* if all the calibration factors are zero the abort */ if ( ! length || (fabs( creal(a) ) < tiny && fabs( cimag(a) ) < tiny) || (fabs( creal(ab) ) < tiny && fabs( cimag(ab) ) < tiny) ) { snprintf( warnMsg, XLAL_NUM_ELEM(warnMsg), "Got %d calibration samples\nalpha and/or beta are zero:\n" "Re a = %e\tIm a = %e\nRe ab = %e\tIm ab = %e", length, creal(a), cimag(a), creal(ab), cimag(ab) ); LALWarning( status, warnMsg ); ABORT( status, CALIBRATIONH_EZERO, CALIBRATIONH_MSGEZERO ); } /* compute the mean of the calibration factors from the sum */ a /= length; ab /= length; /* return the used values of alpha and alphabeta */ params->alpha = a; params->alphabeta = ab; snprintf( warnMsg, XLAL_NUM_ELEM(warnMsg), "Got %d calibration samples\n" "Re a = %e\tIm a = %e\nRe ab = %e\tIm ab = %e", length, creal(a), cimag(a), creal(ab), cimag(ab) ); LALInfo( status, warnMsg ); for ( i = 0; i < n; ++i ) { C[i] = a * C0[i]; R[i] = (ab * (C0[i] * R0[i] - 1.0) + 1.0) / C[i]; } DETATCHSTATUSPTR( status ); RETURN( status ); }
/** * \author Creighton, T. D. * * \brief Computes a continuous waveform with frequency drift and Doppler * modulation from an elliptical orbital trajectory. * * This function computes a quaiperiodic waveform using the spindown and * orbital parameters in <tt>*params</tt>, storing the result in * <tt>*output</tt>. * * In the <tt>*params</tt> structure, the routine uses all the "input" * fields specified in \ref GenerateSpinOrbitCW_h, and sets all of the * "output" fields. If <tt>params-\>f</tt>=\c NULL, no spindown * modulation is performed. If <tt>params-\>oneMinusEcc</tt>\f$\notin(0,1]\f$ * (an open orbit), or if * <tt>params-\>rPeriNorm</tt>\f$\times\f$<tt>params-\>angularSpeed</tt>\f$\geq1\f$ * (faster-than-light speed at periapsis), an error is returned. * * In the <tt>*output</tt> structure, the field <tt>output-\>h</tt> is * ignored, but all other pointer fields must be set to \c NULL. The * function will create and allocate space for <tt>output-\>a</tt>, * <tt>output-\>f</tt>, and <tt>output-\>phi</tt> as necessary. The * <tt>output-\>shift</tt> field will remain set to \c NULL. * * ### Algorithm ### * * For elliptical orbits, we combine \eqref{eq_spinorbit-tr}, * \eqref{eq_spinorbit-t}, and \eqref{eq_spinorbit-upsilon} to get \f$t_r\f$ * directly as a function of the eccentric anomaly \f$E\f$: * \f{eqnarray}{ * \label{eq_tr-e1} * t_r = t_p & + & \left(\frac{r_p \sin i}{c}\right)\sin\omega\\ * & + & \left(\frac{P}{2\pi}\right) \left( E + * \left[v_p(1-e)\cos\omega - e\right]\sin E * + \left[v_p\sqrt{\frac{1-e}{1+e}}\sin\omega\right] * [\cos E - 1]\right) \;, * \f} * where \f$v_p=r_p\dot{\upsilon}_p\sin i/c\f$ is a normalized velocity at * periapsis and \f$P=2\pi\sqrt{(1+e)/(1-e)^3}/\dot{\upsilon}_p\f$ is the * period of the orbit. For simplicity we write this as: * \f{equation}{ * \label{eq_tr-e2} * t_r = T_p + \frac{1}{n}\left( E + A\sin E + B[\cos E - 1] \right) \;, * \f} * * \figure{inject_eanomaly,eps,0.23,Function to be inverted to find eccentric anomaly} * * where \f$T_p\f$ is the \e observed time of periapsis passage and * \f$n=2\pi/P\f$ is the mean angular speed around the orbit. Thus the key * numerical procedure in this routine is to invert the expression * \f$x=E+A\sin E+B(\cos E - 1)\f$ to get \f$E(x)\f$. We note that * \f$E(x+2n\pi)=E(x)+2n\pi\f$, so we only need to solve this expression in * the interval \f$[0,2\pi)\f$, sketched to the right. * * We further note that \f$A^2+B^2<1\f$, although it approaches 1 when * \f$e\rightarrow1\f$, or when \f$v_p\rightarrow1\f$ and either \f$e=0\f$ or * \f$\omega=\pi\f$. Except in this limit, Newton-Raphson methods will * converge rapidly for any initial guess. In this limit, though, the * slope \f$dx/dE\f$ approaches zero at the point of inflection, and an * initial guess or iteration landing near this point will send the next * iteration off to unacceptably large or small values. However, by * restricting all initial guesses and iterations to the domain * \f$E\in[0,2\pi)\f$, one will always end up on a trajectory branch that * will converge uniformly. This should converge faster than the more * generically robust technique of bisection. (Note: the danger with Newton's method * has been found to be unstable for certain binary orbital parameters. So if * Newton's method fails to converge, a bisection algorithm is employed.) * * In this algorithm, we start the computation with an arbitrary initial * guess of \f$E=0\f$, and refine it until the we get agreement to within * 0.01 parts in part in \f$N_\mathrm{cyc}\f$ (where \f$N_\mathrm{cyc}\f$ is the * larger of the number of wave cycles in an orbital period, or the * number of wave cycles in the entire waveform being generated), or one * part in \f$10^{15}\f$ (an order of magnitude off the best precision * possible with \c REAL8 numbers). The latter case indicates that * \c REAL8 precision may fail to give accurate phasing, and one * should consider modeling the orbit as a set of Taylor frequency * coefficients \'{a} la <tt>LALGenerateTaylorCW()</tt>. On subsequent * timesteps, we use the previous timestep as an initial guess, which is * good so long as the timesteps are much smaller than an orbital period. * This sequence of guesses will have to readjust itself once every orbit * (as \f$E\f$ jumps from \f$2\pi\f$ down to 0), but this is relatively * infrequent; we don't bother trying to smooth this out because the * additional tests would probably slow down the algorithm overall. * * Once a value of \f$E\f$ is found for a given timestep in the output * series, we compute the system time \f$t\f$ via \eqref{eq_spinorbit-t}, * and use it to determine the wave phase and (non-Doppler-shifted) * frequency via \eqref{eq_taylorcw-freq} * and \eqref{eq_taylorcw-phi}. The Doppler shift on the frequency is * then computed using \eqref{eq_spinorbit-upsilon} * and \eqref{eq_orbit-rdot}. We use \f$\upsilon\f$ as an intermediate in * the Doppler shift calculations, since expressing \f$\dot{R}\f$ directly in * terms of \f$E\f$ results in expression of the form \f$(1-e)/(1-e\cos E)\f$, * which are difficult to simplify and face precision losses when * \f$E\sim0\f$ and \f$e\rightarrow1\f$. By contrast, solving for \f$\upsilon\f$ is * numerically stable provided that the system <tt>atan2()</tt> function is * well-designed. * * The routine does not account for variations in special relativistic or * gravitational time dilation due to the elliptical orbit, nor does it * deal with other gravitational effects such as Shapiro delay. To a * very rough approximation, the amount of phase error induced by * gravitational redshift goes something like \f$\Delta\phi\sim * fT(v/c)^2\Delta(r_p/r)\f$, where \f$f\f$ is the typical wave frequency, \f$T\f$ * is either the length of data or the orbital period (whichever is * \e smaller), \f$v\f$ is the \e true (unprojected) speed at * periapsis, and \f$\Delta(r_p/r)\f$ is the total range swept out by the * quantity \f$r_p/r\f$ over the course of the observation. Other * relativistic effects such as special relativistic time dilation are * comparable in magnitude. We make a crude estimate of when this is * significant by noting that \f$v/c\gtrsim v_p\f$ but * \f$\Delta(r_p/r)\lesssim 2e/(1+e)\f$; we take these approximations as * equalities and require that \f$\Delta\phi\lesssim\pi\f$, giving: * \f{equation}{ * \label{eq_relativistic-orbit} * f_0Tv_p^2\frac{4e}{1+e}\lesssim1 \;. * \f} * When this critereon is violated, a warning is generated. Furthermore, * as noted earlier, when \f$v_p\geq1\f$ the routine will return an error, as * faster-than-light speeds can cause the emission and reception times to * be non-monotonic functions of one another. */ void LALGenerateEllipticSpinOrbitCW( LALStatus *stat, PulsarCoherentGW *output, SpinOrbitCWParamStruc *params ) { UINT4 n, i; /* number of and index over samples */ UINT4 nSpin = 0, j; /* number of and index over spindown terms */ REAL8 t, dt, tPow; /* time, interval, and t raised to a power */ REAL8 phi0, f0, twopif0; /* initial phase, frequency, and 2*pi*f0 */ REAL8 f, fPrev; /* current and previous values of frequency */ REAL4 df = 0.0; /* maximum difference between f and fPrev */ REAL8 phi; /* current value of phase */ REAL8 p, vDotAvg; /* orbital period, and 2*pi/(period) */ REAL8 vp; /* projected speed at periapsis */ REAL8 upsilon, argument; /* true anomaly, and argument of periapsis */ REAL8 eCosOmega; /* eccentricity * cosine of argument */ REAL8 tPeriObs; /* time of observed periapsis */ REAL8 spinOff; /* spin epoch - orbit epoch */ REAL8 x; /* observed mean anomaly */ REAL8 dx, dxMax; /* current and target errors in x */ REAL8 a, b; /* constants in equation for x */ REAL8 ecc; /* orbital eccentricity */ REAL8 oneMinusEcc, onePlusEcc; /* 1 - ecc and 1 + ecc */ REAL8 e = 0.0; /* eccentric anomaly */ REAL8 de = 0.0; /* eccentric anomaly step */ REAL8 sine = 0.0, cose = 0.0; /* sine of e, and cosine of e minus 1 */ REAL8 *fSpin = NULL; /* pointer to Taylor coefficients */ REAL4 *fData; /* pointer to frequency data */ REAL8 *phiData; /* pointer to phase data */ INITSTATUS(stat); ATTATCHSTATUSPTR( stat ); /* Make sure parameter and output structures exist. */ ASSERT( params, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); ASSERT( output, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); /* Make sure output fields don't exist. */ ASSERT( !( output->a ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->f ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->phi ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); ASSERT( !( output->shift ), stat, GENERATESPINORBITCWH_EOUT, GENERATESPINORBITCWH_MSGEOUT ); /* If Taylor coeficients are specified, make sure they exist. */ if ( params->f ) { ASSERT( params->f->data, stat, GENERATESPINORBITCWH_ENUL, GENERATESPINORBITCWH_MSGENUL ); nSpin = params->f->length; fSpin = params->f->data; } /* Set up some constants (to avoid repeated calculation or dereferencing), and make sure they have acceptable values. */ oneMinusEcc = params->oneMinusEcc; ecc = 1.0 - oneMinusEcc; onePlusEcc = 1.0 + ecc; if ( ecc < 0.0 || oneMinusEcc <= 0.0 ) { ABORT( stat, GENERATESPINORBITCWH_EECC, GENERATESPINORBITCWH_MSGEECC ); } vp = params->rPeriNorm*params->angularSpeed; n = params->length; dt = params->deltaT; f0 = fPrev = params->f0; vDotAvg = params->angularSpeed *sqrt( oneMinusEcc*oneMinusEcc*oneMinusEcc/onePlusEcc ); if ( vp >= 1.0 ) { ABORT( stat, GENERATESPINORBITCWH_EFTL, GENERATESPINORBITCWH_MSGEFTL ); } if ( vp <= 0.0 || dt <= 0.0 || f0 <= 0.0 || vDotAvg <= 0.0 || n == 0 ) { ABORT( stat, GENERATESPINORBITCWH_ESGN, GENERATESPINORBITCWH_MSGESGN ); } /* Set up some other constants. */ twopif0 = f0*LAL_TWOPI; phi0 = params->phi0; argument = params->omega; p = LAL_TWOPI/vDotAvg; a = vp*oneMinusEcc*cos( argument ) + oneMinusEcc - 1.0; b = vp*sqrt( oneMinusEcc/( onePlusEcc ) )*sin( argument ); eCosOmega = ecc*cos( argument ); if ( n*dt > p ) dxMax = 0.01/( f0*n*dt ); else dxMax = 0.01/( f0*p ); if ( dxMax < 1.0e-15 ) { dxMax = 1.0e-15; #ifndef NDEBUG LALWarning( stat, "REAL8 arithmetic may not have sufficient" " precision for this orbit" ); #endif } #ifndef NDEBUG if ( lalDebugLevel & LALWARNING ) { REAL8 tau = n*dt; if ( tau > p ) tau = p; if ( f0*tau*vp*vp*ecc/onePlusEcc > 0.25 ) LALWarning( stat, "Orbit may have significant relativistic" " effects that are not included" ); } #endif /* Compute offset between time series epoch and observed periapsis, and betweem true periapsis and spindown reference epoch. */ tPeriObs = (REAL8)( params->orbitEpoch.gpsSeconds - params->epoch.gpsSeconds ); tPeriObs += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->epoch.gpsNanoSeconds ); tPeriObs += params->rPeriNorm*sin( params->omega ); spinOff = (REAL8)( params->orbitEpoch.gpsSeconds - params->spinEpoch.gpsSeconds ); spinOff += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - params->spinEpoch.gpsNanoSeconds ); /* Allocate output structures. */ if ( ( output->a = (REAL4TimeVectorSeries *) LALMalloc( sizeof(REAL4TimeVectorSeries) ) ) == NULL ) { ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->a, 0, sizeof(REAL4TimeVectorSeries) ); if ( ( output->f = (REAL4TimeSeries *) LALMalloc( sizeof(REAL4TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->f, 0, sizeof(REAL4TimeSeries) ); if ( ( output->phi = (REAL8TimeSeries *) LALMalloc( sizeof(REAL8TimeSeries) ) ) == NULL ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; ABORT( stat, GENERATESPINORBITCWH_EMEM, GENERATESPINORBITCWH_MSGEMEM ); } memset( output->phi, 0, sizeof(REAL8TimeSeries) ); /* Set output structure metadata fields. */ output->position = params->position; output->psi = params->psi; output->a->epoch = output->f->epoch = output->phi->epoch = params->epoch; output->a->deltaT = n*params->deltaT; output->f->deltaT = output->phi->deltaT = params->deltaT; output->a->sampleUnits = lalStrainUnit; output->f->sampleUnits = lalHertzUnit; output->phi->sampleUnits = lalDimensionlessUnit; snprintf( output->a->name, LALNameLength, "CW amplitudes" ); snprintf( output->f->name, LALNameLength, "CW frequency" ); snprintf( output->phi->name, LALNameLength, "CW phase" ); /* Allocate phase and frequency arrays. */ LALSCreateVector( stat->statusPtr, &( output->f->data ), n ); BEGINFAIL( stat ) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); LALDCreateVector( stat->statusPtr, &( output->phi->data ), n ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); /* Allocate and fill amplitude array. */ { CreateVectorSequenceIn in; /* input to create output->a */ in.length = 2; in.vectorLength = 2; LALSCreateVectorSequence( stat->statusPtr, &(output->a->data), &in ); BEGINFAIL( stat ) { TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), stat ); TRY( LALDDestroyVector( stat->statusPtr, &( output->phi->data ) ), stat ); LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; } ENDFAIL( stat ); output->a->data->data[0] = output->a->data->data[2] = params->aPlus; output->a->data->data[1] = output->a->data->data[3] = params->aCross; } /* Fill frequency and phase arrays. */ fData = output->f->data->data; phiData = output->phi->data->data; for ( i = 0; i < n; i++ ) { INT4 nOrb; /* number of orbits since the specified orbit epoch */ /* First, find x in the range [0,2*pi]. */ x = vDotAvg*( i*dt - tPeriObs ); nOrb = (INT4)( x/LAL_TWOPI ); if ( x < 0.0 ) nOrb -= 1; x -= LAL_TWOPI*nOrb; /* Newton-Raphson iteration to find E(x). Maximum of 100 iterations. */ INT4 maxiter = 100, iter = 0; while ( iter<maxiter && fabs( dx = e + a*sine + b*cose - x ) > dxMax ) { iter++; //Make a check on the step-size so we don't step too far de = dx/( 1.0 + a*cose + a - b*sine ); if ( de > LAL_PI ) de = LAL_PI; else if ( de < -LAL_PI ) de = -LAL_PI; e -= de; if ( e < 0.0 ) e = 0.0; else if ( e > LAL_TWOPI ) e = LAL_TWOPI; sine = sin( e ); cose = cos( e ) - 1.0; } /* Bisection algorithm from GSL if Newton's method (above) fails to converge. */ if (iter==maxiter && fabs( dx = e + a*sine + b*cose - x ) > dxMax ) { //Initialize solver const gsl_root_fsolver_type *T = gsl_root_fsolver_bisection; gsl_root_fsolver *s = gsl_root_fsolver_alloc(T); REAL8 e_lo = 0.0, e_hi = LAL_TWOPI; gsl_function F; struct E_solver_params pars = {a, b, x}; F.function = &gsl_E_solver; F.params = &pars; if (gsl_root_fsolver_set(s, &F, e_lo, e_hi) != 0) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; ABORT( stat, -1, "GSL failed to set initial points" ); } INT4 keepgoing = 1; INT4 success = 0; INT4 root_status = keepgoing; e = 0.0; iter = 0; while (root_status==keepgoing && iter<maxiter) { iter++; root_status = gsl_root_fsolver_iterate(s); if (root_status!=keepgoing && root_status!=success) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; ABORT( stat, -1, "gsl_root_fsolver_iterate() failed" ); } e = gsl_root_fsolver_root(s); sine = sin(e); cose = cos(e) - 1.0; if (fabs( dx = e + a*sine + b*cose - x ) > dxMax) root_status = keepgoing; else root_status = success; } if (root_status!=success) { LALFree( output->a ); output->a = NULL; LALFree( output->f ); output->f = NULL; LALFree( output->phi ); output->phi = NULL; gsl_root_fsolver_free(s); ABORT( stat, -1, "Could not converge using bisection algorithm" ); } gsl_root_fsolver_free(s); } /* Compute source emission time, phase, and frequency. */ phi = t = tPow = ( e + LAL_TWOPI*nOrb - ecc*sine )/vDotAvg + spinOff; f = 1.0; for ( j = 0; j < nSpin; j++ ) { f += fSpin[j]*tPow; phi += fSpin[j]*( tPow*=t )/( j + 2.0 ); } /* Appy frequency Doppler shift. */ upsilon = 2.0 * atan2 ( sqrt(onePlusEcc/oneMinusEcc) * sin(0.5*e), cos(0.5*e) ); f *= f0 / ( 1.0 + vp*( cos( argument + upsilon ) + eCosOmega ) /onePlusEcc ); phi *= twopif0; if ( (i > 0) && (fabs( f - fPrev ) > df) ) df = fabs( f - fPrev ); *(fData++) = fPrev = f; *(phiData++) = phi + phi0; } /* for i < n */ /* Set output field and return. */ params->dfdt = df*dt; DETATCHSTATUSPTR( stat ); RETURN( stat ); }