Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_GPS/AP_GPS_UBLOX.cpp
Views: 1798
/*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#if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_NAVIO || \32CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_BH33#define UBLOX_SPEED_CHANGE 134#else35#define UBLOX_SPEED_CHANGE 036#endif373839#define UBLOX_DEBUGGING 040#define UBLOX_FAKE_3DLOCK 041#ifndef CONFIGURE_PPS_PIN42#define CONFIGURE_PPS_PIN 043#endif4445// this is number of epochs per output. A higher value will reduce46// the uart bandwidth needed and allow for higher latency47#define RTK_MB_RTCM_RATE 14849// use this to enable debugging of moving baseline configs50#define UBLOX_MB_DEBUGGING 05152// debug VALGET/VALSET configuration53#define UBLOX_CFG_DEBUGGING 05455extern const AP_HAL::HAL& hal;5657#if UBLOX_DEBUGGING58#if defined(HAL_BUILD_AP_PERIPH)59extern "C" {60void can_printf(const char *fmt, ...);61}62# define Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)63#else64# define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)65#endif66#else67# define Debug(fmt, args ...)68#endif6970#if UBLOX_MB_DEBUGGING71#if defined(HAL_BUILD_AP_PERIPH)72extern "C" {73void can_printf(const char *fmt, ...);74}75# define MB_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)76#else77# define MB_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)78#endif79#else80# define MB_Debug(fmt, args ...)81#endif8283#if UBLOX_CFG_DEBUGGING84#if defined(HAL_BUILD_AP_PERIPH)85extern "C" {86void can_printf(const char *fmt, ...);87}88# define CFG_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)89#else90# define CFG_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)91#endif92#else93# define CFG_Debug(fmt, args ...)94#endif9596AP_GPS_UBLOX::AP_GPS_UBLOX(AP_GPS &_gps,97AP_GPS::Params &_params,98AP_GPS::GPS_State &_state,99AP_HAL::UARTDriver *_port,100AP_GPS::GPS_Role _role) :101AP_GPS_Backend(_gps, _params, _state, _port),102role(_role)103{104// stop any config strings that are pending105gps.send_blob_start(state.instance, nullptr, 0);106107// start the process of updating the GPS rates108_request_next_config();109110#if CONFIGURE_PPS_PIN111_unconfigured_messages |= CONFIG_TP5;112#endif113114#if GPS_MOVING_BASELINE115if (role == AP_GPS::GPS_ROLE_MB_BASE && !mb_use_uart2()) {116rtcm3_parser = NEW_NOTHROW RTCM3_Parser;117if (rtcm3_parser == nullptr) {118GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "u-blox %d: failed RTCMv3 parser allocation", state.instance + 1);119}120_unconfigured_messages |= CONFIG_RTK_MOVBASE;121}122if (role == AP_GPS::GPS_ROLE_MB_ROVER) {123_unconfigured_messages |= CONFIG_RTK_MOVBASE;124state.gps_yaw_configured = true;125}126#endif127}128129AP_GPS_UBLOX::~AP_GPS_UBLOX()130{131#if GPS_MOVING_BASELINE132delete rtcm3_parser;133#endif134}135136#if GPS_MOVING_BASELINE137/*138config for F9 GPS in moving baseline base role139See ZED-F9P integration manual section 3.1.5.6.1140*/141const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Base_uart1[] {142{ ConfigKey::CFG_UART1OUTPROT_RTCM3X, 1},143{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},144{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 0},145{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},146{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, RTK_MB_RTCM_RATE},147{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, RTK_MB_RTCM_RATE},148{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, RTK_MB_RTCM_RATE},149{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, RTK_MB_RTCM_RATE},150{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, RTK_MB_RTCM_RATE},151{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, RTK_MB_RTCM_RATE},152{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, RTK_MB_RTCM_RATE},153{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},154{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},155{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},156{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},157{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},158{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},159{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},160};161162const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Base_uart2[] {163{ ConfigKey::CFG_UART2_ENABLED, 1},164{ ConfigKey::CFG_UART2_BAUDRATE, 460800},165{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 1},166{ ConfigKey::CFG_UART1OUTPROT_RTCM3X, 0},167{ ConfigKey::CFG_UART1INPROT_RTCM3X, 1},168{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},169{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 0},170{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, RTK_MB_RTCM_RATE},171{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, RTK_MB_RTCM_RATE},172{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, RTK_MB_RTCM_RATE},173{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, RTK_MB_RTCM_RATE},174{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, RTK_MB_RTCM_RATE},175{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, RTK_MB_RTCM_RATE},176{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, RTK_MB_RTCM_RATE},177{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},178{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},179{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},180{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},181{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},182{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},183{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},184};185186187/*188config for F9 GPS in moving baseline rover role189See ZED-F9P integration manual section 3.1.5.6.1.190Note that we list the RTCM msg types as 0 to prevent getting RTCM191data from a GPS previously configured as a base192*/193const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Rover_uart1[] {194{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},195{ ConfigKey::CFG_UART1INPROT_RTCM3X, 1},196{ ConfigKey::CFG_UART2INPROT_RTCM3X, 0},197{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 1},198{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},199{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},200{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},201{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},202{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},203{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},204{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},205{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},206{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},207{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},208{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},209{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},210{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},211{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},212{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},213};214215const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_MB_Rover_uart2[] {216{ ConfigKey::CFG_UART2_ENABLED, 1},217{ ConfigKey::CFG_UART2_BAUDRATE, 460800},218{ ConfigKey::CFG_UART2OUTPROT_RTCM3X, 0},219{ ConfigKey::CFG_UART2INPROT_RTCM3X, 1},220{ ConfigKey::CFG_UART1INPROT_RTCM3X, 0},221{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART1, 1},222{ ConfigKey::MSGOUT_UBX_NAV_RELPOSNED_UART2, 0},223{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART2, 0},224{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART2, 0},225{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART2, 0},226{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART2, 0},227{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART2, 0},228{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART2, 0},229{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART2, 0},230{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_0_UART1, 0},231{ ConfigKey::MSGOUT_RTCM_3X_TYPE4072_1_UART1, 0},232{ ConfigKey::MSGOUT_RTCM_3X_TYPE1077_UART1, 0},233{ ConfigKey::MSGOUT_RTCM_3X_TYPE1087_UART1, 0},234{ ConfigKey::MSGOUT_RTCM_3X_TYPE1097_UART1, 0},235{ ConfigKey::MSGOUT_RTCM_3X_TYPE1127_UART1, 0},236{ ConfigKey::MSGOUT_RTCM_3X_TYPE1230_UART1, 0},237};238#endif // GPS_MOVING_BASELINE239240/*241config changes for M10242we need to use B1C not B1 signal for Beidou on M10 to allow solid 5Hz,243and also disable Glonass and enable QZSS244*/245const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_M10[] {246{ ConfigKey::CFG_SIGNAL_BDS_ENA, 1},247{ ConfigKey::CFG_SIGNAL_BDS_B1_ENA, 0},248{ ConfigKey::CFG_SIGNAL_BDS_B1C_ENA, 1},249{ ConfigKey::CFG_SIGNAL_GLO_ENA, 0},250{ ConfigKey::CFG_SIGNAL_QZSS_ENA, 1},251{ ConfigKey::CFG_SIGNAL_QZSS_L1CA_ENA, 1},252{ ConfigKey::CFG_SIGNAL_QZSS_L1S_ENA, 1},253{ ConfigKey::CFG_NAVSPG_DYNMODEL, 8}, // Air < 4g254};255256257/*258config changes for L5 modules259*/260const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_L5_ovrd_ena[] {261{ConfigKey::CFG_SIGNAL_L5_HEALTH_OVRD, 1},262{ConfigKey::CFG_SIGNAL_GPS_L5_ENA, 1},263};264265const AP_GPS_UBLOX::config_list AP_GPS_UBLOX::config_L5_ovrd_dis[] {266{ConfigKey::CFG_SIGNAL_L5_HEALTH_OVRD, 0},267};268269void270AP_GPS_UBLOX::_request_next_config(void)271{272// don't request config if we shouldn't configure the GPS273if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {274return;275}276277// Ensure there is enough space for the largest possible outgoing message278if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_nav_rate)+2)) {279// not enough space - do it next time280return;281}282283if (_unconfigured_messages == CONFIG_RATE_SOL && havePvtMsg) {284/*285we don't need SOL if we have PVT and TIMEGPS. This is needed286as F9P doesn't support the SOL message287*/288_unconfigured_messages &= ~CONFIG_RATE_SOL;289}290291Debug("Unconfigured messages: 0x%x Current message: %u\n", (unsigned)_unconfigured_messages, (unsigned)_next_message);292293// check AP_GPS_UBLOX.h for the enum that controls the order.294// This switch statement isn't maintained against the enum in order to reduce code churn295switch (_next_message++) {296case STEP_PVT:297if(!_request_message_rate(CLASS_NAV, MSG_PVT)) {298_next_message--;299}300break;301case STEP_TIMEGPS:302if(!_request_message_rate(CLASS_NAV, MSG_TIMEGPS)) {303_next_message--;304}305break;306case STEP_PORT:307_request_port();308break;309case STEP_POLL_SVINFO:310// not required once we know what generation we are on311if(_hardware_generation == UBLOX_UNKNOWN_HARDWARE_GENERATION) {312if (!_send_message(CLASS_NAV, MSG_NAV_SVINFO, 0, 0)) {313_next_message--;314}315}316break;317case STEP_POLL_SBAS:318if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {319_send_message(CLASS_CFG, MSG_CFG_SBAS, nullptr, 0);320} else {321_unconfigured_messages &= ~CONFIG_SBAS;322}323break;324case STEP_POLL_NAV:325if (!_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS, nullptr, 0)) {326_next_message--;327}328break;329case STEP_POLL_GNSS:330if (supports_F9_config()) {331if (last_configured_gnss != params.gnss_mode) {332_unconfigured_messages |= CONFIG_F9;333}334break;335}336if (!_send_message(CLASS_CFG, MSG_CFG_GNSS, nullptr, 0)) {337_next_message--;338}339break;340case STEP_POLL_TP5:341#if CONFIGURE_PPS_PIN342if (!_send_message(CLASS_CFG, MSG_CFG_TP5, nullptr, 0)) {343_next_message--;344}345#endif346break;347case STEP_NAV_RATE:348if (!_send_message(CLASS_CFG, MSG_CFG_RATE, nullptr, 0)) {349_next_message--;350}351break;352case STEP_POSLLH:353if(!_request_message_rate(CLASS_NAV, MSG_POSLLH)) {354_next_message--;355}356break;357case STEP_STATUS:358if(!_request_message_rate(CLASS_NAV, MSG_STATUS)) {359_next_message--;360}361break;362case STEP_SOL:363if(!_request_message_rate(CLASS_NAV, MSG_SOL)) {364_next_message--;365}366break;367case STEP_VELNED:368if(!_request_message_rate(CLASS_NAV, MSG_VELNED)) {369_next_message--;370}371break;372case STEP_DOP:373if(! _request_message_rate(CLASS_NAV, MSG_DOP)) {374_next_message--;375}376break;377case STEP_MON_HW:378if(!_request_message_rate(CLASS_MON, MSG_MON_HW)) {379_next_message--;380}381break;382case STEP_MON_HW2:383if(!_request_message_rate(CLASS_MON, MSG_MON_HW2)) {384_next_message--;385}386break;387case STEP_RAW:388#if UBLOX_RXM_RAW_LOGGING389if(gps._raw_data == 0) {390_unconfigured_messages &= ~CONFIG_RATE_RAW;391} else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAW)) {392_next_message--;393}394#else395_unconfigured_messages & = ~CONFIG_RATE_RAW;396#endif397break;398case STEP_RAWX:399#if UBLOX_RXM_RAW_LOGGING400if(gps._raw_data == 0) {401_unconfigured_messages &= ~CONFIG_RATE_RAW;402} else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAWX)) {403_next_message--;404}405#else406_unconfigured_messages & = ~CONFIG_RATE_RAW;407#endif408break;409case STEP_VERSION:410if(!_have_version && !hal.util->get_soft_armed()) {411_request_version();412} else {413_unconfigured_messages &= ~CONFIG_VERSION;414}415break;416case STEP_TMODE:417if (supports_F9_config()) {418if (!_configure_valget(ConfigKey::TMODE_MODE)) {419_next_message--;420}421}422break;423case STEP_RTK_MOVBASE:424#if GPS_MOVING_BASELINE425if (supports_F9_config()) {426static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart1), "done_mask too small, base1");427static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart2), "done_mask too small, base2");428static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart1), "done_mask too small, rover1");429static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart2), "done_mask too small, rover2");430if (role == AP_GPS::GPS_ROLE_MB_BASE) {431const config_list *list = mb_use_uart2()?config_MB_Base_uart2:config_MB_Base_uart1;432uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Base_uart2):ARRAY_SIZE(config_MB_Base_uart1);433if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {434_next_message--;435}436}437if (role == AP_GPS::GPS_ROLE_MB_ROVER) {438const config_list *list = mb_use_uart2()?config_MB_Rover_uart2:config_MB_Rover_uart1;439uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Rover_uart2):ARRAY_SIZE(config_MB_Rover_uart1);440if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {441_next_message--;442}443}444}445#endif446break;447case STEP_TIM_TM2:448#if UBLOX_TIM_TM2_LOGGING449if(!_request_message_rate(CLASS_TIM, MSG_TIM_TM2)) {450_next_message--;451}452#else453_unconfigured_messages &= ~CONFIG_TIM_TM2;454#endif455break;456457case STEP_F9: {458if (_hardware_generation == UBLOX_F9) {459uint8_t cfg_count = populate_F9_gnss();460// special handling of F9 config461if (cfg_count > 0) {462CFG_Debug("Sending F9 settings, GNSS=%u", 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// GNSS takes 0.5s to reset477if (AP_HAL::millis() - _f9_config_time < 500) {478_next_message--;479break;480}481_f9_config_time = 0;482uint8_t cfg_count = populate_F9_gnss();483// special handling of F9 config484if (cfg_count > 0) {485CFG_Debug("Validating F9 settings, GNSS=%u", params.gnss_mode);486// now validate all of the settings, this is a no-op if the first call succeeded487if (!_configure_config_set(config_GNSS, cfg_count, CONFIG_F9, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {488_next_message--;489}490}491}492break;493}494case STEP_M10: {495if (_hardware_generation == UBLOX_M10) {496// special handling of M10 config497const config_list *list = config_M10;498const uint8_t list_length = ARRAY_SIZE(config_M10);499Debug("Sending M10 settings");500if (!_configure_config_set(list, list_length, CONFIG_M10, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {501_next_message--;502}503}504break;505}506507case STEP_L5: {508if (supports_l5 && option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {509const config_list *list = config_L5_ovrd_ena;510const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_ena);511if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {512_next_message--;513}514} else if (supports_l5 && !option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {515const config_list *list = config_L5_ovrd_dis;516const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_dis);517if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {518_next_message--;519}520}521break;522}523524default:525// this case should never be reached, do a full reset if it is hit526_next_message = STEP_PVT;527break;528}529}530531void532AP_GPS_UBLOX::_verify_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate) {533uint8_t desired_rate;534uint32_t config_msg_id;535switch(msg_class) {536case CLASS_NAV:537switch(msg_id) {538case MSG_POSLLH:539desired_rate = havePvtMsg ? 0 : RATE_POSLLH;540config_msg_id = CONFIG_RATE_POSLLH;541break;542case MSG_STATUS:543desired_rate = havePvtMsg ? 0 : RATE_STATUS;544config_msg_id = CONFIG_RATE_STATUS;545break;546case MSG_SOL:547desired_rate = havePvtMsg ? 0 : RATE_SOL;548config_msg_id = CONFIG_RATE_SOL;549break;550case MSG_PVT:551desired_rate = RATE_PVT;552config_msg_id = CONFIG_RATE_PVT;553break;554case MSG_TIMEGPS:555desired_rate = RATE_TIMEGPS;556config_msg_id = CONFIG_RATE_TIMEGPS;557break;558case MSG_VELNED:559desired_rate = havePvtMsg ? 0 : RATE_VELNED;560config_msg_id = CONFIG_RATE_VELNED;561break;562case MSG_DOP:563desired_rate = RATE_DOP;564config_msg_id = CONFIG_RATE_DOP;565break;566default:567return;568}569break;570case CLASS_MON:571switch(msg_id) {572case MSG_MON_HW:573desired_rate = RATE_HW;574config_msg_id = CONFIG_RATE_MON_HW;575break;576case MSG_MON_HW2:577desired_rate = RATE_HW2;578config_msg_id = CONFIG_RATE_MON_HW2;579break;580default:581return;582}583break;584#if UBLOX_RXM_RAW_LOGGING585case CLASS_RXM:586switch(msg_id) {587case MSG_RXM_RAW:588desired_rate = gps._raw_data;589config_msg_id = CONFIG_RATE_RAW;590break;591case MSG_RXM_RAWX:592desired_rate = gps._raw_data;593config_msg_id = CONFIG_RATE_RAW;594break;595default:596return;597}598break;599#endif // UBLOX_RXM_RAW_LOGGING600#if UBLOX_TIM_TM2_LOGGING601case CLASS_TIM:602if (msg_id == MSG_TIM_TM2) {603desired_rate = RATE_TIM_TM2;604config_msg_id = CONFIG_TIM_TM2;605break;606}607return;608#endif // UBLOX_TIM_TM2_LOGGING609default:610return;611}612613if (rate == desired_rate) {614// coming in at correct rate; mark as configured615_unconfigured_messages &= ~config_msg_id;616return;617}618619// coming in at wrong rate; try to configure it620_configure_message_rate(msg_class, msg_id, desired_rate);621_unconfigured_messages |= config_msg_id;622_cfg_needs_save = true;623}624625// Requests the ublox driver to identify what port we are using to communicate626void627AP_GPS_UBLOX::_request_port(void)628{629if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+2)) {630// not enough space - do it next time631return;632}633_send_message(CLASS_CFG, MSG_CFG_PRT, nullptr, 0);634}635636// Ensure there is enough space for the largest possible outgoing message637// Process bytes available from the stream638//639// The stream is assumed to contain only messages we recognise. If it640// contains other messages, and those messages contain the preamble641// bytes, it is possible for this code to fail to synchronise to the642// stream immediately. Without buffering the entire message and643// re-processing it from the top, this is unavoidable. The parser644// attempts to avoid this when possible.645//646bool647AP_GPS_UBLOX::read(void)648{649bool parsed = false;650uint32_t millis_now = AP_HAL::millis();651652// walk through the gps configuration at 1 message per second653if (millis_now - _last_config_time >= _delay_time) {654_request_next_config();655_last_config_time = millis_now;656if (_unconfigured_messages) { // send the updates faster until fully configured657if (!havePvtMsg && (_unconfigured_messages & CONFIG_REQUIRED_INITIAL)) {658_delay_time = 300;659} else {660_delay_time = 750;661}662} else {663_delay_time = 2000;664}665}666667if(!_unconfigured_messages && gps._save_config && !_cfg_saved &&668_num_cfg_save_tries < 5 && (millis_now - _last_cfg_sent_time) > 5000 &&669!hal.util->get_soft_armed()) {670//save the configuration sent until now671if (gps._save_config == 1 ||672(gps._save_config == 2 && _cfg_needs_save)) {673_save_cfg();674}675}676677const uint16_t numc = MIN(port->available(), 8192U);678for (uint16_t i = 0; i < numc; i++) { // Process bytes received679680// read the next byte681uint8_t data;682if (!port->read(data)) {683break;684}685#if AP_GPS_DEBUG_LOGGING_ENABLED686log_data(&data, 1);687#endif688689#if GPS_MOVING_BASELINE690if (rtcm3_parser) {691if (rtcm3_parser->read(data)) {692// we've found a RTCMv3 packet. We stop parsing at693// this point and reset u-blox parse state. We need to694// stop parsing to give the higher level driver a695// chance to send the RTCMv3 packet to another (rover)696// GPS697_step = 0;698break;699}700}701#endif702703reset:704switch(_step) {705706// Message preamble detection707//708// If we fail to match any of the expected bytes, we reset709// the state machine and re-consider the failed byte as710// the first byte of the preamble. This improves our711// chances of recovering from a mismatch and makes it less712// likely that we will be fooled by the preamble appearing713// as data in some other message.714//715case 1:716if (PREAMBLE2 == data) {717_step++;718break;719}720_step = 0;721Debug("reset %u", __LINE__);722FALLTHROUGH;723case 0:724if(PREAMBLE1 == data)725_step++;726break;727728// Message header processing729//730// We sniff the class and message ID to decide whether we731// are going to gather the message bytes or just discard732// them.733//734// We always collect the length so that we can avoid being735// fooled by preamble bytes in messages.736//737case 2:738_step++;739_class = data;740_ck_b = _ck_a = data; // reset the checksum accumulators741break;742case 3:743_step++;744_ck_b += (_ck_a += data); // checksum byte745_msg_id = data;746break;747case 4:748_step++;749_ck_b += (_ck_a += data); // checksum byte750_payload_length = data; // payload length low byte751break;752case 5:753_step++;754_ck_b += (_ck_a += data); // checksum byte755756_payload_length += (uint16_t)(data<<8);757if (_payload_length > sizeof(_buffer)) {758Debug("large payload %u", (unsigned)_payload_length);759// assume any payload bigger then what we know about is noise760_payload_length = 0;761_step = 0;762goto reset;763}764_payload_counter = 0; // prepare to receive payload765if (_payload_length == 0) {766// bypass payload and go straight to checksum767_step++;768}769break;770771// Receive message data772//773case 6:774_ck_b += (_ck_a += data); // checksum byte775if (_payload_counter < sizeof(_buffer)) {776_buffer[_payload_counter] = data;777}778if (++_payload_counter == _payload_length)779_step++;780break;781782// Checksum and message processing783//784case 7:785_step++;786if (_ck_a != data) {787Debug("bad cka %x should be %x", data, _ck_a);788_step = 0;789goto reset;790}791break;792case 8:793_step = 0;794if (_ck_b != data) {795Debug("bad ckb %x should be %x", data, _ck_b);796break; // bad checksum797}798799#if GPS_MOVING_BASELINE800if (rtcm3_parser) {801// this is a uBlox packet, discard any partial RTCMv3 state802rtcm3_parser->reset();803}804#endif805if (_parse_gps()) {806parsed = true;807}808break;809}810}811return parsed;812}813814// Private Methods /////////////////////////////////////////////////////////////815void AP_GPS_UBLOX::log_mon_hw(void)816{817#if HAL_LOGGING_ENABLED818if (!should_log()) {819return;820}821struct log_Ubx1 pkt = {822LOG_PACKET_HEADER_INIT(LOG_GPS_UBX1_MSG),823time_us : AP_HAL::micros64(),824instance : state.instance,825noisePerMS : _buffer.mon_hw_60.noisePerMS,826jamInd : _buffer.mon_hw_60.jamInd,827aPower : _buffer.mon_hw_60.aPower,828agcCnt : _buffer.mon_hw_60.agcCnt,829config : _unconfigured_messages,830};831if (_payload_length == 68) {832pkt.noisePerMS = _buffer.mon_hw_68.noisePerMS;833pkt.jamInd = _buffer.mon_hw_68.jamInd;834pkt.aPower = _buffer.mon_hw_68.aPower;835pkt.agcCnt = _buffer.mon_hw_68.agcCnt;836}837AP::logger().WriteBlock(&pkt, sizeof(pkt));838#endif839}840841void AP_GPS_UBLOX::log_mon_hw2(void)842{843#if HAL_LOGGING_ENABLED844if (!should_log()) {845return;846}847848struct log_Ubx2 pkt = {849LOG_PACKET_HEADER_INIT(LOG_GPS_UBX2_MSG),850time_us : AP_HAL::micros64(),851instance : state.instance,852ofsI : _buffer.mon_hw2.ofsI,853magI : _buffer.mon_hw2.magI,854ofsQ : _buffer.mon_hw2.ofsQ,855magQ : _buffer.mon_hw2.magQ,856};857AP::logger().WriteBlock(&pkt, sizeof(pkt));858#endif859}860861#if UBLOX_TIM_TM2_LOGGING862void AP_GPS_UBLOX::log_tim_tm2(void)863{864#if HAL_LOGGING_ENABLED865if (!should_log()) {866return;867}868869// @LoggerMessage: UBXT870// @Description: uBlox specific UBX-TIM-TM2 logging, see uBlox interface description871// @Field: TimeUS: Time since system startup872// @Field: I: GPS instance number873// @Field: ch: Channel (i.e. EXTINT) upon which the pulse was measured874// @Field: flags: Bitmask875// @Field: count: Rising edge counter876// @Field: wnR: Week number of last rising edge877// @Field: MsR: Tow of rising edge878// @Field: SubMsR: Millisecond fraction of tow of rising edge in nanoseconds879// @Field: wnF: Week number of last falling edge880// @Field: MsF: Tow of falling edge881// @Field: SubMsF: Millisecond fraction of tow of falling edge in nanoseconds882// @Field: accEst: Accuracy estimate883884AP::logger().WriteStreaming("UBXT",885"TimeUS,I,ch,flags,count,wnR,MsR,SubMsR,wnF,MsF,SubMsF,accEst",886"s#----ss-sss",887"F-----CI-CII",888"QBBBHHIIHIII",889AP_HAL::micros64(),890state.instance,891_buffer.tim_tm2.ch,892_buffer.tim_tm2.flags,893_buffer.tim_tm2.count,894_buffer.tim_tm2.wnR,895_buffer.tim_tm2.towMsR,896_buffer.tim_tm2.towSubMsR,897_buffer.tim_tm2.wnF,898_buffer.tim_tm2.towMsF,899_buffer.tim_tm2.towSubMsF,900_buffer.tim_tm2.accEst);901#endif902}903#endif // UBLOX_TIM_TM2_LOGGING904905#if UBLOX_RXM_RAW_LOGGING906void AP_GPS_UBLOX::log_rxm_raw(const struct ubx_rxm_raw &raw)907{908#if HAL_LOGGING_ENABLED909if (!should_log()) {910return;911}912913uint64_t now = AP_HAL::micros64();914for (uint8_t i=0; i<raw.numSV; i++) {915struct log_GPS_RAW pkt = {916LOG_PACKET_HEADER_INIT(LOG_GPS_RAW_MSG),917time_us : now,918iTOW : raw.iTOW,919week : raw.week,920numSV : raw.numSV,921sv : raw.svinfo[i].sv,922cpMes : raw.svinfo[i].cpMes,923prMes : raw.svinfo[i].prMes,924doMes : raw.svinfo[i].doMes,925mesQI : raw.svinfo[i].mesQI,926cno : raw.svinfo[i].cno,927lli : raw.svinfo[i].lli928};929AP::logger().WriteBlock(&pkt, sizeof(pkt));930}931#endif932}933934void AP_GPS_UBLOX::log_rxm_rawx(const struct ubx_rxm_rawx &raw)935{936#if HAL_LOGGING_ENABLED937if (!should_log()) {938return;939}940941uint64_t now = AP_HAL::micros64();942943struct log_GPS_RAWH header = {944LOG_PACKET_HEADER_INIT(LOG_GPS_RAWH_MSG),945time_us : now,946rcvTow : raw.rcvTow,947week : raw.week,948leapS : raw.leapS,949numMeas : raw.numMeas,950recStat : raw.recStat951};952AP::logger().WriteBlock(&header, sizeof(header));953954for (uint8_t i=0; i<raw.numMeas; i++) {955struct log_GPS_RAWS pkt = {956LOG_PACKET_HEADER_INIT(LOG_GPS_RAWS_MSG),957time_us : now,958prMes : raw.svinfo[i].prMes,959cpMes : raw.svinfo[i].cpMes,960doMes : raw.svinfo[i].doMes,961gnssId : raw.svinfo[i].gnssId,962svId : raw.svinfo[i].svId,963freqId : raw.svinfo[i].freqId,964locktime : raw.svinfo[i].locktime,965cno : raw.svinfo[i].cno,966prStdev : raw.svinfo[i].prStdev,967cpStdev : raw.svinfo[i].cpStdev,968doStdev : raw.svinfo[i].doStdev,969trkStat : raw.svinfo[i].trkStat970};971AP::logger().WriteBlock(&pkt, sizeof(pkt));972}973#endif974}975#endif // UBLOX_RXM_RAW_LOGGING976977void AP_GPS_UBLOX::unexpected_message(void)978{979Debug("Unexpected message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);980if (++_disable_counter == 0) {981// disable future sends of this message id, but982// only do this every 256 messages, as some983// message types can't be disabled and we don't984// want to get into an ack war985Debug("Disabling message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);986_configure_message_rate(_class, _msg_id, 0);987}988}989990// return size of a config key, or 0 if unknown991uint8_t AP_GPS_UBLOX::config_key_size(ConfigKey key) const992{993// step over the value994const uint8_t key_size = (uint32_t(key) >> 28) & 0x07; // mask off the storage size995switch (key_size) {996case 0x1: // bit997case 0x2: // byte998return 1;999case 0x3: // 2 bytes1000return 2;1001case 0x4: // 4 bytes1002return 4;1003case 0x5: // 8 bytes1004return 8;1005default:1006break;1007}1008// invalid1009return 0;1010}10111012/*1013find index of a config key in the active_config list, or -11014*/1015int8_t AP_GPS_UBLOX::find_active_config_index(ConfigKey key) const1016{1017if (active_config.list == nullptr) {1018return -1;1019}1020for (uint8_t i=0; i<active_config.count; i++) {1021if (key == active_config.list[i].key) {1022return (int8_t)i;1023}1024}10251026return -1;1027}10281029bool1030AP_GPS_UBLOX::_parse_gps(void)1031{1032if (_class == CLASS_ACK) {1033Debug("ACK %u", (unsigned)_msg_id);10341035if(_msg_id == MSG_ACK_ACK) {1036switch(_buffer.ack.clsID) {1037case CLASS_CFG:1038switch(_buffer.ack.msgID) {1039case MSG_CFG_CFG:1040_cfg_saved = true;1041_cfg_needs_save = false;1042break;1043case MSG_CFG_GNSS:1044_unconfigured_messages &= ~CONFIG_GNSS;1045break;1046case MSG_CFG_MSG:1047// There is no way to know what MSG config was ack'ed, assume it was the last1048// one requested. To verify it rerequest the last config we sent. If we miss1049// the actual ack we will catch it next time through the poll loop, but that1050// will be a good chunk of time later.1051break;1052case MSG_CFG_NAV_SETTINGS:1053_unconfigured_messages &= ~CONFIG_NAV_SETTINGS;1054break;1055case MSG_CFG_RATE:1056// The GPS will ACK a update rate that is invalid. in order to detect this1057// only accept the rate as configured by reading the settings back and1058// validating that they all match the target values1059break;1060case MSG_CFG_SBAS:1061_unconfigured_messages &= ~CONFIG_SBAS;1062break;1063case MSG_CFG_TP5:1064_unconfigured_messages &= ~CONFIG_TP5;1065break;1066}10671068break;1069case CLASS_MON:1070switch(_buffer.ack.msgID) {1071case MSG_MON_HW:1072_unconfigured_messages &= ~CONFIG_RATE_MON_HW;1073break;1074case MSG_MON_HW2:1075_unconfigured_messages &= ~CONFIG_RATE_MON_HW2;1076break;1077}1078}1079}1080if(_msg_id == MSG_ACK_NACK) {1081switch(_buffer.nack.clsID) {1082case CLASS_CFG:1083switch(_buffer.nack.msgID) {1084case MSG_CFG_VALGET:1085CFG_Debug("NACK VALGET 0x%x", (unsigned)_buffer.nack.msgID);1086if (active_config.list != nullptr) {1087/*1088likely this device does not support fetching multiple keys at once, go one at a time1089*/1090if (active_config.fetch_index == -1) {1091CFG_Debug("NACK starting %u", unsigned(active_config.count));1092active_config.fetch_index = 0;1093} else {1094// the device does not support the config key we asked for,1095// consider the bit as done1096active_config.done_mask |= (1U<<active_config.fetch_index);1097CFG_Debug("NACK %d 0x%x done=0x%x",1098int(active_config.fetch_index),1099unsigned(active_config.list[active_config.fetch_index].key),1100unsigned(active_config.done_mask));1101if (active_config.done_mask == (1U<<active_config.count)-1) {1102// all done!1103_unconfigured_messages &= ~active_config.unconfig_bit;1104}1105active_config.fetch_index++;1106}1107if (active_config.fetch_index < active_config.count) {1108_configure_valget(active_config.list[active_config.fetch_index].key);1109}1110}1111break;1112case MSG_CFG_VALSET:1113CFG_Debug("NACK VALSET 0x%x 0x%x", (unsigned)_buffer.nack.msgID,1114unsigned(active_config.list[active_config.set_index].key));1115if (is_gnss_key(active_config.list[active_config.set_index].key)) {1116GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "GPS %u: unable to configure band 0x%02x",1117unsigned(state.instance + 1), unsigned(active_config.list[active_config.set_index].key));11181119}1120break;1121}1122}1123}1124return false;1125}11261127if (_class == CLASS_CFG) {1128switch(_msg_id) {1129case MSG_CFG_NAV_SETTINGS:1130Debug("Got settings %u min_elev %d drLimit %u\n",1131(unsigned)_buffer.nav_settings.dynModel,1132(int)_buffer.nav_settings.minElev,1133(unsigned)_buffer.nav_settings.drLimit);1134_buffer.nav_settings.mask = 0;1135if (gps._navfilter != AP_GPS::GPS_ENGINE_NONE &&1136_buffer.nav_settings.dynModel != gps._navfilter) {1137// we've received the current nav settings, change the engine1138// settings and send them back1139Debug("Changing engine setting from %u to %u\n",1140(unsigned)_buffer.nav_settings.dynModel, (unsigned)gps._navfilter);1141_buffer.nav_settings.dynModel = gps._navfilter;1142_buffer.nav_settings.mask |= 1;1143}1144if (gps._min_elevation != -100 &&1145_buffer.nav_settings.minElev != gps._min_elevation) {1146Debug("Changing min elevation to %d\n", (int)gps._min_elevation);1147_buffer.nav_settings.minElev = gps._min_elevation;1148_buffer.nav_settings.mask |= 2;1149}1150if (_buffer.nav_settings.mask != 0) {1151_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS,1152&_buffer.nav_settings,1153sizeof(_buffer.nav_settings));1154_unconfigured_messages |= CONFIG_NAV_SETTINGS;1155_cfg_needs_save = true;1156} else {1157_unconfigured_messages &= ~CONFIG_NAV_SETTINGS;1158}1159return false;11601161#if UBLOX_GNSS_SETTINGS1162case MSG_CFG_GNSS:1163if (params.gnss_mode != 0 && !supports_F9_config()) {1164struct ubx_cfg_gnss start_gnss = _buffer.gnss;1165uint8_t gnssCount = 0;1166Debug("Got GNSS Settings %u %u %u %u:\n",1167(unsigned)_buffer.gnss.msgVer,1168(unsigned)_buffer.gnss.numTrkChHw,1169(unsigned)_buffer.gnss.numTrkChUse,1170(unsigned)_buffer.gnss.numConfigBlocks);1171#if UBLOX_DEBUGGING1172for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {1173Debug(" %u %u %u 0x%08x\n",1174(unsigned)_buffer.gnss.configBlock[i].gnssId,1175(unsigned)_buffer.gnss.configBlock[i].resTrkCh,1176(unsigned)_buffer.gnss.configBlock[i].maxTrkCh,1177(unsigned)_buffer.gnss.configBlock[i].flags);1178}1179#endif11801181for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {1182if((params.gnss_mode & (1 << i)) && i != GNSS_SBAS) {1183gnssCount++;1184}1185}1186for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {1187// Reserve an equal portion of channels for all enabled systems that supports it1188if(params.gnss_mode & (1 << _buffer.gnss.configBlock[i].gnssId)) {1189if(GNSS_SBAS !=_buffer.gnss.configBlock[i].gnssId && (_hardware_generation > UBLOX_M8 || GNSS_GALILEO !=_buffer.gnss.configBlock[i].gnssId)) {1190_buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);1191_buffer.gnss.configBlock[i].maxTrkCh = _buffer.gnss.numTrkChHw;1192} else {1193if(GNSS_SBAS ==_buffer.gnss.configBlock[i].gnssId) {1194_buffer.gnss.configBlock[i].resTrkCh = 1;1195_buffer.gnss.configBlock[i].maxTrkCh = 3;1196}1197if(GNSS_GALILEO ==_buffer.gnss.configBlock[i].gnssId) {1198_buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);1199_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 eight1200}1201}1202_buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags | 0x00000001;1203} else {1204_buffer.gnss.configBlock[i].resTrkCh = 0;1205_buffer.gnss.configBlock[i].maxTrkCh = 0;1206_buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags & 0xFFFFFFFE;1207}1208}1209if (memcmp(&start_gnss, &_buffer.gnss, sizeof(start_gnss))) {1210_send_message(CLASS_CFG, MSG_CFG_GNSS, &_buffer.gnss, 4 + (8 * _buffer.gnss.numConfigBlocks));1211_unconfigured_messages |= CONFIG_GNSS;1212_cfg_needs_save = true;1213} else {1214_unconfigured_messages &= ~CONFIG_GNSS;1215}1216} else {1217_unconfigured_messages &= ~CONFIG_GNSS;1218}1219return false;1220#endif12211222case MSG_CFG_SBAS:1223if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {1224Debug("Got SBAS settings %u %u %u 0x%x 0x%x\n",1225(unsigned)_buffer.sbas.mode,1226(unsigned)_buffer.sbas.usage,1227(unsigned)_buffer.sbas.maxSBAS,1228(unsigned)_buffer.sbas.scanmode2,1229(unsigned)_buffer.sbas.scanmode1);1230if (_buffer.sbas.mode != gps._sbas_mode) {1231_buffer.sbas.mode = gps._sbas_mode;1232_send_message(CLASS_CFG, MSG_CFG_SBAS,1233&_buffer.sbas,1234sizeof(_buffer.sbas));1235_unconfigured_messages |= CONFIG_SBAS;1236_cfg_needs_save = true;1237} else {1238_unconfigured_messages &= ~CONFIG_SBAS;1239}1240} else {1241_unconfigured_messages &= ~CONFIG_SBAS;1242}1243return false;1244case MSG_CFG_MSG:1245if(_payload_length == sizeof(ubx_cfg_msg_rate_6)) {1246// can't verify the setting without knowing the port1247// request the port again1248if(_ublox_port >= UBLOX_MAX_PORTS) {1249_request_port();1250return false;1251}1252_verify_rate(_buffer.msg_rate_6.msg_class, _buffer.msg_rate_6.msg_id,1253_buffer.msg_rate_6.rates[_ublox_port]);1254} else {1255_verify_rate(_buffer.msg_rate.msg_class, _buffer.msg_rate.msg_id,1256_buffer.msg_rate.rate);1257}1258return false;1259case MSG_CFG_PRT:1260_ublox_port = _buffer.prt.portID;1261return false;1262case MSG_CFG_RATE:1263if(_buffer.nav_rate.measure_rate_ms != params.rate_ms ||1264_buffer.nav_rate.nav_rate != 1 ||1265_buffer.nav_rate.timeref != 0) {1266_configure_rate();1267_unconfigured_messages |= CONFIG_RATE_NAV;1268_cfg_needs_save = true;1269} else {1270_unconfigured_messages &= ~CONFIG_RATE_NAV;1271}1272return false;12731274#if CONFIGURE_PPS_PIN1275case MSG_CFG_TP5: {1276// configure the PPS pin for 1Hz, zero delay1277Debug("Got TP5 ver=%u 0x%04x %u\n",1278(unsigned)_buffer.nav_tp5.version,1279(unsigned)_buffer.nav_tp5.flags,1280(unsigned)_buffer.nav_tp5.freqPeriod);1281#ifdef HAL_GPIO_PPS1282hal.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);1283#endif1284const uint16_t desired_flags = 0x003f;1285const uint16_t desired_period_hz = _pps_freq;12861287if (_buffer.nav_tp5.flags != desired_flags ||1288_buffer.nav_tp5.freqPeriod != desired_period_hz) {1289_buffer.nav_tp5.tpIdx = 0;1290_buffer.nav_tp5.reserved1[0] = 0;1291_buffer.nav_tp5.reserved1[1] = 0;1292_buffer.nav_tp5.antCableDelay = 0;1293_buffer.nav_tp5.rfGroupDelay = 0;1294_buffer.nav_tp5.freqPeriod = desired_period_hz;1295_buffer.nav_tp5.freqPeriodLock = desired_period_hz;1296_buffer.nav_tp5.pulseLenRatio = 1;1297_buffer.nav_tp5.pulseLenRatioLock = 2;1298_buffer.nav_tp5.userConfigDelay = 0;1299_buffer.nav_tp5.flags = desired_flags;1300_send_message(CLASS_CFG, MSG_CFG_TP5,1301&_buffer.nav_tp5,1302sizeof(_buffer.nav_tp5));1303_unconfigured_messages |= CONFIG_TP5;1304_cfg_needs_save = true;1305} else {1306_unconfigured_messages &= ~CONFIG_TP5;1307}1308return false;1309}1310#endif // CONFIGURE_PPS_PIN1311case MSG_CFG_VALGET: {1312uint8_t cfg_len = _payload_length - sizeof(ubx_cfg_valget);1313const uint8_t *cfg_data = (const uint8_t *)(&_buffer) + sizeof(ubx_cfg_valget);1314while (cfg_len >= 5) {1315ConfigKey id;1316memcpy(&id, cfg_data, sizeof(uint32_t));1317cfg_len -= 4;1318cfg_data += 4;1319switch (id) {1320case ConfigKey::TMODE_MODE: {1321uint8_t mode = cfg_data[0];1322if (mode != 0) {1323// ask for mode 0, to disable TIME mode1324mode = 0;1325_configure_valset(ConfigKey::TMODE_MODE, &mode);1326_cfg_needs_save = true;1327_unconfigured_messages |= CONFIG_TMODE_MODE;1328} else {1329_unconfigured_messages &= ~CONFIG_TMODE_MODE;1330}1331break;1332}1333default:1334break;1335}1336// see if it is in active config list1337int8_t cfg_idx = find_active_config_index(id);1338if (cfg_idx >= 0) {1339CFG_Debug("valset(0x%lx): %u", uint32_t(id), (*cfg_data) & 0x1);1340const uint8_t key_size = config_key_size(id);1341if (cfg_len < key_size1342// for keys of length 1 only the LSB is significant1343|| (key_size == 1 && (active_config.list[cfg_idx].value & 0x1) != (*cfg_data & 0x1))1344|| memcmp(&active_config.list[cfg_idx].value, cfg_data, key_size) != 0) {1345_configure_valset(id, &active_config.list[cfg_idx].value, active_config.layers);1346_unconfigured_messages |= active_config.unconfig_bit;1347active_config.done_mask &= ~(1U << cfg_idx);1348active_config.set_index = cfg_idx;1349_cfg_needs_save = true;1350} else {1351active_config.done_mask |= (1U << cfg_idx);1352CFG_Debug("done %u mask=0x%x all_mask=0x%x",1353unsigned(cfg_idx),1354unsigned(active_config.done_mask),1355(1U<<active_config.count)-1);1356if (active_config.done_mask == (1U<<active_config.count)-1) {1357// all done!1358_unconfigured_messages &= ~active_config.unconfig_bit;1359}1360}1361if (active_config.fetch_index >= 0 &&1362active_config.fetch_index < active_config.count &&1363id == active_config.list[active_config.fetch_index].key) {1364active_config.fetch_index++;1365if (active_config.fetch_index < active_config.count) {1366_configure_valget(active_config.list[active_config.fetch_index].key);1367CFG_Debug("valget %d 0x%x", int(active_config.fetch_index),1368unsigned(active_config.list[active_config.fetch_index].key));1369}1370}1371} else {1372CFG_Debug("valget no active config for 0x%lx", (uint32_t)id);1373}13741375// step over the value1376uint8_t step_size = config_key_size(id);1377if (step_size == 0) {1378return false;1379}1380cfg_len -= step_size;1381cfg_data += step_size;1382}1383}1384}1385}13861387if (_class == CLASS_MON) {1388switch(_msg_id) {1389case MSG_MON_HW:1390if (_payload_length == 60 || _payload_length == 68) {1391log_mon_hw();1392}1393break;1394case MSG_MON_HW2:1395if (_payload_length == 28) {1396log_mon_hw2();1397}1398break;1399case MSG_MON_VER: {1400bool check_L1L5 = false;1401_have_version = true;1402strncpy(_version.hwVersion, _buffer.mon_ver.hwVersion, sizeof(_version.hwVersion));1403strncpy(_version.swVersion, _buffer.mon_ver.swVersion, sizeof(_version.swVersion));1404void* mod = memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "MOD=", 4);1405if (mod != nullptr) {1406strncpy(_module, (char*)mod+4, UBLOX_MODULE_LEN-1);1407}14081409GCS_SEND_TEXT(MAV_SEVERITY_INFO,1410"u-blox %s%s%d HW: %s SW: %s",1411_module, mod != nullptr ? " " : "",1412state.instance + 1,1413_version.hwVersion,1414_version.swVersion);1415// check for F9 and M9. The F9 does not respond to SVINFO,1416// so we need to use MON_VER for hardware generation1417if (strncmp(_version.hwVersion, "00190000", 8) == 0) {1418if (strncmp(_version.swVersion, "EXT CORE 1", 10) == 0) {1419// a F91420if (_hardware_generation != UBLOX_F9) {1421// need to ensure time mode is correctly setup on F91422_unconfigured_messages |= CONFIG_TMODE_MODE;1423}1424_hardware_generation = UBLOX_F9;1425_unconfigured_messages |= CONFIG_F9;1426_unconfigured_messages &= ~CONFIG_GNSS;1427if (strncmp(_module, "ZED-F9P", UBLOX_MODULE_LEN) == 0) {1428_hardware_variant = UBLOX_F9_ZED;1429} else if (strncmp(_module, "NEO-F9P", UBLOX_MODULE_LEN) == 0) {1430_hardware_variant = UBLOX_F9_NEO;1431}1432}1433if (strncmp(_version.swVersion, "EXT CORE 4", 10) == 0) {1434// a M91435_hardware_generation = UBLOX_M9;1436}1437check_L1L5 = true;1438}1439// check for M101440if (strncmp(_version.hwVersion, "000A0000", 8) == 0) {1441_hardware_generation = UBLOX_M10;1442_unconfigured_messages |= CONFIG_M10;1443// M10 does not support CONFIG_GNSS1444_unconfigured_messages &= ~CONFIG_GNSS;1445check_L1L5 = true;1446}1447if (check_L1L5) {1448// check if L1L5 in extension1449if (memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "L1L5", 4) != nullptr) {1450supports_l5 = true;1451GCS_SEND_TEXT(MAV_SEVERITY_INFO, "u-blox supports L5 Band");1452_unconfigured_messages |= CONFIG_L5;1453}1454}1455break;1456}1457default:1458unexpected_message();1459}1460return false;1461}14621463#if UBLOX_RXM_RAW_LOGGING1464if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAW && gps._raw_data != 0) {1465log_rxm_raw(_buffer.rxm_raw);1466return false;1467} else if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAWX && gps._raw_data != 0) {1468log_rxm_rawx(_buffer.rxm_rawx);1469return false;1470}1471#endif // UBLOX_RXM_RAW_LOGGING14721473#if UBLOX_TIM_TM2_LOGGING1474if ((_class == CLASS_TIM) && (_msg_id == MSG_TIM_TM2) && (_payload_length == 28)) {1475log_tim_tm2();1476return false;1477}1478#endif // UBLOX_TIM_TM2_LOGGING14791480if (_class != CLASS_NAV) {1481unexpected_message();1482return false;1483}14841485switch (_msg_id) {1486case MSG_POSLLH:1487Debug("MSG_POSLLH next_fix=%u", next_fix);1488if (havePvtMsg) {1489_unconfigured_messages |= CONFIG_RATE_POSLLH;1490break;1491}1492_check_new_itow(_buffer.posllh.itow);1493_last_pos_time = _buffer.posllh.itow;1494state.location.lng = _buffer.posllh.longitude;1495state.location.lat = _buffer.posllh.latitude;1496state.have_undulation = true;1497state.undulation = (_buffer.posllh.altitude_msl - _buffer.posllh.altitude_ellipsoid) * 0.001;1498set_alt_amsl_cm(state, _buffer.posllh.altitude_msl / 10);14991500state.status = next_fix;1501_new_position = true;1502state.horizontal_accuracy = _buffer.posllh.horizontal_accuracy*1.0e-3f;1503state.vertical_accuracy = _buffer.posllh.vertical_accuracy*1.0e-3f;1504state.have_horizontal_accuracy = true;1505state.have_vertical_accuracy = true;1506#if UBLOX_FAKE_3DLOCK1507state.location.lng = 1491652300L;1508state.location.lat = -353632610L;1509state.location.alt = 58400;1510state.vertical_accuracy = 0;1511state.horizontal_accuracy = 0;1512#endif1513break;1514case MSG_STATUS:1515Debug("MSG_STATUS fix_status=%u fix_type=%u",1516_buffer.status.fix_status,1517_buffer.status.fix_type);1518_check_new_itow(_buffer.status.itow);1519if (havePvtMsg) {1520_unconfigured_messages |= CONFIG_RATE_STATUS;1521break;1522}1523if (_buffer.status.fix_status & NAV_STATUS_FIX_VALID) {1524if( (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) &&1525(_buffer.status.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {1526next_fix = AP_GPS::GPS_OK_FIX_3D_DGPS;1527}else if( _buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) {1528next_fix = AP_GPS::GPS_OK_FIX_3D;1529}else if (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_2D) {1530next_fix = AP_GPS::GPS_OK_FIX_2D;1531}else{1532next_fix = AP_GPS::NO_FIX;1533state.status = AP_GPS::NO_FIX;1534}1535}else{1536next_fix = AP_GPS::NO_FIX;1537state.status = AP_GPS::NO_FIX;1538}1539#if UBLOX_FAKE_3DLOCK1540state.status = AP_GPS::GPS_OK_FIX_3D;1541next_fix = state.status;1542#endif1543break;1544case MSG_DOP:1545Debug("MSG_DOP");1546noReceivedHdop = false;1547_check_new_itow(_buffer.dop.itow);1548state.hdop = _buffer.dop.hDOP;1549state.vdop = _buffer.dop.vDOP;1550#if UBLOX_FAKE_3DLOCK1551state.hdop = 130;1552state.hdop = 170;1553#endif1554break;1555case MSG_SOL:1556Debug("MSG_SOL fix_status=%u fix_type=%u",1557_buffer.solution.fix_status,1558_buffer.solution.fix_type);1559_check_new_itow(_buffer.solution.itow);1560if (havePvtMsg) {1561state.time_week = _buffer.solution.week;1562break;1563}1564if (_buffer.solution.fix_status & NAV_STATUS_FIX_VALID) {1565if( (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) &&1566(_buffer.solution.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {1567next_fix = AP_GPS::GPS_OK_FIX_3D_DGPS;1568}else if( _buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) {1569next_fix = AP_GPS::GPS_OK_FIX_3D;1570}else if (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_2D) {1571next_fix = AP_GPS::GPS_OK_FIX_2D;1572}else{1573next_fix = AP_GPS::NO_FIX;1574state.status = AP_GPS::NO_FIX;1575}1576}else{1577next_fix = AP_GPS::NO_FIX;1578state.status = AP_GPS::NO_FIX;1579}1580if(noReceivedHdop) {1581state.hdop = _buffer.solution.position_DOP;1582}1583state.num_sats = _buffer.solution.satellites;1584if (next_fix >= AP_GPS::GPS_OK_FIX_2D) {1585state.last_gps_time_ms = AP_HAL::millis();1586state.time_week_ms = _buffer.solution.itow;1587state.time_week = _buffer.solution.week;1588}1589#if UBLOX_FAKE_3DLOCK1590next_fix = state.status;1591state.num_sats = 10;1592state.time_week = 1721;1593state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;1594state.last_gps_time_ms = AP_HAL::millis();1595state.hdop = 130;1596#endif1597break;15981599#if GPS_MOVING_BASELINE1600case MSG_RELPOSNED:1601{1602if (role != AP_GPS::GPS_ROLE_MB_ROVER) {1603// ignore RELPOSNED if not configured as a rover1604break;1605}1606// note that we require the yaw to come from a fixed solution, not a float solution1607// yaw from a float solution would only be acceptable with a very large separation between1608// GPS modules1609const uint32_t valid_mask = static_cast<uint32_t>(RELPOSNED::relPosHeadingValid) |1610static_cast<uint32_t>(RELPOSNED::relPosValid) |1611static_cast<uint32_t>(RELPOSNED::gnssFixOK) |1612static_cast<uint32_t>(RELPOSNED::isMoving) |1613static_cast<uint32_t>(RELPOSNED::carrSolnFixed);1614const uint32_t invalid_mask = static_cast<uint32_t>(RELPOSNED::refPosMiss) |1615static_cast<uint32_t>(RELPOSNED::refObsMiss) |1616static_cast<uint32_t>(RELPOSNED::carrSolnFloat);16171618_check_new_itow(_buffer.relposned.iTOW);1619if (_buffer.relposned.iTOW != _last_relposned_itow+200) {1620// useful for looking at packet loss on links1621MB_Debug("RELPOSNED ITOW %u %u\n", unsigned(_buffer.relposned.iTOW), unsigned(_last_relposned_itow));1622}1623_last_relposned_itow = _buffer.relposned.iTOW;1624MB_Debug("RELPOSNED flags: %lx valid: %lx invalid: %lx\n", _buffer.relposned.flags, valid_mask, invalid_mask);1625if (((_buffer.relposned.flags & valid_mask) == valid_mask) &&1626((_buffer.relposned.flags & invalid_mask) == 0)) {1627if (calculate_moving_base_yaw(_buffer.relposned.relPosHeading * 1e-5,1628_buffer.relposned.relPosLength * 0.01,1629_buffer.relposned.relPosD*0.01)) {1630state.have_gps_yaw_accuracy = true;1631state.gps_yaw_accuracy = _buffer.relposned.accHeading * 1e-5;1632_last_relposned_ms = AP_HAL::millis();1633}1634state.relPosHeading = _buffer.relposned.relPosHeading * 1e-5;1635state.relPosLength = _buffer.relposned.relPosLength * 0.01;1636state.relPosD = _buffer.relposned.relPosD * 0.01;1637state.accHeading = _buffer.relposned.accHeading * 1e-5;1638state.relposheading_ts = AP_HAL::millis();1639} else {1640state.have_gps_yaw_accuracy = false;1641}1642}1643break;1644#endif // GPS_MOVING_BASELINE16451646case MSG_PVT:1647Debug("MSG_PVT");16481649havePvtMsg = true;1650// position1651_check_new_itow(_buffer.pvt.itow);1652_last_pvt_itow = _buffer.pvt.itow;1653_last_pos_time = _buffer.pvt.itow;1654state.location.lng = _buffer.pvt.lon;1655state.location.lat = _buffer.pvt.lat;1656state.have_undulation = true;1657state.undulation = (_buffer.pvt.h_msl - _buffer.pvt.h_ellipsoid) * 0.001;1658set_alt_amsl_cm(state, _buffer.pvt.h_msl / 10);1659switch (_buffer.pvt.fix_type)1660{1661case 0:1662state.status = AP_GPS::NO_FIX;1663break;1664case 1:1665state.status = AP_GPS::NO_FIX;1666break;1667case 2:1668state.status = AP_GPS::GPS_OK_FIX_2D;1669break;1670case 3:1671state.status = AP_GPS::GPS_OK_FIX_3D;1672if (_buffer.pvt.flags & 0b00000010) // diffsoln1673state.status = AP_GPS::GPS_OK_FIX_3D_DGPS;1674if (_buffer.pvt.flags & 0b01000000) // carrsoln - float1675state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FLOAT;1676if (_buffer.pvt.flags & 0b10000000) // carrsoln - fixed1677state.status = AP_GPS::GPS_OK_FIX_3D_RTK_FIXED;1678break;1679case 4:1680GCS_SEND_TEXT(MAV_SEVERITY_INFO,1681"Unexpected state %d", _buffer.pvt.flags);1682state.status = AP_GPS::GPS_OK_FIX_3D;1683break;1684case 5:1685state.status = AP_GPS::NO_FIX;1686break;1687default:1688state.status = AP_GPS::NO_FIX;1689break;1690}1691next_fix = state.status;1692_new_position = true;1693state.horizontal_accuracy = _buffer.pvt.h_acc*1.0e-3f;1694state.vertical_accuracy = _buffer.pvt.v_acc*1.0e-3f;1695state.have_horizontal_accuracy = true;1696state.have_vertical_accuracy = true;1697// SVs1698state.num_sats = _buffer.pvt.num_sv;1699// velocity1700_last_vel_time = _buffer.pvt.itow;1701state.ground_speed = _buffer.pvt.gspeed*0.001f; // m/s1702state.ground_course = wrap_360(_buffer.pvt.head_mot * 1.0e-5f); // Heading 2D deg * 1000001703state.have_vertical_velocity = true;1704state.velocity.x = _buffer.pvt.velN * 0.001f;1705state.velocity.y = _buffer.pvt.velE * 0.001f;1706state.velocity.z = _buffer.pvt.velD * 0.001f;1707state.have_speed_accuracy = true;1708state.speed_accuracy = _buffer.pvt.s_acc*0.001f;1709_new_speed = true;1710// dop1711if(noReceivedHdop) {1712state.hdop = _buffer.pvt.p_dop;1713state.vdop = _buffer.pvt.p_dop;1714}17151716if (_buffer.pvt.fix_type >= 2) {1717state.last_gps_time_ms = AP_HAL::millis();1718}17191720// time1721state.time_week_ms = _buffer.pvt.itow;1722#if UBLOX_FAKE_3DLOCK1723state.location.lng = 1491652300L;1724state.location.lat = -353632610L;1725state.location.alt = 58400;1726state.vertical_accuracy = 0;1727state.horizontal_accuracy = 0;1728state.status = AP_GPS::GPS_OK_FIX_3D;1729state.num_sats = 10;1730state.time_week = 1721;1731state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;1732state.last_gps_time_ms = AP_HAL::millis();1733state.hdop = 130;1734state.speed_accuracy = 0;1735next_fix = state.status;1736#endif1737break;1738case MSG_TIMEGPS:1739Debug("MSG_TIMEGPS");1740_check_new_itow(_buffer.timegps.itow);1741if (_buffer.timegps.valid & UBX_TIMEGPS_VALID_WEEK_MASK) {1742state.time_week = _buffer.timegps.week;1743}1744break;1745case MSG_VELNED:1746Debug("MSG_VELNED");1747if (havePvtMsg) {1748_unconfigured_messages |= CONFIG_RATE_VELNED;1749break;1750}1751_check_new_itow(_buffer.velned.itow);1752_last_vel_time = _buffer.velned.itow;1753state.ground_speed = _buffer.velned.speed_2d*0.01f; // m/s1754state.ground_course = wrap_360(_buffer.velned.heading_2d * 1.0e-5f); // Heading 2D deg * 1000001755state.have_vertical_velocity = true;1756state.velocity.x = _buffer.velned.ned_north * 0.01f;1757state.velocity.y = _buffer.velned.ned_east * 0.01f;1758state.velocity.z = _buffer.velned.ned_down * 0.01f;1759velocity_to_speed_course(state);1760state.have_speed_accuracy = true;1761state.speed_accuracy = _buffer.velned.speed_accuracy*0.01f;1762#if UBLOX_FAKE_3DLOCK1763state.speed_accuracy = 0;1764#endif1765_new_speed = true;1766break;1767case MSG_NAV_SVINFO:1768{1769Debug("MSG_NAV_SVINFO\n");1770static const uint8_t HardwareGenerationMask = 0x07;1771_check_new_itow(_buffer.svinfo_header.itow);1772_hardware_generation = _buffer.svinfo_header.globalFlags & HardwareGenerationMask;1773switch (_hardware_generation) {1774case UBLOX_5:1775case UBLOX_6:1776// only 7 and newer support CONFIG_GNSS1777_unconfigured_messages &= ~CONFIG_GNSS;1778break;1779case UBLOX_7:1780case UBLOX_M8:1781#if UBLOX_SPEED_CHANGE1782port->begin(4000000U);1783Debug("Changed speed to 4Mhz for SPI-driven UBlox\n");1784#endif1785break;1786default:1787hal.console->printf("Wrong Ublox Hardware Version%u\n", _hardware_generation);1788break;1789};1790_unconfigured_messages &= ~CONFIG_VERSION;1791/* We don't need that anymore */1792_configure_message_rate(CLASS_NAV, MSG_NAV_SVINFO, 0);1793break;1794}1795default:1796Debug("Unexpected NAV message 0x%02x", (unsigned)_msg_id);1797if (++_disable_counter == 0) {1798Debug("Disabling NAV message 0x%02x", (unsigned)_msg_id);1799_configure_message_rate(CLASS_NAV, _msg_id, 0);1800}1801return false;1802}18031804if (state.have_gps_yaw) {1805// when we are a rover we want to ensure we have both the new1806// PVT and the new RELPOSNED message so that we give a1807// consistent view1808if (AP_HAL::millis() - _last_relposned_ms > 400) {1809// we have stopped receiving valid RELPOSNED messages, disable yaw reporting1810state.have_gps_yaw = false;1811} else if (_last_relposned_itow != _last_pvt_itow) {1812// wait until ITOW matches1813return false;1814}1815}18161817// we only return true when we get new position and speed data1818// this ensures we don't use stale data1819if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {1820_new_speed = _new_position = false;1821return true;1822}1823return false;1824}18251826/*1827* handle pps interrupt1828*/1829#ifdef HAL_GPIO_PPS1830void1831AP_GPS_UBLOX::pps_interrupt(uint8_t pin, bool high, uint32_t timestamp_us)1832{1833_last_pps_time_us = AP_HAL::micros64();1834}18351836void1837AP_GPS_UBLOX::set_pps_desired_freq(uint8_t freq)1838{1839_pps_freq = freq;1840_unconfigured_messages |= CONFIG_TP5;1841}1842#endif184318441845// UBlox auto configuration18461847/*1848* update checksum for a set of bytes1849*/1850void1851AP_GPS_UBLOX::_update_checksum(uint8_t *data, uint16_t len, uint8_t &ck_a, uint8_t &ck_b)1852{1853while (len--) {1854ck_a += *data;1855ck_b += ck_a;1856data++;1857}1858}185918601861/*1862* send a ublox message1863*/1864bool1865AP_GPS_UBLOX::_send_message(uint8_t msg_class, uint8_t msg_id, const void *msg, uint16_t size)1866{1867if (port->txspace() < (sizeof(struct ubx_header) + 2 + size)) {1868return false;1869}1870struct ubx_header header;1871uint8_t ck_a=0, ck_b=0;1872header.preamble1 = PREAMBLE1;1873header.preamble2 = PREAMBLE2;1874header.msg_class = msg_class;1875header.msg_id = msg_id;1876header.length = size;18771878_update_checksum((uint8_t *)&header.msg_class, sizeof(header)-2, ck_a, ck_b);1879_update_checksum((uint8_t *)msg, size, ck_a, ck_b);18801881port->write((const uint8_t *)&header, sizeof(header));1882port->write((const uint8_t *)msg, size);1883port->write((const uint8_t *)&ck_a, 1);1884port->write((const uint8_t *)&ck_b, 1);1885return true;1886}18871888/*1889* requests the given message rate for a specific message class1890* and msg_id1891* returns true if it sent the request, false if waiting on knowing the port1892*/1893bool1894AP_GPS_UBLOX::_request_message_rate(uint8_t msg_class, uint8_t msg_id)1895{1896// Without knowing what communication port is being used it isn't possible to verify1897// always ensure we have a port before sending the request1898if(_ublox_port >= UBLOX_MAX_PORTS) {1899_request_port();1900return false;1901} else {1902struct ubx_cfg_msg msg;1903msg.msg_class = msg_class;1904msg.msg_id = msg_id;1905return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));1906}1907}19081909/*1910* configure a UBlox GPS for the given message rate for a specific1911* message class and msg_id1912*/1913bool1914AP_GPS_UBLOX::_configure_message_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate)1915{1916if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_msg_rate)+2)) {1917return false;1918}19191920struct ubx_cfg_msg_rate msg;1921msg.msg_class = msg_class;1922msg.msg_id = msg_id;1923msg.rate = rate;1924return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));1925}19261927/*1928* configure F9/M10 based key/value pair - VALSET1929*/1930bool1931AP_GPS_UBLOX::_configure_valset(ConfigKey key, const void *value, uint8_t layers)1932{1933if (!supports_F9_config()) {1934return false;1935}19361937const uint8_t len = config_key_size(key);1938struct ubx_cfg_valset msg {};1939uint8_t buf[sizeof(msg)+len];1940if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {1941return false;1942}1943msg.version = 1;1944msg.layers = layers;1945msg.transaction = 0;1946msg.key = uint32_t(key);1947memcpy(buf, &msg, sizeof(msg));1948memcpy(&buf[sizeof(msg)], value, len);1949auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, sizeof(buf));1950return ret;1951}19521953/*1954* configure F9 based key/value pair - VALGET1955*/1956bool1957AP_GPS_UBLOX::_configure_valget(ConfigKey key)1958{1959if (!supports_F9_config()) {1960return false;1961}1962struct {1963struct ubx_cfg_valget msg;1964ConfigKey key;1965} msg {};1966if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(msg)+2)) {1967return false;1968}1969msg.msg.version = 0;1970msg.msg.layers = 0; // ram1971msg.key = key;1972return _send_message(CLASS_CFG, MSG_CFG_VALGET, &msg, sizeof(msg));1973}19741975/*1976* configure F9 based key/value pair for a complete configuration set1977*1978* this method requests each configuration variable from the GPS.1979* When we handle the reply in _parse_gps we may then choose to set a1980* MSG_CFG_VALSET back to the GPS if we don't like its response.1981*/1982bool1983AP_GPS_UBLOX::_configure_config_set(const config_list *list, uint8_t count, uint32_t unconfig_bit, uint8_t layers)1984{1985active_config.list = list;1986active_config.count = count;1987active_config.done_mask = 0;1988active_config.unconfig_bit = unconfig_bit;1989active_config.layers = layers;1990// we start by fetching multiple values at once (with fetch_index1991// -1) then if we get a NACK for VALGET we switch to fetching one1992// value at a time. This copes with the M10S which can only fetch1993// one value at a time1994active_config.fetch_index = -1;19951996uint8_t buf[sizeof(ubx_cfg_valget)+count*sizeof(ConfigKey)];1997struct ubx_cfg_valget msg {};1998if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {1999return false;2000}2001msg.version = 0;2002msg.layers = 0; // ram2003memcpy(buf, &msg, sizeof(msg));2004for (uint8_t i=0; i<count; i++) {2005memcpy(&buf[sizeof(msg)+i*sizeof(ConfigKey)], &list[i].key, sizeof(ConfigKey));2006}2007return _send_message(CLASS_CFG, MSG_CFG_VALGET, buf, sizeof(buf));2008}20092010/*2011* configure F9 based key/value pair for a complete configuration set2012*2013* this method sends all the key/value pairs in a block, but makes no attempt to check2014* the results. Sending in a block is necessary for updates such as GNSS where certain2015* combinations are invalid and setting one at a time will not produce the correct result2016* if the result needs to be validated then a subsequent _configure_config_set() can be2017* issued which will get all the values and reset those that are not properly updated.2018*/2019bool2020AP_GPS_UBLOX::_configure_list_valset(const config_list *list, uint8_t count, uint8_t layers)2021{2022if (!supports_F9_config()) {2023return false;2024}20252026struct ubx_cfg_valset msg {};2027uint8_t buf[sizeof(msg)+sizeof(config_list)*count];2028if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {2029return false;2030}2031msg.version = 1;2032msg.layers = layers;2033msg.transaction = 0;2034uint32_t msg_len = sizeof(msg) - sizeof(msg.key);2035memcpy(buf, &msg, msg_len);20362037uint8_t* payload = &buf[msg_len];2038for (uint8_t i=0; i<count; i++) {2039const uint8_t len = config_key_size(list[i].key);2040memcpy(payload, &list[i].key, sizeof(ConfigKey));2041payload += sizeof(ConfigKey);2042msg_len += sizeof(ConfigKey);2043memcpy(payload, &list[i].value, len);2044payload += len;2045msg_len += len;2046}20472048auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, msg_len);2049return ret;2050}20512052/*2053* save gps configurations to non-volatile memory sent until the call of2054* this message2055*/2056void2057AP_GPS_UBLOX::_save_cfg()2058{2059static const ubx_cfg_cfg save_cfg {2060clearMask: 0,2061saveMask: SAVE_CFG_ALL,2062loadMask: 02063};2064_send_message(CLASS_CFG, MSG_CFG_CFG, &save_cfg, sizeof(save_cfg));2065_last_cfg_sent_time = AP_HAL::millis();2066_num_cfg_save_tries++;2067GCS_SEND_TEXT(MAV_SEVERITY_INFO,2068"GPS %d: u-blox saving config",2069state.instance + 1);2070}20712072/*2073detect a Ublox GPS. Adds one byte, and returns true if the stream2074matches a UBlox2075*/2076bool2077AP_GPS_UBLOX::_detect(struct UBLOX_detect_state &state, uint8_t data)2078{2079reset:2080switch (state.step) {2081case 1:2082if (PREAMBLE2 == data) {2083state.step++;2084break;2085}2086state.step = 0;2087FALLTHROUGH;2088case 0:2089if (PREAMBLE1 == data)2090state.step++;2091break;2092case 2:2093state.step++;2094state.ck_b = state.ck_a = data;2095break;2096case 3:2097state.step++;2098state.ck_b += (state.ck_a += data);2099break;2100case 4:2101state.step++;2102state.ck_b += (state.ck_a += data);2103state.payload_length = data;2104break;2105case 5:2106state.step++;2107state.ck_b += (state.ck_a += data);2108state.payload_counter = 0;2109break;2110case 6:2111state.ck_b += (state.ck_a += data);2112if (++state.payload_counter == state.payload_length)2113state.step++;2114break;2115case 7:2116state.step++;2117if (state.ck_a != data) {2118state.step = 0;2119goto reset;2120}2121break;2122case 8:2123state.step = 0;2124if (state.ck_b == data) {2125// a valid UBlox packet2126return true;2127} else {2128goto reset;2129}2130}2131return false;2132}21332134void2135AP_GPS_UBLOX::_request_version(void)2136{2137_send_message(CLASS_MON, MSG_MON_VER, nullptr, 0);2138}21392140void2141AP_GPS_UBLOX::_configure_rate(void)2142{2143struct ubx_cfg_nav_rate msg;2144// require a minimum measurement rate of 5Hz2145msg.measure_rate_ms = gps.get_rate_ms(state.instance);2146msg.nav_rate = 1;2147msg.timeref = 0; // UTC time2148_send_message(CLASS_CFG, MSG_CFG_RATE, &msg, sizeof(msg));2149}21502151static const char *reasons[] = {"navigation rate",2152"posllh rate",2153"status rate",2154"solution rate",2155"velned rate",2156"dop rate",2157"hw monitor rate",2158"hw2 monitor rate",2159"raw rate",2160"version",2161"navigation settings",2162"GNSS settings",2163"SBAS settings",2164"PVT rate",2165"time pulse settings",2166"TIMEGPS rate",2167"Time mode settings",2168"RTK MB",2169"TIM TM2",2170"F9",2171"M10",2172"L5 Enable Disable"};21732174static_assert((1 << ARRAY_SIZE(reasons)) == CONFIG_LAST, "UBLOX: Missing configuration description");21752176void2177AP_GPS_UBLOX::broadcast_configuration_failure_reason(void) const {2178for (uint8_t i = 0; i < ARRAY_SIZE(reasons); i++) {2179if (_unconfigured_messages & (1 << i)) {2180GCS_SEND_TEXT(MAV_SEVERITY_INFO, "GPS %u: u-blox %s configuration 0x%02x",2181(unsigned int)(state.instance + 1), reasons[i], (unsigned int)_unconfigured_messages);2182break;2183}2184}2185}21862187/*2188return velocity lag in seconds2189*/2190bool AP_GPS_UBLOX::get_lag(float &lag_sec) const2191{2192switch (_hardware_generation) {2193case UBLOX_UNKNOWN_HARDWARE_GENERATION:2194lag_sec = 0.22f;2195// always bail out in this case, it's used to indicate we have yet to receive a valid2196// hardware generation, however the user may have inhibited us detecting the generation2197// so if we aren't allowed to do configuration, we will accept this as the default delay2198return gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE;2199case UBLOX_5:2200case UBLOX_6:2201default:2202lag_sec = 0.22f;2203break;2204case UBLOX_7:2205case UBLOX_M8:2206// based on flight logs the 7 and 8 series seem to produce about 120ms lag2207lag_sec = 0.12f;2208break;2209case UBLOX_F9:2210case UBLOX_M9:2211case UBLOX_M10:2212// F9 lag not verified yet from flight log, but likely to be at least2213// as good as M82214lag_sec = 0.12f;2215#if GPS_MOVING_BASELINE2216if (role == AP_GPS::GPS_ROLE_MB_BASE ||2217role == AP_GPS::GPS_ROLE_MB_ROVER) {2218// the moving baseline rover will lag about 40ms from the2219// base. We need to provide the more conservative value to2220// ensure that the EKF allocates a larger enough buffer2221lag_sec += 0.04;2222}2223#endif2224break;2225};2226return true;2227}22282229#if HAL_LOGGING_ENABLED2230void AP_GPS_UBLOX::Write_AP_Logger_Log_Startup_messages() const2231{2232AP_GPS_Backend::Write_AP_Logger_Log_Startup_messages();22332234if (_have_version) {2235AP::logger().Write_MessageF("u-blox %d HW: %s SW: %s",2236state.instance+1,2237_version.hwVersion,2238_version.swVersion);2239}2240}2241#endif22422243// uBlox specific check_new_itow(), handling message length2244void AP_GPS_UBLOX::_check_new_itow(uint32_t itow)2245{2246check_new_itow(itow, _payload_length + sizeof(ubx_header) + 2);2247}22482249// support for retrieving RTCMv3 data from a moving baseline base2250bool AP_GPS_UBLOX::get_RTCMV3(const uint8_t *&bytes, uint16_t &len)2251{2252#if GPS_MOVING_BASELINE2253if (rtcm3_parser) {2254len = rtcm3_parser->get_len(bytes);2255return len > 0;2256}2257#endif2258return false;2259}22602261// clear previous RTCM3 packet2262void AP_GPS_UBLOX::clear_RTCMV3(void)2263{2264#if GPS_MOVING_BASELINE2265if (rtcm3_parser) {2266rtcm3_parser->clear_packet();2267}2268#endif2269}22702271// ublox specific healthy checks2272bool AP_GPS_UBLOX::is_healthy(void) const2273{2274#if CONFIG_HAL_BOARD == HAL_BOARD_SITL2275if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {2276// allow for fake ublox moving baseline2277return true;2278}2279#endif2280#if GPS_MOVING_BASELINE2281if ((role == AP_GPS::GPS_ROLE_MB_BASE ||2282role == AP_GPS::GPS_ROLE_MB_ROVER) &&2283!supports_F9_config()) {2284// need F9 or above for moving baseline2285return false;2286}2287if (role == AP_GPS::GPS_ROLE_MB_BASE && rtcm3_parser == nullptr && !mb_use_uart2()) {2288// we haven't initialised RTCMv3 parser2289return false;2290}2291#endif2292return true;2293}22942295// populate config_GNSS with F9 GNSS configuration2296uint8_t AP_GPS_UBLOX::populate_F9_gnss(void)2297{2298uint8_t cfg_count = 0;22992300if (params.gnss_mode == 0) {2301_unconfigured_messages &= ~CONFIG_F9;2302last_configured_gnss = params.gnss_mode;2303return 0;2304}23052306if ((_unconfigured_messages & CONFIG_F9) != 0) {2307// ZED-F9P defaults are2308// GPS L1C/A+L2C(ZED)2309// SBAS L1C/A2310// GALILEO E1+E5B(ZED)+E5A(NEO)2311// BEIDOU B1+B2(ZED)+B2A(NEO)2312// QZSS L1C/A+L2C(ZED)+L5(NEO)2313// GLONASS L1+L2(ZED)2314// IMES not supported2315// GPS and QZSS should be enabled/disabled together, but we will leave them alone2316// QZSS and SBAS can only be enabled if GPS is enabled23172318if (config_GNSS == nullptr) {2319config_GNSS = (config_list*)calloc(UBLOX_MAX_GNSS_CONFIG_BLOCKS*3, sizeof(config_list));2320}23212322if (config_GNSS == nullptr) {2323return 0;2324}23252326uint8_t gnss_mode = params.gnss_mode;2327gnss_mode |= 1U<<GNSS_GPS;2328gnss_mode |= 1U<<GNSS_QZSS;2329gnss_mode &= ~(1U<<GNSS_IMES);2330params.gnss_mode.set_and_save(gnss_mode);23312332for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {2333bool ena = gnss_mode & (1U<<i);2334switch (i) {2335case GNSS_SBAS:2336config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_ENA, ena };2337config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_L1CA_ENA, ena };2338break;2339case GNSS_GALILEO:2340config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_ENA, ena };2341config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E1_ENA, ena };2342if (_hardware_variant == UBLOX_F9_ZED) {2343config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5B_ENA, ena };2344} else {2345config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5A_ENA, ena };2346}2347break;2348case GNSS_BEIDOU:2349config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_ENA, ena };2350config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B1_ENA, ena };2351if (_hardware_variant == UBLOX_F9_ZED) {2352config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2_ENA, ena };2353} else {2354config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2A_ENA, ena };2355}2356break;2357case GNSS_GLONASS:2358config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_ENA, ena };2359config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L1_ENA, ena };2360if (_hardware_variant == UBLOX_F9_ZED) {2361config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L2_ENA, ena };2362}2363break;2364// not supported or leave alone2365case GNSS_IMES:2366case GNSS_QZSS:2367case GNSS_GPS:2368break;2369}2370}2371}23722373last_configured_gnss = params.gnss_mode;23742375return cfg_count;2376}23772378// return true if GPS is capable of F9 config2379bool AP_GPS_UBLOX::supports_F9_config(void) const2380{2381return _hardware_generation == UBLOX_F9 || _hardware_generation == UBLOX_M10;2382}23832384// return true if GPS is capable of F9 config2385bool AP_GPS_UBLOX::is_gnss_key(ConfigKey key) const2386{2387return (unsigned(key) & 0xFFFF0000) == 0x10310000;2388}23892390#endif239123922393