dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
NMEA.c
Go to the documentation of this file.
1 
15 /*
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 3 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful, but
22  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24  * for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with this program; if not, see <http://www.gnu.org/licenses/>
28  */
29 
30 #include "openpilot.h"
31 #include "pios.h"
32 
33 #if defined(PIOS_INCLUDE_GPS_NMEA_PARSER)
34 
35 #include "gpsposition.h"
36 #include "NMEA.h"
37 #include "gpstime.h"
38 #include "gpssatellites.h"
39 #include "GPS.h"
40 
41 //#define ENABLE_DEBUG_MSG ///< define to enable debug-messages
42 
43 
44 
45 // Debugging
46 #ifdef ENABLE_DEBUG_MSG
47 #define DEBUG_MSG_IN
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__)
58 #else
59 #define DEBUG_MSG(format, ...)
60 #endif
61 
62 #define MAX_NB_PARAMS 20
63 /* NMEA sentence parsers */
64 
65 struct nmea_parser {
66  const char *prefix;
67  bool(*handler) (GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam);
68 };
69 
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);
76 
77 const static struct nmea_parser nmea_parsers[] = {
78  {
79  .prefix = "GGA",
80  .handler = nmeaProcessGPGGA,
81  },
82  {
83  .prefix = "VTG",
84  .handler = nmeaProcessGPVTG,
85  },
86  {
87  .prefix = "GSA",
88  .handler = nmeaProcessGPGSA,
89  },
90  {
91  .prefix = "RMC",
92  .handler = nmeaProcessGPRMC,
93  },
94  {
95  .prefix = "ZDA",
96  .handler = nmeaProcessGPZDA,
97  },
98  {
99  .prefix = "GSV",
100  .handler = nmeaProcessGPGSV,
101  },
102 };
103 
104 int parse_nmea_stream (uint8_t c, char *gps_rx_buffer, GPSPositionData *GpsData, struct GPS_RX_STATS *gpsRxStats)
105 {
106  static uint8_t rx_count = 0;
107  static bool start_flag = false;
108  static bool found_cr = false;
109 
110  // detect start while acquiring stream
111  if (!start_flag && (c == '$')) // NMEA identifier found
112  {
113  start_flag = true;
114  found_cr = false;
115  rx_count = 0;
116  }
117  else
118  if (!start_flag)
119  return PARSER_ERROR;
120 
121  if (rx_count >= NMEA_MAX_PACKET_LENGTH)
122  {
123  // The buffer is already full and we haven't found a valid NMEA sentence.
124  // Flush the buffer and note the overflow event.
125  gpsRxStats->gpsRxOverflow++;
126  start_flag = false;
127  found_cr = false;
128  rx_count = 0;
129  return PARSER_OVERRUN;
130  }
131  else
132  {
133  gps_rx_buffer[rx_count] = c;
134  rx_count++;
135  }
136 
137  // look for ending '\r\n' sequence
138  if (!found_cr && (c == '\r') )
139  found_cr = true;
140  else
141  if (found_cr && (c != '\n') )
142  found_cr = false; // false end flag
143  else
144  if (found_cr && (c == '\n') )
145  {
146  // The NMEA functions require a zero-terminated string
147  // As we detected \r\n, the string as for sure 2 bytes long, we will also strip the \r\n
148  gps_rx_buffer[rx_count-2] = 0;
149 
150  // prepare to parse next sentence
151  start_flag = false;
152  found_cr = false;
153  rx_count = 0;
154  // Our rxBuffer must look like this now:
155  // [0] = '$'
156  // ... = zero or more bytes of sentence payload
157  // [end_pos - 1] = '\r'
158  // [end_pos] = '\n'
159  //
160  // Prepare to consume the sentence from the buffer
161 
162  // Validate the checksum over the sentence
163  if (!NMEA_checksum(&gps_rx_buffer[1]))
164  { // Invalid checksum. May indicate dropped characters on Rx.
165  //PIOS_DEBUG_PinHigh(2);
166  gpsRxStats->gpsRxChkSumError++;
167  //PIOS_DEBUG_PinLow(2);
168  return PARSER_ERROR;
169  }
170  else
171  { // Valid checksum, use this packet to update the GPS position
172  if (!NMEA_update_position(&gps_rx_buffer[1], GpsData)) {
173  //PIOS_DEBUG_PinHigh(2);
174  gpsRxStats->gpsRxParserError++;
175  //PIOS_DEBUG_PinLow(2);
176  }
177  else
178  gpsRxStats->gpsRxReceived++;;
179 
180  return PARSER_COMPLETE;
181  }
182  }
183  return PARSER_INCOMPLETE;
184 }
185 
186 const static struct nmea_parser *NMEA_find_parser_by_prefix(const char *prefix)
187 {
188  if (!prefix) {
189  return (NULL);
190  }
191 
192  for (uint8_t i = 0; i < NELEMENTS(nmea_parsers); i++) {
193  const struct nmea_parser *parser = &nmea_parsers[i];
194 
195  /* Use strcmp to check for exact equality over the entire prefix */
196  if (!strcmp(prefix, parser->prefix)) {
197  /* Found an appropriate parser */
198  return (parser);
199  }
200  }
201 
202  /* No matching parser for this prefix */
203  return (NULL);
204 }
205 
212 bool NMEA_checksum(char *nmea_sentence)
213 {
214  uint8_t checksum_computed = 0;
215  uint8_t checksum_received;
216 
217  while (*nmea_sentence != '\0' && *nmea_sentence != '*') {
218  checksum_computed ^= *nmea_sentence;
219  nmea_sentence++;
220  }
221 
222  /* Make sure we're now pointing at the checksum */
223  if (*nmea_sentence == '\0') {
224  /* Buffer ran out before we found a checksum marker */
225  return false;
226  }
227 
228  /* Load the checksum from the buffer */
229  checksum_received = strtol(nmea_sentence + 1, NULL, 16);
230 
231  //PIOS_COM_SendFormattedStringNonBlocking(COM_DEBUG_USART,"$%d=%d\r\n",checksum_received,checksum_computed);
232 
233  return (checksum_computed == checksum_received);
234 }
235 
236 /*
237  * This function only exists to deal with a linking
238  * failure in the stdlib function strtof(). This
239  * implementation does not rely on the _sbrk() syscall
240  * like strtof() does.
241  */
242 
243 /* Parse a number encoded in a string of the format:
244  * [-]NN.nnnnn
245  * into a signed whole part and an unsigned fractional part.
246  * The fract_units field indicates the units of the fractional part as
247  * 1 whole = 10^fract_units fract
248  */
249 static bool NMEA_parse_real(int32_t * whole, uint32_t * fract, uint8_t * fract_units, char *field)
250 {
251  char *s = field;
252  char *field_w;
253  char *field_f;
254 
255  PIOS_DEBUG_Assert(whole);
256  PIOS_DEBUG_Assert(fract);
257  PIOS_DEBUG_Assert(fract_units);
258 
259  field_w = strsep(&s, ".");
260  field_f = s;
261 
262  *whole = strtol(field_w, NULL, 10);
263 
264  if (field_f) {
265  /* decimal was found so we may have a fractional part */
266  *fract = strtoul(field_f, NULL, 10);
267  *fract_units = strlen(field_f);
268  } else {
269  /* no decimal was found, fractional part is zero */
270  *fract = 0;
271  *fract_units = 0;
272  }
273 
274  return true;
275 }
276 
277 static float NMEA_real_to_float(char *nmea_real)
278 {
279  int32_t whole;
280  uint32_t fract;
281  uint8_t fract_units;
282 
283  /* Sanity checks */
284  PIOS_DEBUG_Assert(nmea_real);
285 
286  if (!NMEA_parse_real(&whole, &fract, &fract_units, nmea_real)) {
287  return false;
288  }
289 
290  /* Convert to float */
291  return (((float)whole) + fract * powf(10.0f, -fract_units));
292 }
293 
294 /*
295  * Parse a field in the format:
296  * DD[D]MM.mmmm[mm]
297  * into a fixed-point representation in units of (degrees * 1e-7)
298  */
299 static bool NMEA_latlon_to_fixed_point(int32_t * latlon, char *nmea_latlon, bool negative)
300 {
301  int32_t num_DDDMM;
302  uint32_t num_m;
303  uint8_t units;
304 
305  /* Sanity checks */
306  PIOS_DEBUG_Assert(nmea_latlon);
307  PIOS_DEBUG_Assert(latlon);
308 
309  if (*nmea_latlon == '\0') { /* empty lat/lon field */
310  return false;
311  }
312 
313  if (!NMEA_parse_real(&num_DDDMM, &num_m, &units, nmea_latlon)) {
314  return false;
315  }
316 
317  /* scale up the mmmm[mm] field apropriately depending on # of digits */
318  /* not using 1eN notation because that forces fixed point and lost precision */
319  switch (units) {
320  case 0:
321  /* no digits, value is zero so no scaling */
322  break;
323  case 1: /* m */
324  num_m *= 1000000; /* m000000 */
325  break;
326  case 2: /* mm */
327  num_m *= 100000; /* mm00000 */
328  break;
329  case 3: /* mmm */
330  num_m *= 10000; /* mmm0000 */
331  break;
332  case 4: /* mmmm */
333  num_m *= 1000; /* mmmm000 */
334  break;
335  case 5: /* mmmmm */
336  num_m *= 100; /* mmmmm00 */
337  break;
338  case 6: /* mmmmmm */
339  num_m *= 10; /* mmmmmm0 */
340  break;
341  default:
342  /* unhandled format */
343  num_m = 0.0f;
344  break;
345  }
346 
347  *latlon = (num_DDDMM / 100) * 10000000; /* scale the whole degrees */
348  *latlon += (num_DDDMM % 100) * 10000000 / 60; /* add in the scaled decimal whole minutes */
349  *latlon += num_m / 60; /* add in the scaled decimal fractional minutes */
350 
351  if (negative)
352  *latlon *= -1;
353 
354  return true;
355 }
356 
357 
364 bool NMEA_update_position(char *nmea_sentence, GPSPositionData *GpsData)
365 {
366  char* p = nmea_sentence;
367  char* params[MAX_NB_PARAMS];
368  uint8_t nbParams;
369 
370 #ifdef DEBUG_MSG_IN
371  DEBUG_MSG("\"%s\"\n", nmea_sentence);
372 #endif
373 
374  // Split the nmea sentence it its parameters, separated by ","
375  // Sample NMEA message: "GPRMC,000131.736,V,,,,,0.00,0.00,060180,,,N*43"
376 
377  // The first parameter starts at the beginning of the message
378  p += 2; // Skip the first two characters (e.g., GP, GN) which is the talker ID
379  params[0] = p;
380  nbParams = 1;
381  while (*p != 0) {
382  if (*p == '*') {
383  // After the * comes the "CRC", we are done,
384  *p = 0; // Zero-terminate this parameter
385  break;
386  } else if (*p == ',') {
387  // This is the end of this parameter
388  *p = 0; // Zero-terminate this parameter
389  // Start new parameter
390  if (nbParams==MAX_NB_PARAMS)
391  break;
392  params[nbParams] = p+1; // For sure there is something at p+1 because at p there is ","
393  nbParams++;
394  }
395  p++;
396  }
397 
398 
399 #ifdef DEBUG_PARAMS
400  int i;
401  for (i=0;i<nbParams; i++) {
402  DEBUG_MSG(" %d \"%s\"\n", i, params[i]);
403  }
404 #endif
405 
406  // The first parameter is the message name, lets see if we find a parser for it
407  const struct nmea_parser *parser;
408  parser = NMEA_find_parser_by_prefix(params[0]);
409  if (!parser) {
410  // No parser found
411  DEBUG_MSG(" NO PARSER (\"%s\")\n", params[0]);
412  return false;
413  }
414 
415  #ifdef DEBUG_MGSID_IN
416  DEBUG_MSG("%s ", params[0]);
417  #endif
418  // Send the message to the parser and get it update the GpsData
419  // Information from various different NMEA messages are temporarily
420  // cumulated in the GpsData structure. An actual GPSPosition update
421  // is triggered by GGA messages only. This message type sets the
422  // gpsDataUpdated flag to request this.
423  bool gpsDataUpdated = false;
424 
425  if (!parser->handler(GpsData, &gpsDataUpdated, params, nbParams)) {
426  // Parse failed
427  DEBUG_MSG("PARSE FAILED (\"%s\")\n", params[0]);
428  if (gpsDataUpdated && (GpsData->Status == GPSPOSITION_STATUS_NOFIX)) {
429  GPSPositionSet(GpsData);
430  }
431  return false;
432  }
433 
434 
435  // All is fine :) Update object if data has changed
436  if (gpsDataUpdated) {
437  #ifdef DEBUG_MGSID_IN
438  DEBUG_MSG("U");
439  #endif
440  GPSPositionSet(GpsData);
441  }
442 
443  #ifdef DEBUG_MGSID_IN
444  DEBUG_MSG("\n");
445  #endif
446  return true;
447 }
448 
449 
455 static bool nmeaProcessGPGGA(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
456 {
457 
458  if (nbParam != 15)
459  return false;
460 
461 #ifdef NMEA_DEBUG_GGA
462  DEBUG_MSG("\n UTC=%s\n", param[1]);
463  DEBUG_MSG(" Lat=%s %s\n", param[2], param[3]);
464  DEBUG_MSG(" Long=%s %s\n", param[4], param[5]);
465  DEBUG_MSG(" Fix=%s\n", param[6]);
466  DEBUG_MSG(" Sat=%s\n", param[7]);
467  DEBUG_MSG(" HDOP=%s\n", param[8]);
468  DEBUG_MSG(" Alt=%s %s\n", param[9], param[10]);
469  DEBUG_MSG(" GeoidSep=%s\n\n", param[11]);
470 #endif
471 
472  *gpsDataUpdated = true;
473 
474  // check for invalid GPS fix
475  // do this first to make sure we get this information, even if later checks exit
476  // this function early
477  if (param[6][0] == '0') {
478  GpsData->Status = GPSPOSITION_STATUS_NOFIX; // treat invalid fix as NOFIX
479  }
480 
481  // get latitude [DDMM.mmmmm] [N|S]
482  if (!NMEA_latlon_to_fixed_point(&GpsData->Latitude, param[2], param[3][0] == 'S')) {
483  return false;
484  }
485 
486  // get longitude [dddmm.mmmmm] [E|W]
487  if (!NMEA_latlon_to_fixed_point(&GpsData->Longitude, param[4], param[5][0] == 'W')) {
488  return false;
489  }
490 
491  // get number of satellites used in GPS solution
492  GpsData->Satellites = atoi(param[7]);
493 
494  // get altitude (in meters mm.m)
495  GpsData->Altitude = NMEA_real_to_float(param[9]);
496 
497  // geoid separation
498  GpsData->GeoidSeparation = NMEA_real_to_float(param[11]);
499 
500  return true;
501 }
502 
508 static bool nmeaProcessGPRMC(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
509 {
510  if (nbParam != 13)
511  return false;
512 
513 #ifdef NMEA_DEBUG_RMC
514  DEBUG_MSG("\n UTC=%s\n", param[1]);
515  DEBUG_MSG(" Lat=%s %s\n", param[3], param[4]);
516  DEBUG_MSG(" Long=%s %s\n", param[5], param[6]);
517  DEBUG_MSG(" Speed=%s\n", param[7]);
518  DEBUG_MSG(" Course=%s\n", param[8]);
519  DEBUG_MSG(" DateOfFix=%s\n\n", param[9]);
520 #endif
521 
522  *gpsDataUpdated = false;
523 
524  GPSTimeData gpst;
525  GPSTimeGet(&gpst);
526 
527  // get UTC time [hhmmss.sss]
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;
532 
533  // don't process void sentences
534  if (param[2][0] == 'V') {
535  return false;
536  }
537 
538  // get latitude [DDMM.mmmmm] [N|S]
539  if (!NMEA_latlon_to_fixed_point(&GpsData->Latitude, param[3], param[4][0] == 'S')) {
540  return false;
541  }
542 
543  // get longitude [dddmm.mmmmm] [E|W]
544  if (!NMEA_latlon_to_fixed_point(&GpsData->Longitude, param[5], param[6][0] == 'W')) {
545  return false;
546  }
547 
548  // get speed in knots
549  GpsData->Groundspeed = NMEA_real_to_float(param[7]) * 0.51444f; // to m/s
550 
551  // get True course
552  GpsData->Heading = NMEA_real_to_float(param[8]);
553 
554  // get Date of fix
555  // TODO: Should really not use a float here to be safe
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);
560  gpst.Year += 2000;
561  GPSTimeSet(&gpst);
562 
563  return true;
564 }
565 
571 static bool nmeaProcessGPVTG(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
572 {
573  if (nbParam != 9 && nbParam != 10 /*GTOP GPS seems to gemnerate an extra parameter...*/)
574  return false;
575 
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]);
579 #endif
580 
581  *gpsDataUpdated = false;
582 
583  GpsData->Heading = NMEA_real_to_float(param[1]);
584  GpsData->Groundspeed = NMEA_real_to_float(param[5]) * 0.51444f; // to m/s
585 
586  return true;
587 }
588 
594 static bool nmeaProcessGPZDA(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
595 {
596  if (nbParam != 7)
597  return false;
598 
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]);
602  #endif
603 
604  *gpsDataUpdated = false; // Here we will never provide a new GPS value
605 
606  // No new data data extracted
607  GPSTimeData gpst;
608  GPSTimeGet(&gpst);
609 
610  // get UTC time [hhmmss.sss]
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;
615 
616  // Get Date
617  gpst.Day = atoi(param[2]);
618  gpst.Month = atoi(param[3]);
619  gpst.Year = atoi(param[4]);
620 
621  GPSTimeSet(&gpst);
622  return true;
623 }
624 
625 static GPSSatellitesData gsv_partial;
626 /* Bitmaps of which sentences we're looking for to allow us to handle out-of-order GSVs */
627 static uint8_t gsv_expected_mask;
628 static uint8_t gsv_processed_mask;
629 /* Error counters */
630 static uint16_t gsv_incomplete_error;
631 static uint16_t gsv_duplicate_error;
632 
633 static bool nmeaProcessGPGSV(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
634 {
635  if (nbParam < 4)
636  return false;
637 
638 #ifdef NMEA_DEBUG_GSV
639  DEBUG_MSG("\n Sentence=%s/%s\n", param[2], param[1]);
640  DEBUG_MSG(" Sats=%s\n", param[3]);
641 #endif
642 
643  uint8_t nbSentences = atoi(param[1]);
644  uint8_t currSentence = atoi(param[2]);
645 
646  *gpsDataUpdated = false;
647 
648  if (nbSentences < 1 || nbSentences > 8 || currSentence < 1 || currSentence > nbSentences)
649  return false;
650 
651  gsv_partial.SatsInView = atoi(param[3]);
652 
653  // Find out if this is the first sentence in the GSV set
654  if (currSentence == 1) {
655  if (gsv_expected_mask != gsv_processed_mask) {
656  // We are starting over when we haven't yet finished our previous GSV group
657  gsv_incomplete_error++;
658  }
659 
660  // First GSV sentence in the sequence, reset our expected_mask
661  gsv_expected_mask = (1 << nbSentences) - 1;
662  }
663 
664  uint8_t current_sentence_id = (1 << (currSentence - 1));
665  if (gsv_processed_mask & current_sentence_id) {
666  /* Duplicate sentence in this GSV set */
667  gsv_duplicate_error++;
668  } else {
669  /* Note that we've seen this sentence */
670  gsv_processed_mask |= current_sentence_id;
671  }
672 
673  uint8_t parIdx = 4;
674 
675 #ifdef NMEA_DEBUG_GSV
676  DEBUG_MSG(" PRN:");
677 #endif
678 
679  /* Make sure this sentence can fit in our GPSSatellites object */
680  if ((currSentence * 4) <= NELEMENTS(gsv_partial.PRN)) {
681  /* Process 4 blocks of satellite info */
682  for (uint8_t i = 0; parIdx+4 <= nbParam && i < 4; i++) {
683  uint8_t sat_index = ((currSentence - 1) * 4) + i;
684 
685  // Get sat info
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]);
692 #endif
693  }
694  }
695 #ifdef NMEA_DEBUG_GSV
696  DEBUG_MSG("\n");
697 #endif
698 
699 
700  /* Find out if we're finished processing all GSV sentences in the set */
701  if ((gsv_expected_mask != 0) && (gsv_processed_mask == gsv_expected_mask)) {
702  /* GSV set has been fully processed. Update the GPSSatellites object. */
703  GPSSatellitesSet(&gsv_partial);
704  memset((void *)&gsv_partial, 0, sizeof(gsv_partial));
705  gsv_expected_mask = 0;
706  gsv_processed_mask = 0;
707  }
708 
709  return true;
710 }
711 
717 static bool nmeaProcessGPGSA(GPSPositionData * GpsData, bool* gpsDataUpdated, char* param[], uint8_t nbParam)
718 {
719  if (nbParam != 18)
720  return false;
721 
722 #ifdef NMEA_DEBUG_GSA
723  DEBUG_MSG("\n Status=%s\n", param[2]);
724  DEBUG_MSG(" PDOP=%s\n", param[15]);
725  DEBUG_MSG(" HDOP=%s\n", param[16]);
726  DEBUG_MSG(" VDOP=%s\n", param[17]);
727 #endif
728 
729  *gpsDataUpdated = false;
730 
731  switch (atoi(param[2])) {
732  case 1:
733  GpsData->Status = GPSPOSITION_STATUS_NOFIX;
734  break;
735  case 2:
736  GpsData->Status = GPSPOSITION_STATUS_FIX2D;
737  break;
738  case 3:
739  GpsData->Status = GPSPOSITION_STATUS_FIX3D;
740  break;
741  default:
742  /* Unhandled */
743  return false;
744  break;
745  }
746 
747  // next field: PDOP
748  GpsData->PDOP = NMEA_real_to_float(param[15]);
749 
750  // next field: HDOP
751  GpsData->HDOP = NMEA_real_to_float(param[16]);
752 
753  // next field: VDOP
754  GpsData->VDOP = NMEA_real_to_float(param[17]);
755 
756  // NMEA doesn't have this. Assume we have a nominally 5m accurate
757  // receiver and scale accordingly.
758  GpsData->Accuracy = GpsData->PDOP * 5.0f;
759 
760  return true;
761 }
762 
763 #endif // PIOS_INCLUDE_GPS_NMEA_PARSER
764 
static char * gps_rx_buffer
Definition: GPS.c:84
#define PARSER_OVERRUN
Definition: GPS.h:39
uint16_t gpsRxReceived
Definition: GPS.h:45
Main PiOS header to include all the compiled in PiOS options.
#define NELEMENTS(x)
Definition: pios.h:192
bool NMEA_checksum(char *nmea_sentence)
static char * strsep(char **stringp, const char *delim)
Definition: dronin-strsep.h:44
#define PIOS_DEBUG_Assert(test)
Definition: pios_debug.h:51
bool NMEA_update_position(char *nmea_sentence, GPSPositionData *GpsData)
#define PARSER_ERROR
Definition: GPS.h:40
#define DEBUG_MSG(format,...)
Definition: systemmod.c:71
uint16_t gpsRxChkSumError
Definition: GPS.h:46
Include file of the GPS module.
uint16_t gpsRxParserError
Definition: GPS.h:48
#define PARSER_COMPLETE
Definition: GPS.h:42
uint8_t i
Definition: msp_messages.h:97
tuple f
Definition: px_mkfw.py:81
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
Definition: GPS.c:86
uint16_t gpsRxOverflow
Definition: GPS.h:47
#define NMEA_MAX_PACKET_LENGTH
Definition: NMEA.h:37
uint8_t p
Definition: msp_messages.h:96
#define PARSER_INCOMPLETE
Definition: GPS.h:41