Add readNavigationDatabase. Update the AssistNow Autonomous example.

This commit is contained in:
PaulZC
2021-11-30 20:09:59 +00:00
parent 33b8e1deb2
commit d0143181fe
7 changed files with 441 additions and 24 deletions
+1 -1
View File
@@ -5,7 +5,6 @@ When the user calls one of the methods the library will poll the u-blox module f
* Wait for a minimum of 25 ms between polls (configured dynamically when update rate is set) * Wait for a minimum of 25 ms between polls (configured dynamically when update rate is set)
* Write 0xFD to module * Write 0xFD to module
* Read two bytes (0xFD and 0xFE) for bytes available * Read two bytes (0xFD and 0xFE) for bytes available
* If 0x7F or 0xFF then no bytes are available
* Otherwise, read number of bytes and process into NMEA, UBX, or RTCM frame. * Otherwise, read number of bytes and process into NMEA, UBX, or RTCM frame.
* If checksum is valid, flag frame as complete. * If checksum is valid, flag frame as complete.
@@ -58,6 +57,7 @@ In v2.0, the full list of messages which can be processed and logged automatical
- UBX-NAV-CLOCK (0x01 0x22): Clock solution - UBX-NAV-CLOCK (0x01 0x22): Clock solution
- UBX-NAV-SVIN (0x01 0x3B): Survey-in data (**only with High Precision GNSS products**) - UBX-NAV-SVIN (0x01 0x3B): Survey-in data (**only with High Precision GNSS products**)
- UBX-NAV-RELPOSNED (0x01 0x3C): Relative positioning information in NED frame (**only with High Precision GNSS products**) - UBX-NAV-RELPOSNED (0x01 0x3C): Relative positioning information in NED frame (**only with High Precision GNSS products**)
- UBX-NAV-AOPSTATUS (0x01 0x60): AssistNow Autonomous status
- UBX-RXM-SFRBX (0x02 0x13): Broadcast navigation data subframe - UBX-RXM-SFRBX (0x02 0x13): Broadcast navigation data subframe
- UBX-RXM-RAWX (0x02 0x15): Multi-GNSS raw measurement data (**only with ADR or High Precision GNSS or Time Sync products**) - UBX-RXM-RAWX (0x02 0x15): Multi-GNSS raw measurement data (**only with ADR or High Precision GNSS or Time Sync products**)
- UBX-TIM-TM2 (0x0D 0x03): Time mark data - UBX-TIM-TM2 (0x0D 0x03): Time mark data
@@ -7,6 +7,10 @@
This example shows how to enable, check the status of, and read the AssistNow Autonomous data from the module. This example shows how to enable, check the status of, and read the AssistNow Autonomous data from the module.
Note: this example will only work on boards which have plenty of RAM available.
The database can be several kBytes in length and needs to be stored twice:
once inside the library (by readNavigationDatabase); and again in this example code.
Feel like supporting open source hardware? Feel like supporting open source hardware?
Buy a board from SparkFun! Buy a board from SparkFun!
SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663 SparkFun Thing Plus - ESP32 WROOM: https://www.sparkfun.com/products/15663
@@ -49,6 +53,8 @@ void setup()
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
//myGNSS.enableDebugging(Serial, true); // Uncomment this line to see helpful debug messages on Serial
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Enable AssistNow Autonomous data collection. // Enable AssistNow Autonomous data collection.
@@ -63,35 +69,85 @@ void setup()
} }
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Keep calling getAOPSTATUSstatus until it returns zero (indicating AssistNow Autonomous data collection si complete) // Keep calling getAOPSTATUSstatus until it returns zero (indicating AssistNow Autonomous data collection is complete)
// or the user presses a key // or the user presses a key
Serial.println(F("AssistNow Autonomous data collection is in progress. Press any key to quit.")); Serial.println(F("AssistNow Autonomous data collection is in progress. Press any key to quit."));
Serial.println(F("NAV AOPSTATUS status indicates when the AssistNow Autonomous subsystem is idle (0) or running (not 0)."));
bool keepGoing = true; bool keepGoing = true;
int zerosSeen = 0; // Keep track of how many times aopStatus has been zero
while (Serial.available()) Serial.read(); // Empty the serial buffer while (Serial.available()) Serial.read(); // Empty the serial buffer
while (keepGoing && !Serial.available()); // Wait keepGoing to go false, or for the arrival of a keypress while (keepGoing && !Serial.available()) // Wait for keepGoing to go false, or for the arrival of a keypress
{ {
delay(1000); delay(1000);
Serial.print(F("NAV AOPSTATUS status is: "));
uint8_t aopStatus = myGNSS.getAOPSTATUSstatus(); uint8_t aopStatus = myGNSS.getAOPSTATUSstatus();
Serial.print(aopStatus); Serial.print(F("NAV AOPSTATUS status is: "));
Serial.println(F(". (Don't worry! This could take a _long_ time...)")); Serial.println(aopStatus);
if (aopStatus == 0) // aopStatus will be zero when the AssistNow Autonomous subsystem is idle - i.e. data collection is complete if (aopStatus == 0) // aopStatus will be zero when the AssistNow Autonomous subsystem is idle - i.e. data collection is complete
keepGoing = false; {
zerosSeen++; // Keep track of how long aopStatus has been zero
if (zerosSeen >= 30)
keepGoing = false; // Stop after seeing 30 consecutive zeros
}
else
{
zerosSeen = 0; // Reset the number of zeros seen
}
} }
if (!keepGoing) if (!keepGoing)
Serial.print(F("AssistNow Autonomous data collection is complete!")); Serial.println(F("AssistNow Autonomous data collection is complete!"));
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Read the AssistNow Autonomous data from the module and pretty-print it (so it can be copied and pasted into Example2) // Read the AssistNow Autonomous data from the module and pretty-print it (so it can be copied and pasted into Example2)
#define MAX_DATABASE_LENGTH 32768 // Allocate 32kBytes to store the navigation database
size_t maxDatabaseLen = MAX_DATABASE_LENGTH;
uint8_t *database = new uint8_t[MAX_DATABASE_LENGTH]; // The database will be stored here
Serial.println(F("Storage has been allocated for the database.")); Serial.flush();
size_t actualDatabaseLen = myGNSS.readNavigationDatabase(database, maxDatabaseLen); // Read the database
Serial.print(F("The Navigation Database length was "));
Serial.println(actualDatabaseLen);
if (actualDatabaseLen == maxDatabaseLen)
Serial.println(F("There was not enough memory to store the entire database. Some data will have been lost!"));
// Pretty-print the database so it can be copied into Example2
Serial.println(F("Copy and paste the following into Example2, so you can write it back to the module:"));
Serial.println();
Serial.print(F("size_t databaseLen = "));
Serial.print(actualDatabaseLen);
Serial.println(F(";"));
Serial.print(F("const uint8_t database["));
Serial.print(actualDatabaseLen);
Serial.println(F("] = {"));
size_t i;
for(i = 0; i < actualDatabaseLen; i++)
{
if ((i % 32) == 0)
Serial.print(F(" 0x"));
if (*(database + i) < 0x10) // Print leading zero
Serial.print(F("0"));
Serial.print(*(database + i), HEX);
if (i == (actualDatabaseLen - 1))
Serial.println();
else if ((i % 32) == 31)
Serial.println(F(","));
else
Serial.print(F(", 0x"));
}
Serial.println(F("};"));
} }
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void loop() void loop()
{ {
// Nothing to do here
} }
+30
View File
@@ -174,3 +174,33 @@ AssistNow Autonomous is disabled by default. You can enable it by calling ```set
* set ```aopCfg``` to 1 to enable AssistNow Autonomous, or 0 to disable it * set ```aopCfg``` to 1 to enable AssistNow Autonomous, or 0 to disable it
* ```aopOrbMaxErr``` is used to set the 'lifetime' of the AssistNow data. It is recommended to set aopOrbMaxErr to 0 (the default value). This instructs the module to use the firmware default value that corresponds to a default orbit data validity of approximately three days (for GPS satellites observed once) and up to six days (for GPS and GLONASS satellites observed multiple times over a period of at least half a day). * ```aopOrbMaxErr``` is used to set the 'lifetime' of the AssistNow data. It is recommended to set aopOrbMaxErr to 0 (the default value). This instructs the module to use the firmware default value that corresponds to a default orbit data validity of approximately three days (for GPS satellites observed once) and up to six days (for GPS and GLONASS satellites observed multiple times over a period of at least half a day).
Once AssistNow Autonomous is enabled, you can monitor its progress via the ```status``` field in the UBX-NAV-AOPSTATUS message. You can read the ```status``` by calling ```getAOPSTATUSstatus```. It will return zero when the AssistNow Autonomous data collection is complete. Non-zero values indicate that data collection is still in progress.
* <b>uint8_t getAOPSTATUSstatus(uint16_t maxWait);</b>
* <b>uint8_t getAOPSTATUSuseAOP(uint16_t maxWait);</b>
We have included full 'auto' support for UBX-NAV-AOPSTATUS, so you can have the message delivered periodically, add a callback for it, and/or log it to the file buffer:
* <b>bool getAOPSTATUS(uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUS(bool enabled, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUS(bool enabled, bool implicitUpdate, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait);</b>
* <b>bool setAutoAOPSTATUScallback(void (*callbackPointer)(UBX_NAV_AOPSTATUS_data_t), uint16_t maxWait);</b>
* <b>bool assumeAutoAOPSTATUS(bool enabled, bool implicitUpdate);</b>
* <b>void flushAOPSTATUS();</b>
* <b>void logNAVAOPSTATUS(bool enabled);</b>
The AssistNow Autonomous data is stored in the module's RAM memory. If that RAM is Battery-Backed - all SparkFun GNSS boards include battery back-up - then the data will be available after the module is powered down and powered back up again. However, you can also read (poll) the navigation database and store the contents in processor memory. ```readNavigationDatabase``` allows you to do that:
* <b>size_t readNavigationDatabase(uint8_t *dataBytes, size_t maxNumDataBytes, uint16_t maxWait);</b>
Data is written to ```dataBytes```. Set ```maxNumDataBytes``` to the (maximum) size of dataBytes. If the database exceeds maxNumDataBytes, the excess bytes will be lost.
```readNavigationDatabase``` returns the number of database bytes written to ```dataBytes```. The return value will be equal to ```maxNumDataBytes``` if excess data was received.
```readNavigationDatabase``` will timeout after ```maxWait``` milliseconds - in case the final UBX-MGA-ACK was missed.
You can then write the database back into the module using ```pushAssistNowData```. Don't forget to call ```setUTCTimeAssistance``` _before_ ```pushAssistNowData```.
Note: UBX-MGA-DBD messages are only intended to be sent back to the same receiver that generated them. They are firmware-specific.
+1
View File
@@ -95,6 +95,7 @@ setUTCTimeAssistance KEYWORD2
setPositionAssistanceXYZ KEYWORD2 setPositionAssistanceXYZ KEYWORD2
setPositionAssistanceLLH KEYWORD2 setPositionAssistanceLLH KEYWORD2
findMGAANOForDate KEYWORD2 findMGAANOForDate KEYWORD2
readNavigationDatabase KEYWORD2
setFileBufferSize KEYWORD2 setFileBufferSize KEYWORD2
getFileBufferSize KEYWORD2 getFileBufferSize KEYWORD2
+301 -6
View File
@@ -350,6 +350,12 @@ void SFE_UBLOX_GNSS::end(void)
packetUBXMGAACK = NULL; // Redundant? packetUBXMGAACK = NULL; // Redundant?
} }
if (packetUBXMGADBD != NULL)
{
delete packetUBXMGADBD;
packetUBXMGADBD = NULL; // Redundant?
}
if (packetUBXHNRATT != NULL) if (packetUBXHNRATT != NULL)
{ {
if (packetUBXHNRATT->callbackData != NULL) if (packetUBXHNRATT->callbackData != NULL)
@@ -1096,7 +1102,15 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
break; break;
case UBX_CLASS_MGA: case UBX_CLASS_MGA:
{ {
switch (ID)
{
case UBX_MGA_ACK_DATA0:
if (packetUBXMGAACK != NULL) result = true; if (packetUBXMGAACK != NULL) result = true;
break;
case UBX_MGA_DBD:
if (packetUBXMGADBD != NULL) result = true;
break;
}
} }
break; break;
case UBX_CLASS_HNR: case UBX_CLASS_HNR:
@@ -1234,7 +1248,15 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID)
break; break;
case UBX_CLASS_MGA: case UBX_CLASS_MGA:
{ {
switch (ID)
{
case UBX_MGA_ACK_DATA0:
maxSize = UBX_MGA_ACK_DATA0_LEN; maxSize = UBX_MGA_ACK_DATA0_LEN;
break;
case UBX_MGA_DBD:
maxSize = UBX_MGA_DBD_LEN; // UBX_MGA_DBD_LEN is actually a maximum length. The packets could be shorter than this.
break;
}
} }
break; break;
case UBX_CLASS_HNR: case UBX_CLASS_HNR:
@@ -1263,7 +1285,7 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
{ {
if ((currentSentence == NONE) || (currentSentence == NMEA)) if ((currentSentence == NONE) || (currentSentence == NMEA))
{ {
if (incoming == 0xB5) //UBX binary frames start with 0xB5, aka μ if (incoming == UBX_SYNCH_1) //UBX binary frames start with 0xB5, aka μ
{ {
//This is the start of a binary sentence. Reset flags. //This is the start of a binary sentence. Reset flags.
//We still don't know the response class //We still don't know the response class
@@ -1295,9 +1317,9 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r
if (currentSentence == UBX) if (currentSentence == UBX)
{ {
//Decide what type of response this is //Decide what type of response this is
if ((ubxFrameCounter == 0) && (incoming != 0xB5)) //ISO 'μ' if ((ubxFrameCounter == 0) && (incoming != UBX_SYNCH_1)) //ISO 'μ'
currentSentence = NONE; //Something went wrong. Reset. currentSentence = NONE; //Something went wrong. Reset.
else if ((ubxFrameCounter == 1) && (incoming != 0x62)) //ASCII 'b' else if ((ubxFrameCounter == 1) && (incoming != UBX_SYNCH_2)) //ASCII 'b'
currentSentence = NONE; //Something went wrong. Reset. currentSentence = NONE; //Something went wrong. Reset.
// Note to future self: // Note to future self:
// There may be some duplication / redundancy in the next few lines as processUBX will also // There may be some duplication / redundancy in the next few lines as processUBX will also
@@ -2809,6 +2831,61 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
if (packetUBXMGAACK->head == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN) if (packetUBXMGAACK->head == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN)
packetUBXMGAACK->head = 0; packetUBXMGAACK->head = 0;
} }
else
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->println(F("processUBXpacket: packetUBXMGAACK is full. ACK will be lost!"));
}
}
}
}
else if (msg->id == UBX_MGA_DBD && msg->len <= UBX_MGA_DBD_LEN) // Message length may be less than UBX_MGA_DBD_LEN. UBX_MGA_DBD_LEN is the maximum it will be.
{
//Parse various byte fields into storage - but only if we have memory allocated for it
if (packetUBXMGADBD != NULL)
{
// Calculate how many DBDs are already stored in the ring buffer
uint8_t dbdBufferContains;
if (packetUBXMGADBD->head >= packetUBXMGADBD->tail) // Check if wrap-around has occurred
{
// Wrap-around has not occurred so do a simple subtraction
dbdBufferContains = packetUBXMGADBD->head - packetUBXMGADBD->tail;
}
else
{
// Wrap-around has occurred so do a simple subtraction but add in the buffer length (UBX_MGA_DBD_RINGBUFFER_LEN)
dbdBufferContains = ((uint8_t)(((uint16_t)packetUBXMGADBD->head + (uint16_t)UBX_MGA_DBD_RINGBUFFER_LEN) - (uint16_t)packetUBXMGADBD->tail));
}
// Have we got space to store this DBD?
if (dbdBufferContains < (UBX_MGA_DBD_RINGBUFFER_LEN - 1))
{
// Yes, we have, so store it
// We need to save the entire message - header, payload and checksum
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryHeader1 = UBX_SYNCH_1;
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryHeader2 = UBX_SYNCH_2;
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryClass = UBX_CLASS_MGA;
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryID = UBX_MGA_DBD;
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryLenLSB = (uint8_t)(msg->len & 0xFF); // We need to store the length of the DBD entry. The entry itself does not contain a length...
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryLenMSB = (uint8_t)((msg->len >> 8) & 0xFF);
for (uint16_t i = 0; i < msg->len; i++)
{
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntry[i] = extractByte(msg, i);
}
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryChecksumA = msg->checksumA;
packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryChecksumB = msg->checksumB;
// Increment the head
packetUBXMGADBD->head++;
if (packetUBXMGADBD->head == UBX_MGA_DBD_RINGBUFFER_LEN)
packetUBXMGADBD->head = 0;
}
else
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->println(F("processUBXpacket: packetUBXMGADBD is full. DBD data will be lost!"));
}
}
} }
} }
break; break;
@@ -4596,6 +4673,215 @@ size_t SFE_UBLOX_GNSS::findMGAANOForDateInternal(const uint8_t *dataBytes, size_
return (dataPtr); return (dataPtr);
} }
// Read the whole navigation data base. The receiver will send all available data from its internal database.
// Data is written to dataBytes. Set maxNumDataBytes to the (maximum) size of dataBytes.
// If the database exceeds maxNumDataBytes, the excess bytes will be lost.
// The function returns the number of database bytes written to dataBytes.
// The return value will be equal to maxNumDataBytes if excess data was received.
// The function will timeout after maxWait milliseconds - in case the final UBX-MGA-ACK was missed.
size_t SFE_UBLOX_GNSS::readNavigationDatabase(uint8_t *dataBytes, size_t maxNumDataBytes, uint16_t maxWait)
{
// Allocate RAM to store the MGA ACK message
if (packetUBXMGAACK == NULL) initPacketUBXMGAACK(); //Check that RAM has been allocated for the MGA_ACK data
if (packetUBXMGAACK == NULL) //Bail if the RAM allocation failed
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if (_printDebug == true)
{
_debugSerial->println(F("readNavigationDatabase: packetUBXMGAACK RAM allocation failed!"));
}
#endif
return ((size_t)0);
}
if (packetUBXMGAACK->head != packetUBXMGAACK->tail) // Does the MGA ACK ringbuffer contain any data?
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if (_printDebug == true)
{
_debugSerial->println(F("readNavigationDatabase: packetUBXMGAACK contains unprocessed data. Clearing it."));
}
#endif
packetUBXMGAACK->tail = packetUBXMGAACK->head; // Clear the buffer by setting the tail equal to the head
}
// Allocate RAM to store the MGA DBD messages
if (packetUBXMGADBD == NULL) initPacketUBXMGADBD(); //Check that RAM has been allocated for the MGA_DBD data
if (packetUBXMGADBD == NULL) //Bail if the RAM allocation failed
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->println(F("readNavigationDatabase: packetUBXMGADBD RAM allocation failed!"));
}
#endif
return ((size_t)0);
}
if (packetUBXMGADBD->head != packetUBXMGADBD->tail) // Does the MGA DBD ringbuffer contain any data?
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if (_printDebug == true)
{
_debugSerial->println(F("readNavigationDatabase: packetUBXMGADBD contains unprocessed data. Clearing it."));
}
#endif
packetUBXMGADBD->tail = packetUBXMGADBD->head; // Clear the buffer by setting the tail equal to the head
}
// Record what ackAiding is currently set to so we can restore it
uint8_t currentAckAiding = getAckAiding();
if (currentAckAiding == 255)
currentAckAiding = 0; // If the get failed, disable the ACKs when returning
// Enable ackAiding
setAckAiding(1);
// Record what i2cPollingWait is currently set to so we can restore it
uint8_t currentI2cPollingWait = i2cPollingWait;
// Set the I2C polling wait to 1ms
i2cPollingWait = 1;
// Construct the poll message:
uint8_t pollNaviDatabase[8]; // Create the UBX-MGA-DBD message by hand
memset(pollNaviDatabase, 0x00, 8); // Set all unused / reserved bytes and the checksum to zero
pollNaviDatabase[0] = UBX_SYNCH_1; // Sync char 1
pollNaviDatabase[1] = UBX_SYNCH_2; // Sync char 2
pollNaviDatabase[2] = UBX_CLASS_MGA; // Class
pollNaviDatabase[3] = UBX_MGA_DBD; // ID
pollNaviDatabase[4] = 0x00; // Length LSB
pollNaviDatabase[5] = 0x00; // Length MSB
for (uint8_t i = 2; i < 6; i++) // Calculate the checksum
{
pollNaviDatabase[6] += pollNaviDatabase[i];
pollNaviDatabase[7] += pollNaviDatabase[6];
}
// Push the poll message to the module.
// Do not Wait for an ACK - the DBD data will start arriving immediately.
size_t pushResult = pushAssistNowDataInternal(0, false, pollNaviDatabase, (size_t)8, SFE_UBLOX_MGA_ASSIST_ACK_NO, 0);
// Check pushResult == 1. Redundant?!
if (pushResult != 1)
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if (_printDebug == true)
{
_debugSerial->println(F("readNavigationDatabase: pushAssistNowDataInternal failed!"));
}
#endif
i2cPollingWait = currentI2cPollingWait; // Restore i2cPollingWait
setAckAiding(currentAckAiding); // Restore Ack Aiding
return ((size_t)0);
}
// Now keep checking for the arrival of UBX-MGA-DBD packets and write them to dataBytes
bool keepGoing = true;
unsigned long startTime = millis();
uint32_t databaseEntriesRX = 0; // Keep track of how many database entries are received
size_t numBytesReceived = 0; // Keep track of how many bytes are received
while (keepGoing && (millis() < (startTime + maxWait)))
{
checkUblox();
while (packetUBXMGADBD->head != packetUBXMGADBD->tail) // Does the MGA DBD ringbuffer contain any data?
{
// The data will be valid - process will have already checked it. So we can simply copy the data into dataBuffer.
// We do not need to check if there is room to store the entire database entry. pushAssistNowData will check the data before pushing it.
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryHeader1;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryHeader2;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryClass;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryID;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenLSB;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenMSB;
size_t msgLen = (((size_t)packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenMSB) * 256) + ((size_t)packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenLSB);
for (size_t i = 0; i < msgLen; i++)
{
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntry[i];
}
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryChecksumA;
if (numBytesReceived < maxNumDataBytes)
*(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryChecksumB;
// Increment the tail
packetUBXMGADBD->tail++;
if (packetUBXMGADBD->tail == UBX_MGA_DBD_RINGBUFFER_LEN)
packetUBXMGADBD->tail = 0;
databaseEntriesRX++; // Increment the number of entries received
}
// The final MGA-ACK is sent at the end of the DBD packets. So, we need to check the ACK buffer _after_ the DBD buffer.
while (packetUBXMGAACK->head != packetUBXMGAACK->tail) // Does the MGA ACK ringbuffer contain any data?
{
// Check if we've received the correct ACK
bool dataAckd = true;
dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgId == UBX_MGA_DBD); // Check if the message ID matches
dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[0] == (uint8_t)(databaseEntriesRX & 0xFF)); // Check if the ACK contents match databaseEntriesRX
dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[1] == (uint8_t)((databaseEntriesRX >> 8) & 0xFF));
dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[2] == (uint8_t)((databaseEntriesRX >> 16) & 0xFF));
dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[3] == (uint8_t)((databaseEntriesRX >> 24) & 0xFF));
if (dataAckd) // Is the ACK valid?
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->print(F("readNavigationDatabase: ACK received. databaseEntriesRX is "));
_debugSerial->print(databaseEntriesRX);
_debugSerial->print(F(". numBytesReceived is "));
_debugSerial->print(numBytesReceived);
_debugSerial->print(F(". DBD read complete after "));
_debugSerial->print(millis() - startTime);
_debugSerial->println(F(" ms"));
}
#endif
keepGoing = false;
}
// Increment the tail
packetUBXMGAACK->tail++;
if (packetUBXMGAACK->tail == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN)
packetUBXMGAACK->tail = 0;
}
}
if (keepGoing) // If keepGoing is still true, we must have timed out
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
{
_debugSerial->println(F("readNavigationDatabase: DBD RX timed out!"));
}
}
i2cPollingWait = currentI2cPollingWait; // Restore i2cPollingWait
setAckAiding(currentAckAiding); // Restore Ack Aiding
return (numBytesReceived);
}
// PRIVATE: Allocate RAM for packetUBXMGADBD and initialize it
bool SFE_UBLOX_GNSS::initPacketUBXMGADBD()
{
packetUBXMGADBD = new UBX_MGA_DBD_t; //Allocate RAM for the main struct
if (packetUBXMGADBD == NULL)
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
_debugSerial->println(F("initPacketUBXMGADBD: RAM alloc failed!"));
return (false);
}
packetUBXMGADBD->head = 0; // Initialize the ring buffer pointers
packetUBXMGADBD->tail = 0;
return (true);
}
// Support for data logging // Support for data logging
//Set the file buffer size. This must be called _before_ .begin //Set the file buffer size. This must be called _before_ .begin
@@ -4768,7 +5054,7 @@ bool SFE_UBLOX_GNSS::storePacket(ubxPacket *msg)
} }
//Store the two sync chars //Store the two sync chars
uint8_t sync_chars[] = {0xB5, 0x62}; uint8_t sync_chars[] = {UBX_SYNCH_1, UBX_SYNCH_2};
writeToFileBuffer(sync_chars, 2); writeToFileBuffer(sync_chars, 2);
//Store the Class & ID //Store the Class & ID
@@ -10805,12 +11091,15 @@ uint32_t SFE_UBLOX_GNSS::getProcessNMEAMask()
//Max is 40Hz(?!) //Max is 40Hz(?!)
bool SFE_UBLOX_GNSS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait) bool SFE_UBLOX_GNSS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait)
{ {
if (navFreq == 0) // Return now if navFreq is zero
return (false);
if (navFreq > 40) if (navFreq > 40)
navFreq = 40; // Limit navFreq to 40Hz so i2cPollingWait is set correctly navFreq = 40; // Limit navFreq to 40Hz so i2cPollingWait is set correctly
//Adjust the I2C polling timeout based on update rate //Adjust the I2C polling timeout based on update rate
//Do this even if the sendCommand fails //Do this even if the sendCommand fails
i2cPollingWaitNAV = 1000 / (((int)navFreq) * 4); //This is the number of ms to wait between checks for new I2C data i2cPollingWaitNAV = 1000 / (((int)navFreq) * 4); //This is the number of ms to wait between checks for new I2C data. Max is 250. Min is 6.
i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR
//Query the module //Query the module
@@ -10861,6 +11150,9 @@ bool SFE_UBLOX_GNSS::setMeasurementRate(uint16_t rate, uint16_t maxWait)
rate = 25; rate = 25;
//Adjust the I2C polling timeout based on update rate //Adjust the I2C polling timeout based on update rate
if (rate >= 1000)
i2cPollingWaitNAV = 250;
else
i2cPollingWaitNAV = rate / 4; //This is the number of ms to wait between checks for new I2C data i2cPollingWaitNAV = rate / 4; //This is the number of ms to wait between checks for new I2C data
i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR
@@ -12177,12 +12469,15 @@ bool SFE_UBLOX_GNSS::getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensor
// Returns true if the setHNRNavigationRate is successful // Returns true if the setHNRNavigationRate is successful
bool SFE_UBLOX_GNSS::setHNRNavigationRate(uint8_t rate, uint16_t maxWait) bool SFE_UBLOX_GNSS::setHNRNavigationRate(uint8_t rate, uint16_t maxWait)
{ {
if (rate == 0) // Return now if rate is zero
return (false);
if (rate > 40) if (rate > 40)
rate = 40; // Limit rate to 40Hz so i2cPollingWait is set correctly rate = 40; // Limit rate to 40Hz so i2cPollingWait is set correctly
//Adjust the I2C polling timeout based on update rate //Adjust the I2C polling timeout based on update rate
//Do this even if the sendCommand is not ACK'd //Do this even if the sendCommand is not ACK'd
i2cPollingWaitHNR = 1000 / (((int)rate) * 4); //This is the number of ms to wait between checks for new I2C data i2cPollingWaitHNR = 1000 / (((int)rate) * 4); //This is the number of ms to wait between checks for new I2C data. Max 250. Min 6.
i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR
packetCfg.cls = UBX_CLASS_CFG; packetCfg.cls = UBX_CLASS_CFG;
+15 -4
View File
@@ -741,6 +741,15 @@ public:
size_t findMGAANOForDate(const String &dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture = 0); size_t findMGAANOForDate(const String &dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture = 0);
size_t findMGAANOForDate(const uint8_t *dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture = 0); size_t findMGAANOForDate(const uint8_t *dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture = 0);
// Read the whole navigation data base. The receiver will send all available data from its internal database.
// Data is written to dataBytes. Set maxNumDataBytes to the (maximum) size of dataBytes.
// If the database exceeds maxNumDataBytes, the excess bytes will be lost.
// The function returns the number of database bytes written to dataBytes.
// The return value will be equal to maxNumDataBytes if excess data was received.
// The function will timeout after maxWait milliseconds - in case the final UBX-MGA-ACK was missed.
#define defaultNavDBDMaxWait 3100
size_t readNavigationDatabase(uint8_t *dataBytes, size_t maxNumDataBytes, uint16_t maxWait = defaultNavDBDMaxWait);
// Support for data logging // Support for data logging
void setFileBufferSize(uint16_t bufferSize); // Set the size of the file buffer. This must be called _before_ .begin. void setFileBufferSize(uint16_t bufferSize); // Set the size of the file buffer. This must be called _before_ .begin.
uint16_t getFileBufferSize(void); // Return the size of the file buffer uint16_t getFileBufferSize(void); // Return the size of the file buffer
@@ -878,15 +887,15 @@ public:
uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = 250); //Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = 250); //Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = 250); //Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = 250); //Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
// get and set functions for all of the "automatic" message processing
// Navigation (NAV)
// getPVT will only return data once in each navigation cycle. By default, that is once per second. // getPVT will only return data once in each navigation cycle. By default, that is once per second.
// Therefore we should set defaultMaxWait to slightly longer than that. // Therefore we should set defaultMaxWait to slightly longer than that.
// If you change the navigation frequency to (e.g.) 4Hz using setNavigationFrequency(4) // If you change the navigation frequency to (e.g.) 4Hz using setNavigationFrequency(4)
// then you should use a shorter maxWait. 300msec would be about right: getPVT(300) // then you should use a shorter maxWait. 300msec would be about right: getPVT(300)
// get and set functions for all of the "automatic" message processing
// Navigation (NAV)
bool getNAVPOSECEF(uint16_t maxWait = defaultMaxWait); // NAV POSECEF bool getNAVPOSECEF(uint16_t maxWait = defaultMaxWait); // NAV POSECEF
bool setAutoNAVPOSECEF(bool enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic POSECEF reports at the navigation frequency bool setAutoNAVPOSECEF(bool enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic POSECEF reports at the navigation frequency
bool setAutoNAVPOSECEF(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic POSECEF reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update bool setAutoNAVPOSECEF(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic POSECEF reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
@@ -1325,6 +1334,7 @@ public:
UBX_HNR_INS_t *packetUBXHNRINS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_HNR_INS_t *packetUBXHNRINS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_MGA_DBD_t *packetUBXMGADBD = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame uint16_t rtcmFrameCounter = 0; //Tracks the type of incoming byte inside RTCM frame
@@ -1401,6 +1411,7 @@ private:
bool initPacketUBXHNRINS(); // Allocate RAM for packetUBXHNRINS and initialize it bool initPacketUBXHNRINS(); // Allocate RAM for packetUBXHNRINS and initialize it
bool initPacketUBXHNRPVT(); // Allocate RAM for packetUBXHNRPVT and initialize it bool initPacketUBXHNRPVT(); // Allocate RAM for packetUBXHNRPVT and initialize it
bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it
bool initPacketUBXMGADBD(); // Allocate RAM for packetUBXMGADBD and initialize it
//Variables //Variables
TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware
+24
View File
@@ -1728,6 +1728,30 @@ typedef struct
UBX_MGA_ACK_DATA0_data_t data[UBX_MGA_ACK_DATA0_RINGBUFFER_LEN]; // Create a storage array for the MGA ACK packets UBX_MGA_ACK_DATA0_data_t data[UBX_MGA_ACK_DATA0_RINGBUFFER_LEN]; // Create a storage array for the MGA ACK packets
} UBX_MGA_ACK_DATA0_t; } UBX_MGA_ACK_DATA0_t;
// UBX-MGA-DBD (0x13 0x80): Navigation database dump entry
const uint16_t UBX_MGA_DBD_LEN = 164; // "The maximum payload size for firmware 2.01 onwards is 164 bytes"
typedef struct
{
uint8_t dbdEntryHeader1; // We need to save the entire message - header, payload and checksum
uint8_t dbdEntryHeader2;
uint8_t dbdEntryClass;
uint8_t dbdEntryID;
uint8_t dbdEntryLenLSB; // We need to store the length of the DBD entry. The entry itself does not contain a length...
uint8_t dbdEntryLenMSB;
uint8_t dbdEntry[UBX_MGA_DBD_LEN];
uint8_t dbdEntryChecksumA;
uint8_t dbdEntryChecksumB;
} UBX_MGA_DBD_data_t;
#define UBX_MGA_DBD_RINGBUFFER_LEN 256 // Provide storage for MGA DBD packets. TO DO: confirm if 256 is large enough!
typedef struct
{
uint8_t head;
uint8_t tail;
UBX_MGA_DBD_data_t data[UBX_MGA_DBD_RINGBUFFER_LEN]; // Create a storage array for the MGA DBD packets
} UBX_MGA_DBD_t;
// HNR-specific structs // HNR-specific structs
// UBX-HNR-PVT (0x28 0x00): High rate output of PVT solution // UBX-HNR-PVT (0x28 0x00): High rate output of PVT solution