Python GPS模块:读取最新GPS数据
我一直在尝试使用标准的GPS (gps.py)模块,这个模块是在Python 2.6中用的。它应该能作为一个客户端,从在Ubuntu上运行的gpsd读取GPS数据。
根据GPSD网页上关于客户端设计的文档(GPSD客户端使用指南),我应该能用以下代码(稍微修改过的例子)来获取最新的GPS读数(我主要关心的是经纬度)。
from gps import *
session = gps() # assuming gpsd running with default options on port 2947
session.stream(WATCH_ENABLE|WATCH_NEWSTYLE)
report = session.next()
print report
但是如果我反复使用next(),它给我的都是队列底部的缓冲值(也就是会话开始时的数据),而不是最新的GPS读数。有没有办法用这个库获取更新的值呢?也就是说,能不能把流定位到最新的值?
有没有人用这个库写过代码示例,能让我获取到我想要的值?
这是我想做的事情:
- 启动会话
- 等待用户在我的代码中调用gps_poll()方法
- 在这个方法里读取最新的TPV(时间、位置、速度)报告,并返回经纬度
- 再回到等待用户调用gps_poll()的状态
4 个回答
我来分享一下我的经验。
出于某种原因,我的树莓派总是会继续运行一个线程,结果我不得不强制重启树莓派。
所以我把sysnthesizerpatel的内容和我在Dan Mandel的博客上找到的一个答案结合起来了,链接在这里。
我的gps_poller类是这样的:
import os
from gps import *
from time import *
import time
import threading
class GpsPoller(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.session = gps(mode=WATCH_ENABLE)
self.current_value = None
self.running = True
def get_current_value(self):
return self.current_value
def run(self):
try:
while self.running:
self.current_value = self.session.next()
except StopIteration:
pass
正在使用的代码是这样的:
from gps_poll import *
if __name__ == '__main__':
gpsp = GpsPoller()
try:
gpsp.start()
while True:
os.system('clear')
report = gpsp.get_current_value()
# print report
try:
if report.keys()[0] == 'epx':
print report['lat']
print report['lon']
time.sleep(.5)
except(AttributeError, KeyError):
pass
time.sleep(0.5)
except(KeyboardInterrupt, SystemExit):
print "\nKilling Thread.."
gpsp.running = False
gpsp.join()
print "Done.\nExiting."
上面的回答对于使用现代版本的gpsd并且只需要在特定时间获取数据的人来说,效率很低而且过于复杂,因为他们并不需要持续的数据流。
大多数GPS设备每秒至少会发送一次位置信息。因为很多基于GPS的应用程序需要实时更新,所以我看到的大多数gpsd客户端示例都是通过监控gpsd的数据流来接收实时更新(基本上是GPS发送信息的频率)。
然而,如果像提问者的情况那样,你并不需要持续的数据流,而只是想在需要时获取最新的位置信息(比如通过用户操作或其他事件),那么有一种更高效、更简单的方法:让gpsd缓存最新的位置信息,并在需要时查询它。
gpsd JSON协议中有一个?POLL;
请求,它会返回gpsd最近接收到的GPS信息。这样你就不需要遍历之前的gps消息,也不需要不断读取新消息以避免缓冲区满,你可以在gpsd会话开始时发送一个?WATCH={"enable":true}
消息,然后在需要时用?POLL;
查询最新的位置信息。响应会是一个包含gpsd从GPS接收到的最新信息的JSON对象。
如果你使用的是Python3,我发现最简单的方法是使用gpsd-py3这个在pypi上可用的包。要连接到gpsd,获取最新的位置信息,并打印当前的位置:
import gpsd
gpsd.connect()
packet = gpsd.get_current()
print(packet.position())
你可以在需要新的位置信息时重复调用gpsd.get_current()
,在后台,gpsd包会执行?POLL;
调用并返回一个表示响应的对象。
使用内置的gps
模块来做到这一点并不是特别简单,但还有很多其他的Python客户端可用,而且用任何可以进行套接字通信的工具来实现这个功能也相对简单,包括这个使用telnet
的示例:
$ telnet localhost 2947
Trying ::1...
Connected to localhost.
Escape character is '^]'.
{"class":"VERSION","release":"3.16","rev":"3.16","proto_major":3,"proto_minor":11}
?WATCH={"enable":true}
{"class":"DEVICES","devices":[{"class":"DEVICE","path":"/dev/pts/10","driver":"SiRF","activated":"2018-03-02T21:14:52.687Z","flags":1,"native":1,"bps":4800,"parity":"N","stopbits":1,"cycle":1.00}]}
{"class":"WATCH","enable":true,"json":false,"nmea":false,"raw":0,"scaled":false,"timing":false,"split24":false,"pps":false}
?POLL;
{"class":"POLL","time":"2018-03-02T21:14:54.873Z","active":1,"tpv":[{"class":"TPV","device":"/dev/pts/10","mode":3,"time":"2005-06-09T14:34:53.280Z","ept":0.005,"lat":46.498332203,"lon":7.567403907,"alt":1343.165,"epx":24.829,"epy":25.326,"epv":78.615,"track":10.3788,"speed":0.091,"climb":-0.085,"eps":50.65,"epc":157.23}],"gst":[{"class":"GST","device":"/dev/pts/10","time":"1970-01-01T00:00:00.000Z","rms":0.000,"major":0.000,"minor":0.000,"orient":0.000,"lat":0.000,"lon":0.000,"alt":0.000}],"sky":[{"class":"SKY","device":"/dev/pts/10","time":"2005-06-09T14:34:53.280Z","xdop":1.66,"ydop":1.69,"vdop":3.42,"tdop":3.05,"hdop":2.40,"gdop":5.15,"pdop":4.16,"satellites":[{"PRN":23,"el":6,"az":84,"ss":0,"used":false},{"PRN":28,"el":7,"az":160,"ss":0,"used":false},{"PRN":8,"el":66,"az":189,"ss":45,"used":true},{"PRN":29,"el":13,"az":273,"ss":0,"used":false},{"PRN":10,"el":51,"az":304,"ss":29,"used":true},{"PRN":4,"el":15,"az":199,"ss":36,"used":true},{"PRN":2,"el":34,"az":241,"ss":41,"used":true},{"PRN":27,"el":71,"az":76,"ss":42,"used":true}]}]}
?POLL;
{"class":"POLL","time":"2018-03-02T21:14:58.856Z","active":1,"tpv":[{"class":"TPV","device":"/dev/pts/10","mode":3,"time":"2005-06-09T14:34:53.280Z","ept":0.005,"lat":46.498332203,"lon":7.567403907,"alt":1343.165,"epx":24.829,"epy":25.326,"epv":78.615,"track":10.3788,"speed":0.091,"climb":-0.085,"eps":50.65,"epc":157.23}],"gst":[{"class":"GST","device":"/dev/pts/10","time":"1970-01-01T00:00:00.000Z","rms":0.000,"major":0.000,"minor":0.000,"orient":0.000,"lat":0.000,"lon":0.000,"alt":0.000}],"sky":[{"class":"SKY","device":"/dev/pts/10","time":"2005-06-09T14:34:53.280Z","xdop":1.66,"ydop":1.69,"vdop":3.42,"tdop":3.05,"hdop":2.40,"gdop":5.15,"pdop":4.16,"satellites":[{"PRN":23,"el":6,"az":84,"ss":0,"used":false},{"PRN":28,"el":7,"az":160,"ss":0,"used":false},{"PRN":8,"el":66,"az":189,"ss":45,"used":true},{"PRN":29,"el":13,"az":273,"ss":0,"used":false},{"PRN":10,"el":51,"az":304,"ss":29,"used":true},{"PRN":4,"el":15,"az":199,"ss":36,"used":true},{"PRN":2,"el":34,"az":241,"ss":41,"used":true},{"PRN":27,"el":71,"az":76,"ss":42,"used":true}]}]}
你需要定期检查 'session.next()',这里的问题是你在处理一个串行接口——你得到的结果是按照接收到的顺序来的。你需要自己维护一个 'current_value',这个值是最新获取到的结果。
如果你不去检查这个会话对象,最终你的UART FIFO会填满,这样你就不会再收到任何新值了。
可以考虑使用一个线程来处理这个,不要等用户去调用 gps_poll(),你应该一直在检查,当用户想要获取新值时,他们可以使用 'get_current_value()',这个函数会返回 current_value。
我随便想的可能就是这么简单:
import threading
import time
from gps import *
class GpsPoller(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.session = gps(mode=WATCH_ENABLE)
self.current_value = None
def get_current_value(self):
return self.current_value
def run(self):
try:
while True:
self.current_value = self.session.next()
time.sleep(0.2) # tune this, you might not get values that quickly
except StopIteration:
pass
if __name__ == '__main__':
gpsp = GpsPoller()
gpsp.start()
# gpsp now polls every .2 seconds for new data, storing it in self.current_value
while 1:
# In the main thread, every 5 seconds print the current value
time.sleep(5)
print gpsp.get_current_value()