Trouble getting RTCM data

Currently working with 2 ZED-F9P RTK GPS Modules and 2 LoRa Thing Plus Explorable boards. I’m using one as a base station and the other as a client. My hope is to send the RTCM data over the radio to be sent over I2C to the second GPS unit but I’m having a few issues. I’m able to send data and receive it over the radio just fine but I believe something in my approach must be incorrect as when I look to see if the correction data is being received on U-Center I don’t see it anywhere and the module never enters RTK mode. A couple of times in the testing process I did see something show up as ‘Text Communication’ but that didn’t seem to mean much. My current idea is to collect all the bytes in a single RTCM message and then send that over the radio in the form of a byte array. This part seems to work as intended but the GPS module doesn’t seem to know what it is. My code for the base station is

#include <Wire.h> //Needed for I2C to GNSS
#include <RadioLib.h>
#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
SFE_UBLOX_GNSS myGNSS;

//#define SERIAL_OUTPUT // Uncomment this line to push the RTCM data to a Serial port
SX1262 radio = new Module(D36, D40, D44, D39, SPI1);
void setup()
{
  delay(1000);
  
  Serial.begin(115200);
  while (!Serial); //Wait for user to open terminal
  Serial.println(F("u-blox Base Station example"));
  int state = radio.begin(915.0, 250.0, 7, 5, 0x34, 20, 10, 0, false);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
#ifdef SERIAL_OUTPUT
  // If our board supports it, we can output the RTCM data automatically on (e.g.) Serial1
  Serial1.begin(115200);
  myGNSS.setRTCMOutputPort(Serial1);
#endif

  Wire.begin();
  Wire.setClock(400000); //Increase I2C clock speed to 400kHz

  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);
  }

  // Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate
  //myGNSS.factoryDefault(); delay(5000);

  myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3); // Ensure RTCM3 is enabled
  myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save the communications port settings to flash and BBR

  while (Serial.available()) Serial.read(); //Clear any latent chars in serial buffer
  Serial.println(F("Press any key to begin Survey-In"));
  while (Serial.available() == 0) ; //Wait for user to press a key

  bool response = myGNSS.newCfgValset(); // Create a new Configuration Item VALSET message
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_I2C, 1); //Enable message 1005 to output through I2C port, message every second
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1074_I2C, 1);
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1084_I2C, 1);
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1094_I2C, 1);
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1124_I2C, 1);
  response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_I2C, 10); // Enable message 1230 every 10 seconds
  response &= myGNSS.sendCfgValset(); // Send the VALSET

  // Use _UART1 for the above six messages to direct RTCM messages out UART1
  // _UART2, _USB and _SPI are also available
  // For example: response &= myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_UART1, 1);

  if (response == true)
  {
    Serial.println(F("RTCM messages enabled"));
  }
  else
  {
    Serial.println(F("RTCM failed to enable. Are you sure you have an ZED-F9P?"));
    while (1); //Freeze
  }

  //Check if Survey is in Progress before initiating one
  // From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
  // Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
  // You can either read the data from packetUBXNAVSVIN directly
  // or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; and getSurveyInMeanAccuracy
  response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (request can take a long time)
  
  if (response == false) // Check if fresh data was received
  {
    Serial.println(F("Failed to get Survey In status"));
    while (1); //Freeze
  }

  if (myGNSS.getSurveyInActive() == true) // Use the helper function
  //if (myGNSS.packetUBXNAVSVIN->data.active > 0) // Or we could read active directly
  {
    Serial.print(F("Survey already in progress."));
  }
  else
  {
    //Start survey - define the minimum observationTime and requiredAccuracy
    uint32_t observationTime =    60; float requiredAccuracy = 5.0; //  60 seconds, 5.0m
    //uint32_t observationTime =   300; float requiredAccuracy = 2.0; // 300 seconds, 2.0m
    //uint32_t observationTime = 86400; float requiredAccuracy = 2.0; //  24 hours,   2.0m
    
    response = myGNSS.enableSurveyModeFull(observationTime, requiredAccuracy, VAL_LAYER_RAM); //Enable Survey in. Save setting in RAM layer only (not BBR)
    if (response == false)
    {
      Serial.println(F("Survey start failed. Freezing..."));
      while (1);
    }
    Serial.println(F("Survey started."));
    Serial.print(F("This will run until "));
    Serial.print(observationTime);
    Serial.print(F("s have passed _and_ better than "));
    Serial.print(requiredAccuracy, 2);
    Serial.println(F("m accuracy is achieved."));
    Serial.println();
  }

  while(Serial.available()) Serial.read(); //Clear buffer
  
  //Begin waiting for survey to complete
  while (myGNSS.getSurveyInValid() == false) // Call the helper function
  //while (myGNSS.packetUBXNAVSVIN->data.valid == 0) // Or we could read valid directly
  {
    if(Serial.available())
    {
      byte incoming = Serial.read();
      if(incoming == 'x')
      {
        //Stop survey mode
        response = myGNSS.disableSurveyMode(); //Disable survey
        Serial.println(F("Survey stopped"));
        break;
      }
    }

    // From v2.0, the data from getSurveyStatus (UBX-NAV-SVIN) is returned in UBX_NAV_SVIN_t packetUBXNAVSVIN
    // Please see u-blox_structs.h for the full definition of UBX_NAV_SVIN_t
    // You can either read the data from packetUBXNAVSVIN directly
    // or can use the helper functions: getSurveyInActive; getSurveyInValid; getSurveyInObservationTime; getSurveyInObservationTimeFull; and getSurveyInMeanAccuracy
    response = myGNSS.getSurveyStatus(2000); //Query module for SVIN status with 2000ms timeout (req can take a long time)
    
    if (response == true) // Check if fresh data was received
    {
      Serial.print(F("\r\n\r\nPress x to end survey - "));
      Serial.print(F("Time elapsed: "));
      Serial.print((String)myGNSS.getSurveyInObservationTimeFull()); // Call the helper function
      Serial.print(F(" ("));
      Serial.print((String)myGNSS.packetUBXNAVSVIN->data.dur); // Read the survey-in duration directly from packetUBXNAVSVIN

      Serial.print(F(") Accuracy: "));
      Serial.print(myGNSS.getSurveyInMeanAccuracy()); // Call the helper function
      Serial.print(F(" ("));
      // Read the mean accuracy directly from packetUBXNAVSVIN and manually convert from mm*0.1 to m
      float meanAcc = ((float)myGNSS.packetUBXNAVSVIN->data.meanAcc) / 10000.0;
      Serial.print(meanAcc); 
      Serial.println(F(")"));
    }
    else
    {
      Serial.println(F("\r\nSVIN request failed"));
    }

    delay(1000);
  }
  Serial.println(F("\r\nSurvey valid!"));

  Serial.println(F("Base survey complete! RTCM now broadcasting."));

  myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_RTCM3); //Set the I2C port to output UBX and RTCM sentences (not really an option, turns on NMEA as well)
}

