2012-06-09 23:49:03 +02:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* @addtogroup OpenPilotModules OpenPilot Modules
|
|
|
|
* @{
|
2016-07-03 03:51:44 +02:00
|
|
|
* @addtogroup GPSModule GPS Module
|
2012-06-09 23:49:03 +02:00
|
|
|
* @brief Process GPS information (UBX binary format)
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file UBX.c
|
2016-02-09 20:43:44 +01:00
|
|
|
* @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015-2016.
|
|
|
|
* The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
|
2016-07-03 03:51:44 +02:00
|
|
|
* @brief GPS module, handles GPS and UBX stream
|
2012-06-09 23:49:03 +02:00
|
|
|
* @see The GNU Public License (GPL) Version 3
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "openpilot.h"
|
|
|
|
#include "pios.h"
|
2014-08-07 14:34:43 +02:00
|
|
|
#include "pios_math.h"
|
2014-08-21 17:35:17 +02:00
|
|
|
#include <pios_helpers.h>
|
2014-08-22 02:43:30 +02:00
|
|
|
#include <pios_delay.h>
|
2016-02-11 06:55:47 +01:00
|
|
|
|
2012-06-28 09:02:14 +02:00
|
|
|
#if defined(PIOS_INCLUDE_GPS_UBX_PARSER)
|
2016-02-11 06:55:47 +01:00
|
|
|
|
2014-08-05 17:25:28 +02:00
|
|
|
#include "inc/UBX.h"
|
|
|
|
#include "inc/GPS.h"
|
2014-08-21 16:55:16 +02:00
|
|
|
#include <string.h>
|
2014-10-31 19:56:38 +01:00
|
|
|
|
2016-02-11 06:55:47 +01:00
|
|
|
#if !defined(PIOS_GPS_MINIMAL)
|
2014-09-13 16:15:02 +02:00
|
|
|
#include <auxmagsupport.h>
|
2014-08-20 21:05:56 +02:00
|
|
|
static bool useMag = false;
|
2016-02-11 06:55:47 +01:00
|
|
|
#endif /* !defined(PIOS_GPS_MINIMAL) */
|
|
|
|
|
|
|
|
// this is set and used by this low level ubx code
|
|
|
|
// it is also reset by the ubx configuration code (UBX6 vs. UBX7) in ubx_autoconfig.c
|
|
|
|
GPSPositionSensorSensorTypeOptions ubxSensorType = GPSPOSITIONSENSOR_SENSORTYPE_UNKNOWN;
|
2014-08-22 10:10:21 +02:00
|
|
|
|
2014-08-21 16:55:16 +02:00
|
|
|
static bool usePvt = false;
|
2014-08-21 17:35:17 +02:00
|
|
|
static uint32_t lastPvtTime = 0;
|
2014-08-22 02:43:30 +02:00
|
|
|
|
|
|
|
// parse table item
|
2014-08-21 17:35:17 +02:00
|
|
|
typedef struct {
|
|
|
|
uint8_t msgClass;
|
|
|
|
uint8_t msgID;
|
|
|
|
void (*handler)(struct UBXPacket *, GPSPositionSensorData *GpsPosition);
|
|
|
|
} ubx_message_handler;
|
|
|
|
|
|
|
|
// parsing functions, roughly ordered by reception rate (higher rate messages on top)
|
|
|
|
static void parse_ubx_nav_posllh(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_nav_velned(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_nav_sol(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_nav_dop(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
2016-02-11 06:55:47 +01:00
|
|
|
#if !defined(PIOS_GPS_MINIMAL)
|
2014-11-15 22:39:17 +01:00
|
|
|
static void parse_ubx_nav_pvt(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_timeutc(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_nav_svinfo(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_op_sys(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
2014-10-31 19:56:38 +01:00
|
|
|
static void parse_ubx_op_mag(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_ack_ack(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_ack_nak(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
|
|
|
static void parse_ubx_mon_ver(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition);
|
2016-02-11 06:55:47 +01:00
|
|
|
#endif /* !defined(PIOS_GPS_MINIMAL) */
|
2014-08-21 17:35:17 +02:00
|
|
|
|
|
|
|
const ubx_message_handler ubx_handler_table[] = {
|
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_POSLLH, .handler = &parse_ubx_nav_posllh },
|
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_VELNED, .handler = &parse_ubx_nav_velned },
|
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_SOL, .handler = &parse_ubx_nav_sol },
|
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_DOP, .handler = &parse_ubx_nav_dop },
|
2016-02-11 06:55:47 +01:00
|
|
|
#if !defined(PIOS_GPS_MINIMAL)
|
2014-11-15 22:39:17 +01:00
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_PVT, .handler = &parse_ubx_nav_pvt },
|
2014-10-31 19:56:38 +01:00
|
|
|
{ .msgClass = UBX_CLASS_OP_CUST, .msgID = UBX_ID_OP_MAG, .handler = &parse_ubx_op_mag },
|
2014-08-21 17:35:17 +02:00
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_SVINFO, .handler = &parse_ubx_nav_svinfo },
|
|
|
|
{ .msgClass = UBX_CLASS_NAV, .msgID = UBX_ID_NAV_TIMEUTC, .handler = &parse_ubx_nav_timeutc },
|
2012-06-28 09:02:14 +02:00
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
{ .msgClass = UBX_CLASS_OP_CUST, .msgID = UBX_ID_OP_SYS, .handler = &parse_ubx_op_sys },
|
|
|
|
{ .msgClass = UBX_CLASS_ACK, .msgID = UBX_ID_ACK_ACK, .handler = &parse_ubx_ack_ack },
|
|
|
|
{ .msgClass = UBX_CLASS_ACK, .msgID = UBX_ID_ACK_NAK, .handler = &parse_ubx_ack_nak },
|
|
|
|
|
|
|
|
{ .msgClass = UBX_CLASS_MON, .msgID = UBX_ID_MON_VER, .handler = &parse_ubx_mon_ver },
|
2016-02-11 06:55:47 +01:00
|
|
|
#endif /* !defined(PIOS_GPS_MINIMAL) */
|
2014-08-21 17:35:17 +02:00
|
|
|
};
|
|
|
|
#define UBX_HANDLER_TABLE_SIZE NELEMENTS(ubx_handler_table)
|
2014-08-22 02:43:30 +02:00
|
|
|
|
|
|
|
// detected hw version
|
2014-08-22 10:10:21 +02:00
|
|
|
int32_t ubxHwVersion = -1;
|
|
|
|
|
|
|
|
// Last received Ack/Nak
|
|
|
|
struct UBX_ACK_ACK ubxLastAck;
|
|
|
|
struct UBX_ACK_NAK ubxLastNak;
|
2014-08-22 02:43:30 +02:00
|
|
|
|
2014-08-04 17:56:28 +02:00
|
|
|
// If a PVT sentence is received in the last UBX_PVT_TIMEOUT (ms) timeframe it disables VELNED/POSLLH/SOL/TIMEUTC
|
2014-08-22 02:43:30 +02:00
|
|
|
#define UBX_PVT_TIMEOUT (1000)
|
2012-06-28 09:02:14 +02:00
|
|
|
|
2016-02-11 06:55:47 +01:00
|
|
|
// parse incoming character stream for messages in UBX binary format
|
2015-05-20 01:33:31 +02:00
|
|
|
int parse_ubx_stream(uint8_t *rx, uint16_t len, char *gps_rx_buffer, GPSPositionSensorData *GpsData, struct GPS_RX_STATS *gpsRxStats)
|
2012-06-28 09:02:14 +02:00
|
|
|
{
|
2013-05-18 14:17:26 +02:00
|
|
|
enum proto_states {
|
|
|
|
START,
|
|
|
|
UBX_SY2,
|
|
|
|
UBX_CLASS,
|
|
|
|
UBX_ID,
|
|
|
|
UBX_LEN1,
|
|
|
|
UBX_LEN2,
|
|
|
|
UBX_PAYLOAD,
|
|
|
|
UBX_CHK1,
|
|
|
|
UBX_CHK2,
|
|
|
|
FINISHED
|
|
|
|
};
|
2016-02-02 15:17:56 +01:00
|
|
|
enum restart_states {
|
|
|
|
RESTART_WITH_ERROR,
|
|
|
|
RESTART_NO_ERROR
|
|
|
|
};
|
2015-02-28 10:48:26 +01:00
|
|
|
static uint16_t rx_count = 0;
|
2016-02-11 06:55:47 +01:00
|
|
|
static enum proto_states proto_state = START;
|
2015-02-28 13:18:24 +01:00
|
|
|
struct UBXPacket *ubx = (struct UBXPacket *)gps_rx_buffer;
|
2016-02-11 06:55:47 +01:00
|
|
|
int ret = PARSER_INCOMPLETE; // message not (yet) complete
|
2016-02-09 21:11:52 +01:00
|
|
|
uint16_t i = 0;
|
2015-11-15 09:44:28 +01:00
|
|
|
uint16_t restart_index = 0;
|
2016-02-02 15:17:56 +01:00
|
|
|
enum restart_states restart_state;
|
2016-02-11 06:55:47 +01:00
|
|
|
uint8_t c;
|
2015-05-20 01:33:31 +02:00
|
|
|
|
2016-02-02 15:17:56 +01:00
|
|
|
// switch continue is the normal condition and comes back to here for another byte
|
|
|
|
// switch break is the error state that branches to the end and restarts the scan at the byte after the first sync byte
|
2015-05-19 00:37:53 +02:00
|
|
|
while (i < len) {
|
|
|
|
c = rx[i++];
|
2014-09-27 00:17:00 +02:00
|
|
|
switch (proto_state) {
|
|
|
|
case START: // detect protocol
|
|
|
|
if (c == UBX_SYNC1) { // first UBX sync char found
|
2015-11-15 09:44:28 +01:00
|
|
|
proto_state = UBX_SY2;
|
|
|
|
// restart here, at byte after SYNC1, if we fail to parse
|
|
|
|
restart_index = i;
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_SY2:
|
|
|
|
if (c == UBX_SYNC2) { // second UBX sync char found
|
|
|
|
proto_state = UBX_CLASS;
|
|
|
|
} else {
|
2016-02-02 15:17:56 +01:00
|
|
|
restart_state = RESTART_NO_ERROR;
|
|
|
|
break;
|
2014-09-27 00:17:00 +02:00
|
|
|
}
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_CLASS:
|
|
|
|
ubx->header.class = c;
|
|
|
|
proto_state = UBX_ID;
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_ID:
|
|
|
|
ubx->header.id = c;
|
|
|
|
proto_state = UBX_LEN1;
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_LEN1:
|
|
|
|
ubx->header.len = c;
|
|
|
|
proto_state = UBX_LEN2;
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_LEN2:
|
|
|
|
ubx->header.len += (c << 8);
|
|
|
|
if (ubx->header.len > sizeof(UBXPayload)) {
|
|
|
|
gpsRxStats->gpsRxOverflow++;
|
2015-11-15 09:44:28 +01:00
|
|
|
#if defined(PIOS_GPS_MINIMAL)
|
2016-02-02 15:17:56 +01:00
|
|
|
restart_state = RESTART_NO_ERROR;
|
2015-11-15 09:44:28 +01:00
|
|
|
#else
|
2016-02-02 15:17:56 +01:00
|
|
|
restart_state = RESTART_WITH_ERROR;
|
2015-11-15 09:44:28 +01:00
|
|
|
#endif
|
2016-02-11 06:55:47 +01:00
|
|
|
break;
|
2014-09-27 00:17:00 +02:00
|
|
|
} else {
|
2015-11-15 09:44:28 +01:00
|
|
|
if (ubx->header.len == 0) {
|
|
|
|
proto_state = UBX_CHK1;
|
|
|
|
} else {
|
|
|
|
proto_state = UBX_PAYLOAD;
|
|
|
|
rx_count = 0;
|
|
|
|
}
|
2014-09-27 00:17:00 +02:00
|
|
|
}
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_PAYLOAD:
|
|
|
|
if (rx_count < ubx->header.len) {
|
|
|
|
ubx->payload.payload[rx_count] = c;
|
|
|
|
if (++rx_count == ubx->header.len) {
|
|
|
|
proto_state = UBX_CHK1;
|
|
|
|
}
|
2015-05-19 00:37:53 +02:00
|
|
|
}
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_CHK1:
|
|
|
|
ubx->header.ck_a = c;
|
|
|
|
proto_state = UBX_CHK2;
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2014-09-27 00:17:00 +02:00
|
|
|
case UBX_CHK2:
|
|
|
|
ubx->header.ck_b = c;
|
2016-02-02 15:17:56 +01:00
|
|
|
// OP GPSV9 sends data with bad checksums this appears to happen because it drops data
|
|
|
|
// this has been proven by running it without autoconfig and testing:
|
|
|
|
// data coming from OPV9 "GPS+MCU" port the checksum errors happen roughly every 5 to 30 seconds
|
|
|
|
// same data coming from OPV9 "GPS Only" port the checksums are always good
|
2016-02-09 20:43:44 +01:00
|
|
|
// this also occasionally causes parse_ubx_message() to issue alarms because not all the messages were received
|
2016-02-02 15:17:56 +01:00
|
|
|
// see OP GPSV9 comment in parse_ubx_message() for further information
|
2016-02-09 20:43:44 +01:00
|
|
|
if (checksum_ubx_message(ubx)) {
|
2015-11-15 09:44:28 +01:00
|
|
|
gpsRxStats->gpsRxReceived++;
|
|
|
|
proto_state = START;
|
2016-02-09 20:43:44 +01:00
|
|
|
// overwrite PARSER_INCOMPLETE with PARSER_COMPLETE
|
|
|
|
// but don't overwrite PARSER_ERROR with PARSER_COMPLETE
|
|
|
|
// pass PARSER_ERROR to caller if it happens even once
|
|
|
|
// only pass PARSER_COMPLETE back to caller if we parsed a full set of GPS data
|
|
|
|
// that allows the caller to know if we are parsing GPS data
|
|
|
|
// or just other packets for some reason (mis-configuration)
|
2016-02-09 21:11:52 +01:00
|
|
|
if (parse_ubx_message(ubx, GpsData) == GPSPOSITIONSENSOR_OBJID
|
2016-02-09 20:43:44 +01:00
|
|
|
&& ret == PARSER_INCOMPLETE) {
|
|
|
|
ret = PARSER_COMPLETE;
|
2015-11-15 09:44:28 +01:00
|
|
|
}
|
2014-09-27 00:17:00 +02:00
|
|
|
} else {
|
|
|
|
gpsRxStats->gpsRxChkSumError++;
|
2016-02-02 15:17:56 +01:00
|
|
|
restart_state = RESTART_WITH_ERROR;
|
|
|
|
break;
|
2014-09-27 00:17:00 +02:00
|
|
|
}
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2015-05-20 01:33:31 +02:00
|
|
|
default:
|
2015-11-15 09:44:28 +01:00
|
|
|
continue;
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
2014-09-27 00:17:00 +02:00
|
|
|
|
2015-11-15 09:44:28 +01:00
|
|
|
// this simple restart doesn't work across calls
|
|
|
|
// but it does work within a single call
|
|
|
|
// and it does the expected thing across calls
|
|
|
|
// if restarting due to error detected in 2nd call to this function (on split packet)
|
|
|
|
// then we just restart at index 0, which is mid-packet, not the second byte
|
2016-02-02 15:17:56 +01:00
|
|
|
if (restart_state == RESTART_WITH_ERROR) {
|
2016-02-09 21:11:52 +01:00
|
|
|
ret = PARSER_ERROR; // inform caller that we found at least one error (along with 0 or more good packets)
|
2016-02-02 15:17:56 +01:00
|
|
|
}
|
2015-11-16 00:42:47 +01:00
|
|
|
rx += restart_index; // restart parsing just past the most recent SYNC1
|
2015-11-15 09:44:28 +01:00
|
|
|
len -= restart_index;
|
|
|
|
i = 0;
|
|
|
|
proto_state = START;
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
2015-05-20 01:33:31 +02:00
|
|
|
|
2014-09-27 00:17:00 +02:00
|
|
|
return ret;
|
2012-06-28 09:02:14 +02:00
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
|
|
|
|
// Keep track of various GPS messages needed to make up a single UAVO update
|
|
|
|
// time-of-week timestamp is used to correlate matching messages
|
2013-05-18 14:17:26 +02:00
|
|
|
#define POSLLH_RECEIVED (1 << 0)
|
|
|
|
#define STATUS_RECEIVED (1 << 1)
|
|
|
|
#define DOP_RECEIVED (1 << 2)
|
|
|
|
#define VELNED_RECEIVED (1 << 3)
|
|
|
|
#define SOL_RECEIVED (1 << 4)
|
|
|
|
#define ALL_RECEIVED (SOL_RECEIVED | VELNED_RECEIVED | DOP_RECEIVED | POSLLH_RECEIVED)
|
|
|
|
#define NONE_RECEIVED 0
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
static struct msgtracker {
|
|
|
|
uint32_t currentTOW; // TOW of the message set currently in progress
|
|
|
|
uint8_t msg_received; // keep track of received message types
|
|
|
|
} msgtracker;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
|
|
|
// Check if a message belongs to the current data set and register it as 'received'
|
2013-05-18 14:17:26 +02:00
|
|
|
bool check_msgtracker(uint32_t tow, uint8_t msg_flag)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2013-05-18 14:17:26 +02:00
|
|
|
if (tow > msgtracker.currentTOW ? true // start of a new message set
|
|
|
|
: (msgtracker.currentTOW - tow > 6 * 24 * 3600 * 1000)) { // 6 days, TOW wrap around occured
|
|
|
|
msgtracker.currentTOW = tow;
|
|
|
|
msgtracker.msg_received = NONE_RECEIVED;
|
|
|
|
} else if (tow < msgtracker.currentTOW) { // message outdated (don't process)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
msgtracker.msg_received |= msg_flag; // register reception of this msg type
|
|
|
|
return true;
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
bool checksum_ubx_message(struct UBXPacket *ubx)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2013-05-18 14:17:26 +02:00
|
|
|
int i;
|
|
|
|
uint8_t ck_a, ck_b;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
ck_a = ubx->header.class;
|
|
|
|
ck_b = ck_a;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
ck_a += ubx->header.id;
|
|
|
|
ck_b += ck_a;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
ck_a += ubx->header.len & 0xff;
|
|
|
|
ck_b += ck_a;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
ck_a += ubx->header.len >> 8;
|
|
|
|
ck_b += ck_a;
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
for (i = 0; i < ubx->header.len; i++) {
|
|
|
|
ck_a += ubx->payload.payload[i];
|
|
|
|
ck_b += ck_a;
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
if (ubx->header.ck_a == ck_a &&
|
|
|
|
ubx->header.ck_b == ck_b) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_posllh(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2014-08-21 16:55:16 +02:00
|
|
|
if (usePvt) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-21 17:35:17 +02:00
|
|
|
struct UBX_NAV_POSLLH *posllh = &ubx->payload.nav_posllh;
|
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
if (check_msgtracker(posllh->iTOW, POSLLH_RECEIVED)) {
|
2013-05-22 21:45:06 +02:00
|
|
|
if (GpsPosition->Status != GPSPOSITIONSENSOR_STATUS_NOFIX) {
|
2013-05-18 14:17:26 +02:00
|
|
|
GpsPosition->Altitude = (float)posllh->hMSL * 0.001f;
|
|
|
|
GpsPosition->GeoidSeparation = (float)(posllh->height - posllh->hMSL) * 0.001f;
|
|
|
|
GpsPosition->Latitude = posllh->lat;
|
|
|
|
GpsPosition->Longitude = posllh->lon;
|
|
|
|
}
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_sol(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2014-08-21 16:55:16 +02:00
|
|
|
if (usePvt) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-21 17:35:17 +02:00
|
|
|
struct UBX_NAV_SOL *sol = &ubx->payload.nav_sol;
|
2013-05-18 14:17:26 +02:00
|
|
|
if (check_msgtracker(sol->iTOW, SOL_RECEIVED)) {
|
|
|
|
GpsPosition->Satellites = sol->numSV;
|
|
|
|
|
|
|
|
if (sol->flags & STATUS_FLAGS_GPSFIX_OK) {
|
|
|
|
switch (sol->gpsFix) {
|
|
|
|
case STATUS_GPSFIX_2DFIX:
|
2013-05-22 21:45:06 +02:00
|
|
|
GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_FIX2D;
|
2013-05-18 14:17:26 +02:00
|
|
|
break;
|
|
|
|
case STATUS_GPSFIX_3DFIX:
|
2017-06-23 19:42:58 +02:00
|
|
|
GpsPosition->Status = (sol->flags & STATUS_FLAGS_DIFFSOLN) ? GPSPOSITIONSENSOR_STATUS_FIX3DDGNSS : GPSPOSITIONSENSOR_STATUS_FIX3D;
|
2013-05-18 14:17:26 +02:00
|
|
|
break;
|
2013-05-22 21:45:06 +02:00
|
|
|
default: GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
|
|
|
} else { // fix is not valid so we make sure to treat is as NOFIX
|
2013-05-22 21:45:06 +02:00
|
|
|
GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_dop(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2014-08-21 17:35:17 +02:00
|
|
|
struct UBX_NAV_DOP *dop = &ubx->payload.nav_dop;
|
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
if (check_msgtracker(dop->iTOW, DOP_RECEIVED)) {
|
|
|
|
GpsPosition->HDOP = (float)dop->hDOP * 0.01f;
|
|
|
|
GpsPosition->VDOP = (float)dop->vDOP * 0.01f;
|
|
|
|
GpsPosition->PDOP = (float)dop->pDOP * 0.01f;
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_velned(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2014-08-21 16:55:16 +02:00
|
|
|
if (usePvt) {
|
|
|
|
return;
|
|
|
|
}
|
2013-05-22 21:45:06 +02:00
|
|
|
GPSVelocitySensorData GpsVelocity;
|
2014-08-21 17:35:17 +02:00
|
|
|
struct UBX_NAV_VELNED *velned = &ubx->payload.nav_velned;
|
2013-05-18 14:17:26 +02:00
|
|
|
if (check_msgtracker(velned->iTOW, VELNED_RECEIVED)) {
|
2013-05-22 21:45:06 +02:00
|
|
|
if (GpsPosition->Status != GPSPOSITIONSENSOR_STATUS_NOFIX) {
|
2013-05-18 14:17:26 +02:00
|
|
|
GpsVelocity.North = (float)velned->velN / 100.0f;
|
|
|
|
GpsVelocity.East = (float)velned->velE / 100.0f;
|
|
|
|
GpsVelocity.Down = (float)velned->velD / 100.0f;
|
2013-05-22 21:45:06 +02:00
|
|
|
GPSVelocitySensorSet(&GpsVelocity);
|
2013-05-18 14:17:26 +02:00
|
|
|
GpsPosition->Groundspeed = (float)velned->gSpeed * 0.01f;
|
|
|
|
GpsPosition->Heading = (float)velned->heading * 1.0e-5f;
|
|
|
|
}
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
2016-02-11 06:55:47 +01:00
|
|
|
|
2014-11-15 22:39:17 +01:00
|
|
|
#if !defined(PIOS_GPS_MINIMAL)
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_pvt(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2014-07-17 18:30:47 +02:00
|
|
|
{
|
2014-08-21 17:35:17 +02:00
|
|
|
lastPvtTime = PIOS_DELAY_GetuS();
|
2014-07-17 18:30:47 +02:00
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
GPSVelocitySensorData GpsVelocity;
|
|
|
|
struct UBX_NAV_PVT *pvt = &ubx->payload.nav_pvt;
|
2014-08-04 17:56:28 +02:00
|
|
|
check_msgtracker(pvt->iTOW, (ALL_RECEIVED));
|
2014-08-03 15:04:40 +02:00
|
|
|
|
2014-08-04 11:04:35 +02:00
|
|
|
GpsVelocity.North = (float)pvt->velN * 0.001f;
|
|
|
|
GpsVelocity.East = (float)pvt->velE * 0.001f;
|
|
|
|
GpsVelocity.Down = (float)pvt->velD * 0.001f;
|
2014-08-03 15:27:30 +02:00
|
|
|
GPSVelocitySensorSet(&GpsVelocity);
|
|
|
|
|
2014-08-04 11:04:35 +02:00
|
|
|
GpsPosition->Groundspeed = (float)pvt->gSpeed * 0.001f;
|
2014-08-03 15:27:30 +02:00
|
|
|
GpsPosition->Heading = (float)pvt->heading * 1.0e-5f;
|
|
|
|
GpsPosition->Altitude = (float)pvt->hMSL * 0.001f;
|
|
|
|
GpsPosition->GeoidSeparation = (float)(pvt->height - pvt->hMSL) * 0.001f;
|
|
|
|
GpsPosition->Latitude = pvt->lat;
|
|
|
|
GpsPosition->Longitude = pvt->lon;
|
|
|
|
GpsPosition->Satellites = pvt->numSV;
|
2014-08-04 17:56:28 +02:00
|
|
|
GpsPosition->PDOP = pvt->pDOP * 0.01f;
|
2017-06-23 19:42:58 +02:00
|
|
|
|
2014-08-04 11:04:35 +02:00
|
|
|
if (pvt->flags & PVT_FLAGS_GNSSFIX_OK) {
|
2017-06-23 19:42:58 +02:00
|
|
|
switch (pvt->fixType) {
|
|
|
|
case PVT_FIX_TYPE_2D:
|
|
|
|
GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_FIX2D;
|
|
|
|
break;
|
|
|
|
case PVT_FIX_TYPE_3D:
|
|
|
|
GpsPosition->Status = (pvt->flags & PVT_FLAGS_DIFFSOLN) ? GPSPOSITIONSENSOR_STATUS_FIX3DDGNSS : GPSPOSITIONSENSOR_STATUS_FIX3D;
|
|
|
|
break;
|
|
|
|
default: GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
|
|
|
|
}
|
|
|
|
} else { // fix is not valid so we make sure to treat is as NOFIX
|
2014-08-03 15:27:30 +02:00
|
|
|
GpsPosition->Status = GPSPOSITIONSENSOR_STATUS_NOFIX;
|
|
|
|
}
|
2014-11-15 22:39:17 +01:00
|
|
|
|
2014-08-03 15:27:30 +02:00
|
|
|
if (pvt->valid & PVT_VALID_VALIDTIME) {
|
|
|
|
// Time is valid, set GpsTime
|
|
|
|
GPSTimeData GpsTime;
|
|
|
|
|
|
|
|
GpsTime.Year = pvt->year;
|
|
|
|
GpsTime.Month = pvt->month;
|
|
|
|
GpsTime.Day = pvt->day;
|
|
|
|
GpsTime.Hour = pvt->hour;
|
|
|
|
GpsTime.Minute = pvt->min;
|
|
|
|
GpsTime.Second = pvt->sec;
|
|
|
|
|
|
|
|
GPSTimeSet(&GpsTime);
|
|
|
|
}
|
2014-08-21 16:55:16 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_timeutc(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2014-08-21 16:55:16 +02:00
|
|
|
if (usePvt) {
|
|
|
|
return;
|
|
|
|
}
|
2014-08-21 17:35:17 +02:00
|
|
|
|
|
|
|
struct UBX_NAV_TIMEUTC *timeutc = &ubx->payload.nav_timeutc;
|
2014-04-12 11:48:18 +02:00
|
|
|
// Test if time is valid
|
|
|
|
if ((timeutc->valid & TIMEUTC_VALIDTOW) && (timeutc->valid & TIMEUTC_VALIDWKN)) {
|
|
|
|
// Time is valid, set GpsTime
|
|
|
|
GPSTimeData GpsTime;
|
|
|
|
|
|
|
|
GpsTime.Year = timeutc->year;
|
|
|
|
GpsTime.Month = timeutc->month;
|
|
|
|
GpsTime.Day = timeutc->day;
|
|
|
|
GpsTime.Hour = timeutc->hour;
|
|
|
|
GpsTime.Minute = timeutc->min;
|
|
|
|
GpsTime.Second = timeutc->sec;
|
2016-09-03 21:44:41 +02:00
|
|
|
GpsTime.Millisecond = (int16_t)(timeutc->nano / 1000000);
|
2014-04-12 11:48:18 +02:00
|
|
|
GPSTimeSet(&GpsTime);
|
|
|
|
} else {
|
|
|
|
// Time is not valid, nothing to do
|
2013-05-18 14:17:26 +02:00
|
|
|
return;
|
|
|
|
}
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_nav_svinfo(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2013-05-18 14:17:26 +02:00
|
|
|
uint8_t chan;
|
|
|
|
GPSSatellitesData svdata;
|
2014-08-21 17:35:17 +02:00
|
|
|
struct UBX_NAV_SVINFO *svinfo = &ubx->payload.nav_svinfo;
|
2013-05-18 14:17:26 +02:00
|
|
|
|
|
|
|
svdata.SatsInView = 0;
|
2015-03-01 16:44:30 +01:00
|
|
|
|
|
|
|
// First, use slots for SVs actually being received
|
2013-05-18 14:17:26 +02:00
|
|
|
for (chan = 0; chan < svinfo->numCh; chan++) {
|
2015-02-28 11:01:08 +01:00
|
|
|
if (svdata.SatsInView < GPSSATELLITES_PRN_NUMELEM && svinfo->sv[chan].cno > 0) {
|
2014-08-06 12:55:07 +02:00
|
|
|
svdata.Azimuth[svdata.SatsInView] = svinfo->sv[chan].azim;
|
|
|
|
svdata.Elevation[svdata.SatsInView] = svinfo->sv[chan].elev;
|
2013-05-18 14:17:26 +02:00
|
|
|
svdata.PRN[svdata.SatsInView] = svinfo->sv[chan].svid;
|
|
|
|
svdata.SNR[svdata.SatsInView] = svinfo->sv[chan].cno;
|
|
|
|
svdata.SatsInView++;
|
|
|
|
}
|
|
|
|
}
|
2015-03-01 16:44:30 +01:00
|
|
|
|
|
|
|
// Now try to add the rest
|
|
|
|
for (chan = 0; chan < svinfo->numCh; chan++) {
|
|
|
|
if (svdata.SatsInView < GPSSATELLITES_PRN_NUMELEM && 0 == svinfo->sv[chan].cno) {
|
|
|
|
svdata.Azimuth[svdata.SatsInView] = svinfo->sv[chan].azim;
|
|
|
|
svdata.Elevation[svdata.SatsInView] = svinfo->sv[chan].elev;
|
|
|
|
svdata.PRN[svdata.SatsInView] = svinfo->sv[chan].svid;
|
|
|
|
svdata.SNR[svdata.SatsInView] = svinfo->sv[chan].cno;
|
|
|
|
svdata.SatsInView++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
// fill remaining slots (if any)
|
|
|
|
for (chan = svdata.SatsInView; chan < GPSSATELLITES_PRN_NUMELEM; chan++) {
|
2014-08-06 12:55:07 +02:00
|
|
|
svdata.Azimuth[chan] = 0;
|
|
|
|
svdata.Elevation[chan] = 0;
|
2013-05-18 14:17:26 +02:00
|
|
|
svdata.PRN[chan] = 0;
|
|
|
|
svdata.SNR[chan] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
GPSSatellitesSet(&svdata);
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_ack_ack(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
|
|
|
{
|
|
|
|
struct UBX_ACK_ACK *ack_ack = &ubx->payload.ack_ack;
|
|
|
|
|
2014-08-22 10:10:21 +02:00
|
|
|
ubxLastAck = *ack_ack;
|
2014-08-21 17:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_ubx_ack_nak(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
|
|
|
{
|
|
|
|
struct UBX_ACK_NAK *ack_nak = &ubx->payload.ack_nak;
|
|
|
|
|
2014-08-22 10:10:21 +02:00
|
|
|
ubxLastNak = *ack_nak;
|
2014-08-21 17:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_ubx_mon_ver(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
|
|
|
{
|
|
|
|
struct UBX_MON_VER *mon_ver = &ubx->payload.mon_ver;
|
|
|
|
|
2016-02-11 06:55:47 +01:00
|
|
|
ubxHwVersion = atoi(mon_ver->hwVersion);
|
|
|
|
ubxSensorType = (ubxHwVersion >= UBX_HW_VERSION_8) ? GPSPOSITIONSENSOR_SENSORTYPE_UBX8 :
|
|
|
|
((ubxHwVersion >= UBX_HW_VERSION_7) ? GPSPOSITIONSENSOR_SENSORTYPE_UBX7 : GPSPOSITIONSENSOR_SENSORTYPE_UBX);
|
2015-04-30 05:46:47 +02:00
|
|
|
// send sensor type right now because on UBX NEMA we don't get a full set of messages
|
|
|
|
// and we want to be able to see sensor type even on UBX NEMA GPS's
|
2016-02-11 06:55:47 +01:00
|
|
|
GPSPositionSensorSensorTypeSet((uint8_t *)&ubxSensorType);
|
2014-08-21 17:35:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_ubx_op_sys(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
|
|
|
{
|
|
|
|
struct UBX_OP_SYSINFO *sysinfo = &ubx->payload.op_sysinfo;
|
|
|
|
GPSExtendedStatusData data;
|
|
|
|
|
2014-09-24 01:03:33 +02:00
|
|
|
data.FlightTime = sysinfo->flightTime;
|
|
|
|
data.BoardType[0] = sysinfo->board_type;
|
|
|
|
data.BoardType[1] = sysinfo->board_revision;
|
|
|
|
memcpy(&data.FirmwareHash, &sysinfo->sha1sum, GPSEXTENDEDSTATUS_FIRMWAREHASH_NUMELEM);
|
|
|
|
memcpy(&data.FirmwareTag, &sysinfo->commit_tag_name, GPSEXTENDEDSTATUS_FIRMWARETAG_NUMELEM);
|
2014-08-21 17:35:17 +02:00
|
|
|
data.Options = sysinfo->options;
|
|
|
|
data.Status = GPSEXTENDEDSTATUS_STATUS_GPSV9;
|
|
|
|
GPSExtendedStatusSet(&data);
|
|
|
|
}
|
2014-10-31 19:56:38 +01:00
|
|
|
|
2014-08-21 17:35:17 +02:00
|
|
|
static void parse_ubx_op_mag(struct UBXPacket *ubx, __attribute__((unused)) GPSPositionSensorData *GpsPosition)
|
|
|
|
{
|
|
|
|
if (!useMag) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
struct UBX_OP_MAG *mag = &ubx->payload.op_mag;
|
2014-09-13 16:15:02 +02:00
|
|
|
float mags[3] = { mag->x, mag->y, mag->z };
|
|
|
|
auxmagsupport_publish_samples(mags, AUXMAGSENSOR_STATUS_OK);
|
2014-08-21 17:35:17 +02:00
|
|
|
}
|
2014-10-31 19:56:38 +01:00
|
|
|
#endif /* if !defined(PIOS_GPS_MINIMAL) */
|
2014-08-21 17:35:17 +02:00
|
|
|
|
2012-06-09 23:49:03 +02:00
|
|
|
// UBX message parser
|
2012-06-28 09:02:14 +02:00
|
|
|
// returns UAVObjectID if a UAVObject structure is ready for further processing
|
2013-05-22 21:45:06 +02:00
|
|
|
uint32_t parse_ubx_message(struct UBXPacket *ubx, GPSPositionSensorData *GpsPosition)
|
2012-06-09 23:49:03 +02:00
|
|
|
{
|
2013-05-18 14:17:26 +02:00
|
|
|
uint32_t id = 0;
|
2014-08-21 17:35:17 +02:00
|
|
|
static bool ubxInitialized = false;
|
2014-08-04 17:56:28 +02:00
|
|
|
|
|
|
|
if (!ubxInitialized) {
|
|
|
|
// initialize dop values. If no DOP sentence is received it is safer to initialize them to a high value rather than 0.
|
|
|
|
GpsPosition->HDOP = 99.99f;
|
|
|
|
GpsPosition->PDOP = 99.99f;
|
|
|
|
GpsPosition->VDOP = 99.99f;
|
|
|
|
ubxInitialized = true;
|
|
|
|
}
|
|
|
|
// is it using PVT?
|
2014-08-21 16:55:16 +02:00
|
|
|
usePvt = (lastPvtTime) && (PIOS_DELAY_GetuSSince(lastPvtTime) < UBX_PVT_TIMEOUT * 1000);
|
2014-08-21 17:35:17 +02:00
|
|
|
for (uint8_t i = 0; i < UBX_HANDLER_TABLE_SIZE; i++) {
|
|
|
|
const ubx_message_handler *handler = &ubx_handler_table[i];
|
|
|
|
if (handler->msgClass == ubx->header.class && handler->msgID == ubx->header.id) {
|
|
|
|
handler->handler(ubx, GpsPosition);
|
2014-08-21 16:55:16 +02:00
|
|
|
break;
|
|
|
|
}
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
2014-08-09 12:49:21 +02:00
|
|
|
|
2016-02-11 06:55:47 +01:00
|
|
|
GpsPosition->SensorType = ubxSensorType;
|
2014-08-22 23:35:13 +02:00
|
|
|
|
2013-05-18 14:17:26 +02:00
|
|
|
if (msgtracker.msg_received == ALL_RECEIVED) {
|
2016-02-02 15:17:56 +01:00
|
|
|
// leave BaudRate field alone!
|
2015-05-21 10:37:44 +02:00
|
|
|
GPSPositionSensorBaudRateGet(&GpsPosition->BaudRate);
|
2013-05-22 21:45:06 +02:00
|
|
|
GPSPositionSensorSet(GpsPosition);
|
2013-05-18 14:17:26 +02:00
|
|
|
msgtracker.msg_received = NONE_RECEIVED;
|
2013-05-22 21:45:06 +02:00
|
|
|
id = GPSPOSITIONSENSOR_OBJID;
|
2014-09-21 16:38:52 +02:00
|
|
|
} else {
|
|
|
|
uint8_t status;
|
|
|
|
GPSPositionSensorStatusGet(&status);
|
|
|
|
if (status == GPSPOSITIONSENSOR_STATUS_NOGPS) {
|
|
|
|
// Some ubx thing has been received so GPS is there
|
2016-02-02 15:17:56 +01:00
|
|
|
//
|
|
|
|
// OP GPSV9 will sometimes cause this NOFIX
|
|
|
|
// because GPSV9 drops data which causes checksum errors which causes GPS.c to set the status to NOGPS
|
|
|
|
// see OP GPSV9 comment in parse_ubx_stream() for further information
|
2014-09-21 16:38:52 +02:00
|
|
|
status = GPSPOSITIONSENSOR_STATUS_NOFIX;
|
|
|
|
GPSPositionSensorStatusSet(&status);
|
|
|
|
}
|
2013-05-18 14:17:26 +02:00
|
|
|
}
|
|
|
|
return id;
|
2012-06-09 23:49:03 +02:00
|
|
|
}
|
|
|
|
|
2014-10-31 19:56:38 +01:00
|
|
|
#if !defined(PIOS_GPS_MINIMAL)
|
2016-02-02 15:17:56 +01:00
|
|
|
void op_gpsv9_load_mag_settings()
|
2014-07-17 20:43:16 +02:00
|
|
|
{
|
2016-02-02 15:17:56 +01:00
|
|
|
if (auxmagsupport_get_type() == AUXMAGSETTINGS_TYPE_GPSV9) {
|
2014-08-20 21:05:56 +02:00
|
|
|
useMag = true;
|
2016-02-02 15:17:56 +01:00
|
|
|
} else {
|
|
|
|
useMag = false;
|
2014-08-20 21:05:56 +02:00
|
|
|
}
|
2014-07-17 20:43:16 +02:00
|
|
|
}
|
2016-02-11 06:55:47 +01:00
|
|
|
#endif // !defined(PIOS_GPS_MINIMAL)
|
|
|
|
#endif // defined(PIOS_INCLUDE_GPS_UBX_PARSER)
|