N-ADR-003: GNSS Degradation & Timestamp Authority
Decision record for GNSS degradation handling, fallback clock hierarchy, and timestamp quality flags.
Metadata
- ADR ID: N-ADR-003
- Title: Timestamp Authority During GNSS Degradation & Fallback Clock Hierarchy
- Status: Proposed
- Date: 2026-03-15 (proposed)
- Owner: Node firmware lead
- Target Decision Date: 2026-04-10
- Relates to: Node-Gaps-Deferred#timestamp-authority-during-gnss-degradation
Problem / Context
The node acquires GNSS timing from a receiver (Node-Hardware-Interface). However, GNSS may degrade in real-world scenarios (urban canyon, launch pad under building, jamming). The architecture must define fallback behavior:
- Degradation detection: How does node know GNSS is degraded? (confidence metric, loss of lock duration?)
- Fallback chain: If GNSS unavailable, what's the next clock source? (central NTP, system clock, degraded flag?)
- Timestamp quality: Can downstream consumers distinguish GNSS-sourced vs. fallback timestamps?
- Coexistence: If GNSS signal is weak (low SNR in time solution), do we use it anyway or prefer fallback?
- Notification: How does node alert central that GNSS is degraded?
- Recovery: When GNSS becomes available again, how does node resynchronize?
Current State
- Node-Hardware-Interface specifies GNSS receiver hardware but no degradation strategy
- ADR-003: Clock Authority discusses system-level time synchronization but leaves node-level fallback open
- Prototypes assume GNSS always available
Why This Matters
- Robustness: Missions can occur indoors, under structures, or in RF-constrained environments
- Auditability: Post-mission analysis depends on knowing timestamp reliability
- Trajectory accuracy: Bad timestamps propagate to trajectory reconstruction errors
- Multi-target sync: If one target loses GNSS and others don't, synchronization skew grows
Deferred Decision Options
Option A: GNSS-Primary with System-Clock Fallback (Conservative)
Approach: Node uses GNSS as primary time source. If GNSS lock is lost for >10s, fall back to system clock. Mark frames with degradation flag.
Clock hierarchy:
- GNSS (when locked and SNR >threshold)
- System clock (when GNSS unavailable)
- Last-good GNSS time + drift estimate (if central NTP not available)
Degradation detection:
- GNSS loss of lock (receiver stops outputting valid fixes)
- SNR low (<X dB?) even if technically locked
- Central NTP is >10 msec away (clock slipping)
Pros:
- Simple: two time sources, clear priority
- Conservative: prefer GNSS (more accurate) when available
- Fallback is always available (system clock)
Cons:
- System clock drifts ~50ppm (0.2 sec/hour) → unacceptable over long missions
- No central NTP as backup (missing hybrid approach of ADR-003)
- If GNSS goes bad, operator may not notice (no alert unless checking logs)
Implementation:
import time
from datetime import datetime, timedelta
class TimestampAuthority:
def __init__(self):
self.gnss_locked = False
self.last_gnss_time = None
self.gnss_loss_start = None
def update_gnss(self, gnss_time_ns, lock_status, snr_db):
"""Update GNSS status from receiver."""
if lock_status and snr_db > 20: # Locked and good SNR
self.gnss_locked = True
self.last_gnss_time = gnss_time_ns
self.gnss_loss_start = None
else:
if self.gnss_locked:
self.gnss_loss_start = time.time_ns()