Парсинг NMEA

Elmirus
Offline
Зарегистрирован: 07.06.2017

Добрый вечер, подскажите пожалуйста как правильно парсить координаты NMEA, или как изменить формат координат, которые извлекают уже существующие библиотеки

Например, нужно соорудить такую строчку: 130402213013,+380680000000,GPRMC,173013.000,A,6146.4979,N,03421.2399,E,1.92,21.48,020413,,,A*5B,F,, imei:867567020000000,00,-16.4,F:3.73V,0,139,49646,250,99,1478,68A7\n\r

От GPS модуля приходит такая вот штуковина: $GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E

То есть мне нужно извлечь координаты в таком формате 6146.4979,N,03421.2399,E и вставить их в нужную мне строку, которую описывал первой, например присвою их переменным и уже там буду играться как умею, ==НО== парсить, я совсем не умею и когда использовал библиотеку TinyGPS - координаты мне приходили в таком формате: LAT=46.576423 LON=30.788080, то есть проблема у меня в том как при помощи библиотеки или куска кода для парсинга

из этого - 46.576423 30.788080 сделать это вот 4634.54557 03047.50473 ??? Бо и точки не там стоят да и количество цифр совсем не то в каком формате принимает сервер.

Сервер принимает в таком формате 4634.54557 03047.50473, а в таком 46.576423 30.788080 не принимает, помогите пожалуйста!

 

отправляю данные вот такой функцией:

//разбиваю одну строку на несколько, что б в функции вместо тестового cord применять message где и должны хранится координаты
const char resource[] = "130402213013,+380688260000,GPRMC,175135.000,A,";
const char cord[] = "6146.4979,N,03421.2399,E";   //вот такие координаты может принять сервер
const char stat[] = ",1.92,21.48,100817,,,A*5B,F,, imei:867567021260000,00,-16.4,F:3.73V,0,139,49646,250,99,1478,68A7\n\r";

void TcpGprsMessage(String message){

     sendData("AT+CREG?",3000,DEBUG);     
     sendData("AT+CGATT=1",1000,DEBUG);
     sendData("AT+CGDCONT=1,\"IP\",\"www.ab.kyivstar.net\"",1000,DEBUG);
     sendData("AT+CGACT=1,1",1000,DEBUG);
     sendData("AT+CIPSTART=\"TCP\",\"88.198.136.232\",10001",3000,DEBUG);
     sendData("AT+CIPSEND=80",1000,DEBUG);
     sendData(String("GET ") + resource + cord + stat, 2000,DEBUG); 
     delay(1000);
     sendData("AT+CIPCLOSE",2000,DEBUG);     //Close TCP
     delay(100);

 

}

 

Сам код:

#define DEBUG true

char byteGPS=-1;
char linea[300] = "";
char comandoGPR[7] = "$GPRMC";
int cont=0;
int bien=0;
int conta=0;
int indices[13];

int GPS_time=30;                        // When the board gets the 30 times location information successfully and the location information will be send by sms.      
String target_phone = "+380634250000"; // Your phone number,be careful need to add a country code before the cellphone number

String GPS_position="";
int GPS_position_count=0;


const char resource[] = "130402213013,+380688260000,GPRMC,175135.000,A,";
const char cord[] = "6146.4979,N,03421.2399,E";
const char stat[] = ",1.92,21.48,100817,,,A*5B,F,, imei:867567021260000,00,-16.4,F:3.73V,0,139,49646,250,99,1478,68A7\n\r";


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(8,OUTPUT);
  digitalWrite(5, HIGH); 
  digitalWrite(4, LOW); 
  digitalWrite(8, LOW); 
  delay(2000);
  digitalWrite(8, HIGH); 
  delay(3000);       
  digitalWrite(8, LOW);
  Serial.println("A7 Power ON!");
  sendData("AT+GPS=0",3000,DEBUG);     //Close GPS
  for(int i=0;i<2;i++){                //Make sure the GPS has been turned on
    sendData("AT+GPSRD=1",1000,DEBUG);
    Serial1.println("AT+GPS=1");
  }
  Serial.println("*********************************************************");
  Serial.println("**If don`t display 'GPS positioning....',please reboot.**");
  Serial.println("*********************************************************");
}

void loop()
{
    testgps();
}

void testgps(){
  while(Serial1.available()){
   byteGPS=Serial1.read();  
  // Read a byte of the serial port
   if (byteGPS == -1) {       
    // See if the port is empty yet
   } 
   else {
     // note: there is a potential buffer overflow here!
     linea[conta]=byteGPS;        // If there is serial port data, it is put in the buffer
     conta++;                      
     //Serial.print(byteGPS);    // '//',есди удалить то будут отправлятся все данные 
     if (byteGPS==13){
      // If the received byte is = to 13, end of transmission
      // note: the actual end of transmission is <CR><LF> (i.e. 0x13 0x10)
      cont=0;
      bien=0;
      // The following for loop starts at 1, because this code is clowny and the first byte is the <LF> (0x10) from the previous transmission.
       for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
         if (linea[i]==comandoGPR[i-1]){
           bien++;
         }
       }
       if(bien==6){ 
        // If yes, continue and process the data
        //Data Partitioning
         for (int i=0;i<300;i++){
           if (linea[i]==','){    // check for the position of the  "," separator
             // note: again, there is a potential buffer overflow here!
             indices[cont]=i;
             cont++;
           }
           if (linea[i]=='*'){    // ... and the "*"
             indices[12]=i;
             cont++;
           }
         }
         //panel data, for example:Direction (E/W):Longitude-Direction(N/S):Latitude<--->E:11350.51872-N:2236.40687        
         for(int i=5;i>1;i--){
           for (int j=indices[i];j<(indices[i+1]-1);j++){
             GPS_position+=linea[j+1];
           }
           if((i==5)||(i==3)){
             GPS_position+=":";  
           }else if(i==4){
             GPS_position+="-";   
           }
         }
         //If the return ":-:", it means empty data, continue positioning 
         if(GPS_position==":-:"){
          Serial.println("GPS positioning....");
         }else{
            Serial.println(GPS_position);
            GPS_position_count++;
            //When GPS_position_count is equivalent to GPS_time, stop positioning and start to send sms messages
            if(GPS_position_count==GPS_time){
            GPS_position_count=0;          //Reset count
            sendData("AT+GPS=0",1000,DEBUG);
            delay(1000); 
            //SendTextMessage(GPS_position); //SMS
            TcpGprsMessage(GPS_position); //Internet
            sendData("AT+GPS=1",1000,DEBUG); 
           }
                  
         }
       }
       GPS_position="";
       conta=0;                    // Reset the buffer
       for (int i=0;i<300;i++){    //  
       linea[i]=' ';             
       }                
     }
   }  
   }
}

void TcpGprsMessage(String message){

     sendData("AT+CREG?",3000,DEBUG);     
     sendData("AT+CGATT=1",1000,DEBUG);
     sendData("AT+CGDCONT=1,\"IP\",\"www.ab.kyivstar.net\"",1000,DEBUG);
     sendData("AT+CGACT=1,1",1000,DEBUG);
     sendData("AT+CIPSTART=\"TCP\",\"88.198.136.232\",10001",3000,DEBUG);
     sendData("AT+CIPSEND=80",1000,DEBUG);
     sendData(String("GET ") + resource + message + stat, 2000,DEBUG); 
     delay(1000);
     sendData("AT+CIPCLOSE",2000,DEBUG);     //Close TCP
     delay(100);

     //sendData(String("GET ") + resource + imei + message,2000,DEBUG); 
}

