minmea.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. /*
  2. * Copyright © 2014 Kosma Moczek <kosma@cloudyourcar.com>
  3. * This work is free. You can redistribute it and/or modify it under the
  4. * terms of the Do What The Fuck You Want To Public License, Version 2,
  5. * as published by Sam Hocevar. See the COPYING file for more details.
  6. */
  7. #include "minmea.h"
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <stdarg.h>
  12. #define boolstr(s) ((s) ? "true" : "false")
  13. static int hex2int(char c)
  14. {
  15. if (c >= '0' && c <= '9')
  16. return c - '0';
  17. if (c >= 'A' && c <= 'F')
  18. return c - 'A' + 10;
  19. if (c >= 'a' && c <= 'f')
  20. return c - 'a' + 10;
  21. return -1;
  22. }
  23. bool minmea_check(const char *sentence)
  24. {
  25. uint8_t checksum = 0x00;
  26. // Sequence length is limited.
  27. if (strlen(sentence) > MINMEA_MAX_LENGTH + 3)
  28. return false;
  29. // A valid sentence starts with "$".
  30. if (*sentence++ != '$')
  31. return false;
  32. // The optional checksum is an XOR of all bytes between "$" and "*".
  33. while (*sentence && *sentence != '*' && isprint((unsigned char) *sentence))
  34. checksum ^= *sentence++;
  35. if (*sentence == '*') {
  36. // Extract checksum.
  37. sentence++;
  38. int upper = hex2int(*sentence++);
  39. if (upper == -1)
  40. return false;
  41. int lower = hex2int(*sentence++);
  42. if (lower == -1)
  43. return false;
  44. int expected = upper << 4 | lower;
  45. // Check for checksum mismatch.
  46. if (checksum != expected)
  47. return false;
  48. }
  49. // The only stuff allowed at this point is a newline.
  50. if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n"))
  51. return false;
  52. return true;
  53. }
  54. static inline bool minmea_isfield(char c) {
  55. return isprint((unsigned char) c) && c != ',' && c != '*';
  56. }
  57. bool minmea_scan(const char *sentence, const char *format, ...)
  58. {
  59. bool result = false;
  60. va_list ap;
  61. va_start(ap, format);
  62. const char *field = sentence;
  63. #define next_field() \
  64. do { \
  65. while (minmea_isfield(*sentence++)) {} \
  66. field = sentence; \
  67. } while (0)
  68. while (*format) {
  69. char type = *format++;
  70. switch (type) {
  71. case 'c': { // Single character field (char).
  72. char value = '\0';
  73. if (minmea_isfield(*field))
  74. value = *field;
  75. else
  76. value = '\0';
  77. *va_arg(ap, char *) = value;
  78. } break;
  79. case 'd': { // Single character direction field (int).
  80. int value = 0;
  81. if (minmea_isfield(*field)) {
  82. switch (*field) {
  83. case 'N':
  84. case 'E':
  85. value = 1;
  86. break;
  87. case 'S':
  88. case 'W':
  89. value = -1;
  90. break;
  91. default:
  92. goto end;
  93. }
  94. }
  95. *va_arg(ap, int *) = value;
  96. } break;
  97. case 'f': { // Fractional value with scale (int, int).
  98. int sign = 0;
  99. int value = -1;
  100. int scale = 0;
  101. while (minmea_isfield(*field)) {
  102. if (*field == '+' && !sign && value == -1) {
  103. sign = 1;
  104. } else if (*field == '-' && !sign && value == -1) {
  105. sign = -1;
  106. } else if (isdigit((unsigned char) *field)) {
  107. if (value == -1)
  108. value = 0;
  109. value = (10 * value) + (*field - '0');
  110. if (scale)
  111. scale *= 10;
  112. } else if (*field == '.' && scale == 0) {
  113. scale = 1;
  114. } else {
  115. goto end;
  116. }
  117. field++;
  118. }
  119. if ((sign || scale) && value == -1)
  120. goto end;
  121. if (value == -1) {
  122. value = 0;
  123. scale = 0;
  124. }
  125. if (sign)
  126. value *= sign;
  127. *va_arg(ap, int *) = value;
  128. *va_arg(ap, int *) = scale;
  129. } break;
  130. case 'i': { // Integer value, default 0 (int).
  131. int value;
  132. char *endptr;
  133. value = strtol(field, &endptr, 10);
  134. if (minmea_isfield(*endptr))
  135. goto end;
  136. *va_arg(ap, int *) = value;
  137. } break;
  138. case 's': { // String value (char *).
  139. char *buf = va_arg(ap, char *);
  140. while (minmea_isfield(*field))
  141. *buf++ = *field++;
  142. *buf = '\0';
  143. } break;
  144. case 't': { // NMEA talker+sequence identifier (char *).
  145. if (field[0] != '$')
  146. goto end;
  147. for (int i=0; i<5; i++)
  148. if (!minmea_isfield(field[1+i]))
  149. goto end;
  150. char *buf = va_arg(ap, char *);
  151. memcpy(buf, field+1, 5);
  152. buf[5] = '\0';
  153. } break;
  154. case 'D': { // Date (int, int, int), -1 if empty.
  155. struct minmea_date *date = va_arg(ap, struct minmea_date *);
  156. int d = -1, m = -1, y = -1;
  157. // Always six digits.
  158. for (int i=0; i<6; i++)
  159. if (!isdigit((unsigned char) field[i]))
  160. goto end_D;
  161. d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10);
  162. m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10);
  163. y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10);
  164. end_D:
  165. date->day = d;
  166. date->month = m;
  167. date->year = y;
  168. } break;
  169. case 'T': { // Time (int, int, int, int), -1 if empty.
  170. struct minmea_time *time = va_arg(ap, struct minmea_time *);
  171. int h = -1, i = -1, s = -1, u = -1;
  172. // Minimum required: integer time.
  173. for (int i=0; i<6; i++)
  174. if (!isdigit((unsigned char) field[i]))
  175. goto end_T;
  176. h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10);
  177. i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10);
  178. s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10);
  179. field += 6;
  180. // Extra: fractional time. Saved as microseconds.
  181. if (*field++ == '.') {
  182. int value = 0;
  183. int scale = 1000000;
  184. while (isdigit((unsigned char) *field) && scale > 1) {
  185. value = (value * 10) + (*field++ - '0');
  186. scale /= 10;
  187. }
  188. u = value * scale;
  189. } else {
  190. u = 0;
  191. }
  192. end_T:
  193. time->hours = h;
  194. time->minutes = i;
  195. time->seconds = s;
  196. time->microseconds = u;
  197. } break;
  198. case '_': { // Ignore the field.
  199. } break;
  200. default: { // Unknown.
  201. goto end;
  202. } break;
  203. }
  204. // Advance to next field.
  205. next_field();
  206. }
  207. result = true;
  208. end:
  209. va_end(ap);
  210. return result;
  211. }
  212. enum minmea_type minmea_type(const char *sentence)
  213. {
  214. if (!minmea_check(sentence))
  215. return MINMEA_INVALID;
  216. char type[6];
  217. if (!minmea_scan(sentence, "t", type))
  218. return MINMEA_INVALID;
  219. if (!strcmp(type, "GPRMC"))
  220. return MINMEA_GPRMC;
  221. if (!strcmp(type, "GPGGA"))
  222. return MINMEA_GPGGA;
  223. if (!strcmp(type, "GPGSA"))
  224. return MINMEA_GPGSA;
  225. return MINMEA_UNKNOWN;
  226. }
  227. bool minmea_parse_gprmc(struct minmea_gprmc *frame, const char *sentence)
  228. {
  229. // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
  230. char type[6];
  231. char validity;
  232. int latitude_direction;
  233. int longitude_direction;
  234. int variation_direction;
  235. if (!minmea_scan(sentence, "tTcfdfdffDfd",
  236. type,
  237. &frame->time,
  238. &validity,
  239. &frame->latitude, &frame->latitude_scale, &latitude_direction,
  240. &frame->longitude, &frame->longitude_scale, &longitude_direction,
  241. &frame->speed, &frame->speed_scale,
  242. &frame->course, &frame->course_scale,
  243. &frame->date,
  244. &frame->variation, &frame->variation_scale, &variation_direction))
  245. return false;
  246. if (strcmp(type, "GPRMC"))
  247. return false;
  248. frame->valid = (validity == 'A');
  249. frame->latitude *= latitude_direction;
  250. frame->longitude *= longitude_direction;
  251. frame->variation *= variation_direction;
  252. return true;
  253. }
  254. bool minmea_parse_gpgga(struct minmea_gpgga *frame, const char *sentence)
  255. {
  256. // $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
  257. char type[6];
  258. int latitude_direction;
  259. int longitude_direction;
  260. if (!minmea_scan(sentence, "tTfdfdiiffcfci_",
  261. type,
  262. &frame->time,
  263. &frame->latitude, &frame->latitude_scale, &latitude_direction,
  264. &frame->longitude, &frame->longitude_scale, &longitude_direction,
  265. &frame->fix_quality,
  266. &frame->satellites_tracked,
  267. &frame->hdop, &frame->hdop_scale,
  268. &frame->altitude, &frame->altitude_scale, &frame->altitude_units,
  269. &frame->height, &frame->height_scale, &frame->height_units,
  270. &frame->dgps_age))
  271. return false;
  272. if (strcmp(type, "GPGGA"))
  273. return false;
  274. frame->latitude *= latitude_direction;
  275. frame->longitude *= longitude_direction;
  276. return true;
  277. }
  278. bool minmea_parse_gpgsa(struct minmea_gpgsa *frame, const char *sentence)
  279. {
  280. // $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
  281. char type[6];
  282. if (!minmea_scan(sentence, "tciiiiiiiiiiiiifff",
  283. type,
  284. &frame->mode,
  285. &frame->fix_type,
  286. &frame->sats[0],
  287. &frame->sats[1],
  288. &frame->sats[2],
  289. &frame->sats[3],
  290. &frame->sats[4],
  291. &frame->sats[5],
  292. &frame->sats[6],
  293. &frame->sats[7],
  294. &frame->sats[8],
  295. &frame->sats[9],
  296. &frame->sats[10],
  297. &frame->sats[11],
  298. &frame->pdop,
  299. &frame->pdop_scale,
  300. &frame->hdop,
  301. &frame->hdop_scale,
  302. &frame->vdop,
  303. &frame->vdop_scale
  304. )){
  305. return false;
  306. }
  307. if (strcmp(type, "GPGSA"))
  308. return false;
  309. return true;
  310. }
  311. int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, const struct minmea_time *time)
  312. {
  313. if (date->year == -1 || time->hours == -1)
  314. return -1;
  315. struct tm tm;
  316. tm.tm_year = 2000 + date->year - 1900;
  317. tm.tm_mon = date->month - 1;
  318. tm.tm_mday = date->day;
  319. tm.tm_hour = time->hours;
  320. tm.tm_min = time->minutes;
  321. tm.tm_sec = time->seconds;
  322. tm.tm_isdst = 0;
  323. time_t timestamp = timegm(&tm);
  324. if (timestamp != -1) {
  325. tv->tv_sec = timestamp;
  326. tv->tv_usec = time->microseconds;
  327. return 0;
  328. } else {
  329. return -1;
  330. }
  331. }
  332. /* vim: set ts=4 sw=4 et: */