通过 Python 中的正则表达式解析 GPS 接收器输出

发布于 2024-07-09 10:45:33 字数 1253 浏览 15 评论 0原文

我有一个朋友正在完成航空航天工程硕士学位。 在他的期末项目中,他所在的小团队负责编写跟踪气象气球、火箭和卫星的程序。 该程序接收来自 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,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

以下是对数据的一些进一步解释:

“我看起来需要五样东西 从每一行中。 并牢记 这些区域中的任何一个都可能是 空的。 这意味着只有两个 逗号彼此相邻。 这样的 as ',,,' 有两个字段可以 随时都可以满。 仅其中一些 他们有两个或三个选择 可能是,但我认为我不应该是 指望这一点。”

,我的朋友能够从用于跟踪最近气象气球发射的 GPS 接收器获取完整的日志。数据相当长,所以我将其全部放在 这个pastebin

我自己对正则表达式还很陌生,所以我正在寻求一些帮助。

I have a friend who is finishing up his masters degree in aerospace engineering. For his final project, he is on a small team tasked with writing a program for tracking weather balloons, rockets and satellites. The program receives input from a GPS device, does calculations with the data, and uses the results of those calculations to control a series of motors designed to orientate a directional communication antenna, so the balloon, rocket or satellite always stays in focus.

Though somewhat of a (eternal) beginner myself, I have more programming experience than my friend. So when he asked me for advice, I convinced him to write the program in Python, my language of choice.

At this point in the project, we are working on the code that parses the input from the GPS device. Here is some example input, with the data we need to extract in bold:

$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

Here is some further explanation of the data:

"I looks like I'll need five things
out of every line. And bear in mind
that any one of these area's may be
empty. Meaning there will be just two
commas right next to each other. Such
as ',,,' There are two fields that may
be full at any time. Some of them only
have two or three options that they
may be but I don't think I should be
counting on that."

Two days ago my friend was able to acquire the full log from the GPS receiver used to track a recent weather balloon launch. The data is quite long, so I put it all in this pastebin.

I am still rather new with regular expressions myself, so I am looking for some assistance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

三寸金莲 2024-07-16 10:45:34

分裂应该可以解决问题。 这也是提取数据的好方法:

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

splitting should do the trick. Here's a good way to extract the data, as well:

>>> 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)
宁愿没拥抱 2024-07-16 10:45:34

您可以使用 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 的作者

You could use a library like pynmea2 for parsing the NMEA log.

>>> 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: I am the author of pynmea2

暖阳 2024-07-16 10:45:34

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

It's simpler to use split than a 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 ']
>>> 
三月梨花 2024-07-16 10:45:34

这些是逗号分隔值,因此使用 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

您必须将某些值转换为浮点数。 例如,样本数据第一行的第三个值是:

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

Those are comma separated values, so using a csv library is the easiest solution.

I threw that sample data you have into /var/tmp/sampledata, then I did this:

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

You can then process the data however you wish. It looks a little odd with the '**' at the start and end of some of the values, you might want to strip that stuff off, you can do:

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

You will have to cast some values as floats. So for example, the 3rd value on the first line of sample data is:

>> data = '**4250.5589'
>> print float(data.strip('*'))
4250.5589
夏末的微笑 2024-07-16 10:45:34

您还应该首先检查数据的校验和。 它是通过对 $ 和 * 之间的字符(不包括它们)进行异或并将其与末尾的十六进制值进行比较来计算的。

你的pastebin看起来有一些损坏的行。 这是一个简单的检查,它假设该行以 $ 开头,并且末尾没有 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

You should also first check the checksum of the data. It is calculated by XORing the characters between the $ and the * (not including them) and comparing it to the hex value at the end.

Your pastebin looks like it has some corrupt lines in it. Here is a simple check, it assumes that the line starts with $ and has no CR/LF at the end. To build a more robust parser you need to search for the '$' and work through the string until hitting the '*'.

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
叫嚣ゝ 2024-07-16 10:45:34

这是一个 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 指的是 32 度,48.7780 分钟(纬度)
  • 第二个数字 (11355.7832) 指的是 >113 度,55.7832 分钟(经度)

它们不能按原样用于公式中。 它们必须转换为十进制。

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

This is a GPRMC string. After splitting the string, you need to parse latitude and longitude values.

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

