1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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.
|