use nalgebra::{Matrix3, Vector3};
use std::f64::consts::TAU;
use crate::time::Time;
const T0: f64 = 2451545.0;
const DAY_S: f64 = 86400.0;
pub fn gmst1982(jd_ut1: f64, frac_ut1: f64) -> (f64, f64) {
let t = (jd_ut1 - T0 + frac_ut1) / 36525.0;
let g = 67310.54841 + (8640184.812866 + (0.093104 + (-6.2e-6) * t) * t) * t;
let dg = 8640184.812866 + (0.093104 * 2.0 + (-6.2e-6 * 3.0) * t) * t;
let theta = ((jd_ut1 % 1.0 + frac_ut1 + g / DAY_S) % 1.0) * TAU;
let theta_dot = (1.0 + dg / (DAY_S * 36525.0)) * TAU;
(theta, theta_dot)
}
fn rot_z(angle: f64) -> Matrix3<f64> {
let (s, c) = angle.sin_cos();
Matrix3::new(c, s, 0.0, -s, c, 0.0, 0.0, 0.0, 1.0)
}
pub fn teme_to_gcrs(t: &Time) -> Matrix3<f64> {
let ut1 = t.ut1();
let jd_ut1 = ut1.floor();
let frac_ut1 = ut1 - jd_ut1;
let (theta, _theta_dot) = gmst1982(jd_ut1, frac_ut1);
let gast_rad = t.gast() / 24.0 * TAU;
let angle = theta - gast_rad;
rot_z(angle) * t.m_matrix()
}
pub fn teme_to_gcrs_full(t: &Time) -> (Matrix3<f64>, f64) {
let ut1 = t.ut1();
let jd_ut1 = ut1.floor();
let frac_ut1 = ut1 - jd_ut1;
let (theta, theta_dot) = gmst1982(jd_ut1, frac_ut1);
let gast_rad = t.gast() / 24.0 * TAU;
let angle = theta - gast_rad;
let r = rot_z(angle) * t.m_matrix();
(r, theta_dot)
}
pub fn transform_teme_to_gcrs(
t: &Time,
pos_teme: &Vector3<f64>,
vel_teme: &Vector3<f64>,
) -> (Vector3<f64>, Vector3<f64>) {
let (r, theta_dot) = teme_to_gcrs_full(t);
let pos_gcrs = r * pos_teme;
let omega = theta_dot / DAY_S;
let vel_corrected = Vector3::new(
vel_teme.x - omega * pos_teme.y,
vel_teme.y + omega * pos_teme.x,
vel_teme.z,
);
let vel_gcrs = r * vel_corrected;
(pos_gcrs, vel_gcrs)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::time::Timescale;
use approx::assert_relative_eq;
#[test]
fn test_gmst1982_at_j2000() {
let (theta, _) = gmst1982(2451545.0, 0.0);
let theta_deg = theta.to_degrees();
let theta_deg = ((theta_deg % 360.0) + 360.0) % 360.0;
assert_relative_eq!(theta_deg, 280.46, epsilon = 0.5);
}
#[test]
fn test_rot_z_identity() {
let r = rot_z(0.0);
let v = Vector3::new(1.0, 0.0, 0.0);
let result = r * v;
assert_relative_eq!(result.x, 1.0, epsilon = 1e-10);
assert_relative_eq!(result.y, 0.0, epsilon = 1e-10);
}
#[test]
fn test_rot_z_90_degrees() {
let r = rot_z(std::f64::consts::FRAC_PI_2);
let v = Vector3::new(1.0, 0.0, 0.0);
let result = r * v;
assert_relative_eq!(result.x, 0.0, epsilon = 1e-10);
assert_relative_eq!(result.y, -1.0, epsilon = 1e-10);
}
#[test]
fn test_teme_to_gcrs_returns_rotation_matrix() {
let ts = Timescale::default();
let t = ts.tdb_jd(2451545.0);
let r = teme_to_gcrs(&t);
let det = r.determinant();
assert_relative_eq!(det, 1.0, epsilon = 1e-10);
let rrt = r * r.transpose();
assert_relative_eq!(rrt[(0, 0)], 1.0, epsilon = 1e-10);
assert_relative_eq!(rrt[(1, 1)], 1.0, epsilon = 1e-10);
assert_relative_eq!(rrt[(2, 2)], 1.0, epsilon = 1e-10);
}
}