GenerateEllipticSpinOrbitCW.c

Go to the documentation of this file.
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 }

Generated on Sat Sep 6 03:06:59 2008 for LAL by  doxygen 1.5.2