void loop()
{
  myGNSS.checkUblox(); //See if new data is available. Process bytes as they come in.

  delay(250); //Don't pound too hard on the I2C bus
}

//This function gets called from the SparkFun u-blox Arduino Library.
//As each RTCM byte comes in you can specify what to do with it
//Useful for passing the RTCM correction data to a radio, Ntrip broadcaster, etc.

#define RTCM_DATA_SIZE 256 // Define the size of the RTCM data array
#define PREAMBLE_BYTE 0xD3

uint8_t rtcData[RTCM_DATA_SIZE]; // Create a byte array to store the RTCM data
static uint16_t byteCounter = 0; // Counter to keep track of the received bytes

void DevUBLOXGNSS::processRTCM(uint8_t incoming)
{
  
    if (incoming == PREAMBLE_BYTE) {
        // Received the start of a new RTCM message
        if (byteCounter > 0) {
            // If there was data from the previous message, transmit it
            int state = radio.transmit(rtcData, byteCounter);
            if (state == RADIOLIB_ERR_NONE) {
                Serial.print(byteCounter);
                Serial.println(" byte RTCM Data Sent");
            } 
            else {
                Serial.print(F("Failed to send RTCM data, error code: "));
                Serial.println(state);
            }
        }

        // Reset the byteCounter for the new message
        byteCounter = 0;
    }

    if (byteCounter < RTCM_DATA_SIZE) {
        rtcData[byteCounter] = incoming; // Store incoming byte in the array
        byteCounter++;
    }
    /*
    rtcData[byteCounter] = incoming;
    byteCounter++;
    if (byteCounter == RTCM_DATA_SIZE) {
            // If there was data from the previous message, transmit it
            int state = radio.transmit(rtcData, RTCM_DATA_SIZE);
            if (state == RADIOLIB_ERR_NONE) {
                Serial.print(byteCounter);
                Serial.println(" byte RTCM Data Sent");
                //delay(1000);
            } 
            else {
                Serial.print(F("Failed to send RTCM data, error code: "));
                Serial.println(state);
            }
            byteCounter = 0;
        }
       */
        // Reset the byteCounter for the new message
        
    

}

