use affn::cartesian;
use affn::centers::ReferenceCenter;
use affn::frames::ReferenceFrame;
use qtty::unit::Kilometer;
use qtty::{KmPerSecond, KmPerSecondSquared, Second};
use tempoch::{ContinuousScale, Time};
pub type Position<C, F> = cartesian::Position<C, F, Kilometer>;
pub type Velocity<F> = cartesian::Velocity<F, KmPerSecond>;
pub type Acceleration<F> = cartesian::Acceleration<F, KmPerSecondSquared>;
#[derive(Debug)]
pub struct DynamicsState<S, C, F>
where
S: ContinuousScale,
C: ReferenceCenter,
F: ReferenceFrame,
{
pub epoch: Time<S>,
pub position: Position<C, F>,
pub velocity: Velocity<F>,
}
impl<S, C, F> Clone for DynamicsState<S, C, F>
where
S: ContinuousScale,
C: ReferenceCenter,
F: ReferenceFrame,
{
fn clone(&self) -> Self {
Self {
epoch: self.epoch,
position: self.position.clone(),
velocity: self.velocity,
}
}
}
impl<S, C, F> Copy for DynamicsState<S, C, F>
where
S: ContinuousScale,
C: ReferenceCenter,
F: ReferenceFrame,
C::Params: Copy,
{
}
impl<S, C, F> PartialEq for DynamicsState<S, C, F>
where
S: ContinuousScale,
C: ReferenceCenter,
F: ReferenceFrame,
{
fn eq(&self, other: &Self) -> bool {
self.epoch == other.epoch
&& self.position.x() == other.position.x()
&& self.position.y() == other.position.y()
&& self.position.z() == other.position.z()
&& self.velocity.x() == other.velocity.x()
&& self.velocity.y() == other.velocity.y()
&& self.velocity.z() == other.velocity.z()
}
}
impl<S, C, F> DynamicsState<S, C, F>
where
S: ContinuousScale,
C: ReferenceCenter,
F: ReferenceFrame,
{
#[inline]
pub fn new(epoch: Time<S>, position: Position<C, F>, velocity: Velocity<F>) -> Self {
Self {
epoch,
position,
velocity,
}
}
#[inline]
pub fn advance(&self, deriv: &StateDerivative<F>, dt: Second) -> Self {
let dt_s = dt.value();
let new_pos = Position::<C, F>::new_with_params(
self.position.center_params().clone(),
self.position.x().value() + dt_s * deriv.vel.x().value(),
self.position.y().value() + dt_s * deriv.vel.y().value(),
self.position.z().value() + dt_s * deriv.vel.z().value(),
);
let new_vel = Velocity::<F>::new(
self.velocity.x().value() + dt_s * deriv.acc.x().value(),
self.velocity.y().value() + dt_s * deriv.acc.y().value(),
self.velocity.z().value() + dt_s * deriv.acc.z().value(),
);
Self {
epoch: self.epoch,
position: new_pos,
velocity: new_vel,
}
}
#[inline]
pub fn advance_with_epoch(&self, deriv: &StateDerivative<F>, dt: Second) -> Self {
let mut next = self.advance(deriv, dt);
next.epoch = self.epoch + dt;
next
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StateDerivative<F>
where
F: ReferenceFrame,
{
pub vel: Velocity<F>,
pub acc: Acceleration<F>,
}
impl<F: ReferenceFrame> StateDerivative<F> {
#[inline]
pub fn new(vel: Velocity<F>, acc: Acceleration<F>) -> Self {
Self { vel, acc }
}
#[inline]
pub fn velocity(&self) -> Velocity<F> {
self.vel
}
#[inline]
pub fn acceleration(&self) -> Acceleration<F> {
self.acc
}
#[inline]
pub fn rk4_combine(k1: &Self, k2: &Self, k3: &Self, k4: &Self) -> Self {
let inv6 = 1.0 / 6.0;
let vx = inv6
* (k1.vel.x().value()
+ 2.0 * k2.vel.x().value()
+ 2.0 * k3.vel.x().value()
+ k4.vel.x().value());
let vy = inv6
* (k1.vel.y().value()
+ 2.0 * k2.vel.y().value()
+ 2.0 * k3.vel.y().value()
+ k4.vel.y().value());
let vz = inv6
* (k1.vel.z().value()
+ 2.0 * k2.vel.z().value()
+ 2.0 * k3.vel.z().value()
+ k4.vel.z().value());
let ax = inv6
* (k1.acc.x().value()
+ 2.0 * k2.acc.x().value()
+ 2.0 * k3.acc.x().value()
+ k4.acc.x().value());
let ay = inv6
* (k1.acc.y().value()
+ 2.0 * k2.acc.y().value()
+ 2.0 * k3.acc.y().value()
+ k4.acc.y().value());
let az = inv6
* (k1.acc.z().value()
+ 2.0 * k2.acc.z().value()
+ 2.0 * k3.acc.z().value()
+ k4.acc.z().value());
Self {
vel: Velocity::<F>::new(vx, vy, vz),
acc: Acceleration::<F>::new(ax, ay, az),
}
}
#[inline]
pub fn scaled(&self, factor: f64) -> Self {
Self {
vel: self.vel.scale(factor),
acc: self.acc.scale(factor),
}
}
#[inline]
pub fn add(&self, other: &Self) -> Self {
Self {
vel: self.vel + other.vel,
acc: self.acc + other.acc,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use affn::centers::ReferenceCenter;
use affn::frames::ReferenceFrame;
use qtty::unit::Kilometer;
use qtty::{KmPerSecond, KmPerSecondSquared, Second};
use tempoch::{Time, TT};
#[derive(Debug, Clone, Copy)]
struct Inertial;
impl ReferenceFrame for Inertial {
fn frame_name() -> &'static str {
"Inertial"
}
}
#[derive(Debug, Clone, Copy)]
struct Center;
impl ReferenceCenter for Center {
type Params = ();
fn center_name() -> &'static str {
"Center"
}
}
fn make_state() -> DynamicsState<TT, Center, Inertial> {
DynamicsState::new(
Time::<TT>::from_raw_j2000_seconds(Second::new(0.0)).unwrap(),
affn::cartesian::Position::<Center, Inertial, Kilometer>::new(7000.0, 0.0, 0.0),
affn::cartesian::Velocity::<Inertial, KmPerSecond>::new(0.0, 7.5, 0.0),
)
}
fn make_deriv() -> StateDerivative<Inertial> {
StateDerivative::new(
affn::cartesian::Velocity::<Inertial, KmPerSecond>::new(0.0, 7.5, 0.0),
affn::cartesian::Acceleration::<Inertial, KmPerSecondSquared>::new(-0.8, 0.0, 0.0),
)
}
#[test]
fn partial_eq_same_state() {
let s = make_state();
let t = make_state();
assert_eq!(s, t);
}
#[test]
fn advance_with_epoch_advances_epoch() {
let s = make_state();
let d = make_deriv();
let dt = Second::new(10.0);
let s2 = s.advance_with_epoch(&d, dt);
assert!((s2.epoch - s.epoch - dt).value().abs() < 1e-12);
}
#[test]
fn state_derivative_velocity_accessor() {
let d = make_deriv();
assert_eq!(d.velocity().x().value(), d.vel.x().value());
}
#[test]
fn state_derivative_acceleration_accessor() {
let d = make_deriv();
assert_eq!(d.acceleration().x().value(), d.acc.x().value());
}
#[test]
fn scaled_doubles_components() {
let d = make_deriv();
let d2 = d.scaled(2.0);
assert!((d2.vel.y().value() - 2.0 * d.vel.y().value()).abs() < 1e-14);
assert!((d2.acc.x().value() - 2.0 * d.acc.x().value()).abs() < 1e-14);
}
#[test]
fn add_sums_components() {
let d = make_deriv();
let d2 = d.add(&d);
assert!((d2.vel.y().value() - 2.0 * d.vel.y().value()).abs() < 1e-14);
}
#[test]
fn rk4_combine_uniform_gives_same_as_scaled() {
let d = make_deriv();
let combined = StateDerivative::rk4_combine(&d, &d, &d, &d);
assert!((combined.vel.x().value() - d.vel.x().value()).abs() < 1e-14);
assert!((combined.acc.x().value() - d.acc.x().value()).abs() < 1e-14);
}
}