diff options
| author | Jason McCormick <jason@mfamily.org> | 2026-11-06 19:10:44 -0500 |
|---|---|---|
| committer | Jason McCormick <jason@mfamily.org> | 2026-11-06 19:10:44 -0500 |
| commit | 4ab66536b650e608039c0c3a205da897cb8bd2c5 (patch) | |
| tree | 2bc890404b0ff33907383b64acc231cb0a777653 /README.md | |
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..afbadef --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# gps-clock-dash + +A lightweight live dashboard for a GPS + PPS time server running chrony and gpsd. + +One static Go binary (about 5 MB, standard library only) runs on the time server. +Once a second it polls `chronyc -c tracking | sources | sourcestats`, keeps a +connection open to gpsd's JSON stream, and pushes a combined snapshot to +browsers over Server-Sent Events. The web page (HTML, CSS and vanilla JS, with +no build step and no external requests) is built into the binary. + +## Panels + +- **Clock**: UTC to the millisecond, local time, day of year, MJD, Unix time, + GPS week and time of week, leap status, and this browser's offset from the + server (measured with an NTP-style round trip to `api/time`). Also an overall + health badge: *Locked to PPS*, *Degraded* or *Not synchronised*, with reasons. +- **Time quality**: system offset, RMS offset, reference, stratum, frequency, + residual frequency, skew, root delay and dispersion, update interval. +- **Sources**: every chrony source with its selection state, poll interval, + 8-bit reach register, last sample offset ± error bound, and σ (standard deviation). +- **History**: system offset and PPS sample offset, frequency (ppm), and + satellites used/in view, over 10, 30 or 60 minutes (kept in memory by the server). +- **GPS receiver**: fix type, latitude and longitude (4 decimal places), + altitude, position error, DOPs, estimated NMEA time error, serial latency + (gpsd TOFF), and receiver/driver. +- **Satellites**: sky plot (azimuth/elevation, filled when used in the fix), + counts per constellation, and signal strength per satellite. + +## Build & deploy + +```sh +make arm64 # build/gps-clock-dash-arm64 for the Pi +make deploy # scp + install to /usr/local/bin + systemd unit on yttrium (needs sudo) +``` + +The service listens on **:8473**. + +| Flag | Default | | +|---|---|---| +| `-listen` | `:8473` | HTTP listen address | +| `-gpsd` | `127.0.0.1:2947` | gpsd address | +| `-chronyc` | `chronyc` | path to chronyc | +| `-pps-refid` | `GPS` | chrony refid of the PPS refclock (used for health and the history chart) | +| `-history` | `3600` | seconds of history to keep | + +### Endpoints + +`/` dashboard · `/events` SSE stream · `/api/status` latest snapshot · +`/api/history` history samples · `/api/time` server time · `/healthz` + +### Behind nginx + +All URLs in the page are relative, so it works at any sub-path. See +[deploy/nginx-location.conf](deploy/nginx-location.conf). The important parts +for SSE are `proxy_buffering off` and a long `proxy_read_timeout`. The server +also sends `X-Accel-Buffering: no`. Once nginx is in front, switch the unit to +`-listen 127.0.0.1:8473` (or the LAN address nginx connects to). + +### NTP client request rate (optional) + +`chronyc serverstats` needs chronyd's privileged socket, so the unit runs +unprivileged and that row stays hidden. To show it, run the service as +`_chrony` (see the comments in the unit file). The tradeoff is that the +process could then issue any chronyc command. |
