check_coord_system.c
Go to the documentation of this file.
00001 #include <math.h>
00002 
00003 #include <check.h>
00004 #include "check_utils.h"
00005 
00006 #include <coord_system.h>
00007 #include <constants.h>
00008 
00009 /* Maximum allowable error in quantities with units of length (in meters). */
00010 #define MAX_DIST_ERROR_M 1e-6
00011 /* Maximum allowable error in quantities with units of angle (in sec of arc).
00012  * 1 second of arc on the equator is ~31 meters. */
00013 #define MAX_ANGLE_ERROR_SEC 1e-7
00014 #define MAX_ANGLE_ERROR_RAD (MAX_ANGLE_ERROR_SEC*(D2R/3600.0))
00015 
00016 /* Semi-major axis. */
00017 #define EARTH_A 6378137.0
00018 /* Semi-minor axis. */
00019 #define EARTH_B 6356752.31424517929553985595703125
00020 
00021 #define NUM_COORDS 10
00022 const double llhs[NUM_COORDS][3] = {
00023   {0, 0, 0},        /* On the Equator and Prime Meridian. */
00024   {0, 180*D2R, 0},  /* On the Equator. */
00025   {0, 90*D2R, 0},   /* On the Equator. */
00026   {0, -90*D2R, 0},  /* On the Equator. */
00027   {90*D2R, 0, 0},   /* North pole. */
00028   {-90*D2R, 0, 0},  /* South pole. */
00029   {90*D2R, 0, 22},  /* 22m above the north pole. */
00030   {-90*D2R, 0, 22}, /* 22m above the south pole. */
00031   {0, 0, 22},       /* 22m above the Equator and Prime Meridian. */
00032   {0, 180*D2R, 22}, /* 22m above the Equator. */
00033 };
00034 const double ecefs[NUM_COORDS][3] = {
00035   {EARTH_A, 0, 0},
00036   {-EARTH_A, 0, 0},
00037   {0, EARTH_A, 0},
00038   {0, -EARTH_A, 0},
00039   {0, 0, EARTH_B},
00040   {0, 0, -EARTH_B},
00041   {0, 0, (EARTH_B+22)},
00042   {0, 0, -(EARTH_B+22)},
00043   {(22+EARTH_A), 0, 0},
00044   {-(22+EARTH_A), 0, 0},
00045 };
00046 
00047 START_TEST(test_llhdeg2rad) 
00048 {
00049   double rads[3];
00050 
00051   llhdeg2rad(llhs[0], rads);
00052   
00053   //We expect the zero-point to be the same in degrees and radians
00054   for (int n=0; n<3; n++) {
00055     fail_unless(rads[n] == 0);
00056   }
00057 
00058   //We expect an arbitrary point to convert correctly
00059   double swiftHomeLLH[3] = {37.779804,-122.391751, 60.0};
00060   llhdeg2rad(swiftHomeLLH, rads);
00061                              
00062   fail_unless(fabs(rads[0] - 0.659381970558) < MAX_ANGLE_ERROR_RAD);
00063   fail_unless(fabs(rads[1] + 2.136139032231) < MAX_ANGLE_ERROR_RAD);
00064   fail_unless(rads[2] == swiftHomeLLH[2]);
00065 
00066 
00067 }
00068 END_TEST
00069 
00070 START_TEST(test_wgsllh2ecef)
00071 {
00072   double ecef[3];
00073 
00074   wgsllh2ecef(llhs[_i], ecef);
00075 
00076   for (int n=0; n<3; n++) {
00077     fail_unless(!isnan(ecef[n]), "NaN in output from wgsllh2ecef.");
00078     double err = fabs(ecef[n] - ecefs[_i][n]);
00079     fail_unless(err < MAX_DIST_ERROR_M,
00080       "Conversion from WGS84 LLH to ECEF has >1e-6m error:\n"
00081       "LLH: %f, %f, %f\n"
00082       "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
00083       llhs[_i][0]*R2D, llhs[_i][1]*R2D, llhs[_i][2],
00084       (ecef[0] - ecefs[_i][0])*1e3,
00085       (ecef[1] - ecefs[_i][1])*1e3,
00086       (ecef[2] - ecefs[_i][2])*1e3
00087     );
00088   }
00089 }
00090 END_TEST
00091 
00092 START_TEST(test_wgsecef2llh)
00093 {
00094   double llh[3];
00095 
00096   wgsecef2llh(ecefs[_i], llh);
00097 
00098   for (int n=0; n<3; n++) {
00099     fail_unless(!isnan(llh[n]), "NaN in output from wgsecef2llh.");
00100   }
00101 
00102   double lat_err = fabs(llh[0] - llhs[_i][0]);
00103   double lon_err = fabs(llh[1] - llhs[_i][1]);
00104   double hgt_err = fabs(llh[2] - llhs[_i][2]);
00105   fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
00106               (lon_err < MAX_ANGLE_ERROR_RAD) &&
00107               (hgt_err < MAX_DIST_ERROR_M),
00108     "Conversion from WGS84 ECEF to LLH has >1e-6 {rad, m} error:\n"
00109     "ECEF: %f, %f, %f\n"
00110     "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
00111     ecefs[_i][0], ecefs[_i][1], ecefs[_i][2],
00112     (llh[0] - llhs[_i][0])*(R2D*3600),
00113     (llh[1] - llhs[_i][1])*(R2D*3600),
00114     (llh[2] - llhs[_i][2])*1e3
00115   );
00116 }
00117 END_TEST
00118 
00119 START_TEST(test_wgsllh2ecef2llh)
00120 {
00121   double ecef[3];
00122   double llh[3];
00123 
00124   wgsllh2ecef(llhs[_i], ecef);
00125   wgsecef2llh(ecef, llh);
00126 
00127   for (int n=0; n<3; n++) {
00128     fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
00129     fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
00130   }
00131 
00132   double lat_err = fabs(llh[0] - llhs[_i][0]);
00133   double lon_err = fabs(llh[1] - llhs[_i][1]);
00134   double hgt_err = fabs(llh[2] - llhs[_i][2]);
00135   fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
00136               (lon_err < MAX_ANGLE_ERROR_RAD) &&
00137               (hgt_err < MAX_DIST_ERROR_M),
00138     "Converting WGS84 LLH to ECEF and back again does not return the "
00139     "original values.\n"
00140     "Initial LLH: %f, %f, %f\n"
00141     "ECEF: %f, %f, %f\n"
00142     "Final LLH: %f, %f, %f\n"
00143     "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
00144     R2D*llhs[_i][0], R2D*llhs[_i][1], llhs[_i][2],
00145     ecef[0], ecef[1], ecef[2],
00146     R2D*llh[0], R2D*llh[1], llh[2],
00147     (llh[0] - llhs[_i][0])*(R2D*3600),
00148     (llh[1] - llhs[_i][1])*(R2D*3600),
00149     (llh[2] - llhs[_i][2])*1e3
00150   );
00151 }
00152 END_TEST
00153 
00154 START_TEST(test_wgsecef2llh2ecef)
00155 {
00156   double llh[3];
00157   double ecef[3];
00158 
00159   wgsecef2llh(ecefs[_i], llh);
00160   wgsllh2ecef(llh, ecef);
00161 
00162   for (int n=0; n<3; n++) {
00163     fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
00164     fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
00165   }
00166 
00167   for (int n=0; n<3; n++) {
00168     double err = fabs(ecef[n] - ecefs[_i][n]);
00169     fail_unless(err < MAX_DIST_ERROR_M,
00170       "Converting WGS84 ECEF to LLH and back again does not return the "
00171       "original values.\n"
00172       "Initial ECEF: %f, %f, %f\n"
00173       "LLH: %f, %f, %f\n"
00174       "Final ECEF: %f, %f, %f\n"
00175       "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
00176       ecefs[_i][0], ecefs[_i][1], ecefs[_i][2],
00177       R2D*llh[0], R2D*llh[1], llh[2],
00178       ecef[0], ecef[1], ecef[2],
00179       (ecef[0] - ecefs[_i][0])*1e3,
00180       (ecef[1] - ecefs[_i][1])*1e3,
00181       (ecef[2] - ecefs[_i][2])*1e3
00182     );
00183   }
00184 }
00185 END_TEST
00186 
00187 START_TEST(test_random_wgsllh2ecef2llh)
00188 {
00189   double ecef[3];
00190   double llh_init[3];
00191   double llh[3];
00192 
00193   seed_rng();
00194 
00195   llh_init[0] = D2R*frand(-90, 90);
00196   llh_init[1] = D2R*frand(-180, 180);
00197   llh_init[2] = frand(-EARTH_A, 4*EARTH_A);
00198 
00199   wgsllh2ecef(llh_init, ecef);
00200   wgsecef2llh(ecef, llh);
00201 
00202   for (int n=0; n<3; n++) {
00203     fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
00204     fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
00205   }
00206 
00207   double lat_err = fabs(llh[0] - llh_init[0]);
00208   double lon_err = fabs(llh[1] - llh_init[1]);
00209   double hgt_err = fabs(llh[2] - llh_init[2]);
00210   fail_unless((lat_err < MAX_ANGLE_ERROR_RAD) &&
00211               (lon_err < MAX_ANGLE_ERROR_RAD) &&
00212               (hgt_err < MAX_DIST_ERROR_M),
00213     "Converting random WGS84 LLH to ECEF and back again does not return the "
00214     "original values.\n"
00215     "Initial LLH: %f, %f, %f\n"
00216     "ECEF: %f, %f, %f\n"
00217     "Final LLH: %f, %f, %f\n"
00218     "Lat error (arc sec): %g\nLon error (arc sec): %g\nH error (mm): %g",
00219     R2D*llh_init[0], R2D*llh_init[1], llh_init[2],
00220     ecef[0], ecef[1], ecef[2],
00221     R2D*llh[0], R2D*llh[1], llh[2],
00222     (llh[0] - llh_init[0])*(R2D*3600),
00223     (llh[1] - llh_init[1])*(R2D*3600),
00224     (llh[2] - llh_init[2])*1e3
00225   );
00226 
00227   fail_unless((R2D*llh[0] >= -90) && (R2D*llh[0] <= 90),
00228       "Converting random WGS86 ECEF gives latitude out of bounds.\n"
00229       "ECEF: %f, %f, %f\n"
00230       "LLH: %f, %f, %f\n",
00231       ecef[0], ecef[1], ecef[2],
00232       R2D*llh[0], R2D*llh[1], llh[2]
00233   );
00234 
00235   fail_unless((R2D*llh[1] >= -180) && (R2D*llh[1] <= 180),
00236       "Converting random WGS86 ECEF gives longitude out of bounds.\n"
00237       "ECEF: %f, %f, %f\n"
00238       "LLH: %f, %f, %f\n",
00239       ecef[0], ecef[1], ecef[2],
00240       R2D*llh[0], R2D*llh[1], llh[2]
00241   );
00242 
00243   fail_unless(llh[2] > -EARTH_A,
00244       "Converting random WGS86 ECEF gives height out of bounds.\n"
00245       "ECEF: %f, %f, %f\n"
00246       "LLH: %f, %f, %f\n",
00247       ecef[0], ecef[1], ecef[2],
00248       R2D*llh[0], R2D*llh[1], llh[2]
00249   );
00250 }
00251 END_TEST
00252 
00253 START_TEST(test_random_wgsecef2llh2ecef)
00254 {
00255   double ecef_init[3];
00256   double llh[3];
00257   double ecef[3];
00258 
00259   seed_rng();
00260 
00261   ecef_init[0] = frand(-4*EARTH_A, 4*EARTH_A);
00262   ecef_init[1] = frand(-4*EARTH_A, 4*EARTH_A);
00263   ecef_init[2] = frand(-4*EARTH_A, 4*EARTH_A);
00264 
00265   wgsecef2llh(ecef_init, llh);
00266   wgsllh2ecef(llh, ecef);
00267 
00268   for (int n=0; n<3; n++) {
00269     fail_unless(!isnan(llh[n]), "NaN in LLH output from conversion.");
00270     fail_unless(!isnan(ecef[n]), "NaN in ECEF output from conversion.");
00271   }
00272 
00273   for (int n=0; n<3; n++) {
00274     double err = fabs(ecef[n] - ecef_init[n]);
00275     fail_unless(err < MAX_DIST_ERROR_M,
00276       "Converting random WGS84 ECEF to LLH and back again does not return the "
00277       "original values.\n"
00278       "Initial ECEF: %f, %f, %f\n"
00279       "LLH: %f, %f, %f\n"
00280       "Final ECEF: %f, %f, %f\n"
00281       "X error (mm): %g\nY error (mm): %g\nZ error (mm): %g",
00282       ecef_init[0], ecef_init[1], ecef_init[2],
00283       R2D*llh[0], R2D*llh[1], llh[2],
00284       ecef[0], ecef[1], ecef[2],
00285       (ecef[0] - ecef_init[0])*1e3,
00286       (ecef[1] - ecef_init[1])*1e3,
00287       (ecef[2] - ecef_init[2])*1e3
00288     );
00289   }
00290 
00291   fail_unless((R2D*llh[0] >= -90) && (R2D*llh[0] <= 90),
00292       "Converting random WGS86 ECEF gives latitude out of bounds.\n"
00293       "Initial ECEF: %f, %f, %f\n"
00294       "LLH: %f, %f, %f\n",
00295       ecef_init[0], ecef_init[1], ecef_init[2],
00296       R2D*llh[0], R2D*llh[1], llh[2]
00297   );
00298 
00299   fail_unless((R2D*llh[1] >= -180) && (R2D*llh[1] <= 180),
00300       "Converting random WGS86 ECEF gives longitude out of bounds.\n"
00301       "Initial ECEF: %f, %f, %f\n"
00302       "LLH: %f, %f, %f\n",
00303       ecef_init[0], ecef_init[1], ecef_init[2],
00304       R2D*llh[0], R2D*llh[1], llh[2]
00305   );
00306 
00307   fail_unless(llh[2] > -EARTH_A,
00308       "Converting random WGS86 ECEF gives height out of bounds.\n"
00309       "Initial ECEF: %f, %f, %f\n"
00310       "LLH: %f, %f, %f\n",
00311       ecef_init[0], ecef_init[1], ecef_init[2],
00312       R2D*llh[0], R2D*llh[1], llh[2]
00313   );
00314 }
00315 END_TEST
00316 
00317 /* Check simply that passing the ECEF position the same as the
00318  * reference position returns (0, 0, 0) in NED frame */
00319 START_TEST(test_random_wgsecef2ned_d_0) {
00320   s32 i, j;
00321   double ned[3];
00322 
00323   seed_rng();
00324   for (i = 0; i < 222; i++) {
00325     const double ecef[3] = {frand(-1e8, 1e8),
00326                             frand(-1e8, 1e8),
00327                             frand(-1e8, 1e8)};
00328     wgsecef2ned_d(ecef, ecef, ned);
00329     for (j = 0; j < 3; j++)
00330       fail_unless(fabs(ned[j]) < 1e-8,
00331                   "NED vector to reference ECEF point "
00332                   "has nonzero element %d: %lf\n"
00333                   "(point was <%lf %lf %lf>)\n",
00334                   ned[j], ecef[0], ecef[1], ecef[2]);
00335   }
00336 }
00337 END_TEST
00338 
00339 Suite* coord_system_suite(void)
00340 {
00341   Suite *s = suite_create("Coordinate systems");
00342 
00343   /* Core test case */
00344   TCase *tc_core = tcase_create("Core");
00345   tcase_add_test(tc_core, test_llhdeg2rad);  
00346   tcase_add_loop_test(tc_core, test_wgsllh2ecef, 0, NUM_COORDS);
00347   tcase_add_loop_test(tc_core, test_wgsecef2llh, 0, NUM_COORDS);
00348   tcase_add_loop_test(tc_core, test_wgsllh2ecef2llh, 0, NUM_COORDS);
00349   tcase_add_loop_test(tc_core, test_wgsecef2llh2ecef, 0, NUM_COORDS);
00350   suite_add_tcase(s, tc_core);
00351 
00352   TCase *tc_random = tcase_create("Random");
00353   tcase_add_loop_test(tc_random, test_random_wgsllh2ecef2llh, 0, 22);
00354   tcase_add_loop_test(tc_random, test_random_wgsecef2llh2ecef, 0, 22);
00355   tcase_add_loop_test(tc_random, test_random_wgsecef2ned_d_0, 0, 22);
00356   suite_add_tcase(s, tc_random);
00357 
00358   return s;
00359 }
00360 


swiftnav
Author(s):
autogenerated on Sat Jun 8 2019 18:55:28