use satkit::earthgravity::{self, GravityModel};
use satkit::frametransform::ierstable;
use satkit::{Duration, Instant, TimeScale};
fn offline_env() -> bool {
std::env::var("SATKIT_OFFLINE").is_ok_and(|v| !v.is_empty() && v != "0")
}
#[test]
fn offline_gravity_acceleration() {
let pos_itrf = numeris::vector![7000.0e3, 0.0, 0.0];
let a = earthgravity::accel(&pos_itrf, 20, 20, GravityModel::EGM96);
let mag = a.norm();
assert!((mag - 8.135).abs() < 0.02, "|a| = {mag}");
assert!(a[0] < 0.0, "points toward Earth");
for model in [
GravityModel::JGM3,
GravityModel::JGM2,
GravityModel::ITUGrace16,
] {
let b = earthgravity::accel(&pos_itrf, 12, 12, model);
assert!((b.norm() - mag).abs() < 0.01, "{model:?}");
}
}
#[test]
fn offline_iers_tables_and_precession_nutation() {
ierstable::preload().expect("IERS tables available without data files");
let t = Instant::from_datetime(2024, 6, 1, 0, 0, 0.0).unwrap();
let q = satkit::frametransform::qcirs2gcrs(&t);
let (_axis, angle) = q.to_axis_angle();
let angle = angle.abs().min(std::f64::consts::TAU - angle.abs());
assert!(
angle > 0.10f64.to_radians() && angle < 0.20f64.to_radians(),
"{angle}"
);
}
#[test]
fn offline_time_scales() {
let t = Instant::from_datetime(2024, 1, 1, 0, 0, 0.0).unwrap();
let tai = t.as_mjd_with_scale(TimeScale::TAI);
let utc = t.as_mjd_with_scale(TimeScale::UTC);
assert!(((tai - utc) * 86400.0 - 37.0).abs() < 1e-6);
let tt = t.as_mjd_with_scale(TimeScale::TT);
assert!(((tt - tai) * 86400.0 - 32.184).abs() < 1e-6);
let _ = t + Duration::from_days(1.5);
}
#[test]
fn offline_sgp4() {
let lines = [
"ISS (ZARYA)".to_string(),
"1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927".to_string(),
"2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537".to_string(),
];
let mut tle = satkit::TLE::from_lines(&lines)
.unwrap()
.into_iter()
.next()
.unwrap();
let t = tle.epoch + Duration::from_hours(1.0);
let s = satkit::sgp4::sgp4(&mut tle, &[t]).unwrap();
let r = (s.pos[(0, 0)].powi(2) + s.pos[(1, 0)].powi(2) + s.pos[(2, 0)].powi(2)).sqrt();
assert!((r - 6.78e6).abs() < 5e4, "r = {r}");
let v = (s.vel[(0, 0)].powi(2) + s.vel[(1, 0)].powi(2) + s.vel[(2, 0)].powi(2)).sqrt();
assert!((v - 7.66e3).abs() < 1e2, "v = {v}");
}
#[test]
fn offline_kepler_and_lambert() {
use satkit::kepler::Anomaly;
let k = satkit::Kepler::new(7000.0e3, 0.001, 0.9, 0.1, 0.2, Anomaly::True(0.3));
let (r0, v0) = k.to_pv();
let (r1, _v1) = k.propagate(&Duration::from_seconds(600.0)).to_pv();
assert!((r0.norm() - 7000.0e3).abs() < 2e4);
let sols = satkit::lambert::lambert(&r0, &r1, 600.0, satkit::consts::MU_EARTH, true).unwrap();
let (v1, _v2) = sols[0];
assert!((v1 - v0).norm() < 0.5, "{}", (v1 - v0).norm());
}
#[test]
fn offline_missing_ephemeris_is_typed_error() {
if !offline_env() || satkit::utils::data_found() {
eprintln!("skipped: ephemeris present or SATKIT_OFFLINE not set");
return;
}
let t = Instant::from_datetime(2024, 1, 1, 0, 0, 0.0).unwrap();
let err = satkit::jplephem::geocentric_pos(satkit::SolarSystem::Moon, &t)
.expect_err("no ephemeris and no network must be an error");
let msg = err.to_string();
assert!(msg.contains("SATKIT_OFFLINE"), "{msg}");
assert!(msg.contains("linux_p1550p2650.440"), "{msg}");
assert!(
msg.contains("https://"),
"should list the manifest sources: {msg}"
);
}