pub struct Ruggiero {
pub objectives: Vec<ObjectiveEfficiency>,
pub max_eclipse_prct: Option<f64>,
pub init_state: Spacecraft,
}Expand description
Ruggiero defines the closed loop guidance law from IEPC 2011-102
Fields§
§objectives: Vec<ObjectiveEfficiency>Stores the objectives, and their associated efficiency threshold (set to zero if not minimum efficiency).
max_eclipse_prct: Option<f64>If defined, coast until vehicle is out of the provided eclipse state.
init_state: SpacecraftImplementations§
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_info(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(
58 StateParameter::Element(OrbitalElement::SemiMajorAxis),
59 42_165.0,
60 20.0,
61 ),
62 Objective::within_tolerance(
63 StateParameter::Element(OrbitalElement::Eccentricity),
64 0.001,
65 5e-5,
66 ),
67 Objective::within_tolerance(
68 StateParameter::Element(OrbitalElement::Inclination),
69 0.05,
70 1e-2,
71 ),
72 ];
73
74 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2)?;
75 println!("{ruggiero_ctrl}");
76
77 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
78
79 let mut jgm3_meta = MetaFile {
80 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
81 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
82 };
83 jgm3_meta.process(true)?;
84
85 let harmonics = GravityField::new(GravityFieldData::from_cof(
86 &jgm3_meta.uri,
87 8,
88 8,
89 true,
90 almanac.frame_info(IAU_EARTH_FRAME)?,
91 )?);
92 orbital_dyn.accel_models.push(harmonics);
93
94 let srp_dyn = SolarPressure::default_flux(EARTH_J2000, &almanac)?;
95 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
96 .with_guidance_law(ruggiero_ctrl.clone());
97
98 println!("{sc_dynamics}");
99
100 // Finally, let's use the Monte Carlo framework built into Nyx to propagate spacecraft.
101
102 // Let's start by defining the dispersion.
103 // 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.
104 // Note that additional validation on the MVN is in progress -- https://github.com/nyx-space/nyx/issues/339.
105 let mc_rv = MvnSpacecraft::new(
106 sc,
107 vec![StateDispersion::zero_mean(
108 StateParameter::Element(OrbitalElement::SemiMajorAxis),
109 3.0,
110 )],
111 )?;
112
113 let my_mc = MonteCarlo::new(
114 sc, // Nominal state
115 mc_rv,
116 "03_geo_sk".to_string(), // Scenario name
117 None, // No specific seed specified, so one will be drawn from the computer's entropy.
118 );
119
120 // Build the propagator setup.
121 let setup = Propagator::rk89(
122 sc_dynamics.clone(),
123 IntegratorOptions::builder()
124 .min_step(10.0_f64.seconds())
125 .error_ctrl(ErrorControl::RSSCartesianStep)
126 .build(),
127 );
128
129 let num_runs = 25;
130 let rslts = my_mc.run_until_epoch(setup, almanac.clone(), sc.epoch() + prop_time, num_runs);
131
132 assert_eq!(rslts.runs.len(), num_runs);
133
134 rslts.to_parquet("03_geo_sk.parquet", ExportCfg::default())?;
135
136 Ok(())
137}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_info(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(
69 StateParameter::Element(OrbitalElement::SemiMajorAxis),
70 42_165.0,
71 20.0,
72 ),
73 Objective::within_tolerance(
74 StateParameter::Element(OrbitalElement::Eccentricity),
75 0.001,
76 5e-5,
77 ),
78 Objective::within_tolerance(
79 StateParameter::Element(OrbitalElement::Inclination),
80 0.05,
81 1e-2,
82 ),
83 ];
84
85 // Ensure that we only thrust if we have more than 20% illumination.
86 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
87 println!("{ruggiero_ctrl}");
88
89 // Define the high fidelity dynamics
90
91 // Set up the spacecraft dynamics.
92
93 // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
94 // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
95 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
96
97 // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
98 // We're using the JGM3 model here, which is the default in GMAT.
99 let mut jgm3_meta = MetaFile {
100 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
101 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
102 };
103 // And let's download it if we don't have it yet.
104 jgm3_meta.process(true)?;
105
106 // Build the spherical harmonics.
107 // The harmonics must be computed in the body fixed frame.
108 // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
109 let harmonics = GravityField::new(
110 GravityFieldData::from_cof(
111 &jgm3_meta.uri,
112 8,
113 8,
114 true,
115 almanac.frame_info(IAU_EARTH_FRAME)?,
116 )
117 .unwrap(),
118 );
119
120 // Include the spherical harmonics into the orbital dynamics.
121 orbital_dyn.accel_models.push(harmonics);
122
123 // We define the solar radiation pressure, using the default solar flux and accounting only
124 // for the eclipsing caused by the Earth.
125 let srp_dyn = SolarPressure::default_flux(EARTH_J2000, &almanac)?;
126
127 // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
128 // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
129 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
130 .with_guidance_law(ruggiero_ctrl.clone());
131
132 println!("{orbit:x}");
133
134 // We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
135 let (final_state, traj) = Propagator::rk89(
136 sc_dynamics.clone(),
137 IntegratorOptions::builder()
138 .min_step(10.0_f64.seconds())
139 .error_ctrl(ErrorControl::RSSCartesianStep)
140 .build(),
141 )
142 .with(sc, almanac.clone())
143 .for_duration_with_traj(prop_time)?;
144
145 let prop_usage = sc.mass.prop_mass_kg - final_state.mass.prop_mass_kg;
146 println!("{:x}", final_state.orbit);
147 println!("prop usage: {prop_usage:.3} kg");
148
149 // Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
150 traj.to_parquet("./03_geo_raise.parquet", ExportCfg::default())?;
151
152 for status_line in ruggiero_ctrl.status(&final_state) {
153 println!("{status_line}");
154 }
155
156 ruggiero_ctrl
157 .achieved(&final_state)
158 .expect("objective not achieved");
159
160 Ok(())
161}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 efficiency(
parameter: &StateParameter,
osc_orbit: &Orbit,
) -> Result<f64, GuidanceError>
pub fn efficiency( 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_info(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(
69 StateParameter::Element(OrbitalElement::SemiMajorAxis),
70 42_165.0,
71 20.0,
72 ),
73 Objective::within_tolerance(
74 StateParameter::Element(OrbitalElement::Eccentricity),
75 0.001,
76 5e-5,
77 ),
78 Objective::within_tolerance(
79 StateParameter::Element(OrbitalElement::Inclination),
80 0.05,
81 1e-2,
82 ),
83 ];
84
85 // Ensure that we only thrust if we have more than 20% illumination.
86 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
87 println!("{ruggiero_ctrl}");
88
89 // Define the high fidelity dynamics
90
91 // Set up the spacecraft dynamics.
92
93 // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
94 // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
95 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
96
97 // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
98 // We're using the JGM3 model here, which is the default in GMAT.
99 let mut jgm3_meta = MetaFile {
100 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
101 crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
102 };
103 // And let's download it if we don't have it yet.
104 jgm3_meta.process(true)?;
105
106 // Build the spherical harmonics.
107 // The harmonics must be computed in the body fixed frame.
108 // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
109 let harmonics = GravityField::new(
110 GravityFieldData::from_cof(
111 &jgm3_meta.uri,
112 8,
113 8,
114 true,
115 almanac.frame_info(IAU_EARTH_FRAME)?,
116 )
117 .unwrap(),
118 );
119
120 // Include the spherical harmonics into the orbital dynamics.
121 orbital_dyn.accel_models.push(harmonics);
122
123 // We define the solar radiation pressure, using the default solar flux and accounting only
124 // for the eclipsing caused by the Earth.
125 let srp_dyn = SolarPressure::default_flux(EARTH_J2000, &almanac)?;
126
127 // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
128 // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
129 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
130 .with_guidance_law(ruggiero_ctrl.clone());
131
132 println!("{orbit:x}");
133
134 // We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
135 let (final_state, traj) = Propagator::rk89(
136 sc_dynamics.clone(),
137 IntegratorOptions::builder()
138 .min_step(10.0_f64.seconds())
139 .error_ctrl(ErrorControl::RSSCartesianStep)
140 .build(),
141 )
142 .with(sc, almanac.clone())
143 .for_duration_with_traj(prop_time)?;
144
145 let prop_usage = sc.mass.prop_mass_kg - final_state.mass.prop_mass_kg;
146 println!("{:x}", final_state.orbit);
147 println!("prop usage: {prop_usage:.3} kg");
148
149 // Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
150 traj.to_parquet("./03_geo_raise.parquet", ExportCfg::default())?;
151
152 for status_line in ruggiero_ctrl.status(&final_state) {
153 println!("{status_line}");
154 }
155
156 ruggiero_ctrl
157 .achieved(&final_state)
158 .expect("objective not achieved");
159
160 Ok(())
161}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: &Almanac)
fn next(&self, sc: &mut Spacecraft, almanac: &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>
Auto Trait Implementations§
impl Freeze for Ruggiero
impl RefUnwindSafe for Ruggiero
impl Send for Ruggiero
impl Sync for Ruggiero
impl Unpin for Ruggiero
impl UnsafeUnpin for Ruggiero
impl UnwindSafe for Ruggiero
Blanket Implementations§
impl<T> Allocation for T
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> FromDhall for Twhere
T: DeserializeOwned,
impl<T> FromDhall for Twhere
T: DeserializeOwned,
fn from_dhall(v: &Value) -> Result<T, Error>
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<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
§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.§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.