ChroGPS Dash Technical Specifications
ChroGPS Dash is engineered with a “zero-dependency” philosophy, ensuring maximum performance and long-term maintainability. This page details the internal architecture, data processing logic, and technical design decisions that power the dashboard.
Architecture Overview
ChroGPS Dash is a 100% self-contained application, packaged as a single index.php file. This includes all backend logic, CSS (supporting 22 themes), and frontend JavaScript.
- Backend: Pure PHP (8.1+).
- Frontend: Vanilla JavaScript (ES6+) and CSS3.
- Dependencies: None. No external charting libraries (e.g., Chart.js), no external fonts (Google Fonts), and no trackers.
- Minification: Built-in server-side minifier that strips whitespace and comments from the HTML/CSS/JS buffer before it is sent to the client, reducing payload size by ~27%.
For a deeper look at how the file is structured, how requests are routed, and how data flows from hardware to browser, see the Architecture page.
Data Acquisition & Processing
The dashboard interacts with system services using a combination of low-level sockets and command-line utilities.
GPSd Interaction
Data is retrieved from gpsd via a JSON-RPC socket connection (localhost:2947). The dashboard uses the ?WATCH and ?POLL commands to gather:
- SKY/TPV messages: For satellite positions (azimuth/elevation), signal strength (SNR), and time-position-velocity data.
- Satellite Batching: Logic is implemented to merge incremental SKY messages into a complete GNSS epoch, preventing “ghosting” when receivers report constellations in staggered batches.
- Persistent Cache: A local JSON cache (
/var/tmp/cgpsd-sat-cache.json) preserves satellite metadata across connections to maintain a stable skyview.
Chrony Integration
NTP metrics are gathered using chronyc commands (executed via sudo with scoped permissions):
chronyc tracking: For core sync metrics (Offset, RMS Offset, Frequency).chronyc sources: For real-time peer status and reachability.chronyc serverstats: For infrastructure monitoring.- Configuration Parsing: The dashboard directly parses
/etc/chrony/chrony.confto display activemaxdrift,makestep, andrefclockdirectives.
Performance Algorithms
Efficient Log Traversal (Binary Search)
To handle 24-hour history windows without significant CPU or memory overhead, ChroGPS Dash uses a specialized Binary Search algorithm on its log files.
- O(log n) Seeks: Instead of reading full log files (which can grow to thousands of lines), the engine “seeks” to the exact timestamp cutoff.
- Convergence: The algorithm typically finds the correct starting point in ~20 iterations, regardless of log file size.
Custom SVG Graphing Engine
Graphs are generated entirely on the server as raw SVG paths. This eliminates the need for heavy client-side JavaScript libraries.
- Dual-Axis Support: Allows simultaneous plotting of unrelated metrics (e.g., Offset vs. Frequency).
- Automatic Scaling: The engine automatically scales units (ns → µs → ms → s) based on the data range.
- Subtle Fills & Zero-Lines: Dynamic detection of zero-crossings for accurate visual representation of offset drift.
- Ten Graphs Total: PPS Offset, System Performance, Clock Stability, Frequency Steering, Root Dispersion, NTP Measurements, Satellite Visibility, GPS Signal SNR Trend, DOP History, and Satellite SNR by Constellation (one line per constellation, plotted over time).
Internal Logic & Heuristics
Antenna Health Score
A 0-100% “Signal Integrity” score derived from live satellite SNR.
- Logic: Averages the SNR (dBHz) of every satellite GPSd currently includes in the position fix (
used: true), then linearly maps that average onto a 15-38 dBHz scale (15 dBHz -> 0%, 38 dBHz -> 100%). - Implicit Filtering: Only satellites GPSd has already selected for the fix are counted – weak or poor-geometry satellites GPSd excludes don’t drag the score down. This filtering comes from GPSd’s own fix-selection logic, not an explicit horizon/elevation filter in ChroGPS Dash.
Sync Precision Score
A tiered score that quantifies server timing quality, based on the absolute clock offset from Chrony’s tracking data. The score steps down across log-spaced offset bands: 100% (< 1 µs), 95% (< 10 µs), 90% (< 100 µs), 80% (< 1 ms), 60% (< 10 ms), 40% (< 100 ms), 20% (>= 100 ms). This normalizes the range from sub-microsecond PPS (Pulse-Per-Second) precision to standard millisecond-level WAN peer synchronization onto a single 0-100% scale.
Solar Tracking
Calculates the Sun’s current Azimuth and Elevation using pure client-side JavaScript. This allows users to correlate SNR drops with solar interference (e.g., the sun passing behind a satellite) without external API dependencies.
Security & Operations
hash_equals). The token is never transmitted in URLs – only via HTTP POST.
- Admin Panel: Token-protected configuration management.
- Dashboard Authentication Gate: An optional full-dashboard lock screen (
DASHBOARD_REQUIRE_AUTH) that presents an authentication prompt to unauthenticated visitors before any page content or data is served. Uses an HMAC session cookie (HttpOnly,SameSite=Strict) derived from the admin token - regenerating the token invalidates all existing sessions automatically. The gate is enforced at both the HTML page level and every AJAX data endpoint server-side. The systemd poller timer (chrogps-poller.service) is exempt - localhost requests always bypass the data gate so history logging continues uninterrupted. - Pi Throttling Diagnostics: For Raspberry Pi hardware, the dashboard performs deep bitmask parsing of
vcgencmd get_throttled. This distinguishes between current issues (Under-voltage, Throttling) and historical events that occurred since the last boot - critical for diagnosing frequency instability in Stratum 1 environments. - One-Click Updates: Real-time progress streaming using Server-Sent Events (SSE) and atomic file replacement. See Update Notifications & One-Click Updater below for the full mechanism.
- Privacy: Optional IP masking for connected NTP clients (masking octets of IPv4 and hextets of IPv6).
- Binary Utilities: Uses scoped helper scripts in
/usr/local/bin/to allow the web user (www-data) to restart services or purge logs safely without full root access.
Update Notifications & One-Click Updater
When CHECK_UPDATES is enabled in settings, ChroGPS Dash periodically checks for newer versions and provides a one-click path to apply them from the browser – no SSH or manual file transfer required.
Version Check & Notification
The frontend calls ?ajax=version, which is a server-side proxy to the Git repository tree API. The server extracts the blob SHA of index.php from the API response and returns the short (10-character) hash. Using a server-side proxy avoids browser CORS restrictions and keeps the remote API URL out of page source.
Version checks are throttled to once every 8 hours via three localStorage keys:
| Key | Contents |
|---|---|
cgpsd-update-check-time |
Timestamp (ms) of the last API call |
cgpsd-update-latest |
Latest remote hash from the last check |
cgpsd-update-current |
Installed version hash at the time of the last check |
checkVersion() fires 2 seconds after page load – delayed so it never blocks initial rendering – and is then re-invoked every hour. The 8-hour gate inside the function prevents redundant API calls; the hourly setInterval exists only to restore the update pill from cache immediately if the page has been open across an 8-hour boundary.
On every page load, persisted state is read first: if a previous check found an update and the installed version has not changed, the update pill appears immediately without a new API call. If the installed version has changed (e.g., an update was just applied), all three keys are cleared automatically.
When a newer version is detected, an update pill appears in the footer showing the current and available version hashes. Clicking it opens the one-click updater panel in the admin area.
One-Click Updater
The ?ajax=do_update endpoint (POST, admin token required) streams real-time progress to the browser using Server-Sent Events (SSE). The updater executes these steps in order, emitting a JSON event for each:
- Fetches the latest version info from the Git API to confirm the target hash.
- Downloads the new
index.phpfrom the Git raw URL to a temporary file. - Validates: checks the download is non-empty and runs
php -lsyntax check. - Injects the new version hash into the
DASH_VERSIONconstant in the downloaded source. - Performs an atomic
rename()over the liveindex.php– the running file is never partially overwritten. Immediately after, it flushes PHP’s OPcache (opcache_reset()) so the rest of the request executes the new code instead of the old file’s cached bytecode. - Migrates
cgpsd-settings.phpautomatically: parses$defaultConfigfrom the newly installed version, compares it against the user’s existing settings file, and injects any missing setting blocks. Adding a new setting to$defaultConfigis all that is required for it to propagate to all installations on the next update. - Purges obsolete settings: removes
$ENABLE_SAT_HISTORYand$ENABLE_PPS_HISTORYfrom the settings file (both are now auto-detected from data-file existence instead), and touches any rolling history-log data files that are missing. - Infrastructure health check: confirms
chrogps-poller.timeris active. This step is advisory only – it warns rather than fails, since the update has already completed successfully by this point.
Each SSE event is a JSON object: {"type": "step"|"ok"|"error"|"done", "msg": "..."}. If any of steps 1-7 fails, an error event is emitted and the existing install is left untouched; step 8 never fails the update, it only surfaces a warning.
Stale Page Detection
Every ?ajax=1 response includes a server_version key containing the live DASH_VERSION. If a browser tab is open when an update completes (from a different tab, or the CLI), the next poll detects data.server_version !== currentVersion, hides the update pill, clears the localStorage update cache, and displays a toast prompting the user to reload.