Why is my ZED-F9P responding so slowly?

See below the output at Serial monitor from my code. Although I have setNavigationFrequency to 2 solutions/second it often takes over 2 seconds to respond, and even then it only updates the GPS coordinates every 5 - 10 seconds. (NB: the ms value in square brackets at end of each line is time spent in that loop)

Also it sometimes respond really quick, even < 100ms.
What am I doing wrong? (TIA)

My hardware:
Micromod Main Double board, fitted with:
Processor board : Micromod Teensy
Function board 0: Micromod ZED-F9P GNSS
Function board 1: Micromod ESP-32 WiFi/Bluetooth

My code, on Teensy:

#include <Streaming.h> 
#include <TimeLib.h> 
#include <SparkFun_u-blox_GNSS_v3.h> 

#define ZED Serial1                       // for TX of RTCM3 corrections + RX of GNS status data 

SFE_UBLOX_GNSS_SERIAL gnss;

time_t timeU;                             // UTC 
uint32_t t0,t1,t2;                        // timestamps
int32_t latitude, longitude, altitude;    // GPS coords

void setup() {
  Serial.begin(115200);                   // not needed teensy
  while (!Serial && millis() < 1000);
  delay(1000);
  Serial << "\n\n======= GPS_basic.A =======\n";
  initGNS();
  processGPStime();                       // UTC time
  reportDateTime(timeU);
  Serial << '\n' << '\n';
  latitude  = gnss.getLatitude();                     // (10^7)°
  longitude = gnss.getLongitude();                    // (10^7)°
  Serial << "Lat/Lon: " << latitude << " / " << longitude << " ° (*10^7)";
  Serial << "\n\n------ setup() done -----\n"; 
  t0 = millis();
}

uint loops = 0;

void loop() {  
  t1 = millis();
  //if (t1 - t0 >= 1000) {
    Serial << _WIDTHZ(++loops,3) << ": ";
    processGPStime();
    reportDateTime(timeU);
    latitude  = gnss.getLatitude(); 
    longitude = gnss.getLongitude();
    Serial << "  Lat/Lon " << latitude << " / " << longitude; // << "° (*10^7) ";
    t0 = millis();
    Serial << "  [" << (t0-t1) << " ms]\n";    
  //}
}

void initGNS(){  
  Serial << " * initialise GNSS  : ";
  ZED.begin(921600);                                            // 8 x 115200 bps = 115.2 kB/s 
  while (!gnss.begin(ZED)) {                                    // Connect to ZED-F9P over Serial 
          Serial << ".";
          delay (1000);
          }
  gnss.setUART1Output(COM_TYPE_UBX);                            // same, for Serial/UART1 connections
  gnss.setNavigationFrequency(2);                               // set for 2 solutions/second  
  Serial << "OK [2 sol/sec]\n";   
  Serial << " * Wait for GPS Fix : ";      
  while (!gnss.getGnssFixOk()) {                                // Connect to the u-blox module 
          Serial << ".";
          delay (1000);
          }
  Serial << "OK\n";
  Serial << " * Wait for GPS PVT : ";
  while (!gnss.getPVT()) {
    Serial << "."; 
    delay(500);
    } 
  Serial << " OK\n\n";  
  }

void processGPStime(){
  tmElements_t tm;  
  tm.Year   = gnss.getYear(1) - 1970;                           // load UTC time elements
  tm.Month  = gnss.getMonth(1);
  tm.Day    = gnss.getDay(1);
  tm.Hour   = gnss.getHour(1);
  tm.Minute = gnss.getMinute(1);
  tm.Second = gnss.getSecond(1);    
  timeU = makeTime(tm);                                         // UTC = GPS time (as unixtime) 
  }

void reportDateTime(time_t t){
  if (t >=  946684800 && t < 2524608000){                 // if in valid years (1/1/2000 to 31/12/2049)
    Serial << "UTC "; 
    Serial  << _WIDTHZ(  hour(t),2) << ":" 
            << _WIDTHZ(minute(t),2) << ":" 
            << _WIDTHZ(second(t),2) << " "; 
    Serial  << dayShortStr(weekday(t)) << " "
            << _WIDTHZ(  day(t),2) << "/"
            << _WIDTHZ(month(t),2) << "/"
            <<          year(t);
    }
  else Serial << " *** time invalid (2) ***";
  }

I don’t think 2 Hz would overwhelm UART2 at 38400 baud, but would suggest you disable output on UART2

