NMEA-protocol en berichtbeschrijving
Inhoudsopgave
A protocol Een regel is een geheel van regels dat definieert hoe gegevens worden geformatteerd, verzonden en geïnterpreteerd tussen twee of meer apparaten, zodat ze elkaar kunnen begrijpen.
Zie het als een taal met strikte grammaticaregels: zowel de zender als de ontvanger moeten zich aan exact dezelfde regels houden, anders is de boodschap betekenisloos. Zonder protocollen zou elke fabrikant zijn eigen formaat bedenken en zouden apparaten van verschillende merken niet met elkaar kunnen communiceren.
Het NMEA-0183-protocol (hierna NMEA genoemd) is de industriestandaard voor GNSS-technologie.
Deze pagina dient als naslagwerk voor de definitie van het NMEA-protocol en bevat tevens een beschrijving van de meest gebruikte berichten. Mocht u een bericht missen of een typefout aantreffen, contact En we lossen het op 🙂
Populaire NMEA-berichten
| Typ hier uw vraag of bericht | Beschrijving | Beschikbaarheid |
|---|---|---|
| GGA | GPS-positiegegevens — positie, hoogte, nauwkeurigheid van de positiebepaling en aantal satellieten | Alle ontvangers |
| GLL | Geografische positie — breedte- en lengtegraad met tijd en status | Alle ontvangers |
| GNS | GNSS-fixgegevens — vergelijkbaar met GGA, maar ondersteunt meerdere constellaties (GPS, GLONASS, Galileo...) | Alle ontvangers |
| GRS | GNSS-bereikresiduen — residuen van bereiken die in de navigatieoplossing worden gebruikt | Alle ontvangers |
| GSA | GNSS DOP en actieve satellieten — fixatietype (2D/3D) en gebruikte satellieten | Alle ontvangers |
| GST | GNSS-pseudobereikfoutstatistieken — schattingen van de positiefout (RMS, breedtegraad, lengtegraad, hoogte) | Alle ontvangers |
| GSV | Zichtbare GNSS-satellieten — aantal, elevatie, azimut en signaalsterkte van de zichtbare satellieten | Alle ontvangers |
| HDT | Ware koers — de werkelijke koers van het schip ten opzichte van het ware noorden | Septentrio Mosaic-H simpleRTK3B Heading |
| INSPVAXA | Sensorfusiegegevens — geïntegreerde positie, snelheid, oriëntatie en hun geschatte fouten. | Unicore UM981 simpleRTK3B Fusion |
| PUBX,00 | Positiegegevens — breedtegraad, lengtegraad, hoogte en fixkwaliteit (u-blox -apparaten) | Alles u-blox ontvangers |
| PUBX,04 | Tijdstip — UTC-tijd en klokgegevens (u-blox -apparaten) | Alles u-blox ontvangers |
| RMC | Aanbevolen minimale specifieke GNSS-gegevens: positie, snelheid, koers en datum | Alle ontvangers |
| RED | Draaisnelheid — de snelheid waarmee het vaartuig roteert in graden per minuut. | Septentrio Mosaic-H simpleRTK3B Heading |
| VTG | Koers over de grond en grondsnelheid — koers en snelheid in knopen en km/u | Alle ontvangers |
| Verenigde Staten van Amerika | Tijd en datum — UTC-tijd, dag, maand, jaar en lokale tijdzone | Alle ontvangers |
NMEA-berichtstructuur
Elk bericht begint met een $ een teken gevolgd door een korte code die het type gegevens identificeert (zie tabel in de volgende sectie).
De ontvanger vult vervolgens alle gegevensvelden in, gescheiden door komma's — breedtegraad, lengtegraad, hoogte, tijd, aantal satellieten, enz. — en sluit het bericht af met een checksum, wat een klein getal is waarmee het ontvangende apparaat kan controleren of de gegevens tijdens de overdracht niet beschadigd zijn geraakt.
Het bericht eindigt met een regelovergang en het volgende bericht begint direct daarna.
Onderstaande afbeelding geeft een overzicht van hoe een NMEA-bericht wordt gegenereerd.
NMEA-checksumgeneratie
Codevoorbeelden om de NMEA-checksum te genereren op basis van een NMEA-payload:
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-checksumvalidatie
Als je wilt controleren of een NMEA-bericht geldig is, gebruik dan de onderstaande voorbeeldcode:
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 NMEA-checksumcalculator
Controlesom (hexadecimaal)
--
Controlesom (decimaal)
--
Laadvermogen lengte
--
Volledige zin
-
Controleer een zin