use crate::detection::{analytic_pd, detection_boundary};
use crate::holdover::{coast_phase_sigma, QuantumClockClass};
use crate::qtrade::{TradeEvidence, TradeFom, TradeFrame};
use crate::quantum_devices::EntanglementTimeLink;
use crate::representativeness::{Gap, Representativeness};
use crate::tpl::{cusum_latency_s, timing_protection_level_ns, TplInputs};
use crate::verification::VerificationStatus;
fn d_integration_s() -> f64 {
1.0
}
fn d_dissemination_interval_s() -> f64 {
100.0
}
fn d_link_loss_db() -> f64 {
30.0
}
fn d_classical_link_sigma_s() -> f64 {
1.0e-9 }
fn d_monitor_pfa() -> f64 {
1.0e-3
}
fn d_attack_delay_s() -> f64 {
5.0e-9 }
fn d_clock_fault_sigma() -> f64 {
4.0 }
fn clock_coast_sigma_s(psds: (f64, f64, f64), interval_s: f64) -> f64 {
coast_phase_sigma(psds.0, psds.1, psds.2, interval_s)
}
#[derive(Clone, Copy, Debug, serde::Deserialize)]
pub struct QuantumTimeTransferScenario {
#[serde(default = "d_integration_s")]
pub integration_s: f64,
#[serde(default = "d_dissemination_interval_s")]
pub dissemination_interval_s: f64,
#[serde(default = "d_link_loss_db")]
pub link_loss_db: f64,
#[serde(default = "d_classical_link_sigma_s")]
pub classical_link_sigma_s: f64,
#[serde(default = "d_monitor_pfa")]
pub monitor_pfa: f64,
#[serde(default = "d_attack_delay_s")]
pub attack_delay_s: f64,
#[serde(default = "d_clock_fault_sigma")]
pub clock_fault_sigma: f64,
}
impl Default for QuantumTimeTransferScenario {
fn default() -> Self {
QuantumTimeTransferScenario {
integration_s: d_integration_s(),
dissemination_interval_s: d_dissemination_interval_s(),
link_loss_db: d_link_loss_db(),
classical_link_sigma_s: d_classical_link_sigma_s(),
monitor_pfa: d_monitor_pfa(),
attack_delay_s: d_attack_delay_s(),
clock_fault_sigma: d_clock_fault_sigma(),
}
}
}
#[derive(Clone, Debug, serde::Serialize)]
pub struct QuantumTimeTransferReport {
pub quantum_chain_sigma_s: f64,
pub classical_chain_sigma_s: f64,
pub quantum_link_rate_hz: f64,
pub protection_level_ns: f64,
pub security_pd: f64,
pub monitor_pfa: f64,
pub anomaly_pd: f64,
pub anomaly_cusum_latency_s: f64,
pub trade: TradeEvidence,
}
impl QuantumTimeTransferScenario {
pub fn run(&self) -> QuantumTimeTransferReport {
let q_clock = QuantumClockClass::OpticalLattice;
let q_link = EntanglementTimeLink {
link_loss_db: self.link_loss_db,
..Default::default()
};
let q_link_sigma = q_link.timing_precision_s(self.integration_s);
let q_clock_coast = clock_coast_sigma_s(q_clock.psds(), self.dissemination_interval_s);
let quantum_chain_sigma_s = (q_link_sigma.powi(2) + q_clock_coast.powi(2)).sqrt();
let c_psds = crate::clock_state::ClockClass::Csac.psds();
let c_clock_coast = clock_coast_sigma_s(c_psds, self.dissemination_interval_s);
let classical_chain_sigma_s =
(self.classical_link_sigma_s.powi(2) + c_clock_coast.powi(2)).sqrt();
let (q_wf, q_rw, q_drift) = q_clock.psds();
let tpl = TplInputs {
q_wf,
q_rw,
q_drift,
r: quantum_chain_sigma_s.max(1e-15),
tau: self.integration_s.max(1e-3),
samples: (self.dissemination_interval_s / self.integration_s.max(1e-3)).max(1.0),
k: 5.0,
detection_latency_s: self.integration_s.max(1e-3),
};
let protection_level_ns = timing_protection_level_ns(&tpl);
let sigma_mon = quantum_chain_sigma_s.max(1e-15);
let gamma = detection_boundary(sigma_mon, self.monitor_pfa);
let security_pd = analytic_pd(self.attack_delay_s, sigma_mon, gamma);
let fault_mu = self.clock_fault_sigma * sigma_mon;
let anomaly_pd = analytic_pd(fault_mu, sigma_mon, gamma);
let anomaly_cusum_latency_s = cusum_latency_s(
0.5,
5.0,
self.clock_fault_sigma,
self.integration_s.max(1e-3),
);
let rep =
Representativeness::modelled("quantum vs classical end-to-end time transfer", (3, 4))
.with_assumption(
"optical-lattice clock + entanglement link vs CSAC + RF two-way link",
)
.with_assumption(
"illustrative, public-source device/link parameters; seeded synthetic",
)
.with_gap(Gap::new(
"real clock + optical link hardware and a space channel demonstration",
"Phase B2 hardware-in-the-loop",
));
let trade = TradeEvidence::new(TradeFrame::new("quantum-time-transfer", 0), rep)
.with_fom(TradeFom {
name: "end-to-end time-transfer precision".into(),
unit: "s".into(),
quantum: quantum_chain_sigma_s,
classical: classical_chain_sigma_s,
higher_is_better: false,
ci95: None,
status: VerificationStatus::Modelled,
})
.with_fom(TradeFom {
name: "reference clock 1 s stability".into(),
unit: "sigma_y(1 s)".into(),
quantum: q_clock.adev_1s(),
classical: crate::clock_state::ClockClass::Csac.adev_1s(),
higher_is_better: false,
ci95: None,
status: VerificationStatus::Modelled,
});
QuantumTimeTransferReport {
quantum_chain_sigma_s,
classical_chain_sigma_s,
quantum_link_rate_hz: q_link.detected_coincidence_rate_hz(),
protection_level_ns,
security_pd,
monitor_pfa: self.monitor_pfa,
anomaly_pd,
anomaly_cusum_latency_s,
trade,
}
}
}
pub fn to_svg(r: &QuantumTimeTransferReport) -> String {
let q = r.quantum_chain_sigma_s;
let c = r.classical_chain_sigma_s;
let max = q.max(c).max(1e-18);
let qh = (q / max * 180.0).min(180.0);
let ch = (c / max * 180.0).min(180.0);
format!(
"<svg xmlns='http://www.w3.org/2000/svg' width='320' height='220'>\
<rect width='320' height='220' fill='white'/>\
<text x='10' y='20' font-size='12'>quantum-time-transfer (MODELLED)</text>\
<rect x='60' y='{}' width='60' height='{:.1}' fill='#3a6'/>\
<text x='62' y='210' font-size='10'>quantum</text>\
<rect x='180' y='{}' width='60' height='{:.1}' fill='#c44'/>\
<text x='182' y='210' font-size='10'>classical</text></svg>",
200.0 - qh,
qh,
200.0 - ch,
ch
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quantum_precision_improves_with_integration() {
let short = QuantumTimeTransferScenario {
integration_s: 1.0,
..Default::default()
};
let long = QuantumTimeTransferScenario {
integration_s: 100.0,
..Default::default()
};
assert!(long.run().quantum_chain_sigma_s <= short.run().quantum_chain_sigma_s);
}
#[test]
fn quantum_can_win_and_can_lose() {
let good = QuantumTimeTransferScenario {
link_loss_db: 10.0,
..Default::default()
}
.run();
assert!(good.quantum_chain_sigma_s < good.classical_chain_sigma_s);
let bad = QuantumTimeTransferScenario {
link_loss_db: 80.0,
..Default::default()
}
.run();
assert!(bad.quantum_chain_sigma_s > bad.classical_chain_sigma_s);
}
#[test]
fn protection_level_is_finite_positive() {
let r = QuantumTimeTransferScenario::default().run();
assert!(r.protection_level_ns.is_finite() && r.protection_level_ns > 0.0);
}
#[test]
fn security_fom_in_range_and_grows_with_attack_delay() {
let small = QuantumTimeTransferScenario {
attack_delay_s: 1e-9,
..Default::default()
}
.run();
let large = QuantumTimeTransferScenario {
attack_delay_s: 20e-9,
..Default::default()
}
.run();
for v in [small.security_pd, large.security_pd] {
assert!((0.0..=1.0).contains(&v), "Pd out of range: {v}");
}
assert!(
large.security_pd >= small.security_pd,
"larger attack must be at least as detectable"
);
}
#[test]
fn anomaly_detection_is_consistent() {
let r = QuantumTimeTransferScenario::default().run();
assert!((0.0..=1.0).contains(&r.anomaly_pd));
assert!(r.anomaly_cusum_latency_s.is_finite() && r.anomaly_cusum_latency_s > 0.0);
let big = QuantumTimeTransferScenario {
clock_fault_sigma: 8.0,
..Default::default()
}
.run();
assert!(big.anomaly_pd >= r.anomaly_pd);
}
#[test]
fn trade_is_honest() {
let r = QuantumTimeTransferScenario::default().run();
assert!(
r.trade.is_honest(),
"violations: {:?}",
r.trade.honesty_violations()
);
assert!(r.trade.representativeness.is_valid());
}
}