GPS NEO-M9N PVT speed

I’m running this GPS with an Arduino RP2040, and I think the GPS is fine. While the following code snippet normally runs in 1100-1300 ms, it’s occasionally 3500 ms or more. What do I need to do to prevent the extra wait? The setup is straight from Example 1.
/begin snippet
int PDOP = 1000;
while (PDOP > 250) { // latitude == 0 | longitude == 0) {
Serial.print("Waiting for GPS lock ");
Serial.println(millis());
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) {
latitude = myGNSS.getLatitude();
longitude = myGNSS.getLongitude();
PDOP = myGNSS.getPDOP();
}
delay(1000);
}
/end snippet

Thank you!

I want this setup to control an autonomous vehicle, and don’t think I can let it go for 3.5 seconds without update. TIA!

Hi,

Delete the delay(1000) and give it another try.

Hope this helps,
Paul

Paul,

Many thanks for your reply!

It looks like it still wants to take an occasional nap, which is not good for my rocket. Serial monitor output:

Waiting for GPS lock 7830

Waiting for GPS lock 7831

Waiting for GPS lock 7831

Waiting for GPS lock 20860

Waiting for GPS lock 20860

Waiting for GPS lock 20861

I'm hoping to find and fix the cause of this delay.  Some buffer or something or a setting?

Best regards,

       Roger

Hi Roger,

Your posts don’t really tell me the full story. I’m going to have to make some guesses here.

From the code snippet you provided, millis() should be increasing in ~1000msec intervals each time your code goes around the while loop. If you are polling the Position Velocity Time, it can take up to a full second for PVT to be returned. I’m guessing that the times you provided, ~7800 and ~20860, are the final print from multiple tests? I.e. the time when getInvalidLlh is false (the LLH becomes valid) and PDOP is less than 250 - on multiple tests?

If you are powering off the module between each test, then I think this is expected. Your code is waiting for LLH to become valid (and for PDOP to be less than 250). I.e. you are measuring the “time to first valid fix”. So, yes, that could take a variable amount of time. It depends on the NEO being able to do a hot start, and your view of the sky as it calculates LLH.

I don’t have an Arduino RP2040, so I can’t replicate your setup. All I can do is look at your code and the output you provide and try and figure out what’s going on. If you need more help, please post your full code and the full output from two tests - one where it got a fix quickly, and one where it took 7 or 20 seconds.

Best wishes,
Paul

Paul,

Thanks for the reply!

Overall what I’m trying to do is get the RP2040 and GPS to guide a finless rocket straight up, so locating the launch lat/lon is critical. I can wait on getting a good fix: it’s worth it. However a 3.5 second period lacking guidance could be disastrous on a 20 second burn.

I’ll include the code, though it’s a total hack job in its current state.

Thanks again, and best regards,

Roger

/*
Configuring the GNSS to automatically send position reports over I2C
By: Nathan Seidle and Thorsten von Eicken
SparkFun Electronics
License: MIT. See license file for more information but you can
basically do whatever you want with this code.

This example shows how to configure the U-Blox GNSS the send navigation reports automatically
and retrieving the latest one via getPVT. This eliminates the blocking in getPVT while the GNSS
produces a fresh navigation solution at the expense of returning a slighly old solution.

This can be used over serial or over I2C, this example shows the I2C use. With serial the GNSS
simply outputs the UBX_NAV_PVT packet. With I2C it queues it into its internal I2C buffer (4KB in
size?) where it can be retrieved in the next I2C poll.

Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: [https://www.sparkfun.com/products/15136](https://www.sparkfun.com/products/15136)
NEO-M8P RTK: [https://www.sparkfun.com/products/15005](https://www.sparkfun.com/products/15005)
SAM-M8Q: [https://www.sparkfun.com/products/15106](https://www.sparkfun.com/products/15106)

Hardware Connections:
Plug a Qwiic cable into the GNSS and a BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper ([https://www.sparkfun.com/products/14425](https://www.sparkfun.com/products/14425))
Open the serial monitor at 115200 baud to see the output
*/

