Protokol NMEA a popis zprávy
Obsah
A protokol je sada pravidel, která definují, jak jsou data formátována, přenášena a interpretována mezi dvěma nebo více zařízeními, aby si vzájemně rozuměla.
Představte si to jako jazyk s přísnými gramatickými pravidly – odesílatel i příjemce musí dodržovat úplně stejná pravidla, jinak je zpráva bezvýznamná. Bez protokolů by si každý výrobce vymyslel vlastní formát a zařízení od různých značek by spolu nemohla komunikovat.
Protokol NMEA-0183 (dále jen NMEA) je průmyslovým standardem pro technologii GNSS.
Tuto stránku jsme připravili jako referenci pro definici protokolu NMEA a také pro popis nejčastějších zpráv. Pokud vám nějaká zpráva unikne nebo najdete nějaký překlep, kontaktujte nás a my to opravíme 🙂
Oblíbené zprávy NMEA
| Zpráva | Popis | dostupnost |
|---|---|---|
| GGA | Data o fixaci globálního pozičního systému – poloha, nadmořská výška, kvalita fixace a počet satelitů | Všichni přijímači |
| GLL | Zeměpisná poloha – zeměpisná šířka a délka s časem a stavem | Všichni přijímači |
| GNS | Data o fixaci GNSS – podobná GGA, ale podporuje více souhvězdí (GPS, GLONASS, Galileo...) | Všichni přijímači |
| GRS | Zbytky dosahu GNSS – zbytky dosahů použitých v navigačním řešení | Všichni přijímači |
| GSA | GNSS DOP a aktivní satelity – typ fixu (2D/3D) a použité satelity | Všichni přijímači |
| GST | Statistika chyb pseudovzdáleností GNSS — odhady chyb polohy (RMS, zeměpisná šířka, délka, nadmořská výška) | Všichni přijímači |
| GSV | Zobrazené satelity GNSS – počet, nadmořská výška, azimut a síla signálu viditelných satelitů | Všichni přijímači |
| HDT | Skutečný směr – skutečný směr plavidla vzhledem k skutečnému severu | Septentrio Mosaic-H simpleRTK3B Heading |
| INSPVAXA | Data fúze senzorů – integrovaná poloha, rychlost, orientace a jejich odhadované chyby | Unicore UM981 simpleRTK3B Fusion |
| PUBX,00 | Údaje o poloze – zeměpisná šířka, délka, nadmořská výška a kvalita fixu (u-blox zařízení) | Vše u-blox přijímače |
| PUBX,04 | Denní čas — čas a hodiny UTC (u-blox zařízení) | Vše u-blox přijímače |
| RMC | Doporučené minimální specifické údaje GNSS – poloha, rychlost, kurz a datum | Všichni přijímači |
| RED | Rychlost otáčení – rychlost otáčení plavidla ve stupních za minutu | Septentrio Mosaic-H simpleRTK3B Heading |
| VTG | Kurz vzhledem k zemi a rychlost od země – dráha a rychlost v uzlech a km/h | Všichni přijímači |
| USA | Čas a datum — čas UTC, den, měsíc, rok a místní časové pásmo | Všichni přijímači |
Struktura zpráv NMEA
Každá zpráva začíná slovem $ znak následovaný krátkým kódem, který identifikuje typ dat, která obsahuje (viz tabulka v další části).
Přijímač poté vyplní všechna datová pole oddělená čárkami – zeměpisnou šířku, délku, nadmořskou výšku, čas, počet satelitů atd. – a zprávu zakončí slovem kontrolní součet, což je malé číslo, které umožňuje přijímajícímu zařízení ověřit, zda data nebyla během přenosu poškozena.
Zpráva končí zalomením řádku a bezprostředně poté začíná další zpráva.
Obrázek níže shrnuje, jak se generuje zpráva NMEA.
Generování kontrolního součtu NMEA
Příklady kódu pro generování kontrolního součtu NMEA na základě datové zátěže 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)
Ověření kontrolního součtu NMEA
Pokud chcete ověřit, zda je zpráva NMEA platná, použijte níže uvedený příklad kódu:
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"
Online kalkulačka kontrolního součtu NMEA
Kontrolní součet (hex)
--
Kontrolní součet (desetinný)
--
Délka užitečného zatížení
--
Celá věta
-
Ověření věty