33 #if defined(PIOS_INCLUDE_GPS_NMEA_PARSER)
35 #include "gpsposition.h"
38 #include "gpssatellites.h"
46 #ifdef ENABLE_DEBUG_MSG
48 //#define DEBUG_PARAMS
49 #define DEBUG_MGSID_IN
50 #define NMEA_DEBUG_PKT
51 #define NMEA_DEBUG_GGA
52 #define NMEA_DEBUG_VTG
53 #define NMEA_DEBUG_RMC
54 #define NMEA_DEBUG_GSA
55 #define NMEA_DEBUG_GSV
56 #define NMEA_DEBUG_ZDA
57 #define DEBUG_MSG(format, ...) DEBUG_PRINTF(2, format, ## __VA_ARGS__)
59 #define DEBUG_MSG(format, ...)
62 #define MAX_NB_PARAMS 20
67 bool(*handler) (GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
70 static bool nmeaProcessGPGGA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
71 static bool nmeaProcessGPRMC(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
72 static bool nmeaProcessGPVTG(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
73 static bool nmeaProcessGPGSA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
74 static bool nmeaProcessGPZDA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
75 static bool nmeaProcessGPGSV(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam);
77 const static struct nmea_parser nmea_parsers[] = {
80 .handler = nmeaProcessGPGGA,
84 .handler = nmeaProcessGPVTG,
88 .handler = nmeaProcessGPGSA,
92 .handler = nmeaProcessGPRMC,
96 .handler = nmeaProcessGPZDA,
100 .handler = nmeaProcessGPGSV,
106 static uint8_t rx_count = 0;
107 static bool start_flag =
false;
108 static bool found_cr =
false;
111 if (!start_flag && (c ==
'$'))
133 gps_rx_buffer[rx_count] = c;
138 if (!found_cr && (c ==
'\r') )
141 if (found_cr && (c !=
'\n') )
144 if (found_cr && (c ==
'\n') )
148 gps_rx_buffer[rx_count-2] = 0;
186 const static struct nmea_parser *NMEA_find_parser_by_prefix(
const char *prefix)
193 const struct nmea_parser *
parser = &nmea_parsers[
i];
196 if (!strcmp(prefix, parser->prefix)) {
214 uint8_t checksum_computed = 0;
215 uint8_t checksum_received;
217 while (*nmea_sentence !=
'\0' && *nmea_sentence !=
'*') {
218 checksum_computed ^= *nmea_sentence;
223 if (*nmea_sentence ==
'\0') {
229 checksum_received = strtol(nmea_sentence + 1, NULL, 16);
233 return (checksum_computed == checksum_received);
249 static bool NMEA_parse_real(int32_t * whole, uint32_t * fract, uint8_t * fract_units,
char *field)
259 field_w =
strsep(&s,
".");
262 *whole = strtol(field_w, NULL, 10);
266 *fract = strtoul(field_f, NULL, 10);
267 *fract_units = strlen(field_f);
277 static float NMEA_real_to_float(
char *nmea_real)
286 if (!NMEA_parse_real(&whole, &fract, &fract_units, nmea_real)) {
291 return (((
float)whole) + fract * powf(10.0
f, -fract_units));
299 static bool NMEA_latlon_to_fixed_point(int32_t * latlon,
char *nmea_latlon,
bool negative)
309 if (*nmea_latlon ==
'\0') {
313 if (!NMEA_parse_real(&num_DDDMM, &num_m, &units, nmea_latlon)) {
347 *latlon = (num_DDDMM / 100) * 10000000;
348 *latlon += (num_DDDMM % 100) * 10000000 / 60;
349 *latlon += num_m / 60;
366 char*
p = nmea_sentence;
367 char* params[MAX_NB_PARAMS];
386 }
else if (*p ==
',') {
390 if (nbParams==MAX_NB_PARAMS)
392 params[nbParams] = p+1;
401 for (i=0;i<nbParams; i++) {
407 const struct nmea_parser *
parser;
408 parser = NMEA_find_parser_by_prefix(params[0]);
411 DEBUG_MSG(
" NO PARSER (\"%s\")\n", params[0]);
415 #ifdef DEBUG_MGSID_IN
423 bool gpsDataUpdated =
false;
425 if (!parser->handler(GpsData, &gpsDataUpdated, params, nbParams)) {
427 DEBUG_MSG(
"PARSE FAILED (\"%s\")\n", params[0]);
428 if (gpsDataUpdated && (GpsData->Status == GPSPOSITION_STATUS_NOFIX)) {
429 GPSPositionSet(GpsData);
436 if (gpsDataUpdated) {
437 #ifdef DEBUG_MGSID_IN
440 GPSPositionSet(GpsData);
443 #ifdef DEBUG_MGSID_IN
455 static bool nmeaProcessGPGGA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
461 #ifdef NMEA_DEBUG_GGA
463 DEBUG_MSG(
" Lat=%s %s\n", param[2], param[3]);
464 DEBUG_MSG(
" Long=%s %s\n", param[4], param[5]);
468 DEBUG_MSG(
" Alt=%s %s\n", param[9], param[10]);
469 DEBUG_MSG(
" GeoidSep=%s\n\n", param[11]);
472 *gpsDataUpdated =
true;
477 if (param[6][0] ==
'0') {
478 GpsData->Status = GPSPOSITION_STATUS_NOFIX;
482 if (!NMEA_latlon_to_fixed_point(&GpsData->Latitude, param[2], param[3][0] ==
'S')) {
487 if (!NMEA_latlon_to_fixed_point(&GpsData->Longitude, param[4], param[5][0] ==
'W')) {
492 GpsData->Satellites = atoi(param[7]);
495 GpsData->Altitude = NMEA_real_to_float(param[9]);
498 GpsData->GeoidSeparation = NMEA_real_to_float(param[11]);
508 static bool nmeaProcessGPRMC(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
513 #ifdef NMEA_DEBUG_RMC
515 DEBUG_MSG(
" Lat=%s %s\n", param[3], param[4]);
516 DEBUG_MSG(
" Long=%s %s\n", param[5], param[6]);
519 DEBUG_MSG(
" DateOfFix=%s\n\n", param[9]);
522 *gpsDataUpdated =
false;
528 float hms = NMEA_real_to_float(param[1]);
529 gpst.Second = (int)hms % 100;
530 gpst.Minute = (((int)hms - gpst.Second) / 100) % 100;
531 gpst.Hour = (int)hms / 10000;
534 if (param[2][0] ==
'V') {
539 if (!NMEA_latlon_to_fixed_point(&GpsData->Latitude, param[3], param[4][0] ==
'S')) {
544 if (!NMEA_latlon_to_fixed_point(&GpsData->Longitude, param[5], param[6][0] ==
'W')) {
549 GpsData->Groundspeed = NMEA_real_to_float(param[7]) * 0.51444f;
552 GpsData->Heading = NMEA_real_to_float(param[8]);
556 float date = NMEA_real_to_float(param[9]);
557 gpst.Year = (int)date % 100;
558 gpst.Month = (((int)date - gpst.Year) / 100) % 100;
559 gpst.Day = (int)(date / 10000);
571 static bool nmeaProcessGPVTG(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
573 if (nbParam != 9 && nbParam != 10 )
576 #ifdef NMEA_DEBUG_RMC
577 DEBUG_MSG(
"\n Heading=%s %s\n", param[1], param[2]);
578 DEBUG_MSG(
" GroundSpeed=%s %s\n", param[5], param[6]);
581 *gpsDataUpdated =
false;
583 GpsData->Heading = NMEA_real_to_float(param[1]);
584 GpsData->Groundspeed = NMEA_real_to_float(param[5]) * 0.51444f;
594 static bool nmeaProcessGPZDA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
599 #ifdef NMEA_DEBUG_ZDA
600 DEBUG_MSG(
"\n Time=%s (hhmmss.ss)\n", param[1]);
601 DEBUG_MSG(
" Date=%s/%s/%s (d/m/y)\n", param[2], param[3], param[4]);
604 *gpsDataUpdated =
false;
611 float hms = NMEA_real_to_float(param[1]);
612 gpst.Second = (int)hms % 100;
613 gpst.Minute = (((int)hms - gpst.Second) / 100) % 100;
614 gpst.Hour = (int)hms / 10000;
617 gpst.Day = atoi(param[2]);
618 gpst.Month = atoi(param[3]);
619 gpst.Year = atoi(param[4]);
625 static GPSSatellitesData gsv_partial;
627 static uint8_t gsv_expected_mask;
628 static uint8_t gsv_processed_mask;
630 static uint16_t gsv_incomplete_error;
631 static uint16_t gsv_duplicate_error;
633 static bool nmeaProcessGPGSV(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
638 #ifdef NMEA_DEBUG_GSV
639 DEBUG_MSG(
"\n Sentence=%s/%s\n", param[2], param[1]);
643 uint8_t nbSentences = atoi(param[1]);
644 uint8_t currSentence = atoi(param[2]);
646 *gpsDataUpdated =
false;
648 if (nbSentences < 1 || nbSentences > 8 || currSentence < 1 || currSentence > nbSentences)
651 gsv_partial.SatsInView = atoi(param[3]);
654 if (currSentence == 1) {
655 if (gsv_expected_mask != gsv_processed_mask) {
657 gsv_incomplete_error++;
661 gsv_expected_mask = (1 << nbSentences) - 1;
664 uint8_t current_sentence_id = (1 << (currSentence - 1));
665 if (gsv_processed_mask & current_sentence_id) {
667 gsv_duplicate_error++;
670 gsv_processed_mask |= current_sentence_id;
675 #ifdef NMEA_DEBUG_GSV
680 if ((currSentence * 4) <=
NELEMENTS(gsv_partial.PRN)) {
682 for (uint8_t i = 0; parIdx+4 <= nbParam && i < 4; i++) {
683 uint8_t sat_index = ((currSentence - 1) * 4) +
i;
686 gsv_partial.PRN[sat_index] = atoi(param[parIdx++]);
687 gsv_partial.Elevation[sat_index] = NMEA_real_to_float(param[parIdx++]);
688 gsv_partial.Azimuth[sat_index] = NMEA_real_to_float(param[parIdx++]);
689 gsv_partial.SNR[sat_index] = atoi(param[parIdx++]);
690 #ifdef NMEA_DEBUG_GSV
691 DEBUG_MSG(
" %d", gsv_partial.PRN[sat_index]);
695 #ifdef NMEA_DEBUG_GSV
701 if ((gsv_expected_mask != 0) && (gsv_processed_mask == gsv_expected_mask)) {
703 GPSSatellitesSet(&gsv_partial);
704 memset((
void *)&gsv_partial, 0,
sizeof(gsv_partial));
705 gsv_expected_mask = 0;
706 gsv_processed_mask = 0;
717 static bool nmeaProcessGPGSA(GPSPositionData * GpsData,
bool* gpsDataUpdated,
char* param[], uint8_t nbParam)
722 #ifdef NMEA_DEBUG_GSA
729 *gpsDataUpdated =
false;
731 switch (atoi(param[2])) {
733 GpsData->Status = GPSPOSITION_STATUS_NOFIX;
736 GpsData->Status = GPSPOSITION_STATUS_FIX2D;
739 GpsData->Status = GPSPOSITION_STATUS_FIX3D;
748 GpsData->PDOP = NMEA_real_to_float(param[15]);
751 GpsData->HDOP = NMEA_real_to_float(param[16]);
754 GpsData->VDOP = NMEA_real_to_float(param[17]);
758 GpsData->Accuracy = GpsData->PDOP * 5.0f;
763 #endif // PIOS_INCLUDE_GPS_NMEA_PARSER
static char * gps_rx_buffer
Main PiOS header to include all the compiled in PiOS options.
bool NMEA_checksum(char *nmea_sentence)
static char * strsep(char **stringp, const char *delim)
#define PIOS_DEBUG_Assert(test)
bool NMEA_update_position(char *nmea_sentence, GPSPositionData *GpsData)
#define DEBUG_MSG(format,...)
uint16_t gpsRxChkSumError
Include file of the GPS module.
uint16_t gpsRxParserError
Include file to process NMEA data.
int parse_nmea_stream(uint8_t, char *, GPSPositionData *, struct GPS_RX_STATS *)
Includes PiOS and core architecture components.
static struct GPS_RX_STATS gpsRxStats
#define NMEA_MAX_PACKET_LENGTH
#define PARSER_INCOMPLETE