#include <Wire.h> //Needed for I2C to GNSS
#include <Arduino_LSM6DSOX.h>
// #include <WiFiNINA.h>

#include "SparkFun_u-blox_GNSS_Arduino_Library.h" //[http://librarymanager/All#SparkFun_u-blox_GNSS](http://librarymanager/All#SparkFun_u-blox_GNSS)
SFE_UBLOX_GNSS myGNSS;
#include <SD.h>
//We always need to set the CS Pin
int CS_pin = 4; // 10;
// int pow_pin = 8;
int record = 0;
long milly;

// first KF
long lat = 449242185;
long lon = -934849672;
float p = 100;
float r1 = 100;
float K = 0.5;

// Const acc, const vel KF
long PadLat = 449241984; // Mtka dddddddd
long PadLong = -934849672;
float pl = float(PadLat);
float cosLat = cos(pl / 10000000.0 * PI / 180);
long etime;
float xN = 0; // Initial postion guess - on the pad
float xE = 0;
float vN = 0.0;
float vE = 0.0; // Initial velocity - still
float accN = 0.0;
float accE = 0.0; // Initial acceleration - still
float PxN = 6.01 * 6.01; // Initial position uncertainty
float PxE = PxN;
float PvN = 1; //0.5 * 0.5; // also m or m^2
float PvE = 1;
float rx = 2.0; // * 2.0; // m! GPS position uncertainty - 2m
float rv = 0.05 * 0.05; // GPS velocity uncertainty - 0.05 m/s
float ra = (0.061 / 1000 * 9.8) * (0.061 / 1000 * 9.8); // 2040 accel variance - 0.061mg
float KvN; // Kalman gain velocity North
float KvE;
float KxN; // Kalman gain North position
float KxE;
float dT; // time increment
float x, y, z; // 2040 accels - body frame
float xcorr, ycorr, zcorr; // accelerometer corrections
File dataKFile;
#include <Adafruit_BNO055.h> // Thanks, Adafruit!
#include <Adafruit_Sensor.h>
// Check I2C device address and correct line below (by default address is 0x29 or 0x28)
// id, address
Adafruit_BNO055 bno = Adafruit_BNO055(55, 0x28, &Wire);
// Quaternion data
imu::Quaternion quat, padquat, nowquat;

void setup() {
Serial.begin(19200);
while (!Serial)
; //Wait for user to open terminal
// Serial.println("SparkFun u-blox Example");

pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
delay(1);
// now it's safe to use SD.begin(4) and Ethernet.begin()
// Serial.begin(9600);
// delay(1000);
Serial.println("Initializing Card");
//CS Pin is an output
if (!SD.begin(CS_pin)) {
Serial.println("Card Failure");
return;
}
Serial.println("Card Ready");

Wire.begin();

if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}

myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.setNavigationFrequency(20); // 2); //Produce two solutions per second max 25
myGNSS.setAutoPVT(true); //Tell the GNSS to "send" each solution
//myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR

/* Initialise the sensor */
if (!bno.begin()) {
/* There was a problem detecting the BNO055 ... check your connections */
Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!");
while (1)
;
}

delay(1000);

/* Use external crystal for better accuracy */
bno.setExtCrystalUse(true);

Serial.println("Getting data points");
File dataFile = SD.open("LLH.csv", FILE_WRITE);
if (dataFile) {
dataFile.println("Record, Millis, Lat, Lon, Alt, VelN, VelE, accY, accZ"); // -x is flight direction
dataFile.close();
}
dataKFile = SD.open("KF.csv", FILE_WRITE);
if (dataKFile) {
dataKFile.println("Record, Millis, y-ycorr, accN, velN, PvN, ra, KvelN, xN, PxN, KxN, z-zcorr, accE, velE, PvE, ra, KvelE, xE, PxE, KxE"); // -x is flight direction
dataKFile.close();
}
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");

