【问题标题】:Parsing and converting output from a GPS Dongle解析和转换 GPS 加密狗的输出
【发布时间】:2021-04-07 01:42:27
【问题描述】:

我买了一个便宜的 ublox7 GPS 加密狗并将其粘贴在我的树莓派 3 上。当我查看输出并尝试将其粘贴到地图程序中时,我得到了奇怪的结果。这是使用名为“pynmea2”的库解析后设备的一些示例输出。

$GPGLL,3745.81303,N,12214.62049,W,175033.00,A,D*7C

我对如何将此输出转换为有用的东西进行了一些研究,并找到了一个公式,该公式涉及将数字除以 0.6。

Doing GPS Conversion – Degrees to Latitude Longitude and vice versa

所以我编写了一个 python 程序来尝试捕获和转换所有这些,并且输出相差一英里。我正试图弄清楚我做错了什么,我怎么会离得这么近,但仍然离我大约一英里?

from time import sleep
import pynmea2
import serial
import re

degree_sign = u"\N{DEGREE SIGN}"

ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1.0)
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
while True:
    line = sio.readline()
    msg = pynmea2.parse(line)
    msg = str(msg)
    if re.search("GPGLL", msg):
        raw_nums = re.findall(r'\b\d*\.\d*', msg)

        lat_whole = (raw_nums[0])
        lat_part1 = lat_whole[0:2]
        lat_part2 = lat_whole[2:4]
        lat_part2 = int(lat_part2)
        lat_part2 = lat_part2 / .6
        lat_part2 = int(lat_part2)
        lat_part2 = str(lat_part2)
        lat_part3 = lat_whole[5:9]
        lat_part3 = float(lat_part3)
        lat_part3 = lat_part3 / .6
        lat_part3 = round(lat_part3, 0)
        lat_part3 = int(lat_part3)
        lat_part3 = str(lat_part3)

        lon_whole = raw_nums[1]
        lon_part1 = lon_whole[0:3]
        lon_part1 = int(lon_part1)
        lon_part1 = -lon_part1
        lon_part1 = str(lon_part1)
        lon_part2 = lon_whole[3:5]
        lon_part2 = int(lon_part2)
        lon_part2 = lon_part2 / .6
        lon_part2 = str(lon_part2)
        lon_part2 = lon_part2[0:2]
        lon_part3 = lon_whole[6:10]
        lon_part3 = float(lon_part3)
        lon_part3 = lon_part3 / .6
        lon_part3 = round(lon_part3, 0)
        lon_part3 = int(lon_part3)
        lon_part3 = str(lon_part3)

        print(lat_part1 + "." + lat_part2 + lat_part3 +"," , lon_part1 + "." + lon_part2 + lon_part3)
        print(lat_part1+degree_sign+lat_part2+"'"+lat_part3+"\"" + "N", lon_part1 + degree_sign+ lon_part2 + "'" + lon_part3+"\"" + "W")
        sleep(1)

这里是正则表达式使用 pynmea2 输出生成的列表:

['3745.81246', '12214.61512', '224329.00'] 分配给 raw_nums。

脚本的输出:

37.7513540, -122.2310268

37°75'13540"N -122°23'10268"W

将输出的第一个位输入到谷歌地图会显示一个离我很近但大约一英里外的地方,第二个数字由于某种原因在谷歌地图上不起作用 - 但它在苹果地图上起作用。

我的问题:

  • 我知道至少有 100 种更好的方法来编写这段代码,你有什么建议可以更快地到达那里吗?
  • 这个公式有意义吗?我是否正确应用它?
  • 您是否明白为什么这应该返回接近但没有雪茄的结果?
  • 你知道为什么第二行输出不能作为谷歌地图的输入吗?
  • 我以 12 美元从亚马逊购买的 ublox 7 GPS 加密狗的准确度应该是多少?

在此先感谢,我真的很感激。

更新:我在gps coordinates conversion 上查找了我的地址 他们为我的地址显示的纬度是 3745.50084,而我的 gps 报告的是 3745.81246。所以看起来我是从糟糕的数据开始的......

