apsis-rs/crates/orbit/apsis-maneuver/src/impulse.rs

133 lines
5.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The impulsive maneuver.
use crate::error::{ManeuverError, ManeuverErrorKind};
use apsis_math::Vec3;
/// An instantaneous change in velocity.
///
/// An impulse is a velocity change applied in zero time, so it changes the
/// velocity of a state but not its position. That is the model for a burn
/// short compared with the orbital period, and it is how a delta-v budget is
/// written.
///
/// The change is stored as a vector in the same frame as the state it will
/// be applied to, so the caller is responsible for expressing it in that
/// frame. The magnitude is the delta-v, the quantity a propellant budget is
/// measured in.
///
/// 瞬时速度变化。
///
/// 冲量是在零时间内施加的速度变化,故只改变状态的速度而不改变其位置。这是相对
/// 轨道周期很短的点火的模型,也是编写 delta-v 预算所用的模型。
///
/// 该变化以矢量形式存储在与待施加状态相同的坐标系中,因此调用方负责把它表达
/// 在该系中。其模即 delta-v,推进剂预算以此计量。
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Impulse {
delta_v: Vec3,
}
impl Impulse {
/// Creates an impulse from a velocity change vector, in kilometers per
/// second.
///
/// Returns an error when a component is not finite or when the vector is
/// zero, because a zero change is not a maneuver and is almost always a
/// mistake in a plan.
///
/// 由速度变化矢量创建冲量,单位为千米每秒。
///
/// 当某个分量非有限或矢量为零时返回错误,因为零变化不是机动,且几乎总是
/// 计划中的笔误。
pub fn new(delta_v: Vec3) -> Result<Self, ManeuverError> {
if !delta_v.is_finite() {
return Err(ManeuverError::new(ManeuverErrorKind::NotFinite)
.with_detail("the velocity change is not finite"));
}
if delta_v.norm() == 0.0 {
return Err(ManeuverError::new(ManeuverErrorKind::InvalidState)
.with_detail("the velocity change is zero"));
}
Ok(Self { delta_v })
}
/// Creates an impulse of a given magnitude along a direction.
///
/// The direction is normalized, so the magnitude is the delta-v whatever
/// the length of the direction vector. Returns an error when the
/// magnitude is not positive and finite or when the direction is zero or
/// not finite.
///
/// 沿给定方向创建指定大小的冲量。
///
/// 方向被归一化,故无论方向矢量多长,其大小即 delta-v。当大小非正或非有限,
/// 或方向为零或非有限时返回错误。
pub fn along(direction: Vec3, magnitude: f64) -> Result<Self, ManeuverError> {
if !magnitude.is_finite() || magnitude <= 0.0 {
return Err(
ManeuverError::new(ManeuverErrorKind::NonPositiveAcceleration)
.with_detail(format!("magnitude {magnitude}")),
);
}
let unit = direction.normalize().ok_or_else(|| {
ManeuverError::new(ManeuverErrorKind::InvalidState)
.with_detail("the direction is zero or not finite")
})?;
Self::new(unit * magnitude)
}
/// Returns the velocity change vector, in kilometers per second.
///
/// 返回速度变化矢量,单位为千米每秒。
pub const fn delta_v(self) -> Vec3 {
self.delta_v
}
/// Returns the magnitude of the change, in kilometers per second.
///
/// This is the delta-v the burn costs.
///
/// 返回该变化的大小,单位为千米每秒。
///
/// 这就是该次点火所需的 delta-v。
pub fn magnitude(self) -> f64 {
self.delta_v.norm()
}
/// Returns a copy of the change scaled by a factor.
///
/// A factor of zero produces a zero impulse, which [`Impulse::new`]
/// rejects, so this returns an error for it rather than a value that
/// cannot be built.
///
/// 返回该变化按某因子缩放后的副本。
///
/// 因子为零会得到零冲量,而 [`Impulse::new`] 拒绝这种情况,故本函数对该
/// 因子返回错误,而不是给出一个无法构造的值。
pub fn scaled(self, factor: f64) -> Result<Self, ManeuverError> {
Self::new(self.delta_v * factor)
}
/// Returns a copy of the change reversed, which undoes the burn.
///
/// 返回取反后的变化副本,即撤销该次点火。
pub fn reversed(self) -> Self {
Self {
delta_v: -self.delta_v,
}
}
/// Applies the impulse to a velocity, returning the new velocity.
///
/// 把该冲量作用于一个速度,返回新的速度。
pub fn apply_to_velocity(self, velocity: Vec3) -> Vec3 {
velocity + self.delta_v
}
}
impl core::fmt::Display for Impulse {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "impulse of {:.6} km/s", self.magnitude())
}
}