void SendTextMessage(String message)
{ 
  sendData("AT+CMGF=1",5000,DEBUG);            //Set the SMS in text mode
  delay(100);
  sendData("AT+CMGS="+target_phone,2000,DEBUG);//send sms message to the cellphone , be careful need to add a country code before the cellphone number
  delay(100);
  sendData(message,2000,DEBUG);                //the content of the message
  delay(100);
  Serial1.println((char)26);                  //the ASCII code of the ctrl+z is 26
  delay(100);
  sendData("",1000,DEBUG);                     //Clear serial data
  delay(100);
}

void sendData(String command, const int timeout, boolean debug)
{
    String response = "";    
    Serial1.println(command); 
    long int time = millis();   
    while( (time+timeout) > millis()){
      while(Serial1.available()){       
        response += (char)Serial1.read(); 
      }  
    }    
    if(debug){
      Serial.print(response);
    }    
}

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы хотите научиться парсить или получить готовый код?

Elmirus
Offline
Зарегистрирован: 07.06.2017

Ну с отсутствием достаточного времени пытаюсь учиться уже на готовых решениях. В интернете искал полуготовые варианты и эксперементировал, но к сожалению ничего понять не смог. В вышеуказанном коде, который на 60% не мой есть парсер (выдает в таком формате E,03047.49174,N,4634.54769, что совсем наоборот и с лишними разделителями), но как он разбирает строку, я никак немогу вдуплить, что б подкоректировать. Хотелось бы решения по проще, что б мог методом тыка почучуть разбирать и учиться, или может на крайний случай как-то можно в TinyGPS получать координаты в нужном формате.  

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ответа не понял. В общем так, если интересно, я могу Вас шаг за шагом провести и через недельку Вы сможете писать практически любой парсер, который Вам нужен. Но это надо работать. Готовое рештене - это не ко мне, я заказов на ищу исполнителя не беру.

Elmirus
Offline
Зарегистрирован: 07.06.2017

Я только за! Помогите пожалуйста. Скайп, вайбер, телеграм?

toc
Offline
Зарегистрирован: 09.02.2013

Elmirus,
1. вы можете словами описать нужный формат?
2. как вы понимаете "парсинг"?
3. для парсинга(в моём понимании) nmea строки подходит упомянутая библиотека.
4. прочитайте полностью https://ru.m.wikipedia.org/wiki/Географические_координаты

Elmirus
Offline
Зарегистрирован: 07.06.2017

Нужно вытянуть вот этот кусочек 6146.4979,N,03421.2399,E

Из этого $GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E

То есть после третьей запято1 до седьмой запятой и убрать по последней цифре перед Е и N, что б после точки было 4 цифры а не 5. TinyGPS парсит не в том формате.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Elmirus пишет:
Из этого $GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E

У Вас этот текст в каком виде? Какого типа? Массив char?

Elmirus
Offline
Зарегистрирован: 07.06.2017

Да, он самый char. (byteGPS)

То есть пиходит в монитор порта вот такая шняга и из 2й строки $GPRMC нужно оставить только выделенное красным:

 

Кусок моего сооружения:

Elmirus
Offline
Зарегистрирован: 07.06.2017

toc пишет:
Elmirus, 1. вы можете словами описать нужный формат? 2. как вы понимаете "парсинг"? 3. для парсинга(в моём понимании) nmea строки подходит упомянутая библиотека. 4. прочитайте полностью https://ru.m.wikipedia.org/wiki/Географические_координаты

TinyGPS выдает долготу и широту в отличном общепринятом формате, но никто не виноват, что китайские устройства отправляют координаты через "жопу", под которые и подстроены GPS сервисы. Учитывая изложенное невозможно "сказать" GPS сервисам, что китайские устройства говно, а мои правильные, так что перенастраивайте свои серверы только под мой формат данных)

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Elmirus пишет:
TinyGPS выдает долготу и широту в отличном общепринятом формате, но никто не виноват, что  под которые и подстроены GPS сервисы. Учитывая изложенное невозможно "сказать" GPS сервисам, что китайские устройства говно, а мои правильные, так что перенастраивайте свои серверы только под мой формат данных)

А теперь скажу вам горькую правду. Это не китайские устройства отправляют координаты через "жопу", а вы банально не умеете работать со строковыми переменными.  Да и банально все "ардуинщики" "выше этого". Если будущих программистов в вузах натаскивают на это, то любители на это забивают болт. 

ПС: К сожалению этот пробел знаний у меня меня и расстраивает. Так что я сейчас тупо пытаюсь его закрыть. 

Elmirus
Offline
Зарегистрирован: 07.06.2017

Увы, проблема остаеться проблемой... Я раньше не думал, что упрусь в такой забор.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Elmirus пишет:

Увы, проблема остаеться проблемой... Я раньше не думал, что упрусь в такой забор.

А чего тормозить учитесь https://rsdn.org/article/cpp/cstr.xml .  Это основы . Причем самые что не на есть. Без них ну ни как. Я же говорю что программистам по Си что тем кто занимается связью ну ни как. Без этого ну только мигать светодиодом или скачивать чужие и кривые скетчи.

Вот еще https://rsdn.org/article/cpp/cstr.xml#ERFAC  Но лучше статью изучить с начала.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Elmirus пишет:

Да, он самый char. (byteGPS)

Тогда забыли про своё устройство, отложили его подальше на полочку, взяли ардуинку, компьютер, написали вот такой скетч:

void setup(void) {
    char * s = "та байда, которую Вы получаете";
    Serial.begin(115200);
    Serail.println(s);
}

void loop(void) {}

Убедились, что она печатает ту самую байду. Если я где-то опечатался (писал прямо здесь), пот подправлили и таки убедились.

После этого читаем пр функции strchr и strlen и пробуем выполнить слеждующее задание: напечатать все элмемнты исходной строки между запятыми, какждый элемент на отдельной строке.

Т.е., если исходная байда у Вас "12,ab,,cd", то результат должен быть

12
ab

cd

Делайте. Как сделаете, выкладывайте код. Если не получится, тоже выкладывайте код. Тогда продолжим.

Elmirus
Offline
Зарегистрирован: 07.06.2017

Еще пытаюсь разбираться, но уже достиг какихто результатов:

Так посчитал колличество символов:

void setup(void) {
    char * s = "$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E";
    Serial.begin(115200);
    int i = 0;
    for (i=0;i<strlen(s);i++) {
       
    }
   Serial.println(i);
}

void loop(void) {}

А так вдуплил, как начинать с какогото символа, сstrrchr начинать с конца, а strchr начинать с начала:

void setup(void) {
char *s = "$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E";
char *p;

p = strchr(s, ','); 
p = strrchr(s, 'o'); 

    Serial.begin(115200);

    Serial.println(p);
   

    
}

void loop(void) {}

Осталось "навеное" склеить это в одно целое, надеюсь до завтра разберусь

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Вот вам еще один пробел в ваших знаниях http://cpp.com.ru/kr_cbook/ch5kr.html#p53

