Skip to content

Commit c8cd69e

Browse files
committed
Add GPS Profile support.
This patch makes two major changes to address the current complexity of GPS Configuration. GpsProfile, a new protobuf we use to configure a pre-tuned selection of GPS settings. The aim is to replace the current 5 configuration options with a single user-friendly label that is tailored to real-world Meshtastic use cases. Based on a users selection of pedestrian, vehicle, or fixed position, we apply different settings to GPS Update Interval, Position Broadcast Interval, Smart Broadcast Min Interval, Smart Broadcast Min Distance, and Position Flags in firmware. This should allow the apps to drastically simplify the position configuration shown to users. In this patch we take advantage of the profie to support new features in firmware to improve the accuracy of our position information. For example, taking advantage of advanced features in GPS hardware specific to each case. Because of this, some erroneous positions will be discarded based on physically impossible movement. More information about the selections for each use case is available in this [google sheet](https://docs.google.com/spreadsheets/d/1-f9z5zx2VCYqE6ivYXm-XqtLeVZSGMKpC3G3wma6-vw/edit?gid=633840990#gid=633840990) As part of the config screen simplication, this patch deprecates PositionFlags, which will now be selected in firmware based on the GPS Profile. PositionFlags has long been seen as a complicated and confusing burden in the apps. We determined that very few users use them, with the majority happy with the default set, and some of the 'advanced' flags don't even work. Depends on: meshtastic/protobufs#749
1 parent 718fd11 commit c8cd69e

File tree

4 files changed

+173
-52
lines changed

4 files changed

+173
-52
lines changed

src/gps/GPS.cpp

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,20 @@ uint8_t GPS::makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_siz
221221
return (payload_size + 10);
222222
}
223223

