apsis-rs/crates/physics/apsis-frames/src/iau2000/teme.rs

140 lines
6.2 KiB
Rust

//! The True Equator Mean Equinox frame and its transformation to the
//! Earth-fixed frame.
//!
//! The SGP4 propagator produces its state in the True Equator Mean Equinox
//! (TEME) frame. TEME is the true equator of date, so its third axis is the
//! true celestial pole and it carries the nutation, but its first axis
//! points at the mean equinox rather than at the true equinox.
//!
//! Because the first axis is the mean equinox, the transformation to the
//! Earth-fixed frame uses Greenwich mean sidereal time, not the apparent
//! value. That is the detail that is easy to get wrong: subtracting the
//! equation of the equinoxes as well, as one would for a true-equinox frame,
//! rotates the result by about a thousandth of a degree, which is a
//! kilometre of ground position for a low Earth satellite.
//!
//! The time is therefore the UT1 Julian date, from which the mean sidereal
//! time follows, and the polar motion is applied last.
//!
//! Source: the relation between TEME and the Earth-fixed frame is given in
//! Vallado, D. A., *Fundamentals of Astrodynamics and Applications*, 4th
//! edition, microcosm Press, 2013, section 3.7. The sidereal time
//! expression is the one used by the SGP4 reference implementation, as
//! given in the same work and implemented by `eraGmst82`.
use super::terrestrial::PolarMotion;
use apsis_math::Mat3;
use core::f64::consts::PI;
/// The number of seconds in a day.
///
/// 一日的秒数。
const SECONDS_PER_DAY: f64 = 86_400.0;
/// Converts seconds of time to radians.
///
/// A full turn is 86 400 seconds of time, so one second of time is
/// `2 pi / 86400` radians. Sidereal time is quoted in seconds of time, not in
/// arcseconds, and the two differ by a factor of fifteen.
///
/// 把时间秒换算为弧度。
///
/// 一整圈是 86 400 时间秒,故一时间秒是 `2 pi / 86400` 弧度。恒星时以时间
/// 秒而非角秒给出,二者相差十五倍。
const SECONDS_OF_TIME_TO_RADIAN: f64 = 2.0 * PI / SECONDS_PER_DAY;
/// Returns Greenwich mean sidereal time from the 1982 expression, in
/// radians.
///
/// The expression is a polynomial in the number of Julian centuries from
/// J2000.0 plus the fraction of the day in seconds of time. It is the
/// sidereal time the SGP4 theory was fitted against, which is why it is used
/// here rather than the modern expression: the element sets are consistent
/// with it.
///
/// The Julian date is taken in two parts, a whole number of days and a
/// fraction, because a single `f64` near 2 461 000 has a resolution of about
/// 40 microseconds and the sidereal time is wanted to better than that. The
/// caller that has only one value may pass the whole part and the fraction
/// separately, or pass a value twice: `(jd, 0.0)`, `(2400000.5, mjd)`, and
/// `(jd - 0.5, 0.5)` are all accepted, but the split with the smaller whole
/// part preserves more of the fraction.
///
/// Source: as implemented by `eraGmst82`.
///
/// 返回 1982 表达式给出的格林尼治平恒星时,单位为弧度。
///
/// 该表达式是自 J2000.0 起儒略世纪数加上当日以时间秒计的零头所构成的多项
/// 式。它是 SGP4 理论所拟合的恒星时,故此处用它而非现代表达式:根数集与
/// 它一致。
///
/// 儒略日按两部分取,一为整数天数、一为零头,因为接近 2 461 000 的单个
/// `f64` 分辨率约为 40 微秒,而所要求的恒星时优于该值。只有一个值的调用
/// 方可以分别传入整数部分与零头,或把同一个值传两次:`(jd, 0.0)`、
/// `(2400000.5, mjd)` 与 `(jd - 0.5, 0.5)` 都可接受,但整数部分更小的
/// 拆分能保留更多零头。
pub fn greenwich_mean_sidereal_time_1982(day: f64, fraction: f64) -> f64 {
// The constant absorbs the half day between the start of the Julian
// date and the start of the day.
// 该常数吸收了儒略日起点与当日起点之间的半日。
let a = 24_110.548_41 - SECONDS_PER_DAY / 2.0;
let b = 8_640_184.812_866;
let c = 0.093_104;
let d = -6.2e-6;
// The reference orders the two parts so that the smaller is the first,
// which is the arrangement that preserves the most precision.
// 参考实现把两部分排序使较小者在前,这是最能保留精度的安排。
let (first, second) = if day < fraction {
(day, fraction)
} else {
(fraction, day)
};
let t = (first + (second - 2_451_545.0)) / 36_525.0;
let seconds_of_time = SECONDS_PER_DAY * (first.rem_euclid(1.0) + second.rem_euclid(1.0));
let seconds = (a + (b + (c + d * t) * t) * t) + seconds_of_time;
(seconds * SECONDS_OF_TIME_TO_RADIAN).rem_euclid(2.0 * PI)
}
/// Returns the rotation from the True Equator Mean Equinox frame to the
/// Earth-fixed frame.
///
/// The argument is the UT1 Julian date, because the rotation is the Earth's
/// rotation, and the polar motion is supplied by the caller because it is
/// measured rather than modelled.
///
/// This is the transformation to apply to the position and velocity that
/// `apsis-sgp4` produces.
///
/// 返回从真赤道平春分点坐标系到地固系的转动。
///
/// 参数是 UT1 儒略日,因为该转动即地球自转;极移由调用方提供,因为它是
/// 实测而非建模的。
///
/// 这是施加于 `apsis-sgp4` 所产生的位置与速度的变换。
pub fn teme_to_terrestrial_matrix(
ut1_day: f64,
ut1_fraction: f64,
polar_motion: PolarMotion,
) -> Mat3 {
let sidereal = greenwich_mean_sidereal_time_1982(ut1_day, ut1_fraction);
// The composition applies the Earth rotation first and the polar motion
// last, with each new rotation on the left of the accumulator.
// 该合成先施加地球自转、最后施加极移,每个新转动都在累加器的左侧。
polar_motion.matrix() * rotation_z(sidereal)
}
/// Returns a rotation of `angle` about the third axis, in radians.
///
/// The convention is the one the reference helpers use: the rotation is
/// applied to the frame, so composing `a * b` applies `b` first.
///
/// 返回绕第三轴转 `angle` 的旋转,单位为弧度。
///
/// 约定即各参考辅助函数所用者:转动施加于坐标系,故合成 `a * b` 时先施加
/// `b`。
fn rotation_z(angle: f64) -> Mat3 {
super::precession::rotation_z(angle)
}