Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / flight / modules / GPS / UBX.c
blobe8a79a3b51859b4a0444e135a28ae7f5b17042d8
1 /**
2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
4 * @{
5 * @addtogroup GPSModule GPS Module
6 * @brief Process GPS information (UBX binary format)
7 * @{
9 * @file UBX.c
10 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
11 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
12 * @brief GPS module, handles GPS and UBX 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
25 * for more details.
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"
33 #include "pios.h"
34 #include "pios_math.h"
35 #include <pios_helpers.h>
36 #include <pios_delay.h>
38 #if defined(PIOS_INCLUDE_GPS_UBX_PARSER)
40 #include "inc/UBX.h"
41 #include "inc/GPS.h"
42 #include <string.h>
44 #if !defined(PIOS_GPS_MINIMAL)
45 #include <auxmagsupport.h>
46 static bool useMag = false;
47 #endif /* !defined(PIOS_GPS_MINIMAL) */
49 // this is set and used by this low level ubx code
50 // it is also reset by the ubx configuration code (UBX6 vs. UBX7) in ubx_autoconfig.c
51 GPSPositionSensorSensorTypeOptions ubxSensorType = GPSPOSITIONSENSOR_SENSORTYPE_UNKNOWN;
53 static bool usePvt = false;
54 static uint32_t lastPvtTime = 0;
56 // parse table item
57 typedef struct {
58 uint8_t msgClass;
59 uint8_t msgID;
60 void (*handler)(struct UBXPacket *, GPSPositionSensorData *GpsPosition);
61 } ubx_message_handler;
63 // parsing functions, roughly ordered by reception rate (higher rate messages on top)
64 static void parse_ubx_nav_posllh(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
65 static void parse_ubx_nav_velned(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
66 static void parse_ubx_nav_sol(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
67 static void parse_ubx_nav_dop(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
68 #if !defined(PIOS_GPS_MINIMAL)
69 static void parse_ubx_nav_pvt(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
70 static void parse_ubx_nav_timeutc(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
71 static void parse_ubx_nav_svinfo(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
72 static void parse_ubx_op_sys(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
73 static void parse_ubx_op_mag(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
74 static void parse_ubx_ack_ack(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
75 static void parse_ubx_ack_nak(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
76 static void parse_ubx_mon_ver(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
77 #endif /* !defined(PIOS_GPS_MINIMAL) */
79 const ubx_message_handler ubx_handler_table[] = {
80 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_POSLLH, .handler = &parse_ubx_nav_posllh },
81 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_VELNED, .handler = &parse_ubx_nav_velned },
82 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_SOL, .handler = &parse_ubx_nav_sol },
83 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_DOP, .handler = &parse_ubx_nav_dop },
84 #if !defined(PIOS_GPS_MINIMAL)
85 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_PVT, .handler = &parse_ubx_nav_pvt },
86 { .msgClass = UBX_CLASS_OP_CUST, .msgID = UBX_ID_OP_MAG, .handler = &parse_ubx_op_mag },
87 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_SVINFO, .handler = &parse_ubx_nav_svinfo },
88 { .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_TIMEUTC, .handler = &parse_ubx_nav_timeutc },
90 { .msgClass = UBX_CLASS_OP_CUST, .msgID = UBX_ID_OP_SYS, .handler = &parse_ubx_op_sys },
91 { .msgClass = UBX_CLASS_ACK, .msgID = UBX_ID_ACK_ACK, .handler = &parse_ubx_ack_ack },
92 { .msgClass = UBX_CLASS_ACK, .msgID = UBX_ID_ACK_NAK, .handler = &parse_ubx_ack_nak },
94 { .msgClass = UBX_CLASS_MON, .msgID = UBX_ID_MON_VER, .handler = &parse_ubx_mon_ver },
95 #endif /* !defined(PIOS_GPS_MINIMAL) */
97 #define UBX_HANDLER_TABLE_SIZE NELEMENTS(ubx_handler_table)
99 // detected hw version
100 int32_t ubxHwVersion = -1;
102 // Last received Ack/Nak
103 struct UBX_ACK_ACK ubxLastAck;
104 struct UBX_ACK_NAK ubxLastNak;
106 // If a PVT sentence is received in the last UBX_PVT_TIMEOUT (ms) timeframe it disables VELNED/POSLLH/SOL/TIMEUTC
107 #define UBX_PVT_TIMEOUT (1000)
109 // parse incoming character stream for messages in UBX binary format
110 int parse_ubx_stream(uint8_t *rx, uint16_t len, char *gps_rx_buffer, GPSPositionSensorData *GpsData, struct GPS_RX_STATS *gpsRxStats)
112 enum proto_states {
113 START,
114 UBX_SY2,
115 UBX_CLASS,
116 UBX_ID,
117 UBX_LEN1,
118 UBX_LEN2,
119 UBX_PAYLOAD,
120 UBX_CHK1,
121 UBX_CHK2,
122 FINISHED
124 enum restart_states {
125 RESTART_WITH_ERROR,
126 RESTART_NO_ERROR
128 static uint16_t rx_count = 0;
129 static enum proto_states proto_state = START;
130 struct UBXPacket *ubx = (struct UBXPacket *)gps_rx_buffer;
131 int ret = PARSER_INCOMPLETE; // message not (yet) complete
132 uint16_t i = 0;
133 uint16_t restart_index = 0;
134 enum restart_states restart_state;
135 uint8_t c;
137 // switch continue is the normal condition and comes back to here for another byte
138 // switch break is the error state that branches to the end and restarts the scan at the byte after the first sync byte
139 while (i < len) {
140 c = rx[i++];
141 switch (proto_state) {
142 case START: // detect protocol
143 if (c == UBX_SYNC1) { // first UBX sync char found
144 proto_state = UBX_SY2;
145 // restart here, at byte after SYNC1, if we fail to parse
146 restart_index = i;
148 continue;
149 case UBX_SY2:
150 if (c == UBX_SYNC2) { // second UBX sync char found
151 proto_state = UBX_CLASS;
152 } else {
153 restart_state = RESTART_NO_ERROR;
154 break;
156 continue;
157 case UBX_CLASS:
158 ubx->header.class = c;
159 proto_state = UBX_ID;
160 continue;
161 case UBX_ID:
162 ubx->header.id = c;
163 proto_state = UBX_LEN1;
164 continue;
165 case UBX_LEN1:
166 ubx->header.len = c;
167 proto_state = UBX_LEN2;
168 continue;
169 case UBX_LEN2:
170 ubx->header.len += (c << 8);
171 if (ubx->header.len > sizeof(UBXPayload)) {
172 gpsRxStats->gpsRxOverflow++;
173 #if defined(PIOS_GPS_MINIMAL)
174 restart_state = RESTART_NO_ERROR;
175 #else
176 /* UBX-NAV-SVINFO (id 30) and UBX-NAV-SAT (id 35) packets are NOT critical to the navigation.
177 Their only use is to update the nice GPS constellation widget in the GCS.
178 These packets become very large when a lot of SV (Space Vehicles) are tracked. (8 + 12 * <number of SV>) bytes
180 In the case of 3 simultaneously enabled GNSS, it is easy to reach the currently defined tracking limit of 32 SV.
181 The memory taken up by this is 8 + 12 * 32 = 392 bytes
183 The NEO-M8N has been seen to send out information for more than 32 SV. This causes overflow errors.
184 We will ignore these informational packets if they become too large.
185 The downside of this is no more constellation updates in the GCS when we reach the limit.
187 An alternative fix could be to increment the maximum number of satellites: MAX_SVS and UBX_CFG_GNSS_NUMCH_VER8 in UBX.h
188 This would use extra memory for little gain. Both fixes can be combined.
190 Tests indicate that, once we reach this amount of tracked SV, the NEO-M8N module positioning output
191 becomes jittery (in time) and therefore less accurate.
193 The recommendation is to limit the maximum number of simultaneously used GNSS to a value of 2.
194 This will help keep the number of tracked satellites in line.
196 if ((ubx->header.class == 0x01) && ((ubx->header.id == 0x30) || (ubx->header.id == 0x35))) {
197 restart_state = RESTART_NO_ERROR;
198 } else {
199 restart_state = RESTART_WITH_ERROR;
201 #endif
202 // We won't see the end of the packet. Which means it is useless to do any further processing.
203 // Therefore scan for the start of the next packet.
204 proto_state = START;
205 break;
206 } else {
207 if (ubx->header.len == 0) {
208 proto_state = UBX_CHK1;
209 } else {
210 proto_state = UBX_PAYLOAD;
211 rx_count = 0;
214 continue;
215 case UBX_PAYLOAD:
216 if (rx_count < ubx->header.len) {
217 ubx->payload.payload[rx_count] = c;
218 if (++rx_count == ubx->header.len) {
219 proto_state = UBX_CHK1;
222 continue;
223 case UBX_CHK1:
224 ubx->header.ck_a = c;
225 proto_state = UBX_CHK2;
226 continue;
227 case UBX_CHK2:
228 ubx->header.ck_b = c;
229 // OP GPSV9 sends data with bad checksums this appears to happen because it drops data
230 // this has been proven by running it without autoconfig and testing:
231 // data coming from OPV9 "GPS+MCU" port the checksum errors happen roughly every 5 to 30 seconds
232 // same data coming from OPV9 "GPS Only" port the checksums are always good
233 // this also occasionally causes parse_ubx_message() to issue alarms because not all the messages were received
234 // see OP GPSV9 comment in parse_ubx_message() for further information
235 if (checksum_ubx_message(ubx)) {
236 gpsRxStats->gpsRxReceived++;
237 proto_state = START;
238 // overwrite PARSER_INCOMPLETE with PARSER_COMPLETE
239 // but don't overwrite PARSER_ERROR with PARSER_COMPLETE
240 // pass PARSER_ERROR to caller if it happens even once
241 // only pass PARSER_COMPLETE back to caller if we parsed a full set of GPS data
242 // that allows the caller to know if we are parsing GPS data
243 // or just other packets for some reason (mis-configuration)
244 if (parse_ubx_message(ubx, GpsData) == GPSPOSITIONSENSOR_OBJID
245 && ret == PARSER_INCOMPLETE) {
246 ret = PARSER_COMPLETE;
248 } else {
249 gpsRxStats->gpsRxChkSumError++;
250 restart_state = RESTART_WITH_ERROR;
251 break;
253 continue;
254 default:
255 continue;
258 // this simple restart doesn't work across calls
259 // but it does work within a single call
260 // and it does the expected thing across calls
261 // if restarting due to error detected in 2nd call to this function (on split packet)
262 // then we just restart at index 0, which is mid-packet, not the second byte
263 if (restart_state == RESTART_WITH_ERROR) {
264 ret = PARSER_ERROR; // inform caller that we found at least one error (along with 0 or more good packets)
266 rx += restart_index; // restart parsing just past the most recent SYNC1
267 len -= restart_index;
268 i = 0;
269 proto_state = START;
272 return ret;
275 // Keep track of various GPS messages needed to make up a single UAVO update
276 // time-of-week timestamp is used to correlate matching messages
277 #define POSLLH_RECEIVED (1 << 0)
278 #define STATUS_RECEIVED (1 << 1)
279 #define DOP_RECEIVED (1 << 2)
280 #define VELNED_RECEIVED (1 << 3)
281 #define SOL_RECEIVED (1 << 4)
282 #define ALL_RECEIVED (SOL_RECEIVED | VELNED_RECEIVED | DOP_RECEIVED | POSLLH_RECEIVED)
283 #define NONE_RECEIVED 0
285 static struct msgtracker {
286 uint32_t currentTOW; // TOW of the message set currently in progress
287 uint8_t msg_received; // keep track of received message types
288 } msgtracker;
290 // Check if a message belongs to the current data set and register it as 'received'
291 bool check_msgtracker(uint32_t tow, uint8_t msg_flag)
293 if (tow > msgtracker.currentTOW ? true // start of a new message set
294 : (msgtracker.currentTOW - tow > 6 * 24 * 3600 * 1000)) { // 6 days, TOW wrap around occured
295 msgtracker.currentTOW = tow;
296 msgtracker.msg_received = NONE_RECEIVED;
297 } else if (tow < msgtracker.currentTOW) { // message outdated (don't process)
298 return false;
301 msgtracker.msg_received |= msg_flag; // register reception of this msg type
302 return true;
305 bool checksum_ubx_message(struct UBXPacket *ubx)
307 int i;
308 uint8_t ck_a, ck_b;
310 ck_a = ubx->header.class;
311 ck_b = ck_a;
313 ck_a += ubx->header.id;
314 ck_b += ck_a;
316 ck_a += ubx->header.len & 0xff;
317 ck_b += ck_a;
319 ck_a += ubx->header.len >> 8;
320 ck_b += ck_a;
322 for (i = 0; i < ubx->header.len; i++) {
323 ck_a += ubx->payload.payload[i];
324 ck_b += ck_a;
327 if (ubx->header.ck_a == ck_a &&
328 ubx->header.ck_b == ck_b) {
329 return true;
330 } else {
331 return false;
335 static void parse_ubx_nav_posllh(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
337 if (usePvt) {
338 return;
340 struct UBX_NAV_POSLLH *posllh = &ubx->payload.nav_posllh;
342 if (check_msgtracker(posllh->iTOW, POSLLH_RECEIVED)) {
343 if (GpsPosition->Status != GPSPOSITIONSENSOR_STATUS_NOFIX) {
344 GpsPosition->Altitude = (float)posllh->hMSL * 0.001f;
345 GpsPosition->GeoidSeparation = (float)(posllh->height - posllh->hMSL) * 0.001f;
346 GpsPosition->Latitude = posllh->lat;
347 GpsPosition->Longitude = posllh->lon;
352 static void parse_ubx_nav_sol(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
354 if (usePvt) {
355 return;
357 struct UBX_NAV_SOL *sol = &ubx->payload.nav_sol;
358 if (check_msgtracker(sol->iTOW, SOL_RECEIVED)) {
359 GpsPosition->Satellites = sol->numSV;
361 if (sol->flags & STATUS_FLAGS_GPSFIX_OK) {
362 switch (sol->gpsFix) {
363 case STATUS_GPSFIX_2DFIX:
364 GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_FIX2D;
365 break;
366 case STATUS_GPSFIX_3DFIX:
367 GpsPosition->Status = (sol->flags & STATUS_FLAGS_DIFFSOLN) ? GPSPOSITIONSENSOR_STATUS_FIX3DDGNSS : GPSPOSITIONSENSOR_STATUS_FIX3D;
368 break;
369 default: GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
371 } else { // fix is not valid so we make sure to treat is as NOFIX
372 GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
377 static void parse_ubx_nav_dop(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
379 struct UBX_NAV_DOP *dop = &ubx->payload.nav_dop;
381 if (check_msgtracker(dop->iTOW, DOP_RECEIVED)) {
382 GpsPosition->HDOP = (float)dop->hDOP * 0.01f;
383 GpsPosition->VDOP = (float)dop->vDOP * 0.01f;
384 GpsPosition->PDOP = (float)dop->pDOP * 0.01f;
388 static void parse_ubx_nav_velned(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
390 if (usePvt) {
391 return;
393 GPSVelocitySensorData GpsVelocity;
394 struct UBX_NAV_VELNED *velned = &ubx->payload.nav_velned;
395 if (check_msgtracker(velned->iTOW, VELNED_RECEIVED)) {
396 if (GpsPosition->Status != GPSPOSITIONSENSOR_STATUS_NOFIX) {
397 GpsVelocity.North = (float)velned->velN / 100.0f;
398 GpsVelocity.East = (float)velned->velE / 100.0f;
399 GpsVelocity.Down = (float)velned->velD / 100.0f;
400 GPSVelocitySensorSet(&GpsVelocity);
401 GpsPosition->Groundspeed = (float)velned->gSpeed * 0.01f;
402 GpsPosition->Heading = (float)velned->heading * 1.0e-5f;
407 #if !defined(PIOS_GPS_MINIMAL)
408 static void parse_ubx_nav_pvt(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
410 lastPvtTime = PIOS_DELAY_GetuS();
412 GPSVelocitySensorData GpsVelocity;
413 struct UBX_NAV_PVT *pvt = &ubx->payload.nav_pvt;
414 check_msgtracker(pvt->iTOW, (ALL_RECEIVED));
416 GpsVelocity.North = (float)pvt->velN * 0.001f;
417 GpsVelocity.East = (float)pvt->velE * 0.001f;
418 GpsVelocity.Down = (float)pvt->velD * 0.001f;
419 GPSVelocitySensorSet(&GpsVelocity);
421 GpsPosition->Groundspeed = (float)pvt->gSpeed * 0.001f;
422 GpsPosition->Heading = (float)pvt->heading * 1.0e-5f;
423 GpsPosition->Altitude = (float)pvt->hMSL * 0.001f;
424 GpsPosition->GeoidSeparation = (float)(pvt->height - pvt->hMSL) * 0.001f;
425 GpsPosition->Latitude = pvt->lat;
426 GpsPosition->Longitude = pvt->lon;
427 GpsPosition->Satellites = pvt->numSV;
428 GpsPosition->PDOP = pvt->pDOP * 0.01f;
430 if (pvt->flags & PVT_FLAGS_GNSSFIX_OK) {
431 switch (pvt->fixType) {
432 case PVT_FIX_TYPE_2D:
433 GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_FIX2D;
434 break;
435 case PVT_FIX_TYPE_3D:
436 GpsPosition->Status = (pvt->flags & PVT_FLAGS_DIFFSOLN) ? GPSPOSITIONSENSOR_STATUS_FIX3DDGNSS : GPSPOSITIONSENSOR_STATUS_FIX3D;
437 break;
438 default: GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
440 } else { // fix is not valid so we make sure to treat is as NOFIX
441 GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
444 if (pvt->valid & PVT_VALID_VALIDTIME) {
445 // Time is valid, set GpsTime
446 GPSTimeData GpsTime;
448 GpsTime.Year = pvt->year;
449 GpsTime.Month = pvt->month;
450 GpsTime.Day = pvt->day;
451 GpsTime.Hour = pvt->hour;
452 GpsTime.Minute = pvt->min;
453 GpsTime.Second = pvt->sec;
455 GPSTimeSet(&GpsTime);
459 static void parse_ubx_nav_timeutc(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
461 if (usePvt) {
462 return;
465 struct UBX_NAV_TIMEUTC *timeutc = &ubx->payload.nav_timeutc;
466 // Test if time is valid
467 if ((timeutc->valid & TIMEUTC_VALIDTOW) && (timeutc->valid & TIMEUTC_VALIDWKN)) {
468 // Time is valid, set GpsTime
469 GPSTimeData GpsTime;
471 GpsTime.Year = timeutc->year;
472 GpsTime.Month = timeutc->month;
473 GpsTime.Day = timeutc->day;
474 GpsTime.Hour = timeutc->hour;
475 GpsTime.Minute = timeutc->min;
476 GpsTime.Second = timeutc->sec;
477 GpsTime.Millisecond = (int16_t)(timeutc->nano / 1000000);
478 GPSTimeSet(&GpsTime);
479 } else {
480 // Time is not valid, nothing to do
481 return;
485 static void parse_ubx_nav_svinfo(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
487 uint8_t chan;
488 GPSSatellitesData svdata;
489 struct UBX_NAV_SVINFO *svinfo = &ubx->payload.nav_svinfo;
491 svdata.SatsInView = 0;
493 // First, use slots for SVs actually being received
494 for (chan = 0; chan < svinfo->numCh; chan++) {
495 if (svdata.SatsInView < GPSSATELLITES_PRN_NUMELEM && svinfo->sv[chan].cno > 0) {
496 svdata.Azimuth[svdata.SatsInView] = svinfo->sv[chan].azim;
497 svdata.Elevation[svdata.SatsInView] = svinfo->sv[chan].elev;
498 svdata.PRN[svdata.SatsInView] = svinfo->sv[chan].svid;
499 svdata.SNR[svdata.SatsInView] = svinfo->sv[chan].cno;
500 svdata.SatsInView++;
504 // Now try to add the rest
505 for (chan = 0; chan < svinfo->numCh; chan++) {
506 if (svdata.SatsInView < GPSSATELLITES_PRN_NUMELEM && 0 == svinfo->sv[chan].cno) {
507 svdata.Azimuth[svdata.SatsInView] = svinfo->sv[chan].azim;
508 svdata.Elevation[svdata.SatsInView] = svinfo->sv[chan].elev;
509 svdata.PRN[svdata.SatsInView] = svinfo->sv[chan].svid;
510 svdata.SNR[svdata.SatsInView] = svinfo->sv[chan].cno;
511 svdata.SatsInView++;
515 // fill remaining slots (if any)
516 for (chan = svdata.SatsInView; chan < GPSSATELLITES_PRN_NUMELEM; chan++) {
517 svdata.Azimuth[chan] = 0;
518 svdata.Elevation[chan] = 0;
519 svdata.PRN[chan] = 0;
520 svdata.SNR[chan] = 0;
523 GPSSatellitesSet(&svdata);
526 static void parse_ubx_ack_ack(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
528 struct UBX_ACK_ACK *ack_ack = &ubx->payload.ack_ack;
530 ubxLastAck = *ack_ack;
533 static void parse_ubx_ack_nak(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
535 struct UBX_ACK_NAK *ack_nak = &ubx->payload.ack_nak;
537 ubxLastNak = *ack_nak;
540 static void parse_ubx_mon_ver(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
542 struct UBX_MON_VER *mon_ver = &ubx->payload.mon_ver;
544 ubxHwVersion = atoi(mon_ver->hwVersion);
545 ubxSensorType = (ubxHwVersion >= UBX_HW_VERSION_8) ? GPSPOSITIONSENSOR_SENSORTYPE_UBX8 :
546 ((ubxHwVersion >= UBX_HW_VERSION_7) ? GPSPOSITIONSENSOR_SENSORTYPE_UBX7 : GPSPOSITIONSENSOR_SENSORTYPE_UBX);
547 // send sensor type right now because on UBX NEMA we don't get a full set of messages
548 // and we want to be able to see sensor type even on UBX NEMA GPS's
549 GPSPositionSensorSensorTypeSet((uint8_t *)&ubxSensorType);
552 static void parse_ubx_op_sys(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
554 struct UBX_OP_SYSINFO *sysinfo = &ubx->payload.op_sysinfo;
555 GPSExtendedStatusData data;
557 data.FlightTime = sysinfo->flightTime;
558 data.BoardType[0] = sysinfo->board_type;
559 data.BoardType[1] = sysinfo->board_revision;
560 memcpy(&data.FirmwareHash, &sysinfo->sha1sum, GPSEXTENDEDSTATUS_FIRMWAREHASH_NUMELEM);
561 memcpy(&data.FirmwareTag, &sysinfo->commit_tag_name, GPSEXTENDEDSTATUS_FIRMWARETAG_NUMELEM);
562 data.Options = sysinfo->options;
563 data.Status = GPSEXTENDEDSTATUS_STATUS_GPSV9;
564 GPSExtendedStatusSet(&data);
567 static void parse_ubx_op_mag(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
569 if (!useMag) {
570 return;
572 struct UBX_OP_MAG *mag = &ubx->payload.op_mag;
573 float mags[3] = { mag->x, mag->y, mag->z };
574 auxmagsupport_publish_samples(mags, AUXMAGSENSOR_STATUS_OK);
576 #endif /* if !defined(PIOS_GPS_MINIMAL) */
578 // UBX message parser
579 // returns UAVObjectID if a UAVObject structure is ready for further processing
580 uint32_t parse_ubx_message(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
582 uint32_t id = 0;
583 static bool ubxInitialized = false;
585 if (!ubxInitialized) {
586 // initialize dop values. If no DOP sentence is received it is safer to initialize them to a high value rather than 0.
587 GpsPosition->HDOP = 99.99f;
588 GpsPosition->PDOP = 99.99f;
589 GpsPosition->VDOP = 99.99f;
590 ubxInitialized = true;
592 // is it using PVT?
593 usePvt = (lastPvtTime) && (PIOS_DELAY_GetuSSince(lastPvtTime) < UBX_PVT_TIMEOUT * 1000);
594 for (uint8_t i = 0; i < UBX_HANDLER_TABLE_SIZE; i++) {
595 const ubx_message_handler *handler = &ubx_handler_table[i];
596 if (handler->msgClass == ubx->header.class && handler->msgID == ubx->header.id) {
597 handler->handler(ubx, GpsPosition);
598 break;
602 GpsPosition->SensorType = ubxSensorType;
604 if (msgtracker.msg_received == ALL_RECEIVED) {
605 // leave BaudRate field alone!
606 GPSPositionSensorBaudRateGet(&GpsPosition->BaudRate);
607 GPSPositionSensorSet(GpsPosition);
608 msgtracker.msg_received = NONE_RECEIVED;
609 id = GPSPOSITIONSENSOR_OBJID;
610 } else {
611 uint8_t status;
612 GPSPositionSensorStatusGet(&status);
613 if (status == GPSPOSITIONSENSOR_STATUS_NOGPS) {
614 // Some ubx thing has been received so GPS is there
616 // OP GPSV9 will sometimes cause this NOFIX
617 // because GPSV9 drops data which causes checksum errors which causes GPS.c to set the status to NOGPS
618 // see OP GPSV9 comment in parse_ubx_stream() for further information
619 status = GPSPOSITIONSENSOR_STATUS_NOFIX;
620 GPSPositionSensorStatusSet(&status);
623 return id;
626 #if !defined(PIOS_GPS_MINIMAL)
627 void op_gpsv9_load_mag_settings()
629 if (auxmagsupport_get_type() == AUXMAGSETTINGS_TYPE_GPSV9) {
630 useMag = true;
631 } else {
632 useMag = false;
635 #endif // !defined(PIOS_GPS_MINIMAL)
636 #endif // defined(PIOS_INCLUDE_GPS_UBX_PARSER)