Communicating with the GPS
==========================

The code communicates with the GPS by sending and receiving specially formatted
sentences. The format used is the NMEA 0183 protocol specified by the National
Marine Electronics Association. This was designed for boat navigation and
control systems and is widely used by GPSs.

In general, you configure the device to send the sentences that you want at the
frequency you need and then receive a flow of GPS update messages.

Sentences received from the GPS module use the same format, irrespective of the
manufacturer. Sentences sent to the GPS module to control it, and answers to
these commands, are proprietary to the manufacturer.

**NOTE:** All of the example commands used in this documentation, and
the examples folder, are for the MediaTek 333X GPS chips used in Adafruit
products. Make sure to check the datasheet for your GPS chip if it is different.

Sentence format
---------------

$TAG[,DATA[,DATA...]]*hh<CR><LF>

* '$' is the opening delimiter
* TAG is the tag describing the type of message.

  * The tag for a proprietary (chipset specific) message is composed of

    * 'P' for proprietary.
    * 'ABC', a 3 letter code for the manufacturer, eg. 'MTK' for MediaTek.
    * 'CODE', a manufacturer specified code for the command or answer.
      *Note: This can be made up of letters and numbers and there is no
      required length.*

    'PMTK220' is the Mediatek command for setting the update rate.

    *Note: not all commands have an answer counterpart*

  * The tag for a received data sentence is of the form TTDDD, where:

    * TT is the talker sending the data. The list of talkers is large but we
      are only interested in ones starting with a 'G':

      * GA - Galileo (Europe)
      * GB - BeiDou (China)
      * GI - NavIC (India)
      * GL - GLONASS (Russia)
      * GP - GPS (US)
      * GQ - QZSS (Japan)
      * GN - GNSS, a combination of the above

    * DDD is the data type of the sentence, this determines how to decode it.
      Again, the list of data types is long but we are only interested in a
      few:

      * RMC - Recommended Minimum Navigation Information
      * GLL - Geographic Position - Latitude/Longitude
      * GGA - Global Positioning System Fix Data
      * VTG - Track made good and Ground speed *(not currently parsed)*
      * ZDA - Time & Date - UTC, day, month, year and local time zone *(not
        currently parsed)*
      * GSA - GPS `DOP
        <https://en.wikipedia.org/wiki/Dilution_of_precision_(navigation)>`_
        and active satellites
      * GSV - Satellites in view
      * GRS - GPS Range Residuals *(not currently parsed)*
      * GST - GPS Pseudorange Noise Statistics *(not currently parsed)*

* DATA is separated from the TAG by a comma and is a comma separated list of
  data. Proprietary commands, and answers, will specify on their datasheet what
  the list of data is. The normal sentences generated by GPS modules are
  specified by NMEA. An unofficial list is `here
  <https://gpsd.gitlab.io/gpsd/NMEA.html>`_.
* '*' is the end of data delimiter.
* hh is the 1-byte checksum of all characters between '$' and '*' in
  hexadecimal.
* <CR><LF> is the mandatory sentence terminator

Checksums
---------

When sending commands with the `send_command()` method it will add the
necessary delimiters and calculate the checksum for you, eg.

.. code-block:: python

  gps.send_command(b'PMTK220,1000')

When receiving answers or data from the GPS module, if you use the `update()`
method to poll the device it will reject any sentences with an invalid
checksum and then try to parse the data. However, you can choose to manually
pull data with the :py:meth:`~adafruit_gps.GPS.read` or
:py:meth:`~adafruit_gps.GPS.readline` which will do no parsing or checksum
validation.


Initial Configuration
---------------------

.. code-block:: python

  import board
  import busio
  import adafruit_gps

  USE_UART = True # Change this to False to connect via I2C

  if USE_UART:
      # Create a serial connection for the GPS connection.
      uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10)

      # for a computer, use the pyserial library for uart access
      # import serial
      # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10)

      # Create a GPS module instance.
      gps = adafruit_gps.GPS(uart, debug=False)  # Use UART/pyserial
  else:
      # If using I2C, we'll create an I2C interface to talk to using default pins
      i2c = board.I2C()

      # Create a GPS module instance.
      gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False)  # Use I2C interface

