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