mod ierstable;
mod qcirs2gcrs;
use super::astrotime::{AstroTime, Scale};
use std::f64::consts::PI;
use nalgebra as na;
type Vec3 = na::Vector3<f64>;
pub type Quat = na::UnitQuaternion<f64>;
use super::earth_orientation_params;
pub use qcirs2gcrs::qcirs2gcrs;
pub use qcirs2gcrs::qcirs2gcrs_dxdy;
#[inline]
pub(crate) fn qrot_xcoord(theta: f64) -> Quat {
Quat::from_axis_angle(&Vec3::x_axis(), -theta)
}
#[inline]
pub(crate) fn qrot_ycoord(theta: f64) -> Quat {
Quat::from_axis_angle(&Vec3::y_axis(), -theta)
}
#[inline]
pub(crate) fn qrot_zcoord(theta: f64) -> Quat {
Quat::from_axis_angle(&Vec3::z_axis(), -theta)
}
pub fn gmst(tm: &AstroTime) -> f64 {
let tut1: f64 = (tm.to_mjd(Scale::UT1) - 51544.5) / 36525.0;
let mut gmst: f64 = 67310.54841
+ tut1 * ((876600.0 * 3600.0 + 8640184.812866) + tut1 * (0.093104 + tut1 * -6.2e-6));
gmst = (gmst % 86400.0) / 240.0 * PI / 180.0;
return gmst;
}
pub fn eqeq(tm: &AstroTime) -> f64 {
let d: f64 = tm.to_mjd(Scale::TT) - 51544.5;
let omega = PI / 180.0 * (125.04 - 0.052954 * d);
let l = (280.47 + 0.98565 * d) * PI / 180.0;
let epsilon = (23.4393 - 0.0000004 * d) * PI / 180.0;
let d_psi = (-0.000319 * f64::sin(omega) - 0.000024 * f64::sin(2.0 * l)) * 15.0 * PI / 180.0;
d_psi * f64::cos(epsilon)
}
pub fn gast(tm: &AstroTime) -> f64 {
gmst(tm) + eqeq(tm)
}
pub fn earth_rotation_angle(tm: &AstroTime) -> f64 {
let t = tm.to_jd(Scale::UT1);
let f = t % 1.0;
2.0 * PI * ((0.7790572732640 + f + 0.00273781191135448 * (t - 2451545.0)) % 1.0)
}
pub fn qitrf2tirs(tm: &AstroTime) -> Quat {
const ASEC2RAD: f64 = PI / 180.0 / 3600.0;
let eop = earth_orientation_params::get(tm).unwrap();
let xp = eop[1] * ASEC2RAD;
let yp = eop[2] * ASEC2RAD;
let t_tt = (tm.to_mjd(Scale::TT) - 51544.5) / 36525.0;
let sp = -47.0e-6 * ASEC2RAD * t_tt;
qrot_zcoord(-sp) * qrot_ycoord(xp) * qrot_xcoord(yp)
}
pub fn qteme2itrf(tm: &AstroTime) -> Quat {
qitrf2tirs(tm).conjugate() * qrot_zcoord(gmst(tm))
}
pub fn qmod2gcrf(tm: &AstroTime) -> Quat {
const ASEC2RAD: f64 = PI / 180.0 / 3600.0;
let tt = (tm.to_mjd(Scale::TT) - 51544.5) / 36525.0;
let zeta = 2.650545
+ tt * (2306.083227
+ tt * (0.2988499 + tt * (0.01801828 + tt * (-0.000005971 + tt * -0.0000003173))));
let z = -2.650545
+ tt * (2306.077181
+ tt * (1.0927348 + tt * (0.01826837 + tt * (-0.000028596 + tt * -0.0000002904))));
let theta = tt
* (2004.191903
+ tt * (-0.42949342 + tt * (-0.04182264 + tt * (-0.000007089 + tt * -0.0000001274))));
return qrot_zcoord(zeta * ASEC2RAD)
* qrot_ycoord(-theta * ASEC2RAD)
* qrot_zcoord(z * ASEC2RAD);
}
pub fn qgcrf2itrf_approx(tm: &AstroTime) -> Quat {
let qitrf2tod_approx: Quat = qrot_zcoord(-gast(tm));
(qmod2gcrf(tm) * qtod2mod_approx(tm) * qitrf2tod_approx).conjugate()
}
pub fn qitrf2gcrf_approx(tm: &AstroTime) -> Quat {
qgcrf2itrf_approx(tm).conjugate()
}
pub fn qtod2mod_approx(tm: &AstroTime) -> Quat {
let d = tm.to_mjd(Scale::TT) - 51544.5;
let t = d / 36525.0;
const DEG2RAD: f64 = PI / 180.0;
let delta_psi = DEG2RAD
* (-0.0048 * f64::sin((125.0 - 0.05295 * d) * DEG2RAD)
- 0.0004 * f64::sin((200.9 + 1.97129 * d) * DEG2RAD));
let delta_epsilon = DEG2RAD
* (0.0026 * f64::cos((125.0 - 0.05295 * d) * DEG2RAD)
+ 0.0002 * f64::cos((200.9 + 1.97129 * d) * DEG2RAD));
let epsilon_a = DEG2RAD
* ((23.0 + 26.0 / 60.0 + 21.406 / 3600.0)
+ t * (-46.836769 / 3600.0
+ t * (-0.0001831 / 3600.0
+ t * (0.00200340 / 3600.0
+ t * (-5.76e-7 / 3600.0 + t * -4.34E-8 / 3600.0)))));
let epsilon = epsilon_a + delta_epsilon;
qrot_xcoord(-epsilon_a) * qrot_zcoord(delta_psi) * qrot_xcoord(epsilon)
}
pub fn qitrf2gcrf(tm: &AstroTime) -> Quat {
let eop = earth_orientation_params::get(tm).unwrap();
let w = {
const ASEC2RAD: f64 = PI / 180.0 / 3600.0;
let xp = eop[1] * ASEC2RAD;
let yp = eop[2] * ASEC2RAD;
let t_tt = (tm.to_mjd(Scale::TT) - 51544.5) / 36525.0;
let sp = -47.0e-6 * ASEC2RAD * t_tt;
qrot_zcoord(-sp) * qrot_ycoord(xp) * qrot_xcoord(yp)
};
let r = qtirs2cirs(tm);
let q = qcirs2gcrs_dxdy(tm, Some((eop[4], eop[5])));
q * r * w
}
pub fn qgcrf2itrf(tm: &AstroTime) -> Quat {
qitrf2gcrf(tm).conjugate()
}
#[inline]
pub fn qtirs2cirs(tm: &AstroTime) -> Quat {
qrot_zcoord(-earth_rotation_angle(tm))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::astrotime::{AstroTime, Scale};
type Vec3 = na::Vector3<f64>;
#[test]
fn test_gmst() {
let mut tm = AstroTime::from_datetime(1992, 8, 20, 12, 14, 0.0);
let tdiff = tm.to_mjd(Scale::UT1) - tm.to_mjd(Scale::UTC);
tm = tm - tdiff;
let gmval = gmst(&tm) * 180.0 / PI;
let truth = -207.4212121875;
assert!(((gmval - truth) / truth).abs() < 1.0e-6)
}
#[test]
fn test_gcrs2itrf() {
let tm = &AstroTime::from_datetime(2004, 4, 6, 7, 51, 28.386009);
let pitrf = Vec3::new(-1033.4793830, 7901.2952754, 6380.3565958);
let t_tt = (tm.to_jd(Scale::TT) - 2451545.0) / 36525.0;
assert!((t_tt - 0.0426236319).abs() < 1.0e-8);
let dut1 = (tm.to_mjd(Scale::UT1) - tm.to_mjd(Scale::UTC)) * 86400.0;
assert!((dut1 + 0.4399619).abs() < 0.01);
let delta_at = (tm.to_mjd(Scale::TAI) - tm.to_mjd(Scale::UTC)) * 86400.0;
assert!((delta_at - 32.0).abs() < 1.0e-7);
let ptirs = qitrf2tirs(&tm) * pitrf;
assert!((ptirs[0] + 1033.4750312).abs() < 1.0e-4);
assert!((ptirs[1] - 7901.3055856).abs() < 1.0e-4);
assert!((ptirs[2] - 6380.3445327).abs() < 1.0e-4);
let era = earth_rotation_angle(&tm);
assert!((era * 180.0 / PI - 312.7552829).abs() < 1.0e-5);
let pcirs = qrot_zcoord(-era) * ptirs;
println!("pcirs = {:?}", pcirs);
assert!((pcirs[0] - 5100.0184047).abs() < 1e-3);
assert!((pcirs[1] - 6122.7863648).abs() < 1e-3);
assert!((pcirs[2] - 6380.3446237).abs() < 1e-3);
let pgcrf = qcirs2gcrs_dxdy(tm, None) * pcirs;
println!("pgcrf = {:?}", pgcrf);
assert!((pgcrf[0] - 5102.508959).abs() < 1e-3);
assert!((pgcrf[1] - 6123.011403).abs() < 1e-3);
assert!((pgcrf[2] - 6378.136925).abs() < 1e-3);
}
}