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 21 themes), and frontend JavaScript.
- Backend: Pure PHP (8.2+).
- 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.
Internal Logic & Heuristics
Antenna Health Score
A proprietary heuristic calculates a 0-100% “Signal Integrity” score.
- Logic: It analyzes the SNR (dBHz) of active satellites, applying weights based on signal strength.
- Noise Filtering: Low-horizon satellites (prone to multipath interference) are automatically filtered out to ensure the score reflects the actual quality of the antenna installation.
Sync Precision Score
A logarithmic score that quantifies server timing quality. It normalizes the range from sub-microsecond PPS (Pulse-Per-Second) precision to standard millisecond-level WAN peer synchronization.
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 Gitea 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 Gitea API to confirm the target hash.
- Downloads the new
index.phpfrom the Gitea 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. - 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.
Each SSE event is a JSON object: {"type": "step"|"ok"|"error"|"done", "msg": "..."}. If any step fails, an error event is emitted and the existing install is left untouched.
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.