13

航空宇宙工学の修士号を取得しようとしている友人がいます。彼の最後のプロジェクトでは、気象観測気球、ロケット、人工衛星を追跡するためのプログラムを作成する小さなチームに所属しています。このプログラムは、GPS デバイスから入力を受け取り、データを使用して計算を行い、それらの計算の結果を使用して、指向性通信アンテナの向きを変えるように設計された一連のモーターを制御します。これにより、気球、ロケット、または衛星は常に焦点を合わせたままになります。

私自身はやや(永遠の)初心者ですが、友人よりもプログラミングの経験が豊富です。それで、彼が私にアドバイスを求めたとき、私は自分が選んだ言語である 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,W ,3,02,24.5,3 10 ,,,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,6,1,0 02.2, 25722.5 ,M,,,*00 $GPRMC,066487.954, 4572.0089,S,45572.3345,W ,3,09,15.0, 35000.00 ,M,,,*1F

データの詳細な説明は次のとおりです。

「すべての行から 5 つのものが必要になるようです。そして、これらの領域のいずれかが空である可能性があることに注意してください。つまり、2 つのコンマが隣り合っているだけです。「,,,」などがあります。いつでも満員になる可能性のある 2 つのフィールドがあります。2 つまたは 3 つのオプションしかないフィールドもありますが、それを当てにするべきではないと思います。」

2 日前、私の友人は、最近の気象気球の打ち上げを追跡するために使用された GPS 受信機から完全なログを取得することができました。データがかなり長いので、このペーストビンに全部入れました。

私はまだ正規表現に慣れていないので、助けを求めています。

4

8 に答える 8

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)
于 2008-11-22T21:00:01.883 に答える
8

NMEA ログの解析には、pynmea2などのライブラリを使用できます。

>>> 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の作者です

于 2014-05-30T06:11:12.547 に答える
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 ']
>>> 
于 2008-11-22T20:54:18.277 に答える
5

これらはコンマ区切りの値であるため、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

いくつかの値をfloatとしてキャストする必要があります。したがって、たとえば、サンプルデータの最初の行の3番目の値は次のとおりです。

>> data = '**4250.5589'
>> print float(data.strip('*'))
4250.5589
于 2008-11-24T04:49:39.190 に答える
4

また、最初にデータのチェックサムを確認する必要があります。これは、$ と * の間の文字 (それらを含まない) を XOR し、最後の 16 進数値と比較することによって計算されます。

ペーストビンに破損した行が含まれているようです。これは簡単なチェックです。行が $ で始まり、最後に 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
于 2008-11-22T23:09:34.040 に答える
2

GPS データ ストリームをさらに広範囲に分析する必要がある場合は、データを名前付きデータ フィールドに分割する pyparsing ソリューションを次に示します。ペーストビン化されたデータをファイル 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 レコードは、投稿に含まれているものと完全には一致していないようですが、必要に応じてこの例を調整できるはずです。

于 2011-03-05T10:04:34.883 に答える
1

コードを少し修正することをお勧めします。前世紀のデータを解析するために使用すると、日付が将来のように見えるためです (たとえば、1994 年ではなく 2094 年)。

私の修正は完全に正確ではありませんが、70 年代以前は GPS データが存在しなかったという立場をとっています。

RMC センテンスの def parse 関数で、フォーマット行を次のように置き換えます。

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

これは、年の 2 桁の yy を見て、過去 70 年に前世紀の文を扱っていると仮定します。今日の日付と比較して、将来のデータを処理するたびに、実際には前世紀のものであると想定することで、より適切に実行できます。

上記で提供されたすべてのコードに感謝します...私はこれを楽しんでいました。

于 2011-08-27T13:28:45.853 に答える
0

これはGPRMC文字列です。文字列を分割したら、緯度と経度の値を解析する必要があります。

line = "$GPRMC,199304.973,3248.7780,N,11355.7832,W,1,06,02.2,25722.5,M,,,*00"
line = line.split(",")

緯度経度部分([..., '3248.7780', 'N', '11355.7832, 'W', ...]):

  • 最初の数値は純粋な数値ではなく、文字列のように連結された数値です。つまり、度、分(緯度)3248.7780を指します3248.7780
  • 2 番目の数字 ( 11355.7832) は113、度、55.7832分 (経度)を表します。

そのまま式に使用することはできません。これらは 10 進数に変換する必要があります。

def toDD(s):
    d = float(s[:-7])
    m = float(s[-7:]) / 60
    return d + m

lat_lon = (toDD(line[2]), line[3], toDD(line[4]), line[5])
print(lat_lon)

# (32.81296666666667, 'N', 113.92972, 'W')
于 2022-02-20T20:56:12.167 に答える