pub struct IntegratorOptionsBuilder<TypedBuilderFields = ((), (), (), (), (), (), (), ())> { /* private fields */ }
Expand description
Builder for IntegratorOptions
instances.
See IntegratorOptions::builder()
for more info.
Implementations§
source§impl<__min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<((), __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
impl<__min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<((), __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
pub fn init_step( self, init_step: Duration, ) -> IntegratorOptionsBuilder<((Duration,), __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
source§impl<__init_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, (), __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
impl<__init_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, (), __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
sourcepub fn min_step(
self,
min_step: Duration,
) -> IntegratorOptionsBuilder<(__init_step, (Duration,), __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
pub fn min_step( self, min_step: Duration, ) -> IntegratorOptionsBuilder<(__init_step, (Duration,), __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
Examples found in repository?
examples/03_geo_analysis/stationkeeping.rs (line 107)
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Set up the dynamics like in the orbit raise.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Define the GEO orbit, and we're just going to maintain it very tightly.
let earth_j2000 = almanac.frame_from_uid(EARTH_J2000)?;
let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
println!("{orbit:x}");
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
// Set up the spacecraft dynamics like in the orbit raise example.
let prop_time = 30.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_164.0, 5.0), // 5 km
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2)?;
println!("{ruggiero_ctrl}");
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
jgm3_meta.process(true)?;
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true)?,
);
orbital_dyn.accel_models.push(harmonics);
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{sc_dynamics}");
// Finally, let's use the Monte Carlo framework built into Nyx to propagate spacecraft.
// Let's start by defining the dispersion.
// 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.
// Note that additional validation on the MVN is in progress -- https://github.com/nyx-space/nyx/issues/339.
let mc_rv = MultivariateNormal::new(
sc,
vec![StateDispersion::zero_mean(StateParameter::SMA, 3.0)],
)?;
let my_mc = MonteCarlo::new(
sc, // Nominal state
mc_rv,
"03_geo_sk".to_string(), // Scenario name
None, // No specific seed specified, so one will be drawn from the computer's entropy.
);
// Build the propagator setup.
let setup = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
);
let num_runs = 25;
let rslts = my_mc.run_until_epoch(setup, almanac.clone(), sc.epoch() + prop_time, num_runs);
assert_eq!(rslts.runs.len(), num_runs);
// For all of the resulting trajectories, we'll want to compute the percentage of penumbra and umbra.
rslts.to_parquet(
"03_geo_sk.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
Ok(())
}
More examples
examples/03_geo_analysis/raise.rs (line 121)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Dynamics models require planetary constants and ephemerides to be defined.
// Let's start by grabbing those by using ANISE's latest MetaAlmanac.
// This will automatically download the DE440s planetary ephemeris,
// the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
// parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
// planetary constants kernels.
// For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
// Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
// references to many functions.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
// Fetch the EME2000 frame from the Almabac
let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
// Define the orbit epoch
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Build the spacecraft itself.
// Using slide 6 of https://aerospace.org/sites/default/files/2018-11/Davis-Mayberry_HPSEP_11212018.pdf
// for the "next gen" SEP characteristics.
// GTO start
let orbit = Orbit::keplerian(24505.9, 0.725, 7.05, 0.0, 0.0, 0.0, epoch, eme2k);
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
let prop_time = 180.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_165.0, 20.0),
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
// Ensure that we only thrust if we have more than 20% illumination.
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
println!("{ruggiero_ctrl}");
// Define the high fidelity dynamics
// Set up the spacecraft dynamics.
// Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
// The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
// We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
// We're using the JGM3 model here, which is the default in GMAT.
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
// And let's download it if we don't have it yet.
jgm3_meta.process(true)?;
// Build the spherical harmonics.
// The harmonics must be computed in the body fixed frame.
// We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true).unwrap(),
);
// Include the spherical harmonics into the orbital dynamics.
orbital_dyn.accel_models.push(harmonics);
// We define the solar radiation pressure, using the default solar flux and accounting only
// for the eclipsing caused by the Earth.
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
// Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
// acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{:x}", orbit);
// We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
let (final_state, traj) = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
)
.with(sc, almanac.clone())
.for_duration_with_traj(prop_time)?;
let fuel_usage = sc.fuel_mass_kg - final_state.fuel_mass_kg;
println!("{:x}", final_state.orbit);
println!("fuel usage: {:.3} kg", fuel_usage);
// Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
traj.to_parquet(
"./03_geo_raise.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
for status_line in ruggiero_ctrl.status(&final_state) {
println!("{status_line}");
}
ruggiero_ctrl
.achieved(&final_state)
.expect("objective not achieved");
Ok(())
}
source§impl<__init_step, __min_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, (), __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
impl<__init_step, __min_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, (), __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
pub fn max_step( self, max_step: Duration, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, (Duration,), __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
source§impl<__init_step, __min_step, __max_step, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, (), __attempts, __fixed_step, __error_ctrl, __integration_frame)>
impl<__init_step, __min_step, __max_step, __attempts, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, (), __attempts, __fixed_step, __error_ctrl, __integration_frame)>
pub fn tolerance( self, tolerance: f64, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, (f64,), __attempts, __fixed_step, __error_ctrl, __integration_frame)>
source§impl<__init_step, __min_step, __max_step, __tolerance, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, (), __fixed_step, __error_ctrl, __integration_frame)>
impl<__init_step, __min_step, __max_step, __tolerance, __fixed_step, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, (), __fixed_step, __error_ctrl, __integration_frame)>
pub fn attempts( self, attempts: u8, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, (u8,), __fixed_step, __error_ctrl, __integration_frame)>
source§impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, (), __error_ctrl, __integration_frame)>
impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __error_ctrl, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, (), __error_ctrl, __integration_frame)>
pub fn fixed_step( self, fixed_step: bool, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, (bool,), __error_ctrl, __integration_frame)>
source§impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, (), __integration_frame)>
impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __integration_frame> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, (), __integration_frame)>
sourcepub fn error_ctrl(
self,
error_ctrl: ErrorControl,
) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, (ErrorControl,), __integration_frame)>
pub fn error_ctrl( self, error_ctrl: ErrorControl, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, (ErrorControl,), __integration_frame)>
Examples found in repository?
examples/03_geo_analysis/stationkeeping.rs (line 108)
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Set up the dynamics like in the orbit raise.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Define the GEO orbit, and we're just going to maintain it very tightly.
let earth_j2000 = almanac.frame_from_uid(EARTH_J2000)?;
let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
println!("{orbit:x}");
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
// Set up the spacecraft dynamics like in the orbit raise example.
let prop_time = 30.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_164.0, 5.0), // 5 km
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2)?;
println!("{ruggiero_ctrl}");
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
jgm3_meta.process(true)?;
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true)?,
);
orbital_dyn.accel_models.push(harmonics);
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{sc_dynamics}");
// Finally, let's use the Monte Carlo framework built into Nyx to propagate spacecraft.
// Let's start by defining the dispersion.
// 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.
// Note that additional validation on the MVN is in progress -- https://github.com/nyx-space/nyx/issues/339.
let mc_rv = MultivariateNormal::new(
sc,
vec![StateDispersion::zero_mean(StateParameter::SMA, 3.0)],
)?;
let my_mc = MonteCarlo::new(
sc, // Nominal state
mc_rv,
"03_geo_sk".to_string(), // Scenario name
None, // No specific seed specified, so one will be drawn from the computer's entropy.
);
// Build the propagator setup.
let setup = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
);
let num_runs = 25;
let rslts = my_mc.run_until_epoch(setup, almanac.clone(), sc.epoch() + prop_time, num_runs);
assert_eq!(rslts.runs.len(), num_runs);
// For all of the resulting trajectories, we'll want to compute the percentage of penumbra and umbra.
rslts.to_parquet(
"03_geo_sk.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
Ok(())
}
More examples
examples/03_geo_analysis/raise.rs (line 122)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Dynamics models require planetary constants and ephemerides to be defined.
// Let's start by grabbing those by using ANISE's latest MetaAlmanac.
// This will automatically download the DE440s planetary ephemeris,
// the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
// parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
// planetary constants kernels.
// For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
// Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
// references to many functions.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
// Fetch the EME2000 frame from the Almabac
let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
// Define the orbit epoch
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Build the spacecraft itself.
// Using slide 6 of https://aerospace.org/sites/default/files/2018-11/Davis-Mayberry_HPSEP_11212018.pdf
// for the "next gen" SEP characteristics.
// GTO start
let orbit = Orbit::keplerian(24505.9, 0.725, 7.05, 0.0, 0.0, 0.0, epoch, eme2k);
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
let prop_time = 180.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_165.0, 20.0),
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
// Ensure that we only thrust if we have more than 20% illumination.
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
println!("{ruggiero_ctrl}");
// Define the high fidelity dynamics
// Set up the spacecraft dynamics.
// Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
// The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
// We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
// We're using the JGM3 model here, which is the default in GMAT.
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
// And let's download it if we don't have it yet.
jgm3_meta.process(true)?;
// Build the spherical harmonics.
// The harmonics must be computed in the body fixed frame.
// We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true).unwrap(),
);
// Include the spherical harmonics into the orbital dynamics.
orbital_dyn.accel_models.push(harmonics);
// We define the solar radiation pressure, using the default solar flux and accounting only
// for the eclipsing caused by the Earth.
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
// Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
// acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{:x}", orbit);
// We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
let (final_state, traj) = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
)
.with(sc, almanac.clone())
.for_duration_with_traj(prop_time)?;
let fuel_usage = sc.fuel_mass_kg - final_state.fuel_mass_kg;
println!("{:x}", final_state.orbit);
println!("fuel usage: {:.3} kg", fuel_usage);
// Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
traj.to_parquet(
"./03_geo_raise.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
for status_line in ruggiero_ctrl.status(&final_state) {
println!("{status_line}");
}
ruggiero_ctrl
.achieved(&final_state)
.expect("objective not achieved");
Ok(())
}
source§impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, ())>
impl<__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, ())>
sourcepub fn integration_frame(
self,
integration_frame: Frame,
) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, (Option<Frame>,))>
pub fn integration_frame( self, integration_frame: Frame, ) -> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, (Option<Frame>,))>
If a frame is specified and the propagator state is in a different frame, it it changed to this frame prior to integration.
Note, when setting this, it’s recommended to call strip
on the Frame.
source§impl<__init_step: Optional<Duration>, __min_step: Optional<Duration>, __max_step: Optional<Duration>, __tolerance: Optional<f64>, __attempts: Optional<u8>, __fixed_step: Optional<bool>, __error_ctrl: Optional<ErrorControl>, __integration_frame: Optional<Option<Frame>>> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
impl<__init_step: Optional<Duration>, __min_step: Optional<Duration>, __max_step: Optional<Duration>, __tolerance: Optional<f64>, __attempts: Optional<u8>, __fixed_step: Optional<bool>, __error_ctrl: Optional<ErrorControl>, __integration_frame: Optional<Option<Frame>>> IntegratorOptionsBuilder<(__init_step, __min_step, __max_step, __tolerance, __attempts, __fixed_step, __error_ctrl, __integration_frame)>
sourcepub fn build(self) -> IntegratorOptions
pub fn build(self) -> IntegratorOptions
Finalise the builder and create its IntegratorOptions
instance
Examples found in repository?
examples/03_geo_analysis/stationkeeping.rs (line 109)
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Set up the dynamics like in the orbit raise.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Define the GEO orbit, and we're just going to maintain it very tightly.
let earth_j2000 = almanac.frame_from_uid(EARTH_J2000)?;
let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
println!("{orbit:x}");
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
// Set up the spacecraft dynamics like in the orbit raise example.
let prop_time = 30.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_164.0, 5.0), // 5 km
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2)?;
println!("{ruggiero_ctrl}");
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
jgm3_meta.process(true)?;
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true)?,
);
orbital_dyn.accel_models.push(harmonics);
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{sc_dynamics}");
// Finally, let's use the Monte Carlo framework built into Nyx to propagate spacecraft.
// Let's start by defining the dispersion.
// 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.
// Note that additional validation on the MVN is in progress -- https://github.com/nyx-space/nyx/issues/339.
let mc_rv = MultivariateNormal::new(
sc,
vec![StateDispersion::zero_mean(StateParameter::SMA, 3.0)],
)?;
let my_mc = MonteCarlo::new(
sc, // Nominal state
mc_rv,
"03_geo_sk".to_string(), // Scenario name
None, // No specific seed specified, so one will be drawn from the computer's entropy.
);
// Build the propagator setup.
let setup = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
);
let num_runs = 25;
let rslts = my_mc.run_until_epoch(setup, almanac.clone(), sc.epoch() + prop_time, num_runs);
assert_eq!(rslts.runs.len(), num_runs);
// For all of the resulting trajectories, we'll want to compute the percentage of penumbra and umbra.
rslts.to_parquet(
"03_geo_sk.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
Ok(())
}
More examples
examples/03_geo_analysis/raise.rs (line 123)
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
fn main() -> Result<(), Box<dyn Error>> {
pel::init();
// Dynamics models require planetary constants and ephemerides to be defined.
// Let's start by grabbing those by using ANISE's latest MetaAlmanac.
// This will automatically download the DE440s planetary ephemeris,
// the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
// parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
// planetary constants kernels.
// For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
// Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
// references to many functions.
let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
// Fetch the EME2000 frame from the Almabac
let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
// Define the orbit epoch
let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
// Build the spacecraft itself.
// Using slide 6 of https://aerospace.org/sites/default/files/2018-11/Davis-Mayberry_HPSEP_11212018.pdf
// for the "next gen" SEP characteristics.
// GTO start
let orbit = Orbit::keplerian(24505.9, 0.725, 7.05, 0.0, 0.0, 0.0, epoch, eme2k);
let sc = Spacecraft::builder()
.orbit(orbit)
.dry_mass_kg(1000.0) // 1000 kg of dry mass
.fuel_mass_kg(1000.0) // 1000 kg of fuel, totalling 2.0 tons
.srp(SrpConfig::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
.thruster(Thruster {
// "NEXT-STEP" row in Table 2
isp_s: 4435.0,
thrust_N: 0.472,
})
.mode(GuidanceMode::Thrust) // Start thrusting immediately.
.build();
let prop_time = 180.0 * Unit::Day;
// Define the guidance law -- we're just using a Ruggiero controller as demonstrated in AAS-2004-5089.
let objectives = &[
Objective::within_tolerance(StateParameter::SMA, 42_165.0, 20.0),
Objective::within_tolerance(StateParameter::Eccentricity, 0.001, 5e-5),
Objective::within_tolerance(StateParameter::Inclination, 0.05, 1e-2),
];
// Ensure that we only thrust if we have more than 20% illumination.
let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
println!("{ruggiero_ctrl}");
// Define the high fidelity dynamics
// Set up the spacecraft dynamics.
// Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
// The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
// We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
// We're using the JGM3 model here, which is the default in GMAT.
let mut jgm3_meta = MetaFile {
uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
};
// And let's download it if we don't have it yet.
jgm3_meta.process(true)?;
// Build the spherical harmonics.
// The harmonics must be computed in the body fixed frame.
// We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
let harmonics = Harmonics::from_stor(
almanac.frame_from_uid(IAU_EARTH_FRAME)?,
HarmonicsMem::from_cof(&jgm3_meta.uri, 8, 8, true).unwrap(),
);
// Include the spherical harmonics into the orbital dynamics.
orbital_dyn.accel_models.push(harmonics);
// We define the solar radiation pressure, using the default solar flux and accounting only
// for the eclipsing caused by the Earth.
let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
// Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
// acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
.with_guidance_law(ruggiero_ctrl.clone());
println!("{:x}", orbit);
// We specify a minimum step in the propagator because the Ruggiero control would otherwise drive this step very low.
let (final_state, traj) = Propagator::rk89(
sc_dynamics.clone(),
IntegratorOptions::builder()
.min_step(10.0_f64.seconds())
.error_ctrl(ErrorControl::RSSCartesianStep)
.build(),
)
.with(sc, almanac.clone())
.for_duration_with_traj(prop_time)?;
let fuel_usage = sc.fuel_mass_kg - final_state.fuel_mass_kg;
println!("{:x}", final_state.orbit);
println!("fuel usage: {:.3} kg", fuel_usage);
// Finally, export the results for analysis, including the penumbra percentage throughout the orbit raise.
traj.to_parquet(
"./03_geo_raise.parquet",
Some(vec![
&EclipseLocator::cislunar(almanac.clone()).to_penumbra_event()
]),
ExportCfg::default(),
almanac,
)?;
for status_line in ruggiero_ctrl.status(&final_state) {
println!("{status_line}");
}
ruggiero_ctrl
.achieved(&final_state)
.expect("objective not achieved");
Ok(())
}
Trait Implementations§
Auto Trait Implementations§
impl<TypedBuilderFields> Freeze for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: Freeze,
impl<TypedBuilderFields> RefUnwindSafe for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: RefUnwindSafe,
impl<TypedBuilderFields> Send for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: Send,
impl<TypedBuilderFields> Sync for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: Sync,
impl<TypedBuilderFields> Unpin for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: Unpin,
impl<TypedBuilderFields> UnwindSafe for IntegratorOptionsBuilder<TypedBuilderFields>where
TypedBuilderFields: UnwindSafe,
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
Mutably borrows from an owned value. Read more
source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
source§unsafe fn clone_to_uninit(&self, dst: *mut T)
unsafe fn clone_to_uninit(&self, dst: *mut T)
🔬This is a nightly-only experimental API. (
clone_to_uninit
)§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>
Converts
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>
Converts
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>
The inverse inclusion map: attempts to construct
self
from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
Checks if
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
Use with care! Same as
self.to_subset
but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
The inclusion map: converts
self
to the equivalent element of its superset.