Domanda

Ho un amico che sta finendo il suo master in ingegneria aerospaziale. Per il suo progetto finale, fa parte di un piccolo team incaricato di scrivere un programma per tracciare palloni meteorologici, razzi e satelliti. Il programma riceve input da un dispositivo GPS, esegue calcoli con i dati e utilizza i risultati di tali calcoli per controllare una serie di motori progettati per orientare un'antenna di comunicazione direzionale, quindi il pallone, il razzo o il satellite rimangono sempre a fuoco.

Sebbene io stesso sia un principiante (eterno), ho più esperienza di programmazione del mio amico. Quindi, quando mi ha chiesto un consiglio, l'ho convinto a scrivere il programma in Python, la mia lingua preferita.

A questo punto del progetto, stiamo lavorando sul codice che analizza l'input dal dispositivo GPS. Ecco alcuni input di esempio, con i dati che dobbiamo estrarre in grassetto:

$ 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, 1.000,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

Ecco qualche ulteriore spiegazione dei dati:

  

" Sembra che avrò bisogno di cinque cose   fuori da ogni linea. E tenere a mente   che una di queste aree potrebbe essere   vuoto. Significa che ce ne saranno solo due   virgole una accanto all'altra. Come   come ',,,' Ci sono due campi che possono   essere pieno in qualsiasi momento. Solo alcuni   hanno due o tre opzioni che hanno   potrebbe essere, ma non credo che dovrei esserlo   contando su questo. "

Due giorni fa il mio amico è stato in grado di acquisire il registro completo dal ricevitore GPS utilizzato per tracciare un recente lancio del pallone meteorologico. I dati sono piuttosto lunghi, quindi ho inserito tutto in questo pastebin .

Sono ancora piuttosto nuovo con le espressioni regolari, quindi cerco un po 'di assistenza.

È stato utile?

Soluzione

la divisione dovrebbe fare il trucco. Ecco un buon modo per estrarre anche i dati:

>>> 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)

Altri suggerimenti

È più semplice usare split di una 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 ']
>>> 

Questi sono valori separati da virgola, quindi l'uso di una libreria csv è la soluzione più semplice.

Ho gettato quei dati campione che hai in / var / tmp / sampledata, quindi ho fatto questo:

>>> 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']

È quindi possibile elaborare i dati come desiderato. Sembra un po 'strano con il' ** 'all'inizio e alla fine di alcuni dei valori, potresti voler eliminare quella roba, puoi fare:

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

Dovrai lanciare alcuni valori come float. Ad esempio, il terzo valore sulla prima riga di dati di esempio è:

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

Dovresti anche prima controllare il checksum dei dati. Viene calcolato XORing i caratteri tra $ e * (non includendoli) e confrontandolo con il valore esadecimale alla fine.

Sembra che il tuo pastebin abbia alcune linee corrotte. Ecco un semplice controllo, presuppone che la riga inizi con $ e non abbia CR / LF alla fine. Per creare un parser più robusto devi cercare '$' e lavorare attraverso la stringa fino a colpire '*'.

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

È possibile utilizzare una libreria come pynmea2 per l'analisi del registro 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)

Disclaimer: sono l'autore di pynmea2

Se hai bisogno di fare qualche analisi più approfondita dei tuoi flussi di dati GPS, ecco una soluzione pyparsing che suddivide i tuoi dati in campi di dati con nome. Ho estratto i dati incollati in un file gpsstream.txt e li ho analizzati con il seguente:

"""
 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

I record $ GPRMC nel tuo filebin non sembrano corrispondere perfettamente a quelli che hai incluso nel tuo post, ma dovresti essere in grado di adattare questo esempio se necessario.

Suggerisco una piccola correzione nel codice perché, se utilizzata per analizzare i dati del secolo precedente, la data apparirà in futuro (ad esempio 2094 anziché 1994)

La mia correzione non è completamente precisa, ma prendo la posizione che prima degli anni '70 non esistevano dati GPS.

Nella funzione def parse per le frasi RMC basta sostituire la riga del formato con:

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])

Questo esaminerà le due cifre dell'anno e supponiamo che l'anno passato 70 abbiamo a che fare con frasi del secolo precedente. Si potrebbe fare meglio confrontandolo con la data odierna e supponendo che ogni volta che si tratterà di alcuni dati in futuro, essi sono in effetti risalenti al secolo scorso

Grazie per tutte le parti di codice fornite sopra ... Mi sono divertito un po 'con questo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top