Décoder une trame NMEA
Introduction
Le récepteur GPS de votre téléphone ne se contente pas de calculer votre position : il la publie ensuite à toutes les applications qui ont besoin de la connaître. Mais sous quelle forme ? Comment cette suite de mesures satellites devient-elle « 48,8566° N, 2,3522° E » ? La réponse passe par un protocole textuel des années 1980, étonnamment encore en service aujourd'hui : le NMEA 0183.
Un standard maritime devenu universel
NMEA est l'acronyme de la National Marine Electronics Association, qui a
défini ce protocole pour faire dialoguer entre eux les équipements
électroniques des navires : sonde, gouvernail automatique, traceur de cartes,
récepteur GPS… La version 0183 désigne une norme publiée en 1983, encore
largement utilisée.
Ce qu'il faut en retenir pour SNT :
- Le NMEA 0183 est textuel — il transmet des chaînes de caractères ASCII, lisibles à l'œil nu.
- Chaque message est une trame : une ligne autonome qui commence par
$et se termine par un saut de ligne. - Chaque trame contient des champs séparés par des virgules, comme un fichier CSV.
Anatomie d'une trame GGA
La trame la plus courante est la trame $GPGGA (Global Positioning
System Fix Data). Elle donne la position calculée à un instant précis. Voici
un exemple réel :
$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
À première vue, illisible. Mais l'œil s'y fait vite : il suffit de repérer les champs principaux que vous avez besoin de savoir reconnaître.
| Champ | Valeur | Signification |
|---|---|---|
| heure UTC | 123519 | 12h 35min 19s |
| latitude | 4807.038 | 48° 07,038′ |
| hémisphère | N | Nord |
| longitude | 01131.000 | 011° 31,000′ |
| méridien | E | Est |
C'est tout ce qu'il faut viser : être capable de pointer dans une trame où sont la latitude, l'hémisphère, la longitude, le méridien et l'heure. La trame contient d'autres informations techniques (qualité du fix, nombre de satellites, altitude, vérification…), regroupées dans la note ci-dessous pour qui veut creuser.
Dans la trame ci-dessus, à quelle heure UTC la position a-t-elle été mesurée ?
Du format NMEA aux degrés décimaux
Les coordonnées NMEA ne sont pas en degrés décimaux : elles sont au format
degrés-minutes (DDMM.MMMM pour la latitude, DDDMM.MMMM pour la
longitude). Pour les convertir en degrés décimaux, qu'utilisent la plupart des
applications de cartographie, on applique :
Reprenons la latitude 4807.038 :
- degrés entiers : 48
- minutes : 07,038
- conversion :
Et la longitude 01131.000 :
- degrés entiers : 011
- minutes : 31,000
- conversion :
La position décodée est donc : 48,1173° N, 11,5167° E — ce qui place le récepteur quelque part à Munich, en Allemagne.
48.1173, 11.5167 dans la barre de
recherche d'une carte en ligne. Le point bleu apparaîtra au cœur de la
capitale bavaroise.Décoder, à la main ou en pseudo-code
Décoder une trame, ce n'est rien d'autre que découper la chaîne autour des virgules puis lire les morceaux dans l'ordre. À la main, vous suivez la ligne du regard et vous numérotez les champs. En pseudo-code Python, l'idée s'écrit ainsi :
decouper_autour_des_virgules n'existe pas sous ce nom et n'est pas
exécutable telle quelle. En vraie syntaxe, on appellerait la méthode
split(",") sur la chaîne. Ce qui compte ici est la logique : « je
découpe, puis je lis le bon champ ».trame = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
champs = decouper_autour_des_virgules(trame)
heure = champs[1] # "123519"
latitude = champs[2] # "4807.038"
hemisphere = champs[3] # "N"
longitude = champs[4] # "01131.000"
meridien = champs[5] # "E"
Pour la conversion en degrés décimaux, le travail se fait en français aussi bien qu'en code :
On prend les deux premiers caractères de la latitude pour les degrés, puis tout ce qui suit pour les minutes. On applique la formule .
Sur 4807.038 : les deux premiers caractères sont 48, le reste est
07.038. Donc . Aucun outil informatique
particulier n'est requis pour faire ce calcul.
Si une trame indique une latitude de '5030.000' Nord, à combien de degrés décimaux cela correspond-il ?
Le checksum, gardien de l'intégrité
Vous avez peut-être remarqué le *47 à la fin de la trame. C'est un
checksum : un petit code de vérification qui permet au récepteur de
détecter une éventuelle corruption en transmission. Il est calculé par XOR de
tous les caractères entre $ et *, et écrit en hexadécimal.
En pratique, vous n'avez pas à le calculer à la main : les bibliothèques le font pour vous. Mais sa présence rappelle un principe général de tout protocole de communication numérique : on vérifie toujours qu'un message est arrivé intact avant de lui faire confiance.
D'autres types de trames
Le NMEA 0183 définit des dizaines de trames. Les plus courantes :
| Type | Rôle |
|---|---|
$GPGGA | Position complète avec altitude |
$GPRMC | Position + vitesse + cap |
$GPGSA | Satellites utilisés et qualité géométrique |
$GPGSV | Satellites visibles dans le ciel |
$GPVTG | Vitesse et direction de déplacement |
Un récepteur typique envoie plusieurs de ces trames par seconde. C'est à l'application qui les reçoit de choisir lesquelles décoder selon ses besoins.
Pour aller plus loin
La quasi-totalité des modules GPS « hobby » (par exemple le NEO-6M de u-blox, vendu une dizaine d'euros) sortent du NMEA 0183 sur leur port série. Branché à un Arduino ou à un Raspberry Pi, ce module permet de bricoler un traceur de position en quelques lignes de code — un excellent projet pour prolonger ce cours dans le chapitre sur l'informatique embarquée.