and my code for the receiver is

#include <Wire.h> //Needed for I2C to GNSS
#include <RadioLib.h>
#include <SparkFun_u-blox_GNSS_v3.h> //http://librarymanager/All#SparkFun_u-blox_GNSS_v3
SFE_UBLOX_GNSS myGNSS;
SX1262 radio = new Module(D36, D40, D44, D39, SPI1);
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
  double latitude = ubxDataStruct->lat; // Print the latitude
  Serial.print(F("Lat: "));
  Serial.print(latitude / 10000000.0, 7);

  double longitude = ubxDataStruct->lon; // Print the longitude
  Serial.print(F("  Long: "));
  Serial.print(longitude / 10000000.0, 7);

  double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
  Serial.print(F("  Height: "));
  Serial.print(altitude / 1000.0, 3);

  uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
  Serial.print(F("  Fix: "));
  Serial.print(fixType);
  if (fixType == 0)
    Serial.print(F(" (None)"));
  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 == 3)
    Serial.print(F(" (GNSS + Dead Reckoning)"));
  else if (fixType == 5)
    Serial.print(F(" (Time Only)"));
  else
    Serial.print(F(" (UNKNOWN)"));

  uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
  Serial.print(F("  Carrier Solution: "));
  Serial.print(carrSoln);
  if (carrSoln == 0)
    Serial.print(F(" (None)"));
  else if (carrSoln == 1)
    Serial.print(F(" (Floating)"));
  else if (carrSoln == 2)
    Serial.print(F(" (Fixed)"));
  else
    Serial.print(F(" (UNKNOWN)"));

  uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
  Serial.print(F("  Horizontal Accuracy Estimate: "));
  Serial.print(hAcc);
  Serial.print(F(" (mm)"));

  Serial.println();    
}
void printRTCMdata1005(RTCM_1005_data_t *rtcmData1005)
{
  double x = rtcmData1005->AntennaReferencePointECEFX;
  x /= 10000.0; // Convert to m
  double y = rtcmData1005->AntennaReferencePointECEFY;
  y /= 10000.0; // Convert to m
  double z = rtcmData1005->AntennaReferencePointECEFZ;
  z /= 10000.0; // Convert to m

  Serial.print(F("NTRIP Server RTCM 1005:  ARP ECEF-X: "));
  Serial.print(x, 4); // 4 decimal places
  Serial.print(F("  Y: "));
  Serial.print(y, 4); // 4 decimal places
  Serial.print(F("  Z: "));
  Serial.println(z, 4); // 4 decimal places
}

//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// Callback: printRTCMdata1006 will be called when new RTCM 1006 data has been parsed from pushRawData
// See u-blox_structs.h for the full definition of RTCM_1006_data_t
//         _____  You can use any name you like for the callback. Use the same name when you call setRTCM1006InputcallbackPtr
//        /                 _____  This _must_ be RTCM_1006_data_t
//        |                /                   _____ You can use any name you like for the struct
//        |                |                  /
//        |                |                  |
void printRTCMdata1006(RTCM_1006_data_t *rtcmData1006)
{
  double x = rtcmData1006->AntennaReferencePointECEFX;
  x /= 10000.0; // Convert to m
  double y = rtcmData1006->AntennaReferencePointECEFY;
  y /= 10000.0; // Convert to m
  double z = rtcmData1006->AntennaReferencePointECEFZ;
  z /= 10000.0; // Convert to m
  double h = rtcmData1006->AntennaHeight;
  h /= 10000.0; // Convert to m

  Serial.print(F("NTRIP Server RTCM 1006:  ARP ECEF-X: "));
  Serial.print(x, 4); // 4 decimal places
  Serial.print(F("  Y: "));
  Serial.print(y, 4); // 4 decimal places
  Serial.print(F("  Z: "));
  Serial.print(z, 4); // 4 decimal places
  Serial.print(F("  Height: "));
  Serial.println(h, 4); // 4 decimal places
}

