Descrizione del protocollo e dei messaggi NMEA
Sommario
A protocollo È un insieme di regole che definisce come i dati vengono formattati, trasmessi e interpretati tra due o più dispositivi affinché possano comprendersi reciprocamente.
Pensatela come una lingua con regole grammaticali rigide: sia il mittente che il destinatario devono seguire esattamente le stesse regole, altrimenti il messaggio è privo di significato. Senza protocolli, ogni produttore inventerebbe il proprio formato e i dispositivi di marche diverse non sarebbero in grado di comunicare tra loro.
Il protocollo NMEA-0183 (d'ora in poi NMEA) è lo standard di settore per la tecnologia GNSS.
Abbiamo preparato questa pagina come riferimento per la definizione del protocollo NMEA e per fornire anche una descrizione dei messaggi più comuni. Se ti manca qualche messaggio o trovi qualche errore di battitura, Contattaci e lo sistemeremo 🙂
Messaggi popolari di NMEA
| Messaggio | Descrizione | Disponibilità |
|---|---|---|
| GGA | Dati di posizionamento globale (GPS): posizione, altitudine, qualità del rilevamento e numero di satelliti. | Tutti i ricevitori |
| GLL | Posizione geografica: latitudine e longitudine con tempo e stato | Tutti i ricevitori |
| GNS | Dati di fix GNSS — simile a GGA ma supporta più costellazioni (GPS, GLONASS, Galileo...) | Tutti i ricevitori |
| GRS | Residui di portata GNSS — residui delle distanze utilizzate nella soluzione di navigazione | Tutti i ricevitori |
| GSA | GNSS DOP e satelliti attivi: tipo fisso (2D/3D) e satelliti utilizzati | Tutti i ricevitori |
| GST | Statistiche sull'errore di pseudodistanza GNSS: stime dell'errore di posizione (RMS, latitudine, longitudine, altitudine) | Tutti i ricevitori |
| GSV | Satelliti GNSS visibili: numero, elevazione, azimut e intensità del segnale dei satelliti visibili | Tutti i ricevitori |
| HDT | Direzione vera — rotta effettiva della nave rispetto al nord geografico | Septentrio Mosaic-H simpleRTK3B Heading |
| INSPVAXA | Dati di fusione dei sensori: posizione, velocità, assetto integrati e relativi errori stimati. | Unicore UM981 simpleRTK3B Fusion |
| PUBX,00 | Dati di posizione: latitudine, longitudine, altitudine e qualità del fix (u-blox dispositivi) | Tutti u-blox Ricevitori |
| PUBX,04 | Ora del giorno — Ora UTC e dati dell'orologiou-blox dispositivi) | Tutti u-blox Ricevitori |
| RMC | Dati GNSS minimi raccomandati: posizione, velocità, rotta e data. | Tutti i ricevitori |
| RED | Velocità di virata: velocità di rotazione dell'imbarcazione in gradi al minuto. | Septentrio Mosaic-H simpleRTK3B Heading |
| VTG | Rotta e velocità rispetto al terreno: rotta e velocità in nodi e km/h | Tutti i ricevitori |
| Stati Uniti d'America | Ora e data: ora UTC, giorno, mese, anno e fuso orario locale. | Tutti i ricevitori |
Struttura del messaggio NMEA
Ogni messaggio inizia con un $ un simbolo seguito da un codice breve che identifica il tipo di dati che contiene (vedi tabella nella sezione successiva).
Il ricevitore quindi inserisce tutti i campi dati separati da virgole — latitudine, longitudine, altitudine, ora, numero di satelliti, ecc. — e termina il messaggio con un checksum, che è un piccolo numero che consente al dispositivo ricevente di verificare che i dati non siano stati corrotti durante la trasmissione.
Il messaggio termina con un'interruzione di riga e il messaggio successivo inizia immediatamente dopo.
L'immagine seguente riassume il processo di generazione di un messaggio NMEA.
Generazione del checksum NMEA
Esempi di codice per generare il checksum NMEA in base a un payload 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)
Validazione del checksum NMEA
Se si desidera verificare se un messaggio NMEA è legittimo o meno, utilizzare il codice di esempio riportato di seguito:
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"
Calcolatore online del checksum NMEA
Checksum (esadecimale)
--
Checksum (decimale)
--
lunghezza del carico utile
--
Frase completa
-
Verificare una frase