sparkfun / SparkFun_u-blox_GNSS_Arduino_Library

An Arduino library which allows you to communicate seamlessly with the full range of u-blox GNSS modules
Other
227 stars 100 forks source link

How to send RTK corrections from base to rover via I2C? #219

Closed dizcza closed 6 months ago

dizcza commented 6 months ago

Sorry for opening an issue for a question but I don't see other means asking you...

I need to send RTCM bytes available at the void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming) function from base (NEO M8P) to rover (also M8P) via I2C. How to do this? I don't see such an API for the rover module to be able to accept RTCM raw bytes from base.

I mean, RTCM bytes will be broadcasted with LoRa radio but the communication between the NEO M8P module and ESP32 is done via I2C.

My setup:

NEO M8P base -- (I2C) --> ESP32 --> LoRa --> ... --> LoRa --> ESP32 -- (I2C) --> NEO M8P rover

I don't mind extending the API if it doesn't exist, but I don't know where to start.

PaulZC commented 6 months ago

Hi Danylo (@dizcza ),

Please see pushRawData: https://github.com/search?q=repo%3Asparkfun%2FSparkFun_u-blox_GNSS_Arduino_Library%20pushrawdata&type=code

Best, Paul

dizcza commented 6 months ago

I've been doing my first RTK base-rover pairing and spent the entire day figuring out why I get empty RELPOS messages in the rover. After the survey is completed, calling

  response &= myGNSS.enableRTCMmessage(UBX_RTCM_1005, COM_PORT_I2C, 1); //Enable message 1005 to output through I2C port, message every second
  response &= myGNSS.enableRTCMmessage(UBX_RTCM_1077, COM_PORT_I2C, 1);
  response &= myGNSS.enableRTCMmessage(UBX_RTCM_1087, COM_PORT_I2C, 1);
  response &= myGNSS.enableRTCMmessage(UBX_RTCM_1230, COM_PORT_I2C, 10); //Enable message every 10 seconds

as suggested in your examples is not enough. It turns out, in order to output 1005 (or 4072 in case of a moving base) messages, the user has to set the base station in a fixed mode with the UBX-CFG-TMODE3 message:

// ECEF X, Y, and Z values are easily extracted from the SurveyIn packets
bool SFE_UBLOX_GPS_ADD::setRTKFixedMode(int32_t ecefX, int32_t ecefY, int32_t ecefZ) {
    UBX_CFG_TMODE3_data_t data;
    ubxPacket packetCfg = {0, 0, 0, 0, 0, (uint8_t*) &data, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};

    if (getSurveyMode(&data) == false)
    {
        return (false);
    }

    packetCfg.cls = UBX_CLASS_CFG;
    packetCfg.id = UBX_CFG_TMODE3;
    packetCfg.len = 40;
    packetCfg.startingSpot = 0;

    data.flags.bits.mode = 2;  // Fixed Mode
    data.ecefXOrLat = ecefX;
    data.ecefYOrLon = ecefY;
    data.ecefZOrAlt = ecefZ;

    bool response = sendCommand(&packetCfg) == SFE_UBLOX_STATUS_DATA_SENT;

    return response;
}

Correct me if I'm wrong but without explicitly setting the base station coordinates, I was getting empty RELPOS packets on the rover side. As soon as I fixed the base station coordinates, I immediately got non-zero relative positions on both NEO-M8P-00 and ZED-F9P rovers. I'm using ESP-NOW to send binary RTCM3 data between the base and the rover and then push raw data via I2C.


Another question is how to confirm the successful parsing of input RTCM messages in the rover. For this, I utilize UBX_RXM_RTCM messages:

typedef struct
{
    uint8_t version;
    union
    {
        uint8_t all;
        struct
        {
            uint8_t crcFailed : 1;  // 0 when RTCM message received and passed CRC check, 1 when failed
            uint8_t msgUsed : 2; // 2 = RTCM message used successfully by the receiver, 1 = not used, 0 = do not know
        } bits;
    } flags;
    uint16_t subType; // not available on NEO-M8P
    uint16_t refStation;
    uint16_t msgType;
} UBX_RXM_RTCM_data_t;

bool SFE_UBLOX_GPS_ADD::getRTCMInfo(UBX_RXM_RTCM_data_t* data) {
    uint8_t customPayload[MAX_PAYLOAD_SIZE]{};
    ubxPacket packetCfg = {0, 0, 0, 0, 0, customPayload, 0, 0, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED, SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED};

    // Query the module
    packetCfg.cls = UBX_CLASS_RXM;
    packetCfg.id = UBX_RXM_RTCM;
    packetCfg.len = 0;
    packetCfg.startingSpot = 0;

    if (sendCommand(&packetCfg) != SFE_UBLOX_STATUS_DATA_RECEIVED) {
        return false;
    }

    data->version = extractLong(&packetCfg, 0);
    data->flags.all = extractByte(&packetCfg, 1);
    data->subType = extractSignedInt(&packetCfg, 2);
    data->refStation = extractSignedInt(&packetCfg, 4);
    data->msgType = extractSignedInt(&packetCfg, 6);

    ESP_LOGI(TAG, "getRTCMInfo:");
    ESP_LOGI(TAG, "  flags:");
    ESP_LOGI(TAG, "    crcFailed: %u", data->flags.bits.crcFailed);
    ESP_LOGI(TAG, "    msgUsed: %u", data->flags.bits.msgUsed);
    ESP_LOGI(TAG, "  refStation %u", data->refStation);
    ESP_LOGI(TAG, "  msgType %u", data->msgType);

    return true;
}

