Skip to content

coverclock/com-diag-hazer

Repository files navigation

com-diag-hazer

Parse NMEA strings and other typical output from GNSS devices.

Copyright

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

Except where noted, this software is an original work of its author.

Trademarks

"Digital Aggregates Corporation" is a registered trademark of the Digital Aggregates Corporation, Arvada, Colorado, USA.

"Chip Overclock" is a registered trademark of John Sloan, Arvada, Colorado, USA.

License

Licensed under the terms in LICENSE.txt.

Contact

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

Disclaimer

I am a software developer. I have been writing code since circa 1970 and started working full time in the field in 1976. I have an M.S. (1983) and a B.S. (1980) in computer science from an accredited program at a university. What I am not is a hardware or radio engineer, nor am I an expert in global navigation satellite systems. Behind all the software work I present here is a vast collection of hardware, firmware, and radio frequency expertise contributed by the developers of the GNSS products I use.

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 GNSS devices.

I also make use of Lady Heather, a terrific real-time GNSS monitoring program. I run it, along with my own software, and a script that periodically runs NTP query (ntpq), 24x7 on a Raspberry Pi with a touch-sensitive LCD display. This makes it easy for me to keep an eye on GPS and other GNSS constellations and on my various NTP micro-servers that depend upon GNSS for their clock discipline.

Abstract

This file is part of the Digital Aggregates Corporation Hazer project. Hazer is an open source toolkit for interpreting data streams typically generated by GPS and other GNSS receivers and emitted from serial(ish) ports, including USB and Bluetooth.

Hazer has a layered architecture that allows it to be used in a variety of ways. You can use the gpstool application in full-screen mode, watching its output in real-time. The app can be run in the background, for example as a daemon, in "headless" mode, and you can display its full-screen output in real-time at will in one or more windows. You can run the app in report mode, in which it emits the same lines of output, in the same fixed format, to standard output, and your own application can parse the results, for example via a shell pipeline or even via a socket. Or you can write your own code and call the C functions in the underlying library directly.

While Hazer is the name of the repository, it incorporates features in support of several different related projects described below, some of which are works in progress, and which may be found in this or other Digital Aggregates repositories:

  • Calico - Garmin CPO support;
  • Conestoga - UDP/serial gateway;
  • Drover - Septentrio SBF support (work in progress);
  • Fothergill - LoRa/serial gateway;
  • Hazer - NMEA support;
  • Dally - WitMotion WT901 motion sensor support;
  • Metronome - precision time and frequency support;
  • Nicker - PointPerfect SPARTN corrections via Inmarsat;
  • Stagecoach - UDP/OpenSSL gateway;
  • Tesoro - OSM support;
  • Tumbleweed - RTCM support for RTK DGNSS;
  • Wheatstone - LTE-M support;
  • Yodel - U-blox UBX support including for IMU.

The Hazer repo includes parsers for four different input formats - NMEA, UBX (U-blox), RTCM, and CPO (Garmin) - 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 and CPO records as "packets", and RTCM records as "messages".

NMEA sentences are printable ASCII messages that conform to the National Marine Electronics Association (NMEA) 0183 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 packets may include not only data generated by the GNSS receiver, but also data generated by an Inertial Measurement Unit (IMU), part of a Inertial Navigation System (INS). 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.

CPO packets are in a proprietary binary data format generated by some devices made by Garmin. CPO (no clue what this stands for, but Garmin uses it in their scant documentation) is supported by a parallel stack called Calico. The documentation for the Garmin CPO binary output format is slim enough that a lot of reverse engineering and, frankly, guesswork went into its implementation. It's highly experimental.

More broadly: Hazer uses GNSS to do geolocation for applications like moving map displays; Yodel uses GNSS with high precision geolocation and integrated inertial measurement features of specific u-blox devices; and Tumbleweed uses 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 including serial USB, from files or anything that looks like a file e.g. a FIFO (a.k.a. a named pipe), 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 an rtktool application that is a point-to-multipoint RTCM 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 Tumbleweed (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.

Pro Tips

When you link against the library or use any of the binaries or scripts that are artifacts of the build process, the linker and the shell have to know where to find those artifacts. Furthermore, some of the binaries or scripts may depend upon values in your environment to work correctly.

You need to set the LANG (Language) environmental variable to set your locale to use the U.S. version of UTF-8. This allows applications to correctly display Unicode symbols like the degree symbol and the plus/minus symbol. If you use the bash shell (as I do), you can put the following line in your .profile in your home directory so that it is set everytime you log in (as I do). Or you can just set it when you need to.

export LANG=en_US.UTF-8

On some platforms, you may need to install or generate the Unicode locales for this to work. On Microsoft's Windows Subsystem for Linux (WSL) version 2, I did something like this.

sudo apt-get install locales-all
sudo update-locale LANG=${LANG}
sudo locale-gen $LANG

If you don't install libraries, binaries, and scripts in one of the usual system locations like /usr/local/lib and /usr/local/bin (I typically don't), you can temporarily modify your environment so that the linker and your shell can find them. This bash sourcing script is an artifact of the build process and sets the PATH and LD_LIBRARY_PATH environmental variables and exports them.

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

Some scripts in the bin and fun directories save stdout, stderr, CSV, or other data in files. By default, most scripts save such output files in the out/${TARGET}/tmp subdirectory of the code base, where TARGET is set to the name of the Makefile configuration file (typically "host"). This location can be overridden if the environmental variable below is set.

export COM_DIAG_HAZER_SAVDIR="${HOME}/save"

The libraries, binaries, and scripts make use of the Diminuto logging system. The importance of log messages is classified into eight severity levels, ranging from DEBUG (log mask 0x01, which may emit a firehose of information) to EMERGENCY (log mask 0x80, in which case your system is probably in deep trouble). You can control which level of messages are emitted, either to standard error (if your application has a controlling terminal), or to the system log (if your application, like a daemon, does not). One way to control this is to set the log mask in your environment.

export COM_DIAG_DIMINUTO_LOG_MASK=0xfe

The log mask value is an eight-bit number in decimal, hexadecimal, or even octal. In addition, the string ~0 can be used to enable all log levels, equivalent to 255, 0xff, or 0377. (Generally I find 0xfe to be a good starting point.)

You can alter the log mask in real-time for gpstool by creating a file named com_diag_hazer_log_mask.msk in the current working directory in which gpstool is running, and containing just the log mask numeric string in the same format as the value of the environmental variable above. gpstool imports the log mask from this file, if it exists (it's not an error if it doesn't), at start up, and whenever the process receives a SIGHUP signal.

If the environmental variable is not defined, and the file does not exist, the default log mask value for the Diminuto Log feature is used.

Manual Pages and Reference Manual

These PDFs of the manual pages and associated reference manual were built from Hazer's embedded Doxygen comments on 2024-03-25 using version 61.0.1 . They will not reflect changes made since then.

The unit tests (Hazer/tst), functional tests (Hazer/fun), and command line uilities (Hazer/bin) contain useful and non-trivial examples of how to use the Hazer library.

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. (Details of building Diminuto are shown below.)

Tool Chain

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

Utilities

Some of the Hazer scripts use command line utilities that not all Linux/GNU distros install by default.

sudo apt-get install bc
sudo apt-get install inotify-tools
sudo apt-get install socat
sudo apt-get install expect
sudo apt-get install sharutils

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

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 application 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 were written by Mr. Karney. The original source files can be found at

https://geographiclib.sourceforge.io

and are 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

Reverse Dependencies

The Tesoro project integrates Hazer with an OpenStreetMaps (OSM) tile server to create a moving map display. Tesoro steers the display by taking location updates, in the form of datagrams containing JSON over UDP, either in real-time or from playback, from Hazer using scripts like csvfollow and csvplayback.

Branching

Hazer is big and complex enough that I sometimes move 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.

Versioning

I tag major 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

Related

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

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

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

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

Sentences

Hazer processes the following standard NMEA sentences.

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

Hazer processes the following proprietary u-blox NMEA-style sentences.

  • PUBX,00 - emitted by u-blox devices for POSITION. (u-blox 8 31.3 pp. 163-167)
  • PUBX,03 - emitted by u-blox devices for SVSTATUS. (u-blox 8 31.3 pp. 163-167)
  • PUBX,04 - emitted by u-blox devices for TIME. (u-blox 8 31.3 pp. 163-167)

Hazer recognizes and logs the following proprietary NMEA-style sentences.

  • PAIR - emitted by some Quectel devices.
  • PGRM - emitted by some Garmin devices.
  • PMTK - emitted by some GTop/MTK devices.
  • PSRF - emitted by some SiRF devices.
  • PUBX - emitted by some u-blox devices.

Yodel processes or logs the following binary UBX packets.

  • 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)

Calico processes the following binary CPO packets.

  • PVT - Position, Velocity, and Time data. (Garmin GPS 18x Tech Specs, p. 27)
  • SDR - Satellite Data Record data. (Garmin GPS 18x Tech Specs, p. 26)

Tumbleweed recognizes RTCM binary 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 (as in The Big Dipper) a.k.a. COMPASS - China
  • GI - Navigation with Indian Constellation (NavIC) a.k.a. Indian Regional Navigation Satellite System (IRNSS) - India
  • 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
  • GQ - Quasi-Zenith Satellite System (QZSS) - Japan

Support for these talkers, which may not be in the NMEA standard, but which have been defined in the documentation of some devices I have used, has been unit tested, but the talkers have never been observed in the wild.

  • BD - BeiDou (as in The Big Dipper) a.k.a. COMPASS - 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 GNSS receivers.

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

Support for these satellite identifiers has been unit tested but has these identifiers have never been observed in the wild. (Hazer has successfully used GNSS devices that are receiving signals from the BeiDou and Galileo constellations, but those devices used the GSA System ID field so that Hazer does not have to infer the constellation from the satellite identifier.)

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

Devices

I have successfully tested Hazer with the following GPS chipsets.

  • MediaTek MTK3339
  • Quectel L80-R
  • Quectel L89
  • Septentrio Mosaic-X5
  • SiRF Star II
  • SiRF Star III
  • SiRF Star IV
  • U-Blox 6
  • U-Blox 7
  • U-Blox 8
  • U-Blox 9
  • U-Blox 10

I have successfully tested Hazer with the following serial-to-USB chipsets.

  • Cygnal Integrated Products
  • FTDI
  • Microchip MCP2221 USB-I2C/UART
  • Prolific
  • Silicon Labs CP2104
  • U-Blox (when it is integrated into the GPS chip itself)

I have successfully tested Hazer with the following GNSS devices.

  • Adafruit Ultimate GPS (GTop PA6H/MediaTek MTK3339+CP2104, 9600 8N1, v10c4pea60, ttyUSB, 1Hz) 1
  • Ardusimple SimpleGNSS (U-Blox 10/UBX-NEO-F10T, 38400 8n1, v0403p6015, ttyUSB, 1Hz) 2 3 4 5 6
  • Ardusimple SimpleRTK2B (U-Blox 9/UBX-ZED-F9P, 230400 8N1, v1516p01a9, ttyACM, 1Hz) 3 7
  • Bad Elf GPS Pro + (STMicroelectronics/MediaTek MTK, 9600 8n1, v0483p5740, ttyACM, 1Hz) 8 9
  • Digi XBIB-C-GPS (U-blox 8/UBX-CAM-M8Q, 9600 8N1, N/A, ttyS, 1Hz) 8
  • Eleduino Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) 10
  • Ettus Research (National Instruments) USRP B210, GNURadio 3.7.11, GNSS-SDR 0.0.10 11
  • Garmin GLO (?MTK?, Bluetooth, N/A, rfcomm, 10Hz) 12 13
  • Garmin GLO 2 (?MTK?, Bluetooth, N/A, rfcomm, 10Hz) 12 13 14
  • Garmin GPS 18x PC (?, 4800/9600 8n1, N/A, ttyS, 1Hz) 15
  • Garmin GPS 18x USB (?, 4800/9600 8n1, v04d8p00dd, ttyACM, 1Hz) 15
  • Generic Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) 10
  • GlobalSat BU-353N (Quectel L89?+Prolific, 4800 8N1, v067Bp23a3, ttyUSB, 1Hz) 16
  • GlobalSat BU-353N(10Hz) (Quectel L89?+Prolific, 115200 8N1, v067Bp23a3, ttyUSB, 10Hz) 16
  • GlobalSat BU-353N5 (Quectel L89?+Prolific, 4800 8N1, v067Bp23a3, ttyUSB, 1Hz) 17
  • GlobalSat BU-353S4 (SiRF Star IV+Prolific, 4800 8N1, v067Bp2303, ttyUSB, 1Hz)
  • GlobalSat BU-353S4-5Hz (SiRF Star IV+Prolific, 115200 8N1, v067Bp2303, ttyUSB, 5Hz) 18
  • GlobalSat BU-353W10 (U-Blox 8/UBX-M8030, 9600 8N1, v1546p01a8, ttyACM, 1Hz) 19 10 8 3 20
  • GlobalSat ND-105C (SiRF Star III+Prolific, 4800 8N1, v067Bp2303, ttyUSB, 1Hz)
  • Jackson Labs Technologies CSAC GPSDO (U-Blox 6/UBX-LEA-6T, 115200 8n1, N/A, ttyS, 1Hz)
  • Lynq Technologies Smart Compass (U-Blox 8/UBX-CAM-M8Q, 115200 8n1, v1fc9p0194, ttyACM, 1Hz)
  • MakerFocus USB-Port-GPS (Quectel L80-R+Cygnal, 9600 8N1, v10C4pEA60, ttyUSB, 1Hz) 10 2
  • NaviSys GR-701W (U-Blox 7+Prolific, 9600 8N1, v067Bp2303, ttyUSB, 1Hz) 21 22 3
  • Pharos GPS-360 (SiRF Star II+Prolific, 4800 8N1, v067BpAAA0, ttyUSB, 1Hz) 23
  • Pharos GPS-500 (SiRF Star III+Prolific, 4800 8N1, v067BpAAA0, ttyUSB, 1Hz) 23
  • RaceDayQuads RDQ Micro M8N (U-Blox 8/UBX-NEO-M8N, 9600 8n1, N/A, ttyS, 1Hz) 8 3 24
  • Septentrio Mosaic-X5 (Mosaic, 115200 8n1, v152ap85c0, ttyACM, 1Hz) 25
  • Sourcingbay GM1-86 (U-Blox 7, 9600 8n1, v1546p01A7, ttyACM, 1Hz) 10
  • SparkFun GPS-RTK2 (U-Blox 9/UBX-ZED-F9P, 230400 8N1, v1516p01a9, ttyACM, 1Hz) 3 7
  • SparkFun NEO-M8N (U-Blox 8/UBX-NEO-M8N, 115200 8N1, v1546p01a9, ttyACM, 1Hz) 2 3 7
  • SparkFun NEO-M8U (U-Blox 8/UBX-NEO-M8U, 115200 8N1, v1546p01a8, ttyACM, 1Hz) 8 3 26
  • SparkFun SAM-M8Q (U-Blox 8/UBX-SAM-M8Q, 9600 8n1, N/A, ttyS, 1Hz) 8 2 3 24
  • SparkFun SARA-R5 (U-Blox 8/M8030/UBX-R5, autobaud, v1a86p7523, ttyUSB, 1Hz) 27 28 29
  • SparkFun ZED-F9P NEO-D9S (U-Blox 9/UBX-ZED-F9P+UBX-NEO-D9S, 38400 8N1+9600 8N1, v1546p01a9+V1546p01a9? ttyACM+ttyACM, 1Hz)
  • SparkFun ZED-F9R (U-Blox 9/UBX-ZED-F9R, 230400 8N1, v1516p01a9, ttyACM, 1Hz) 3 7 26
  • SparkFun ZED-F9T (U-Blox 9/UBX-ZED-F9T, 38400 8N1, v1516p01a9, ttyACM, 1Hz) 3 7 30
  • Stratux Vk-162 Gmouse (U-Blox 7, 9600 8N1, v1546p01A7, ttyACM, 1Hz) 10
  • TOPGNSS GN-803G (U-Blox 8/UBX-M8030-KT, 9600 8N1, v1546p01a8, ttyACM, 1Hz) 10 8 3
  • Uputronics Raspberry Pi GPS Expansion Board v4.1 (U-Blox 8/M8, 9600 8N1, N/A, ttyS, 1Hz) 8

Note that when sending commands to and parsing output from GNSS devices, Postel's Law - "Be conservative in what you send, be liberal in what you accept." - applies. Also, there is plenty of ambiguity and underspecification to be found in standards like NMEA 0183.

Notes

GlobalSat is also known as GlobalSat Technology, USGlobalSat, US GlobalSat Technology, or GlobalSat Worldcom, but is apparently not the same as the Globalsat mobile satellite service provider.

GlobalTop Technology is also known as GTop, and is part of Sierra Wireless.

SiRF is part of Qualcom.

Devices (Other)

In the context of this repository, I also deal with devices that are not GNSS receivers but are related to navigation or timing, like intertial measurement units (IMUs). Output from these devices isn't processed by gpstool but by other means. The utility serialtool from the Diminuto repository is particularly useful in this respect.

  • WitMotion WT901BLECL5.0 IMU (WT901, 115200 8n1, v1a86p7523, ttyUSB, 1Hz)

Note that some devices are both GNSS receivers and IMUs, and these may be processed by gpstool.

Platforms

I routinely test Hazer and gpstool (and therefore, many of the features of the Diminuto library on which they depend) on the following development platforms, in no particular order.

Intel NUC7i7BNH
Intel Core i7-7567U x86_64 x2 x2
Ubuntu 22.04.2 LTS "Jammy Jellyfish"
Linux 5.19.0
GNU 11.3.0

Raspberry Pi 4 Model B BCM2835
ARM Cortex-A72 x4
Ubuntu 22.04.2 LTS "Jammy Jellyfish"
Linux 5.15.0
GNU 11.3.0

Raspberry Pi 4 Model B BCM2835
ARM Cortex-A72 x4
Raspbian 11 "bullseye"
Linux 6.1.19
GNU 10.2.1

I run very recent versions of Hazer and gpstool on the following test platforms, which run 24x7.

Raspberry Pi 3 Model B+ BCM2835
ARM Cortex-A72 x4
Raspbian 10 "buster"
Linux 5.10.103
GNU 8.3.0
(Differential GNSS Base Station)

Raspberry Pi 3 Model B+ BCM2835
ARM Cortex-A72 x4
Raspbian 9 "stretch"
Linux 4.19.66
GNU 6.3.0
(Differential GNSS Stationary Rover)

Raspberry Pi 3 Model B+ BCM2835
ARM Cortex-A72 x4
Raspbian 10 "buster"
Linux 4.19.50
GNU 8.3.0
(Differential GNSS RTCM Server)

Raspberry Pi 3 Model B+ BCM2835
ARM Cortex-A72 x4
Raspbian 9 "stretch"
Linux 4.14.30
GNU 6.3.0
(NTP Server Monitoring System)

In the past, I have tested various versions of Hazer and gpstool on the following platforms, which give you some idea of the wide variety of computers one may use.

Dell OptiPlex 7040
Intel Core i7-6700T x86_64 x4 x2
Ubuntu 16.04.2 "Xenial"
Linux 4.4.0
GNU 5.4.0

HP Envy x360 Convertible Model 15
AMD Ryzen 7 5825U x86_64 x16
Ubuntu 22.04.2 "Jammy Jellyfish"
Linux 5.15.90 (Microsoft 11 Windows Subsystem for Linux 2)
GNU 11.3.0

HP Mini 110-1100 Netbook
Intel Atom N270 i686 x2
Mint 19.3 "Tricia"
Linux 5.0.0
GNU 7.5.0

GPD Micro PC
Intel Celeron N4100 x86_64 x2 x2
Ubuntu MATE 19.10 "Eoan"
Linux 5.3.0
GNU 9.2.1

OrangePi 5
aarch64 x8
Ubuntu 22.04.2 LTS (Jammy Jellyfish)
Linux 5.10.110
GNU 11.3.0

Pi-Top 3 (Raspberry Pi 3 Model B+)
Broadcom BCM2837B0 Cortex-A53 ARMv7 x4
pi-topOS "Polaris" (Raspbian 9.9 "Stretch")
Linux 4.19.66
GNU 6.3.0

Pi-Top 4 (Raspberry Pi 4 Model B)
Broadcom BCM2711 Cortex-A72 ARMv8 x4
pi-topOS "Sirius" (Raspbian 10 "Buster")
Linux 5.4.79
GNU 8.3.0

Raspberry Pi Zero 2 W Rev 1.0
Broadcom BCM2835 ARMv7 x4
Raspbian 11 "Bullseye"
Linux 6.1.21
GNU 10.2.1

StarFive VisionFive (RISC-V SBC)
sifive u74-mc riscv64 x2
Fedora 33 "Rawhide"
Linux 5.15.10
GNU 10.3.1

StarFive VisionFive 2 (RISC-V SBC)
sifive u74-mc riscv64 x4
Debian 11.3.0
Linux 5.15.10
GNU 11.3.0

VMware Workstation 15 Pro under Windows 10
Intel Core i7-3520M x86_64 x2
Ubuntu 19.10 "Eoan" (Microsoft Common Base Linux "Mariner")
Linux 5.3.0
GNU 9.2.1

Your mileage may vary. Hazer and gpstool are not resource intensive. I routinely run gpstool on an old 32-bit Intel i686 netbook, and run several simultaneous instances of gpstool on a single Rasbperry Pi 4B.

Diagnostics

Errors

Error messages may be logged by gpstool with the following error numbers (errno) for malformed input data. (Errors reported by system calls and library functions may also cause error messages not related to the input data to be logged.)

  • EINVAL - invalid character in a field e.g. alpha in a numeric field.
  • EIO - error in the end matter e.g. checksum or cyclic redundancy check (CRC).
  • ENODATA - below minimum length or number of fields.
  • ENOMSG - error in the start matter e.g. no discernable identifiers.
  • ERANGE - time, latitude, longitude, etc. value out of range.

Each Hazer library function that parses NMEA sentences (hazer), UBX packets (yodel), or RTCM messages (tumbleweed) returns a value >= 0 if the data were valid and any PNT structures passed to the function were updated. If the function returns a value < 0, the data were rejected, and errno is set to a value. If errno > 0, then the data were invalid and the error number, as indicated in the list above, suggests the reason why. If errno == 0, then the data were valid but indicated that the GNSS device did not have a valid solution, so the PNT structures were not updated.

Synchronization

The following log messages regarding input stream synchronization may be reported by gpstool.

  • Sync Start - one of the state machines has signaled that a valid NMEA, UBX, or RTCM frame has been read with expected beginning and ending matter, including a correct checksum or CRC. The input stream is synchronized where it had not been before.
  • Sync Stop - all three state machines have entered their stop states. This indicates that all have given up trying to synchronize to the input stream. All three state machines will be restarted.
  • Sync Lost - the input stream had been synchronized, but at the beginning of the next frame, none of the expected beginning matter for a NMEA, UBX, or RTCM frame was recognized. All three state machines will be restarted.

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

Chip Overclock, "A Moving Map Display Using OpenStreetMap", 2021-02, https://coverclock.blogspot.com/2021/02/a-moving-map-display-using.html

Chip Overclock, "The OpenStreetMap Moving Map In Real-Time", 2021-02, https://coverclock.blogspot.com/2021/02/the-openstreetmap-moving-map-in-real.html

Chip Overclock, "The OpenStreetMap Moving Map On Mobile Devices", 2021-03, https://coverclock.blogspot.com/2021/03/the-openstreetmap-moving-map-on-mobile.html

Chip Overclock, "A Static Route Map Display Using OpenStreetMap", 2021-03, https://coverclock.blogspot.com/2021/03/a-static-route-map-display-using.html

Chip Overclock, "Advanced Static Route Maps With OpenStreetMap", 2021-03, https://coverclock.blogspot.com/2021/03/advanced-static-route-maps-with.html

Chip Overclock, "Where the RF Meets the Road", 2021-03, https://coverclock.blogspot.com/2021/03/where-rf-meets-road.html

Chip Overclock, "Solar Power", 2022-03, https://coverclock.blogspot.com/2022/03/solar-power.html

Chip Overclock, "It's About Time", 2022-08, https://coverclock.blogspot.com/2022/08/its-about-time.html

Chip Overclock, "Bob Green Road", 2023-02-12, https://coverclock.blogspot.com/2023/02/it-will-probably-come-as-no-surprise.html

Chip Overclock, "Playing with a RISV-V SBC Running Linux", https://coverclock.blogspot.com/2022/08/playing-with-risc-v-sbc-running-linux.html

Chip Overclock, "Hazer with the U-blox NEO-F10T GNSS Receiver on the Ardusimple SimpleGNSS Board", 2023-07-03, https://coverclock.blogspot.com/2023/07/hazer-with-u-blox-neo-f10t-gnss.html

Media

John Sloan, "BU-353W10 Tear Down", album, https://flic.kr/s/aHsmWxTpfs

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

John Sloan, "Decimal Degrees", album, https://flic.kr/s/aHsmV3GnBe

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, "Tesoro", album, https://flic.kr/s/aHsmTB3tme

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, "Screen Recording 2021 02 18 at 8 55 20 AM", video, https://youtu.be/GjR7fPQRCZc

John Sloan, "Screen Recording 2021 02 24 at 10 06 20 AM", video, https://youtu.be/-6p3w1SxW1A

John Sloan, "Wheatstone", album, https://flic.kr/s/aHsmVu1wkK

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

John Sloan, "UBX NEO F10T Video", video, https://youtu.be/gCKnlchwkzU

John Sloan, "UBX NEO F10T Screen", video, https://youtube.com/shorts/nC-NkjRSWRk?feature=share

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

Amy Fox, "Precision Matters: The Critical Importance of Decimal Places", blis.com, 2017-07-09

Garmin, "GPS 18x TECHNICAL SPECIFICATIONS", 190-00879-08 Rev. D, Garmin International, Inc., 2011-10

Garmin, "Garmin Device Interface Specification", 001-00063-00 Rev. G, Garmin International, Inc., 2020-04-14

Garmin, "Garmin Proprietary NMEA 0183 Sentences TECHNICAL SPECIFICATIONS", 190-00684-00 Rev. C, Garmin International, Inc., 2008-12

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

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

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

Grosse-Kunstleve RW, Terwilliger TC, Sauter NK, Adams PD, "Automatic Fortran to C++ conversion with FABLE", Source Code for Biology and Medicine 2012, 7:5

GTop, "PMTK command packet", Revision A11, GlobalTop, 2012

Tony Haddrell, Marino Phocas, Nico Ricquier, "Mobile Phone GPS Antennas - Can They Be Better?", GPS World, 2010-02

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

ISO, "Standard representation of geographic point location by coordinates", Second edition, ISO 6709:2008(E), 2008-07-15

ISO, "Data elements and interchange formats - Information interchange - Representation of dates and times", First edition, ISO8601:1988(E), 1988-06-15

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

G. Klyne, C. Newman, "Date and Time on the Internet: Timestamps", RFC3339, IETF, July 2002

H. Lyons, "Atomic Clocks", Scientific American, 1957-02

Richard Mason et al., Analyzing a More Resilient National Positioning, Navigation, and Timing Capability, Homeland Security Operational Analysis Center, RAND Corporation, 2021

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-06

NMEA 0183, "Standard for Interfacing Marine Electronic Devices", Version 4.11, National Marine Electronics Association, 2018-10

NMEA 0183, "Standard for Interfacing Machine Electronic Devices", Version 4.30, National Marine Electronics Association, 2023-12

NMEA 0183, "Technical Bulletin - NMEA 0183 Errata Version 4.11"", ERRATA #0183 20190507 GSV Sentence

NMEA 0183, "Technical Bulletin - NMEA 0183 Errata Version 4.11"", ERRATA #0183 20190515 GSV Sentence

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

Quectel, "L89 R2.0 GNSS Protocol Specification", L89_R2.0_GNSS_Protocol_Specification_V1.0, (Preliminary), 2020-04-29

Eric S. Raymond, "NMEA Revealed", 2023-05-03, https://gpsd.gitlab.io/gpsd/NMEA.html

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

Thingstream, "PointPerfect getting started", Thingstream, 2022

Thingstream, "PointPerfect L-band Configuration", Thingstream, 2022

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, "ZED-F9P Integration Manual", UBX-18010802-R03, ublox, 2018-12-20

u-blox 9, "ZED-F9T series Product Summary", UBX-18011626-R06, ublox, 2022

u-blox 9, "ZED-F9T Data Sheet", UBX-18053713-R07, ublox, 2022-01-04

u-blox 9, "u-blox ZED-F9T Interface Description", UBX-18053584-R02, ublox, 2019-06-13

u-blox 9, "ZED-F9T Integration Manual", UBX-19005590-R05, ublox, 2020-11-18

u-blox 9, "ZED-F9P-02B High precision GNSS module Professional grade Data sheet", UBX-21023276-R03, ublox, 2023-03-24

u-blox 9, "ZED-F9P u-blox F9 high precision GNSS module Integration Manual", UBX-18010802-R12, ublox, 2022-05-03

u-blox 9, "u-blox F9 HPG 1.32 u-blox F9 high precision GNSS receiver Interface Description", UBX-22008968-R01, ublox, 2022-05-02

u-blox 9, "Product summary ZED-F9P series u-blox F9 high precision GNSS modules", UBX-17005151-R14, ublox, 2023

u-blox 9, "NEO-D9S Correction data receiver Integration manual", UBX-19026111-R08, ublox, 2023-01-05

u-blox 9, "u-blox D9 PMP 1.04 u-blox D9 correction data receiver Interface description", UBX-21040023-R01, ublox, 2021-10-21

u-blox 9, "NEO-D9S and ZED-F9 configuration SPARTN L-band correction data reception Application Note", UBX-22008160-R02, ublox, 2022-07-27

u-blox 9, "Product summary NEO-D9S u-blox D9 correction data receiver", UBX-17010946-R05, ublox, 2021

u-blox 10, "NEO-F10T High precision timing GNSS module Professional Grade Data Sheet", UBX-22022576-R02, ublox, 2023-03-30

u-blox 10, "NEO-F10T u-blox F10 GNSS timing module Integration Manual", UBX-22018271-R01, ublox, 2023-04-05

u-blox 10, "Product Summary NEO-F10T module u-blox F10 high accuracy timing module", UBX-22025534-R02, ublox, 2023

u-blox 10, "u-blox F10 TIM 3.01 - u-blox F10 GNSS timing receiver - Interface Description", UBX-23003447-R01, ublox, 2023-03-21

u-blox, "u-center GNSS evaluation software for Windows User guide", UBX-13005250-R30, ublox, 2022-05-24

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

https://www.calculator.net/distance-calculator.html

Resources

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

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

https://gpsd.gitlab.io/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

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

https://jsonlines.org

https://www.faa.gov/about/office_org/headquarters_offices/avs/offices/aam/cami/library/online_libraries/aerospace_medicine/tutorial/media/III.4.1.4_Describing_Orbits.pdf

https://www.gpsworldbuyersguide.com

https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

https://youtu.be/NvpbW7JRu0Q "Philomena Cunk's Moments of Wonder Ep. 1: Time", Charlie Brooker's Weekly Wipe, BBC, video

https://tf.nist.gov/general/pdf/1568.pdf Harold Lyons, "Atomic Clocks", Scientific American, 1957-02 (archived on a NIST website)

http://freenmea.net/docs

https://www.sparkfun.com/datasheets/GPS/NMEA%20Reference%20Manual-Rev2.1-Dec07.pdf

https://www.celestis.com/resources/faq/what-are-the-azimuth-and-elevation-of-a-satellite/

https://ciechanow.ski/gps/

https://learn.sparkfun.com/tutorials/how-to-upgrade-firmware-of-a-u-blox-gnss-receiver/all

https://www.wired.com/story/satellite-time-distribution/ W. Ralston, "When the UK's Timing Systems Fail, This Service Will Save Them", WIRED, 2022-08-26

https://stjarnhimlen.se/comp/time.html Paul Schlyter, "Time Scales"

https://www.gpsrchive.com/Shared/Satellites/GPS%20vs%20GLONASS%20vs%20Galileo.html

https://www.ietf.org/timezones/data/leap-seconds.list

https://geodesy.noaa.gov/CORS/resources/gpscals.shtml

http://leapsecond.com/java/gpsclock.htm

https://youtu.be/FUHkTs-Ipfg?si=BWIJwMdPdK-iGkKA "The SAT Question Everyone Got Wrong"

Soundtrack

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

Build

Diminuto

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

Hazer

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

Documentation

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

(Diminuto has a similer documentation build process.)

Administration

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.

Most Linux distros will require the program accessing the GPS/GNSS device to either have root privileges or be in the "dialout" group. The latter is of course preferred.

sudo usermod -a -G dialout ${USER}

${USER} will need to log out and back in for this to take effect.

It is also possible to edit the udev rules to make the device readable and writeable by the ${USER} (okay) or anyone (nope).

Depending on what features of Hazer you choose to use, you will need to define some services in the /etc/services file. The port numbers are your choices to make. Note that the TCP and UDP port numbers for the same service (e.g. tesoro) can be (and typically are) the same number. Port number values may range from 0 to 65353 and must be unique. Linux also assigns "ephemeral" or temporary port numbers which will not appear in the /etc/services file.

tumbleweed 22222/udp  # Tumbleweed RTK source/sink
tesoro     33333/tcp  # Tesoro JSON source
tesoro     33333/udp  # Tesoro JSON sink
hazer      44444/udp  # Hazer NMEA source/sink

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
export LANG=en_US.UTF-8 # You can put this in your .profile.
. out/host/bin/setup
make unit-test

Functional Tests

There are a bunch of scripts that use actual hardware. Some of them require home-built test fixtures and General Purpose Input/Output (GPIO) pins. But there is a small collection of functional tests that do not require any GNSS device at all. Mostly I use these to make sure that gpstool does not throw an assert and core dump.

cd ~/src/com-diag-hazer/Hazer
export LANG=en_US.UTF-8 # You can put this in your .profile.
. out/host/bin/setup
make functional-test

Directories

  • app - application source files (utilities with more than one source file).
  • bin - utility source files.
  • cfg - configuration makefiles.
  • dat - data captured from Hazer, Yodel, and Tumbleweed tests (some in LFS).
  • fun - functional test source files.
  • inc - public header files.
  • out - build and test artifacts (not under source control).
  • 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)/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.

Bash Sourced Files

  • setup - defines and exports shell variables like PATH and LD_LIBRARY_PATH into the environment.
  • diminuto - defines shell variables like Arch, Release, Revision, and Vintage into the current shell.

Applications

  • dgmtool - multipoint-to-multipoint CSV UDP-to-TCP forwarder.
  • geodesic - computes the geodesic distance in meters between two WGS84 coordinates.
  • gpstool - Hazer's multi-purpose GNSS tool.
  • rtktool - Tumbleweed's point-to-multipoint RTK UDP-to-UDP 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 unterminated NMEA, UBX, or RTCM packets and adds end matter.
  • compasstool - converts a bearing in decimal degrees to an 8, 16, or 32 compass point.
  • csv2dgm - forwards subset of gpstool CSV fields from stdin to a UDP endpoint, serial port, etc.
  • 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.
  • replay - replay a gpstool catenate (-C) file in non-real-time.

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.
  • 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.
  • csvparts - splits gpstool CSV file into smaller files in subdirectories.

Google Maps Moving Map (DEPRECATED)

  • 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.

Google Earth Keyhole Markup Language (KML)

  • 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.
  • out2kmlpoints - converts gpstool OUT files to KML 2.3 XML to visualize points.

Intertial Measurement Unit (Yodel)

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

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.
  • rtk2dgm - simulates a rover by sending RTK keep alives to an RTK router.
  • 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.

Safe Position Augmentation for Real-Time Navigation (Nicker)

  • spartan - configures the UBX-NEO-D9S and the UBX-ZED-F9P for SPARTN mode.
  • spartan2 - runs gpstool headless against the configured UBX-ZED-F9P.
  • windows - run the spartan (by default) field test with multiple windows.

OpenStreetMap Moving Map (Tesoro)

  • csvdataset - convert CSV file into a JSON object array for a static map.
  • csvfollow - follow a CSV file in real-time, forward as JSON to a UDP endpoint.
  • csvmeter - meters lines from a gpstool CSV file based on interarrival times.
  • csvplayback - playback a stored CSV file, forward JSON to a UDP endpoint.
  • jsonmeter - meters lines from a JSON file based on interarrival times.
  • tracker - capture CSV with gpstool, forward JSON to an UDP endpoint, with peruse and hups.

UDP over LTE-M (Wheatstone)

  • sarar5 - tracker using a USB-attached U-Blox SARA-R5 LTE-M radio (WIP).
  • xbee3 - tracker using a serial-attached Digi XBEE 3 LTE-M radio.
  • xbee3shutdown - shuts down Digi XBEE 3 LTE-M radio (recommended before powering off).

Precision Time and Frequency Reference (Metronome)

  • reference - configures the ZED-F9T to emit 1PPS (TP1) and 10MHz (TP2).

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.

WT901 IMU (Dally)

  • wt901setup - emit WT901 query strings to standard output.
  • wt901tool - process WT901 device output via standard input.

Functional Tests

  • bu353s4 - exercises the GlobalSat BU-353S4 receiver.
  • bu353w10 - exercises the GlobalSat BU-353W10 receiver.
  • bu353w10C - uses cat to send stored data files to gpstool.
  • bu353w10D - uses socat to send data from GlobalSat BU-353W10 to gpstool over a UDP socket.
  • 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.
  • bu353w10P - exercises the GlobalSat BU-353W10 receiver configured for U-blox PUBX messages.
  • bu353w10S - uses socat to send data from GlobalSat BU-353W10 to gpstool over named pipe.
  • bu353w10T - exercises the GlobalSat BU-353W10 receiver with no additional configuration.
  • bu353w10V - exercises the GlobalSat BU-353W10 receiver using verbose mode.
  • 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.
  • fothergill - runs gpstool and forwards CSV traces across LoRa radio channel.
  • gn803g - exercises the TOPGNSS GN-803G receiver.
  • gps18xpc2cpo - places the Garmin GPS-18x PC in CPO mode (9600 baud).
  • gps18xpc2nmea - places the Garmin GPS-18x PC in NMEA mode (4800 baud).
  • gps18xpccpo - operates the Garmin GPS-18x PC in CPO mode (9600 baud).
  • gps18xpcnmea - operates the Garmin GPS-18x PC in NMEA mode (4800 baud).
  • gps18xusb2cpo - places the Garmin GPS-18x USB in CPO mode (9600 baud).
  • gps18xusb2nmea - places the Garmin GPS-18x USB in NMEA mode (4800 baud).
  • gps18xusbcpo - operates the Garmin GPS-18x USB in CPO mode (9600 baud).
  • gps18xusbnmea - operates the Garmin GPS-18x USB in NMEA mode (4800 baud).
  • gpsproplusblue - exercises the Bad Elf GPS Pro+ receiver via Bluetooth.
  • gpsproplususb - exercises the Bad Elf GPS Pro+ receiver via USB.
  • gr701w - exercises the NaviSys GR701W receiver.
  • gr701w1pps - 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.
  • neof10t - exercises the Ardusimple SimpleGNSS board.
  • neof10tR - same as neof10t but reports to standard output.
  • neof10tpps - same as neof10t but supporting 1PPS.
  • neof10tH - same as neof10t but headless.
  • neof10tHpps - same as neof10tH but supporting 1PPS.
  • neof10tH00 - same as neof10tH but with Ardusimple version 00 configuration.
  • neof10tH01 - same as neof10tH but with Adrusimple version 01 configuration.
  • neof10tHinit - same as neof10tH but with my configuration.
  • neof10tHinitpps - same as neof10tHinit but supporting 1PPS.
  • neom8n - exercises the RDQ Micro M8N board.
  • neom8n1pps - exercises the SparkFun NEO-M8N board and its 1PPS output.
  • neom8u - exercises the SparkFun GPS Dead Reckoning board.
  • pmtk - exercises the Adafruit Ultimate GPS board.
  • 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.
  • streamdump - socat to phex.
  • streamprint - socat to gpstool with a lot of debug output.
  • talkers - processes a file of synthetic input.
  • tesorochoosefile - emit CSV output to an observation file for Tesoro.
  • tesorodraganddrop - emit CSV output to an observation file for Tesoro.
  • tesoroselectchannel - emit CSV output to a UDP endpoint for Tesoro.
  • 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.
  • ubxcoldstart - sends UBX message to cold start U-blox device.
  • ubxmonitor - sends UBX messages to query U-blox device for internal states.
  • ubxrestoredefaults - sends UBX message to restore defaults to U-blox battery backed RAM (BBRAM).
  • uputronics - exercises the Uputronics GPS board for the Rasperry Pi.
  • zedf9p-neod9s - exercise UBX-ZED-F9P + UBX-NEO-D9S pair (headless).
  • zedf9p-neod9s-config - configure UBX-ZED-F9P + UBX-NEO-D9S pair.
  • zedf9r - exercise UBX-ZED-F9R device.
  • zedf9t - exercise UBX-ZED-F9T device.
  • zedf9t-configure - configure UBX-ZED-F9T RAM for 1PPS (TP1) and 10MHz (TP2).
  • zedf9t-factory - factory reset UBX-ZED-F9T device.
  • zedf9t-flash - configure the UBX-ZED-F9T flash for 1PPS (TP1) and 10MHz (TP2).
  • zedf9t-reset - software reset UBX-ZED-F9T 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.

I have enough code, both scripts and C code, that depends on the CSV format, not to mention archived CSV files, that it is a big deal to change. I've started encoding special characters at the end of the NAM hostname string instead of adding another field. Expect more of this.

  • 0 - NAM: hostname of computer running gpstool plus "!" if SIGHUP.
  • 1 - NUM: sequence number of observation.
  • 2 - FIX: 0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=combined, 5=time only.
  • 3 - SYS: 0=ensemble, 1=GPS, 2=GLONASS, 3=GALILEO, 4=BEIDOU.
  • 4 - SAT: number of satellites used in position fix.
  • 5 - CLK: UTC from host time in decimal seconds since the POSIX Epoch.
  • 6 - TIM: UTC from GPS time in decimal seconds since the POSIX Epoch.
  • 7 - LAT: WGS84 latitude in decimal degrees.
  • 8 - LON: WGS84 longitude in decimal degrees.
  • 9 - HAC: reported horizontal error in decimal meters.
  • 10 - MSL: altitude above Mean Sea Level in decimal meters.
  • 11 - GEO: altitude above WGS84 ellipse in decimal meters.
  • 12 - VAC: reported vertical error in decimal meters.
  • 13 - SOG: speed over ground in decimal knots.
  • 14 - COG: course over ground in decimal degrees.
  • 15 - ROL: roll in decimal degrees from IMU.
  • 16 - PIT: pitch in decimal degrees from IMU.
  • 17 - YAW: yaw in decimal degrees form IMU.
  • 18 - RAC: roll accuracy in decimal degrees.
  • 19 - PAC: pitch accuracy in decimal degrees.
  • 20 - YAC: yaw accuracy in decimal degrees.
  • 21 - OBS: number of survey observations.
  • 22 - 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.

csv2dgm

Piping the CSV snippet into the script csv2dgm and providing an IP host address and port number like this

csv2dgm -j -U hostname:5555

sends a subset of the CSV line to an endpoint as a UDP datagram in JSON format. Receiving the datagrams using a command like this

socat -u UDP6-RECV:5555 -

produces output that looks like this.

{ "NAM": "neon", "NUM": 116, "TIM": 1598455524, "LAT": 39.7943026, "LON": -105.1533253, "MSL": 1708.000, "LBL": "2020-08-26T15:25:24Z" }
{ "NAM": "neon", "NUM": 117, "TIM": 1598455525, "LAT": 39.7943030, "LON": -105.1533263, "MSL": 1708.100, "LBL": "2020-08-26T15:25:25Z" }
{ "NAM": "neon", "NUM": 118, "TIM": 1598455526, "LAT": 39.7943030, "LON": -105.1533276, "MSL": 1708.200, "LBL": "2020-08-26T15:25:26Z" }
{ "NAM": "neon", "NUM": 119, "TIM": 1598455527, "LAT": 39.7943031, "LON": -105.1533286, "MSL": 1708.300, "LBL": "2020-08-26T15:25:27Z" }
{ "NAM": "neon", "NUM": 120, "TIM": 1598455528, "LAT": 39.7943035, "LON": -105.1533300, "MSL": 1708.500, "LBL": "2020-08-26T15:25:28Z" }

This output can be processed by the fun/channel script in another project, Tesoro, which uses this JSON and some JavaScript code to drive a moving map display using an OpenStreetMap tile server.

The same utility can be used to send the same subset of the CSV output to a serial port. I use this to transmit the CSV data via an Digi XBEE 3 LTE-M radio. If the XBEE is configured correctly, it can send the same datagram to the same endpoint via LTE-M, yielding the same result as above. I use LTE-M SIMs for AT&T's One Rate plan for this.

csv2dgm -j -D /dev/ttyUSB0 -b 9600 -8 -1 -n

csvfollow and csvplayback

The csvfollow script follows (tail -f) a CSV file being output in real-time by gpstool and uses csv2dgm to forward the corresponding JSON datagrams to (for example) a system that is using them to steer a moving map display.

csvfollow 192.168.1.253:tesoro out/host/tmp/vehicle.csv

The csvplayback script uses csvmeter to meter out an existing CSV file at approximately the same rate as it was originally generated by gpstool and uses csv2dgm to forward the corresponding JSON datagrams to (for example) a system that is using them to steer a moving map display.

csvplayback 192.168.1.253:tesoro dat/yodel/20200917/vehicle.csv

See the README in the Tesoro repository for more information.

csvdataset

The csvdataset script is a filter that reads a CSV file and produces a JSON dataset suitable for importation into the Tesoro choosedataset feature to create a static route map. Multiple routes can be rendered on the same map, the the properties of the route line (color, weight, etc.) can be changed either globally or on a per route basis.

csvdataset < dat/yodel/20200917/vehicle.csv > 20200917.json

The entire JSON dataset has to be processed and stored in memory by Tesoro. (This is a requirement of the underlying Leaflet third-party library.) For very large datasets this can be problematic. One way to handle this is to sample the CSV file. An optional value on the command line can cause csvdataset to sample the input file. The first and last data point is always included regardless of the sampling modulo value.

csvdataset 10 < dat/yodel/20200917/vehicle.csv > 20200917.json

See the README in the Tesoro repository for more information.

Help

gpstool

> gpstool -?
usage: gpstool
               [ -d ] [ -v ] [ -z ]
               [ -D DEVICE [ -b BPS ] [ -7 | -8 ] [ -e | -o | -n ] [ -1 | -2 ] [ -l | -m ] [ -h ] [ -s ] | -S FILE ] [ -B BYTES ]
               [ -R | -E | -H HEADLESS | -P ] [ -F SECONDS ] [ -i SECONDS ] [ -t SECONDS ] [ -a ]
               [ -C FILE ]
               [ -O FILE ]
               [ -L FILE ]
               [ -T FILE [ -f SECONDS ] ]
               [ -N FILE ]
               [ -Q FILE [ -q MASK ] ]
               [ -K [ -k MASK ] ]
               [ -A STRING ... ] [ -U STRING ... ] [ -W STRING ... ] [ -Z STRING ... ] [ -w SECONDS ] [ -x ]
               [ -4 | -6 ]
               [ -G :PORT | -G HOST:PORT [ -g MASK ] ]
               [ -Y :PORT | -Y HOST:PORT [ -y SECONDS ] ]
               [ -I CHIP:LINE | -I NAME | -c ]
               [ -p CHIP:LINE | -p NAME ]
               [ -M ] [ -X MASK ] [ -V ]
       -1              Use one stop bit for DEVICE.
       -2              Use two stop bits for DEVICE.
       -4              Prefer IPv4 for HOST.
       -6              Prefer IPv6 for HOST.
       -7              Use seven data bits for DEVICE.
       -8              Use eight data bits for DEVICE.
       -A STRING       Collapse STRING, append Ubx end matter, write to DEVICE, expect ACK/NAK.
       -A ''           Exit when this empty STRING is processed.
       -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 SECONDS      Update report no more than every SECONDS seconds, 0 always, <0 never.
       -G HOST:PORT    Use remote HOST 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 CHIP:LINE    Take 1PPS from GPIO CHIP LINE (requires -D) (LINE<0 active low).
       -I NAME         Take 1PPS from GPIO NAME (requires -D) (-NAME active low).
       -K              Write input to DEVICE sinK from datagram source.
       -L FILE         Write pretty-printed input to Listing 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.
       -Q FILE         Write validated input to FILE or named pipe.
       -R              Print a Report on standard output.
       -S FILE         Use source FILE or named pipe for input.
       -T FILE         Save the PVT CSV Trace to FILE.
       -U STRING       Collapse STRING, append Ubx end matter, write to DEVICE.
       -U ''           Exit when this empty STRING is processed.
       -V              Log Version in the form of release, vintage, and revision.
       -W STRING       Collapse STRING, append NMEA end matter, Write to DEVICE.
       -W ''           Exit when this empty STRING is processed.
       -X MASK         Enable special test modes via MASK.
       -Y HOST:PORT    Use remote HOST and PORT as keepalive sink and surveYor source.
       -Y :PORT        Use local PORT as surveYor source.
       -Z STRING       Collapse STRING, write to DEVICE.
       -Z ''           Exit when this empty STRING is processed.
       -a              Display Active satellite views first.
       -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.
       -f SECONDS      Set trace Frequency to 1/SECONDS.
       -g MASK         Set dataGram sink mask (NMEA=1, UBX=2, RTCM=4, CPO=8, default=15).
       -h              Use RTS/CTS Hardware flow control for DEVICE.
       -i SECONDS      Bypass input check every SECONDS seconds, 0 always, <0 never.
       -k MASK         Set device sinK mask (NMEA=1, UBX=2, RTCM=4, CPO=8, default=15).
       -l              Use Local control for DEVICE.
       -m              Use Modem control for DEVICE.
       -n              Use No parity for DEVICE.
       -o              Use Odd parity for DEVICE.
       -p CHIP:LINE    Assert GPIO outPut CHIP LINE with 1PPS (requires -D and -I or -c) (LINE<0 active low).
       -p NAME         Assert GPIO outPut NAME with 1PPS (requires -D and -I or -c) (-NAME active low).
       -q MASK         Set Queue mask (NMEA=1, UBX=2, RTCM=4, CPO=8, default=15).
       -s              Use XON/XOFF (c-Q/c-S) Software flow control for DEVICE.
       -t SECONDS      Timeout GNSS data after SECONDS seconds [0..255].
       -u CCM          Use CCM for convergence threshold in centicentimeters.
       -v              Display Verbose output on standard error.
       -w SECONDS      Write STRING to DEVICE no more than every SECONDS seconds, 0 always, <0 never.
       -x              EXit if a NAK is received.
       -y SECONDS      Send surveYor a keep alive every SECONDS seconds, 0 always, <0 never.
       -z              Exit if all state machines stop.

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.

csv2dgm

> csv2dgm -?
usage: csv2dgm [ -d ] [ -v ] [ -c | -h | -j | | -q | -s | -x | -y ] [ -t ] [ -D DEVICE [ -b BPS ] [ -7 | -8 ] [ -1 | -2 ] [ -e | -o | -n ] [ -m ] [ -r ] ] [ -F FILE ] [ -M MODE ] [ -U HOST:PORT ]
       -1              Set DEVICE to 1 stop bit.
       -2              Set DEVICE to 2 stop bits.
       -7              Set DEVICE to 7 data bits.
       -8              Set DEVICE to 8 data bits.
       -D DEVICE       Write datagram to DEVICE.
       -F FILE         Save latest datagram in observation FILE.
       -M MODE         Set FILE mode to MODE.
       -U HOST:PORT    Forward datagrams to HOST:PORT.
       -b BPS          Set DEVICE to BPS bits per second.
       -c              Emit CSV.
       -d              Enable debug output.
       -e              Set DEVICE to even parity.
       -h              Emit HTML.
       -j              Emit JSON.
       -o              Set DEVICE to odd parity.
       -m              Set DEVICE to use modem control.
       -n              Set DEVICE to no parity.
       -q              Emit URL Query.
       -r              Set DEVICE to use hardware flow control.
       -s              Emit Shell commands.
       -t              Write to standard output.
       -v              Enable verbose output.
       -x              Emit XML.
       -y              Emit YAML.

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).

For More Information

Acknowledgements

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

Thank you to Kathy Kadnuck of the Valley Water District, who, thanks to her collection of infrastructure survey maps, helped me to determine the location of an old NGS survey marker in my neighborhood which, apparently, had been buried under a sidewalk as part of a development in the past few decades. (Remarkably, it is not illegal to landscape over an NGS survey marker. It's merely illegal to remove or damage them. If a municipality needs to find the marker for official business, be prepared to have your landscaping back-hoe'ed up. And be billed for it.)

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.

Thanks to several manufacturers of GNSS devices, or of products that have GNSS devices embedded within them, who, because of design flaws and bugs in their products, led to vast improvements in this code. No, I'm not kidding: almost every time I test a new GNSS-based product I find the opportunity for improvement.

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.

Footnotes

Footnotes

  1. Equipped with a CR1220 3V button cell.

  2. I have used the 1PPS indication exported via a digital output pin. 2 3 4

  3. Supports UBX. 2 3 4 5 6 7 8 9 10 11 12

  4. Receives GPS (U.S.), GLONASS (Russia), Galileo (EU), COMPASS (China), and NavIC (India) concurrently.

  5. The labels on the PVT (1pps) and PWR LEDs on this board are reversed.

  6. Consider using a powered USB hub if EOFs typically indicating a USB disconnect are observed.

  7. Receives GPS (U.S.), GLONASS (Russia), Galileo (EU), and COMPASS (China) concurrently. 2 3 4 5

  8. Receives GPS (U.S.) and GLONASS (Russia) constellations concurrently. 2 3 4 5 6 7 8

  9. Bluetooth pairs with Raspberry Pi after update to FW 3.0.0.

  10. Emits all sorts of interesting stuff in unsolicited $GPTXT or $GNTXT sentences. 2 3 4 5 6 7

  11. A software defined radio (SDR).

  12. Uses Bluetooth (see procedure for pairing in https://github.com/coverclock/com-diag-hazer/blob/master/REMARKS.md). 2

  13. Supposed to receive both GPS and GLONASS but my GLO and both of my GLO 2 units just receive GPS. 2

  14. Rumored to differ from the original GLO only by its increased battery life.

  15. In either NMEA or CPO (binary) mode. 2

  16. Vendor web site and OEM packaging correctly states GPS only. 2

  17. Emits GSAs with GLONASS id (2) but only emits GSVs with GPS talker (GP).

  18. Initially updates at 1Hz until it achieves a fix, than updates at 5Hz.

  19. A good inexpensive introductory GPS device easily acquired from numerous sources; it is my go-to device to regression test Hazer and gpstool.

  20. A tear down of the BU-353W10 reveals it has a button cell for its battery backed RAM.

  21. Receives GPS (U.S.) or GLONASS (Russia) constellations via configuration.

  22. I have used the 1PPS indication exported via Data Carrier Detect (DCD).

  23. Install udev rules in overlay to prevent ModemManager from toying with device. 2

  24. Make sure you mount this component side down, patch antenna side up. 2

  25. Enumerates multiple USB devices: Ethernet port with router and web server, serial ports, storage device.

  26. Has integrated Inertial Measurement Unit (IMU). 2

  27. This SparkFun board requires opening (cutting) and closing (soldering) traces for dual UART operation.

  28. This U-Blox gen 8 receiver is embedded in a U-Blox LTE-M module which required a lot of config via AT commands.

  29. 1PPS (a.k.a. TPS in the docs) only has a pulse width of a few microseconds hence LED inoperative (bug).

  30. Timing and frequency reference claimed +/- 5ns.

About

Parse NMEA sentences and other typical output from GNSS devices.

Resources

License

Stars

Watchers

Forks

Packages

No packages published