Loading [MathJax]/extensions/TeX/AMSsymbols.js
LAL 7.7.0.1-da3b9d3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
GeocentricGeodeticTest.c
Go to the documentation of this file.
1/*
2* Copyright (C) 2008 Teviet Creighton
3*
4* This program is free software; you can redistribute it and/or modify
5* it under the terms of the GNU General Public License as published by
6* the Free Software Foundation; either version 2 of the License, or
7* (at your option) any later version.
8*
9* This program is distributed in the hope that it will be useful,
10* but WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12* GNU General Public License for more details.
13*
14* You should have received a copy of the GNU General Public License
15* along with with program; see the file COPYING. If not, write to the
16* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17* MA 02110-1301 USA
18*/
19
20#include <math.h>
21#include <stdlib.h>
22#include <lal/LALStdio.h>
23#include <lal/LALStdlib.h>
24#include <lal/LALConstants.h>
25#include <lal/AVFactories.h>
26#include <lal/Random.h>
27#include <lal/Date.h>
28#include <lal/SkyCoordinates.h>
29
30/**
31 * \author Creighton, T. D.
32 * \file
33 * \ingroup SkyCoordinates_h
34 *
35 * \brief Tests geocentric to geodetic conversion.
36 *
37 * ### Usage ###
38 *
39 * \code
40 * GeocentricGeodeticTest [-x xmin xmax nx] [-y ymin ymax ny]
41 * [-z zmin zmax nz] [-v] [-d debuglevel]
42 * \endcode
43 *
44 * ### Description ###
45 *
46 * This program converts a point or set of points from geocentric to
47 * geodetic coordinates and back, using the routines
48 * <tt>LALGeocentricToGeodetic()</tt> and <tt>LALGeodeticToGeocentric()</tt>.
49 * The reconverted position is compared with the original, and the
50 * maximum difference (in metres) reported to \c stderr. The
51 * following option flags are accepted:
52 * <ul>
53 * <li><tt>-x</tt> Specifies a range and number of points to span in
54 * the geocentric Cartesian \f$x\f$-coordinate. Range limits are given in
55 * Earth radii, and the number of points must be at least 1. If not
56 * specified, a single value of 0 is assumed.</li>
57 * <li><tt>-y</tt> As <tt>-x</tt>, above, but for the \f$y\f$-coordinate.</li>
58 * <li><tt>-z</tt> As <tt>-x</tt>, above, but for the \f$z\f$-coordinate.</li>
59 * <li><tt>-v</tt> Specifies verbosity level. The default is level 0,
60 * printing only the maximum difference to \c stderr. Level 1 in
61 * addition prints to \c stdout the difference measured at each
62 * point. Level 2 prints the elevation of the point, followed by the
63 * difference, both in metres (this is to facilitate shaded diagrams such
64 * as \ref inject_geodetictest "this figure". Level 3 prints the \f$x\f$, \f$y\f$, and \f$z\f$
65 * coordinates of each point followed by the difference measured at that
66 * point (all in metres). Level 4 prints the geocentric Cartesian
67 * coordinates, above, folowed by the geodetic elevation, latitude and
68 * longitude, followed by the difference (in metres or degrees as
69 * appropriate).</li>
70 * <li><tt>-d</tt> Sets the debug level to \c debuglevel. If not
71 * specified, level 0 is assumed.</li>
72 * </ul>
73 * If neither <tt>-x</tt>, <tt>-y</tt>, or <tt>-z</tt> were specified, then the
74 * program will test a single randomly generated position between 0.5 and
75 * 2 Earth radii, and return an error if the conversion produces a
76 * difference greater than a micron.
77 *
78 * ### Algorithm ###
79 *
80 * \anchor inject_geodetictest
81 * \image html inject_geodetictest.png "Precision of geocentric-geodetic conversion algorithm. Shaded ellipse is the reference ellipsoid. The wedges of (comparatively) lower precision occur near the equator\, where the series expansion in B is required."
82 *
83 * See \ref TerrestrialCoordinates_c for documentation about the
84 * geocentric/geodetic conversion algorithm. Since
85 * <tt>LALGeodeticToGeocentric()</tt> is fairly straightforward and
86 * numerically robust, this program is basically a test of the
87 * <tt>LALGeocentricToGeodetic()</tt> algorithm.
88 *
89 * Running with verbosity level 2 gives error data that can be used to
90 * generate figures such as \ref inject_geodetictest "this figure". First we note
91 * that for points near the surface of the Earth, the position error is
92 * never greater than a micron, or about one part in \f$10^{12}\f$ --- a
93 * reasonable expectation for double-precision arithmetic. The largest
94 * errors occur near the equator, where the expression for \f$v\f$
95 * experiences loss of precision. In this limit we replace the "exact"
96 * expression for \f$v\f$ with a power series, as described in
97 * <tt>TerrestrialCoordinates.c()</tt>. This restores precision near the
98 * equatorial plane. The point of transition has been tuned by hand, so
99 * that the errors due to series truncation just within the wedge are
100 * about the same as the numerical loss of precision just outside of it.
101 */
102
103/** \name Error Codes */
104/** @{ */
105#define GEOCENTRICGEODETICTESTC_ENORM 0 /**< Normal exit */
106#define GEOCENTRICGEODETICTESTC_ESUB 1 /**< Subroutine failed */
107#define GEOCENTRICGEODETICTESTC_EARG 2 /**< Error parsing arguments */
108#define GEOCENTRICGEODETICTESTC_EMEM 3 /**< Out of memory */
109#define GEOCENTRICGEODETICTESTC_ETEST 4 /**< Test case failed */
110/** @} */
111
112/** \cond DONT_DOXYGEN */
113#define GEOCENTRICGEODETICTESTC_MSGENORM "Normal exit"
114#define GEOCENTRICGEODETICTESTC_MSGESUB "Subroutine failed"
115#define GEOCENTRICGEODETICTESTC_MSGEARG "Error parsing arguments"
116#define GEOCENTRICGEODETICTESTC_MSGEMEM "Out of memory"
117#define GEOCENTRICGEODETICTESTC_MSGETEST "Test case failed"
118
119
120/* Default parameter settings. */
121
122/* Usage format string. */
123#define USAGE "Usage: %s [-x xmin xmax nx] [-y ymin ymax ny]\n" \
124"\t[-z zmin zmax nz] [-o outfle] [-d debuglevel]\n"
125
126/* Macros for printing errors and testing subroutines. */
127#define ERROR( code, msg, statement ) \
128do \
129if ( lalDebugLevel & LALERROR ) \
130{ \
131 LALPrintError( "Error[0] %d: program %s, file %s, line %d, %s\n" \
132 " %s %s\n", (code), *argv, __FILE__, \
133 __LINE__, "$Id$", statement ? \
134 statement : "", (msg) ); \
135} \
136while (0)
137
138#define INFO( statement ) \
139do \
140if ( lalDebugLevel & LALINFO ) \
141{ \
142 LALPrintError( "Info[0]: program %s, file %s, line %d, %s\n" \
143 " %s\n", *argv, __FILE__, __LINE__, \
144 "$Id$", (statement) ); \
145} \
146while (0)
147
148#define WARNING( statement ) \
149do \
150if ( lalDebugLevel & LALWARNING ) \
151{ \
152 LALPrintError( "Warning[0]: program %s, file %s, line %d, %s\n" \
153 " %s\n", *argv, __FILE__, __LINE__, \
154 "$Id$", (statement) ); \
155} \
156while (0)
157
158#define SUB( func, statusptr ) \
159do \
160if ( (func), (statusptr)->statusCode ) \
161{ \
162 ERROR( GEOCENTRICGEODETICTESTC_ESUB, \
163 GEOCENTRICGEODETICTESTC_MSGESUB, \
164 "Function call \"" #func "\" failed:" ); \
165 return GEOCENTRICGEODETICTESTC_ESUB; \
166} \
167while (0)
168
169int
170main( int argc, char **argv )
171{
172 int arg; /* command-line argument counter */
173 BOOLEAN xyz = 0; /* whether -x, -y, or -z options were given */
174 INT4 verbosity = 0; /* verbosity level */
175 REAL8 x_1 = 0.0, x_2 = 0.0, dx; /* range and increment in x */
176 REAL8 y_1 = 0.0, y_2 = 0.0, dy; /* range and increment in y */
177 REAL8 z_1 = 0.0, z_2 = 0.0, dz; /* range and increment in z */
178 INT4 nx = 1, ny = 1, nz = 1; /* number of steps in each direction */
179 INT4 i, j, k; /* index in each direction */
180 REAL8 x, y, z, ddx, ddy, ddz; /* position and error in each direction */
181 REAL8 ddr, ddmax = 0.0; /* overall and maximum position error */
182 static LALStatus stat; /* status structure */
183 EarthPosition earth; /* terrestrial coordinates */
184
185
186 /*******************************************************************
187 * PARSE ARGUMENTS (arg stores the current position) *
188 *******************************************************************/
189
190 arg = 1;
191 while ( arg < argc ) {
192
193 /* Parse range options. */
194 if ( !strcmp( argv[arg], "-x" ) ) {
195 if ( argc > arg + 3 ) {
196 arg++;
197 xyz = 1;
198 x_1 = atof( argv[arg++] );
199 x_2 = atof( argv[arg++] );
200 nx = atoi( argv[arg++] );
201 } else {
203 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
204 LALPrintError( USAGE, *argv );
206 }
207 } else if ( !strcmp( argv[arg], "-y" ) ) {
208 if ( argc > arg + 3 ) {
209 arg++;
210 xyz = 1;
211 y_1 = atof( argv[arg++] );
212 y_2 = atof( argv[arg++] );
213 ny = atoi( argv[arg++] );
214 } else {
216 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
217 LALPrintError( USAGE, *argv );
219 }
220 } else if ( !strcmp( argv[arg], "-z" ) ) {
221 if ( argc > arg + 3 ) {
222 arg++;
223 xyz = 1;
224 z_1 = atof( argv[arg++] );
225 z_2 = atof( argv[arg++] );
226 nz = atoi( argv[arg++] );
227 } else {
229 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
230 LALPrintError( USAGE, *argv );
232 }
233 }
234
235 /* Parse verbosity option. */
236 else if ( !strcmp( argv[arg], "-v" ) ) {
237 if ( argc > arg + 1 ) {
238 arg++;
239 verbosity = atoi( argv[arg++] );
240 } else {
242 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
243 LALPrintError( USAGE, *argv );
245 }
246 }
247
248 /* Parse debug level option. */
249 else if ( !strcmp( argv[arg], "-d" ) ) {
250 if ( argc > arg + 1 ) {
251 arg++;
252 } else {
254 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
255 LALPrintError( USAGE, *argv );
257 }
258 }
259
260 /* Check for unrecognized options. */
261 else if ( argv[arg][0] == '-' ) {
263 GEOCENTRICGEODETICTESTC_MSGEARG, 0 );
264 LALPrintError( USAGE, *argv );
266 }
267 } /* End of argument parsing loop. */
268
269 /*******************************************************************
270 * SET PARAMETERS *
271 *******************************************************************/
272
273 /* If none of -x, -y, -z were given, generate a random position. */
274 if ( !xyz ) {
275 REAL4 lon, coslat, sinlat, rad; /* polar coordinates */
276 RandomParams *rparams = NULL; /* pseudorandom sequence parameters */
277 rparams = XLALCreateRandomParams( 0 );
278 lon = LAL_TWOPI * XLALUniformDeviate( rparams );
279 sinlat = XLALUniformDeviate( rparams );
280 coslat = sqrt( 1.0 - sinlat*sinlat );
281 rad = 1.5*XLALUniformDeviate( rparams ) + 0.5;
282 x_1 = x_2 = rad*coslat*cos( lon );
283 y_1 = y_2 = rad*coslat*sin( lon );
284 z_1 = z_2 = rad*sinlat;
285 XLALDestroyRandomParams( rparams );
286 }
287
288 /* Compute stepsizes. */
289 dx = dy = dz = 0.0;
290 if ( nx > 1 )
291 dx = ( x_2 - x_1 )/( nx - 1.0 );
292 if ( ny > 1 )
293 dy = ( y_2 - y_1 )/( ny - 1.0 );
294 if ( nz > 1 )
295 dz = ( z_2 - z_1 )/( nz - 1.0 );
296
297 /*******************************************************************
298 * PERFORM TEST *
299 *******************************************************************/
300
301 /* Loop over each direction. */
302 for ( i = 0; i < nx; i++ ) {
303 x = LAL_REARTH_SI*( x_1 + i*dx );
304 for ( j = 0; j < ny; j++ ) {
305 y = LAL_REARTH_SI*( y_1 + j*dy );
306 for ( k = 0; k < nz; k++ ) {
307 z = LAL_REARTH_SI*( z_1 + k*dz );
308
309 /* Do transformation. */
310 earth.x = x;
311 earth.y = y;
312 earth.z = z;
313 SUB( LALGeocentricToGeodetic( &stat, &earth ), &stat );
314 SUB( LALGeodeticToGeocentric( &stat, &earth ), &stat );
315
316 /* Compute difference. */
317 ddx = x - earth.x;
318 ddy = y - earth.y;
319 ddz = z - earth.z;
320 ddr = sqrt( ddx*ddx + ddy*ddy + ddz*ddz );
321 if ( ddr > ddmax )
322 ddmax = ddr;
323 if ( verbosity == 1 )
324 fprintf( stdout, "%+23.16e\n", ddr );
325 else if ( verbosity == 2 )
326 fprintf( stdout, "%+23.16e %+23.16e\n", earth.elevation, ddr );
327 else if ( verbosity == 3 )
328 fprintf( stdout, "%+23.16e %+23.16e %+23.16e %+23.16e\n",
329 x, y, z, ddr );
330 else if ( verbosity == 4 )
331 fprintf( stdout, "%+23.16e %+23.16e %+23.16e"
332 " %+23.16e %+23.16e %+23.16e %+23.16e\n", x, y, z,
334 LAL_180_PI*earth.geodetic.longitude, ddr );
335 }
336 }
337 }
338
339 /* Print maximum. */
340 fprintf( stdout, "Maximum error: %.16em\n", ddmax );
341 if ( !xyz && ddmax > 1.0e-6 ) {
343 GEOCENTRICGEODETICTESTC_MSGETEST, 0 );
345 }
347 INFO( GEOCENTRICGEODETICTESTC_MSGENORM );
349}
350/** \endcond */
#define GEOCENTRICGEODETICTESTC_ENORM
Normal exit.
#define GEOCENTRICGEODETICTESTC_EARG
Error parsing arguments.
#define GEOCENTRICGEODETICTESTC_ETEST
Test case failed.
#define USAGE
Definition: GzipTest.c:27
void LALCheckMemoryLeaks(void)
Definition: LALMalloc.c:784
#define SUB(func, statusptr)
#define ERROR(code, msg, statement)
#define INFO(statement)
#define fprintf
int main(int argc, char *argv[])
Definition: cache.c:25
#define LAL_180_PI
pi/180 is the number of radians in one degree
Definition: LALConstants.h:187
#define LAL_REARTH_SI
Earth equatorial radius, m.
Definition: LALConstants.h:416
#define LAL_TWOPI
2*pi is circumference of a circle divided by its radius
Definition: LALConstants.h:180
unsigned char BOOLEAN
Boolean logical type, see Headers LAL(Atomic)Datatypes.h for more details.
double REAL8
Double precision real floating-point number (8 bytes).
int32_t INT4
Four-byte signed integer.
float REAL4
Single precision real floating-point number (4 bytes).
int LALPrintError(const char *fmt,...)
Definition: LALError.c:46
RandomParams * XLALCreateRandomParams(INT4 seed)
Definition: Random.c:102
REAL4 XLALUniformDeviate(RandomParams *params)
Definition: Random.c:146
void XLALDestroyRandomParams(RandomParams *params)
Definition: Random.c:140
void LALGeocentricToGeodetic(LALStatus *, EarthPosition *location)
void LALGeodeticToGeocentric(LALStatus *, EarthPosition *location)
This structure stores the location of a point on (or near) the surface of the Earth in both geodetic ...
REAL8 elevation
The vertical distance of the point above the reference ellipsoid, in metres.
REAL8 z
The Earth-fixed geocentric Cartesian coordinates of the point, in metres.
SkyPosition geodetic
The geographic coordinates of the upward vertical direction from the point; that is,...
LAL status structure, see The LALStatus structure for more details.
Definition: LALDatatypes.h:947
This structure contains the parameters necessary for generating the current sequence of random number...
Definition: Random.h:86
REAL8 longitude
The longitudinal coordinate (in radians), as defined above.
REAL8 latitude
The latitudinal coordinate (in radians), as defined above.