Usage:

        bool response = myGNSS.pushRawData(incomingData, len);
        if (!response)
        {
            log_w("RTCM message wasn't accepted");
        }
        UBX_RXM_RTCM_data_t data;
        myGNSS.getRTCMInfo(&data);

But I'm always getting msgUsed=0 (I expect msgUsed=2) even if the packet seems to be processed correctly

[5943973][I][SparkFun_myGNSS.cpp:275] getRTCMInfo(): [ublox] getRTCMInfo:
[5943973][I][SparkFun_myGNSS.cpp:277] getRTCMInfo(): [ublox]   flags:
[5943975][I][SparkFun_myGNSS.cpp:278] getRTCMInfo(): [ublox]     crcFailed: 0
[5943981][I][SparkFun_myGNSS.cpp:279] getRTCMInfo(): [ublox]     msgUsed: 0
[5943988][I][SparkFun_myGNSS.cpp:280] getRTCMInfo(): [ublox]   refStation 0
[5943995][I][SparkFun_myGNSS.cpp:281] getRTCMInfo(): [ublox]   msgType 1077

Also, the doc says these messages are output immediately upon an RTCM message parsing. If I send several RTCM binary messages, I get only one confirmation.

Have you assessed the correctness of RTCM input data in rovers?

PaulZC commented 6 months ago

Hi Danylo (@dizcza ),

Please see the NEO-M8P examples, specifically the two Moving Base examples. For a Moving Base Base this line is essential. See also setStaticPosition.

For corrections monitoring, the library includes support for UBX-RXM-COR. It provides feedback on RTCM3, L-Band (PMP) and SPARTN corrections.

For the ZED-F9P, I'd recommend switching to v3 of the library.

Best, Paul

dizcza commented 6 months ago

For corrections monitoring, the library includes support for UBX-RXM-COR. It provides feedback on RTCM3, L-Band (PMP) and SPARTN corrections.

Thanks, I overlooked this one. Looks like this is exactly what I was looking for. And I didn't know about the setStaticPosition as well - the API is quite huge to digest all functions.

For the NEO-M8P and ZED-F9P, I'd recommend switching to v3 of the library.

I'd switch if the readme states otherwise:

If you are using an older module, like the M8, you will need to use version 2 of this library.

So I'm puzzled whether I can use v3.

For a Moving Base Base this line is essential.

Yes, I borrowed this line in my code as well (I should have mentioned this in my previous reply), so adding this line wasn't enough - only setting the fixed position helped. I wonder if you didn't set the fixed position and yet were able to get non-empty RELPOS packets on the rover side...

In my setup, all stations - base and rovers - lie still on the ground, I just need to know their exact position (relative to the base). If my base station doesn't move, can I decrease the rate of 1005 RTCM messages, f.i. to output each 10 seconds only? The docs don't mention this.

PaulZC commented 6 months ago

Oh, yes, my bad. Keep using v2 of the library with the NEO-M8P - it doesn't support the configuration interface.

Re. the rate of 1005: I don't know... I suspect it needs to be 1Hz...

You might find this discussion useful:

https://forum.sparkfun.com/viewtopic.php?p=239720#p239720 https://forum.sparkfun.com/viewtopic.php?p=239779#p239779

dizcza commented 6 months ago

Unfortunately, UBX-RXM-COR are not supported on M8 - they are not even described in the docs.

I'll proceed as is. Thanks for your help.

dizcza commented 4 months ago

Hi Paul, I finally got an RTK FIX for my base-rover setup, and I'm very happy about it!

Since my base is stationary (I'm using setStaticPosition), I noticed that it always sends the same 1005 and even 1230 RTCM corrections. My Lora bandwidth is narrow and I want to squeeze the information as much as possible. Therefore, I'll transmit 1005 and 1230 only once, and of course, on the rover side, I'll be pushing them to the ublox module alongside with always updating 1074 and 1084 messages. The only problem is that I don't know whether

Can you confirm it? Does ublox website provide information about RTCM message lengths? I cannot find this info. On various other sources, I find different numbers of bytes of 1005 compared to my discovery. I've been experimenting for a while and my discovery seems to be valid. But I'm unsure if this is a feature of the coordinates I tried (Ukraine) or this is true in any part of the globe.

PaulZC commented 4 months ago

Hi Danylo (@dizcza ),

RTCM 10403 defines the message format:

https://rtcm.myshopify.com/products/rtcm-10403-3-differential-gnss-global-navigation-satellite-systems-services-version-3-amendment-2-may-20-2021

RTCM1005 is always 3+19+3 bytes. RTCM1230 is variable length: 3+4+2*N+3, where N = Number of Code-Phase Biases (max 4).

These links may help:

https://github.com/sparkfun/SparkFun_u-blox_GNSS_v3/blob/4ac1a71da82bc5cd7d5c9afd461eec96e0ebe09f/src/u-blox_GNSS.cpp#L7588-L7684

https://github.com/sparkfun/SparkFun_u-blox_GNSS_v3/blob/4ac1a71da82bc5cd7d5c9afd461eec96e0ebe09f/src/u-blox_GNSS.cpp#L7553-L7568

https://github.com/sparkfun/SparkFun_u-blox_PointPerfect_Library/blob/be02bf28a329018ec138b834c6db7101271a07b0/examples/Example1_LBand_SPARTN_Parsing/Example1_LBand_SPARTN_Parsing.ino#L120-L183

Best wishes, Paul

dizcza commented 4 months ago

RTCM1005 is always 3+19+3 bytes. RTCM1230 is variable length: 3+4+2*N+3, where N = Number of Code-Phase Biases (max 4).

Thank you for this valuable information. I couldn't find such a simple answer elsewhere.

And thanks for the parsing links. I tried to find such a parser but the ones I saw were never complete.

Cheers, Danylo