224+
// Create our custom CAS packet, with specified Dynamic Mode from GPS profile
225+
uint8_t GPS::makeCASNAVXPacket(uint8_t dynamicMode)
226+
{
227+
// Base configuration (mutable copy of _message_CAS_CFG_NAVX_CONF)
228+
static uint8_t config[sizeof(_message_CAS_CFG_NAVX_CONF)];
229+
memcpy(config, _message_CAS_CFG_NAVX_CONF, sizeof(_message_CAS_CFG_NAVX_CONF));
230+
231+
// Update Dynamic Mode (index 4)
232+
config[4] = dynamicMode;
233+
234+
// Create CAS packet with updated configuration
235+
return makeCASPacket(0x06, 0x07, sizeof(config), config);
236+
}
237+
224238
GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
225239
{
226240
uint8_t buffer[768] = {0};
@@ -535,8 +549,21 @@ bool GPS::setup()
535549
// only ask for RMC and GGA
536550
_serial_gps->write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
537551
delay(250);
538-
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
539-
_serial_gps->write("$PCAS11,3*1E\r\n");
552+
553+
// Set Dynamic Mode based on GPS Profile
554+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
555+
_serial_gps->write("$PCAS11,1*1C\r\n");
556+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) {
557+
_serial_gps->write("$PCAS11,2*1F\r\n");
558+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) {
559+
_serial_gps->write("$PCAS11,3*1E\r\n");
560+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) {
561+
_serial_gps->write("$PCAS11,6*1B\r\n");
562+
} else {
563+
// Default to pedestrian mode
564+
_serial_gps->write("$PCAS11,2*1F\r\n");
565+
}
566+
540567
delay(250);
541568
} else if (gnssModel == GNSS_MODEL_MTK_L76B) {
542569
// Waveshare Pico-GPS hat uses the L76B with 9600 baud
@@ -555,8 +582,19 @@ bool GPS::setup()
555582
// Enable PPS for 2D/3D fix only
556583
_serial_gps->write("$PMTK285,3,100*3F\r\n");
557584
delay(250);
558-
// Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
559-
_serial_gps->write("$PMTK886,1*29\r\n");
585+
// Set Dynamic Mode based on GPS Profile
586+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
587+
_serial_gps->write("$PMTK886,1*29\r\n"); // No fixed mode, so limit to pedestrian.
588+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) {
589+
_serial_gps->write("$PMTK886,1*29\r\n");
590+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) {
591+
_serial_gps->write("$PMTK886,0*28\r\n");
592+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) {
593+
_serial_gps->write("$PMTK886,3*2B\r\n"); // technically balloon mode, since Aviation mode has 10 ft cieling
594+
} else {
595+
// Default to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
596+
_serial_gps->write("$PMTK886,1*29\r\n");
597+
}
560598
delay(250);
561599
} else if (gnssModel == GNSS_MODEL_MTK_PA1010D) {
562600
// PA1010D is used in the Pimoroni GPS board.
@@ -571,6 +609,13 @@ bool GPS::setup()
571609
// Enable SBAS / WAAS
572610
_serial_gps->write("$PMTK301,2*2E\r\n");
573611
delay(250);
612+
// PA1010D doesn't support dynamic modes,
613+
// we set FIXED_POSITION so it doesn't change anything if speed < 2 m/s (maximum value supported)
614+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
615+
_serial_gps->write("$PMTK386,2.0*3F\r\n");
616+
} else {
617+
_serial_gps->write("$PMTK386,0.0*3C\r\n"); // turn off if we're not in fixed position mode
618+
}
574619
} else if (gnssModel == GNSS_MODEL_MTK_PA1616S) {
575620
// PA1616S is used in some GPS breakout boards from Adafruit
576621
// PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here.
@@ -583,9 +628,28 @@ bool GPS::setup()
583628
// Enable SBAS / WAAS
584629
_serial_gps->write("$PMTK301,2*2E\r\n");
585630
delay(250);
631+
// PA1616S doesn't support dynamic modes,
632+
// we set FIXED_POSITION so it doesn't change anything if speed < 2 m/s (maximum value supported)
633+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
634+
_serial_gps->write("$PMTK386,2.0*3F\r\n");
635+
} else {
636+
_serial_gps->write("$PMTK386,0.0*3C\r\n"); // turn off if we're not in fixed position mode
637+
}
638+
586639
} else if (gnssModel == GNSS_MODEL_ATGM336H) {
587640
// Set the intial configuration of the device - these _should_ work for most AT6558 devices
588-
msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF);
641+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
642+
msglen = makeCASNAVXPacket(0x01);
643+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) {
644+
msglen = makeCASNAVXPacket(0x02);
645+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) {
646+
msglen = makeCASNAVXPacket(0x03);
647+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) {
648+
msglen = makeCASNAVXPacket(0x06); // Airborne <2g acceleration. OK for jets, need 0x07 for rockets.
649+
} else {
650+
// Default to pedestrian mode
651+
msglen = makeCASNAVXPacket(0x02);
652+
}
589653
_serial_gps->write(UBXscratch, msglen);
590654
if (getACKCas(0x06, 0x07, 250) != GNSS_RESPONSE_OK) {
591655
LOG_WARN("ATGM336H: Could not set Config");
@@ -629,6 +693,13 @@ bool GPS::setup()
629693
delay(250);
630694
_serial_gps->write("$CFGMSG,6,1,0\r\n");
631695
delay(250);
696+
// UC6580 doesn't support dynamic modes (UM620 does - but we've never seen it in the wild),
697+
// we set FIXED_POSITION so it doesn't change anything if speed < 31500 cm/s (maximum value supported)
698+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
699+
_serial_gps->write("$CFGDYN,h11,0,51500\r\n");
700+
} else {
701+
_serial_gps->write("$CFGDYN,h11,0,0\r\n"); // turn off if we're not in fixed position mode
702+
}
632703
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
633704

634705
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN ||
@@ -652,6 +723,21 @@ bool GPS::setup()
652723

