1#![doc = include_str!("./README.md")]
2extern crate log;
3extern crate nyx_space as nyx;
4extern crate pretty_env_logger as pel;
5
6use anise::{
7 almanac::metaload::MetaFile,
8 constants::{
9 celestial_objects::{MOON, SUN},
10 frames::{EARTH_J2000, IAU_EARTH_FRAME},
11 },
12};
13use hifitime::{Epoch, TimeUnits, Unit};
14use nyx::{
15 cosmic::{eclipse::EclipseLocator, GuidanceMode, Mass, MetaAlmanac, Orbit, SRPData},
16 dynamics::{
17 guidance::{GuidanceLaw, Ruggiero, Thruster},
18 Harmonics, OrbitalDynamics, SolarPressure, SpacecraftDynamics,
19 },
20 io::{gravity::HarmonicsMem, ExportCfg},
21 md::{prelude::Objective, StateParameter},
22 propagators::{ErrorControl, IntegratorOptions, Propagator},
23 Spacecraft,
24};
25use std::{error::Error, sync::Arc};
26
27fn main() -> Result<(), Box<dyn Error>> {
28 pel::init();
29
30 let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
40 let eme2k = almanac.frame_from_uid(EARTH_J2000).unwrap();
42 let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
44
45 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)) .srp(SRPData::from_area(3.0 * 6.0)) .thruster(Thruster {
57 isp_s: 4435.0,
59 thrust_N: 0.472,
60 })
61 .mode(GuidanceMode::Thrust) .build();
63
64 let prop_time = 180.0 * Unit::Day;
65
66 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 let ruggiero_ctrl = Ruggiero::from_max_eclipse(objectives, sc, 0.2).unwrap();
75 println!("{ruggiero_ctrl}");
76
77 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
84
85 let mut jgm3_meta = MetaFile {
88 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
89 crc32: Some(0xF446F027), };
91 jgm3_meta.process(true)?;
93
94 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 orbital_dyn.accel_models.push(harmonics);
104
105 let srp_dyn = SolarPressure::default(EARTH_J2000, almanac.clone())?;
108
109 let sc_dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn)
112 .with_guidance_law(ruggiero_ctrl.clone());
113
114 println!("{:x}", orbit);
115
116 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 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}