【问题讨论】:

    标签: python regex gps


    【解决方案1】:

    如果您从 GPS 设备解析的字符串始终是您指定的格式,您可以简单地用逗号分割字符串,例如 split_msg = msg.split(",")。那么你的纬度将是split_msg[1] 和你的长split_msg[3]。以索引 2 和 4 为航向。

    lat 以 DDmm.mm 的形式提供,long 以 DDDmm.mm 的形式提供,您似乎已经在上面捕获了。所以 3745.81246 将是 37 度和 45.81246 分钟。您可以取分钟的小数部分(即 0.81246)并乘以 60 以获得秒数。所以你会得到 37 度 45 分 48.75 秒。作为健全性检查,分钟和秒应始终小于 60,因为其中任何一个为 60 都会增加下一个值(例如,60 分钟以度为单位,60 秒以一分钟为单位)。

    要将分钟数转换为十进制度数,只需将分钟数除以 60 (45.81246/60=0.763541),然后将其添加到您的度数中。所以 3745.81246 会变成 37.763541。

    所以在 if 语句中:

    split_msg = msg.split(",")
    lat, lat_dir, long, long_dir = split_msg[1:5]
    lat_d, lat_m = float(lat[:2]), float(lat[2:])
    long_d, long_m = float(long[:3]), float(long[3:])
    lat_dec = lat_d + lat_m/60
    long_dec = long_d + long_m/60
    lat_min = math.floor(lat_m)
    lat_sec = 60*(lat_m - lat_min)
    long_min = math.floor(long_m)
    long_sec = 60*(long_m - long_min)
    print(f"{lat_dec} {lat_dir}, {long_dec} {long_dir}")
    print(f"{lat_d}{degree_sign} {lat_min}' {lat_sec}\" {lat_dir}, {long_d}{degree_sign} {long_min}' {long_sec}\" {long_dir}")
    

    我没有测试过上面的代码,但这是我解决这个问题的一般方法。

    【讨论】:

    • 这看起来很有希望,感谢您的详细回复。我在路上,但今晚会尝试实施你的建议。再次感谢。
    【解决方案2】:

    由于您使用的是pynmea2 库,因此您可以利用对象属性来简化后续步骤。

    import pynmea2
    
    line = "$GPGLL,3745.81303,N,12214.62049,W,175033.00,A,D*7C"
    nmeaobj = pynmea2.parse(line)
    
    coord = f'{nmeaobj.latitude} {nmeaobj.longitude}'
    print(coord)
    # 37.763551 -122.243675
    

    脚本输出和库之间的十进制度数差异约为 0.012,这与 Degree precision versus length 表中引用的精度长度 (1.1132 km) 相似。这可以解释为什么您会看到大约一英里的差异。

    abs(-122.243675 - -122.2310268)
    0.012648200000000998
    
    abs(37.7635505 - 37.7513540)
    0.01219650000000172
    

    您可以使用上一个链接中引用的公式将十进制度转换为 DMS 组件,这将产生一个有效的位置。但请注意,坐标的方向 (NS/WE) 未包含在最终的字符串形式中。

    def dd_to_dms(coord):
        d = int(coord)
        abs_d = abs(coord-d)
        m = int(60 * abs_d)
        s = 3600 * abs_d - 60 * m
        return d,m,s
    
    lat = '''%02d°%02d'%07.4f"''' % dd_to_dms(nmeaobj.latitude)
    lon = '''%02d°%02d'%07.4f"''' % dd_to_dms(nmeaobj.longitude)
    print(f'{lat} {lon}')
    # 37°45'48.7818" -122°14'37.2294"
    

    第二行输出不能作为谷歌地图的输入,因为如前所述,分钟和秒应始终小于 60。此外,将坐标方向添加到负数可能会使您(取决于用于解析字符串的算法)由于度数符号(或坐标方向NS/WE)被忽略,或者在谷歌地图的情况下,简单地在所需位置的相反方向上“行走”不了解坐标。

    37°45'48.7818" -122°14'37.2294"    # works
    37°45'48.7818"N -122°14'37.2294"W  # don't work
    37°45'48.7818"N 122°14'37.2294"W   # works
    

    【讨论】:

    • 我期待着尝试这个!我希望我有我的 gps 加密狗。感谢您的详细回复。
    猜你喜欢
    • 1970-01-01
    • 2021-04-01
    • 2012-06-20
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 2021-08-04
    • 2018-08-16
    • 1970-01-01
    相关资源
    最近更新 更多