Описание протокола и сообщения NMEA
Содержание
A протокол Это набор правил, определяющих, как данные форматируются, передаются и интерпретируются между двумя или более устройствами, чтобы они могли понимать друг друга.
Представьте это как язык со строгими грамматическими правилами — и отправитель, и получатель должны следовать одним и тем же правилам, иначе сообщение бессмысленно. Без протоколов каждый производитель изобрел бы свой собственный формат, и устройства разных марок не смогли бы взаимодействовать друг с другом.
Протокол NMEA-0183 (далее NMEA) является отраслевым стандартом для технологии GNSS.
Мы подготовили эту страницу в качестве справочника по определению протокола NMEA, а также для предоставления описания наиболее часто используемых сообщений. Если вы пропустили какое-либо сообщение или обнаружили опечатку, Свяжитесь с нами И мы это исправим 🙂
Популярные сообщения NMEA
| Сообщение | Описание | Доступность |
|---|---|---|
| GGA | Данные системы глобального позиционирования (GPS) — местоположение, высота, качество определения местоположения и количество спутников. | Все приемники |
| GLL | Географическое положение — широта и долгота с указанием времени и статуса. | Все приемники |
| GNS | Данные о местоположении GNSS — аналогичны GGA, но поддерживают несколько спутниковых систем (GPS, GLONASS, Galileo...) | Все приемники |
| GRS | Остаточные значения дальности GNSS — остаточные значения дальности, использованной в навигационном решении. | Все приемники |
| GSA | GNSS DOP и активные спутники — тип фиксации (2D/3D) и используемые спутники. | Все приемники |
| GST | Статистика ошибок псевдодальности GNSS — оценки ошибок определения местоположения (среднеквадратичное отклонение, широта, долгота, высота). | Все приемники |
| GSV | Спутники GNSS в поле зрения — количество, угол места, азимут и уровень сигнала видимых спутников. | Все приемники |
| HDT | Истинный курс — фактический курс судна относительно истинного севера. | Septentrio Mosaic-H simpleRTK3B Heading |
| ИНСПАВАКСА | Данные, полученные методом слияния данных с датчиков — интегрированные данные о положении, скорости, ориентации и их оценочные погрешности. | Unicore UM981 simpleRTK3B Fusion |
| PUBX,00 | Данные о местоположении — широта, долгота, высота и качество определения местоположения (u-blox устройства) | Все u-blox приемники |
| PUBX,04 | Время суток — данные о времени и часах по UTC (Всемирному координированному времени).u-blox устройства) | Все u-blox приемники |
| RMC | Рекомендуемый минимальный набор необходимых данных GNSS: местоположение, скорость, курс и дата. | Все приемники |
| RED | Скорость вращения — скорость вращения судна в градусах в минуту. | Septentrio Mosaic-H simpleRTK3B Heading |
| VTG | Курс относительно земли и скорость относительно земли — траектория и скорость в узлах и км/ч. | Все приемники |
| Соединенные Штаты Америки | Время и дата — время UTC, день, месяц, год и местный часовой пояс. | Все приемники |
Структура сообщения NMEA
Каждое сообщение начинается с $ Знак, за которым следует короткий код, идентифицирующий тип содержащихся в нем данных (см. таблицу в следующем разделе).
Затем получатель заполняет все поля данных, разделенные запятыми — широту, долготу, высоту, время, количество спутников и т. д. — и завершает сообщение. контрольная суммаЭто небольшое число, позволяющее принимающему устройству убедиться в том, что данные не были повреждены во время передачи.
Сообщение заканчивается переносом строки, и следующее сообщение начинается сразу после него.
На приведенном ниже рисунке наглядно показано, как генерируется сообщение NMEA.
генерация контрольной суммы NMEA
Примеры кода для генерации контрольной суммы NMEA на основе данных NMEA:
def nmea_checksum(payload):
checksum = 0
for char in payload:
checksum ^= ord(char)
return f"{checksum:02X}"
# Pass only the part between $ and *
print(nmea_checksum("GNGGA,092725.00,4717.11399,N,00833.91986,E,1,08,1.01,499.6,M,48.0,M,,"))
# Returns: '4E' (or whatever the correct checksum is)
Проверка контрольной суммы NMEA
Чтобы проверить подлинность сообщения NMEA, воспользуйтесь приведенным ниже примером кода:
def validate_nmea(sentence):
sentence = sentence.strip()
if not sentence.startswith('$') or '*' not in sentence:
return False
payload, claimed = sentence[1:].split('*', 1)
checksum = 0
for char in payload:
checksum ^= ord(char)
return f"{checksum:02X}" == claimed.strip()[:2].upper()
print(validate_nmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A")) # True
print(validate_nmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*FF")) # False
print(validate_nmea("invalid sentence")) # False
#include
#include
#include
#include
bool validate_nmea(const char *sentence) {
if (!sentence || *sentence != '$') return false;
const char *star = strchr(sentence, '*');
if (!star || strlen(star) < 3) return false;
uint8_t checksum = 0;
const char *p = sentence + 1;
while (p != star) {
checksum ^= (uint8_t)*p++;
}
uint8_t claimed;
if (sscanf(star + 1, "%2hhX", &claimed) != 1) return false;
return checksum == claimed;
}
int main() {
printf("%d\n", validate_nmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A")); // 1
printf("%d\n", validate_nmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*FF")); // 0
printf("%d\n", validate_nmea(NULL)); // 0
printf("%d\n", validate_nmea("invalid")); // 0
return 0;
}
function validateNmea(sentence) {
sentence = sentence.trim();
if (!sentence.startsWith('$') || !sentence.includes('*')) return false;
const starIdx = sentence.indexOf('*');
const payload = sentence.slice(1, starIdx);
const claimed = sentence.slice(starIdx + 1, starIdx + 3).toUpperCase();
if (claimed.length < 2 || !/^[0-9A-F]{2}$/.test(claimed)) return false;
let checksum = 0;
for (let i = 0; i < payload.length; i++) {
checksum ^= payload.charCodeAt(i);
}
return checksum.toString(16).toUpperCase().padStart(2, '0') === claimed;
}
validateNmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A"); // true
validateNmea("$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*FF"); // false
validateNmea("invalid"); // false
#include
#include
#include
uint8_t nmea_checksum(const char *sentence) {
// Skip leading '$' if present
if (*sentence == '$') sentence++;
uint8_t checksum = 0;
while (*sentence && *sentence != '*') {
checksum ^= (uint8_t)*sentence++;
}
return checksum;
}
int main() {
const char *sentence = "$GNGGA,092725.00,4717.11399,N,00833.91986,E,1,08,1.01,499.6,M,48.0,M,,";
printf("Checksum: %02X\n", nmea_checksum(sentence));
return 0;
}
function nmeaChecksum(sentence) {
// Strip leading $ and everything from * onwards
sentence = sentence.replace(/^\$/, '').split('*')[0];
let checksum = 0;
for (let i = 0; i < sentence.length; i++) {
checksum ^= sentence.charCodeAt(i);
}
return checksum.toString(16).toUpperCase().padStart(2, '0');
}
nmeaChecksum("GNGGA,092725.00,4717.11399,N,00833.91986,E,1,08,1.01,499.6,M,48.0,M,,");
// Returns: "4E"
Онлайн-калькулятор контрольной суммы NMEA
Контрольная сумма (шестнадцатеричная)
--
Контрольная сумма (в десятичном формате)
--
Длина полезной нагрузки
--
Полное предложение
-
Проверьте правильность предложения.