1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// SPDX-License-Identifier: Apache-2.0
//! Spoofing-detection security figure of merit.
//!
//! A time-spoofing attack feeds the receiver a false GNSS timing signal that
//! slowly drags its clock solution. The classical defence is a *clock-aided
//! integrity monitor*: the receiver predicts time forward from its own clock and
//! cross-checks the GNSS-derived time against that prediction, flagging a fault
//! when the two disagree by more than a few sigma.
//!
//! This is a **single-clock consistency monitor**, NOT a multi-satellite RAIM
//! detector: there are no pseudorange residuals across several satellites, no
//! protection level, and no probability of hazardously misleading information.
//! The innovation-vs-sigma test is mathematically the same shape as the fault
//! detection in Brown, "A baseline GPS RAIM scheme" (and Groves, *Principles of
//! GNSS, Inertial, and Multisensor Integrated Navigation Systems*, integrity
//! chapter), but the score it produces is an analytic *spoof-detectability
//! bound* for a given clock — not an implementation of RAIM. Real multi-SV
//! RAIM/ARAIM with HPL/VPL is a roadmap item; see `docs/INTEGRITY.md`.
//!
//! Over a coherent monitoring window of length `tau`, the comparison uncertainty
//! has two independent contributions:
//!
//! ```text
//! sigma_mon^2(tau) = r / m + q_wf * tau + q_rw * tau^3 / 3
//! ```
//!
//! * `r / m` — the GNSS phase-measurement noise (variance `r`) beaten down by
//! averaging the `m` samples taken within the window.
//! * `q_wf * tau + q_rw * tau^3 / 3` — the clock's own coast uncertainty over the
//! window, exactly the holdover error growth (white-FM plus random-walk-FM,
//! NIST SP 1065). A *better* clock makes this term smaller.
//!
//! With enough averaging the measurement term shrinks and the clock stability sets
//! the floor — the regime in which a superior clock detects a smaller, slower
//! spoof. The smallest spoof offset the monitor flags is `k * sigma_mon(tau)` for
//! a detection multiplier `k` (sigmas, chosen for a low false-alarm rate).
//!
//! The security score is this detection floor expressed relative to the
//! operational timing spec: a spoof only matters if it can move the solution by
//! about the spec threshold, so
//!
//! ```text
//! security = clamp(1 - min_detectable_offset_ns / threshold_ns, 0, 1).
//! ```
//!
//! `1` means a harmful (spec-threshold) spoof sits far above the detection floor
//! and is always caught; `0` means a spoof can reach the operational threshold
//! while staying under the detection floor — undetectable and harmful.
//!
//! All functions are pure and deterministic.
/// Detection multiplier (sigmas) for a low false-alarm-rate spoof monitor (~5σ).
pub const SPOOF_DETECT_K: f64 = 5.0;
/// Coherent spoof-monitoring window (s): the interval over which the GNSS-derived
/// time is cross-checked against the clock's own coasted prediction.
pub const SPOOF_MONITOR_S: f64 = 600.0;
/// 1-sigma floor (s) of the clock-aided spoof monitor over a window `tau` (s),
/// with per-sample phase-measurement variance `r` (s^2) averaged over `samples`
/// observations and the clock's white-FM / random-walk-FM PSDs `q_wf`, `q_rw`:
///
/// `sigma = sqrt(r / samples + q_wf * tau + q_rw * tau^3 / 3)`.
///
/// `samples` is clamped to at least one observation.
/// Smallest time-spoof offset (ns) the monitor flags over the window: the
/// detection multiplier `k` times the monitor's 1-sigma floor.
/// Spoof-detection security score in `[0, 1]` relative to the timing spec.
///
/// The monitor averages `m = round(tau / dt)` GNSS samples over the window.
/// Returns `1 - min_detectable_offset_ns / threshold_ns`, clamped to `[0, 1]`.
/// Higher is better; a non-positive `threshold_ns` yields `0`.