"""
`Gps`
====================================================
GPS utilities module. Can interact, control and read data from a GPS module,
such as latitude, longitude, and more.
"""
import busio
import digitalio
import board
import adafruit_gps
[docs]class Gps(adafruit_gps.GPS):
"""Creates the Gps interface
:param boolean debug: print errors if they occur , optionnal
Example usage:
.. code-block:: python
from elevages_numeriques.gps import *
gps = Gps() # same as gps = Gps(debug=False)
gps.update()
print(gps.header)
print(gps)
"""
_KEYS = (
'datetime', 'latitude', 'longitude', 'altitude', 'speed', 'fix_quality', 'satellites', 'horizontal_dilution')
_DIS_PIN = None # Sphinx autodoc will throw a NameError when trying to get board.D10
if hasattr(board, 'D10'): # Do that instead to allow compilation without the correct microcontroler
_DIS_PIN = digitalio.DigitalInOut(board.D10)
else:
_DIS_PIN = 10
@property
def header(self):
"""
Property returning the list of currently enabled GPS data fields in CSV format
:return: A semicolon-separated list of the enabled fields
"""
return ';'.join(self._fields)
def __init__(self, debug=False):
uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=3000)
super(Gps, self).__init__(uart, debug=debug)
self._fields = list()
self._fields.extend(Gps._KEYS[0:3])
Gps._DIS_PIN.direction = digitalio.Direction.OUTPUT
Gps._DIS_PIN.value = False
def __str__(self):
content = list()
for key in self._fields:
if key == 'datetime':
content.append(self._timestamp_to_datetime())
elif key == 'speed':
content.append(self._knots_to_kmh())
elif key == 'altitude':
content.append(self.altitude_m)
else:
content.append(getattr(self, key))
for i in range(len(content)):
content[i] = str(content[i])
return ';'.join(content)
[docs] def enable(self, val=True):
"""
Enables or disables the GPS module to save energy
:param bool val: Enables the module if True, disables it otherwise
"""
Gps._DIS_PIN.value = not val
[docs] def update(self):
"""
Fetches the latest data coming from the GPS module
Call this method at least once before reading a GPS position
:return: True if update succeeded, False if it failed
"""
try:
return super(Gps, self).update()
except UnicodeError:
return False
[docs] def set_logging(self, field_name, enable):
"""
Enables or disables the logging output for the given field
:param string field_name: The field name, examples below
:param bool enable: Wether to enable or not the given field
Available fields:
.. code-block:: python
'datetime' # Current date and time (formatted as 'day/month/year hour:min:sec')
# In UTC time (that's Greenwich timezone)
'latitude' # Current device latitude
'longitude' # Current device longitude
'altitude' # Current device altitude (in m)
'speed' # Current device speed (in km/h)
'fix_quality' # Quality class of the positionning signal
# 0 = invalid
# 1 = Standard GPS fix
# 2 = Differential GPS fix (super precise)
'satellites' # Number of visible satellites, the more the better.
# GPS positionning is impossible below 3 satellites
'horizontal_dilution' # How spread are the satellites in the sky ? The lower the better
# A high dilution can lead to imprecisions in the GPS signal
"""
if field_name == '*' or field_name == 'all': # wildcard : enable or disable ALL fields
if enable:
self._fields = list()
self._fields.extend(Gps._KEYS[0:len(Gps._KEYS)])
else:
self._fields = list()
return None # all fields were added or deleted, stop here
# One-by-one enabling or disabling field
if not enable and field_name in self._fields:
self._fields.remove(field_name)
elif enable and field_name not in self._fields:
if field_name not in Gps._KEYS:
if self.debug:
print('unknown field {}'.format(field_name))
raise RuntimeError('unknown field {}'.format(field_name))
else:
self._fields.append(field_name)
def _timestamp_to_datetime(self):
"""
Formats a struct_time into human-readable datetime
"""
return '{}/{}/{} {}:{}:{}'.format(
self.timestamp_utc.tm_mday,
self.timestamp_utc.tm_mon,
self.timestamp_utc.tm_year,
self.timestamp_utc.tm_hour,
self.timestamp_utc.tm_min,
self.timestamp_utc.tm_sec)
def _knots_to_kmh(self):
try:
return self.speed_knots / 1852
except TypeError:
return 0