Парсинг NMEA

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

"раставить" указатели и "занулить" запятые - КРАСИВО, ЧЕРТ ВОЗЬМИ !!!

stepan_sotnikov
Offline
Зарегистрирован: 06.04.2019

Пойду переваривать... В очередной раз огромное спасибо. Могу сказать я и со strchr понаписал громздко.
по сравнению с Вашей реализацией.
И не смотря, что вторая реализация Ваша (без  "лишних" функций) отдаленно напоминает мою, но
У меня просто нагромождение какое то, даже не смотря на попутные мои  вычисления.
Для себя уяснил, что я плохо работаю с разыменованием указателей... меня это сильно путает... и путает.
Пойду тренироваться на кошечках ... 
 

boolean prs(char * s, float * latlon) {
  int i = 0; //счетчик
  int adr[13]; // сюда мы запишем адреса разделителей ','
  int checksum = 0; //вычисленная Контрольная сумма
  int check = 0; // Контрольная сумма полученная из сообщением NMEA
  int k = 0; //счетчик для записи адресов разделителей
  while (s[++i] != '*') { //следим за началом контрольной суммы, она начинается после '*'
    checksum ^= (int) s[i]; // считаем побитно хор контрольную сумму
    if (s[i] == ',') {
      adr[k++] = i; //записываем в массив адреса разделителей в исходном массиве
    }
    s[i] -= '0'; // переводим знаки в цифры
  }
  while (s[++i] != '\r') {
    if (s[i] > 58) {
      s[i] -= 55; 
    } else {
      s[i] -= '0'; 
    }
   check = check * 0x10 + s[i]; //переводи в НЕХ к каждому следующий разряд прибавляем к предыдущему
  }
  /*проверка соответствия контрольных */
  check = check - checksum; // если не ноль то сумма не совпала...
  if (check != 0) {
    ERROR_0 //контрольная сумма не верна
    return false; //обработка ERROR
  }

Да еще в прошлый раз для меня было не ясно зачем вводить такую конструкцию 
while ((sizeof(nmeaStringParts) > nmeaStringPartNo) && *ptrCurrentChar){} 
а не так?

while ( *ptrCurrentChar) {}

 попозже сам допетрил... 
 

sadman41
Offline
Зарегистрирован: 19.10.2016

С sizeof() я, конечно, наплутал - забыл на размер первого элемента поделить. Но в целом - это чтобы не вылезти за размеры массива указателей и не начать писать куда попало.

С подсчётом CRC у меня так вышло:

const char nmeaFieldSeparator       = ',';
const char nmeaSignDataStart        = '$';
const char nmeaSignDataEnd          = '*';
const char nmeaSignFieldEnd         = '\0';

const uint8_t gprmcFieldsNumber     = 0x0E;
const uint8_t gprmcFieldNoLatitude  = 0x03;
const uint8_t gprmcFieldNoLongitude = 0x05;
const uint8_t gprmcFieldNoCrc       = 0x0D;

char nmeaString[] = "$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E";

void setup() {
  char *ptrCurrentChar, *ptrPartStart;
  char* nmeaStringParts[gprmcFieldsNumber];
  uint8_t nmeaStringPartNo = 0x00, makeCRC = false, parsingDone, calculatedCRC = 0x00, recivedCRC;

  Serial.begin(115200);
  Serial.println("NMEA split and CRC calculating example\n");

  ptrCurrentChar = ptrPartStart = nmeaString;
  // nmeaString can have nothing data
  parsingDone = (nmeaSignFieldEnd == *ptrCurrentChar);

  // gprmcFieldsNumber => sizeof(nmeaStringParts) / sizeof(nmeaStringParts[0])
  while (!parsingDone && (gprmcFieldsNumber > nmeaStringPartNo)) {
    if (nmeaSignDataEnd == *ptrCurrentChar) {
      // Stopping CRC calculating if '*' detected
      makeCRC = false;
    }
    if (makeCRC) {
      // Calculate CRC if allowed
      calculatedCRC ^= *ptrCurrentChar;
    }
    if (nmeaSignDataStart  == *ptrCurrentChar) {
      // Starting CRC calculating if '$' detected
      makeCRC = true;
    }

    parsingDone = (nmeaSignFieldEnd == *ptrCurrentChar);

    if (nmeaFieldSeparator == *ptrCurrentChar || nmeaSignDataEnd == *ptrCurrentChar || parsingDone) {
      // Taking chunk of the NMEA string when field separator, data end sign, or end of line was detected
      *ptrCurrentChar = '\0';
      Serial.print("No: "); Serial.print(nmeaStringPartNo);
      Serial.print(",\tchunk: "); Serial.print(ptrPartStart);
      nmeaStringParts[nmeaStringPartNo++] = ptrPartStart;
      ptrPartStart = ptrCurrentChar + 1;
      Serial.print(",\ttail: "); Serial.println(ptrPartStart);
    }
    ptrCurrentChar++;
  }

  for (uint8_t i = 0x00; nmeaStringPartNo > i; i++) {
    Serial.print(i); Serial.print('\t'); Serial.println(nmeaStringParts[i]);
  }
  Serial.println();

  Serial.print("CRC (recived): "); Serial.println(nmeaStringParts[gprmcFieldNoCrc]);
  Serial.print("CRC (calculated): "); Serial.println(calculatedCRC, HEX);

  recivedCRC = strtoul(nmeaStringParts[gprmcFieldNoCrc], NULL, 16);
  Serial.print("NMEA CRC is "); Serial.println((recivedCRC == calculatedCRC) ? "OK" : "BAD");

  float latitude  = atof(nmeaStringParts[gprmcFieldNoLatitude]);
  float longitude = atof(nmeaStringParts[gprmcFieldNoLongitude]);

  Serial.print("latitude: "); Serial.println(latitude, 6);
  Serial.print("longitude: "); Serial.println(longitude, 6);
}

void loop() {}

Конечно, в реале ASCIIZ строки не будет, наверное, и завершать цикл разбора придётся по другому терминатору. Надо будет подключить GPS от автомобильного видеорегистратора, посмотреть что там в реальности прёт и с какой скоростью.

 

stepan_sotnikov
Offline
Зарегистрирован: 06.04.2019

если паметь не изменяет мне, NMEA 0183 3 версии 3, ограничивает строку 82 ли 83 симовола... так, что читать дальше смысла нет, значит идет мусор... в реали "детские модули" да и посерьезнее, очень любят зависать и слать мусор...  Самое важное это '*' потом два занак контрольной суммы... '/0' может и не прилететь... А может  '/0' слать когда не надо...  Скоро прибудут из Китая GY-NEO6MV2 потому, что на работе, мои специ ругаются на меня... "Вы вот геодезист, вот и говорите нам, что делать... А програмированием занимаемся Мы", "Если Вы хотите, мы зделаем, оно хоть в космос полетит" "зачем вам ардуино? 32 хотя бы, с вашей математикой 64 ели ели справляются" А мне, не надо чего то конкретного. Я просто хочу своими руками, а не в теории... не по ГОСТам, и стандартам =)))
Завтра буду изучать, вашу программу... На работе в una, загружу... Мне нравится элегантность в Вашем кодинге =)

 

PRC
Offline
Зарегистрирован: 03.02.2019

NMEA не терминирует конец строки 0. В конце будут CR&LF.

NAYGER
Offline
Зарегистрирован: 15.03.2020

Elmirus пишет:

Получилось сделать как Вы говорили, но при помощи strtok таким образом:

#include "string.h"


char s[] ="$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E";
char * p;

void setup(void) {
Serial.begin(115200);

p = strtok (s,",");

  while (p != NULL)
  {
   Serial.println(p);
    p = strtok (NULL, ",");
  }
    
}

void loop(void) {}

вышло:

$GPRMC
214603.000
A
4634.54557
N
03047.50473
E
0.00
0.00
040617
A*6E
 
В какой среде можно запустить этот код, для проверки?
Code::Blocks ругается на строчки:
Serial.begin(115200);
Serial.println(p);