Pregunta

Tengo un amigo que está terminando su maestría en ingeniería aeroespacial. Para su proyecto final, está en un pequeño equipo encargado de escribir un programa para rastrear globos meteorológicos, cohetes y satélites. El programa recibe información de un dispositivo GPS, realiza cálculos con los datos y utiliza los resultados de esos cálculos para controlar una serie de motores diseñados para orientar una antena de comunicación direccional, de modo que el globo, el cohete o el satélite siempre permanezcan enfocados.

Aunque soy un principiante (eterno), tengo más experiencia en programación que mi amigo. Entonces, cuando me pidió consejo, lo convencí de que escribiera el programa en Python, mi idioma de elección.

En este punto del proyecto, estamos trabajando en el código que analiza la entrada del dispositivo GPS. Aquí hay un ejemplo de entrada, con los datos que necesitamos extraer en negrita:

$ 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

Aquí hay una explicación más detallada de los datos:

  

" Parece que necesitaré cinco cosas   fuera de cada línea. Y tener en cuenta   que cualquiera de estas áreas puede ser   vacío. Lo que significa que solo habrá dos   comas una al lado de la otra. Tal   como ',,,' Hay dos campos que pueden   estar lleno en cualquier momento. Solo algunos de ellos   tener dos o tres opciones que ellos   puede ser, pero no creo que deba ser   contando con eso. "

Hace dos días, mi amigo pudo obtener el registro completo del receptor GPS utilizado para rastrear un lanzamiento reciente de un globo meteorológico. Los datos son bastante largos, así que lo puse todo en este pastebin .

Todavía soy bastante nuevo con expresiones regulares, así que estoy buscando ayuda.

¿Fue útil?

Solución

dividir debería hacer el truco. Aquí también hay una buena manera de extraer los datos:

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

Otros consejos

Es más sencillo usar split que una expresión regular.

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

Esos son valores separados por comas, por lo que usar una biblioteca csv es la solución más fácil.

Lancé esos datos de muestra que tienes en / var / tmp / sampledata, luego hice esto:

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

Puede procesar los datos como lo desee. Parece un poco extraño con el '**' al principio y al final de algunos de los valores, es posible que desee quitar esas cosas, puede hacer:

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

Tendrá que emitir algunos valores como flotantes. Entonces, por ejemplo, el tercer valor en la primera línea de datos de muestra es:

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

También debe verificar primero la suma de verificación de los datos. Se calcula haciendo XOR de los caracteres entre $ y * (sin incluirlos) y comparándolo con el valor hexadecimal al final.

Parece que su pastebin tiene algunas líneas corruptas. Aquí hay una verificación simple, se supone que la línea comienza con $ y no tiene CR / LF al final. Para construir un analizador más robusto, debe buscar el '$' y trabajar a través de la cadena hasta presionar el '*'.

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

Puede usar una biblioteca como pynmea2 para analizar el 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)

Descargo de responsabilidad: soy el autor de pynmea2

Si necesita hacer un análisis más extenso de sus flujos de datos GPS, aquí hay una solución de análisis que divide sus datos en campos de datos con nombre. Extraje sus datos pegados en un archivo gpsstream.txt, y los analicé con lo siguiente:

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

Los registros $ GPRMC en su pastebin no parecen coincidir con los que incluyó en su publicación, pero debería poder ajustar este ejemplo según sea necesario.

Sugiero una pequeña corrección en su código porque si se usa para analizar datos del siglo anterior, la fecha se ve en algún momento en el futuro (por ejemplo, 2094 en lugar de 1994)

Mi solución no es completamente precisa, pero tomo la posición de que antes de los años 70 no existían datos de GPS.

En la función de análisis de def para las oraciones RMC, simplemente reemplace la línea de formato por:

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

Esto examinará los dos dígitos yy del año y supondrá que el año pasado 70 estamos tratando con oraciones del siglo anterior. Podría hacerse mejor comparando con la fecha de hoy y suponiendo que cada vez que maneje algunos datos en el futuro, en realidad son del siglo pasado

Gracias por todos los códigos que proporcionó anteriormente ... Me divertí un poco con esto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top