diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6c6cdba6de..abb152f55d 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -221,6 +221,20 @@ uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz return (payload_size + 10); } +// Create our custom CAS packet, with specified Dynamic Mode from GPS profile +uint8_t GPS::makeCASNAVXPacket(uint8_t dynamicMode) +{ + // Base configuration (mutable copy of _message_CAS_CFG_NAVX_CONF) + static uint8_t config[sizeof(_message_CAS_CFG_NAVX_CONF)]; + memcpy(config, _message_CAS_CFG_NAVX_CONF, sizeof(_message_CAS_CFG_NAVX_CONF)); + + // Update Dynamic Mode (index 4) + config[4] = dynamicMode; + + // Create CAS packet with updated configuration + return makeCASPacket(0x06, 0x07, sizeof(config), config); +} + GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis) { uint8_t buffer[768] = {0}; @@ -535,8 +549,21 @@ bool GPS::setup() // only ask for RMC and GGA _serial_gps->write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n"); delay(250); - // Switch to Vehicle Mode, since SoftRF enables Aviation < 2g - _serial_gps->write("$PCAS11,3*1E\r\n"); + + // Set Dynamic Mode based on GPS Profile + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$PCAS11,1*1C\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) { + _serial_gps->write("$PCAS11,2*1F\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) { + _serial_gps->write("$PCAS11,3*1E\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) { + _serial_gps->write("$PCAS11,6*1B\r\n"); + } else { + // Default to pedestrian mode + _serial_gps->write("$PCAS11,2*1F\r\n"); + } + delay(250); } else if (gnssModel == GNSS_MODEL_MTK_L76B) { // Waveshare Pico-GPS hat uses the L76B with 9600 baud @@ -555,8 +582,19 @@ bool GPS::setup() // Enable PPS for 2D/3D fix only _serial_gps->write("$PMTK285,3,100*3F\r\n"); delay(250); - // Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s) - _serial_gps->write("$PMTK886,1*29\r\n"); + // Set Dynamic Mode based on GPS Profile + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$PMTK886,1*29\r\n"); // No fixed mode, so limit to pedestrian. + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) { + _serial_gps->write("$PMTK886,1*29\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) { + _serial_gps->write("$PMTK886,0*28\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) { + _serial_gps->write("$PMTK886,3*2B\r\n"); // technically balloon mode, since Aviation mode has 10 ft cieling + } else { + // Default to Fitness Mode, for running and walking purpose with low speed (<5 m/s) + _serial_gps->write("$PMTK886,1*29\r\n"); + } delay(250); } else if (gnssModel == GNSS_MODEL_MTK_PA1010D) { // PA1010D is used in the Pimoroni GPS board. @@ -571,6 +609,13 @@ bool GPS::setup() // Enable SBAS / WAAS _serial_gps->write("$PMTK301,2*2E\r\n"); delay(250); + // PA1010D doesn't support dynamic modes, + // we set FIXED_POSITION so it doesn't change anything if speed < 2 m/s (maximum value supported) + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$PMTK386,2.0*3F\r\n"); + } else { + _serial_gps->write("$PMTK386,0.0*3C\r\n"); // turn off if we're not in fixed position mode + } } else if (gnssModel == GNSS_MODEL_MTK_PA1616S) { // PA1616S is used in some GPS breakout boards from Adafruit // PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here. @@ -583,9 +628,28 @@ bool GPS::setup() // Enable SBAS / WAAS _serial_gps->write("$PMTK301,2*2E\r\n"); delay(250); + // PA1616S doesn't support dynamic modes, + // we set FIXED_POSITION so it doesn't change anything if speed < 2 m/s (maximum value supported) + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$PMTK386,2.0*3F\r\n"); + } else { + _serial_gps->write("$PMTK386,0.0*3C\r\n"); // turn off if we're not in fixed position mode + } + } else if (gnssModel == GNSS_MODEL_ATGM336H) { // Set the intial configuration of the device - these _should_ work for most AT6558 devices - msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF); + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + msglen = makeCASNAVXPacket(0x01); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) { + msglen = makeCASNAVXPacket(0x02); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) { + msglen = makeCASNAVXPacket(0x03); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) { + msglen = makeCASNAVXPacket(0x06); // Airborne <2g acceleration. OK for jets, need 0x07 for rockets. + } else { + // Default to pedestrian mode + msglen = makeCASNAVXPacket(0x02); + } _serial_gps->write(UBXscratch, msglen); if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) { LOG_WARN("ATGM336H: Could not set Config"); @@ -629,6 +693,13 @@ bool GPS::setup() delay(250); _serial_gps->write("$CFGMSG,6,1,0\r\n"); delay(250); + // UC6580 doesn't support dynamic modes (UM620 does - but we've never seen it in the wild), + // we set FIXED_POSITION so it doesn't change anything if speed < 31500 cm/s (maximum value supported) + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$CFGDYN,h11,0,51500\r\n"); + } else { + _serial_gps->write("$CFGDYN,h11,0,0\r\n"); // turn off if we're not in fixed position mode + } } else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) { if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN || @@ -652,6 +723,21 @@ bool GPS::setup() delay(250); _serial_gps->write("$PAIR513*3D\r\n"); // save configuration + + // Set Dynamic Mode based on GPS Profile + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) { + _serial_gps->write("$PAIR080,4*2A\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) { + _serial_gps->write("$PAIR080,1*2F\r\n"); + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) { + _serial_gps->write("$PAIR080,0*2E\r\n"); // technically 'normal' mode + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) { + _serial_gps->write("$PAIR080,3*2D\r\n"); // technically balloon mode + } else { + // Default to pedestrian mode + _serial_gps->write("$PAIR080,1*2F\r\n"); + } + } else if (gnssModel == GNSS_MODEL_UBLOX6) { clearBuffer(); SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500); diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 8ba1ce0a60..8986430180 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -201,6 +201,7 @@ class GPS : private concurrency::OSThread // Create a ublox packet for editing in memory uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg); + uint8_t makeCASNAVXPacket(uint8_t dynamicMode); // scratch space for creating ublox packets uint8_t UBXscratch[250] = {0}; diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp index df9aece0ad..d0fdd81302 100644 --- a/src/mesh/NodeDB.cpp +++ b/src/mesh/NodeDB.cpp @@ -623,28 +623,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) config.security.private_key.size = 0; } config.security.public_key.size = 0; -#ifdef PIN_GPS_EN - config.position.gps_en_gpio = PIN_GPS_EN; -#endif -#if defined(USERPREFS_CONFIG_GPS_MODE) - config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE; -#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; -#elif !defined(GPS_RX_PIN) - if (config.position.rx_gpio == 0) - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; - else - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; -#else - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; -#endif -#ifdef USERPREFS_CONFIG_SMART_POSITION_ENABLED - config.position.position_broadcast_smart_enabled = USERPREFS_CONFIG_SMART_POSITION_ENABLED; -#else - config.position.position_broadcast_smart_enabled = true; -#endif - config.position.broadcast_smart_minimum_distance = 100; - config.position.broadcast_smart_minimum_interval_secs = 30; + if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER) config.device.node_info_broadcast_secs = default_node_info_broadcast_secs; config.security.serial_enabled = true; @@ -691,12 +670,6 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) config.bluetooth.mode = hasScreen ? meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN : meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; #endif - // for backward compat, default position flags are ALT+MSL - config.position.position_flags = - (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL | - meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING | - meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW); - // Set default value for 'Mesh via UDP' #if HAS_UDP_MULTICAST #ifdef USERPREFS_NETWORK_ENABLED_PROTOCOLS @@ -746,21 +719,11 @@ void NodeDB::installDefaultConfig(bool preserveKey = false) #endif initConfigIntervals(); + initConfigGPS(); } void NodeDB::initConfigIntervals() { -#ifdef USERPREFS_CONFIG_GPS_UPDATE_INTERVAL - config.position.gps_update_interval = USERPREFS_CONFIG_GPS_UPDATE_INTERVAL; -#else - config.position.gps_update_interval = default_gps_update_interval; -#endif -#ifdef USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL - config.position.position_broadcast_secs = USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL; -#else - config.position.position_broadcast_secs = default_broadcast_interval_secs; -#endif - config.power.ls_secs = default_ls_secs; config.power.min_wake_secs = default_min_wake_secs; config.power.sds_secs = default_sds_secs; @@ -775,6 +738,77 @@ void NodeDB::initConfigIntervals() #endif } +void NodeDB::initConfigGPS() +{ +#ifdef PIN_GPS_EN + config.position.gps_en_gpio = PIN_GPS_EN; +#endif +#if defined(USERPREFS_CONFIG_GPS_MODE) + config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE; +#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; +#elif !defined(GPS_RX_PIN) + if (config.position.rx_gpio == 0) + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT; + else + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; +#else + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; +#endif +#ifdef USERPREFS_CONFIG_SMART_POSITION_ENABLED + config.position.position_broadcast_smart_enabled = USERPREFS_CONFIG_SMART_POSITION_ENABLED; +#else + config.position.position_broadcast_smart_enabled = true; +#endif + + const uint32_t base_flags = + (meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW | + meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE); + + if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION || + IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE, + meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) { + config.position.position_flags = base_flags; + config.position.position_broadcast_smart_enabled = false; + config.position.position_broadcast_secs = ONE_DAY / 2; + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) { + config.position.position_flags = (base_flags | meshtastic_Config_PositionConfig_PositionFlags_SPEED | + meshtastic_Config_PositionConfig_PositionFlags_HEADING); + config.position.broadcast_smart_minimum_distance = 50; + config.position.broadcast_smart_minimum_interval_secs = 60; + config.position.position_broadcast_secs = 900; + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) { + + config.position.position_flags = (base_flags | meshtastic_Config_PositionConfig_PositionFlags_SPEED | + meshtastic_Config_PositionConfig_PositionFlags_HEADING); + config.position.broadcast_smart_minimum_distance = 500; + config.position.broadcast_smart_minimum_interval_secs = 120; + config.position.position_broadcast_secs = 900; + } else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) { + config.position.position_flags = + (base_flags | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL | + meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION | + meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING); + config.position.broadcast_smart_minimum_distance = 200; + config.position.broadcast_smart_minimum_interval_secs = 60; + config.position.position_broadcast_secs = 900; + + } else { // original settings + config.position.position_flags = + (base_flags | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL | + meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING); + config.position.broadcast_smart_minimum_distance = 100; + config.position.broadcast_smart_minimum_interval_secs = 30; + config.position.position_broadcast_secs = 900; + } +#ifdef USERPREFS_CONFIG_GPS_UPDATE_INTERVAL + config.position.gps_update_interval = USERPREFS_CONFIG_GPS_UPDATE_INTERVAL; +#endif +#ifdef USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL + config.position.position_broadcast_secs = USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL; +#endif +} + void NodeDB::installDefaultModuleConfig() { LOG_INFO("Install default ModuleConfig"); @@ -905,6 +939,7 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role) if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) { initConfigIntervals(); initModuleConfigIntervals(); + initConfigGPS(); moduleConfig.telemetry.device_update_interval = default_telemetry_broadcast_interval_secs; config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY; owner.has_is_unmessagable = true; @@ -1214,11 +1249,10 @@ void NodeDB::loadFromDisk() &meshtastic_DeviceState_msg, &devicestate); // See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786 - // It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of our - // critical config may still be valid (in the other files - loaded next). - // Also, if we did fail on reading we probably failed on the enormous (and non critical) nodeDB. So DO NOT install default - // device state. - // if (state != LoadFileResult::LOAD_SUCCESS) { + // It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of + // our critical config may still be valid (in the other files - loaded next). Also, if we did fail on reading we probably + // failed on the enormous (and non critical) nodeDB. So DO NOT install default device state. if (state != + // LoadFileResult::LOAD_SUCCESS) { // installDefaultDeviceState(); // Our in RAM copy might now be corrupt //} else { if ((state != LoadFileResult::LOAD_SUCCESS) || (devicestate.version < DEVICESTATE_MIN_VER)) { @@ -1829,8 +1863,8 @@ bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p) if (seenFrom && seenTo) return false; // we've seen both, and neither is a favorite, so we can stop searching early - // Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching - // all favorited nodes first. + // Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after + // searching all favorited nodes first. } return false; diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h index e8724f2c95..06e4ec31b4 100644 --- a/src/mesh/NodeDB.h +++ b/src/mesh/NodeDB.h @@ -229,7 +229,7 @@ class NodeDB */ size_t getNumOnlineMeshNodes(bool localOnly = false); - void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(NodeNum nodeNum); + void initConfigIntervals(), initModuleConfigIntervals(), initConfigGPS(), resetNodes(), removeNodeByNum(NodeNum nodeNum); bool factoryReset(bool eraseBleBonds = false);