nyx_space/dynamics/sequence/
config.rs1use anise::frames::FrameUid;
19use anise::prelude::Almanac;
20use serde::{Deserialize, Serialize};
21use serde_dhall::{SimpleType, StaticType};
22use std::collections::HashMap;
23use std::sync::Arc;
24
25use crate::dynamics::{GravityField, OrbitalDynamics};
26use crate::dynamics::{SolidTides, SpacecraftDynamics};
27use crate::io::gravity::GravityFieldData;
28use crate::propagators::Propagator;
29use crate::{
30 dynamics::{
31 Drag, PointMasses, SolarPressure,
32 guidance::{Maneuver, ObjectiveEfficiency, ObjectiveWeight},
33 },
34 io::gravity::GravityFieldConfig,
35 propagators::{IntegratorMethod, IntegratorOptions},
36};
37
38use crate::dynamics::sequence::discrete_event::DiscreteEvent;
39
40#[cfg(feature = "python")]
41use pyo3::prelude::*;
42
43#[derive(Clone, Debug, Serialize, Deserialize)]
44pub enum Phase {
45 Terminate,
46 Activity {
47 name: String,
48 propagator: String,
49 guidance: Option<Box<GuidanceConfig>>,
50 on_entry: Option<Box<DiscreteEvent>>,
52 disabled: bool,
54 },
55}
56
57impl StaticType for Phase {
58 fn static_type() -> SimpleType {
59 let mut variants = HashMap::new();
60
61 variants.insert("Terminate".to_string(), None);
64
65 let mut activity_fields = HashMap::new();
67
68 activity_fields.insert("name".to_string(), String::static_type());
69 activity_fields.insert("propagator".to_string(), String::static_type());
70
71 activity_fields.insert(
73 "guidance".to_string(),
74 <Option<GuidanceConfig> as StaticType>::static_type(),
75 );
76
77 activity_fields.insert(
78 "on_entry".to_string(),
79 <Option<DiscreteEvent> as StaticType>::static_type(),
80 );
81
82 activity_fields.insert("disabled".to_string(), bool::static_type());
83
84 variants.insert(
85 "Activity".to_string(),
86 Some(SimpleType::Record(activity_fields)),
87 );
88
89 SimpleType::Union(variants)
90 }
91}
92
93#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
98#[cfg_attr(feature = "python", pyclass(from_py_object))]
99pub struct Dynamics {
100 pub accel_models: AccelModels,
101 pub force_models: ForceModels,
102}
103
104impl Dynamics {
105 pub fn build(&self, almanac: Arc<Almanac>) -> Result<SpacecraftDynamics, String> {
106 let mut orbital_dyn = OrbitalDynamics::two_body();
108 if let Some(point_masses) = &self.accel_models.point_masses {
109 orbital_dyn
110 .accel_models
111 .push(Arc::new(point_masses.clone()));
112 }
113 if let Some(gravity_cfg) = &self.accel_models.gravity_field {
114 let grav_data = GravityFieldData::from_config(gravity_cfg.clone(), &almanac)
115 .map_err(|e| e.to_string())?;
116 let gravity_field = GravityField::new(grav_data);
117 orbital_dyn.accel_models.push(gravity_field);
118 }
119 if let Some(solid_tides) = &self.accel_models.solid_tides {
120 orbital_dyn.accel_models.push(Arc::new(solid_tides.clone()));
121 }
122 let mut sc_dyn = SpacecraftDynamics::new(orbital_dyn);
124
125 if let Some(srp) = &self.force_models.solar_pressure {
126 sc_dyn.force_models.push(Arc::new(srp.clone()));
127 }
128
129 if let Some(drag) = &self.force_models.drag {
130 sc_dyn.force_models.push(Arc::new(drag.clone()));
131 }
132
133 Ok(sc_dyn)
135 }
136}
137
138#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
144#[cfg_attr(feature = "python", pyclass(from_py_object))]
145pub struct PropagatorConfig {
146 pub dynamics: Dynamics,
147 pub method: IntegratorMethod,
148 pub options: IntegratorOptions,
149}
150
151impl PropagatorConfig {
152 pub fn build(&self, almanac: Arc<Almanac>) -> Result<Propagator<SpacecraftDynamics>, String> {
153 Ok(Propagator::new(
154 self.dynamics.build(almanac)?,
155 self.method,
156 self.options,
157 ))
158 }
159}
160
161#[derive(Clone, Default, Serialize, Deserialize, Debug)]
167#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
168pub struct AccelModels {
169 pub point_masses: Option<PointMasses>,
170 pub gravity_field: Option<GravityFieldConfig>,
171 pub solid_tides: Option<SolidTides>,
172}
173
174#[derive(Clone, Default, Serialize, Deserialize, Debug)]
179#[cfg_attr(feature = "python", pyclass(from_py_object, get_all, set_all))]
180pub struct ForceModels {
181 pub solar_pressure: Option<SolarPressure>,
182 pub drag: Option<Drag>,
183}
184
185#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
186pub struct GuidanceConfig {
187 pub thruster_model: String,
188 pub disable_prop_mass: bool,
189 pub law: SteeringLaw,
190}
191
192#[derive(Clone, Debug, Serialize, Deserialize, StaticType)]
194pub enum SteeringLaw {
195 FiniteBurn(Maneuver),
196 Kluever {
197 objectives: Vec<ObjectiveWeight>,
199 max_eclipse_prct: Option<f64>,
201 },
202 Ruggiero {
203 objectives: Vec<ObjectiveEfficiency>,
205 max_eclipse_prct: Option<f64>,
207 },
208}
209
210impl StaticType for AccelModels {
211 fn static_type() -> serde_dhall::SimpleType {
212 let mut fields = HashMap::new();
213
214 fields.insert(
215 "point_masses".to_string(),
216 SimpleType::Optional(Box::new(PointMasses::static_type())),
217 );
218
219 #[allow(dead_code)]
220 #[derive(StaticType)]
221 struct GravityFieldDhall(GravityFieldConfig, FrameUid);
222
223 fields.insert(
224 "gravity_field".to_string(),
225 SimpleType::Optional(Box::new(GravityFieldDhall::static_type())),
226 );
227
228 fields.insert(
229 "solid_tides".to_string(),
230 SimpleType::Optional(Box::new(SolidTides::static_type())),
231 );
232
233 SimpleType::Record(fields)
234 }
235}
236
237impl StaticType for ForceModels {
238 fn static_type() -> serde_dhall::SimpleType {
239 let mut fields = HashMap::new();
240
241 fields.insert(
242 "solar_pressure".to_string(),
243 SimpleType::Optional(Box::new(SolarPressure::static_type())),
244 );
245
246 fields.insert(
247 "drag".to_string(),
248 SimpleType::Optional(Box::new(Drag::static_type())),
249 );
250
251 SimpleType::Record(fields)
252 }
253}