00001 /* 00002 * Copyright (C) 2007 Reinhard Prix, Teviet Creighton 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with with program; see the file COPYING. If not, write to the 00016 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 00017 * MA 02111-1307 USA 00018 */ 00019 00020 /****************** <lalVerbatim file="GenerateEllipticSpinOrbitCWCV"> 00021 Author: Creighton, T. D. 00022 $Id: GenerateEllipticSpinOrbitCW.c,v 1.6 2007/06/08 14:41:47 bema Exp $ 00023 **************************************************** </lalVerbatim> */ 00024 00025 /********************************************************** <lalLaTeX> 00026 00027 \providecommand{\lessim}{\stackrel{<}{\scriptstyle\sim}} 00028 \providecommand{\greatersim}{\stackrel{>}{\scriptstyle\sim}} 00029 00030 \subsection{Module \texttt{GenerateEllipticSpinOrbitCW.c}} 00031 \label{ss:GenerateEllipticSpinOrbitCW.c} 00032 00033 Computes a continuous waveform with frequency drift and Doppler 00034 modulation from an elliptical orbital trajectory. 00035 00036 \subsubsection*{Prototypes} 00037 \vspace{0.1in} 00038 \input{GenerateEllipticSpinOrbitCWCP} 00039 \idx{LALGenerateEllipticSpinOrbitCW()} 00040 00041 \subsubsection*{Description} 00042 00043 This function computes a quaiperiodic waveform using the spindown and 00044 orbital parameters in \verb@*params@, storing the result in 00045 \verb@*output@. 00046 00047 In the \verb@*params@ structure, the routine uses all the ``input'' 00048 fields specified in \verb@GenerateSpinOrbitCW.h@, and sets all of the 00049 ``output'' fields. If \verb@params->f@=\verb@NULL@, no spindown 00050 modulation is performed. If \verb@params->oneMinusEcc@$\notin(0,1]$ 00051 (an open orbit), or if 00052 \verb@params->rPeriNorm@$\times$\verb@params->angularSpeed@$\geq1$ 00053 (faster-than-light speed at periapsis), an error is returned. 00054 00055 In the \verb@*output@ structure, the field \verb@output->h@ is 00056 ignored, but all other pointer fields must be set to \verb@NULL@. The 00057 function will create and allocate space for \verb@output->a@, 00058 \verb@output->f@, and \verb@output->phi@ as necessary. The 00059 \verb@output->shift@ field will remain set to \verb@NULL@. 00060 00061 \subsubsection*{Algorithm} 00062 00063 For elliptical orbits, we combine Eqs.~(\ref{eq:spinorbit-tr}), 00064 (\ref{eq:spinorbit-t}), and~(\ref{eq:spinorbit-upsilon}) to get $t_r$ 00065 directly as a function of the eccentric anomaly $E$: 00066 \begin{eqnarray} 00067 t_r = t_p & + & \left(\frac{r_p \sin i}{c}\right)\sin\omega \nonumber\\ 00068 \label{eq:tr-e1} 00069 & + & \left(\frac{P}{2\pi}\right) \left( E + 00070 \left[v_p(1-e)\cos\omega - e\right]\sin E 00071 + \left[v_p\sqrt{\frac{1-e}{1+e}}\sin\omega\right] 00072 [\cos E - 1]\right) \;, 00073 \end{eqnarray} 00074 where $v_p=r_p\dot{\upsilon}_p\sin i/c$ is a normalized velocity at 00075 periapsis and $P=2\pi\sqrt{(1+e)/(1-e)^3}/\dot{\upsilon}_p$ is the 00076 period of the orbit. For simplicity we write this as: 00077 \begin{equation} 00078 \label{eq:tr-e2} 00079 t_r = T_p + \frac{1}{n}\left( E + A\sin E + B[\cos E - 1] \right) \;, 00080 \end{equation} 00081 \begin{wrapfigure}{r}{0.28\textwidth} 00082 \vspace{-4ex} 00083 \begin{center} 00084 \resizebox{0.23\textwidth}{!}{\includegraphics{inject_eanomaly}} \\ 00085 %\parbox{0.23\textwidth}{\caption{\label{fig:binary-orbit} Function to 00086 %be inverted to find eccentric anomaly.}} 00087 \end{center} 00088 \vspace{-4ex} 00089 \end{wrapfigure} 00090 where $T_p$ is the \emph{observed} time of periapsis passage and 00091 $n=2\pi/P$ is the mean angular speed around the orbit. Thus the key 00092 numerical procedure in this routine is to invert the expression 00093 $x=E+A\sin E+B(\cos E - 1)$ to get $E(x)$. We note that 00094 $E(x+2n\pi)=E(x)+2n\pi$, so we only need to solve this expression in 00095 the interval $[0,2\pi)$, sketched to the right. 00096 00097 We further note that $A^2+B^2<1$, although it approaches 1 when 00098 $e\rightarrow1$, or when $v_p\rightarrow1$ and either $e=0$ or 00099 $\omega=\pi$. Except in this limit, Newton-Raphson methods will 00100 converge rapidly for any initial guess. In this limit, though, the 00101 slope $dx/dE$ approaches zero at the point of inflection, and an 00102 initial guess or iteration landing near this point will send the next 00103 iteration off to unacceptably large or small values. However, by 00104 restricting all initial guesses and iterations to the domain 00105 $E\in[0,2\pi)$, one will always end up on a trajectory branch that 00106 will converge uniformly. This should converge faster than the more 00107 generically robust technique of bisection. 00108 00109 In this algorithm, we start the computation with an arbitrary initial 00110 guess of $E=0$, and refine it until the we get agreement to within 00111 0.01 parts in part in $N_\mathrm{cyc}$ (where $N_\mathrm{cyc}$ is the 00112 larger of the number of wave cycles in an orbital period, or the 00113 number of wave cycles in the entire waveform being generated), or one 00114 part in $10^{15}$ (an order of magnitude off the best precision 00115 possible with \verb@REAL8@ numbers). The latter case indicates that 00116 \verb@REAL8@ precision may fail to give accurate phasing, and one 00117 should consider modeling the orbit as a set of Taylor frequency 00118 coefficients \'{a} la \verb@LALGenerateTaylorCW()@. On subsequent 00119 timesteps, we use the previous timestep as an initial guess, which is 00120 good so long as the timesteps are much smaller than an orbital period. 00121 This sequence of guesses will have to readjust itself once every orbit 00122 (as $E$ jumps from $2\pi$ down to 0), but this is relatively 00123 infrequent; we don't bother trying to smooth this out because the 00124 additional tests would probably slow down the algorithm overall. 00125 00126 Once a value of $E$ is found for a given timestep in the output 00127 series, we compute the system time $t$ via Eq.~(\ref{eq:spinorbit-t}), 00128 and use it to determine the wave phase and (non-Doppler-shifted) 00129 frequency via Eqs.~(\ref{eq:taylorcw-freq}) 00130 and~(\ref{eq:taylorcw-phi}). The Doppler shift on the frequency is 00131 then computed using Eqs.~(\ref{eq:spinorbit-upsilon}) 00132 and~(\ref{eq:orbit-rdot}). We use $\upsilon$ as an intermediate in 00133 the Doppler shift calculations, since expressing $\dot{R}$ directly in 00134 terms of $E$ results in expression of the form $(1-e)/(1-e\cos E)$, 00135 which are difficult to simplify and face precision losses when 00136 $E\sim0$ and $e\rightarrow1$. By contrast, solving for $\upsilon$ is 00137 numerically stable provided that the system \verb@atan2()@ function is 00138 well-designed. 00139 00140 The routine does not account for variations in special relativistic or 00141 gravitational time dilation due to the elliptical orbit, nor does it 00142 deal with other gravitational effects such as Shapiro delay. To a 00143 very rough approximation, the amount of phase error induced by 00144 gravitational redshift goes something like $\Delta\phi\sim 00145 fT(v/c)^2\Delta(r_p/r)$, where $f$ is the typical wave frequency, $T$ 00146 is either the length of data or the orbital period (whichever is 00147 \emph{smaller}), $v$ is the \emph{true} (unprojected) speed at 00148 periapsis, and $\Delta(r_p/r)$ is the total range swept out by the 00149 quantity $r_p/r$ over the course of the observation. Other 00150 relativistic effects such as special relativistic time dilation are 00151 comparable in magnitude. We make a crude estimate of when this is 00152 significant by noting that $v/c\greatersim v_p$ but 00153 $\Delta(r_p/r)\lessim 2e/(1+e)$; we take these approximations as 00154 equalities and require that $\Delta\phi\lessim\pi$, giving: 00155 \begin{equation} 00156 \label{eq:relativistic-orbit} 00157 f_0Tv_p^2\frac{4e}{1+e}\lessim1 \;. 00158 \end{equation} 00159 When this critereon is violated, a warning is generated. Furthermore, 00160 as noted earlier, when $v_p\geq1$ the routine will return an error, as 00161 faster-than-light speeds can cause the emission and reception times to 00162 be non-monotonic functions of one another. 00163 00164 \subsubsection*{Uses} 00165 \begin{verbatim} 00166 LALMalloc() LALFree() 00167 LALSCreateVectorSequence() LALSDestroyVectorSequence() 00168 LALSCreateVector() LALSDestroyVector() 00169 LALDCreateVector() LALDDestroyVector() 00170 LALSnprintf() LALWarning() 00171 \end{verbatim} 00172 00173 \subsubsection*{Notes} 00174 00175 \vfill{\footnotesize\input{GenerateEllipticSpinOrbitCWCV}} 00176 00177 ******************************************************* </lalLaTeX> */ 00178 00179 #include <lal/LALStdio.h> 00180 #include <lal/LALStdlib.h> 00181 #include <lal/LALConstants.h> 00182 #include <lal/Units.h> 00183 #include <lal/AVFactories.h> 00184 #include <lal/SeqFactories.h> 00185 #include <lal/SimulateCoherentGW.h> 00186 #include <lal/GenerateSpinOrbitCW.h> 00187 00188 NRCSID( GENERATEELLIPTICSPINORBITCWC, "$Id: GenerateEllipticSpinOrbitCW.c,v 1.6 2007/06/08 14:41:47 bema Exp $" ); 00189 00190 /* <lalVerbatim file="GenerateEllipticSpinOrbitCWCP"> */ 00191 void 00192 LALGenerateEllipticSpinOrbitCW( LALStatus *stat, 00193 CoherentGW *output, 00194 SpinOrbitCWParamStruc *params ) 00195 { /* </lalVerbatim> */ 00196 UINT4 n, i; /* number of and index over samples */ 00197 UINT4 nSpin = 0, j; /* number of and index over spindown terms */ 00198 REAL8 t, dt, tPow; /* time, interval, and t raised to a power */ 00199 REAL8 phi0, f0, twopif0; /* initial phase, frequency, and 2*pi*f0 */ 00200 REAL8 f, fPrev; /* current and previous values of frequency */ 00201 REAL4 df = 0.0; /* maximum difference between f and fPrev */ 00202 REAL8 phi; /* current value of phase */ 00203 REAL8 p, vDotAvg; /* orbital period, and 2*pi/(period) */ 00204 REAL8 vp; /* projected speed at periapsis */ 00205 REAL8 upsilon, argument; /* true anomaly, and argument of periapsis */ 00206 REAL8 eCosOmega; /* eccentricity * cosine of argument */ 00207 REAL8 tPeriObs; /* time of observed periapsis */ 00208 REAL8 spinOff; /* spin epoch - orbit epoch */ 00209 REAL8 x; /* observed mean anomaly */ 00210 REAL8 dx, dxMax; /* current and target errors in x */ 00211 REAL8 a, b; /* constants in equation for x */ 00212 REAL8 ecc; /* orbital eccentricity */ 00213 REAL8 oneMinusEcc, onePlusEcc; /* 1 - ecc and 1 + ecc */ 00214 REAL8 e = 0.0; /* eccentric anomaly */ 00215 REAL8 sine = 0.0, cose = 0.0; /* sine of e, and cosine of e minus 1 */ 00216 REAL8 *fSpin = NULL; /* pointer to Taylor coefficients */ 00217 REAL4 *fData; /* pointer to frequency data */ 00218 REAL8 *phiData; /* pointer to phase data */ 00219 00220 INITSTATUS( stat, "LALGenerateEllipticSpinOrbitCW", 00221 GENERATEELLIPTICSPINORBITCWC ); 00222 ATTATCHSTATUSPTR( stat ); 00223 00224 /* Make sure parameter and output structures exist. */ 00225 ASSERT( params, stat, GENERATESPINORBITCWH_ENUL, 00226 GENERATESPINORBITCWH_MSGENUL ); 00227 ASSERT( output, stat, GENERATESPINORBITCWH_ENUL, 00228 GENERATESPINORBITCWH_MSGENUL ); 00229 00230 /* Make sure output fields don't exist. */ 00231 ASSERT( !( output->a ), stat, GENERATESPINORBITCWH_EOUT, 00232 GENERATESPINORBITCWH_MSGEOUT ); 00233 ASSERT( !( output->f ), stat, GENERATESPINORBITCWH_EOUT, 00234 GENERATESPINORBITCWH_MSGEOUT ); 00235 ASSERT( !( output->phi ), stat, GENERATESPINORBITCWH_EOUT, 00236 GENERATESPINORBITCWH_MSGEOUT ); 00237 ASSERT( !( output->shift ), stat, GENERATESPINORBITCWH_EOUT, 00238 GENERATESPINORBITCWH_MSGEOUT ); 00239 00240 /* If Taylor coeficients are specified, make sure they exist. */ 00241 if ( params->f ) { 00242 ASSERT( params->f->data, stat, GENERATESPINORBITCWH_ENUL, 00243 GENERATESPINORBITCWH_MSGENUL ); 00244 nSpin = params->f->length; 00245 fSpin = params->f->data; 00246 } 00247 00248 /* Set up some constants (to avoid repeated calculation or 00249 dereferencing), and make sure they have acceptable values. */ 00250 oneMinusEcc = params->oneMinusEcc; 00251 ecc = 1.0 - oneMinusEcc; 00252 onePlusEcc = 1.0 + ecc; 00253 if ( ecc < 0.0 || oneMinusEcc <= 0.0 ) { 00254 ABORT( stat, GENERATESPINORBITCWH_EECC, 00255 GENERATESPINORBITCWH_MSGEECC ); 00256 } 00257 vp = params->rPeriNorm*params->angularSpeed; 00258 n = params->length; 00259 dt = params->deltaT; 00260 f0 = fPrev = params->f0; 00261 vDotAvg = params->angularSpeed 00262 *sqrt( oneMinusEcc*oneMinusEcc*oneMinusEcc/onePlusEcc ); 00263 if ( vp >= 1.0 ) { 00264 ABORT( stat, GENERATESPINORBITCWH_EFTL, 00265 GENERATESPINORBITCWH_MSGEFTL ); 00266 } 00267 if ( vp <= 0.0 || dt <= 0.0 || f0 <= 0.0 || vDotAvg <= 0.0 || 00268 n == 0 ) { 00269 ABORT( stat, GENERATESPINORBITCWH_ESGN, 00270 GENERATESPINORBITCWH_MSGESGN ); 00271 } 00272 00273 /* Set up some other constants. */ 00274 twopif0 = f0*LAL_TWOPI; 00275 phi0 = params->phi0; 00276 argument = params->omega; 00277 p = LAL_TWOPI/vDotAvg; 00278 a = vp*oneMinusEcc*cos( argument ) + oneMinusEcc - 1.0; 00279 b = vp*sqrt( oneMinusEcc/( onePlusEcc ) )*sin( argument ); 00280 eCosOmega = ecc*cos( argument ); 00281 if ( n*dt > p ) 00282 dxMax = 0.01/( f0*n*dt ); 00283 else 00284 dxMax = 0.01/( f0*p ); 00285 if ( dxMax < 1.0e-15 ) { 00286 dxMax = 1.0e-15; 00287 #ifndef NDEBUG 00288 LALWarning( stat, "REAL8 arithmetic may not have sufficient" 00289 " precision for this orbit" ); 00290 #endif 00291 } 00292 #ifndef NDEBUG 00293 if ( lalDebugLevel & LALWARNING ) { 00294 REAL8 tau = n*dt; 00295 if ( tau > p ) 00296 tau = p; 00297 if ( f0*tau*vp*vp*ecc/onePlusEcc > 0.25 ) 00298 LALWarning( stat, "Orbit may have significant relativistic" 00299 " effects that are not included" ); 00300 } 00301 #endif 00302 00303 /* Compute offset between time series epoch and observed periapsis, 00304 and betweem true periapsis and spindown reference epoch. */ 00305 tPeriObs = (REAL8)( params->orbitEpoch.gpsSeconds - 00306 params->epoch.gpsSeconds ); 00307 tPeriObs += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - 00308 params->epoch.gpsNanoSeconds ); 00309 tPeriObs += params->rPeriNorm*sin( params->omega ); 00310 spinOff = (REAL8)( params->orbitEpoch.gpsSeconds - 00311 params->spinEpoch.gpsSeconds ); 00312 spinOff += 1.0e-9 * (REAL8)( params->orbitEpoch.gpsNanoSeconds - 00313 params->spinEpoch.gpsNanoSeconds ); 00314 00315 /* Allocate output structures. */ 00316 if ( ( output->a = (REAL4TimeVectorSeries *) 00317 LALMalloc( sizeof(REAL4TimeVectorSeries) ) ) == NULL ) { 00318 ABORT( stat, GENERATESPINORBITCWH_EMEM, 00319 GENERATESPINORBITCWH_MSGEMEM ); 00320 } 00321 memset( output->a, 0, sizeof(REAL4TimeVectorSeries) ); 00322 if ( ( output->f = (REAL4TimeSeries *) 00323 LALMalloc( sizeof(REAL4TimeSeries) ) ) == NULL ) { 00324 LALFree( output->a ); output->a = NULL; 00325 ABORT( stat, GENERATESPINORBITCWH_EMEM, 00326 GENERATESPINORBITCWH_MSGEMEM ); 00327 } 00328 memset( output->f, 0, sizeof(REAL4TimeSeries) ); 00329 if ( ( output->phi = (REAL8TimeSeries *) 00330 LALMalloc( sizeof(REAL8TimeSeries) ) ) == NULL ) { 00331 LALFree( output->a ); output->a = NULL; 00332 LALFree( output->f ); output->f = NULL; 00333 ABORT( stat, GENERATESPINORBITCWH_EMEM, 00334 GENERATESPINORBITCWH_MSGEMEM ); 00335 } 00336 memset( output->phi, 0, sizeof(REAL8TimeSeries) ); 00337 00338 /* Set output structure metadata fields. */ 00339 output->position = params->position; 00340 output->psi = params->psi; 00341 output->a->epoch = output->f->epoch = output->phi->epoch 00342 = params->epoch; 00343 output->a->deltaT = n*params->deltaT; 00344 output->f->deltaT = output->phi->deltaT = params->deltaT; 00345 output->a->sampleUnits = lalStrainUnit; 00346 output->f->sampleUnits = lalHertzUnit; 00347 output->phi->sampleUnits = lalDimensionlessUnit; 00348 LALSnprintf( output->a->name, LALNameLength, "CW amplitudes" ); 00349 LALSnprintf( output->f->name, LALNameLength, "CW frequency" ); 00350 LALSnprintf( output->phi->name, LALNameLength, "CW phase" ); 00351 00352 /* Allocate phase and frequency arrays. */ 00353 LALSCreateVector( stat->statusPtr, &( output->f->data ), n ); 00354 BEGINFAIL( stat ) { 00355 LALFree( output->a ); output->a = NULL; 00356 LALFree( output->f ); output->f = NULL; 00357 LALFree( output->phi ); output->phi = NULL; 00358 } ENDFAIL( stat ); 00359 LALDCreateVector( stat->statusPtr, &( output->phi->data ), n ); 00360 BEGINFAIL( stat ) { 00361 TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), 00362 stat ); 00363 LALFree( output->a ); output->a = NULL; 00364 LALFree( output->f ); output->f = NULL; 00365 LALFree( output->phi ); output->phi = NULL; 00366 } ENDFAIL( stat ); 00367 00368 /* Allocate and fill amplitude array. */ 00369 { 00370 CreateVectorSequenceIn in; /* input to create output->a */ 00371 in.length = 2; 00372 in.vectorLength = 2; 00373 LALSCreateVectorSequence( stat->statusPtr, &(output->a->data), &in ); 00374 BEGINFAIL( stat ) { 00375 TRY( LALSDestroyVector( stat->statusPtr, &( output->f->data ) ), 00376 stat ); 00377 TRY( LALDDestroyVector( stat->statusPtr, &( output->phi->data ) ), 00378 stat ); 00379 LALFree( output->a ); output->a = NULL; 00380 LALFree( output->f ); output->f = NULL; 00381 LALFree( output->phi ); output->phi = NULL; 00382 } ENDFAIL( stat ); 00383 output->a->data->data[0] = output->a->data->data[2] = params->aPlus; 00384 output->a->data->data[1] = output->a->data->data[3] = params->aCross; 00385 } 00386 00387 /* Fill frequency and phase arrays. */ 00388 fData = output->f->data->data; 00389 phiData = output->phi->data->data; 00390 for ( i = 0; i < n; i++ ) { 00391 INT4 nOrb; /* number of orbits since the specified orbit epoch */ 00392 00393 /* First, find x in the range [0,2*pi]. */ 00394 x = vDotAvg*( i*dt - tPeriObs ); 00395 nOrb = (INT4)( x/LAL_TWOPI ); 00396 if ( x < 0.0 ) 00397 nOrb -= 1; 00398 x -= LAL_TWOPI*nOrb; 00399 00400 /* Newton-Raphson iteration to find E(x). */ 00401 while ( fabs( dx = e + a*sine + b*cose - x ) > dxMax ) { 00402 e -= dx/( 1.0 + a*cose + a - b*sine ); 00403 if ( e < 0.0 ) 00404 e = 0.0; 00405 else if ( e > LAL_TWOPI ) 00406 e = LAL_TWOPI; 00407 sine = sin( e ); 00408 cose = cos( e ) - 1.0; 00409 } 00410 00411 /* Compute source emission time, phase, and frequency. */ 00412 phi = t = tPow = 00413 ( e + LAL_TWOPI*nOrb - ecc*sine )/vDotAvg + spinOff; 00414 f = 1.0; 00415 for ( j = 0; j < nSpin; j++ ) { 00416 f += fSpin[j]*tPow; 00417 phi += fSpin[j]*( tPow*=t )/( j + 2.0 ); 00418 } 00419 00420 /* Appy frequency Doppler shift. */ 00421 upsilon = 2.0*atan2( sqrt( -onePlusEcc*cose ), sqrt( oneMinusEcc*( cose + 2.0 ) ) ); 00422 f *= f0 / ( 1.0 + vp*( cos( argument + upsilon ) + eCosOmega ) /onePlusEcc ); 00423 phi *= twopif0; 00424 if ( (i > 0) && (fabs( f - fPrev ) > df) ) 00425 df = fabs( f - fPrev ); 00426 *(fData++) = fPrev = f; 00427 *(phiData++) = phi + phi0; 00428 00429 } /* for i < n */ 00430 00431 /* Set output field and return. */ 00432 params->dfdt = df*dt; 00433 DETATCHSTATUSPTR( stat ); 00434 RETURN( stat ); 00435 }
1.5.2