sparkfun/SparkFun_u-blox_GNSS_v3

ZED-F9K / ZED-F9R Priority Navigation Mode

shecker opened this issue · 17 comments

Hello,

we would like to receive PVT, ATT and INS solutions at 30Hz from the ZED-F9K.

As a base example, we have modified line 58 myGNSS.setNavigationFrequency(30) in Example7_OutputRate. When indoors (in a GPS denied area), this works well and we can poll the model at 30Hz without any problems. However, when going outdoors and having Fix Type 3 or 4, the rate drops down to 5-8Hz. We are using an ESP32 and connect the ZED-F9K via I2C at 400kHz.
We have also tried the same module with a direct USB-C connection and using the ublox-ros node and are able to obtain data at 30Hz even in Fix Type 3 and 4 scenarios.
Does anyone know why this may be happening and potential solutions?

Thanks

Hi Simon,

You are probably exceeding the bandwidth of I2C at 400kHz. The module will "throttle" the messages if its internal buffer becomes full.

PVT is 100 bytes. ATT is 40 bytes. INS is 44 bytes. Together that is approx 1840 bits. At 30Hz, the bit rate will be ~55K bits/sec. But that's without overheads, and/or any delays inside your logging code or SD interface.

Are you polling the messages? Or using the "Auto" methods?

Do you have any other messages enabled?

Maybe you could use SPI instead? Our library supports that too.

I hope this helps.

I'll leave this issue open for a while, just in case anyone else has better ideas.

Best wishes,
Paul

If you still have the NMEA GSV messages enabled - they are by default - you will get dramatically more messages when outdoors compared to indoors. Maybe try disabling all NMEA messages using the port settings?

Thanks for the quick reply @PaulZC!

I believe we turn off all messages apart from UBX via myGNSS.setI2COutput(COM_TYPE_UBX); is this the only thing we need to set? We are polling the messages from the loop.
Can you confirm that you are receiving 30Hz with a F9K or F9R?
Thank you very much!

Basic code is as follows:

setup(){
  myGNSS.setDynamicModel(DYN_MODEL_AUTOMOTIVE);
  myGNSS.setI2COutput(COM_TYPE_UBX);
  myGNSS.setI2CpollingWait(5);
  myGNSS.setNavigationFrequency(30);
  myGNSS.setAutoPVTcallbackPtr(NULL);
  myGNSS.setAutoNAVATTcallbackPtr(NULL);
  myGNSS.setAutoESFINScallbackPtr(NULL);
}

loop(){
  const TickType_t frequency = 33;
  TickType_t lastWake = xTaskGetTickCount(); 
  while(true){
      myGNSS.checkUblox(); // Check for the arrival of new data and process it.
      if (myGNSS.packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid == true) {
          myGNSS.packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid = false;
          int latitude = (int32_t)myGNSS.packetUBXNAVPVT->data.lat;
          // ... read more data from PVT, ATT and INS in the same way ... //
      }
      vTaskDelayUntil( &lastWake, frequency); 
    }
}

Hi Simon,

What happens if you increase your tick frequency? Maybe to 99Hz? I'm wondering if the incoming data and tick samples are "beating" in some way?

Correct, myGNSS.setI2COutput(COM_TYPE_UBX); will disable all NMEA messages.

I tend not to use the F9R. And I don't have a F9K... I have had the F9P outputting and logging RXM RAWX data at 20Hz, but that was using SPI not I2C.

Best,
Paul

Hi Paul,

I set the tick frequency to 1kHz and also observe that when the F9K is in a GPS denied area (dead-reckoning mode) it will output a solution at 30Hz. The issue only arises when the module is actively calculating a location based on satellite data.
We also tested the older NEO-M8U and are able to achieve 30Hz with active satellite localization. This is running on the same HW and i2c bus.

Best,
Simon

Hi Simon,

It still makes me think that you have an extra message enabled - in Battery-Backed-RAM. Perhaps something like UBX-NAV-SAT. That would explain an increase in the number of messages outdoors. Perhaps try:

myGNSS.factoryDefault();
delay(5000);

after myGNSS.begin - just to make sure that any extra UBX messages are disabled. You only need to do this once - it sets the defaults in BBR too.

And/or you could connect u-center via USB and check UBX-MON-COMMS. That will show how much data is flowing through each port.

Did you get a reply from u-blox re. whether it is possible to upgrade the F9K firmware?

Best wishes,
Paul

Hi Paul,

