minmea.c 12 KB

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