diff --git a/Firmware/RTK_Everywhere/AP-Config/src/main.js b/Firmware/RTK_Everywhere/AP-Config/src/main.js
index c8e1c761a..24fa64a56 100644
--- a/Firmware/RTK_Everywhere/AP-Config/src/main.js
+++ b/Firmware/RTK_Everywhere/AP-Config/src/main.js
@@ -103,11 +103,13 @@ function parseIncoming(msg) {
show("ppConfig");
show("ethernetConfig");
show("ntpConfig");
- hide("portsConfig");
+ show("portsConfig");
+ hide("externalPortOptions");
show("logToSDCard");
hide("galileoHasSetting");
hide("tiltConfig");
hide("beeperControl");
+ show("useAssistNowCheckbox");
}
else if (platformPrefix == "Facet v2") {
show("baseConfig");
@@ -115,11 +117,12 @@ function parseIncoming(msg) {
hide("ethernetConfig");
hide("ntpConfig");
show("portsConfig");
- hide("noExternalPortOptions");
+ show("externalPortOptions");
show("logToSDCard");
hide("galileoHasSetting");
hide("tiltConfig");
hide("beeperControl");
+ show("useAssistNowCheckbox");
}
else if (platformPrefix == "Facet mosaic") {
show("baseConfig");
@@ -127,10 +130,11 @@ function parseIncoming(msg) {
hide("ethernetConfig");
hide("ntpConfig");
show("portsConfig");
- hide("noExternalPortOptions");
+ show("externalPortOptions");
show("logToSDCard");
hide("tiltConfig");
hide("beeperControl");
+ hide("useAssistNowCheckbox");
}
else if (platformPrefix == "Torch") {
show("baseConfig");
@@ -138,14 +142,14 @@ function parseIncoming(msg) {
hide("ethernetConfig");
hide("ntpConfig");
show("portsConfig");
-
hide("externalPortOptions");
- show("noExternalPortOptions");
hide("logToSDCard");
hide("constellationSbas"); //Not supported on UM980
+ show("useAssistNowCheckbox"); //Does the PPL use MGA? Not sure...
+
select = ge("dynamicModel");
let newOption = new Option('Survey', '0');
select.add(newOption, undefined);
@@ -435,6 +439,7 @@ function parseIncoming(msg) {
ge("enableARPLogging").dispatchEvent(new CustomEvent('change'));
ge("enableAutoFirmwareUpdate").dispatchEvent(new CustomEvent('change'));
ge("enableAutoReset").dispatchEvent(new CustomEvent('change'));
+ ge("useLocalizedDistribution").dispatchEvent(new CustomEvent('change'));
updateECEFList();
updateGeodeticList();
@@ -1384,6 +1389,15 @@ document.addEventListener("DOMContentLoaded", (event) => {
}
});
+ ge("useLocalizedDistribution").addEventListener("change", function () {
+ if (ge("useLocalizedDistribution").checked) {
+ show("localizedDistributionTileLevelDropdown");
+ }
+ else {
+ hide("localizedDistributionTileLevelDropdown");
+ }
+ });
+
ge("enableExternalPulse").addEventListener("change", function () {
if (ge("enableExternalPulse").checked == true) {
show("externalPulseConfigDetails");
diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino
index e2ef52f87..434f193fb 100644
--- a/Firmware/RTK_Everywhere/MQTT_Client.ino
+++ b/Firmware/RTK_Everywhere/MQTT_Client.ino
@@ -60,19 +60,20 @@ MQTT_Client.ino
------------------------------------------------------------------------------*/
-// TODO
-// We should restructure this.
-// The hard-coded MQTT_CLIENT_SUBSCRIBE_KEY/SPARTN/ASSIST states are problematic.
-// We should have a vector of topics to be subscribed to.
-// Code will add/erase topics to/from the vector as needed.
-// If the MQTT Client is OFF and it sees that the vector has at least one entry,
-// the client starts, connects and subscribes.
-// While connected and subscribed, the MQTT Client keeps checking the vector.
-// Topics which are subscribed to but are no longer in the vector are unsubscribed.
-// If the vector becomes empty, the MQTT Client disconnects and goes back to OFF.
-// This would make it a lot easier to: only subscribe to the key topic when needed;
-// subscribe to the localized distribution topic. See #150
-// (This is how Michael's hpg does it...)
+// How this works:
+// There are two vectors of topics:
+// mqttSubscribeTopics contains the topics we should be subscribed to
+// mqttClientSubscribedTopics contains the topics we are currently subscribed to
+// While connected, the mqttClientUpdate state machine compares mqttClientSubscribedTopics to mqttSubscribeTopics
+// New topics on mqttSubscribeTopics are subscribed to one at a time (one topic per call of mqttClientUpdate)
+// (this will make things easier for cellular - the LARA-R6 can only subscribe to one topic at once)
+// Topics no longer on mqttSubscribeTopics are unsubscribed one at a time (one topic per call of mqttClientUpdate)
+// (ditto)
+// Initially we subscribe to the key distribution topic and the continental correction topic (if available)
+// If enabled, we also subscribe to the AssistNow MGA topic
+// If localized distribution is enabled and we have a 3D fix, we subscribe to the dict topic
+// When the dict is received, we subscribe to the nearest localized topic and unsubscribe from the continental topic
+// When the AssistNow MGA data arrives, we unsubscribe and subscribe to AssistNow updates
#ifdef COMPILE_MQTT_CLIENT
@@ -94,9 +95,6 @@ enum MQTTClientState
MQTT_CLIENT_ON, // WIFI_STATE_START state
MQTT_CLIENT_NETWORK_STARTED, // Connecting to WiFi access point or Ethernet
MQTT_CLIENT_CONNECTING_2_SERVER, // Connecting to the MQTT server
- MQTT_CLIENT_SUBSCRIBE_KEY, // Subscribe to the MQTT_TOPIC_KEY
- MQTT_CLIENT_SUBSCRIBE_SPARTN, // Subscribe to the MQTT_TOPIC_SPARTN
- MQTT_CLIENT_SUBSCRIBE_ASSIST, // Subscribe to the MQTT_TOPIC_ASSISTNOW
MQTT_CLIENT_SERVICES_CONNECTED, // Connected to the MQTT services
// Insert new states here
MQTT_CLIENT_STATE_MAX // Last entry in the state list
@@ -107,9 +105,6 @@ const char *const mqttClientStateName[] = {
"MQTT_CLIENT_ON",
"MQTT_CLIENT_NETWORK_STARTED",
"MQTT_CLIENT_CONNECTING_2_SERVER",
- "MQTT_CLIENT_SUBSCRIBE_KEY",
- "MQTT_CLIENT_SUBSCRIBE_SPARTN",
- "MQTT_CLIENT_SUBSCRIBE_ASSIST",
"MQTT_CLIENT_SERVICES_CONNECTED",
};
@@ -117,13 +112,21 @@ const int mqttClientStateNameEntries = sizeof(mqttClientStateName) / sizeof(mqtt
const RtkMode_t mqttClientMode = RTK_MODE_ROVER | RTK_MODE_BASE_SURVEY_IN;
-const char MQTT_TOPIC_ASSISTNOW[] = "/pp/ubx/mga"; // AssistNow (MGA) topic
+const String MQTT_TOPIC_ASSISTNOW = "/pp/ubx/mga"; // AssistNow (MGA) topic
+const String MQTT_TOPIC_ASSISTNOW_UPDATES = "/pp/ubx/mga/updates"; // AssistNow (MGA) topic - updates only
// Note: the key and correction topics are now stored in settings - extracted from ZTP
+const char localizedPrefix[] = "pp/ip/L"; // The localized distribution topic prefix. Note: starts with "pp", not "/pp"
//----------------------------------------
// Locals
//----------------------------------------
+std::vector mqttSubscribeTopics; // List of MQTT topics to be subscribed to
+std::vector mqttClientSubscribedTopics; // List of topics currently subscribed to
+
+String localizedDistributionDictTopic = "";
+String localizedDistributionTileTopic = "";
+
static MqttClient *mqttClient;
static char *mqttClientCertificateBuffer = nullptr; // Buffer for client certificate
@@ -225,9 +228,6 @@ void mqttClientPrintStateSummary()
break;
case MQTT_CLIENT_CONNECTING_2_SERVER:
- case MQTT_CLIENT_SUBSCRIBE_KEY:
- case MQTT_CLIENT_SUBSCRIBE_SPARTN:
- case MQTT_CLIENT_SUBSCRIBE_ASSIST:
systemPrint("Connecting");
break;
@@ -289,9 +289,11 @@ void mqttClientPrintStatus()
// Called when a subscribed message arrives
void mqttClientReceiveMessage(int messageSize)
{
- const uint16_t mqttLimit = 2048;
- static uint8_t *mqttData = nullptr; // Allocate memory to hold the MQTT data. Never freed
- if (mqttData == nullptr)
+ // The Level 3 localized distribution dictionary topic can be up to 25KB
+ // The full AssistNow (MGA) topic can be ~11KB
+ const uint16_t mqttLimit = 26000;
+ static uint8_t *mqttData = nullptr;
+ if (mqttData == nullptr) // Allocate memory to hold the MQTT data. Never freed
{
if (online.psram == true)
mqttData = (uint8_t *)ps_malloc(mqttLimit);
@@ -325,8 +327,86 @@ void mqttClientReceiveMessage(int messageSize)
if (mqttCount > 0)
{
+ // Check for localizedDistributionDictTopic
+ if ((localizedDistributionDictTopic.length() > 0)
+ && (strcmp(topic, localizedDistributionDictTopic.c_str()) == 0))
+ {
+ // We should be using a JSON library to read the nodes. But JSON is
+ // heavy on RAM and the dict could be 25KB for Level 3.
+ // Use sscanf instead.
+ //
+ // {
+ // "tile": "L2N5375W00125",
+ // "nodeprefix": "pp/ip/L2N5375W00125/",
+ // "nodes": [
+ // "N5200W00300",
+ // "N5200W00200",
+ // "N5200W00100",
+ // "N5200E00000",
+ // "N5200E00100",
+ // "N5300W00300",
+ // "N5300W00200",
+ // "N5300W00100",
+ // "N5300E00000",
+ // "N5400W00200",
+ // "N5400W00100",
+ // "N5500W00300",
+ // "N5500W00200"
+ // ],
+ // "endpoint": "pp-eu.services.u-blox.com"
+ // }
+ char *nodes = strstr((const char *)mqttData, "\"nodes\":[");
+ if (nodes != nullptr)
+ {
+ nodes += strlen("\"nodes\":["); // Point to the first node
+ float minDist = 99999.0; // Minimum distance to tile center in centidegrees
+ char *preservedTile;
+ char *tile = strtok_r(nodes, ",", &preservedTile);
+ int latitude = int(gnssGetLatitude() * 100.0); // Centidegrees
+ int longitude = int(gnssGetLongitude() * 100.0); // Centidegrees
+ while (tile != nullptr)
+ {
+ char ns, ew;
+ int lat, lon;
+ if (sscanf(tile, "\"%c%d%c%d\"", &ns, &lat, &ew, &lon) == 4)
+ {
+ if (ns == 'S')
+ lat = 0 - lat;
+ if (ew == 'W')
+ lon = 0 - lon;
+ float factorLon = cos(radians(latitude / 100.0)); // Scale lon by the lat
+ float distScaled = pow(pow(lat - latitude, 2) + pow((lon - longitude) * factorLon, 2), 0.5); // Calculate distance to tile center in centidegrees
+ if (distScaled < minDist)
+ {
+ minDist = distScaled;
+ tile[12] = 0; // Convert the second quote to NULL for snprintf
+ char tileTopic[50];
+ snprintf(tileTopic, sizeof(tileTopic), "%s", localizedDistributionDictTopic.c_str());
+ snprintf(&tileTopic[strlen(localizedPrefix) + 13], sizeof(tileTopic) - (strlen(localizedPrefix) + 13), "%s", tile + 1); // Start after the first quote
+ localizedDistributionTileTopic = tileTopic;
+ }
+ }
+ tile = strtok_r(nullptr, ",", &preservedTile);
+ }
+ }
+
+ mqttClientLastDataReceived = millis();
+ break; // Break now - the dict topic should not be pushed
+ }
+
+ // Is this the full AssistNow MGA data? If so, unsubscribe and subscribe to updates
+ if (strcmp(topic, MQTT_TOPIC_ASSISTNOW.c_str()) == 0)
+ {
+ std::vector::iterator pos = std::find(mqttSubscribeTopics.begin(), mqttSubscribeTopics.end(), MQTT_TOPIC_ASSISTNOW);
+ if (pos != mqttSubscribeTopics.end())
+ mqttSubscribeTopics.erase(pos);
+
+ mqttSubscribeTopics.push_back(MQTT_TOPIC_ASSISTNOW_UPDATES);
+ }
+
// Are these keys? If so, update our local copy
- if (strstr(topic, settings.pointPerfectKeyDistributionTopic) != nullptr)
+ if ((strlen(settings.pointPerfectKeyDistributionTopic) > 0 )
+ && (strcmp(topic, settings.pointPerfectKeyDistributionTopic) == 0))
{
// Separate the UBX message into its constituent Key/ToW/Week parts
uint8_t *payLoad = &mqttData[6];
@@ -389,7 +469,15 @@ void mqttClientReceiveMessage(int messageSize)
if (present.gnss_zedf9p)
{
// Only push SPARTN if the priority says we can
- if (strstr(topic, settings.regionalCorrectionTopics[settings.geographicRegion]) != nullptr)
+ if (
+ // We can get correction data from the continental topic
+ ((strlen(settings.regionalCorrectionTopics[settings.geographicRegion]) > 0)
+ && (strcmp(topic, settings.regionalCorrectionTopics[settings.geographicRegion]) == 0))
+ ||
+ // Or from the localized distribution tile topic
+ ((localizedDistributionTileTopic.length() > 0)
+ && (strcmp(topic, localizedDistributionTileTopic.c_str()) == 0))
+ )
{
// SPARTN
updateCorrectionsLastSeen(CORR_IP);
@@ -416,6 +504,10 @@ void mqttClientReceiveMessage(int messageSize)
else
{
// KEYS or MGA
+ if (((settings.debugMqttClientData == true) || (settings.debugCorrections == true)) &&
+ !inMainMenu)
+ systemPrintf("Pushing %d bytes from %s topic to GNSS\r\n", mqttCount, topic);
+
gnssPushRawData(mqttData, mqttCount);
bytesPushed += mqttCount;
}
@@ -581,6 +673,7 @@ void mqttClientUpdate()
// Shutdown the MQTT client when the mode or setting changes
DMW_st(mqttClientSetState, mqttClientState);
+
if (NEQ_RTK_MODE(mqttClientMode) || (!enableMqttClient))
{
if (mqttClientState > MQTT_CLIENT_OFF)
@@ -735,70 +828,38 @@ void mqttClientUpdate()
// The MQTT server is now connected
mqttClient->onMessage(mqttClientReceiveMessage);
- reportHeapNow(settings.debugMqttClientState);
- mqttClientSetState(MQTT_CLIENT_SUBSCRIBE_KEY);
- break;
- }
+ mqttSubscribeTopics.clear(); // Clear the list of MQTT topics to be subscribed to
+ mqttClientSubscribedTopics.clear(); // Clear the list of topics currently subscribed to
+ localizedDistributionDictTopic = "";
+ localizedDistributionTileTopic = "";
- // Subscribe to the topic key
- case MQTT_CLIENT_SUBSCRIBE_KEY: {
- // Determine if the network has failed
- if (networkIsShuttingDown(NETWORK_USER_MQTT_CLIENT))
+ // Subscribe to AssistNow MGA if enabled
+ if (settings.useAssistNow)
{
- // Failed to connect to the network, attempt to restart the network
- mqttClientStop(true); // Was mqttClientRestart(); - #StopVsRestart
- break;
- }
-
- // Subscribe to the key distribution topic. This is provided during ZTP
- if (!mqttClient->subscribe(settings.pointPerfectKeyDistributionTopic))
- {
- mqttClientRestart();
- systemPrintln("ERROR: Subscription to key distribution topic failed!!");
- //mqttClientRestart(); // Why twice? TODO
- break;
+ mqttSubscribeTopics.push_back(MQTT_TOPIC_ASSISTNOW);
}
-
- if (settings.debugMqttClientState)
- systemPrintln("MQTT client subscribed to key distribution topic");
-
- mqttClientSetState(MQTT_CLIENT_SUBSCRIBE_SPARTN);
- break;
- }
-
- // Subscribe to the topic SPARTN
- case MQTT_CLIENT_SUBSCRIBE_SPARTN: {
- // Determine if the network has failed
- if (networkIsShuttingDown(NETWORK_USER_MQTT_CLIENT))
- {
- // Failed to connect to the network, attempt to restart the network
- mqttClientStop(true); // Was mqttClientRestart(); - #StopVsRestart
- break;
- }
-
- // Subscribe to the correction topic for our region - if we have one. L-Band-only does not.
+ // Subscribe to the key distribution topic
+ mqttSubscribeTopics.push_back(String(settings.pointPerfectKeyDistributionTopic));
+ // Subscribe to the continental correction topic for our region - if we have one. L-Band-only does not.
if (strlen(settings.regionalCorrectionTopics[settings.geographicRegion]) > 0)
{
- if (!mqttClient->subscribe(settings.regionalCorrectionTopics[settings.geographicRegion]))
- {
- mqttClientRestart();
- systemPrintln("ERROR: Subscription to corrections topic failed!!");
- //mqttClientRestart(); // Why twice? TODO
- break;
- }
-
- if (settings.debugMqttClientState)
- systemPrintln("MQTT client subscribed to corrections topic");
+ mqttSubscribeTopics.push_back(String(settings.regionalCorrectionTopics[settings.geographicRegion]));
}
else
{
if (settings.debugMqttClientState)
systemPrintln("MQTT client - no corrections topic. Continuing...");
}
+ // Subscribing to the localized distribution dictionary and local tile is handled by MQTT_CLIENT_SERVICES_CONNECTED
+ // since we need a 3D fix for those
+ mqttClientLastDataReceived = millis(); // Prevent MQTT_CLIENT_SERVICES_CONNECTED from going immediately into timeout
+
+ reportHeapNow(settings.debugMqttClientState);
mqttClientSetState(MQTT_CLIENT_SERVICES_CONNECTED);
+
break;
- }
+ } // /case MQTT_CLIENT_CONNECTING_2_SERVER
case MQTT_CLIENT_SERVICES_CONNECTED: {
// Determine if the network has failed
@@ -817,9 +878,136 @@ void mqttClientUpdate()
{
systemPrintln("MQTT client data timeout. Disconnecting...");
mqttClientRestart();
+ break;
+ }
+
+ // Check if there are any new topics that should be subscribed to
+ bool breakOut = false;
+ for (auto it = mqttSubscribeTopics.begin(); it != mqttSubscribeTopics.end(); it = std::next(it))
+ {
+ String topic = *it;
+ std::vector::iterator pos = std::find(mqttClientSubscribedTopics.begin(), mqttClientSubscribedTopics.end(), topic);
+ if (pos == mqttClientSubscribedTopics.end()) // The mqttSubscribeTopics is not in mqttClientSubscribedTopics, so subscribe
+ {
+ if (settings.debugMqttClientState)
+ systemPrintf("MQTT_Client subscribing to topic %s\r\n", topic.c_str());
+ if (mqttClient->subscribe(topic.c_str()))
+ {
+ breakOut = true; // Break out of this state as we have successfully subscribed
+ mqttClientSubscribedTopics.push_back(topic);
+ }
+ else
+ {
+ mqttClientRestart();
+ if (settings.debugMqttClientState)
+ systemPrintf("MQTT_Client subscription to topic %s failed. Restarting\r\n", topic.c_str());
+ breakOut = true; // Break out of this state as the subscribe failed and a restart is needed
+ }
+ }
+ if (breakOut)
+ break; // Break out of the first for loop
}
+ if (breakOut)
+ break; // Break out of this state
+
+ // Check if there are any obsolete topics that should be unsubscribed
+ for (auto it = mqttClientSubscribedTopics.begin(); it != mqttClientSubscribedTopics.end(); it = std::next(it))
+ {
+ String topic = *it;
+ std::vector::iterator pos = std::find(mqttSubscribeTopics.begin(), mqttSubscribeTopics.end(), topic);
+ if (pos == mqttSubscribeTopics.end()) // The mqttClientSubscribedTopics is not in mqttSubscribeTopics, so unsubscribe
+ {
+ if (settings.debugMqttClientState)
+ systemPrintf("MQTT_Client unsubscribing from topic %s\r\n", topic.c_str());
+ if (mqttClient->unsubscribe(topic.c_str()))
+ {
+ breakOut = true; // Break out of this state as we have successfully unsubscribed
+ mqttClientSubscribedTopics.erase(it);
+ }
+ else
+ {
+ mqttClientRestart();
+ if (settings.debugMqttClientState)
+ systemPrintf("MQTT_Client unsubscribe from topic %s failed. Restarting\r\n", topic.c_str());
+ breakOut = true; // Break out of this state as the subscribe failed and a restart is needed
+ }
+ }
+ if (breakOut)
+ break; // Break out of the first for loop
+ }
+ if (breakOut)
+ break; // Break out of this state
+
+ // Check if localized distribution is enabled
+ if ((strlen(settings.regionalCorrectionTopics[settings.geographicRegion]) > 0) && (settings.useLocalizedDistribution))
+ {
+ uint8_t fixType = gnssGetFixType();
+ double latitude = gnssGetLatitude(); // degrees
+ double longitude = gnssGetLongitude(); // degrees
+ if (fixType >= 3) // If we have a 3D fix
+ {
+ // If both the dict and tile topics are empty, prepare to subscribe to the dict topic
+ if ((localizedDistributionDictTopic.length() == 0) && (localizedDistributionTileTopic.length() == 0))
+ {
+ float tileDelta = 2.5; // 2.5 degrees (10 degrees and 5 degrees are also possible)
+ if ((settings.localizedDistributionTileLevel == 0) || (settings.localizedDistributionTileLevel == 3))
+ tileDelta = 10.0;
+ if ((settings.localizedDistributionTileLevel == 1) || (settings.localizedDistributionTileLevel == 4))
+ tileDelta = 5.0;
+
+ float lat = latitude; // Degrees
+ lat = floor(lat / tileDelta) * tileDelta; // Calculate the tile center in degrees
+ lat += tileDelta / 2.0;
+ int lat_i = round(lat * 100.0); // integer centidegrees
+
+ float lon = longitude; // Degrees
+ lon = floor(lon / tileDelta) * tileDelta; // Calculate the tile center in degrees
+ lon += tileDelta / 2.0;
+ int lon_i = round(lon * 100.0); // integer centidegrees
+
+ char dictTopic[50];
+ snprintf(dictTopic, sizeof(dictTopic), "%s%c%c%04d%c%05d/dict",
+ localizedPrefix, char(0x30 + settings.localizedDistributionTileLevel),
+ (lat_i < 0) ? 'S' : 'N', abs(lat_i),
+ (lon_i < 0) ? 'W' : 'E', abs(lon_i));
+
+
+ localizedDistributionDictTopic = dictTopic;
+ mqttSubscribeTopics.push_back(localizedDistributionDictTopic);
+
+ breakOut = true;
+ }
+
+ // localizedDistributionTileTopic is populated by mqttClientReceiveMessage
+ // If both the dict and tile topics are populated, prepare to subscribe to the localized tile topic
+ // Empty localizedDistributionDictTopic afterwardds to prevent this state being repeated
+ if ((localizedDistributionDictTopic.length() > 0) && (localizedDistributionTileTopic.length() > 0))
+ {
+ // Subscribe to the localizedDistributionTileTopic
+ mqttSubscribeTopics.push_back(localizedDistributionTileTopic);
+ // Unsubscribe from the localizedDistributionDictTopic
+ std::vector::iterator pos = std::find(mqttSubscribeTopics.begin(), mqttSubscribeTopics.end(), localizedDistributionDictTopic);
+ if (pos != mqttSubscribeTopics.end())
+ mqttSubscribeTopics.erase(pos);
+ // Unsubscribe from the continental corrections
+ pos = std::find(mqttSubscribeTopics.begin(), mqttSubscribeTopics.end(), String(settings.regionalCorrectionTopics[settings.geographicRegion]));
+ if (pos != mqttSubscribeTopics.end())
+ mqttSubscribeTopics.erase(pos);
+
+ localizedDistributionDictTopic = ""; // Empty localizedDistributionDictTopic to prevent this state being repeated
+ breakOut = true;
+ }
+
+
+ }
+ }
+ if (breakOut)
+ break; // Break out of this state
+
+
+ // Add any new state checking above this line
break;
- }
+ } // /case MQTT_CLIENT_SERVICES_CONNECTED
}
// Periodically display the MQTT client state
diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino
index d58f6edb9..1f5b9dad2 100644
--- a/Firmware/RTK_Everywhere/NtripClient.ino
+++ b/Firmware/RTK_Everywhere/NtripClient.ino
@@ -833,7 +833,7 @@ void ntripClientUpdate()
// Push RTCM to GNSS module over I2C / SPI
gnssPushRawData(rtcmData, rtcmCount);
- if ((settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
+ if ((settings.debugCorrections || settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
(!inMainMenu))
{
PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA);
@@ -842,7 +842,7 @@ void ntripClientUpdate()
}
else
{
- if ((settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
+ if ((settings.debugCorrections || settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) &&
(!inMainMenu))
{
PERIODIC_CLEAR(PD_NTRIP_CLIENT_DATA);
diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino
index ac08aa92b..763fe443b 100644
--- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino
+++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino
@@ -815,7 +815,6 @@ unsigned long lastGnssToPpl = 0;
int commandCount;
int16_t *commandIndex;
-volatile bool forceKeyAttempt = false; // Set to true to force a key provisioning attempt
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
// Display boot times
diff --git a/Firmware/RTK_Everywhere/States.ino b/Firmware/RTK_Everywhere/States.ino
index e0bec2aa1..8f7494157 100644
--- a/Firmware/RTK_Everywhere/States.ino
+++ b/Firmware/RTK_Everywhere/States.ino
@@ -503,7 +503,7 @@ void stateUpdate()
break;
case (STATE_KEYS_REQUESTED): {
- forceKeyAttempt = true; // Force a key update
+ settings.requestKeyUpdate = true; // Force a key update
changeState(lastSystemState); // Return to the last system state
}
break;
diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino
index 59cd243a9..76402334b 100644
--- a/Firmware/RTK_Everywhere/Tasks.ino
+++ b/Firmware/RTK_Everywhere/Tasks.ino
@@ -270,7 +270,7 @@ void sendGnssBuffer()
{
if (gnssPushRawData(bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead))
{
- if (PERIODIC_DISPLAY(PD_ZED_DATA_TX))
+ if (settings.debugCorrections || PERIODIC_DISPLAY(PD_ZED_DATA_TX))
{
PERIODIC_CLEAR(PD_ZED_DATA_TX);
systemPrintf("Sent %d BT bytes to GNSS\r\n", bluetoothOutgoingToGnssHead);
@@ -280,7 +280,7 @@ void sendGnssBuffer()
}
else
{
- if (PERIODIC_DISPLAY(PD_ZED_DATA_TX))
+ if (settings.debugCorrections || PERIODIC_DISPLAY(PD_ZED_DATA_TX))
{
PERIODIC_CLEAR(PD_ZED_DATA_TX);
systemPrintf("%d BT bytes NOT sent due to priority\r\n", bluetoothOutgoingToGnssHead);
diff --git a/Firmware/RTK_Everywhere/menuPP.ino b/Firmware/RTK_Everywhere/menuPP.ino
index 903257014..e42c4da77 100644
--- a/Firmware/RTK_Everywhere/menuPP.ino
+++ b/Firmware/RTK_Everywhere/menuPP.ino
@@ -911,6 +911,12 @@ void menuPointPerfect()
else
systemPrintln("No keys");
+ if ((settings.useLocalizedDistribution) && (localizedDistributionTileTopic.length() > 0))
+ {
+ systemPrint("Most recent localized distribution topic: ");
+ systemPrintln(localizedDistributionTileTopic.c_str()); // From MQTT_Client.ino
+ }
+
// How this works:
// There are three PointPerfect corrections plans: IP-only, L-Band-only, L-Band+IP
// For IP-only - e.g. Torch:
@@ -952,10 +958,31 @@ void menuPointPerfect()
else
systemPrintln("Disabled");
systemPrint("3) Request Key Update: ");
- if (forceKeyAttempt == true)
+ if (settings.requestKeyUpdate == true)
systemPrintln("Requested");
else
systemPrintln("Not requested");
+ systemPrint("4) Use localized distribution: ");
+ if (settings.useLocalizedDistribution == true)
+ systemPrintln("Enabled");
+ else
+ systemPrintln("Disabled");
+ if (settings.useLocalizedDistribution)
+ {
+ systemPrint("5) Localized distribution tile level: ");
+ systemPrint(settings.localizedDistributionTileLevel);
+ systemPrint(" (");
+ systemPrint(localizedDistributionTileLevelNames[settings.localizedDistributionTileLevel]);
+ systemPrintln(")");
+ }
+ if (productVariant != RTK_FACET_MOSAIC)
+ {
+ systemPrint("a) Use AssistNow: ");
+ if (settings.useAssistNow == true)
+ systemPrintln("Enabled");
+ else
+ systemPrintln("Disabled");
+ }
#endif // COMPILE_NETWORK
systemPrintln("c) Clear the Keys");
@@ -976,18 +1003,32 @@ void menuPointPerfect()
{
settings.enablePointPerfectCorrections ^= 1;
restartRover = true; // Require a rover restart to enable / disable RTCM for PPL
- forceKeyAttempt = settings.enablePointPerfectCorrections; // Force a key update - or don't
+ settings.requestKeyUpdate = settings.enablePointPerfectCorrections; // Force a key update - or don't
}
#ifdef COMPILE_NETWORK
else if (incoming == 2 && pointPerfectIsEnabled())
{
settings.autoKeyRenewal ^= 1;
- forceKeyAttempt = settings.autoKeyRenewal; // Force a key update - or don't
+ settings.requestKeyUpdate = settings.autoKeyRenewal; // Force a key update - or don't
}
else if (incoming == 3 && pointPerfectIsEnabled())
{
- forceKeyAttempt ^= 1;
+ settings.requestKeyUpdate ^= 1;
+ }
+ else if (incoming == 4 && pointPerfectIsEnabled())
+ {
+ settings.useLocalizedDistribution ^= 1;
+ }
+ else if (incoming == 5 && pointPerfectIsEnabled() && settings.useLocalizedDistribution)
+ {
+ settings.localizedDistributionTileLevel++;
+ if (settings.localizedDistributionTileLevel >= LOCALIZED_DISTRIBUTION_TILE_LEVELS)
+ settings.localizedDistributionTileLevel = 0;
+ }
+ else if (incoming == 'a' && pointPerfectIsEnabled() && (productVariant != RTK_FACET_MOSAIC))
+ {
+ settings.useAssistNow ^= 1;
}
#endif // COMPILE_NETWORK
else if (incoming == 'c' && pointPerfectIsEnabled())
@@ -1170,7 +1211,7 @@ void provisioningSetState(uint8_t newState)
}
unsigned long provisioningStartTime;
-const unsigned long provisioningTimeout = 20000;
+const unsigned long provisioningTimeout = 120000;
void updateProvisioning()
{
@@ -1197,13 +1238,13 @@ void updateProvisioning()
{
if ((online.rtc)
|| (millis() > (provisioningStartTime + provisioningTimeout))
- || (forceKeyAttempt))
+ || (settings.requestKeyUpdate))
provisioningSetState(PROVISIONING_NOT_STARTED);
}
break;
case PROVISIONING_NOT_STARTED:
{
- if (settings.enablePointPerfectCorrections && (settings.autoKeyRenewal || forceKeyAttempt))
+ if (settings.enablePointPerfectCorrections && (settings.autoKeyRenewal || settings.requestKeyUpdate))
{
provisioningSetState(PROVISIONING_CHECK_REMAINING);
}
@@ -1273,7 +1314,8 @@ void updateProvisioning()
break;
case PROVISIONING_STARTING:
{
- forceKeyAttempt = false;
+ settings.requestKeyUpdate = false;
+ recordSystemSettings(); // Record these settings to unit
ztpResponse = ZTP_NOT_STARTED; // HTTP_Client will update this
httpClientModeNeeded = true; // This will start the HTTP_Client
provisioningStartTime = millis(); // Record the start time so we can timeout
@@ -1360,6 +1402,14 @@ void updateProvisioning()
httpClientModeNeeded = false; // Tell HTTP_Client to give up. (But it probably already has...)
displayAlreadyRegistered(5000);
+ provisioningSetState(PROVISIONING_KEYS_REMAINING);
+ }
+ else if (ztpResponse == ZTP_UNKNOWN_ERROR)
+ {
+ systemPrintln("updateProvisioning: ZTP_UNKNOWN_ERROR");
+
+ httpClientModeNeeded = false; // Tell HTTP_Client to give up. (But it probably already has...)
+
provisioningSetState(PROVISIONING_KEYS_REMAINING);
}
}
@@ -1395,12 +1445,14 @@ void updateProvisioning()
break;
case PROVISIONING_WAIT_ATTEMPT:
{
- if (forceKeyAttempt)
+ if (settings.requestKeyUpdate)
provisioningSetState(PROVISIONING_STARTING);
else if (!settings.enablePointPerfectCorrections || !settings.autoKeyRenewal)
provisioningSetState(PROVISIONING_OFF);
// When did we last try to get keys? Attempt every 24 hours - or every 15 mins for DEVELOPER
- else if (online.rtc && (rtc.getEpoch() - settings.lastKeyAttempt > ( ENABLE_DEVELOPER ? (15 * 60) : (60 * 60 * 24))))
+ //else if (online.rtc && (rtc.getEpoch() - settings.lastKeyAttempt > ( ENABLE_DEVELOPER ? (15 * 60) : (60 * 60 * 24))))
+ // When did we last try to get keys? Attempt every 24 hours
+ else if (online.rtc && (rtc.getEpoch() - settings.lastKeyAttempt > (60 * 60 * 24)))
{
settings.lastKeyAttempt = rtc.getEpoch(); // Mark it
recordSystemSettings(); // Record these settings to unit
diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h
index d36e08d07..535ef7b1e 100644
--- a/Firmware/RTK_Everywhere/settings.h
+++ b/Firmware/RTK_Everywhere/settings.h
@@ -1369,10 +1369,27 @@ struct Settings
bool outputTipAltitude = false; // If enabled, subtract the pole length and APC from the GNSS receiver's reported altitude
+ // Localized distribution
+ bool useLocalizedDistribution = false;
+ uint8_t localizedDistributionTileLevel = 5;
+ bool useAssistNow = false;
+
+ bool requestKeyUpdate = false; // Set to true to force a key provisioning attempt
+
// Add new settings to appropriate group above or create new group
// Then also add to the same group in rtkSettingsEntries below
} settings;
+const uint8_t LOCALIZED_DISTRIBUTION_TILE_LEVELS = 6;
+const char *localizedDistributionTileLevelNames[LOCALIZED_DISTRIBUTION_TILE_LEVELS] = {
+ "1000 x 1000km sparse",
+ "500 x 500km sparse",
+ "250 x 250km sparse",
+ "1000 x 1000km high density",
+ "500 x 500km high density",
+ "250 x 250km high density",
+};
+
typedef enum {
_bool = 0,
_int,
@@ -1863,6 +1880,13 @@ const RTK_Settings_Entry rtkSettingsEntries[] =
{ 0, 0, 1, 0, 1, 1, 1, 1, _uint32_t, 0, & settings.outputTipAltitude, "outputTipAltitude", },
+ // Localized distribution
+ { 0, 1, 1, 0, 1, 1, 1, 1, _bool, 0, & settings.useLocalizedDistribution, "useLocalizedDistribution", },
+ { 0, 1, 1, 0, 1, 1, 1, 1, _uint8_t, 0, & settings.localizedDistributionTileLevel, "localizedDistributionTileLevel", },
+ { 0, 1, 1, 0, 1, 1, 0, 1, _bool, 0, & settings.useAssistNow, "useAssistNow", },
+
+ { 0, 1, 1, 0, 1, 1, 1, 1, _bool, 0, & settings.requestKeyUpdate, "requestKeyUpdate", },
+
// Add new settings to appropriate group above or create new group
// Then also add to the same group in settings above
// F