cours1 min de lecture

Décoder une trame NMEA

La forme numérique brute d'une position GPS, et comment en extraire latitude et longitude.
programme

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.
Bien que conçu pour la marine, le NMEA 0183 est devenu le langage commun de quasiment tous les récepteurs GNSS du monde. C'est ce que produisent les modules GPS qu'on trouve dans les Arduino, les drones, les anciens GPS de voiture, et même beaucoup de smartphones en interne.

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.

ChampValeurSignification
heure UTC12351912h 35min 19s
latitude4807.03848° 07,038′
hémisphèreNNord
longitude01131.000011° 31,000′
méridienEEst

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 :

degreˊs deˊcimaux=degreˊs entiers+minutes60\text{degrés décimaux} = \text{degrés entiers} + \frac{\text{minutes}}{60}

Reprenons la latitude 4807.038 :

  • degrés entiers : 48
  • minutes : 07,038
  • conversion : 48+7,03860=48,1173°48 + \dfrac{7{,}038}{60} = 48{,}1173°

Et la longitude 01131.000 :

  • degrés entiers : 011
  • minutes : 31,000
  • conversion : 11+31,00060=11,5167°11 + \dfrac{31{,}000}{60} = 11{,}5167°

La position décodée est donc : 48,1173° N, 11,5167° E — ce qui place le récepteur quelque part à Munich, en Allemagne.

Pour vérifier rapidement, copiez 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 :

Le bloc ci-dessous est du pseudo-code Python : la fonction 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 D+M/60D + M/60.

Sur 4807.038 : les deux premiers caractères sont 48, le reste est 07.038. Donc 48+7,038/60=48,1173°48 + 7{,}038/60 = 48{,}1173°. 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 :

TypeRôle
$GPGGAPosition complète avec altitude
$GPRMCPosition + vitesse + cap
$GPGSASatellites utilisés et qualité géométrique
$GPGSVSatellites visibles dans le ciel
$GPVTGVitesse 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.

Le format NMEA est ancien et présente plusieurs limites : précision réduite (quelques chiffres après la virgule), pas de chiffrement, débit faible (4800 bauds par défaut). Les équipements modernes utilisent souvent NMEA 2000 (binaire, plus rapide) ou des protocoles propriétaires, mais NMEA 0183 reste la lingua franca pour l'apprentissage et les modules grand public.

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.