653724
delay(250);
654725
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
726+
727+
// Set Dynamic Mode based on GPS Profile
728+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION) {
729+
_serial_gps->write("$PAIR080,4*2A\r\n");
730+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) {
731+
_serial_gps->write("$PAIR080,1*2F\r\n");
732+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) {
733+
_serial_gps->write("$PAIR080,0*2E\r\n"); // technically 'normal' mode
734+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) {
735+
_serial_gps->write("$PAIR080,3*2D\r\n"); // technically balloon mode
736+
} else {
737+
// Default to pedestrian mode
738+
_serial_gps->write("$PAIR080,1*2F\r\n");
739+
}
740+
655741
} else if (gnssModel == GNSS_MODEL_UBLOX6) {
656742
clearBuffer();
657743
SEND_UBX_PACKET(0x06, 0x02, _message_DISABLE_TXT_INFO, "disable text info messages", 500);

src/gps/GPS.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class GPS : private concurrency::OSThread
201201
// Create a ublox packet for editing in memory
202202
uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
203203
uint8_t makeCASPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
204+
uint8_t makeCASNAVXPacket(uint8_t dynamicMode);
204205

205206
// scratch space for creating ublox packets
206207
uint8_t UBXscratch[250] = {0};

src/mesh/NodeDB.cpp

Lines changed: 80 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -623,28 +623,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
623623
config.security.private_key.size = 0;
624624
}
625625
config.security.public_key.size = 0;
626-
#ifdef PIN_GPS_EN
627-
config.position.gps_en_gpio = PIN_GPS_EN;
628-
#endif
629-
#if defined(USERPREFS_CONFIG_GPS_MODE)
630-
config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE;
631-
#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT
632-
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
633-
#elif !defined(GPS_RX_PIN)
634-
if (config.position.rx_gpio == 0)
635-
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
636-
else
637-
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
638-
#else
639-
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
640-
#endif
641-
#ifdef USERPREFS_CONFIG_SMART_POSITION_ENABLED
642-
config.position.position_broadcast_smart_enabled = USERPREFS_CONFIG_SMART_POSITION_ENABLED;
643-
#else
644-
config.position.position_broadcast_smart_enabled = true;
645-
#endif
646-
config.position.broadcast_smart_minimum_distance = 100;
647-
config.position.broadcast_smart_minimum_interval_secs = 30;
626+
648627
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER)
649628
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
650629
config.security.serial_enabled = true;
@@ -691,12 +670,6 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
691670
config.bluetooth.mode = hasScreen ? meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN
692671
: meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
693672
#endif
694-
// for backward compat, default position flags are ALT+MSL
695-
config.position.position_flags =
696-
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
697-
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
698-
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
699-
700673
// Set default value for 'Mesh via UDP'
701674
#if HAS_UDP_MULTICAST
702675
#ifdef USERPREFS_NETWORK_ENABLED_PROTOCOLS
@@ -746,21 +719,11 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
746719
#endif
747720

748721
initConfigIntervals();
722+
initConfigGPS();
749723
}
750724

751725
void NodeDB::initConfigIntervals()
752726
{
753-
#ifdef USERPREFS_CONFIG_GPS_UPDATE_INTERVAL
754-
config.position.gps_update_interval = USERPREFS_CONFIG_GPS_UPDATE_INTERVAL;
755-
#else
756-
config.position.gps_update_interval = default_gps_update_interval;
757-
#endif
758-
#ifdef USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL
759-
config.position.position_broadcast_secs = USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL;
760-
#else
761-
config.position.position_broadcast_secs = default_broadcast_interval_secs;
762-
#endif
763-
764727
config.power.ls_secs = default_ls_secs;
765728
config.power.min_wake_secs = default_min_wake_secs;
766729
config.power.sds_secs = default_sds_secs;
@@ -775,6 +738,77 @@ void NodeDB::initConfigIntervals()
775738
#endif
776739
}
777740