We tried your suggestions, but unfortunately none of these are allowing us to receive location updates at 30Hz. I also went ahead and ordered a ZED-F9R (we upgraded to the latest firmware on this device) but are seeing the same behavior as in the F9K. Once the module get a fix, the rate drops drastically (in case of the F9R it's to around 10Hz).
We also noticed that after the firmware update on the F9R that it looses it's calibration almost immediately (we set the module to AUTOMOTIVE mode and are testing in a car). See the attached screenshot.

For our use case it is very important that we can obtain the 30Hz, do you have any idea where this issue is coming from? Is this a problem with the module itself or with the library?

Thanks,
Simon
FixTypeZED-F9R

Hi Simon,

I will try to find time to write a data-logging example for PVT, ATT and INS at 30Hz. I have a ZED-F9R I can use for testing. I won't be able to put it in a vehicle, but I will be able to test it with a good satellite signal. It will take me a few days though - I am busy working on other projects at the moment. Please ping me if I have not replied by the end of the week.

Best,
Paul

Hi Simon,

I'm taking a quick look at this...

So far, I can get the expected output at 20Hz, but it drops back to 1Hz when I try 25Hz.

Here's my code:

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
SFE_UBLOX_GNSS myGNSS;

unsigned long millisSinceLastPVT = 0;
unsigned long millisSinceLastATT = 0;
unsigned long millisSinceLastINS = 0;
uint8_t messageState = 0;

void newPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
  static unsigned long lastPVT = 0;
  millisSinceLastPVT = millis() - lastPVT;
  lastPVT = millis();
  messageState ^= 0x1; // Toggle a bit in mesageState
}

void newATTdata(UBX_NAV_ATT_data_t *ubxDataStruct)
{
  static unsigned long lastATT = 0;
  millisSinceLastATT = millis() - lastATT;
  lastATT = millis();
  messageState ^= 0x2; // Toggle a bit in mesageState
}

void newINSdata(UBX_ESF_INS_data_t *ubxDataStruct)
{
  static unsigned long lastINS = 0;
  millisSinceLastINS = millis() - lastINS;
  lastINS = millis();
  messageState ^= 0x4; // Toggle a bit in mesageState
}

void setup()
{
  Serial.begin(115200);

  Wire.begin();
  Wire.setClock(400000);

  //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

  while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
  {
    delay(1000);
  }

  myGNSS.factoryDefault(); delay(5000); //Reset the GNSS module

  myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)

  myGNSS.setNavigationFrequency(20); //Produce solutions at 30Hz

  myGNSS.setAutoPVTcallbackPtr(&newPVTdata); // Enable automatic NAV PVT messages
  myGNSS.setAutoNAVATTcallbackPtr(&newATTdata); // Enable automatic NAV ATT messages
  myGNSS.setAutoESFINScallbackPtr(&newINSdata); // Enable automatic ESF INS messages
}

void loop()
{
  myGNSS.checkUblox(); // Check for the arrival of new data and process it.
  myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.

  static uint8_t prevMessageState = 0;
  // Wait until all three messages have been received. (We don't know what order they will arrive in...)
  if (((prevMessageState == 0x0) && (messageState == 0x7)) || ((prevMessageState == 0x7) && (messageState == 0x0)))
  {
    prevMessageState = messageState;
    Serial.print(millisSinceLastPVT); // Format the data for Serial Plotter
    Serial.print(',');
    Serial.print(millisSinceLastATT);
    Serial.print(',');
    Serial.println(millisSinceLastINS);
  }
}

I'm going to try opening the I2C pull-up jumper links. Then I'll try SPI.

More later,
Paul

Hi Simon,

Same result on SPI. I'm OK at 20Hz, but as soon as I try to push it to 25Hz it drops back to 1Hz.

Investigating further, I connected u-center simultaneously. If I run my example at 20Hz and then try to change CFG-RATE-MEAS to 40ms, the VALSET is NACK'd. I.e. the module thinks 40ms / 25Hz is not supported...

image

This is without an antenna attached - to replicate your indoor measurements.

I don't know where this leaves us? I think I need a coffee...

More later,
Paul

PS: Here's the SPI code in case it comes in useful:

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
SFE_UBLOX_GNSS_SPI myGNSS;

unsigned long millisSinceLastPVT = 0;
unsigned long millisSinceLastATT = 0;
unsigned long millisSinceLastINS = 0;
uint8_t messageState = 0;

void newPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
  static unsigned long lastPVT = 0;
  millisSinceLastPVT = millis() - lastPVT;
  lastPVT = millis();
  messageState ^= 0x1; // Toggle a bit in mesageState
}

void newATTdata(UBX_NAV_ATT_data_t *ubxDataStruct)
{
  static unsigned long lastATT = 0;
  millisSinceLastATT = millis() - lastATT;
  lastATT = millis();
  messageState ^= 0x2; // Toggle a bit in mesageState
}

