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, MOON_J2000},
11 },
12};
13use hifitime::{Epoch, Unit};
14use nyx::{
15 Spacecraft, State,
16 cosmic::{Mass, MetaAlmanac, Orbit, SRPData},
17 dynamics::{GravityField, OrbitalDynamics, SolarPressure, SpacecraftDynamics},
18 io::{ExportCfg, gravity::GravityFieldData},
19 propagators::Propagator,
20};
21use polars::{df, prelude::ParquetWriter};
22
23use std::fs::File;
24use std::{error::Error, sync::Arc};
25
26fn main() -> Result<(), Box<dyn Error>> {
27 pel::init();
28 let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
38 let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
40
41 let earth_j2000 = almanac.frame_info(EARTH_J2000)?;
47
48 let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
51 println!("{orbit:x}");
53
54 let state_bf = almanac.transform_to(orbit, IAU_EARTH_FRAME, None)?;
55 let (orig_lat_deg, orig_long_deg, orig_alt_km) = state_bf.latlongalt()?;
56
57 let sc = Spacecraft::builder()
63 .orbit(orbit)
64 .mass(Mass::from_dry_mass(9.60))
65 .srp(SRPData {
66 area_m2: 10e-4,
67 coeff_reflectivity: 1.1,
68 })
69 .build();
70 println!("{sc:x}");
71
72 let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
77
78 let mut jgm3_meta = MetaFile {
81 uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
82 crc32: Some(0xF446F027), };
84 jgm3_meta.process(true)?;
86
87 let harmonics_21x21 = GravityField::new(
91 GravityFieldData::from_cof(
92 &jgm3_meta.uri,
93 21,
94 21,
95 true,
96 almanac.frame_info(IAU_EARTH_FRAME)?,
97 )
98 .unwrap(),
99 );
100
101 orbital_dyn.accel_models.push(harmonics_21x21);
103
104 let srp_dyn = SolarPressure::new(vec![EARTH_J2000, MOON_J2000], &almanac)?;
107
108 let dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn);
111
112 println!("{dynamics}");
113
114 let (future_sc, trajectory) = Propagator::default(dynamics)
118 .with(sc, almanac.clone())
119 .until_epoch_with_traj(epoch + Unit::Century * 0.03)?;
120
121 println!("=== High fidelity propagation ===");
122 println!(
123 "SMA changed by {:.3} km",
124 orbit.sma_km()? - future_sc.orbit.sma_km()?
125 );
126 println!(
127 "ECC changed by {:.6}",
128 orbit.ecc()? - future_sc.orbit.ecc()?
129 );
130 println!(
131 "INC changed by {:.3e} deg",
132 orbit.inc_deg()? - future_sc.orbit.inc_deg()?
133 );
134 println!(
135 "RAAN changed by {:.3} deg",
136 orbit.raan_deg()? - future_sc.orbit.raan_deg()?
137 );
138 println!(
139 "AOP changed by {:.3} deg",
140 orbit.aop_deg()? - future_sc.orbit.aop_deg()?
141 );
142 println!(
143 "TA changed by {:.3} deg",
144 orbit.ta_deg()? - future_sc.orbit.ta_deg()?
145 );
146
147 println!("{trajectory}");
149
150 println!("Spacecraft params after 3 years without active control:\n{future_sc:x}");
151
152 let analysis_step = Unit::Minute * 5;
157
158 trajectory.to_parquet(
159 "./03_geo_hf_prop.parquet",
160 ExportCfg::builder().step(analysis_step).build(),
161 )?;
162
163 let mut offset_s = vec![];
167 let mut epoch_str = vec![];
168 let mut longitude_deg = vec![];
169 let mut latitude_deg = vec![];
170 let mut altitude_km = vec![];
171
172 for state in trajectory.every(analysis_step) {
173 let this_epoch = state.epoch();
177
178 offset_s.push((this_epoch - orbit.epoch).to_seconds());
179 epoch_str.push(this_epoch.to_isoformat());
180
181 let state_bf = almanac.transform_to(state.orbit, IAU_EARTH_FRAME, None)?;
182 let (lat_deg, long_deg, alt_km) = state_bf.latlongalt()?;
183 longitude_deg.push(long_deg);
184 latitude_deg.push(lat_deg);
185 altitude_km.push(alt_km);
186 }
187
188 println!(
189 "Longitude changed by {:.3} deg -- Box is 0.1 deg E-W",
190 orig_long_deg - longitude_deg.last().unwrap()
191 );
192
193 println!(
194 "Latitude changed by {:.3} deg -- Box is 0.05 deg N-S",
195 orig_lat_deg - latitude_deg.last().unwrap()
196 );
197
198 println!(
199 "Altitude changed by {:.3} km -- Box is 30 km",
200 orig_alt_km - altitude_km.last().unwrap()
201 );
202
203 let mut sk_df = df!(
205 "Offset (s)" => offset_s.clone(),
206 "Epoch (UTC)" => epoch_str.clone(),
207 "Longitude E-W (deg)" => longitude_deg,
208 "Latitude N-S (deg)" => latitude_deg,
209 "Altitude (km)" => altitude_km,
210
211 )?;
212
213 let file = File::create("./03_geo_lla.parquet").expect("Could not create file");
215
216 ParquetWriter::new(file).finish(&mut sk_df)?;
218
219 Ok(())
220}