ZED F9P safe ubx logs for PPK with RTKLIB to sd-card

Hey Guys
I want to implement PPK for a project at my university, where we want to analyze the movement of avalanches. Which requires us to just safe the data locally on the Arduino board.
For this, I have a custom Arduino board with a SD card to store the data and a ZED F9P GPS module. I can’t figure out how to safe the .uxb logs(The ones from the U-Center) to my SD card.
So I could convert the ubx logs to NAV and OBS Data in RTCONV and then use RTKPOST for PPK.

My Code so far only safes simple GPS-Data to the SD Card, which was needed for the first test.

Any help would be greatly appreciated. Thanks guys

#include <Arduino.h>
#include <SPI.h>
#include <SdFat.h>
#include <SparkFun_u-blox_GNSS_v3.h>


#define SPI_SD_CS_PIN   5
#define SPI_SD_MOSI_PIN 9
#define SPI_SD_MISO_PIN 6
#define SPI_SD_SCK_PIN  28

SPIClass SD_SPI(&sercom1, SPI_SD_MISO_PIN, SPI_SD_SCK_PIN, SPI_SD_MOSI_PIN, SPI_PAD_3_SCK_1, SERCOM_RX_PAD_2);

#define SD_CONFIG SdSpiConfig(SPI_SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(4), &SD_SPI) // test with 4 MHZ was 16

SdFat sd;
File dataFile;
File rinexFile;

TwoWire SYS_I2C(&sercom5, 3, 2);
TwoWire GPS_I2C(&sercom4, 16, 17);

SFE_UBLOX_GNSS myGNSS;

long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
uint8_t interruptPin = 28;
volatile int repetitions = 1;

struct GNSSTime {
    uint8_t hour;
    uint8_t minute;
    uint8_t second;
    uint16_t millisecond;
    uint8_t day;
    uint8_t month;
    uint16_t year;
};

void setup()
{
  pinMode(interruptPin, INPUT_PULLUP);

  pinMode(43, OUTPUT); // 43 SD-Card
  digitalWrite(43, HIGH);
  pinMode(45, OUTPUT); //45 Z9P | 41 M10Q
  digitalWrite(45, HIGH);
  
  GPS_I2C.begin();
  delay(2000);
  Serial.begin(115200);
  unsigned long startTime = millis();
  while(!Serial && (millis() - startTime < 1000)) // Wait max 1 seconds
  {
    delay(10);
  }
  Serial.println("Start");

  if(!sd.begin(SD_CONFIG))
  {
    Serial.println("SD card initialization failed");
    sd.initErrorHalt(&Serial);
  }
  Serial.println("SD card initialized!");
  sd.ls(&Serial, LS_SIZE);
  Serial.println("End");
  if (myGNSS.begin(GPS_I2C, 0x42) == false)
    {
    Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
    while (1)
      ;
    }

}

String formatGNSSDate(const GNSSTime& time) {
    char buffer[20];
    snprintf(buffer, sizeof(buffer), "%02d/%02d/%d",
            time.day, time.month, time.year);
    return String(buffer);
}

String formatGNSSTime(const GNSSTime& time) {
    char buffer[20];
    snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d.%u",
            time.hour, time.minute, time.second, time.millisecond);
    return String(buffer);
}

void saveGPSDataToSD(const GNSSTime& time,long latitude, long longitude, long altitude, byte fixType, byte RTK)
{
  // Open the file. Only one file can be open at a time,
  // so you have to close this one before opening another.
  dataFile = sd.open("gps_data.txt", FILE_WRITE);

  // If the file is available, write to it:
  if (dataFile) {
    // Write time data first
    dataFile.print(time.year);
    dataFile.print("/");
    dataFile.print(time.month);
    dataFile.print("/");
    dataFile.print(time.day);
    dataFile.print(" ");
    dataFile.print(time.hour);
    dataFile.print(":");
    dataFile.print(time.minute);
    dataFile.print(":");
    dataFile.print(time.second);
    dataFile.print(".");
    dataFile.print(time.millisecond);
    dataFile.print(",");

    dataFile.print(millis());
    dataFile.print(",");
    dataFile.print(latitude);
    dataFile.print(",");
    dataFile.print(longitude);
    dataFile.print(",");
    dataFile.print(altitude);
    dataFile.print(",");
    dataFile.print(fixType);
    dataFile.print(",");
    dataFile.println(RTK);
    dataFile.close();
    Serial.println("Data saved to SD card");
  }
  // If the file isn't open, pop up an error:
  else {
    Serial.println("Error opening gps_data.txt");
  }
}

