Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

com-diag-hazer

Parse NMEA strings and other typical output from GNSS devices.

Thanks

Say Thanks!

Copyright

Copyright 2017-2020 by the Digital Aggregates Corporation, Colorado, USA.

License

Licensed under the terms in LICENSE.txt.

Contact

Chip Overclock
Digital Aggregates Corporation/PNT Division
3440 Youngfield Street, Suite 209
Wheat Ridge CO 80033 USA
http://www.diag.com
mailto:coverclock@diag.com

Abstract

This file is part of the Digital Aggregates Corporation Hazer project. Hazer is a toolkit for interpreting data streams typically generated by GPS and other GNSS receivers and output over serial(ish) ports, including USB. The Hazer project includes parsers for three different input formats - NMEA, UBX, and RTCM - and interprets many of the data records represented in those formats into C structures.

For purposes of commenting in my C code, I find it useful to refer to NMEA records as "sentences" (terminology consistent with the NMEA standard), UBX records as "packets", and RTCM records as "messages".

NMEA sentences are printable ASCII messages that conform to the National Marine Electronics Association (NMEA) 0183 4.10 specification. NMEA 0183 describes the output produced by most receiving devices of Global Navigation Satellite Systems (GNSS), of which the U.S. Global Positioning System (GPS), formerly known as Navstar, is an example. The NMEA software stack is named Hazer (same as the Git repository).

UBX packets are in a proprietary binary data format generated by devices made by U-Blox, a Swiss designer and manufacturer of GNSS receivers. UBX is supported by a parallel stack called Yodel.

RTCM messages are in a binary data format used in Differential GNSS (DGNSS) that makes use of Real-Time Kinematics (RTK). This allows communicating GNSS receivers to compare notes and potentially, over time, achieve very high degrees of precision down to centimeters. RTCM was developed by the Radio Technical Commision for Maritime services (RTCM). RTCM is supported by a parallel stack called Tumbleweed.

More broadly: Hazer used GNSS to do real-time moving map displays using Google Earth; Yodel used GNSS with high precision GNSS and integrated inertial measurement features of specific u-blox devices; and Tumbleweed used Differental GNSS with specific u-blox devices. All three projects reside in the Hazer repository.

When I use the term "Hazer", I am not very good about distinguishing whether I am talking about the software stack, the project, or the entire repo.

Hazer includes a gpstool application as a kind of Swiss Army knife for dealing with various GNSS devices. gpstool accepts data streams from standard input, from serial(ish) devices, or from a UDP socket, and can send validated data to a remote UDP socket, write it to a device, and display the interpreted information.

Hazer also includes a rtktool application that is a point-to-multipoint GNSS router that can receive data via a UDP socket from a remote gpstool and forward it to one or more remote gpstools via UDP. This is used along with gpstool to implement a DGNSS system using a stationary base station in survey mode to provide real-time corrections to one or more mobile rovers. This project, called Tumblweed (same as the RTCM software stack), does not use the Networked Transport of RTCM via Internet Protocol (Ntrip), but instead uses its own trivial data format consisting of raw RTCM messages preceeded by a four-byte sequence number carried over UDP datagrams.

If you're wondering why I don't use the excellent open source GPS Daemon (gpsd) and its GPS Monitor (gpsmon), the answer is: I have, in many projects, typically in conjunction with the open source NTPsec Daemon (ntpd). Hazer was developed as an excuse for me to learn in detail more about how GPS works and how NMEA and UBX sentences are formatted (because I only learn by doing), and also to develop an NMEA and UBX parsing library that I can incorporate into the kinds of embedded systems I am frequently called to work upon. Hazer and gpstool have also turned out to be really useful tools for testing and evaluating GPS devices.

This software is an original work of its author and the Digital Aggregates Corporation PNT division.

Dependencies

Diminuto

The Hazer library and its utilities depend on my Diminuto library. Diminuto is a general purpose C-based systems programming library that supports serial port configuration, socket-based communication, and a passle of other useful stuff. gpstool and rtktool are excellent examples of how to leverage Diminuto to get a lot done in not so much C code. I use Diminuto in virtually all of my C-based projects, and sometimes in other languages too that support C-linkage. Portions of Diminuto have also shipped in products from several of my clients.

Building

These days most Linux distributions do not include the basic tools to build C and C++ software (despite having themselves been developed using them).

sudo apt-get install gcc
sudo apt-get install g++
sudo apt-get install make

Documentation

If you want to make documentation, doxygen and related tools will need to be installed.

sudo apt-get install doxygen
sudo apt-get install texlive-base
sudo apt-get install texlive-fonts-recommended
sudo apt-get install texlive-fonts-extra
sudo apt-get install texlive-science

You can make HTML, man page, and PDF documentation based on the Doxygen comments embedded in the source code by using the following make targets.

make documentation
make manuals

You can find the resulting documentation in the following directories. The variable TARGET will be replaced by "host" unless you are using some special Makefile configuration (for example, for cross-compilation).

out/${TARGET}/doc/html
out/${TARGET}/doc/man
out/${TARGET}/doc/pdf

The gpstool application is a good example of how to use the Hazer, Tumbleweed, and Yodel features. (It is also a good example of how to use the features of the Diminuto library.)

Workflow

For my own development workflow, I have also found it useful to install other packages that are often optional in various Linux/GNU distros. Your mileage may vary.

sudo apt-get install openssh-server
sudo apt-get install git
sudo apt-get install vim
sudo apt-get install screen

bc

If you want to use several of the post-processing scripts for the optional CSV output file, you may need to install the standard command line (lab) bench calculator (bc) utility.

sudo apt-get install bc

inotifywait

If you want to run the peruse script, which several other scripts make use of, you may need to install the inotify tools package.

sudo apt-get install inotify-tools

LFS

The dat directory is where I collect data from tests of Hazer (moving map), Tumblweeed (differential GNSS), and Yodel (integrated intertial measurement). Some of the comma separated value (CSV) files are too large (over a hundred megabytes uncompressed) to be pushed to GitHub in the normal way. These files use GitHub's Large File Storage (LFS).

sudo apt-get install git-lfs

Unless you install GitHub's LFS feature (which manifests as the "git lfs" command), files stored in LFS will appear to have contents that look something like this.

version https://git-lfs.github.com/spec/v1
oid sha256:bcb222ac76e51cad8010086754d4cadeeebd0161b51029d6870a0d806db5f42f
size 137136500

You have to initialize LFS once per machine for every repo it is used in.

git lfs install

You can also safely skip installing LFS, and ignore the large data files, to use this repo.

geodesic

The geodesic utility is based on algorithms described in

Charles F. F. Karney, "Algorithms for geodesics", Journal for Geodesy, 2013-01, 87.1, pp. 43..55

and uses one .c file and one .h file, included in this repository, that was written by Mr. Karney and which can be found at

https://geographiclib.sourceforge.io

and which is licensed under the MIT license.

locale

The gpstool, rtktool, and mapstool programs depend on running in a POSIX locale that allows the use of Unicode characters like the degree symbol. Locales like "POSIX" and "C" don't support this, at least not on the systems I have. Locales like "en_US.UTF-8" work okay. Your mileage may vary.

dialout

GNSS modules typically express a serial port - either an actual serial port, a USB serial port emulation ('''ttyUSB'''), or a modem emulation ('''ttyACM'''). The easiest way to get read/write access to such a port as a non-root user is by adding that user to the '''dialout''' group. The user may have to log out and back in again to have this take effect.

sudo adduser pi dialout

Versioning

Both Hazer and Diminuto are complex enough that I moved to a "master" and "develop" dual branch model of development. I make and test major changes in the "develop" branch, and when I think I have a stable release, I merge "develop" into the "master" branch. I still make what I consider to be minor changes in the master branch.

I still try to tag releases with a three-number tuple that is defined in the build Makefile for each project. Release numbers, e.g. 22.2.1, consist of a major number (22), a minor number (2), and a build number (1). The major number changes when I've made a change significant enough that I think applications using the library will likely need to be changed, even though they may compile. The minor number changes when I've added new features or functionality, but existing features and functionality haven't changed. The build number changes when I've fixed a bug so that existing features or functionality works as described (even though this may break workarounds in applications).

The major and minor numbers are incorporated into the name of the shared object (dynamic link library) produced by the build.

The vintage is the date and time of the most recent build in UTC and expressed in ISO8601 format.

The revision is the Git commit number.

The release, revision, vintage, and a bunch of other stuff, are embedded as modules in the Hazer and Diminuto libraries. Those modules can be linked into an application. Each project has a binary executable named vintage that displays this information.

The gpstool and rtktool applications can be run with a -V flag to display the release, vintage, and revision of the Hazer library with which they were built.

Repositories

https://github.com/coverclock/com-diag-hazer

https://github.com/coverclock/com-diag-diminuto

Sentences

Hazer recognizes the following received NMEA sentences.

  • GGA - Global Positioning System Fix Data (NMEA 0183 Version 4.10 p. 68)
  • GLL - Geographic Position - Latitude/Longitude (NMEA 0183 Version 4.10 p. 87)
  • GSA - GNSS DOP and Active Satellites (NMEA 0183 Version 4.10 p. 92)
  • GSV - GNSS Satellites In View (NMEA 0183 Version 4.10 p. 96)
  • RMC - Recommended Minimum Specific GNSS Data (NMEA 0183 Version 4.10 p. 113)
  • TXT - Text Transmission (NMEA 0183 Version 4.10 p. 124)
  • VTG - Course Over Ground & Ground Speed (NMEA 0183 Version 4.10 p. 127)

Yodel recognizes the following received UBX messages.

  • UBX-ACK-ACK - Acknowledge UBX input and indicate success. (u-blox 9 p. 38)
  • UBX-ACK-NAK - Acknowledge UBX input and indicate failure. (u-blox 9 p. 38)
  • UBX-CFG-VALGET - Get Configuration Value. (u-blox 9 p. 85)
  • UBX-MON-COMMS - Monitor utilization of communication ports. (u-blox 9 p. 131)
  • UBX-MON-HW - Monitor Hardware to detect jamming. (u-blox 8 R15 p. 285)
  • UBX-MON-VER - Monitor hardware and software Version. (u-blox 9 p. 139)
  • UBX-NAV-ATT - Report IMU attitude relative to geodetic. (u-blox 8, p. 317)
  • UBX-NAV-HPPOSLLH - Report high precision lat/lon and height. (u-blox 9 p. 145)
  • UBX-NAV-ODO - Report internal odometer. (u-blox 8 p. 327)
  • UBX-NAV-PVT - Report Position, Velocity, Time solution. (u-blox 8 p. 332)
  • UBX-NAV-STATUS - Report Status to detect spoofing. (u-blox 8 R15 p. 316)
  • UBX-NAV-SVIN - Report Survey-in status on DGNSS Base. (u-blox 9 p. 163)
  • UBX-RXM-RTCM - RXM RTCM input status on DGNSS Rover. (u-blox 9 p. 181)

Tumbleweed recognizes RTCM messages with a valid CRC but does not process their contents. As a special case, an RTCM message with a zero payload length is used by gpstool and rtktool as a keep alive message.

Talkers

Hazer recognizes the following talkers as belonging to the corresponding constellations and systems.

These talkers have been observed in the wild coming from actual GPS receivers.

  • GA - Galileo (as in Galileo Galilei) - EU
  • GB - BeiDou 2 (as in The Big Dipper) a.k.a. COMPASS - China
  • GL - Globalnaya Navigazionnaya Sputnikovaya Sistema (GLONASS) - Russia
  • GN - Global Navigation Satellite System (GNSS) - Generic
  • GP - Global Positioning System (GPS) a.k.a. Navstar GPS - USA

Support for these talkers has been unit tested but the talkers have never been observed in the wild.

  • BD - BeiDou (as in The Big Dipper) - China
  • QZ - Quasi-Zenith Satellite System (QZSS) - Japan

Identifiers

Receivers that implement the GSA System ID field introduced in NMEA 4.10 may reuse satellite identifiers. For example, I see GPS and Galileo both using identifier 9 on the U-Blox 9 receiver. Despite NMEA and vendor documentation to the contrary, I do not find the satellite identifer convention reliable and only use it as a last resort on older receivers that do not implement the System ID field to identify the constellation. Hazer recognizes the following satellite identifiers in the GSA and GSV messages.

These satellite identifiers have been observed in the wild coming from actual GPS receivers.

  • GPS - 1..32
  • SBAS - 33..64
  • GLONASS - 65..96

Support for these satellite identifiers has been unit tested but has never been exercised using actual GPS receivers.

  • IMES - 173..182
  • QZSS - 193..197
  • BeiDou - 201..235
  • Galileo - 301..336
  • BeiDou 2 - 401..437

Devices

Hazer has been successfully tested with the following GPS chipsets.

  • Quectel L80-R
  • SiRF Star II
  • SiRF Star III
  • SiRF Star IV
  • U-Blox 6
  • U-Blox 7
  • U-Blox 8
  • U-Blox 9

Hazer has been successfully tested with the following serial-to-USB chipsets used by GPS devices.

  • Cygnal Integrated Products
  • FTDI
  • Prolific
  • U-Blox (apparently integrated into the GPS chip itself)

Hazer has been successfully tested with the following GPS receivers.

  • Ardusimple SimpleRTK2B (U-Blox 9, UBX-ZED-F9P, 230400 8N1, v1516p01a9, ttyACM, 1Hz) [8] [10]
  • Eleduino Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) [2]
  • Ettus Research (National Instruments) USRP B210, GNURadio 3.7.11, GNSS-SDR 0.0.10 [9]
  • Garmin GLO (unknown, Bluetooth, N/A, rfcomm, 10Hz) [4]
  • Generic Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) [2]
  • GlobalSat BU-353S4-5Hz (SiRF Star IV/Prolific, 115200 8N1, v067Bp2303, ttyUSB, 5Hz) [0]
  • GlobalSat BU-353S4 (SiRF Star IV/Prolific, 4800 8N1, v067Bp2303, ttyUSB, 1Hz) [0] [1]
  • GlobalSat BU-353W10 (U-Blox 8, UBX-M8030, 9600 8N1, v1546p01a8, ttyACM, 1Hz) [0] [1] [2] [4] [8] [11]
  • GlobalSat ND-105C (SiRF Star III/Prolific, 4800 8N1, v067Bp2303, ttyUSB, 1Hz) [0]
  • Jackson Labs Technologies CSAC GPSDO (U-Blox UBX-LEA-6T, 115200 8n1, N/A, ttyACM, 1Hz)
  • MakerFocus USB-Port-GPS (Quectel L80-R/Cygnal, 9600 8N1, v10C4pEA60, ttyUSB, 1Hz) [2] [6]
  • NaviSys GR-701W (U-Blox 7/Prolific, 9600 8N1, v067Bp2303, ttyUSB, 1Hz) [5] [7] [8]
  • Pharos GPS-360 (SiRF Star II/Prolific, 4800 8N1, v067BpAAA0, ttyUSB, 1Hz) [3]
  • Pharos GPS-500 (SiRF Star III/Prolific, 4800 8N1, v067BpAAA0, ttyUSB, 1Hz) [3]
  • Sourcingbay GM1-86 (U-Blox 7, 9600 8n1, p1546v01A7, ttyACM, 1Hz) [2]
  • SparkFun GPS Dead Reckoning gen 8 (U-Blox 8, UBX-NEO-M8U, 115200 8N1, v1546p01a8, ttyACM, 1Hz) [4] [8] [12]
  • SparkFun GPS NEO-M8N (U-Blox 8, UBX-NEO-M8N, 115200 8N1, v1546p01a9, ttyACM, 1Hz) [8] [10]
  • SparkFun GPS-RTK2 (U-Blox 9, UBX-ZED-F9P, 230400 8N1, v1516p01a9, ttyACM, 1Hz) [8] [10] [11]
  • SparkFun GPS Dead Reckoning gen 9 (U-Blox 9, UBX-ZED-F9R, 230400 8N1, v1516p01a9, ttyACM, 1Hz) [8] [10] [12]
  • Stratux Vk-162 Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) [2]
  • TOPGNSS GN-803G (U-Blox 8, UBX-M8030-KT, 9600 8N1, v1546p01a8, ttyACM, 1Hz) [2] [4] [8]
  • Uputronics Raspberry Pi GPS Expansion Board v4.1 (U-Blox 8, M8, 9600 8n1, N/A, ttyAMA, 1Hz) [4]

