use crate::{
ephemerides::{EphemerisError, SPKWritingSnafu},
naif::{
daf::{data_types::DataType, FileRecord, NameRecord, SummaryRecord, RCRD_LEN},
spk::summary::SPKSummaryRecord,
SPK,
},
NaifId,
};
use bytes::BytesMut;
use log::warn;
use snafu::ensure;
use std::{fs::File, io::Write};
use zerocopy::IntoBytes;
use super::Ephemeris;
impl Ephemeris {
pub fn to_spice_bsp(
&self,
naif_id: NaifId,
data_type: Option<DataType>,
) -> Result<SPK, EphemerisError> {
if self.state_data.is_empty() {
return Err(EphemerisError::SPKWritingError {
details: "ephemeris file contains no state data".to_string(),
});
}
if let Some(data_type) = data_type {
ensure!(
[
DataType::Type13HermiteUnequalStep,
DataType::Type12HermiteEqualStep,
DataType::Type9LagrangeUnequalStep
]
.contains(&data_type),
SPKWritingSnafu {
details:
("provided data type must be either Type 13 Hermite or Type 9 Lagrange")
.to_string()
}
);
}
if self.includes_covariance() {
warn!("ephemeris contains covariance, which is NOT copied to the SPICE BSP file");
}
let mut bytes = vec![];
let mut statedata_bytes = vec![];
let mut file_rcrd = FileRecord::spk("Nyx Space ANISE");
let mut raw_names = [0x20; RCRD_LEN];
for (dest, src) in raw_names
.iter_mut()
.zip(format!("{} (converted by Nyx Space ANISE)", self.object_id).as_bytes())
{
*dest = *src;
}
let name_rcrd = NameRecord { raw_names };
let interpolation = match data_type {
None => self.interpolation,
Some(desired_type) => desired_type,
};
let first_orbit = self.state_data.first_key_value().unwrap().1.orbit;
let first_frame = first_orbit.frame;
let last_orbit = self.state_data.last_key_value().unwrap().1.orbit;
let mut spk_summary = SPKSummaryRecord {
start_epoch_et_s: first_orbit.epoch.to_et_seconds(),
end_epoch_et_s: last_orbit.epoch.to_et_seconds(),
target_id: naif_id,
center_id: first_frame.ephemeris_id,
frame_id: first_frame.orientation_id,
data_type_i: interpolation.into(),
start_idx: 0,
end_idx: 0,
};
let daf_summary = SummaryRecord {
next_record: 0.0,
prev_record: 0.0,
num_summaries: 1.0,
};
let mut state_data = Vec::with_capacity(self.state_data.len() * 7);
let mut epoch_data = Vec::with_capacity(self.state_data.len());
let mut epoch_registry = Vec::with_capacity(self.state_data.len() / 100);
for (idx, (_, entry)) in self.state_data.iter().enumerate() {
let orbit = entry.orbit;
state_data.extend_from_slice(&[
orbit.radius_km.x.to_ne_bytes(),
orbit.radius_km.y.to_ne_bytes(),
orbit.radius_km.z.to_ne_bytes(),
orbit.velocity_km_s.x.to_ne_bytes(),
orbit.velocity_km_s.y.to_ne_bytes(),
orbit.velocity_km_s.z.to_ne_bytes(),
]);
epoch_data.push(orbit.epoch.to_et_seconds().to_ne_bytes());
if idx > 0 && (idx + 1) % 100 == 0 {
epoch_registry.push(orbit.epoch.to_et_seconds().to_ne_bytes());
}
}
statedata_bytes.extend_from_slice(&state_data);
statedata_bytes.extend_from_slice(&epoch_data);
statedata_bytes.extend_from_slice(&epoch_registry);
let samples_m1 = match interpolation {
DataType::Type9LagrangeUnequalStep => self.degree,
DataType::Type13HermiteUnequalStep | DataType::Type12HermiteEqualStep => {
(self.degree - 1) / 2
}
_ => self.degree,
};
statedata_bytes.push((samples_m1 as f64).to_ne_bytes());
statedata_bytes.push((self.state_data.len() as f64).to_ne_bytes());
let start_idx = 385;
let end_idx = start_idx + statedata_bytes.len() - 1;
spk_summary.start_idx = start_idx as i32;
spk_summary.end_idx = end_idx as i32;
file_rcrd.free_addr = (end_idx + 1) as u32;
place_in_rcrd(file_rcrd.as_bytes(), &mut bytes);
let summaries = [daf_summary.as_bytes(), spk_summary.as_bytes()].concat();
place_in_rcrd(&summaries, &mut bytes);
place_in_rcrd(name_rcrd.as_bytes(), &mut bytes);
bytes.extend_from_slice(statedata_bytes.as_bytes());
let u8_bytes = bytes.as_bytes();
let mut padded_bytes = vec![0u8; (u8_bytes.len() as f64 / 1024.0).ceil() as usize * 1024];
padded_bytes[..u8_bytes.len()].copy_from_slice(u8_bytes);
let mut spk = SPK {
bytes: BytesMut::from(&padded_bytes[..]),
crc32: None,
_daf_type: std::marker::PhantomData,
};
spk.set_crc32();
Ok(spk)
}
pub fn write_spice_bsp(
&self,
naif_id: NaifId,
output_fname: &str,
data_type: Option<DataType>,
) -> Result<(), EphemerisError> {
let spk = self.to_spice_bsp(naif_id, data_type)?;
match File::create(output_fname) {
Ok(mut file) => {
if let Err(e) = file.write_all(&spk.bytes) {
return Err(EphemerisError::SPKWritingError {
details: format!("{e}"),
});
};
}
Err(e) => {
return Err(EphemerisError::SPKWritingError {
details: format!("{e}"),
})
}
};
Ok(())
}
}
fn place_in_rcrd(input_bytes: &[u8], output_bytes: &mut Vec<u8>) {
let mut rcrd_bytes = [0x0; RCRD_LEN];
for (dest, src) in rcrd_bytes.iter_mut().zip(input_bytes) {
*dest = *src;
}
output_bytes.extend_from_slice(&rcrd_bytes);
}