test_geoid.c 10 KB

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