apsis-rs/crates/orbit/apsis-kepler/tests/anomaly.rs

112 lines
3.9 KiB
Rust

//! Tests for the anomaly conversions.
//!
//! 近点角换算的测试。
use apsis_kepler::{
eccentric_from_mean, eccentric_from_true, mean_from_eccentric, mean_from_true,
true_from_eccentric, true_from_mean, wrap_to_pi,
};
use core::f64::consts::PI;
#[test]
fn wrap_places_angles_in_the_half_open_interval() {
for turns in -3..=3 {
for offset in [0.0, 0.25, 0.5, 0.75] {
let angle = PI + (f64::from(turns) + offset) * 2.0 * PI;
let wrapped = wrap_to_pi(angle);
assert!(
(-PI..PI).contains(&wrapped),
"angle {angle} wrapped to {wrapped}"
);
// The result differs from the input by a whole number of turns.
// 结果与输入相差整数圈。
let turns_removed = (angle - wrapped) / (2.0 * PI);
assert!((turns_removed - turns_removed.round()).abs() < 1e-9);
}
}
}
#[test]
fn wrap_keeps_values_already_in_range() {
for angle in [-3.0, -1.0, 0.0, 1.0, 3.0] {
assert!((wrap_to_pi(angle) - angle).abs() < 1e-12);
}
}
#[test]
fn keplers_equation_holds_for_the_solution() {
// M = E - e sin E must be satisfied by the returned eccentric anomaly.
// 所返回的偏近点角必须满足 M = E - e sin E。
for eccentricity in [0.0, 0.01, 0.1, 0.5, 0.9, 0.99] {
for mean in [-3.0, -1.0, 0.0, 0.5, 1.5, 3.0] {
let eccentric = eccentric_from_mean(mean, eccentricity);
let recovered = mean_from_eccentric(eccentric, eccentricity);
assert!(
(recovered - mean).abs() < 1e-9,
"e={eccentricity} M={mean} recovered={recovered}"
);
}
}
}
#[test]
fn eccentric_and_true_anomaly_round_trip() {
for eccentricity in [0.0, 0.05, 0.3, 0.7, 0.95] {
for true_anomaly in [-3.0, -1.0, 0.0, 0.25, 1.0, 3.0] {
let eccentric = eccentric_from_true(true_anomaly, eccentricity);
let back = true_from_eccentric(eccentric, eccentricity);
assert!(
(back - true_anomaly).abs() < 1e-9,
"e={eccentricity} nu={true_anomaly} back={back}"
);
}
}
}
#[test]
fn mean_and_true_anomaly_round_trip() {
for eccentricity in [0.0, 0.05, 0.3, 0.7, 0.95] {
for true_anomaly in [-2.5, -0.5, 0.0, 0.75, 2.0] {
let mean = mean_from_true(true_anomaly, eccentricity);
let back = true_from_mean(mean, eccentricity);
assert!(
(back - true_anomaly).abs() < 1e-8,
"e={eccentricity} nu={true_anomaly} back={back}"
);
}
}
}
#[test]
fn a_circular_orbit_has_equal_anomalies() {
// With zero eccentricity every anomaly is the same angle.
// 偏心率为零时每种近点角都是同一个角。
for angle in [-2.0, -0.5, 0.0, 1.0, 2.5] {
assert!((eccentric_from_mean(angle, 0.0) - angle).abs() < 1e-12);
assert!((true_from_eccentric(angle, 0.0) - angle).abs() < 1e-12);
assert!((true_from_mean(angle, 0.0) - angle).abs() < 1e-12);
}
}
#[test]
fn anomalies_at_perigee_are_zero() {
for eccentricity in [0.0, 0.3, 0.8] {
assert!(eccentric_from_mean(0.0, eccentricity).abs() < 1e-12);
assert!(true_from_mean(0.0, eccentricity).abs() < 1e-12);
assert!(mean_from_true(0.0, eccentricity).abs() < 1e-12);
}
}
#[test]
fn true_anomaly_exceeds_mean_anomaly_before_apogee() {
// For an eccentric orbit the satellite spends less time near perigee,
// so the true anomaly runs ahead of the mean anomaly in the first half
// of the orbit.
// 对偏心轨道,卫星在近地点附近花的时间较少,故在轨道的上半段真近点角领先于平
// 近点角。
let eccentricity = 0.5;
let mean = 1.0;
let true_anomaly = true_from_mean(mean, eccentricity);
assert!(true_anomaly > mean, "nu={true_anomaly} M={mean}");
}