nyx_space/md/opti/multipleshooting/
altitude_heuristic.rs1use super::ctrlnodes::Node;
20use super::multishoot::MultipleShooting;
21pub use super::CostFunction;
22use super::{
23 MultiShootAlmanacSnafu, MultiShootPhysicsSnafu, MultiShootTrajSnafu, MultipleShootingError,
24 TargetingSnafu,
25};
26use crate::errors::TargetingError;
27use crate::md::{prelude::*, PropSnafu};
28use crate::{Orbit, Spacecraft};
29use log::error;
30use snafu::ResultExt;
31
32impl<'a> MultipleShooting<'a, Node, 3, 3> {
33 pub fn linear_altitude_heuristic(
39 x0: Spacecraft,
40 xf: Orbit,
41 node_count: usize,
42 body_frame: Frame,
43 prop: &'a Propagator<SpacecraftDynamics>,
44 almanac: Arc<Almanac>,
45 ) -> Result<Self, MultipleShootingError> {
46 if node_count < 3 {
47 error!("At least three nodes are needed for a multiple shooting optimization");
48 return Err(MultipleShootingError::TargetingError {
49 segment: 0_usize,
50 source: Box::new(TargetingError::UnderdeterminedProblem),
51 });
52 }
53
54 let delta_t = xf.epoch - x0.epoch();
55 let xf_bf = almanac
56 .transform_to(xf, body_frame, None)
57 .context(MultiShootAlmanacSnafu {
58 action: "converting node into the body frame",
59 })?;
60
61 let duration_increment = (xf.epoch - x0.epoch()) / (node_count as f64);
62
63 let (_, traj) = prop
64 .with(x0, almanac.clone())
65 .for_duration_with_traj(delta_t)
66 .context(PropSnafu)
67 .context(TargetingSnafu { segment: 0_usize })?;
68
69 let mut nodes = Vec::with_capacity(node_count + 1);
71 let mut prev_node_epoch = x0.epoch();
72
73 let inertial_frame = x0.orbit.frame;
74 for i in 0..node_count {
75 let this_epoch = prev_node_epoch + duration_increment;
77 let orbit_point = traj.at(this_epoch).context(MultiShootTrajSnafu)?.orbit;
78 let orbit_point_bf = almanac
80 .clone()
81 .transform_to(orbit_point, body_frame, None)
82 .context(MultiShootAlmanacSnafu {
83 action: "converting node into the body frame",
84 })?;
85
86 let desired_alt_i = (xf_bf.height_km().context(MultiShootPhysicsSnafu)?
88 - orbit_point_bf.height_km().context(MultiShootPhysicsSnafu)?)
89 / ((node_count - i) as f64).sqrt();
90 let node_bf = Orbit::try_latlongalt(
92 orbit_point_bf
93 .latitude_deg()
94 .context(MultiShootPhysicsSnafu)?,
95 orbit_point_bf.longitude_deg(),
96 orbit_point_bf.height_km().context(MultiShootPhysicsSnafu)? + desired_alt_i,
97 this_epoch,
98 body_frame,
99 )
100 .context(MultiShootPhysicsSnafu)?;
101
102 let this_node = almanac
104 .transform_to(node_bf, inertial_frame, None)
105 .context(MultiShootAlmanacSnafu {
106 action: "converting node back into the inertial frame",
107 })?
108 .radius_km;
109
110 nodes.push(Node {
111 x: this_node[0],
112 y: this_node[1],
113 z: this_node[2],
114 vmag: 0.0,
115 frame: inertial_frame,
116 epoch: this_epoch,
117 });
118 prev_node_epoch = this_epoch;
119 }
120 Ok(Self {
121 prop,
122 targets: nodes,
123 x0,
124 xf,
125 current_iteration: 0,
126 max_iterations: 100,
127 improvement_threshold: 0.01,
128 variables: [
129 Vary::VelocityX.into(),
130 Vary::VelocityY.into(),
131 Vary::VelocityZ.into(),
132 ],
133 all_dvs: Vec::with_capacity(node_count),
134 })
135 }
136}