while (1)
;
}
// Wait for GPS lock
long latitude = 0;
long longitude = 0;
int PDOP = 1000;
while (PDOP > 250) { // latitude == 0 | longitude == 0) {
Serial.print("Waiting for GPS lock ");
Serial.println(millis());
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) {
// latitude = myGNSS.getLatitude();
// longitude = myGNSS.getLongitude();
PDOP = myGNSS.getPDOP();
}
// delay(1000);
}
// delay(10000); // give it some more time

int count = 0;
/*
PadLat = 0;
PadLong = 0;
*/
Serial.println("Finding myself");

while (K > 0.01) {
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) {
/* if (abs(PadLat - 449241057) < 10000 & abs(PadLong - -934849672) < 15000) { // specific to tonka ...
PadLat += myGNSS.getLatitude();
PadLong += myGNSS.getLongitude();
count++;
*/
latitude = myGNSS.getLatitude();
// old KF
// Serial.println();
K = p / (p + r1);
lat = lat + K * (latitude - lat);
p = (1 - K) * p;
/*
Serial.print("Lat ");
Serial.print(lat);
Serial.print(" K ");
Serial.println(K, 4);
*/
}
PadLat = lat;
}
K = 0.5;
p = 100; // reset for longitude KF
while (K > 0.01) {
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) {
longitude = myGNSS.getLongitude();
// old KF
// Serial.println();
K = p / (p + r1);
lon = lon + K * (longitude - lon);
p = (1 - K) * p;
/*
Serial.print("Lat ");
Serial.print(lat);
Serial.print(" K ");
Serial.println(K, 4);
*/
}
}
PadLat = lat;
PadLong = lon;
pl = float(PadLat);
cosLat = cos(pl / 10000000 * PI / 180);
Serial.print("cosLat: ");
Serial.println(cosLat, 5);

/*PadLat /= count;
PadLong /= count; */
Serial.print("Pad Lat: ");
Serial.print(PadLat);
Serial.print(" Pad Long: ");
Serial.println(PadLong);
/*
Serial.print(" count: ");
Serial.println(count);
*/
for (int i = 0; i < 200; i++) {
if (myGNSS.getPVT() && (myGNSS.getInvalidLlh() == false)) {
File dataFile = SD.open("LLH.csv", FILE_WRITE);
delay(10);
if (dataFile) {
milly = millis();
dataFile.print(record);
dataFile.print(",");
dataFile.print(milly);
dataFile.print(",");
latitude = myGNSS.getLatitude();
dataFile.print(latitude);
dataFile.print(",");
longitude = myGNSS.getLongitude();
dataFile.print(longitude);
dataFile.print(",");
long altitude = myGNSS.getAltitude();
dataFile.print(altitude);
dataFile.print(",");
int nedNorthVel = myGNSS.getNedNorthVel(); // mm/s
dataFile.print(nedNorthVel);
dataFile.print(",");
int nedEastVel = myGNSS.getNedEastVel();
dataFile.print(nedEastVel);

if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);

// dataFile.print(x);
dataFile.print(",");
dataFile.print(y, 5);
dataFile.print(",");
dataFile.print(z, 5);
}
dataFile.println();
delay(10);

dataFile.close();
// Serial.println(record);
record++;
delay(10);
} else {
Serial.println("Couldn't open log file");
}
} else {
delay(10);
}
}
// Calibrate accelerometers
xcorr = 0.0;
ycorr = 0.0;
zcorr = 0.0;
count = 0;
for (int i = 0; i < 2000; i++) {
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(x, y, z);
xcorr += x;
ycorr = ycorr + y;
zcorr += z;
count++;
}
}
xcorr /= count;
ycorr = ycorr / count;
zcorr /= count;
Serial.print("ycorr ");
Serial.println(ycorr, 5);
etime = millis();

