#26 2025-04-05 23:41:00

jokeer
Гість

Re: парсинг NMEA **проблема

Якщо ваша проблема вирішується через регекспи - значить у вас 2 проблеми wink регексп для перевірки коректності email це кілька рядків.
А кінцеві автомати якось пройшли мимо. Учили, але як від діаграми перейти до коду я так і не врюхав.

#27 2025-04-06 09:01:09

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Якщо ваша проблема вирішується через регекспи - значить у вас 2 проблеми wink регексп для перевірки коректності email це кілька рядків.

Ви про цей вираз? Так і формат адреси відповідно до RFC 822 непростий. До того ж, такі вирази не пишуться вручну, а генеруються з простіших частин. Ви ж не звинувачуєте компілятор C++, що він генерує машинний код, який людині складно розібрати? Регулярний вираз - той же "машинний код" для КА.

jokeer пише:

А кінцеві автомати якось пройшли мимо. Учили, але як від діаграми перейти до коду я так і не врюхав.

"Звичайна" програма з розгалуженнями і циклами - це теж КА (як мінімум).

Неактивний

#28 2025-04-06 10:14:38

jokeer
Гість

Re: парсинг NMEA **проблема

Я правильно розумію, що якщо на мові yacc описати правила для формату email, то можна отримати regexp? Цікава методика, але як дебажити помилки? Тестами хіба що.

#29 2025-04-06 10:46:15

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Я правильно розумію, що якщо на мові yacc описати правила для формату email, то можна отримати regexp?

Не regexp, а код, який реалізує алгоритм, еквівалентний regexp. Тільки у yacc можливостей на порядок більше, він може генерувати парсер для будь-якої Тьюринг-повної мови.

jokeer пише:

але як дебажити помилки? Тестами хіба що.

Тестами помилки тільки виявляються, як і скрізь. Зміни вносяться в опис граматики. Мабуть є якісь опції і для дебага. Я далеко не експерт в парсер-генераторах, тільки краєм вуха бачив.

Неактивний

#30 2025-04-06 14:37:26

jokeer
Гість

Re: парсинг NMEA **проблема

Ну, мабуть існують задачі, які простіше описати на декларативній мові. Але csv - навряд чи цей випадок smile

#31 2025-04-06 16:13:14

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Ну, мабуть існують задачі, які простіше описати на декларативній мові.

Мені здається, навпаки, для більшості задач, які намагаються вирішувати за допомогою ардуіни (в тому числі і тут на форумі), декларативна парадигма так і проситься. Але то таке, філософія.

jokeer пише:

Але csv - навряд чи цей випадок smile

Це поки самі value не містять в собі comma, які можуть бути не тільки екрановані, а і в складі літералів у одинарних або подвійних лапках, наприклад.

Неактивний

#32 2025-04-07 09:12:15

jokeer
Гість

Re: парсинг NMEA **проблема

>> декларативна парадигма так і проситься.

Ну так то воно так. А є приклади реалізації, щоб не по уродськи ? wink Всякі yml воно прикольно, але вся імперитивщина закопана під капотом, крок вбік і починається цирк з конями.

#33 2025-04-07 10:15:00

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

А є приклади реалізації, щоб не по уродськи ? wink

Мені поки що не зустрічались. А самому розробляти - часу на це немає, та і для однієї людини воно розтягнеться на роки. Воно ж одне тягне за собою інше.
Від існуючих ардуінівських бібліотек доведеться відмовитись одразу, а кому воно потрібне без бібліотек? Найбільш розповсюджені ще якось більш-менш реалістично портувати під нову парадигму, але ж тисячі їх. Ще й уніфікація HAL'а для різних архітектур.
На виході генерувати код на C? Сучасні компілятори не дуже підходять для embedded з обмеженими ресурсами. Наприклад, з LTO практично неможливо передбачити, скільки стеку потрібно тій чи іншій задачі. Інші налаштування оптимізації - все, приїхали. Потрібно розробляти свій компілятор з блекджеком і повіями, та ще й щоб підтримував зоопарк цільових архітектур. Невдячна це справа.

Неактивний

#34 2025-04-07 10:50:08

jokeer
Гість

Re: парсинг NMEA **проблема

