NMEA données structurées

NMEA : des données structurées

Principe et objectifs

Lors de séances précedentes, nous avons découvert que les programmes échangent des données structurées : Le logiciel qui met en forme les données issues des satellites GPS fabrique une trame NMEA, dans laquelle les différents champs sont séparés par une virgule.

Cette séparation des données s’appelle : comma separated value (csv).

Le format CSV permet d’organiser les informations dans un ordre précis. Dans chaque ligne, les données respectent le même ordre. Le fichier de données csv peut alors être facilement ouvert avec un tableur (ou être sauvegardé en .csv à partir d’un tableur : Un caractère particulier – généralement la virgule – permet au logiciel de placer les valeurs dans des colonnes différentes.

exemple de fichier csv

passage d'un tableau au fichier csv

Une librairie Python peut être utilisée pour découper ces informations et les mettre sous forme de liste.

librairies utilisées

  • folium (pour la creation de carte openstreetmap)
  • csv (apporte la fonction reader() pour le decoupage d’une ligne en csv.

Que contient la trame NMEA ?

Parmi les différentes lignes constituant la trame, l’une d’entre elles débute par les symboles $GPGGA

Les données sont mises dans un format particulier expliqué ici :

extrait trame NMEA

détail d'une trame NMEA

Les valeurs numériques recherchées sont celles de la latitude et de la longitude du lieu.

Ces valeurs sont mises sous la forme DDMM.MMMM :

lorsque l’on lit 4836.5375, la valeur doit être comprise comme : 48°36,5375'

c’est à dire :

  • 48°
  • 36,5375 minutes d’arc

Traitement des trames NMEA sous forme de chaine de caractère

Après l’ouverture du fichier avec l’instruction fichier = open('docs/nmea.log','r') :

Chaque ligne du fichier nmea.log est mise dans la liste lignes avec l’instruction lignes = fichier.readlines()

Pour acceder à la ligne n°2, par exemple, il faut écrire : lignes[1]

Le script suivant permet d’afficher tous les éléments de la liste lignes grâce à la boucle bornée for i,ligne in enumerate(lignes):

fichier = open('docs/nmea.log','r')

lignes = fichier.readlines() 
     
for i,ligne in enumerate(lignes):
                  
    if ligne !='\n':  #  tester si la ligne est non vide
        print('lignes['+ str(i)+'] \n'+ ligne)             
                
fichier.close()                    

Utiliser la librairie csv et manipuler une liste

la fonction reader de la librairie csv

executer le script suivant pour créer l’objet trames qui contient tous les éléments du fichier nmea.log

Repérer que la fonction utilisée reader() utilise pour paramètre le caractère ‘,’. On pourrait choisir d’autres types de séparateurs, comme ‘;’ par exemple. Mais ici, le fichier nmea.log utilise bien ‘,’.

manipulation d’une liste

La boucle bornée for row in trames: a pour rôle de créer une liste trame où chaque élément correspond à l’une des lignes du fichier nmea.log. Ces éléments sont ajoutés dans la liste trame à l’aide de la fonction append

Chaque élément trame[i] est lui même une liste. Ainsi, pour afficher un élément de la liste trame, il faut faire trame[i]. On peut également atteindre chaque élément de trame[i] en faisant trame[i][j].

from csv import reader
trame = []
trames = reader(lignes, delimiter=',')

for row in trames:
    trame.append(row)

à vous de jouer

  1. dans la cellule suivante, taper trame[0] pour afficher le premier élément de la liste trame (correspond à la première ligne du fichier nmea.log)
  2. dans la cellule qui suit, taper trame[0][0] pour afficher le premier champs de trame[0] : s’agit-il bien de $GPGSA, comme on peut le voir dans la liste des lignes de nmea.log ?
  3. Modifier alors le contenu des 2 cellules que vous venez d’utiliser pour accéder aux valeurs des coordonnées GPS latitude et longitude indiquées dans le fichier.
trame[]
trame[0][]

transformer la chaine de caractère en un nombre

La valeur retournée par trame[6][2] est de la forme : ‘4341.9791’, avec des guillemets. C’est une chaine de caractères, même si le contenu est constitué de chiffres. On ne pourra pas faire d’opérations mathématiques dessus, à moins de la transformer en nombre.

Il faut utiliser pour cela la fonction float() : par exemple : float(trame[6][2]) qui devrait renvoyer 4341.9791 : un nombre

Deiviser ce résultat par 100 pour avoir la partie entière qui sera constituée uniquement des degrés : trame[6][2]/100

Mettre le résultat dans la variable lat puis afficher le résulat : print(lat)

lat = float(trame[][])/100
print(lat)

Pour conserver les 2 premiers chiffres : extraire la partie entière avec la fonction int():

int(lat)

Mettre le résultat dans la variable lat_deg (la partie correspondant aux degrés entiers) puis afficher le résultat

lat_deg = 
print(lat_deg)

Retirer la partie entière de lat pour ne conserver que les nombres après la virgule : lat - lat_deg

Affecter le résultat à la variable lat_min et afficher le résultat

lat_min = 
print(lat_min)

Enfin : transformer lat_min en une valeur decimale : lat_min *100 /60 Affecter à nouveau le résultat à lat_min . Afficher lat_min

lat_min = 
print(lat_min)

Reconstituer la valeur numérique

  • affecter alors à la variable lat la nouvelle valeur lat_deg + lat_min

Afficher la valeur de lat

lat = 
print(lat)

Pour aller plus loin (partie facultative…mais conseillée)

Vous allez écrire une fonction appelée conversion_base10(latlon) qui devra transfromer la chaine de caractère extraite de la trame NMEA (chaine de caractères ‘ddmm.mmmm’) en une valeur décimale. Le traitement sera identique à celui que vous avez réalisé. On donne l’algorithme de cette fonction :

fonction conversion_base10(latlon):
    l ← flottant(latlon)/100
    l_deg ← entier(l)
    l_min ← l - l_deg
    l_min ← l_min*100/60
    l ← l_deg + l_min
    retourne l

Faire le même travail pour la longitude.

Créer les variables lon, lon_deg et lon_min à partir du fichier nmea.log. On pourra, au choix, utiliser la même procedure que pour lat, ou bien, utiliser la fonction conversion_base10()



Vérification

Pensez à vérifier vos résultats en affichant avec print() les valeurs de chacune des variables (lat, lon, lat_deg, lat_min…)

Lorsque vous êtes satisfaits du résultat, executez la cellule suivante pour afficher un marqueur sur la carte, qui sera géolocalisé à partir de votre latitude et longitude…

Remarque : lors de la permière execution du code suivant, il peut être utile d’enlever le # au debut de la première ligne afin de charger la librairie folium.

Remettre alors le commentaire pour les prochaines fois où vous executez ce code (il n’est pas necessaire de recharger cette librairie une deuxieme fois au cours de la même session)

#!pip install folium
import folium
carte = folium.Map(location=[lat,lon], zoom_start=10)
folium.Marker([lat,lon]).add_to(carte)
display(carte)