Source code for qwiic_titan_gps

#-----------------------------------------------------------------------------
# qwiic_titan_gps.py
#
# Python library for the SparkFun's GPS Breakout - XA1110. 
#
# SparkFun GPS Breakout - XA1110
#   https://www.sparkfun.com/products/14414
#
#------------------------------------------------------------------------
#
# Written by SparkFun Electronics, November 2019
#
# This python library supports the SparkFun Electroncis Qwiic sensor/board
# ecosystem.
#
# More information on Qwiic is at https://www.sparkfun.com/qwiic
#
# Do you like this library? Help support SparkFun. Buy a board!
#==================================================================================
# Copyright (c) 2019 SparkFun Electronics
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#==================================================================================
#
# The goal of this is to keep the public interface pythonic, but internal is
# internal and code is kept as close to its Arduino source library as possible.
#
# pylint: disable=line-too-long, bad-whitespace, invalid-name, too-many-public-methods
#

"""

qwiic_titan_gps
===============
Python library for `SparkFun GPS Breakout -
XA1110 <https://www.sparkfun.com/products/14414>`_

This python package is a port of the existing `SparkFun GPS Arduino Library <https://github.com/sparkfun/SparkFun_I2C_GPS_Arduino_Library>`_.

This package can be used in conjunction with the overall `SparkFun Qwiic Python Package <https://github.com/sparkfun/Qwiic_Py>`_

New to Qwiic? Take a look at the entire `SparkFun Qwiic ecosystem <https://www.sparkfun.com/qwiic>`_.

"""
from __future__ import print_function,division

import sys
import qwiic_i2c
import pynmea2

#======================================================================
# NOTE: For Raspberry Pi
#======================================================================
# For this sensor to work on the Raspberry Pi, I2C clock stretching
# must be enabled.
#
# To do this:
#   - Login as root to the target Raspberry Pi
#   - Open the file /boot/config.txt in your favorite editor (vi, nano ...etc)
#   - Scroll down until the block that contains the following is found:
#           dtparam=i2c_arm=on
#           dtparam=i2s=on
#           dtparam=spi=on
#   - Add the following line:
#           # Enable I2C clock stretching
#           dtparam=i2c_arm_baudrate=10000
#
#   - Save the file
#   - Reboot the Raspberry Pi
#======================================================================

def __checkIsOnRPi():

    # Are we on a Pi or Linux?

    if sys.platform not in ('linux', 'linux2'):
        return False

    # we can find out if we are on a RPI by looking at the contents
    # of /proc/device-tree/compatible

    try:
        with open('/proc/device-tree/compatible', 'r') as fCompat:

            systype = fCompat.read()

            return systype.find('raspberrypi') != -1
    except IOError:
        return False

# check if stretching is set if on a Raspberry Pi. 
#
def _checkForRPiI2CClockStretch():

    # Check if we're on a Raspberry Pi first.
    if not __checkIsOnRPi():
        return

    # read the boot config file and see if the clock stretch parameter is set
    try:
        with open('/boot/config.txt') as fConfig:

            strConfig = fConfig.read()
            for line in strConfig.split('\n'):
                if line.find('i2c_arm_baudrate') == -1:
                    continue

                # start with a comment?
                if line.strip().startswith('#'):
                    break

                # is the value less <= 10000
                params = line.split('=')
                if int(params[-1]) <= 10000:
                    # Stretching is enabled and set correctly.
                    return

                break
    except IOError:
        pass

    # if we are here, then we are on a Raspberry Pi and Clock Stretching isn't
    # set correctly.
    # Print out a message!

    print("""
============================================================================
 NOTE:

 For the SparkFun GPS Breakout to work on the Raspberry Pi, I2C clock stretching
 must be enabled.

 The following line must be added to the file /boot/config.txt

    dtparam=i2c_arm_baudrate=10000

 For more information, see the note at:
          https://github.com/sparkfun/qwiic_ublox_gps_py
============================================================================
        """)

# Define the device name and I2C addresses. These are set in the class definition
# as class variables, making them available without having to create a class instance.
# This allows higher level logic to rapidly create a index of Qwiic devices at
# runtime.
#
# The name of this device
_DEFAULT_NAME = "Qwiic Titan GPS"

# Some devices have multiple available addresses - this is a list of these addresses.
# NOTE: The first address in this list is considered the default I2C address for the
# device.
_AVAILABLE_I2C_ADDRESS = [0x10]