741+
void NodeDB::initConfigGPS()
742+
{
743+
#ifdef PIN_GPS_EN
744+
config.position.gps_en_gpio = PIN_GPS_EN;
745+
#endif
746+
#if defined(USERPREFS_CONFIG_GPS_MODE)
747+
config.position.gps_mode = USERPREFS_CONFIG_GPS_MODE;
748+
#elif !HAS_GPS || GPS_DEFAULT_NOT_PRESENT
749+
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
750+
#elif !defined(GPS_RX_PIN)
751+
if (config.position.rx_gpio == 0)
752+
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT;
753+
else
754+
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
755+
#else
756+
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
757+
#endif
758+
#ifdef USERPREFS_CONFIG_SMART_POSITION_ENABLED
759+
config.position.position_broadcast_smart_enabled = USERPREFS_CONFIG_SMART_POSITION_ENABLED;
760+
#else
761+
config.position.position_broadcast_smart_enabled = true;
762+
#endif
763+
764+
const uint32_t base_flags =
765+
(meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW |
766+
meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE);
767+
768+
if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_FIXED_POSITION ||
769+
IS_ONE_OF(config.device.role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE,
770+
meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
771+
config.position.position_flags = base_flags;
772+
config.position.position_broadcast_smart_enabled = false;
773+
config.position.position_broadcast_secs = ONE_DAY / 2;
774+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_PEDESTRIAN) {
775+
config.position.position_flags = (base_flags | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
776+
meshtastic_Config_PositionConfig_PositionFlags_HEADING);
777+
config.position.broadcast_smart_minimum_distance = 50;
778+
config.position.broadcast_smart_minimum_interval_secs = 60;
779+
config.position.position_broadcast_secs = 900;
780+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_VEHICLE) {
781+
782+
config.position.position_flags = (base_flags | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
783+
meshtastic_Config_PositionConfig_PositionFlags_HEADING);
784+
config.position.broadcast_smart_minimum_distance = 500;
785+
config.position.broadcast_smart_minimum_interval_secs = 120;
786+
config.position.position_broadcast_secs = 900;
787+
} else if (config.position.gps_profile == meshtastic_Config_PositionConfig_GpsProfile_AIRBORNE) {
788+
config.position.position_flags =
789+
(base_flags | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
790+
meshtastic_Config_PositionConfig_PositionFlags_GEOIDAL_SEPARATION |
791+
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING);
792+
config.position.broadcast_smart_minimum_distance = 200;
793+
config.position.broadcast_smart_minimum_interval_secs = 60;
794+
config.position.position_broadcast_secs = 900;
795+
796+
} else { // original settings
797+
config.position.position_flags =
798+
(base_flags | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
799+
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING);
800+
config.position.broadcast_smart_minimum_distance = 100;
801+
config.position.broadcast_smart_minimum_interval_secs = 30;
802+
config.position.position_broadcast_secs = 900;
803+
}
804+
#ifdef USERPREFS_CONFIG_GPS_UPDATE_INTERVAL
805+
config.position.gps_update_interval = USERPREFS_CONFIG_GPS_UPDATE_INTERVAL;
806+
#endif
807+
#ifdef USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL
808+
config.position.position_broadcast_secs = USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL;
809+
#endif
810+
}
811+
778812
void NodeDB::installDefaultModuleConfig()
779813
{
780814
LOG_INFO("Install default ModuleConfig");
@@ -905,6 +939,7 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
905939
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
906940
initConfigIntervals();
907941
initModuleConfigIntervals();
942+
initConfigGPS();
908943
moduleConfig.telemetry.device_update_interval = default_telemetry_broadcast_interval_secs;
909944
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
910945
owner.has_is_unmessagable = true;
@@ -1214,11 +1249,10 @@ void NodeDB::loadFromDisk()
12141249
&meshtastic_DeviceState_msg, &devicestate);
12151250

12161251
// See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786
1217-
// It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of our
1218-
// critical config may still be valid (in the other files - loaded next).
1219-
// Also, if we did fail on reading we probably failed on the enormous (and non critical) nodeDB. So DO NOT install default
1220-
// device state.
1221-
// if (state != LoadFileResult::LOAD_SUCCESS) {
1252+
// It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of
1253+
// our critical config may still be valid (in the other files - loaded next). Also, if we did fail on reading we probably
1254+
// failed on the enormous (and non critical) nodeDB. So DO NOT install default device state. if (state !=
1255+
// LoadFileResult::LOAD_SUCCESS) {
12221256
// installDefaultDeviceState(); // Our in RAM copy might now be corrupt
12231257
//} else {
12241258
if ((state != LoadFileResult::LOAD_SUCCESS) || (devicestate.version < DEVICESTATE_MIN_VER)) {
@@ -1829,8 +1863,8 @@ bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
18291863
if (seenFrom && seenTo)
18301864
return false; // we've seen both, and neither is a favorite, so we can stop searching early
18311865

1832-
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching
1833-
// all favorited nodes first.
1866+
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after
1867+
// searching all favorited nodes first.
18341868
}
18351869

18361870
return false;

src/mesh/NodeDB.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ class NodeDB
229229
*/
230230
size_t getNumOnlineMeshNodes(bool localOnly = false);
231231

232-
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(NodeNum nodeNum);
232+
void initConfigIntervals(), initModuleConfigIntervals(), initConfigGPS(), resetNodes(), removeNodeByNum(NodeNum nodeNum);
233233

234234
bool factoryReset(bool eraseBleBonds = false);
235235

0 commit comments

Comments
 (0)