iforce2d / weenyPRU

LinuxCNC component and firmware for Raspberry Pi to communicate with a STM32 microcontroller.
GNU General Public License v2.0
49 stars 9 forks source link

Questions about the UBX parser #1

Closed brightproject closed 4 months ago

brightproject commented 8 months ago

Hello @iforce2d I apologize in advance for disturbing you and for posting this issue off-topic, but I couldn’t find another way to contact you. I was interested in the parser from the video. I ran the example code on my GPS receiver NEO-6M with MCU stm32f411. I was not able to set the frequency to 10 Hz, so I am content with the output once every 1000 ms (1 Hz). Data was sent to the serial port, but the question immediately arose - why sometimes strange time with zero lat/lon?

iTOW:324403200 lat/lon: 0.00,0.00 height: 25.86 hMSL: 1711276.00 hAcc: 0.00 vAcc: 8414.82
iTOW:31615000 lat/lon: 49.51,8.76 height: 227.68 hMSL: 214.45 hAcc: 13.21 vAcc: 9.59
iTOW:31615500 lat/lon: 49.51,8.76 height: 227.39 hMSL: 214.16 hAcc: 13.19 vAcc: 9.58
iTOW:31616000 lat/lon: 49.51,8.76 height: 227.16 hMSL: 213.93 hAcc: 13.18 vAcc: 9.58
iTOW:31616500 lat/lon: 49.51,8.76 height: 227.01 hMSL: 213.78 hAcc: 13.17 vAcc: 9.57
iTOW:31617500 lat/lon: 49.51,8.76 height: 226.51 hMSL: 213.29 hAcc: 13.17 vAcc: 9.58
iTOW:329515008 lat/lon: 0.00,0.00 height: 25.86 hMSL: 1711276.00 hAcc: 0.00 vAcc: 8414.82
iTOW:31618500 lat/lon: 49.51,8.76 height: 225.98 hMSL: 212.76 hAcc: 13.16 vAcc: 9.59
iTOW:31619500 lat/lon: 49.51,8.76 height: 225.44 hMSL: 212.21 hAcc: 13.15 vAcc: 9.60
iTOW:31620000 lat/lon: 49.51,8.76 height: 225.22 hMSL: 212.00 hAcc: 13.15 vAcc: 9.60
iTOW:31620500 lat/lon: 49.51,8.76 height: 224.93 hMSL: 211.70 hAcc: 13.15 vAcc: 9.60
iTOW:325451776 lat/lon: 0.00,0.00 height: 25.86 hMSL: 1711276.00 hAcc: 0.00 vAcc: 8414.82
iTOW:31621500 lat/lon: 49.51,8.76 height: 224.50 hMSL: 211.27 hAcc: 13.15 vAcc: 9.60
iTOW:31622000 lat/lon: 49.51,8.76 height: 224.30 hMSL: 211.07 hAcc: 13.14 vAcc: 9.60

Is this problem possible due to the low frequency of information output via UART from the module? I found information that seems to explain the problem, but I'm not sure. https://github.com/sparkfun/OpenLog_Artemis/issues/53 Regarding the issue of parsing VELNED, I have much greater difficulties. VELNED

Parsing code, based on your example below:

#define SERIAL_BAUD 115200
#define GNSS_BAUD 9600

HardwareSerial Serial2(USART2); // PA2(TX2), PA3(RX2)

const unsigned char UBX_HEADER[] = {0xB5, 0x62};

struct NAV_VELNED {
  unsigned char cls;
  unsigned char id;
  unsigned short len;
  unsigned long iTOW; // U4
  long velN; // I4
  long velE;
  long velD;
  unsigned long speed;
  unsigned long gSpeed;
  long heading;
  unsigned long sAcc;
  unsigned long cAcc;
};

NAV_VELNED velned;

void calcChecksum(unsigned char* CK) {
  memset(CK, 0, 2);
  for (int i = 0; i < (int)sizeof(NAV_VELNED); i++) {
    CK[0] += ((unsigned char*)(&velned))[i];
    CK[1] += CK[0];
  }
}

bool processGPS() {
  static int fpos = 0;
  static unsigned char checksum[2];
  const int payloadSize = sizeof(NAV_VELNED);

  while ( Serial2.available() ) {
    byte c = Serial2.read();
    if ( fpos < 2 ) {
      if ( c == UBX_HEADER[fpos] )
        fpos++;
      else
        fpos = 0;
    }
    else {
      if ( (fpos-2) < payloadSize )
        ((unsigned char*)(&velned))[fpos-2] = c;

      fpos++;

      if ( fpos == (payloadSize+2) ) {
        calcChecksum(checksum);
      }
      else if ( fpos == (payloadSize+3) ) {
        if ( c != checksum[0] )
          fpos = 0;
      }
      else if ( fpos == (payloadSize+4) ) {
        fpos = 0;
        if ( c == checksum[1] ) {
          return true;
        }
      }
      else if ( fpos > (payloadSize+4) ) {
        fpos = 0;
      }
    }
  }
  return false;
}

