Телеметрия (часть 1)
Тимофей, по-моему, сейчас как раз самое время вернуться к исходной шереровской схеме и отделить математику от отображаловки !
… Сейчас же проект его уже явно перерос.
+1
Для остального кода у меня времени и места - выше крыши. …Целочисленная арифметика только.
Ну и возьмите мегу 64(644) и утапливайте в неё всё 😉 По габаритам выйдет не сильно больше м8, а по возможностям сильно больше.
Насчёт плавучки - у мег аппартный умножитель, иар очень неплохо умеет его пользовать. Сейчас сделал проект на тини2313, прибор для корректировки цифровых спидометров. Он включается между датчиком скорости и спидометром, измеряет период входных импульсов и выдаёт выходные с коррекцией, введённой извне (или определённой автоматически). То есть спидометр показывает правильно независимо от шин, главной пары и прочего тюнинха.
Ну так вот, там одна плавучка, и всё это работает на килогерцах! По трём прерываниям и в реалтайме. На тиньке! И ничего - иар упихал всё это в 1992 байта 😃
На разработку ушло два дня. А сколько ушло бы на асме? 😉
Вы просто загоняете себя в угол… Уже сейчас огранициваете качественный параметр - отображение курса - из-за наличия тригонометрии. А могли бы загнать таблицу Брадиса в память и очень быстро считать всё, что надо (для ЖПС тоже полезно).
Можете считать это блажью, но вот у меня пункт такой: хочу, чтобы всё было утоптано в один кристалл. Целый кристалл для одной только отображаловки - чудовищно расточительно.
Ну, как говорилось в одной пословице, “Хозяин-барин: хочет - пляшет, хочет - удавится.” 😉
Просто я, например, неторопливо обдумываю уже следующие шаги: привод для “наведения” на модель направленной антенны приемника (чтобы не вертеть ее руками), а следом - и зачатки “автопилота”, при потере аплинк-сигнала возвращающего аппарат “на базу”…
И в один корпус это не лезет уже не столько даже по быстродействию и объемам памяти, сколько архитектурно-идеологически.
Вообще, запихивание измериловки в один кристалл с отображаловом у меня было шагом чисто тестовым и осознанно промежуточным, так что я, признаться, нынче искренне удивлен, как много Вам удалось туда затолкать разной арифметики… Однако, несмотря на все достижения, путь этот - явно тупиковый: как с точки зрения расширябельности, так и по удобству дальнейшего сопровождения проекта. И если уж Вы решились разводить “нормальную” плату - то, imho, самое время пожертвовать на ней лишний квадратный сантиметр под второй кристалл.
(Впрочем, я никого не уговариваю 😃, а просто высказываю “мысли вслух”.)
Я вовсе не отказываюсь от второго кристалла, просто планы у меня на него несколько другие, и не совсем еще на 100% определенные.
На второй плате - логгинг данных в память, расчет управления для беспилотного полета, интерфейс с PC для слива-залива данных и конфигурирования системы, и.т.д.
Решение предполагается двухплатным: одна, нынешняя плата (ну разве что переразведенная, чтоб проводки к ногам не паять и чтоб помехи убить) будет законченным решением, и вторая в паре с первой - законченное решение. Хочешь - паяй одну, хочешь больше - паяй две. На первой плате - “измериловка” и “отображаловка”, на второй - “большая математика” и интерфейс с PC.
привод для “наведения” на модель направленной антенны приемника (чтобы не вертеть ее руками), а
Делали мы такое 😃 GPS в самолёте засекает координаты базы, потом антена наводится на передаваемую с борта координату самолёта. Прикольно работало!
Особенно эффектно, когда самолёт возвращается с “задания” и стыкуется с наземкой по радио. Сложеная до этого антена разворачивается и шустренько ловит самик 😁
Кстати, я свернул в вывод буковок на экран в цикл, это экономит ну просто дохрена места (по объему - около 25% всего кода !!). Почему Шеререр (или Artie? 😃) не сделал этого сразу - непонятно.
Букивки у меня 8х6. Нужно код с 32-мя вызовами макроса shiftout заменить на следующее. По объему это вот следующее как раз равно одному макросу shiftout:
LDI R17, 31
LPM R16,Z
OUT VIDPORT,R16
NOP
NOP
LSL R16
OUT VIDPORT,R16
NOP
NOP
LSL R16
OUT VIDPORT,R16
NOP
NOP
_output_char:
LSL R16
OUT VIDPORT,R16
NOP
NOP
LSL R16
OUT VIDPORT,R16
LD ZL,Y+
LSL R16
OUT VIDPORT,R16
LPM R16,Z
OUT VIDPORT,R16
NOP
NOP
LSL R16
OUT VIDPORT,R16
NOP
LSL R16
DEC R17
OUT VIDPORT,R16
BRNE _output_char
NOP
LSL R16
OUT VIDPORT,R16
NOP
NOP
LSL R16
OUT VIDPORT,R16
LD ZL,Y+
LSL R16
OUT VIDPORT,R16
Кстати, я свернул в вывод буковок на экран в цикл, это экономит ну просто дохрена места (по объему - около 25% всего кода !!).
Действительно, красиво. 😃 - Вот что значит свежий взгляд !
… Причем, у меня и времени на пиксел на один цикл больше… (Вы так и не перешли на 20МГц тактовой ?)
Почему Шеререр (или Artie? 😃) не сделал этого сразу - непонятно.
“Элементарно, Уотсен !” - Потому, что тупые 😊. Оба.
На самом деле, объема под код здесь было - хоть опой ешь (у меня до сих пор всего около 70% занято), а разорвать цикл посередине - просто не догадались. (Хочется думать, что если/когда бы приперло - тоже сообразили бы, но это самооправдание…)
>Вы так и не перешли на 20МГц тактовой ?
Ну , официально Atmega8-16 до 16 МГц, выше - разгон. Некошерно 😉
>объема под код здесь было - хоть опой ешь
У меня парсинг NMEA и перевод из ASCII float в целые неожиданно много места занял (пишу с претензией на универсальность, чтоб любое количество знаков после запятой понимал, об ошибках парсинга рапортовал и т.д.), поэтому пришлось еще раз критически посмотреть на код. Под горячую руку попал вывод букивок 😉
Ну , официально Atmega8-16 до 16 МГц, выше - разгон. Некошерно 😉
А я сразу поставил 88ую; - в частности и из-за тактовой.
У меня парсинг NMEA и перевод из ASCII float в целые неожиданно много места занял (пишу с претензией на универсальность, чтоб любое количество знаков после запятой понимал, об ошибках парсинга рапортовал и т.д.), поэтому пришлось еще раз критически посмотреть на код. Под горячую руку попал вывод букивок 😉
С “горячей рукой” все понятно, а координаты я в свое время делил “пополам”: градусы с минутами превращал в целые минуты, вычитал из них “базовые” координаты, умножал на тыщу и прибавлял к ним, как целые, доли минут.
В результате получались координаты без потери точности, умещавшиеся не то что в long - в двухбайтовый int…
Навигацию вокруг всего земного шара на такой математике, конечно, осуществлять нельзя было, но в пределах пары десятков километров - “на ура”. Правда, для пересчета дальности был нужен целочисленный квадратный корень, который как-то очень плохо “таблетизировался”…
Нынче же я на все эти “изыски” наплевал, и считаю в [почти честной] плавучке, благо double sin (double) требует что-то всего около 500-700uS (или как-то похоже). Со всеми отсюда вытекающими.
>целочисленный квадратный корень
Есть пара неплохих алгоритмов. Successive approximation - бинарный поиск. Быстрый, но требует умножния.
И что-то похожее на Cordic. Медленный, но компактнее я в жизни не видел. Я его использую, только расширенный до 4 байт:
;***************************************************************************
;*
;* "sqrt" - square root routine
;*
;* This subroutine extracts the square root
;* R17:R16 is input
;* The result is placed in R18
;* R19, R20 are temporary registers
;***************************************************************************
sqrt:
clr R18
ldi R19,1; initialize the seed to be subtracted
clr R20; for each iteration
_loop:
sub R16,R19
sbc R17,R20
brlo _rexit
inc R18
subi R19, low(-2); keep the number to subtract ODD
sbci R20, high(-2); strange add via subtracting a neg number
rjmp _loop
_rexit:
ret
Ну, и особенно меня прёт писать такие вот вещи (используются при парсинге). Попробуйте с плавучкой хотя бы близко подойти к такой скорости и объему. 10 клоков, 10 команд:
;***************************************************************************
;*
;* mul10_2 - procedure that multiplies a 2-byte number by 10
;*
;* R17:R16 is input
;*
;*
;* R19:R18 is output
;* procedure works by this rule: 10=8+2
;***************************************************************************
mul10_2:
ADD R16, R16 ; multiplying input by 2
ADC R17, R17
MOV R19, R17 ; copying input*2 to output
MOV R18, R16
ADD R16, R16 ; multiplying input by 2, now input=input*4
ADC R17, R17
ADD R16, R16 ; multiplying input by 2, now input=input*8
ADC R17, R17
ADD R18, R16 ; adding input*8 to output
ADC R19, R17
RET
Всё, больше не выпендриваюсь, сначала доделаю ГПС…
>целочисленный квадратный корень
Есть пара неплохих алгоритмов. Successive approximation - бинарный поиск. Быстрый, но требует умножния.
Угу. Именно так и было сделано, благо в мегах есть аппаратное умножение.
И что-то похожее на Cordic. Медленный, но компактнее я в жизни не видел.
А такого я и не знал, но впредь буду иметь в виду.
“Мы тоже не всего читали Кнута…” 😉
Ну, и особенно меня прёт писать такие вот вещи (используются при парсинге). Попробуйте с плавучкой хотя бы близко подойти к такой скорости и объему. 10 клоков, 10 команд:
Это “классика жанра”, так что тут хвастаться нечем… - Лучше покажите такое же красивое, быстрое и каскадируемое _деление_ на 10. 😛
Однако, все хорошо к месту.
Скажите, а курс, например, в GPVTG:
VTG - Velocity made good. The gps receiver may use the LC prefix instead of GP if it is emulating Loran output.
$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*33
where:
VTG Track made good and ground speed
054.7,T True track made good (degrees)
034.4,M Magnetic track made good
005.5,N Ground speed, knots
010.2,K Ground speed, Kilometers per hour
*33 Checksum
как отсчитывается? В градусах, от направления на север, и против часовой стрелки? То есть, 90 градусов - это на запад?
Мне надо стрелку курса на картинке на нужный угол повернуть, а я вот тут на такой фигне запнулся 😕
И это вот, забегая вперед…
Кто-то говорил, что существуют варианты автопилота с управлением по одному каналу - руддеру. Ну, или по двум-трем - руддер, руль высоты и мотор. Имеет право на жизнь? Или по-хорошему, всё-таки надо городить систему определения положения аппарата, скажем, на пирометрах, и рулить руддером, элеронами, рулем высоты и мотором, поддерживая на заранее прошитом уровне высоту и скорость, постоянно мониторя и корректируя вектор курса и ориентацию аппарата в пространстве по крену-тангажу?
Я просто недавно пообщался с умными людьми на полетушках, и сделал вывод: если уж на самике стоит GPS модуль, камера, передатчик и еще черт те знает что, то терять его нельзя никак. Дорого слишком. Надо при потере сигнала с передатчика возвращать его на базу.
Вот, в общем-то, и определился функционал второй платы моей телеметрии со вторым процессором на борту. Математика и логика там не такая уж там сложная, но голову поломать всё равно придется 😃
Скажите, а курс, например, в GPVTG:
как отсчитывается? В градусах, от направления на север, и против часовой стрелки? То есть, 90 градусов - это на запад?
Курс - он от севера и _по_ часовой стрелке. 90deg - это восток. 😎
У меня вертится не стрелка, а “картушка” (на 36 пеленгов), поэтому я считаю так:
[codebox] track= 365 - atoi §;
if (track > 359)
track= 0;
else
track/= 10;
i= (uint8_t) track;[/codebox]
p - поинтер на отпарсированный параметр, i - “номер пеленга” для отрисовки.
Плюс 5 градусов - чтобы от 355 и до 5 “картушка” стояла севером ровно вверх.
И это вот, забегая вперед…
Кто-то говорил, что существуют варианты автопилота с управлением по одному каналу - руддеру. Ну, или по двум-трем - руддер, руль высоты и мотор. Имеет право на жизнь? Или по-хорошему, всё-таки надо городить систему определения положения аппарата, скажем, на пирометрах, и рулить руддером, элеронами, рулем высоты и мотором, поддерживая на заранее прошитом уровне высоту и скорость, постоянно мониторя и корректируя вектор курса и ориентацию аппарата в пространстве по крену-тангажу?
ика и логика там не такая уж там сложная, но голову поломать всё равно придется 😃
От самолета, однако, зависит.
Берем аппарат с отчетливым V крыла - и рулим только руддером (вернее, тут даже рулить не надо: положил его в нужную (или вообще, в любую) сторону на небольшой угол и ждешь, пока курс не станет нужным… 😁
Но высоту в каких-то пределах поддерживать все-таки придется, и делать это нужно не по gps’у, а по барометрическому альтметру.
А пирометры нужны для менее стабильных аппаратов. У папараццев, например, ЛК, так что без активного руления как креном, так и тангажом им никуда…
Всей логики - ПИ[Д]-регулятор. - Только коэффициенты подобрать ! 😁
Уфф почти добил код GPS. Да, на cях бы было проще писать 😮
Зато всё утопталось в один кристалл. Теперь - испытания, даже не знаю, как их в условиях дома и двора проводить 😃
Время инициализации платы телеметрии увеличил до минуты: за это время GPS модуль [по идее] должен выйти из какого угодно холодного старта, войти в 3D Fix и начать отдавать NMEA строки с релевантными данными для запоминания стартовой позиции. За стартовую позицию принимаются данные, полученные в 1 минуту 0 секунд со времени старта телеметрии, не глядя на режим 2D или 3D или количество спутников. При этом количество спутников и режим 2D/3D Fix на экране отображается, если что-то пошло не так, плату можно рестартнуть.
Ждать, пока не придут гарантированно адекватные 3D fix данные, нецелесообразно, потому что парсинг усложняется в разы - порядок следования NMEA строк у каждого GPS модуля свой собственный. Полагаться на то, что вот в этот вот момент в GPGSA показан 3D fix, а значит в последующих строках идут данные с 3D Fix’ом, нельзя. Кто знает, вдруг между GPGSA и GPRMC модуль свалился в 2D fix, а в пачке строк GPGSA идет после GPRMC, и информация о том, что это 2D Fix, а не 3D, придет уже после того, как 2D-данные ошибочно приняты за 3D. Как это обходить, я вообще не знаю.
В общем, стратегия такая: отображаем всё, что выдает модуль, он умный. Выбрасываем лишь самые неправдоподобные показания. Ну, и гасим указатель курса при скорости меньше 5 км/ч.
Если GPS модуль не обнаружен ( с USART не принят ни один символ ‘$’), плата не показывает “карту” и работает с одними лишь датчиками.
Из NMEA строк обрабатываются GPRMC, GPGLL, GPVTG, GPGGA, GPGSA.
Фильтрации данных на предмет выбрасывания неправдоподобно больших разниц по широте и долготе пока нет. Скоро приделается.
Дополнительно к курсу и направлению на базу на “карте” на экран выводится количество спутников, 0/2D/3D Fix, скорость относительно земли, расстояние до базы, высота.
Скажите, минута - достаточно для старта любого GPS модуля?
Добавление: ну, для душевного спокойствия можно за стартовую позицию взять не единичный сэмпл с приемника, а усреднение по 8 сэмплам. Ст о ит?
Теперь - испытания, даже не знаю, как их в условиях дома и двора проводить 😃
“Тоже мне, фокус !” 😎
За стартовую позицию принимаются данные, полученные в 1 минуту 0 секунд со времени старта телеметрии, не глядя на режим 2D или 3D или количество спутников.
[…]
Добавление: ну, для душевного спокойствия можно за стартовую позицию взять не единичный сэмпл с приемника, а усреднение по 8 сэмплам. Ст о ит?
А я вывел кнопочку, бо человек - он всяко умнее железяки будет…
По ней же и ноль высоты фиксируется, ну и вообще для отладки сподручно.
Может быть, я уже в который раз открываю Америку, но я таки нашел компактный и быстрый алгоритм извлечения корня из 32-битного целого. Аналог “деления столбиком” в двоичной системе:
unsigned short lsqrt(unsigned long a){
unsigned long rem = 0;
unsigned long root = 0;
for(int i=0; i<16; i++){
root <<= 1;
rem = ((rem<< 2) + (a >> 30));
a <<= 2;
root ++;
if(root<= rem){
rem -= root;
root++;
}
else
root--;
}
return (unsigned short)(root >> 1);
}
С предыдущим алгоритмом (раза в три компактнее, но медленным аж жуть) нахождение корня занимало аж до 800000+ циклов процессора. Ни о каком вывод на экран в течение этого процесса и речи не шло. А с этим алгоритмом - сами можете примерно подсчитать, раз этак в тысячу быстрее 😃
Если еще интересно , почитай здесь : forum.sources.ru/index.php?showtopic=9933.
Как я понял, при желании алгоритм можно еще ускорить, но наверно ценой места?
Скажите, минута - достаточно для старта любого GPS модуля?
Добавление: ну, для душевного спокойствия можно за стартовую позицию взять не единичный сэмпл с приемника, а усреднение по 8 сэмплам. Ст о ит?
Нет, недостаточно. Некторые стартуют 3 минуты, некоторые 10 секунд (при горячем старте). Почему не делать простой монитор параметра А? По сути, это зеркало 2Д/3Д режима, но там тупо показывают - релевантны координаты или нет.
Усреднение безсмысленно, ИМХО. Точност не возрастёт.
Чтобы повысить точность, нужно записать 8-16 семплов, проанализировать их (разница показаний по прямой соседних семплов) и если всё в пределах допуска, то брать один семпл, который больше нравится.
Например, если патчевая антена направлена вбок, к стене здания, где стоит микроволновка, показания могут плясать на ±100 км.
Ждать, пока не придут гарантированно адекватные 3D fix данные, нецелесообразно, потому что парсинг усложняется в разы - порядок следования NMEA строк у каждого GPS модуля свой собственный. Полагаться на то, что вот в этот вот момент в GPGSA показан 3D fix, а значит в последующих строках идут данные с 3D Fix’ом, нельзя. Кто знает, вдруг между GPGSA и GPRMC модуль свалился в 2D fix, а в пачке строк GPGSA идет после GPRMC, и информация о том, что это 2D Fix, а не 3D, придет уже после того, как 2D-данные ошибочно приняты за 3D. Как это обходить, я вообще не знаю.
Обходить надо слева 😃 Строки, а именно пачка строк, получаются из одного определения. Если в GGA параметр такой, то во всех остаьных он будет таким же. Следующий пакет будет иметь другие параметры. Максимум, что может меняться в течение одного пакета, это время.
Про парсинг говорили уже. На Си надо было писать 😃 Всего три вложеных свитча…
Типа вот. Больше не позволяет форум вставить:
interrupt [USART1_RXC] void RXC_isr(void)
{
tempGPS = UDR1;
switch (iGPS) {
case ‘F’: {
switch (Scheduler_task) {
//-------------------------------------------- классифицируем заголовок
case 1:
if (tempGPS == ‘$’) { Scheduler_task++; }
break;
case 2:
if (tempGPS == ‘G’) { Scheduler_task++; }
else { Emergency_exit(); }
break;
case 3:
if (tempGPS == ‘P’) { Scheduler_task++; }
else { Emergency_exit(); }
break;
case 4:
switch (tempGPS) {
case ‘G’: Scheduler_task = 5;
break;
case ‘R’: Scheduler_task = 55;
break;
case ‘V’: Scheduler_task = 105;
break;
default: Mistake_exit();
break;
}
break;
//---------------------------------------------- классифицируем второй символ заголовка строки
case 5:
switch (tempGPS) {
case ‘G’: Scheduler_task++;
break;
case ‘S’: Scheduler_task = 156;
break;
default: Mistake_exit();
break;
}
break;
case 55:
if (tempGPS == ‘M’) { Scheduler_task++; }
else { Mistake_exit(); }
break;
case 105:
if (tempGPS == ‘T’) { Scheduler_task++; }
else { Mistake_exit(); }
break;
//---------------------------------------------- классифицируем третий символ заголовка строки
case 6:
if (tempGPS == ‘A’) { Scheduler_task++; iGPS = ‘G’; }
else { Mistake_exit(); }
break;
case 56:
if (tempGPS == ‘C’) { Scheduler_task++; iGPS = ‘R’; }
else { Mistake_exit(); }
break;
case 106:
if (tempGPS == ‘G’) { Scheduler_task++; iGPS = ‘V’; }
else { Mistake_exit(); }
break;
case 156:
if (tempGPS == ‘A’) { Scheduler_task++; iGPS = ‘S’; }
else { Mistake_exit(); }
break;
} // end of switch (Scheduler_task)
break;
}
case ‘G’: {
switch (Scheduler_task) {
//----------------------------------------------- а вот теперь пошёл разбор строки GGA
case 7:
if (tempGPS == ‘,’) { chislo_propuskov++; }
if (chislo_propuskov == 6) {
Scheduler_task++;
chislo_propuskov = 0;
}
break;
case 8:
if ( tempGPS != ‘,’ ) { rejim_opredelenia = tempGPS; }
else { Scheduler_task++; }
break;
…
>Если в GGA параметр такой, то во всех остаьных он будет таким же. Следующий пакет будет иметь другие параметры.
А можно получить в студию номер поля с этим мараметром в строке NMEA? Ну, или любой критерий, позволяющий с уверенностью говорить, что именно во эта вот строка - из пакета номер Х.
>Почему не делать простой монитор параметра А?
Лехко. Только я в GPS-делах не гуру, потому и делаю глупости.
А код - да, у меня на асме почти такой же код.
LDI ZL, LOW (NMEA_COMPLETE_STRING); loading NMEA sentence header
LDI ZH, HIGH (NMEA_COMPLETE_STRING)
LD R16, Z+; $ symbol
LD R17, Z+; G symbol
LD R18, Z+; P symbol
LD R19, Z+; ? symbol
LD R20, Z+; ? symbol
LD R21, Z+; ? symbol
STS NMEA_HEADER_C1, R19
STS NMEA_HEADER_C2, R20
STS NMEA_HEADER_C3, R21
LDS R19, NMEA_HEADER_C1; processing RMC sentence
LDS R20, NMEA_HEADER_C2
LDS R21, NMEA_HEADER_C3
CPI R19, 'R'
BRNE _not_RMC_sentence
CPI R20, 'M'
BRNE _not_RMC_sentence
CPI R21, 'C'
BRNE _not_RMC_sentence
LDI R16, 3
RCALL NMEA_parse_latitude
LDI R16, 5
RCALL NMEA_parse_longtitude
LDI R16, 8
RCALL NMEA_parse_heading
_not_RMC_sentence:
LDS R19, NMEA_HEADER_C1; processing VTG sentence
LDS R20, NMEA_HEADER_C2
LDS R21, NMEA_HEADER_C3
CPI R19, 'V'
BRNE _not_VTG_sentence
CPI R20, 'T'
BRNE _not_VTG_sentence
CPI R21, 'G'
BRNE _not_VTG_sentence
LDI R16, 1
RCALL NMEA_parse_heading
LDI R16, 7
RCALL NMEA_parse_speed
_not_VTG_sentence:
LDS R19, NMEA_HEADER_C1; processing GGA sentence
LDS R20, NMEA_HEADER_C2
LDS R21, NMEA_HEADER_C3
CPI R19, 'G'
BRNE _not_GGA_sentence
CPI R20, 'G'
BRNE _not_GGA_sentence
CPI R21, 'A'
BRNE _not_GGA_sentence
LDI R16, 2
RCALL NMEA_parse_latitude
LDI R16, 4
RCALL NMEA_parse_longtitude
RCALL NMEA_clear_parse_error
LDI R16, 7
RCALL NMEA_parse_integer_number
LDS R20, NMEA_PARSE_ERROR
SBRS R20, 0
STS NUM_SATELLITES, R16
_not_GGA_sentence:
LDS R19, NMEA_HEADER_C1; processing GSA sentence
LDS R20, NMEA_HEADER_C2
LDS R21, NMEA_HEADER_C3
CPI R19, 'G'
BRNE _not_GSA_sentence
CPI R20, 'S'
BRNE _not_GSA_sentence
CPI R21, 'A'
BRNE _not_GSA_sentence
RCALL NMEA_clear_parse_error
LDI R16, 2
RCALL NMEA_parse_integer_number
LDS R20, NMEA_PARSE_ERROR
SBRS R20, 0
STS TMP_FIXMODE, R17
_not_GSA_sentence:
LDS R19, NMEA_HEADER_C1; processing GLL sentence
LDS R20, NMEA_HEADER_C2
LDS R21, NMEA_HEADER_C3
CPI R19, 'G'
BRNE _not_GLL_sentence
CPI R20, 'L'
BRNE _not_GLL_sentence
CPI R21, 'L'
BRNE _not_GLL_sentence
LDI R16, 1
RCALL NMEA_parse_latitude
LDI R16, 3
RCALL NMEA_parse_longtitude
_not_GLL_sentence:
“Какой кошмар !” [Фрекен Бок] 😲
Я в таких случаях (когда набор возможных тэгов ограничен и заранее известен) пользуюсь следующим совершенно ненаучным, но вполне эффективным способом:
uint8_t nmea_hash (char *p)
{
uint8_t i, h=0;
for (i=0; i<5; i++)
{
h<<= 1;
if (h & 0x80)
{
h&= 0x7F;
h++;
}
h^= *p++;
}
return h;
}
...
switch (nmea_hash (NmeaBuffer))
{
case hash_GPGGA:
...
case hash_GPVTG:
...
“Формула” такой хэш-функции подбирается практически от балды; достаточно чтобы на ожидаемом массиве тэгов результат был уникальным. На ассемблере оно получается еще лучше - буквально в две-три команды и без проверок, поскольку можно оперировать флагом переноса…
Данная конкретная конструкция “настроена” на набор сентенсов от Garmin’а.
PS: Да, я заполняю буфер, начиная с ‘$’, и заканчиваю по ‘*’, сразу же их отбрасывая. Чексам проверять ленюсь. 😃