2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup GPSModule GPS Module
6 * @brief Process GPS information (NMEA format)
10 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2016.
11 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
12 * @brief GPS module, handles GPS and NMEA stream
13 * @see The GNU Public License (GPL) Version 3
15 *****************************************************************************/
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 #include "openpilot.h"
35 #if defined(PIOS_INCLUDE_GPS_NMEA_PARSER)
37 #include "gpspositionsensor.h"
40 #include "gpssatellites.h"
43 // #define ENABLE_DEBUG_MSG ///< define to enable debug-messages
44 #define DEBUG_PORT PIOS_COM_TELEM_RF ///< defines which serial port is used for debug-messages
47 #ifdef ENABLE_DEBUG_MSG
48 // #define DEBUG_MSG_IN ///< define to display the incoming NMEA messages
49 // #define DEBUG_PARAMS ///< define to display the incoming NMEA messages split into its parameters
50 // #define DEBUG_MSGID_IN ///< define to display the names of the incoming NMEA messages
51 // #define NMEA_DEBUG_PKT ///< define to enable debug of all NMEA messages
52 // #define NMEA_DEBUG_GGA ///< define to enable debug of GGA messages
53 // #define NMEA_DEBUG_VTG ///< define to enable debug of VTG messages
54 // #define NMEA_DEBUG_RMC ///< define to enable debug of RMC messages
55 // #define NMEA_DEBUG_GSA ///< define to enable debug of GSA messages
56 // #define NMEA_DEBUG_GSV ///< define to enable debug of GSV messages
57 // #define NMEA_DEBUG_ZDA ///< define to enable debug of ZDA messages
58 #define DEBUG_MSG(format, ...) PIOS_COM_SendFormattedString(DEBUG_PORT, format,##__VA_ARGS__)
60 #define DEBUG_MSG(format, ...)
63 #define MAX_NB_PARAMS 20
64 /* NMEA sentence parsers */
68 bool (*handler
)(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
71 static bool nmeaProcessGxGGA(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
72 static bool nmeaProcessGxRMC(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
73 static bool nmeaProcessGxVTG(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
74 static bool nmeaProcessGxGSA(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
75 #if !defined(PIOS_GPS_MINIMAL)
76 static bool nmeaProcessGxZDA(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
77 static bool nmeaProcessGxGSV(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
);
78 #endif // PIOS_GPS_MINIMAL
80 static const struct nmea_parser nmea_parsers
[] = {
83 .handler
= nmeaProcessGxGGA
,
87 .handler
= nmeaProcessGxVTG
,
91 .handler
= nmeaProcessGxGSA
,
95 .handler
= nmeaProcessGxRMC
,
97 #if !defined(PIOS_GPS_MINIMAL)
100 .handler
= nmeaProcessGxZDA
,
104 .handler
= nmeaProcessGxGSV
,
106 #endif // PIOS_GPS_MINIMAL
109 int parse_nmea_stream(uint8_t *rx
, uint8_t len
, char *gps_rx_buffer
, GPSPositionSensorData
*GpsData
, struct GPS_RX_STATS
*gpsRxStats
)
111 static uint8_t rx_count
= 0;
112 static bool start_flag
= false;
113 static bool found_cr
= false;
114 bool goodParse
= false;
120 // detect start while acquiring stream
121 // if we find a $ in the middle it was a bad packet (e.g. maybe UBX binary),
122 // and this may be the start of another packet
123 // silently cancel the current sentence
124 if (c
== '$') { // NMEA identifier found
127 if (!start_flag
) { // if no NMEA identifier ('$') found yet
128 if (c
== '$') { // NMEA identifier found
133 // find a likely candidate for a NMEA string
134 // skip over some e.g. uBlox packets
136 p
= memchr(&rx
[i
], '$', len
- i
);
142 // loop to restart at the $ if there is one
146 if (rx_count
>= NMEA_MAX_PACKET_LENGTH
) {
147 // The buffer is already full and we haven't found a valid NMEA sentence.
148 // Flush the buffer and note the overflow event.
149 gpsRxStats
->gpsRxOverflow
++;
153 gps_rx_buffer
[rx_count
++] = c
;
156 // look for ending '\r\n' sequence
157 if (!found_cr
&& (c
== '\r')) {
159 } else if (found_cr
) {
161 found_cr
= false; // false end flag
163 // The NMEA functions require a zero-terminated string
164 // As we detected \r\n, the string as for sure 2 bytes long, we will also strip the \r\n
165 gps_rx_buffer
[rx_count
- 2] = 0;
167 // prepare to parse next sentence
169 // Our rxBuffer must look like this now:
171 // ... = zero or more bytes of sentence payload
172 // [end_pos - 1] = '\r'
175 // Prepare to consume the sentence from the buffer
177 // Validate the checksum over the sentence
178 if (!NMEA_checksum(&gps_rx_buffer
[1])) { // Invalid checksum. May indicate dropped characters on Rx.
179 // PIOS_DEBUG_PinHigh(2);
180 gpsRxStats
->gpsRxChkSumError
++;
181 // PIOS_DEBUG_PinLow(2);
182 } else { // Valid checksum, use this packet to update the GPS position
183 if (!NMEA_update_position(&gps_rx_buffer
[1], GpsData
)) {
184 // PIOS_DEBUG_PinHigh(2);
185 gpsRxStats
->gpsRxParserError
++;
186 // PIOS_DEBUG_PinLow(2);
188 gpsRxStats
->gpsRxReceived
++;
198 // if so much as one good sentence we return a good status so the connection status says "alive"
199 // if we didn't do this, a lot of garbage (e.g. UBX protocol) mixed in with enough NMEA to fly
200 // might think the GPS was offline
201 return PARSER_COMPLETE
;
203 return PARSER_INCOMPLETE
;
207 static const struct nmea_parser
*NMEA_find_parser_by_prefix(const char *prefix
)
213 for (uint8_t i
= 0; i
< NELEMENTS(nmea_parsers
); i
++) {
214 const struct nmea_parser
*parser
= &nmea_parsers
[i
];
216 /* Use strcmp to check for exact equality over the entire prefix */
217 if (!strcmp(prefix
, parser
->prefix
)) {
218 /* Found an appropriate parser */
223 /* No matching parser for this prefix */
228 * Computes NMEA sentence checksum
229 * \param[in] Buffer for parsed nmea sentence
230 * \return false checksum not valid
231 * \return true checksum valid
233 bool NMEA_checksum(char *nmea_sentence
)
235 uint8_t checksum_computed
= 0;
236 uint8_t checksum_received
;
238 while (*nmea_sentence
!= '\0' && *nmea_sentence
!= '*') {
239 checksum_computed
^= *nmea_sentence
;
243 /* Make sure we're now pointing at the checksum */
244 if (*nmea_sentence
== '\0') {
245 /* Buffer ran out before we found a checksum marker */
249 /* Load the checksum from the buffer */
250 checksum_received
= strtol(nmea_sentence
+ 1, NULL
, 16);
252 // PIOS_COM_SendFormattedStringNonBlocking(COM_DEBUG_USART,"$%d=%d\r\n",checksum_received,checksum_computed);
254 return checksum_computed
== checksum_received
;
258 * This function only exists to deal with a linking
259 * failure in the stdlib function strtof(). This
260 * implementation does not rely on the _sbrk() syscall
261 * like strtof() does.
264 /* Parse a number encoded in a string of the format:
266 * into a signed whole part and an unsigned fractional part.
267 * The fract_units field indicates the units of the fractional part as
268 * 1 whole = 10^fract_units fract
270 static bool NMEA_parse_real(int32_t *whole
, uint32_t *fract
, uint8_t *fract_units
, char *field
)
276 PIOS_DEBUG_Assert(whole
);
277 PIOS_DEBUG_Assert(fract
);
278 PIOS_DEBUG_Assert(fract_units
);
279 PIOS_DEBUG_Assert(field
);
281 field_w
= strsep(&s
, ".");
284 *whole
= strtol(field_w
, NULL
, 10);
287 /* decimal was found so we may have a fractional part */
288 *fract
= strtoul(field_f
, NULL
, 10);
289 *fract_units
= strlen(field_f
);
291 /* no decimal was found, fractional part is zero */
299 static float NMEA_real_to_float(char *nmea_real
)
305 if (!NMEA_parse_real(&whole
, &fract
, &fract_units
, nmea_real
)) {
309 /* Convert to float */
310 return ((float)whole
) + fract
* powf(10.0f
, -fract_units
);
314 * Parse a field in the format:
316 * into a fixed-point representation in units of (degrees * 1e-7)
318 static bool NMEA_latlon_to_fixed_point(int32_t *latlon
, char *nmea_latlon
, bool negative
)
325 PIOS_DEBUG_Assert(nmea_latlon
);
326 PIOS_DEBUG_Assert(latlon
);
328 if (*nmea_latlon
== '\0') { /* empty lat/lon field */
332 if (!NMEA_parse_real(&num_DDDMM
, &num_m
, &units
, nmea_latlon
)) {
336 /* scale up the mmmm[mm] field apropriately depending on # of digits */
337 /* not using 1eN notation because that forces fixed point and lost precision */
340 /* no digits, value is zero so no scaling */
343 num_m
*= 1000000; /* m000000 */
346 num_m
*= 100000; /* mm00000 */
349 num_m
*= 10000; /* mmm0000 */
352 num_m
*= 1000; /* mmmm000 */
355 num_m
*= 100; /* mmmmm00 */
358 num_m
*= 10; /* mmmmmm0 */
361 /* unhandled format */
366 *latlon
= (num_DDDMM
/ 100) * 10000000; /* scale the whole degrees */
367 *latlon
+= (num_DDDMM
% 100) * 10000000 / 60; /* add in the scaled decimal whole minutes */
368 *latlon
+= num_m
/ 60; /* add in the scaled decimal fractional minutes */
379 * Parses a complete NMEA sentence and updates the GPSPositionSensor UAVObject
380 * \param[in] An NMEA sentence with a valid checksum
381 * \return true if the sentence was successfully parsed
382 * \return false if any errors were encountered with the parsing
384 bool NMEA_update_position(char *nmea_sentence
, GPSPositionSensorData
*GpsData
)
386 char *p
= nmea_sentence
;
387 char *params
[MAX_NB_PARAMS
];
391 DEBUG_MSG("\"%s\"\n", nmea_sentence
);
394 // Split the nmea sentence it its parameters, separated by ","
395 // Sample NMEA message: "GPRMC,000131.736,V,,,,,0.00,0.00,060180,,,N*43"
397 // The first parameter starts at the beginning of the message
398 // Skip first two character, allow GL, GN, GP...
404 // After the * comes the "CRC", we are done,
405 *p
= 0; // Zero-terminate this parameter
407 } else if (*p
== ',') {
408 // This is the end of this parameter
409 *p
= 0; // Zero-terminate this parameter
410 // Start new parameter
411 if (nbParams
== MAX_NB_PARAMS
) {
414 params
[nbParams
] = p
+ 1; // For sure there is something at p+1 because at p there is ","
423 for (i
= 0; i
< nbParams
; i
++) {
424 DEBUG_MSG(" %d \"%s\"\n", i
, params
[i
]);
428 // The first parameter is the message name, lets see if we find a parser for it
429 const struct nmea_parser
*parser
;
430 parser
= NMEA_find_parser_by_prefix(params
[0]);
433 #ifdef DEBUG_MSGID_IN
434 DEBUG_MSG(" NO PARSER (\"%s\")\n", params
[0]);
439 #ifdef DEBUG_MSGID_IN
440 DEBUG_MSG("%s %d ", params
[0]);
442 // Send the message to the parser and get it update the GpsData
443 // Information from various different NMEA messages are temporarily
444 // cumulated in the GpsData structure. An actual GPSPositionSensor update
445 // is triggered by GGA messages only. This message type sets the
446 // gpsDataUpdated flag to request this.
447 bool gpsDataUpdated
= false;
449 if (!parser
->handler(GpsData
, &gpsDataUpdated
, params
, nbParams
)) {
451 #ifdef DEBUG_MSGID_IN
452 DEBUG_MSG("PARSE FAILED (\"%s\")\n", params
[0]);
454 if (gpsDataUpdated
&& (GpsData
->Status
== GPSPOSITIONSENSOR_STATUS_NOFIX
)) {
455 // leave my new field alone!
456 GPSPositionSensorBaudRateGet(&GpsData
->BaudRate
);
457 GPSPositionSensorSet(GpsData
);
463 // All is fine :) Update object if data has changed
464 if (gpsDataUpdated
) {
465 #ifdef DEBUG_MSGID_IN
468 // leave my new field alone!
469 GPSPositionSensorBaudRateGet(&GpsData
->BaudRate
);
470 GPSPositionSensorSet(GpsData
);
473 #ifdef DEBUG_MSGID_IN
481 * Parse an NMEA GxGGA sentence and update the given UAVObject
482 * \param[in] A pointer to a GPSPositionSensor UAVObject to be updated.
483 * \param[in] An NMEA sentence with a valid checksum
485 static bool nmeaProcessGxGGA(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
491 #ifdef NMEA_DEBUG_GGA
492 DEBUG_MSG("\n UTC=%s\n", param
[1]);
493 DEBUG_MSG(" Lat=%s %s\n", param
[2], param
[3]);
494 DEBUG_MSG(" Long=%s %s\n", param
[4], param
[5]);
495 DEBUG_MSG(" Fix=%s\n", param
[6]);
496 DEBUG_MSG(" Sat=%s\n", param
[7]);
497 DEBUG_MSG(" HDOP=%s\n", param
[8]);
498 DEBUG_MSG(" Alt=%s %s\n", param
[9], param
[10]);
499 DEBUG_MSG(" GeoidSep=%s %s\n\n", param
[11]);
502 *gpsDataUpdated
= true;
504 // check for invalid GPS fix
505 // do this first to make sure we get this information, even if later checks exit
506 // this function early
507 if (param
[6][0] == '0') {
508 GpsData
->Status
= GPSPOSITIONSENSOR_STATUS_NOFIX
; // treat invalid fix as NOFIX
511 // get latitude [DDMM.mmmmm] [N|S]
512 if (!NMEA_latlon_to_fixed_point(&GpsData
->Latitude
, param
[2], param
[3][0] == 'S')) {
516 // get longitude [dddmm.mmmmm] [E|W]
517 if (!NMEA_latlon_to_fixed_point(&GpsData
->Longitude
, param
[4], param
[5][0] == 'W')) {
521 // get number of satellites used in GPS solution
522 GpsData
->Satellites
= atoi(param
[7]);
524 // get altitude (in meters mm.m)
525 GpsData
->Altitude
= NMEA_real_to_float(param
[9]);
528 GpsData
->GeoidSeparation
= NMEA_real_to_float(param
[11]);
529 GpsData
->SensorType
= GPSPOSITIONSENSOR_SENSORTYPE_NMEA
;
534 * Parse an NMEA GxRMC sentence and update the given UAVObject
535 * \param[in] A pointer to a GPSPositionSensor UAVObject to be updated.
536 * \param[in] An NMEA sentence with a valid checksum
538 static bool nmeaProcessGxRMC(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
544 #ifdef NMEA_DEBUG_RMC
545 DEBUG_MSG("\n UTC=%s\n", param
[1]);
546 DEBUG_MSG(" Lat=%s %s\n", param
[3], param
[4]);
547 DEBUG_MSG(" Long=%s %s\n", param
[5], param
[6]);
548 DEBUG_MSG(" Speed=%s\n", param
[7]);
549 DEBUG_MSG(" Course=%s\n", param
[8]);
550 DEBUG_MSG(" DateOfFix=%s\n\n", param
[9]);
553 *gpsDataUpdated
= false;
555 #if !defined(PIOS_GPS_MINIMAL)
559 // get UTC time [hhmmss.sss]
560 float hms
= NMEA_real_to_float(param
[1]);
561 gpst
.Second
= (int)hms
% 100;
562 gpst
.Minute
= (((int)hms
- gpst
.Second
) / 100) % 100;
563 gpst
.Hour
= (int)hms
/ 10000;
564 #endif // PIOS_GPS_MINIMAL
566 // don't process void sentences
567 if (param
[2][0] == 'V') {
571 // get latitude [DDMM.mmmmm] [N|S]
572 if (!NMEA_latlon_to_fixed_point(&GpsData
->Latitude
, param
[3], param
[4][0] == 'S')) {
576 // get longitude [dddmm.mmmmm] [E|W]
577 if (!NMEA_latlon_to_fixed_point(&GpsData
->Longitude
, param
[5], param
[6][0] == 'W')) {
581 // get speed in knots
582 GpsData
->Groundspeed
= NMEA_real_to_float(param
[7]) * 0.51444f
; // to m/s
585 GpsData
->Heading
= NMEA_real_to_float(param
[8]);
587 #if !defined(PIOS_GPS_MINIMAL)
589 // TODO: Should really not use a float here to be safe
590 float date
= NMEA_real_to_float(param
[9]);
591 gpst
.Year
= (int)date
% 100;
592 gpst
.Month
= (((int)date
- gpst
.Year
) / 100) % 100;
593 gpst
.Day
= (int)(date
/ 10000);
596 #endif // PIOS_GPS_MINIMAL
602 * Parse an NMEA GxVTG sentence and update the given UAVObject
603 * \param[in] A pointer to a GPSPositionSensor UAVObject to be updated.
604 * \param[in] An NMEA sentence with a valid checksum
606 static bool nmeaProcessGxVTG(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
608 if (nbParam
!= 9 && nbParam
!= 10 /*GTOP GPS seems to gemnerate an extra parameter...*/) {
612 #ifdef NMEA_DEBUG_RMC
613 DEBUG_MSG("\n Heading=%s %s\n", param
[1], param
[2]);
614 DEBUG_MSG(" GroundSpeed=%s %s\n", param
[5], param
[6]);
617 *gpsDataUpdated
= false;
619 GpsData
->Heading
= NMEA_real_to_float(param
[1]);
620 GpsData
->Groundspeed
= NMEA_real_to_float(param
[5]) * 0.51444f
; // to m/s
625 #if !defined(PIOS_GPS_MINIMAL)
627 * Parse an NMEA GxZDA sentence and update the @ref GPSTime object
628 * \param[in] A pointer to a GPSPositionSensor UAVObject to be updated (unused).
629 * \param[in] An NMEA sentence with a valid checksum
631 static bool nmeaProcessGxZDA(__attribute__((unused
)) GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
637 #ifdef NMEA_DEBUG_ZDA
638 DEBUG_MSG("\n Time=%s (hhmmss.ss)\n", param
[1]);
639 DEBUG_MSG(" Date=%s/%s/%s (d/m/y)\n", param
[2], param
[3], param
[4]);
642 *gpsDataUpdated
= false; // Here we will never provide a new GPS value
644 // No new data data extracted
648 // get UTC time [hhmmss.sss]
649 float hms
= NMEA_real_to_float(param
[1]);
650 gpst
.Second
= (int)hms
% 100;
651 gpst
.Minute
= (((int)hms
- gpst
.Second
) / 100) % 100;
652 gpst
.Hour
= (int)hms
/ 10000;
655 gpst
.Day
= atoi(param
[2]);
656 gpst
.Month
= atoi(param
[3]);
657 gpst
.Year
= atoi(param
[4]);
663 static GPSSatellitesData gsv_partial
;
664 /* Bitmaps of which sentences we're looking for to allow us to handle out-of-order GSVs */
665 static uint8_t gsv_expected_mask
;
666 static uint8_t gsv_processed_mask
;
668 static uint16_t gsv_incomplete_error
;
669 static uint16_t gsv_duplicate_error
;
671 static bool nmeaProcessGxGSV(__attribute__((unused
)) GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
677 #ifdef NMEA_DEBUG_GSV
678 DEBUG_MSG("\n Sentence=%s/%s\n", param
[2], param
[1]);
679 DEBUG_MSG(" Sats=%s\n", param
[3]);
682 uint8_t nbSentences
= atoi(param
[1]);
683 uint8_t currSentence
= atoi(param
[2]);
685 *gpsDataUpdated
= false;
687 if (nbSentences
< 1 || nbSentences
> 8 || currSentence
< 1 || currSentence
> nbSentences
) {
691 gsv_partial
.SatsInView
= atoi(param
[3]);
693 // Find out if this is the first sentence in the GSV set
694 if (currSentence
== 1) {
695 if (gsv_expected_mask
!= gsv_processed_mask
) {
696 // We are starting over when we haven't yet finished our previous GSV group
697 gsv_incomplete_error
++;
700 // First GSV sentence in the sequence, reset our expected_mask
701 gsv_expected_mask
= (1 << nbSentences
) - 1;
704 uint8_t current_sentence_id
= (1 << (currSentence
- 1));
705 if (gsv_processed_mask
& current_sentence_id
) {
706 /* Duplicate sentence in this GSV set */
707 gsv_duplicate_error
++;
709 /* Note that we've seen this sentence */
710 gsv_processed_mask
|= current_sentence_id
;
715 #ifdef NMEA_DEBUG_GSV
719 /* Make sure this sentence can fit in our GPSSatellites object */
720 if ((currSentence
* 4) <= NELEMENTS(gsv_partial
.PRN
)) {
721 /* Process 4 blocks of satellite info */
722 for (uint8_t i
= 0; parIdx
+ 4 <= nbParam
&& i
< 4; i
++) {
723 uint8_t sat_index
= ((currSentence
- 1) * 4) + i
;
726 gsv_partial
.PRN
[sat_index
] = atoi(param
[parIdx
++]);
727 gsv_partial
.Elevation
[sat_index
] = atoi(param
[parIdx
++]);
728 gsv_partial
.Azimuth
[sat_index
] = atoi(param
[parIdx
++]);
729 gsv_partial
.SNR
[sat_index
] = atoi(param
[parIdx
++]);
730 #ifdef NMEA_DEBUG_GSV
731 DEBUG_MSG(" %d", gsv_partial
.PRN
[sat_index
]);
735 #ifdef NMEA_DEBUG_GSV
740 /* Find out if we're finished processing all GSV sentences in the set */
741 if ((gsv_expected_mask
!= 0) && (gsv_processed_mask
== gsv_expected_mask
)) {
742 /* GSV set has been fully processed. Update the GPSSatellites object. */
743 GPSSatellitesSet(&gsv_partial
);
744 memset((void *)&gsv_partial
, 0, sizeof(gsv_partial
));
745 gsv_expected_mask
= 0;
746 gsv_processed_mask
= 0;
751 #endif // PIOS_GPS_MINIMAL
754 * Parse an NMEA GPGSA sentence and update the given UAVObject
755 * \param[in] A pointer to a GPSPositionSensor UAVObject to be updated.
756 * \param[in] An NMEA sentence with a valid checksum
758 static bool nmeaProcessGxGSA(GPSPositionSensorData
*GpsData
, bool *gpsDataUpdated
, char *param
[], uint8_t nbParam
)
764 #ifdef NMEA_DEBUG_GSA
765 DEBUG_MSG("\n Status=%s\n", param
[2]);
766 DEBUG_MSG(" PDOP=%s\n", param
[15]);
767 DEBUG_MSG(" HDOP=%s\n", param
[16]);
768 DEBUG_MSG(" VDOP=%s\n", param
[17]);
771 *gpsDataUpdated
= false;
773 switch (atoi(param
[2])) {
775 GpsData
->Status
= GPSPOSITIONSENSOR_STATUS_NOFIX
;
778 GpsData
->Status
= GPSPOSITIONSENSOR_STATUS_FIX2D
;
781 GpsData
->Status
= GPSPOSITIONSENSOR_STATUS_FIX3D
;
791 GpsData
->PDOP
= NMEA_real_to_float(param
[15]);
794 GpsData
->HDOP
= NMEA_real_to_float(param
[16]);
797 GpsData
->VDOP
= NMEA_real_to_float(param
[17]);
802 #endif // PIOS_INCLUDE_GPS_NMEA_PARSER