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
00010 #define MAX_DIST_ERROR_M 1e-6
00011
00012
00013 #define MAX_ANGLE_ERROR_SEC 1e-7
00014 #define MAX_ANGLE_ERROR_RAD (MAX_ANGLE_ERROR_SEC*(D2R/3600.0))
00015
00016
00017 #define EARTH_A 6378137.0
00018
00019 #define EARTH_B 6356752.31424517929553985595703125
00020
00021 #define NUM_COORDS 10
00022 const double llhs[NUM_COORDS][3] = {
00023 {0, 0, 0},
00024 {0, 180*D2R, 0},
00025 {0, 90*D2R, 0},
00026 {0, -90*D2R, 0},
00027 {90*D2R, 0, 0},
00028 {-90*D2R, 0, 0},
00029 {90*D2R, 0, 22},
00030 {-90*D2R, 0, 22},
00031 {0, 0, 22},
00032 {0, 180*D2R, 22},
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
00054 for (int n=0; n<3; n++) {
00055 fail_unless(rads[n] == 0);
00056 }
00057
00058
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
00318
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
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