2019-08-03 02:06:06 +02:00
|
|
|
import threading
|
|
|
|
import time
|
|
|
|
|
|
|
|
from platypush.backend import Backend
|
|
|
|
from platypush.message.event.gps import GPSVersionEvent, GPSDeviceEvent, GPSUpdateEvent
|
|
|
|
|
|
|
|
|
|
|
|
class GpsBackend(Backend):
|
|
|
|
"""
|
|
|
|
This backend can interact with a GPS device and listen for events.
|
|
|
|
|
|
|
|
Once installed gpsd you need to run it and associate it to your device. Example if your GPS device communicates
|
|
|
|
over USB and is available on /dev/ttyUSB0::
|
|
|
|
|
|
|
|
[sudo] gpsd /dev/ttyUSB0 -F /var/run/gpsd.sock
|
|
|
|
|
|
|
|
The best option is probably to run gpsd at startup as a systemd service.
|
|
|
|
"""
|
|
|
|
|
|
|
|
_fail_sleep_time = 5.0
|
2019-08-13 22:25:28 +02:00
|
|
|
_lat_lng_tolerance = 1e-5
|
|
|
|
_alt_tolerance = 0.5
|
2019-08-03 02:06:06 +02:00
|
|
|
|
|
|
|
def __init__(self, gpsd_server='localhost', gpsd_port=2947, **kwargs):
|
|
|
|
"""
|
|
|
|
:param gpsd_server: gpsd daemon server name/address (default: localhost)
|
|
|
|
:type gpsd_server: str
|
|
|
|
:param gpsd_port: Port of the gpsd daemon (default: 2947)
|
|
|
|
:type gpsd_port: int or str
|
|
|
|
"""
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
|
|
|
|
self.gpsd_server = gpsd_server
|
|
|
|
self.gpsd_port = gpsd_port
|
|
|
|
self._session = None
|
|
|
|
self._session_lock = threading.RLock()
|
|
|
|
self._devices = {}
|
|
|
|
|
|
|
|
def _get_session(self):
|
2019-12-01 23:35:05 +01:00
|
|
|
import gps
|
|
|
|
|
2019-08-03 02:06:06 +02:00
|
|
|
with self._session_lock:
|
|
|
|
if not self._session:
|
2023-09-24 16:54:43 +02:00
|
|
|
self._session = gps.gps(
|
|
|
|
host=self.gpsd_server, port=self.gpsd_port, reconnect=True
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
self._session.stream(gps.WATCH_ENABLE | gps.WATCH_NEWSTYLE)
|
|
|
|
|
|
|
|
return self._session
|
|
|
|
|
|
|
|
def _gps_report_to_event(self, report):
|
|
|
|
if report.get('class').lower() == 'version':
|
2023-09-24 16:54:43 +02:00
|
|
|
return GPSVersionEvent(
|
|
|
|
release=report.get('release'),
|
|
|
|
rev=report.get('rev'),
|
|
|
|
proto_major=report.get('proto_major'),
|
|
|
|
proto_minor=report.get('proto_minor'),
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
if report.get('class').lower() == 'devices':
|
|
|
|
for device in report.get('devices', []):
|
2023-09-24 16:54:43 +02:00
|
|
|
if device.get(
|
|
|
|
'path'
|
|
|
|
) not in self._devices or device != self._devices.get('path'):
|
2020-08-22 14:25:31 +02:00
|
|
|
# noinspection DuplicatedCode
|
2019-08-03 02:06:06 +02:00
|
|
|
self._devices[device.get('path')] = device
|
2023-09-24 16:54:43 +02:00
|
|
|
return GPSDeviceEvent(
|
|
|
|
path=device.get('path'),
|
|
|
|
activated=device.get('activated'),
|
|
|
|
native=device.get('native'),
|
|
|
|
bps=device.get('bps'),
|
|
|
|
parity=device.get('parity'),
|
|
|
|
stopbits=device.get('stopbits'),
|
|
|
|
cycle=device.get('cycle'),
|
|
|
|
driver=device.get('driver'),
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
if report.get('class').lower() == 'device':
|
2020-08-22 14:25:31 +02:00
|
|
|
# noinspection DuplicatedCode
|
2019-08-03 02:06:06 +02:00
|
|
|
self._devices[report.get('path')] = report
|
2023-09-24 16:54:43 +02:00
|
|
|
return GPSDeviceEvent(
|
|
|
|
path=report.get('path'),
|
|
|
|
activated=report.get('activated'),
|
|
|
|
native=report.get('native'),
|
|
|
|
bps=report.get('bps'),
|
|
|
|
parity=report.get('parity'),
|
|
|
|
stopbits=report.get('stopbits'),
|
|
|
|
cycle=report.get('cycle'),
|
|
|
|
driver=report.get('driver'),
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
if report.get('class').lower() == 'tpv':
|
2023-09-24 16:54:43 +02:00
|
|
|
return GPSUpdateEvent(
|
|
|
|
device=report.get('device'),
|
|
|
|
latitude=report.get('lat'),
|
|
|
|
longitude=report.get('lon'),
|
|
|
|
altitude=report.get('alt'),
|
|
|
|
mode=report.get('mode'),
|
|
|
|
epv=report.get('epv'),
|
|
|
|
eph=report.get('eph'),
|
|
|
|
sep=report.get('sep'),
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
|
|
|
|
def run(self):
|
|
|
|
super().run()
|
2023-09-24 16:54:43 +02:00
|
|
|
self.logger.info(
|
|
|
|
'Initialized GPS backend on {}:{}'.format(self.gpsd_server, self.gpsd_port)
|
|
|
|
)
|
2019-08-13 22:25:28 +02:00
|
|
|
last_event = None
|
2019-08-03 02:06:06 +02:00
|
|
|
|
|
|
|
while not self.should_stop():
|
|
|
|
try:
|
|
|
|
session = self._get_session()
|
|
|
|
report = session.next()
|
|
|
|
event = self._gps_report_to_event(report)
|
2023-09-24 16:54:43 +02:00
|
|
|
if event and (
|
|
|
|
last_event is None
|
|
|
|
or abs(
|
|
|
|
(last_event.args.get('latitude') or 0)
|
|
|
|
- (event.args.get('latitude') or 0)
|
|
|
|
)
|
|
|
|
>= self._lat_lng_tolerance
|
|
|
|
or abs(
|
|
|
|
(last_event.args.get('longitude') or 0)
|
|
|
|
- (event.args.get('longitude') or 0)
|
|
|
|
)
|
|
|
|
>= self._lat_lng_tolerance
|
|
|
|
or abs(
|
|
|
|
(last_event.args.get('altitude') or 0)
|
|
|
|
- (event.args.get('altitude') or 0)
|
|
|
|
)
|
|
|
|
>= self._alt_tolerance
|
|
|
|
):
|
2019-08-03 02:06:06 +02:00
|
|
|
self.bus.post(event)
|
2019-08-13 22:25:28 +02:00
|
|
|
last_event = event
|
2019-08-03 02:06:06 +02:00
|
|
|
except Exception as e:
|
|
|
|
if isinstance(e, StopIteration):
|
2023-09-24 16:54:43 +02:00
|
|
|
self.logger.warning(
|
|
|
|
'GPS service connection lost, check that gpsd is running'
|
|
|
|
)
|
2019-08-03 02:06:06 +02:00
|
|
|
else:
|
|
|
|
self.logger.exception(e)
|
|
|
|
|
|
|
|
self._session = None
|
|
|
|
time.sleep(self._fail_sleep_time)
|
|
|
|
|
|
|
|
|
|
|
|
# vim:sw=4:ts=4:et:
|