2008-11-22 3 views
12

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

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

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

$ 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, Вт, 3,02,24.5, 1000.23, M ,,, 0000 * 1F $ GPRMC, 044584,936, 1276,5539, N, 88734,1543, Е , 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

Вот некоторые дополнительные пояснение данных:

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

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

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

+0

Кстати, ваш $ GPRMC линия не кажется, чтобы соответствовать стандарту. http://home.mira.net/~gnb/gps/nmea.html#gprmc Я что-то упустил? – 2008-11-22 21:04:29

+0

Спасибо, что указал, что Федерико. Я обязательно посмотрю на это. – crashsystems 2008-11-22 21:06:11

+0

Кажется, это больше $ GPGGA линии. – 2008-11-22 21:14:08

ответ

15

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

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

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

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

Ха, это было бы похоже на то, чтобы я выбрал более сложное решение! – crashsystems 2008-11-22 20:59:02

4

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

Ваш паштет выглядит так, будто в нем есть некоторые коррумпированные линии. Вот простая проверка, она предполагает, что строка начинается с $ и не имеет 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 
5

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

я бросил, что данные выборки у вас есть в/вар/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 

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

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

Если вам нужно сделать некоторые более обширный анализ ваших потоков данных GPS, здесь Pyparsing решение, которое разбивает ваши данные в имени полей данных. Я извлек свои pastebin'ned данные в файл 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 записи в вашем Pastebin, кажется, не совсем совпадают с теми, которые вы включили в вашу должность , но вы должны иметь возможность при необходимости отрегулировать этот пример.

1

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

Мой фикс не полностью точны, но Я согласен с тем, что до 70-х годов никаких данных GPS не существовало.

В функции Защиты синтаксического анализа для 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]) 

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

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

3

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