Footnotes:

[0] GlobalSat is the company formerly known as USGlobalSat.
[1] An excellent all around GPS receiver easily acquired from numerous sources.
[2] Emits all sorts of interesting stuff in unsolicited $GPTXT or $GNTXT sentences.
[3] Install udev rules in overlay to prevent ModemManager from toying with device.
[4] Receives GPS (U.S.) and GLONASS (Russia) constellations concurrently.
[5] Receives GPS (U.S.) or GLONASS (Russia) constellations via configuration.
[6] Supports One Pulse Per Second (1PPS) by toggling digital output pin.
[7] Supports One Pulse Per Second (1PPS) by toggling Data Carrier Detect (DCD).
[8] Supports UBX.
[9] A software defined radio (SDR).
[10] Receives GPS (U.S.), GLONASS (Russia), Galileo (EU), and COMPASS (China) concurrently.
[11] This is a GNSS receiver against which I regression test Hazer.
[12] Has integrated Inertial Measurement Unit (IMU).

Platforms

Various releases of Hazer have been tested on one or more the following targets and platforms.

Dell OptiPlex 7040
Intel Core i7-6700T x86_64 @ 2.8GHz x 4 x 2
Ubuntu 14.04.4 "Trusty Tahr"
Linux 4.2.0
gcc 4.8.4

Intel NUC5i7RYH
Intel Core i7-5557U x86_64 @ 3.10GHz x 2 x 2
Ubuntu 16.04.2 "Xenial Xerus"
Linux 4.10.0
gcc 5.4.0

Intel NUC5i7RYH
Intel Core i7-5557U x86_64 @ 3.10GHz x 2 x 2
Ubuntu 18.04.1 "Bionic Beaver"
Linux 4.15.0
gcc 7.3.0

Raspberry Pi 2 Model B
Broadcom BCM2836 Cortex-A7 ARMv7 @ 900MHz x 4
Raspbian 8.0 "Jessie"
Linux 4.4.34
gcc 4.9.2

Raspberry Pi 3 Model B
Broadcom BCM2837 Cortex-A53 ARMv7 @ 1.2GHz x 4
Raspbian 8.0 "Jessie"
Linux 4.4.34
gcc 4.9.2

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 9.4 "Stretch"
Linux 4.14.34
gcc 6.3.0

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 9.8 "Stretch"
Linux 4.14.98
gcc 6.3.0
DGNSS Base (Generation 1)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 9.8 "Stretch"
Linux 4.14.98
gcc 6.3.0
DGNSS Rover (Generation 1)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 9.9 "Stretch"
Linux 4.19.42
gcc 6.3.0
DGNSS Router (Generation 2)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 10 "Buster"
Linux 4.19.50
gcc 8.3.0
DGNSS Base (Generation 1)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 10 "Buster"
Linux 4.19.50
gcc 8.3.0
DGNSS Rover (Generation 1)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 10 "Buster"
Linux 4.19.50
gcc 8.3.0
DGNSS Base (Generation 1)

Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 10 "Buster"
Linux 4.19.50
gcc 8.3.0
DGNSS Router (Generation 2)

Intel NUC7i7BNH
Intel Core i7-7567U x86_64 @ 3.50GHz x 2 x 2
Ubuntu 16.04.5 "Xenial Xerus"
Linux 4.15.0
gcc 5.4.0

Intel NUC7i7BNH
Intel Core i7-7567U x86_64 @ 3.50GHz x 2 x 2
Ubuntu 19.04 "Disco Dingo"
Linux 5.0.0
gcc 8.3.0

Raspberry Pi 4 Model B
ARMv8 64-bit
Broadcom BCM2711 Cortex-A72 ARMv8 @ 1.5GHz x 4
Raspbian 10 "Buster"
Linux 4.19.58
gcc 8.3.0

VM running in VMware Workstation 15 Pro under Windows 10
Intel x86_64 64-bit
Intel Core i7-3520M @ 2.90GHz x 2
Ubuntu 19.10 "Eoan"
Linux 5.3.0
gcc 9.2.1

HP Mini 110-1100
Intel i686 32-bit
Intel Atom N270 @ 1.6GHz x 2
Mint 19.3 "Tricia"
Linux 5.0.0
gcc 7.5.0

Pi-Top 3
Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
pi-topOS "Polaris" (Raspbian 9.9 "Stretch")
Linux 4.19.66
gcc 6.3.0
DGNSS Rover (Generation 3)

Pi-Top 3
Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
Raspbian 10 "Buster"
Linux 4.19.97
gcc 8.3.0
DGNSS Rover (Generation 3)

Pi-Top 3
Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
pi-topOS "Sirius" (Raspbian 9.10 "Buster")
Linux 4.19.75
gcc 8.3.0
DGNSS Rover (Generation 3)

Pi-Top 3
Raspberry Pi 3 Model B+
Broadcom BCM2837B0 Cortex-A53 ARMv7 @ 1.4GHz x 4
pi-topOS "Sirius" (Raspbian 9.10 "Buster")
Linux 4.19.97
gcc 8.3.0
DGNSS Rover (Generation 3)

GPD Micro PC
Intel x86_64
Intel Celeron N4100 @ 1.10GHz x 2 x 2
Ubuntu MATE 19.10 "eoan"
Linux 5.3.0
gcc 9.2.1

Articles

Chip Overclock, "Better Never Than Late", 2017-02, http://coverclock.blogspot.com/2017/02/better-never-than-late.html

Chip Overclock, "Time and Space", 2017-09, https://coverclock.blogspot.com/2017/09/time-space.html

Chip Overclock, "A Menagerie of GPS Devices", 2018-04, https://coverclock.blogspot.com/2018/04/a-menagerie-of-gps-devices-with-usb.html

Chip Overclock, "Practical Geolocation", 2018-08, https://coverclock.blogspot.com/2018/08/practical-geolocation.html

Chip Overclock, "Practical Geolocation II", 2018-08, https://coverclock.blogspot.com/2018/08/practical-geolocation-ii.html

Chip Overclock, "We Have Met the Enemy and He Is Us", 2018-09, https://coverclock.blogspot.com/2018/09/we-have-met-enemy-and-he-is-us.html

Chip Overclock, "GPS Satellite PRN 4", 2018-11, https://coverclock.blogspot.com/2018/11/gps-satellite-prn-4.html

Chip Overclock, "This Is What You Have To Deal With", 2019-06, https://coverclock.blogspot.com/2019/06/this-is-what-you-have-to-deal-with.html

Chip Overclock, "Geolocation While Airborne", 2019-09, https://coverclock.blogspot.com/2019/09/geotagging-while-airborne.html

Chip Overclock, "When Learning By Doing Goes To Eleven", 2020-03, https://coverclock.blogspot.com/2020/03/when-learning-by-doing-goes-to-eleven.html

Chip Overclock, "Improvisational Engineering", 2020-04, https://coverclock.blogspot.com/2020/04/improvisational-engineering.html

Chip Overclock, "Keyhole", 2020-05, https://coverclock.blogspot.com/2020/05/keyhole.html

Chip Overclock, "Pseudorange Multilateration", 2020-05, https://coverclock.blogspot.com/2020/05/pseudorange-multilateration.html

Chip Overclock, "Dilution of Precision", 2020-05, https://coverclock.blogspot.com/2020/05/dilution-of-precision.html

Chip Overclock, "Practical Differential Geolocation", 2020-05, https://coverclock.blogspot.com/2020/05/practical-differential-geolocation.html

Chip Overclock, "Frames of Reference IV", 2020-05, https://coverclock.blogspot.com/2020/05/frames-of-reference-iv.html

Chip Overclock, "Negative Results Are Still Results", 2020-05, https://coverclock.blogspot.com/2020/05/negative-results-are-still-results.html

Chip Overclock, "Location, Location, Location", 2020-06, https://coverclock.blogspot.com/2020/06/location-location-location.html

Chip Overclock, "Headless", 2020-06, https://coverclock.blogspot.com/2020/06/headless.html>

Chip Overclock, "Dead Reckoning", 2020-09, https://coverclock.blogspot.com/2020/09/dead-reckoning.html

Media

John Sloan, "Dead Reckoning", album, https://flic.kr/s/aHsmPsGqjA

John Sloan, "GN803G and Hazer 8.0.0", video, https://youtu.be/ZXT_37PvmhE

John Sloan, "GPS Receiver Gallery", album, https://flic.kr/s/aHsmvoXqSH

John Sloan, "Hazer", album, https://flic.kr/s/aHskRMLrx7

John Sloan, "Hazer Test 2017-02-15 15:45 UTC", video, https://youtu.be/UluGfpqpiQw

John Sloan, "NGS AA7126", album, https://flic.kr/s/aHsmEnM8We

John Sloan, "NGS KK1446", album, https://flic.kr/s/aHsmFKdcgF

John Sloan, "NGS KK1770", album, https://flic.kr/s/aHskTG9K1h

John Sloan, "NTP Clock Gallery, album, https://flic.kr/s/aHsmgrizkL

John Sloan, "Time and Space", album, https://flic.kr/s/aHsm3ghwxP

John Sloan, "Time and Space", video, https://youtu.be/szoT23ZBcVU

John Sloan, "Tumbleweed", album, https://flic.kr/s/aHsmcdEgq6

John Sloan, "Yodel", album, https://flic.kr/s/aHsmNDPmDN

References

David Burggraf ed., "OGC KML 2.3", Open Geospatial Consortium, 2015-08-04

Paul E. Ceruzzi, GPS, MIT Press, 2018

David Doyle, "NAD-83 vs WGS-84", NGS, 2000-05-10

David Doyle and Ed McKay, "NGS SURVEY MARKER ACCURACY", NGS, 2000-05-04

M. Dunn at al., "Navstar GPS Space Segment/Navigation User Interfaces", IS-GPS-200H, Global Positioning Systems Directorate / Systems Engineering & Integration, 2013-09-24

Dane E. Ericksen, "NAD 83: What Is It And Why You Should Care", 1994 SBE National Convention and World Media Expo, 1994

Geocaching.com, "Benchmark Hunting", 2019-09-16

Mohinder S. Gerwal et al., Global Navigation Satellite Systems, Inertial Navigation, and Integration, Wiley, 2013

GIS Geography, "Geodetic Datums: NAD 27, NAD 83 and WGS 84", 2019-03-04

John G. Grimes et al., Global Position System Standard Positioning Service Performance Standard, 4th edition, 2008-09

IGS et al., "RINEX - The Receiver Independent Exchange Format", Version 3.03, RTCM-SC104, 2015-07-14

Elliott D. Kaplan ed., Understanding GPS principles and Applications, Artech House, 1996

Charles F. F. Karney, "Algorithms for geodesics", Journal for Geodesy, 2013-01, 87.1, pp. 43..55

GPS NAVSTAR, "Global Positioning System Standard Positioning Service Signal Specification", 2nd Edition, 1995-06-02

Greg Milner, Pinpoint: How GPS is Changing Technology, Culture, and Our Minds, W. W. Norton, 2017-05-16

NMEA 0183, "Standard for Interfacing Marine Electronic Devices", Version 4.10, National Marine Electronics Association, 2012

NovAtel, "An Introduction to GNSS", 2nd ed., NovAtel Inc., 2015

RTCM 10403.3, "Differential GNSS (Global Navigation Satellite Systems) Services - Version 3", 141-2016-SC104-STD, 2016-10-07

RTCM 10410.1, "Networked Transport of RTCM via Internet Protocol (NTRIP) - Version 2.0", 111-2009-SC104-STD + 139-2011-SC104-STD, 2011-06-28

SAE 6857, "Requirements for a Terrestrial Based Positioning, Navigation, and Timing (PNT) System to Improve Nagivation Solutions and Ensure Critical Infrastructure Security", SAE6857, 2018-04

u-blox 7, "Receiver Description Including Protocol Specification V14", GPS.G7-SW-12001-B, ublox, 65525, 2013-02-01

u-blox 8, "Receiver Description Including Protocol Specification", v15-20.30.22-23.01, UBX-13003221-R15, ublox, 26415b7, 2018-03-06

u-blox 9, "ZED-F9P Interface Description", UBX-18010854-R05, ublox, 6cc4473, 2018-12-20

u-blox 9 integration, "ZED-F9P Integration Manual", UBX-18010802-R03, ublox, 2018-12-20

Tools

https://www.notams.faa.gov/dinsQueryWeb/

https://pilotweb.nas.faa.gov/PilotWeb/noticesAction.do?queryType=ALLGPS&formatType=ICAO

https://www.navcen.uscg.gov/?pageName=gpsAlmanacs

https://navcen.uscg.gov/?Do=constellationStatus

https://celestrak.com/GPS/NANU/description.php

https://www.gsc-europa.eu/system-status/Constellation-Information

https://www.ngs.noaa.gov/NGSDataExplorer/

https://www.ngs.noaa.gov/NCAT/

https://tagis.dep.wv.gov/convert/

https://www.gpsvisualizer.com/map_input?form=googleearth

https://www.gpsbabel.org

https://github.com/gpsbabel/gpsbabel

https://geodesy.noaa.gov/cgi-bin/HTDP/htdp.prl?f1=4&f2=1

http://www.earthpoint.us/Convert.aspx

Resources

http://www.catb.org/gpsd/NMEA.txt

http://www.catb.org/gpsd/NMEA.html

https://support.google.com/earth/answer/148095

http://earth.google.com/intl/ar/userguide/v4/index.htm

http://static.googleusercontent.com/media/earth.google.com/en//userguide/v4/google_earth_user_guide.pdf

https://support.google.com/earth/answer/168344

https://dl.google.com/earth/client/GE7/release_7_1_8/googleearth-pro-7.1.8.3036.dmg

https://support.google.com/maps/answer/18539

https://fossies.org/linux/misc/gpsd-3.17.tar.gz/gpsd-3.17/test/daemon/beidou-gb.log

http://ktuukkan.github.io/marine-api/0.9.0/javadoc/net/sf/marineapi/nmea/sentence/TalkerId.html

https://github.com/mvglasow/satstat/wiki/NMEA-IDs

https://www.rapidtables.com/convert/number/degrees-to-degrees-minutes-seconds.html

https://in-the-sky.org/satmap_radar.php Note: when using the "Live Map of Satellite Positions" page, "Satellites above your horizon" tab, the displayed view is as if you are looking upwards into the sky. This means that the azimuth, which starts at 0 degrees at North, increases in the counter clockwise direction. This is probably obvious to the astronomers in the audience, but it wasn't to me.

https://www.gpsworld.com/the-almanac/

https://en.wikipedia.org/wiki/Global_Positioning_System

https://en.wikipedia.org/wiki/List_of_GPS_satellites

https://gssc.esa.int/navipedia/index.php/GPS_Navigation_Message

ftp://ftp.agi.com/pub/Catalog/Almanacs/SEM/GPSAlmanac.al3

https://www.rtca.org/sites/default/files/intentional_gps_interference_approved.pdf

https://github.com/mvglasow/satstat/wiki/NMEA-IDs

https://web.archive.org/web/20130225182002/http://www.colorado.edu/geography/gcraft/notes/gps/gpseow.htm