// Quaternion data
padquat = bno.getQuat();
printquat("pad", padquat);
}

boolean once = true;
void loop() {
// TBD
}

With the 1000 ms delay reinstated, here is an output:

Waiting for GPS lock 15551

Waiting for GPS lock 16855

Waiting for GPS lock 18165

Waiting for GPS lock 19494

Waiting for GPS lock 20794

Waiting for GPS lock 22103

Waiting for GPS lock 23393

Waiting for GPS lock 24408

Waiting for GPS lock 25408

Waiting for GPS lock 26452

Waiting for GPS lock 27500

Waiting for GPS lock 28544

Waiting for GPS lock 29589

Waiting for GPS lock 30631

Waiting for GPS lock 31676

Waiting for GPS lock 32718

Waiting for GPS lock 33766

Waiting for GPS lock 34820

Waiting for GPS lock 35869

Waiting for GPS lock 36913

Waiting for GPS lock 37960

Waiting for GPS lock 39008

Waiting for GPS lock 40010

Waiting for GPS lock 55686

Waiting for GPS lock 56686

It took a big nap after 40010, which is what I'd like to solve.

Thanks!!

Hi Roger,

Thanks for sharing your code. That helps a lot.

I didn’t realize you were running the NEO at 20Hz. The issue will be UART1. By default, UART1 will be trying to output the standard NMEA messages - at 38400 baud. At 20Hz, those messages will overflow the port - and cause problems on I2C. The solution is to disable NMEA on UART1:

myGNSS.setUART1Output(COM_TYPE_UBX);

Or, disable it completely with:

myGNSS.setUART1Output(0);

This should solve your issue.

Best wishes,
Paul

And do delete the delay(1000). That will cause problems - at 20Hz. You will have messages backing up in the module’s I2C buffer during the delay.

1 Like

Definitely going to want to cull the messages on unused interfaces, and get just down to position messages. Watch for $GNTXT “txbuf,alloc” warnings, this indicates data is log-jamming on other slower interfaces.
Between 10 and 25 Hz it going to drop to 16 satellite solutions. Consider GPS only operation.
Consider Airborne 4G dynamics.

Thanks to you both! I’m learning lots here.

While the loop is running faster now, it still occasionally slows. What can I check with the GPS to see if it’s the cause?

TIA, again!

Hi Roger,

Things to try:

  • Try setting the navigation rate to a more modest 10Hz, or 5Hz.

  • Try increasing the I2C clock speed to 400kHz with Wire.setClock(400000); after the Wire.begin();

Do either make things better - or worse?

I am seeing some posts about the RP2040 having issues with I2C clock stretching. The u-blox modules do use clock stretching. I hope it’s not that, as that could be tricky to diagnose and solve… Do you have another board you could test your code on - ideally with a different type of processor?

Best,
Paul

Hi Roger,

I am doing my best to try and replicate your issue…

I now have a SparkFun Thing Plus RP2040. I have it connected to a SparkFun NEO-M9N SMA breakout via Qwiic. I am running Earle Philhower’s RP2040 board package 3.9.5. It has a board definition for the Thing Plus. I am running a test script which will trigger my logic analyzer if the PVT message is ‘late’. See the code below. (The Thing Plus Qwiic connector is Wire1, not Wire)

I was wondering if your issue was related to the I2C pull-ups. We recommend removing any extra pull-up resistors from the bus when using u-blox modules, as the modules have their own built-in internal pull-ups. But doing that may cause problems for any other devices on the bus, and I know you also have a BNO055 on there too. In my setup I have (re)connected the pull-ups on both the RP2040 Thing Plus and the NEO breakout, but it still doesn’t replicate your issue.

The code is chugging along nicely. I can get it to glitch by disconnecting and reconnecting the antenna. But the delay is only a few milliseconds and it recovers on the next cycle. Nothing like the delays you are reporting…

