use crate::aliases::{Position, Velocity};
use crate::frame::Frame;
use crate::time_scale::{SecondsSince, TDB};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = ""))]
pub struct CartesianState<F: Frame> {
pub position: Position<F>,
pub velocity: Velocity<F>,
pub epoch: SecondsSince<TDB>,
}
impl<F: Frame> CartesianState<F> {
#[inline]
pub const fn new(
position: Position<F>,
velocity: Velocity<F>,
epoch: SecondsSince<TDB>,
) -> Self {
Self {
position,
velocity,
epoch,
}
}
#[inline]
pub fn relabel_to<F2: Frame>(self) -> CartesianState<F2> {
CartesianState {
position: self.position.relabel_to::<F2>(),
velocity: self.velocity.relabel_to::<F2>(),
epoch: self.epoch,
}
}
}
impl<F: Frame> Copy for CartesianState<F> {}
impl<F: Frame> Clone for CartesianState<F> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<F: Frame> core::fmt::Debug for CartesianState<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CartesianState")
.field("position", &self.position)
.field("velocity", &self.velocity)
.field("epoch_s", &self.epoch.as_seconds())
.finish()
}
}
impl<F: Frame> PartialEq for CartesianState<F> {
fn eq(&self, other: &Self) -> bool {
self.position == other.position
&& self.velocity == other.velocity
&& self.epoch.value == other.epoch.value
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use super::*;
use crate::ext::Vec3Ext;
use crate::frame::RootInertial;
use glam::DVec3;
#[test]
fn cartesian_state_json_round_trips_bit_exact() {
let state = CartesianState::<RootInertial>::new(
DVec3::new(7_000_123.5, -42.25, 1_234_567.875).m_at::<RootInertial>(),
DVec3::new(-0.5, 7_546.125, 3.0).m_per_s_at::<RootInertial>(),
SecondsSince::<TDB>::from_seconds(86_400.5),
);
let json = serde_json::to_string(&state).expect("serialize");
let back: CartesianState<RootInertial> = serde_json::from_str(&json).expect("deserialize");
let (p0, p1) = (state.position.raw_si(), back.position.raw_si());
let (v0, v1) = (state.velocity.raw_si(), back.velocity.raw_si());
assert_eq!(
p0.to_array().map(f64::to_bits),
p1.to_array().map(f64::to_bits)
);
assert_eq!(
v0.to_array().map(f64::to_bits),
v1.to_array().map(f64::to_bits)
);
assert_eq!(
state.epoch.as_seconds().to_bits(),
back.epoch.as_seconds().to_bits()
);
}
#[test]
fn cartesian_state_serializes_as_raw_si_triples() {
let state = CartesianState::<RootInertial>::new(
DVec3::new(1.0, 2.0, 3.0).m_at::<RootInertial>(),
DVec3::new(4.0, 5.0, 6.0).m_per_s_at::<RootInertial>(),
SecondsSince::<TDB>::from_seconds(10.0),
);
let value: serde_json::Value = serde_json::to_value(state).expect("to_value");
assert_eq!(value["position"], serde_json::json!([1.0, 2.0, 3.0]));
assert_eq!(value["velocity"], serde_json::json!([4.0, 5.0, 6.0]));
assert_eq!(value["epoch"], serde_json::json!(10.0));
}
}