void setup() 
{
  Serial.begin(SERIAL_BAUD);
  Serial2.begin(GNSS_BAUD);
}

void loop() {
  if ( processGPS() ) {
    Serial.print("iTOW: "); Serial.print(velned.iTOW);
    Serial.print(" velN: "); Serial.print(velned.velN * 1e-2f);
    Serial.print(" velE: "); Serial.print(velned.velE * 1e-2f);
    Serial.print(" velD: "); Serial.print(velned.velD * 1e-2f);
    Serial.print(" speed: "); Serial.print(velned.speed);
    Serial.print(" gSpeed: "); Serial.print(velned.gSpeed);
    Serial.print(" heading: "); Serial.print(velned.heading * 1e-5f);  // same that divide on 100000.0f
    Serial.print(" sAcc: "); Serial.print(velned.sAcc);
    Serial.print(" cAcc: "); Serial.print(velned.cAcc/100000.0f);  // same that multiply on 1e-5f
    Serial.println();
  }
}

This code produces a result...but it happens once every 4 minutes. You can look at the time difference between the iTOW.

iTOW: 37094500 velN: -0.01 velE: -0.02 velD: -0.07 speed: 7 gSpeed: 2 heading: 284.72 sAcc: 53 cAcc: 42.52
iTOW: 37102500 velN: 0.02 velE: 0.10 velD: -0.01 speed: 11 gSpeed: 11 heading: 285.19 sAcc: 94 cAcc: 38.25
iTOW: 37342500 velN: -0.26 velE: 0.91 velD: 0.17 speed: 96 gSpeed: 94 heading: 106.45 sAcc: 126 cAcc: 42.51

Could you please clarify some of the issues I encountered? Sincerely!

iforce2d commented 8 months ago

Better to ask a question in the comments of the video. I had this problem occasionally, I think it was because I had not disabled other messages. The code in my video only expects to receive one type of message.

brightproject commented 8 months ago

etter to ask a question in the comments of the video.

There I asked the question first, but there were fears that the material was old and the question would get lost. Yes, and I have a lot of messages, in addition to the one I want to parse. binary_console_ubx

I'll try to turn off other messages and look at the parsing speed. You could make a repository of your examples from YouTube on Github. YouTube is still not exactly a platform for discussing code.

I modified the code in order not to spawn functions. It displays two messages, but very, very slowly. At the same time, the NEO-6M module produces data with a frequency of 10Hz.

#define SERIAL_BAUD 115200
#define GNSS_BAUD 9600

// #define POSLLH
#define VELNED

HardwareSerial Serial2(USART2); // PA2(TX2), PA3(RX2)

const unsigned char UBX_HEADER[] = {0xB5, 0x62};

struct NAV_POSLLH {
  unsigned char cls;
  unsigned char id;
  unsigned short len;
  unsigned long iTOW;
  long lon;
  long lat;
  long height;
  long hMSL;
  unsigned long hAcc;
  unsigned long vAcc;
} posllh;

struct NAV_VELNED {
  unsigned char cls;
  unsigned char id;
  unsigned short len;
  unsigned long iTOW; // U4
  long velN; // I4
  long velE;
  long velD;
  unsigned long speed;
  unsigned long gSpeed;
  long heading;
  unsigned long sAcc;
  unsigned long cAcc;
} velned;

void setup() 
{
  Serial.begin(SERIAL_BAUD);
  Serial2.begin(GNSS_BAUD);
}

