NMEA-Protokoll und Nachrichtenbeschreibung
Inhaltsverzeichnis
A Protokoll ist ein Regelwerk, das festlegt, wie Daten zwischen zwei oder mehr Geräten formatiert, übertragen und interpretiert werden, damit diese einander verstehen können.
Man kann es sich wie eine Sprache mit strengen Grammatikregeln vorstellen – Sender und Empfänger müssen sich exakt an dieselben Regeln halten, sonst ist die Nachricht bedeutungslos. Ohne Protokolle würde jeder Hersteller sein eigenes Format entwickeln, und Geräte verschiedener Marken wären nicht mehr miteinander kompatibel.
Das NMEA-0183-Protokoll (im Folgenden NMEA) ist der Industriestandard für GNSS-Technologie.
Wir haben diese Seite als Referenz für die Definition des NMEA-Protokolls und zur Beschreibung der gängigsten Meldungen erstellt. Sollten Sie eine Meldung vermissen oder einen Tippfehler finden, Kontakt aufnehmen und wir werden es reparieren 🙂
Beliebte NMEA-Nachrichten
| Nachricht | Beschreibung | Verfügbarkeit |
|---|---|---|
| GGA | Globale Positionierungssystem-Positionsdaten – Position, Höhe, Positionsgenauigkeit und Anzahl der Satelliten | Alle Empfänger |
| GLL | Geografische Position – Breitengrad und Längengrad mit Zeitangabe und Status | Alle Empfänger |
| GNS | GNSS-Fixdaten – ähnlich wie GGA, unterstützt aber mehrere Satellitenkonstellationen (GPS, GLONASS, Galileo...) | Alle Empfänger |
| GRS | GNSS-Reichweitenresiduen – Residuen der in der Navigationslösung verwendeten Entfernungen | Alle Empfänger |
| GSA | GNSS DOP und aktive Satelliten – Fixtyp (2D/3D) und verwendete Satelliten | Alle Empfänger |
| GST | GNSS-Pseudorange-Fehlerstatistik — Positionsfehlerschätzungen (RMS, Breitengrad, Längengrad, Höhe) | Alle Empfänger |
| GSV | GNSS-Satelliten im Sichtfeld – Anzahl, Elevation, Azimut und Signalstärke der sichtbaren Satelliten | Alle Empfänger |
| HDT | Tatsächlicher Kurs – der tatsächliche Kurs des Schiffes relativ zu Nord | Septentrio Mosaic-H simpleRTK3B Heading |
| INSPVAXA | Sensorfusionsdaten – integrierte Position, Geschwindigkeit, Lage und deren geschätzte Fehler | Unicore UM981 simpleRTK3B Fusion |
| PUBX,00 | Positionsdaten – Breitengrad, Längengrad, Höhe und Positionsgenauigkeit (u-blox Geräte) | Alle u-blox Empfänger |
| PUBX,04 | Tageszeit — UTC-Zeit- und Uhrzeitdaten (u-blox Geräte) | Alle u-blox Empfänger |
| RMC | Empfohlene Mindest-GNSS-Daten – Position, Geschwindigkeit, Kurs und Datum | Alle Empfänger |
| VERROTTEN | Drehgeschwindigkeit – Rotationsgeschwindigkeit des Schiffes in Grad pro Minute | Septentrio Mosaic-H simpleRTK3B Heading |
| VTG | Kurs und Geschwindigkeit über Grund – Kurs und Geschwindigkeit in Knoten und km/h | Alle Empfänger |
| USA | Uhrzeit und Datum – UTC-Zeit, Tag, Monat, Jahr und lokale Zeitzone | Alle Empfänger |
NMEA-Nachrichtenstruktur
Jede Nachricht beginnt mit einem $ ein Zeichen gefolgt von einem kurzen Code, der die Art der darin enthaltenen Daten angibt (siehe Tabelle im nächsten Abschnitt).
Der Empfänger füllt dann alle durch Kommas getrennten Datenfelder aus – Breitengrad, Längengrad, Höhe, Zeit, Anzahl der Satelliten usw. – und beendet die Nachricht mit einem Prüfsumme, wobei es sich um eine kleine Zahl handelt, die es dem Empfangsgerät ermöglicht zu überprüfen, ob die Daten während der Übertragung beschädigt wurden.
Die Nachricht endet mit einem Zeilenumbruch und die nächste Nachricht beginnt unmittelbar danach.
Die folgende Abbildung veranschaulicht, wie eine NMEA-Nachricht generiert wird.
NMEA-Prüfsummengenerierung
Codebeispiele zur Generierung der NMEA-Prüfsumme basierend auf einer NMEA-Nutzlast:
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-Prüfsummenvalidierung
Wenn Sie überprüfen möchten, ob eine NMEA-Nachricht legitim ist oder nicht, verwenden Sie den folgenden Beispielcode:
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-Prüfsummenrechner
Prüfsumme (hexadezimal)
--
Prüfsumme (Dezimal)
--
Nutzlastlänge
--
Vollständiger Satz
-
Einen Satz überprüfen