In latitude and longitude part ([..., '3248.7780', 'N', '11355.7832, 'W', ...]):

  • The first number is not a pure number, it is a number which is concatenated like a string. I mean, 3248.7780 refers 32 degree, 48.7780 minutes (latitude)
  • The second number (11355.7832) refers 113 degree, 55.7832 minutes (longitude)

They cannot be used in a formula as they are. They have to be converted to decimal degree.

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')
江挽川 2024-07-16 10:45:34

如果您需要对 GPS 数据流进行更广泛的分析,这里有一个 pyparsing 解决方案,可将您的数据分解为命名数据字段。 我将您的 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

您的 Pastebin 中的 $GPRMC 记录似乎与您在帖子中包含的记录不太匹配,但您应该能够根据需要调整此示例。

If you need to do some more extensive analysis of your GPS data streams, here is a pyparsing solution that breaks up your data into named data fields. I extracted your pastebin'ned data to a file gpsstream.txt, and parsed it with the following:

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

The $GPRMC records in your pastebin don't seem to quite match with the ones you included in your post, but you should be able to adjust this example as necessary.

怀里藏娇 2024-07-16 10:45:34

我建议在您的代码中进行一个小修复,因为如果用于解析上个世纪的数据,则日期看起来像是未来的某个时间(例如 2094 年而不是 1994 年)。

我的修复并不完全准确,但我采取的立场是,在70 年代没有 GPS 数据。

在 RMC 句子的 def 解析函数中,只需将格式行替换为:

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 年我们正在处理上世纪的句子。
通过与今天的日期进行比较并假设每次您将来处理一些数据时,它们实际上来自上个世纪,可以更好地完成

感谢您在上面提供的所有代码片段...我玩得很开心这。

I suggest a small fix in your code because if used to parse data from the previous century the date looks like sometime in the future (for instance 2094 instead of 1994)

My fix is not fully accurate, but I take the stand that prior to the 70's no GPS data existed.

In the def parse function for RMC sentences just replace the format line by:

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

This will look at the two yy digits of the year and assume that past year 70 we are dealing with sentences from the previous century.
It could be better done by comparing to today's date and assuming that every time you deal with some data in the future, they are in fact from the past century

Thanks for all the pieces of code your provided above... I had some fun with this.

聆听风音 2024-07-16 10:45:34

这是一个老问题,它要求一个正则表达式。
我在这里的贡献是在 regex101.com 上验证的表达式:

RMC,[\d\.]*,A,([\d\.]+),([NS]),([\d\.]+),([EW]),[\d\.]*,([\d\.]*)

您可以使用如下内容:

import re

p = re.compile(r'RMC,[\d\.]*,A,([\d\.]+),([NS]),([\d\.]+),([EW]),[\d\.]*,([\d\.]*)')

nmea = r'$GPRMC,150714.696,A,2829.6203,N,08039.0335,W,0.00,,211108,,*0A'

x = p.search(nmea)
if not x is None:
  print(f"lat: {x.group(1)} {x.group(2)}")
  print(f"lon: {x.group(3)} {x.group(4)}")
  print(f"heading: {x.group(5) if len(x.group(5)) > 0 else 'None'}")

它具有您需要的 5 个捕获组(纬度、北/南、经度、东/西、航向),并且它将仅过滤有效的修复(那些以“A”作为第三个参数的)。
一些观察:

  • 14 年前,只有 GPS 接收器很常见,因此使用以“$GPRMC”开头的正则表达式是合理的,但现在您可以拥有多星座 GNSS 接收器,它输出可以与以下内容相关的消息: GPS、GLONASS、BEIDOU、GALILEO 或混合的,前缀可以根据星座而变化。 因此,我只保留了“RMC”标识符。
  • 我已经让航向是可选的,以防没有任何移动。
  • 请记住,您必须将纬度和经度转换为十进制值 ( Degrees.fraction_of_ Degrees° ) 或度°分'秒' 格式,才能在常规地图中有意义。 NMEA 句子使用一种奇怪的格式,度数与分钟级联,并带有分钟的小数部分,因此您必须将度数和分钟分开,然后再次将它们混合在一起,如下所示:
  lat_nmea = float(x.group(1))
  lat_deg = lat_nmea//100
  lat_min = lat_nmea - 100*lat_deg
  lat_deg = lat_deg + lat_min/60
  print(f"conv lat {x.group(1)} -> {lat_deg}")

  lon_nmea = float(x.group(3))
  lon_deg = lon_nmea//100
  lon_min = lon_nmea - 100*lon_deg
  lon_deg = lon_deg + lon_min/60
  print(f"conv lon {x.group(3)} -> {lon_deg}")

This is an old question, and it was asking for a regular expression.
My contribution here is an expression validated on regex101.com:

RMC,[\d\.]*,A,([\d\.]+),([NS]),([\d\.]+),([EW]),[\d\.]*,([\d\.]*)

You can use something like this:

import re

p = re.compile(r'RMC,[\d\.]*,A,([\d\.]+),([NS]),([\d\.]+),([EW]),[\d\.]*,([\d\.]*)')

nmea = r'$GPRMC,150714.696,A,2829.6203,N,08039.0335,W,0.00,,211108,,*0A'

x = p.search(nmea)
if not x is None:
  print(f"lat: {x.group(1)} {x.group(2)}")
  print(f"lon: {x.group(3)} {x.group(4)}")
  print(f"heading: {x.group(5) if len(x.group(5)) > 0 else 'None'}")

It has the 5 capturing groups that you needed (latitude, N/S, longitude, E/W, heading) and it will filter only valid fixes (those with an 'A' as 3rd parameter).
Some observations:

  • 14 years ago it was common to have only GPS receivers, so it was reasonable to use a regular expression that started with "$GPRMC", but nowadays you can have a multiconstellation GNSS receiver, that outputs messages that can be related to GPS, GLONASS, BEIDOU, GALILEO or something mixed, and the prefix can change accordingly to the constellation. For this reason, I've kept only the "RMC" identifier.
  • I've let heading to be optional in case there's no movement.
  • Remember that you must convert the latitude and longitude to decimal values (degrees.fraction_of_degrees°) or to degrees°minutes'seconds'' format to make some sense in regular maps. NMEA sentences use an strange format, with degrees concatenated to the minutes with fractional part of minutes, so you'll have to separate the degrees and minutes and then mix them again with something like this:
  lat_nmea = float(x.group(1))
  lat_deg = lat_nmea//100
  lat_min = lat_nmea - 100*lat_deg
  lat_deg = lat_deg + lat_min/60
  print(f"conv lat {x.group(1)} -> {lat_deg}")

  lon_nmea = float(x.group(3))
  lon_deg = lon_nmea//100
  lon_min = lon_nmea - 100*lon_deg
  lon_deg = lon_deg + lon_min/60
  print(f"conv lon {x.group(3)} -> {lon_deg}")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文