/**/
//--------------------------------------------------
//-------Компановка-------------------------------------------
const char *s = "$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E";
//-----main()---------------------------------------------
void setup(void) {

  Serial.begin(9600);
  unsigned int len = strlen(s);
  char *p = new char[len + 1];
  for (unsigned int i = 0; i <= len; i++) {
    p[i] = s[i];
  }
  Serial.println(p);
}
void loop(void) {}
/*Скетч использует 2144 байт (6%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 266 байт (12%) динамической памяти, оставляя 1782 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Elmirus
Offline
Зарегистрирован: 07.06.2017

ЕвгенийП пишет:

Т.е., если исходная байда у Вас "12,ab,,cd", то результат должен быть

12
ab

cd

Делайте. Как сделаете, выкладывайте код. Если не получится, тоже выкладывайте код. Тогда продолжим.

 

Получилось сделать как Вы говорили, но при помощи 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
 

 

После чего, я решил переделать через оператор FOR с приминением IF 

#include "string.h"


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

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

p = strtok (s,",");

  for(int i=0; p != NULL; i++)
  {
      a++;
      if (a == 5) {         // тут указываю число расчлененной части S
        Serial.println(p);
      }
  
    p = strtok (NULL, ",");
  }
    
}

void loop(void) {}

И получилось 03047.50473 то, что в принципе и нужно, но опять таки как отнять тут последнюю цифу?

 

Еще пытался сделать с strchr и 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 = strchr(s, 'A') + 2; // тут начало от А +2 символа
p = strtok(p, "N");     // тут после , обрезает лишнее но как сделать типо -2 ?

    Serial.println(p);
   
}

void loop(void) {}

Получилось 4634.54557, но я невдупляю как сделать в strok - 2 смвлома (, и 7), если бы вышло, то думаю это позволило бы достич желаемого результата в какой то мере. 

До srtlen пока еще не додуплил, разбираюсь. Пытаюсь разбиратся дальше...

Вроде бы начинает приходить почучуть просветление в чайник, но что-то чувствую, что движусь не по той дороге.

 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Забудьте про strtok. Используем  только strchr. Даже strlen не используем. Только так Вы получите понимание процесса. Использовать strtok - всё равно, что готовый скетч скачать.

Elmirus
Offline
Зарегистрирован: 07.06.2017

ЕвгенийП пишет:

Забудьте про strtok. Используем  только strchr. Даже strlen не используем. Только так Вы получите понимание процесса. Использовать strtok - всё равно, что готовый скетч скачать.

А..., я случайно наткнулся на strtok. Буду дальше переваривать, я прост подумал что strchr один не может сделать нужного

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Как раз может, но "вручную" без автоматизации. А Вам это и надо. Вам надо понимать как работать со строкой, а не найти какую-то волшебную библиотеку, которая всё по щучьему велению сделает.

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

Мне очень интересно, как бы вы дальше продолжили обучение и рассказ.
Кратнкая история, ковырял сходную тему про GPS, наткнулся на эту ветку формума. Увидел Вашу задачку, ну и подумал... Тю, это же просто...
 

void setup() 
{
  char *s = "та байда, которую Вы получаете";
    Serial.begin(9600);
      Serial.println (s);
      //___________________________
        int dln = (prs(s));
         Serial.print("длинна строки = ");
           Serial.println (dln);
}

char prs (char *s)
  { 
    int i = -1;
      while (s[++i] != NULL)
        {
           if (s[i] == ',')
             {
                 Serial.println ();
                  Serial.println (s[i]);
                    i++;
             }
          Serial.print(s[i]);
      }
    Serial.println();
  return i;
  }
//______________________________________________
void loop()
{
}

А дальше, открыл для себя удивительные кирилические символы. И дикий пробел в понимании Char.
Для меня это раньше был int или int[], при выводи итерпритируется как символ, а теперь... когда надо напечатать символ надо в консоль послать от двух элементов массива, где они чудом как то складываться.
 ШэВ все пропало. Нужно продолжение твоего урока. Как мне отсортировать напремер не по ',' а по кирилической букве 'o' которая занимает 2 байта? Не прибегая к strtok, strchr, strlen?
 

lean_74
Offline
Зарегистрирован: 22.12.2015

stepan_sotnikov пишет:

Как мне отсортировать напремер не по ',' а по кирилической букве 'o' которая занимает 2 байта? Не прибегая к strtok, strchr, strlen?

Как вариант, перекодировать из UTF-8 в однобайтовую русскую кодировку Windows-1251, как тут 

b707
Offline
Зарегистрирован: 26.05.2017

начнем с того, нафига вообще обрабатывать кириллицу? Кириллицу, в крайнем случае, надо показывать пользователю на экране, а любую обработку и межпроцессорный обмен уж точно надо программировать только на латинице.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Нипатриатична!

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

1С нигодуэ.

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

ЕвгенийП, в рамках изучения языка и освоения платформы, тренеруюсь писать програмку. Выбрал для себя именно эту тему ибо, работаю в этой сфере и она мне близка.
Это мое обучение. По этому ожидаю от Вас, советов, критики, подсказок, наставлений. Вот код который я потихньку пишу, и попутно изучаю платформу. Программа откоментированна. По этим коментариям можно понять где я чего то не понимаю. Программа состоит из двух функций. Одна читает массив, формирует два массива вернее исходный меняет, второй формирует. Второй массив содержит адресса разделителей. Далее на основе этих данных формируем массив из двух элементов, куда записывает долготы и широты собранные функцией dir.

void setup()
{
Serial.begin(9600);
}

char   prs (char *s, float *latlon)// 
{
  int adr[13];// сюда мы запишем адреса разделителей ',' 
  int checksum = 0; //вычисленная Контрольная сумма
  int check = 0; // Контрольная сумма полученная сообщением NMEA
  int i = 0;//счетчик
  int k = 0;//счетчик для записи адресов разделителей
  while (s[++i] != '*')
  {
    checksum ^= (int) s[i];// считаем побитно хор контрольную сумму
    if (s[i] == ',') 
    {
      adr[k++] = i; //записываем в массив адреса разделителей в исходном массиве
    }
    s[i] -= '0';// переводим знаки в цифры 
  }
  // считываем контрольную сумму
  while (s[++i])//именно инкремент в начале, в противном случае проскакиваем
  {
    if (s[i] > 58)// больше то это цифры HEX>10DEC 
    {
      s[i] -= 55;//стандартом определенно заглавные буквы и в HEX
    }
    else
    {
      s[i] -= '0';//если менее 58 то это цифра, преобразуем вычитания знака '0'
    }
    /*если значение символа больше 57 (символ 9), то это уже буквы (числа) HEX
      начиная с А - 65 имеет значение 65-55=10 DEC или А в HEX, если значение меньше 58 то смело вычитаем '0' получаем из
      имени числа, его значение ;-)
      если, что то пойдет не так значит и не контрольная сумма и была =)
      65=A B C E F
      0=48 9=57
    */
    check = check * 0x10 + s[i]; //переводи в НЕХ к каждoму следующий разряд прибавляем к предыдущему
  }
  /*проверка соответствия контрольных сумм*/
  Serial.println(checksum, HEX);
  Serial.println(check, HEX);
  check = check - checksum; // если не ноль то сумма не совпала...
if (check != 0)
{
  Serial.println("*****ERROR*****"); 
 
//return; обработка ERROR
}
  
  /*
   * для GPRMC
   * 0-1 время
   * 1-2 коректность
   * 2-3 широта
   * 3-4 полушарие с-ю
   * 4-5 долгота
   * 5-6 полушарие з-в
   */
