use anise::frames::FrameUid;
use anise::prelude::Almanac;
use serde::{Deserialize, Serialize};
use serde_dhall::{SimpleType, StaticType};
use std::collections::HashMap;
use std::sync::Arc;
use crate::dynamics::SpacecraftDynamics;
use crate::dynamics::{GravityField, OrbitalDynamics};
use crate::io::gravity::GravityFieldData;
use crate::propagators::Propagator;
use crate::{
dynamics::{
Drag, PointMasses, SolarPressure,
guidance::{Maneuver, ObjectiveEfficiency, ObjectiveWeight},
},
io::gravity::GravityFieldConfig,
propagators::{IntegratorMethod, IntegratorOptions},
};
use crate::dynamics::sequence::discrete_event::DiscreteEvent;
#[cfg(feature = "python")]
use pyo3::prelude::*;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Phase {
Terminate,
Activity {
name: String,
propagator: String,
guidance: Option<Box<GuidanceConfig>>,
on_entry: Option<Box<DiscreteEvent>>,
disabled: bool,
},
}
impl StaticType for Phase {
fn static_type() -> SimpleType {
let mut variants = HashMap::new();
variants.insert("Terminate".to_string(), None);
let mut activity_fields = HashMap::new();
activity_fields.insert("name".to_string(), String::static_type());
activity_fields.insert("propagator".to_string(), String::static_type());
activity_fields.insert(
"guidance".to_string(),
<Option<GuidanceConfig> as StaticType>::static_type(),
);
activity_fields.insert(
"on_entry".to_string(),
<Option<DiscreteEvent> as StaticType>::static_type(),
);
activity_fields.insert("disabled".to_string(), bool::static_type());
variants.insert(
"Activity".to_string(),
Some(SimpleType::Record(activity_fields)),
);
SimpleType::Union(variants)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
#[cfg_attr(feature = "python", pyclass(from_py_object))]
pub struct Dynamics {
pub accel_models: AccelModels,
pub force_models: ForceModels,
}
impl Dynamics {
pub fn build(&self, almanac: Arc<Almanac>) -> Result<SpacecraftDynamics, String> {
let mut orbital_dyn = OrbitalDynamics::two_body();
if let Some(point_masses) = &self.accel_models.point_masses {
orbital_dyn
.accel_models
.push(Arc::new(point_masses.clone()));
}
if let Some(gravity_cfg) = &self.accel_models.gravity_field {
let grav_data = GravityFieldData::from_config(gravity_cfg.clone(), &almanac)
.map_err(|e| e.to_string())?;
let gravity_field = GravityField::new(grav_data);
orbital_dyn.accel_models.push(gravity_field);
}
let mut sc_dyn = SpacecraftDynamics::new(orbital_dyn);
if let Some(srp) = &self.force_models.solar_pressure {
sc_dyn.force_models.push(Arc::new(srp.clone()));
}
if let Some(drag) = &self.force_models.drag {
sc_dyn.force_models.push(Arc::new(*drag));
}
Ok(sc_dyn)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
#[cfg_attr(feature = "python", pyclass(from_py_object))]
pub struct PropagatorConfig {
pub dynamics: Dynamics,
pub method: IntegratorMethod,
pub options: IntegratorOptions,
}
impl PropagatorConfig {
pub fn build(&self, almanac: Arc<Almanac>) -> Result<Propagator<SpacecraftDynamics>, String> {
Ok(Propagator::new(
self.dynamics.build(almanac)?,
self.method,
self.options,
))
}
}
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
pub struct AccelModels {
pub point_masses: Option<PointMasses>,
pub gravity_field: Option<GravityFieldConfig>,
}
#[derive(Clone, Default, Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
pub struct ForceModels {
pub solar_pressure: Option<SolarPressure>,
pub drag: Option<Drag>,
}
#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
pub struct GuidanceConfig {
pub thruster_model: String,
pub disable_prop_mass: bool,
pub law: SteeringLaw,
}
#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
pub enum SteeringLaw {
FiniteBurn(Maneuver),
Kluever {
objectives: Vec<ObjectiveWeight>,
max_eclipse_prct: Option<f64>,
},
Ruggiero {
objectives: Vec<ObjectiveEfficiency>,
max_eclipse_prct: Option<f64>,
},
}
impl StaticType for AccelModels {
fn static_type() -> serde_dhall::SimpleType {
let mut fields = HashMap::new();
fields.insert(
"point_masses".to_string(),
SimpleType::Optional(Box::new(PointMasses::static_type())),
);
#[allow(dead_code)]
#[derive(StaticType)]
struct GravityFieldDhall(GravityFieldConfig, FrameUid);
fields.insert(
"gravity_field".to_string(),
SimpleType::Optional(Box::new(GravityFieldDhall::static_type())),
);
SimpleType::Record(fields)
}
}
impl StaticType for ForceModels {
fn static_type() -> serde_dhall::SimpleType {
let mut fields = HashMap::new();
fields.insert(
"solar_pressure".to_string(),
SimpleType::Optional(Box::new(SolarPressure::static_type())),
);
fields.insert(
"drag".to_string(),
SimpleType::Optional(Box::new(Drag::static_type())),
);
SimpleType::Record(fields)
}
}