apsis-rs/crates/orbit/apsis-ephemeris/src/lib.rs

98 lines
4.3 KiB
Rust

//! Ephemeris sampling and interpolation.
//!
//! A propagator gives the state of a satellite at any instant, but calling
//! it millions of times is wasteful when the result is only needed to draw a
//! track or to search for a crossing. This crate records the state at a
//! grid of times, called samples, and answers queries between them by
//! interpolation.
//!
//! The two halves are deliberately independent:
//!
//! - [`Ephemeris`] is a table of times and states. It knows nothing about
//! where the states came from, so a table propagated by SGP4, computed by
//! two-body theory, or read from a file all work the same way.
//! - [`Sampler`] fills a table by calling a function that produces a state
//! from a time. In this workspace that function is normally
//! `apsis_sgp4::Satellite::propagate_at`, but the sampler only requires
//! a closure, so a test can use an analytic function.
//!
//! Interpolation is Hermite cubic, which uses both the position and the
//! velocity at each end of an interval. Using the velocity is what makes it
//! accurate: a cubic through two positions alone has no information about
//! how fast the satellite is moving, while Hermite reconstruction matches
//! the slope at both ends and so reproduces a smooth orbit far better for
//! the same sample spacing. On a circular orbit the error is of order the
//! fourth power of the step, so halving the step cuts the error about
//! sixteen fold.
//!
//! Times are in seconds from an origin chosen by the caller, matching the
//! rest of the orbit layer. Positions are in kilometers and velocities in
//! kilometers per second.
//!
//! ```
//! use apsis_ephemeris::{Sample, Sampler};
//! use apsis_kepler::{GravityParameter, KeplerianElements};
//!
//! // A circular orbit at the Earth's equatorial radius.
//! let elements = KeplerianElements {
//! semi_major_axis: 7_000.0,
//! eccentricity: 0.0,
//! inclination: 0.9,
//! right_ascension: 0.0,
//! argument_of_perigee: 0.0,
//! true_anomaly: 0.0,
//! };
//! let state_at = |seconds: f64| {
//! let mean = 2.0 * core::f64::consts::PI * seconds
//! / elements.period(GravityParameter::WGS84);
//! let state = elements
//! .with_mean_anomaly(mean)
//! .to_state(GravityParameter::WGS84)
//! .expect("the orbit is valid");
//! Sample::new(seconds, state.position, state.velocity)
//! };
//!
//! let sampler = Sampler::new(0.0, 600.0, 60.0);
//! let ephemeris = sampler.sample(state_at).expect("the grid is usable");
//! assert_eq!(ephemeris.len(), 11);
//!
//! // A query between two samples is interpolated.
//! let state = ephemeris.state_at(30.0).expect("the time is covered");
//! assert!(state.position.norm() > 6_900.0);
//! ```
//!
//! 星历采样与插值。
//!
//! 传播器给出卫星在任意瞬时的状态,但当结果只用于绘制轨迹或搜索穿越时,调用
//! 它数百万次是浪费的。本 crate 在一组时刻网格上记录状态(称为样本),并用
//! 插值回答其间的查询。
//!
//! 两半是有意独立的:
//!
//! - [`Ephemeris`] 是时刻与状态的表。它对状态来自何处一无所知,故由 SGP4
//! 传播、由二体理论计算,或从文件读取的表都以相同方式工作。
//! - [`Sampler`] 通过调用一个由时刻产生状态的函数来填充表。在本工作区中该
//! 函数通常是 `apsis_sgp4::Satellite::propagate_at`,但采样器只需要一个
//! 闭包,故测试可用解析函数。
//!
//! 插值是 Hermite 三次插值,它同时使用区间两端的位与速度。使用速度正是它
//! 准确的原因:只过两个位置的三次式没有卫星移动多快的信息,而 Hermite 重构
//! 匹配两端的斜率,故在相同采样间距下能好得多地复现光滑轨道。在圆轨道上误差
//! 是步长的四阶量,故步长减半使误差约减为十六分之一。
//!
//! 时间以距调用方所选原点的秒数计,与 orbit 层其余部分一致。位置以千米计,
//! 速度以千米每秒计。
#![forbid(unsafe_code)]
#![deny(missing_docs)]
mod ephemeris;
mod error;
mod sampler;
pub use ephemeris::{Ephemeris, Sample, hermite_position, hermite_state, hermite_velocity};
pub use error::{EphemerisError, EphemerisErrorKind};
pub use sampler::Sampler;
pub use apsis_math::Vec3;