/*пока не рассматриваються другие сообщния NMEA*/
latlon[0] = dir (s, adr, 2, 3);
latlon[1] = dir (s, adr, 4, 5);
}
  
 //_________________
 /* функция преобразования в число
  *  в функцию передаем ссылку на масси (уже преобразованный из знаков в чилса
  *  ссылка на массив содержащий адреса разделителей
  *  и два числа между порядковый номер разделителя
  */
  float dir(char * on, int * adres, int strt, int stp ) {
  int i = adres[stp];// в переменную записываем значения адресс конечного разделителя
  int l = 0;//счетчик разряда целой части числа
  float itog = 0; //иницилизация переменной адресс которой возращает наша функция
  boolean flag = true;// флаг обозначающий, что мы находимся после точки разделяющие дробную часть 
    while (--i > adres [strt])
  {
    if (on[i] == -2) {// -2 два это значение которое остается после вычитания из '.' '0'
      flag = false; // переключаем флаг, 
      --i;
    }
    if (flag) 
    {
      itog = (itog + (float) on[i]) / 10; // сбор числа начинаем с правой части, потихоньку сдвигая в разрядах в права делением на 10
                                         // эта дает возможность собрать правую часть числа без значительной потери точности
    }
    else
    {
      itog = (itog + (float) on[i] + (float) on[--i] * 10); // собираем левую часть числа она всегда 2 значная ибо в одном градусе 60 минут
      itog = itog / 60; //минуты перевели градусы градусы
      l = 1;
      while  (--i > adres [strt]) {
      itog = itog + (float)on[i] * l;  //добавили градусы их может быть до 3 цыфер 
      l = l * 10; // множитель для разряда (градусов)
      }

    }

  }
  
  /* для недопущения поетри точности, возможно для некотрых функций правельней передавать отдельно грдусы, отдельно десятые градусы
  потеря точности на 60 широты примерно 200мм в плане*/
  return itog;//вернули ссылку на результат
}

void loop()
{
char *incoming = "$GPRMC,000000.000,A,0000.00000,N,0000.00000,E,0.00,0.00,000000,,,A*5E";
  float latlon[]={0,0};
    Serial.println (incoming);
  prs (incoming, latlon);
  Serial.println (latlon[0],7);
  Serial.println (latlon[1],7);
 /* данные для поверки
"$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55";
"$GPRMC,092313.000,A,5956.39888,N,3018.53407,E,0.00,0.00,060419,,,A*5C";
"$GPRMC,092315.000,A,5956.39931,N,3018.53316,E,0.00,0.00,060419,,,A*5E";
"$GPRMC,092319.000,A,5956.40037,N,3018.53042,E,0.00,0.00,060419,,,A*51";
"$GPRMC,092320.000,A,5956.40077,N,3018.52922,E,0.00,0.00,060419,,,A*51";
"$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57";
"$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57";
"$GPRMG,092320.000,A,5956.00,N,3018.508,E,0.00,0.00,060419,,,A*00"; 
*/
delay (1000000);
}
/* ATmega328P
Скетч использует 4220 байт (13%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 286 байт (13%) динамической памяти, оставляя 1762 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

Вас atof() принципиально не устраивает?

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

Взял на заметку, поизучаю. Это мое обучение. можно же и просто подключить библиотеку и вместо всего что я тут изобразил написать gps.f_get_position(&lat, &lon);
Спасибо. Буду изучать эту функцию. Уже в карции почитал. Надо переходить к практики.
 

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

Еще раз спасибо за подсказку... изучил функцию. Попробывал применить в учебной своей реализации, но это надо менять вообще весь подход который я выбрал. Это следующий этап работа со строковыми операторами или как правильно они называються в этом языке. Попробывал сегодня забыть про все кроме "Используем  только strchr." как говорил ЕвгенийП... в http://arduino.ru/forum/programmirovanie/parsing-nmea#comment-302584
но что то не уловили его идею, что он имел в виду? Кроме того выяснил, что мой (возможно кривой способ) дает примерно такой же примерно результат как и atof() и по точности, скорости и памяти... Сейчас я поставил себе задачу научиться работать с переменными указателями массивами. И просто почувствовать язык. И его особенности. Я уже уяснил, что здесь нет зашиты от дурака, совсем нет.  

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

stepan_sotnikov пишет:

Еще раз спасибо за подсказку... изучил функцию. Попробывал применить в учебной своей реализации, но это надо менять вообще весь подход который я выбрал. 

Это перманентное состояние при обучении, чего бояться?

Менять особо не надо. Достаточно все запятые менять на '\0' и передавать atof()-у только указатель на начало фрагмента. Дальше он всё сделает сам.

stepan_sotnikov пишет:

Попробывал сегодня забыть про все кроме "Используем  только strchr." как говорил ЕвгенийП... в http://arduino.ru/forum/programmirovanie/parsing-nmea#comment-302584 но что то не уловили его идею, что он имел в виду?

ptr=strchr(s, ',') даёт указатель на запятую. Таким образом каждый вызов этой функции (вернее - последующий ptr++) ставит нас на начало следующего фрагмента (начало подстроки). А по адресу ptr можно и '\0' положить, сформировав таким образом кучу полноценных c-string: "$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55" => "$GPRMC\0092403.000\0A\05959.99840\0N\03018.53490\0E\00.00\00.00\0060419\0\0\0A*55"
Сохраняя указатели на начало подстрок в массив получаете... "массив" готовых c-string, которые уже потом хоть куда.
 

stepan_sotnikov пишет:

Кроме того выяснил, что мой (возможно кривой способ) дает примерно такой же примерно результат как и atof() и по точности, скорости и памяти...

Сомнительно, конечно, но на частоте 16Mhz сложно уловить разницу ;)

stepan_sotnikov пишет:

Я уже уяснил, что здесь нет зашиты от дурака, совсем нет.  

Компилятор - ваш друг. Включите уровень отладки "All".

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

stepan_sotnikov пишет:
Попробывал сегодня забыть про все кроме "Используем  только strchr." как говорил ЕвгенийП... в но что то не уловили его идею, что он имел в виду?
Я имел в виду, что это базовая функция. Более сложные написаны с её использованием. Если Вы сумете работать с нею, то Вы 1) будете в общих чертах понимать как устроены остальные функции изнутри; 2) сможете уверенно пользоваться всем остальным.

Это как научиться считать параметры электрических цепей вручную или пользоваться, например, мультисимом или протеусом, чтобы они посчитали все напряжения и сопротивления в схеме. Если сразу начать со второго - велик риск, что просто "насобачитесь как мартышка без глубокого понимания" и это всё сломается на первой же нетривиальной схеме, где те по каким-то причинам глюкнут, а если с первого, то потом можно и вторым пользоваться, но с пониманием что и как, а значит и с пониманием ограничений и особенностей такого моедлирования и автоматического расчёта.

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

strchr(s, ',')  дает ссылку на первую запятую. Дальше по конструкции у  меня ступор... Завтра подумаю... и погуглю...

void loop()
{
char *incoming = "$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55";
int pr; //переменная для strchr() даёт указатель на запятую
pr=strchr(incoming,',');//получили указатель на первую запятую
Serial.println(pr,DEC);
int bgn=incoming;// адрес начала массива incoming[]
Serial.println(bgn);// 
char *f=pr; 
Serial.println(f);
char prr=strchr(f,',');//по идеи f указатель на первую запятую, и strchr()должен найти след в массиве
char *g=prr; // но этого не происходит
Serial.println(g);
delay (1000000);
}

 

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

ЕвгенийП это  яуже понял. Я сам сторонник таких методов... Следую указанному Вами курсу, решил задачу без знаний функций, теперь разбираюсь в базовой... Но что то плохо дается... пока не соображу... 

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

Продолжаю обучатся и колупать. Вроде бы все работает. По прежнему не удалось одолеть strchr(); но поизучал UART ну и свместил...  

/*Используем библиотеку SoftwareSerial версии 1.0
  Скетч использует 6154 байт (19%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 569 байт (27%) динамической памяти, оставляя 1479 байт для локальных переменных. Максимум: 2048 байт.ATmega328P
  Учебная програама. Чтение UART NMEA сообщения и разбор GPRMC с получением GG.GGGG Широта/Долгота
  http://arduino.ru Stepan_Sotnikov
*/


