pub struct Ruggiero {
pub objectives: [Option<Objective>; 5],
pub ηthresholds: [f64; 5],
pub max_eclipse_prct: Option<f64>,
/* private fields */
}
Expand description
Ruggiero defines the closed loop guidance law from IEPC 2011-102
Fields§
§objectives: [Option<Objective>; 5]
Stores the objectives
ηthresholds: [f64; 5]
Stores the minimum efficiency to correct a given orbital element, defaults to zero (i.e. always correct)
max_eclipse_prct: Option<f64>
If define, coast until vehicle is out of the provided eclipse state.
Implementations§
Source§impl Ruggiero
The Ruggiero is a locally optimal guidance law of a state for specific osculating elements.
NOTE: The efficiency parameters for AoP is NOT implemented: the paper’s formulation is broken.
WARNING: Objectives must be in degrees!
impl Ruggiero
The Ruggiero is a locally optimal guidance law of a state for specific osculating elements. NOTE: The efficiency parameters for AoP is NOT implemented: the paper’s formulation is broken. WARNING: Objectives must be in degrees!
Sourcepub fn simple(
objectives: &[Objective],
initial: Spacecraft,
) -> Result<Arc<Self>, NyxError>
pub fn simple( objectives: &[Objective], initial: Spacecraft, ) -> Result<Arc<Self>, NyxError>
Creates a new Ruggiero locally optimal control as an Arc Note: this returns an Arc so it can be plugged into the Spacecraft dynamics directly.
Sourcepub fn from_ηthresholds(
objectives: &[Objective],
ηthresholds: &[f64],
initial: Spacecraft,
) -> Result<Arc<Self>, NyxError>
pub fn from_ηthresholds( objectives: &[Objective], ηthresholds: &[f64], initial: Spacecraft, ) -> Result<Arc<Self>, NyxError>
Creates a new Ruggiero locally optimal control with the provided efficiency threshold. If the efficiency to correct the mapped orbital element is greater than the threshold, then the control law will be applied to this orbital element. Note: this returns an Arc so it can be plugged into the Spacecraft dynamics directly.
Sourcepub fn from_max_eclipse(
objectives: &[Objective],
initial: Spacecraft,
max_eclipse: f64,
) -> Result<Arc<Self>, NyxError>
pub fn from_max_eclipse( objectives: &[Objective], initial: Spacecraft, max_eclipse: f64, ) -> Result<Arc<Self>, NyxError>
Creates a new Ruggiero locally optimal control as an Arc Note: this returns an Arc so it can be plugged into the Spacecraft dynamics directly.
Examples found in repository?
28fn main() -> Result<(), Box<dyn Error>> {
29 pel::init();
30 // Set up the dynamics like in the orbit raise.
31 let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
32 let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
33
34 // Define the GEO orbit, and we're just going to maintain it very tightly.
35 let earth_j2000 = almanac.frame_from_uid(EARTH_J2000)?;
36 let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
37 println!("{orbit:x}");
38
39 let sc = Spacecraft::builder()
40 .orbit(orbit)
41 .mass(Mass::from_dry_and_prop_masses(1000.0, 1000.0)) // 1000 kg of dry mass and prop, totalling 2.0 tons
42 .srp(SRPData::from_area(3.0 * 6.0)) // Assuming 1 kW/m^2 or 18 kW, giving a margin of 4.35 kW for on-propulsion consumption
43 .thruster(Thruster {
44 // "NEXT-STEP" row in Table 2
45 isp_s: 4435.0,
46 thrust_N: 0.472,
47 })
48 .mode(GuidanceMode::Thrust) // Start thrusting immediately.
49 .build();
50
51 // Set up the spacecraft dynamics like in the orbit raise example.
52
53 let prop_time = 30.0 * Unit::Day;
54
55 // Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
56 let objectives = &[
57 Objective::within_tolerance(StateParameter::SMA, 42_164.0, 5.0), // 5 km
58 Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
59 Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
60 ];
61
62 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2)?;
63 println!("{ruggiero_ctrl}");
64
65 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
66
67 let mut jgm3_meta = MetaFile {
68 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
69 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
70 };
71 jgm3_meta.process(true)?;
72
73 let harmonics = Harmonics::from_stor(
74 almanac.frame_from_uid(IAU_EARTH_FRAME)?,
75 HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true)?,
76 );
77 orbital_dyn.accel_models.push(harmonics);
78
79 let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
80 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
81 .with_guidance_law(ruggiero_ctrl.clone());
82
83 println!("{sc_dynamics}");
84
85 // Finally, let's use the Monte Carlo framework built into Nyx to propagate spacecraft.
86
87 // Let's start by defining the dispersion.
88 // The MultivariateNormal structure allows us to define the dispersions in any of the orbital parameters, but these are applied directly in the Cartesian state space.
89 // Note that additional validation on the MVN is in progress -- https://github.com/nyx-space/nyx/issues/339.
90 let mc_rv = MvnSpacecraft::new(
91 sc,
92 vec![StateDispersion::zero_mean(StateParameter::SMA, 3.0)],
93 )?;
94
95 let my_mc = MonteCarlo::new(
96 sc, // Nominal state
97 mc_rv,
98 "03_geo_sk".to_string(), // Scenario name
99 None, // No specific seed specified, so one will be drawn from the computer's entropy.
100 );
101
102 // Build the propagator setup.
103 let setup = Propagator::rk89(
104 sc_dynamics.clone(),
105 IntegratorOptions::builder()
106 .min_step(10.0_f64.seconds())
107 .error_ctrl(ErrorControl::RSSCartesianStep)
108 .build(),
109 );
110
111 let num_runs = 25;
112 let rslts = my_mc.run_until_epoch(setup, almanac.clone(), sc.epoch() + prop_time, num_runs);
113
114 assert_eq!(rslts.runs.len(), num_runs);
115
116 // For all of the resulting trajectories, we'll want to compute the percentage of penumbra and umbra.
117
118 rslts.to_parquet(
119 "03_geo_sk.parquet",
120 Some(vec![
121 &EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
122 ]),
123 ExportCfg::default(),
124 almanac,
125 )?;
126
127 Ok(())
128}
More examples
27fn main() -> Result<(), Box<dyn Error>> {
28 pel::init();
29
30 // Dynamics models require planetary constants and ephemerides to be defined.
31 // Let's start by grabbing those by using ANISE's latest MetaAlmanac.
32 // This will automatically download the DE440s planetary ephemeris,
33 // the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
34 // parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
35 // planetary constants kernels.
36 // For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
37 // Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
38 // references to many functions.
39 let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
40 // Fetch the EME2000 frame from the Almabac
41 let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
42 // Define the orbit epoch
43 let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
44
45 // Build the spacecraft itself.
46 // Using slide 6 of https://aerospace.org/sites/default/files/2018-11/Davis-Mayberry_HPSEP_11212018.pdf
47 // for the "next gen" SEP characteristics.
48
49 // GTO start
50 let orbit = Orbit::keplerian(24505.9, 0.725, 7.05, 0.0, 0.0, 0.0, epoch, eme2k);
51
52 let sc = Spacecraft::builder()
53 .orbit(orbit)
54 .mass(Mass::from_dry_and_prop_masses(1000.0, 1000.0)) // 1000 kg of dry mass and prop, totalling 2.0 tons
55 .srp(SRPData::from_area(3.0 * 6.0)) // Assuming 1 kW/m^2 or 18 kW, giving a margin of 4.35 kW for on-propulsion consumption
56 .thruster(Thruster {
57 // "NEXT-STEP" row in Table 2
58 isp_s: 4435.0,
59 thrust_N: 0.472,
60 })
61 .mode(GuidanceMode::Thrust) // Start thrusting immediately.
62 .build();
63
64 let prop_time = 180.0 * Unit::Day;
65
66 // Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
67 let objectives = &[
68 Objective::within_tolerance(StateParameter::SMA, 42_165.0, 20.0),
69 Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
70 Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
71 ];
72
73 // Ensure that we only thrust if we have more than 20% illumination.
74 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
75 println!("{ruggiero_ctrl}");
76
77 // Define the high fidelity dynamics
78
79 // Set up the spacecraft dynamics.
80
81 // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
82 // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
83 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
84
85 // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
86 // We're using the JGM3 model here, which is the default in GMAT.
87 let mut jgm3_meta = MetaFile {
88 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
89 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
90 };
91 // And let's download it if we don't have it yet.
92 jgm3_meta.process(true)?;
93
94 // Build the spherical harmonics.
95 // The harmonics must be computed in the body fixed frame.
96 // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
97 let harmonics = Harmonics::from_stor(
98 almanac.frame_from_uid(IAU_EARTH_FRAME)?,
99 HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true).unwrap(),
100 );
101
102 // Include the spherical harmonics into the orbital dynamics.
103 orbital_dyn.accel_models.push(harmonics);
104
105 // We define the solar radiation pressure, using the default solar flux and accounting only
106 // for the eclipsing caused by the Earth.
107 let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
108
109 // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
110 // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
111 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
112 .with_guidance_law(ruggiero_ctrl.clone());
113
114 println!("{:x}", orbit);
115
116 // We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
117 let (final_state, traj) = Propagator::rk89(
118 sc_dynamics.clone(),
119 IntegratorOptions::builder()
120 .min_step(10.0_f64.seconds())
121 .error_ctrl(ErrorControl::RSSCartesianStep)
122 .build(),
123 )
124 .with(sc, almanac.clone())
125 .for_duration_with_traj(prop_time)?;
126
127 let prop_usage = sc.mass.prop_mass_kg - final_state.mass.prop_mass_kg;
128 println!("{:x}", final_state.orbit);
129 println!("prop usage: {:.3} kg", prop_usage);
130
131 // Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
132 traj.to_parquet(
133 "./03_geo_raise.parquet",
134 Some(vec![
135 &EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
136 ]),
137 ExportCfg::default(),
138 almanac,
139 )?;
140
141 for status_line in ruggiero_ctrl.status(&final_state) {
142 println!("{status_line}");
143 }
144
145 ruggiero_ctrl
146 .achieved(&final_state)
147 .expect("objective not achieved");
148
149 Ok(())
150}
Sourcepub fn set_max_eclipse(&mut self, max_eclipse: f64)
pub fn set_max_eclipse(&mut self, max_eclipse: f64)
Sets the maximum eclipse during which we can thrust.
Sourcepub fn efficency(
parameter: &StateParameter,
osc_orbit: &Orbit,
) -> Result<f64, GuidanceError>
pub fn efficency( parameter: &StateParameter, osc_orbit: &Orbit, ) -> Result<f64, GuidanceError>
Returns the efficiency η ∈ [0; 1] of correcting a specific orbital element at the provided osculating orbit
Sourcepub fn status(&self, state: &Spacecraft) -> Vec<String>
pub fn status(&self, state: &Spacecraft) -> Vec<String>
Returns whether the guidance law has achieved all goals
Examples found in repository?
27fn main() -> Result<(), Box<dyn Error>> {
28 pel::init();
29
30 // Dynamics models require planetary constants and ephemerides to be defined.
31 // Let's start by grabbing those by using ANISE's latest MetaAlmanac.
32 // This will automatically download the DE440s planetary ephemeris,
33 // the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
34 // parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
35 // planetary constants kernels.
36 // For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
37 // Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
38 // references to many functions.
39 let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
40 // Fetch the EME2000 frame from the Almabac
41 let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
42 // Define the orbit epoch
43 let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
44
45 // Build the spacecraft itself.
46 // Using slide 6 of https://aerospace.org/sites/default/files/2018-11/Davis-Mayberry_HPSEP_11212018.pdf
47 // for the "next gen" SEP characteristics.
48
49 // GTO start
50 let orbit = Orbit::keplerian(24505.9, 0.725, 7.05, 0.0, 0.0, 0.0, epoch, eme2k);
51
52 let sc = Spacecraft::builder()
53 .orbit(orbit)
54 .mass(Mass::from_dry_and_prop_masses(1000.0, 1000.0)) // 1000 kg of dry mass and prop, totalling 2.0 tons
55 .srp(SRPData::from_area(3.0 * 6.0)) // Assuming 1 kW/m^2 or 18 kW, giving a margin of 4.35 kW for on-propulsion consumption
56 .thruster(Thruster {
57 // "NEXT-STEP" row in Table 2
58 isp_s: 4435.0,
59 thrust_N: 0.472,
60 })
61 .mode(GuidanceMode::Thrust) // Start thrusting immediately.
62 .build();
63
64 let prop_time = 180.0 * Unit::Day;
65
66 // Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
67 let objectives = &[
68 Objective::within_tolerance(StateParameter::SMA, 42_165.0, 20.0),
69 Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
70 Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
71 ];
72
73 // Ensure that we only thrust if we have more than 20% illumination.
74 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
75 println!("{ruggiero_ctrl}");
76
77 // Define the high fidelity dynamics
78
79 // Set up the spacecraft dynamics.
80
81 // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
82 // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
83 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
84
85 // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
86 // We're using the JGM3 model here, which is the default in GMAT.
87 let mut jgm3_meta = MetaFile {
88 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
89 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
90 };
91 // And let's download it if we don't have it yet.
92 jgm3_meta.process(true)?;
93
94 // Build the spherical harmonics.
95 // The harmonics must be computed in the body fixed frame.
96 // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
97 let harmonics = Harmonics::from_stor(
98 almanac.frame_from_uid(IAU_EARTH_FRAME)?,
99 HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true).unwrap(),
100 );
101
102 // Include the spherical harmonics into the orbital dynamics.
103 orbital_dyn.accel_models.push(harmonics);
104
105 // We define the solar radiation pressure, using the default solar flux and accounting only
106 // for the eclipsing caused by the Earth.
107 let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
108
109 // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
110 // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
111 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
112 .with_guidance_law(ruggiero_ctrl.clone());
113
114 println!("{:x}", orbit);
115
116 // We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
117 let (final_state, traj) = Propagator::rk89(
118 sc_dynamics.clone(),
119 IntegratorOptions::builder()
120 .min_step(10.0_f64.seconds())
121 .error_ctrl(ErrorControl::RSSCartesianStep)
122 .build(),
123 )
124 .with(sc, almanac.clone())
125 .for_duration_with_traj(prop_time)?;
126
127 let prop_usage = sc.mass.prop_mass_kg - final_state.mass.prop_mass_kg;
128 println!("{:x}", final_state.orbit);
129 println!("prop usage: {:.3} kg", prop_usage);
130
131 // Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
132 traj.to_parquet(
133 "./03_geo_raise.parquet",
134 Some(vec![
135 &EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
136 ]),
137 ExportCfg::default(),
138 almanac,
139 )?;
140
141 for status_line in ruggiero_ctrl.status(&final_state) {
142 println!("{status_line}");
143 }
144
145 ruggiero_ctrl
146 .achieved(&final_state)
147 .expect("objective not achieved");
148
149 Ok(())
150}
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Ruggiero
impl<'de> Deserialize<'de> for Ruggiero
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl GuidanceLaw for Ruggiero
impl GuidanceLaw for Ruggiero
Source§fn achieved(&self, state: &Spacecraft) -> Result<bool, GuidanceError>
fn achieved(&self, state: &Spacecraft) -> Result<bool, GuidanceError>
Returns whether the guidance law has achieved all goals
Source§fn next(&self, sc: &mut Spacecraft, almanac: Arc<Almanac>)
fn next(&self, sc: &mut Spacecraft, almanac: Arc<Almanac>)
Update the state for the next iteration
Source§fn direction(&self, sc: &Spacecraft) -> Result<Vector3<f64>, GuidanceError>
fn direction(&self, sc: &Spacecraft) -> Result<Vector3<f64>, GuidanceError>
Source§fn throttle(&self, sc: &Spacecraft) -> Result<f64, GuidanceError>
fn throttle(&self, sc: &Spacecraft) -> Result<f64, GuidanceError>
impl Copy for Ruggiero
Auto Trait Implementations§
impl Freeze for Ruggiero
impl RefUnwindSafe for Ruggiero
impl Send for Ruggiero
impl Sync for Ruggiero
impl Unpin for Ruggiero
impl UnwindSafe for Ruggiero
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> FromDhall for Twhere
T: DeserializeOwned,
impl<T> FromDhall for Twhere
T: DeserializeOwned,
fn from_dhall(v: &Value) -> Result<T, Error>
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more§impl<T> Pointable for T
impl<T> Pointable for T
§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self
from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self
is actually part of its subset T
(and can be converted to it).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset
but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self
to the equivalent element of its superset.