https://www.novatel.com/support/knowledge-and-learning/published-papers-and-documents/unit-conversions/

http://geostar-navi.com/files/docs/geos5/GeoS_NMEA_protocol_v4_0_eng.pdf

http://ozzmaker.com/wp-content/uploads/2016/08/M10478-M10578-NMEA_Sentence_Output.pdf

https://drotek.gitbook.io/rtk-f9p-positioning-solutions/tutorials/updating-zed-f9p-firmware

https://www.ardusimple.com/simplertk2b-hookup-guide/

https://www.ardusimple.com/simplertk2b-hack-1-unleash-the-usb-power-of-simplertk2b/

https://www.ardusimple.com/simplertk2b-hack-2/

https://www.ardusimple.com/simplertk2b-hack-3/

https://www.ardusimple.com/question/xbee-usage-documentation-for-simplertk/

https://www.ardusimple.com/question/problem-in-configuring-base-in-time-mode-sending-out-rtcm3-messages-from-base/

https://www.ardusimple.com/question/both-units-now-blink-gps-to-xbee-but-no-xbee-to-gps/

https://www.digi.com/resources/documentation/Digidocs/90001456-13/concepts/c_transparent_mode_detailed.htm?tocpath=XBee%20transparent%20mode%7CXBee%20transparent%20mode%20in%20detail%7C_____0

https://github.com/digidotcom/xbee_ansic_library

https://github.com/ukyg9e5r6k7gubiekd6/gpsd/blob/master/crc24q.c

https://youtu.be/T-g27KS0yiY Adam Hart-Davis, "The Clock That Changed the World", A History of the World, BBC, video

https://www.movable-type.co.uk/scripts/latlong.html

https://en.wikipedia.org/wiki/Haversine_formula

https://en.wikipedia.org/wiki/North_American_Plate

https://cs.nyu.edu/visual/home/proj/tiger/gisfaq.html

https://www.solidsignal.com

https://www.tigersupplies.com

https://www.surveying.com

https://www.sparkfun.com/products/15136

https://register.gotowebinar.com/recording/6016509329100006146

https://www.scientificamerican.com/article/gps-is-easy-to-hack-and-the-u-s-has-no-backup/ Paul Tullis, "GPS Is Easy To Hack, And The U.S. Has No Backup", Scientific American, 2019-12-01 (published in the 2019-12 print edition under the title "GPS Down")

https://www.cbsnews.com/news/global-positioning-system-preparing-the-next-generation-of-gps/ CBS Sunday Morning, "Preparing the next generation of GPS", 2019-12-01, video

http://www.aholme.co.uk/GPS/Main.htm

https://time.gov

https://aerospaceamerica.aiaa.org/features/cosmic-gps/ Adam Hadhazy, "Cosmic GPS", Aerospace America, 2020-05

https://www.gps.gov

Soundtrack

https://www.youtube.com/playlist?list=PLd7Yo1333iA9FMHIG_VmuBCVTQUzB-BZF

Build

Clone and build Diminuto, which is used by gpstool although not by libhazer. (Or follow the directions in the Diminuto README.)

cd ~
mkdir -p src
cd src
git clone https://github.com/coverclock/com-diag-diminuto
cd com-diag-diminuto/Diminuto
make pristine
make depend
make all

