use core::fmt;
use der::{Decode, Encode, Reader, Writer};
use hifitime::{Epoch, Unit};
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(C)]
pub struct PhaseAngle<const N: usize> {
pub offset_deg: f64,
pub rate_deg: f64,
pub accel_deg: f64,
pub coeffs_count: u8,
pub coeffs: [f64; N],
}
impl<const N: usize> PhaseAngle<N> {
pub fn maybe_new(data: &[f64]) -> Option<Self> {
if data.len() < 3 || data.len() - 3 > N {
return None;
}
let mut coeffs = [0.0; N];
for (i, coeff) in data.iter().skip(3).enumerate() {
coeffs[i] = *coeff;
}
Some(Self {
offset_deg: data[0],
rate_deg: *data.get(1).unwrap_or(&0.0),
accel_deg: *data.get(2).unwrap_or(&0.0),
coeffs_count: (data.len() as u8).saturating_sub(3),
coeffs,
})
}
pub fn evaluate_deg(&self, epoch: Epoch, rate_unit: Unit) -> f64 {
let factor = epoch.to_tdb_duration().to_unit(rate_unit);
self.offset_deg + self.rate_deg * factor + self.accel_deg * factor.powi(2)
}
}
impl<const N: usize> Encode for PhaseAngle<N> {
fn encoded_len(&self) -> der::Result<der::Length> {
self.offset_deg.encoded_len()?
+ self.rate_deg.encoded_len()?
+ self.accel_deg.encoded_len()?
+ self.coeffs_count.encoded_len()?
+ self.coeffs.encoded_len()?
}
fn encode(&self, encoder: &mut impl Writer) -> der::Result<()> {
self.offset_deg.encode(encoder)?;
self.rate_deg.encode(encoder)?;
self.accel_deg.encode(encoder)?;
self.coeffs_count.encode(encoder)?;
self.coeffs.encode(encoder)
}
}
impl<'a, const N: usize> Decode<'a> for PhaseAngle<N> {
fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
Ok(Self {
offset_deg: decoder.decode()?,
rate_deg: decoder.decode()?,
accel_deg: decoder.decode()?,
coeffs_count: decoder.decode()?,
coeffs: decoder.decode()?,
})
}
}
impl<const N: usize> fmt::Display for PhaseAngle<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.accel_deg.abs() > 0.0 {
write!(
f,
"{} + {} t + {} t^2",
self.offset_deg, self.rate_deg, self.accel_deg
)
} else {
write!(f, "{} + {} t", self.offset_deg, self.rate_deg)
}
}
}
impl<const N: usize> Default for PhaseAngle<N> {
fn default() -> Self {
Self {
offset_deg: Default::default(),
rate_deg: Default::default(),
accel_deg: Default::default(),
coeffs_count: Default::default(),
coeffs: [0.0; N],
}
}
}
#[cfg(test)]
mod phase_angle_ut {
use super::{Decode, Encode, Epoch, PhaseAngle};
use hifitime::{TimeUnits, Unit};
#[test]
fn zero_repr() {
let repr = PhaseAngle::<32> {
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = PhaseAngle::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn example_repr() {
let repr = PhaseAngle::<0> {
offset_deg: 125.045,
rate_deg: -0.052992,
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = PhaseAngle::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
assert_eq!(
repr.evaluate_deg(Epoch::from_tdb_seconds(0.0), Unit::Century),
125.045
);
assert_eq!(
repr.evaluate_deg(Epoch::from_tdb_duration(1.days()), Unit::Century),
125.04499854915811
);
}
}