#include <SoftwareSerial.h>
#define rxPin 10
#define txPin 11
SoftwareSerial mSerial = SoftwareSerial(rxPin , txPin);
void setup() {
  Serial.begin (57600);// !!! установить скорость порта
  while (!Serial) {}
  mSerial.begin(9600);
}
char *ridS ()
{
  char  *incoming = "$000000000000000000000000000000000000000*00\r0000000000000000000000000000000000000000000000000000000000000000";
  //'*00\r' обязательно, если не указть, в случаи не получение данных не считаеться контрольная сумма, и происходит переполнение
   
  boolean flg = true; //false
  int i = 0;
  char buff;
  long k=0;
  
  k=0;
  while (flg)
  {
    buff = mSerial.read ();
    
    //   Serial.print(buff);
    if (buff == '$')//Ждем начала NMEA
    { flg = false;   //выходим из режима ожидания
      //Serial.println("+++Вышли из ожидания+++");
      //необходим выходи если данные таки не придут!!!!
      incoming [i++] = '$'; //присовили массиву певрвое значение
    }
  //Serial.println(k);
  delay (1);
  if (k++>3500){Serial.println("превышено время ожидания соединения"); return incoming;}
  }
  //Serial.println("***Собираем сообщение NMEA");//собираем NMEA сообщение
  flg = true;
  k=0;
  while (flg) {
    buff = mSerial.read ();
    if (buff != -1) //если есть данные читаем
    {
      incoming [i] = buff;//записываем в массив
      ++i;
      if (i > 100) //контроль выхода за пределы
      {
        //Serial.println("!!!!!---ERROR---!!!!!");
        // Serial.print("Выход за пределы памяти");
        // Serial.println(i);
        i = 0;
      }//вылезаем за перделы выделеной памяти *incoming
    }
    if (k++>3500){Serial.println("обрыв соединения"); return incoming;}//выход в случаи обрыва передачи данных
     if (buff == '\r' ) // следим за концом пакета //// УСТАРЕВШЕЕ -> следим за началом передачи следующего пакета '$'(отследить по '\r' '\n' '\0' не получалось
    { //
      flg = false;//переключили флаг для выхода из  while
    }
  }
  // Serial.println("Получили данные");
  // Serial.println(incoming);
  //Serial.println("_____конец функции_____");
  return incoming;
}
boolean   prs (char *s, float *latlon)//
{
  int i = 0;//счетчик
  int adr[13];// сюда мы запишим адреса разделителей ','
  int checksum = 0; //вычисленная Контрольная сумма
  int check = 0; // Контрольная сумма полученая сообщением NMEA
  int k = 0;//счетчик для записи адресов разделителей
  // Serial.println ("зашли в PRS");
  //  Serial.println ("Получили в PRS ссыдку на ");
  // Serial.println (s);

  while (s[++i] != '*')//следим за началом контрольной контрольной суммы, она начинаеться после '*'
  {
    checksum ^= (int) s[i];// считаем побитно хор контрольную сумму
    if (s[i] == ',')
    {
      adr[k++] = i; //записываем в массив адреса разделителей в исходном массиве
    }
    s[i] -= '0';// переводим знаки в цифры
  }
  // считываем контрольную сумму '*00\r' Очень важны момент, возможно выход за пределы массива
  while (s[++i] != '\r') //именно инкремент в начале, в противном случае проскаиваем
  {
    if (s[i] > 58)// больше, то это цифры HEX>10DEC
    {
      s[i] -= 55;//стандартом определенно заглавные буквы и в HEX
    }
    else
    {
      s[i] -= '0';//если менее 58 то это цифра, преобразуем вычитания знака '0'
    }
    /*если значение символа больше 57 (символ 9), то это уже буквы (числа) HEX
      начиная с А - 65 имеет значение 65-55=10 DEC или А в HEX, если значение меньше 58 то смело вычитаем '0' получаем из
      имени числа, его значение ;-)
      если, что то пойдет не так значит и не контрольная сумма и была =)
      65=A B C E F
      0=48 9=57
    */
    check = check * 0x10 + s[i]; //переводи в НЕХ к каждoму следующий разряд прибавляем к предыдущему
  }
  /*проверка соответсвия контрольных сумм*/
  //  Serial.println(checksum, HEX);
  //  Serial.println(check, HEX);
  check = check - checksum; // если не ноль то сумма не совпала...
  if (check != 0)
  {
    Serial.println("*****ERROR*****");
    return false; //обработка ERROR
  }
  //Serial.println("Котрольная сумма верна");
  /*
     для GPRMC
     0-1 время
     1-2 коректность
     2-3 широта
     3-4 полушарие с-ю
     4-5 долгота
     5-6 полушарие з-в
  */
  /*пока не рассматриваються другие сообщния NMEA*/
  latlon[0] = dir (s, adr, 2, 3);
  latlon[1] = dir (s, adr, 4, 5);
return true;
}

//_________________
/* функция преобразования в число
    в функцию передаем ссылку на масси (уже преобразованный из знаков в чилса
    ссылка на маасив содержащий адресса разделителей
    и два числа между порядковый номер разделителя
*/
float dir(char * on, int * adres, int strt, int stp ) {
  int i = adres[stp];// в переменную записываем значения адресс конечного разделителя
  int l = 0;//счетчик разряда целой части числа
  float itog = 0; //иницилизация переменной адресс которой возращает наша функция
  boolean flag = true;// флаг обозначающий, что мы находимся после точки разделяющие дробную часть
  while (--i > adres [strt])
  {
    if (on[i] == -2) {// -2 два это значение которое остаеться после вычитания из '.' '0'
      flag = false; // переключаем флаг,
      --i;
    }
    if (flag)
    {
      itog = (itog + (float) on[i]) / 10; // сбор числа начинаем с правой части, потихоньку сдвигая в разрядах в права делением на 10
      // Serial.println(itog,7);                                 // эта дает возможность собрать правую часть числа без значительной потери точности
    }
    else
    {
      itog = (itog + ((float) on[i]) + ((float) on[--i]) * 10.0); // собираем левую часть числа она всегда 2 значная ибо в одном градусе 60 минут
      itog = itog / 60; //минуты перевели градусы градусы
      l = 1;
      while  (--i > adres [strt]) {
        itog = itog + (float)on[i] * l;  //добавили градусы их может быть до 3 цыфер
        l = l * 10; // множитель для разряда (градусов)
      }
    }
  }
  /* для недопущения поетри точности, возможно для некотрых функций правельней передавать отдельно грдусы, отдельно десятые градусы
    потеря точности на 60 широты примерно 200мм в плане*/
  return itog;//вернули ссылку на результат
}

void loop() {
  //Serial.println (ridS ());
  float latlon[] = {0, 0};//Массив ШИрота/Долгота GGG.GGGGG /GG.GGGGG
  //  Serial.println (incoming);
  if  (prs (ridS (), latlon)){Serial.print (latlon[0], 7);
  Serial.print (" ");
  Serial.println (latlon[1], 7);}
 else{ Serial.println ("ошибка чтения");}
  /* выводимый результат
    46.5757598 30.7917461
    59.9999732 30.3089141
    59.9399795 30.3089008
    59.9399871 30.3088855
    59.9400062 30.3088397
    59.9400138 30.3088207
    59.9400253 30.3087959
    59.9400253 30.3087959
  *****ERROR*****
    0.0000000 0.0000000


  */

}