So, sorry, I can’t replicate what you’re seeing. My advice would be to try changing the pull-ups to see if that makes any difference. And try running my code example - making sure you have nothing else connected to the bus, to isolate the issue. See if that makes any difference. (Change Wire1 to Wire if you need to.)

Best wishes,
Paul

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

#include "SparkFun_u-blox_GNSS_Arduino_Library.h" // http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;

unsigned long lastPVT; // Record when the last PVT message was received

// Check GPS lock
long latitude = 0;
long longitude = 0;
int PDOP = 1000;
bool invalidLLH = true;

// Trigger pin for logic analyzer
int triggerPin = 22;

void setup() {
  delay(2000);
  
  pinMode(triggerPin, OUTPUT);
  digitalWrite(triggerPin, HIGH);
  
  Serial.begin(115200); // Use 115200. 19200 could be too slow...
  while (!Serial)
    ; //Wait for user to open terminal

  Wire1.begin(); // SparkFun Thing Plus RP2040 Qwiic port is Wire1, not Wire
  
  if (myGNSS.begin(Wire1) == false) //Connect to the u-blox module using Wire1 port
  {
    Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
    while (1)
      ;
  }
  
  myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
  myGNSS.setUART1Output(COM_TYPE_UBX); //Set the UART1 port to output UBX only (turn off NMEA noise)
  myGNSS.setNavigationFrequency(20); //Produce 20 solutions per second max 25
  myGNSS.setAutoPVT(true); //Tell the GNSS to "send" each solution (make PVT periodic)
  //myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR

  Serial.println("Here we go!");

  lastPVT = millis();
}

void loop() {
  if (myGNSS.getPVT()) { // getPVT returns true when a new PVT message has been received
    lastPVT = millis();
    latitude = myGNSS.getLatitude();
    longitude = myGNSS.getLongitude();
    PDOP = myGNSS.getPDOP();
    invalidLLH = myGNSS.getInvalidLlh();
    if (invalidLLH == true) {
      Serial.println("LLH is invalid!");
      Serial.flush();
    }
  }
  
  if ((millis() - lastPVT) > 55) { // At 20Hz, getPVT should return true every 50ms
    digitalWrite(triggerPin, LOW);
    Serial.print("lastPVT was more than ");
    Serial.print(millis() - lastPVT);
    Serial.println(" ms ago!");
    Serial.flush();
  }
  else {
    digitalWrite(triggerPin, HIGH);
  }
}

Paul, thanks once again. When I run your code, it still hangs up for 3+ seconds occasionally. What makes it do that? More importantly, how do I prevent it? Is there something that I can flush, for example? When I run my code with the BNO removed, thus nothing else on the I2C bus, I also get occasional 3+ second hangups, evev when just polling the chip while PDOP decreases. Did I get a bum chip? You mentioned pull-ups, and I have none at present or earlier.

Hi Roger,

It’s unlikely to be a faulty chip. This sounds more like a bus issue.

Which Arduino RP2040 board package are you using? I had good results with Earle Philhower’s RP2040 board package 3.9.5. Are you using the same one? (I haven’t tried the Arduino Mbed OS RP2040 Boards package.)

Best wishes,
Paul

Having had difficulty with Philhower’s package on my Mac, I was using an Mbed package successfully. Earlier I had an SD card in the project, connected by SPI. With the SD card removed I’m still getting lapses, but most are closer to 1 second than three. Would there be some connection between I2C and SPI causing delays?

Hi Roger,

From your description, I can’t tell if the issue is linked to the GNSS and I2C, or if it is being caused by the other devices and code you’re adding.

Please try the above code example again. Change only the Wire port if you need to - from Wire1 to Wire. Does that example work successfully - without hangs? Then change / add one thing and retest each time. If you add a bunch of changes all in one go, it is very difficult to work out which change caused the hang…

Best wishes,
Paul