You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

370 lines
9.9KB

  1. /*
  2. * gpsdclient.c -- support functions for GPSD clients
  3. *
  4. * This file is Copyright (c) 2010-2018 by the GPSD project
  5. * SPDX-License-Identifier: BSD-2-clause
  6. */
  7. #include "gpsd_config.h" /* must be before all includes */
  8. #include <assert.h>
  9. #include <math.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <strings.h> /* for strcasecmp() */
  14. #include "gps.h"
  15. #include "gpsdclient.h"
  16. #include "os_compat.h"
  17. static struct exportmethod_t exportmethods[] = {
  18. #if defined(DBUS_EXPORT_ENABLE)
  19. {"dbus", GPSD_DBUS_EXPORT, "DBUS broadcast"},
  20. #endif /* defined(DBUS_EXPORT_ENABLE) */
  21. #ifdef SHM_EXPORT_ENABLE
  22. {"shm", GPSD_SHARED_MEMORY, "shared memory"},
  23. #endif /* SOCKET_EXPORT_ENABLE */
  24. #ifdef SOCKET_EXPORT_ENABLE
  25. {"sockets", NULL, "JSON via sockets"},
  26. #endif /* SOCKET_EXPORT_ENABLE */
  27. };
  28. /* convert value of double degrees to a buffer.
  29. * add suffix_pos or suffix_neg depending on sign.
  30. * buffer should be at least 20 bytes.
  31. * Return a pointer to the buffer.
  32. *
  33. * deg_str_type:
  34. * deg_dd : return DD.ddddddd[suffix]
  35. * deg_ddmm : return DD MM.mmmmmm'[suffix]
  36. * deg_ddmmss : return DD MM' SS.sssss"[suffix]
  37. *
  38. * returns 'n/a' for 360 < f or -360 > f
  39. *
  40. * NOTE: 360.0 is rolled over to 0.0
  41. *
  42. * for cm level accuracy, at sea level, we need degrees
  43. * to 7+ decimal places
  44. * Ref: https://en.wikipedia.org/wiki/Decimal_degrees
  45. *
  46. */
  47. char *deg_to_str2(enum deg_str_type type, double f,
  48. char *buf, unsigned int buf_size,
  49. const char *suffix_pos, const char *suffix_neg)
  50. {
  51. int dsec, sec, deg, min;
  52. double fdsec, fsec, fdeg, fmin;
  53. const char *suffix = "";
  54. if (20 > buf_size) {
  55. (void)strlcpy(buf, "Err", buf_size);
  56. return buf;
  57. }
  58. if (!isfinite(f) || 360.0 < fabs(f)) {
  59. (void)strlcpy(buf, "n/a", buf_size);
  60. return buf;
  61. }
  62. /* suffix? */
  63. if (0.0 > f) {
  64. f = -f;
  65. if (NULL != suffix_neg) {
  66. suffix = suffix_neg;
  67. }
  68. } else if (NULL != suffix_pos) {
  69. suffix = suffix_pos;
  70. }
  71. /* add rounding quanta */
  72. /* IEEE 754 wants round to nearest even.
  73. * We cheat and just round to nearest.
  74. * Intel trying to kill off round to nearest even. */
  75. switch (type) {
  76. default:
  77. /* huh? */
  78. type = deg_dd;
  79. /* FALLTHROUGH */
  80. case deg_dd:
  81. /* DD.dddddddd */
  82. f += 0.5 * 1e-8; /* round up */
  83. break;
  84. case deg_ddmm:
  85. /* DD MM.mmmmmm */
  86. f += (0.5 * 1e-6) / 60; /* round up */
  87. break;
  88. case deg_ddmmss:
  89. f += (0.5 * 1e-5) / 3600; /* round up */
  90. break;
  91. }
  92. fmin = modf(f, &fdeg);
  93. deg = (int)fdeg;
  94. if (360 == deg) {
  95. /* fix round-up roll-over */
  96. deg = 0;
  97. fmin = 0.0;
  98. }
  99. if (deg_dd == type) {
  100. /* DD.dddddddd */
  101. long frac_deg = (long)(fmin * 100000000.0);
  102. /* cm level accuracy requires the %08ld */
  103. (void)snprintf(buf, buf_size, "%3d.%08ld%s", deg, frac_deg, suffix);
  104. return buf;
  105. }
  106. fsec = modf(fmin * 60, &fmin);
  107. min = (int)fmin;
  108. if (deg_ddmm == type) {
  109. /* DD MM.mmmmmm */
  110. sec = (int)(fsec * 1000000.0);
  111. (void)snprintf(buf, buf_size, "%3d %02d.%06d'%s", deg, min, sec,
  112. suffix);
  113. return buf;
  114. }
  115. /* else DD MM SS.sss */
  116. fdsec = modf(fsec * 60.0, &fsec);
  117. sec = (int)fsec;
  118. dsec = (int)(fdsec * 100000.0);
  119. (void)snprintf(buf, buf_size, "%3d %02d' %02d.%05d\"%s", deg, min, sec,
  120. dsec, suffix);
  121. return buf;
  122. }
  123. /* convert absolute value of double degrees to a static string.
  124. * Return a pointer to the static string.
  125. * WARNING: Not thread safe.
  126. *
  127. * deg_str_type:
  128. * deg_dd : return DD.ddddddd
  129. * deg_ddmm : return DD MM.mmmmmm'
  130. * deg_ddmmss : return DD MM' SS.sssss"
  131. *
  132. * returns 'n/a' for 360 < f
  133. *
  134. * NOTE: 360.0 is rolled over to 0.0
  135. *
  136. * for cm level accuracy, at sea level, we need degrees
  137. * to 7+ decimal places
  138. * Ref: https://en.wikipedia.org/wiki/Decimal_degrees
  139. *
  140. */
  141. char *deg_to_str(enum deg_str_type type, double f)
  142. {
  143. static char buf[20];
  144. return deg_to_str2(type, f, buf, sizeof(buf), "", "");
  145. }
  146. /*
  147. * check the environment to determine proper GPS units
  148. *
  149. * clients should only call this if no user preference is specified on
  150. * the command line or via X resources.
  151. *
  152. * return imperial - Use miles/feet
  153. * nautical - Use knots/feet
  154. * metric - Use km/meters
  155. * unspecified - use compiled default
  156. *
  157. * In order check these environment vars:
  158. * GPSD_UNITS one of:
  159. * imperial = miles/feet
  160. * nautical = knots/feet
  161. * metric = km/meters
  162. * LC_MEASUREMENT
  163. * en_US = miles/feet
  164. * C = miles/feet
  165. * POSIX = miles/feet
  166. * [other] = km/meters
  167. * LANG
  168. * en_US = miles/feet
  169. * C = miles/feet
  170. * POSIX = miles/feet
  171. * [other] = km/meters
  172. *
  173. * if none found then return compiled in default
  174. */
  175. enum unit gpsd_units(void)
  176. {
  177. char *envu = NULL;
  178. if ((envu = getenv("GPSD_UNITS")) != NULL && *envu != '\0') {
  179. if (0 == strcasecmp(envu, "imperial")) {
  180. return imperial;
  181. }
  182. if (0 == strcasecmp(envu, "nautical")) {
  183. return nautical;
  184. }
  185. if (0 == strcasecmp(envu, "metric")) {
  186. return metric;
  187. }
  188. /* unrecognized, ignore it */
  189. }
  190. if (((envu = getenv("LC_MEASUREMENT")) != NULL && *envu != '\0')
  191. || ((envu = getenv("LANG")) != NULL && *envu != '\0')) {
  192. if (strncasecmp(envu, "en_US", 5) == 0
  193. || strcasecmp(envu, "C") == 0 || strcasecmp(envu, "POSIX") == 0) {
  194. return imperial;
  195. }
  196. /* Other, must be metric */
  197. return metric;
  198. }
  199. /* TODO: allow a compile time default here */
  200. return unspecified;
  201. }
  202. void gpsd_source_spec(const char *arg, struct fixsource_t *source)
  203. /* standard parsing of a GPS data source spec */
  204. {
  205. /* the casts attempt to head off a -Wwrite-strings warning */
  206. source->server = (char *)"localhost";
  207. source->port = (char *)DEFAULT_GPSD_PORT;
  208. source->device = NULL;
  209. if (arg != NULL) {
  210. char *colon1, *skipto, *rbrk;
  211. source->spec = (char *)arg;
  212. assert(source->spec != NULL);
  213. skipto = source->spec;
  214. if (*skipto == '[' && (rbrk = strchr(skipto, ']')) != NULL) {
  215. skipto = rbrk;
  216. }
  217. colon1 = strchr(skipto, ':');
  218. if (colon1 != NULL) {
  219. char *colon2;
  220. *colon1 = '\0';
  221. if (colon1 != source->spec) {
  222. source->server = source->spec;
  223. }
  224. source->port = colon1 + 1;
  225. colon2 = strchr(source->port, ':');
  226. if (colon2 != NULL) {
  227. *colon2 = '\0';
  228. source->device = colon2 + 1;
  229. }
  230. } else if (strchr(source->spec, '/') != NULL) {
  231. source->device = source->spec;
  232. } else {
  233. source->server = source->spec;
  234. }
  235. }
  236. if (*source->server == '[') {
  237. char *rbrk = strchr(source->server, ']');
  238. ++source->server;
  239. if (rbrk != NULL)
  240. *rbrk = '\0';
  241. }
  242. }
  243. /* lat/lon to Maidenhead */
  244. char *maidenhead(double lat, double lon)
  245. {
  246. /*
  247. * Specification at
  248. * https://en.wikipedia.org/wiki/Maidenhead_Locator_System
  249. *
  250. * There's a fair amount of slop in how Maidenhead converters operate
  251. * that can make it look like this one is wrong.
  252. *
  253. * 1. Many return caps for paces 5 and 6 when according to the spec
  254. * they should return smalls.
  255. *
  256. * 2. Some converters, including QGrid from which this code was originally
  257. * derived, add an 0.5 offset to the divided e and n just before it
  258. * is cast to integer and used for places 5 and 6. This appears to be
  259. * intended as a round-to-nearest hack (as opposed to the implicit
  260. * round down from the cast). If I'm reading the spec right it
  261. * is not correct to do this.
  262. */
  263. static char buf[7];
  264. int t1;
  265. /* longitude */
  266. if (179.99999 < lon) {
  267. /* force 180, just inside lon_sq 'R'
  268. * otherwise we get illegal 'S' */
  269. lon = 179.99999;
  270. }
  271. lon += 180.0;
  272. t1 = (int)(lon / 20);
  273. buf[0] = (char)t1 + 'A';
  274. if ('R' < buf[0]) {
  275. /* A to R */
  276. buf[0] = 'R';
  277. }
  278. lon -= (float)t1 * 20.0;
  279. t1 = (int)lon / 2;
  280. buf[2] = (char)t1 + '0';
  281. lon -= (float)t1 * 2;
  282. buf[4] = (char) ((int)(lon * 12.0)) + 'a';
  283. /* latitude */
  284. if (89.99999 < lat) {
  285. /* force 90 to just inside lat_sq 'R'
  286. * otherwise we get illegal 'S' */
  287. lat = 89.99999;
  288. }
  289. lat += 90.0;
  290. t1 = (int)(lat / 10.0);
  291. buf[1] = (char)t1 + 'A';
  292. if ('R' < buf[1]) {
  293. /* A to R, North Pole is R */
  294. buf[1] = 'R';
  295. }
  296. lat -= (float)t1 * 10.0;
  297. buf[3] = (char)lat + '0';
  298. lat -= (int)lat;
  299. lat *= 24; // convert to 24 division
  300. buf[5] = (char)((int)lat) + 'a';
  301. buf[6] = '\0';
  302. return buf;
  303. }
  304. #define NITEMS(x) (int)(sizeof(x)/sizeof(x[0])) /* from gpsd.h-tail */
  305. struct exportmethod_t *export_lookup(const char *name)
  306. /* Look up an available export method by name */
  307. {
  308. struct exportmethod_t *mp, *method = NULL;
  309. for (mp = exportmethods;
  310. mp < exportmethods + NITEMS(exportmethods);
  311. mp++)
  312. if (strcmp(mp->name, name) == 0)
  313. method = mp;
  314. return method;
  315. }
  316. void export_list(FILE *fp)
  317. /* list known export methods */
  318. {
  319. struct exportmethod_t *method;
  320. for (method = exportmethods;
  321. method < exportmethods + NITEMS(exportmethods);
  322. method++)
  323. (void)fprintf(fp, "%s: %s\n", method->name, method->description);
  324. }
  325. struct exportmethod_t *export_default(void)
  326. {
  327. return (NITEMS(exportmethods) > 0) ? &exportmethods[0] : NULL;
  328. }
  329. /* gpsdclient.c ends here */