/* для поверки использовалась UNA
    с приведеным ниже кодом
  #include <SoftwareSerial.h>
  //#define rxPin 10
  //#define txPin 11
  //SoftwareSerial mSerial=SoftwareSerial(10,11);
  void setup() {
  Serial.begin (9600);
  while (!Serial) {}
  // Serial.begin(230400);

  // put your setup code here, to run once:

  }

  void loop() {
  delay (3000);

  Serial.println ("$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E");
  //delay(1000);
  Serial.println ( "$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55");
  //delay(500);
  Serial.println ("$GPRMC,092313.000,A,5956.39888,N,3018.53407,E,0.00,0.00,060419,,,A*5C");
  // delay(250);
  Serial.println ("$GPRMC,092315.000,A,5956.39931,N,3018.53316,E,0.00,0.00,060419,,,A*5E");
  // delay(120);
  Serial.println ("$GPRMC,092319.000,A,5956.40037,N,3018.53042,E,0.00,0.00,060419,,,A*51");
  // delay(60);
  Serial.println ("$GPRMC,092320.000,A,5956.40077,N,3018.52922,E,0.00,0.00,060419,,,A*51");
  // delay(30);
  Serial.println ("$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57");
  // delay(15);
  Serial.println ("$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57");
  // delay(7);
  Serial.println ("$GPRMG,092320.000,A,5956.00,N,3018.508,E,0.00,0.00,060419,,,A*00");
  //delay(1); ;


  }
*/




 

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

stepan_sotnikov пишет:

Продолжаю обучатся и колупать. Вроде бы все работает.
По прежнему не удалось одолеть strchr(); 

Подмогну немного. Не совсем, конечно, мне нравится код, но пока в голову другое не пришло (хотя я вообще бы со strchr() не заморачивался, если честно и побегал по строке самостоятельно)

И, да, - странно строка во float преобразуется. Не знаю - допустимы в координатах такие отклонения или нет...

#define FIELDS_SEPARATOR        ','
#define GPRMC_FIELD_LATITUDE    0x03
#define GPRMC_FIELD_LONGITUDE   0x05

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

void setup() {
  char* ptrSeparator = nmeaString;
  char* nmeaStringParts[15];
  uint8_t nmeaStringPartNo = 0x00;

  Serial.begin(115200);
  Serial.println("strchr() example\n");

  nmeaStringParts[nmeaStringPartNo] = ptrSeparator;
  while ((sizeof(nmeaStringParts) > nmeaStringPartNo) && ptrSeparator) {
    nmeaStringPartNo++;
    ptrSeparator = strchr(ptrSeparator, FIELDS_SEPARATOR);
    if (ptrSeparator) {
      *ptrSeparator = '\0';
      ptrSeparator++;
    }
    nmeaStringParts[nmeaStringPartNo] = ptrSeparator;
    Serial.print("No: "); Serial.print(nmeaStringPartNo - 1);
    Serial.print(",\tchunk: "); Serial.print(nmeaStringParts[nmeaStringPartNo - 1]);
    Serial.print(",\ttail: "); Serial.println(ptrSeparator);
  }

  Serial.print("\nFound parts: "); Serial.println(nmeaStringPartNo);
  Serial.println("\nNo\tValue\n-------------------------------------------");
  for (uint8_t i = 0x00; nmeaStringPartNo > i; i++) {
    Serial.print(i); Serial.print('\t'); Serial.println(nmeaStringParts[i]);
  }
  Serial.println();

  float latitude  = atof(nmeaStringParts[GPRMC_FIELD_LATITUDE]);
  float longitude = atof(nmeaStringParts[GPRMC_FIELD_LONGITUDE]);

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

void loop() {}

 

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

попробую узучить Ваш код... позже скорее всего задам вопросы... пока я решаю вопросы на этой платформе в "лоб", мноигии консрукции мне не знакомы. Ну я думаю Вы и сами поняли мой уровень ;-) Что это первая программа на этом разновидности языка и платформе. Все что я использую это http://arduino.ru/Reference . Я очень благодарен за участие и помощь. 

enjoyneering
enjoyneering аватар
Offline
Зарегистрирован: 05.09.2016

я не критикую, просто хочу для себя разобраться. всем отличие и какие риемущества вот этого

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

от этого

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

спасибо.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

enjoyneering пишет:

хочу для себя разобраться. всем отличие и какие риемущества вот этого

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

от этого

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

Ну, тут много отличий. Парочка навскидку:

1.
Вверху указатель, а внизу массив. У них соответсвенно, разные sizeof. У верхнего - 2, а у нижнего - 71. Соответсвенно нижний можно передать (в качестве параметра), любому, заточенному на sizeof шаблону. К примеру, если передать  нижний EEPROM.put(), она вполне адекватно запишет в еепром строку, а если передать верхний, то запишутся только первые два симмвола.

2.
Строка вверху - константа (и компилятор об этом скажет, если попытаться прямо так скомпилировать). А строка внизу не константа, а просто область памяти с произвольным доступом.

В принципе, можно продолжать, но главное вроде уже сказано. 

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

то есть обявлять  char *s = "xxxxxx"; а потом работать с ним как char s[] не совсем корректно? Т.к. константа она и на то и константа, что бы быть константой =)))
Изменил свой код. Так же предусмотрел различные ошибки работы в программе.

/*Используем библиотеку SoftwareSerial версии 1.0
 * Скетч использует 6208 байт (19%) памяти устройства. Всего доступно 32256 байт.
 * Глобальные переменные используют 631 байт (30%) динамической памяти, оставляя 1417 байт для локальных переменных. Максимум: 2048 байт.
 * ATmega328P
 * Учебная программа. Чтение UART NMEA сообщения и разбор GPRMC с получением GG.GGGG Широта/Долгота
 * В программе предусмотрены обработки ошибок:
 * Отсутствие данных по UART "превышено время ожидания соединения"
 * Данные по UART перестали поступать "обрыв соединения"
 * Длинна сообщение не соответствует стандарту (NMEA 0183 rev 3.0)  (82 символа) "превышена максимальная длина сообщения NMEA"
 * Выход из функции происходит при не соответствии контрольных сумм, 
 * с выводом сообщения  "*****ERROR*****" При некорректных данных функция возвращает false
 *  <a href="http://arduino.ru" title="http://arduino.ru" rel="nofollow">http://arduino.ru</a> Stepan_Sotnikov
 */

#include <SoftwareSerial.h>

# define rxPin 10
# define txPin 11
char rzv[] = "$000000000000000000000000000000000000000*00\r000000000000000000000000000000000000000000*00\r";
//Максимальная длина сообщения ограничена 82 символами (NMEA 0183 rev 3.0)