void newINSdata(UBX_ESF_INS_data_t *ubxDataStruct)
{
  static unsigned long lastINS = 0;
  millisSinceLastINS = millis() - lastINS;
  lastINS = millis();
  messageState ^= 0x4; // Toggle a bit in mesageState
}

void setup()
{
  delay(1000);
  
  Serial.begin(115200);
  SPI.begin();

  //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

  while (myGNSS.begin(4) == false) //Connect to the u-blox module using SPI. CS is pin 4
  {
    delay(1000);
  }

  myGNSS.factoryDefault(); delay(5000); //Reset the GNSS module

  myGNSS.setSPIOutput(COM_TYPE_UBX); //Set the SPI port to output UBX only (turn off NMEA noise)

  myGNSS.setNavigationFrequency(20); //Produce solutions at Hz

  myGNSS.setAutoPVTcallbackPtr(&newPVTdata); // Enable automatic NAV PVT messages
  myGNSS.setAutoNAVATTcallbackPtr(&newATTdata); // Enable automatic NAV ATT messages
  myGNSS.setAutoESFINScallbackPtr(&newINSdata); // Enable automatic ESF INS messages
}

void loop()
{
  myGNSS.checkUblox(); // Check for the arrival of new data and process it.
  myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.

  static uint8_t prevMessageState = 0;
  // Wait until all three messages have been received. (We don't know what order they will arrive in...)
  if (((prevMessageState == 0x0) && (messageState == 0x7)) || ((prevMessageState == 0x7) && (messageState == 0x0)))
  {
    prevMessageState = messageState;
    Serial.print(millisSinceLastPVT); // Format the data for Serial Plotter
    Serial.print(',');
    Serial.print(millisSinceLastATT);
    Serial.print(',');
    Serial.println(millisSinceLastINS);
  }
}

Ah. More clues:

image

image

OK. The trick is:

myGNSS.setVal8(UBLOX_CFG_RATE_NAV_PRIO, 30); //Output rate of priority navigation mode messages

Full SPI code:

#include <Wire.h> //Needed for I2C to GPS

#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
SFE_UBLOX_GNSS_SPI myGNSS;

unsigned long millisSinceLastPVT = 0;
unsigned long millisSinceLastATT = 0;
unsigned long millisSinceLastINS = 0;
uint8_t messageState = 0;

void newPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
  static unsigned long lastPVT = 0;
  millisSinceLastPVT = millis() - lastPVT;
  lastPVT = millis();
  messageState ^= 0x1; // Toggle a bit in mesageState
}

void newATTdata(UBX_NAV_ATT_data_t *ubxDataStruct)
{
  static unsigned long lastATT = 0;
  millisSinceLastATT = millis() - lastATT;
  lastATT = millis();
  messageState ^= 0x2; // Toggle a bit in mesageState
}

void newINSdata(UBX_ESF_INS_data_t *ubxDataStruct)
{
  static unsigned long lastINS = 0;
  millisSinceLastINS = millis() - lastINS;
  lastINS = millis();
  messageState ^= 0x4; // Toggle a bit in mesageState
}

void setup()
{
  delay(1000);
  
  Serial.begin(115200);
  SPI.begin();

  //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

  while (myGNSS.begin(4) == false) //Connect to the u-blox module using SPI. CS is pin 4
  {
    delay(1000);
  }

  myGNSS.factoryDefault(); delay(5000); //Reset the GNSS module

  myGNSS.setSPIOutput(COM_TYPE_UBX); //Set the SPI port to output UBX only (turn off NMEA noise)

  myGNSS.setAutoPVTcallbackPtr(&newPVTdata); // Enable automatic NAV PVT messages
  myGNSS.setAutoNAVATTcallbackPtr(&newATTdata); // Enable automatic NAV ATT messages
  myGNSS.setAutoESFINScallbackPtr(&newINSdata); // Enable automatic ESF INS messages

  myGNSS.setVal8(UBLOX_CFG_RATE_NAV_PRIO, 30); //Output rate of priority navigation mode messages
}

void loop()
{
  myGNSS.checkUblox(); // Check for the arrival of new data and process it.
  myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.

  static uint8_t prevMessageState = 0;
  // Wait until all three messages have been received. (We don't know what order they will arrive in...)
  if (((prevMessageState == 0x0) && (messageState == 0x7)) || ((prevMessageState == 0x7) && (messageState == 0x0)))
  {
    prevMessageState = messageState;
    Serial.print(millisSinceLastPVT); // Format the data for Serial Plotter
    Serial.print(',');
    Serial.print(millisSinceLastATT);
    Serial.print(',');
    Serial.println(millisSinceLastINS);
  }
}

