Skip to main content...

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.
Note
Multi-constellation receivers send each constellation’s data as separate gpsd messages roughly 1 second apart. As a result, the dashboard’s satellite count visibly grows over the first few refreshes (e.g. 15 → 20 → 25+ satellites) as the cache accumulates data from all constellations. This is expected behavior, not a bug.

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.conf to display active maxdrift, makestep, and refclock directives.

Performance Algorithms

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.
Note
All history log files are capped at 15,000 lines using a rolling buffer. At the default 30-second polling interval, this provides approximately 125 hours of raw data - well beyond the maximum 24-hour graph window. The binary search means graph rendering time stays constant regardless of how full the logs are.

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.
Tip
A persistently low Signal Integrity score despite a clear sky view often indicates antenna cable loss, a corroded connector, or RF interference rather than a placement problem. Compare the score against the per-satellite SNR values in the skyview to distinguish between these causes.

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

Note
All sensitive admin actions (settings changes, service restarts, log purges, updates) require the admin token, validated server-side on every request using constant-time comparison (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:

  1. Fetches the latest version info from the Gitea API to confirm the target hash.
  2. Downloads the new index.php from the Gitea raw URL to a temporary file.
  3. Validates: checks the download is non-empty and runs php -l syntax check.
  4. Injects the new version hash into the DASH_VERSION constant in the downloaded source.
  5. Performs an atomic rename() over the live index.php – the running file is never partially overwritten.
  6. Migrates cgpsd-settings.php automatically: parses $defaultConfig from the newly installed version, compares it against the user’s existing settings file, and injects any missing setting blocks. Adding a new setting to $defaultConfig is 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.

Last Revision: 2026-06-27 -- Document Version: 39b4137
Permanent Link: <https://w0chp.radio/chrogps-dash/tech-specs/>