Question

J'ai un ami qui termine sa maîtrise en génie aérospatial. Pour son dernier projet, il fait partie d'une petite équipe chargée de rédiger un programme de suivi des ballons météo, des fusées et des satellites. Le programme reçoit les données d’un appareil GPS, effectue des calculs avec les données et utilise ces résultats pour contrôler une série de moteurs conçus pour orienter une antenne de communication directionnelle de sorte que le ballon, la fusée ou le satellite reste toujours au point.

Même si je suis moi-même un débutant (éternel), j’ai plus d’expérience en programmation que mon ami. Alors, quand il m'a demandé conseil, je l'ai convaincu d'écrire le programme en Python, ma langue de prédilection.

À ce stade du projet, nous travaillons sur le code qui analyse l’entrée du périphérique GPS. Voici un exemple d’entrée, avec les données à extraire en gras:

$ GPRMC, 092204.999, 4250.5589, S, 14718.5084, E , 1,12,24,4, 89,6 , M ,,, 0000 * 1F $ GPRMC, 093345.679, 4234.7899, N, 11344.2567, W , 3,02,24,5, 1000.23 , M ,,, 0000 * 1F $ GPRMC, 044584.936, 1276.5539, N, 88734.1543, E , 2,04,33,5, 600.323 , M ,,, * 00 $ GPRMC, 199304.973, 3248.7780, N, 11355,7832, W , 1,06,02,2, 25722,5 , M ,,, * 00 $ GPRMC, 066487.954, 4572.0089, S, 45572.3345, W , 3,09,15.0, 35000,00 , M ,,, * 1F

Voici quelques explications supplémentaires sur les données:

  

"On dirait que j'ai besoin de cinq choses   hors de chaque ligne. Et gardez à l'esprit   que l'un de ces domaines peut être   vide. Ce qui signifie qu'il n'y aura que deux   virgules juste à côté de l'autre. Tel   comme ',,,' Il y a deux champs qui peuvent   être complet à tout moment. Certains d'entre eux seulement   ont deux ou trois options qu'ils   peut être mais je ne pense pas que je devrais être   comptant sur cela. "

Il y a deux jours, mon ami a pu obtenir le journal complet du récepteur GPS utilisé pour suivre un lancement récent d'un ballon météo. Les données sont assez longues, je les ai donc toutes répertoriées dans cette pastebin .

Moi-même, je suis encore un peu nouveau avec des expressions régulières, alors je cherche de l'aide.

Était-ce utile?

La solution

la scission devrait faire l'affaire. Voici un bon moyen d'extraire les données:

>>> line = "$GPRMC,199304.973,3248.7780,N,11355.7832,W,1,06,02.2,25722.5,M,,,*00"
>>> line = line.split(",")
>>> neededData = (float(line[2]), line[3], float(line[4]), line[5], float(line[9]))
>>> print neededData
(3248.7779999999998, 'N', 11355.7832, 'W', 25722.5)

Autres conseils

Il est plus simple d'utiliser split qu'un regex.

>>> line="$GPRMC,092204.999,4250.5589,S,14718.5084,E,1,12,24.4,89.6,M,,,0000*1F "
>>> line.split(',')
['$GPRMC', '092204.999', '4250.5589', 'S', '14718.5084', 'E', '1', '12', '24.4', '89.6', 'M', '', '', '0000*1F ']
>>> 

Ce sont des valeurs séparées par des virgules. L’utilisation d’une bibliothèque csv est donc la solution la plus simple.

J'ai jeté votre échantillon de données dans / var / tmp / sampledata, puis je l'ai fait:

