| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- #include "GPS_parser.h"
- #include <ctype.h>
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #define GPS_SENTENCE_GGA 1
- #define GPS_SENTENCE_RMC 2
- #define GPS_SENTENCE_OTHER 0
- static int32_t gps_parse_decimal(const char *term)
- {
- bool negative = (*term == '-');
- if (negative) ++term;
- int32_t ret = 100 * (int32_t)atol(term);
- while (isdigit((unsigned char)*term)) ++term;
- if (*term == '.' && isdigit((unsigned char)term[1])) {
- ret += 10 * (term[1] - '0');
- if (isdigit((unsigned char)term[2]))
- ret += term[2] - '0';
- }
- return negative ? -ret : ret;
- }
- static void gps_parse_degrees(const char *term, gps_raw_degrees_t *deg)
- {
- uint32_t leftOfDecimal = (uint32_t)atol(term);
- uint16_t minutes = (uint16_t)(leftOfDecimal % 100);
- uint32_t multiplier = 10000000UL;
- uint32_t tenMillionthsOfMinutes = minutes * multiplier;
- deg->deg = (uint16_t)(leftOfDecimal / 100);
- while (isdigit((unsigned char)*term)) ++term;
- if (*term == '.') {
- while (isdigit((unsigned char)*++term)) {
- multiplier /= 10;
- tenMillionthsOfMinutes += (*term - '0') * multiplier;
- }
- }
- deg->billionths = (5 * tenMillionthsOfMinutes + 1) / 3;
- deg->negative = false;
- }
- static void gps_location_commit(gps_location_t *location)
- {
- location->rawLatData = location->rawNewLatData;
- location->rawLngData = location->rawNewLngData;
- location->fixQuality = location->newFixQuality;
- location->fixMode = location->newFixMode;
- location->lastCommitTime = gps_parser_millis();
- location->valid = true;
- location->updated = true;
- }
- static void gps_date_commit(gps_date_t *date)
- {
- date->date = date->newDate;
- date->lastCommitTime = gps_parser_millis();
- date->valid = true;
- date->updated = true;
- }
- static void gps_time_commit(gps_time_t *time)
- {
- time->time = time->newTime;
- time->lastCommitTime = gps_parser_millis();
- time->valid = true;
- time->updated = true;
- }
- static void gps_decimal_commit(gps_decimal_t *value)
- {
- value->val = value->newval;
- value->lastCommitTime = gps_parser_millis();
- value->valid = true;
- value->updated = true;
- }
- static void gps_integer_commit(gps_integer_t *value)
- {
- value->val = value->newval;
- value->lastCommitTime = gps_parser_millis();
- value->valid = true;
- value->updated = true;
- }
- static int gps_from_hex(char c)
- {
- if (c >= 'A' && c <= 'F') return c - 'A' + 10;
- if (c >= 'a' && c <= 'f') return c - 'a' + 10;
- if (c >= '0' && c <= '9') return c - '0';
- return 0;
- }
- static inline bool gps_field_is_numeric(const char *term)
- {
- return term[0] != '\0';
- }
- void gps_parser_init(gps_parser_t *gps)
- {
- gps->parity = 0;
- gps->isChecksumTerm = false;
- gps->curSentenceType = GPS_SENTENCE_OTHER;
- gps->curTermNumber = 0;
- gps->curTermOffset = 0;
- gps->sentenceHasFix = false;
- gps->encodedCharCount = 0;
- gps->sentencesWithFixCount = 0;
- gps->failedChecksumCount = 0;
- gps->passedChecksumCount = 0;
- gps->term[0] = '\0';
- memset(&gps->location, 0, sizeof(gps->location));
- memset(&gps->date, 0, sizeof(gps->date));
- memset(&gps->time, 0, sizeof(gps->time));
- memset(&gps->speed, 0, sizeof(gps->speed));
- memset(&gps->course, 0, sizeof(gps->course));
- memset(&gps->altitude, 0, sizeof(gps->altitude));
- memset(&gps->satellites, 0, sizeof(gps->satellites));
- memset(&gps->hdop, 0, sizeof(gps->hdop));
- }
- static bool gps_end_of_term_handler(gps_parser_t *gps)
- {
- if (gps->isChecksumTerm) {
- uint8_t checksum = (uint8_t)(16 * gps_from_hex(gps->term[0]) + gps_from_hex(gps->term[1]));
- if (checksum == gps->parity) {
- gps->passedChecksumCount++;
- if (gps->sentenceHasFix) gps->sentencesWithFixCount++;
- if (gps->curSentenceType == GPS_SENTENCE_RMC) {
- gps_date_commit(&gps->date);
- gps_time_commit(&gps->time);
- if (gps->sentenceHasFix) {
- gps_location_commit(&gps->location);
- gps_decimal_commit(&gps->speed);
- gps_decimal_commit(&gps->course);
- }
- } else if (gps->curSentenceType == GPS_SENTENCE_GGA) {
- gps_time_commit(&gps->time);
- if (gps->sentenceHasFix) {
- gps_location_commit(&gps->location);
- gps_decimal_commit(&gps->altitude);
- }
- gps_integer_commit(&gps->satellites);
- gps_decimal_commit(&gps->hdop);
- }
- return true;
- }
- gps->failedChecksumCount++;
- return false;
- }
- if (gps->curTermNumber == 0) {
- if (gps->term[0] == 'G' && strchr("PNABL", gps->term[1]) != NULL) {
- if (!strcmp(gps->term + 2, "RMC")) gps->curSentenceType = GPS_SENTENCE_RMC;
- else if (!strcmp(gps->term + 2, "GGA")) gps->curSentenceType = GPS_SENTENCE_GGA;
- else gps->curSentenceType = GPS_SENTENCE_OTHER;
- } else {
- gps->curSentenceType = GPS_SENTENCE_OTHER;
- }
- return false;
- }
- if (gps->curSentenceType != GPS_SENTENCE_OTHER && gps_field_is_numeric(gps->term)) {
- switch ((gps->curSentenceType << 5) | gps->curTermNumber) {
- case (GPS_SENTENCE_RMC << 5) | 1:
- case (GPS_SENTENCE_GGA << 5) | 1:
- gps->time.newTime = (uint32_t)gps_parse_decimal(gps->term);
- break;
- case (GPS_SENTENCE_RMC << 5) | 2:
- gps->sentenceHasFix = (gps->term[0] == 'A');
- break;
- case (GPS_SENTENCE_RMC << 5) | 3:
- case (GPS_SENTENCE_GGA << 5) | 2:
- gps_parse_degrees(gps->term, &gps->location.rawNewLatData);
- break;
- case (GPS_SENTENCE_RMC << 5) | 4:
- case (GPS_SENTENCE_GGA << 5) | 3:
- gps->location.rawNewLatData.negative = (gps->term[0] == 'S');
- break;
- case (GPS_SENTENCE_RMC << 5) | 5:
- case (GPS_SENTENCE_GGA << 5) | 4:
- gps_parse_degrees(gps->term, &gps->location.rawNewLngData);
- break;
- case (GPS_SENTENCE_RMC << 5) | 6:
- case (GPS_SENTENCE_GGA << 5) | 5:
- gps->location.rawNewLngData.negative = (gps->term[0] == 'W');
- break;
- case (GPS_SENTENCE_RMC << 5) | 7:
- gps->speed.newval = gps_parse_decimal(gps->term);
- break;
- case (GPS_SENTENCE_RMC << 5) | 8:
- gps->course.newval = gps_parse_decimal(gps->term);
- break;
- case (GPS_SENTENCE_RMC << 5) | 9:
- gps->date.newDate = (uint32_t)atol(gps->term);
- break;
- case (GPS_SENTENCE_GGA << 5) | 6:
- gps->sentenceHasFix = (gps->term[0] > '0');
- gps->location.newFixQuality = gps->term[0];
- break;
- case (GPS_SENTENCE_GGA << 5) | 7:
- gps->satellites.newval = (uint32_t)atol(gps->term);
- break;
- case (GPS_SENTENCE_GGA << 5) | 8:
- gps->hdop.newval = gps_parse_decimal(gps->term);
- break;
- case (GPS_SENTENCE_GGA << 5) | 9:
- gps->altitude.newval = gps_parse_decimal(gps->term);
- break;
- case (GPS_SENTENCE_RMC << 5) | 12:
- gps->location.newFixMode = gps->term[0];
- break;
- default:
- break;
- }
- }
- return false;
- }
- bool gps_parser_encode(gps_parser_t *gps, char c)
- {
- gps->encodedCharCount++;
- switch (c) {
- case ',':
- gps->parity ^= (uint8_t)c;
- /* fall through */
- case '\r':
- case '\n':
- case '*':
- if (gps->curTermOffset < sizeof(gps->term)) {
- gps->term[gps->curTermOffset] = '\0';
- bool valid = gps_end_of_term_handler(gps);
- gps->curTermNumber++;
- gps->curTermOffset = 0;
- gps->isChecksumTerm = (c == '*');
- return valid;
- }
- gps->curTermNumber++;
- gps->curTermOffset = 0;
- gps->isChecksumTerm = (c == '*');
- return false;
- case '$':
- gps->curTermNumber = 0;
- gps->curTermOffset = 0;
- gps->parity = 0;
- gps->curSentenceType = GPS_SENTENCE_OTHER;
- gps->isChecksumTerm = false;
- gps->sentenceHasFix = false;
- return false;
- default:
- if (gps->curTermOffset < sizeof(gps->term) - 1) {
- gps->term[gps->curTermOffset++] = c;
- }
- if (!gps->isChecksumTerm) {
- gps->parity ^= (uint8_t)c;
- }
- return false;
- }
- }
- bool gps_location_is_valid(const gps_location_t *location) { return location->valid; }
- bool gps_location_is_updated(const gps_location_t *location) { return location->updated; }
- uint32_t gps_location_age(const gps_location_t *location) {
- return location->valid ? gps_parser_millis() - location->lastCommitTime : UINT32_MAX;
- }
- double gps_location_lat(const gps_location_t *location)
- {
- double v = location->rawLatData.deg + location->rawLatData.billionths / 1000000000.0;
- return location->rawLatData.negative ? -v : v;
- }
- double gps_location_lng(const gps_location_t *location)
- {
- double v = location->rawLngData.deg + location->rawLngData.billionths / 1000000000.0;
- return location->rawLngData.negative ? -v : v;
- }
- char gps_location_fix_quality(const gps_location_t *location) { return location->fixQuality; }
- char gps_location_fix_mode(const gps_location_t *location) { return location->fixMode; }
- bool gps_date_is_valid(const gps_date_t *date) { return date->valid; }
- uint16_t gps_date_year(const gps_date_t *date) { return (uint16_t)(date->date % 100) + 2000; }
- uint8_t gps_date_month(const gps_date_t *date) { return (uint8_t)((date->date / 100) % 100); }
- uint8_t gps_date_day(const gps_date_t *date) { return (uint8_t)(date->date / 10000); }
- bool gps_time_is_valid(const gps_time_t *time) { return time->valid; }
- uint8_t gps_time_hour(const gps_time_t *time) { return (uint8_t)(time->time / 1000000); }
- uint8_t gps_time_minute(const gps_time_t *time) { return (uint8_t)((time->time / 10000) % 100); }
- uint8_t gps_time_second(const gps_time_t *time) { return (uint8_t)((time->time / 100) % 100); }
- uint8_t gps_time_centisecond(const gps_time_t *time) { return (uint8_t)(time->time % 100); }
- bool gps_decimal_is_valid(const gps_decimal_t *value) { return value->valid; }
- int32_t gps_decimal_value(const gps_decimal_t *value) { return value->val; }
- gps_integer_t *gps_get_satellites(gps_parser_t *gps) { return &gps->satellites; }
- uint32_t gps_parser_chars_processed(const gps_parser_t *gps) { return gps->encodedCharCount; }
- uint32_t gps_parser_sentences_with_fix(const gps_parser_t *gps) { return gps->sentencesWithFixCount; }
- uint32_t gps_parser_failed_checksum(const gps_parser_t *gps) { return gps->failedChecksumCount; }
- uint32_t gps_parser_passed_checksum(const gps_parser_t *gps) { return gps->passedChecksumCount; }
|