void setup()
{
  delay(1000);
  Wire.begin();
  Serial.begin(115200);
  Serial.println(F("SparkFun u-blox GNSS example"));
  int state = radio.begin(915.0, 250.0, 7, 5, 0x34, 20, 10, 0, false);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

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

  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)
      ;
  }

  // Check that this platform supports 64-bit (8 byte) double
  if (sizeof(double) < 8)
  {
    Serial.println(F("Warning! Your platform does not support 64-bit double."));
    Serial.println(F("The latitude and longitude will be inaccurate."));
  }

  myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
  myGNSS.setI2CInput(COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_RTCM3);
  myGNSS.setMainTalkerID(SFE_UBLOX_MAIN_TALKER_ID_GP);
  myGNSS.setVal8(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, 10);
  myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED);
  //myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT);
  myGNSS.setNavigationFrequency(1); //Set output to 5 times a second. Change the RAM layer only.

  byte rate;
  if (myGNSS.getNavigationFrequency(&rate)) //Get the update rate of this module
  {
    Serial.print("Current update rate: ");
    Serial.println(rate);
  }
  else
  {
    Serial.println(F("getNavigationFrequency failed!"));
  }
  myGNSS.setAutoPVTcallbackPtr(&printPVTdata);
  myGNSS.setRTCM1005InputcallbackPtr(&printRTCMdata1005); // Set up a callback to print the RTCM 1005 Antenna Reference Position from the correction data
  myGNSS.setRTCM1006InputcallbackPtr(&printRTCMdata1006);

  
}
#define RTCM_DATA_SIZE 512*4

