gpsd Python 客户端
我正在尝试为Gpsd写一个非常简单的Python客户端,但在运行脚本一段时间后出现了这个错误:
Traceback (most recent call last):
File "gps_cap.py", line 13, in <module>
g.stream()
File "/usr/lib/python2.6/site-packages/gps/gps.py", line 348, in stream
gpsjson.stream(self, flags)
File "/usr/lib/python2.6/site-packages/gps/client.py", line 176, in stream
return self.send(arg + "}")
File "/usr/lib/python2.6/site-packages/gps/client.py", line 111, in send
self.sock.send(commands)
socket.error: [Errno 104] Connection reset by peer
这是我的Python代码:
import os
from gps import *
from time import *
g = gps(mode=WATCH_ENABLE)
while 1:
os.system('clear')
g.poll()
if PACKET_SET:
g.stream()
print
print ' GPS reading'
print '----------------------------------------'
print 'latitude ' , g.fix.latitude
print 'longitude ' , g.fix.longitude
print 'time utc ' , g.utc,' + ', g.fix.time
print 'altitude ' , g.fix.altitude
print 'epc ' , g.fix.epc
print 'epd ' , g.fix.epd
print 'eps ' , g.fix.eps
print 'epx ' , g.fix.epx
print 'epv ' , g.fix.epv
print 'ept ' , g.fix.ept
print 'speed ' , g.fix.speed
print 'climb ' , g.fix.climb
print 'track ' , g.fix.track
print 'mode ' , g.fix.mode
print
print 'sats ' , g.satellites
sleep(1)
有没有人能帮我解决这个问题?我在ArchLinux系统上运行Gpsd 2.95。
谢谢!
3 个回答
为了让一个老问题继续被关注,下面是关于GPS3的最新情况,这是一款支持Python 2.7到3.5的gpsd客户端,你可以在https://github.com/wadda/gps3找到它。
GPS3有两个主要部分:GPSDSocket类和Fix类。
GPSD会以多种“类别”发送JSON数据,比如TPV、SKY等等。在连接到GPSD后,GPS3会把这些JSON对象拆解成字典格式(比如Fix.TPV['lat']
、Fix.SKY['satellites']
等)。
通常的使用方式是创建一个实例,比如fix = gps3.Fix()
,然后所有可用的数据都可以通过原始JSON对象的名称来获取(例如fix.TPV['speed']
、fix.TPV['alt']
等)。
你可以参考一个演示应用程序gegps3.py,它会创建一个kml
文件(/tmp/gps3_live.kml
),可以在Google Earth中查看。
#!/usr/bin/env python3
# coding=utf-8
"""
GPS3 (gps3.py) is a Python 2.7-3.5 GPSD interface (http://www.catb.org/gpsd)
Defaults host='127.0.0.1', port=2947, gpsd_protocol='json'
GPS3 has two classes.
1) 'GPSDSocket' to create a GPSD socket connection and request/retreive GPSD output.
2) 'Fix' unpacks the streamed gpsd data into python dictionaries.
These dictionaries are literated from the JSON data packet sent from the GPSD.
Import import gps3
Instantiate gps_connection = gps3.GPSDSocket(host='192.168.0.4')
gps_fix = gps3.Fix()
Iterate for new_data in gps_connection:
if new_data:
gps_fix.refresh(new_data)
Use print('Altitude = ',gps_fix.TPV['alt'])
print('Latitude = ',gps_fix.TPV['lat'])
Consult Lines 152-ff for Attribute/Key possibilities.
or http://www.catb.org/gpsd/gpsd_json.html
Run human.py; python[X] human.py [arguments] for a human experience.
"""
from __future__ import print_function
import json
import select
import socket
import sys
__author__ = 'Moe'
__copyright__ = 'Copyright 2015-2016 Moe'
__license__ = 'MIT'
__version__ = '0.2'
HOST = '127.0.0.1' # gpsd
GPSD_PORT = 2947 # defaults
PROTOCOL = 'json' # "
class GPSDSocket(object):
"""Establish a socket with gpsd, by which to send commands and receive data."""
def __init__(self, host=HOST, port=GPSD_PORT, gpsd_protocol=PROTOCOL, devicepath=None):
self.devicepath_alternate = devicepath
self.response = None
self.protocol = gpsd_protocol
self.streamSock = None
if host:
self.connect(host, port)
def connect(self, host, port):
"""Connect to a host on a given port.
Arguments:
port: default port=2947
host: default host='127.0.0.1'
"""
for alotta_stuff in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
family, socktype, proto, _canonname, host_port = alotta_stuff
try:
self.streamSock = socket.socket(family, socktype, proto)
self.streamSock.connect(host_port)
self.streamSock.setblocking(False)
self.watch(gpsd_protocol=self.protocol)
except OSError as error:
sys.stderr.write('\nGPSDSocket.connect OSError is-->', error)
sys.stderr.write('\nAttempt to connect to a gpsd at {0} on port \'{1}\' failed:\n'.format(host, port))
sys.exit(1) # TODO: gpsd existence check and start
def watch(self, enable=True, gpsd_protocol='json', devicepath=None):
"""watch gpsd in various gpsd_protocols or devices.
Arguments:
self:
enable: (bool) stream data to socket
gpsd_protocol: (str) 'json' | 'nmea' | 'rare' | 'raw' | 'scaled' | 'split24' | 'pps'
devicepath: (str) device path - '/dev/ttyUSBn' for some number n or '/dev/whatever_works'
Returns:
command: (str) e.g., '?WATCH={"enable":true,"json":true};'
"""
# N.B.: 'timing' requires special attention, as it is undocumented and lives with dragons.
command = '?WATCH={{"enable":true,"{0}":true}}'.format(gpsd_protocol)
if gpsd_protocol == 'rare': # 1 for a channel, gpsd reports the unprocessed NMEA or AIVDM data stream
command = command.replace('"rare":true', '"raw":1')
if gpsd_protocol == 'raw': # 2 channel that processes binary data, received data verbatim without hex-dumping.
command = command.replace('"raw":true', '"raw",2')
if not enable:
command = command.replace('true', 'false') # sets -all- command values false .
if devicepath:
command = command.replace('}', ',"device":"') + devicepath + '"}'
return self.send(command)
def send(self, commands):
"""Ship commands to the daemon
Arguments:
commands: e.g., '?WATCH={{'enable':true,'json':true}}'|'?VERSION;'|'?DEVICES;'|'?DEVICE;'|'?POLL;'
"""
# The POLL command requests data from the last-seen fixes on all active GPS devices.
# Devices must previously have been activated by ?WATCH to be pollable.
if sys.version_info[0] < 3: # Not less than 3, but 'broken hearted' because
self.streamSock.send(commands) # 2.7 chokes on 'bytes' and 'encoding='
else:
self.streamSock.send(bytes(commands, encoding='utf-8')) # It craps out here when there is no gpsd running
# TODO: Add recovery, check gpsd existence, re/start, etc..
def __iter__(self):
"""banana""" # <------- for scale
return self
def next(self, timeout=0):
"""Return empty unless new data is ready for the client.
Arguments:
timeout: Default timeout=0 range zero to float specifies a time-out as a floating point
number in seconds. Will sit and wait for timeout seconds. When the timeout argument is omitted
the function blocks until at least one file descriptor is ready. A time-out value of zero specifies
a poll and never blocks.
"""
try:
waitin, _waitout, _waiterror = select.select((self.streamSock,), (), (), timeout)
if not waitin: return
else:
gpsd_response = self.streamSock.makefile() # '.makefile(buffering=4096)' In strictly Python3
self.response = gpsd_response.readline()
return self.response
except OSError as error:
sys.stderr.write('The readline OSError in GPSDSocket.next is this: ', error)
__next__ = next # Workaround for changes in iterating between Python 2.7 and 3
def close(self):
"""turn off stream and close socket"""
if self.streamSock:
self.watch(enable=False)
self.streamSock.close()
self.streamSock = None
class Fix(object):
"""Retrieve JSON Object(s) from GPSDSocket and unpack it into respective
gpsd 'class' dictionaries, TPV, SKY, etc. yielding hours of fun and entertainment.
"""
def __init__(self):
"""Potential data packages from gpsd for a generator of class attribute dictionaries"""
packages = {'VERSION': {'release', 'proto_major', 'proto_minor', 'remote', 'rev'},
'TPV': {'alt', 'climb', 'device', 'epc', 'epd', 'eps', 'ept', 'epv', 'epx', 'epy', 'lat', 'lon', 'mode', 'speed', 'tag', 'time', 'track'},
'SKY': {'satellites', 'gdop', 'hdop', 'pdop', 'tdop', 'vdop', 'xdop', 'ydop'},
# Subset of SKY: 'satellites': {'PRN', 'ss', 'el', 'az', 'used'} # is always present.
'GST': {'alt', 'device', 'lat', 'lon', 'major', 'minor', 'orient', 'rms', 'time'},
'ATT': {'acc_len', 'acc_x', 'acc_y', 'acc_z', 'depth', 'device', 'dip', 'gyro_x', 'gyro_y', 'heading', 'mag_len', 'mag_st', 'mag_x',
'mag_y', 'mag_z', 'pitch', 'pitch_st', 'roll', 'roll_st', 'temperature', 'time', 'yaw', 'yaw_st'},
# 'POLL': {'active', 'tpv', 'sky', 'time'},
'PPS': {'device', 'clock_sec', 'clock_nsec', 'real_sec', 'real_nsec', 'precision'},
'TOFF': {'device', 'clock_sec', 'clock_nsec','real_sec', 'real_nsec' },
'DEVICES': {'devices', 'remote'},
'DEVICE': {'activated', 'bps', 'cycle', 'mincycle', 'driver', 'flags', 'native', 'parity', 'path', 'stopbits', 'subtype'},
# 'AIS': {} # see: http://catb.org/gpsd/AIVDM.html
'ERROR': {'message'}} # TODO: Full suite of possible GPSD output
for package_name, dataset in packages.items():
_emptydict = {key: 'n/a' for key in dataset}
setattr(self, package_name, _emptydict)
self.DEVICES['devices'] = {key: 'n/a' for key in packages['DEVICE']} # How does multiple listed devices work?
# self.POLL = {'tpv': self.TPV, 'sky': self.SKY, 'time': 'n/a', 'active': 'n/a'}
def refresh(self, gpsd_data_package):
"""Sets new socket data as Fix attributes in those initialied dictionaries
Arguments:
self:
gpsd_data_package (json object):
Provides:
self attribute dictionaries, e.g., self.TPV['lat'], self.SKY['gdop']
Raises:
AttributeError: 'str' object has no attribute 'keys' when the device falls out of the system
ValueError, KeyError: most likely extra, or mangled JSON data, should not happen, but that
applies to a lot of things.
"""
try:
fresh_data = json.loads(gpsd_data_package) # The reserved word 'class' is popped from JSON object class
package_name = fresh_data.pop('class', 'ERROR') # gpsd data package errors are also 'ERROR'.
package = getattr(self, package_name, package_name) # packages are named for JSON object class
for key in package.keys(): # TODO: Rollover and retry. It fails here when device disappears
package[key] = fresh_data.get(key, 'n/a') # Updates and restores 'n/a' if key is absent in the socket
# response, present --> 'key: 'n/a'' instead.'
except AttributeError: # 'str' object has no attribute 'keys'
print('No Data')
return
except (ValueError, KeyError) as error:
sys.stderr.write(str(error)) # Look for extra data in stream
return
if __name__ == '__main__':
print('\n', __doc__)
#
# Someday a cleaner Python interface will live here
#
# End
我觉得这段代码来自gpsd的使用说明书,挺靠谱的;另外,感谢提供的bootstrap代码。
http://gpsd.berlios.de/client-howto.html
如果你聪明的话,可能已经在想,如果客户端的应用程序没有像gpsd那样快速读取数据,那这个守护进程会怎么处理呢?答案是:最终,socket的缓冲区会填满,守护进程写入数据时会出错,然后它会关闭这个客户端的socket。
只要你的应用程序每秒检查并读取一次socket数据,你就不会遇到这个问题——而且一秒钟的时间其实足够你回到主循环中去处理其他事情。
我知道这个问题已经很久了,但我还是把我的答案放在这里,以防将来有人需要:
#! /usr/bin/python
# Written by Dan Mandle http://dan.mandle.me September 2012
# License: GPL 2.0
import os
from gps import *
from time import *
import time
import threading
gpsd = None #seting the global variable
os.system('clear') #clear the terminal (optional)
class GpsPoller(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
global gpsd #bring it in scope
gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info
self.current_value = None
self.running = True #setting the thread running to true
def run(self):
global gpsd
while gpsp.running:
gpsd.next() #this will continue to loop and grab EACH set of gpsd info to clear the buffer
if __name__ == '__main__':
gpsp = GpsPoller() # create the thread
try:
gpsp.start() # start it up
while True:
#It may take a second or two to get good data
#print gpsd.fix.latitude,', ',gpsd.fix.longitude,' Time: ',gpsd.utc
os.system('clear')
print
print ' GPS reading'
print '----------------------------------------'
print 'latitude ' , gpsd.fix.latitude
print 'longitude ' , gpsd.fix.longitude
print 'time utc ' , gpsd.utc,' + ', gpsd.fix.time
print 'altitude (m)' , gpsd.fix.altitude
print 'eps ' , gpsd.fix.eps
print 'epx ' , gpsd.fix.epx
print 'epv ' , gpsd.fix.epv
print 'ept ' , gpsd.fix.ept
print 'speed (m/s) ' , gpsd.fix.speed
print 'climb ' , gpsd.fix.climb
print 'track ' , gpsd.fix.track
print 'mode ' , gpsd.fix.mode
print
print 'sats ' , gpsd.satellites
time.sleep(5) #set to whatever
except (KeyboardInterrupt, SystemExit): #when you press ctrl+c
print "\nKilling Thread..."
gpsp.running = False
gpsp.join() # wait for the thread to finish what it's doing
print "Done.\nExiting."
这段代码可以和线程一起工作,能把gpsd的数据漂亮地显示在屏幕上。你可以通过按Ctrl + C来终止它。
所有的感谢都要给http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/