От ж.. суперцикл по ардуїнівскі це просто, але чим більше елементів в системі тим незрозуміліше.
rtos нахвалюють, і навіть під atmega воно є. Але ще не пробував.

#35 2025-04-07 11:04:37

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

rtos нахвалюють, і навіть під atmega воно є.

Так правду кажуть, мультипоточність - для тих, хто не зрозумів конечні автомати smile
Це зручно, але кожній задачі потрібен стек, як мінімум щоб зберігати контекст. На Attiny13 ОЗУ 64 байта, а контекст регістрів - 32 байта. На Attiny15 взагалі ОЗУ нема smile

Неактивний

#36 2025-04-07 11:46:21

jokeer
Гість

Re: парсинг NMEA **проблема

Для використання attiny повинна бути дуже поважна причина wink

#37 2025-04-07 12:29:36

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Для використання attiny повинна бути дуже поважна причина wink

Колись була: ціна. Зараз, із китайськими CH32 по "гривні за відро", це майже не актуально.

Неактивний

#38 2025-04-07 12:50:24

jokeer
Гість

Re: парсинг NMEA **проблема

Мені китайські stc8 сподобались. 8 ніг, обв'язки немає. Програмуються трохи по наркоманськи, ага.

#39 2025-04-07 13:12:04

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Мені китайські stc8 сподобались. 8 ніг, обв'язки немає. Програмуються трохи по наркоманськи, ага.

Цікаво. Цих не доводилось щупати.
Трохи грався з CH32V. Наче нічо, але в "продакшн" їх брати поки б не ризикував. Документація мізерна, є питання щодо надійності в довгостроковій перспективі. Ну і треба розуміти, що кілобайт на AVR8 і кілобайт на RV32 - це дещо різний кілобайт.

Неактивний

#40 2025-04-07 13:55:42

jokeer
Гість

Re: парсинг NMEA **проблема

Кілобайти то такоє.. Там мабуть і периферія трохи незвична, і тактування..

#41 2025-04-07 14:24:37

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

Кілобайти то такоє.. Там мабуть і периферія трохи незвична, і тактування..

Та периферія здерта з STM32. Тільки ядро RISC-V.

Неактивний

#42 2025-04-07 14:56:19

jokeer
Гість

Re: парсинг NMEA **проблема

І програматор свій, китайський, ні з чим не сумісний wink

#43 2025-04-09 22:16:59

renoshnik
Учасник
Зареєстрований: 2017-04-03
Повідомлень: 1,057

Re: парсинг NMEA **проблема

Якось так ....

#include <Adafruit_GFX.h>    	// Core graphics library
#include <Adafruit_ST7735.h> 	// Hardware-specific library for ST7735
#include <SPI.h>
#define TFT_CS        10
#define TFT_RST        8 		// Or set to -1 and connect to Arduino RESET pin
#define TFT_DC         9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
#include <SoftwareSerial.h>
#include <string.h>
SoftwareSerial gpsSerial(4, 3); // (RX, TX Arduino)

String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
const int timezoneOffset = 3;	// Часовой пояс (например, +3 для EEST)
boolean valid = false;			// флаг валидности координат
boolean drive = false;			// флаг движения
boolean overtime = false;		// флаг время/скорость

//	char tokenKnot[] = "10.5"; 		// Пример тестового значения


void setup() {
//								Serial.begin(9600);
gpsSerial.begin(9600);
tft.initR(INITR_BLACKTAB);   	// initialize a ST7735S chip, black tab
tft.setRotation(3);     		// Установка ориентации экрана
tft.fillScreen(ST77XX_BLACK);	// Установка начального цвета фона
}

void loop() {
  while (gpsSerial.available()) {
    char c = gpsSerial.read(); 	// Считать один символ из GPS
    if (c == 'n') { 			// Проверить, является ли символ концом NMEA-строки
      GPS_run(nmeaSentence); 	// Передать строку в GPS_run() для обработки
      nmeaSentence = ""; 		// Очистить строку для следующей NMEA-строки
    } else if (c != 'r') { 	// Игнорировать символ возврата каретки
      nmeaSentence += c; 		// Добавить символ к строке NMEA
    }
  }
}

