LAL  7.5.0.1-ec27e42
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 ) \
128 do \
129 if ( 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 } \
136 while (0)
137 
138 #define INFO( statement ) \
139 do \
140 if ( 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 } \
146 while (0)
147 
148 #define WARNING( statement ) \
149 do \
150 if ( 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 } \
156 while (0)
157 
158 #define SUB( func, statusptr ) \
159 do \
160 if ( (func), (statusptr)->statusCode ) \
161 { \
162  ERROR( GEOCENTRICGEODETICTESTC_ESUB, \
163  GEOCENTRICGEODETICTESTC_MSGESUB, \
164  "Function call \"" #func "\" failed:" ); \
165  return GEOCENTRICGEODETICTESTC_ESUB; \
166 } \
167 while (0)
168 
169 int
170 main( 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,
333  earth.elevation, LAL_180_PI*earth.geodetic.latitude,
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.