>>> import csv
>>> for line in csv.reader(open('/var/tmp/sampledata')):
...   print line
['$GPRMC', '092204.999', '**4250.5589', 'S', '14718.5084', 'E**', '1', '12', '24.4', '**89.6**', 'M', '', '', '0000\\*1F']
['$GPRMC', '093345.679', '**4234.7899', 'N', '11344.2567', 'W**', '3', '02', '24.5', '**1000.23**', 'M', '', '', '0000\\*1F']
['$GPRMC', '044584.936', '**1276.5539', 'N', '88734.1543', 'E**', '2', '04', '33.5', '**600.323**', 'M', '', '', '\\*00']
['$GPRMC', '199304.973', '**3248.7780', 'N', '11355.7832', 'W**', '1', '06', '02.2', '**25722.5**', 'M', '', '', '\\*00']
['$GPRMC', '066487.954', '**4572.0089', 'S', '45572.3345', 'W**', '3', '09', '15.0', '**35000.00**', 'M', '', '', '\\*1F']

Vous pouvez ensuite traiter les données comme vous le souhaitez. Cela semble un peu étrange avec le '**' au début et à la fin de certaines valeurs, vous pouvez supprimer ces informations, vous pouvez le faire:

>> eastwest = 'E**'
>> eastwest = eastwest.strip('*')
>> print eastwest
E

Vous devrez transtyper certaines valeurs en tant que flottants. Ainsi, par exemple, la 3ème valeur sur la première ligne de données exemple est:

>> data = '**4250.5589'
>> print float(data.strip('*'))
4250.5589

Vous devriez également d'abord vérifier la somme de contrôle des données. Il est calculé en comparant les caractères compris entre $ et * (sans les inclure) et en les comparant à la valeur hexadécimale à la fin.

Votre pastebin semble contenir des lignes corrompues. Voici un contrôle simple, il suppose que la ligne commence par $ et n’a pas de CR / LF à la fin. Pour construire un analyseur syntaxique plus robuste, vous devez rechercher le '$' et parcourir la chaîne jusqu'à atteindre le '*'.

def check_nmea0183(s):
    """
    Check a string to see if it is a valid NMEA 0183 sentence
    """
    if s[0] != ':
        return False
    if s[-3] != '*':
        return False

    checksum = 0
    for c in s[1:-3]:
        checksum ^= ord(c)

    if int(s[-2:],16) != checksum:
        return False

    return True

Vous pouvez utiliser une bibliothèque telle que pynmea2 pour analyser le journal NMEA.

>>> import pynmea2
>>> msg = pynmea2.parse('$GPGGA,142927.829,2831.4705,N,08041.0067,W,1,07,1.0,7.9,M,-31.2,M,0.0,0000*4F')
>>> msg.timestamp, msg.latitude, msg.longitude, msg.altitude
(datetime.time(14, 29, 27), 28.524508333333333, -80.683445, 7.9)

Avertissement: je suis l'auteur de pynmea2

Si vous avez besoin d'analyses plus approfondies de vos flux de données GPS, voici une solution personnalisée qui décompose vos données en champs de données nommés. J'ai extrait vos données pastebin'ned dans un fichier gpsstream.txt et l'ai analysé comme suit:

"""
 Parse NMEA 0183 codes for GPS data
 http://en.wikipedia.org/wiki/NMEA_0183

 (data formats from http://www.gpsinformation.org/dale/nmea.htm)
"""
from pyparsing import *

lead = "<*>quot;
code = Word(alphas.upper(),exact=5)
end = "*"
COMMA = Suppress(',')
cksum = Word(hexnums,exact=2).setParseAction(lambda t:int(t[0],16))

# define basic data value forms, and attach conversion actions
word = Word(alphanums)
N,S,E,W = map(Keyword,"NSEW")
integer = Regex(r"-?\d+").setParseAction(lambda t:int(t[0]))
real = Regex(r"-?\d+\.\d*").setParseAction(lambda t:float(t[0]))
timestamp = Regex(r"\d{2}\d{2}\d{2}\.\d+")
timestamp.setParseAction(lambda t: t[0][:2]+':'+t[0][2:4]+':'+t[0][4:])
def lonlatConversion(t):
    t["deg"] = int(t.deg)
    t["min"] = float(t.min)
    t["value"] = ((t.deg + t.min/60.0) 
                    * {'N':1,'S':-1,'':1}[t.ns] 
                    * {'E':1,'W':-1,'':1}[t.ew])