void GPS_run(String nmea) {
  if (nmea.startsWith("$GPRMC")) { 					// Проверка на идентификатор строки
//								Serial.println("______________________________"); 
//								Serial.println("_______ вход в $GPRMC ***1*** ");
    char nmeaCStr[nmea.length() + 1];
    nmea.toCharArray(nmeaCStr, nmea.length() + 1);
    int fieldIndex = 0;
    char *token = nmeaCStr;
    char *commaPos = strchr(token, ',');			// Найти первую запятую
    while (fieldIndex <= 13) {
    if (commaPos) {
    *commaPos = ''; 								// Разделяем строку на части

if (fieldIndex == 1) {printFormattedTime(token);token = nullptr;}	// выводим время 1
if (fieldIndex == 2) {printFormattedValid(token);token = nullptr;}	// выводим валидность координат 2	
if (fieldIndex == 7) {printFormattedSpeed(token);token = nullptr;}// выводим скорость 7
if (fieldIndex == 9) {printFormattedDate(token);token = nullptr;}	// выводим дату 9

token = commaPos + 1;
commaPos = strchr(token, ',');} 
else {break;}
      fieldIndex++;}
	}	
	
  if (nmea.startsWith("$GPGGA")) { 					// Проверка на идентификатор строки
//								Serial.println("______________________________");  
//								Serial.println("_______ вход в $GPGGA ***2*** ");
	char nmeaCStr[nmea.length() + 1];
    nmea.toCharArray(nmeaCStr, nmea.length() + 1);
    int fieldIndex = 0;
    char *token = nmeaCStr;
    char *commaPos = strchr(token, ',');			// Найти первую запятую
    while (fieldIndex <= 14) {
    if (commaPos) {
    *commaPos = ''; 								// Разделяем строку на части
 
if (fieldIndex == 7) {printFormattedSatelit(token);token = nullptr;}// выводим спутники 7

token = commaPos + 1;
commaPos = strchr(token, ',');} 
else {break;}
      fieldIndex++;}
	}	
}


// Функция контроля достоверности полученных координат
void printFormattedValid(char* rawValid) {
//									Serial.print("Полученная rawValid       : ");
//	Serial.print(valid); Serial.print(" - "); Serial.println(rawValid);
if (strcmp(rawValid, "V") == 0) {valid = false;}
if (strcmp(rawValid, "A") == 0) {valid = true;}
}


// Функция для форматирования количества спутников
void printFormattedSatelit(char* rawSatel) {
//									Serial.print("Полученная rawSatel       : ");
//									Serial.println(rawSatel);
	static char previousSatel[5];
	if (strcmp(rawSatel, previousSatel) != 0) {
	tft.fillRect(125, 1, 29, 20, ST77XX_BLACK);}
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(1);
tft.setCursor(12, 10);
tft.print("active satellites");
if (valid == false) {tft.setTextColor(ST77XX_RED);}
if (valid == true) {tft.setTextColor(ST77XX_GREEN);}
tft.setTextSize(2); 
tft.setCursor(127, 5);
tft.print(rawSatel);
	strncpy(previousSatel, rawSatel, sizeof(previousSatel) - 1);
    previousSatel[sizeof(previousSatel) - 1] = '';
}


// Функция для форматирования скорости (knot -> km/h)
void printFormattedSpeed(char* rawKnot) {
//    Serial.print("Полученная rawKnot : ");
//    Serial.println(rawKnot);
    tft.setTextColor(ST77XX_ORANGE);
    tft.setTextSize(5);
    tft.setCursor(35, 45);
    float Kmh = atof(rawKnot) * 1.852;
  int speedKmh = (int)Kmh;
  if (speedKmh == 0) {drive = false; return;} 
  else {drive = true;}  
//    Serial.print("Конвертируем узлы в км/ч speedKmh : ");
//    Serial.println(speedKmh);
    char fullSpeed[5];
    sprintf(fullSpeed, "%03d", speedKmh);
	tft.fillRect(5, 40, 155, 45, ST77XX_BLACK);
//  tft.drawRect(5, 40, 155, 45, ST77XX_WHITE);
    tft.print(fullSpeed); 
//    Serial.print("Форматируем число с ведущими нулями fullSpeed: ");
//    Serial.println(fullSpeed);
}