Configuring the GPS
-------------------

.. code-block:: python

  # Set update rate to 1000 milliseconds (1Hz)
  gps.send_command(b"PMTK220,1000")

  # Ask for specific data to be sent.
  #                          A B C D E F G H                   I
  gps.send_command(b'PMTK314,1,1,5,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,0')

  #   A - send GLL sentences
  #   B - send RMC sentences
  #   C - send VTG sentences
  #   D - send GGA sentences
  #   E - send GSA sentences
  #   F - send GSV sentences
  #   G - send GRS sentences
  #   H - send GST sentences
  #   I - send ZDA sentences

  # The number is how often to send the sentence compared to the update frequency.
  # If the update frequency is 500ms and the number is 5, it will send that message
  # every 2.5 seconds.

**Note:** Be aware that some data types send multiple sentences per update. So
if you ask for 5 different types of data at 1Hz, you need to be able to handle
at least 10 sentences per second. If the data is not read fast enough, the
internal buffer and backlog behaviour is not specified.

Poll for data
-------------

.. code-block:: python

  while True:
      if gps.update():
          # A valid sentence was received - do something
          if gps.has_fix:
              print(f"{gps.latitude:.6f},{gps.longitude:.6f}")
          else:
              print("Waiting for a fix...")
      else:
          # No valid sentence was received, wait a moment.
          time.sleep(100)

The `update()` call takes care of reading data from the device and parsing it
into usable data. This can then be accessed using the property accessors, eg.
`has_fix`, `datetime`, latitude, longitude etc.

Selected Data Types
===================

RMC - Recommended Minimum Navigation Information
------------------------------------------------
::

           1         2 3       4 5        6 7   8   9   10  11 12
           |         | |       | |        | |   |   |    |   | |
    $--RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh
    $GNRMC,001031.00,A,4404.13993,N,12118.86023,W,0.146,,100117,,,A*7B

1. Time (UTC)
2. Status, A = Valid, V = Warning
3. Latitude
4. N or S
5. Longitude
6. E or W
7. Speed over ground, knots
8. Track made good, degrees true
9. Date, ddmmyy
10. Magnetic Variation, degrees
11. E or W
12. FAA mode indicator (NMEA 2.3 and later)
13. Checksum

GGA - Global Positioning System Fix Data
----------------------------------------
::

           1         2       3 4        5 6 7  8   9  10 11 12 13  14   15
           |         |       | |        | | |  |   |   | |   | |   |    |
    $--GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
    $GNGGA,001043.00,4404.14036,N,12118.85961,W,1,12,0.98,1113.0,M,-21.3,M,,*47

1. Time (UTC)
2. Latitude
3. N or S (North or South)
4. Longitude
5. E or W (East or West)
6. GPS Quality Indicator:

   0. Fix not available
   1. GPS fix
   2. Differential GPS fix
   3. PPS fix (values above 2 are NMEA 0183 v2.3 features)
   4. Real Time Kinematic
   5. Float RTK
   6. Estimated (dead reckoning)
   7. Manual input mode
   8. Simulation mode

7. Number of satellites in view, 00 - 12
8. Horizontal dilution of precision
9. Antenna altitude above/below mean-sea-level (geoid)
10. Units of antenna altitude, meters
11. Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level below ellipsoid
12. Units of geoidal separation, meters
13. Age of differential GPS data, time in seconds since last SC104 type 1 or 9 update, empty field when DGPS is not used
14. Differential reference station ID, 0000-1023
15. Checksum

Info about NMEA taken from `here (2001)
<https://www.tronico.fi/OH6NT/docs/NMEA0183.pdf>`_.
and `here (2021)
<https://gpsd.gitlab.io/gpsd/NMEA.html>`_
