use super::dataset::DataSetT;
use super::SpacecraftDataSet;
use der::{Decode, Encode, Reader, Writer};
pub use drag::DragData;
pub use inertia::Inertia;
pub use mass::Mass;
use serde::{Deserialize, Serialize};
pub use srp::SRPData;
use tabled::{settings::Style, Table, Tabled};
mod drag;
mod inertia;
mod mass;
mod srp;
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct SpacecraftData {
pub mass: Option<Mass>,
pub srp_data: Option<SRPData>,
pub drag_data: Option<DragData>,
pub inertia: Option<Inertia>,
}
impl DataSetT for SpacecraftData {
const NAME: &'static str = "spacecraft data";
}
impl SpacecraftData {
fn available_data(&self) -> u8 {
let mut bits: u8 = 0;
if self.mass.is_some() {
bits |= 1 << 0;
}
if self.srp_data.is_some() {
bits |= 1 << 1;
}
if self.drag_data.is_some() {
bits |= 1 << 2;
}
if self.inertia.is_some() {
bits |= 1 << 3;
}
bits
}
}
impl Encode for SpacecraftData {
fn encoded_len(&self) -> der::Result<der::Length> {
let available_flags = self.available_data();
available_flags.encoded_len()?
+ self.mass.encoded_len()?
+ self.srp_data.encoded_len()?
+ self.drag_data.encoded_len()?
+ self.inertia.encoded_len()?
}
fn encode(&self, encoder: &mut impl Writer) -> der::Result<()> {
self.available_data().encode(encoder)?;
self.mass.encode(encoder)?;
self.srp_data.encode(encoder)?;
self.drag_data.encode(encoder)?;
self.inertia.encode(encoder)
}
}
impl<'a> Decode<'a> for SpacecraftData {
fn decode<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
let data_flags: u8 = decoder.decode()?;
let mass_kg = if data_flags & (1 << 0) != 0 {
Some(decoder.decode()?)
} else {
None
};
let srp_data = if data_flags & (1 << 1) != 0 {
Some(decoder.decode()?)
} else {
None
};
let drag_data = if data_flags & (1 << 2) != 0 {
Some(decoder.decode()?)
} else {
None
};
let inertia = if data_flags & (1 << 3) != 0 {
Some(decoder.decode()?)
} else {
None
};
Ok(Self {
mass: mass_kg,
srp_data,
drag_data,
inertia,
})
}
}
#[derive(Tabled, Default)]
struct SpacecraftRow {
#[tabled(rename = "Name")]
name: String,
#[tabled(rename = "ID")]
id: String,
#[tabled(rename = "Mass")]
mass: String,
#[tabled(rename = "SRP")]
srp: String,
#[tabled(rename = "Drag")]
drag: String,
#[tabled(rename = "Inertia")]
inertia: String,
}
impl SpacecraftDataSet {
pub fn describe(&self) -> String {
let binding = self.lut.entries();
let mut values = binding.values().collect::<Vec<_>>().to_vec();
values.sort_by_key(|(opt_id, _)| match opt_id {
Some(id) => *id,
None => 0,
});
let mut rows = Vec::new();
for (opt_id, opt_name) in values {
let data = if let Some(id) = opt_id {
self.get_by_id(*id).unwrap()
} else {
self.get_by_name(&opt_name.clone().unwrap()).unwrap()
};
let row = SpacecraftRow {
name: match opt_name {
Some(name) => name.clone(),
None => "Unset".to_string(),
},
id: match opt_id {
Some(id) => format!("{id}"),
None => "Unset".to_string(),
},
mass: format!("{:?}", data.mass),
srp: format!("{:?}", data.srp_data),
drag: format!("{:?}", data.drag_data),
inertia: format!("{:?}", data.inertia),
};
rows.push(row);
}
let mut tbl = Table::new(rows);
tbl.with(Style::modern());
format!("{tbl}")
}
}
#[cfg(test)]
mod spacecraft_constants_ut {
use super::{Decode, DragData, Encode, Inertia, Mass, SRPData, SpacecraftData};
#[test]
fn sc_min_repr() {
let repr = SpacecraftData::default();
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_with_srp_only() {
let repr = SpacecraftData {
srp_data: Some(SRPData::default()),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_with_drag_only() {
let repr = SpacecraftData {
drag_data: Some(DragData::default()),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_with_mass_only() {
let repr = SpacecraftData {
mass: Some(Mass::default()),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_with_inertial_only() {
let repr = SpacecraftData {
inertia: Some(Inertia::default()),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_with_srp_mass_inertia() {
let repr = SpacecraftData {
srp_data: Some(SRPData {
area_m2: 2.0,
coeff_reflectivity: 1.8,
}),
inertia: Some(Inertia {
orientation_id: -20,
i_xx_kgm2: 120.0,
i_yy_kgm2: 180.0,
i_zz_kgm2: 220.0,
i_xy_kgm2: 20.0,
i_xz_kgm2: -15.0,
i_yz_kgm2: 30.0,
}),
mass: Some(Mass::from_dry_and_prop_masses(150.0, 50.6)),
..Default::default()
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
#[test]
fn sc_full() {
let repr = SpacecraftData {
srp_data: Some(SRPData {
area_m2: 2.0,
coeff_reflectivity: 1.8,
}),
inertia: Some(Inertia {
orientation_id: -20,
i_xx_kgm2: 120.0,
i_yy_kgm2: 180.0,
i_zz_kgm2: 220.0,
i_xy_kgm2: 20.0,
i_xz_kgm2: -15.0,
i_yz_kgm2: 30.0,
}),
mass: Some(Mass::from_dry_and_prop_masses(150.0, 50.6)),
drag_data: Some(DragData::default()),
};
let mut buf = vec![];
repr.encode_to_vec(&mut buf).unwrap();
let repr_dec = SpacecraftData::from_der(&buf).unwrap();
assert_eq!(repr, repr_dec);
}
}