test_geoid.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /* test driver for the ECEF to WGS84 conversions, and
  2. * magnetic variance, in geoid.c
  3. *
  4. * Keep in sync with test_clienthelpers.py
  5. *
  6. * This file is Copyright (c) 2010-2019 by the GPSD project
  7. * SPDX-License-Identifier: BSD-2-clause
  8. */
  9. /* first so the #defs work */
  10. #include "../gpsd_config.h"
  11. #include <math.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <stdarg.h>
  15. #include <unistd.h> /* for getopt() */
  16. #include "../gpsd.h"
  17. struct test3 {
  18. double lat;
  19. double lon;
  20. double separation;
  21. double variation;
  22. char *desc;
  23. };
  24. struct test3 tests3[] = {
  25. /* wgs84 separation, cm precision
  26. * online calculator:
  27. * https://geographiclib.sourceforge.io/cgi-bin/GeoidEval
  28. *
  29. * magnetic variation, hundredths of a degree precision.
  30. *
  31. * same tests as test_clienthelpers.py. Keep them in sync.
  32. */
  33. // Easter Island: EGM2008 -3.8178, EGM96 -4.9979, EGM84 -5.1408
  34. // wmm2015 2.90
  35. {27.1127, -109.3497, -31.29, 8.45, "Easter Island"},
  36. // Kalahari: EGM2008, 20.6560, EGM96, 20.8419, EGM84 23.4496
  37. // wmm2015 6.73
  38. {25.5920, 21.0937, 21.80, 3.35, "Kalahari Desert"},
  39. // Greenland: EGM2008 40.3981, EGM96 40.5912, EGM84 41.7056
  40. // wmm2015 28.26 AWEFUL!
  41. {71.7069, -42.6043, 40.11, -28.25, "Greenland"},
  42. // Kuk Swamp: EGM2008 62.8837, EGM96, 62.8002, EGM84, 64.5655
  43. // wmm2015 9.11
  44. // seems to be over a gravitational anomaly
  45. {5.7837, 144.3317, 62.34, 2.42, "Kuk Swamp PG"},
  46. // KBDN: EGM2008 -20.3509, EGM96 -19.8008, EGM84 -18.5562
  47. // wmm2015 14.61
  48. {44.094556, -121.200222, -21.95, 14.40, "Bend Airport, OR (KBDN)"},
  49. // PANC: EGM2008 7.6508, EGM96 7.8563, EGM84 8.0838
  50. // wmm2015 15.78
  51. {61.171333, -149.991164, 13.54, 15.52, "Anchorage Airport, AK (PANC)"},
  52. // KDEN: EGM2008 -18.1941, EGM96 -18.4209, EGM84 -15.9555
  53. // wmm2015 7.95
  54. {39.861666, -104.673166, -18.15, 7.84, "Denver Airport, CO (KDEN)"},
  55. // LHR: EGM2008 46.4499, EGM96 46.3061, EGM84 47.7620
  56. // wmm2015 0.17
  57. {51.46970, -0.45943, 46.26, -0.28, "London Heathrow Airport, UK (LHR)"},
  58. // SCPE: EGM2008 37.9592, EGM96 39.3400, EGM84 46.6604
  59. // wmm2015 -6.17
  60. {-22.92170, -68.158401, 35.46, -6.11,
  61. "San Pedro de Atacama Airport, CL (SCPE)"},
  62. // SIN: EGM2008 8.6453, EGM96 8.3503, EGM84 8.2509
  63. // wmm2015 0.22
  64. {1.350190, 103.994003, 7.51, 0.17, "Singapore Changi Airport, SG (SIN)"},
  65. // UURB: EGM2008 13.6322, EGM96 13.6448, EGM84 13.1280
  66. // wmm2015 11.41
  67. {55.617199, 38.06000, 13.22, 11.42, "Moscow Bykovo Airport, RU (UUBB)"},
  68. // SYD: EGM2008 13.0311, EGM96 13.3736, EGM84 13.3147
  69. // wmm2015 -4.28
  70. {33.946098, 151.177002, 13.59, -4.26, "Sydney Airport, AU (SYD)"},
  71. // Doyle: EGM2008 -23.3366, EGM96 -23.3278, EGM84 -21.1672
  72. // wmm2015 13.35
  73. {40, -120, -23.34, 13.35, "Near Doyle, CA"},
  74. // test calc at delta lat == 0
  75. // North Poll: EGM2008 14.8980, EGM96 13.6050, EGM84 13.0980
  76. // wmm2015 1.75
  77. {90, 0, 14.90, 1.75, "North Poll 0"},
  78. // wmm2015 3.75
  79. {90, 2, 14.90, 3.75, "North Poll 2"},
  80. // wmm2015 4.25
  81. {90, 2.5, 14.90, 4.25, "North Poll 2.5"},
  82. // wmm2015 1.75
  83. {90, 3, 14.90, 4.75, "North Poll 3"},
  84. // wmm2015 1.75
  85. {90, 5, 14.90, 6.75, "North Poll 5"},
  86. // wmm2015 -178.25
  87. {90, 180, 14.90, -178.25, "North Poll"},
  88. // Equator 0, EGM2008 17.2260, EGM96 17.1630, EGM84 18.3296
  89. // wmm2015 -4.84
  90. {0, 0, 17.23, -4.84, "Equator 0W"},
  91. // South Poll: EGM2008 -30.1500, EGM96 -29.5350, EGM84 -29.7120
  92. // wmm2015 -30.80
  93. {-90, 0, -30.15, -30.80, "South Poll"},
  94. // test calc at delta lon == 0
  95. // 2 0: EGM2008 17.1724, EGM96 16.8962, EGM84 17.3676
  96. // wmm2015 -4.17
  97. {2, 0, 18.42, -4.23, "2N 0W"},
  98. // 2.5 0: EGM2008 16.5384, EGM96 16.5991, EGM84 17.0643
  99. // wmm2015 -4.02
  100. {2.5, 0, 18.71, -4.08, "2.5N 0W"},
  101. // 3 0: EGM2008 16.7998, EGM96 16.6161, EGM84 16.7857
  102. // wmm2015 -3.87
  103. {3, 0, 19.01, -3.92, "3N 0W"},
  104. // 3.5 0: EGM2008 17.0646, EGM96 17.0821, EGM84 16.7220
  105. // wmm2015 -3.72
  106. {3.5, 0, 19.31, -3.77, "3.5N 0W"},
  107. // 5 0: EGM2008 20.1991, EGM96 20.4536, EGM84 20.3181
  108. // wmm2015 -3.31
  109. {5, 0, 20.20, -3.31, "5N 0W"},
  110. // test calc on diagonal
  111. // Equator 0, EGM2008 17.2260, EGM96 17.1630, EGM84 18.3296
  112. // wmm2015 -4.84
  113. // 2 2: EGM2008 16.2839, EGM96 16.1579, EGM84 17.5354
  114. // wmm2015 -3.53
  115. {2, 2, 18.39, -3.60, "2N 2E"},
  116. // 2.5 2.5: EGM2008 15.7918, EGM96 15.5314, EGM84 16.3230
  117. // wmm2015 -3.24
  118. {2.5, 2.5, 18.78, -3.30, "2.5N 2.5E"},
  119. // 3 3: EGM2008 15.2097, EGM96 15.0751, EGM84 14.6542
  120. // wmm2015 -2.95
  121. {3, 3, 19.20, -3.01, "3N 3E"},
  122. // 3.5 3.5: EGM2008 14.8706, EGM96 14.6668, EGM84 13.9592
  123. // wmm2015 -3.72
  124. {3.5, 3.5, 19.66, -2.73, "3.5N 3.5E"},
  125. // some 5x5 points, s/b exact EGM2008, +/- rounding
  126. // 5, 5: EGM2008 21.2609, EGM96 20.8917, EGM84 20.3509
  127. // wmm2015 -1.91
  128. {5, 5, 21.26, -1.91, "5, 5"},
  129. // -5, -5: EGM2008 17.1068, EGM96 16.8362, EGM84 17.5916
  130. // wmm2015 -9.03
  131. {-5, -5, 17.11, -9.03, "-5, -5"},
  132. // -5, 5: EGM2008 9.3988, EGM96 9.2399, EGM84 9.7948
  133. // wmm2015 -4.80
  134. {-5, 5, 9.40, -4.80, "-5, 5"},
  135. // 5, -5: EGM2008 25.7668, EGM96 25.6144, EGM84 25.1224
  136. // wmm2015 -4.90
  137. {5, -5, 25.77, -4.90, "5, 5"},
  138. // test data for some former corners in the code
  139. // 0, -78.452222: EGM2008 26.8978, EGM96 25.3457, EGM84 26.1507
  140. // wmm2015 -3.87
  141. {0, -78.452222, 15.98, -3.89, "Equatorial Sign Bolivia"},
  142. // 51.4778067, 0: EGM2008 45.8961, EGM96 45.7976, EGM84 47.2468
  143. // wmm2015 -0.10
  144. {51.4778067, 0, 45.46, -0.11, "Lawn Greenwich Observatory UK"},
  145. // 0, 180: EGM2008 21.2813, EGM96 21.1534, EGM84 21.7089
  146. // wmm2015 9.75
  147. {0, 180, 21.28, 9.75, "Far away from Google default"},
  148. {0, -180, 21.28, 9.75, "Away far from Google default"},
  149. };
  150. struct test4 {
  151. double lat1;
  152. double lon1;
  153. double lat2;
  154. double lon2;
  155. double distance; /* distance in meters */
  156. double ib; /* initial bearina, radiansg */
  157. double fb; /* final bearina, radiansg */
  158. };
  159. // tests for earth_distance_and_bearings()
  160. // Online distance calculators give different answers.
  161. // This seems to match the "Vincenty Formula"
  162. struct test4 tests4[] = {
  163. // zero
  164. {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
  165. // about 1 cm
  166. {0.00000009, 0.0, 0.0, 0.0, 0.0100, GPS_PI, GPS_PI},
  167. {0.0, 0.00000009, 0.0, 0.0, 0.0100, -(GPS_PI / 2), -(GPS_PI / 2)},
  168. {0.0, 0.0, 0.00000009, 0.0, 0.0100, -GPS_PI, -GPS_PI},
  169. {0.0, 0.0, 0.0, 0.00000009, 0.0100, GPS_PI / 2, GPS_PI / 2},
  170. // one degree
  171. {1.0, 0.0, 0.0, 0.0, 110574.3886, GPS_PI, GPS_PI},
  172. {0.0, 1.0, 0.0, 0.0, 111319.4908, -(GPS_PI / 2), -(GPS_PI / 2)},
  173. {0.0, 0.0, 1.0, 0.0, 110574.3886, -GPS_PI, -GPS_PI},
  174. {0.0, 0.0, 0.0, 1.0, 111319.4908, GPS_PI / 2, GPS_PI / 2},
  175. // 90 degrees
  176. {90.0, 0.0, 0.0, 0.0, 10001965.7293, GPS_PI, GPS_PI},
  177. {0.0, 90.0, 0.0, 0.0, 10018754.1714, -(GPS_PI / 2), -(GPS_PI / 2)},
  178. {0.0, 0.0, 90.0, 0.0, 10001965.7293, -GPS_PI, -GPS_PI},
  179. {0.0, 0.0, 0.0, 90.0, 10018754.1714, GPS_PI / 2, GPS_PI / 2},
  180. };
  181. int main(int argc, char **argv)
  182. {
  183. int verbose = 0;
  184. size_t i;
  185. int fail_count = 0;
  186. int option;
  187. while ((option = getopt(argc, argv, "h?vV")) != -1) {
  188. switch (option) {
  189. default:
  190. fail_count = 1;
  191. /* FALLTHROUGH */
  192. case '?':
  193. /* FALLTHROUGH */
  194. case 'h':
  195. (void)fputs("usage: test_gpsdclient [-v] [-V]\n", stderr);
  196. exit(fail_count);
  197. case 'V':
  198. (void)fprintf( stderr, "test_gpsdclient %s\n",
  199. VERSION);
  200. exit(EXIT_SUCCESS);
  201. case 'v':
  202. verbose = 1;
  203. break;
  204. }
  205. }
  206. if (0 < verbose)
  207. printf("wgs84_separation() tests\n");
  208. for (i = 0; i < (sizeof(tests3)/sizeof(struct test3)); i++) {
  209. double result;
  210. result = wgs84_separation(tests3[i].lat, tests3[i].lon);
  211. if (0 == isfinite(result) ||
  212. 0.01 < fabs(tests3[i].separation - result)) {
  213. printf("ERROR: %.2f %.2f separation was %.2f s/b %.2f, %s\n",
  214. tests3[i].lat, tests3[i].lon,
  215. result, tests3[i].separation, tests3[i].desc);
  216. fail_count++;
  217. } else if (0 < verbose) {
  218. printf("%.2f %.2f separation was %.2f s/b %.2f, %s\n",
  219. tests3[i].lat, tests3[i].lon,
  220. result, tests3[i].separation, tests3[i].desc);
  221. }
  222. }
  223. if (0 < verbose)
  224. printf("mag_var() tests\n");
  225. for (i = 0; i < (sizeof(tests3)/sizeof(struct test3)); i++) {
  226. double result;
  227. result = mag_var(tests3[i].lat, tests3[i].lon);
  228. if (0 == isfinite(result) ||
  229. 0.01 < fabs(tests3[i].variation - result)) {
  230. printf("ERROR: %.2f %.2f mag_var was %.2f s/b %.2f, %s\n",
  231. tests3[i].lat, tests3[i].lon,
  232. result, tests3[i].variation, tests3[i].desc);
  233. fail_count++;
  234. } else if (0 < verbose) {
  235. printf("%.2f %.2f mag_var was %.2f s/b %.2f, %s\n",
  236. tests3[i].lat, tests3[i].lon,
  237. result, tests3[i].variation, tests3[i].desc);
  238. }
  239. }
  240. for (i = 0; i < (sizeof(tests4)/sizeof(struct test4)); i++) {
  241. double result, ib, fb;
  242. result = earth_distance_and_bearings(tests4[i].lat1, tests4[i].lon1,
  243. tests4[i].lat2, tests4[i].lon2,
  244. &ib, &fb);
  245. if (0 == isfinite(result) ||
  246. 0.001 < fabs(tests4[i].distance - result)) {
  247. printf("ERROR earth_distance_and bearings(%.8f, %.8f, %.8f, "
  248. "%.8f,,) = %.4f, %.2f, %.2f s/b %.4f, %.2f, %.2f\n",
  249. tests4[i].lat1, tests4[i].lon1,
  250. tests4[i].lat2, tests4[i].lon2,
  251. result, ib, fb,
  252. tests4[i].distance, tests4[i].ib, tests4[i].fb);
  253. } else if (0 < verbose) {
  254. printf("earth_distance_and_bearings(%.8f, %.8f, %.8f, %.8f) = "
  255. "%.4f, %.2f, %.2f\n",
  256. tests4[i].lat1, tests4[i].lon1,
  257. tests4[i].lat2, tests4[i].lon2,
  258. tests4[i].distance, tests4[i].ib, tests4[i].fb);
  259. }
  260. }
  261. exit(fail_count);
  262. }