With no antenna attached, I see:

image

With antenna attached we are still OK:

image

I think we are done? Please close this issue - if you are happy!

Best,
Paul

Ah. OK. I2C is still having problems - even at 400kHz:

image

I think SPI is the way forward for you....

Here's the hybrid example:

#include <Wire.h> //Needed for I2C to GPS

#define USE_I2C // Comment to use SPI

#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
#ifdef USE_I2C
SFE_UBLOX_GNSS myGNSS;
#else
SFE_UBLOX_GNSS_SPI myGNSS;
#endif

unsigned long millisSinceLastPVT = 0;
unsigned long millisSinceLastATT = 0;
unsigned long millisSinceLastINS = 0;
uint8_t messageState = 0;

void newPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
  static unsigned long lastPVT = 0;
  millisSinceLastPVT = millis() - lastPVT;
  lastPVT = millis();
  messageState ^= 0x1; // Toggle a bit in mesageState
}

void newATTdata(UBX_NAV_ATT_data_t *ubxDataStruct)
{
  static unsigned long lastATT = 0;
  millisSinceLastATT = millis() - lastATT;
  lastATT = millis();
  messageState ^= 0x2; // Toggle a bit in mesageState
}

void newINSdata(UBX_ESF_INS_data_t *ubxDataStruct)
{
  static unsigned long lastINS = 0;
  millisSinceLastINS = millis() - lastINS;
  lastINS = millis();
  messageState ^= 0x4; // Toggle a bit in mesageState
}

void setup()
{
  delay(1000);
  
  Serial.begin(115200);
  
#ifdef USE_I2C
  Wire.begin();
  Wire.setClock(400000);
#else
  SPI.begin();
#endif

  //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial

#ifdef USE_I2C
  while (myGNSS.begin() == false) //Connect to the u-blox module using I2C
#else
  while (myGNSS.begin(4) == false) //Connect to the u-blox module using SPI. CS is pin 4
#endif
  {
    delay(1000);
  }

  myGNSS.factoryDefault(); delay(5000); //Reset the GNSS module

#ifdef USE_I2C
  myGNSS.setI2CpollingWait(5); //Set the polling wait to 5ms (needed because we are not calling setNavigationFrequency)
  myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
#else
  myGNSS.setSPIOutput(COM_TYPE_UBX); //Set the SPI port to output UBX only (turn off NMEA noise)
#endif

  myGNSS.setAutoPVTcallbackPtr(&newPVTdata); // Enable automatic NAV PVT messages
  myGNSS.setAutoNAVATTcallbackPtr(&newATTdata); // Enable automatic NAV ATT messages
  myGNSS.setAutoESFINScallbackPtr(&newINSdata); // Enable automatic ESF INS messages

  myGNSS.setVal8(UBLOX_CFG_RATE_NAV_PRIO, 30); //Output rate of priority navigation mode messages
}

void loop()
{
  myGNSS.checkUblox(); // Check for the arrival of new data and process it.
  myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.

  static uint8_t prevMessageState = 0;
  // Wait until all three messages have been received. (We don't know what order they will arrive in...)
  if (((prevMessageState == 0x0) && (messageState == 0x7)) || ((prevMessageState == 0x7) && (messageState == 0x0)))
  {
    prevMessageState = messageState;
    Serial.print(millisSinceLastPVT); // Format the data for Serial Plotter
    Serial.print(',');
    Serial.print(millisSinceLastATT);
    Serial.print(',');
    Serial.println(millisSinceLastINS);
  }
}

Hi Paul, I was just about to write you. We came to the exact same conclusion!
The way we understand it is that for 30Hz on F9R we need to set the rate differently. Instead of setting CFG-RATE-MEAS to 30Hz, we set it to around 5Hz and then update the CFG-RATE-PRIO to 30Hz. We should probably update this method in the library.
Thanks for all your help!
Best,
Simon

I've uploaded a screenshot of our settings from this ublox post

CFG-RATE-MEAS = 200 ms // Nominal time b/w GNSS measurements
CFG-RATE-NAV = 5 // Ratio of number of measurements to navigation solutions
CFG-RATE-PRIO = 30 Hz // Output rate of priority navigation mode msgs

EDIT: by using these settings we are also able to achieve 30Hz via I2C at 400kHz.

Screenshot 2023-03-07 at 13 59 37

Thanks Simon - glad that's working for you,

Strange... I'm still seeing ~15Hz:

image

I still think SPI is the way forward for you.

Best,
Paul