Path: blob/master/libraries/AP_GPS/AP_GPS_UBLOX.cpp
9565 views
/*1This program is free software: you can redistribute it and/or modify2it under the terms of the GNU General Public License as published by3the Free Software Foundation, either version 3 of the License, or4(at your option) any later version.56This program is distributed in the hope that it will be useful,7but WITHOUT ANY WARRANTY; without even the implied warranty of8MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9GNU General Public License for more details.1011You should have received a copy of the GNU General Public License12along with this program. If not, see <http://www.gnu.org/licenses/>.13*/1415//16// u-blox GPS driver for ArduPilot17// Origin code by Michael Smith, Jordi Munoz and Jose Julio, DIYDrones.com18// Substantially rewritten for new GPS driver structure by Andrew Tridgell19//20#include "AP_GPS_UBLOX.h"2122#if AP_GPS_UBLOX_ENABLED2324#include "AP_GPS.h"25#include <AP_HAL/Util.h>26#include <AP_Logger/AP_Logger.h>27#include <GCS_MAVLink/GCS.h>28#include "RTCM3_Parser.h"29#include <stdio.h>3031#ifndef UBLOX_SPEED_CHANGE32#define UBLOX_SPEED_CHANGE 033#endif343536#define UBLOX_DEBUGGING 037#define UBLOX_FAKE_3DLOCK 038#ifndef CONFIGURE_PPS_PIN39#define CONFIGURE_PPS_PIN 040#endif4142// this is number of epochs per output. A higher value will reduce43// the uart bandwidth needed and allow for higher latency44#define RTK_MB_RTCM_RATE 14546// use this to enable debugging of moving baseline configs47#define UBLOX_MB_DEBUGGING 04849// debug VALGET/VALSET configuration50#define UBLOX_CFG_DEBUGGING 05152extern const AP_HAL::HAL& hal;5354#if UBLOX_DEBUGGING55#if defined(HAL_BUILD_AP_PERIPH)56extern "C" {57void can_printf(const char *fmt, ...);58}59# define Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)60#else61# define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)62#endif63#else64# define Debug(fmt, args ...)65#endif6667#if UBLOX_MB_DEBUGGING68#if defined(HAL_BUILD_AP_PERIPH)69extern "C" {70void can_printf(const char *fmt, ...);71}72# define MB_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)73#else74# define MB_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)75#endif76#else77# define MB_Debug(fmt, args ...)78#endif7980#if UBLOX_CFG_DEBUGGING81#if defined(HAL_BUILD_AP_PERIPH)82extern "C" {83void can_printf(const char *fmt, ...);84}85# define CFG_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)86#else87# define CFG_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)88#endif89#else90# define CFG_Debug(fmt, args ...)91#endif9293AP_GPS_UBLOX::AP_GPS_UBLOX(AP_GPS &_gps,94AP_GPS::Params &_params,95AP_GPS::GPS_State &_state,96AP_HAL::UARTDriver *_port,97AP_GPS::GPS_Role _role) :98AP_GPS_Backend(_gps, _params, _state, _port),99role(_role)100{101// stop any config strings that are pending102gps.send_blob_start(state.instance, nullptr, 0);103104// start the process of updating the GPS rates105_request_next_config();106107#if CONFIGURE_PPS_PIN108_unconfigured_messages |= CONFIG_TP5;109#endif110111#if GPS_MOVING_BASELINE112if (role == AP_GPS::GPS_ROLE_MB_BASE && !mb_use_uart2()) {113rtcm3_parser = NEW_NOTHROW RTCM3_Parser;114if (rtcm3_parser == nullptr) {115GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "u-blox %d: failed RTCMv3 parser allocation", state.instance + 1);116}117_unconfigured_messages |= CONFIG_RTK_MOVBASE;118}119if (role == AP_GPS::GPS_ROLE_MB_ROVER) {120_unconfigured_messages |= CONFIG_RTK_MOVBASE;121state.gps_yaw_configured = true;122}123#endif124}125126AP_GPS_UBLOX::~AP_GPS_UBLOX()127{128#if GPS_MOVING_BASELINE129delete rtcm3_parser;130#endif131132free(config_GNSS);133}134135#if GPS_MOVING_BASELINE136/*137config for F9 GPS in moving baseline base role138See ZED-F9P integration manual section 3.1.5.6.1139*/140const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Base_uart1[] {141{ ConfigKey::CFG_UART1OUTPROT_RTCM3X, 1},142{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},143{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 0},144{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},145{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, RTK_MB_RTCM_RATE},146{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, RTK_MB_RTCM_RATE},147{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, RTK_MB_RTCM_RATE},148{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, RTK_MB_RTCM_RATE},149{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, RTK_MB_RTCM_RATE},150{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, RTK_MB_RTCM_RATE},151{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, RTK_MB_RTCM_RATE},152{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},153{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},154{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},155{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},156{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},157{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},158{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},159};160161const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Base_uart2[] {162{ ConfigKey::CFG_UART2_ENABLED, 1},163{ ConfigKey::CFG_UART2_BAUDRATE, 460800},164{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 1},165{ ConfigKey::CFG_UART1OUTPROT_RTCM3X, 0},166{ ConfigKey::CFG_UART1INPROT_RTCM3X, 1},167{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},168{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 0},169{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, RTK_MB_RTCM_RATE},170{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, RTK_MB_RTCM_RATE},171{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, RTK_MB_RTCM_RATE},172{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, RTK_MB_RTCM_RATE},173{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, RTK_MB_RTCM_RATE},174{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, RTK_MB_RTCM_RATE},175{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, RTK_MB_RTCM_RATE},176{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},177{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},178{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},179{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},180{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},181{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},182{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},183};184185186/*187config for F9 GPS in moving baseline rover role188See ZED-F9P integration manual section 3.1.5.6.1.189Note that we list the RTCM msg types as 0 to prevent getting RTCM190data from a GPS previously configured as a base191*/192const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Rover_uart1[] {193{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},194{ ConfigKey::CFG_UART1INPROT_RTCM3X, 1},195{ ConfigKey::CFG_UART2INPROT_RTCM3X, 0},196{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 1},197{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},198{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},199{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},200{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},201{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},202{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},203{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},204{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},205{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},206{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},207{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},208{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},209{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},210{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},211{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},212};213214const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Rover_uart2[] {215{ ConfigKey::CFG_UART2_ENABLED, 1},216{ ConfigKey::CFG_UART2_BAUDRATE, 460800},217{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},218{ ConfigKey::CFG_UART2INPROT_RTCM3X, 1},219{ ConfigKey::CFG_UART1INPROT_RTCM3X, 0},220{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 1},221{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},222{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},223{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},224{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},225{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},226{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},227{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},228{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},229{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},230{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},231{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},232{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},233{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},234{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},235{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},236};237#endif // GPS_MOVING_BASELINE238239/*240config changes for M10241we need to use B1C not B1 signal for Beidou on M10 to allow solid 5Hz,242and also disable Glonass and enable QZSS243*/244const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_M10[] {245{ ConfigKey::CFG_SIGNAL_BDS_ENA, 1},246{ ConfigKey::CFG_SIGNAL_BDS_B1_ENA, 0},247{ ConfigKey::CFG_SIGNAL_BDS_B1C_ENA, 1},248{ ConfigKey::CFG_SIGNAL_GLO_ENA, 0},249{ ConfigKey::CFG_SIGNAL_QZSS_ENA, 1},250{ ConfigKey::CFG_SIGNAL_QZSS_L1CA_ENA, 1},251{ ConfigKey::CFG_SIGNAL_QZSS_L1S_ENA, 1},252{ ConfigKey::CFG_NAVSPG_DYNMODEL, 8}, // Air < 4g253};254255256/*257config changes for L5 modules258*/259const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_L5_ovrd_ena[] {260{ConfigKey::CFG_SIGNAL_L5_HEALTH_OVRD, 1},261{ConfigKey::CFG_SIGNAL_GPS_L5_ENA, 1},262};263264const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_L5_ovrd_dis[] {265{ConfigKey::CFG_SIGNAL_L5_HEALTH_OVRD, 0},266};267268void269AP_GPS_UBLOX::_request_next_config(void)270{271// don't request config if we shouldn't configure the GPS272if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {273return;274}275276// Ensure there is enough space for the largest possible outgoing message277if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_nav_rate)+2)) {278// not enough space - do it next time279return;280}281282if ((_unconfigured_messages & CONFIG_RATE_SOL) != 0 && havePvtMsg) {283/*284we don't need SOL if we have PVT and TIMEGPS. This is needed285as F9P doesn't support the SOL message286*/287_unconfigured_messages &= ~CONFIG_RATE_SOL;288}289290Debug("Unconfigured messages: 0x%x Current message: %u\n", (unsigned)_unconfigured_messages, (unsigned)_next_message);291292// check AP_GPS_UBLOX.h for the enum that controls the order.293// This switch statement isn't maintained against the enum in order to reduce code churn294switch (_next_message++) {295case STEP_PVT:296if(!_request_message_rate(CLASS_NAV, MSG_PVT)) {297_next_message--;298}299break;300case STEP_TIMEGPS:301if(!_request_message_rate(CLASS_NAV, MSG_TIMEGPS)) {302_next_message--;303}304break;305case STEP_PORT:306_request_port();307break;308case STEP_POLL_SVINFO:309// not required once we know what generation we are on310if(_hardware_generation == UBLOX_UNKNOWN_HARDWARE_GENERATION) {311if (!_send_message(CLASS_NAV, MSG_NAV_SVINFO, 0, 0)) {312_next_message--;313}314}315break;316case STEP_POLL_SBAS:317if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {318_send_message(CLASS_CFG, MSG_CFG_SBAS, nullptr, 0);319} else {320_unconfigured_messages &= ~CONFIG_SBAS;321}322break;323case STEP_POLL_NAV:324if (!_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS, nullptr, 0)) {325_next_message--;326}327break;328case STEP_POLL_GNSS:329if (supports_F9_config()) {330if (last_configured_gnss != params.gnss_mode) {331_unconfigured_messages |= CONFIG_F9;332}333break;334}335if (!_send_message(CLASS_CFG, MSG_CFG_GNSS, nullptr, 0)) {336_next_message--;337}338break;339case STEP_POLL_TP5:340#if CONFIGURE_PPS_PIN341if (!_send_message(CLASS_CFG, MSG_CFG_TP5, nullptr, 0)) {342_next_message--;343}344#endif345break;346case STEP_NAV_RATE:347if (!_send_message(CLASS_CFG, MSG_CFG_RATE, nullptr, 0)) {348_next_message--;349}350break;351case STEP_POSLLH:352if(!_request_message_rate(CLASS_NAV, MSG_POSLLH)) {353_next_message--;354}355break;356case STEP_STATUS:357if(!_request_message_rate(CLASS_NAV, MSG_STATUS)) {358_next_message--;359}360break;361case STEP_SOL:362if(!_request_message_rate(CLASS_NAV, MSG_SOL)) {363_next_message--;364}365break;366case STEP_VELNED:367if(!_request_message_rate(CLASS_NAV, MSG_VELNED)) {368_next_message--;369}370break;371case STEP_DOP:372if(! _request_message_rate(CLASS_NAV, MSG_DOP)) {373_next_message--;374}375break;376case STEP_MON_HW:377if(!_request_message_rate(CLASS_MON, MSG_MON_HW)) {378_next_message--;379}380break;381case STEP_MON_HW2:382if(!_request_message_rate(CLASS_MON, MSG_MON_HW2)) {383_next_message--;384}385break;386case STEP_RAW:387#if UBLOX_RXM_RAW_LOGGING388if(gps._raw_data == 0) {389_unconfigured_messages &= ~CONFIG_RATE_RAW;390} else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAW)) {391_next_message--;392}393#else394_unconfigured_messages & = ~CONFIG_RATE_RAW;395#endif396break;397case STEP_RAWX:398#if UBLOX_RXM_RAW_LOGGING399if(gps._raw_data == 0) {400_unconfigured_messages &= ~CONFIG_RATE_RAW;401} else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAWX)) {402_next_message--;403}404#else405_unconfigured_messages & = ~CONFIG_RATE_RAW;406#endif407break;408case STEP_VERSION:409if(!_have_version && !hal.util->get_soft_armed()) {410_request_version();411} else {412_unconfigured_messages &= ~CONFIG_VERSION;413}414break;415case STEP_TMODE:416if (supports_F9_config()) {417if (!_configure_valget(ConfigKey::TMODE_MODE)) {418_next_message--;419}420}421break;422case STEP_RTK_MOVBASE:423#if GPS_MOVING_BASELINE424if (supports_F9_config()) {425static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart1), "done_mask too small, base1");426static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart2), "done_mask too small, base2");427static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart1), "done_mask too small, rover1");428static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart2), "done_mask too small, rover2");429if (role == AP_GPS::GPS_ROLE_MB_BASE) {430const config_list *list = mb_use_uart2()?config_MB_Base_uart2:config_MB_Base_uart1;431uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Base_uart2):ARRAY_SIZE(config_MB_Base_uart1);432if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {433_next_message--;434}435}436if (role == AP_GPS::GPS_ROLE_MB_ROVER) {437const config_list *list = mb_use_uart2()?config_MB_Rover_uart2:config_MB_Rover_uart1;438uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Rover_uart2):ARRAY_SIZE(config_MB_Rover_uart1);439if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {440_next_message--;441}442}443}444#endif445break;446case STEP_TIM_TM2:447#if UBLOX_TIM_TM2_LOGGING448if(!_request_message_rate(CLASS_TIM, MSG_TIM_TM2)) {449_next_message--;450}451#else452_unconfigured_messages &= ~CONFIG_TIM_TM2;453#endif454break;455456case STEP_F9: {457if (_hardware_generation == UBLOX_F9 ||458_hardware_generation == UBLOX_M10) {459uint8_t cfg_count = populate_F9_gnss();460// special handling of F9 config461if (cfg_count > 0) {462CFG_Debug("Sending F9 settings, GNSS=%u", unsigned(params.gnss_mode));463464if (!_configure_list_valset(config_GNSS, cfg_count, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {465_next_message--;466break;467}468_f9_config_time = AP_HAL::millis();469}470}471break;472}473474case STEP_F9_VALIDATE: {475if (_hardware_generation == UBLOX_F9 ||476_hardware_generation == UBLOX_M10) {477// GNSS takes 0.5s to reset478if (AP_HAL::millis() - _f9_config_time < 500) {479_next_message--;480break;481}482_f9_config_time = 0;483uint8_t cfg_count = populate_F9_gnss();484// special handling of F9 config485if (cfg_count > 0) {486CFG_Debug("Validating F9 settings, GNSS=%u", unsigned(params.gnss_mode));487// now validate all of the settings, this is a no-op if the first call succeeded488if (!_configure_config_set(config_GNSS, cfg_count, CONFIG_F9, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {489_next_message--;490}491}492}493break;494}495case STEP_M10: {496if (_hardware_generation == UBLOX_M10) {497// special handling of M10 config498const config_list *list = config_M10;499const uint8_t list_length = ARRAY_SIZE(config_M10);500Debug("Sending M10 settings");501if (!_configure_config_set(list, list_length, CONFIG_M10, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {502_next_message--;503}504}505break;506}507508case STEP_L5: {509if (supports_l5 && option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {510const config_list *list = config_L5_ovrd_ena;511const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_ena);512if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {513_next_message--;514}515} else if (supports_l5 && !option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {516const config_list *list = config_L5_ovrd_dis;517const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_dis);518if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {519_next_message--;520}521}522break;523}524525default:526// this case should never be reached, do a full reset if it is hit527_next_message = STEP_PVT;528break;529}530}531532void533AP_GPS_UBLOX::_verify_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate) {534uint8_t desired_rate;535uint32_t config_msg_id;536switch(msg_class) {537case CLASS_NAV:538switch(msg_id) {539case MSG_POSLLH:540desired_rate = havePvtMsg ? 0 : RATE_POSLLH;541config_msg_id = CONFIG_RATE_POSLLH;542break;543case MSG_STATUS:544desired_rate = havePvtMsg ? 0 : RATE_STATUS;545config_msg_id = CONFIG_RATE_STATUS;546break;547case MSG_SOL:548desired_rate = havePvtMsg ? 0 : RATE_SOL;549config_msg_id = CONFIG_RATE_SOL;550break;551case MSG_PVT:552desired_rate = RATE_PVT;553config_msg_id = CONFIG_RATE_PVT;554break;555case MSG_TIMEGPS:556desired_rate = RATE_TIMEGPS;557config_msg_id = CONFIG_RATE_TIMEGPS;558break;559case MSG_VELNED:560desired_rate = havePvtMsg ? 0 : RATE_VELNED;561config_msg_id = CONFIG_RATE_VELNED;562break;563case MSG_DOP:564desired_rate = RATE_DOP;565config_msg_id = CONFIG_RATE_DOP;566break;567default:568return;569}570break;571case CLASS_MON:572switch(msg_id) {573case MSG_MON_HW:574desired_rate = RATE_HW;575config_msg_id = CONFIG_RATE_MON_HW;576break;577case MSG_MON_HW2:578desired_rate = RATE_HW2;579config_msg_id = CONFIG_RATE_MON_HW2;580break;581default:582return;583}584break;585#if UBLOX_RXM_RAW_LOGGING586case CLASS_RXM:587switch(msg_id) {588case MSG_RXM_RAW:589desired_rate = gps._raw_data;590config_msg_id = CONFIG_RATE_RAW;591break;592case MSG_RXM_RAWX:593desired_rate = gps._raw_data;594config_msg_id = CONFIG_RATE_RAW;595break;596default:597return;598}599break;600#endif // UBLOX_RXM_RAW_LOGGING601#if UBLOX_TIM_TM2_LOGGING602case CLASS_TIM:603if (msg_id == MSG_TIM_TM2) {604desired_rate = RATE_TIM_TM2;605config_msg_id = CONFIG_TIM_TM2;606break;607}608return;609#endif // UBLOX_TIM_TM2_LOGGING610default:611return;612}613614if (rate == desired_rate) {615// coming in at correct rate; mark as configured616_unconfigured_messages &= ~config_msg_id;617return;618}619620// coming in at wrong rate; try to configure it621_configure_message_rate(msg_class, msg_id, desired_rate);622_unconfigured_messages |= config_msg_id;623_cfg_needs_save = true;624}625626// Requests the ublox driver to identify what port we are using to communicate627void628AP_GPS_UBLOX::_request_port(void)629{630if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+2)) {631// not enough space - do it next time632return;633}634_send_message(CLASS_CFG, MSG_CFG_PRT, nullptr, 0);635}636637// Ensure there is enough space for the largest possible outgoing message638// Process bytes available from the stream639//640// The stream is assumed to contain only messages we recognise. If it641// contains other messages, and those messages contain the preamble642// bytes, it is possible for this code to fail to synchronise to the643// stream immediately. Without buffering the entire message and644// re-processing it from the top, this is unavoidable. The parser645// attempts to avoid this when possible.646//647bool648AP_GPS_UBLOX::read(void)649{650bool parsed = false;651uint32_t millis_now = AP_HAL::millis();652653// walk through the gps configuration at 1 message per second654if (millis_now - _last_config_time >= _delay_time) {655_request_next_config();656_last_config_time = millis_now;657if (_unconfigured_messages) {658// send the updates faster until fully configured659_delay_time = 200;660} else {661_delay_time = 2000;662}663}664665#if 0666// this can be modified to force a particular config state for667// state machine config debugging668static bool done_force_config_error;669if (!_unconfigured_messages && !done_force_config_error) {670done_force_config_error = true;671_unconfigured_messages = CONFIG_F9 | CONFIG_RATE_SOL;672GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Forcing config state 0x%04x", unsigned(_unconfigured_messages));673}674#endif675676if(!_unconfigured_messages && gps._save_config && !_cfg_saved &&677_num_cfg_save_tries < 5 && (millis_now - _last_cfg_sent_time) > 5000 &&678!hal.util->get_soft_armed()) {679//save the configuration sent until now680if (gps._save_config == 1 ||681(gps._save_config == 2 && _cfg_needs_save)) {682_save_cfg();683}684}685686const uint16_t numc = MIN(port->available(), 8192U);687for (uint16_t i = 0; i < numc; i++) { // Process bytes received688689// read the next byte690uint8_t data;691if (!port->read(data)) {692break;693}694#if AP_GPS_DEBUG_LOGGING_ENABLED695log_data(&data, 1);696#endif697698#if GPS_MOVING_BASELINE699if (rtcm3_parser) {700if (rtcm3_parser->read(data)) {701// we've found a RTCMv3 packet. We stop parsing at702// this point and reset u-blox parse state. We need to703// stop parsing to give the higher level driver a704// chance to send the RTCMv3 packet to another (rover)705// GPS706_step = 0;707break;708}709}710#endif711712reset:713switch(_step) {714715// Message preamble detection716//717// If we fail to match any of the expected bytes, we reset718// the state machine and re-consider the failed byte as719// the first byte of the preamble. This improves our720// chances of recovering from a mismatch and makes it less721// likely that we will be fooled by the preamble appearing722// as data in some other message.723//724case 1:725if (PREAMBLE2 == data) {726_step++;727break;728}729_step = 0;730Debug("reset %u", __LINE__);731FALLTHROUGH;732case 0:733if(PREAMBLE1 == data)734_step++;735break;736737// Message header processing738//739// We sniff the class and message ID to decide whether we740// are going to gather the message bytes or just discard741// them.742//743// We always collect the length so that we can avoid being744// fooled by preamble bytes in messages.745//746case 2:747_step++;748_class = data;749_ck_b = _ck_a = data; // reset the checksum accumulators750break;751case 3:752_step++;753_ck_b += (_ck_a += data); // checksum byte754_msg_id = data;755break;756case 4:757_step++;758_ck_b += (_ck_a += data); // checksum byte759_payload_length = data; // payload length low byte760break;761case 5:762_step++;763_ck_b += (_ck_a += data); // checksum byte764765_payload_length += (uint16_t)(data<<8);766if (_payload_length > sizeof(_buffer)) {767Debug("large payload %u", (unsigned)_payload_length);768// assume any payload bigger then what we know about is noise769_payload_length = 0;770_step = 0;771goto reset;772}773_payload_counter = 0; // prepare to receive payload774if (_payload_length == 0) {775// bypass payload and go straight to checksum776_step++;777}778break;779780// Receive message data781//782case 6:783_ck_b += (_ck_a += data); // checksum byte784if (_payload_counter < sizeof(_buffer)) {785_buffer[_payload_counter] = data;786}787if (++_payload_counter == _payload_length)788_step++;789break;790791// Checksum and message processing792//793case 7:794_step++;795if (_ck_a != data) {796Debug("bad cka %x should be %x", data, _ck_a);797_step = 0;798goto reset;799}800break;801case 8:802_step = 0;803if (_ck_b != data) {804Debug("bad ckb %x should be %x", data, _ck_b);805break; // bad checksum806}807808#if GPS_MOVING_BASELINE809if (rtcm3_parser) {810// this is a uBlox packet, discard any partial RTCMv3 state811rtcm3_parser->reset();812}813#endif814if (_parse_gps()) {815parsed = true;816}817break;818}819}820return parsed;821}822823// Private Methods /////////////////////////////////////////////////////////////824void AP_GPS_UBLOX::log_mon_hw(void)825{826#if HAL_LOGGING_ENABLED827if (!should_log()) {828return;829}830struct log_Ubx1 pkt = {831LOG_PACKET_HEADER_INIT(LOG_GPS_UBX1_MSG),832time_us : AP_HAL::micros64(),833instance : state.instance,834noisePerMS : _buffer.mon_hw_60.noisePerMS,835jamInd : _buffer.mon_hw_60.jamInd,836aPower : _buffer.mon_hw_60.aPower,837agcCnt : _buffer.mon_hw_60.agcCnt,838config : _unconfigured_messages,839};840if (_payload_length == 68) {841pkt.noisePerMS = _buffer.mon_hw_68.noisePerMS;842pkt.jamInd = _buffer.mon_hw_68.jamInd;843pkt.aPower = _buffer.mon_hw_68.aPower;844pkt.agcCnt = _buffer.mon_hw_68.agcCnt;845}846AP::logger().WriteBlock(&pkt, sizeof(pkt));847#endif848}849850void AP_GPS_UBLOX::log_mon_hw2(void)851{852#if HAL_LOGGING_ENABLED853if (!should_log()) {854return;855}856857struct log_Ubx2 pkt = {858LOG_PACKET_HEADER_INIT(LOG_GPS_UBX2_MSG),859time_us : AP_HAL::micros64(),860instance : state.instance,861ofsI : _buffer.mon_hw2.ofsI,862magI : _buffer.mon_hw2.magI,863ofsQ : _buffer.mon_hw2.ofsQ,864magQ : _buffer.mon_hw2.magQ,865};866AP::logger().WriteBlock(&pkt, sizeof(pkt));867#endif868}869870#if UBLOX_TIM_TM2_LOGGING871void AP_GPS_UBLOX::log_tim_tm2(void)872{873#if HAL_LOGGING_ENABLED874if (!should_log()) {875return;876}877878// @LoggerMessage: UBXT879// @Description: uBlox specific UBX-TIM-TM2 logging, see uBlox interface description880// @Field: TimeUS: Time since system startup881// @Field: I: GPS instance number882// @Field: ch: Channel (i.e. EXTINT) upon which the pulse was measured883// @Field: flags: Bitmask884// @Field: count: Rising edge counter885// @Field: wnR: Week number of last rising edge886// @Field: MsR: Tow of rising edge887// @Field: SubMsR: Millisecond fraction of tow of rising edge in nanoseconds888// @Field: wnF: Week number of last falling edge889// @Field: MsF: Tow of falling edge890// @Field: SubMsF: Millisecond fraction of tow of falling edge in nanoseconds891// @Field: accEst: Accuracy estimate892893AP::logger().WriteStreaming("UBXT",894"TimeUS,I,ch,flags,count,wnR,MsR,SubMsR,wnF,MsF,SubMsF,accEst",895"s#----ss-sss",896"F-----CI-CII",897"QBBBHHIIHIII",898AP_HAL::micros64(),899state.instance,900_buffer.tim_tm2.ch,901_buffer.tim_tm2.flags,902_buffer.tim_tm2.count,903_buffer.tim_tm2.wnR,904_buffer.tim_tm2.towMsR,905_buffer.tim_tm2.towSubMsR,906_buffer.tim_tm2.wnF,907_buffer.tim_tm2.towMsF,908_buffer.tim_tm2.towSubMsF,909_buffer.tim_tm2.accEst);910#endif911}912#endif // UBLOX_TIM_TM2_LOGGING913914#if UBLOX_RXM_RAW_LOGGING915void AP_GPS_UBLOX::log_rxm_raw(const struct ubx_rxm_raw &raw)916{917#if HAL_LOGGING_ENABLED918if (!should_log()) {919return;920}921922uint64_t now = AP_HAL::micros64();923for (uint8_t i=0; i<raw.numSV; i++) {924struct log_GPS_RAW pkt = {925LOG_PACKET_HEADER_INIT(LOG_GPS_RAW_MSG),926time_us : now,927iTOW : raw.iTOW,928week : raw.week,929numSV : raw.numSV,930sv : raw.svinfo[i].sv,931cpMes : raw.svinfo[i].cpMes,932prMes : raw.svinfo[i].prMes,933doMes : raw.svinfo[i].doMes,934mesQI : raw.svinfo[i].mesQI,935cno : raw.svinfo[i].cno,936lli : raw.svinfo[i].lli937};938AP::logger().WriteBlock(&pkt, sizeof(pkt));939}940#endif941}942943void AP_GPS_UBLOX::log_rxm_rawx(const struct ubx_rxm_rawx &raw)944{945#if HAL_LOGGING_ENABLED946if (!should_log()) {947return;948}949950uint64_t now = AP_HAL::micros64();951952struct log_GPS_RAWH header = {953LOG_PACKET_HEADER_INIT(LOG_GPS_RAWH_MSG),954time_us : now,955rcvTow : raw.rcvTow,956week : raw.week,957leapS : raw.leapS,958numMeas : raw.numMeas,959recStat : raw.recStat960};961AP::logger().WriteBlock(&header, sizeof(header));962963for (uint8_t i=0; i<raw.numMeas; i++) {964struct log_GPS_RAWS pkt = {965LOG_PACKET_HEADER_INIT(LOG_GPS_RAWS_MSG),966time_us : now,967prMes : raw.svinfo[i].prMes,968cpMes : raw.svinfo[i].cpMes,969doMes : raw.svinfo[i].doMes,970gnssId : raw.svinfo[i].gnssId,971svId : raw.svinfo[i].svId,972freqId : raw.svinfo[i].freqId,973locktime : raw.svinfo[i].locktime,974cno : raw.svinfo[i].cno,975prStdev : raw.svinfo[i].prStdev,976cpStdev : raw.svinfo[i].cpStdev,977doStdev : raw.svinfo[i].doStdev,978trkStat : raw.svinfo[i].trkStat979};980AP::logger().WriteBlock(&pkt, sizeof(pkt));981}982#endif983}984#endif // UBLOX_RXM_RAW_LOGGING985986void AP_GPS_UBLOX::unexpected_message(void)987{988Debug("Unexpected message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);989if (++_disable_counter == 0) {990// disable future sends of this message id, but991// only do this every 256 messages, as some992// message types can't be disabled and we don't993// want to get into an ack war994Debug("Disabling message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);995_configure_message_rate(_class, _msg_id, 0);996}997}998999// return size of a config key, or 0 if unknown1000uint8_t AP_GPS_UBLOX::config_key_size(ConfigKey key) const1001{1002// step over the value1003const uint8_t key_size = (uint32_t(key) >> 28) & 0x07; // mask off the storage size1004switch (key_size) {1005case 0x1: // bit1006case 0x2: // byte1007return 1;1008case 0x3: // 2 bytes1009return 2;1010case 0x4: // 4 bytes1011return 4;1012case 0x5: // 8 bytes1013return 8;1014default:1015break;1016}1017// invalid1018return 0;1019}10201021/*1022find index of a config key in the active_config list, or -11023*/1024int8_t AP_GPS_UBLOX::find_active_config_index(ConfigKey key) const1025{1026if (active_config.list == nullptr) {1027return -1;1028}1029for (uint8_t i=0; i<active_config.count; i++) {1030if (key == active_config.list[i].key) {1031return (int8_t)i;1032}1033}10341035return -1;1036}10371038bool1039AP_GPS_UBLOX::_parse_gps(void)1040{1041if (_class == CLASS_ACK) {1042Debug("ACK %u", (unsigned)_msg_id);10431044if(_msg_id == MSG_ACK_ACK) {1045switch(_buffer.ack.clsID) {1046case CLASS_CFG:1047switch(_buffer.ack.msgID) {1048case MSG_CFG_CFG:1049_cfg_saved = true;1050_cfg_needs_save = false;1051break;1052case MSG_CFG_GNSS:1053_unconfigured_messages &= ~CONFIG_GNSS;1054break;1055case MSG_CFG_MSG:1056// There is no way to know what MSG config was ack'ed, assume it was the last1057// one requested. To verify it rerequest the last config we sent. If we miss1058// the actual ack we will catch it next time through the poll loop, but that1059// will be a good chunk of time later.1060break;1061case MSG_CFG_NAV_SETTINGS:1062_unconfigured_messages &= ~CONFIG_NAV_SETTINGS;1063break;1064case MSG_CFG_RATE:1065// The GPS will ACK a update rate that is invalid. in order to detect this1066// only accept the rate as configured by reading the settings back and1067// validating that they all match the target values1068break;1069case MSG_CFG_SBAS:1070_unconfigured_messages &= ~CONFIG_SBAS;1071break;1072case MSG_CFG_TP5:1073_unconfigured_messages &= ~CONFIG_TP5;1074break;1075}10761077break;1078case CLASS_MON:1079switch(_buffer.ack.msgID) {1080case MSG_MON_HW:1081_unconfigured_messages &= ~CONFIG_RATE_MON_HW;1082break;1083case MSG_MON_HW2:1084_unconfigured_messages &= ~CONFIG_RATE_MON_HW2;1085break;1086}1087}1088}1089if(_msg_id == MSG_ACK_NACK) {1090switch(_buffer.nack.clsID) {1091case CLASS_CFG:1092switch(_buffer.nack.msgID) {1093case MSG_CFG_VALGET:1094CFG_Debug("NACK VALGET 0x%x", (unsigned)_buffer.nack.msgID);1095if (active_config.list != nullptr) {1096/*1097likely this device does not support fetching multiple keys at once, go one at a time1098*/1099if (active_config.fetch_index == -1) {1100CFG_Debug("NACK starting %u", unsigned(active_config.count));1101active_config.fetch_index = 0;1102use_single_valget = true;1103} else {1104// the device does not support the config key we asked for,1105// consider the bit as done1106active_config.done_mask |= (1U<<active_config.fetch_index);1107CFG_Debug("NACK %d 0x%x done=0x%x",1108int(active_config.fetch_index),1109unsigned(active_config.list[active_config.fetch_index].key),1110unsigned(active_config.done_mask));1111if (active_config.done_mask == (1U<<active_config.count)-1 ||1112active_config.fetch_index >= active_config.count) {1113// all done!1114_unconfigured_messages &= ~active_config.unconfig_bit;1115}1116active_config.fetch_index++;1117}1118if (active_config.fetch_index < active_config.count) {1119_configure_valget(active_config.list[active_config.fetch_index].key);1120}1121}1122break;1123case MSG_CFG_VALSET:1124if (active_config.list != nullptr) {1125CFG_Debug("NACK VALSET 0x%x 0x%x", (unsigned)_buffer.nack.msgID,1126unsigned(active_config.list[active_config.set_index].key));1127if (is_gnss_key(active_config.list[active_config.set_index].key)) {1128GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "GPS %u: unable to configure band 0x%02x",1129unsigned(state.instance + 1), unsigned(active_config.list[active_config.set_index].key));11301131}1132}1133break;1134}1135}1136}1137return false;1138}11391140if (_class == CLASS_CFG) {1141switch(_msg_id) {1142case MSG_CFG_NAV_SETTINGS:1143Debug("Got settings %u min_elev %d drLimit %u\n",1144(unsigned)_buffer.nav_settings.dynModel,1145(int)_buffer.nav_settings.minElev,1146(unsigned)_buffer.nav_settings.drLimit);1147_buffer.nav_settings.mask = 0;1148if (gps._navfilter != AP_GPS::GPS_ENGINE_NONE &&1149_buffer.nav_settings.dynModel != gps._navfilter) {1150// we've received the current nav settings, change the engine1151// settings and send them back1152Debug("Changing engine setting from %u to %u\n",1153(unsigned)_buffer.nav_settings.dynModel, (unsigned)gps._navfilter);1154_buffer.nav_settings.dynModel = gps._navfilter;1155_buffer.nav_settings.mask |= 1;1156}1157if (gps._min_elevation != -100 &&1158_buffer.nav_settings.minElev != gps._min_elevation) {1159Debug("Changing min elevation to %d\n", (int)gps._min_elevation);1160_buffer.nav_settings.minElev = gps._min_elevation;1161_buffer.nav_settings.mask |= 2;1162}1163if (_buffer.nav_settings.mask != 0) {1164_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS,1165&_buffer.nav_settings,1166sizeof(_buffer.nav_settings));1167_unconfigured_messages |= CONFIG_NAV_SETTINGS;1168_cfg_needs_save = true;1169} else {1170_unconfigured_messages &= ~CONFIG_NAV_SETTINGS;1171}1172return false;11731174#if UBLOX_GNSS_SETTINGS1175case MSG_CFG_GNSS:1176if (params.gnss_mode != 0 && !supports_F9_config()) {1177struct ubx_cfg_gnss start_gnss = _buffer.gnss;1178uint8_t gnssCount = 0;1179Debug("Got GNSS Settings %u %u %u %u:\n",1180(unsigned)_buffer.gnss.msgVer,1181(unsigned)_buffer.gnss.numTrkChHw,1182(unsigned)_buffer.gnss.numTrkChUse,1183(unsigned)_buffer.gnss.numConfigBlocks);1184#if UBLOX_DEBUGGING1185for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {1186Debug(" %u %u %u 0x%08x\n",1187(unsigned)_buffer.gnss.configBlock[i].gnssId,1188(unsigned)_buffer.gnss.configBlock[i].resTrkCh,1189(unsigned)_buffer.gnss.configBlock[i].maxTrkCh,1190(unsigned)_buffer.gnss.configBlock[i].flags);1191}1192#endif11931194for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {1195if((params.gnss_mode & (1 << i)) && i != GNSS_SBAS) {1196gnssCount++;1197}1198}1199for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {1200// Reserve an equal portion of channels for all enabled systems that supports it1201if(params.gnss_mode & (1 << _buffer.gnss.configBlock[i].gnssId)) {1202if(GNSS_SBAS !=_buffer.gnss.configBlock[i].gnssId && (_hardware_generation > UBLOX_M8 || GNSS_GALILEO !=_buffer.gnss.configBlock[i].gnssId)) {1203_buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);1204_buffer.gnss.configBlock[i].maxTrkCh = _buffer.gnss.numTrkChHw;1205} else {1206if(GNSS_SBAS ==_buffer.gnss.configBlock[i].gnssId) {1207_buffer.gnss.configBlock[i].resTrkCh = 1;1208_buffer.gnss.configBlock[i].maxTrkCh = 3;1209}1210if(GNSS_GALILEO ==_buffer.gnss.configBlock[i].gnssId) {1211_buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);1212_buffer.gnss.configBlock[i].maxTrkCh = 8; //Per the M8 receiver description UBX-13003221 - R16, 4.1.1.3 it is not recommended to set the number of galileo channels higher then eight1213}1214}1215_buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags | 0x00000001;1216} else {1217_buffer.gnss.configBlock[i].resTrkCh = 0;1218_buffer.gnss.configBlock[i].maxTrkCh = 0;1219_buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags & 0xFFFFFFFE;1220}1221}1222if (memcmp(&start_gnss, &_buffer.gnss, sizeof(start_gnss))) {1223_send_message(CLASS_CFG, MSG_CFG_GNSS, &_buffer.gnss, 4 + (8 * _buffer.gnss.numConfigBlocks));1224_unconfigured_messages |= CONFIG_GNSS;1225_cfg_needs_save = true;1226} else {1227_unconfigured_messages &= ~CONFIG_GNSS;1228}1229} else {1230_unconfigured_messages &= ~CONFIG_GNSS;1231}1232return false;1233#endif12341235case MSG_CFG_SBAS:1236if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {1237Debug("Got SBAS settings %u %u %u 0x%x 0x%x\n",1238(unsigned)_buffer.sbas.mode,1239(unsigned)_buffer.sbas.usage,1240(unsigned)_buffer.sbas.maxSBAS,1241(unsigned)_buffer.sbas.scanmode2,1242(unsigned)_buffer.sbas.scanmode1);1243if (_buffer.sbas.mode != gps._sbas_mode) {1244_buffer.sbas.mode = gps._sbas_mode;1245_send_message(CLASS_CFG, MSG_CFG_SBAS,1246&_buffer.sbas,1247sizeof(_buffer.sbas));1248_unconfigured_messages |= CONFIG_SBAS;1249_cfg_needs_save = true;1250} else {1251_unconfigured_messages &= ~CONFIG_SBAS;1252}1253} else {1254_unconfigured_messages &= ~CONFIG_SBAS;1255}1256return false;1257case MSG_CFG_MSG:1258if(_payload_length == sizeof(ubx_cfg_msg_rate_6)) {1259// can't verify the setting without knowing the port1260// request the port again1261if(_ublox_port >= UBLOX_MAX_PORTS) {1262_request_port();1263return false;1264}1265_verify_rate(_buffer.msg_rate_6.msg_class, _buffer.msg_rate_6.msg_id,1266_buffer.msg_rate_6.rates[_ublox_port]);1267} else {1268_verify_rate(_buffer.msg_rate.msg_class, _buffer.msg_rate.msg_id,1269_buffer.msg_rate.rate);1270}1271return false;1272case MSG_CFG_PRT:1273_ublox_port = _buffer.prt.portID;1274return false;1275case MSG_CFG_RATE:1276if(_buffer.nav_rate.measure_rate_ms != params.rate_ms ||1277_buffer.nav_rate.nav_rate != 1 ||1278_buffer.nav_rate.timeref != 0) {1279_configure_rate();1280_unconfigured_messages |= CONFIG_RATE_NAV;1281_cfg_needs_save = true;1282} else {1283_unconfigured_messages &= ~CONFIG_RATE_NAV;1284}1285return false;12861287#if CONFIGURE_PPS_PIN1288case MSG_CFG_TP5: {1289// configure the PPS pin for 1Hz, zero delay1290Debug("Got TP5 ver=%u 0x%04x %u\n",1291(unsigned)_buffer.nav_tp5.version,1292(unsigned)_buffer.nav_tp5.flags,1293(unsigned)_buffer.nav_tp5.freqPeriod);1294#ifdef HAL_GPIO_PPS1295hal.gpio->attach_interrupt(HAL_GPIO_PPS, FUNCTOR_BIND_MEMBER(&AP_GPS_UBLOX::pps_interrupt, void, uint8_t, bool, uint32_t), AP_HAL::GPIO::INTERRUPT_FALLING);1296#endif1297const uint16_t desired_flags = 0x003f;1298const uint16_t desired_period_hz = _pps_freq;12991300if (_buffer.nav_tp5.flags != desired_flags ||1301_buffer.nav_tp5.freqPeriod != desired_period_hz) {1302_buffer.nav_tp5.tpIdx = 0;1303_buffer.nav_tp5.reserved1[0] = 0;1304_buffer.nav_tp5.reserved1[1] = 0;1305_buffer.nav_tp5.antCableDelay = 0;1306_buffer.nav_tp5.rfGroupDelay = 0;1307_buffer.nav_tp5.freqPeriod = desired_period_hz;1308_buffer.nav_tp5.freqPeriodLock = desired_period_hz;1309_buffer.nav_tp5.pulseLenRatio = 1;1310_buffer.nav_tp5.pulseLenRatioLock = 2;1311_buffer.nav_tp5.userConfigDelay = 0;1312_buffer.nav_tp5.flags = desired_flags;1313_send_message(CLASS_CFG, MSG_CFG_TP5,1314&_buffer.nav_tp5,1315sizeof(_buffer.nav_tp5));1316_unconfigured_messages |= CONFIG_TP5;1317_cfg_needs_save = true;1318} else {1319_unconfigured_messages &= ~CONFIG_TP5;1320}1321return false;1322}1323#endif // CONFIGURE_PPS_PIN1324case MSG_CFG_VALGET: {1325uint8_t cfg_len = _payload_length - sizeof(ubx_cfg_valget);1326const uint8_t *cfg_data = (const uint8_t *)(&_buffer) + sizeof(ubx_cfg_valget);1327while (cfg_len >= 5) {1328ConfigKey id;1329memcpy(&id, cfg_data, sizeof(uint32_t));1330cfg_len -= 4;1331cfg_data += 4;1332switch (id) {1333case ConfigKey::TMODE_MODE: {1334uint8_t mode = cfg_data[0];1335if (mode != 0) {1336// ask for mode 0, to disable TIME mode1337mode = 0;1338_configure_valset(ConfigKey::TMODE_MODE, &mode);1339_cfg_needs_save = true;1340_unconfigured_messages |= CONFIG_TMODE_MODE;1341} else {1342_unconfigured_messages &= ~CONFIG_TMODE_MODE;1343}1344break;1345}1346default:1347break;1348}1349// see if it is in active config list1350int8_t cfg_idx = find_active_config_index(id);1351if (cfg_idx >= 0) {1352CFG_Debug("valset(0x%lx): %u", (long unsigned)id, unsigned((*cfg_data) & 0x1));1353const uint8_t key_size = config_key_size(id);1354if (cfg_len < key_size1355// for keys of length 1 only the LSB is significant1356|| (key_size == 1 && (active_config.list[cfg_idx].value & 0x1) != (*cfg_data & 0x1))1357|| memcmp(&active_config.list[cfg_idx].value, cfg_data, key_size) != 0) {1358_configure_valset(id, &active_config.list[cfg_idx].value, active_config.layers);1359_unconfigured_messages |= active_config.unconfig_bit;1360active_config.done_mask &= ~(1U << cfg_idx);1361active_config.set_index = cfg_idx;1362_cfg_needs_save = true;1363} else {1364active_config.done_mask |= (1U << cfg_idx);1365CFG_Debug("done %u mask=0x%x all_mask=0x%x",1366unsigned(cfg_idx),1367unsigned(active_config.done_mask),1368(1U<<active_config.count)-1);1369if (active_config.done_mask == (1U<<active_config.count)-1) {1370// all done!1371_unconfigured_messages &= ~active_config.unconfig_bit;1372}1373}1374if (active_config.fetch_index >= 0 &&1375active_config.fetch_index < active_config.count &&1376id == active_config.list[active_config.fetch_index].key) {1377active_config.fetch_index++;1378if (active_config.fetch_index < active_config.count) {1379_configure_valget(active_config.list[active_config.fetch_index].key);1380CFG_Debug("valget %d 0x%x", int(active_config.fetch_index),1381unsigned(active_config.list[active_config.fetch_index].key));1382}1383}1384} else {1385CFG_Debug("valget no active config for 0x%lx", (long unsigned)id);1386}13871388// step over the value1389uint8_t step_size = config_key_size(id);1390if (step_size == 0) {1391return false;1392}1393cfg_len -= step_size;1394cfg_data += step_size;1395}1396}1397}1398}13991400if (_class == CLASS_MON) {1401switch(_msg_id) {1402case MSG_MON_HW:1403if (_payload_length == 60 || _payload_length == 68) {1404log_mon_hw();1405}1406break;1407case MSG_MON_HW2:1408if (_payload_length == 28) {1409log_mon_hw2();1410}1411break;1412case MSG_MON_VER: {1413bool check_L1L5 = false;1414_have_version = true;1415strncpy(_version.hwVersion, _buffer.mon_ver.hwVersion, sizeof(_version.hwVersion));1416strncpy(_version.swVersion, _buffer.mon_ver.swVersion, sizeof(_version.swVersion));1417void* mod = memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "MOD=", 4);1418if (mod != nullptr) {1419strncpy(_module, (char*)mod+4, UBLOX_MODULE_LEN-1);1420}14211422GCS_SEND_TEXT(MAV_SEVERITY_INFO,1423"u-blox %s%s%d HW: %s SW: %s",1424_module, mod != nullptr ? " " : "",1425state.instance + 1,1426_version.hwVersion,1427_version.swVersion);1428// check for F9 and M9. The F9 does not respond to SVINFO,1429// so we need to use MON_VER for hardware generation1430if (strncmp(_version.hwVersion, "00190000", 8) == 0) {1431if (strncmp(_version.swVersion, "EXT CORE 1", 10) == 0) {1432// a F91433if (_hardware_generation != UBLOX_F9) {1434// need to ensure time mode is correctly setup on F91435_unconfigured_messages |= CONFIG_TMODE_MODE;1436}1437_hardware_generation = UBLOX_F9;1438_unconfigured_messages |= CONFIG_F9;1439_unconfigured_messages &= ~CONFIG_GNSS;1440if (strncmp(_module, "ZED-F9P", UBLOX_MODULE_LEN) == 0) {1441_hardware_variant = UBLOX_F9_ZED;1442} else if (strncmp(_module, "NEO-F9P", UBLOX_MODULE_LEN) == 0) {1443_hardware_variant = UBLOX_F9_NEO;1444}1445}1446if (strncmp(_version.swVersion, "EXT CORE 4", 10) == 0) {1447// a M91448_hardware_generation = UBLOX_M9;1449}1450check_L1L5 = true;1451}1452// check for M101453if (strncmp(_version.hwVersion, "000A0000", 8) == 0) {1454_hardware_generation = UBLOX_M10;1455_unconfigured_messages |= CONFIG_M10;1456// M10 does not support CONFIG_GNSS1457_unconfigured_messages &= ~CONFIG_GNSS;1458check_L1L5 = true;14591460// M10 does not support multi-valued VALGET1461use_single_valget = true;1462}1463if (check_L1L5) {1464// check if L1L5 in extension1465if (memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "L1L5", 4) != nullptr) {1466supports_l5 = true;1467GCS_SEND_TEXT(MAV_SEVERITY_INFO, "u-blox supports L5 Band");1468_unconfigured_messages |= CONFIG_L5;1469}1470}1471break;1472}1473default:1474unexpected_message();1475}1476return false;1477}14781479#if UBLOX_RXM_RAW_LOGGING1480if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAW && gps._raw_data != 0) {1481log_rxm_raw(_buffer.rxm_raw);1482return false;1483} else if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAWX && gps._raw_data != 0) {1484log_rxm_rawx(_buffer.rxm_rawx);1485return false;1486}1487#endif // UBLOX_RXM_RAW_LOGGING14881489#if UBLOX_TIM_TM2_LOGGING1490if ((_class == CLASS_TIM) && (_msg_id == MSG_TIM_TM2) && (_payload_length == 28)) {1491log_tim_tm2();1492return false;1493}1494#endif // UBLOX_TIM_TM2_LOGGING14951496if (_class != CLASS_NAV) {1497unexpected_message();1498return false;1499}15001501switch (_msg_id) {1502case MSG_POSLLH:1503Debug("MSG_POSLLH next_fix=%u", next_fix);1504if (havePvtMsg) {1505_unconfigured_messages |= CONFIG_RATE_POSLLH;1506break;1507}1508_check_new_itow(_buffer.posllh.itow);1509_last_pos_time = _buffer.posllh.itow;1510state.location.lng = _buffer.posllh.longitude;1511state.location.lat = _buffer.posllh.latitude;1512state.have_undulation = true;1513state.undulation = (_buffer.posllh.altitude_msl - _buffer.posllh.altitude_ellipsoid) * 0.001;1514set_alt_amsl_cm(state, _buffer.posllh.altitude_msl / 10);15151516state.status = next_fix;1517_new_position = true;1518state.horizontal_accuracy = _buffer.posllh.horizontal_accuracy*1.0e-3f;1519state.vertical_accuracy = _buffer.posllh.vertical_accuracy*1.0e-3f;1520state.have_horizontal_accuracy = true;1521state.have_vertical_accuracy = true;1522#if UBLOX_FAKE_3DLOCK1523state.location.lng = 1491652300L;1524state.location.lat = -353632610L;1525state.location.alt = 58400;1526state.vertical_accuracy = 0;1527state.horizontal_accuracy = 0;1528#endif1529break;1530case MSG_STATUS:1531Debug("MSG_STATUS fix_status=%u fix_type=%u",1532_buffer.status.fix_status,1533_buffer.status.fix_type);1534_check_new_itow(_buffer.status.itow);1535if (havePvtMsg) {1536// when we have PVT we don't need status, just change the rate for STATUS to zero1537_unconfigured_messages &= ~CONFIG_RATE_STATUS;1538_configure_message_rate(CLASS_NAV, _msg_id, 0);1539break;1540}1541if (_buffer.status.fix_status & NAV_STATUS_FIX_VALID) {1542if( (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) &&1543(_buffer.status.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {1544next_fix = AP_GPS::GPS_OK_FIX_3D_DGPS;1545}else if( _buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) {1546next_fix = AP_GPS::GPS_OK_FIX_3D;1547}else if (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_2D) {1548next_fix = AP_GPS::GPS_OK_FIX_2D;1549}else{1550next_fix = AP_GPS::NO_FIX;1551state.status = AP_GPS::NO_FIX;1552}1553}else{1554next_fix = AP_GPS::NO_FIX;1555state.status = AP_GPS::NO_FIX;1556}1557#if UBLOX_FAKE_3DLOCK1558state.status = AP_GPS::GPS_OK_FIX_3D;1559next_fix = state.status;1560#endif1561break;1562case MSG_DOP:1563Debug("MSG_DOP");1564noReceivedHdop = false;1565_check_new_itow(_buffer.dop.itow);1566state.hdop = _buffer.dop.hDOP;1567state.vdop = _buffer.dop.vDOP;1568#if UBLOX_FAKE_3DLOCK1569state.hdop = 130;1570state.hdop = 170;1571#endif1572break;1573case MSG_SOL:1574Debug("MSG_SOL fix_status=%u fix_type=%u",1575_buffer.solution.fix_status,1576_buffer.solution.fix_type);1577_check_new_itow(_buffer.solution.itow);1578if (havePvtMsg) {1579state.time_week = _buffer.solution.week;1580break;1581}1582if (_buffer.solution.fix_status & NAV_STATUS_FIX_VALID) {1583if( (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) &&1584(_buffer.solution.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {1585next_fix = AP_GPS::GPS_OK_FIX_3D_DGPS;1586}else if( _buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) {1587next_fix = AP_GPS::GPS_OK_FIX_3D;1588}else if (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_2D) {1589next_fix = AP_GPS::GPS_OK_FIX_2D;1590}else{1591next_fix = AP_GPS::NO_FIX;1592state.status = AP_GPS::NO_FIX;1593}1594}else{1595next_fix = AP_GPS::NO_FIX;1596state.status = AP_GPS::NO_FIX;1597}1598if(noReceivedHdop) {1599state.hdop = _buffer.solution.position_DOP;1600}1601state.num_sats = _buffer.solution.satellites;1602if (next_fix >= AP_GPS::GPS_OK_FIX_2D) {1603state.last_gps_time_ms = AP_HAL::millis();1604state.time_week_ms = _buffer.solution.itow;1605state.time_week = _buffer.solution.week;1606}1607#if UBLOX_FAKE_3DLOCK1608next_fix = state.status;1609state.num_sats = 10;1610state.time_week = 1721;1611state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;1612state.last_gps_time_ms = AP_HAL::millis();1613state.hdop = 130;1614#endif1615break;16161617#if GPS_MOVING_BASELINE1618case MSG_RELPOSNED:1619{1620if (role != AP_GPS::GPS_ROLE_MB_ROVER) {1621// ignore RELPOSNED if not configured as a rover1622break;1623}1624// note that we require the yaw to come from a fixed solution, not a float solution1625// yaw from a float solution would only be acceptable with a very large separation between1626// GPS modules1627const uint32_t valid_mask = static_cast<uint32_t>(RELPOSNED::relPosHeadingValid) |1628static_cast<uint32_t>(RELPOSNED::relPosValid) |1629static_cast<uint32_t>(RELPOSNED::gnssFixOK) |1630static_cast<uint32_t>(RELPOSNED::isMoving) |1631static_cast<uint32_t>(RELPOSNED::carrSolnFixed);1632const uint32_t invalid_mask = static_cast<uint32_t>(RELPOSNED::refPosMiss) |1633static_cast<uint32_t>(RELPOSNED::refObsMiss) |1634static_cast<uint32_t>(RELPOSNED::carrSolnFloat);16351636_check_new_itow(_buffer.relposned.iTOW);1637if (_buffer.relposned.iTOW != _last_relposned_itow+200) {1638// useful for looking at packet loss on links1639MB_Debug("RELPOSNED ITOW %u %u\n", unsigned(_buffer.relposned.iTOW), unsigned(_last_relposned_itow));1640}1641_last_relposned_itow = _buffer.relposned.iTOW;1642MB_Debug("RELPOSNED flags: %lx valid: %lx invalid: %lx\n", _buffer.relposned.flags, valid_mask, invalid_mask);1643if (((_buffer.relposned.flags & valid_mask) == valid_mask) &&1644((_buffer.relposned.flags & invalid_mask) == 0)) {1645if (calculate_moving_base_yaw(_buffer.relposned.relPosHeading * 1e-5,1646_buffer.relposned.relPosLength * 0.01,1647_buffer.relposned.relPosD*0.01)) {1648state.have_gps_yaw_accuracy = true;1649state.gps_yaw_accuracy = _buffer.relposned.accHeading * 1e-5;1650_last_relposned_ms = AP_HAL::millis();1651}1652state.relPosHeading = _buffer.relposned.relPosHeading * 1e-5;1653state.relPosLength = _buffer.relposned.relPosLength * 0.01;1654state.relPosD = _buffer.relposned.relPosD * 0.01;1655state.accHeading = _buffer.relposned.accHeading * 1e-5;1656state.relposheading_ts = AP_HAL::millis();1657} else {1658state.have_gps_yaw_accuracy = false;1659}1660}1661break;1662#endif // GPS_MOVING_BASELINE16631664case MSG_PVT:1665Debug("MSG_PVT");16661667havePvtMsg = true;16681669// if we have PVT we don't want MSG_STATUS1670_unconfigured_messages &= ~CONFIG_RATE_STATUS;16711672// position1673_check_new_itow(_buffer.pvt.itow);1674// Only adjust if:1675// we already have a valid week,1676// PVT iTOW wrapped,1677// and our time_week_ms still looks like end-of-week1678// (meaning we haven't already accepted the rollover via TIMEGPS/SOL)1679if (state.time_week != 0 &&1680_last_pvt_itow != 0 &&1681_buffer.pvt.itow < _last_pvt_itow &&1682state.time_week_ms > END_OF_WEEK_THRESHOLD_MS &&1683_buffer.pvt.itow < START_OF_WEEK_THRESHOLD_MS) {1684state.time_week++;1685}16861687_last_pvt_itow = _buffer.pvt.itow;1688_last_pos_time = _buffer.pvt.itow;1689state.location.lng = _buffer.pvt.lon;1690state.location.lat = _buffer.pvt.lat;1691state.have_undulation = true;1692state.undulation = (_buffer.pvt.h_msl - _buffer.pvt.h_ellipsoid) * 0.001;1693set_alt_amsl_cm(state, _buffer.pvt.h_msl / 10);1694switch (_buffer.pvt.fix_type)1695{1696case 0:1697state.status = AP_GPS::NO_FIX;1698break;1699case 1:1700state.status = AP_GPS::NO_FIX;1701break;1702case 2:1703state.status = AP_GPS::GPS_OK_FIX_2D;1704break;1705case 3:1706state.status = AP_GPS::GPS_OK_FIX_3D;1707if (_buffer.pvt.flags & 0b00000010) // diffsoln1708state.status = AP_GPS::GPS_OK_FIX_3D_DGPS;1709if (_buffer.pvt.flags & 0b01000000) // carrsoln - float1710state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT;1711if (_buffer.pvt.flags & 0b10000000) // carrsoln - fixed1712state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FIXED;1713break;1714case 4:1715GCS_SEND_TEXT(MAV_SEVERITY_INFO,1716"Unexpected state %d", _buffer.pvt.flags);1717state.status = AP_GPS::GPS_OK_FIX_3D;1718break;1719case 5:1720state.status = AP_GPS::NO_FIX;1721break;1722default:1723state.status = AP_GPS::NO_FIX;1724break;1725}1726next_fix = state.status;1727_new_position = true;1728state.horizontal_accuracy = _buffer.pvt.h_acc*1.0e-3f;1729state.vertical_accuracy = _buffer.pvt.v_acc*1.0e-3f;1730state.have_horizontal_accuracy = true;1731state.have_vertical_accuracy = true;1732// SVs1733state.num_sats = _buffer.pvt.num_sv;1734// velocity1735_last_vel_time = _buffer.pvt.itow;1736state.ground_speed = _buffer.pvt.gspeed*0.001f; // m/s1737state.ground_course = wrap_360(_buffer.pvt.head_mot * 1.0e-5f); // Heading 2D deg * 1000001738state.have_vertical_velocity = true;1739state.velocity.x = _buffer.pvt.velN * 0.001f;1740state.velocity.y = _buffer.pvt.velE * 0.001f;1741state.velocity.z = _buffer.pvt.velD * 0.001f;1742state.have_speed_accuracy = true;1743state.speed_accuracy = _buffer.pvt.s_acc*0.001f;1744_new_speed = true;1745// dop1746if(noReceivedHdop) {1747state.hdop = _buffer.pvt.p_dop;1748state.vdop = _buffer.pvt.p_dop;1749}17501751if (_buffer.pvt.fix_type >= 2) {1752state.last_gps_time_ms = AP_HAL::millis();1753}17541755// time1756state.time_week_ms = _buffer.pvt.itow;1757#if UBLOX_FAKE_3DLOCK1758state.location.lng = 1491652300L;1759state.location.lat = -353632610L;1760state.location.alt = 58400;1761state.vertical_accuracy = 0;1762state.horizontal_accuracy = 0;1763state.status = AP_GPS::GPS_OK_FIX_3D;1764state.num_sats = 10;1765state.time_week = 1721;1766state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;1767state.last_gps_time_ms = AP_HAL::millis();1768state.hdop = 130;1769state.speed_accuracy = 0;1770next_fix = state.status;1771#endif1772break;1773case MSG_TIMEGPS:1774Debug("MSG_TIMEGPS");1775_check_new_itow(_buffer.timegps.itow);1776if (_buffer.timegps.valid & UBX_TIMEGPS_VALID_WEEK_MASK) {1777state.time_week_ms = _buffer.timegps.itow;1778state.time_week = _buffer.timegps.week;1779state.last_gps_time_ms = AP_HAL::millis();1780}1781break;1782case MSG_VELNED:1783Debug("MSG_VELNED");1784if (havePvtMsg) {1785_unconfigured_messages |= CONFIG_RATE_VELNED;1786break;1787}1788_check_new_itow(_buffer.velned.itow);1789_last_vel_time = _buffer.velned.itow;1790state.ground_speed = _buffer.velned.speed_2d*0.01f; // m/s1791state.ground_course = wrap_360(_buffer.velned.heading_2d * 1.0e-5f); // Heading 2D deg * 1000001792state.have_vertical_velocity = true;1793state.velocity.x = _buffer.velned.ned_north * 0.01f;1794state.velocity.y = _buffer.velned.ned_east * 0.01f;1795state.velocity.z = _buffer.velned.ned_down * 0.01f;1796velocity_to_speed_course(state);1797state.have_speed_accuracy = true;1798state.speed_accuracy = _buffer.velned.speed_accuracy*0.01f;1799#if UBLOX_FAKE_3DLOCK1800state.speed_accuracy = 0;1801#endif1802_new_speed = true;1803break;1804case MSG_NAV_SVINFO:1805{1806Debug("MSG_NAV_SVINFO\n");1807static const uint8_t HardwareGenerationMask = 0x07;1808_check_new_itow(_buffer.svinfo_header.itow);1809_hardware_generation = _buffer.svinfo_header.globalFlags & HardwareGenerationMask;1810switch (_hardware_generation) {1811case UBLOX_5:1812case UBLOX_6:1813// only 7 and newer support CONFIG_GNSS1814_unconfigured_messages &= ~CONFIG_GNSS;1815break;1816case UBLOX_7:1817case UBLOX_M8:1818#if UBLOX_SPEED_CHANGE1819port->begin(4000000U);1820Debug("Changed speed to 4Mhz for SPI-driven UBlox\n");1821#endif1822break;1823default:1824hal.console->printf("Wrong Ublox Hardware Version%u\n", _hardware_generation);1825break;1826};1827_unconfigured_messages &= ~CONFIG_VERSION;1828/* We don't need that anymore */1829_configure_message_rate(CLASS_NAV, MSG_NAV_SVINFO, 0);1830break;1831}1832default:1833Debug("Unexpected NAV message 0x%02x", (unsigned)_msg_id);1834if (++_disable_counter == 0) {1835Debug("Disabling NAV message 0x%02x", (unsigned)_msg_id);1836_configure_message_rate(CLASS_NAV, _msg_id, 0);1837}1838return false;1839}18401841if (state.have_gps_yaw) {1842// when we are a rover we want to ensure we have both the new1843// PVT and the new RELPOSNED message so that we give a1844// consistent view1845if (AP_HAL::millis() - _last_relposned_ms > 400) {1846// we have stopped receiving valid RELPOSNED messages, disable yaw reporting1847state.have_gps_yaw = false;1848} else if (_last_relposned_itow != _last_pvt_itow) {1849// wait until ITOW matches1850return false;1851}1852}18531854// we only return true when we get new position and speed data1855// this ensures we don't use stale data1856if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {1857_new_speed = _new_position = false;1858return true;1859}1860return false;1861}18621863/*1864* handle pps interrupt1865*/1866#ifdef HAL_GPIO_PPS1867void1868AP_GPS_UBLOX::pps_interrupt(uint8_t pin, bool high, uint32_t timestamp_us)1869{1870_last_pps_time_us = AP_HAL::micros64();1871}18721873void1874AP_GPS_UBLOX::set_pps_desired_freq(uint8_t freq)1875{1876_pps_freq = freq;1877_unconfigured_messages |= CONFIG_TP5;1878}1879#endif188018811882// UBlox auto configuration18831884/*1885* update checksum for a set of bytes1886*/1887void1888AP_GPS_UBLOX::_update_checksum(uint8_t *data, uint16_t len, uint8_t &ck_a, uint8_t &ck_b)1889{1890while (len--) {1891ck_a += *data;1892ck_b += ck_a;1893data++;1894}1895}189618971898/*1899* send a ublox message1900*/1901bool1902AP_GPS_UBLOX::_send_message(uint8_t msg_class, uint8_t msg_id, const void *msg, uint16_t size)1903{1904if (port->txspace() < (sizeof(struct ubx_header) + 2 + size)) {1905return false;1906}1907struct ubx_header header;1908uint8_t ck_a=0, ck_b=0;1909header.preamble1 = PREAMBLE1;1910header.preamble2 = PREAMBLE2;1911header.msg_class = msg_class;1912header.msg_id = msg_id;1913header.length = size;19141915_update_checksum((uint8_t *)&header.msg_class, sizeof(header)-2, ck_a, ck_b);1916_update_checksum((uint8_t *)msg, size, ck_a, ck_b);19171918port->write((const uint8_t *)&header, sizeof(header));1919port->write((const uint8_t *)msg, size);1920port->write((const uint8_t *)&ck_a, 1);1921port->write((const uint8_t *)&ck_b, 1);1922return true;1923}19241925/*1926* requests the given message rate for a specific message class1927* and msg_id1928* returns true if it sent the request, false if waiting on knowing the port1929*/1930bool1931AP_GPS_UBLOX::_request_message_rate(uint8_t msg_class, uint8_t msg_id)1932{1933// Without knowing what communication port is being used it isn't possible to verify1934// always ensure we have a port before sending the request1935if(_ublox_port >= UBLOX_MAX_PORTS) {1936_request_port();1937return false;1938} else {1939struct ubx_cfg_msg msg;1940msg.msg_class = msg_class;1941msg.msg_id = msg_id;1942return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));1943}1944}19451946/*1947* configure a UBlox GPS for the given message rate for a specific1948* message class and msg_id1949*/1950bool1951AP_GPS_UBLOX::_configure_message_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate)1952{1953if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_msg_rate)+2)) {1954return false;1955}19561957struct ubx_cfg_msg_rate msg;1958msg.msg_class = msg_class;1959msg.msg_id = msg_id;1960msg.rate = rate;1961return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));1962}19631964/*1965* configure F9/M10 based key/value pair - VALSET1966*/1967bool1968AP_GPS_UBLOX::_configure_valset(ConfigKey key, const void *value, uint8_t layers)1969{1970if (!supports_F9_config()) {1971return false;1972}19731974const uint8_t len = config_key_size(key);1975struct ubx_cfg_valset msg {};1976uint8_t buf[sizeof(msg)+len];1977if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {1978return false;1979}1980msg.version = 1;1981msg.layers = layers;1982msg.transaction = 0;1983msg.key = uint32_t(key);1984memcpy(buf, &msg, sizeof(msg));1985memcpy(&buf[sizeof(msg)], value, len);1986auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, sizeof(buf));1987return ret;1988}19891990/*1991* configure F9 based key/value pair - VALGET1992*/1993bool1994AP_GPS_UBLOX::_configure_valget(ConfigKey key)1995{1996if (!supports_F9_config()) {1997return false;1998}1999struct {2000struct ubx_cfg_valget msg;2001ConfigKey key;2002} msg {};2003if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(msg)+2)) {2004return false;2005}2006msg.msg.version = 0;2007msg.msg.layers = 0; // ram2008msg.key = key;2009return _send_message(CLASS_CFG, MSG_CFG_VALGET, &msg, sizeof(msg));2010}20112012/*2013* configure F9 based key/value pair for a complete configuration set2014*2015* this method requests each configuration variable from the GPS.2016* When we handle the reply in _parse_gps we may then choose to set a2017* MSG_CFG_VALSET back to the GPS if we don't like its response.2018*/2019bool2020AP_GPS_UBLOX::_configure_config_set(const config_list *list, uint8_t count, uint32_t unconfig_bit, uint8_t layers)2021{2022active_config.list = list;2023active_config.count = count;2024active_config.done_mask = 0;2025active_config.unconfig_bit = unconfig_bit;2026active_config.layers = layers;2027// we start by fetching multiple values at once (with fetch_index2028// -1) then if we get a NACK for VALGET we switch to fetching one2029// value at a time. This copes with the M10S which can only fetch2030// one value at a time2031active_config.fetch_index = use_single_valget? 0 :-1;20322033uint8_t buf[sizeof(ubx_cfg_valget)+count*sizeof(ConfigKey)];2034struct ubx_cfg_valget msg {};2035if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {2036return false;2037}2038msg.version = 0;2039msg.layers = 0; // ram2040memcpy(buf, &msg, sizeof(msg));2041for (uint8_t i=0; i<count; i++) {2042memcpy(&buf[sizeof(msg)+i*sizeof(ConfigKey)], &list[i].key, sizeof(ConfigKey));2043}2044return _send_message(CLASS_CFG, MSG_CFG_VALGET, buf, sizeof(buf));2045}20462047/*2048* configure F9 based key/value pair for a complete configuration set2049*2050* this method sends all the key/value pairs in a block, but makes no attempt to check2051* the results. Sending in a block is necessary for updates such as GNSS where certain2052* combinations are invalid and setting one at a time will not produce the correct result2053* if the result needs to be validated then a subsequent _configure_config_set() can be2054* issued which will get all the values and reset those that are not properly updated.2055*/2056bool2057AP_GPS_UBLOX::_configure_list_valset(const config_list *list, uint8_t count, uint8_t layers)2058{2059if (!supports_F9_config()) {2060return false;2061}20622063struct ubx_cfg_valset msg {};2064uint8_t buf[sizeof(msg)+sizeof(config_list)*count];2065if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {2066return false;2067}2068msg.version = 1;2069msg.layers = layers;2070msg.transaction = 0;2071uint32_t msg_len = sizeof(msg) - sizeof(msg.key);2072memcpy(buf, &msg, msg_len);20732074uint8_t* payload = &buf[msg_len];2075for (uint8_t i=0; i<count; i++) {2076const uint8_t len = config_key_size(list[i].key);2077memcpy(payload, &list[i].key, sizeof(ConfigKey));2078payload += sizeof(ConfigKey);2079msg_len += sizeof(ConfigKey);2080memcpy(payload, &list[i].value, len);2081payload += len;2082msg_len += len;2083}20842085auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, msg_len);2086return ret;2087}20882089/*2090* save gps configurations to non-volatile memory sent until the call of2091* this message2092*/2093void2094AP_GPS_UBLOX::_save_cfg()2095{2096static const ubx_cfg_cfg save_cfg {2097clearMask: 0,2098saveMask: SAVE_CFG_ALL,2099loadMask: 02100};2101_send_message(CLASS_CFG, MSG_CFG_CFG, &save_cfg, sizeof(save_cfg));2102_last_cfg_sent_time = AP_HAL::millis();2103_num_cfg_save_tries++;2104GCS_SEND_TEXT(MAV_SEVERITY_INFO,2105"GPS %d: u-blox saving config",2106state.instance + 1);2107}21082109/*2110detect a Ublox GPS. Adds one byte, and returns true if the stream2111matches a UBlox2112*/2113bool2114AP_GPS_UBLOX::_detect(struct UBLOX_detect_state &state, uint8_t data)2115{2116reset:2117switch (state.step) {2118case 1:2119if (PREAMBLE2 == data) {2120state.step++;2121break;2122}2123state.step = 0;2124FALLTHROUGH;2125case 0:2126if (PREAMBLE1 == data)2127state.step++;2128break;2129case 2:2130state.step++;2131state.ck_b = state.ck_a = data;2132break;2133case 3:2134state.step++;2135state.ck_b += (state.ck_a += data);2136break;2137case 4:2138state.step++;2139state.ck_b += (state.ck_a += data);2140state.payload_length = data;2141break;2142case 5:2143state.step++;2144state.ck_b += (state.ck_a += data);2145state.payload_counter = 0;2146break;2147case 6:2148state.ck_b += (state.ck_a += data);2149if (++state.payload_counter == state.payload_length)2150state.step++;2151break;2152case 7:2153state.step++;2154if (state.ck_a != data) {2155state.step = 0;2156goto reset;2157}2158break;2159case 8:2160state.step = 0;2161if (state.ck_b == data) {2162// a valid UBlox packet2163return true;2164} else {2165goto reset;2166}2167}2168return false;2169}21702171void2172AP_GPS_UBLOX::_request_version(void)2173{2174_send_message(CLASS_MON, MSG_MON_VER, nullptr, 0);2175}21762177void2178AP_GPS_UBLOX::_configure_rate(void)2179{2180struct ubx_cfg_nav_rate msg;2181// require a minimum measurement rate of 5Hz2182msg.measure_rate_ms = gps.get_rate_ms(state.instance);2183msg.nav_rate = 1;2184msg.timeref = 0; // UTC time2185_send_message(CLASS_CFG, MSG_CFG_RATE, &msg, sizeof(msg));2186}21872188static const char *reasons[] = {"navigation rate",2189"posllh rate",2190"status rate",2191"solution rate",2192"velned rate",2193"dop rate",2194"hw monitor rate",2195"hw2 monitor rate",2196"raw rate",2197"version",2198"navigation settings",2199"GNSS settings",2200"SBAS settings",2201"PVT rate",2202"time pulse settings",2203"TIMEGPS rate",2204"Time mode settings",2205"RTK MB",2206"TIM TM2",2207"F9",2208"M10",2209"L5 Enable Disable"};22102211static_assert((1 << ARRAY_SIZE(reasons)) == CONFIG_LAST, "UBLOX: Missing configuration description");22122213void2214AP_GPS_UBLOX::broadcast_configuration_failure_reason(void) const {2215for (uint8_t i = 0; i < ARRAY_SIZE(reasons); i++) {2216if (_unconfigured_messages & (1 << i)) {2217GCS_SEND_TEXT(MAV_SEVERITY_INFO, "GPS %u: u-blox %s configuration 0x%02x",2218(unsigned int)(state.instance + 1), reasons[i], (unsigned int)_unconfigured_messages);2219break;2220}2221}2222}22232224/*2225return velocity lag in seconds2226*/2227bool AP_GPS_UBLOX::get_lag(float &lag_sec) const2228{2229switch (_hardware_generation) {2230case UBLOX_UNKNOWN_HARDWARE_GENERATION:2231lag_sec = 0.22f;2232// always bail out in this case, it's used to indicate we have yet to receive a valid2233// hardware generation, however the user may have inhibited us detecting the generation2234// so if we aren't allowed to do configuration, we will accept this as the default delay2235return gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE;2236case UBLOX_5:2237case UBLOX_6:2238default:2239lag_sec = 0.22f;2240break;2241case UBLOX_7:2242case UBLOX_M8:2243// based on flight logs the 7 and 8 series seem to produce about 120ms lag2244lag_sec = 0.12f;2245break;2246case UBLOX_F9:2247case UBLOX_M9:2248case UBLOX_M10:2249// F9 lag not verified yet from flight log, but likely to be at least2250// as good as M82251lag_sec = 0.12f;2252#if GPS_MOVING_BASELINE2253if (role == AP_GPS::GPS_ROLE_MB_BASE ||2254role == AP_GPS::GPS_ROLE_MB_ROVER) {2255// the moving baseline rover will lag about 40ms from the2256// base. We need to provide the more conservative value to2257// ensure that the EKF allocates a larger enough buffer2258lag_sec += 0.04;2259}2260#endif2261break;2262};2263return true;2264}22652266#if HAL_LOGGING_ENABLED2267void AP_GPS_UBLOX::Write_AP_Logger_Log_Startup_messages() const2268{2269AP_GPS_Backend::Write_AP_Logger_Log_Startup_messages();22702271if (_have_version) {2272AP::logger().Write_MessageF("u-blox %d HW: %s SW: %s",2273state.instance+1,2274_version.hwVersion,2275_version.swVersion);2276}2277}2278#endif22792280// uBlox specific check_new_itow(), handling message length2281void AP_GPS_UBLOX::_check_new_itow(uint32_t itow)2282{2283check_new_itow(itow, _payload_length + sizeof(ubx_header) + 2);2284}22852286// support for retrieving RTCMv3 data from a moving baseline base2287bool AP_GPS_UBLOX::get_RTCMV3(const uint8_t *&bytes, uint16_t &len)2288{2289#if GPS_MOVING_BASELINE2290if (rtcm3_parser) {2291len = rtcm3_parser->get_len(bytes);2292return len > 0;2293}2294#endif2295return false;2296}22972298// clear previous RTCM3 packet2299void AP_GPS_UBLOX::clear_RTCMV3(void)2300{2301#if GPS_MOVING_BASELINE2302if (rtcm3_parser) {2303rtcm3_parser->clear_packet();2304}2305#endif2306}23072308// ublox specific healthy checks2309bool AP_GPS_UBLOX::is_healthy(void) const2310{2311#if CONFIG_HAL_BOARD == HAL_BOARD_SITL2312if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {2313// allow for fake ublox moving baseline2314return true;2315}2316#endif2317#if GPS_MOVING_BASELINE2318if ((role == AP_GPS::GPS_ROLE_MB_BASE ||2319role == AP_GPS::GPS_ROLE_MB_ROVER) &&2320!supports_F9_config()) {2321// need F9 or above for moving baseline2322return false;2323}2324if (role == AP_GPS::GPS_ROLE_MB_BASE && rtcm3_parser == nullptr && !mb_use_uart2()) {2325// we haven't initialised RTCMv3 parser2326return false;2327}2328#endif2329return true;2330}23312332// populate config_GNSS with F9 GNSS configuration2333uint8_t AP_GPS_UBLOX::populate_F9_gnss(void)2334{2335uint8_t cfg_count = 0;23362337if (params.gnss_mode == 0) {2338_unconfigured_messages &= ~CONFIG_F9;2339last_configured_gnss = params.gnss_mode;2340return 0;2341}23422343if ((_unconfigured_messages & CONFIG_F9) != 0) {2344// ZED-F9P defaults are2345// GPS L1C/A+L2C(ZED)2346// SBAS L1C/A2347// GALILEO E1+E5B(ZED)+E5A(NEO)2348// BEIDOU B1+B2(ZED)+B2A(NEO)2349// QZSS L1C/A+L2C(ZED)+L5(NEO)2350// GLONASS L1+L2(ZED)2351// IMES not supported2352// GPS and QZSS should be enabled/disabled together, but we will leave them alone2353// QZSS and SBAS can only be enabled if GPS is enabled23542355if (config_GNSS == nullptr) {2356config_GNSS = (config_list*)calloc(UBLOX_MAX_GNSS_CONFIG_BLOCKS*3, sizeof(config_list));2357}23582359if (config_GNSS == nullptr) {2360return 0;2361}23622363uint8_t gnss_mode = params.gnss_mode;2364gnss_mode |= 1U<<GNSS_GPS;2365gnss_mode |= 1U<<GNSS_QZSS;2366gnss_mode &= ~(1U<<GNSS_IMES);2367params.gnss_mode.set_and_save(gnss_mode);23682369for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {2370bool ena = gnss_mode & (1U<<i);2371switch (i) {2372case GNSS_SBAS:2373config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_ENA, ena };2374config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_L1CA_ENA, ena };2375break;2376case GNSS_GALILEO:2377config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_ENA, ena };2378config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E1_ENA, ena };2379if (_hardware_variant == UBLOX_F9_ZED) {2380config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5B_ENA, ena };2381} else {2382config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5A_ENA, ena };2383}2384break;2385case GNSS_BEIDOU:2386config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_ENA, ena };2387config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B1_ENA, ena };2388if (_hardware_variant == UBLOX_F9_ZED) {2389config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2_ENA, ena };2390} else {2391config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2A_ENA, ena };2392}2393break;2394case GNSS_GLONASS:2395config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_ENA, ena };2396config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L1_ENA, ena };2397if (_hardware_variant == UBLOX_F9_ZED) {2398config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L2_ENA, ena };2399}2400break;2401case GNSS_NAVIC:2402config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_NAVIC_ENA, ena };2403config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_NAVIC_L5_ENA, ena };2404break;2405// not supported or leave alone2406case GNSS_IMES:2407case GNSS_QZSS:2408case GNSS_GPS:2409break;2410}2411}2412}24132414last_configured_gnss = params.gnss_mode;24152416return cfg_count;2417}24182419// return true if GPS is capable of F9 config2420bool AP_GPS_UBLOX::supports_F9_config(void) const2421{2422return _hardware_generation == UBLOX_F9 || _hardware_generation == UBLOX_M10;2423}24242425// return true if GPS is capable of F9 config2426bool AP_GPS_UBLOX::is_gnss_key(ConfigKey key) const2427{2428return (unsigned(key) & 0xFFFF0000) == 0x10310000;2429}24302431#endif243224332434