// Функция для форматирования времени с учётом часового пояса (UTC -> ЧЧ:ММ:СС)
void printFormattedTime(char* utcTime) {
//									Serial.print("Полученная utcTime       : ");
//									Serial.println(utcTime);
  char fullTime[9]; 								// Переменная для хранения строки времени в формате "HH:MM:SS"
  char hh[3], mm[3], ss[3];
  strncpy(hh, utcTime, 2); hh[2] = '';
  strncpy(mm, utcTime + 2, 2); mm[2] = '';
  strncpy(ss, utcTime + 4, 2); ss[2] = '';
  int hour = atoi(hh) + timezoneOffset;				// Коррекция часа в зависимости от timezoneOffset
  if (hour < 0) hour += 24; 						// Если час отрицательный, добавляем 24
  if (hour >= 24) hour -= 24; 						// Если час превышает 24, вычитаем 24
  sprintf(fullTime, "%02d:%s:%s", hour, mm, ss);	// Формируем строку времени
// время вверху
	if (drive == false) {
tft.fillRect(5, 40, 155, 45, ST77XX_BLACK);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(3);
tft.setCursor(10, 50); tft.print(fullTime); } 
	if (drive == true) { 
tft.fillRect(15, 94, 130, 25, ST77XX_BLACK);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.setCursor(30, 100); tft.print(fullTime); 
	overtime = true;}
}


// Функция для форматирования даты (ДД.ММ.ГГГГ)
void printFormattedDate(char* rawDate) {
//									Serial.print("Полученная drive d rawDate : ");
//									Serial.println(drive);
	if (drive == true) {return;}
//									Serial.print("Полученная rawDate       : ");
//									Serial.println(rawDate);
	static char previousDate[11];
	if (strcmp(rawDate, previousDate) != 0 || overtime == true) {
	tft.fillRect(15, 94, 130, 25, ST77XX_BLACK);}
	overtime = false;
char fullDate[11]; 									// Переменная для хранения полной строки даты (формат "DD.MM.YYYY")
char dd[3], mm[3], yy[3]; 							// Обратите внимание, что yy теперь размера 3, а не 5
strncpy(dd, rawDate, 2); dd[2] = '';
strncpy(mm, rawDate + 2, 2); mm[2] = '';
strncpy(yy, rawDate + 4, 2); yy[2] = '';
sprintf(fullDate, "%s.%s.20%s", dd, mm, yy);		// Формируем строку даты
tft.setTextColor(ST77XX_MAGENTA);
tft.setTextSize(2);
tft.setCursor(20, 100);
tft.print(fullDate);
	strncpy(previousDate, rawDate, sizeof(previousDate) - 1);
    previousDate[sizeof(previousDate) - 1] = '';
}

Неактивний

#44 2025-04-09 23:54:14

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

renoshnik пише:
String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
...
nmeaSentence += c; 		// Добавить символ к строке NMEA
...
char nmeaCStr[nmea.length() + 1];
nmea.toCharArray(nmeaCStr, nmea.length() + 1);
...

Навіщо зберігати символи в String, якщо потім їх копіювати в char[] ?

renoshnik пише:
if (fieldIndex == 1) {printFormattedTime(token);token = nullptr;}	// выводим время 1
if (fieldIndex == 2) {printFormattedValid(token);token = nullptr;}	// выводим валидность координат 2	
if (fieldIndex == 7) {printFormattedSpeed(token);token = nullptr;}// выводим скорость 7
if (fieldIndex == 9) {printFormattedDate(token);token = nullptr;}	// выводим дату 9

token = commaPos + 1;

Навіщо присвоювати token = nullptr, якщо відразу ж присвоюється token = commaPos + 1?
Раз на то пішло, то хоча б

switch (fieldIndex) {
    case 1: printFormattedTime(token); break;
    case 2: printFormattedValid(token); break;
    case 7: printFormattedSpeed(token); break;
    case 9: printFormattedDate(token); break;
    default: break;
}
token = commaPos + 1;
renoshnik пише:
float Kmh = atof(rawKnot) * 1.852;
int speedKmh = (int)Kmh;

Якщо вже так хочеться float, навіщо проміжна змінна і C-style кастинг? Такий же результат дасть

int speedKmh = atof(rawKnot) * 1.852;

