use kshana::sbas::{sbas_protection_level, SbasErrorModel, SbasMode, SbasSat};
const DEG: f64 = std::f64::consts::PI / 180.0;
const K_V_ORACLE: f64 = 5.33;
type SatRow = (f64, f64, f64);
const F0: &[SatRow] = &[
(19.0651, 176.31, 1.958562),
(42.6512, 148.8728, 1.430721),
(77.5947, 62.7627, 1.24434),
(63.7754, 218.5625, 1.281244),
(31.3713, 292.354, 1.606398),
];
const F1: &[SatRow] = &[
(5.0562, 335.6853, 2.748428),
(23.7356, 175.5345, 1.799016),
(62.5324, 278.6834, 1.299358),
(46.8385, 145.1215, 1.391119),
(32.845, 56.6363, 1.689508),
(73.1207, 60.0882, 1.288052),
(59.8705, 211.0886, 1.306134),
(35.5422, 294.7292, 1.529274),
(20.548, 100.8735, 2.150393),
];
const F2: &[SatRow] = &[
(6.3949, 332.1993, 2.682291),
(28.2861, 174.7634, 1.675249),
(63.8174, 268.2769, 1.312994),
(50.6037, 140.7646, 1.382604),
(29.9138, 53.214, 1.763046),
(68.8676, 59.6108, 1.304467),
(55.7081, 205.8538, 1.330621),
(39.6128, 296.877, 1.537565),
];
const F3: &[SatRow] = &[
(7.3372, 328.7234, 2.636831),
(7.3357, 293.9829, 2.636938),
(32.6903, 173.9559, 1.580174),
(63.9554, 257.653, 1.308954),
(53.8829, 135.7406, 1.361231),
(26.8787, 50.4996, 1.865814),
(64.869, 60.0934, 1.322201),
(51.5122, 202.1189, 1.353585),
(43.5708, 298.7897, 1.491537),
];
const F4: &[SatRow] = &[
(7.9414, 325.1194, 2.504675),
(10.7601, 295.8398, 2.336865),
(37.1603, 173.0195, 1.503573),
(12.7886, 72.0872, 3.563409),
(63.0106, 247.2949, 1.294534),
(56.7567, 129.7, 1.345682),
(23.6578, 48.2732, 2.142725),
(60.9245, 61.1153, 1.343416),
(47.1818, 199.2484, 1.384175),
(47.618, 300.5469, 1.387714),
];
const F5: &[SatRow] = &[
(8.1974, 321.4232, 2.487776),
(14.2619, 297.5803, 2.16142),
(41.6789, 171.8812, 1.442177),
(6.8435, 38.0424, 3.296626),
(14.0082, 68.136, 8.103587),
(61.0932, 238.0224, 1.301565),
(59.0907, 122.5603, 1.33531),
(20.3001, 46.4943, 2.428854),
(57.0403, 62.4908, 1.368582),
(51.7535, 302.1054, 1.359535),
];
const FIXTURES: &[(&[SatRow], f64, f64)] = &[
(F0, 17.6549, 20.292),
(F1, 7.485, 12.384),
(F2, 7.5733, 14.3297),
(F3, 7.1971, 13.5098),
(F4, 7.0478, 13.3614),
(F5, 8.5337, 14.351),
];
#[test]
fn sbas_protection_level_matches_rtklib_sbas_oracle() {
for (fi, &(sat_rows, oracle_hpl, oracle_vpl)) in FIXTURES.iter().enumerate() {
let sats: Vec<SbasSat> = sat_rows
.iter()
.map(|&(el_deg, az_deg, sigma_m)| SbasSat {
el_rad: el_deg * DEG,
az_rad: az_deg * DEG,
err: SbasErrorModel::uniform(sigma_m),
})
.collect();
let pl = sbas_protection_level(&sats, SbasMode::PrecisionApproach)
.unwrap_or_else(|| panic!("fixture {fi}: protection level returned None"));
let dh = (pl.hpl_m - oracle_hpl).abs();
assert!(
dh < 2e-3,
"fixture {fi} HPL: kshana {:.6} m vs RTKLIB SBAS-PL {:.4} m (|Δ|={dh:.2e})",
pl.hpl_m,
oracle_hpl
);
let du_oracle = oracle_vpl / K_V_ORACLE;
let dv = (pl.d_u_m - du_oracle).abs();
assert!(
dv < 2e-3,
"fixture {fi} d_U: kshana {:.6} m vs RTKLIB SBAS-PL VPL/K_V {:.6} m (|Δ|={dv:.2e})",
pl.d_u_m,
du_oracle
);
let vpl = pl.vpl_m.expect("PA mode yields a VPL");
assert!(
vpl <= oracle_vpl && vpl > 0.99 * oracle_vpl,
"fixture {fi} VPL {:.6} not in (0.99·oracle, oracle] of {:.4}",
vpl,
oracle_vpl
);
}
}