2013-05-29 20:52:21 -03:00
|
|
|
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
2013-08-29 02:34:34 -03:00
|
|
|
/*
|
|
|
|
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, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2010-09-06 19:31:18 -03:00
|
|
|
//
|
2014-03-28 16:52:27 -03:00
|
|
|
// u-blox GPS driver for ArduPilot
|
|
|
|
// Origin code by Michael Smith, Jordi Munoz and Jose Julio, DIYDrones.com
|
|
|
|
// Substantially rewitten for new GPS driver structure by Andrew Tridgell
|
2010-09-06 19:31:18 -03:00
|
|
|
//
|
2014-03-28 16:52:27 -03:00
|
|
|
#include <AP_GPS.h>
|
|
|
|
#include "AP_GPS_UBLOX.h"
|
2014-03-23 22:02:37 -03:00
|
|
|
#include <DataFlash.h>
|
2010-09-06 17:16:50 -03:00
|
|
|
|
2012-06-08 03:39:12 -03:00
|
|
|
#define UBLOX_DEBUGGING 0
|
2013-10-02 03:10:22 -03:00
|
|
|
#define UBLOX_FAKE_3DLOCK 0
|
2012-06-08 03:39:12 -03:00
|
|
|
|
2013-01-06 00:10:04 -04:00
|
|
|
extern const AP_HAL::HAL& hal;
|
2012-06-15 02:53:14 -03:00
|
|
|
|
|
|
|
#if UBLOX_DEBUGGING
|
2013-03-20 01:42:07 -03:00
|
|
|
# define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
|
2012-06-08 03:39:12 -03:00
|
|
|
#else
|
2012-08-21 23:19:52 -03:00
|
|
|
# define Debug(fmt, args ...)
|
2012-06-08 03:39:12 -03:00
|
|
|
#endif
|
|
|
|
|
2014-03-23 22:02:37 -03:00
|
|
|
/*
|
|
|
|
only do detailed hardware logging on boards likely to have more log
|
|
|
|
storage space
|
|
|
|
*/
|
|
|
|
#if HAL_CPU_CLASS >= HAL_CPU_CLASS_75
|
|
|
|
#define UBLOX_HW_LOGGING 1
|
|
|
|
#else
|
|
|
|
#define UBLOX_HW_LOGGING 0
|
|
|
|
#endif
|
|
|
|
|
2014-03-28 16:52:27 -03:00
|
|
|
AP_GPS_UBLOX::AP_GPS_UBLOX(AP_GPS &_gps, AP_GPS::GPS_State &_state, AP_HAL::UARTDriver *_port) :
|
|
|
|
AP_GPS_Backend(_gps, _state, _port),
|
|
|
|
_step(0),
|
|
|
|
_msg_id(0),
|
|
|
|
_payload_length(0),
|
|
|
|
_payload_counter(0),
|
|
|
|
_fix_count(0),
|
|
|
|
_class(0),
|
|
|
|
_new_position(0),
|
|
|
|
_new_speed(0),
|
|
|
|
need_rate_update(false),
|
|
|
|
_disable_counter(0),
|
|
|
|
next_fix(AP_GPS::NO_FIX),
|
|
|
|
rate_update_step(0),
|
2014-04-23 05:15:30 -03:00
|
|
|
_last_5hz_time(0),
|
2014-03-28 16:52:27 -03:00
|
|
|
_last_hw_status(0)
|
2010-09-06 17:16:50 -03:00
|
|
|
{
|
2014-03-31 06:52:07 -03:00
|
|
|
// stop any config strings that are pending
|
|
|
|
gps.send_blob_start(state.instance, NULL, 0);
|
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// configure the GPS for the messages we want
|
|
|
|
_configure_gps();
|
2010-09-06 17:16:50 -03:00
|
|
|
}
|
|
|
|
|
2013-10-01 23:32:32 -03:00
|
|
|
/*
|
|
|
|
send the next step of rate updates to the GPS. This reconfigures the
|
|
|
|
GPS on the fly to have the right message rates. It needs to be
|
|
|
|
careful to only send a message if there is sufficient buffer space
|
|
|
|
available on the serial port to avoid it blocking the CPU
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::send_next_rate_update(void)
|
|
|
|
{
|
2014-03-28 16:52:27 -03:00
|
|
|
if (port->txspace() < (int16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_nav_rate)+2)) {
|
2013-10-01 23:32:32 -03:00
|
|
|
// not enough space - do it next time
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-02 03:10:22 -03:00
|
|
|
//hal.console->printf_P(PSTR("next_rate: %u\n"), (unsigned)rate_update_step);
|
|
|
|
|
2014-03-23 22:02:37 -03:00
|
|
|
switch (rate_update_step++) {
|
2013-10-01 23:32:32 -03:00
|
|
|
case 0:
|
|
|
|
_configure_navigation_rate(200);
|
|
|
|
break;
|
|
|
|
case 1:
|
2014-03-23 22:02:37 -03:00
|
|
|
_configure_message_rate(CLASS_NAV, MSG_POSLLH, 1); // 28+8 bytes
|
2013-10-01 23:32:32 -03:00
|
|
|
break;
|
|
|
|
case 2:
|
2014-03-23 22:02:37 -03:00
|
|
|
_configure_message_rate(CLASS_NAV, MSG_STATUS, 1); // 16+8 bytes
|
2013-10-01 23:32:32 -03:00
|
|
|
break;
|
|
|
|
case 3:
|
2014-03-23 22:02:37 -03:00
|
|
|
_configure_message_rate(CLASS_NAV, MSG_SOL, 1); // 52+8 bytes
|
2013-10-01 23:32:32 -03:00
|
|
|
break;
|
|
|
|
case 4:
|
2014-03-23 22:02:37 -03:00
|
|
|
_configure_message_rate(CLASS_NAV, MSG_VELNED, 1); // 36+8 bytes
|
2013-10-01 23:32:32 -03:00
|
|
|
break;
|
2014-03-23 22:02:37 -03:00
|
|
|
#if UBLOX_HW_LOGGING
|
|
|
|
case 5:
|
|
|
|
// gather MON_HW at 0.5Hz
|
|
|
|
_configure_message_rate(CLASS_MON, MSG_MON_HW, 2); // 64+8 bytes
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
// gather MON_HW2 at 0.5Hz
|
|
|
|
_configure_message_rate(CLASS_MON, MSG_MON_HW2, 2); // 24+8 bytes
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
2013-10-01 23:32:32 -03:00
|
|
|
need_rate_update = false;
|
|
|
|
rate_update_step = 0;
|
2014-03-23 22:02:37 -03:00
|
|
|
break;
|
2013-10-01 23:32:32 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-06 19:31:18 -03:00
|
|
|
// Process bytes available from the stream
|
|
|
|
//
|
|
|
|
// The stream is assumed to contain only messages we recognise. If it
|
|
|
|
// contains other messages, and those messages contain the preamble
|
|
|
|
// bytes, it is possible for this code to fail to synchronise to the
|
|
|
|
// stream immediately. Without buffering the entire message and
|
|
|
|
// re-processing it from the top, this is unavoidable. The parser
|
|
|
|
// attempts to avoid this when possible.
|
|
|
|
//
|
2010-12-24 02:35:09 -04:00
|
|
|
bool
|
|
|
|
AP_GPS_UBLOX::read(void)
|
2010-09-06 17:16:50 -03:00
|
|
|
{
|
2012-08-21 23:19:52 -03:00
|
|
|
uint8_t data;
|
|
|
|
int16_t numc;
|
|
|
|
bool parsed = false;
|
2011-10-28 15:52:50 -03:00
|
|
|
|
2013-10-01 23:32:32 -03:00
|
|
|
if (need_rate_update) {
|
|
|
|
send_next_rate_update();
|
|
|
|
}
|
|
|
|
|
2014-03-28 16:52:27 -03:00
|
|
|
numc = port->available();
|
2012-08-21 23:19:52 -03:00
|
|
|
for (int16_t i = 0; i < numc; i++) { // Process bytes received
|
2011-10-28 15:52:50 -03:00
|
|
|
|
|
|
|
// read the next byte
|
2014-03-28 16:52:27 -03:00
|
|
|
data = port->read();
|
2011-10-28 15:52:50 -03:00
|
|
|
|
2013-01-05 01:32:42 -04:00
|
|
|
reset:
|
2011-10-28 15:52:50 -03:00
|
|
|
switch(_step) {
|
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// Message preamble detection
|
|
|
|
//
|
|
|
|
// If we fail to match any of the expected bytes, we reset
|
|
|
|
// the state machine and re-consider the failed byte as
|
|
|
|
// the first byte of the preamble. This improves our
|
|
|
|
// chances of recovering from a mismatch and makes it less
|
|
|
|
// likely that we will be fooled by the preamble appearing
|
|
|
|
// as data in some other message.
|
|
|
|
//
|
2011-10-28 15:52:50 -03:00
|
|
|
case 1:
|
|
|
|
if (PREAMBLE2 == data) {
|
|
|
|
_step++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_step = 0;
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("reset %u", __LINE__);
|
|
|
|
// FALLTHROUGH
|
2011-10-28 15:52:50 -03:00
|
|
|
case 0:
|
|
|
|
if(PREAMBLE1 == data)
|
|
|
|
_step++;
|
|
|
|
break;
|
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// Message header processing
|
|
|
|
//
|
|
|
|
// We sniff the class and message ID to decide whether we
|
|
|
|
// are going to gather the message bytes or just discard
|
|
|
|
// them.
|
|
|
|
//
|
|
|
|
// We always collect the length so that we can avoid being
|
|
|
|
// fooled by preamble bytes in messages.
|
|
|
|
//
|
2011-10-28 15:52:50 -03:00
|
|
|
case 2:
|
|
|
|
_step++;
|
2012-08-21 23:19:52 -03:00
|
|
|
_class = data;
|
|
|
|
_ck_b = _ck_a = data; // reset the checksum accumulators
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
_step++;
|
2012-08-21 23:19:52 -03:00
|
|
|
_ck_b += (_ck_a += data); // checksum byte
|
2011-10-28 15:52:50 -03:00
|
|
|
_msg_id = data;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
_step++;
|
2012-08-21 23:19:52 -03:00
|
|
|
_ck_b += (_ck_a += data); // checksum byte
|
|
|
|
_payload_length = data; // payload length low byte
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
_step++;
|
2012-08-21 23:19:52 -03:00
|
|
|
_ck_b += (_ck_a += data); // checksum byte
|
2012-06-10 03:34:13 -03:00
|
|
|
|
|
|
|
_payload_length += (uint16_t)(data<<8);
|
2012-08-21 23:19:52 -03:00
|
|
|
if (_payload_length > 512) {
|
|
|
|
Debug("large payload %u", (unsigned)_payload_length);
|
|
|
|
// assume very large payloads are line noise
|
|
|
|
_payload_length = 0;
|
|
|
|
_step = 0;
|
2013-01-05 01:32:42 -04:00
|
|
|
goto reset;
|
2012-08-21 23:19:52 -03:00
|
|
|
}
|
|
|
|
_payload_counter = 0; // prepare to receive payload
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// Receive message data
|
|
|
|
//
|
2011-10-28 15:52:50 -03:00
|
|
|
case 6:
|
2012-08-21 23:19:52 -03:00
|
|
|
_ck_b += (_ck_a += data); // checksum byte
|
|
|
|
if (_payload_counter < sizeof(_buffer)) {
|
|
|
|
_buffer.bytes[_payload_counter] = data;
|
|
|
|
}
|
2011-10-28 15:52:50 -03:00
|
|
|
if (++_payload_counter == _payload_length)
|
|
|
|
_step++;
|
|
|
|
break;
|
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// Checksum and message processing
|
|
|
|
//
|
2011-10-28 15:52:50 -03:00
|
|
|
case 7:
|
|
|
|
_step++;
|
2012-06-08 03:39:12 -03:00
|
|
|
if (_ck_a != data) {
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("bad cka %x should be %x", data, _ck_a);
|
2013-01-05 01:32:42 -04:00
|
|
|
_step = 0;
|
|
|
|
goto reset;
|
2012-08-21 23:19:52 -03:00
|
|
|
}
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
_step = 0;
|
2012-06-08 03:39:12 -03:00
|
|
|
if (_ck_b != data) {
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("bad ckb %x should be %x", data, _ck_b);
|
|
|
|
break; // bad checksum
|
|
|
|
}
|
2011-10-28 15:52:50 -03:00
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
if (_parse_gps()) {
|
|
|
|
parsed = true;
|
2011-10-28 15:52:50 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parsed;
|
2010-09-06 17:16:50 -03:00
|
|
|
}
|
|
|
|
|
2010-09-06 19:31:18 -03:00
|
|
|
// Private Methods /////////////////////////////////////////////////////////////
|
2014-03-23 22:02:37 -03:00
|
|
|
#if UBLOX_HW_LOGGING
|
|
|
|
|
|
|
|
void AP_GPS_UBLOX::log_mon_hw(void)
|
|
|
|
{
|
2014-03-28 16:52:27 -03:00
|
|
|
if (gps._DataFlash == NULL || !gps._DataFlash->logging_started()) {
|
2014-03-23 22:02:37 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
struct log_Ubx1 pkt = {
|
2014-08-17 08:45:32 -03:00
|
|
|
LOG_PACKET_HEADER_INIT(LOG_UBX1_MSG),
|
2014-03-23 22:02:37 -03:00
|
|
|
timestamp : hal.scheduler->millis(),
|
2014-03-28 16:52:27 -03:00
|
|
|
instance : state.instance,
|
2014-04-02 20:55:05 -03:00
|
|
|
noisePerMS : _buffer.mon_hw_60.noisePerMS,
|
|
|
|
jamInd : _buffer.mon_hw_60.jamInd,
|
|
|
|
aPower : _buffer.mon_hw_60.aPower
|
2014-03-23 22:02:37 -03:00
|
|
|
};
|
2014-04-02 20:55:05 -03:00
|
|
|
if (_payload_length == 68) {
|
|
|
|
pkt.noisePerMS = _buffer.mon_hw_68.noisePerMS;
|
|
|
|
pkt.jamInd = _buffer.mon_hw_68.jamInd;
|
|
|
|
pkt.aPower = _buffer.mon_hw_68.aPower;
|
|
|
|
}
|
2014-03-28 16:52:27 -03:00
|
|
|
gps._DataFlash->WriteBlock(&pkt, sizeof(pkt));
|
2014-03-23 22:02:37 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
void AP_GPS_UBLOX::log_mon_hw2(void)
|
|
|
|
{
|
2014-03-28 16:52:27 -03:00
|
|
|
if (gps._DataFlash == NULL || !gps._DataFlash->logging_started()) {
|
2014-03-23 22:02:37 -03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct log_Ubx2 pkt = {
|
2014-08-17 08:45:32 -03:00
|
|
|
LOG_PACKET_HEADER_INIT(LOG_UBX2_MSG),
|
2014-03-23 22:02:37 -03:00
|
|
|
timestamp : hal.scheduler->millis(),
|
2014-03-28 16:52:27 -03:00
|
|
|
instance : state.instance,
|
2014-03-23 22:02:37 -03:00
|
|
|
ofsI : _buffer.mon_hw2.ofsI,
|
|
|
|
magI : _buffer.mon_hw2.magI,
|
|
|
|
ofsQ : _buffer.mon_hw2.ofsQ,
|
|
|
|
magQ : _buffer.mon_hw2.magQ,
|
|
|
|
};
|
2014-03-28 16:52:27 -03:00
|
|
|
gps._DataFlash->WriteBlock(&pkt, sizeof(pkt));
|
2014-03-23 22:02:37 -03:00
|
|
|
}
|
|
|
|
#endif // UBLOX_HW_LOGGING
|
|
|
|
|
|
|
|
void AP_GPS_UBLOX::unexpected_message(void)
|
|
|
|
{
|
|
|
|
Debug("Unexpected message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);
|
|
|
|
if (++_disable_counter == 0) {
|
|
|
|
// disable future sends of this message id, but
|
|
|
|
// only do this every 256 messages, as some
|
|
|
|
// message types can't be disabled and we don't
|
|
|
|
// want to get into an ack war
|
|
|
|
Debug("Disabling message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);
|
|
|
|
_configure_message_rate(_class, _msg_id, 0);
|
|
|
|
}
|
|
|
|
}
|
2010-09-06 19:31:18 -03:00
|
|
|
|
2010-12-24 02:35:09 -04:00
|
|
|
bool
|
2010-09-06 19:31:18 -03:00
|
|
|
AP_GPS_UBLOX::_parse_gps(void)
|
2010-09-06 17:16:50 -03:00
|
|
|
{
|
2012-08-21 23:19:52 -03:00
|
|
|
if (_class == CLASS_ACK) {
|
|
|
|
Debug("ACK %u", (unsigned)_msg_id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_class == CLASS_CFG && _msg_id == MSG_CFG_NAV_SETTINGS) {
|
2014-09-04 01:43:29 -03:00
|
|
|
Debug("Got settings %u min_elev %d drLimit %u\n",
|
|
|
|
(unsigned)_buffer.nav_settings.dynModel,
|
|
|
|
(int)_buffer.nav_settings.minElev,
|
|
|
|
(unsigned)_buffer.nav_settings.drLimit);
|
|
|
|
_buffer.nav_settings.mask = 0;
|
2014-03-28 16:52:27 -03:00
|
|
|
if (gps._navfilter != AP_GPS::GPS_ENGINE_NONE &&
|
|
|
|
_buffer.nav_settings.dynModel != gps._navfilter) {
|
2012-08-21 23:19:52 -03:00
|
|
|
// we've received the current nav settings, change the engine
|
|
|
|
// settings and send them back
|
|
|
|
Debug("Changing engine setting from %u to %u\n",
|
2014-03-28 16:52:27 -03:00
|
|
|
(unsigned)_buffer.nav_settings.dynModel, (unsigned)gps._navfilter);
|
|
|
|
_buffer.nav_settings.dynModel = gps._navfilter;
|
2014-09-04 01:43:29 -03:00
|
|
|
_buffer.nav_settings.mask |= 1;
|
|
|
|
}
|
|
|
|
if (gps._min_elevation != -100 &&
|
|
|
|
_buffer.nav_settings.minElev != gps._min_elevation) {
|
|
|
|
Debug("Changing min elevation to %d\n", (int)gps._min_elevation);
|
|
|
|
_buffer.nav_settings.minElev = gps._min_elevation;
|
|
|
|
_buffer.nav_settings.mask |= 2;
|
|
|
|
}
|
|
|
|
if (_buffer.nav_settings.mask != 0) {
|
2012-08-21 23:19:52 -03:00
|
|
|
_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS,
|
|
|
|
&_buffer.nav_settings,
|
|
|
|
sizeof(_buffer.nav_settings));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-09-04 01:27:05 -03:00
|
|
|
if (_class == CLASS_CFG && _msg_id == MSG_CFG_SBAS && gps._sbas_mode != 2) {
|
|
|
|
Debug("Got SBAS settings %u %u %u 0x%x 0x%x\n",
|
|
|
|
(unsigned)_buffer.sbas.mode,
|
|
|
|
(unsigned)_buffer.sbas.usage,
|
|
|
|
(unsigned)_buffer.sbas.maxSBAS,
|
|
|
|
(unsigned)_buffer.sbas.scanmode2,
|
|
|
|
(unsigned)_buffer.sbas.scanmode1);
|
|
|
|
if (_buffer.sbas.mode != gps._sbas_mode) {
|
|
|
|
_buffer.sbas.mode = gps._sbas_mode;
|
|
|
|
_send_message(CLASS_CFG, MSG_CFG_SBAS,
|
|
|
|
&_buffer.sbas,
|
|
|
|
sizeof(_buffer.sbas));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-23 22:02:37 -03:00
|
|
|
#if UBLOX_HW_LOGGING
|
|
|
|
if (_class == CLASS_MON) {
|
|
|
|
if (_msg_id == MSG_MON_HW) {
|
2014-04-02 20:55:05 -03:00
|
|
|
if (_payload_length == 60 || _payload_length == 68) {
|
|
|
|
log_mon_hw();
|
|
|
|
}
|
2014-03-23 22:02:37 -03:00
|
|
|
} else if (_msg_id == MSG_MON_HW2) {
|
2014-04-02 20:55:05 -03:00
|
|
|
if (_payload_length == 28) {
|
|
|
|
log_mon_hw2();
|
|
|
|
}
|
2014-03-23 22:02:37 -03:00
|
|
|
} else {
|
|
|
|
unexpected_message();
|
2012-08-21 23:19:52 -03:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2014-03-23 22:02:37 -03:00
|
|
|
#endif // UBLOX_HW_LOGGING
|
|
|
|
|
|
|
|
if (_class != CLASS_NAV) {
|
|
|
|
unexpected_message();
|
|
|
|
return false;
|
|
|
|
}
|
2012-06-10 03:34:13 -03:00
|
|
|
|
2011-10-28 15:52:50 -03:00
|
|
|
switch (_msg_id) {
|
|
|
|
case MSG_POSLLH:
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("MSG_POSLLH next_fix=%u", next_fix);
|
2014-04-14 19:23:12 -03:00
|
|
|
_last_pos_time = _buffer.posllh.time;
|
2014-03-28 16:52:27 -03:00
|
|
|
state.location.lng = _buffer.posllh.longitude;
|
|
|
|
state.location.lat = _buffer.posllh.latitude;
|
|
|
|
state.location.alt = _buffer.posllh.altitude_msl / 10;
|
|
|
|
state.status = next_fix;
|
2012-08-21 23:19:52 -03:00
|
|
|
_new_position = true;
|
2013-10-10 05:15:07 -03:00
|
|
|
#if UBLOX_FAKE_3DLOCK
|
2014-03-28 16:52:27 -03:00
|
|
|
state.location.lng = 1491652300L;
|
|
|
|
state.location.lat = -353632610L;
|
|
|
|
state.location.alt = 58400;
|
2013-10-10 05:15:07 -03:00
|
|
|
#endif
|
2012-08-21 23:19:52 -03:00
|
|
|
break;
|
2011-10-28 15:52:50 -03:00
|
|
|
case MSG_STATUS:
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("MSG_STATUS fix_status=%u fix_type=%u",
|
|
|
|
_buffer.status.fix_status,
|
|
|
|
_buffer.status.fix_type);
|
2013-03-25 07:08:47 -03:00
|
|
|
if (_buffer.status.fix_status & NAV_STATUS_FIX_VALID) {
|
|
|
|
if( _buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) {
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::GPS_OK_FIX_3D;
|
2013-03-25 07:08:47 -03:00
|
|
|
}else if (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_2D) {
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::GPS_OK_FIX_2D;
|
2013-03-25 07:08:47 -03:00
|
|
|
}else{
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::NO_FIX;
|
|
|
|
state.status = AP_GPS::NO_FIX;
|
2013-03-25 07:08:47 -03:00
|
|
|
}
|
2013-03-25 04:24:14 -03:00
|
|
|
}else{
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::NO_FIX;
|
|
|
|
state.status = AP_GPS::NO_FIX;
|
2012-08-21 23:19:52 -03:00
|
|
|
}
|
2013-10-02 03:10:22 -03:00
|
|
|
#if UBLOX_FAKE_3DLOCK
|
2014-03-28 16:52:27 -03:00
|
|
|
state.status = AP_GPS::GPS_OK_FIX_3D;
|
|
|
|
next_fix = state.status;
|
2013-10-02 03:10:22 -03:00
|
|
|
#endif
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
case MSG_SOL:
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("MSG_SOL fix_status=%u fix_type=%u",
|
|
|
|
_buffer.solution.fix_status,
|
|
|
|
_buffer.solution.fix_type);
|
2013-03-25 21:21:47 -03:00
|
|
|
if (_buffer.solution.fix_status & NAV_STATUS_FIX_VALID) {
|
|
|
|
if( _buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) {
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::GPS_OK_FIX_3D;
|
2013-03-25 21:21:47 -03:00
|
|
|
}else if (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_2D) {
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::GPS_OK_FIX_2D;
|
2013-03-25 07:08:47 -03:00
|
|
|
}else{
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::NO_FIX;
|
|
|
|
state.status = AP_GPS::NO_FIX;
|
2013-03-25 07:08:47 -03:00
|
|
|
}
|
2013-03-25 04:24:14 -03:00
|
|
|
}else{
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = AP_GPS::NO_FIX;
|
|
|
|
state.status = AP_GPS::NO_FIX;
|
2012-08-21 23:19:52 -03:00
|
|
|
}
|
2014-03-28 16:52:27 -03:00
|
|
|
state.num_sats = _buffer.solution.satellites;
|
|
|
|
state.hdop = _buffer.solution.position_DOP;
|
|
|
|
if (next_fix >= AP_GPS::GPS_OK_FIX_2D) {
|
|
|
|
state.last_gps_time_ms = hal.scheduler->millis();
|
|
|
|
if (state.time_week == _buffer.solution.week &&
|
|
|
|
state.time_week_ms + 200 == _buffer.solution.time) {
|
2013-11-05 00:18:25 -04:00
|
|
|
// we got a 5Hz update. This relies on the way
|
|
|
|
// that uBlox gives timestamps that are always
|
|
|
|
// multiples of 200 for 5Hz
|
2014-03-28 16:52:27 -03:00
|
|
|
_last_5hz_time = state.last_gps_time_ms;
|
2013-11-05 00:18:25 -04:00
|
|
|
}
|
2014-03-28 16:52:27 -03:00
|
|
|
state.time_week_ms = _buffer.solution.time;
|
|
|
|
state.time_week = _buffer.solution.week;
|
2013-10-23 08:13:48 -03:00
|
|
|
}
|
2013-10-02 03:10:22 -03:00
|
|
|
#if UBLOX_FAKE_3DLOCK
|
2014-03-28 16:52:27 -03:00
|
|
|
next_fix = state.status;
|
|
|
|
state.num_sats = 10;
|
|
|
|
state.hdop = 200;
|
|
|
|
state.time_week = 1721;
|
|
|
|
state.time_week_ms = hal.scheduler->millis() + 3*60*60*1000 + 37000;
|
|
|
|
state.last_gps_time_ms = hal.scheduler->millis();
|
2013-10-02 03:10:22 -03:00
|
|
|
#endif
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
case MSG_VELNED:
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("MSG_VELNED");
|
2014-04-14 19:23:12 -03:00
|
|
|
_last_vel_time = _buffer.velned.time;
|
2014-03-28 16:52:27 -03:00
|
|
|
state.ground_speed = _buffer.velned.speed_2d*0.01f; // m/s
|
|
|
|
state.ground_course_cd = _buffer.velned.heading_2d / 1000; // Heading 2D deg * 100000 rescaled to deg * 100
|
|
|
|
state.have_vertical_velocity = true;
|
|
|
|
state.velocity.x = _buffer.velned.ned_north * 0.01f;
|
|
|
|
state.velocity.y = _buffer.velned.ned_east * 0.01f;
|
|
|
|
state.velocity.z = _buffer.velned.ned_down * 0.01f;
|
2012-08-21 23:19:52 -03:00
|
|
|
_new_speed = true;
|
2011-10-28 15:52:50 -03:00
|
|
|
break;
|
|
|
|
default:
|
2012-08-21 23:19:52 -03:00
|
|
|
Debug("Unexpected NAV message 0x%02x", (unsigned)_msg_id);
|
|
|
|
if (++_disable_counter == 0) {
|
|
|
|
Debug("Disabling NAV message 0x%02x", (unsigned)_msg_id);
|
|
|
|
_configure_message_rate(CLASS_NAV, _msg_id, 0);
|
|
|
|
}
|
2011-10-28 15:52:50 -03:00
|
|
|
return false;
|
|
|
|
}
|
2012-06-08 03:39:12 -03:00
|
|
|
|
2012-08-21 23:19:52 -03:00
|
|
|
// we only return true when we get new position and speed data
|
|
|
|
// this ensures we don't use stale data
|
2014-04-14 19:23:12 -03:00
|
|
|
if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {
|
2012-08-21 23:19:52 -03:00
|
|
|
_new_speed = _new_position = false;
|
2012-11-04 23:31:29 -04:00
|
|
|
_fix_count++;
|
2013-11-05 00:18:25 -04:00
|
|
|
if ((hal.scheduler->millis() - _last_5hz_time) > 15000U && !need_rate_update) {
|
2013-10-01 23:32:32 -03:00
|
|
|
// the GPS is running slow. It possibly browned out and
|
|
|
|
// restarted with incorrect parameters. We will slowly
|
|
|
|
// send out new parameters to fix it
|
|
|
|
need_rate_update = true;
|
|
|
|
rate_update_step = 0;
|
2013-11-05 00:18:25 -04:00
|
|
|
_last_5hz_time = hal.scheduler->millis();
|
2013-10-01 23:32:32 -03:00
|
|
|
}
|
|
|
|
|
2014-09-04 01:27:05 -03:00
|
|
|
if (_fix_count == 50 && gps._sbas_mode != 2) {
|
|
|
|
// ask for SBAS settings every 20 seconds
|
|
|
|
Debug("Asking for SBAS setting\n");
|
|
|
|
_send_message(CLASS_CFG, MSG_CFG_SBAS, NULL, 0);
|
|
|
|
}
|
2012-11-04 23:31:29 -04:00
|
|
|
if (_fix_count == 100) {
|
|
|
|
// ask for nav settings every 20 seconds
|
|
|
|
Debug("Asking for engine setting\n");
|
|
|
|
_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS, NULL, 0);
|
2013-09-04 00:18:00 -03:00
|
|
|
_fix_count = 0;
|
2012-11-04 23:31:29 -04:00
|
|
|
}
|
2012-08-21 23:19:52 -03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2010-09-06 17:16:50 -03:00
|
|
|
}
|
2012-06-10 03:34:13 -03:00
|
|
|
|
|
|
|
|
|
|
|
// UBlox auto configuration
|
|
|
|
|
|
|
|
/*
|
2012-08-21 23:19:52 -03:00
|
|
|
* update checksum for a set of bytes
|
2012-06-10 03:34:13 -03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::_update_checksum(uint8_t *data, uint8_t len, uint8_t &ck_a, uint8_t &ck_b)
|
|
|
|
{
|
2012-08-21 23:19:52 -03:00
|
|
|
while (len--) {
|
|
|
|
ck_a += *data;
|
|
|
|
ck_b += ck_a;
|
|
|
|
data++;
|
|
|
|
}
|
2012-06-10 03:34:13 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2012-08-21 23:19:52 -03:00
|
|
|
* send a ublox message
|
2012-06-10 03:34:13 -03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::_send_message(uint8_t msg_class, uint8_t msg_id, void *msg, uint8_t size)
|
|
|
|
{
|
2012-08-21 23:19:52 -03:00
|
|
|
struct ubx_header header;
|
|
|
|
uint8_t ck_a=0, ck_b=0;
|
|
|
|
header.preamble1 = PREAMBLE1;
|
|
|
|
header.preamble2 = PREAMBLE2;
|
|
|
|
header.msg_class = msg_class;
|
|
|
|
header.msg_id = msg_id;
|
|
|
|
header.length = size;
|
|
|
|
|
|
|
|
_update_checksum((uint8_t *)&header.msg_class, sizeof(header)-2, ck_a, ck_b);
|
|
|
|
_update_checksum((uint8_t *)msg, size, ck_a, ck_b);
|
|
|
|
|
2014-03-28 16:52:27 -03:00
|
|
|
port->write((const uint8_t *)&header, sizeof(header));
|
|
|
|
port->write((const uint8_t *)msg, size);
|
|
|
|
port->write((const uint8_t *)&ck_a, 1);
|
|
|
|
port->write((const uint8_t *)&ck_b, 1);
|
2012-06-10 03:34:13 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2012-08-21 23:19:52 -03:00
|
|
|
* configure a UBlox GPS for the given message rate for a specific
|
|
|
|
* message class and msg_id
|
2012-06-10 03:34:13 -03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::_configure_message_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate)
|
|
|
|
{
|
2012-08-21 23:19:52 -03:00
|
|
|
struct ubx_cfg_msg_rate msg;
|
|
|
|
msg.msg_class = msg_class;
|
|
|
|
msg.msg_id = msg_id;
|
|
|
|
msg.rate = rate;
|
|
|
|
_send_message(CLASS_CFG, MSG_CFG_SET_RATE, &msg, sizeof(msg));
|
2012-06-10 03:34:13 -03:00
|
|
|
}
|
|
|
|
|
2013-10-01 23:32:32 -03:00
|
|
|
/*
|
|
|
|
* configure a UBlox GPS navigation solution rate of 200ms
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::_configure_navigation_rate(uint16_t rate_ms)
|
|
|
|
{
|
|
|
|
struct ubx_cfg_nav_rate msg;
|
|
|
|
msg.measure_rate_ms = rate_ms;
|
|
|
|
msg.nav_rate = 1;
|
|
|
|
msg.timeref = 0; // UTC time
|
|
|
|
_send_message(CLASS_CFG, MSG_CFG_RATE, &msg, sizeof(msg));
|
|
|
|
}
|
|
|
|
|
2012-06-10 03:34:13 -03:00
|
|
|
/*
|
2012-08-21 23:19:52 -03:00
|
|
|
* configure a UBlox GPS for the given message rate
|
2012-06-10 03:34:13 -03:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
AP_GPS_UBLOX::_configure_gps(void)
|
|
|
|
{
|
2014-03-28 16:52:27 -03:00
|
|
|
port->begin(38400U);
|
2012-08-21 23:19:52 -03:00
|
|
|
|
2013-10-02 03:15:15 -03:00
|
|
|
// start the process of updating the GPS rates
|
|
|
|
need_rate_update = true;
|
2013-11-05 00:18:25 -04:00
|
|
|
_last_5hz_time = hal.scheduler->millis();
|
2013-10-02 03:15:15 -03:00
|
|
|
rate_update_step = 0;
|
2012-08-21 23:19:52 -03:00
|
|
|
|
|
|
|
// ask for the current navigation settings
|
2012-11-04 23:31:29 -04:00
|
|
|
Debug("Asking for engine setting\n");
|
2012-08-21 23:19:52 -03:00
|
|
|
_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS, NULL, 0);
|
2012-06-10 03:34:13 -03:00
|
|
|
}
|
2012-09-17 01:43:07 -03:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
detect a Ublox GPS. Adds one byte, and returns true if the stream
|
|
|
|
matches a UBlox
|
|
|
|
*/
|
|
|
|
bool
|
2014-03-28 16:52:27 -03:00
|
|
|
AP_GPS_UBLOX::_detect(struct UBLOX_detect_state &state, uint8_t data)
|
2012-09-17 01:43:07 -03:00
|
|
|
{
|
2013-01-05 01:32:42 -04:00
|
|
|
reset:
|
2014-03-28 00:50:44 -03:00
|
|
|
switch (state.step) {
|
2012-09-17 01:43:07 -03:00
|
|
|
case 1:
|
|
|
|
if (PREAMBLE2 == data) {
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
}
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step = 0;
|
2012-09-17 01:43:07 -03:00
|
|
|
case 0:
|
|
|
|
if (PREAMBLE1 == data)
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 2:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
|
|
|
state.ck_b = state.ck_a = data;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 3:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
|
|
|
state.ck_b += (state.ck_a += data);
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 4:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
|
|
|
state.ck_b += (state.ck_a += data);
|
|
|
|
state.payload_length = data;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 5:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
|
|
|
state.ck_b += (state.ck_a += data);
|
|
|
|
state.payload_counter = 0;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 6:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.ck_b += (state.ck_a += data);
|
|
|
|
if (++state.payload_counter == state.payload_length)
|
|
|
|
state.step++;
|
2012-09-17 01:43:07 -03:00
|
|
|
break;
|
|
|
|
case 7:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step++;
|
|
|
|
if (state.ck_a != data) {
|
|
|
|
state.step = 0;
|
2013-01-05 01:32:42 -04:00
|
|
|
goto reset;
|
2012-09-17 01:43:07 -03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
2014-03-28 00:50:44 -03:00
|
|
|
state.step = 0;
|
|
|
|
if (state.ck_b == data) {
|
2012-09-17 01:43:07 -03:00
|
|
|
// a valid UBlox packet
|
|
|
|
return true;
|
2013-01-05 01:32:42 -04:00
|
|
|
} else {
|
|
|
|
goto reset;
|
2012-09-17 01:43:07 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|