GPS_parser.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. #include "GPS_parser.h"
  2. #include <ctype.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <math.h>
  6. #define GPS_SENTENCE_GGA 1
  7. #define GPS_SENTENCE_RMC 2
  8. #define GPS_SENTENCE_OTHER 0
  9. static int32_t gps_parse_decimal(const char *term)
  10. {
  11. bool negative = (*term == '-');
  12. if (negative) ++term;
  13. int32_t ret = 100 * (int32_t)atol(term);
  14. while (isdigit((unsigned char)*term)) ++term;
  15. if (*term == '.' && isdigit((unsigned char)term[1])) {
  16. ret += 10 * (term[1] - '0');
  17. if (isdigit((unsigned char)term[2]))
  18. ret += term[2] - '0';
  19. }
  20. return negative ? -ret : ret;
  21. }
  22. static void gps_parse_degrees(const char *term, gps_raw_degrees_t *deg)
  23. {
  24. uint32_t leftOfDecimal = (uint32_t)atol(term);
  25. uint16_t minutes = (uint16_t)(leftOfDecimal % 100);
  26. uint32_t multiplier = 10000000UL;
  27. uint32_t tenMillionthsOfMinutes = minutes * multiplier;
  28. deg->deg = (uint16_t)(leftOfDecimal / 100);
  29. while (isdigit((unsigned char)*term)) ++term;
  30. if (*term == '.') {
  31. while (isdigit((unsigned char)*++term)) {
  32. multiplier /= 10;
  33. tenMillionthsOfMinutes += (*term - '0') * multiplier;
  34. }
  35. }
  36. deg->billionths = (5 * tenMillionthsOfMinutes + 1) / 3;
  37. deg->negative = false;
  38. }
  39. static void gps_location_commit(gps_location_t *location)
  40. {
  41. location->rawLatData = location->rawNewLatData;
  42. location->rawLngData = location->rawNewLngData;
  43. location->fixQuality = location->newFixQuality;
  44. location->fixMode = location->newFixMode;
  45. location->lastCommitTime = gps_parser_millis();
  46. location->valid = true;
  47. location->updated = true;
  48. }
  49. static void gps_date_commit(gps_date_t *date)
  50. {
  51. date->date = date->newDate;
  52. date->lastCommitTime = gps_parser_millis();
  53. date->valid = true;
  54. date->updated = true;
  55. }
  56. static void gps_time_commit(gps_time_t *time)
  57. {
  58. time->time = time->newTime;
  59. time->lastCommitTime = gps_parser_millis();
  60. time->valid = true;
  61. time->updated = true;
  62. }
  63. static void gps_decimal_commit(gps_decimal_t *value)
  64. {
  65. value->val = value->newval;
  66. value->lastCommitTime = gps_parser_millis();
  67. value->valid = true;
  68. value->updated = true;
  69. }
  70. static void gps_integer_commit(gps_integer_t *value)
  71. {
  72. value->val = value->newval;
  73. value->lastCommitTime = gps_parser_millis();
  74. value->valid = true;
  75. value->updated = true;
  76. }
  77. static int gps_from_hex(char c)
  78. {
  79. if (c >= 'A' && c <= 'F') return c - 'A' + 10;
  80. if (c >= 'a' && c <= 'f') return c - 'a' + 10;
  81. if (c >= '0' && c <= '9') return c - '0';
  82. return 0;
  83. }
  84. static inline bool gps_field_is_numeric(const char *term)
  85. {
  86. return term[0] != '\0';
  87. }
  88. void gps_parser_init(gps_parser_t *gps)
  89. {
  90. gps->parity = 0;
  91. gps->isChecksumTerm = false;
  92. gps->curSentenceType = GPS_SENTENCE_OTHER;
  93. gps->curTermNumber = 0;
  94. gps->curTermOffset = 0;
  95. gps->sentenceHasFix = false;
  96. gps->encodedCharCount = 0;
  97. gps->sentencesWithFixCount = 0;
  98. gps->failedChecksumCount = 0;
  99. gps->passedChecksumCount = 0;
  100. gps->term[0] = '\0';
  101. memset(&gps->location, 0, sizeof(gps->location));
  102. memset(&gps->date, 0, sizeof(gps->date));
  103. memset(&gps->time, 0, sizeof(gps->time));
  104. memset(&gps->speed, 0, sizeof(gps->speed));
  105. memset(&gps->course, 0, sizeof(gps->course));
  106. memset(&gps->altitude, 0, sizeof(gps->altitude));
  107. memset(&gps->satellites, 0, sizeof(gps->satellites));
  108. memset(&gps->hdop, 0, sizeof(gps->hdop));
  109. }
  110. static bool gps_end_of_term_handler(gps_parser_t *gps)
  111. {
  112. if (gps->isChecksumTerm) {
  113. uint8_t checksum = (uint8_t)(16 * gps_from_hex(gps->term[0]) + gps_from_hex(gps->term[1]));
  114. if (checksum == gps->parity) {
  115. gps->passedChecksumCount++;
  116. if (gps->sentenceHasFix) gps->sentencesWithFixCount++;
  117. if (gps->curSentenceType == GPS_SENTENCE_RMC) {
  118. gps_date_commit(&gps->date);
  119. gps_time_commit(&gps->time);
  120. if (gps->sentenceHasFix) {
  121. gps_location_commit(&gps->location);
  122. gps_decimal_commit(&gps->speed);
  123. gps_decimal_commit(&gps->course);
  124. }
  125. } else if (gps->curSentenceType == GPS_SENTENCE_GGA) {
  126. gps_time_commit(&gps->time);
  127. if (gps->sentenceHasFix) {
  128. gps_location_commit(&gps->location);
  129. gps_decimal_commit(&gps->altitude);
  130. }
  131. gps_integer_commit(&gps->satellites);
  132. gps_decimal_commit(&gps->hdop);
  133. }
  134. return true;
  135. }
  136. gps->failedChecksumCount++;
  137. return false;
  138. }
  139. if (gps->curTermNumber == 0) {
  140. if (gps->term[0] == 'G' && strchr("PNABL", gps->term[1]) != NULL) {
  141. if (!strcmp(gps->term + 2, "RMC")) gps->curSentenceType = GPS_SENTENCE_RMC;
  142. else if (!strcmp(gps->term + 2, "GGA")) gps->curSentenceType = GPS_SENTENCE_GGA;
  143. else gps->curSentenceType = GPS_SENTENCE_OTHER;
  144. } else {
  145. gps->curSentenceType = GPS_SENTENCE_OTHER;
  146. }
  147. return false;
  148. }
  149. if (gps->curSentenceType != GPS_SENTENCE_OTHER && gps_field_is_numeric(gps->term)) {
  150. switch ((gps->curSentenceType << 5) | gps->curTermNumber) {
  151. case (GPS_SENTENCE_RMC << 5) | 1:
  152. case (GPS_SENTENCE_GGA << 5) | 1:
  153. gps->time.newTime = (uint32_t)gps_parse_decimal(gps->term);
  154. break;
  155. case (GPS_SENTENCE_RMC << 5) | 2:
  156. gps->sentenceHasFix = (gps->term[0] == 'A');
  157. break;
  158. case (GPS_SENTENCE_RMC << 5) | 3:
  159. case (GPS_SENTENCE_GGA << 5) | 2:
  160. gps_parse_degrees(gps->term, &gps->location.rawNewLatData);
  161. break;
  162. case (GPS_SENTENCE_RMC << 5) | 4:
  163. case (GPS_SENTENCE_GGA << 5) | 3:
  164. gps->location.rawNewLatData.negative = (gps->term[0] == 'S');
  165. break;
  166. case (GPS_SENTENCE_RMC << 5) | 5:
  167. case (GPS_SENTENCE_GGA << 5) | 4:
  168. gps_parse_degrees(gps->term, &gps->location.rawNewLngData);
  169. break;
  170. case (GPS_SENTENCE_RMC << 5) | 6:
  171. case (GPS_SENTENCE_GGA << 5) | 5:
  172. gps->location.rawNewLngData.negative = (gps->term[0] == 'W');
  173. break;
  174. case (GPS_SENTENCE_RMC << 5) | 7:
  175. gps->speed.newval = gps_parse_decimal(gps->term);
  176. break;
  177. case (GPS_SENTENCE_RMC << 5) | 8:
  178. gps->course.newval = gps_parse_decimal(gps->term);
  179. break;
  180. case (GPS_SENTENCE_RMC << 5) | 9:
  181. gps->date.newDate = (uint32_t)atol(gps->term);
  182. break;
  183. case (GPS_SENTENCE_GGA << 5) | 6:
  184. gps->sentenceHasFix = (gps->term[0] > '0');
  185. gps->location.newFixQuality = gps->term[0];
  186. break;
  187. case (GPS_SENTENCE_GGA << 5) | 7:
  188. gps->satellites.newval = (uint32_t)atol(gps->term);
  189. break;
  190. case (GPS_SENTENCE_GGA << 5) | 8:
  191. gps->hdop.newval = gps_parse_decimal(gps->term);
  192. break;
  193. case (GPS_SENTENCE_GGA << 5) | 9:
  194. gps->altitude.newval = gps_parse_decimal(gps->term);
  195. break;
  196. case (GPS_SENTENCE_RMC << 5) | 12:
  197. gps->location.newFixMode = gps->term[0];
  198. break;
  199. default:
  200. break;
  201. }
  202. }
  203. return false;
  204. }
  205. bool gps_parser_encode(gps_parser_t *gps, char c)
  206. {
  207. gps->encodedCharCount++;
  208. switch (c) {
  209. case ',':
  210. gps->parity ^= (uint8_t)c;
  211. /* fall through */
  212. case '\r':
  213. case '\n':
  214. case '*':
  215. if (gps->curTermOffset < sizeof(gps->term)) {
  216. gps->term[gps->curTermOffset] = '\0';
  217. bool valid = gps_end_of_term_handler(gps);
  218. gps->curTermNumber++;
  219. gps->curTermOffset = 0;
  220. gps->isChecksumTerm = (c == '*');
  221. return valid;
  222. }
  223. gps->curTermNumber++;
  224. gps->curTermOffset = 0;
  225. gps->isChecksumTerm = (c == '*');
  226. return false;
  227. case '$':
  228. gps->curTermNumber = 0;
  229. gps->curTermOffset = 0;
  230. gps->parity = 0;
  231. gps->curSentenceType = GPS_SENTENCE_OTHER;
  232. gps->isChecksumTerm = false;
  233. gps->sentenceHasFix = false;
  234. return false;
  235. default:
  236. if (gps->curTermOffset < sizeof(gps->term) - 1) {
  237. gps->term[gps->curTermOffset++] = c;
  238. }
  239. if (!gps->isChecksumTerm) {
  240. gps->parity ^= (uint8_t)c;
  241. }
  242. return false;
  243. }
  244. }
  245. bool gps_location_is_valid(const gps_location_t *location) { return location->valid; }
  246. bool gps_location_is_updated(const gps_location_t *location) { return location->updated; }
  247. uint32_t gps_location_age(const gps_location_t *location) {
  248. return location->valid ? gps_parser_millis() - location->lastCommitTime : UINT32_MAX;
  249. }
  250. double gps_location_lat(const gps_location_t *location)
  251. {
  252. double v = location->rawLatData.deg + location->rawLatData.billionths / 1000000000.0;
  253. return location->rawLatData.negative ? -v : v;
  254. }
  255. double gps_location_lng(const gps_location_t *location)
  256. {
  257. double v = location->rawLngData.deg + location->rawLngData.billionths / 1000000000.0;
  258. return location->rawLngData.negative ? -v : v;
  259. }
  260. char gps_location_fix_quality(const gps_location_t *location) { return location->fixQuality; }
  261. char gps_location_fix_mode(const gps_location_t *location) { return location->fixMode; }
  262. bool gps_date_is_valid(const gps_date_t *date) { return date->valid; }
  263. uint16_t gps_date_year(const gps_date_t *date) { return (uint16_t)(date->date % 100) + 2000; }
  264. uint8_t gps_date_month(const gps_date_t *date) { return (uint8_t)((date->date / 100) % 100); }
  265. uint8_t gps_date_day(const gps_date_t *date) { return (uint8_t)(date->date / 10000); }
  266. bool gps_time_is_valid(const gps_time_t *time) { return time->valid; }
  267. uint8_t gps_time_hour(const gps_time_t *time) { return (uint8_t)(time->time / 1000000); }
  268. uint8_t gps_time_minute(const gps_time_t *time) { return (uint8_t)((time->time / 10000) % 100); }
  269. uint8_t gps_time_second(const gps_time_t *time) { return (uint8_t)((time->time / 100) % 100); }
  270. uint8_t gps_time_centisecond(const gps_time_t *time) { return (uint8_t)(time->time % 100); }
  271. bool gps_decimal_is_valid(const gps_decimal_t *value) { return value->valid; }
  272. int32_t gps_decimal_value(const gps_decimal_t *value) { return value->val; }
  273. gps_integer_t *gps_get_satellites(gps_parser_t *gps) { return &gps->satellites; }
  274. uint32_t gps_parser_chars_processed(const gps_parser_t *gps) { return gps->encodedCharCount; }
  275. uint32_t gps_parser_sentences_with_fix(const gps_parser_t *gps) { return gps->sentencesWithFixCount; }
  276. uint32_t gps_parser_failed_checksum(const gps_parser_t *gps) { return gps->failedChecksumCount; }
  277. uint32_t gps_parser_passed_checksum(const gps_parser_t *gps) { return gps->passedChecksumCount; }