void loop() {

#ifdef POSLLH

  bool GPS_POSLLH = processGPSData(sizeof(NAV_POSLLH), UBX_HEADER, &posllh);

  if ( GPS_POSLLH ) {

  Serial.print("iTOW:");      Serial.print(posllh.iTOW);
  Serial.print(" lat/lon: "); Serial.print(posllh.lat/10000000.0f); 
  Serial.print(","); 
  Serial.print(posllh.lon/10000000.0f);
  Serial.print(" height: ");  Serial.print(posllh.height/1000.0f);
  Serial.print(" hMSL: ");    Serial.print(posllh.hMSL/1000.0f);
  Serial.print(" hAcc: ");    Serial.print(posllh.hAcc/1000.0f);
  Serial.print(" vAcc: ");    Serial.print(posllh.vAcc/1000.0f);
  Serial.println();

  }

    #endif

    #ifdef VELNED

  bool GPS_VELNED = processGPSData(sizeof(NAV_VELNED), UBX_HEADER, &velned);

  if ( GPS_VELNED ) {

  Serial.print("iTOW: "); Serial.print(velned.iTOW); // GPS Millisecond Time of Week
  Serial.print(" velN: "); Serial.print(velned.velN * 1e-2f); // m/s
  Serial.print(" velE: "); Serial.print(velned.velE * 1e-2f); // m/s
  Serial.print(" velD: "); Serial.print(velned.velD * 1e-2f); // m/s
  Serial.print(" Spd (3D): "); Serial.print(velned.speed * 1e-2f); // m/s
  Serial.print(" GndSpd (2D): "); Serial.print(velned.gSpeed * 1e-2f); // m/s
  Serial.print(" Hdg (2D): "); Serial.print(velned.heading * 1e-5f); // same that divide on 100000.0f
  Serial.print(" Spd Acc: "); Serial.print(velned.sAcc * 1e-2f);
  Serial.print(" Crs/Hdg Acc: "); Serial.print(velned.cAcc/100000.0f);
  Serial.println();

  }

    #endif

}

void calcChecksum(unsigned char* CK, const void* INIT, size_t size) {
  memset(CK, 0, 2);
  const unsigned char* data = (const unsigned char*)INIT;
  for (size_t i = 0; i < size; i++) {
    CK[0] += data[i];
    CK[1] += CK[0];
  }
}

bool processGPSData(const int payloadSize, const unsigned char* header, void* dataStruct) {
  static int fpos = 0;
  static unsigned char checksum[2];

  while (Serial2.available() > 0) {
    byte c = Serial2.read();
    if (fpos < 2) {
      if (c == header[fpos])
        fpos++;
      else
        fpos = 0;
    } else {
      if ((fpos - 2) < payloadSize)
        ((unsigned char*)dataStruct)[fpos - 2] = c;

      fpos++;

      if (fpos == (payloadSize + 2)) {
        calcChecksum(checksum, dataStruct, payloadSize);
      } else if (fpos == (payloadSize + 3)) {
        if (c != checksum[0])
          fpos = 0;
      } else if (fpos == (payloadSize + 4)) {
        fpos = 0;
        if (c == checksum[1]) {
          return true;
        }
      } else if (fpos > (payloadSize + 4)) {
        fpos = 0;
      }
    }
  }
  return false;
}

If compile code with

#define POSLLH
// #define VELNED

Output to serial is fast

iTOW:58672700 lat/lon: 49.51,8.76 height: 188.51 hMSL: 175.28 hAcc: 2.30 vAcc: 1.94
iTOW:58672800 lat/lon: 49.51,8.76 height: 188.51 hMSL: 175.29 hAcc: 2.30 vAcc: 1.94
iTOW:58672900 lat/lon: 49.51,8.76 height: 188.52 hMSL: 175.30 hAcc: 2.30 vAcc: 1.94
iTOW:58673000 lat/lon: 49.51,8.76 height: 188.54 hMSL: 175.31 hAcc: 2.30 vAcc: 1.94

If compile code with

// #define POSLLH
#define VELNED

Output to serial is very very slow

iTOW: 58712700 velN: 0.02 velE: -0.04 velD: -0.04 Spd (3D): 0.06 GndSpd (2D): 0.04 Hdg (2D): 133.72 Spd Acc: 0.26 Crs/Hdg Acc: 46.51
iTOW: 58729900 velN: 0.07 velE: -0.28 velD: -0.04 Spd (3D): 0.29 GndSpd (2D): 0.29 Hdg (2D): 281.74 Spd Acc: 0.46 Crs/Hdg Acc: 41.36
iTOW: 58751300 velN: 0.00 velE: 0.07 velD: -0.01 Spd (3D): 0.07 GndSpd (2D): 0.07 Hdg (2D): 99.11 Spd Acc: 0.32 Crs/Hdg Acc: 41.25

Output is spontaneous, once every 5 - 30 seconds.

iforce2d commented 4 months ago

As I said, the code in my video only expects to receive one type of message.

If the GPS module is outputting other messages, my code will start reading bytes in expecting them to be for the one message it knows about. This will result in the CRC check not matching, and the algorithm resets to waiting for the next 'µb' header marker. Depending on the length of your desired packet and the unrecognized ones, and how many unrecognized packet types are being output, most of the time that header marker will be for some other packet, and so the process repeats over and over. Once in a while, you get lucky and the packet you want will be detected.

I made a follow-up video where I showed how you could detect and use more than one type of packet: https://youtu.be/ylxwOg2pXrc?t=377