Парсинг NMEA
- Войдите на сайт для отправки комментариев
Добрый вечер, подскажите пожалуйста как правильно парсить координаты 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); } }
Вы хотите научиться парсить или получить готовый код?
Ну с отсутствием достаточного времени пытаюсь учиться уже на готовых решениях. В интернете искал полуготовые варианты и эксперементировал, но к сожалению ничего понять не смог. В вышеуказанном коде, который на 60% не мой есть парсер (выдает в таком формате E,03047.49174,N,4634.54769, что совсем наоборот и с лишними разделителями), но как он разбирает строку, я никак немогу вдуплить, что б подкоректировать. Хотелось бы решения по проще, что б мог методом тыка почучуть разбирать и учиться, или может на крайний случай как-то можно в TinyGPS получать координаты в нужном формате.
Ответа не понял. В общем так, если интересно, я могу Вас шаг за шагом провести и через недельку Вы сможете писать практически любой парсер, который Вам нужен. Но это надо работать. Готовое рештене - это не ко мне, я заказов на ищу исполнителя не беру.
Я только за! Помогите пожалуйста. Скайп, вайбер, телеграм?
Elmirus,
1. вы можете словами описать нужный формат?
2. как вы понимаете "парсинг"?
3. для парсинга(в моём понимании) nmea строки подходит упомянутая библиотека.
4. прочитайте полностью https://ru.m.wikipedia.org/wiki/Географические_координаты
Нужно вытянуть вот этот кусочек 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 парсит не в том формате.
У Вас этот текст в каком виде? Какого типа? Массив char?
Да, он самый char. (byteGPS)
То есть пиходит в монитор порта вот такая шняга и из 2й строки $GPRMC нужно оставить только выделенное красным:
Кусок моего сооружения:
TinyGPS выдает долготу и широту в отличном общепринятом формате, но никто не виноват, что китайские устройства отправляют координаты через "жопу", под которые и подстроены GPS сервисы. Учитывая изложенное невозможно "сказать" GPS сервисам, что китайские устройства говно, а мои правильные, так что перенастраивайте свои серверы только под мой формат данных)
А теперь скажу вам горькую правду. Это не китайские устройства отправляют координаты через "жопу", а вы банально не умеете работать со строковыми переменными. Да и банально все "ардуинщики" "выше этого". Если будущих программистов в вузах натаскивают на это, то любители на это забивают болт.
ПС: К сожалению этот пробел знаний у меня меня и расстраивает. Так что я сейчас тупо пытаюсь его закрыть.
Увы, проблема остаеться проблемой... Я раньше не думал, что упрусь в такой забор.
Увы, проблема остаеться проблемой... Я раньше не думал, что упрусь в такой забор.
А чего тормозить учитесь https://rsdn.org/article/cpp/cstr.xml . Это основы . Причем самые что не на есть. Без них ну ни как. Я же говорю что программистам по Си что тем кто занимается связью ну ни как. Без этого ну только мигать светодиодом или скачивать чужие и кривые скетчи.
Вот еще https://rsdn.org/article/cpp/cstr.xml#ERFAC Но лучше статью изучить с начала.
Да, он самый char. (byteGPS)
Тогда забыли про своё устройство, отложили его подальше на полочку, взяли ардуинку, компьютер, написали вот такой скетч:
Убедились, что она печатает ту самую байду. Если я где-то опечатался (писал прямо здесь), пот подправлили и таки убедились.
После этого читаем пр функции strchr и strlen и пробуем выполнить слеждующее задание: напечатать все элмемнты исходной строки между запятыми, какждый элемент на отдельной строке.
Т.е., если исходная байда у Вас "12,ab,,cd", то результат должен быть
12
ab
cd
Делайте. Как сделаете, выкладывайте код. Если не получится, тоже выкладывайте код. Тогда продолжим.
Еще пытаюсь разбираться, но уже достиг какихто результатов:
Так посчитал колличество символов:
А так вдуплил, как начинать с какогото символа, сstrrchr начинать с конца, а strchr начинать с начала:
Осталось "навеное" склеить это в одно целое, надеюсь до завтра разберусь
Вот вам еще один пробел в ваших знаниях http://cpp.com.ru/kr_cbook/ch5kr.html#p53
Т.е., если исходная байда у Вас "12,ab,,cd", то результат должен быть
12
ab
cd
Делайте. Как сделаете, выкладывайте код. Если не получится, тоже выкладывайте код. Тогда продолжим.
Получилось сделать как Вы говорили, но при помощи strtok таким образом:
вышло:
После чего, я решил переделать через оператор FOR с приминением IF
И получилось 03047.50473 то, что в принципе и нужно, но опять таки как отнять тут последнюю цифу?
Еще пытался сделать с strchr и strtok:
Получилось 4634.54557, но я невдупляю как сделать в strok - 2 смвлома (, и 7), если бы вышло, то думаю это позволило бы достич желаемого результата в какой то мере.
До srtlen пока еще не додуплил, разбираюсь. Пытаюсь разбиратся дальше...
Вроде бы начинает приходить почучуть просветление в чайник, но что-то чувствую, что движусь не по той дороге.
Забудьте про strtok. Используем только strchr. Даже strlen не используем. Только так Вы получите понимание процесса. Использовать strtok - всё равно, что готовый скетч скачать.
Забудьте про strtok. Используем только strchr. Даже strlen не используем. Только так Вы получите понимание процесса. Использовать strtok - всё равно, что готовый скетч скачать.
А..., я случайно наткнулся на strtok. Буду дальше переваривать, я прост подумал что strchr один не может сделать нужного
Как раз может, но "вручную" без автоматизации. А Вам это и надо. Вам надо понимать как работать со строкой, а не найти какую-то волшебную библиотеку, которая всё по щучьему велению сделает.
Мне очень интересно, как бы вы дальше продолжили обучение и рассказ.
Кратнкая история, ковырял сходную тему про GPS, наткнулся на эту ветку формума. Увидел Вашу задачку, ну и подумал... Тю, это же просто...
А дальше, открыл для себя удивительные кирилические символы. И дикий пробел в понимании Char.
Для меня это раньше был int или int[], при выводи итерпритируется как символ, а теперь... когда надо напечатать символ надо в консоль послать от двух элементов массива, где они чудом как то складываться.
ШэВ все пропало. Нужно продолжение твоего урока. Как мне отсортировать напремер не по ',' а по кирилической букве 'o' которая занимает 2 байта? Не прибегая к strtok, strchr, strlen?
Как мне отсортировать напремер не по ',' а по кирилической букве 'o' которая занимает 2 байта? Не прибегая к strtok, strchr, strlen?
Как вариант, перекодировать из UTF-8 в однобайтовую русскую кодировку Windows-1251, как тут
начнем с того, нафига вообще обрабатывать кириллицу? Кириллицу, в крайнем случае, надо показывать пользователю на экране, а любую обработку и межпроцессорный обмен уж точно надо программировать только на латинице.
Нипатриатична!
1С нигодуэ.
ЕвгенийП, в рамках изучения языка и освоения платформы, тренеруюсь писать програмку. Выбрал для себя именно эту тему ибо, работаю в этой сфере и она мне близка.
Это мое обучение. По этому ожидаю от Вас, советов, критики, подсказок, наставлений. Вот код который я потихньку пишу, и попутно изучаю платформу. Программа откоментированна. По этим коментариям можно понять где я чего то не понимаю. Программа состоит из двух функций. Одна читает массив, формирует два массива вернее исходный меняет, второй формирует. Второй массив содержит адресса разделителей. Далее на основе этих данных формируем массив из двух элементов, куда записывает долготы и широты собранные функцией dir.
Вас atof() принципиально не устраивает?
Взял на заметку, поизучаю. Это мое обучение. можно же и просто подключить библиотеку и вместо всего что я тут изобразил написать gps.f_get_position(&lat, &lon);
Спасибо. Буду изучать эту функцию. Уже в карции почитал. Надо переходить к практики.
Еще раз спасибо за подсказку... изучил функцию. Попробывал применить в учебной своей реализации, но это надо менять вообще весь подход который я выбрал. Это следующий этап работа со строковыми операторами или как правильно они называються в этом языке. Попробывал сегодня забыть про все кроме "Используем только strchr." как говорил ЕвгенийП... в http://arduino.ru/forum/programmirovanie/parsing-nmea#comment-302584
но что то не уловили его идею, что он имел в виду? Кроме того выяснил, что мой (возможно кривой способ) дает примерно такой же примерно результат как и atof() и по точности, скорости и памяти... Сейчас я поставил себе задачу научиться работать с переменными указателями массивами. И просто почувствовать язык. И его особенности. Я уже уяснил, что здесь нет зашиты от дурака, совсем нет.
Еще раз спасибо за подсказку... изучил функцию. Попробывал применить в учебной своей реализации, но это надо менять вообще весь подход который я выбрал.
Это перманентное состояние при обучении, чего бояться?
Менять особо не надо. Достаточно все запятые менять на '\0' и передавать atof()-у только указатель на начало фрагмента. Дальше он всё сделает сам.
Попробывал сегодня забыть про все кроме "Используем только strchr." как говорил ЕвгенийП... в http://arduino.ru/forum/programmirovanie/parsing-nmea#comment-302584 но что то не уловили его идею, что он имел в виду?
Кроме того выяснил, что мой (возможно кривой способ) дает примерно такой же примерно результат как и atof() и по точности, скорости и памяти...
Сомнительно, конечно, но на частоте 16Mhz сложно уловить разницу ;)
Компилятор - ваш друг. Включите уровень отладки "All".
Это как научиться считать параметры электрических цепей вручную или пользоваться, например, мультисимом или протеусом, чтобы они посчитали все напряжения и сопротивления в схеме. Если сразу начать со второго - велик риск, что просто "насобачитесь как мартышка без глубокого понимания" и это всё сломается на первой же нетривиальной схеме, где те по каким-то причинам глюкнут, а если с первого, то потом можно и вторым пользоваться, но с пониманием что и как, а значит и с пониманием ограничений и особенностей такого моедлирования и автоматического расчёта.
strchr(s, ',') дает ссылку на первую запятую. Дальше по конструкции у меня ступор... Завтра подумаю... и погуглю...
ЕвгенийП это яуже понял. Я сам сторонник таких методов... Следую указанному Вами курсу, решил задачу без знаний функций, теперь разбираюсь в базовой... Но что то плохо дается... пока не соображу...
Продолжаю обучатся и колупать. Вроде бы все работает. По прежнему не удалось одолеть strchr(); но поизучал UART ну и свместил...
Продолжаю обучатся и колупать. Вроде бы все работает.
По прежнему не удалось одолеть strchr();
Подмогну немного. Не совсем, конечно, мне нравится код, но пока в голову другое не пришло (хотя я вообще бы со strchr() не заморачивался, если честно и побегал по строке самостоятельно)
И, да, - странно строка во float преобразуется. Не знаю - допустимы в координатах такие отклонения или нет...
попробую узучить Ваш код... позже скорее всего задам вопросы... пока я решаю вопросы на этой платформе в "лоб", мноигии консрукции мне не знакомы. Ну я думаю Вы и сами поняли мой уровень ;-) Что это первая программа на этом разновидности языка и платформе. Все что я использую это http://arduino.ru/Reference . Я очень благодарен за участие и помощь.
я не критикую, просто хочу для себя разобраться. всем отличие и какие риемущества вот этого
от этого
спасибо.
хочу для себя разобраться. всем отличие и какие риемущества вот этого
от этого
Ну, тут много отличий. Парочка навскидку:
1.
Вверху указатель, а внизу массив. У них соответсвенно, разные sizeof. У верхнего - 2, а у нижнего - 71. Соответсвенно нижний можно передать (в качестве параметра), любому, заточенному на sizeof шаблону. К примеру, если передать нижний EEPROM.put(), она вполне адекватно запишет в еепром строку, а если передать верхний, то запишутся только первые два симмвола.
2.
Строка вверху - константа (и компилятор об этом скажет, если попытаться прямо так скомпилировать). А строка внизу не константа, а просто область памяти с произвольным доступом.
В принципе, можно продолжать, но главное вроде уже сказано.
то есть обявлять
char
*s = "xxxxxx"; а потом работать с ним как
char
s[] не совсем корректно? Т.к. константа она и на то и константа, что бы быть константой =)))
Изменил свой код. Так же предусмотрел различные ошибки работы в программе.
Ну, Вы же сами недавно удивлялись почему "литерал" изменяется. Именно потому, что с константой работают как с переменной.
И, да, - странно строка во float преобразуется. Не знаю - допустимы в координатах такие отклонения или нет...
Еще раз спасибо =) Как в том анектдоте ) "Вчера читал папин пейджер..., много думал.. "на вопрос отвечу так, допустимость орпределяется точностью необходимой и достаточной =)))
Почти полностью разобрался в программе остался вопрос один "А че, так можно было?" =)) обстрактненько =)))
Еще один вопрос. После того, как самостоятельно написал пару рабочих вариантов програм разбора NMEA сообщения, в том числе и использованием функций работы со строками, решил изучить популярные библиотеки к Arduino. И изучить решения других пользователей. Обратил внимание, что библиотеки в основном не используют функции работ со строками. С чем это связанно?
На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.
На вопрос об абстрактных библиотеках абстрактных авторов
А также об использовании или неиспользовании абстрактных "строк".
Обратил внимание, что библиотеки в основном не используют функции работ со строками. С чем это связанно?
Когда-то мне понадобилось написать программы обработки достаточно объемных текстов (точнее, xml), характерный объем - десятки Гбайт. Т.к. на тот момент у меня было два "активных" языка: Фортран и Паскаль, выбрал, естественно, второй. Библиотеки для работы со строками в Прскале не было, поэтому пришлось писать самому. Ну а, учитывая объемы, приходилось писать так, чтобы работало быстро.
Потом часть этого нужно было переписать на Си. Т.к. как раз там есть развитыве библиотеки работы со строками, при переписывании интенсивно использовал их. Результат: программа на Си работала примерно на 15% медленнее, чем программа на Паскале. Даже при том, что оптимизатор на Си был гораздо мощнее Паскалевского.
Вывод: хотя использование библиотеки существенно ускоряет написание кода, но самописный код, оптимизированный для конкретной ситуации, заметно выигрывает у кода стандартных библиотек по существенным для проекта параметрам. Разумеется, при наличии у кодописателя некоторой минимальной квалификации, чтобы не делать грубых ошибок.
Микроконтроллеры - системы с очень ограниченными ресурсами, поэтому для них оптимизация кода под конкретный проект оказывается гораздо более важной, чем для ПК. Думаю, именно поэтому авторы и пишут собственные библиотеки при наличии стандартных.
Вон, кстати, свеженький плач очередной жертвы тезиса "String всё делает сам, думать и знать ничего не надо".
На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.
В принципи, мне andriano растолковал. Но для примера уточняю:
https://github.com/amperka/TroykaGPS/blob/master/src/TroykaGPS.cpp
вот от Амперки библиотека для GPS
Так они работают со строками (strcat, strlen, dtostrf, strncpy - всё в полный рост!). В том-то и беда Вашего поста, что Вы не указали какую именно реализацию строк Вы имеете в виду.
Да прочитал свой вопрос... Весьма расплывчато задал... подразумивалось strtok, strchr. Ну кроме atof Амперка ее использует в отличии от библиотеки TinyGPS.
Это как часто бывает, изучаешь новый метод или функцию, и поначалу кажиться, что это панацея и спасения... =)))
подразумивалось strtok, strchr.
так это из той же "оперы", что и strcat, strlen, dtostrf, strncpy. Не понадобилось людям, вот и не используют. Понадобилось бы - использовали бы. Я то думал, Вы класс String имете в виду.
На вопрос об абстрактных библиотеках абстрактных авторов можно дать только абстрактные ответы.
В принципи, мне andriano растолковал.
Как ЕвгенийП подчернул - ответ зависит от смысла термина "строка". В честном Си ASCIIZ строки (null-terminated) и от массивов не отличаются почти, в нечестном же Wiring'e введён класс String, который с одной стороны удобен, а с другой - мозги канифолит (разбазариванием RAM) всем, кто пришёл в МК-программирование с языков более... не знаю как это по-научному называется... вобщем, в которых строки - это объекты и существует Gargabe Collector.
Вот, к примеру, такой же "парсинг", что и выше, но только без функций из string.h, заточенный на ASCIIZ. В принципе для МК он должен быть полегче (нет вызовов "лишних" функций). В "пробежку" по строке можно и подсчёт CRC сразу запихать, к слову.