void loop()
{
  myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
  myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
   // Create a byte array to store the RTCM data
  int state = radio.startReceive(RADIOLIB_SX126X_RX_TIMEOUT_NONE);
  if (state == RADIOLIB_ERR_NONE) {
    
    int numBytes = radio.getPacketLength();
    if (numBytes !=0){
    uint8_t rtcData[numBytes];
    int state = radio.readData(rtcData, numBytes);
    if (state == RADIOLIB_ERR_NONE) {

    
    
      
      //Wire.write(rtcData,numBytes);
    myGNSS.pushRawData(((uint8_t *)&rtcData), numBytes,false);
    //Serial.println("RTCM Data Recieved");
    
    
    
  

    
    
 
    
  
  
  
  /*
  int state = radio.receive(rtcData,RTCM_DATA_SIZE); // Receive data into the byte array
  myGNSS.pushRawData(rtcData, RTCM_DATA_SIZE,false);
  
    
  // Data received successfully
  // Push the RTCM data to the GNSS module
  
    
  
  //Query module. The module only responds when a new position is available.
  
    // getHighResLatitude: returns the latitude from HPPOSLLH as an int32_t in degrees * 10^-7
    // getHighResLatitudeHp: returns the high resolution component of latitude from HPPOSLLH as an int8_t in degrees * 10^-9
    // getHighResLongitude: returns the longitude from HPPOSLLH as an int32_t in degrees * 10^-7
    // getHighResLongitudeHp: returns the high resolution component of longitude from HPPOSLLH as an int8_t in degrees * 10^-9
    // getElipsoid: returns the height above ellipsoid as an int32_t in mm
    // getElipsoidHp: returns the high resolution component of the height above ellipsoid as an int8_t in mm * 10^-1
    // getMeanSeaLevel: returns the height above mean sea level as an int32_t in mm
    // getMeanSeaLevelHp: returns the high resolution component of the height above mean sea level as an int8_t in mm * 10^-1
    // getHorizontalAccuracy: returns the horizontal accuracy estimate from HPPOSLLH as an uint32_t in mm * 10^-1

    // First, let's collect the position data
    int32_t latitude = myGNSS.getHighResLatitude();
    int8_t latitudeHp = myGNSS.getHighResLatitudeHp();
    int32_t longitude = myGNSS.getHighResLongitude();
    int8_t longitudeHp = myGNSS.getHighResLongitudeHp();
    int32_t ellipsoid = myGNSS.getElipsoid();
    int8_t ellipsoidHp = myGNSS.getElipsoidHp();
    int32_t msl = myGNSS.getMeanSeaLevel();
    int8_t mslHp = myGNSS.getMeanSeaLevelHp();
    uint32_t accuracy = myGNSS.getHorizontalAccuracy();

    // Defines storage for the lat and lon as double
    double d_lat; // latitude
    double d_lon; // longitude

    // Assemble the high precision latitude and longitude
    d_lat = ((double)latitude) / 10000000.0; // Convert latitude from degrees * 10^-7 to degrees
    d_lat += ((double)latitudeHp) / 1000000000.0; // Now add the high resolution component (degrees * 10^-9 )
    d_lon = ((double)longitude) / 10000000.0; // Convert longitude from degrees * 10^-7 to degrees
    d_lon += ((double)longitudeHp) / 1000000000.0; // Now add the high resolution component (degrees * 10^-9 )

   // Print the lat and lon
    Serial.print("Lat (deg): ");
    Serial.print(d_lat, 9);
    Serial.print(", Lon (deg): ");
    Serial.print(d_lon, 9);

    // Now define float storage for the heights and accuracy
    float f_ellipsoid;
    float f_msl;
    float f_accuracy;

    // Calculate the height above ellipsoid in mm * 10^-1
    f_ellipsoid = (ellipsoid * 10) + ellipsoidHp;
    // Now convert to m
    f_ellipsoid = f_ellipsoid / 10000.0; // Convert from mm * 10^-1 to m

    // Calculate the height above mean sea level in mm * 10^-1
    f_msl = (msl * 10) + mslHp;
    // Now convert to m
    f_msl = f_msl / 10000.0; // Convert from mm * 10^-1 to m

    // Convert the horizontal accuracy (mm * 10^-1) to a float
    f_accuracy = accuracy;
    // Now convert to m
    f_accuracy = f_accuracy / 10000.0; // Convert from mm * 10^-1 to m

    // Finally, do the printing
    Serial.print(", Ellipsoid (m): ");
    Serial.print(f_ellipsoid, 4); // Print the ellipsoid with 4 decimal places

    Serial.print(", Mean Sea Level (m): ");
    Serial.print(f_msl, 4); // Print the mean sea level with 4 decimal places

    Serial.print(", Accuracy (m): ");
    Serial.println(f_accuracy, 4); // Print the accuracy with 4 decimal places
  */
    }
  }
  }
}

If anyone has any idea as to what’s going wrong here it would be much appreciated.

. There could be a few reasons for this issue. Here are some possible explanations and suggestions to address the problem:

RTCM Data Parsing: Ensure that your RTCM data is correctly parsed and processed. Verify that the RTCM messages are correctly formatted and being passed to the GNSS module for further processing.

Radio Configuration: Double-check your radio configuration to confirm that it is set up to transmit and receive data effectively. Ensure that the radio is properly initialized and that the transmission and reception parameters are correctly configured.

I2C Communication: Verify that the I2C communication between your devices is functioning as expected. Check for any potential issues that might disrupt or slow down the data transfer process.

Software Logic: Review the logic of your software implementation, especially the parts related to handling RTCM data. Make sure that your code is correctly structured and that data is being processed and transmitted in the expected format.

Hardware Setup: Check your hardware setup for any loose connections or issues that might be affecting data transmission and reception. Ensure that all components are securely connected and functioning properly.

By addressing these potential areas of concern, you may be able to identify and resolve the issues you’re encountering with your RTCM data transmission and RTK mode activation.

Hello, I am working on zedf9p rtk with my team, we are trying to use LORA radio between base rover, we estimate that the rtcm data that needs to be sent will exceed the lora capacity due to the package size, I wonder how we can package this rtcm data, can you share an e-mail address with which we can contact you? , let’s mutual help, thank you.

my linkedin is

https://www.linkedin.com/in/%C3%B6mer-f … 2F1A%3D%3D

my gmail : omerfaruk5874@gmail.com