use std::{collections::HashMap, str::FromStr};
use log::warn;
use super::{parser::Assignment, KPLItem, KPLValue, Parameter};
#[derive(Debug, Default)]
pub struct FKItem {
pub body_id: Option<i32>,
pub name: Option<String>,
pub data: HashMap<Parameter, KPLValue>,
}
impl KPLItem for FKItem {
type Parameter = Parameter;
fn extract_key(data: &Assignment) -> i32 {
if data.keyword.starts_with("FRAME_") || data.keyword.starts_with("TKFRAME_") {
let onward = &data.keyword[data.keyword.find('_').unwrap() + 1..];
match onward.find('_') {
Some(frm_key_pos) => match onward[..frm_key_pos].parse::<i32>() {
Ok(frame_id) => frame_id,
Err(_) => {
data.value.trim().parse::<i32>().unwrap_or(-1)
}
},
None => -1,
}
} else {
-1
}
}
fn data(&self) -> &HashMap<Self::Parameter, KPLValue> {
&self.data
}
fn parse(&mut self, data: Assignment) {
if data.keyword.starts_with("FRAME_") || data.keyword.starts_with("TKFRAME_") {
if data.value.is_empty() {
warn!("Empty value for `{}` -- ignoring", data.keyword);
return;
}
match self.body_id {
None => {
let next_ = data.keyword.find('_').unwrap();
self.name = Some(data.keyword[next_ + 1..].to_string());
if let Ok(parsed_id) = data.value.parse::<i32>() {
self.body_id = Some(parsed_id);
} else {
warn!("Failed to parse body ID from value `{}`", data.value);
}
}
Some(body_id) => {
let param = data
.keyword
.replace("TKFRAME_", "_")
.replace("FRAME_", "_")
.replace(&format!("_{body_id}_"), "");
if let Ok(param) = Parameter::from_str(¶m) {
self.data.insert(param, data.to_value());
} else {
warn!("Unknown parameter `{param}` -- ignoring");
}
}
}
}
}
}
#[cfg(test)]
mod fk_ut {
use crate::{
constants::orientations::{MOON_ME_DE421, MOON_ME_DE440_ME421},
naif::kpl::parser::convert_fk,
};
use super::{FKItem, KPLValue, Parameter};
#[test]
fn test_parse_fk() {
use crate::naif::kpl::parser::parse_file;
let assignments = parse_file::<_, FKItem>("../data/moon_080317.txt", false).unwrap();
assert_eq!(assignments.len(), 5);
assert_eq!(assignments[&31000].name, Some("MOON_PA".to_string()));
assert_eq!(assignments[&31000].body_id, Some(31000));
assert_eq!(
assignments[&31000].data[&Parameter::Class],
KPLValue::Integer(4)
);
assert_eq!(
assignments[&31000].data[&Parameter::ClassId],
KPLValue::Integer(31000)
);
assert_eq!(
assignments[&31000].data[&Parameter::Center],
KPLValue::Integer(301)
);
assert_eq!(
assignments[&31000].data[&Parameter::Matrix],
KPLValue::Matrix(vec![1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0])
);
assert_eq!(
assignments[&31000].data[&Parameter::Relative],
KPLValue::String("MOON_PA_DE421".to_string())
);
assert_eq!(assignments[&31000].data.len(), 5);
assert_eq!(assignments[&31001].name, Some("MOON_ME".to_string()));
assert_eq!(assignments[&31001].body_id, Some(31001));
assert_eq!(
assignments[&31001].data[&Parameter::Class],
KPLValue::Integer(4)
);
assert_eq!(
assignments[&31001].data[&Parameter::ClassId],
KPLValue::Integer(31001)
);
assert_eq!(
assignments[&31001].data[&Parameter::Center],
KPLValue::Integer(301)
);
assert_eq!(
assignments[&31001].data[&Parameter::Matrix],
KPLValue::Matrix(vec![1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0])
);
assert_eq!(
assignments[&31001].data[&Parameter::Relative],
KPLValue::String("MOON_ME_DE421".to_string())
);
assert_eq!(assignments[&31001].data.len(), 5);
assert_eq!(assignments[&31006].name, Some("MOON_PA_DE421".to_string()));
assert_eq!(assignments[&31006].body_id, Some(31006));
assert_eq!(assignments[&31006].data[&Parameter::Class], 2.into());
assert_eq!(assignments[&31006].data[&Parameter::ClassId], 31006.into());
assert_eq!(assignments[&31006].data[&Parameter::Center], 301.into());
assert_eq!(assignments[&31006].data.len(), 3);
assert_eq!(assignments[&31002].name, Some("MOON_PA_DE403".to_string()));
assert_eq!(assignments[&31002].body_id, Some(31002));
assert_eq!(assignments[&31002].data[&Parameter::Class], 2.into());
assert_eq!(assignments[&31002].data[&Parameter::ClassId], 31002.into());
assert_eq!(assignments[&31002].data[&Parameter::Center], 301.into());
assert_eq!(assignments[&31002].data.len(), 3);
assert_eq!(assignments[&31007].name, Some("MOON_ME_DE421".to_string()));
assert_eq!(assignments[&31007].body_id, Some(31007));
assert_eq!(assignments[&31007].data[&Parameter::Class], 4.into());
assert_eq!(assignments[&31007].data[&Parameter::ClassId], 31007.into());
assert_eq!(assignments[&31007].data[&Parameter::Center], 301.into());
assert_eq!(
assignments[&31007].data[&Parameter::Units],
KPLValue::String("ARCSECONDS".to_string())
);
assert_eq!(
assignments[&31007].data[&Parameter::Relative],
KPLValue::String("MOON_PA_DE421".to_string())
);
assert_eq!(
assignments[&31007].data[&Parameter::Angles],
KPLValue::Matrix(vec![67.92, 78.56, 0.30])
);
assert_eq!(
assignments[&31007].data[&Parameter::Axes],
KPLValue::Matrix(vec![3.0, 2.0, 1.0])
);
assert_eq!(assignments[&31007].data.len(), 7);
}
#[test]
fn test_convert_fk() {
use std::path::PathBuf;
use std::str::FromStr;
use crate::math::rotation::{r1, r2, r3, DCM};
let dataset = convert_fk("../data/moon_080317.txt", false).unwrap();
assert_eq!(dataset.len(), 5, "expected three items");
let moon_me = dataset.get_by_name("MOON_ME_DE421").unwrap();
let moon_me_by_id = dataset.get_by_id(MOON_ME_DE421).unwrap();
assert_eq!(moon_me_by_id, moon_me);
let expected = r3((67.92 / 3600.0_f64).to_radians())
* r2((78.56 / 3600.0_f64).to_radians())
* r1((0.30 / 3600.0_f64).to_radians());
assert!((DCM::from(moon_me).rot_mat - expected).norm() < 1e-10);
println!("CRC32 = {}", dataset.crc32());
dataset
.save_as(&PathBuf::from_str("../data/moon_fk.epa").unwrap(), true)
.unwrap();
}
#[test]
fn build_de440_moon_fk() {
use std::path::PathBuf;
use std::str::FromStr;
use crate::math::rotation::{r1, r2, r3, DCM};
let dataset = convert_fk("../data/moon_de440_220930.txt", false).unwrap();
assert_eq!(dataset.len(), 4, "expected three items");
let moon_me = dataset.get_by_name("MOON_ME_DE440_ME421").unwrap();
let moon_me_by_id = dataset.get_by_id(MOON_ME_DE440_ME421).unwrap();
assert_eq!(moon_me_by_id, moon_me);
let expected = r3((67.8526 / 3600.0_f64).to_radians())
* r2((78.6944 / 3600.0_f64).to_radians())
* r1((0.2785 / 3600.0_f64).to_radians());
assert!((DCM::from(moon_me).rot_mat - expected).norm() < 1e-10);
println!("CRC32 = {}", dataset.crc32()); dataset
.save_as(
&PathBuf::from_str("../data/moon_fk_de440.epa").unwrap(),
true,
)
.unwrap();
}
}