/* * NMEA Wrapper for PC Compilation * Provides simplified NMEA parsing using main.h for PC compatibility */ #define PC_BUILD #include "main.h" /* Prevent duplicate includes */ #define _NMEA_H_ 1 /* Helper function from nmea.c */ static unsigned char nmea_checksum(const char *s) { unsigned char c = 0; while (*s && *s != '*') { c ^= *s; s++; } return c; } /* String comparison function from nmea.c */ static char gp_comp(const char *s1, const char *s2) { while (*s1 && *s2) { if (*s1 != *s2) return *s1 - *s2; s1++; s2++; } if (*s1 == ',' && !*s2) return 0; return *s1 - *s2; } /* Forward declarations */ static time_t gp_rmc_parse(const char *str); static void gp_gga_parse(const char *str); static void gp_vtg_parse(const char *str); /* Field extraction helper */ static const char* get_field(const char **str, char *buf, size_t bufsize) { const char *s = *str; size_t i = 0; while (*s && *s != ',') s++; if (*s == ',') s++; while (*s && *s != ',' && *s != '*' && i < bufsize - 1) { buf[i++] = *s++; } buf[i] = '\0'; *str = s; return buf; } /* Parse time from NMEA */ static time_t parse_time(const char *timestr, const char *datestr) { struct tm t = {0}; if (strlen(timestr) >= 6) { t.tm_hour = (timestr[0] - '0') * 10 + (timestr[1] - '0'); t.tm_min = (timestr[2] - '0') * 10 + (timestr[3] - '0'); t.tm_sec = (timestr[4] - '0') * 10 + (timestr[5] - '0'); } if (strlen(datestr) >= 6) { t.tm_mday = (datestr[0] - '0') * 10 + (datestr[1] - '0'); t.tm_mon = (datestr[2] - '0') * 10 + (datestr[3] - '0') - 1; t.tm_year = (datestr[4] - '0') * 10 + (datestr[5] - '0') + 100; } return mktime(&t); } /* Parse coordinate */ static float parse_coord(const char *coord, const char *dir) { float deg, min; char *p; deg = strtof(coord, &p); if (!p || p == coord) return 0.0; min = fmodf(deg, 100.0f); deg = floorf(deg / 100.0f); float result = deg + min / 60.0f; if (*dir == 'S' || *dir == 'W') result = -result; return result; } /* RMC parser */ static time_t gp_rmc_parse(const char *str) { char buf[32]; const char *p = str; char timestr[16] = "", datestr[16] = "", status[2] = ""; char lat[16] = "", latdir[2] = "", lon[16] = "", londir[2] = ""; while (*p && *p != ',') p++; get_field(&p, timestr, sizeof(timestr)); get_field(&p, status, sizeof(status)); get_field(&p, lat, sizeof(lat)); get_field(&p, latdir, sizeof(latdir)); get_field(&p, lon, sizeof(lon)); get_field(&p, londir, sizeof(londir)); get_field(&p, buf, sizeof(buf)); /* speed */ get_field(&p, buf, sizeof(buf)); /* course */ get_field(&p, datestr, sizeof(datestr)); if (status[0] == 'A') { location.lat = parse_coord(lat, latdir); location.lon = parse_coord(lon, londir); utc = parse_time(timestr, datestr); location.time = utc; return utc; } return 0; } /* GGA parser */ static void gp_gga_parse(const char *str) { char buf[32]; const char *p = str; char alt[16] = "", lat[16] = "", latdir[2] = ""; char lon[16] = "", londir[2] = ""; while (*p && *p != ',') p++; get_field(&p, buf, sizeof(buf)); /* time */ get_field(&p, lat, sizeof(lat)); get_field(&p, latdir, sizeof(latdir)); get_field(&p, lon, sizeof(lon)); get_field(&p, londir, sizeof(londir)); get_field(&p, buf, sizeof(buf)); /* fix quality */ get_field(&p, buf, sizeof(buf)); /* satellites */ get_field(&p, buf, sizeof(buf)); /* HDOP */ get_field(&p, alt, sizeof(alt)); if (strlen(lat) > 0) { location.lat = parse_coord(lat, latdir); location.lon = parse_coord(lon, londir); } if (strlen(alt) > 0) { location.alt = atof(alt); } } /* VTG parser stub */ static void gp_vtg_parse(const char *str) { (void)str; } /* Stub functions */ void pmtk001_parse(const char *str) { (void)str; } void gps_initialize(void) {} void check_min_sat_limit(void) {} /* Main parse function */ time_t gps_parse(const char *str) { signed int len = strlen(str) - 2; const char *checksum; unsigned char calc_checksum, inc_checksum; signed int i; char c; if (len < 4) return 0; for (i = len - 1; i && i >= (len - 2); i--) { if (str[i] == '*') break; } checksum = str + i + 1; inc_checksum = 0; while (*checksum && isalnum(*checksum)) { inc_checksum *= 16; c = *checksum++; if (c >= '0' && c <= '9') { inc_checksum += c - '0'; } else if (c >= 'a' && c <= 'f') { inc_checksum += c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { inc_checksum += c - 'A' + 10; } else { return 0; } } str++; calc_checksum = nmea_checksum(str); if (inc_checksum != calc_checksum) { xprintf("Invalid NMEA checksum: got %02X, expected %02X\n", inc_checksum, calc_checksum); return 0; } if (!gp_comp(str, "GPRMC") || !gp_comp(str, "GNRMC") || !gp_comp(str, "BDRMC") || !gp_comp(str, "GARMC")) { return gp_rmc_parse(str); } if (!gp_comp(str, "GPGGA") || !gp_comp(str, "GNGGA") || !gp_comp(str, "BDGGA") || !gp_comp(str, "GAGGA")) { gp_gga_parse(str); return 0; } if (!gp_comp(str, "GPVTG") || !gp_comp(str, "GNVTG")) { gp_vtg_parse(str); return 0; } return 0; } UINT get_line(char *buff, UINT sz_buf) { (void)buff; (void)sz_buf; return 0; }