Otherwise you’re going to want to inspect the output on UART1 to understand what’s happening there.

I doubt that. The only instuctions that are slow are getLatittude() and getLongitude(). If I comment them out, I have to enable the if (t1-t0) >= 1000) { test, to slow the loop to a readable speed.

The question is why are these two requests to the ZED adding over 2 seconds delay on most occasions, but not always? I should be able to get a current Lat/Lon readings twice a second, since I’ve set up for 2 solutions per second.

The time output isn’t exactly consistent either, with the supposed elapsed time.
So either somethings blocking, or there are secondary issues.
You’ll have to debug the library, and see what pumps the data, or if you have to spin on the getPVT().
Observing what’s coming out the ZED’s UART1 would be instructive as to whether you’re fighting an issue with the receiver, or your application of the library.

Yes it’s weird the time gap jumps around. I’ve even seen it as short as 85ms, although even that’s a long time in a Teensy.

AFAIK my code is not unusual. I did start from Sparkfun examples way back, but I’d like to know if my code is basically wrong, or not.

Well its not working the way you want, I’d take that as a sign..

Perhaps try something closer to this, where getPVT() might pump/gate the availability of new data more coherently.

Note it’s use of gnss.setAutoPVT(true); //Tell the GNSS to ā€œsendā€ each solution

If you’re instead POLLING, the round trip of the query/response might be multiple epochs, rather than simply output with periodicity.

1 Like

Hi @ninja2 ,

I agree with @clive1, I think the slow rate is all due to the way you have structured your code. Your use of 1ms timeouts is unusual and unlikely to work the way you expect it to. By default, the library polls (requests) the PVT message. Making it periodic (setting it to ā€œAutoā€) helps a lot and changes the way getPVT works. getPVT will then return true if fresh PVT data has arrived since the last call, otherwise it returns false.

For the ZED-F9P, I’d recommend using v3 of the library. It looks like you already are. Clive’s link is to a v2 example. For v3, the simplest ā€œAuto PVTā€ example is below.

I hope this helps,
Paul

1 Like

Well it turns out clive1’s early hunch about secondary issues was right. For some reason the ZED-F9P doesn’t like to use 921600 baud. If I reduce back to 115200 it works much better, although not ideal yet.

(Why 921600 I hear you say. Prior to this discussion I was developing a reliable feed from a public RTCM3 correction data stream into the ESP32 over WiFi, and forwarded over Serial into the Teensy for forwarding onto the ZED-F9P. This proved unreliable at first so I tried several fixes including buffering and the 921600 baud rates. In the end buffering was enough, even at 115200 to the ZED)

Here’s my code and output now. It curious that it starts out with < 500ms between readings (i.e. 2 solutions/sec) but settles at around 1 solution / sec.

That rate is OK for my purposes but I’d like to know why doesn’t it stay at 2/sec?

Note there are no calls to getPVT() or autoPVT() at all now:

#include <Streaming.h> 
#include <TimeLib.h> 
#include <SparkFun_u-blox_GNSS_v3.h> 

SFE_UBLOX_GNSS_SERIAL gnss;

#define ZED Serial1                       // for TX of RTCM3 corrections + RX of GNS status data 

time_t timeU;                             // UTC 
uint32_t t0,t1,t2;                        // timestamps
int32_t latitude, longitude, accuracy;    // GPS coords

int32_t offset = 0;

void setup() {
  Serial.begin(115200);                   // not needed teensy
  while (!Serial && millis() < 1000);
  delay(1000);
  Serial << "\n\n======= ZED_online =======\n";
  initGNS();
  processGPStime();                       // UTC time
  reportDateTime(timeU);
  t0 = millis();
  Serial << "\n --- setup() done ---\n"; 
}

uint loops = 0;

void loop() {  
  t1 = millis();
//if (t1 - t0 >= 500) {
  Serial << _WIDTHZ(++loops,3) << ": ";
  processGPStime();
  reportDateTime(timeU);
  latitude  = gnss.getLatitude(); 
  longitude = gnss.getLongitude();
  accuracy  = gnss.getPositionAccuracy();
  Serial  << "  Lat/Lon " << latitude+offset 
          <<        " / " << longitude-offset
          <<       " +/-" << accuracy << " (mm)";
  t0 = millis();
  Serial << "  [" << (t0-t1) << " ms]\n";    
//}
}

void initGNS(){  
  Serial << "* initialise GNSS  : ";
  ZED.begin(115200);                                                  // ditto
//ZED.begin(921600);                                                  // doesn't work!
  while (!gnss.begin(ZED)) {                                    // Connect to ZED-F9P over Serial 
          Serial << ".";
          delay (1000);
          }
  gnss.setUART1Output(COM_TYPE_UBX);                            // same, for Serial/UART1 connections
  gnss.setNavigationFrequency(2);                               // set for 2 solutions/second  
  Serial << "OK [2 sol/sec]\n";   
  Serial << "* Wait for GPS Fix : ";      
  while (!gnss.getGnssFixOk()) {                                // Connect to the u-blox module 
          Serial << ".";
          delay (1000);
          }
  Serial << "OK\n";
  }

void processGPStime(){
  tmElements_t tm;  
  tm.Year   = gnss.getYear(1) - 1970;                           // load UTC time elements
  tm.Month  = gnss.getMonth(1);
  tm.Day    = gnss.getDay(1);
  tm.Hour   = gnss.getHour(1);
  tm.Minute = gnss.getMinute(1);
  tm.Second = gnss.getSecond(1);    
  timeU = makeTime(tm);                                         // UTC = GPS time (as unixtime) 
  }

void reportDateTime(time_t t){
  if (t >=  946684800 && t < 2524608000){                 // if in valid years (1/1/2000 to 31/12/2049)
    Serial << "* UTC "; 
    Serial  << _WIDTHZ(  hour(t),2) << ":" 
            << _WIDTHZ(minute(t),2) << ":" 
            << _WIDTHZ(second(t),2) << " "; 
    Serial  << dayShortStr(weekday(t)) << " "
            << _WIDTHZ(  day(t),2) << "/"
            << _WIDTHZ(month(t),2) << "/"
            <<          year(t);
    }
  else Serial << " *** time invalid (2) ***";
  }

Because you haven’t selected UBX-NAV-PVT to be output periodically?

As Clive says, making PVT periodic (setAutoPVT) will help a lot. Also, you need to restructure your code. Move all of the getYear() … getSecond() and getLatitude(), getLongitude() and getPositionAccuracy() inside a single if (gnss.getPVT()) { ... } statement. Remove those 1 millisecond timeouts: use getYear() not getYear(1).

I hope this helps,
Paul

Thanks Paul ( & Clive), I’m happy to do those changes but I’d like to know why I’m doing them … since AFAIK the output I’m seeing is working fine, so what difference will re-instating getPVT() make? Is there a Wiki or similar explaining how to use this library? From the AutoPVT example you shared I can see there’s a lot of capability, and I’d like to understand better.

PS: you keep mentioning 1 ms timeouts, sorry but I don’t know what you mean? I don’t have any in this code, and the line //if (t1 - t0 >= 500) { is commented out/disabled.

Perhaps you mean the delay(1000) instructions inside the while loops?

Because you are polling the PVT message, instead of it being periodic, the library polls (requests) each PVT message and waits (stalls) until it arrives. That can take up to the full navigation cycle to be delivered, 500ms in your case, depending on when the poll takes place.

Something in your code is causing the message to be polled twice in each cycle. I can’t see exactly where that is happening, but it is happening. It usually happens if you call (e.g.) getLatitude twice in one cycle. The library recognises that the Latitude is ā€˜stale’ - has been read before - and so sends a fresh poll and waits for fresh data.

If you restructure your code, and make PVT periodic, you will get better results.

The (1) in getYear(1) sets the timeout to 1ms. That is probably having undesirable side-effects…

Ah! Got it! getPositionAccuracy polls the NAV-HPPOSECEF message, not the NAV-PVT message. It returns the HPPOSECEF pAcc (position accuracy) value. So, you are polling two messages each cycle: PVT and HPPOSECEF. Each will take (up to) 500ms to be returned.

If you replace getPositionAccuracy with getHorizontalAccEst, you will see a big improvement. getHorizontalAccEst returns the hAcc (horizontal accuracy estimate) from the PVT message.

1 Like

And I just proved your theory/discovery correct! :slightly_smiling_face: using my version of the AutoPVT example clive1 pointed to earlier. It has all your suggestions in the code, including even this:

if (gnss.getPVT() && (!gnss.getInvalidLlh())) {

(I don’t know what getInvalidLlh() does. But it looks impressive)

Following are 2 snapshots of the output from my AutoPVT code.

In the first I am calling both getHorizontalAccEst() and getPositionAccuracy in each loop(). Their values appear in the HAcc and Pos.Acc columns. Note the time gaps in the last 3 columns, summing to ~ 1 second or half the rate of 2 solutions/second I chose (as you predict)

In the Second I removed getPositionAccuracy while still calling getHorizontalAccEst()
Now the time gaps in the last 3 columns, summing to ~ 500ms, matching 2 solutions/second (again as you predicted)


Here is the loop() section of my code, after getPositionAccuracy was commented out. I’d like to know what all these mean … I can guess a few, but SIV has me stumped:

void loop(){
  if (gnss.getPVT() && (!gnss.getInvalidLlh())) {
    t1 = millis();
    int32_t latitude  = gnss.getLatitude() ;
    int32_t longitude = gnss.getLongitude();
    int32_t altitude  = gnss.getAltitude();
  //int32_t accuracy  = gnss.getPositionAccuracy();
    int PDOP            = gnss.getPDOP();
    int nedNorthVel     = gnss.getNedNorthVel();
    int nedEastVel      = gnss.getNedEastVel();
    int nedDownVel      = gnss.getNedDownVel();
    int verticalAccEst  = gnss.getVerticalAccEst();
    int horizontalAccEst= gnss.getHorizontalAccEst();
    int speedAccEst     = gnss.getSpeedAccEst();
    int headAccEst      = gnss.getHeadingAccEst();
    byte SIV            = gnss.getSIV();
    Serial  << latitude << "/" << longitude << '\t'
            << altitude     << '\t' 
            << SIV          << '\t' 
            << PDOP         << '\t' 
            << nedNorthVel  << '\t' 
            << nedEastVel   << '\t' 
            << nedDownVel   << '\t' 
            << verticalAccEst   << '\t' 
            << horizontalAccEst << '\t' 
            << speedAccEst  << '\t' 
            << headAccEst; // << '\t'
          //<< accuracy;
    if (gnss.getHeadVehValid()) {
      int headVeh = gnss.getHeadVeh();
      int magDec  = gnss.getMagDec();
      int magAcc = gnss.getMagAcc();
      Serial << "\tHeadVeh: " <<  headVeh <<  " ° (*10^-5)";
      Serial << "\tMagDec: "  <<  magDec  <<  " ° (*10^-2)";
      Serial << "\tMagAcc: "  <<  magAcc  <<  " ° (*10^-2)";
    } // getHeadVehValid()
    //else Serial << " (XX)";
    t2 = millis();
    Serial << " ["  << _WIDTH((t1-t0),4) 
           << " +"  << _WIDTH((t2-t1),4) 
           << " = " << _WIDTH((t2-t0),4) << " ms ]\n";
    t0 = millis();
  } // if 
}

BTW note that if (gnss.getHeadVehValid()) { is never true (i.e headVeh, magDec and magAcc are never reported). compass readings perhaps?

ā€œSIVā€ means ā€œSatellites In Viewā€. It’s the wrong name. We kept it only for backward compatibility with older code. It’s actually numSV - the number of Space Vehicles used in the navigation solution.

You’re heading into Automotive Dead Reckoning territory there. That’s only applicable on some modules:

image

OK I may as well remove that headVeh section.
I assume this would work if I had one of your ZED-F9R Dead Reckoning modules?

A few more Q’s if I may:

Which document did you take those extracts from, was it
u-blox F9 HPG 1.50 - Interface description ?

Is there an equivalent to autoPVT/getPVT for the NAV-HPPOSECEF message? I would like to use getPositionAccuracy as it seems to be a single 3D value, whereas getHorizontalAccEst is only 1D. Is it possible to get this info without the time penalty? Or is the normal usage to only poll it every, say, 10 seconds?

Yes, the Interface Description is the ultimate reference.

Yes, NAV-HPPOSECEF has full ā€œAutoā€ support in the library too: setAutoNAVHPPOSECEF etc.. There isn’t an example for ā€œAutoā€ HPPOSECEF, but there is a polling example.

For best results, call getPositionAccuracy from inside if (getNAVHPPOSECEF()) { ... }.

There’s no time penalty if you switch to periodic messages. getPVT() and getNAVHPPOSECEF() will independently return true when new data arrives. You can set the message rates independently to whatever you need. setAutoNAVHPPOSECEFrate(20) would set the message rate to one message every 20 navigation intervals: 10s in your case.

You might find the callbacks useful. They keep your code loop really clean and compact. See CallbackExample1_NAV_PVT.

I’m sorry there’s no wiki. The examples are the wiki - and there’s a ton of them…

1 Like