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