TerrestrialCoordinates.c

Go to the documentation of this file.
00001 /*
00002 *  Copyright (C) 2007 Jolien Creighton, Reinhard Prix, Teviet Creighton, John Whelan
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 /** \file
00021  * \ingroup SkyCoordinates
00022  * \author Creighton, T. D.
00023  * \date  $Date: 2008/05/22 19:27:42 $
00024  * \brief Converts among equatorial, geographic, and horizon coordinates.
00025  * 
00026  * $Id: TerrestrialCoordinates.c,v 1.20 2008/05/22 19:27:42 teviet Exp $
00027  * 
00028 
00029 \par Description
00030 
00031 The functions LALEquatorialToGeographic() and
00032 LALGeographicToEquatorial() convert between equatorial and
00033 geographic coordinate systems, reading coordinates in the first system
00034 from <tt>*input</tt> and storing the new coordinates in <tt>*output</tt>.
00035 The two pointers may point to the same object, in which case the
00036 conversion is done in place.  The functions will also check
00037 <tt>input->system</tt> and set <tt>output->system</tt> as appropriate.
00038 Because the geographic coordinate system is not fixed, one must also
00039 specify the time of the transformation in <tt>*gpsTime</tt>.
00040 
00041 The function LALSystemToHorizon() transforms coordinates from
00042 either celestial equatorial coordinates or geographic coordinates to a
00043 horizon coordinate system, reading coordinates in the first system
00044 from <tt>*input</tt> and storing the horizon coordinates in
00045 <tt>*output</tt>, as above.  The parameter <tt>*zenith</tt> specifies the
00046 direction of the vertical axis <em>in the original coordinate
00047 system</em>; the routine checks to see that <tt>input->system</tt> and
00048 <tt>zenith->system</tt> agree.  Normally this routine is used to convert
00049 from <em>geographic</em> latitude and longitude to a horizon system, in
00050 which case <tt>*zenith</tt> simply stores the geographic (geodetic)
00051 coordinates of the observer; if converting from equatorial
00052 coordinates, <tt>zenith->longitude</tt> should store the local mean
00053 sidereal time of the horizon system.
00054 
00055 The function LALHorizonToSystem() does the reverse of the
00056 above, transforming coordinates from horizon coordinates to either
00057 equatorial or geographic coordinates as specified by
00058 <tt>zenith->system</tt>; the value of <tt>output->system</tt> is set to
00059 agree with <tt>zenith->system</tt>.
00060 
00061 Although it is conventional to specify an observation location by its
00062 <em>geodetic</em> coordinates, some routines may provide or require
00063 <em>geocentric</em> coordinates.  The routines
00064 LALGeocentricToGeodetic() and LALGeodeticToGeocentric()
00065 perform this computation, reading and writing to the variable
00066 parameter structure <tt>*location</tt>.  The function
00067 LALGeocentricToGeodetic() reads the fields <tt>location->x</tt>,
00068 <tt>y</tt>, <tt>z</tt>, and computes <tt>location->zenith</tt> and
00069 <tt>location->altitude</tt>.  The function
00070 LALGeodeticToGeocentric() does the reverse, and also sets the
00071 fields <tt>location->position</tt> and <tt>location->radius</tt>.
00072 
00073 \par Algorithm
00074 
00075 These routines follow the formulae in Sec.5.1 of \ref Lang_K1999,
00076 which we reproduce below.
00077 
00078 \paragraph{Geographic coordinates:} Since geographic and equatorial
00079 coordinates share the same \f$z\f$-axis, the geographic latitude \f$\phi\f$ of
00080 a direction in space is the same as its declination \f$\delta\f$, and
00081 longitude \f$\lambda\f$ and right ascension \f$\alpha\f$ differ only through
00082 the rotation of the Earth:
00083 \f{equation}
00084 \label{eq:lambda-geographic}
00085 \lambda = \alpha - \left(\frac{2\pi\,\mathrm{radians}}
00086         {24\,\mathrm{hours}}\right)\times\mathrm{GMST} \; ,
00087 \f}
00088 where GMST is Greenwich mean sidereal time.  The conversion routines
00089 here simply use the functions in the <tt>date</tt> package to compute
00090 GMST for a given GPS time, and add it to the longitude.  While this is
00091 simple enough, it does involve several function calls, so it is
00092 convenient to collect these into one routine.
00093 
00094 \paragraph{Horizon coordinates:} We correct a typographical
00095 error on the second line of Eq.(5.45) of \ref Lang_K1999 (it should
00096 have \f$\cos A\f$, not \f$\sin A\f$).  We also note that while our latitudinal
00097 coordinate is just the altitude \f$a\f$ in this system, our longitudinal
00098 coordinate increases counterclockwise, and thus corresponds to the
00099 <em>negative</em> of the azimuth \f$A\f$ as defined by \ref Lang_K1999.
00100 So we have:
00101 \f{eqnarray}
00102 \label{eq:altitude-horizon}
00103 a & = & \arcsin(\sin\delta\sin\phi + \cos\delta\cos\phi\cos h) \; , \\
00104 \label{eq:azimuth-horizon}
00105 -A & = & \arctan\!2(\cos\delta\sin h, \sin\delta\cos\phi -
00106                 \cos\delta\sin\phi\cos h) \; ,
00107 \f}
00108 where \f$\delta\f$ is the declination (geographic latitude) of the
00109 direction being transformed, \f$\phi\f$ is the geographic latitude of the
00110 observer's zenith (i.e.\ the observer's <em>geodetic</em> latitude), and
00111 \f$h\f$ is the <em>hour angle</em> of the direction being transformed.  This
00112 is defined as:
00113 \f{eqnarray}
00114 h & = & \lambda_\mathrm{zenith} - \lambda \nonumber\\
00115   & = & \mathrm{LMST} - \alpha \nonumber
00116 \f}
00117 where LMST is the local mean sidereal time at the point of
00118 observation.  The inverse transformation is:
00119 \f{eqnarray}
00120 \label{eq:delta-horizon}
00121 \delta & = & \arcsin(\sin a\sin\phi + \cos a\cos A\cos\phi) \; , \\
00122 \label{eq:h-horizon}
00123 h & = & \arctan\!2[\cos a\sin(-A), \sin a\cos\phi -
00124                 \cos a\cos A\sin\phi] \; .
00125 \f}
00126 As explained in <tt>CelestialCoordinates.c</tt>, the function
00127 \f$\arctan\!2(y,x)\f$ returns the argument of the complex number \f$x+iy\f$.
00128 
00129 \image html inject_geodetic.png "Fig. 1: The difference between geodetic and geocentric latitude."
00130 \latexonly
00131 \newpage
00132 \begin{wrapfigure}{r}{0.35\textwidth}
00133 \vspace{-2ex}
00134 \begin{center}
00135 \resizebox{0.3\textwidth}{!}{\includegraphics{inject_geodetic}} \\
00136 \parbox{0.3\textwidth}{\caption{\label{fig:geodetic} The difference
00137 between geodetic and geocentric latitude.}}
00138 \end{center}
00139 \vspace{-2ex}
00140 \end{wrapfigure}
00141 \paragraph{Geocentric coordinates:} 
00142 \endlatexonly \htmlonly
00143 <b>Geocentric coordinates:</b>\endhtmlonly
00144 As shown in 
00145 \latexonly Fig.~\ref{fig:geodetic},\endlatexonly
00146 \htmlonly Fig. 1,\endhtmlonly
00147 the ellipticity of the Earth means that the
00148 vertical axis of a point on the Earth's surface does not pass through
00149 the geometric centre of the Earth.  This means that the geodetic
00150 latitude of a location (defined as the latitude angle
00151 \f$\phi_\mathrm{geodetic}\f$ of that location's zenith direction) is
00152 typically some 10 arcminutes larger than its geocentric latitude
00153 (defined as the latitude angle \f$\phi_\mathrm{geographic}\f$ of the
00154 position vector from the geocentre through the location).
00155 Cartographers traditionally refer to locations by their geodetic
00156 coordinates, since these can be determined locally; however,
00157 geocentric coordinates are required if one wants to construct a
00158 uniform Cartesian system for the Earth as a whole.
00159 
00160 To transform from geodetic to geocentric coordinates, one first
00161 defines a ``reference ellipsoid'', the best-fit ellipsoid to the
00162 surface of the Earth.  This is specified by the polar and equatorial
00163 radii of the Earth \f$r_p\f$ and \f$r_e\f$, or equivalently by \f$r_e\f$ and a
00164 flattening factor:
00165 \f[
00166 f \equiv 1 - \frac{r_p}{r_e} = 0.00335281 \; .
00167 \f]
00168 (This constant will eventually migrate into <tt>LALConstants.h</tt>.)
00169 The surface of the ellipsoid is then specified by the equation
00170 \f[
00171 r = r_e ( 1 - f\sin^2\phi ) \; ,
00172 \f]
00173 where \f$\phi=\phi_\mathrm{geodetic}\f$ is the geodetic latitude.  For
00174 points off of the reference ellipsoid, the transformation from
00175 geodetic coordinates \f$\lambda\f$, \f$\phi\f$ to geocentric Cartesian
00176 coordinates is:
00177 \f{eqnarray}
00178 x & = & ( r_e C + h ) \cos\phi\cos\lambda \; , \\
00179 y & = & ( r_e C + h ) \cos\phi\sin\lambda \; , \\
00180 z & = & ( r_e S + h ) \sin\phi \; ,
00181 \f}
00182 where
00183 \f{eqnarray}
00184 C & = & \frac{1}{\sqrt{\cos^2\phi + (1-f)^2\sin^2\phi}} \; , \\
00185 S & = & (1-f)^2 C \; ,
00186 \f}
00187 and \f$h\f$ is the perpendicular elevation of the location above the
00188 reference ellipsoid.  The geocentric spherical coordinates are given
00189 simply by:
00190 \f{eqnarray}
00191 r & = & \sqrt{ x^2 + y^2 + z^2 } \; , \\
00192 \lambda_\mathrm{geocentric} & = & \lambda \quad = \quad
00193         \lambda_\mathrm{geodetic} \; , \\
00194 \phi_\mathrm{geocentric} & = & \arcsin( z/r ) \; .
00195 \f}
00196 When computing \f$r\f$ we are careful to factor out the largest component
00197 before computing the sum of squares, to avoid floating-point overflow;
00198 however this should be unnecessary for radii near the surface of the
00199 Earth.
00200 
00201 The inverse transformation is somewhat trickier.  Eq.(5.29)
00202 of \ref Lang_K1999 conveniently gives the transformation in terms
00203 of a sequence of intermediate variables, but unfortunately these
00204 variables are not particularly computer-friendly, in that they are
00205 prone to underflow or overflow errors.  The following equations
00206 essentially reproduce this sequence using better-behaved methods of
00207 calculation.
00208 
00209 Given geocentric Cartesian coordinates
00210 \f$x=r\cos\phi_\mathrm{geocentric}\cos\lambda\f$,
00211 \f$y=r\cos\phi_\mathrm{geocentric}\sin\lambda\f$, and
00212 \f$z=r\sin\phi_\mathrm{geocentric}\f$, one computes the following:
00213 \f{eqnarray}
00214 \varpi & = & \sqrt{ \left(\frac{x}{r_e}\right)^2
00215         + \left(\frac{y}{r_e}\right)^2 } \;,\nonumber\\
00216 E & = & (1-f)\left|\frac{z}{r_e}\right| - f(2-f) \;,\nonumber\\
00217 F & = & (1-f)\left|\frac{z}{r_e}\right| + f(2-f) \;,\nonumber\\
00218 P & = & \frac{4}{3}\left( EF + \varpi^2 \right) \quad = \quad
00219         \frac{4}{3}\left[ \varpi^2 + (1-f)^2\left(\frac{z}{r_e}\right)^2
00220                 - f^2(2-f)^2 \right] \;,\nonumber\\
00221 Q & = & 2(F^2 - E^2) \quad = \quad
00222         8f(1-f)(2-f)\left|\frac{z}{r_e}\right| \;,\nonumber\\
00223 D & = & P^3 + \varpi^2 Q^2 \;,\nonumber\\
00224 v & = & \left\{\begin{array}{lr}
00225         \left(\sqrt{D}+\varpi Q\right)^{1/3}
00226                 - \left(\sqrt{D}-\varpi Q\right)^{1/3} &
00227                 D\geq0 \\
00228         2\sqrt{-P}\cos\left(\frac{1}{3}
00229                 \arccos\left[\frac{Q\varpi}{P\sqrt{-P}}\right]\right) &
00230                 D\leq0 \end{array}\right.\nonumber\\
00231 G & = & \mbox{$\frac{1}{2}$}\left(E+\sqrt{E^2 + \varpi v}\right)\;,\nonumber\\
00232 H & = & \frac{\varpi^2 F - \varpi vG}{G^2(2G-E)} \;,\nonumber\\
00233 t & = & G\left(\sqrt{1+H}-1\right) \;.\nonumber
00234 \f}
00235 Once we have \f$t\f$ and \f$\varpi\f$, we can compute the geodetic longitude
00236 \f$\lambda\f$, latitude \f$\phi\f$, and elevation \f$h\f$:
00237 \f{eqnarray}
00238 \lambda & = & \arctan\!2(y,x) \; , \\
00239 \phi & = & \mathrm{sgn}({z})\arctan\left[\frac{2}{1-f}
00240         \left(\frac{(\varpi-t)(\varpi+t)}{\varpi t}\right)\right] \; , \\
00241 h & = & r_e(\varpi-t/\varpi)\cos\phi
00242         + [z-\mathrm{sgn}({z})r_e(1-f)]\sin\phi \; .
00243 \f}
00244 These formulae, however, introduce certain concerns of numerical
00245 precision that have been only partially dealt with in this code.
00246 Specifically:
00247 
00248 - There is a coordinate singularity at \f$\varpi=0\f$, which we deal
00249 with by setting \f$\phi=\pm90^\circ\f$ and \f$\lambda\f$ arbitrarily to
00250 \f$0^\circ\f$.  When \f$z=0\f$ as well, we arbitrarily choose the positive
00251 sign for \f$\phi\f$.  However, the computation of \f$h\f$ in particular has
00252 tricky cancelations as \f$\varpi\rightarrow0\f$, which may give rise to
00253 numerical errors.  These have not yet been thoroughly explored.
00254 
00255 - There is another coordinate singularity when \f$D\rightarrow0\f$,
00256 which defines an ellipsoid with equatorial radius
00257 \f$r_0=r_ef(2-f)=42.6977\f$km and axial height \f$z_0=r_e/(1-f)=42.8413\f$km.
00258 Within this ellipsoid, lines of constant latitude begin to cross one
00259 another.  The listed solution is an analytic continuation of the
00260 exterior solution which assigns these points a unique, if arbitrary,
00261 geodetic latitude.  This solution has some peculiar behaviour, such as
00262 giving points in the equatorial plane a positive latitude.  In
00263 practice, however, users will rarely be interested coordinate
00264 transformations deep within the Earth's core.
00265 
00266 - The equations for \f$v\f$ and \f$G\f$ have square and cube roots of
00267 expressions involving squares and cubes of numbers.  For formal
00268 robustness one should factor out the leading-order dependence, so that
00269 one is assured of taking squares and cubes of numbers near unity.
00270 However, we are using <tt>REAL8</tt> precision, and have already
00271 normalized our distances by the Earth's radius \f$r_e\f$, so the point is
00272 almost certainly irrelevant.
00273 
00274 - The expression for \f$H\f$ may go to zero, leading to precision
00275 errors in \f$t\f$; the number of digits of precision lost is on the order
00276 of the number of leading zeros after the decimal place in \f$H\f$.  I
00277 arbitrarily state that we should not lose more than 4 of our 16
00278 decimal places of precision, meaning that we should series-expand the
00279 square root for \f$H<10^{-4}\f$.  To get our 12 places of precision back,
00280 we need an expansion to \f$H^3\f$:
00281 \f[
00282 t \approx G\left(\frac{1}{2}H - \frac{3}{8}H^2
00283         + \frac{5}{16}H^3\right) \; .
00284 \f]
00285 
00286 \item When computing \f$\phi\f$, we first compute \f$t-\varpi\f$, \f$t+\varpi\f$,
00287 \f$t^{-1}\f$, and \f$\varpi^{-1}\f$, sort them by order of magnitude, and
00288 alternately multiply large and small terms.  We note that if the
00289 argument of the \f$\arctan\f$ function is large we have
00290 \f[
00291 \arctan(x) = \mathrm{sgn}(x)\frac{\pi}{2}
00292         - \arctan\left(\frac{1}{x}\right) \; ,
00293 \f]
00294 but the <tt>atan()</tt> function in the C math library should be smart
00295 enough to do this itself.
00296 
00297 
00298 <b>Ellipsoidal vs. orthometric elevation:</b> In this module it
00299 is assumed that all elevations refer heights above the reference
00300 ellipsoid.  This is the elevation computed by such techniques as GPS
00301 triangulation.  However, the ``true'' orthometric elevation refers to
00302 the height above the mean sea level or <em>geoid</em>, a level surface
00303 in the Earth's gravitational potential.  Thus, even if two points have
00304 the same ellipsoidal elevation, water will still flow from the higher
00305 to the lower orthometric elevation.
00306 
00307 The difference between the geoid and reference ellipsoid is called the
00308 ``undulation of the geoid'', and can vary by over a hundred metres
00309 over the Earth's surface.  However, it can only be determined through
00310 painstaking measurements of local variations in the Earth's
00311 gravitational field.  For this reason we will ignore the undulation of
00312 the geoid.
00313 
00314 \par Uses
00315 \code
00316 LALGPStoUTC()
00317 XLALGreenwichMeanSiderealTime()
00318 LALDHeapSort()
00319 \endcode
00320 
00321 */
00322 
00323 
00324 /*---------- laldoc-documentation follows ---------- */
00325 
00326 /*********************** <lalVerbatim file="TerrestrialCoordinatesCV">
00327 Author: Creighton, T. D.
00328 $Id: TerrestrialCoordinates.c,v 1.20 2008/05/22 19:27:42 teviet Exp $
00329 **************************************************** </lalVerbatim> */
00330 
00331 /********************************************************** <lalLaTeX>
00332 
00333 \subsection{Module \texttt{TerrestrialCoordinates.c}}
00334 \label{ss:TerrestrialCoordinates.c}
00335 
00336 Converts among equatorial, geographic, and horizon coordinates.
00337 
00338 \subsubsection*{Prototypes}
00339 \vspace{0.1in}
00340 \input{TerrestrialCoordinatesCP}
00341 \idx{LALEquatorialToGeographic()}
00342 \idx{LALGeographicToEquatorial()}
00343 \idx{LALSystemToHorizon()}
00344 \idx{LALHorizonToSystem()}
00345 
00346 \subsubsection*{Description}
00347 
00348 The functions \verb@LALEquatorialToGeographic()@ and
00349 \verb@LALGeographicToEquatorial()@ convert between equatorial and
00350 geographic coordinate systems, reading coordinates in the first system
00351 from \verb@*input@ and storing the new coordinates in \verb@*output@.
00352 The two pointers may point to the same object, in which case the
00353 conversion is done in place.  The functions will also check
00354 \verb@input->system@ and set \verb@output->system@ as appropriate.
00355 Because the geographic coordinate system is not fixed, one must also
00356 specify the time of the transformation in \verb@*gpsTime@.
00357 
00358 The function \verb@LALSystemToHorizon()@ transforms coordinates from
00359 either celestial equatorial coordinates or geographic coordinates to a
00360 horizon coordinate system, reading coordinates in the first system
00361 from \verb@*input@ and storing the horizon coordinates in
00362 \verb@*output@, as above.  The parameter \verb@*zenith@ specifies the
00363 direction of the vertical axis \emph{in the original coordinate
00364 system}; the routine checks to see that \verb@input->system@ and
00365 \verb@zenith->system@ agree.  Normally this routine is used to convert
00366 from \emph{geographic} latitude and longitude to a horizon system, in
00367 which case \verb@*zenith@ simply stores the geographic (geodetic)
00368 coordinates of the observer; if converting from equatorial
00369 coordinates, \verb@zenith->longitude@ should store the local mean
00370 sidereal time of the horizon system.
00371 
00372 The function \verb@LALHorizonToSystem()@ does the reverse of the
00373 above, transforming coordinates from horizon coordinates to either
00374 equatorial or geographic coordinates as specified by
00375 \verb@zenith->system@; the value of \verb@output->system@ is set to
00376 agree with \verb@zenith->system@.
00377 
00378 Although it is conventional to specify an observation location by its
00379 \emph{geodetic} coordinates, some routines may provide or require
00380 \emph{geocentric} coordinates.  The routines
00381 \verb@LALGeocentricToGeodetic()@ and \verb@LALGeodeticToGeocentric()@
00382 perform this computation, reading and writing to the variable
00383 parameter structure \verb@*location@.  The function
00384 \verb@LALGeocentricToGeodetic()@ reads the fields \verb@location->x@,
00385 \verb@y@, \verb@z@, and computes \verb@location->zenith@ and
00386 \verb@location->altitude@.  The function
00387 \verb@LALGeodeticToGeocentric()@ does the reverse, and also sets the
00388 fields \verb@location->position@ and \verb@location->radius@.
00389 
00390 \subsubsection*{Algorithm}
00391 
00392 These routines follow the formulae in Sec.~5.1 of~\cite{Lang_K:1999},
00393 which we reproduce below.
00394 
00395 \paragraph{Geographic coordinates:} Since geographic and equatorial
00396 coordinates share the same $z$-axis, the geographic latitude $\phi$ of
00397 a direction in space is the same as its declination $\delta$, and
00398 longitude $\lambda$ and right ascension $\alpha$ differ only through
00399 the rotation of the Earth:
00400 \begin{equation}
00401 \label{eq:lambda-geographic}
00402 \lambda = \alpha - \left(\frac{2\pi\,\mathrm{radians}}
00403         {24\,\mathrm{hours}}\right)\times\mathrm{GMST} \; ,
00404 \end{equation}
00405 where GMST is Greenwich mean sidereal time.  The conversion routines
00406 here simply use the functions in the \verb@date@ package to compute
00407 GMST for a given GPS time, and add it to the longitude.  While this is
00408 simple enough, it does involve several function calls, so it is
00409 convenient to collect these into one routine.
00410 
00411 \paragraph{Horizon coordinates:} We correct a typographical
00412 error on the second line of Eq.~5.45 of~\cite{Lang_K:1999} (it should
00413 have $\cos A$, not $\sin A$).  We also note that while our latitudinal
00414 coordinate is just the altitude $a$ in this system, our longitudinal
00415 coordinate increases counterclockwise, and thus corresponds to the
00416 \emph{negative} of the azimuth $A$ as defined by~\cite{Lang_K:1999}.
00417 So we have:
00418 \begin{eqnarray}
00419 \label{eq:altitude-horizon}
00420 a & = & \arcsin(\sin\delta\sin\phi + \cos\delta\cos\phi\cos h) \; , \\
00421 \label{eq:azimuth-horizon}
00422 -A & = & \arctan\!2(\cos\delta\sin h, \sin\delta\cos\phi -
00423                 \cos\delta\sin\phi\cos h) \; ,
00424 \end{eqnarray}
00425 where $\delta$ is the declination (geographic latitude) of the
00426 direction being transformed, $\phi$ is the geographic latitude of the
00427 observer's zenith (i.e.\ the observer's \emph{geodetic} latitude), and
00428 $h$ is the \emph{hour angle} of the direction being transformed.  This
00429 is defined as:
00430 \begin{eqnarray}
00431 h & = & \lambda_\mathrm{zenith} - \lambda \nonumber\\
00432   & = & \mathrm{LMST} - \alpha \nonumber
00433 \end{eqnarray}
00434 where LMST is the local mean sidereal time at the point of
00435 observation.  The inverse transformation is:
00436 \begin{eqnarray}
00437 \label{eq:delta-horizon}
00438 \delta & = & \arcsin(\sin a\sin\phi + \cos a\cos A\cos\phi) \; , \\
00439 \label{eq:h-horizon}
00440 h & = & \arctan\!2[\cos a\sin(-A), \sin a\cos\phi -
00441                 \cos a\cos A\sin\phi] \; .
00442 \end{eqnarray}
00443 As explained in \verb@CelestialCoordinates.c@, the function
00444 $\arctan\!2(y,x)$ returns the argument of the complex number $x+iy$.
00445 
00446 \newpage
00447 
00448 \begin{wrapfigure}{r}{0.35\textwidth}
00449 \vspace{-2ex}
00450 \begin{center}
00451 \resizebox{0.3\textwidth}{!}{\includegraphics{inject_geodetic}} \\
00452 \parbox{0.3\textwidth}{\caption{\label{fig:geodetic} The difference
00453 between geodetic and geocentric latitude.}}
00454 \end{center}
00455 \vspace{-2ex}
00456 \end{wrapfigure}
00457 \paragraph{Geocentric coordinates:} As shown in
00458 Fig.~\ref{fig:geodetic}, the ellipticity of the Earth means that the
00459 vertical axis of a point on the Earth's surface does not pass through
00460 the geometric centre of the Earth.  This means that the geodetic
00461 latitude of a location (defined as the latitude angle
00462 $\phi_\mathrm{geodetic}$ of that location's zenith direction) is
00463 typically some 10~arcminutes larger than its geocentric latitude
00464 (defined as the latitude angle $\phi_\mathrm{geographic}$ of the
00465 position vector from the geocentre through the location).
00466 Cartographers traditionally refer to locations by their geodetic
00467 coordinates, since these can be determined locally; however,
00468 geocentric coordinates are required if one wants to construct a
00469 uniform Cartesian system for the Earth as a whole.
00470 
00471 To transform from geodetic to geocentric coordinates, one first
00472 defines a ``reference ellipsoid'', the best-fit ellipsoid to the
00473 surface of the Earth.  This is specified by the polar and equatorial
00474 radii of the Earth $r_p$ and $r_e$, or equivalently by $r_e$ and a
00475 flattening factor:
00476 $$
00477 f \equiv 1 - \frac{r_p}{r_e} = 0.00335281 \; .
00478 $$
00479 (This constant will eventually migrate into \verb@LALConstants.h@.)
00480 The surface of the ellipsoid is then specified by the equation
00481 $$
00482 r = r_e ( 1 - f\sin^2\phi ) \; ,
00483 $$
00484 where $\phi=\phi_\mathrm{geodetic}$ is the geodetic latitude.  For
00485 points off of the reference ellipsoid, the transformation from
00486 geodetic coordinates $\lambda$, $\phi$ to geocentric Cartesian
00487 coordinates is:
00488 \begin{eqnarray}
00489 x & = & ( r_e C + h ) \cos\phi\cos\lambda \; , \\
00490 y & = & ( r_e C + h ) \cos\phi\sin\lambda \; , \\
00491 z & = & ( r_e S + h ) \sin\phi \; ,
00492 \end{eqnarray}
00493 where
00494 \begin{eqnarray}
00495 C & = & \frac{1}{\sqrt{\cos^2\phi + (1-f)^2\sin^2\phi}} \; , \\
00496 S & = & (1-f)^2 C \; ,
00497 \end{eqnarray}
00498 and $h$ is the perpendicular elevation of the location above the
00499 reference ellipsoid.  The geocentric spherical coordinates are given
00500 simply by:
00501 \begin{eqnarray}
00502 r & = & \sqrt{ x^2 + y^2 + z^2 } \; , \\
00503 \lambda_\mathrm{geocentric} & = & \lambda \quad = \quad
00504         \lambda_\mathrm{geodetic} \; , \\
00505 \phi_\mathrm{geocentric} & = & \arcsin( z/r ) \; .
00506 \end{eqnarray}
00507 When computing $r$ we are careful to factor out the largest component
00508 before computing the sum of squares, to avoid floating-point overflow;
00509 however this should be unnecessary for radii near the surface of the
00510 Earth.
00511 
00512 The inverse transformation is somewhat trickier.  Eq.~5.29
00513 of~\cite{Lang_K:1999} conveniently gives the transformation in terms
00514 of a sequence of intermediate variables, but unfortunately these
00515 variables are not particularly computer-friendly, in that they are
00516 prone to underflow or overflow errors.  The following equations
00517 essentially reproduce this sequence using better-behaved methods of
00518 calculation.
00519 
00520 Given geocentric Cartesian coordinates
00521 $x=r\cos\phi_\mathrm{geocentric}\cos\lambda$,
00522 $y=r\cos\phi_\mathrm{geocentric}\sin\lambda$, and
00523 $z=r\sin\phi_\mathrm{geocentric}$, one computes the following:
00524 \begin{eqnarray}
00525 \varpi & = & \sqrt{ \left(\frac{x}{r_e}\right)^2
00526         + \left(\frac{y}{r_e}\right)^2 } \;,\nonumber\\
00527 E & = & (1-f)\left|\frac{z}{r_e}\right| - f(2-f) \;,\nonumber\\
00528 F & = & (1-f)\left|\frac{z}{r_e}\right| + f(2-f) \;,\nonumber\\
00529 P & = & \frac{4}{3}\left( EF + \varpi^2 \right) \quad = \quad
00530         \frac{4}{3}\left[ \varpi^2 + (1-f)^2\left(\frac{z}{r_e}\right)^2
00531                 - f^2(2-f)^2 \right] \;,\nonumber\\
00532 Q & = & 2\varpi(F^2 - E^2) \quad = \quad
00533         8\varpi f(1-f)(2-f)\left|\frac{z}{r_e}\right| \;,\nonumber\\
00534 D & = & P^3 + Q^2 \;,\nonumber\\
00535 v & = & \left\{\begin{array}{lr}
00536         \left(\sqrt{D}+Q\right)^{1/3}
00537                 - \left(\sqrt{D}-Q\right)^{1/3} &
00538                 D\geq0 \\
00539         2\sqrt{-P}\cos\left(\frac{1}{3}
00540                 \arccos\left[\frac{Q}{-P\sqrt{-P}}\right]\right) &
00541                 D\leq0 \end{array}\right.\nonumber\\
00542 W & = & \sqrt{E^2 + \varpi v} \nonumber\\[1ex]
00543 G & = & \mbox{$\frac{1}{2}$}\left(E+W\right)\;,\nonumber\\
00544 t & = & \sqrt{G^2+\frac{\varpi^2 F - \varpi vG}{W}}-G \;.\nonumber
00545 \end{eqnarray}
00546 Once we have $t$ and $\varpi$, we can compute the geodetic longitude
00547 $\lambda$, latitude $\phi$, and elevation $h$:
00548 \begin{eqnarray}
00549 \lambda & = & \arctan\!2(y,x) \; , \\
00550 \phi & = & \mathrm{sgn}({z})\arctan\left[\frac{1}{2(1-f)}
00551         \left(\frac{(\varpi-t)(\varpi+t)}{\varpi t}\right)\right] \; , \\
00552 h & = & r_e(\varpi-t/\varpi)\cos\phi
00553         + [z-\mathrm{sgn}({z})r_e(1-f)]\sin\phi \; .
00554 \end{eqnarray}
00555 
00556 \begin{wrapfigure}{r}{0.47\textwidth}
00557 \vspace{-8ex}
00558 \begin{center}
00559 \resizebox{0.42\textwidth}{!}{\includegraphics{inject_geodeticsing}} \\
00560 \parbox{0.42\textwidth}{\caption{\label{fig:geodeticsing} Singular
00561 surfaces in the geodetic coordinate system.  The ellipticity of this
00562 spheroid has been exaggerated compared with the Earth.}}
00563 \end{center}
00564 \vspace{-7ex}
00565 \end{wrapfigure}
00566 \noindent These formulae still leave certain areas where coordinate
00567 singularities or numerical cancelations can occur.  Some of these have
00568 been dealt with in the code:
00569 \begin{itemize}
00570 \item There is a coordinate singularity at $\varpi=0$, which we deal
00571 with by setting $\phi=\pm90^\circ$ and $\lambda$ arbitrarily to
00572 $0^\circ$.  When $z=0$ as well, we arbitrarily choose the positive
00573 sign for $\phi$.  As $\varpi\rightarrow0$, there are cancellations in
00574 the computation of $G$ and $t$, which call for special treatment
00575 (below).
00576 
00577 \item There is another coordinate singularity when $D\leq0$, where
00578 lines of constant geodetic latitude will cross each other, as shown in
00579 Fig.~\ref{fig:geodeticsing}.  That is, a given point within this
00580 region can be assigned a range of geodetic latitudes.  The
00581 multi-valued region lies within an inner ellipsoid $P\leq0$, which in
00582 the case of the Earth has equatorial radius $r_0=r_ef(2-f)=42.6977$km
00583 and axial height $z_0=r_0/(1-f)=42.8413$km.  The formula for $v$,
00584 above, has an analytic continuation to $D\leq0$, assigning consistent
00585 (though not unique) values of latitute and elevation to these points.
00586 \end{itemize}
00587 
00588 \begin{itemize}
00589 \item Near the equator we have $Q\rightarrow0$, and the first
00590 expression for $v$ becomes a difference of nearly-equal numbers,
00591 leading to loss of precision.  To deal with this, we write that
00592 expression for $v$ as:
00593 $$
00594 \begin{array}{rcl@{\qquad}c@{\qquad}l}
00595   v &=& D^{1/6}\left[(1+B)^{1/3}-(1-B)^{1/3}\right]
00596   &\mbox{where}& B \;\;=\;\; \displaystyle \frac{Q}{\sqrt{D}} \\
00597   &\approx& D^{1/6}\left[\frac{2}{3}B+\frac{10}{81}B^3\right]
00598   &\mbox{as}& B \;\;\rightarrow\;\;0
00599 \end{array}
00600 $$
00601 
00602 The switch from the ``exact'' formula to the series expansion is done
00603 for $B<10^{-3}$ (within about $2^\circ$ of the equator).  This was
00604 found by experimentation to be the point where the inaccuracy of the
00605 series expansion is roughly equal to the imprecision in evaluating the
00606 ``exact'' formula.  The resulting position errors are of order 1 part
00607 in $10^{12}$ or less; i.e.\ about 3--4 digits loss of precision.
00608 
00609 \item In some places we have expressions of the form $\sqrt{a^2+b}-a$,
00610 which becomes a difference of nearly equal numbers for $|b|\ll a^2$,
00611 resulting in loss of precision.  There are three distinct lines or
00612 surfaces where this occurs:
00613 \begin{enumerate}
00614 \item Near the ellipsoidal surface $P\approx0$, the expression $\sqrt{D}-Q$
00615 in the equation for $v$ becomes of this form.
00616 \item Near the polar axis $\varpi\approx0$, the expression for $t$
00617 becomes of this form.
00618 \item Near the polar axis $\varpi\approx0$ within the inner ellipsoid,
00619 we have $E<0$ and the expression for $G$ becomes of this form.
00620 \end{enumerate}
00621 In each case, we expand in the small parameter $H=b/a^2$, giving:
00622 $$
00623 \sqrt{a^2+b}-a \;\;\approx\;\; a\left(\mbox{$\frac{1}{2}$}H
00624 -\mbox{$\frac{1}{8}$}H^2+\mbox{$\frac{1}{16}$}H^3\right)
00625 \qquad\mbox{for}\qquad |H| = \left|\frac{b}{a^2}\right| \ll 1
00626 $$
00627 
00628 We switch to the series expansion for $|H|<\times10^{-4}$, which again
00629 is the point where residual errors in the series expansion are about
00630 the same as the loss of precision without the expansion.  This formula
00631 converges much better than the one for $v$ (above), resulting in
00632 negligible loss of precision in the final position.
00633 
00634 \item The only remaining known numerical singularities are at the
00635 poles of the inner ellipsoid, shown as red dots in
00636 Fig.~\ref{fig:geodeticsing}.  These points stubbornly resist
00637 high-precision calculation.  However, they are extremely unlikely to
00638 come up in practice.
00639 \end{itemize}
00640 
00641 \paragraph{Ellipsoidal vs.\ orthometric elevation:} In this module it
00642 is assumed that all elevations refer heights above the reference
00643 ellipsoid.  This is the elevation computed by such techniques as GPS
00644 triangulation.  However, the ``true'' orthometric elevation refers to
00645 the height above the mean sea level or \emph{geoid}, a level surface
00646 in the Earth's gravitational potential.  Thus, even if two points have
00647 the same ellipsoidal elevation, water will still flow from the higher
00648 to the lower orthometric elevation.
00649 
00650 The difference between the geoid and reference ellipsoid is called the
00651 ``undulation of the geoid'', and can vary by over a hundred metres
00652 over the Earth's surface.  However, it can only be determined through
00653 painstaking measurements of local variations in the Earth's
00654 gravitational field.  For this reason we will ignore the undulation of
00655 the geoid.
00656 
00657 \subsubsection*{Uses}
00658 \begin{verbatim}
00659 XLALGreenwichMeanSiderealTime()
00660 \end{verbatim}
00661 
00662 \subsubsection*{Notes}
00663 
00664 \vfill{\footnotesize\input{TerrestrialCoordinatesCV}}
00665 
00666 ******************************************************* </lalLaTeX> */
00667 
00668 
00669 #include <math.h>
00670 #include <lal/LALStdlib.h>
00671 #include <lal/LALConstants.h>
00672 #include <lal/Date.h>
00673 #include <lal/Sort.h>
00674 #include <lal/SkyCoordinates.h>
00675 #include <string.h>
00676 
00677 #define LAL_EARTHFLAT (0.00335281)
00678 #define LAL_HSERIES (0.0001) /* value H below which we expand sqrt(1+H)-1 */
00679 #define LAL_BSERIES (0.001)  /* value B below which we expand v */
00680 
00681 NRCSID( TERRESTRIALCOORDINATESC, "$Id: TerrestrialCoordinates.c,v 1.20 2008/05/22 19:27:42 teviet Exp $" );
00682 
00683 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00684 void
00685 LALEquatorialToGeographic( LALStatus   *stat,
00686                            SkyPosition *output,
00687                            SkyPosition *input,
00688                            LIGOTimeGPS *gpsTime )
00689 { /* </lalVerbatim> */
00690   REAL8 gmst;            /* siderial time (radians) */
00691 
00692   INITSTATUS( stat, "LALEquatorialToGeographic", TERRESTRIALCOORDINATESC );
00693 
00694   /* Make sure parameter structures exist. */
00695   ASSERT( input, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00696   ASSERT( output, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00697   ASSERT( gpsTime, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00698 
00699   /* Make sure we're given the right coordinate system. */
00700   if ( input->system != COORDINATESYSTEM_EQUATORIAL ) {
00701     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00702   }
00703 
00704   /* Compute the Greenwich mean sidereal time. */
00705   gmst = fmod( XLALGreenwichMeanSiderealTime( gpsTime ), LAL_TWOPI );
00706 
00707   /* Add to longitude, and exit. */
00708   output->system = COORDINATESYSTEM_GEOGRAPHIC;
00709   output->longitude = fmod( input->longitude - gmst, LAL_TWOPI );
00710   if ( output->longitude < 0.0 )
00711     output->longitude += LAL_TWOPI;
00712   output->latitude = input->latitude;
00713   RETURN( stat );
00714 }
00715 
00716 
00717 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00718 void
00719 LALGeographicToEquatorial( LALStatus   *stat,
00720                            SkyPosition *output,
00721                            SkyPosition *input,
00722                            LIGOTimeGPS *gpsTime )
00723 { /* </lalVerbatim> */
00724   REAL8 gmst;            /* siderial time (radians) */
00725 
00726   INITSTATUS( stat, "LALEquatorialToGeographic", TERRESTRIALCOORDINATESC );
00727 
00728   /* Make sure parameter structures exist. */
00729   ASSERT( input, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00730   ASSERT( output, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00731   ASSERT( gpsTime, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00732 
00733   /* Make sure we're given the right coordinate system. */
00734   if ( input->system != COORDINATESYSTEM_GEOGRAPHIC ) {
00735     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00736   }
00737 
00738   /* Compute the Greenwich mean sidereal time. */
00739   gmst = fmod( XLALGreenwichMeanSiderealTime( gpsTime ), LAL_TWOPI );
00740 
00741   /* Subtract from longitude, and exit. */
00742   output->system = COORDINATESYSTEM_EQUATORIAL;
00743   output->longitude = fmod( input->longitude + gmst, LAL_TWOPI );
00744   if ( output->longitude < 0.0 )
00745     output->longitude += LAL_TWOPI;
00746   output->latitude = input->latitude;
00747   RETURN( stat );
00748 }
00749 
00750 
00751 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00752 void
00753 LALSystemToHorizon( LALStatus   *stat,
00754                     SkyPosition *output,
00755                     SkyPosition *input,
00756                     const SkyPosition *zenith )
00757 { /* </lalVerbatim> */
00758   REAL8 h, sinH, cosH; /* hour angle, and its sine and cosine */
00759   REAL8 sinP, cosP;    /* sin and cos of zenith latitude */
00760   REAL8 sinD, cosD;    /* sin and cos of position latitude (declination) */
00761   REAL8 sina, sinA, cosA; /* sin and cos of altitude and -azimuth */
00762 
00763   INITSTATUS( stat, "LALSystemToHorizon", TERRESTRIALCOORDINATESC );
00764 
00765   /* Make sure parameter structures exist. */
00766   ASSERT( input, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00767   ASSERT( output, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00768   ASSERT( zenith, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00769 
00770   /* Make sure we have consistent input coordinate systems. */
00771   if ( input->system != zenith->system ) {
00772     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00773   }
00774   if ( ( zenith->system != COORDINATESYSTEM_EQUATORIAL ) &&
00775        ( zenith->system != COORDINATESYSTEM_GEOGRAPHIC ) ) {
00776     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00777   }
00778 
00779   /* Compute intermediates. */
00780   h = zenith->longitude - input->longitude;
00781   sinH = sin( h );
00782   cosH = cos( h );
00783   sinP = sin( zenith->latitude );
00784   cosP = cos( zenith->latitude );
00785   sinD = sin( input->latitude );
00786   cosD = cos( input->latitude );
00787 
00788   /* Compute components. */
00789   sina = sinD*sinP + cosD*cosP*cosH;
00790   sinA = cosD*sinH;
00791   cosA = sinD*cosP - cosD*sinP*cosH;
00792 
00793   /* Compute final results. */
00794   output->system = COORDINATESYSTEM_HORIZON;
00795   output->latitude = asin( sina );
00796   output->longitude = atan2( sinA, cosA );
00797 
00798   /* Optional phase correction. */
00799   if ( output->longitude < 0.0 )
00800     output->longitude += LAL_TWOPI;
00801 
00802   RETURN( stat );
00803 }
00804 
00805 
00806 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00807 void
00808 LALHorizonToSystem( LALStatus   *stat,
00809                     SkyPosition *output,
00810                     SkyPosition *input,
00811                     const SkyPosition *zenith )
00812 { /* </lalVerbatim> */
00813   REAL8 sinP, cosP;       /* sin and cos of zenith latitude */
00814   REAL8 sina, cosa;       /* sin and cos of altitude */
00815   REAL8 sinA, cosA;       /* sin and cos of -azimuth */
00816   REAL8 sinD, sinH, cosH; /* sin and cos of declination and hour angle */
00817 
00818   INITSTATUS( stat, "LALHorizonToSystem", TERRESTRIALCOORDINATESC );
00819 
00820   /* Make sure parameter structures exist. */
00821   ASSERT( input, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00822   ASSERT( output, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00823   ASSERT( zenith, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00824 
00825   /* Make sure we're given the right coordinate system. */
00826   if ( input->system != COORDINATESYSTEM_HORIZON ) {
00827     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00828   }
00829   if ( ( zenith->system != COORDINATESYSTEM_EQUATORIAL ) &&
00830        ( zenith->system != COORDINATESYSTEM_GEOGRAPHIC ) ) {
00831     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00832   }
00833 
00834   /* Compute intermediates. */
00835   sinP = sin( zenith->latitude );
00836   cosP = cos( zenith->latitude );
00837   sina = sin( input->latitude );
00838   cosa = cos( input->latitude );
00839   sinA = sin( input->longitude );
00840   cosA = cos( input->longitude );
00841 
00842   /* Compute components. */
00843   sinD = sina*sinP + cosa*cosA*cosP;
00844   sinH = cosa*sinA;
00845   cosH = sina*cosP - cosa*cosA*sinP;
00846 
00847   /* Compute final results. */
00848   output->system = zenith->system;
00849   output->latitude = asin( sinD );
00850   output->longitude = zenith->longitude - atan2( sinH, cosH );
00851 
00852   /* Optional phase correction. */
00853   if ( output->longitude < 0.0 )
00854     output->longitude += LAL_TWOPI;
00855 
00856   RETURN( stat );
00857 }
00858 
00859 
00860 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00861 void
00862 LALGeodeticToGeocentric( LALStatus *stat, EarthPosition *location )
00863 { /* </lalVerbatim> */
00864   REAL8 c, s; /* position components in and orthogonal to the equator */
00865   REAL8 cosP, sinP; /* cosine and sine of latitude */
00866   REAL8 fFac;       /* ( 1 - f )^2 */
00867   REAL8 x, y;       /* Cartesian coordinates */
00868   REAL8 maxComp, r; /* max{x,y,z}, and sqrt(x^2+y^2+z^2) */
00869 
00870   INITSTATUS( stat, "LALGeodeticToGeocentric", TERRESTRIALCOORDINATESC );
00871 
00872   /* Make sure parameter structure exists. */
00873   ASSERT( location, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00874 
00875   /* Make sure the geodetic coordinates are in the right system. */
00876   if ( location->geodetic.system != COORDINATESYSTEM_GEOGRAPHIC ) {
00877     ABORT( stat, SKYCOORDINATESH_ESYS, SKYCOORDINATESH_MSGESYS );
00878   }
00879 
00880   /* Compute intermediates. */
00881   fFac = 1.0 - LAL_EARTHFLAT;
00882   fFac *= fFac;
00883   cosP = cos( location->geodetic.latitude );
00884   sinP = sin( location->geodetic.latitude );
00885   c = sqrt( 1.0 / ( cosP*cosP + fFac*sinP*sinP ) );
00886   s = fFac*c;
00887   c = ( LAL_REARTH_SI*c + location->elevation )*cosP;
00888   s = ( LAL_REARTH_SI*s + location->elevation )*sinP;
00889 
00890   /* Compute Cartesian coordinates. */
00891   location->x = x = c*cos( location->geodetic.longitude );
00892   location->y = y = c*sin( location->geodetic.longitude );
00893   location->z = s;
00894 
00895   /* Compute the radius. */
00896   maxComp = x;
00897   if ( y > maxComp )
00898     maxComp = y;
00899   if ( s > maxComp )
00900     maxComp = s;
00901   x /= maxComp;
00902   y /= maxComp;
00903   s /= maxComp;
00904   r = sqrt( x*x + y*y + s*s );
00905 
00906   /* Compute the spherical coordinates, and exit. */
00907   location->radius = maxComp*r;
00908   location->geocentric.longitude = location->geodetic.longitude;
00909   location->geocentric.latitude = asin( s / r );
00910   location->geocentric.system = COORDINATESYSTEM_GEOGRAPHIC;
00911   RETURN( stat );
00912 }
00913 
00914 
00915 /* <lalVerbatim file="TerrestrialCoordinatesCP"> */
00916 void
00917 LALGeocentricToGeodetic( LALStatus *stat, EarthPosition *location )
00918 { /* </lalVerbatim> */
00919   REAL8 x, y, z;   /* normalized geocentric coordinates */
00920   REAL8 pi;        /* axial distance */
00921 
00922   /* Declare some local constants. */
00923   const REAL8 rInv = 1.0 / LAL_REARTH_SI;
00924   const REAL8 f1 = 1.0 - LAL_EARTHFLAT;
00925   const REAL8 f2 = LAL_EARTHFLAT*( 2.0 - LAL_EARTHFLAT );
00926 
00927   INITSTATUS( stat, "LALGeocentricToGeodetic", TERRESTRIALCOORDINATESC );
00928 
00929   /* Make sure parameter structure exists. */
00930   ASSERT( location, stat, SKYCOORDINATESH_ENUL, SKYCOORDINATESH_MSGENUL );
00931 
00932   /* See if we've been given a special set of coordinates. */
00933   x = rInv*location->x;
00934   y = rInv*location->y;
00935   z = rInv*location->z;
00936   pi = sqrt( x*x + y*y );
00937   location->geodetic.system = COORDINATESYSTEM_GEOGRAPHIC;
00938   location->geodetic.longitude = atan2( y, x );
00939   location->radius = LAL_REARTH_SI*sqrt( x*x + y*y + z*z );
00940   if ( pi == 0.0 ) {
00941     if ( z >= 0.0 ) {
00942       location->geodetic.latitude = LAL_PI_2;
00943       location->elevation = z - f1;
00944     } else {
00945       location->geodetic.latitude = -LAL_PI_2;
00946       location->elevation = -z - f1;
00947     }
00948     location->elevation *= LAL_REARTH_SI;
00949   }
00950 
00951   /* Do the general transformation even if z=0. */
00952   else {
00953     REAL8 za, e, f, p, p3, q, q2, d, d2, b, v, w, g, h, gh, t, phi, tanP;
00954     /* intermediate variables */
00955 
00956     /* See if we're inside the singular ellipsoid.
00957     if ( pi <= 1.01*f2 ) {
00958       REAL8 z1 = z*f1/f2;
00959       REAL8 p1 = pi/f2;
00960       if ( z1*z1 + p1*p1 < 1.02 ) {
00961         ABORT( stat, SKYCOORDINATESH_ESING, SKYCOORDINATESH_MSGESING );
00962       }
00963     } */
00964 
00965     /* Compute intermediates variables. */
00966     za = f1*fabs( z );
00967     e = za - f2;
00968     f = za + f2;
00969     p = ( 4.0/3.0 )*( pi*pi + za*za - f2*f2 );
00970     p3 = p*p*p;
00971     q = 8.0*pi*f2*za;
00972     q2 = q*q;
00973     h = p3/q2;
00974     d = p3 + q2;
00975 
00976     /* Compute v, using series expansion if necessary. */
00977     if ( d >= 0.0 ) {
00978       d2 = sqrt( d );
00979       b = q/d2;
00980       if ( fabs( h ) < LAL_HSERIES ) {
00981         if ( h < 0.0 )
00982           v = pow( d2 + q, 1.0/3.0 )
00983             + pow( -0.5*q*h*( 1.0 - 0.25*h*( 1.0 - 0.5*h ) ), 1.0/3.0 );
00984         else
00985           v = pow( d2 + q, 1.0/3.0 )
00986             - pow( 0.5*q*h*( 1.0 - 0.25*h*( 1.0 - 0.5*h ) ), 1.0/3.0 );
00987       } else if ( b < LAL_BSERIES ) {
00988         v = pow( d2, 1.0/3.0 )*( b*( 2.0/3.0 + b*b*10.0/81.0 ) );
00989       } else if ( b < 1.0 ) {
00990         v = pow( d2, 1.0/3.0 )*( pow( 1.0 + b, 1.0/3.0 ) -
00991                                  pow( 1.0 - b, 1.0/3.0 ) );
00992       } else {
00993         v = pow( d2, 1.0/3.0 )*( pow( b + 1.0, 1.0/3.0 ) +
00994                                  pow( b - 1.0, 1.0/3.0 ) );
00995       }
00996     } else {
00997       v = 2.0*sqrt( -p )*cos( acos( q/( -p*sqrt( -p ) ) )/3.0 );
00998     }
00999 
01000     /* Compute t, using series expansion if necessary. */
01001     h = v*pi/( e*e );
01002     w = fabs( e )*sqrt( 1.0 + h );
01003     if ( e > 0.0 || h > LAL_HSERIES )
01004       g = 0.5*( e + w );
01005     else
01006       g = -0.25*e*h*( 1.0 - 0.25*h*( 1.0 - 0.5*h ) );      
01007     gh = fabs( pi*( f*pi - v*g )/w );
01008     h = gh/( g*g );
01009     if ( fabs( h ) > LAL_HSERIES )
01010       t = g*( sqrt( 1.0 + h ) - 1.0 );
01011     else
01012       t = 0.5*g*h*( 1.0 - 0.25*h*( 1.0 - 0.5*h ) );
01013 
01014     /* Compute latitude, longitude, and elevation. */
01015     tanP = ( pi - t )*( pi + t )/( 2.0*f1*pi*t );
01016     phi = atan( tanP );
01017     location->geodetic.latitude = phi;
01018     if ( z < 0.0 )
01019       location->geodetic.