use nalgebra::{Matrix3, Rotation3};
use std::ffi::{CStr, CString};
use std::fmt;
use std::path::PathBuf;
use tool::Vector;
pub const TIME_FORMAT: &str = "YYYY-MON-DD HR:MN:SC ::RND";
#[derive(Clone, PartialEq)]
pub struct KernelError {
pub kind: KernelErrorKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum KernelErrorKind {
AlreadyLoaded,
NotLoaded,
}
impl KernelError {
fn description(&self) -> &str {
match self.kind {
KernelErrorKind::AlreadyLoaded => "the kernel is already loaded",
KernelErrorKind::NotLoaded => "the kernel is not loaded, it cannot be unloaded",
}
}
}
impl fmt::Display for KernelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
impl fmt::Debug for KernelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.description().fmt(f)
}
}
fn load<S: AsRef<str>>(name: S) {
let _name = name.as_ref();
unsafe {
let kernel = CString::new(_name).unwrap().into_raw();
crate::c::furnsh_c(kernel);
}
}
fn unload<S: AsRef<str>>(name: S) {
let _name = name.as_ref();
unsafe {
let kernel = CString::new(_name).unwrap().into_raw();
crate::c::unload_c(kernel);
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
enum KernelStatus {
Loaded,
Unloaded,
}
#[derive(Debug, Clone)]
pub struct Kernel {
file: PathBuf,
status: KernelStatus,
}
impl Kernel {
pub fn new<P: Into<PathBuf>>(file: P) -> Result<Self, KernelError> {
let mut kernel = Self {
file: file.into(),
status: KernelStatus::Unloaded,
};
kernel.load()?;
Ok(kernel)
}
pub fn name(&self) -> String {
self.file.to_str().unwrap().to_string()
}
pub fn load(&mut self) -> Result<(), KernelError> {
match self.status {
KernelStatus::Loaded => Err(KernelError {
kind: KernelErrorKind::AlreadyLoaded,
}),
KernelStatus::Unloaded => {
load(self.name());
self.status = KernelStatus::Loaded;
Ok(())
}
}
}
pub fn unload(&mut self) -> Result<(), KernelError> {
match self.status {
KernelStatus::Unloaded => Err(KernelError {
kind: KernelErrorKind::NotLoaded,
}),
KernelStatus::Loaded => {
unload(self.name());
self.status = KernelStatus::Unloaded;
Ok(())
}
}
}
}
pub fn str2et<S: Into<String>>(date: S) -> f64 {
let mut ephemeris_time = 0.0;
unsafe {
let date_c = CString::new(date.into()).unwrap().into_raw();
crate::c::str2et_c(date_c, &mut ephemeris_time);
}
ephemeris_time
}
pub fn timout(time: f64, time_format: &str) -> String {
let size = time_format.len();
let date_c = CString::new(String::with_capacity(size))
.unwrap()
.into_raw();
unsafe {
let time_format_c = CString::new(time_format.to_string()).unwrap().into_raw();
crate::c::timout_c(time, time_format_c, size as i32 + 1, date_c);
CStr::from_ptr(date_c).to_str().unwrap().to_string()
}
}
pub fn spkpos<S: Into<String>>(
target: S,
time: f64,
frame: S,
aberration_correction: S,
observer: S,
) -> (Vector<f64>, f64) {
let mut light_time = 0.0;
let mut position = [0.0, 0.0, 0.0];
unsafe {
let target_c = CString::new(target.into()).unwrap().into_raw();
let frame_c = CString::new(frame.into()).unwrap().into_raw();
let aberration_correction_c = CString::new(aberration_correction.into())
.unwrap()
.into_raw();
let observer_c = CString::new(observer.into()).unwrap().into_raw();
crate::c::spkpos_c(
target_c,
time,
frame_c,
aberration_correction_c,
observer_c,
&mut position[0],
&mut light_time,
);
}
(
Vector::new(position[0], position[1], position[2]),
light_time,
)
}
pub fn pxform<S: Into<String>>(from: S, to: S, time: f64) -> Rotation3<f64> {
let mut rotate = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]];
unsafe {
let from_c = CString::new(from.into()).unwrap().into_raw();
let to_c = CString::new(to.into()).unwrap().into_raw();
crate::c::pxform_c(from_c, to_c, time, &mut rotate[0]);
}
Rotation3::from_matrix(&Matrix3::from_rows(&[
Vector::from_iterator(rotate[0].iter().cloned()).transpose(),
Vector::from_iterator(rotate[1].iter().cloned()).transpose(),
Vector::from_iterator(rotate[2].iter().cloned()).transpose(),
]))
}
pub fn pxfrm2<S: Into<String>>(from: S, to: S, et_from: f64, et_to: f64) -> Rotation3<f64> {
let mut rotate = [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]];
unsafe {
let from_c = CString::new(from.into()).unwrap().into_raw();
let to_c = CString::new(to.into()).unwrap().into_raw();
crate::c::pxfrm2_c(from_c, to_c, et_from, et_to, &mut rotate[0]);
}
Rotation3::from_matrix(&Matrix3::from_rows(&[
Vector::from_iterator(rotate[0].iter().cloned()).transpose(),
Vector::from_iterator(rotate[1].iter().cloned()).transpose(),
Vector::from_iterator(rotate[2].iter().cloned()).transpose(),
]))
}
#[macro_use]
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn already_loaded_description() -> Result<(), KernelError> {
let mut kernel = Kernel::new("rsc/krn/hera_study_PO_EMA_2024.tm")?;
assert_eq!(
kernel.load().err().unwrap().description(),
"the kernel is already loaded"
);
kernel.unload()?;
Ok(())
}
#[test]
#[serial]
fn not_loaded_description() -> Result<(), KernelError> {
let mut kernel = Kernel::new("rsc/krn/hera_study_PO_EMA_2024.tm")?;
kernel.unload()?;
match kernel.unload() {
Ok(_) => (),
Err(e) => assert_eq!(
e.description(),
"the kernel is not loaded, it cannot be unloaded"
),
};
Ok(())
}
}