SoftwareSerial mSerial = SoftwareSerial(rxPin, txPin);
void setup() {
  Serial.begin(115200); // !!! установить скорость порта
  while (!Serial) {}
  mSerial.begin(9600);
}
char * ridS() {
  // char  * incoming = "$000000000000000000000000000000000000000*00\r0000000000000000000000000000000000000000000000000000000000000000";
  //'*00\r' обязательно, если не указать, в случаи не получение данных не считается контрольная сумма, и происходит переполнение
  char * incoming = rzv;
  boolean flg = true; //false
  int i = 0;
  char buff;
  long k = 0;

  k = 0;
  while (flg) {
    buff = mSerial.read();

    //   Serial.print(buff);
    if (buff == '$') //Ждем начала NMEA
    {
      flg = false; //выходим из режима ожидания
      //Serial.println("+++Вышли из ожидания+++");
      //необходим выходи если данные таки не придут!!!!
      incoming[i++] = '$'; //присвоили массиву первое значение
    }
    //Serial.println(k);
    delay(1);
    if (k++ > 3500) {
      Serial.println("превышено время ожидания соединения");
      return incoming;
    }
  }
  //Serial.println("***Собираем сообщение NMEA");//собираем NMEA сообщение
  flg = true;
  k = 0;
  while (flg) {
    buff = mSerial.read();
    if (buff != -1) //если есть данные читаем
    {
      incoming[i] = buff; //записываем в массив
      ++i;
      if (i > 83) //контроль выхода за пределы
      {
        //Serial.println("!!!!!---ERROR---!!!!!");
        Serial.println("превышена максимальная длина сообщения NMEA");
        i = 0;
        return incoming;
      } //вылезаем за пределы выделенной памяти *incoming
    }
    if (k++ > 3500) {
      Serial.println("обрыв соединения");
      return incoming;
    } //выход в случаи обрыва передачи данных
    if (buff == '\r') // следим за концом пакета //// УСТАРЕВШЕЕ -> следим за началом передачи следующего пакета '$'(отследить по '\r' '\n' '\0' не получалось
    { //
      flg = false; //переключили флаг для выхода из  while
    }
  }
  // Serial.println("Получили данные");
  // Serial.println(incoming);
  //Serial.println("_____конец функции_____");
  return incoming;
}
boolean prs(char * s, float * latlon) //
{
  int i = 0; //счетчик
  int adr[13]; // сюда мы запишем адреса разделителей ','
  int checksum = 0; //вычисленная Контрольная сумма
  int check = 0; // Контрольная сумма полученная из сообщением NMEA
  int k = 0; //счетчик для записи адресов разделителей
  // Serial.println ("зашли в PRS");
  //  Serial.println ("Получили в PRS ссылку на ");
  // Serial.println (s);

  while (s[++i] != '*') //следим за началом контрольной суммы, она начинается после '*'
  {
    checksum ^= (int) s[i]; // считаем побитно хор контрольную сумму
    if (s[i] == ',') {
      adr[k++] = i; //записываем в массив адреса разделителей в исходном массиве
    }
    s[i] -= '0'; // переводим знаки в цифры
  }
  // считываем контрольную сумму '*00\r' Очень важны момент, возможно выход за пределы массива
  while (s[++i] != '\r') //именно инкремент в начале, в противном случае проскакиваем
  {
    if (s[i] > 58) // больше, то это цифры HEX>10DEC
    {
      s[i] -= 55; //стандартом определенно заглавные буквы и в HEX
    } else {
      s[i] -= '0'; //если менее 58 то это цифра, преобразуем вычитания знака '0'
    }
    /*если значение символа больше 57 (символ 9), то это уже буквы (числа) HEX
      начиная с А - 65 имеет значение 65-55=10 DEC или А в HEX, если значение меньше 58 то смело вычитаем '0' получаем из
      имени числа, его значение ;-)
      если, что то пойдет не так значит и не контрольная сумма и была =)
      65=A B C E F
      0=48 9=57
    */
    check = check * 0x10 + s[i]; //переводи в НЕХ к каждому следующий разряд прибавляем к предыдущему
  }
  /*проверка соответствия контрольных сумм*/
  //  Serial.println(checksum, HEX);
  //  Serial.println(check, HEX);
  check = check - checksum; // если не ноль то сумма не совпала...
  if (check != 0) {
    Serial.println("*****ERROR*****");
    return false; //обработка ERROR
  }
  //Serial.println("Контрольная сумма верна");
  /*
     для GPRMC
     0-1 время
     1-2 корректность
     2-3 широта
     3-4 полушарие с-ю
     4-5 долгота
     5-6 полушарие з-в
  */
  /*пока не рассматриваются другие сообщения NMEA*/
  latlon[0] = dir(s, adr, 2, 3);
  latlon[1] = dir(s, adr, 4, 5);
  return true;
}

//_________________
/* функция преобразования в число
    в функцию передаем ссылку на массив (уже преобразованный из знаков в числа
    ссылка на массив содержащий адреса разделителей
    и два числа между порядковыми номерами разделителя
*/
float dir(char * on, int * adres, int strt, int stp) {
  int i = adres[stp]; // в переменную записываем значения адрес конечного разделителя
  int l = 0; //счетчик разряда целой части числа
  float itog = 0; //инициализация переменной адрес которой возвращает наша функция
  boolean flag = true; // флаг обозначающий, что мы находимся после точки разделяющие дробную часть
  while (--i > adres[strt]) {
    if (on[i] == -2) { // -2 два это значение которое остаётся после вычитания из '.' '0'
      flag = false; // переключаем флаг,
      --i;
    }
    if (flag) {
      itog = (itog + (float) on[i]) / 10; // сбор числа начинаем с правой части, потихоньку сдвигая в разрядах в права делением на 10
      // Serial.println(itog,7);                                 // эта дает возможность собрать правую часть числа без значительной потери точности
    } else {
      itog = (itog + ((float) on[i]) + ((float) on[--i]) * 10.0); // собираем левую часть числа она всегда 2 значная ибо в одном градусе 60 минут
      itog = itog / 60; //минуты перевели градусы 
      l = 1;
      while (--i > adres[strt]) {
        itog = itog + (float) on[i] * l; //добавили градусы их может быть до 3 знаков
        l = l * 10; // множитель для разряда (градусов)
      }
    }
  }
  /* для недопущения потери точности, возможно для некоторых функций правильней передавать отдельно градусы, отдельно десятые градусы
    потеря точности на 60 широты примерно 200мм в плане*/
  return itog; //вернули ссылку на результат
}

void loop() {
  //Serial.println (ridS ());
  float latlon[] = {
    0,
    0
  }; //Массив Широта/Долгота GGG.GGGGG /GG.GGGGG
  //  Serial.println (incoming);
  if (prs(ridS(), latlon)) {
    Serial.print(latlon[0], 7);
    Serial.print(" ");
    Serial.println(latlon[1], 7);
  } else {
    Serial.println("ошибка чтения");
  }
  /* выводимый результат
46.5757598 30.7917461
59.9999732 30.3089141
59.9399795 30.3089008
59.9399871 30.3088855
59.9400062 30.3088397
59.9400138 30.3088207
59.9400253 30.3087959
59.9400253 30.3087959
*****ERROR*****
ошибка чтения
превышена максимальная длина сообщения NMEA
*****ERROR*****
ошибка чтения
*****ERROR*****
ошибка чтения
46.5757598 30.7917461
59.9999732 30.3089141
59.9399795 30.3089008
59.9399871 30.3088855
59.9400062 30.3088397


  */

}