[docs]class QwiicTitanGps(object): """ QwiicTitanGps :param address: The I2C address to use for the device. If not provided, the default address is used. :param i2c_driver: An existing i2c driver object. If not provided a driver object is created. :return: The Qwiic Titan GPS device object. :rtype: Object """ device_name = _DEFAULT_NAME available_addresses = _AVAILABLE_I2C_ADDRESS MAX_I2C_BUFFER = 32 MAX_GPS_BUFFER = 255 _i2c = qwiic_i2c.getI2CDriver() _RPiCheck = False gnss_messages = { 'Time' : 0, 'Latitude' : 0, 'Lat' : 0, 'Lat_Direction' : "", 'Longitude' : 0, 'Long' : 0, 'Long_Direction' : "", 'Altitude' : 0, 'Altitude_Units' : "", 'Sat_Number' : 0, 'Geo_Separation' : 0, 'Geo_Sep_Units' : "", } def __init__(self, address=None, i2c_driver=None): # As noted above, to run this device on a Raspberry Pi, # clock stretching is needed. # # Lets check if it's enabled. This is done only once in # the session if not QwiicTitanGps._RPiCheck: _checkForRPiI2CClockStretch() QwiicTitanGps._RPiCheck = True # Did the user specify an I2C address? self.address = address if address is not None else self.available_addresses[0] # load the I2C driver if one isn't provided if i2c_driver is None: self._i2c = qwiic_i2c.getI2CDriver() if self._i2c is None: print("Unable to load I2C driver for this platform.") return else: self._i2c = i2c_driver # ----------------------------------
[docs] def is_connected(self): """ Determine if a GPS device is connected to the system.. :return: True if the device is connected, otherwise False. :rtype: bool """ return qwiic_i2c.isDeviceConnected(self.address)
connected = property(is_connected)
[docs] def begin(self): """ Initialize the data transmission lines. :return: Returns True on success, False on failure :rtype: boolean """ return self.is_connected()
[docs] def get_raw_data(self): """ This function pulls GPS data from the module 255 bytes at a time. :return: A string of all the GPS data. :rtype: String """ raw_sentences = "" buffer_tracker = self.MAX_GPS_BUFFER raw_data = [] while buffer_tracker != 0: if buffer_tracker > self.MAX_I2C_BUFFER: raw_data.extend(self._i2c.readBlock(self.address, 0x00, self.MAX_I2C_BUFFER)) buffer_tracker = buffer_tracker - self.MAX_I2C_BUFFER if raw_data[0] == 0x0A: break elif buffer_tracker < self.MAX_I2C_BUFFER: raw_data.extend(self._i2c.readBlock(self.address, 0x00, buffer_tracker)) buffer_tracker = 0 if raw_data[0] == 0x0A: break for raw_bytes in raw_data: raw_sentences = raw_sentences + chr(raw_bytes) return raw_sentences
[docs] def prepare_data(self): """ This function seperates raw GPS data from the module into sentences of GNSS data. :return: A list of all the gathered GPS data. :rtype: List """ sentences = self.get_raw_data() clean_gnss_list = [] complete_sentence_list = [] gnss_list = sentences.split('\n') for sentence in gnss_list: if sentence is not '': clean_gnss_list.append(sentence) for index,sentence in enumerate(clean_gnss_list): if not sentence.startswith('$') and index is not 0: joined = clean_gnss_list[index - 1] + sentence complete_sentence_list.append(joined) else: complete_sentence_list.append(sentence) return complete_sentence_list
[docs] def get_nmea_data(self): """ This function takes a list of GNSS sentences and uses the pynmea2 parser to parse the data. :return: Returns True on success and False otherwise :rtype: Boolean """ gps_data = self.prepare_data() msg = "" for sentence in gps_data: try: msg = pynmea2.parse(sentence) self.add_to_gnss_messages(msg) except pynmea2.nmea.ParseError: pass return True
[docs] def add_to_gnss_messages(self, sentence): """ This function takes parsed GNSS data and assigns them to the respective dictionary key. :return: Returns True :rtype: Boolean """ try: self.gnss_messages['Time'] = sentence.timestamp self.gnss_messages['Lat_Direction'] = sentence.lat_dir self.gnss_messages['Long_Direction'] = sentence.lon_dir self.gnss_messages['Latitude'] = sentence.latitude self.gnss_messages['Lat'] = sentence.lat self.gnss_messages['Longitude'] = sentence.longitude self.gnss_messages['Long'] = sentence.lon self.gnss_messages['Altitude'] = sentence.altitude self.gnss_messages['Altitude_Units'] = sentence.altitude_units self.gnss_messages['Sat_Number'] = sentence.num_sats self.gnss_messages['Geo_Separation'] = sentence.geo_sep self.gnss_messages['Geo_Sep_Units'] = sentence.geo_sep_units except KeyError: pass except AttributeError: pass return True