lat = Regex(r"(?P<deg>\d{2})(?P<min>\d{2}\.\d+),(?P<ns>[NS])").setParseAction(lonlatConversion)
lon = Regex(r"(?P<deg>\d{3})(?P<min>\d{2}\.\d+),(?P<ew>[EW])").setParseAction(lonlatConversion)

# define expression for a complete data record
value = timestamp | Group(lon) | Group(lat) | real | integer | N | S | E | W | word
item = lead + code("code") + COMMA + delimitedList(Optional(value,None))("datafields") + end + cksum("cksum")


def parseGGA(tokens):
    keys = "time lat lon qual numsats horiz_dilut alt _ geoid_ht _ last_update_secs stnid".split()
    for k,v in zip(keys, tokens.datafields):
        if k != '_':
            tokens[k] = v
    #~ print tokens.dump()

def parseGSA(tokens):
    keys = "auto_manual _3dfix prn prn prn prn prn prn prn prn prn prn prn prn pdop hdop vdop".split()
    tokens["prn"] = []
    for k,v in zip(keys, tokens.datafields):
        if k != 'prn':
            tokens[k] = v
        else:
            if v is not None:
                tokens[k].append(v)
    #~ print tokens.dump()

def parseRMC(tokens):
    keys = "time active_void lat lon speed track_angle date mag_var _ signal_integrity".split()
    for k,v in zip(keys, tokens.datafields):
        if k != '_':
            if k == 'date' and v is not None:
                v = "%06d" % v
                tokens[k] = '20%s/%s/%s' % (v[4:],v[2:4],v[:2])
            else:
                tokens[k] = v
    #~ print tokens.dump()


# process sample data
data = open("gpsstream.txt").read().expandtabs()

count = 0
for i,s,e in item.scanString(data):
    # use checksum to validate input 
    linebody = data[s+1:e-3]
    checksum = reduce(lambda a,b:a^b, map(ord, linebody))
    if i.cksum != checksum:
        continue
    count += 1

    # parse out specific data fields, depending on code field
    fn = {'GPGGA' : parseGGA, 
          'GPGSA' : parseGSA,
          'GPRMC' : parseRMC,}[i.code]
    fn(i)

    # print out time/position/speed values
    if i.code == 'GPRMC':
        print "%s %8.3f %8.3f %4d" % (i.time, i.lat.value, i.lon.value, i.speed or 0) 


print count

Les enregistrements $ GPRMC dans votre pastebin ne semblent pas vraiment correspondre à ceux que vous avez inclus dans votre message, mais vous devriez pouvoir ajuster cet exemple si nécessaire.

Je suggère un petit correctif dans votre code car s'il est utilisé pour analyser des données du siècle précédent, la date a une date ultérieure (par exemple, 2094 au lieu de 1994)

Mon correctif n’est pas tout à fait exact, mais j’assume qu’aucune donnée GPS n’existait avant les années 70.

Dans la fonction def parse des phrases RMC, remplacez simplement la ligne de format par:

p = int(v[4:])
print "p = ", p
if p > 70:
    tokens[k] = '19%s/%s/%s' % (v[4:],v[2:4],v[:2])
else:
    tokens[k] = '20%s/%s/%s' % (v[4:],v[2:4],v[:2])

Ceci examinera les deux chiffres de l'année et supposera que l'année passée, nous avons affaire à des phrases du siècle précédent. Cela pourrait être mieux fait en comparant avec la date d'aujourd'hui et en supposant que chaque fois que vous traitez des données à l'avenir, celles-ci datent en fait du siècle dernier

Merci pour tous les morceaux de code que vous avez fournis ci-dessus ... Je me suis amusé avec cela.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top