/* //для поверки использовалась UNA
   // с приведенным ниже кодом
  #include <SoftwareSerial.h>
//#define rxPin 10
//#define txPin 11
SoftwareSerial mSerial=SoftwareSerial(10,11);
void setup() {
  Serial.begin (9600);
  while (!Serial) {}
 // Serial.begin(230400);
  
  // put your setup code here, to run once:

}

void loop() {
  delay (3000);
 
Serial.println ("$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E");
 //delay(1000);
Serial.println ( "$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55");
 //delay(500);
Serial.println ("$GPRMC,092313.000,A,5956.39888,N,3018.53407,E,0.00,0.00,060419,,,A*5C");
// delay(250);
Serial.println ("$GPRMC,092315.000,A,5956.39931,N,3018.53316,E,0.00,0.00,060419,,,A*5E");
// delay(120);
Serial.println ("$GPRMC,092319.000,A,5956.40037,N,3018.53042,E,0.00,0.00,060419,,,A*51");
// delay(60);
Serial.println ("$GPRMC,092320.000,A,5956.40077,N,3018.52922,E,0.00,0.00,060419,,,A*51");
// delay(30);
Serial.println ("$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57");
// delay(15);
Serial.println ("$GPRMC,092320.000,A,5956.40142,N,3018.52778,E,0.00,0.00,060419,,,A*57");
// delay(7);
Serial.println ("$GPRMG,092320.000,A,5956.00,N,3018.508,E,0.00,0.00,060419,,,A*00");
//delay(1); 
Serial.println ("$GPRMG,092320.000dghdfhuyeutyuudfg d677969yukhfjkfkhjklhjlghlhllgflgflfhklfhjkfkjfjhfjkfhfdj00");
//delay(1);
Serial.println ("tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt");
//delay(1); 
Serial.println ("*$");
//delay(1); 
Serial.println ("$GPRMC,214603.000,A,4634.54557,N,03047.50473,E,0.00,0.00,040617,,,A*6E");
 //delay(1000);
Serial.println ( "$GPRMC,092403.000,A,5959.99840,N,3018.53490,E,0.00,0.00,060419,,,A*55");
 //delay(500);
Serial.println ("$GPRMC,092313.000,A,5956.39888,N,3018.53407,E,0.00,0.00,060419,,,A*5C");
// delay(250);
Serial.println ("$GPRMC,092315.000,A,5956.39931,N,3018.53316,E,0.00,0.00,060419,,,A*5E");
// delay(120);
Serial.println ("$GPRMC,092319.000,A,5956.40037,N,3018.53042,E,0.00,0.00,060419,,,A*51");
// delay(60);

}

*/

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, Вы же сами недавно удивлялись почему "литерал" изменяется. Именно потому, что с константой работают как с переменной.

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

sadman41 пишет:

И, да, - странно строка во float преобразуется. Не знаю - допустимы в координатах такие отклонения или нет...

Еще раз спасибо =) Как в том анектдоте ) "Вчера читал папин пейджер..., много думал..  "на вопрос отвечу так, допустимость орпределяется точностью необходимой и достаточной =)))
Почти полностью разобрался  в программе остался вопрос один "А че, так можно было?" =)) обстрактненько =)))

 

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

Еще один вопрос. После того, как самостоятельно написал пару рабочих вариантов  програм разбора NMEA сообщения, в том числе и использованием функций работы со строками, решил изучить популярные библиотеки к Arduino. И изучить решения других пользователей. Обратил внимание, что библиотеки в основном не используют функции работ со строками. С чем это связанно?

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

На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

sadman41 пишет:

На вопрос об абстрактных библиотеках абстрактных авторов 

А также об использовании или неиспользовании абстрактных "строк".

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

stepan_sotnikov пишет:

Обратил внимание, что библиотеки в основном не используют функции работ со строками. С чем это связанно?

Когда-то мне понадобилось написать программы обработки достаточно объемных текстов (точнее, xml), характерный объем - десятки Гбайт. Т.к. на тот момент у меня было два "активных" языка: Фортран и Паскаль, выбрал, естественно, второй. Библиотеки для работы со строками в Прскале не было, поэтому пришлось писать самому. Ну а, учитывая объемы, приходилось писать так, чтобы работало быстро.

Потом часть этого нужно было переписать на Си. Т.к. как раз там есть развитыве библиотеки работы со строками, при переписывании интенсивно использовал их. Результат: программа на Си работала примерно на 15% медленнее, чем программа на Паскале. Даже при том, что оптимизатор на Си был гораздо мощнее Паскалевского.

Вывод: хотя использование библиотеки существенно ускоряет написание кода, но самописный код, оптимизированный для конкретной ситуации, заметно выигрывает у кода стандартных библиотек по существенным для проекта параметрам. Разумеется, при наличии у кодописателя некоторой минимальной квалификации, чтобы не делать грубых ошибок.

Микроконтроллеры - системы с очень ограниченными ресурсами, поэтому для них оптимизация кода под конкретный проект оказывается гораздо более важной, чем для ПК. Думаю, именно поэтому авторы и пишут собственные библиотеки при наличии стандартных.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Вон, кстати, свеженький плач очередной жертвы тезиса "String всё делает сам, думать и знать ничего не надо".

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

sadman41 пишет:

На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.

В принципи, мне andriano растолковал. Но для примера уточняю:
https://github.com/amperka/TroykaGPS/blob/master/src/TroykaGPS.cpp
 вот от Амперки библиотека для GPS

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так они работают со строками (strcat, strlen, dtostrf, strncpy - всё в полный рост!). В том-то и беда Вашего поста, что Вы не указали какую именно реализацию строк Вы имеете в виду.

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

Да прочитал свой вопрос... Весьма расплывчато задал... подразумивалось strtok, strchr. Ну кроме atof Амперка ее использует в отличии от библиотеки TinyGPS.
Это как часто бывает, изучаешь новый метод или функцию, и поначалу кажиться, что это панацея и спасения... =))) 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

stepan_sotnikov пишет:

подразумивалось strtok, strchr. 

так это из той же "оперы", что и strcat, strlen, dtostrf, strncpy. Не понадобилось людям, вот и не используют. Понадобилось бы - использовали бы. Я то думал, Вы класс String имете в виду.

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

stepan_sotnikov пишет:

sadman41 пишет:

На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.

В принципи, мне andriano растолковал.

Как ЕвгенийП подчернул - ответ зависит от смысла термина "строка". В честном Си ASCIIZ строки (null-terminated) и от массивов не отличаются почти, в нечестном же Wiring'e введён класс String, который с одной стороны удобен, а с другой - мозги канифолит (разбазариванием RAM) всем, кто пришёл в МК-программирование с языков более... не знаю как это по-научному называется... вобщем, в которых строки - это объекты и существует Gargabe Collector.

Вот, к примеру, такой же "парсинг", что и выше, но только без функций из string.h, заточенный на ASCIIZ. В принципе для МК он должен быть полегче (нет вызовов "лишних" функций). В "пробежку" по строке можно и подсчёт CRC сразу запихать, к слову.

#define FIELDS_SEPARATOR        ','
#define GPRMC_FIELD_LATITUDE    0x03
#define GPRMC_FIELD_LONGITUDE   0x05

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

void setup() {
  char* ptrCurrentChar = nmeaString;
  char* nmeaStringParts[15];
  uint8_t nmeaStringPartNo = 0x00;
  
  Serial.begin(115200);
  Serial.println("split without strchr() example\n");

  nmeaStringParts[nmeaStringPartNo++] = nmeaString;
  while ((sizeof(nmeaStringParts) > nmeaStringPartNo) && *ptrCurrentChar) {
    if (FIELDS_SEPARATOR == *ptrCurrentChar) {
      *ptrCurrentChar = '\0';
      nmeaStringParts[nmeaStringPartNo++] = ptrCurrentChar + 1;
    }
    ptrCurrentChar++;
  }

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

  float latitude  = atof(nmeaStringParts[GPRMC_FIELD_LATITUDE]);
  float longitude = atof(nmeaStringParts[GPRMC_FIELD_LONGITUDE]);

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

void loop() {}