Clone and build Hazer. (If you don't build Diminuto where the Hazer Makefile expects it, some minor Makefile hacking might be required.)

cd ~
mkdir -p src
cd src
git clone https://github.com/coverclock/com-diag-hazer
cd com-diag-hazer/Hazer
make pristine
make depend
make all

The fs directory contains a file system overlay of files that I've found useful to carefully install in system directories like /etc and /lib. It is not installed automatically, because whether they are helpful, or they reduce your target system to a smoking heap of silicon, depends on your exact circumstances: the target system, the version of Linux it runs, etc. Some of the files under fs are new files in their entirety, some just contain lines that need to be added to the indicated files.

Unit Tests

Set up environment and run tests and utilities. (This establishes the paths for both the Hazer and the Diminuto executables so you don't have to install the libraries and binaries in the system directories.)

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
make sanity
gpstool -?
rtktool -?

Directories

  • app - application source files.
  • bin - utility source files.
  • cfg - configuration makefiles.
  • dat - data captured from Hazer, Yodel, and Tumbleweed tests (some in LFS).
  • ext - sources for extra files not part of this library.
  • fun - functional test source files.
  • inc - public header files.
  • out - build artifacts.
  • fs - file system overlay that may be useful on the host on which Hazer runs.
  • src - feature implementation and private header source files.
  • tst - unit test source files (does not require a GPS receiver).
  • txt - miscellaneous archived text files.

Artifacts

  • out/$(TARGET)/app - application binary executables.
  • out/$(TARGET)/arc - object file archives for static linking.
  • out/$(TARGET)/bin - utility stripped binary executables and scripts.
  • out/$(TARGET)/dep - make dependencies.
  • out/$(TARGET)/ext - executables of extra files if you choose to compile them.
  • out/$(TARGET)/fun - functional test binary executables and scripts.
  • out/$(TARGET)/gen - generated source files.
  • out/$(TARGET)/inc - include (header) files.
  • out/$(TARGET)/lib - shared objects for dynamic linking.
  • out/$(TARGET)/log - log files produced at run-time.
  • out/$(TARGET)/obc - object files.
  • out/$(TARGET)/sym - utility unstripped binary executables.
  • out/$(TARGET)/tmp - temporary files supporting headless operation.
  • out/$(TARGET)/tst - unit test binary executables and scripts.

Applications

  • gpstool - Hazer's multi purpose GNSS pocket tool.
  • rtktool - Tumbleweed's point-to-multipoint datagram router.

Utilities

General Purpose

  • bakepi - monitors Raspberry Pi core temperature which throttles at 82C.
  • bucketbrigade - read from a serial port and forward to another serial port.
  • checksum - takes arguments that are NMEA or UBX packets and adds end matter.
  • compasstool - converts a bearing in decimal degrees to an 8, 16, or 32 compass point.
  • geodesic - computes the WGS84 geodesic distance in meters between two coordinates.
  • haversine - computes the great circle distance in meters between two coordinates.
  • hazer - consumes data from a serial port and reports on stdout.
  • iso8601 - converts seconds since the UNIX epoch into an ISO8601 timestamp.
  • mapstool - convert gpstool coordinate strings to formats accepted by Google Maps.
  • monitor - uses gpstool to monitor device without any configuration.
  • pps - uses Diminuto pintool to multiplex on a 1PPS GPIO pin.

Data Analysis

  • csv2dat - converts gpstool CSV file to a real-time readable output.
  • csv2geo - appends geodesic and altitude differences to gpstool CSV file.
  • csv2iso - converts times in gpstool CSV file into ISO8601-ish timestamps.
  • csv2kml - converts gpstool CSV file to KML 2.3 XML to visualize a line.
  • csv2kmlchanges - converts gpstool CSV file to KML 2.3 XML to visualize fix changes.
  • csv2kmlpoints - converts gpstool CSV file to KML 2.3 XML to visualize points.
  • csv2rmc - converts gpstool CSV file to NMEA RMC sentences.
  • csv2tty - converts gpstool CSV file to a (different) real-time readable output.
  • csvlimits - determines boundary of solutions in a gpstool CSV file.
  • csvmeter - meters lines from a gpstool CSV file based on interarrival times.
  • csvparts - splits gpstool CSV file into smaller files in subdirectories.
  • out2kmlpoints - converts gpstool OUT files to KML 2.3 XML to visualize points.

Moving Map (Hazer)

  • client - runs Google Maps API in Firefox browser under MacOS.
  • consumer - consumes datagrams and reports on stdout.
  • producer - consumes data from serial port and forwards as datagrams.
  • provider - consumes datagrams and forwards to serial port.

Differential GNSS (Tumbleweed)

  • base - configures and runs a UBX-ZED-F9P as a base in survey or fixed mode.
  • benchmark - configures and runs a UBX-ZED-F9P as a corrected rover saving a CSV.
  • control - integrates mobile, peruse, and hups scripts for field testing.
  • field - integrates benchmark, peruse, and hups scripts for field testing.
  • fixed - configures and runs a UBX-ZED-F9P as a base station in fixed mode.
  • mobile - configures and runs a UBX-ZED-F9P as an uncorrected rover.
  • mobilize - like mobile but exit once initialization is complete or fails.
  • proxy - receive UDP packets from the Base and forward to the Rover.
  • router - routes UDP packets received from a base to all rovers.
  • rover - configures and runs a UBX-ZED-F9P as a corrected rover.
  • station - runs a UBX-ZED-F9P with no additional configuration.
  • survey - configures and runs a UBX-ZED-F9P as a base in survey mode.
  • ubxval - converts a number into a UBX-usable form.

Intertial Measurement Unit (Yodel)

  • vehicle - configures and runs a UBX-NEO-M8U with peruse and hups.

Output Control

  • checkpoint - move the out/TARGET/tmp directory to a timestamped name.
  • hup - send SIGHUP to all running instances of gpstool.
  • hups - repeatedly send SIGHUP to all running instances of gpstool on demand.
  • peruse - helper script to watch logs and screens from headless scripts.

Functional Tests

  • bu353s4 - exercises the GlobalSat BU-353S4 receiver.
  • bu353w10 - exercises the GlobalSat BU-353W10 receiver.
  • bu353w10F - exercises the GlobalSat BU-353W10 receiver with slow displays.
  • bu353w10H - exercises the GlobalSat BU-353W10 receiver in headless mode.
  • bu353w10M4 - exercises the GlobalSat BU-353W10 receiver logging PRN 4.
  • bu353w10S - uses socat to send data from GlobalSat BU-353W10 to gpstool over named pipe.
  • bu353W10X - exercises the GlobalSat BU-353W10 receiver while testing data expiration.
  • checksums - exercises the checksum utility.
  • collect - collects output of a device into a file.
  • datagramsink - exercises a datagram source.
  • datagramsource - exercises a datagram sink.
  • dgnss - a script used to test other DNSS scripts.
  • gn803g - exercises the TOPGNSS GN-803G receiver.
  • gr701w - exercises the NaviSys GR701W receiver.
  • gr701wpps - exercises the NaviSys GR701W receiver and its 1PPS output.
  • lowresolution - same as bin/base.sh but with much much lower standards.
  • makerfocus - exercises the MakerFocus GPS board.
  • makerfocuspps - exercises the MakerFocus GPS board and its 1PPS output.
  • neom8npps - exercises the SparkFun NEO-M8N board and its 1PPS output.
  • simplertk2bbase - configures an Ardusimple SimpleRTK2B board as a base.
  • simplertk2b - exercises the Ardusimple SimpleRTK2B board.
  • simplertk2bquery - queries the configuration of an Ardusimple SimpleRTK2B board.
  • simplertk2brover - configures an Ardusimple SimpleRTK2B board as a rover.
  • simplertk2brtcm - collects RTCM messages into a file.
  • simplertk2bsurvey - runs a Ardusimple SimpleRTK2B in survey mode.
  • sink - received UDP packets from the source.
  • sirfstar4 - exercises any SiRF Star 4 device.
  • source - send UDP packets to the sink.
  • talkers - processes a file of synthetic input.
  • tumbleweedkeepalives - serves as a Tumbleweed keepalive sink.
  • tumbleweedpi - Another Tumbleweed functional test.
  • tumbleweedremote - serves as a Tumbleweed RTCM sink.
  • tumbleweed - Tumbleweed functional test.
  • ublox7 - exercises any Ublox 7 device.
  • ublox8debug - exercises any Ublox 8 device with gpstool debug enabled.
  • ublox8 - exercises any Ublox 8 device.
  • ublox9 - exercises any Ublox 9 device.
  • uputronics - exercises the Uputronics GPS board for the Rasperry Pi.
  • zedf9r - exercise UBX-ZED-F9R device.

Comma Separated Value (CSV) Output

The -T flag for gpstool will cause the utility to save the current Position, Velocity, Time (PVT) solution once a second to a "trace" file in CSV format as described below. This makes it easy to analyze results using tools like Excel and several tools provided by Hazer itself. The PVT solution is taken from the high precision u-blox UBX-NAV-HPPOSLLH message if it is available, from the ensemble GNSS solution if it exists, or from one of the four Global Satellite Navigation Systems solutions in this order of preference: GPS, GLONASS, Galileo, BeiDou. The attitude (roll, pitch, yaw) is taken from the UBX-NAV-ATT message from the IMU if it is available. Fields which are not available or are not supported by the receiver have values coded as "0." instead of an empty string to simplify parsing in post-processing.

  • NAM - hostname of computer running gpstool.
  • NUM - sequence number of observation.
  • FIX - 0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=combined, 5=time only.
  • SYS - 0=ensemble, 1=GPS, 2=GLONASS, 3=GALILEO, 4=BEIDOU.
  • SAT - number of satellites used in position fix.
  • CLK - local time in decimal seconds since the POSIX Epoch.
  • TIM - GPS time in decimal seconds since the POSIX Epoch.
  • LAT - WGS84 latitude in decimal degrees.
  • LON - WGS84 longitude in decimal degrees.
  • HAC - reported horizontal error in decimal meters.
  • MSL - altitude above Mean Sea Level in decimal meters.
  • GEO - altitude above WGS84 ellipse in decimal meters.
  • VAC - reported vertical error in decimal meters.
  • SOG - speed over ground in decimal knots.
  • COG - course over ground in decimal degrees.
  • ROL - roll in decimal degrees from IMU.
  • PIT - pitch in decimal degrees from IMU.
  • YAW - yaw in decimal degrees form IMU.
  • RAC - roll accuracy in decimal degrees.
  • PAC - pitch accuracy in decimal degrees.
  • YAC - yaw accuracy in decimal degrees.
  • OBS - number of survey observations.
  • MAC - mean accuracy of survey fix.

A snippet of an actual CSV file looks like this.

"neon", 116, 3, 0, 12, 1598455524.895094741, 1598455524.000000000, 39.7943026, -105.1533253, 0., 1708.000, 1686.500, 0., 0.071000, 0., 0.00000, 80.98829, 0.00000, 20.00000, 43.29752, 167.44616, 0, 0.
"neon", 117, 3, 0, 12, 1598455525.895126606, 1598455525.000000000, 39.7943030, -105.1533263, 0., 1708.100, 1686.600, 0., 0.017000, 0., 0.00000, 80.98829, 0.00000, 20.00000, 43.29752, 167.44616, 0, 0.
"neon", 118, 3, 0, 12, 1598455526.895211419, 1598455526.000000000, 39.7943030, -105.1533276, 0., 1708.200, 1686.700, 0., 0.012000, 0., 0.00000, 80.98829, 0.00000, 20.00000, 43.29752, 167.44616, 0, 0.
"neon", 119, 3, 0, 12, 1598455527.895183560, 1598455527.000000000, 39.7943031, -105.1533286, 0., 1708.300, 1686.800, 0., 0.016000, 0., 0.00000, 80.98829, 0.00000, 20.00000, 43.29752, 167.44616, 0, 0.
"neon", 120, 3, 0, 12, 1598455528.901673983, 1598455528.000000000, 39.7943035, -105.1533300, 0., 1708.500, 1687.000, 0., 0.039000, 0., 0.00000, 80.98829, 0.00000, 20.00000, 43.29752, 167.44616, 0, 0.

csv2tty

Piping the CSV snippet into the script csv2tty produces readable output in fixed columns that looks like this.

GN 12 3D | 2020-08-26T15:25:24Z |  39°47'39"N, 105°09'11"W |  1708m |    0kn |   0° N   |   0°,  80°,   0°
GN 12 3D | 2020-08-26T15:25:25Z |  39°47'39"N, 105°09'11"W |  1708m |    0kn |   0° N   |   0°,  80°,   0°
GN 12 3D | 2020-08-26T15:25:26Z |  39°47'39"N, 105°09'11"W |  1708m |    0kn |   0° N   |   0°,  80°,   0°
GN 12 3D | 2020-08-26T15:25:27Z |  39°47'39"N, 105°09'11"W |  1708m |    0kn |   0° N   |   0°,  80°,   0°
GN 12 3D | 2020-08-26T15:25:28Z |  39°47'39"N, 105°09'11"W |  1708m |    0kn |   0° N   |   0°,  80°,   0°

These columns contain the following information.

  • GN - this fix was made using this talker (GN is an ensemble solution).
  • 12 - twelve satellites were used for this fix.
  • 3D - the fix is three-dimensional (NO=none, IN=IMU, 2D, 3D, GI=GNSS+IMU, TM=time, OT=other).
  • 2020-08-26T15:25:24Z - this is the computed date and time in UTC (Zulu).
  • 39°47'39"N, 105°09'11"W - this is the latitude and longitude in the GNSS datum (typically WGS84).
  • 1708m - this is the altitude above mean sea level (MSL).
  • 0kn - this is the ground speed in knots (nautical miles per hour).
  • 0° N - this is the current true bearing and compass direction.
  • 0°, - this is the roll of the vehicle reference frame from the IMU if available.
  • 80°, - this is the pitch of the vehicle reference frame from the IMU if available.
  • 0° - this is the yaw of the vehicle reference frame from the IMU if available.

The following command pipeline is useful.

tail -f out/host/tmp/example.csv | csv2tty

The peruse command supports this directly when used with a CSV file.

peruse example csv

csv2dat

Piping the CSV snippet into the script csv2dat produces a different readable output in fixed columns that looks like this.

GN 12 3D |    116 | 2020-08-26T09:25:24J | 2020-08-26T15:25:24Z |  39°47'39.489360"N, 105°09'11.971080"W |  1708m   5603ft |    0kn    0mph     0kph |   0° N   |   0°,  80°,   0°
GN 12 3D |    117 | 2020-08-26T09:25:25J | 2020-08-26T15:25:25Z |  39°47'39.490799"N, 105°09'11.974680"W |  1708m   5604ft |    0kn    0mph     0kph |   0° N   |   0°,  80°,   0°
GN 12 3D |    118 | 2020-08-26T09:25:26J | 2020-08-26T15:25:26Z |  39°47'39.490799"N, 105°09'11.979359"W |  1708m   5604ft |    0kn    0mph     0kph |   0° N   |   0°,  80°,   0°
GN 12 3D |    119 | 2020-08-26T09:25:27J | 2020-08-26T15:25:27Z |  39°47'39.491160"N, 105°09'11.982959"W |  1708m   5604ft |    0kn    0mph     0kph |   0° N   |   0°,  80°,   0°
GN 12 3D |    120 | 2020-08-26T09:25:28J | 2020-08-26T15:25:28Z |  39°47'39.492599"N, 105°09'11.987999"W |  1708m   5605ft |    0kn    0mph     0kph |   0° N   |   0°,  80°,   0°

These columns contain the following information.

  • GN - this fix was made using this talker (GN is an ensemble solution).
  • 12 - twelve satellites were used for this fix.
  • 3D - the fix is three-dimensional (NO=none, IN=IMU, 2D, 3D, GI=GNSS+IMU, TM=time, OT=other).
  • 116 - this is the monotonically increasing observation number.
  • 2020-08-26T09:25:24J - this is the local system time the observation was made (Juliet).
  • 2020-08-26T15:25:24Z - this is the computed date and time in UTC (Zulu).
  • 39°47'39.489360"N, 105°09'11.971080"W - this is the higher resolution latitude and longitude in the GNSS datum (typically WGS84).
  • 1708m - this is the altitude above mean sea level (MSL) in meters.
  • 5603ft - this is the altitude above mean sea level (MSL) in feet.
  • 0kn - this is the ground speed in knots (nautical miles per hour).
  • 0mph - this is the ground speed in miles per hour.
  • 0kph - this is the ground speed in kilometers mer hour.
  • 0° N - this is the current true bearing and compass direction.
  • 0°, - this is the roll of the vehicle reference frame from the IMU if available.
  • 80°, - this is the pitch of the vehicle reference frame from the IMU if available.
  • 0° - this is the yaw of the vehicle reference frame from the IMU if available.

I find this format more suitable for viewing the data during post-processing.

Help

gpstool

> gpstool -?
usage: gpstool [ -d ] [ -v ] [ -M ] [ -u ] [ -V ] [ -X ] [ -x ] [ -D DEVICE [ -b BPS ] [ -7 | -8 ] [ -e | -o | -n ] [ -1 | -2 ] [ -l | -m ] [ -h ] [ -s ] | -S FILE ] [ -B BYTES ] [ -O FILE ] [ -C FILE ] [ -t SECONDS ] [ -I PIN | -c ] [ -p PIN ] [ -U STRING ... ] [ -W STRING ... ] [ -R | -E | -F | -H HEADLESS | -P ] [ -L LOG ] [ -G [ IP:PORT | :PORT [ -g MASK ] ] ] [ -Y [ IP:PORT [ -y SECONDS ] | :PORT ] ] [ -K [ -k MASK ] ] [ -N FILE ] [ -T FILE ]
       -1          Use one stop bit for DEVICE.
       -2          Use two stop bits for DEVICE.
       -7          Use seven data bits for DEVICE.
       -8          Use eight data bits for DEVICE.
       -B BYTES    Set the input Buffer size to BYTES bytes.
       -C FILE     Catenate input to FILE or named pipe.
       -D DEVICE   Use DEVICE for input or output.
       -E          Like -R but use ANSI Escape sequences.
       -F          Like -E but reFresh at 1Hz.
       -G IP:PORT  Use remote IP and PORT as dataGram sink.
       -G :PORT    Use local PORT as dataGram source.
       -H HEADLESS Like -R but writes each iteration to HEADLESS file.
       -I PIN      Take 1PPS from GPIO Input PIN (requires -D) (<0 active low).
       -K          Write input to DEVICE sinK from datagram source.
       -L LOG      Write pretty-printed input to LOG file.
       -M          Run in the background as a daeMon.
       -N FILE     Use fix FILE to save ARP LLH for subsequeNt fixed mode.
       -O FILE     Save process identifier in FILE.
       -P          Process incoming data even if no report is being generated.
       -R          Print a Report on standard output.
       -S FILE     Use source FILE or named pipe for input.
       -T FILE     Save the PVT trace to FILE.
       -U STRING   Like -W except expect UBX ACK or NAK response.
       -U ''       Exit when this empty UBX STRING is processed.
       -V          Log Version in the form of release, vintage, and revision.
       -W STRING   Collapse STRING, append checksum, Write to DEVICE.
       -W ''       Exit when this empty Write STRING is processed.
       -X          Enable message eXpiration test mode.
       -Y IP:PORT  Use remote IP and PORT as keepalive sink and surveYor source.
       -Y :PORT    Use local PORT as surveYor source.
       -b BPS      Use BPS bits per second for DEVICE.
       -c          Take 1PPS from DCD (requires -D and implies -m).
       -d          Display Debug output on standard error.
       -e          Use Even parity for DEVICE.
       -g MASK     Set dataGram sink mask (NMEA=1, UBX=2, RTCM=4) default NMEA.
       -h          Use RTS/CTS Hardware flow control for DEVICE.
       -k MASK     Set device sinK mask (NMEA=1, UBX=2, RTCM=4) default NMEA.
       -l          Use Local control for DEVICE.
       -m          Use Modem control for DEVICE.
       -o          Use Odd parity for DEVICE.
       -p PIN      Assert GPIO outPut PIN with 1PPS (requires -D and -I or -c) (<0 active low).
       -n          Use No parity for DEVICE.
       -s          Use XON/XOFF (control-Q/control-S) for DEVICE.
       -t SECONDS  Timeout GNSS data after SECONDS seconds.
       -u          Note Unprocessed input on standard error.
       -v          Display Verbose output on standard error.
       -x          EXit if a NAK is received.
       -y SECONDS  Send surveYor a keep alive every SECONDS seconds.

rtktool

> rtktool -?
usage: rtktool [ -d ] [ -v ] [ -M ] [ -V ] [ -M ] [ -p :PORT ] [ -t SECONDS ]
       -M          Run in the background as a daeMon.
       -V          Log Version in the form of release, vintage, and revision.
       -d          Display Debug output on standard error.
       -p :PORT    Use PORT as the RTCM source and sink port.
       -t SECONDS  Set the client timeout to SECONDS seconds.
       -v          Display Verbose output on standard error.

Display

The display examples below were cut and pasted from actual running instances of gpstool, but not necessarily the same instance, running on the same target with the same GNSS hardware, with the same command line options, using the same version of gpstool. The gpstool display is event driven: each line is displayed if and only if the appropriate NMEA, UBX, or RTCM input was received from the device under test, within the timeout window, regardless of command line options or how the GNSS device was initialized.

While NMEA (and UBX amd RTCM too for that matter) is good about updating the application with new information, it is not so good about letting the application know when that data is no longer relevant. For that reason, all of the data read from the device has associated with it an expiration time in seconds. This can be set from the command line, in the range 0 to the default of 255. If the data is not updated within that duration by new sentences or messages from the GPS device, it is no longer displayed.

INP [ 14] \xd3\0\bL\xe0\0\x8a\0\0\0\0\xa8\xf7*

INP is the most recent data read from the device, either NMEA sentences or UBX packets, with binary data converted into standard C escape sequences.

OUT [  7] \xb5b\x06>\0\0\0

OUT is the most recent data written to the device, as specified on the command line using the -W (NMEA sentence) or -U (UBX message) options.

LOC 2020-06-11T12:24:01.018-07:00+01T 00:00:00.871 34.2.0        11003 hacienda

LOC is the current local time provided by the host system, the elapsed time to first fix, the software release number, the process id, and the local host name. The local time, with a fractional part in milliseconds, includes the time zone offset from UTC in hours and minutes, the current daylight saving time (DST) offset in hours, and the military time zone letter. If the time zone offset is an even number of hours (some aren't: Newfie Time, I'm looking at you), it will be a letter like "T" ("Tango") for Mountain Standard Time as found in Denver; otherwise it will be "J" ("Juliet") to indicate any local time zone.

TIM 2020-06-11T18:24:00.000-00:00+00Z 0pps                             GNSS

TIM is the most recent time solution, in UTC (or 'Z' for "Zulu"), and the current value of the One Pulse Per Second (1PPS) signal if the device provides it and it was enabled on the command line using -c (using data carrier detect or DCD) or -I (using general purpose input/output or GPIO).

POS 39°47'39.364"N, 105°09'12.315"W    39.7942678, -105.1534210        GNSS

POS is the most recent position solution, latitude and longitude, in degrees, hours, minutes, and decimal seconds, and in decimal degrees. Either format can be cut and pasted directly into Google Maps, and the latter into Google Earth. The underlying position data is stored as binary integers in billionths of a minute (nanominutes). But the device under test may not provide that much accuracy; the actual number of significant digits for various data is reported by the INT line (below), but even this may be optimistic - or, for that matter, pessimistic - when compared to what the device is capable of. In particular, technologies like Wide Area Augmentation System (WAAS), multi-band GNSS, differential GPS, Real-Time Kinematics (RTK), and long-term surveying, can potentially achieve remarkable accuracy.

ALT    5608.53'   1709.500m MSL    5537.99'   1688.000m GEO            GNSS

ALT is the most recent altitude solution, in feet and meters, both above Mean Sea Level (MSL), and above the selected GEOdetic datum (typically WGS84). (Similar comments here regarding precision as those for POS.)

COG N     0.000000000°T    0.000000000°M                               GNSS

COG is the most recent course over ground solution, in cardinal compass direction, and the bearing in degrees true, and degrees magnetic (if available). (Similar comments here regarding precision as those for POS.)

SOG       0.012mph       0.010knots       0.018kph       0.005m/s      GNSS

SOG is the most recent speed over ground solution, in miles per hour, knots (nautical miles per hour), kilometers per hour, and maters per second. (Similar comments here regarding precision as those for POS.)

INT GLL [12] DMY TOT (  9 10  5  3  0  0  4  4 )             39526129B tumblewe

INT is internal Hazer state including the name of the sentence (GLL in the example above) that most recently updated the solution, the total number of satellites that contributed to that solution, an indication as to whether the day-month-year value has been set (only occurs once the RMC sentence has been received), an indication as to whether time is incrementing monotonically (it can appear to run backwards when receiving UDP packets because UDP may reorder them), and some metrics as to the number of significant digits provided for various values provided by the device. INT also includes the total number of bytes sent or received - 395,261,29B in this example - over the network. This allows you to keep track of your network utilization, especially important when paying for data on your LTE mobile provider. The right-most field is the name of the device from which gpstool is reading.

MON -jamming  +history  50indicator  63maximum                         ttyACM1

MON displays some of the results received in the UBX-MON-HW message if enabled. Ublox 8 chips with firmware revision 18 and above can provide clues to jamming based on the received signal strength. (N.B. I don't have a way to test this.) This requires that the jamming/interference monitor (ITFM) be calibrated using the UBX-CFG-ITFM message.

STA -spoofing -history       1985ms     303985ms     0epoch            ttyACM1

STA displays some of the results received in the UBX-NAV-STATUS message if enabled. Ublox 8 chips with firmware revision 18 and above can provide clues to spoofing based on comparing navigation solutions from multiple GNSSes if available. (N.B. I don't have a way to test this.) Also shown are the milliseconds since first fix and milliseconds uptime provided by the message.

ATT    0.0° roll ±  20.0°  -73.6° pitch ±  78.8°   57.7° yaw ±  85.0°  IMU

ATT indicates the roll, pitch, and yaw orientation of the u-blox module in those units which are equipped with an intertial measurement unit with a gyroscope and accellerometers.

ODO      7.671mi     12.345km (     42.185mi     67.890km ) ±       0m IMU

ODO indicates the resettable and semi-persistent odometer reading available from some u-blox modules, in both miles and kilometers (the native units are meters), along with the error estimate in meters.

NED         -5mm/s north         -2mm/s east        -41mm/s down (3)   IMU

NED indicates the North-East-Down vehicle frame reading available from the Intertial Measurement Unit (IMU) in some u-blox modules. The number in the parenthesis indicates the nature of the ensemble GNSS and IMU fix: '-' for no fix; '!' for a dead reckoning fix only; '2' for a 2D GNSS fix; '3' for a 3D GNSS fix; '+' for a combined GNSS + dead reckoning fix; '*' for a time only fix; and '?' for an error.

HPP   39.794267897, -105.153420946 ±     0.5237m                       GNSS
HPA   1709.4855m MSL   1687.9856m WGS84 ±     0.8001m                  GNSS

HPP and HPA show the high precision position and altitude available from some Ublox devices, along with their estimated accuracy. This may differ (slightly) from the position and altitude reported via POS and ALT due to the conversion of units done to conform to the NMEA format. When available, the HPP and HPA is expected to be more precise.

NGS  39 47 39.36442(N) 105 09 12.31540(W)                              GNSS

NGS shows the same high precision position as HPP but in the format used in the National Geodetic Survey (NGS) data sheets for coordinates of artifacts such as NGS and municipal survey markers. This makes it easier to compare the Hazer position against examples from the NGS database.

BAS 1active 0valid      18019sec      18020obs       0.1675m           DGNSS
ROV     0:  1094 (    0)                                               DGNSS

BAS or ROV show information about the Ublox device operating in base station or in rover modes. In base (stationary) mode, it shows if the device is actively surveying or if the survey has resolved to a valid location, how many seconds and observations have been consumed during the survey, and what the mean error is. In rover (mobile) mode, it shows what RTCM message was last received and from whom.

RTK 1094 [ 129] rover    <ERNrCERN>                                    DGNSS

RTK show the latest RTCM message received, when operating in base mode (in which case the message was received from the device), or in rover mode (the message was received from the base in a datagram via UDP). The lengths of the most recent message is shown, as is the mode of the system, base or rover. The character sequence between the angle brackets records the last eight RTCM messages that were received, the newest one indicated by the rightmost character in the sequence, as the sequence is progressively shifted left as new messages are received.

ACT [1]  {    28     3    19    24     6     2 } [ 6] [ 8] [23] [32]   NAVSTAR

ACT is the list of active satellites, typically provided seperately for each system or constellation by the device, showing each satellites identifying number (for GPS, this is its pseudo-random noise or PRN code number, but other systems using other conventions), and the number of active satellites in this list, the number of active satellites for this constellation, and the number of active satellites total. Unlike the other report lines, the system or constellation to which the data applies is derived from (in order, depending on availability) the system id in the GSA sentence (only available on devices that support later NMEA versions), or an analysis of the Space Vehicle Identifier based on NMEA conventions, or the talker specified at the beginning of the sentence. The reason for this is that some devices (I'm looking at you, GN803G), specify GNSS as the talker for all GSA sentences when they are computing an ensemble solution (one based on multiple constellations); this causes ambiguity between this case and the case of successive GSA sentences in which the active satellite list has changed. Hazer independently tries to determine the constellation to which the GSA sentence refers when the talker is GNSS. The fourth metric is the maximum number of space vehicles (SVs) or satellites used in the solution since the application began. This is useful when testing different antenna locations, particularly when using the device in a fixed base survey mode. In the example above, the first ACT line indicates that 6 SVs in the NAVSTAR constellation are represented in this particular line, the solution includes 10 total NAVSTAR SVs (so 4 more appear in a subsequent ACT line for NAVSTAR), the solution is using 29 SVs total among all constellations, and at one time as many as 31 SVs were used in the solution. (The U-blox 9 receiver used for this example has a maximum of 32 RF channels, so receiving 31 SVs indicates that antenna placement is good; in this particular case I have the antenna installed in a skylight in my kitchen that is near the peak of the roof of my home.)

DOP   1.09pdop   0.61hdop   0.90vdop                                   NAVSTAR

DOP is the position, horizontal, and vertical dilution of precision - measures of the quality of the position fix (smaller is better) - based on the real-time geometry of the satellites upon which the current solution is based. If multiple constellations are reported, but the DOPs are all the same, the device is typically computing an ensemble solution using multiple constellations.

SAT [  1]     2id  40°elv  205°azm    0dBHz  6sig <   !                NAVSTAR

SAT is the list of satellites in view, including an index that is purely an artifact of Hazer, the Space Vehicle IDentifier (specific to the constellation), its elevation and azimuth in degrees based on its ephemeris, the signal strength (really, a carrier to noise density ratio) in deciBels Hertz of its transmission, a signal identifier indicating which signal or band (e.g. L1 C/A, L2, etc.) is being used, and one or more flags. A flag of '<' indicates that the satellite is on the active list (see ACT above); a '?' indicates that the azimuth and/or the elevation were empty but display as zero (likely that the satellite is not in the transmitted almanac); and an '!' indicates that the signal strength was empty but displays as zero (some receivers use this to indicate the satellite is not being tracked).

Remarks

N.B. Most of the snapshots below were taken from earlier versions of Hazer and its gpstool utility. The snapshots were cut and pasted from actual output and may differ slightly (or greatly) from that of the most current version. For some of these, a * was used to mean the degree symbol; later versions of Hazer display the actual degree symbol using Unicode. I've tried to update the command line examples to reflect the current code, but I may have missed a few here or there.

Sending Commands

gpstool can send initialization commands to the GPS device. These can be either NMEA sentences (without escape sequences) or binary UBX sentence (with escape sequences). In either case, gpstool will automatically append the appropriate ending sequences including a computed NMEA or UBX checksum. Here is an example of gpstool writing proprietary NMEA and binary UBX sequences to a NaviSys GR-701W which has a UBlox 7 chipset.

> gpstool -D /dev/ttyUSB0 -b 9600 -8 -n -1 -E \
      -W "\$PUBX,00" \
      -W "\$PUBX,03" \
      -W "\$PUBX,04" \
      -W "\\xb5\\x62\\x06\\x01\\x08\\x00\\x02\\x13\\x00\\x01\\x00\\x00\\x00\\x00" \
      -W "\\xb5\\x62\\x0a\\x04\\x00\\x00" \
      -W "\\xb5\\x62\\x06\\x31\\x00\\x00" \
      -W "\\xb5\\x62\\x06\\x3e\\x00\\x00" \
      -W "\\xb5\\x62\\x06\\x06\\x00\\x00"

(The code to write commands to the device depends on being driven by incoming data from the device, so if the device is initially silent, this won't work. All the GPS receivers I've tested are chatty by default, so this hasn't been an issue for me.)

If a written command is of zero length (really: has a NUL or \0 character as its first character), gpstool exits. This can be used by a script, for example, to use gpstool to send a command to change the baud rate of the GPS device serial port and exit, and then start a new gpstool with the new baud rate.

As a special case, you can send initialization commands to a U-blox device and wait until it responds with an ACK or NAK before sending the next U-blox command.

> gpstool -D /dev/ttyACM0 -b 230400 -8 -n -1 \
    -U '\xb5\x62\x06\x8a\x09\x00\x00\x01\x00\x00\x01\x00\x03\x20\x00' \
    -U '\xb5\x62\x06\x8a\x09\x00\x00\x01\x00\x00\x05\x00\x53\x10\x00' \
    -U '\xb5\x62\x06\x8a\x09\x00\x00\x01\x00\x00\x04\x00\x77\x10\x01' \
    -U '\xb5\x62\x06\x8a\x09\x00\x00\x01\x00\x00\x04\x00\x78\x10\x00' \
    -U '\xb5\x62\x06\x8a\x09\x00\x00\x01\x00\x00\x6b\x02\x91\x20\x01' \
    -U '\xb5\x62\x06\x01\x03\x00\x01\x14\x01' \
    -U ''

Forwarding Datagrams

Here is an example of using gpstool to read an NMEA sentence stream from a serial device at 115200 8n1, display the data using ANSI escape sequences to control the output terminal, and forwards NMEA sentences to a remote instance of itself listing on port 5555 on host "lead".

> gpstool -D /dev/ttyUSB0 -b 115200 -8 -n -1 -E -G lead:5555

$GPRMC,164659.00,A,3947.65335,N,10509.20343,W,0.063,,310718,,,D*63\r\n
MAP 2018-07-31T16:46:59Z 39*47'39.20"N,105*09'12.20"W  5631.82' N     0.072mph PPS 1
RMC 39.794222,-105.153391  1716.600m   0.000*    0.063knots [10] 9 10 5 0 4
GSA {  51   6  12  48  28  19   2  24   3  17 } [10] pdop 1.70 hdop 0.86 vdop 1.47
GSV [01] sat   2 elv 28 azm 206 snr 40dBHz con GPS
GSV [02] sat   3 elv 14 azm  59 snr 27dBHz con GPS
GSV [03] sat   6 elv 67 azm 166 snr 37dBHz con GPS
GSV [04] sat  12 elv 23 azm 308 snr 33dBHz con GPS
GSV [05] sat  17 elv 54 azm  44 snr 17dBHz con GPS
GSV [06] sat  19 elv 72 azm 352 snr 31dBHz con GPS
GSV [07] sat  22 elv  5 azm  39 snr 21dBHz con GPS
GSV [08] sat  24 elv 40 azm 287 snr 18dBHz con GPS
GSV [09] sat  28 elv 30 azm 112 snr 32dBHz con GPS
GSV [10] sat  46 elv 38 azm 215 snr 34dBHz con GPS
GSV [11] sat  48 elv 36 azm 220 snr 39dBHz con GPS
GSV [12] sat  51 elv 44 azm 183 snr 43dBHz con GPS

Receiving Datagrams

Here is the remote instance of gpstool receiving the NMEA stream via the UDP socket on port 5555.

> gpstool -G :5555 -E

$GPGLL,3947.65274,N,10509.20212,W,164744.00,A,D*7F\r\n
MAP 2018-07-31T16:47:44Z 39*47'39.16"N,105*09'12.12"W  5612.79' N     0.048mph PPS 0
GGA 39.794212,-105.153369  1710.800m   0.000*    0.042knots [10] 9 10 5 0 4
GSA {  51   6  12  48  28  19   2  24   3  17 } [10] pdop 1.71 hdop 0.86 vdop 1.48
GSV [01] sat   2 elv 28 azm 206 snr 38dBHz con GPS
GSV [02] sat   3 elv 14 azm  58 snr 26dBHz con GPS
GSV [03] sat   6 elv 67 azm 165 snr 38dBHz con GPS
GSV [04] sat  12 elv 24 azm 308 snr 33dBHz con GPS
GSV [05] sat  17 elv 54 azm  44 snr 21dBHz con GPS
GSV [06] sat  19 elv 72 azm 353 snr 33dBHz con GPS
GSV [07] sat  22 elv  5 azm  39 snr 18dBHz con GPS
GSV [08] sat  24 elv 41 azm 287 snr 19dBHz con GPS
GSV [09] sat  28 elv 29 azm 112 snr 29dBHz con GPS
GSV [10] sat  46 elv 38 azm 215 snr 34dBHz con GPS
GSV [11] sat  48 elv 36 azm 220 snr 39dBHz con GPS
GSV [12] sat  51 elv 44 azm 183 snr 42dBHz con GPS

Using screen

You can use the screen utility, available for MacOS and Linux/GNU, to capture the NMEA stream on a serial port. (And on Windows systems, I use PuTTY.)

> screen /dev/cu.usbserial-FT8WG16Y 9600 8n1

$GPRMC,190019.00,A,3947.65139,N,10509.20196,W,0.053,,060818,,,D*66
$GPVTG,,T,,M,0.053,N,0.099,K,D*20
$GPGGA,190019.00,3947.65139,N,10509.20196,W,2,10,1.05,1707.9,M,-21.5,M,,0000*5B
$GPGSA,A,3,06,19,24,51,02,12,48,29,25,05,,,1.75,1.05,1.40*08
$GPGSV,4,1,14,02,77,008,30,05,42,164,48,06,32,051,20,09,03,060,*71
$GPGSV,4,2,14,12,73,214,28,17,04,101,13,19,24,092,20,24,06,217,31*71
$GPGSV,4,3,14,25,45,305,29,29,17,294,11,31,03,327,08,46,38,215,30*71
$GPGSV,4,4,14,48,36,220,32,51,44,183,42*7C
$GPGLL,3947.65139,N,10509.20196,W,190019.00,A,D*7E
$GPRMC,190020.00,A,3947.65143,N,10509.20192,W,0.044,,060818,,,D*63
$GPVTG,,T,,M,0.044,N,0.081,K,D*2F
$GPGGA,190020.00,3947.65143,N,10509.20192,W,2,09,1.05,1707.9,M,-21.5,M,,0000*50
$GPGSA,A,3,06,19,24,51,02,12,48,25,05,,,,1.75,1.05,1.40*03
$GPGSV,4,1,14,02,77,008,31,05,42,164,48,06,32,051,21,09,03,060,23*70
$GPGSV,4,2,14,12,73,214,29,17,04,101,12,19,24,092,19,24,06,217,31*7B
$GPGSV,4,3,14,25,45,305,29,29,17,294,09,31,03,327,06,46,38,215,31*77
$GPGSV,4,4,14,48,36,220,32,51,44,183,43*7D
$GPGLL,3947.65143,N,10509.20192,W,190020.00,A,D*7D
$GPRMC,190021.00,A,3947.65143,N,10509.20191,W,0.050,,060818,,,D*64
$GPVTG,,T,,M,0.050,N,0.092,K,D*28
$GPGGA,190021.00,3947.65143,N,10509.20191,W,2,10,1.05,1708.0,M,-21.5,M,,0000*5C

Using gpsd

You can test GPS devices independently of this software using the excellent Linux open source GPS stack. Here is just a simple example of stopping the GPS daemon if it has already been started (make sure you are not going to break something doing this), restarting it in non-deamon debug mode, and running a client against it. In this example, I use the Garmin GLO Bluetooth device I have already set up, and the X11 GPS client. When I'm done, I restart gpsd in normal daemon mode.

> sudo service gpsd stop
> gpsd -N /dev/rfcomm0 &
> xgps
...
> kill %+
> sudo service start gpsd

Using socat

You can use the socat utility, available for Linux/GNU and MacOS flavored systems, to capture the NMEA stream on the UDP port.

> socat UDP6-RECVFROM:5555,reuseaddr,fork STDOUT

$GPGSA,M,3,32,10,14,18,31,11,24,08,21,27,01,,1.3,0.8,1.1*33
$GPGSV,3,1,12,32,79,305,39,10,66,062,41,14,58,247,35,18,40,095,34*72
$GPGSV,3,2,12,31,21,180,45,11,20,309,27,24,19,044,30,08,17,271,31*7A
$GPGSV,3,3,12,21,13,156,39,27,13,233,33,01,09,320,20,51,43,183,42*72

You may be tempted (I was) to dispense with gpstool entirely and use socat to forward NMEA strings to a remote site. Be aware that when used in UDP consumer mode, gpstool expects every UDP datagram to be a fully formed NMEA sentence, because that's how it sends them in UDP producer mode. socat isn't so polite. Since the occasional UDP packet will inevitably be lost, if the output of socat is piped into gpstool, it will see a lot of corruption in the input stream. Using gpstool on both ends means only entire NMEA sentences are lost, and gpstool can recover from this.

> socat OPEN:/dev/ttyUSB0,b115200 UDP6-SENDTO:[::1]:5555

The RMC and GGA sentences contain UTC timestamps (and the RMC contains a DMY datestamp). Hazer rejects sentences for which time runs backwards. Although this should be impossible for the sentences in the stream from a GPS device, it is entirely possible for the UDP stream from a Hazer producer, since UDP packet ordering is not guaranteed.

You might be tempted to use TCP instead of UDP. That sounds like a good idea: guaranteed delivery, packets always in order. However, this can introduce a lot of lantency in the NMEA stream. The result is the NMEA stream received by the consumer may lag signficantly behind real-time, and that lag increases the longer the system runs. So over time it diverges more and more with reality. It is better to lose an NMEA sentence than have it delayed. After all, another more up-to-date sentence is on the way right behind it.

Using Bluetooth

You can use gpstool with Bluetooth GPS units like the Garmin GLO.

> sudo bluetoothctl
power on
agent on
scan on
...
scan off
pair 01:23:45:67:89:AB
quit
> sudo rfcomm bind 0 01:23:45:67:89:AB 1
> sudo chmod 666 /dev/rfcomm0
> gpstool -D /dev/rfcomm0 -E

$GPVTG,350.4,T,341.6,M,000.08,N,0000.15,K,D*18\r\n
MAP 2017-09-14T14:22:05Z 39*47'39.20"N,105*09'12.13"W  5613.45' N     0.092mph
GGA 39.794223,-105.153371  1711.000m 350.400*    0.080knots [12] 10 11 5 4 5
GSA {  30  28  84   2  19   6  91  24  12  22  72   3 } [12] pdop 1.20 hdop 0.70 vdop 1.00
GSV [01] sat  51 elv 43 azm 182 snr 45dBHz con GPS
GSV [02] sat  30 elv  4 azm 161 snr 31dBHz con GPS
GSV [03] sat  28 elv 35 azm 105 snr 22dBHz con GPS
GSV [04] sat  84 elv 45 azm 245 snr 37dBHz con GPS
GSV [05] sat   2 elv 21 azm 204 snr 36dBHz con GPS
GSV [06] sat  19 elv 74 azm 346 snr 42dBHz con GPS
GSV [07] sat   6 elv 56 azm 175 snr 45dBHz con GPS
GSV [08] sat  91 elv 66 azm   5 snr 25dBHz con GPS
GSV [09] sat  24 elv 36 azm 301 snr 26dBHz con GPS
GSV [10] sat  12 elv 13 azm 304 snr 32dBHz con GPS
GSV [11] sat  22 elv  7 azm  46 snr 24dBHz con GPS
GSV [12] sat  72 elv 39 azm 326 snr 30dBHz con GPS
GSV [13] sat   3 elv 14 azm  67 snr 27dBHz con GPS

> sudo rfcomm release 0
> sudo bluetoothctl
power off
quit

Using One Pulse Per Second

Some GPS devices provide a 1Hz One Pulse Per Second (1PPS) signal that is, if implemented correctly, closely phase locked to GPS time. Hazer and its gpstool utility are user-space software running on a non-real-time operating system, so any periodic action by Hazer is at best approximate in terms of period. But handling 1PPS even with some jitter is useful for casual testing of GPS devices.

You can test GPS devices like the NaviSys GR-701W that provide 1PPS by toggling the Data Carrier Detect (DCD) modem control line. This includes devices that have a USB serial interface. Note the addition of the -c flag.

> gpstool -D /dev/ttyUSB0 -b 9600 -8 -n -1 -E -c

$GPRMC,174227.00,A,3947.65321,N,10509.20367,W,0.027,,040518,,,D*68\r\n
MAP 2018-05-04T17:42:27Z 39*47'39.19"N,105*09'12.22"W  5600.00' N     0.031mph 1PPS
RMC 39.794220,-105.153395  1706.900m   0.000*    0.027knots [10] 9 10 5 0 4
GSA {   9   7   8  30  51  27  23  48  28  11 } [10] pdop 2.13 hdop 1.01 vdop 1.88
GSV [01] sat   5 elv 10 azm 297 snr 22dBHz con GPS
GSV [02] sat   7 elv 72 azm 348 snr 31dBHz con GPS
GSV [03] sat   8 elv 56 azm  92 snr 23dBHz con GPS
GSV [04] sat   9 elv 52 azm 187 snr 36dBHz con GPS
GSV [05] sat  11 elv 18 azm 143 snr 34dBHz con GPS
GSV [06] sat  16 elv  1 azm  55 snr  0dBHz con GPS
GSV [07] sat  18 elv  4 azm 125 snr 27dBHz con GPS
GSV [08] sat  23 elv 18 azm 161 snr 43dBHz con GPS
GSV [09] sat  27 elv 29 azm  48 snr 31dBHz con GPS
GSV [10] sat  28 elv 35 azm 246 snr 32dBHz con GPS
GSV [11] sat  30 elv 48 azm 307 snr 35dBHz con GPS
GSV [12] sat  46 elv 38 azm 215 snr 43dBHz con GPS
GSV [13] sat  48 elv 36 azm 220 snr 42dBHz con GPS
GSV [14] sat  51 elv 44 azm 183 snr 37dBHz con GPS

You can also test GPS devices like the MakerFocus USB-Port-GPS that provide 1PPS by toggling a General Purpose I/O (GPIO) pin. This example (which I've run on a Raspberry Pi) uses pin 18. Note the addition of the -I flag. (You may have to run gpstool as root to access the GPIO pins.)

# gpstool -D /dev/ttyUSB1 -b 9600 -8 -n -1 -I 18 -E

$GPGSV,3,1,11,23,85,357,39,16,62,069,40,09,45,311,37,51,43,183,43*75\r\n
MAP 2018-05-08T14:50:25Z 39*47'39.17"N,105*09'12.19"W  5588.51' N     0.000mph
GGA 39.794215,-105.153387  1703.400m   0.000*    0.000knots [09] 8 9 5 3 3
GSA {   8   7  22  26  23   9   3  16  27 } [09] pdop 1.94 hdop 1.03 vdop 1.64
GSV [01] sat  23 elv 85 azm 357 snr 39dBHz con GPS
GSV [02] sat  16 elv 62 azm  69 snr 40dBHz con GPS
GSV [03] sat   9 elv 45 azm 311 snr 37dBHz con GPS
GSV [04] sat  51 elv 43 azm 183 snr 44dBHz con GPS
GSV [05] sat  26 elv 34 azm  48 snr 28dBHz con GPS
GSV [06] sat   3 elv 33 azm 200 snr 44dBHz con GPS
GSV [07] sat   7 elv 23 azm 268 snr 28dBHz con GPS
GSV [08] sat  27 elv 20 azm 126 snr 36dBHz con GPS
GSV [09] sat  22 elv 16 azm 183 snr 42dBHz con GPS
GSV [10] sat   8 elv  5 azm 160 snr 34dBHz con GPS
GSV [11] sat  31 elv  2 azm  73 snr  0dBHz con GPS

gpstool can assert an output GPIO pin in approximate syntonization with the 1PPS signal derived from either DCD or GPIO. This example (which I've run on a Raspberry Pi with the GR-701W) uses pin 16. Note the addition of the -p flag. (Again, you may have to run gpstool as root to access the GPIO pins.)

# gpstool -D /dev/ttyUSB0 -b 9600 -8 -n -1 -E -c -p 16

The GPIO functions implemented in Diminuto and used by gpstool may get confused if gpstool exits ungracefully leaving GPIO pins configured. If necessary, you can deconfigure GPIO pins using the Diminuto pintool utility

# pintool -p 18 -n
# pintool -p 16 -n

True Versus Magnetic Bearings

GPS devices compute the true bearing by comparing successive position fixes to determine your speed and direction. Hence, the true bearing, e.g. "135.000*T", is only reliable if you are moving, and at a speed fast enough to be within the resolution of the accuracy of the position fix. The magnetic bearing is an actual magnetic compass bearing, but is only provided by GPS devices which also have a magnetic compass; otherwise it will be displayed as "0.000*M". The cardinal compass direction, e.g. "SE", is based on the true bearing.

Google Earth Pro

In February 2017 I used Hazer with Google Earth Pro, the desktop version of the web based application. Today, August 2018, the real-time GPS feature of Google Earth Pro no longer seems to work with latest version, 7.3.2, for the Mac (I haven't tried it for other operating systems). Neither does 7.3.1. But 7.1.8 works.

Google Earth Pro only accepts GPS data on a serial port, or at least something that kinda sorta looks like a serial port. I've used a FIFO (named pipe), via the mkfifo(1) command, for stuff like this in the past, but there doesn't seem to be any way to get Pro to recognize the FIFO; only serial(ish) devices may apply here.

So I processed the NMEA stream from a serial-attached GPS device using gpstool running on a Raspberry Pi, then forwarded it via UDP datagrams to another gpstool on the Raspberry Pi. (This is the bin/producer.sh script.)

gpstool -D /dev/ttyUSB0 -b 4800 -8 -n -1 -6 -c -G ip6-localhost:5555 -E

Then I used the second gpstool to forward the NMEA stream out a serial port across a two FTDI USB-to-serial adaptors hooked back-to-back with a null modem in between, to a Mac running Google Earth Pro. (This is the bin/provider.sh script.)

gpstool -G :5555 -K -D /dev/ttyUSB1 -b 4800 -8 -n -1

I used the "GPS Import Realtime" drop down menu in Google Earth Pro to select the USB serial device representing the other end of the FTDI "milking machine". The menu liked the device "/dev/cu.usbserial-XXXXXXXX" where the Xs are some kind of internal identifier on MacOS where Pro was running.

Empirically, and according to a Google search, anecdotally, but undocumentedly, Google Earth Pro appears to only accept serial input at 4800 baud. More recent and advanced GPS devices default to 9600 baud, and can overrun a 4800 baud serial port. So I used a USGlobalSat BU-353S4, which defaults to 4800 baud, as my GPS device on the Linux server.

As Rube Goldberg as this is, it seems to work.

The last time I tried this, in the summer of 2018, the current version of Google Earth at that time, 7.3.2, no longer supported this feature (even though it was documented). Neither did the prior version of 7.3.1. But 7.1.8, which was still available, worked as expected.

In the summer of 2019, Google Earth Pro 7.1.8 no longer works on the most recent edition of MacOS, 10.15 "Catalina". I can no longer do real-time moving map displays using Google Earth Pro. Instead, I use the -C command line option of gpstool to save the PVT fixes to a CSV file, use the csv2kml script in Hazer to convert the CSV file to Keyhole Markup Langauge (KML), then import the resulting KML file into Google Earth Pro for visualization.

NMEA TXT Sentences

Some devices are chatty and emit interesting and sometimes useful information as NMEA TXT sentences. These are recognized by Hazer and logged by gpstool. Some of the functional tests save standard error output in log files under the build artifact directory, or redirected to the system log using the Diminuto log command.

2019-06-18T19:11:44.744147Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,HW UBX-M8030 00080000*60"
2019-06-18T19:11:44.744971Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,ROM CORE 3.01 (107888)*2B"
2019-06-18T19:11:44.745115Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,GPS;GLO;GAL;BDS*77"
2019-06-18T19:11:44.745269Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,SBAS;IMES;QZSS*49"
2019-06-18T19:11:44.745419Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,GNSS OTP=GPS;GLO*37"
2019-06-18T19:11:44.745606Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,LLC=FFFFFFFF-FFFF7CBF-FFED7FAA-FFFFFFFF-FFFFFFF9*52"
2019-06-18T19:11:44.745764Z <INFO> [27876] {7f65cb0405c0} Parse NMEA TXT "$GNTXT,01,01,02,PF=3FF*4B"

The u-blox ZED-F9P issues unsolicited warning and error messages as NMEA TXT messages. For the most part these are undocumented. Like all other such messages, they are logged.

2019-11-15T17:09:05.039345Z <INFO> [24404] {7f1c8e05f600} Parse NMEA TXT "$GNTXT,01,01,00,MISM c 2 t 82497615*6C"

Phantom GPS Satellite PRN 4

Around 2018-11-29T12:00-07:00, I was testing some changes to Hazer with the Ublox-8 based BU353W10 receiver by comparing its results to those of the web site https://in-the-sky.org/satmap_radar.php that presents a real-time sky map of the visible orbiting space vehicles (not just GPS). I noticed that my BU353W10 was reporting GPS PRN 4 as "in view" with a zero elevation and zero azimuth; that vehicle wasn't reported by the sky map. Worse: a little web-search-fu told me that there was no PRN 4; it does not appear in the most recent GPS almanac. The GPS vehicle using PRN 4 was decommisioned and its pseudo-random number code has not yet been reused.

Before I could do much else, PRN 4 dropped from view.

PRN 4 reappeared the next morning around 2018-11-30T09:00-07:00. I quickly dumped the raw NMEA and verified using NMEA 0183 Version 4.10 pp. 96-97 that I wasn't decoding the GSV sentence incorrectly.

$GPGSV,4,1,15,04,,,36,05,04,062,22,10,27,253,32,13,32,053,38*43\r\n
$GPGSV,4,2,15,15,60,092,39,16,15,289,26,20,53,269,,21,72,336,22*75\r\n
$GPGSV,4,3,15,24,06,129,33,26,10,264,15,27,11,322,31,29,36,170,50*78\r\n
$GPGSV,4,4,15,46,38,215,45,48,36,220,43,51,44,183,44*43\r\n

However, I noticed that the elevation and azimuth for PRN 4 weren't actually zero: they were empty strings, although the SNR was a reasonable (and changing over time) value. I coded up a change to Hazer to detect this and mark it, and to gpstool to display a '?' next to that SAT entry. I was able to test this before PRN 4 again dropped from view.

PRN 4 reappeared about twenty minutes later.

SAT [  1]     4:   0*elv    0*azm   33dBHz   ?                         GPS
SAT [  2]     8:   3*elv  328*azm    0dBHz                             GPS
SAT [  3]    10:  43*elv  273*azm   35dBHz <                           GPS
SAT [  4]    13:  16*elv   41*azm    0dBHz                             GPS
SAT [  5]    15:  48*elv   58*azm   37dBHz <                           GPS
SAT [  6]    16:   6*elv  270*azm   26dBHz <                           GPS
SAT [  7]    20:  67*elv  305*azm   35dBHz <                           GPS
SAT [  8]    21:  82*elv   82*azm   27dBHz <                           GPS
SAT [  9]    24:  20*elv  112*azm   30dBHz <                           GPS
SAT [ 10]    27:  28*elv  311*azm   35dBHz <                           GPS
SAT [ 11]    29:  14*elv  171*azm   46dBHz <                           GPS
SAT [ 12]    32:  12*elv  207*azm   41dBHz <                           GPS
SAT [ 13]    46:  38*elv  215*azm   45dBHz                             GPS
SAT [ 14]    48:  36*elv  220*azm   44dBHz <                           GPS
SAT [ 15]    51:  44*elv  183*azm   45dBHz <                           GPS
SAT [ 16]    68:  19*elv   41*azm   27dBHz <                           GLONASS
SAT [ 17]    69:  66*elv    6*azm   33dBHz <                           GLONASS
SAT [ 18]    70:  42*elv  246*azm   18dBHz <                           GLONASS
SAT [ 19]    77:   2*elv   15*azm   22dBHz                             GLONASS
SAT [ 20]    78:   3*elv   60*azm   18dBHz                             GLONASS
SAT [ 21]    83:  20*elv  152*azm   35dBHz <                           GLONASS
SAT [ 22]    84:  78*elv  169*azm   30dBHz <                           GLONASS
SAT [ 23]    85:  40*elv  326*azm   28dBHz <                           GLONASS
SAT [ 24]    89:  67*elv    6*azm    0dBHz                             GLONASS

It continued to drop from view and reappear. Its period of appearance does not coincide with the GPS orbital period.

Neither NMEA 0183 4.10 nor Ublox 8 R15 suggests any interpretation of the empty elevation and azimuth fields. As always, I'm assuming this somehow is a bug in my code. But it does occur to me that PRN 4 would be useful for testing a ground-based GPS transmitter; the period of its appearance would make sense for a transmitter in the continental America time zones.

Checking the FAA Notice To Airmen (NOTAM) notifications on the web, there are GPS disruptions scheduled for the late November/early December time frame, centered on the White Sands Missle Range (WSMR) in New Mexico, and the Yuma Proving Grounds (YPG) in Arizona, either of which is potentially within range of my location in Denver Colorado. So this could be the U.S. military doing testing.

I temporarily added code to gpstool to monitor the comings and goings of GPS PRN 4 and remark upon them in the system log. Here's an example of what those log messages look like during an actual run of just a few hours. '#' is the initial state value when gpstool starts running, '?' means GPS PRN 4 came into view, and ' ' means it exited from view. The log also includes the initial and maximum signal strength, and the transmission duration in milliseconds. All clock times are in MST.

Here is the log for a twenty-four hour period.

Dec  3 09:32:06 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '#' now '?' at 25dBHz
Dec  3 09:42:03 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 38dBHz for 597010ms
Dec  3 09:48:59 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 34dBHz
Dec  3 09:58:51 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 592009ms
Dec  3 10:01:11 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 35dBHz
Dec  3 10:29:57 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 40dBHz for 1726038ms
Dec  3 10:30:11 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 38dBHz
Dec  3 10:32:34 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 39dBHz for 143006ms
Dec  3 10:33:14 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 35dBHz
Dec  3 10:34:27 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 40dBHz for 72998ms
Dec  3 10:34:50 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 33dBHz
Dec  3 10:35:38 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 37dBHz for 48001ms
Dec  3 10:35:50 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 37dBHz
Dec  3 11:37:43 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 43dBHz for 3713086ms
Dec  3 11:42:22 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 29dBHz
Dec  3 11:49:29 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 427014ms
Dec  3 11:59:15 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 25dBHz
Dec  3 11:59:16 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 25dBHz for 999ms
Dec  3 13:35:06 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 23dBHz
Dec  3 13:35:07 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 23dBHz for 998ms
Dec  3 16:37:48 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 31dBHz
Dec  3 16:45:52 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 484015ms
Dec  3 17:08:03 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 29dBHz
Dec  3 17:11:16 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 193003ms
Dec  3 17:14:37 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 30dBHz
Dec  3 17:19:16 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 34dBHz for 279011ms
Dec  3 17:26:53 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 31dBHz
Dec  3 17:41:51 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 898023ms
Dec  3 18:00:33 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 30dBHz
Dec  3 18:06:14 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 341011ms
Dec  3 18:09:17 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 33dBHz
Dec  3 18:22:07 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 37dBHz for 770011ms
Dec  3 18:26:10 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 34dBHz
Dec  3 18:59:11 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 39dBHz for 1981048ms
Dec  3 19:00:54 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 33dBHz
Dec  3 19:30:34 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 38dBHz for 1780046ms
Dec  3 19:33:50 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 31dBHz
Dec  3 19:44:47 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 36dBHz for 657014ms
Dec  3 19:55:24 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 27dBHz
Dec  3 20:02:55 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 33dBHz for 451009ms
Dec  4 05:52:37 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 26dBHz
Dec  4 05:52:39 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 26dBHz for 2001ms
Dec  4 05:52:40 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 26dBHz
Dec  4 05:52:41 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 26dBHz for 1007ms
Dec  4 08:10:39 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 31dBHz
Dec  4 08:16:56 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 33dBHz for 377014ms
Dec  4 08:19:21 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 29dBHz
Dec  4 08:22:17 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 31dBHz for 176011ms
Dec  4 08:28:52 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 32dBHz
Dec  4 08:40:15 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 34dBHz for 683019ms
Dec  4 09:03:34 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 33dBHz
Dec  4 09:17:36 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 38dBHz for 842031ms
Dec  4 09:22:47 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was ' ' now '?' at 35dBHz
Dec  4 09:39:55 nickel gpstool[20052]: gpstool: phantom GPS PRN 4 was '?' now ' ' at 35dBHz for 1027038ms

The durations in this sample last for anywhere from a second to half an hour. During this period the transmissions ceased at 20:00MST and resumed at 06:00MST. This seems correlated with typical "working hours" in the U.S. continental time zones.

On 2018-12-04, the U. S. Coast Guard straightened me out.

*** GENERAL MESSAGE TO ALL GPS USERS ***
ON APPROXIMATELY 10 OCT 2018 SVN36 WILL RESUME TRANSMITTING L-BAND
UTILIZING PRN04. AT L-BAND ACTIVATION, SVN36/PRN04 WILL BE UNUSABLE
UNTIL FURTHER NOTICE.  ADDITIONALLY, NO BROADCAST ALMANACS WILL
INCLUDE SVN36/PRN04 UNTIL FURTHER NOTICE.
*** GENERAL MESSAGE TO ALL GPS USERS ***

On 2018-12-23, the first of the next generation Block IIIA GPS satellites was launched.

*** GENERAL MESSAGE TO ALL GPS USERS ***
GPS III SVN74 (PRN04) WAS LAUNCHED ON 23 DEC 2018 (2018 JDAY 357)
AT 1351 ZULU. THIS SATELLITE WILL UNDERGO EXTENSIVE ON-ORBIT CHECK
OUT AND TESTING PRIOR TO BEING SET HEALTHY.  A USABINIT NANU WILL
BE SENT WHEN THE SATELLITE IS SET ACTIVE TO SERVICE.
*** GENERAL MESSAGE TO ALL GPS USERS ***

Note that the new satellite uses PRN 4. I suspect now that the earlier use of PRN 4 by SVN36 was some kind of control segment testing in advance of the launch of SVN74.

GPS Constellation Status 2019-02-20

The Global Positioning Systems Directoriate proposed a change to the "Navstar GPS Control Segment to User Support Community Interfaces" (ICD-GPS-240 and ICD-GPS-87) to modify the GPS Operational Advisory Message to eliminate information about the orbital plane/slot and clock. This change was approved by the Interface Control Working Group on 2018-12. I've captured a portion of a recent Advisory Message here to snapshot this information before it is removed.

GPS Constellation Active Nanu Status 2/20/19
Plane       Slot    SVN     PRN     Block   Clock
A           1       65      24      IIF     CS      
A           2       52      31      IIR-M   RB      
A           3       64      30      IIF     RB      
A           4       48      7       IIR-M   RB      
B           1       56      16      IIR     RB      
B           2       62      25      IIF     RB      
B           3       44      28      IIR     RB      
B           4       58      12      IIR-M   RB      
B           5       71      26      IIF     RB      
C           1       57      29      IIR-M   RB      
C           2       66      27      IIF     RB      
C           3       72      8       IIF     CS      
C           4       53      17      IIR-M   RB      
C           5       59      19      IIR     RB      
D           1       61      2       IIR     RB      
D           2       63      1       IIF     RB      
D           3       45      21      IIR     RB      
D           4       67      6       IIF     RB      
D           5       46      11      IIR     RB      
D           6       34      18      IIA     RB
E           1       69      3       IIF     RB      
E           2       73      10      IIF     RB      
E           3       50      5       IIR-M   RB      
E           4       51      20      IIR     RB      
E           6       47      22      IIR     RB      
F           1       70      32      IIF     RB      
F           2       55      15      IIR-M   RB      
F           3       68      9       IIF     RB      
F           4       60      23      IIR     RB      
F           5       41      14      IIR     RB      
F           6       43      13      IIR     RB      

GPS Expandable Slot Designations
B5=B1F
D5=D2F
F5=F2F
B1=B1A
D2=D2A
F2=F2A

U-Blox UBX-ZED-F9P Bug: wrong number of satellites reported for GLONASS

The Ardusimple SimpleRTK2B board uses the U-Blox ZED-F9P GPS receiver. I'm pretty sure the firmware in the ZED-F9P-00B-01 chip on my SimpleRTK2B board has a bug. I believe this GSV sentence that it emitted is incorrect.

$GLGSV,3,3,11,85,26,103,25,86,02,152,29,1*75\r\n

This GSV sentence says it is the third of three GSV sentences for the GLONASS constellation, and there are eleven total satellites cited in the three GSV sentences.

GSV is unusual amongst the typical NMEA sentences in that it is variable length. Each GSV sentence can contain at most four satellites, each represented by four numbers: the space vehicle ID (which is specific to the constellation), the SV's elevation in degrees, the SV's azimuth in degrees, and the signal strength in dB Hz.

The two prior GSV sentences for this report of GLONASS satellites in view would have had at most four satellites, for a total of eight, leaving three satellites for this final sentence in the sequence. This sentence only has the metrics for two satellites. Note also that this messages has the optional signal identifier as its last field before the checksum.

I think either there should be a third set of four fields for the eleventh satellite, or the total count should be ten instead of eleven. My software has been modified to account for this malformed message; it originally core dumped with a segmentation violation.

(2019-06-06: U-Blox says this FW bug will be fixed in a subsequent release.)

Ardusimple SimpleRTK2B

The Ardusimple SimpleRTK2B board features a ZED-F9P "9th generation" U-Blox chip. It can be equipped with radios like the ZigBee-based XBee. This allows one SimpleRTK2B to be used as a stationary "base" in high-precision "survey-in" mode, and a second SimpleRTK2B in mobile "rover" mode; the base transmits position corrections to the rover via the radio using the RTCM protocol once the survey has been completed to the configured level of accuracy. This is a form of Differential GNSS and can, over time, achieve very high position (and time) accuracy and precision.

                            Radio Antenna
                                  :
                            [ XBee3 SX ]
                                  ^ 
                                  | 
                                  v 
                 Header <--> XBee UART <--> FTDI <--> USB <--> { XCTU }
                                  ^
                                  |
                                  v
                              UBX UART2
                                  |
                                  v
Header <--> UBX UART1 <--> [ UBX-ZED-F9P ] <--> USB <--> { u-center or gpstool }
                                  :
                              GPS Antenna

In the diagram above I show where you would connect the XBee configuration tool XCTU, or the u-blox configuration tool u-center, but in normal use I only have a host running gpstool connected.

I used XCTU to configure the XBee radio (setting it to Transparent mode, as a Zigbee Router, and a serial port baud rate of 38400 8N1) and persist that configuration in the XBee's non-volatile memory.

The ZED-F9P is configured at run-time using gpstool to send it commands. This configuration is in volatile memory so that the GPS receiver reverts back to its factory defaults when it is power cycled. Among other things, this allows me to use SimpleRTK2B boards interchangeably in the field.

Ultimately, neither the the Digi Xbee3 SX 900MHz radios, nor the Digi Xbee3 LTE-M cellular radios which have the same form factor and are pin compatible, met my needs, although I tried both, the latter using AT&T LTE-M SIMs. As far as I can tell, however, both worked as advertised.

Lost /dev/ttyACM Characters

I've been troubleshooting a weird issue with sequences of characters being lost on the modem-ish (ttyACM) USB connection on a U-blox UBX-ZED-F9P (generation 9) chip. This occurs when using the Ardusimple SimpleRTK2B and Sparkfun GPS-RTK2 boards. I also see it a U-Blox UBX-M8030 (generation 8) chip in a GlobalSat BU353W10 dongle. I've seen in on Intel (Dell) and ARM (Raspberry Pi 3B+ and 4B) systems. I've seen it using my software, using socat, and even just using cat, to collect data off the USB port. I've seen it at a variety of baud rates.

I've described this at length in the article

https://coverclock.blogspot.com/2019/06/this-is-what-you-have-to-deal-with.html

EOF on the Device

Several times, while running this software under Ubunto 19.10 in a virtual machine on a Lenovo ThinkPad T430s running Windows 10 using a U-blox ZED-F9P receiver on a SparkFun GPS-RTK2 board - and only under those circumstances - I've seen gpstool receive an EOF from the input stream. The standard I/O function ferror() returned false and feof() returned true. The tool fired right back up with no problem. This happens very infrequently, and my suspicion is that VMware Workstation 15 Pro is disconnecting the USB interface from the VM for some reason, maybe as a result of Windows 10 power management on the laptop. This is something to especially worry about if you are running a long term survey which would be interrupted by this event. (I was doing this mostly to test VMware and my Ubuntu installation on my field laptop.)

Differential GNSS Using Tumbleweed

Tumbleweed uses three Raspberry Pi 3B+ SBCs and two Ardusimple SimpleRTK2B boards, each equipped with a U-blox UBX-ZED-F9P receiver chip and a multi-band GPS active antenna, to implement a Differential GNSS system which is (at least theoretically) capable of centimeter-level accuracy. One of the Pis is a stationary base station running its ZED-F9P in "survey-in" mode, one of the Pis is a mobile rover whose ZED-F9P receives RTK updates from the base station, and one of the Pis is a router that receives RTK updates from one base station and forwards them to one or more rovers. (You can combine the base and the router on to one Pi, but I chose not to configure my set up that way.)

N.B. The Diminuto logging system used by Hazer writes log messages to standard error if the caller is an interactive process, and to the system log if the caller is a daemon. Running some of these scripts in the background and then logging off to let them continue to run causes the logging system to perceive that they have transitioned from being interactive to being a daemon (and this isn't a mistake). Consequently, subsequent log messages will go to the system log and not the error log file. This can seem a little mysterious.

(In the following examples, I use a script called "peruse", which in turn uses a Diminuto script called "observe". I also make use of the SIGHUP capability of gpstool using a script called "hups" which sends a HUP (hangup) signal to any running instance of gpstool everytime you hit the RETURN (ENTER) key. You can read more about these scripts in the section on headless operation below.)

Router

The Tumbleweed router, which is on my LAN, must have a static IP address or a usable Dynamic DNS (DDNS) address (which is what I do) that can be reached through the firewall. ":tumbleweed" identifies the service on the localhost defined in /etc/services from which to receive and send RTK update datagrams. (My Tumbleweed router is code-named "eljefe". You may come across this and related names in logs, screen shots, etc.)

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
router :tumbleweed &
peruse router err

Base

The Tumbleweed base is typically on my LAN, but can be on the WAN by changing the hostname through which the router is addressed. "tumbleweed:" idenfities the hostname of the router on the LAN as defined in /etc/hosts, and ":tumbleweed" the service on the router on the LAN defined as in /etc/services (typically I defined this to be port 21010) to which to send RTK update datagrams. (I have used two different Tumbleweed bases; the portable version I use with a tripod-mounted anntenna is code-named "bodega" and the one whose antenna is permanently fixed is "hacienda".)

In one window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
survey tumbleweed:tumbleweed /dev/tumbleweed & peruse survey err

In another window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
peruse survey out

Depending on the specified accuracy - encoded in a message sent to the chip by the script - it can take days for the receiver to arrive at a solution that has the required radius of error. This depends greatly on antenna placement as well as other factors that may be less under your control. Putting the antenna in my front yard, which has the usual ground clutter of trees and adjacent houses, resulted in taking about two days to get to a resolution of ten centimeters, about four inches. Using a permanently attached antenna near the peak of the roof of my house took a little over twelve hours to get the same resolution.

Because of this potentially lengthy duration of the survey, you don't want to do it more than once. First rule is: don't move the antenna. (If you do, you'll have to do another survey, no matter what.) Second rule is: don't restart the receiver.

N.B.: Experience in testing the F9P receiver suggests that survey mode has a maximum duration of thirty (30) days. At thirty days plus one second (2592001 seconds total), the error distance calculated by the receiver ceases to become any smaller and the elapsed time of the survey ceases to become any larger. The gpstool utility reports both of these values in the BAS output line. The thirty day limit is probably documented somewhere, and may even be configurable, but if so I haven't found it. See [u-blox 9 Integration, 3.1.5.5.1, "Survey-in"] for details about survey mode.

Fortunately, once a survey is successfully completed, the base station script saves the pertinent results from a successful survey in a file whose names ends in ".fix". This is a human-readable ASCII file that stores the values in a form that can later be imported into another script that runs the receiver in fixed mode, in which the receiver is told what its location is, and so immediately begins transmitting corrections based on this information.

In one window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
fixed tumbleweed:tumbleweed /dev/tumbleweed & peruse fixed err

In another window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
peruse fixed out

The choice between running the base in survey mode or in fixed mode is automated in a script that does latter if the base.fix file is present and seems sane, and the former if it is not. This allows you to restart the base station and have it do the right thing depending on whether or not the survey had been previously completed.

In one window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
base tumbleweed:tumbleweed /dev/tumbleweed & peruse base err

In another window:

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
peruse base out

The .fix file contains the following UBX variables in character hex format in this order.

FIXED_POS_ACC
LAT
LATHP
LON
LONHP
HEIGHT
HEIGHTHP

Here is an example of an actual base.fix file that resulted from a survey done with 10 cm (1000 x 0.1mm) accuracy; note that UBX stores its variables in little-endian byte order.

\xe8\x03\x00\x00
\xb1\x1f\xb8\x17
\x20
\x91\xdc\x52\xc1
\xe5
\x01\x94\x02\x00
\x1d

Geocodes

Here are some geocodes - different ways of encoding and representing the location of the base I am using.

  • Decimal degrees: 39.794275645, -105.153414843
  • Plus Codes: 85FPQRVW+PJ
  • What3Words: ///beard.hobby.funds
  • Degrees Lat Long:  39.7942756°, -105.1534148°
  • Degrees Minutes: 39°47.65654', -105°09.20489'
  • Degrees Minutes Seconds:  39°47'39.3923", -105°09'12.2934"
  • UTM: 13S 486865mE 4404935mN
  • UTM centimeter: 13S 486865.33mE 4404935.48mN
  • MGRS: 13SDE8686504935
  • Grid North: -0.1°
  • GARS: 150LV28
  • Maidenhead: DM79KT10OP10
  • GEOREF: EJQK50794765

Rover

A Tumbleweed rover (there can be more than one) is typically on the WAN, and is agnostic as to the Internet connection (I use a USB LTE modem). "tumbleweed.test:" stands in for the fully qualified domain name (FQDN) on the WAN as (for me) defined by DDNS and resolved via DNS, and ":tumbleweed" the service on the router to which send keep alive datagrams and receive RTK update datagrams as defined in /etc/services. (My rover is code-named "mochila".)

I use just one window in these examples, but you can use two like I did for survey etc. above to monitor the error and output streams in parallel.

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
rover tumbleweed.test:tumbleweed /dev/tumblweed &
peruse rover err# Control-C to exit upon seeing "Ready".
peruse rover out

The rover can also generate a CSV file as it runs that will contain the results of the PVT solution every time is is generated and reported. This CSV file can be imported into a spreadsheet like Excel, converted into a stream of NMEA RMC sentences using the csv2rmc script for use with other GPS tools (including gpstool itself), or converted into a KML (Keyhole Markup Language) XML file using the csv2kml script for use with geodesy tools like Google Earth.

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
benchmark tumbleweed.test:tumbleweed /dev/tumbleweed &
peruse benchmark err# Control-C to exit upon seeing "Ready".
tail -f out/host/tmp/benchmark.csv | csv2rmc | gpstool -R

The rover can also be run as an uncorrected mobile unit, basically a receiver that doesn't support Differential GNSS. (It might be a good idea to power cycle the F9P before changing from rover to mobile; some of the rover configuration seems to be sticky.)

cd ~/src/com-diag-hazer/Hazer
. out/host/bin/setup
# Power cycle the F9P if previously configured for corrections.
mobile /dev/tumbleweed &
peruse mobile err# Control-C to exit upon seeing "Ready".
peruse mobile out

Once I'm confident that everything works and I don't need to check the error log (you can always look at it later), I use this shorthand to fire up the rover. (Something similar works for the base, router, and mobile scripts too.)

cd ~/src/*hazer/Hazer
. out/host/bin/setup
rover tumbleweed.test:tumbleweed & sleep 5 ; peruse rover out

In the field, I commonly run the rover using the benchmark script, which is a rover that saves each UBX precision fix by appending the values of the solution to a Comma Separated Value (CSV) file which can be post-processed in any number of ways for analysis. I combined the use of the benchmark script with the peruse script (to full-screen display the results in real-time) with the hups script (to save the most recent full-screen display to a time-stamped file) in a script named "field". This vastly simplifies field testing (which as I like to point out, is often literally in a field somewhere) and reduces the number of hands and fingers you need to do this kind of testing.

cd ~/src/*hazer/Hazer
. out/host/bin/setup
field tumbleweed.test
# Hit the return key to save the most recent display.

Headless

I'm running all three, router, base, and rover, as simple background processes. But it is also possible to run them in daemon mode, in which case messages normally written to standard error are logged to the system log, which on the Pi is typically in /var/log/syslog. (This also happens when you run the scripts in the background and then later logout. This can be a little confusing since the log will transition from going into the error log file to going into the system log.)

Also, I run all three in "headless" mode, where the screens normally written to standard output are instead written to a file, and a script is used to display the file as it changes; this decouples the router, rover, and base software from the display terminal while still allowing an on-demand real-time display. (This requires that the inotify-tools package be installed on the Pi.)

Both gpstool (which implements the base and the rover) and rtktool (which implements the router) can be run as daemons via a command line switch (although I have not done so in these examples); the headless mode can still be used.

When running in headless mode and receving a hangup signal (SIGHUP), gpstool will checkpoint the headless file: it will create a hardlink to the current headless output file with a timestamp prefix with a resolution in microseconds. This is particularly useful in field testing to capture the relevant data at a specific point in time and space. The hup and hups scripts use the pkill command to send a SIGHUP to all gpstool instances.

Networking

Note that the actual IP address of neither the base nor the rover need be known. This is important because the rover (and sometimes the base) is on the a WAN, in my set up via an LTE modem to a mobile service provider. (Despite my best udev fu, these modems, working in end-user mode, worked like crap until I upgraded to Raspbian 10, at which point they worked flawlessly.) In this case, not only can the IP address on the WAN not be known ahead of time until the modem connects to the carrier network, over the span of a long base survey which can take many many hours depending on the desired level of precision (configured via a UBX message defined in the base script) and the quality of the view of the sky afforded to the base, the IP address provided by the WAN via DHCP, as well as the port number, may change. In my tests, with the base antenna set up in my front yard, an accuracy of ten centimeters (about four inches) requires about sixteen hours of "survey-in" time (your mileage may vary). During this time the mobile network can disconnect from the modem and reconnect. The base and Tumbleweed router routinely deal with this; but it means the IP address you begin with in your survey will not be the one you end up with when the survey is complete or as the base sends subsequent updates to the rover.

The Tumbleweed router does require a fixed IP address for this to work. In my setup, the Tumbleweed router connects to my home IP router (in my case, directly via wired Ethernet). But my IP router gets its globally routable WAN IP address from my internet provider via DHCP. My IP router supports Dynamic DNS (DDNS), in which it automatically sends an notification to my DDNS provider regarding the assigned IP address, who then changes the DNS database to map a fixed FQDN to the provided address. This FQDN is represented by "tumbleweed.test" above. I have configured the firewall in my IP router to forward the port named "tumbleweed" above to and from the same port on the static IP address on my LAN that I assigned to the Tumbleweed router.

As mentioned above, it is not unusual to see a WAN connected rover or base drop off and reappear on the WAN, often with a different port number and IP address. The router makes a note of this; here are the results of an overnight test. The IPv4 addresses are expressed in IPv6 notation, which is what the router uses natively. Also, I've obfuscated portions of the WAN addresses. The base and rover can use either IPv4 or IPv6. You can see the base and rover changing ports and IP addresses, and even sometimes switching between the WAN and the LAN when they have access to both (the latter via my WiFI WLAN).

2019-06-25T23:29:47.791661Z <INFO> [744] {76f6b530} Begin
2019-06-25T23:29:47.792252Z <INFO> [744] {76f6b530} Router (3) ":tumbleweed" [::]:21010
2019-06-25T23:29:47.792322Z <INFO> [744] {76f6b530} Start
2019-06-25T23:30:52.219618Z <NOTE> [744] {76f6b530} Client New base [::ffff:XXX.YYY.0.61]:6571
2019-06-25T23:30:52.219789Z <NOTE> [744] {76f6b530} Client Set base [::ffff:XXX.YYY.0.61]:6571
2019-06-25T23:36:28.213760Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T00:33:19.063809Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T00:33:28.416375Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T00:58:59.064859Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T00:59:08.301363Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T02:38:48.012487Z <NOTE> [744] {76f6b530} Client Old base [::ffff:XXX.YYY.0.61]:6571
2019-06-26T02:38:48.013164Z <NOTE> [744] {76f6b530} Client New base [::ffff:192.168.1.188]:43289
2019-06-26T02:38:48.013336Z <NOTE> [744] {76f6b530} Client Set base [::ffff:192.168.1.188]:43289
2019-06-26T02:39:28.253289Z <NOTE> [744] {76f6b530} Client Old base [::ffff:192.168.1.188]:43289
2019-06-26T02:39:29.070321Z <NOTE> [744] {76f6b530} Client New base [::ffff:XXX.YYY.1.209]:8231
2019-06-26T02:39:29.070540Z <NOTE> [744] {76f6b530} Client Set base [::ffff:XXX.YYY.1.209]:8231
2019-06-26T03:05:28.058945Z <NOTE> [744] {76f6b530} Client New rover [::ffff:192.168.1.1]:42804
2019-06-26T03:05:39.069692Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.33.234]:2645
2019-06-26T03:05:48.256039Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.9.215]:4224
2019-06-26T03:05:59.069366Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:192.168.1.1]:42804
2019-06-26T06:09:28.058577Z <NOTE> [744] {76f6b530} Client New rover [::ffff:192.168.1.1]:42804
2019-06-26T06:09:39.065573Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.9.215]:4224
2019-06-26T06:09:48.108028Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.16.196]:7052
2019-06-26T06:09:59.065301Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:192.168.1.1]:42804
2019-06-26T06:10:08.058974Z <NOTE> [744] {76f6b530} Client New rover [::ffff:192.168.1.182]:42804
2019-06-26T06:10:19.066025Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.16.196]:7052
2019-06-26T06:10:28.266752Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.16.196]:7040
2019-06-26T06:10:39.065709Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:192.168.1.182]:42804
2019-06-26T06:43:20.066215Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.16.196]:7040
2019-06-26T06:43:48.268595Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.16.196]:7040
2019-06-26T09:13:20.020202Z <NOTE> [744] {76f6b530} Client Old base [::ffff:XXX.YYY.1.209]:8231
2019-06-26T09:13:20.220657Z <NOTE> [744] {76f6b530} Client New base [::ffff:XXX.YYY.13.157]:5803
2019-06-26T09:13:20.220928Z <NOTE> [744] {76f6b530} Client Set base [::ffff:XXX.YYY.13.157]:5803
2019-06-26T10:58:19.069629Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:XXX.YYY.16.196]:7040
2019-06-26T10:58:28.052347Z <NOTE> [744] {76f6b530} Client New rover [::ffff:192.168.1.1]:42804
2019-06-26T10:59:08.257400Z <NOTE> [744] {76f6b530} Client New rover [::ffff:XXX.YYY.17.45]:4970
2019-06-26T10:59:18.069004Z <NOTE> [744] {76f6b530} Client Old rover [::ffff:192.168.1.1]:42804
2019-06-26T14:01:28.248198Z <NOTE> [744] {76f6b530} Client Old base [::ffff:XXX.YYY.13.157]:5803
2019-06-26T14:01:29.059184Z <NOTE> [744] {76f6b530} Client New base [::ffff:XXX.YYY.27.107]:4679
2019-06-26T14:01:29.059431Z <NOTE> [744] {76f6b530} Client Set base [::ffff:XXX.YYY.27.107]:4679
2019-06-26T14:02:08.263694Z <NOTE> [744] {76f6b530} Client Old base [::ffff:XXX.YYY.27.107]:4679
2019-06-26T14:02:09.063249Z <NOTE> [744] {76f6b530} Client New base [::ffff:XXX.YYY.27.107]:4686
2019-06-26T14:02:09.063542Z <NOTE> [744] {76f6b530} Client Set base [::ffff:XXX.YYY.27.107]:4686

When a client of the router changes its port or IP address, it appears to be a new rover or base. In the case of the rover, it is immediately registered with the router, which begins feeding it RTK updates from the current base. The entry for the old rover times out when no more keep alives are received, and it is removed from the list of active rovers on the router. In the case of the base, the new base is rejected until similarly the old base times out and is removed when no more updates are received, at which point the new base is registered on the router and its updates are used to feed the rovers.

All internet communication is via UDP datagram. Each datagram contains one and only one fully validated RTCM message complete with checksum and preceeded by a four-byte sequence number. UDP datagrams received out of sequence are dropped by the router or the rover.

The internet route to the rover for RTK updates is determined by the address and port from which a keep alive is received by the router from the rover. The keep alive message is an RTCM message with a zero-length payload. A keep alive message is sent by each rover to the router once every twenty-five seconds. Thirty seconds is a typical timeout after which a firewall or router will remove the UDP return route. (This mechanism was inspired by a similar one used by SIP to route RTP packets via UDP to VoIP phones.)

Hardware

Although Tumbleweed has been implemented using the Ardusimple SimpleRTK2B board, the same software runs on the SparkFun GPS-RTK2 board which uses the same U-blox UBX-ZED-F9P receiver chip. Ublox has since introduced their own F9P application board, the C099-F9P, but I haven't tried it. Since I ended up implementing the inter-board communication channel on the Raspberry Pi, I don't need the support for various radio technologies that both the Ardusimple and the Ublox boards provide.

Issues

Note that the UDP data stream is not encrypted, nor is the source of the datagrams authenticated, so this mechanism is not secure. It should be. I'm pondering how best to accomplish that.

Datagram Transport Layer Security (DTLS) provides encryption and authentication for UDP protocols by adapting TLS (SSL) to datagram semantics, specifically dealing with lost or reordered packets. Unfortunately in doing so, DTLS eliminates the very advantages that caused me to choose UDP over TCP.

Most Linux distros will require that you either be in the "dialout" group (preferred), or run as root, in order to access serial devices like GPS receivers.

Also read the Issues section in the Diminuto README.

Google Maps

The mapstool utility converts strings containing latitude and longitude coordinates of various formats (several of which are output by the gpstool utility) into a decimal degrees format that is understood by Google Maps. You can cut and paste the output of mapstool directly into the search bar of the Google Maps web page.

$ mapstool "39.794212196, -105.153349930"# HPP format
39.794212196, -105.153349930

$ mapstool "39 47 39.16390(N) 105 09 12.05974(W)"# NGS format
39.794212194, -105.153349928

$ mapstool "39°47'39.163\"N, 105°09'12.060\"W"# POS format
39.794211944, -105.153350000

HPP is the U-blox UBX High Precision Position format. It is also a format directly supported by Google Maps.

NGS is the format used in the National Geodetic Survey datasheets. It is mostly for this format that I wrote this utility.

POS is the standard gpstool format. It is also a format (minus the BASH backslash escapes) directly supported by Google Maps.

Nine significant fractional digits are used in the decimal degrees output because that allows for about three significant fractional seconds digits for the input. Also, billionths of a degree is the precision provided by the UBX HPP format.

Haversine and Geodesic Distances

The utility haversine computes the distance between two coordinates expressed in decimal degrees using the Haversine algorithm based on great circles. This is a simple trigonometric approach that (incorrectly) assumes the Earth is a perfect sphere. The output is expressed in meters. The input can be cut and pasted directly from the mapstool utility (see above).

$ haversine 39.794212194, -105.153349928 39.794211944, -105.153350000
0.0285029342

The utility geodesic computes the distance between two coordinates expressed in decimal degress using the Geodesic algorithm based on an elliptical model of the Earth from the WGS84 datum (this is a model used by most GPS receivers). The output is expressed in meters. The input can be cut and pasted directly from the mapstool utility (see above). This is a more accurate, but much more computationally complex, approach than using great circles. This utility makes use of the geodesic.h and geodesic.c files from Charles F. F. Karney's geographiclib, which is licensed under the MIT license.

$ geodesic 39.794212194, -105.153349928 39.794211944, -105.153350000
0.0284344407

Memory leaks

When testing for memory leaks, I strongly recommend using valgrind, specfically with the full leak check option.

valgrind --leak-check=full gpstool ...

I was surprised to find it called out a twelve byte memory leak in the C library's locale handling, which gpstool requires to correctly display Unicode symbols like the degree symbol. Perhaps this will be fixed in a future GNU update (or it's a bug in my code that I can't see).

Acknowledgements

Thanks to Charles F. F. Karney for his MIT licensed geographiclib.

A big thank you to Brad Gabbard, a professional surveyor with Flatirons, Inc., a surveying, engineering, and geomatics firm located in Boulder Colorado. Mr. Gabbard generously shared some results off his professional Trimble GPS rover taken at NGS survey marker KK1446 that I have used to test this code.

Special thanks to Mrs. Overclock for her assistance in road testing (literally) this software and for her understanding regarding having GPS equipment littered all around the house both inside and outside.

About

Parse NMEA sentences and other typical output from GNSS devices.

Resources

License

Packages

No packages published
You can’t perform that action at this time.