use itertools::multizip;
use std::fmt;
use std::path::PathBuf;
use std::vec::Vec;
use tool::{List, Vector, Vectors};
#[derive(Clone)]
pub struct SystemBuilderError {
pub kind: SystemBuilderErrorKind,
}
#[derive(Clone)]
pub enum SystemBuilderErrorKind {
Kernel,
Frame,
Observer,
Target,
StartDate,
Duration,
AberrationCorrection,
}
impl SystemBuilderError {
fn description(&self) -> &str {
match self.kind {
SystemBuilderErrorKind::Kernel => "the kernel must be initialized",
SystemBuilderErrorKind::Frame => "the frame must be initialized",
SystemBuilderErrorKind::Observer => "the observer must be initialized",
SystemBuilderErrorKind::Target => "the target must be initialized",
SystemBuilderErrorKind::StartDate => "the start date must be initialized",
SystemBuilderErrorKind::Duration => "the duration must be initialized",
SystemBuilderErrorKind::AberrationCorrection => {
"the aberration correction must be initialized"
}
}
}
}
impl fmt::Display for SystemBuilderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl fmt::Debug for SystemBuilderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
#[derive(Clone)]
pub struct SystemError {
pub kind: SystemErrorKind,
}
#[derive(Clone)]
pub enum SystemErrorKind {
Kernel(crate::KernelError),
Build(SystemBuilderError),
}
impl SystemError {
fn description(&self) -> String {
match self.kind {
SystemErrorKind::Kernel(ref e) => format!("error with the kernel: {}", e),
SystemErrorKind::Build(ref e) => format!("error during the build: {}", e),
}
}
}
impl fmt::Display for SystemError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl fmt::Debug for SystemError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl From<crate::KernelError> for SystemError {
fn from(err: crate::KernelError) -> SystemError {
SystemError {
kind: SystemErrorKind::Kernel(err),
}
}
}
impl From<SystemBuilderError> for SystemError {
fn from(err: SystemBuilderError) -> SystemError {
SystemError {
kind: SystemErrorKind::Build(err),
}
}
}
#[derive(Debug, Clone)]
pub struct System {
kernel: crate::Kernel,
frame: String,
observer: String,
target: String,
start_date: String,
duration: f64,
aberration_correction: String,
}
impl System {
pub fn kernel(&self) -> &crate::Kernel {
&self.kernel
}
pub fn frame(&self) -> String {
self.frame.clone()
}
pub fn observer(&self) -> String {
self.observer.clone()
}
pub fn target(&self) -> String {
self.target.clone()
}
pub fn start_date(&self) -> String {
self.start_date.clone()
}
pub fn duration(&self) -> f64 {
self.duration
}
pub fn aberration_correction(&self) -> String {
self.aberration_correction.clone()
}
pub fn load(&mut self) -> Result<(), crate::KernelError> {
Ok(self.kernel.load()?)
}
pub fn unload(&mut self) -> Result<(), crate::KernelError> {
Ok(self.kernel.unload()?)
}
pub fn time_start(&self) -> f64 {
crate::str2et(self.start_date())
}
pub fn time_end(&self) -> f64 {
crate::str2et(self.start_date()) + self.duration
}
pub fn position_start(&self) -> Vector<f64> {
let time = self.time_start();
let (position, _) = crate::spkpos(
self.target(),
time,
self.frame(),
self.aberration_correction(),
self.observer(),
);
position
}
pub fn position_end(&self) -> Vector<f64> {
let time = self.time_end();
let (position, _) = crate::spkpos(
self.target(),
time,
self.frame(),
self.aberration_correction(),
self.observer(),
);
position
}
pub fn number_points(&self, time_step: f64) -> usize {
let time_start = self.time_start();
let time_end = self.time_end();
tool::size_range_with_step(time_start, time_end, time_step)
}
pub fn times(&self, time_step: f64) -> List<f64> {
let time_start = self.time_start();
let time_end = self.time_end();
tool::linspace(time_start, time_end, time_step)
}
pub fn times_formatted(&self, time_step: f64) -> Vec<String> {
let times = self.times(time_step);
times
.iter()
.map(|&time| crate::timout(time, crate::TIME_FORMAT))
.collect()
}
pub fn positions(&self, time_step: f64) -> Vectors<f64> {
let times = self.times(time_step);
let mut positions = Vectors::zeros(times.len());
for (time, mut position) in multizip((times.iter(), positions.column_iter_mut())) {
position.copy_from(
&crate::spkpos(
self.target(),
*time,
self.frame(),
self.aberration_correction(),
self.observer(),
)
.0,
);
}
positions
}
}
#[derive(Debug, Clone, Default)]
pub struct SystemBuilder {
kernel: Option<crate::Kernel>,
frame: Option<String>,
observer: Option<String>,
target: Option<String>,
start_date: Option<String>,
duration: Option<f64>,
aberration_correction: Option<String>,
}
impl SystemBuilder {
pub fn kernel<P: Into<PathBuf>>(&mut self, file: P) -> Result<&mut Self, crate::KernelError> {
self.kernel = Some(crate::Kernel::new(file)?);
Ok(self)
}
pub fn frame<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.frame = Some(name.into());
self
}
pub fn observer<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.observer = Some(name.into());
self
}
pub fn target<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.target = Some(name.into());
self
}
pub fn start_date<S: Into<String>>(&mut self, date: S) -> &mut Self {
self.start_date = Some(date.into());
self
}
pub fn duration(&mut self, time: f64) -> &mut Self {
self.duration = Some(time);
self
}
pub fn aberration_correction<S: Into<String>>(&mut self, name: S) -> &mut Self {
self.aberration_correction = Some(name.into());
self
}
pub fn build(&self) -> Result<System, SystemBuilderError> {
match (
self.kernel.as_ref(),
self.frame.as_ref(),
self.observer.as_ref(),
self.target.as_ref(),
self.start_date.as_ref(),
self.duration,
self.aberration_correction.as_ref(),
) {
(
Some(kernel),
Some(frame),
Some(observer),
Some(target),
Some(start),
Some(duration),
Some(aberration),
) => Ok(System {
kernel: kernel.clone(),
frame: frame.clone(),
observer: observer.clone(),
target: target.clone(),
start_date: start.clone(),
duration,
aberration_correction: aberration.clone(),
}),
(None, _, _, _, _, _, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::Kernel,
}),
(_, None, _, _, _, _, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::Frame,
}),
(_, _, None, _, _, _, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::Observer,
}),
(_, _, _, None, _, _, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::Target,
}),
(_, _, _, _, None, _, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::StartDate,
}),
(_, _, _, _, _, None, _) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::Duration,
}),
(_, _, _, _, _, _, None) => Err(SystemBuilderError {
kind: SystemBuilderErrorKind::AberrationCorrection,
}),
}
}
}
#[macro_use]
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn builder_description_kernel() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.frame("J2000")
.observer("HERA")
.target("DIMORPHOS")
.start_date("2027-MAR-23 16:00:00")
.duration(129.0 * tool::DAY)
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the kernel must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_frame() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.observer("HERA")
.target("DIMORPHOS")
.start_date("2027-MAR-23 16:00:00")
.duration(129.0 * tool::DAY)
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the frame must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_observer() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.frame("J2000")
.target("DIMORPHOS")
.start_date("2027-MAR-23 16:00:00")
.duration(129.0 * tool::DAY)
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the observer must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_target() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.frame("J2000")
.observer("HERA")
.start_date("2027-MAR-23 16:00:00")
.duration(129.0 * tool::DAY)
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the target must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_start() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.frame("J2000")
.observer("HERA")
.target("DIMORPHOS")
.duration(129.0 * tool::DAY)
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the start date must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_duration() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.frame("J2000")
.observer("HERA")
.target("DIMORPHOS")
.start_date("2027-MAR-23 16:00:00")
.aberration_correction("NONE")
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the duration must be initialized"
);
Ok(())
}
#[test]
#[serial]
fn builder_description_aberration() -> Result<(), SystemError> {
let system_res = SystemBuilder::default()
.kernel("rsc/krn/hera_study_PO_EMA_2024.tm")?
.frame("J2000")
.observer("HERA")
.target("DIMORPHOS")
.start_date("2027-MAR-23 16:00:00")
.duration(129.0 * tool::DAY)
.build();
assert_eq!(
system_res.err().unwrap().description(),
"the aberration correction must be initialized"
);
Ok(())
}
}