Разбор вывода GPS-приемника через регулярное выражение в Python - PullRequest
12 голосов
/ 22 ноября 2008

У меня есть друг, который заканчивает магистратуру в аэрокосмической технике. Для своего финального проекта он в небольшой команде, которой поручено написать программу для отслеживания метеозондов, ракет и спутников. Программа получает данные от устройства GPS, выполняет расчеты с данными и использует результаты этих вычислений для управления серией двигателей, предназначенных для ориентации антенны направленной связи, поэтому воздушный шар, ракета или спутник всегда остаются в фокусе. *

Хотя я и являюсь чем-то вроде (вечного) новичка, у меня больше опыта программирования, чем у моего друга. Поэтому, когда он попросил у меня совета, я убедил его написать программу на языке Python, который я выбрал.

На данный момент в проекте мы работаем над кодом, который анализирует входные данные с устройства GPS. Вот несколько примеров ввода с данными, которые нам нужно извлечь жирным шрифтом:

$ GPRMC, 092204,999, 4250,5589, S, 14718,5084, Е , 1,12,24.4, 89,6 , М ,,, 0000 * 1F $ GPRMC, 093345.679, 4234,7899, N, 11344,2567, Вт , 3,02,24.5, 1000,23 , М ,,, 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, Вт , 1,06,02.2, * * 25722,5 тысяча двадцать-один , М ,,, * 00 $ GPRMC, 066487.954, 4572,0089, S, 45572,3345, Вт , 3,09,15.0, * 1 025 * 35000,00 , М ,,, * 1F * +1027 *

Вот дальнейшее объяснение данных:

"Похоже, мне понадобятся пять вещей из каждой строки. И имейте ввиду что любая из этих областей может быть пустой. То есть будет только два запятые прямо рядом друг с другом. такие как ',,,' Есть два поля, которые могут быть полным в любое время. Только некоторые из них есть два или три варианта, которые они может быть, но я не думаю, что я должен быть рассчитывая на это. "

Два дня назад мой друг смог получить полный журнал с GPS-приемника, который использовался для отслеживания недавнего запуска метеозонда. Данные довольно длинные, поэтому я положил все это в эту вставку .

Я все еще довольно новичок с регулярными выражениями, поэтому я ищу некоторую помощь.

Ответы [ 7 ]

15 голосов
/ 23 ноября 2008

расщепление должно сделать свое дело. Вот хороший способ извлечения данных:

>>> 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)
8 голосов
/ 22 ноября 2008

Проще использовать split, чем регулярное выражение.

>>> 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 ']
>>> 
5 голосов
/ 24 ноября 2008

Это значения, разделенные запятыми, поэтому проще всего использовать библиотеку csv.

Я бросил эти данные сэмпла в / var / tmp / sampledata, затем сделал это:

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

Затем вы можете обрабатывать данные по своему усмотрению. Это выглядит немного странно с '**' в начале и в конце некоторых значений, вы можете удалить это, вы можете сделать:

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

Вам придется разыгрывать некоторые значения как плавающие. Так, например, 3-е значение в первой строке данных образца:

>> data = '**4250.5589'
>> print float(data.strip('*'))
4250.5589
4 голосов
/ 23 ноября 2008

Вы также должны сначала проверить контрольную сумму данных. Он рассчитывается путем XOR символов между $ и * (не включая их) и сравнения его с шестнадцатеричным значением в конце.

Похоже, ваша вставка содержит несколько искаженных строк. Вот простая проверка, она предполагает, что строка начинается с $ и не имеет CR / LF в конце. Чтобы создать более надежный синтаксический анализатор, вам нужно найти «$» и работать со строкой, пока не будет нажата «*».

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
3 голосов
/ 30 мая 2014

Вы можете использовать библиотеку типа pynmea2 для анализа журнала 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)

Отказ от ответственности: я являюсь автором pynmea2

2 голосов
/ 05 марта 2011

Если вам необходимо провести более подробный анализ ваших потоков данных GPS, вот решение для разбора данных, которое разбивает ваши данные на именованные поля данных. Я извлек ваши данные, вставленные в файл pastebin, в файл gpsstream.txt и проанализировал их следующим образом:

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

Записи $ GPRMC в вашей папке для вставки не совсем совпадают с записями, которые вы включили в ваше сообщение, но вы должны быть в состоянии настроить этот пример по мере необходимости.

1 голос
/ 27 августа 2011

Я предлагаю небольшое исправление в вашем коде, потому что, если использовать его для анализа данных прошлого века, дата будет выглядеть как-нибудь в будущем (например, 2094 вместо 1994)

Мое исправление не совсем точное, но я придерживаюсь позиции, что до 70-х годов данных GPS не существовало.

В функции def parse для предложений RMC просто замените строку формата на:

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

Здесь мы рассмотрим две yy цифры года и предположим, что в прошлом году 70 мы имеем дело с предложениями из прошлого века. Лучше сделать это, сравнив с сегодняшней датой и предположив, что каждый раз, когда вы будете иметь дело с некоторыми данными в будущем, они на самом деле относятся к прошлому веку

Спасибо за все приведенные выше фрагменты кода ... Мне было весело с этим.

...