void loop()
{
  //Query module only every second. Doing it more often will just cause I2C traffic.
  if (millis() - lastTime > 1000)
  {
    lastTime = millis(); //Update the timer

    // Get GNSS time
    GNSSTime time;
    if (myGNSS.getPVT()) {  // Get time from PVT data
        time.hour = myGNSS.getHour();
        time.minute = myGNSS.getMinute();
        time.second = myGNSS.getSecond();
        time.millisecond = myGNSS.getMillisecond();
        time.day = myGNSS.getDay();
        time.month = myGNSS.getMonth();
        time.year = myGNSS.getYear();
            
        // Print time to console
        Serial.print("Date: ");
        Serial.print(formatGNSSDate(time));
        Serial.print(" Time: ");
        Serial.print(formatGNSSTime(time));
        Serial.print(" | ");
    }

    long latitude = myGNSS.getLatitude();
    Serial.print(F("Lat: "));
    Serial.print(latitude);

    long longitude = myGNSS.getLongitude();
    Serial.print(F(" Long: "));
    Serial.print(longitude);

    long altitude = myGNSS.getAltitude();
    Serial.print(F(" Alt: "));
    Serial.print(altitude);

    byte fixType = myGNSS.getFixType();
    Serial.print(F(" Fix: "));
    if(fixType == 0) Serial.print(F("No fix"));
    else if(fixType == 1) Serial.print(F("Dead reckoning"));
    else if(fixType == 2) Serial.print(F("2D"));
    else if(fixType == 3) Serial.print(F("3D"));
    else if(fixType == 4) Serial.print(F("GNSS + Dead reckoning"));
    else if(fixType == 5) Serial.print(F("Time only"));

    byte RTK = myGNSS.getCarrierSolutionType();
    Serial.print(" RTK: ");
    Serial.print(RTK);
    if (RTK == 0) Serial.print(F(" (No solution)"));
    else if (RTK == 1) Serial.print(F(" (High precision floating fix)"));
    else if (RTK == 2) Serial.print(F(" (High precision fix)"));
    Serial.println();

    // Save GPS data to SD card
    saveGPSDataToSD(time, latitude, longitude, altitude, fixType, RTK);

  }
}

This doesn’t save any data that’s usable for PPK / RINEX

The .UBX file content is just the data streamed out of the receiver, you can name the file based on a time/date, like RINEX conventions for say hourly or daily file. Or you can output a time-stamp into the first lines of the file.

Just enable UBX-RXM-RAWX (Observation) and optionally UBX-RXM-SFRBX (Navigation), and just pipe this output to the file.

The .UBX can contain NMEA Sentences, UBX, RTCM3 or SPARTN packets,typically you’d want to cull the output to reduce the volume of data, and perhaps increase baud rate to reduce latency.

Spill data to the file as large aligned blocks, with sector or cluster alignment, using buffering to acheive this. Say 8KB upto 32KB depending on your memory budget. Writing small and unaligned blobs of data to SD Cards is very slow and inefficient.

Perhaps also look over the Artemis logger/ source.

1 Like

Hi @FabianL ,

Please see:

I hope this helps,
Paul

1 Like

Hi Paul

Thanks for your answer, it helped a lot. I have written a new code that saves the data as a .ubx successfully, but after converting it in RTK Conv into a .obs and .nav file I found that I have a lot of obs data. View in image obs-file. But there is no nav data(nav-file) or at least none that could be nearly accurate, I moved the GPS antenna roughly 3 meters. Do you know what could have went wrong so the nav data isn’t collected correctly?


I can only upload 1 pic but the nav plot shows a very thin line in RTK-Plot that’s roughly 300km wide. I really don’t know why and shouldn’t the ground track route be green?

Thanks for any help, it’s greatly appreciated. :slight_smile:
Fabian

Hi Fabian,

It is a long time since I last used RTKLIB, but I think you may not have configured RTKCONV correctly? It should look more like:

If you send me your .ubx file in a private message, I will try and find time to look at it.

Best,
Paul

Also, we recommend Tim Everett’s version of RTKLIB. Is that the one you are using?

Thanks Paul,

I really appreciate your help. It means a lot to me. :grinning:
But I think there is an issue with the direct messages on this forum. I can’t start a personal Chat with anybody because no users are found in the search bar. I can type in PaulZC or any other username, but no members found.

Best Fabian

Hi Fabian,

Just click on my icon and you should see options to Message or Chat? Message please…

Thanks,
Paul

Yeah thats how it should look, but I dont see those options. I looks like I’m not authorised for that.

After running your data through RTKPOST, using Single Positioning Mode: