@@ -6,9 +6,10 @@ language: generic | |||
# context, we disable the C++ builds in the clang case. | |||
env: | |||
- TRAVIS_DEBIAN_DISTRIBUTION=unstable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis.build" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=unstable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis.build libgpsmm=no qt=no" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd clang" TRAVIS_DEBIAN_BUILD_ENVIRONMENT="CC=clang CXX=clang++" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=stable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis.build" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=unstable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis/build.sh" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev qtbase5-dev qt5-default lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd python-serial" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=unstable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis/build.sh" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd python-serial" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=unstable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis/build.sh libgpsmm=no qt=no" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd clang python-serial" TRAVIS_DEBIAN_BUILD_ENVIRONMENT="CC=clang CXX=clang++" | |||
- TRAVIS_DEBIAN_DISTRIBUTION=stable TRAVIS_DEBIAN_BUILD_COMMAND="/bin/bash .travis/build.sh" TRAVIS_DEBIAN_BUILD_DEPENDENCIES="build-essential debhelper po-debconf python-all-dev python-all-dbg xsltproc docbook-xsl docbook-xml asciidoc libncurses-dev libusb-1.0-0-dev libdbus-1-dev libglib2.0-dev libdbus-glib-1-dev makedev libbluetooth-dev libqt4-dev lsb-release bc dpkg-dev scons chrpath dh-buildinfo pps-tools dh-systemd python-serial" | |||
services: | |||
- docker | |||
@@ -17,6 +17,12 @@ SCONSOPTS="${SCONSOPTS} libdir=/usr/lib/${DEB_HOST_MULTIARCH}" | |||
SCONSOPTS="${SCONSOPTS} gpsd_user=gpsd" | |||
SCONSOPTS="${SCONSOPTS} gpsd_group=dialout" | |||
SCONSOPTS="${SCONSOPTS} debug=yes" | |||
### SCONSOPTS="${SCONSOPTS} qt=yes" # The default qt=yes must be overridable | |||
SCONSOPTS="${SCONSOPTS} xgps=no" # Until we figure out the right Gtk3 packages | |||
if dpkg -s qtbase5-dev 1>/dev/null 2>&1; then | |||
SCONSOPTS="${SCONSOPTS} qt_versioned=5" | |||
fi | |||
export SCONSOPTS | |||
@@ -0,0 +1,3 @@ | |||
# gpsd vim settings | |||
# Python 3 does not like tabs in files | |||
:set tabstop=4 shiftwidth=4 expandtab |
@@ -1,6 +1,6 @@ | |||
= GPSD Installation Instructions = | |||
:title: GPSD Installation Instructions | |||
:description: Here are the steps for installing GPSD and verifying its performance. | |||
:description: Steps for installing GPSD and verifying its performance. | |||
:keywords: GPSD, GPS, installation | |||
:author: Eric S. Raymond <esr@thyrsus.com> | |||
:robots:index,follow | |||
@@ -96,6 +96,7 @@ various additional features have additional prerequisites: | |||
|Qt + qmake | libQgpsmm depends on this | |||
|python2.x(x>=6) or 3.y(y>=2) | required for various clients and utilities | |||
|python-GI bindings | the test clients xgps and xgpsspeed, need this | |||
|pyserial | ubxtool and zerk need this | |||
|=============================================================================== | |||
Some ncurses packages contain the terminfo library; some break it out | |||
@@ -105,13 +106,13 @@ separately as libtinfo5 or libtinfo. | |||
=== Install your distributions package(s) === | |||
Up-to-date gpsd packages are generally available for Linux | |||
distributions including Debian and derivatives (including Ubuntu and | |||
Mint), Fedora and derivatives (including CentOS), openSUSE, PCLinuxOS, | |||
Mageia, and Slackware. In the embedded space, CeroWRT carries | |||
GPSD. The GPSD package in the FreeBSD ports tree is also reliably up | |||
to date. Even if your distribution is not on this list, it is quite | |||
likely GPSD has already been packaged for it. | |||
Up-to-date gpsd packages are generally available for Linux distributions | |||
including Debian and derivatives (including Ubuntu and Mint), Fedora and | |||
derivatives (including CentOS), openSUSE, PCLinuxOS, Mageia, Gentoo, and | |||
Slackware. In the embedded space, CeroWRT and Yocto carry GPSD. The | |||
GPSD package in the FreeBSD ports tree is also reliably up to date. | |||
Even if your distribution is not on this list, it is quite likely GPSD | |||
has already been packaged for it. | |||
Whatever distribution you are running, the name of the core GPSD | |||
package containing the service daemon is almost certainly "gpsd". | |||
@@ -141,8 +142,8 @@ watcher modes. You should see lines beginning with '{' that are | |||
JSON objects representing reports from your GPS; these are reports | |||
in GPSD protocol. | |||
4. Start the xgps or cgps client. Calling it with no arguments should | |||
do the right thing. You should see a display panel with | |||
4. Start the xgps or cgps client. Calling it with no arguments | |||
should do the right thing. You should see a display panel with | |||
position/velocity-time information, and a satellite display. The | |||
displays won't look very interesting until the GPS acquires satellite | |||
lock. | |||
@@ -166,11 +167,11 @@ us information to add a new line to the table. Directions are | |||
included on that page. We can also use updates of the latest version | |||
number known to work with hardware already supported. | |||
3. GPSD includes a PHP script that you can use to generate a PHP | |||
status page for your GPS if you wish. (It may not be in the core | |||
package.) It should be manually copied to your HTTP document directory. | |||
The first time it's invoked, it will generate a file called | |||
'gpsd_config.inc' in that directory containing configuration | |||
3. GPSD includes gpsd.php, a PHP script, that you can use to generate | |||
a PHP status page for your GPS if you wish. (It may not be in the | |||
core package.) It should be manually copied to your HTTP document | |||
directory. The first time it's invoked, it will generate a file | |||
called 'gpsd_config.inc' in that directory containing configuration | |||
information; edit to taste. | |||
4. There are other non-essential scripts that may be useful; these | |||
@@ -331,8 +332,6 @@ install, as noted in the file *build.txt* . | |||
=== Other Raspberry Pi tips === | |||
Note: this does not apply to the RasPi3. | |||
Any USB connected GPS that is known to work with gpsd will work fine on | |||
the RasPi. No special instructions apply. | |||
@@ -355,7 +354,7 @@ Reboot so those changes take effect. | |||
Run gpsd like this: | |||
-------------------------------------------------------------- | |||
~ # gpsd -D 5 -N -n /dev/ttyAMA0/dev/pps0 | |||
~ # gpsd -D 5 -N -n /dev/ttyAMA0 /dev/pps0 | |||
-------------------------------------------------------------- | |||
If you are on the RasPi with gpsd version 3.17, or above, /dev/pps0 can | |||
@@ -1,5 +1,30 @@ | |||
GPSD project news | |||
3.19: not released yet | |||
3.18.1: 2018-10-19 (Gary E. Miller <gem@rellim.com>) | |||
Fix some installation issues. | |||
A few minor updates to ubxtool and driver_ubx. | |||
Add contrib/skyview2svg | |||
3.18: 2018-10-02 (Gary E. Miller <gem@rellim.com>) | |||
Add ECEF support to ievermore, italk,Skytraq, SiRF, U-blox drivers. | |||
Add ECEF support to JSON, cgps and xgps. | |||
Add GREIS (Javad) driver from Virgin Orbit. | |||
Add CLI tools zerk and ubxtool to manage JAVAD and u-blox GPS. | |||
Add gnssid:svid to satellite_t, cgps and xgps. PRN will die. | |||
Add gnssid:svid to JSON, cgps and xgps. | |||
Add stricter version checking (more to todo). | |||
More and better regression tests. | |||
Better Python dependency checking, at build time and runtime. | |||
Fix several buffer issues. | |||
New polar plots, and improved statistice, in gpsprof. | |||
gpsd master/slave mode works, first time ever. | |||
All isnan() changed to !isfinite(), fixing many bugs. | |||
Client-side Python libraries may automatically reconnect | |||
Too many other bug fixes and improvements to mention. | |||
Over 1,000 commits from 46 different commiters. | |||
3.17: 2017-09-07 (Eric S. Raymond <esr@snark.thyrsus.com>) | |||
Repair support for non-NMEA devices requring active probing | |||
(e.g. Garmin USB GPSes). Apply OS X build fixes. Fix a SiRF driver | |||
@@ -8,6 +8,13 @@ the "Hacking Guide" on the project website. | |||
The list of bugs exposed by gpsd in other software has moved to | |||
the "Upstream Bugs" page on the project website. | |||
** Installation ** | |||
Improve the uninstall target. Remove library syminks. | |||
Make libdir conform to new Gentoo layout: 32bit in lib/ and 64bit in | |||
lib64/. Doing that also makes the uninstall more complex. | |||
** Documentation ** | |||
We now have two different calibration procedures in the Time Service | |||
@@ -20,14 +27,40 @@ the calibration process less annoying? | |||
** Things to do when we can break compatibility ** | |||
In gps_data_t make device subtype longer. 128 chars long sounds good. | |||
In gps_data_t add more string like subtype for HW version, SW version, etc. | |||
In gps_data_t, save PPS precision; this will require creating a pps struct. | |||
Where would PPS precision come from? | |||
In gps_fix_t, maybe change time away from float to timespec? | |||
Convert all the time_t (double) and timeval (usec) to timespec (nsec). | |||
That could be done one small step at a time. | |||
Add MMSI sequence number fields to AIS type 7. | |||
Make the Python JSON client as smart as the C JSON client. Gonna be | |||
a big job. The C client checks all the JSON classes for completeness | |||
and correctness. For example, a missing JSON field will be replaced | |||
with a default value. Also out of bounds values are fixed. The Python | |||
client does no checks. it just mushes a JSON sentance into a big Python | |||
variable. No validity checking, no missing fields fixed, etc. The C | |||
client uses a generic validator function, fed with templates for each | |||
JSON message. Initially harder than a simple procedural parser, Once | |||
running it is very robust and easy to tweak. | |||
Handle mutifrequency GNSS (B1, E1, L1, L2, B3, L3, L5, L2C, E5, E6, | |||
etc.) | |||
Handle raw measurements, like UBX-RXM-RAW. With an uploader for | |||
postprocessing. | |||
add u-blox IMU (UBX-ESF-RAW) and qErr (UBX-TIM-TP) support. | |||
Adding a sample Go client (gpspipe -> pytogo -> Go ?). | |||
Add RTCM3, RINEX, and/or RAW message forwarders. | |||
Move webgps.py out of contrib. It needs a man page and light cleanup. | |||
** Bugs in gpsd and its clients: | |||
*** Tracker bugs | |||
@@ -7,7 +7,7 @@ | |||
* Cleaned up and simplified by Eric S. Raymond, December 2004. | |||
* | |||
* This file is Copyright (c) 2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#include <X11/IntrinsicP.h> | |||
#include <X11/StringDefs.h> | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* This file is Copyright (c) 2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#ifndef _GPSD_TACHOMETER_H_ | |||
#define _GPSD_TACHOMETER_H_ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* This file is Copyright (c) 2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#ifndef _GPSD_TACHOMETERP_H_ | |||
#define _GPSD_TACHOMETERP_H_ | |||
@@ -3,8 +3,6 @@ | |||
*/ | |||
#define NITEMS(x) (int)(sizeof(x)/sizeof(x[0])) | |||
/*@ -fullinitblock */ | |||
const struct json_attr_t json_ais1[] = { | |||
AIS_HEADER | |||
@@ -1212,7 +1210,6 @@ | |||
}; | |||
/*@ +fullinitblock */ | |||
/* Generated code ends. */ | |||
@@ -1,7 +1,7 @@ | |||
/* bits.c - bitfield extraction code | |||
* | |||
* This file is Copyright (c)2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
* | |||
* Bitfield extraction functions. In each, start is a bit index - not | |||
* a byte index - and width is a bit width. The width is bounded above by | |||
@@ -9,7 +9,7 @@ | |||
* Both 32- and 64-bit systems with gcc are OK with this set. | |||
* | |||
* This file is Copyright (c)2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#ifndef _GPSD_BITS_H_ | |||
#define _GPSD_BITS_H_ | |||
@@ -13,7 +13,7 @@ You can download the lastest gpsd tarball from: | |||
http://download.savannah.gnu.org/releases/gpsd/ | |||
Under Linux, assuming you have all your build prerequisites in place, | |||
these lines will do: | |||
these lines will do, and need to be run as root: | |||
tar -xzf gpsd-X.YY.tar.gz | |||
cd gpsd-X.YY | |||
@@ -22,13 +22,22 @@ these lines will do: | |||
If you get any errors, you need to read the detailed instructions that follow. | |||
If 'scons' fails, it is possible that your target system has moved to | |||
Python 3 as its default 'python' interpreter, but you can work around | |||
it by saying "python2 /usr/bin/scons" or something similar. | |||
Python 3 and removed the program 'python'. Python.org says that if | |||
you have an installed Python, there should be a program in your path | |||
called 'python'. This is specified in PEP 394. This rule is not always | |||
followed. You can work around this by linking python3 to python like | |||
this | |||
Occasionally, builds may fail in completely bizarre ways due to a corrupted | |||
scons database. This seems to relate to ^Cing the build at an inopportune | |||
moment. If you suspect that, see "Reverting to a clean state" below and | |||
then try again. | |||
ln -s /usr/bin/python3 /usr/bin/python | |||
Both 'scons' and 'gpsd' work fine on either Python 2 or Python 3. Which | |||
python you have installed should be transparent to the user, if python | |||
is installed correctly. | |||
Occasionally, builds may fail in completely bizarre ways due to a | |||
corrupted scons database. This seems to relate to ^Cing the build at | |||
an inopportune moment. If you suspect that, see "Reverting to a clean | |||
state" below and then try again. | |||
== Supported platforms == | |||
@@ -41,9 +50,9 @@ installed: | |||
* FreeBSD, NetBSD, OpenBSD | |||
* Android, using the official Google toolchain from the NDK | |||
We consider failure to build and function on any of these platforms | |||
a serious bug; if you encounter it, please complain on the | |||
development list. | |||
We consider failure to build and function on any of these platforms a | |||
serious bug; if you encounter it, please complain on the development | |||
list <gpsd-dev@nongnu.org>. | |||
Port difficulty to any system conforming to POSIX-2001.1 should be minimal. | |||
@@ -60,7 +69,7 @@ Necessary components for any build: | |||
|============================================================================ | |||
|C compiler | gpsd and client library are written in C | |||
|scons | for executing the build recipe | |||
|Python 2.x(x>=6) | for scons and some helper scripts | |||
|Python2.x(x>=6) or 3.y(y>=2) | for scons and some helper scripts | |||
|============================================================================ | |||
=== C compiler === | |||
@@ -104,13 +113,21 @@ extensions. Usually these are called "python" and "python-dev". You | |||
will know you are missing the latter if your compilation fails | |||
because of a missing Python.h. | |||
The xgps and xgpsspeed test clients require the following Python extensions: | |||
The xgps and xgpsspeed clients will only be installed if these Python | |||
extensions are installed: | |||
|============================================================================ | |||
|python-gi | Python bindings for gobject-introspection libraries | |||
|python-gi-cairo | Python bindings for Cairo toolkit under GI | |||
|=========================================================================== | |||
The ubxtool and zerk clients will only be installed if this Python | |||
extensions is installed: | |||
|============================================================================ | |||
|pyserial | Python Serial Port extension | |||
|=========================================================================== | |||
=== Scons === | |||
You will need scons version 2.3.0 (from 2013-03-02) or later to build the code. | |||
@@ -204,11 +221,11 @@ To build gpsd for your host platform from source, simply call 'scons' | |||
in a working-directory copy. (Cross-build is described in a later | |||
section.) | |||
To clean the built files, call 'scons -c' . To clean scons' cache, | |||
call 'scons sconsclean'. Doing both should return your working directory | |||
to a near pristine state as far as building is concerned. Some user | |||
created files may remain, and source code changes will not have been | |||
reverted.. | |||
To clean the built files, call 'scons -c' . To clean scons' cache, call | |||
'scons sconsclean'. Run 'rm -f .sconsign.dblite' to clear the scons | |||
database. Doing all three should return your working directory to a | |||
near pristine state as far as building is concerned. Some user created | |||
files may remain, and source code changes will not have been reverted.. | |||
You can specify the installation prefix, as for an autotools build, by | |||
running "scons prefix=<installation_root>". The default value is | |||
@@ -1 +1,208 @@ | |||
.so gps.1 | |||
'\" t | |||
.\" Title: gps | |||
.\" Author: [see the "AUTHORS" section] | |||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> | |||
.\" Date: 9 Aug 2004 | |||
.\" Manual: GPSD Documentation | |||
.\" Source: The GPSD Project | |||
.\" Language: English | |||
.\" | |||
.TH "GPS" "1" "9 Aug 2004" "The GPSD Project" "GPSD Documentation" | |||
.\" ----------------------------------------------------------------- | |||
.\" * Define some portability stuff | |||
.\" ----------------------------------------------------------------- | |||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
.\" http://bugs.debian.org/507673 | |||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html | |||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||
.ie \n(.g .ds Aq \(aq | |||
.el .ds Aq ' | |||
.\" ----------------------------------------------------------------- | |||
.\" * set default formatting | |||
.\" ----------------------------------------------------------------- | |||
.\" disable hyphenation | |||
.nh | |||
.\" disable justification (adjust text to left margin only) | |||
.ad l | |||
.\" ----------------------------------------------------------------- | |||
.\" * MAIN CONTENT STARTS HERE * | |||
.\" ----------------------------------------------------------------- | |||
.SH "NAME" | |||
cgps, gegps, gps, lcdgps, xgps, xgpsspeed \- test clients for gpsd | |||
.SH "SYNOPSIS" | |||
.HP \w'\fBcgps\fR\ 'u | |||
\fBcgps\fR [\-D\ \fIdebug\-level\fR] [\-h] [\-l\ [[d]\ |\ [m]\ |\ [s]]] [\-m] [\-s] [\-u\ [[i]\ |\ [n]\ |\ [m]]] [\-V] [\fIserver\fR [\fI:port\fR [\fI:device\fR]]] | |||
.HP \w'\fBgegps\fR\ 'u | |||
\fBgegps\fR [\-d\ \fIdirectory\fR] [\-h] [\-i] [\-V] | |||
.HP \w'\fBlcdgps\fR\ 'u | |||
\fBlcdgps\fR [\-h] [\-j] [\-l\ [[d]\ |\ [m]\ |\ [s]]] [\-s] [\-u\ [[i]\ |\ [n]\ |\ [m]]] [\-V] [\fIserver\fR [\fI:port\fR [\fI:device\fR]]] | |||
.HP \w'\fBxgps\fR\ 'u | |||
\fBxgps\fR [\-?] [\-D\ \fIdebug\-level\fR] [\-h] [\-l\ [[d]\ |\ [m]\ |\ [s]]] [\-u\ [[i]\ |\ [n]\ |\ [m]]] [\-V] [\fIserver\fR [\fI:port\fR [\fI:device\fR]]] | |||
.HP \w'\fBxgpsspeed\fR\ 'u | |||
\fBxgpsspeed\fR [\-\-debug\ \fIdebug\-level\fR] [\-\-device\ \fIdevice\fR] [\-h] [\-\-host\ \fIhost\fR] [\-\-landspeed] [\-\-maxspeed\ \fImaxspeed\fR] [\-\-nautical] [\-\-port\ \fIport\fR] [\-\-speedunits\ {[mph]\ |\ [kph]\ |\ [knots]}] [\-V] [\fIserver\fR [\fI:port\fR [\fI:device\fR]]] | |||
.SH "DESCRIPTION" | |||
.PP | |||
These are the demonstration clients shipped with | |||
gpsd\&. They have some common options: | |||
.PP | |||
The | |||
\fB\-h\fR | |||
option causes each client to emit a summary of its options and then exit\&. | |||
.PP | |||
The | |||
\fB\-V\fR | |||
option causes each client to dump the package version and exit\&. | |||
.PP | |||
The | |||
\fB\-l\fR | |||
option, when present, sets the format of latitude and longitude reports\&. The value \*(Aqd\*(Aq produces decimal degrees and is the default\&. The value \*(Aqm\*(Aq produces degrees and decimal minutes\&. The value \*(Aqs\*(Aq produces degrees, minutes, and decimal seconds\&. | |||
.PP | |||
xgps, | |||
cgps, and | |||
lcdgps | |||
look at variables in the environment to figure out what units they should default to using for display \(em imperial, nautical, or metric\&. Here are the variables and values they check: | |||
.sp | |||
.if n \{\ | |||
.RS 4 | |||
.\} | |||
.nf | |||
GPSD_UNITS one of: | |||
imperial = miles/feet | |||
nautical = knots/feet | |||
metric = km/meters | |||
LC_MEASUREMENT | |||
en_US = miles/feet | |||
C = miles/feet | |||
POSIX = miles/feet | |||
[other] = km/meters | |||
LANG | |||
en_US = miles/feet | |||
C = miles/feet | |||
POSIX = miles/feet | |||
[other] = km/meters | |||
.fi | |||
.if n \{\ | |||
.RE | |||
.\} | |||
.PP | |||
These preferences may be overridden by the | |||
\fB\-u\fR | |||
option\&. | |||
.PP | |||
Where present, the | |||
\fB\-u\fR | |||
option can be used to set the system units for display; follow the keyword with \*(Aqi\*(Aq for \*(Aqimperial\*(Aq for American units (feet in altitude and error estimates, miles per hour in speeds), \*(Aqn\*(Aq for \*(Aqnautical\*(Aq (feet in altitude and error estimates, knots in speed) or \*(Aqm\*(Aq for \*(Aqmetric\*(Aq (meters in altitude and error estimates, kilometers per hour in speeds)\&. | |||
.PP | |||
The | |||
\fB\-D\fR | |||
option, when present, sets a debug level; it is primarily for use by GPSD developers\&. It enables various progress messages to standard error\&. | |||
.PP | |||
By default, clients collect data from all compatible devices on localhost, using the default GPSD port 2947\&. An optional argument to any client may specify a server to get data from\&. A colon\-separated suffix is taken as a port number\&. If there is a second colon\-separated suffix, that is taken as a specific device name to be watched\&. However, if the server specification contains square brackets, the part inside them is taken as an IPv6 address and port/device suffixes are only parsed after the trailing bracket\&. Possible cases look like this: | |||
.PP | |||
localhost:/dev/ttyS1 | |||
.RS 4 | |||
Look at the default port of localhost, trying both IPv4 and IPv6 and watching output from serial device 1\&. | |||
.RE | |||
.PP | |||
example\&.com:2317 | |||
.RS 4 | |||
Look at port 2317 on example\&.com, trying both IPv4 and IPv6\&. | |||
.RE | |||
.PP | |||
71\&.162\&.241\&.5:2317:/dev/ttyS3 | |||
.RS 4 | |||
Look at port 2317 at the specified IPv4 address, collecting data from attached serial device 3\&. | |||
.RE | |||
.PP | |||
[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:2317:/dev/ttyS5 | |||
.RS 4 | |||
Look at port 2317 at the specified IPv6 address, collecting data from attached serial device 5\&. | |||
.RE | |||
.PP | |||
Not all clients shipped with GPSD are documented here\&. See also the separate manual pages for | |||
\fBgpspipe\fR(1) | |||
and | |||
\fBgpsmon\fR(1) | |||
and | |||
\fBgpxlogger\fR(1) | |||
\&. | |||
.SS "xgps" | |||
.PP | |||
xgps | |||
is a simple test client for | |||
gpsd | |||
with an X interface\&. It displays current GPS position/time/velocity information and (for GPSes that support the feature) the locations of accessible satellites\&. | |||
.PP | |||
In the sky view, satellites are color\-coded to indicate quality of signal; consult the data display to the left for exact figures in dB\&. Square icons indicate WAAS/EGNOS satellites, circles indicate ordinary GPS satellites\&. Filled icons were used in the last fix, outline icons were not\&. | |||
.SS "xgpsspeed" | |||
.PP | |||
xgpsspeed | |||
is a speedometer that uses position information from the GPS\&. It accepts an \-h option and optional argument as for | |||
gps, or a \-V option to dump the package version and exit\&. | |||
.PP | |||
The default display mode is a speed and track presentation modeled after a marine navigation display; for backward compatibility the \-\-nautical option forces this mode\&. The \-\-landspeed option produces a simple speedometer\&. | |||
.PP | |||
The \-speedunits option can be used to set the speed units for display; follow the keyword with knots for nautical miles per hour, kph for kilometres per hour, or mph for miles per hour\&. The default is miles per hour\&. | |||
.PP | |||
In the nautical mode only, \-\-maxspeed sets the maximum on the speedometer\&. | |||
.SS "cgps" | |||
.PP | |||
cgps | |||
is a client resembling | |||
xgps, but without the pictorial satellite display and able to run on a serial terminal or terminal emulator\&. | |||
.PP | |||
The | |||
\fB\-s\fR | |||
option prevents | |||
cgps | |||
from displaying the data coming from the daemon\&. This display can also be toggled with the s command\&. | |||
.PP | |||
The | |||
\fB\-m\fR | |||
option will display your magnetic heading (as opposed to your true heading)\&. This is a calculated value, not a measured value, and is subject to a potential error of up to two degrees in the areas for which the calculation is valid (currently Western Europe, Alaska, and Lower 48 in the USA)\&. The formulas used are those found in the Aviation Formulary v1\&.43\&. | |||
.PP | |||
cgps | |||
terminates when you send it a SIGHUP or SIGINT; given default terminal settings this will happen when you type Ctrl\-C at it\&. It will also terminate on \*(Aqq\*(Aq | |||
.SS "lcdgps" | |||
.PP | |||
A client that passes | |||
gpsd | |||
data to | |||
lcdproc, turning your car computer into a very expensive and nearly feature\-free GPS receiver\&. Currently assumes a 4x40 LCD and writes data formatted to fit that size screen\&. Also displays 4\- or 6\-character Maidenhead grid square output\&. | |||
.SS "gegps" | |||
.PP | |||
This program collects fixes from | |||
gpsd | |||
and feeds them to a running instance of Google Earth for live location tracking\&. | |||
.PP | |||
The | |||
\fB\-d\fR | |||
argument is the location of the Google Earth installation directory\&. If not specified, it defaults to the current directory\&. | |||
.PP | |||
If you have the free (non\-subscription) version, start by running with the | |||
\fB\-i\fR | |||
option to drop a clue in the Google Earth installation directory, as \*(AqOpen_in_Google_Earth_RT_GPS\&.kml\*(Aq, then open that file in Places (File > Open\&.\&.\&.)\&. Run | |||
gpsd | |||
in the normal way after that\&. | |||
.SH "SEE ALSO" | |||
.PP | |||
\fBgpsd\fR(8), | |||
\fBlibgps\fR(3), | |||
\fBlibgpsmm\fR(3), | |||
\fBgpsfake\fR(1), | |||
\fBgpsctl\fR(1), | |||
\fBgpscat\fR(1), | |||
\fBgpsprof\fR(1)\&. | |||
\fBgpspipe\fR(1)\&. | |||
\fBgpsmon\fR(1)\&. | |||
\fBgpxlogger\fR(1)\&. | |||
.SH "AUTHORS" | |||
.PP | |||
Remco Treffcorn, Derrick Brashear, Russ Nelson & Eric S\&. Raymond, Jeff Francis (cgps), Chen Wei | |||
<weichen302@aol\&.com> | |||
(gegps & xgpsspeed), Robin Wittler | |||
<real@the\-real\&.org> | |||
(xgpsspeed)\&. | |||
.PP | |||
This manual page by Eric S\&. Raymond | |||
<esr@thyrsus\&.com> |
@@ -2,6 +2,13 @@ The following tools are not production-ready. They are included only as | |||
conveniences, examples or rudimetary starting points for other development | |||
efforts. | |||
skyview2svg reads the skyview from gpspipe, and creates an svg image | |||
of the skyview. | |||
gpssnmp a tool to return some SNMP values from the running local gpsd. | |||
clock_test is used to test the latency of the system call clock_getttime(). | |||
binlog and binreplay are probably only useful for people developing | |||
drivers for new protocols, when gpsfake does not yet know what to do | |||
with a log file. These utilities are not particularly clever - they | |||
@@ -6,12 +6,11 @@ | |||
# Note that ../gpsd_config.h is required and is built separately | |||
strl = StaticObject("strl", "../strl.c") | |||
ashctl = Program("ashctl", ["ashctl.c", strl]) | |||
ashctl = Program("ashctl", "ashctl.c") | |||
binlog = Program("binlog", "binlog.c") | |||
binreplay = Program("binreplay", "binreplay.c", parse_flags=['-lutil']) | |||
clock_test = Program("clock_test", "clock_test.c", parse_flags=['-lm']) | |||
lla2ecef = Program("lla2ecef", "lla2ecef.c", parse_flags=['-lm']) | |||
motosend = Program("motosend", ["motosend.c", strl]) | |||
motosend = Program("motosend", "motosend.c") | |||
Default(ashctl, binlog, binreplay, lla2ecef, motosend) | |||
Default(ashctl, binlog, binreplay, clock_test, lla2ecef, motosend) |
@@ -0,0 +1,110 @@ | |||
/* | |||
* clock_test. A simple program to test the latency of the clock_gettime() call | |||
* | |||
* Compile: gcc clock_test.c -lm -o clock_test | |||
* | |||
*/ | |||
#include <getopt.h> /* for getopt() */ | |||
#include <limits.h> /* for LONG_MAX */ | |||
#include <math.h> /* for pow(), sqrt() */ | |||
#include <stdio.h> /* for printf() */ | |||
#include <stdlib.h> /* for qsort() */ | |||
#include <time.h> /* for time_t */ | |||
#define NUM_TESTS 101 /* default samples, make it odd for a clean median */ | |||
#define DELAY 10000000 /* default delay between samples in ns, 10 ms is good */ | |||
int compare_long( const void *ap, const void *bp) | |||
{ | |||
long a = *((long *)ap); | |||
long b = *((long *)bp); | |||
if ( a < b ) return -1; | |||
if ( a > b ) return 1; | |||
return 0; | |||
} | |||
int main(int argc, char **argv) | |||
{ | |||
int i; | |||
int opt; /* for getopts() */ | |||
int verbose = 0; | |||
int samples = NUM_TESTS; | |||
long delay = DELAY; | |||
long *diffs = NULL; | |||
long min = LONG_MAX, max = 0, sum = 0, mean = 0, median = 0; | |||
double stddev = 0.0; | |||
while ((opt = getopt(argc, argv, "d:hvn:")) != -1) { | |||
switch (opt) { | |||
case 'd': | |||
delay = atol(optarg); | |||
break; | |||
case 'n': | |||
samples = atoi(optarg); | |||
/* make odd, for a good median */ | |||
if ( (samples & 1) == 0) { | |||
samples += 1; | |||
} | |||
break; | |||
case 'v': | |||
verbose = 1; | |||
break; | |||
case 'h': | |||
/* fall through */ | |||
default: /* '?' */ | |||
fprintf(stderr, "Usage: %s [-h] [-d nsec] [-n samples] [-v]\n\n", argv[0]); | |||
fprintf(stderr, "-d nsec : nano seconde paus between samples\n"); | |||
fprintf(stderr, "-h : help\n"); | |||
fprintf(stderr, "-n samples : Number of samples, default %d\n", NUM_TESTS); | |||
fprintf(stderr, "-v : verbose\n"); | |||
exit(EXIT_FAILURE); | |||
} | |||
} | |||
diffs = alloca( sizeof(long) * (samples + 2)); /* add 2 for off by one errors */ | |||
/* collect test data */ | |||
for ( i = 0 ; i < samples; i++ ) { | |||
struct timespec now, now1, sleep, sleep1; | |||
(void)clock_gettime(CLOCK_REALTIME, &now); | |||
(void)clock_gettime(CLOCK_REALTIME, &now1); | |||
diffs[i] = now1.tv_nsec - now.tv_nsec; | |||
if ( now1.tv_sec != now.tv_sec ) { | |||
/* clock roll over, fix it */ | |||
diffs[i] += 1000000000; /* add one second */ | |||
} | |||
/* instead of hammering, sleep between tests, let the cache get cold */ | |||
sleep.tv_sec = 0; | |||
sleep.tv_nsec = delay; /* sleep delay */ | |||
/* sleep1 unused, should not be returning early */ | |||
nanosleep(&sleep, &sleep1); | |||
} | |||
/* analyze test data */ | |||
/* print diffs, calculate min and max */ | |||
for ( i = 0 ; i < samples; i++ ) { | |||
if ( verbose > 0 ) { | |||
printf("diff %ld\n", diffs[i]); | |||
} | |||
sum += diffs[i]; | |||
if ( diffs[i] < min ) min = diffs[i]; | |||
if ( diffs[i] > max ) max = diffs[i]; | |||
} | |||
mean = sum / (samples - 1); | |||
qsort( diffs, samples, sizeof(long), compare_long); | |||
median = diffs[(samples / 2) + 1]; | |||
for ( i = 0 ; i < samples; i++ ) { | |||
stddev += pow(diffs[i] - mean, 2); | |||
} | |||
stddev = sqrt(stddev/samples); | |||
printf("samples %d, delay %ld ns\n", samples, delay); | |||
printf("min %ld ns, max %ld ns, mean %ld ns, median %ld ns, StdDev %ld ns\n", | |||
min, max, mean, median, (long)stddev); | |||
} |
@@ -1 +1 @@ | |||
gpsd-3.17/../gps | |||
gpsd-3.18.1/../gps |
@@ -1,7 +1,7 @@ | |||
#! /usr/bin/python | |||
# Written by Dan Mandle http://dan.mandle.me September 2012 | |||
# http://www.danmandle.com/blog/getting-gpsd-to-work-with-python/ | |||
# License: GPL 2.0 | |||
# License: GPL 2.0 | |||
# This code runs compatibly under Python 2 and 3.x for x >= 2. | |||
# Preserve this property! | |||
@@ -13,57 +13,64 @@ from time import * | |||
import time | |||
import threading | |||
gpsd = None #seting the global variable | |||
gpsd = None # setting the global variable | |||
os.system('clear') # clear the terminal (optional) | |||
os.system('clear') #clear the terminal (optional) | |||
class GpsPoller(threading.Thread): | |||
def __init__(self): | |||
threading.Thread.__init__(self) | |||
global gpsd #bring it in scope | |||
gpsd = gps(mode=WATCH_ENABLE) #starting the stream of info | |||
self.current_value = None | |||
self.running = True #setting the thread running to true | |||
def __init__(self): | |||
threading.Thread.__init__(self) | |||
global gpsd # bring it in scope | |||
gpsd = gps(mode=WATCH_ENABLE) # starting the stream of info | |||
self.current_value = None | |||
self.running = True # setting the thread running to true | |||
def run(self): | |||
global gpsd | |||
while gpsp.running: | |||
# this will continue to loop and grab EACH set of | |||
# gpsd info to clear the buffer | |||
next(gpsd) | |||
def run(self): | |||
global gpsd | |||
while gpsp.running: | |||
next(gpsd) #this will continue to loop and grab EACH set of gpsd info to clear the buffer | |||
if __name__ == '__main__': | |||
gpsp = GpsPoller() # create the thread | |||
try: | |||
gpsp.start() # start it up | |||
while True: | |||
#It may take a second or two to get good data | |||
#print gpsd.fix.latitude,', ',gpsd.fix.longitude,' Time: ',gpsd.utc | |||
gpsp = GpsPoller() # create the thread | |||
try: | |||
gpsp.start() # start it up | |||
while True: | |||
# It may take a second or two to get good data | |||
# print(gpsd.fix.latitude,', ',gpsd.fix.longitude,' | |||
# Time: ',gpsd.utc | |||
os.system('clear') | |||
os.system('clear') | |||
print() | |||
print(' GPS reading') | |||
print('----------------------------------------') | |||
print('latitude ', gpsd.fix.latitude) | |||
print('longitude ', gpsd.fix.longitude) | |||
print('time utc ', gpsd.utc, ' + ', gpsd.fix.time) | |||
print('altitude (m)', gpsd.fix.altitude) | |||
print('eps ', gpsd.fix.eps) | |||
print('epx ', gpsd.fix.epx) | |||
print('epv ', gpsd.fix.epv) | |||
print('ept ', gpsd.fix.ept) | |||
print('speed (m/s) ', gpsd.fix.speed) | |||
print('climb ', gpsd.fix.climb) | |||
print('track ', gpsd.fix.track) | |||
print('mode ', gpsd.fix.mode) | |||
print() | |||
print("%s satellites in view:" % len(gpsd.satellites)) | |||
for sat in gpsd.satellites: | |||
print(" %r" % sat) | |||
print() | |||
print(' GPS reading') | |||
print('----------------------------------------') | |||
print('latitude ' , gpsd.fix.latitude) | |||
print('longitude ' , gpsd.fix.longitude) | |||
print('time utc ' , gpsd.utc,' + ', gpsd.fix.time) | |||
print('altitude (m)' , gpsd.fix.altitude) | |||
print('eps ' , gpsd.fix.eps) | |||
print('epx ' , gpsd.fix.epx) | |||
print('epv ' , gpsd.fix.epv) | |||
print('ept ' , gpsd.fix.ept) | |||
print('speed (m/s) ' , gpsd.fix.speed) | |||
print('climb ' , gpsd.fix.climb) | |||
print('track ' , gpsd.fix.track) | |||
print('mode ' , gpsd.fix.mode) | |||
print() | |||
print("%s satellites in view:" % len(gpsd.satellites)) | |||
for sat in gpsd.satellites: | |||
print(" %r" % sat) | |||
time.sleep(5) # set to whatever | |||
time.sleep(5) #set to whatever | |||
except (KeyboardInterrupt, SystemExit): | |||
# when you press ctrl+c | |||
print("\nKilling Thread...") | |||
gpsp.running = False | |||
gpsp.join() # wait for the thread to finish what it's doing | |||
except (KeyboardInterrupt, SystemExit): #when you press ctrl+c | |||
print("\nKilling Thread...") | |||
gpsp.running = False | |||
gpsp.join() # wait for the thread to finish what it's doing | |||
print("Done.\nExiting.") | |||
print("Done.\nExiting.") |
@@ -0,0 +1,91 @@ | |||
/* gpssnmp - poll local gpsd for SNMP variables | |||
* | |||
* Copyright (c) 2016 David Taylor <gpsd@david.taylor.name> | |||
* | |||
* Copyright (c)2018 by the GPSD project | |||
* SPDX-License-Identifier: BSD-2-clause | |||
* | |||
* To build this: | |||
* gcc -o gpssnmp gpssnmp.c -lgps | |||
* | |||
*/ | |||
#include <gps.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
static void usage() { | |||
printf("\n" | |||
"Usage:\n" | |||
"\n" | |||
"to get OID_VISIBLE\n" | |||
" $ gpssnmp -g .1.3.6.1.2.1.25.1.31\n" | |||
" .1.3.6.1.2.1.25.1.31\n" | |||
" gauge\n" | |||
" 13\n" | |||
"\n" | |||
"to get OID_USED\n" | |||
" $ gpssnmp -g .1.3.6.1.2.1.25.1.32\n" | |||
" .1.3.6.1.2.1.25.1.32\n" | |||
" gauge\n" | |||
" 4\n" | |||
"\n" | |||
"to get OID_SNR_AVG\n" | |||
" $ gpssnmp -g .1.3.6.1.2.1.25.1.33\n" | |||
" .1.3.6.1.2.1.25.1.33\n" | |||
" gauge\n" | |||
" 22.250000\n" | |||
"\n"); | |||
} | |||
int main (int argc, char **argv) { | |||
struct gps_data_t gpsdata; | |||
#define OID_VISIBLE ".1.3.6.1.2.1.25.1.31" | |||
#define OID_USED ".1.3.6.1.2.1.25.1.32" | |||
#define OID_SNR_AVG ".1.3.6.1.2.1.25.1.33" | |||
if ((argc > 2) && (strcmp ("-g", argv[1]) == 0)) { | |||
int i; | |||
double snr_total=0; | |||
double snr_avg = 0.0; | |||
int status, used, visible; | |||
status = gps_open (GPSD_SHARED_MEMORY, DEFAULT_GPSD_PORT, &gpsdata); | |||
status = gps_read (&gpsdata); | |||
used = gpsdata.satellites_used; | |||
visible = gpsdata.satellites_visible; | |||
for(i=0; i<=used; i++) { | |||
if (gpsdata.skyview[i].used > 0 && gpsdata.skyview[i].ss > 1) { | |||
// printf ("i: %d, P:%d, ss: %f\n", i, gpsdata.skyview[i].PRN, | |||
// gpsdata.skyview[i].ss); | |||
snr_total+=gpsdata.skyview[i].ss; | |||
} | |||
} | |||
gps_close (&gpsdata); | |||
if (used > 0) { | |||
snr_avg = snr_total / used; | |||
} | |||
if (strcmp (OID_VISIBLE, argv[2]) == 0) { | |||
printf (OID_VISIBLE); | |||
printf ("\n"); | |||
printf ("gauge\n"); | |||
printf ("%d\n", visible); | |||
} | |||
if (strcmp (OID_USED, argv[2]) == 0) { | |||
printf (OID_USED); | |||
printf ("\n"); | |||
printf ("gauge\n"); | |||
printf ("%d\n", used); | |||
} | |||
if (strcmp (OID_SNR_AVG, argv[2]) == 0) { | |||
printf (OID_SNR_AVG); | |||
printf ("\n"); | |||
printf ("gauge\n"); | |||
printf ("%lf\n", snr_avg); | |||
} | |||
} else { | |||
usage(); | |||
} | |||
return 0; | |||
} |
@@ -1,25 +1,37 @@ | |||
#!/usr/bin/env python | |||
# | |||
# ntpshmviz - graph the drift of NTP servers | |||
# Written by Keane Wolter <daemoneye2@gmail.com> | |||
''' | |||
ntpshmviz - graph the drift of NTP servers | |||
Written by Keane Wolter <daemoneye2@gmail.com> | |||
''' | |||
# | |||
# To do: | |||
# | |||
# 1. Add exit button so the user does not need to do <Ctrl>-w | |||
# 2. Allow for a continuous stream of data to be graphed | |||
# | |||
from __future__ import absolute_import, print_function, division | |||
import sys | |||
# need numpy for float128, normal python floats are too small to hold a timespec | |||
# need numpy for float128, normal python floats are too small to | |||
# hold a timespec | |||
import numpy | |||
try: | |||
import matplotlib.pyplot as PLT | |||
from matplotlib.figure import Figure | |||
except: | |||
print("Please make sure matplotlib is installed properly:", sys.exc_info()[0]) | |||
except ImportError: | |||
print("Please make sure matplotlib is installed properly:", | |||
sys.exc_info()[0]) | |||
sys.exit(1) | |||
class ntpOffset: | |||
gps_version = '3.18.1' | |||
class ntpOffset(object): | |||
"The master Class" | |||
def __init__(self, stream): | |||
"Initialize class ntpOffset" | |||
# get the data | |||
self.read_data(stream) | |||
@@ -27,10 +39,11 @@ class ntpOffset: | |||
self.display() | |||
def display(self): | |||
# display the graphs | |||
"display the graphs" | |||
# Alert the user that closing the graphs can be done with "Ctrl-w" | |||
print ("Please note that the graph can be closed with the key combination of <Ctrl-w>") | |||
print ("Please note that the graph can be closed with the " | |||
"key combination of <Ctrl-w>") | |||
PLT.figure() | |||
subplot_value = 211 | |||
@@ -47,12 +60,16 @@ class ntpOffset: | |||
# increment the subplot by 1. | |||
# this allows for each data group to have it's own graph | |||
subplot_value += 1 | |||
# make sure there is no graph or data overlapping each other and display the graph | |||
# make sure there is no graph or data overlapping each other and | |||
# display the graph | |||
PLT.tight_layout() | |||
PLT.show() | |||
def read_data(self, stream): | |||
# Reads data from a ntp log file. Layout is: | |||
"Read data from a ntp log file." | |||
# Layout is: | |||
# | |||
# - The keyword "sample" | |||
# - The NTP unit from which it was collected. | |||
@@ -62,11 +79,11 @@ class ntpOffset: | |||
# - Leep-second notification status | |||
# - Source precision (log(2) of source jitter) | |||
self.ntp_data = {} # data sets for each ntp unit | |||
self.line_counts = {} # Count of total lines for each ntp unit | |||
record = [] # A single record in the file or data stream | |||
offset = 0 # offset value to add to the array | |||
self.lines = 0 # width of graph | |||
self.ntp_data = {} # data sets for each ntp unit | |||
self.line_counts = {} # Count of total lines for each ntp unit | |||
record = [] # A single record in the file or data stream | |||
offset = 0 # offset value to add to the array | |||
self.lines = 0 # width of graph | |||
for line in stream: | |||
if len(line.split(' ')) > 6: | |||
@@ -74,9 +91,11 @@ class ntpOffset: | |||
line = line.lstrip() | |||
record = line.split(' ') | |||
try: | |||
offset = numpy.float128(record[3]) - numpy.float128(record[4]) | |||
offset = (numpy.float128(record[3]) - | |||
numpy.float128(record[4])) | |||
except: | |||
print ("Invalid data: ", sys.exc_info()[0], ". Data was: ", line) | |||
print ("Invalid data: ", sys.exc_info()[0], | |||
". Data was: ", line) | |||
continue | |||
# If the NTP unit is in the dictionary | |||
@@ -84,29 +103,34 @@ class ntpOffset: | |||
# Otherwise, create a new list in the dictionary | |||
# and add the offset. | |||
if record[1] not in self.ntp_data: | |||
self.ntp_data[record[1]] = [] | |||
self.ntp_data[record[1]] = [] | |||
self.line_counts[record[1]] = 0 | |||
self.ntp_data[record[1]].append(offset) | |||
self.line_counts[record[1]] += 1 | |||
stream.close() | |||
if __name__ == "__main__": | |||
if (len(sys.argv) == 2): | |||
if len(sys.argv) >= 2: | |||
if (sys.argv[1] == "-V" or sys.argv[1] == "--version"): | |||
print ("Version 2.0") | |||
sys.exit() | |||
sys.stderr.write("ntpshmviz: Version %s\n" % gps_version) | |||
sys.exit(0) | |||
if (sys.argv[1] == "-h" or sys.argv[1] == "--help"): | |||
print ("Usage: <input stream> | ntpshmviz") | |||
print ("Example: cat SAMPLES | ntpshmviz") | |||
print ("") | |||
print ("Options:") | |||
print ("\t-h: help message") | |||
print ("\t-V: version information") | |||
print ("") | |||
print ("Report ntpshmviz bugs to Keane Wolter, daemoneye2@gmail.com") | |||
print ("gpsd homepage: www.catb.org/gpsd/") | |||
print(""" | |||
Usage: <input stream> | ntpshmviz [-h|--help] [-V|--version] | |||
Example: ntpshmmon -n 100 | ntpshmviz | |||
Options: | |||
\t-h: help message | |||
\t-V: version information | |||
Report ntpshmviz bugs to Keane Wolter, daemoneye2@gmail.com | |||
or gpsd-users@nongnu.org | |||
gpsd homepage: www.catb.org/gpsd/ | |||
""") | |||
sys.exit() | |||
ntpOffset(sys.stdin) | |||
@@ -0,0 +1,367 @@ | |||
#!/usr/bin/env python | |||
# -*- coding: utf-8 -*- | |||
""" | |||
skyview2svg -- Create an SVG image of GPS satellites sky view. | |||
Read from file or stdin the JSON data produced by gpsd, | |||
example usage: | |||
gpspipe -w | skyview2svg > skyview.svg | |||
For GPSD JSON format see: http://www.catb.org/gpsd/gpsd_json.html | |||
""" | |||
# SPDX-License-Identifier: BSD-2-clause | |||
# This code runs compatibly under Python 2 and 3.x for x >= 2. | |||
# Preserve this property! | |||
from __future__ import absolute_import, print_function, division | |||
import datetime | |||
import json | |||
import math | |||
import sys | |||
__author__ = "Niccolo Rigacci" | |||
__copyright__ = "Copyright 2018 Niccolo Rigacci <niccolo@rigacci.org>" | |||
__license__ = "BSD-2-clause" | |||
__email__ = "niccolo@rigacci.org" | |||
__version__ = "3.18.1" | |||
# ------------------------------------------------------------------------ | |||
# ------------------------------------------------------------------------ | |||
def polar2cart(azimuth, elevation, r_max): | |||
"""Convert polar coordinates in cartesian ones.""" | |||
radius = r_max * (1 - elevation / 90.0) | |||
theta = math.radians(float(azimuth - 90)) | |||
return ( | |||
int(radius * math.cos(theta) + 0.5), | |||
int(radius * math.sin(theta) + 0.5) | |||
) | |||
# ------------------------------------------------------------------------ | |||
# ------------------------------------------------------------------------ | |||
def cutoff_err(err, err_min, err_max): | |||
"""Cut-off Estimated Error between min and max.""" | |||
if err is None or err >= err_max: | |||
return err_max, '>' | |||
if err <= err_min: | |||
return err_min, '<' | |||
else: | |||
return err, '' | |||
# ------------------------------------------------------------------------ | |||
# Read JSON data from file or stdin, search a {'class': 'SKY'} line. | |||
# ------------------------------------------------------------------------ | |||
EXIT_CODE = 0 | |||
SKY = None | |||
TPV = None | |||
try: | |||
if len(sys.argv) > 1: | |||
with open(sys.argv[1]) as f: | |||
while True: | |||
SENTENCE = json.loads(f.readline()) | |||
if 'class' in SENTENCE and SENTENCE['class'] == 'SKY': | |||
SKY = SENTENCE | |||
if 'class' in SENTENCE and SENTENCE['class'] == 'TPV': | |||
TPV = SENTENCE | |||
if SKY is not None and TPV is not None: | |||
break | |||
else: | |||
while True: | |||
SENTENCE = json.loads(sys.stdin.readline()) | |||
if 'class' in SENTENCE and SENTENCE['class'] == 'SKY': | |||
SKY = SENTENCE | |||
if 'class' in SENTENCE and SENTENCE['class'] == 'TPV': | |||
TPV = SENTENCE | |||
if SKY is not None and TPV is not None: | |||
sys.stdin.close() | |||
break | |||
except (IOError, ValueError): | |||
# Assume empty data and write msg to stderr. | |||
EXIT_CODE = 100 | |||
sys.stderr.write("Error reading JSON data from file or stdin." | |||
" Creating an empty or partial skyview image.\n") | |||
if SKY is None: | |||
SKY = {} | |||
if TPV is None: | |||
TPV = {} | |||
# ------------------------------------------------------------------------ | |||
# Colors for the SVG styles. | |||
# ------------------------------------------------------------------------ | |||
# Background and label colors. | |||
BACKGROUND_COLOR = '#323232' | |||
LBL_FONT_COLOR = 'white' | |||
FONT_FAMILY = 'Verdana,Arial,Helvetica,sans-serif' | |||
# Compass dial. | |||
COMPASS_STROKE_COLOR = '#9d9d9d' | |||
DIAL_POINT_COLOR = COMPASS_STROKE_COLOR | |||
# Satellites constellation. | |||
SAT_USED_FILL_COLOR = '#00ff00' | |||
SAT_UNUSED_FILL_COLOR = '#d0d0d0' | |||
SAT_USED_STROKE_COLOR = '#0b400b' | |||
SAT_UNUSED_STROKE_COLOR = '#101010' | |||
SAT_USED_TEXT_COLOR = '#000000' | |||
SAT_UNUSED_TEXT_COLOR = '#000000' | |||
# Sat signal/noise ratio box and bars. | |||
BARS_AREA_FILL_COLOR = '#646464' | |||
BARS_AREA_STROKE_COLOR = COMPASS_STROKE_COLOR | |||
BAR_USED_FILL_COLOR = '#00ff00' | |||
BAR_UNUSED_FILL_COLOR = '#ffffff' | |||
BAR_USED_STROKE_COLOR = '#324832' | |||
BAR_UNUSED_STROKE_COLOR = BACKGROUND_COLOR | |||
# ------------------------------------------------------------------------ | |||
# Size and position of elements. | |||
# ------------------------------------------------------------------------ | |||
IMG_WIDTH = 528 | |||
IMG_HEIGHT = 800 | |||
STROKE_WIDTH = int(IMG_WIDTH * 0.007) | |||
# Scale graph bars to accomodate at least MIN_SAT values. | |||
MIN_SAT = 12 | |||
NUM_SAT = MIN_SAT | |||
# Auto-scale: reasonable values for Signal/Noise Ratio and Error. | |||
SNR_MAX = 30.0 # Do not autoscale below this value. | |||
# Auto-scale horizontal and vertical error, in meters. | |||
ERR_MIN = 5.0 | |||
ERR_MAX = 75.0 | |||
# Create an empty list, if satellites list is missing. | |||
if 'satellites' not in SKY.keys(): | |||
SKY['satellites'] = [] | |||
if len(SKY['satellites']) < MIN_SAT: | |||
NUM_SAT = MIN_SAT | |||
else: | |||
NUM_SAT = len(SKY['satellites']) | |||
# Make a sortable array and autoscale SNR. | |||
SATELLITES = {} | |||
for sat in SKY['satellites']: | |||
SATELLITES[sat['PRN']] = sat | |||
if float(sat['ss']) > SNR_MAX: | |||
SNR_MAX = float(sat['ss']) | |||
# Compass dial and satellites placeholders. | |||
CIRCLE_X = int(IMG_WIDTH * 0.50) | |||
CIRCLE_Y = int(IMG_WIDTH * 0.49) | |||
CIRCLE_R = int(IMG_HEIGHT * 0.22) | |||
SAT_WIDTH = int(CIRCLE_R * 0.24) | |||
SAT_HEIGHT = int(CIRCLE_R * 0.14) | |||
# GPS position. | |||
POS_LBL_X = int(IMG_WIDTH * 0.50) | |||
POS_LBL_Y = int(IMG_HEIGHT * 0.62) | |||
# Sat signal/noise ratio box and bars. | |||
BARS_BOX_WIDTH = int(IMG_WIDTH * 0.82) | |||
BARS_BOX_HEIGHT = int(IMG_HEIGHT * 0.14) | |||
BARS_BOX_X = int((IMG_WIDTH - BARS_BOX_WIDTH) * 0.5) | |||
BARS_BOX_Y = int(IMG_HEIGHT * 0.78) | |||
BAR_HEIGHT_MAX = int(BARS_BOX_HEIGHT * 0.72) | |||
BAR_SPACE = int((BARS_BOX_WIDTH - STROKE_WIDTH) / NUM_SAT) | |||
BAR_WIDTH = int(BAR_SPACE * 0.70) | |||
BAR_RADIUS = int(BAR_WIDTH * 0.20) | |||
# Error box and bars. | |||
ERR_BOX_X = int(IMG_WIDTH * 0.65) | |||
ERR_BOX_Y = int(IMG_HEIGHT * 0.94) | |||
ERR_BOX_WIDTH = int((BARS_BOX_X + BARS_BOX_WIDTH) - ERR_BOX_X) | |||
ERR_BOX_HEIGHT = BAR_SPACE * 2 | |||
ERR_BAR_HEIGHT_MAX = int(ERR_BOX_WIDTH - STROKE_WIDTH*2) | |||
# Timestamp | |||
TIMESTAMP_X = int(IMG_WIDTH * 0.50) | |||
TIMESTAMP_Y = int(IMG_HEIGHT * 0.98) | |||
# Text labels. | |||
LBL_FONT_SIZE = int(IMG_WIDTH * 0.036) | |||
LBL_COMPASS_POINTS_SIZE = int(CIRCLE_R * 0.12) | |||
LBL_SAT_SIZE = int(SAT_HEIGHT * 0.75) | |||
LBL_SAT_BAR_SIZE = int(BAR_WIDTH * 0.90) | |||
# Get timestamp from GPS or system. | |||
if 'time' in SKY: | |||
UTC = datetime.datetime.strptime(SKY['time'], '%Y-%m-%dT%H:%M:%S.%fZ') | |||
elif 'time' in TPV: | |||
UTC = datetime.datetime.strptime(TPV['time'], '%Y-%m-%dT%H:%M:%S.%fZ') | |||
else: | |||
UTC = datetime.datetime.utcnow() | |||
TIME_STR = UTC.strftime('%Y-%m-%d %H:%M:%S UTC') | |||
# ------------------------------------------------------------------------ | |||
# Output the SGV image. | |||
# ------------------------------------------------------------------------ | |||
print('''<?xml version="1.0" encoding="UTF-8" ?> | |||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" | |||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | |||
<svg | |||
xmlns="http://www.w3.org/2000/svg" | |||
width="%d" | |||
height="%d">''' % (IMG_WIDTH, IMG_HEIGHT)) | |||
# NOTICE: librsvg v.2.40 has a bug with "chain" multiple class selectors: | |||
# it does not handle a selector like text.label.title and a | |||
# tag class="label title". | |||
print('<style type="text/css">') | |||
# Labels. | |||
print(' text ' | |||
'{ font-family: Verdana,Arial,Helvetica,sans-serif; font-weight: bold;}') | |||
print(' text.label { fill: %s; font-size: %dpx; }' % | |||
(LBL_FONT_COLOR, LBL_FONT_SIZE)) | |||
print(' text.label-title { font-size: %dpx; text-anchor: middle; }' % | |||
(int(LBL_FONT_SIZE * 1.4),)) | |||
print(' text.label-prn { font-size: %dpx; text-anchor: end; }' % | |||
(LBL_SAT_BAR_SIZE,)) | |||
print(' text.label-center { text-anchor: middle; }') | |||
print(' text.label-snr { text-anchor: start; }') | |||
print(' text.label-err { text-anchor: end; }') | |||
# Compass dial. | |||
print(' circle.compass ' | |||
'{ stroke: %s; stroke-width: %d; fill-opacity: 0; }' % | |||
(COMPASS_STROKE_COLOR, STROKE_WIDTH,)) | |||
print(' line.compass { stroke: %s; stroke-width: %d; }' % | |||
(COMPASS_STROKE_COLOR, STROKE_WIDTH)) | |||
print(' text.compass ' | |||
'{ fill: %s; font-size: %dpx; text-anchor: middle; }' % | |||
(DIAL_POINT_COLOR, LBL_COMPASS_POINTS_SIZE)) | |||
# Satellites constellation. | |||
print(' rect.sats { stroke-width: %d; fill-opacity: 1.0; }' % | |||
(STROKE_WIDTH,)) | |||
print(' rect.sats-used { stroke: %s; fill: %s; }' % | |||
(SAT_USED_STROKE_COLOR, SAT_USED_FILL_COLOR)) | |||
print(' rect.sats-unused { stroke: %s; fill: %s; }' % | |||
(SAT_UNUSED_STROKE_COLOR, SAT_UNUSED_FILL_COLOR)) | |||
print(' text.sats { font-size: %dpx; text-anchor: middle; }' % | |||
(LBL_SAT_SIZE,)) | |||
print(' text.sats-used { fill: %s; }' % (SAT_USED_TEXT_COLOR,)) | |||
print(' text.sats-unused { fill: %s; }' % (SAT_UNUSED_TEXT_COLOR,)) | |||
# Box containing bars graph. | |||
print(' rect.box { fill: %s; stroke: %s; stroke-width: %d; }' % | |||
(BARS_AREA_FILL_COLOR, BARS_AREA_STROKE_COLOR, STROKE_WIDTH)) | |||
# Graph bars. | |||
print(' rect.bars { stroke-width: %d; opacity: 1.0; }' % | |||
(STROKE_WIDTH,)) | |||
print(' rect.bars-used { stroke: %s; fill: %s; }' % | |||
(BAR_USED_STROKE_COLOR, BAR_USED_FILL_COLOR)) | |||
print(' rect.bars-unused { stroke: %s; fill: %s; }' % | |||
(BAR_UNUSED_STROKE_COLOR, BAR_UNUSED_FILL_COLOR)) | |||
print('</style>') | |||
# Background and title. | |||
print('<rect width="100%%" height="100%%" fill="%s" />' % | |||
(BACKGROUND_COLOR,)) | |||
print('<text class="label label-title" x="%d" y="%d">' | |||
'Sky View of GPS Satellites</text>' % | |||
(int(IMG_WIDTH * 0.5), int(LBL_FONT_SIZE * 1.5))) | |||
# Sky circle with cardinal points. | |||
print('<circle class="compass" cx="%d" cy="%d" r="%d" />' % | |||
(CIRCLE_X, CIRCLE_Y, CIRCLE_R)) | |||
print('<circle class="compass" cx="%d" cy="%d" r="%d" />' % | |||
(CIRCLE_X, CIRCLE_Y, int(CIRCLE_R / 2))) | |||
print('<line class="compass" x1="%d" y1="%d" x2="%d" y2="%d" />' % | |||
(CIRCLE_X, CIRCLE_Y - CIRCLE_R, CIRCLE_X, CIRCLE_Y + CIRCLE_R)) | |||
print('<line class="compass" x1="%d" y1="%d" x2="%d" y2="%d" />' % | |||
(CIRCLE_X - CIRCLE_R, CIRCLE_Y, CIRCLE_X + CIRCLE_R, CIRCLE_Y)) | |||
print('<text x="%d" y="%d" class="compass">%s</text>' % | |||
(CIRCLE_X, CIRCLE_Y - CIRCLE_R - LBL_COMPASS_POINTS_SIZE, 'N')) | |||
print('<text x="%d" y="%d" class="compass">%s</text>' % | |||
(CIRCLE_X, CIRCLE_Y + CIRCLE_R + LBL_COMPASS_POINTS_SIZE, 'S')) | |||
print('<text x="%d" y="%d" class="compass">%s</text>' % | |||
(CIRCLE_X - CIRCLE_R - LBL_COMPASS_POINTS_SIZE, | |||
CIRCLE_Y + int(LBL_COMPASS_POINTS_SIZE*0.4), 'W')) | |||
print('<text x="%d" y="%d" class="compass">%s</text>' % | |||
(CIRCLE_X + CIRCLE_R + LBL_COMPASS_POINTS_SIZE, | |||
CIRCLE_Y + int(LBL_COMPASS_POINTS_SIZE*0.4), 'E')) | |||
# Lat/lon. | |||
POS_LAT = "%.5f" % (float(TPV['lat']),) if 'lat' in TPV else 'Unknown' | |||
POS_LON = "%.5f" % (float(TPV['lon']),) if 'lon' in TPV else 'Unknown' | |||
print('<text class="label label-center" x="%d" y="%d">Lat/Lon: %s %s</text>' % | |||
(POS_LBL_X, POS_LBL_Y, POS_LAT, POS_LON)) | |||
# Satellites signal/noise ratio box. | |||
print('<rect class="box" x="%d" y="%d" rx="%d" ry="%d" ' | |||
'width="%d" height="%d" />' % | |||
(BARS_BOX_X, BARS_BOX_Y - BARS_BOX_HEIGHT, BAR_RADIUS, | |||
BAR_RADIUS, BARS_BOX_WIDTH, BARS_BOX_HEIGHT)) | |||
SS_LBL_X = int(BARS_BOX_X + STROKE_WIDTH * 1.5) | |||
SS_LBL_Y = int(BARS_BOX_Y - BARS_BOX_HEIGHT + LBL_FONT_SIZE + | |||
STROKE_WIDTH * 1.5) | |||
print('<text class="label label-snr" x="%d" y="%d">' | |||
'Satellites Signal/Noise Ratio</text>' % (SS_LBL_X, SS_LBL_Y)) | |||
# Box for horizontal and vertical estimated error. | |||
if 'epx' in TPV and 'epy' in TPV: | |||
EPX = float(TPV['epx']) | |||
EPY = float(TPV['epy']) | |||
EPH = math.sqrt(EPX**2 + EPY**2) | |||
elif 'eph' in TPV: | |||
EPH = float(TPV['eph']) | |||
else: | |||
EPH = ERR_MAX | |||
EPV = float(TPV['epv']) if 'epv' in TPV else ERR_MAX | |||
ERR_H, SIGN_H = cutoff_err(EPH, ERR_MIN, ERR_MAX) | |||
ERR_V, SIGN_V = cutoff_err(EPV, ERR_MIN, ERR_MAX) | |||
ERR_LBL_X = int(ERR_BOX_X - STROKE_WIDTH * 2.0) | |||
ERR_LBL_Y_OFFSET = STROKE_WIDTH + BAR_WIDTH * 0.6 | |||
print('<rect class="box" x="%d" y="%d" rx="%d" ry="%d" ' | |||
'width="%d" height="%d" />' % | |||
(ERR_BOX_X, ERR_BOX_Y - ERR_BOX_HEIGHT, BAR_RADIUS, | |||
BAR_RADIUS, ERR_BOX_WIDTH, ERR_BOX_HEIGHT)) | |||
# Horizontal error. | |||
POS_X = ERR_BOX_X + STROKE_WIDTH | |||
POS_Y = ERR_BOX_Y - ERR_BOX_HEIGHT + int((BAR_SPACE - BAR_WIDTH) * 0.5) | |||
ERR_H_BAR_HEIGHT = int(ERR_H / ERR_MAX * ERR_BAR_HEIGHT_MAX) | |||
print('<text class="label label-err" x="%d" y="%d">' | |||
'Horizontal error %s%.1f m</text>' % | |||
(ERR_LBL_X, ERR_LBL_Y_OFFSET + POS_Y, SIGN_H, ERR_H)) | |||
print('<rect class="bars bars-used" x="%d" y="%d" rx="%d" ' | |||
' ry="%d" width="%d" height="%d" />' % | |||
(POS_X, POS_Y, BAR_RADIUS, BAR_RADIUS, ERR_H_BAR_HEIGHT, BAR_WIDTH)) | |||
# Vertical error. | |||
POS_Y = POS_Y + BAR_SPACE | |||
ERR_V_BAR_HEIGHT = int(ERR_V / ERR_MAX * ERR_BAR_HEIGHT_MAX) | |||
print('<text class="label label-err" x="%d" y="%d">' | |||
'Vertical error %s%.1f m</text>' % | |||
(ERR_LBL_X, ERR_LBL_Y_OFFSET + POS_Y, SIGN_V, ERR_V)) | |||
print('<rect class="bars bars-used" x="%d" y="%d" rx="%d" ' | |||
' ry="%d" width="%d" height="%d" />' % | |||
(POS_X, POS_Y, BAR_RADIUS, BAR_RADIUS, ERR_V_BAR_HEIGHT, BAR_WIDTH)) | |||
# Satellites and Signal/Noise bars. | |||
i = 0 | |||
for prn in sorted(SATELLITES): | |||
sat = SATELLITES[prn] | |||
BAR_HEIGHT = int(BAR_HEIGHT_MAX * (float(sat['ss']) / SNR_MAX)) | |||
(sat_x, sat_y) = polar2cart(float(sat['az']), float(sat['el']), CIRCLE_R) | |||
sat_x = int(CIRCLE_X + sat_x) | |||
sat_y = int(CIRCLE_Y + sat_y) | |||
rect_radius = int(SAT_HEIGHT * 0.25) | |||
sat_rect_x = int(sat_x - (SAT_WIDTH) / 2) | |||
sat_rect_y = int(sat_y - (SAT_HEIGHT) / 2) | |||
sat_class = 'used' if sat['used'] else 'unused' | |||
print('<rect class="sats sats-%s" x="%d" y="%d" width="%d" ' | |||
' height="%d" rx="%d" ry="%d" />' % | |||
(sat_class, sat_rect_x, sat_rect_y, SAT_WIDTH, SAT_HEIGHT, | |||
rect_radius, rect_radius)) | |||
print('<text class="sats %s" x="%d" y="%d">%s</text>' % | |||
(sat_class, sat_x, sat_y + int(LBL_SAT_SIZE*0.4), sat['PRN'])) | |||
pos_x = (int(BARS_BOX_X + (STROKE_WIDTH * 0.5) + | |||
(BAR_SPACE - BAR_WIDTH) * 0.5 + BAR_SPACE * i)) | |||
pos_y = int(BARS_BOX_Y - BAR_HEIGHT - (STROKE_WIDTH * 1.5)) | |||
print('<rect class="bars bars-%s" x="%d" y="%d" rx="%d" ry="%d" ' | |||
'width="%d" height="%d" />' % | |||
(sat_class, pos_x, pos_y, BAR_RADIUS, BAR_RADIUS, | |||
BAR_WIDTH, BAR_HEIGHT)) | |||
x = int(pos_x + BAR_WIDTH * 0.5) | |||
y = int(BARS_BOX_Y + (STROKE_WIDTH * 1.5)) | |||
print('<text class="label label-prn" x="%d" y="%d" ' | |||
'transform="rotate(270, %d, %d)">%s</text>' % | |||
(x, y, x, y, sat['PRN'])) | |||
i = i + 1 | |||
print('<text class="label label-center" x="%d" y="%d">%s</text>' % | |||
(TIMESTAMP_X, TIMESTAMP_Y, TIME_STR)) | |||
print('</svg>') | |||
sys.exit(EXIT_CODE) |
@@ -3,7 +3,8 @@ | |||
"""webgps.py | |||
This is a Python port of webgps.c from http://www.wireless.org.au/~jhecker/gpsd/ | |||
This is a Python port of webgps.c | |||
from http://www.wireless.org.au/~jhecker/gpsd/ | |||
by Beat Bolli <me+gps@drbeat.li> | |||
It creates a skyview of the currently visible GPS satellites and their tracks | |||
@@ -38,17 +39,29 @@ restart webgps.py without losing accumulated satellite tracks. | |||
from __future__ import absolute_import, print_function, division | |||
import time, math, sys, os, pickle | |||
import math | |||
import os | |||
import pickle | |||
import sys | |||
import time | |||
from gps import * | |||
gps_version = '3.18.1' | |||
if gps.__version__ != gps_version: | |||
sys.stderr.write("webgps.py: ERROR: need gps module version %s, got %s\n" % | |||
(gps_version, gps.__version__)) | |||
sys.exit(1) | |||
TRACKMAX = 1024 | |||
STALECOUNT = 10 | |||
DIAMETER = 200 | |||
def polartocart(el, az): | |||
radius = DIAMETER * (1 - el / 90.0) # * math.cos(Deg2Rad(float(el))) | |||
radius = DIAMETER * (1 - el / 90.0) # * math.cos(Deg2Rad(float(el))) | |||
theta = Deg2Rad(float(az - 90)) | |||
return ( | |||
# Changed this back to normal orientation - fw | |||
@@ -77,7 +90,9 @@ class Track: | |||
def track(self): | |||
'''Return the track as canvas drawing operations.''' | |||
return 'M(%d,%d); ' % self.posn[0] + ''.join(['L(%d,%d); ' % p for p in self.posn[1:]]) | |||
return('M(%d,%d); ' % self.posn[0] + ''.join(['L(%d,%d); ' % | |||
p for p in self.posn[1:]])) | |||
class SatTracks(gps): | |||
'''gpsd client writing HTML5 and <canvas> output.''' | |||
@@ -108,15 +123,17 @@ class SatTracks(gps): | |||
\t\t<tr> | |||
\t\t\t<td> | |||
\t\t\t\t<table border=0 class=num> | |||
\t\t\t\t\t<tr><th>PRN:</th><th>Elev:</th><th>Azim:</th><th>SNR:</th><th>Used:</th></tr> | |||
\t\t\t\t\t<tr><th>PRN:</th><th>Elev:</th><th>Azim:</th><th>SNR:</th> | |||
<th>Used:</th></tr> | |||
""" % jsfile) | |||
sats = self.satellites[:] | |||
sats.sort(key=lambda x: x.PRN) | |||
for s in sats: | |||
fh.write("\t\t\t\t\t<tr><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%s</td></tr>\n" % ( | |||
s.PRN, s.elevation, s.azimuth, s.ss, s.used and 'Y' or 'N' | |||
)) | |||
fh.write("\t\t\t\t\t<tr><td>%d</td><td>%d</td><td>%d</td>" | |||
"<td>%d</td><td>%s</td></tr>\n" % | |||
(s.PRN, s.elevation, s.azimuth, s.ss, | |||
s.used and 'Y' or 'N')) | |||
fh.write("\t\t\t\t</table>\n\t\t\t\t<table border=0>\n") | |||
@@ -131,9 +148,12 @@ class SatTracks(gps): | |||
if self.fix.mode >= MODE_2D: | |||
row('Latitude', deg_to_str(self.fix.latitude, 'SN')) | |||
row('Longitude', deg_to_str(self.fix.longitude, 'WE')) | |||
row('Altitude', self.fix.mode == MODE_3D and "%f m" % self.fix.altitude or 'N/A') | |||
row('Speed', not isnan(self.fix.speed) and "%f m/s" % self.fix.speed or 'N/A') | |||
row('Course', not isnan(self.fix.track) and "%fยฐ" % self.fix.track or 'N/A') | |||
row('Altitude', self.fix.mode == MODE_3D and "%f m" % | |||
self.fix.altitude or 'N/A') | |||
row('Speed', isfinite(self.fix.speed) and "%f m/s" % | |||
self.fix.speed or 'N/A') | |||
row('Course', isfinite(self.fix.track) and "%fยฐ" % | |||
self.fix.track or 'N/A') | |||
else: | |||
row('Latitude', 'N/A') | |||
row('Longitude', 'N/A') | |||
@@ -141,34 +161,35 @@ class SatTracks(gps): | |||
row('Speed', 'N/A') | |||
row('Course', 'N/A') | |||
row('EPX', not isnan(self.fix.epx) and "%f m" % self.fix.epx or 'N/A') | |||
row('EPY', not isnan(self.fix.epy) and "%f m" % self.fix.epy or 'N/A') | |||
row('EPV', not isnan(self.fix.epv) and "%f m" % self.fix.epv or 'N/A') | |||
row('Climb', self.fix.mode == MODE_3D and not isnan(self.fix.climb) and | |||
"%f m/s" % self.fix.climb or 'N/A' | |||
) | |||
row('EPX', isfinite(self.fix.epx) and "%f m" % self.fix.epx or 'N/A') | |||
row('EPY', isfinite(self.fix.epy) and "%f m" % self.fix.epy or 'N/A') | |||
row('EPV', isfinite(self.fix.epv) and "%f m" % self.fix.epv or 'N/A') | |||
row('Climb', self.fix.mode == MODE_3D and isfinite(self.fix.climb) and | |||
"%f m/s" % self.fix.climb or 'N/A') | |||
state = "INIT" | |||
if not (self.valid & ONLINE_SET): | |||
newstate = 0 | |||
state = "OFFLINE" | |||
else: | |||
newstate = self.fix.mode | |||
if newstate == MODE_2D: | |||
state = self.status == STATUS_DGPS_FIX and "2D DIFF FIX" or "2D FIX" | |||
state = "2D FIX" | |||
elif newstate == MODE_3D: | |||
state = self.status == STATUS_DGPS_FIX and "3D DIFF FIX" or "3D FIX" | |||
state = "3D FIX" | |||
else: | |||
state = "NO FIX" | |||
if newstate != self.state: | |||
self.statetimer = time.time() | |||
self.state = newstate | |||
row('State', state + " (%d secs)" % (time.time() - self.statetimer)) | |||
row('State', "%s (%d secs)" % (state, time.time() - self.statetimer)) | |||
fh.write("""\t\t\t\t</table> | |||
\t\t\t</td> | |||
\t\t\t<td> | |||
\t\t\t\t<canvas id=satview width=425 height=425> | |||
\t\t\t\t\t<p>Your browser needs HTML5 <canvas> support to display the satellite view correctly.</p> | |||
\t\t\t\t\t<p>Your browser needs HTML5 <canvas> support to display | |||
the satellite view correctly.</p> | |||
\t\t\t\t</canvas> | |||
\t\t\t\t<script type='text/javascript'>draw_satview();</script> | |||
\t\t\t</td> | |||
@@ -227,14 +248,14 @@ function draw_satview() { | |||
// tracks | |||
ctx.lineWidth = 0.6; | |||
ctx.strokeStyle = 'red'; | |||
"""); | |||
""") | |||
# Draw the tracks | |||
for t in self.sattrack.values(): | |||
if t.posn: | |||
fh.write(" ctx.globalAlpha = %s; ctx.beginPath(); %sctx.stroke();\n" % ( | |||
t.stale == 0 and '0.66' or '1', t.track() | |||
)) | |||
fh.write(" ctx.globalAlpha = %s; ctx.beginPath(); " | |||
"%sctx.stroke();\n" % | |||
(t.stale == 0 and '0.66' or '1', t.track())) | |||
fh.write(""" | |||
// satellites | |||
@@ -261,7 +282,9 @@ function draw_satview() { | |||
fh.write("ctx.rect(%d, %d, 16, 16); " % (x - 8, y - 8)) | |||
else: | |||
fh.write("ctx.arc(%d, %d, 8, 0, circle, 0); " % (x, y)) | |||
fh.write("ctx.fill(); ctx.stroke(); ctx.strokeText('%s', %d, %d);\n" % (s.PRN, x - 6 + offset, y + 4)) | |||
fh.write("ctx.fill(); ctx.stroke(); " | |||
"ctx.strokeText('%s', %d, %d);\n" % | |||
(s.PRN, x - 6 + offset, y + 4)) | |||
fh.write(""" | |||
ctx.restore(); | |||
@@ -328,6 +351,7 @@ function draw_satview() { | |||
): | |||
break | |||
def main(): | |||
argv = sys.argv[1:] | |||
@@ -365,5 +389,6 @@ def main(): | |||
pickle.dump(sat.sattrack, p) | |||
p.close() | |||
if __name__ == '__main__': | |||
main() |
@@ -24,7 +24,7 @@ | |||
* requires this. | |||
* | |||
* This file is Copyright (c) 2008,2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#include <stdbool.h> | |||
#include <stdio.h> | |||
@@ -1,7 +1,7 @@ | |||
/* Interface for CRC-24Q cyclic redundancy chercksum code | |||
* | |||
* This file is Copyright (c) 2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#ifndef _CRC24Q_H_ | |||
#define _CRC24Q_H_ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* This file is Copyright (c) 2010 by the GPSD project | |||
* BSD terms apply: see the file COPYING in the distribution root for details. | |||
* SPDX-License-Identifier: BSD-2-clause | |||
*/ | |||
#include "gpsd.h" | |||
#if defined(DBUS_EXPORT_ENABLE) | |||
@@ -1 +1 @@ | |||
gpsd-3.17/../gps | |||
gpsd-3.18.1/../gps |
@@ -135,6 +135,7 @@ def main(starting_number_of_options=0): | |||
return failed_configurations | |||
if __name__ == '__main__': | |||
failed = main(0) | |||
for row in failed: | |||
@@ -63,7 +63,9 @@ | |||
# Preserve this property! | |||
from __future__ import absolute_import, print_function, division | |||
import sys, getopt | |||
import getopt | |||
import sys | |||
def correct_table(wfp): | |||
# Writes the corrected table. | |||
@@ -74,6 +76,7 @@ def correct_table(wfp): | |||
else: | |||
print(t.rstrip(), file=wfp) | |||
def make_driver_code(wfp): | |||
# Writes calls to bit-extraction macros. | |||
# Requires UBITS, SBITS, UCHARS to act as they do in the AIVDM driver. | |||
@@ -99,8 +102,8 @@ def make_driver_code(wfp): | |||
if not record: | |||
continue | |||
if ftype == 'x': | |||
print("\t/* skip %s bit%s */" % (width, ["", "s"][width>'1']), | |||
file=wfp) | |||
print("\t/* skip %s bit%s */" % (width, | |||
["", "s"][width > '1']), file=wfp) | |||
continue | |||
if ftype[0] == 'a': | |||
arrayname = name | |||
@@ -110,39 +113,44 @@ def make_driver_code(wfp): | |||
if explicit: | |||
lengthfield = last | |||
print(indent + "for (i = 0; i < %s; i++) {" % lengthfield, | |||
file=wfp) | |||
file=wfp) | |||
else: | |||
lengthfield = "n" + arrayname | |||
print(indent + "for (i = 0; ARRAY_BASE + (ELEMENT_SIZE*i) < bitlen; i++) {", file=wfp) | |||
print(indent + "for (i = 0; ARRAY_BASE + " | |||
"(ELEMENT_SIZE*i) < bitlen; i++) {", file=wfp) | |||
indent += step | |||
print(indent + "int a = ARRAY_BASE + (ELEMENT_SIZE*i);", | |||
file=wfp) | |||
file=wfp) | |||
continue | |||
offset = offsets[i].split('-')[0] | |||
if arrayname: | |||
target = "%s.%s[i].%s" % (structnme, arrayname, name) | |||
offset = "a + " + offset | |||
offset = "a + " + offset | |||
else: | |||
target = "%s.%s" % (structname, name) | |||
if ftype[0].lower() in ('u', 'i', 'e'): | |||
print(indent + "%s\t= %sBITS(%s, %s);" % \ | |||
(target, {'u':'U', 'e':'U', 'i':'S'}[ftype[0].lower()], offset, width), file=wfp) | |||
print(indent + "%s\t= %sBITS(%s, %s);" % | |||
(target, | |||
{'u': 'U', 'e': 'U', 'i': 'S'}[ftype[0].lower()], | |||
offset, width), file=wfp) | |||
elif ftype == 't': | |||
print(indent + "UCHARS(%s, %s);" % (offset, target), file=wfp) | |||
elif ftype == 'b': | |||
print(indent + "%s\t= (bool)UBITS(%s, 1);" % (target, offset), | |||
file=wfp) | |||
else: | |||
print(indent + "/* %s bits of type %s */" % (width,ftype), | |||
file=wfp) | |||
print(indent + "/* %s bits of type %s */" % | |||
(width, ftype), file=wfp) | |||
last = name | |||
if arrayname: | |||
indent = base | |||
print(indent + "}", file=wfp) | |||
if not explicit: | |||
print(indent + "%s.%s = ind;" % (structname, lengthfield), file=wfp) | |||
print("#undef ARRAY_BASE", file=wfp) | |||
print("#undef ELEMENT_SIZE", file=wfp) | |||
print(indent + "%s.%s = ind;" % | |||
(structname, lengthfield), file=wfp) | |||
print("#undef ARRAY_BASE", file=wfp) | |||
print("#undef ELEMENT_SIZE", file=wfp) | |||
def make_structure(wfp): | |||
# Write a structure definition correponding to the table. | |||
@@ -152,8 +160,10 @@ def make_structure(wfp): | |||
step = 4 | |||
inwards = step | |||
arrayname = None | |||
def tabify(n): | |||
return ('\t' * (n // 8)) + (" " * (n % 8)) | |||
return ('\t' * (n // 8)) + (" " * (n % 8)) | |||
print(tabify(baseindent) + "struct {", file=wfp) | |||
for (i, t) in enumerate(table): | |||
if '|' in t: | |||
@@ -177,14 +187,14 @@ def make_structure(wfp): | |||
ftype = ftype[1:] | |||
else: | |||
lengthfield = "n%s" % arrayname | |||
print(tabify(baseindent + inwards) | |||
+ "signed int %s;" % lengthfield, file=wfp) | |||
print(tabify(baseindent + inwards) + | |||
"signed int %s;" % lengthfield, file=wfp) | |||
if arrayname.endswith("s"): | |||
typename = arrayname[:-1] | |||
else: | |||
typename = arrayname | |||
print(tabify(baseindent + inwards) + "struct %s_t {" % typename, | |||
file=wfp) | |||
print(tabify(baseindent + inwards) + "struct %s_t {" % | |||
typename, file=wfp) | |||
inwards += step | |||
arraydim = ftype[1:] | |||
continue | |||
@@ -208,9 +218,10 @@ def make_structure(wfp): | |||
if "->" in structname: | |||
typename = structname.split("->")[1] | |||
if "." in typename: | |||
structname = structname.split(".")[1] | |||
structname = structname.split(".")[1] | |||
print(tabify(baseindent) + "} %s;" % typename, file=wfp) | |||
def make_json_dumper(wfp): | |||
# Write the skeleton of a JSON dump corresponding to the table. | |||
# Also, if there are subtables, some initializers | |||
@@ -223,13 +234,16 @@ def make_json_dumper(wfp): | |||
value = value[:-10] | |||
wfp.write(' "%s",\n' % value) | |||
wfp.write(" };\n") | |||
wfp.write('#define DISPLAY_%s(n) (((n) < (unsigned int)NITEMS(%s_vocabulary)) ? %s_vocabulary[n] : "INVALID %s")\n' % (name.upper(), name, name, name.upper())) | |||
wfp.write('#define DISPLAY_%s(n) (((n) < ' | |||
'(unsigned int)NITEMS(%s_vocabulary)) ? ' | |||
'%s_vocabulary[n] : "INVALID %s")\n' % | |||
(name.upper(), name, name, name.upper())) | |||
wfp.write("\n") | |||
record = after is None | |||
# Elements of each tuple type except 'a': | |||
# 1. variable name, | |||
# 2. unscaled printf format | |||
# 3. wrapper for unscaled variable reference | |||
# 3. wrapper for unscaled variable reference | |||
# 4. scaled printf format | |||
# 5. wrapper for scaled variable reference | |||
# Elements of 'a' tuple: | |||
@@ -306,30 +320,33 @@ def make_json_dumper(wfp): | |||
sys.exit(1) | |||
last = name | |||
startspan = 0 | |||
def scaled(i): | |||
return tuples[i][3] is not None | |||
def tslice(e, i): | |||
return [x[i] for x in tuples[startspan:e+1]] | |||
base = " " * 8 | |||
step = " " * 4 | |||
inarray = None | |||
header = "(void)snprintf(buf + strlen(buf), buflen - strlen(buf)," | |||
for (i, (var, uf, uv, sf, sv)) in enumerate(tuples): | |||
if uf == None: | |||
if uf is not None: | |||
print(base + "for (i = 0; i < %s.%s; i++) {" % (structname, sv), | |||
file=wfp) | |||
inarray = var | |||
base = " " * 12 | |||
startspan = i+1 | |||
continue | |||
continue | |||
# At end of tuples, or if scaled flag changes, or if next op is array, | |||
# flush out dump code for a span of fields. | |||
if i+1 == len(tuples): | |||
endit = '}",' | |||
elif tuples[i+1][1] == None: | |||
elif tuples[i+1][1] is not None: | |||
endit = r',\"%s\":[",' % tuples[i+1][0] | |||
elif scaled(i) != scaled(i+1): | |||
endit = ',",' | |||
elif scaled(i) != scaled(i + 1): | |||
endit = ',",' | |||
else: | |||
endit = None | |||
if endit: | |||