242 lines
8.2 KiB
ReStructuredText
242 lines
8.2 KiB
ReStructuredText
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>`_
|