use crate::clock_state::ClockClass;
use crate::holdover::{holdover_seconds, phase_to_range_m, QuantumClockClass};
use crate::inertial::quantum_imu::QuantumNavBudget;
use crate::types::Seconds;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct QParams {
pub q_wf: f64,
pub q_rw: f64,
pub q_drift: f64,
}
impl QParams {
pub fn coast_sigma_s(&self, t: Seconds) -> Seconds {
crate::holdover::coast_phase_sigma(self.q_wf, self.q_rw, self.q_drift, t)
}
pub fn holdover_s(&self, threshold_s: Seconds) -> Seconds {
holdover_seconds(self.q_wf, self.q_rw, self.q_drift, threshold_s)
}
}
fn solve_linear(mut a: Vec<Vec<f64>>, mut b: Vec<f64>) -> Option<Vec<f64>> {
let n = b.len();
for col in 0..n {
let mut piv = col;
for r in (col + 1)..n {
if a[r][col].abs() > a[piv][col].abs() {
piv = r;
}
}
if a[piv][col].abs() < 1e-300 {
return None;
}
a.swap(col, piv);
b.swap(col, piv);
let pivot = a[col].clone();
for r in (col + 1)..n {
let f = a[r][col] / pivot[col];
for (c, val) in a[r].iter_mut().enumerate().skip(col) {
*val -= f * pivot[c];
}
b[r] -= f * b[col];
}
}
let mut x = vec![0.0; n];
for i in (0..n).rev() {
let mut s = b[i];
for c in (i + 1)..n {
s -= a[i][c] * x[c];
}
x[i] = s / a[i][i];
}
Some(x)
}
fn ls_subset(rows: &[[f64; 3]], y: &[f64], subset: &[usize]) -> Option<Vec<f64>> {
let k = subset.len();
let mut scale = vec![0.0; k];
for (j, &col) in subset.iter().enumerate() {
let s: f64 = rows.iter().map(|r| r[col] * r[col]).sum::<f64>().sqrt();
scale[j] = if s > 0.0 { 1.0 / s } else { 1.0 };
}
let mut ata = vec![vec![0.0; k]; k];
let mut aty = vec![0.0; k];
for (i, r) in rows.iter().enumerate() {
for a in 0..k {
let va = r[subset[a]] * scale[a];
aty[a] += va * y[i];
for b in 0..k {
ata[a][b] += va * r[subset[b]] * scale[b];
}
}
}
let sol = solve_linear(ata, aty)?;
Some(sol.iter().enumerate().map(|(j, &c)| c * scale[j]).collect())
}
pub fn qparams_from_adev_curve(taus: &[f64], adevs: &[f64]) -> QParams {
let pts: Vec<(f64, f64)> = taus
.iter()
.zip(adevs.iter())
.filter(|(&t, &s)| t > 0.0 && s.is_finite() && s >= 0.0)
.map(|(&t, &s)| (t, s))
.collect();
if pts.len() < 2 {
return QParams {
q_wf: 0.0,
q_rw: 0.0,
q_drift: 0.0,
};
}
let rows: Vec<[f64; 3]> = pts.iter().map(|&(t, _)| [1.0 / t, t, t * t * t]).collect();
let y: Vec<f64> = pts.iter().map(|&(_, s)| s * s).collect();
let subsets: [&[usize]; 7] = [&[0], &[1], &[2], &[0, 1], &[0, 2], &[1, 2], &[0, 1, 2]];
let mut best: Option<([f64; 3], f64)> = None;
for sub in subsets {
if let Some(coef) = ls_subset(&rows, &y, sub) {
if coef.iter().any(|&c| c < -1e-300) {
continue; }
let mut full = [0.0f64; 3];
for (j, &idx) in sub.iter().enumerate() {
full[idx] = coef[j].max(0.0);
}
let resid: f64 = rows
.iter()
.zip(y.iter())
.map(|(r, &yi)| {
let pred = r[0] * full[0] + r[1] * full[1] + r[2] * full[2];
(pred - yi) * (pred - yi)
})
.sum();
if best.map_or(true, |(_, br)| resid < br) {
best = Some((full, resid));
}
}
}
let c = best.map(|(c, _)| c).unwrap_or([0.0, 0.0, 0.0]);
QParams {
q_wf: c[0],
q_rw: 3.0 * c[1],
q_drift: 20.0 * c[2],
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Provenance {
AssumedFloor,
MeasuredAdev,
}
#[derive(Clone, Copy, Debug)]
pub enum ClockSpec {
Classical(ClockClass),
Quantum(QuantumClockClass),
Measured(QParams),
}
impl ClockSpec {
pub fn qparams(self) -> QParams {
match self {
ClockSpec::Classical(c) => {
let (q_wf, q_rw, q_drift) = c.psds();
QParams {
q_wf,
q_rw,
q_drift,
}
}
ClockSpec::Quantum(c) => {
let (q_wf, q_rw, q_drift) = c.psds();
QParams {
q_wf,
q_rw,
q_drift,
}
}
ClockSpec::Measured(q) => q,
}
}
pub fn provenance(self) -> Provenance {
match self {
ClockSpec::Measured(_) => Provenance::MeasuredAdev,
_ => Provenance::AssumedFloor,
}
}
pub fn holdover_s(self, threshold_s: Seconds) -> Seconds {
self.qparams().holdover_s(threshold_s)
}
}
pub trait PositionDrift {
fn drift_m(&self, t: f64) -> f64;
fn inertial_holdover_s(&self, threshold_m: f64) -> f64 {
if threshold_m <= 0.0 {
return 0.0;
}
if self.drift_m(1.0) == 0.0 && self.drift_m(1.0e6) == 0.0 {
return f64::INFINITY;
}
let (mut lo, mut hi) = (0.0f64, 1.0f64);
let mut guard = 0;
while self.drift_m(hi) < threshold_m {
hi *= 2.0;
guard += 1;
if guard > 60 {
return f64::INFINITY;
}
}
for _ in 0..100 {
let mid = 0.5 * (lo + hi);
if self.drift_m(mid) < threshold_m {
lo = mid;
} else {
hi = mid;
}
}
0.5 * (lo + hi)
}
}
impl PositionDrift for QuantumNavBudget {
fn drift_m(&self, t: f64) -> f64 {
self.position_drift_1sigma(t)
}
fn inertial_holdover_s(&self, threshold_m: f64) -> f64 {
self.holdover_seconds(threshold_m)
}
}
#[derive(Clone, Copy, Debug)]
pub struct ClassicalInsBudget {
pub bias_m_s2: f64,
pub scale_factor_ppm: f64,
pub ref_accel_m_s2: f64,
pub vrw_psd: f64,
}
impl PositionDrift for ClassicalInsBudget {
fn drift_m(&self, t: f64) -> f64 {
let b = 0.5 * self.bias_m_s2 * t * t;
let sf = 0.5 * (self.scale_factor_ppm * 1e-6) * self.ref_accel_m_s2 * t * t;
let vrw2 = self.vrw_psd * t.max(0.0).powi(3) / 3.0;
(b * b + sf * sf + vrw2).sqrt()
}
}
#[derive(Clone, Debug)]
pub struct TradeRow {
pub label: String,
pub timing_holdover_s: f64,
pub inertial_holdover_s: f64,
pub floor_assumed: bool,
}
#[derive(Clone, Debug)]
pub struct TradeResult {
pub timing_threshold_s: f64,
pub position_threshold_m: f64,
pub baseline: TradeRow,
pub candidate: TradeRow,
pub timing_benefit_x: f64,
pub inertial_benefit_x: f64,
pub floor_caveat: Option<String>,
}
impl TradeResult {
pub fn provenance_banner(&self) -> &'static str {
"MODELLED — quantifies a partner benefit; not validated, no flight heritage"
}
}
fn ratio_x(candidate: f64, baseline: f64) -> f64 {
if baseline.is_infinite() {
if candidate.is_infinite() {
1.0
} else {
0.0
}
} else if baseline <= 0.0 {
if candidate > 0.0 {
f64::INFINITY
} else {
0.0
}
} else {
candidate / baseline
}
}
pub fn quantum_vs_classical_trade(
timing_threshold_s: f64,
position_threshold_m: f64,
baseline_clock: ClockSpec,
candidate_clock: ClockSpec,
baseline_ins: &dyn PositionDrift,
candidate_ins: &dyn PositionDrift,
) -> TradeResult {
let b_t = baseline_clock.holdover_s(timing_threshold_s);
let c_t = candidate_clock.holdover_s(timing_threshold_s);
let b_i = baseline_ins.inertial_holdover_s(position_threshold_m);
let c_i = candidate_ins.inertial_holdover_s(position_threshold_m);
let assumed = baseline_clock.provenance() == Provenance::AssumedFloor
|| candidate_clock.provenance() == Provenance::AssumedFloor;
let caveat = if assumed {
Some(
"Holdover to a tight threshold for a very stable clock is governed by \
the ASSUMED long-tau red-noise floor, not the cited σ_y(1 s). Ingest a \
measured ADEV curve (qparams_from_adev_curve) for a defensible number."
.to_string(),
)
} else {
None
};
TradeResult {
timing_threshold_s,
position_threshold_m,
baseline: TradeRow {
label: "classical baseline".into(),
timing_holdover_s: b_t,
inertial_holdover_s: b_i,
floor_assumed: baseline_clock.provenance() == Provenance::AssumedFloor,
},
candidate: TradeRow {
label: "quantum candidate".into(),
timing_holdover_s: c_t,
inertial_holdover_s: c_i,
floor_assumed: candidate_clock.provenance() == Provenance::AssumedFloor,
},
timing_benefit_x: ratio_x(c_t, b_t),
inertial_benefit_x: ratio_x(c_i, b_i),
floor_caveat: caveat,
}
}
pub const RESILIENCE_HORIZON_S: f64 = 1.0e7;
#[derive(Clone, Copy, Debug)]
pub struct EnvelopePoint {
pub t: f64,
pub error_m: f64,
}
#[derive(Clone, Debug)]
pub struct ResilienceEnvelope {
pub points: Vec<EnvelopePoint>,
pub coast_time_s: f64,
pub threshold_m: f64,
pub exceeds_horizon: bool,
pub alt_pnt_active: bool,
}
pub fn resilience_envelope(
clock: ClockSpec,
ins: &dyn PositionDrift,
alt_pnt_bound_m: f64,
threshold_m: f64,
times: &[f64],
) -> ResilienceEnvelope {
let q = clock.qparams();
let bound = alt_pnt_bound_m.max(0.0);
let err_at = |t: f64| {
let pos = ins.drift_m(t).min(bound);
let clk = phase_to_range_m(q.coast_sigma_s(t));
(pos * pos + clk * clk).sqrt()
};
let points: Vec<EnvelopePoint> = times
.iter()
.map(|&t| EnvelopePoint {
t,
error_m: err_at(t),
})
.collect();
let (coast_time_s, exceeds_horizon) = if err_at(RESILIENCE_HORIZON_S) < threshold_m {
(RESILIENCE_HORIZON_S, true)
} else {
let (mut lo, mut hi) = (0.0f64, 1.0f64);
while hi < RESILIENCE_HORIZON_S && err_at(hi) < threshold_m {
hi = (hi * 2.0).min(RESILIENCE_HORIZON_S);
}
for _ in 0..100 {
let mid = 0.5 * (lo + hi);
if err_at(mid) < threshold_m {
lo = mid;
} else {
hi = mid;
}
}
(0.5 * (lo + hi), false)
};
let alt_pnt_active = ins.drift_m(coast_time_s) >= bound;
ResilienceEnvelope {
points,
coast_time_s,
threshold_m,
exceeds_horizon,
alt_pnt_active,
}
}
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InsSpec {
pub bias_m_s2: f64,
pub scale_factor_ppm: f64,
pub ref_accel_m_s2: f64,
pub vrw_psd: f64,
}
impl InsSpec {
fn budget(&self) -> ClassicalInsBudget {
ClassicalInsBudget {
bias_m_s2: self.bias_m_s2,
scale_factor_ppm: self.scale_factor_ppm,
ref_accel_m_s2: self.ref_accel_m_s2,
vrw_psd: self.vrw_psd,
}
}
}
fn qt_default_baseline_ins() -> InsSpec {
InsSpec {
bias_m_s2: 1.0e-3,
scale_factor_ppm: 100.0,
ref_accel_m_s2: 9.81,
vrw_psd: 1.0e-8,
}
}
fn qt_default_candidate_ins() -> InsSpec {
InsSpec {
bias_m_s2: 1.0e-6,
scale_factor_ppm: 1.0e-2,
ref_accel_m_s2: 9.81,
vrw_psd: 1.0e-14,
}
}
fn qt_default_alt_pnt_bound() -> f64 {
1.0e9 }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuantumTradeScenario {
pub kind: String,
pub timing_threshold_s: f64,
pub position_threshold_m: f64,
pub baseline_clock_class: String,
#[serde(default)]
pub candidate_clock_class: Option<String>,
#[serde(default)]
pub candidate_adev_taus: Option<Vec<f64>>,
#[serde(default)]
pub candidate_adev_values: Option<Vec<f64>>,
#[serde(default = "qt_default_baseline_ins")]
pub baseline_ins: InsSpec,
#[serde(default = "qt_default_candidate_ins")]
pub candidate_ins: InsSpec,
#[serde(default)]
pub resilience_times_s: Option<Vec<f64>>,
#[serde(default = "qt_default_alt_pnt_bound")]
pub alt_pnt_bound_m: f64,
}
fn qt_classical_class(s: &str) -> Result<ClockClass, String> {
match s.to_ascii_lowercase().as_str() {
"csac" => Ok(ClockClass::Csac),
"uso" => Ok(ClockClass::Uso),
"dsac" => Ok(ClockClass::Dsac),
other => Err(format!(
"unknown baseline_clock_class '{other}' (csac|uso|dsac)"
)),
}
}
fn qt_quantum_class(s: &str) -> Result<QuantumClockClass, String> {
match s.to_ascii_lowercase().replace('_', "-").as_str() {
"optical-lattice" => Ok(QuantumClockClass::OpticalLattice),
"trapped-ion" => Ok(QuantumClockClass::TrappedIon),
"mercury-ion" => Ok(QuantumClockClass::MercuryIon),
other => Err(format!(
"unknown candidate_clock_class '{other}' (optical-lattice|trapped-ion|mercury-ion)"
)),
}
}
impl QuantumTradeScenario {
pub fn scenario_hash(&self) -> String {
use sha2::{Digest, Sha256};
let c = serde_json::to_string(self).unwrap_or_default();
let mut h = Sha256::new();
h.update(c.as_bytes());
hex::encode(h.finalize())
}
fn candidate_clock(&self) -> Result<(ClockSpec, &'static str), String> {
match (
&self.candidate_adev_taus,
&self.candidate_adev_values,
&self.candidate_clock_class,
) {
(Some(taus), Some(adevs), _) => {
if taus.len() != adevs.len() || taus.len() < 2 {
return Err(
"candidate_adev_taus and candidate_adev_values must be equal-length (>= 2)"
.into(),
);
}
if taus.iter().chain(adevs.iter()).any(|v| !v.is_finite() || *v <= 0.0) {
return Err("ADEV taus/values must be finite and > 0".into());
}
Ok((
ClockSpec::Measured(qparams_from_adev_curve(taus, adevs)),
"measured-ADEV",
))
}
(None, None, Some(c)) => Ok((ClockSpec::Quantum(qt_quantum_class(c)?), "quantum-class")),
(None, None, None) => Err(
"provide either candidate_adev_taus + candidate_adev_values (measured) \
or candidate_clock_class"
.into(),
),
_ => Err(
"candidate_adev_taus and candidate_adev_values must both be present (or both absent)"
.into(),
),
}
}
pub fn run_json(&self) -> Result<(String, String), String> {
if !self.timing_threshold_s.is_finite()
|| self.timing_threshold_s <= 0.0
|| !self.position_threshold_m.is_finite()
|| self.position_threshold_m <= 0.0
{
return Err(
"timing_threshold_s and position_threshold_m must be finite and > 0".into(),
);
}
let baseline_clock = ClockSpec::Classical(qt_classical_class(&self.baseline_clock_class)?);
let (candidate_clock, candidate_source) = self.candidate_clock()?;
let baseline_ins = self.baseline_ins.budget();
let candidate_ins = self.candidate_ins.budget();
let trade = quantum_vs_classical_trade(
self.timing_threshold_s,
self.position_threshold_m,
baseline_clock,
candidate_clock,
&baseline_ins,
&candidate_ins,
);
let times: Vec<f64> = self.resilience_times_s.clone().unwrap_or_else(|| {
vec![
1.0, 10.0, 60.0, 300.0, 600.0, 1800.0, 3600.0, 7200.0, 14400.0,
]
});
let envelope = resilience_envelope(
candidate_clock,
&candidate_ins,
self.alt_pnt_bound_m,
self.position_threshold_m,
×,
);
let value = serde_json::json!({
"kind": "quantum-trade",
"scenario_hash": self.scenario_hash(),
"label": "MODELLED — quantifies (never validates) a partner clock/sensor; not flight-demonstrated",
"candidate_source": candidate_source,
"trade": {
"timing_threshold_s": trade.timing_threshold_s,
"position_threshold_m": trade.position_threshold_m,
"baseline": {
"label": trade.baseline.label,
"timing_holdover_s": trade.baseline.timing_holdover_s,
"inertial_holdover_s": trade.baseline.inertial_holdover_s,
"floor_assumed": trade.baseline.floor_assumed,
},
"candidate": {
"label": trade.candidate.label,
"timing_holdover_s": trade.candidate.timing_holdover_s,
"inertial_holdover_s": trade.candidate.inertial_holdover_s,
"floor_assumed": trade.candidate.floor_assumed,
},
"timing_benefit_x": trade.timing_benefit_x,
"inertial_benefit_x": trade.inertial_benefit_x,
"floor_caveat": trade.floor_caveat,
},
"resilience": {
"coast_time_s": envelope.coast_time_s,
"threshold_m": envelope.threshold_m,
"exceeds_horizon": envelope.exceeds_horizon,
"alt_pnt_active": envelope.alt_pnt_active,
"points": envelope.points.iter()
.map(|p| serde_json::json!({ "t": p.t, "error_m": p.error_m }))
.collect::<Vec<_>>(),
},
});
let summary = format!(
"quantum-trade | candidate {} | timing holdover {:.0}s vs {:.0}s ({:.2}x) | inertial {:.0}s vs {:.0}s ({:.2}x) | resilience coast {:.0}s{} | MODELLED{}",
candidate_source,
trade.candidate.timing_holdover_s,
trade.baseline.timing_holdover_s,
trade.timing_benefit_x,
trade.candidate.inertial_holdover_s,
trade.baseline.inertial_holdover_s,
trade.inertial_benefit_x,
envelope.coast_time_s,
if envelope.exceeds_horizon { " (>=horizon)" } else { "" },
if trade.floor_caveat.is_some() { " [floor-assumed caveat]" } else { "" },
);
let json = serde_json::to_string_pretty(&value).map_err(|e| e.to_string())?;
Ok((json, summary))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::clock_state::q_from_allan;
use crate::inertial::quantum_imu::CaiAccelerometer;
fn ref_cai() -> CaiAccelerometer {
CaiAccelerometer {
wavelength_m: 780.0e-9,
pulse_sep_t: 0.05,
atom_number: 1.0e6,
contrast: 0.5,
cycle_time_s: 0.5,
}
}
#[test]
fn measured_adev_round_trips_against_q_from_allan_oracle() {
let (a, b, d) = (1.0e-12_f64, 1.0e-14_f64, 1.0e-17_f64); let (q_wf, q_rw, q_drift) = q_from_allan(a, b, d);
let taus = [1.0, 3.0, 10.0, 30.0, 100.0, 300.0, 1000.0, 3000.0, 10000.0];
let adevs: Vec<f64> = taus
.iter()
.map(|&t: &f64| (a * a / t + b * b * t + d * d * t * t * t).sqrt())
.collect();
let got = qparams_from_adev_curve(&taus, &adevs);
let rel = |x: f64, y: f64| (x - y).abs() / y.abs().max(1e-300);
assert!(rel(got.q_wf, q_wf) < 1e-3, "q_wf {} vs {}", got.q_wf, q_wf);
assert!(rel(got.q_rw, q_rw) < 1e-2, "q_rw {} vs {}", got.q_rw, q_rw);
assert!(
rel(got.q_drift, q_drift) < 2e-2,
"q_drift {} vs {}",
got.q_drift,
q_drift
);
}
#[test]
fn measured_adev_fit_is_nonnegative_on_pure_white_fm() {
let a1: f64 = 1.0e-12;
let taus = [1.0, 10.0, 100.0, 1000.0, 10000.0];
let adevs: Vec<f64> = taus.iter().map(|&t: &f64| a1 / t.sqrt()).collect();
let got = qparams_from_adev_curve(&taus, &adevs);
assert!(got.q_wf >= 0.0 && got.q_rw >= 0.0 && got.q_drift >= 0.0);
assert!((got.q_wf - a1 * a1).abs() / (a1 * a1) < 1e-3);
assert!(got.q_rw < a1 * a1 * 1e-3);
}
#[test]
fn measured_provenance_carries_no_floor_caveat() {
let measured = ClockSpec::Measured(QParams {
q_wf: 1.0e-26,
q_rw: 1.0e-32,
q_drift: 1.0e-40,
});
assert_eq!(measured.provenance(), Provenance::MeasuredAdev);
let classical = ClockSpec::Classical(ClockClass::Uso);
assert_eq!(classical.provenance(), Provenance::AssumedFloor);
}
#[test]
fn better_clock_holds_over_longer() {
let thr = 1.0e-6; let csac = ClockSpec::Classical(ClockClass::Csac).holdover_s(thr);
let optical = ClockSpec::Quantum(QuantumClockClass::OpticalLattice).holdover_s(thr);
assert!(
optical > csac,
"optical {optical} should exceed CSAC {csac}"
);
}
#[test]
fn quantum_ins_coasts_longer_than_classical_and_trade_reports_benefit() {
let quantum = QuantumNavBudget {
cai: ref_cai(),
bias_m_s2: 1.0e-7,
scale_factor_ppm: 1.0,
ref_accel_m_s2: 0.0,
tau_stability_s: 0.0,
};
let classical = ClassicalInsBudget {
bias_m_s2: 5.0e-5,
scale_factor_ppm: 50.0,
ref_accel_m_s2: 9.81,
vrw_psd: 1.0e-4,
};
let thr_m = 100.0;
let q_hold = quantum.inertial_holdover_s(thr_m);
let c_hold = classical.inertial_holdover_s(thr_m);
assert!(
q_hold > c_hold,
"quantum {q_hold}s should coast past classical {c_hold}s"
);
let trade = quantum_vs_classical_trade(
1.0e-6,
thr_m,
ClockSpec::Classical(ClockClass::Uso),
ClockSpec::Quantum(QuantumClockClass::OpticalLattice),
&classical,
&quantum,
);
assert!(
trade.inertial_benefit_x > 1.0,
"benefit {}",
trade.inertial_benefit_x
);
assert!(trade.timing_benefit_x > 1.0);
assert!(trade.floor_caveat.is_some());
assert!(trade.candidate.floor_assumed);
}
#[test]
fn measured_clock_trade_drops_the_floor_caveat() {
let classical = ClassicalInsBudget {
bias_m_s2: 5.0e-5,
scale_factor_ppm: 50.0,
ref_accel_m_s2: 9.81,
vrw_psd: 1.0e-4,
};
let quantum = QuantumNavBudget {
cai: ref_cai(),
bias_m_s2: 1.0e-7,
scale_factor_ppm: 1.0,
ref_accel_m_s2: 0.0,
tau_stability_s: 0.0,
};
let q = QParams {
q_wf: 1.0e-24,
q_rw: 3.0e-28,
q_drift: 2.0e-31,
};
let trade = quantum_vs_classical_trade(
1.0e-6,
100.0,
ClockSpec::Measured(q),
ClockSpec::Measured(q),
&classical,
&quantum,
);
assert!(trade.floor_caveat.is_none());
assert!(!trade.candidate.floor_assumed);
}
#[test]
fn resilience_envelope_is_monotone_finite_and_horizon_honest() {
let quantum = QuantumNavBudget {
cai: ref_cai(),
bias_m_s2: 1.0e-6,
scale_factor_ppm: 1.0,
ref_accel_m_s2: 0.0,
tau_stability_s: 0.0,
};
let times: Vec<f64> = (0..=120).map(|i| i as f64 * 5.0).collect();
let env = resilience_envelope(
ClockSpec::Quantum(QuantumClockClass::OpticalLattice),
&quantum,
150.0,
500.0,
×,
);
for w in env.points.windows(2) {
assert!(w[1].error_m >= w[0].error_m - 1e-9);
}
assert!(
env.coast_time_s.is_finite() && env.coast_time_s > 0.0,
"a noisy clock must give a finite coast time, got {}",
env.coast_time_s
);
assert!(
env.alt_pnt_active,
"the 150 m bound should be binding at the crossing"
);
let perfect = ClockSpec::Measured(QParams {
q_wf: 0.0,
q_rw: 0.0,
q_drift: 0.0,
});
let env_perf = resilience_envelope(perfect, &quantum, 150.0, 500.0, ×);
assert!(env_perf.exceeds_horizon);
assert!((env_perf.coast_time_s - RESILIENCE_HORIZON_S).abs() < 1.0);
let env2 = resilience_envelope(
ClockSpec::Quantum(QuantumClockClass::OpticalLattice),
&quantum,
1.0e12,
50.0,
×,
);
assert!(env2.coast_time_s.is_finite() && env2.coast_time_s > 0.0);
assert!(!env2.exceeds_horizon);
}
}