А краще

int speedKmh = round(atof(rawKnot) * 1.8523);
renoshnik пише:
char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);

У разі невалідних вхідних даних може бути переповнення буфера. Хоча б

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Хоча у разі невалідних вхідних даних переповнення буфера буде ще на стадії отримання строки з UART.

Остання редакція dimich (2025-04-09 23:54:49)

Неактивний

#45 2025-04-10 06:24:43

jokeer
Гість

Re: парсинг NMEA **проблема

snprintf(fullSpeed, sizeof(fullSpeed)-1

#46 2025-04-10 10:10:03

jokeer
Гість

Re: парсинг NMEA **проблема

char nmeaCStr[nmea.length() + 1];

wtf? хіба так можна?

#47 2025-04-10 10:12:53

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

snprintf(fullSpeed, sizeof(fullSpeed)-1

Це чому? snprintf в avr-libc нічим не відрізняється від інших реалізацій libc:

no more than n characters (including the trailing NUL character) will be converted to s

Неактивний

#48 2025-04-10 10:17:16

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 377

Re: парсинг NMEA **проблема

jokeer пише:

char nmeaCStr[nmea.length() + 1];
wtf? хіба так можна?

Можна в ISO C99, а з gcc можна і в C++. Але чи варто - залежить від контексту. В даному випадку навряд чи.

Неактивний

#49 2025-04-10 11:01:48

renoshnik
Учасник
Зареєстрований: 2017-04-03
Повідомлень: 1,057

Re: парсинг NMEA **проблема

https://youtu.be/0zPiwkYg_ig?si=ZOibZm2RTGjiRXJq

Остання редакція renoshnik (2025-04-10 11:03:22)

Неактивний

#50 2025-04-10 11:30:56

renoshnik
Учасник
Зареєстрований: 2017-04-03
Повідомлень: 1,057

Re: парсинг NMEA **проблема

dimich пише:
renoshnik пише:
String nmeaSentence = ""; 		// Переменная для хранения NMEA-строки
...
nmeaSentence += c; 		// Добавить символ к строке NMEA
...
char nmeaCStr[nmea.length() + 1];
nmea.toCharArray(nmeaCStr, nmea.length() + 1);
...

Навіщо зберігати символи в String, якщо потім їх копіювати в char[] ?

renoshnik пише:
if (fieldIndex == 1) {printFormattedTime(token);token = nullptr;}	// выводим время 1
if (fieldIndex == 2) {printFormattedValid(token);token = nullptr;}	// выводим валидность координат 2	
if (fieldIndex == 7) {printFormattedSpeed(token);token = nullptr;}// выводим скорость 7
if (fieldIndex == 9) {printFormattedDate(token);token = nullptr;}	// выводим дату 9

token = commaPos + 1;

Навіщо присвоювати token = nullptr, якщо відразу ж присвоюється token = commaPos + 1?
Раз на то пішло, то хоча б

switch (fieldIndex) {
    case 1: printFormattedTime(token); break;
    case 2: printFormattedValid(token); break;
    case 7: printFormattedSpeed(token); break;
    case 9: printFormattedDate(token); break;
    default: break;
}
token = commaPos + 1;
renoshnik пише:
float Kmh = atof(rawKnot) * 1.852;
int speedKmh = (int)Kmh;

Якщо вже так хочеться float, навіщо проміжна змінна і C-style кастинг? Такий же результат дасть

int speedKmh = atof(rawKnot) * 1.852;

А краще

int speedKmh = round(atof(rawKnot) * 1.8523);
renoshnik пише:
char fullSpeed[5];
sprintf(fullSpeed, "%03d", speedKmh);

У разі невалідних вхідних даних може бути переповнення буфера. Хоча б

sprintf(fullSpeed, sizeof(fullSpeed), "%03d", speedKmh);

Хоча у разі невалідних вхідних даних переповнення буфера буде ще на стадії отримання строки з UART.



1. - Згоден, що додає додатковий крок, але мені так було зручніше працювати з рядками на етапі побудови.
2. - про token = nullptr, то я шукав звідки лізуть деякі баги і забув потім видалити, дійсно то зайве.
3. - дякую виправив
4. - дякую виправив

Неактивний

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму