Skip to main content

Epoch

Struct Epoch 

#[repr(C)]
pub struct Epoch { pub duration: Duration, pub time_scale: TimeScale, }
Expand description

Defines a nanosecond-precision Epoch.

Refer to the appropriate functions for initializing this Epoch from different time scales or representations.

(Python documentation hints) :type string_repr: str

Fields§

§duration: Duration

An Epoch is always stored as the duration since the beginning of its time scale

§time_scale: TimeScale

Time scale used during the initialization of this Epoch.

Implementations§

§

impl Epoch

pub fn to_gregorian_str(&self, time_scale: TimeScale) -> String

Converts the Epoch to Gregorian in the provided time scale and in the ISO8601 format with the time scale appended to the string

pub fn to_gregorian_utc(&self) -> (i32, u8, u8, u8, u8, u8, u32)

Converts the Epoch to the Gregorian UTC equivalent as (year, month, day, hour, minute, second, nanoseconds).

§Example
use hifitime::Epoch;

let dt_tai = Epoch::from_tai_parts(1, 537582752000000000);

let dt_str = "2017-01-14T00:31:55 UTC";
let dt = Epoch::from_gregorian_str(dt_str).unwrap();

let (y, m, d, h, min, s, _) = dt_tai.to_gregorian_utc();
assert_eq!(y, 2017);
assert_eq!(m, 1);
assert_eq!(d, 14);
assert_eq!(h, 0);
assert_eq!(min, 31);
assert_eq!(s, 55);
#[cfg(feature = "std")]
{
assert_eq!("2017-01-14T00:31:55 UTC", format!("{dt_tai:?}"));
// dt_tai is initialized from TAI, so the default print is the Gregorian in that time system
assert_eq!("2017-01-14T00:32:32 TAI", format!("{dt_tai}"));
// But dt is initialized from UTC, so the default print and the debug print are both in UTC.
assert_eq!("2017-01-14T00:31:55 UTC", format!("{dt}"));
}

pub fn to_gregorian_tai(&self) -> (i32, u8, u8, u8, u8, u8, u32)

Converts the Epoch to the Gregorian TAI equivalent as (year, month, day, hour, minute, second, nanoseconds).

§Example
use hifitime::Epoch;
let dt = Epoch::from_gregorian_tai_at_midnight(1972, 1, 1);
let (y, m, d, h, min, s, _) = dt.to_gregorian_tai();
assert_eq!(y, 1972);
assert_eq!(m, 1);
assert_eq!(d, 1);
assert_eq!(h, 0);
assert_eq!(min, 0);
assert_eq!(s, 0);

pub fn to_gregorian( &self, time_scale: TimeScale, ) -> (i32, u8, u8, u8, u8, u8, u32)

Converts the Epoch to the Gregorian in the provided time scale as (year, month, day, hour, minute, second, nanoseconds).

§Example
use hifitime::{Epoch, TimeScale};
let dt = Epoch::from_gregorian_tai_at_midnight(1972, 1, 1);
let (y, m, d, h, min, s, n) = dt.to_gregorian(TimeScale::TAI);
assert_eq!(y, 1972);
assert_eq!(m, 1);
assert_eq!(d, 1);
assert_eq!(h, 0);
assert_eq!(min, 0);
assert_eq!(s, 0);
assert_eq!(n, 0);

// The epoch will be converted to UTC prior to returning the Gregorian parts.
let (y, m, d, h, min, s, n) = dt.to_gregorian(TimeScale::UTC);
assert_eq!(y, 1971);
assert_eq!(m, 12);
assert_eq!(d, 31);
assert_eq!(h, 23);
assert_eq!(min, 59);
assert_eq!(s, 50);
assert_eq!(n, 0);

pub fn maybe_from_gregorian_tai( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, ) -> Result<Epoch, HifitimeError>

Attempts to build an Epoch from the provided Gregorian date and time in TAI.

pub fn maybe_from_gregorian( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, time_scale: TimeScale, ) -> Result<Epoch, HifitimeError>

Attempts to build an Epoch from the provided Gregorian date and time in the provided time scale.

Note: The month is ONE indexed, i.e. January is month 1 and December is month 12.

pub fn from_gregorian_tai( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, ) -> Epoch

Builds an Epoch from the provided Gregorian date and time in TAI. If invalid date is provided, this function will panic. Use maybe_from_gregorian_tai if unsure.

Examples found in repository?
nyx-core/examples/05_cislunar_spacecraft_link_od/main.rs (line 63)
34fn main() -> Result<(), Box<dyn Error>> {
35    pel::init();
36
37    // ====================== //
38    // === ALMANAC SET UP === //
39    // ====================== //
40
41    let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
42
43    let out = manifest_dir.join("data/04_output/");
44
45    let almanac = Arc::new(
46        Almanac::new(
47            &manifest_dir
48                .join("data/01_planetary/pck08.pca")
49                .to_string_lossy(),
50        )
51        .unwrap()
52        .load(
53            &manifest_dir
54                .join("data/01_planetary/de440s.bsp")
55                .to_string_lossy(),
56        )
57        .unwrap(),
58    );
59
60    let eme2k = almanac.frame_info(EARTH_J2000).unwrap();
61    let moon_iau = almanac.frame_info(IAU_MOON_FRAME).unwrap();
62
63    let epoch = Epoch::from_gregorian_tai(2021, 5, 29, 19, 51, 16, 852_000);
64    let nrho = Orbit::cartesian(
65        166_473.631_302_239_7,
66        -274_715.487_253_382_7,
67        -211_233.210_176_686_7,
68        0.933_451_604_520_018_4,
69        0.436_775_046_841_900_9,
70        -0.082_211_021_250_348_95,
71        epoch,
72        eme2k,
73    );
74
75    let tx_nrho_sc = Spacecraft::from(nrho);
76
77    let state_luna = almanac.transform_to(nrho, MOON_J2000, None).unwrap();
78    println!("Start state (dynamics: Earth, Moon, Sun gravity):\n{state_luna}");
79
80    let bodies = vec![EARTH, SUN];
81    let dynamics = SpacecraftDynamics::new(OrbitalDynamics::point_masses(bodies));
82
83    let setup = Propagator::rk89(
84        dynamics,
85        IntegratorOptions::builder().max_step(0.5.minutes()).build(),
86    );
87
88    /* == Propagate the NRHO vehicle == */
89    let prop_time = 1.1 * state_luna.period().unwrap();
90
91    let (nrho_final, mut tx_traj) = setup
92        .with(tx_nrho_sc, almanac.clone())
93        .for_duration_with_traj(prop_time)
94        .unwrap();
95
96    tx_traj.name = Some("NRHO Tx SC".to_string());
97
98    println!("{tx_traj}");
99
100    /* == Propagate an LLO vehicle == */
101    let llo_orbit =
102        Orbit::try_keplerian_altitude(110.0, 1e-4, 90.0, 0.0, 0.0, 0.0, epoch, moon_iau).unwrap();
103
104    let llo_sc = Spacecraft::builder().orbit(llo_orbit).build();
105
106    let (_, llo_traj) = setup
107        .with(llo_sc, almanac.clone())
108        .until_epoch_with_traj(nrho_final.epoch())
109        .unwrap();
110
111    // Export the subset of the first two hours.
112    llo_traj
113        .clone()
114        .filter_by_offset(..2.hours())
115        .to_parquet_simple(out.join("05_caps_llo_truth.pq"))?;
116
117    /* == Setup the interlink == */
118
119    let mut measurement_types = IndexSet::new();
120    measurement_types.insert(MeasurementType::Range);
121    measurement_types.insert(MeasurementType::Doppler);
122
123    let mut stochastics = IndexMap::new();
124
125    let sa45_csac_allan_dev = 1e-11;
126
127    stochastics.insert(
128        MeasurementType::Range,
129        StochasticNoise::from_hardware_range_km(
130            sa45_csac_allan_dev,
131            10.0.seconds(),
132            link_specific::ChipRate::StandardT4B(),
133            link_specific::SN0::Average(),
134        ),
135    );
136
137    stochastics.insert(
138        MeasurementType::Doppler,
139        StochasticNoise::from_hardware_doppler_km_s(
140            sa45_csac_allan_dev,
141            10.0.seconds(),
142            link_specific::CarrierFreq::SBand(),
143            link_specific::CN0::Average(),
144        ),
145    );
146
147    let interlink = InterlinkTxSpacecraft {
148        traj: tx_traj,
149        measurement_types,
150        integration_time: None,
151        timestamp_noise_s: None,
152        ab_corr: Aberration::LT,
153        stochastic_noises: Some(stochastics),
154    };
155
156    // Devices are the transmitter, which is our NRHO vehicle.
157    let mut devices = BTreeMap::new();
158    devices.insert("NRHO Tx SC".to_string(), interlink);
159
160    let mut configs = BTreeMap::new();
161    configs.insert(
162        "NRHO Tx SC".to_string(),
163        TrkConfig::builder()
164            .strands(vec![Strand {
165                start: epoch,
166                end: nrho_final.epoch(),
167            }])
168            .build(),
169    );
170
171    let mut trk_sim =
172        TrackingArcSim::with_seed(devices.clone(), llo_traj.clone(), configs, 0).unwrap();
173    println!("{trk_sim}");
174
175    let trk_data = trk_sim.generate_measurements(almanac.clone()).unwrap();
176    println!("{trk_data}");
177
178    trk_data
179        .to_parquet_simple(out.clone().join("nrho_interlink_msr.pq"))
180        .unwrap();
181
182    // Run a truth OD where we estimate the LLO position
183    let llo_uncertainty = SpacecraftUncertainty::builder()
184        .nominal(llo_sc)
185        .x_km(1.0)
186        .y_km(1.0)
187        .z_km(1.0)
188        .vx_km_s(1e-3)
189        .vy_km_s(1e-3)
190        .vz_km_s(1e-3)
191        .build();
192
193    let mut proc_devices = devices.clone();
194
195    // Define the initial estimate, randomized, seed for reproducibility
196    let mut initial_estimate = llo_uncertainty.to_estimate_randomized(Some(0)).unwrap();
197    // Inflate the covariance -- https://github.com/nyx-space/nyx/issues/339
198    initial_estimate.covar *= 2.5;
199
200    // Increase the noise in the devices to accept more measurements.
201
202    for link in proc_devices.values_mut() {
203        for noise in &mut link.stochastic_noises.as_mut().unwrap().values_mut() {
204            *noise.white_noise.as_mut().unwrap() *= 3.0;
205        }
206    }
207
208    let init_err = initial_estimate
209        .orbital_state()
210        .ric_difference(&llo_orbit)
211        .unwrap();
212
213    println!("initial estimate:\n{initial_estimate}");
214    println!("RIC errors = {init_err}",);
215
216    let odp = InterlinkKalmanOD::new(
217        setup.clone(),
218        KalmanVariant::ReferenceUpdate,
219        Some(ResidRejectCrit::default()),
220        proc_devices,
221        almanac.clone(),
222    );
223
224    // Shrink the data to process.
225    let arc = trk_data.filter_by_offset(..2.hours());
226
227    let od_sol = odp.process_arc(initial_estimate, &arc).unwrap();
228
229    println!("{od_sol}");
230
231    od_sol
232        .to_parquet(
233            out.join("05_caps_interlink_od_sol.pq"),
234            ExportCfg::default(),
235        )
236        .unwrap();
237
238    let od_traj = od_sol.to_traj().unwrap();
239
240    od_traj
241        .ric_diff_to_parquet(
242            &llo_traj,
243            out.join("05_caps_interlink_llo_est_error.pq"),
244            ExportCfg::default(),
245        )
246        .unwrap();
247
248    let final_est = od_sol.estimates.last().unwrap();
249    assert!(final_est.within_3sigma(), "should be within 3 sigma");
250
251    println!("ESTIMATE\n{final_est:x}\n");
252    let truth = llo_traj.at(final_est.epoch()).unwrap();
253    println!("TRUTH\n{truth:x}");
254
255    let final_err = truth
256        .orbit
257        .ric_difference(&final_est.orbital_state())
258        .unwrap();
259    println!("ERROR {final_err}");
260
261    // Build the residuals versus reference plot.
262    let rvr_sol = odp
263        .process_arc(initial_estimate, &arc.resid_vs_ref_check())
264        .unwrap();
265
266    rvr_sol
267        .to_parquet(
268            out.join("05_caps_interlink_resid_v_ref.pq"),
269            ExportCfg::default(),
270        )
271        .unwrap();
272
273    let final_rvr = rvr_sol.estimates.last().unwrap();
274
275    println!("RMAG error {:.3} m", final_err.rmag_km() * 1e3);
276    println!(
277        "Pure prop error {:.3} m",
278        final_rvr
279            .orbital_state()
280            .ric_difference(&final_est.orbital_state())
281            .unwrap()
282            .rmag_km()
283            * 1e3
284    );
285
286    Ok(())
287}

pub fn from_gregorian_tai_at_midnight(year: i32, month: u8, day: u8) -> Epoch

Initialize from the Gregorian date at midnight in TAI.

pub fn from_gregorian_tai_at_noon(year: i32, month: u8, day: u8) -> Epoch

Initialize from the Gregorian date at noon in TAI

pub fn from_gregorian_tai_hms( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, ) -> Epoch

Initialize from the Gregorian date and time (without the nanoseconds) in TAI

pub fn maybe_from_gregorian_utc( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, ) -> Result<Epoch, HifitimeError>

Attempts to build an Epoch from the provided Gregorian date and time in UTC.

pub fn from_gregorian_utc( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, ) -> Epoch

Builds an Epoch from the provided Gregorian date and time in UTC. If invalid date is provided, this function will panic. Use maybe_from_gregorian_utc if unsure.

pub fn from_gregorian_utc_at_midnight(year: i32, month: u8, day: u8) -> Epoch

Initialize from Gregorian date in UTC at midnight

pub fn from_gregorian_utc_at_noon(year: i32, month: u8, day: u8) -> Epoch

Initialize from Gregorian date in UTC at noon

Examples found in repository?
nyx-core/examples/06_lunar_orbit_determination/main.rs (line 67)
35fn main() -> Result<(), Box<dyn Error>> {
36    pel::init();
37
38    // ====================== //
39    // === ALMANAC SET UP === //
40    // ====================== //
41
42    // Dynamics models require planetary constants and ephemerides to be defined.
43    // Let's start by grabbing those by using ANISE's MetaAlmanac.
44
45    let data_folder: PathBuf = [
46        env!("CARGO_MANIFEST_DIR"),
47        "examples",
48        "06_lunar_orbit_determination",
49    ]
50    .iter()
51    .collect();
52
53    let meta = data_folder.join("metaalmanac.dhall");
54
55    // Load this ephem in the general Almanac we're using for this analysis.
56    let almanac = MetaAlmanac::new(meta.to_string_lossy().as_ref())
57        .map_err(Box::new)?
58        .process(true)
59        .map_err(Box::new)?;
60
61    // Lock the almanac (an Arc is a read only structure).
62    let almanac = Arc::new(almanac);
63
64    // Build a nominal trajectory
65    // TODO: Switch this to a sequence once the OD over a spacecraft sequence is implemented.
66
67    let epoch = Epoch::from_gregorian_utc_at_noon(2024, 2, 29);
68    let moon_j2000 = almanac.frame_info(MOON_J2000)?;
69
70    // To build the trajectory we need to provide a spacecraft template.
71    let orbiter = Spacecraft::builder()
72        .mass(Mass::from_dry_and_prop_masses(1018.0, 900.0))
73        .srp(SRPData {
74            area_m2: 3.9 * 2.7,
75            coeff_reflectivity: 0.96,
76        })
77        .orbit(Orbit::try_keplerian_altitude(
78            150.0, 0.00212, 33.6, 45.0, 45.0, 0.0, epoch, moon_j2000,
79        )?) // Setting a zero orbit here because it's just a template
80        .build();
81
82    // ========================== //
83    // === BUILD NOMINAL TRAJ === //
84    // ========================== //
85
86    // Set up the spacecraft dynamics.
87
88    // Specify that the orbital dynamics must account for the graviational pull of the Earth and the Sun.
89    // The gravity of the Moon will also be accounted for since the spaceraft in a lunar orbit.
90    let mut orbital_dyn = OrbitalDynamics::point_masses(vec![EARTH, SUN, JUPITER_BARYCENTER]);
91
92    // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
93    // We're using the GRAIL JGGRX model.
94    let mut jggrx_meta = MetaFile {
95        uri: "http://public-data.nyxspace.com/nyx/models/Luna_jggrx_1500e_sha.tab.gz".to_string(),
96        crc32: Some(0x6bcacda8), // Specifying the CRC32 avoids redownloading it if it's cached.
97    };
98    // And let's download it if we don't have it yet.
99    jggrx_meta.process(true)?;
100
101    // Build the spherical harmonics.
102    // The harmonics must be computed in the body fixed frame.
103    // We're using the long term prediction of the Moon principal axes frame.
104    let moon_pa_frame = MOON_PA_FRAME.with_orient(31008);
105    let sph_harmonics = GravityField::new(GravityFieldData::from_shadr(
106        &jggrx_meta.uri,
107        80,
108        80,
109        true,
110        almanac.frame_info(moon_pa_frame)?,
111    )?);
112
113    // Include the spherical harmonics into the orbital dynamics.
114    orbital_dyn.accel_models.push(sph_harmonics);
115
116    // We define the solar radiation pressure, using the default solar flux and accounting only
117    // for the eclipsing caused by the Earth and Moon.
118    // Note that by default, enabling the SolarPressure model will also enable the estimation of the coefficient of reflectivity.
119    let srp_dyn = SolarPressure::new(vec![MOON_J2000], &almanac)?;
120
121    // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
122    // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
123    let dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn);
124
125    println!("{dynamics}");
126
127    let setup = Propagator::rk89(dynamics.clone(), IntegratorOptions::default());
128
129    let truth_traj = setup
130        .with(orbiter, almanac.clone())
131        .for_duration_with_traj(Unit::Day * 2)?
132        .1;
133
134    // ==================== //
135    // === OD SIMULATOR === //
136    // ==================== //
137
138    // Load the Deep Space Network ground stations.
139    // Nyx allows you to build these at runtime but it's pretty static so we can just load them from YAML.
140    let ground_station_file = data_folder.join("dsn-network.yaml");
141    let devices = GroundStation::load_named(ground_station_file)?;
142
143    let proc_devices = devices.clone();
144
145    // Typical OD software requires that you specify your own tracking schedule or you'll have overlapping measurements.
146    // Nyx can build a tracking schedule for you based on the first station with access.
147    let configs: BTreeMap<String, TrkConfig> =
148        TrkConfig::load_named(data_folder.join("tracking-cfg.yaml"))?;
149
150    // Build the tracking arc simulation to generate a "standard measurement".
151    let mut trk = TrackingArcSim::<Spacecraft, GroundStation>::with_seed(
152        devices.clone(),
153        truth_traj.clone(),
154        configs,
155        123, // Set a seed for reproducibility
156    )?;
157
158    trk.build_schedule(almanac.clone())?;
159    let arc = trk.generate_measurements(almanac.clone())?;
160    // Save the simulated tracking data
161    arc.to_parquet_simple("./data/04_output/06_lunar_simulated_tracking.parquet")?;
162
163    // We'll note that in our case, we have continuous coverage of LRO when the vehicle is not behind the Moon.
164    println!("{arc}");
165
166    // Now that we have simulated measurements, we'll run the orbit determination.
167
168    // ===================== //
169    // === OD ESTIMATION === //
170    // ===================== //
171
172    let sc = SpacecraftUncertainty::builder()
173        .nominal(orbiter)
174        .frame(LocalFrame::RIC)
175        .x_km(0.5)
176        .y_km(0.5)
177        .z_km(0.5)
178        .vx_km_s(5e-3)
179        .vy_km_s(5e-3)
180        .vz_km_s(5e-3)
181        .build();
182
183    // Build the filter initial estimate, which we will reuse in the filter.
184    let initial_estimate = sc.to_estimate()?;
185
186    println!("== FILTER STATE ==\n{orbiter:x}\n{initial_estimate}");
187
188    // Build the SNC in the Moon J2000 frame, specified as a velocity noise over time.
189    let process_noise = ProcessNoise3D::from_velocity_km_s(
190        &[1e-14, 1e-14, 1e-14],
191        1 * Unit::Hour,
192        10 * Unit::Minute,
193        None,
194    );
195
196    println!("{process_noise}");
197
198    // We'll set up the OD process to reject measurements whose residuals are move than 3 sigmas away from what we expect.
199    let odp = SpacecraftKalmanScalarOD::new(
200        setup,
201        KalmanVariant::ReferenceUpdate,
202        Some(ResidRejectCrit::default()),
203        proc_devices,
204        almanac.clone(),
205    )
206    .with_process_noise(process_noise);
207
208    let od_sol = odp.process_arc(initial_estimate, &arc)?;
209
210    let final_est = od_sol.estimates.last().unwrap();
211
212    println!("{final_est}");
213
214    let ric_err = truth_traj
215        .at(final_est.epoch())?
216        .orbit
217        .ric_difference(&final_est.orbital_state())?;
218    println!("== RIC at end ==");
219    println!("RIC Position (m): {:.3}", ric_err.radius_km * 1e3);
220    println!("RIC Velocity (m/s): {:.3}", ric_err.velocity_km_s * 1e3);
221
222    println!(
223        "Num residuals rejected: #{}",
224        od_sol.rejected_residuals().len()
225    );
226    println!(
227        "Percentage within +/-3: {}",
228        od_sol.residual_ratio_within_threshold(3.0).unwrap()
229    );
230    println!("Whitened residuals normal? {}", od_sol.is_normal(None)?);
231    println!("NIS test success? {}", od_sol.is_nis_consistent(None)?);
232
233    od_sol.to_parquet(
234        "./data/04_output/06_lunar_od_results.parquet",
235        ExportCfg::default(),
236    )?;
237
238    let od_trajectory = od_sol.to_traj()?;
239    // Build the RIC difference.
240    od_trajectory.ric_diff_to_parquet(
241        &truth_traj,
242        "./data/04_output/06_lunar_od_truth_error.parquet",
243        ExportCfg::default(),
244    )?;
245
246    Ok(())
247}

pub fn from_gregorian_utc_hms( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, ) -> Epoch

Initialize from the Gregorian date and time (without the nanoseconds) in UTC

Examples found in repository?
nyx-core/examples/03_geo_analysis/stationkeeping.rs (line 32)
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
Hide additional examples
nyx-core/examples/03_geo_analysis/raise.rs (line 43)
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}
nyx-core/examples/03_geo_analysis/drift.rs (line 39)
26fn main() -> Result<(), Box<dyn Error>> {
27    pel::init();
28    // Dynamics models require planetary constants and ephemerides to be defined.
29    // Let's start by grabbing those by using ANISE's latest MetaAlmanac.
30    // This will automatically download the DE440s planetary ephemeris,
31    // the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
32    // parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
33    // planetary constants kernels.
34    // For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
35    // Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
36    // references to many functions.
37    let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
38    // Define the orbit epoch
39    let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
40
41    // Define the orbit.
42    // First we need to fetch the Earth J2000 from information from the Almanac.
43    // This allows the frame to include the gravitational parameters and the shape of the Earth,
44    // defined as a tri-axial ellipoid. Note that this shape can be changed manually or in the Almanac
45    // by loading a different set of planetary constants.
46    let earth_j2000 = almanac.frame_info(EARTH_J2000)?;
47
48    // Placing this GEO bird just above Colorado.
49    // In theory, the eccentricity is zero, but in practice, it's about 1e-5 to 1e-6 at best.
50    let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
51    // Print in in Keplerian form.
52    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    // Nyx is used for high fidelity propagation, not Keplerian propagation as above.
58    // Nyx only propagates Spacecraft at the moment, which allows it to account for acceleration
59    // models such as solar radiation pressure.
60
61    // Let's build a cubesat sized spacecraft, with an SRP area of 10 cm^2 and a mass of 9.6 kg.
62    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    // Set up the spacecraft dynamics.
73
74    // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
75    // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
76    let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
77
78    // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
79    // We're using the JGM3 model here, which is the default in GMAT.
80    let mut jgm3_meta = MetaFile {
81        uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
82        crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
83    };
84    // And let's download it if we don't have it yet.
85    jgm3_meta.process(true)?;
86
87    // Build the spherical harmonics.
88    // The harmonics must be computed in the body fixed frame.
89    // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
90    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    // Include the spherical harmonics into the orbital dynamics.
102    orbital_dyn.accel_models.push(harmonics_21x21);
103
104    // We define the solar radiation pressure, using the default solar flux and accounting only
105    // for the eclipsing caused by the Earth and Moon.
106    let srp_dyn = SolarPressure::new(vec![EARTH_J2000, MOON_J2000], &almanac)?;
107
108    // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
109    // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
110    let dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn);
111
112    println!("{dynamics}");
113
114    // Finally, let's propagate this orbit to the same epoch as above.
115    // The first returned value is the spacecraft state at the final epoch.
116    // The second value is the full trajectory where the step size is variable step used by the propagator.
117    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    // We also have access to the full trajectory throughout the propagation.
148    println!("{trajectory}");
149
150    println!("Spacecraft params after 3 years without active control:\n{future_sc:x}");
151
152    // With the trajectory, let's build a few data products.
153
154    // 1. Export the trajectory as a parquet file, which includes the Keplerian orbital elements.
155
156    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    // 2. Compute the latitude, longitude, and altitude throughout the trajectory by rotating the spacecraft position into the Earth body fixed frame.
164
165    // We iterate over the trajectory, grabbing a state every two minutes.
166    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        // Convert the GEO bird state into the body fixed frame, and keep track of its latitude, longitude, and altitude.
174        // These define the GEO stationkeeping box.
175
176        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    // Build the station keeping data frame.
204    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    // Create a file to write the Parquet to
214    let file = File::create("./03_geo_lla.parquet").expect("Could not create file");
215
216    // Create a ParquetWriter and write the DataFrame to the file
217    ParquetWriter::new(file).finish(&mut sk_df)?;
218
219    Ok(())
220}
nyx-core/examples/01_orbit_prop/main.rs (line 43)
30fn main() -> Result<(), Box<dyn Error>> {
31    pel::init();
32    // Dynamics models require planetary constants and ephemerides to be defined.
33    // Let's start by grabbing those by using ANISE's latest MetaAlmanac.
34    // This will automatically download the DE440s planetary ephemeris,
35    // the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
36    // parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
37    // planetary constants kernels.
38    // For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
39    // Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
40    // references to many functions.
41    let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
42    // Define the orbit epoch
43    let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
44
45    // Define the orbit.
46    // First we need to fetch the Earth J2000 from information from the Almanac.
47    // This allows the frame to include the gravitational parameters and the shape of the Earth,
48    // defined as a tri-axial ellipoid. Note that this shape can be changed manually or in the Almanac
49    // by loading a different set of planetary constants.
50    let earth_j2000 = almanac.frame_info(EARTH_J2000)?;
51
52    let orbit =
53        Orbit::try_keplerian_altitude(300.0, 0.015, 68.5, 65.2, 75.0, 0.0, epoch, earth_j2000)?;
54    // Print in in Keplerian form.
55    println!("{orbit:x}");
56
57    // There are two ways to propagate an orbit. We can make a quick approximation assuming only two-body
58    // motion. This is a useful first order approximation but it isn't used in real-world applications.
59
60    // This approach is a feature of ANISE.
61    let future_orbit_tb = orbit.at_epoch(epoch + Unit::Day * 3)?;
62    println!("{future_orbit_tb:x}");
63
64    // Two body propagation relies solely on Kepler's laws, so only the true anomaly will change.
65    println!(
66        "SMA changed by {:.3e} km",
67        orbit.sma_km()? - future_orbit_tb.sma_km()?
68    );
69    println!(
70        "ECC changed by {:.3e}",
71        orbit.ecc()? - future_orbit_tb.ecc()?
72    );
73    println!(
74        "INC changed by {:.3e} deg",
75        orbit.inc_deg()? - future_orbit_tb.inc_deg()?
76    );
77    println!(
78        "RAAN changed by {:.3e} deg",
79        orbit.raan_deg()? - future_orbit_tb.raan_deg()?
80    );
81    println!(
82        "AOP changed by {:.3e} deg",
83        orbit.aop_deg()? - future_orbit_tb.aop_deg()?
84    );
85    println!(
86        "TA changed by {:.3} deg",
87        orbit.ta_deg()? - future_orbit_tb.ta_deg()?
88    );
89
90    // Nyx is used for high fidelity propagation, not Keplerian propagation as above.
91    // Nyx only propagates Spacecraft at the moment, which allows it to account for acceleration
92    // models such as solar radiation pressure.
93
94    // Let's build a cubesat sized spacecraft, with an SRP area of 10 cm^2 and a mass of 9.6 kg.
95    let sc = Spacecraft::builder()
96        .orbit(orbit)
97        .mass(Mass::from_dry_mass(9.60))
98        .srp(SRPData {
99            area_m2: 10e-4,
100            coeff_reflectivity: 1.1,
101        })
102        .build();
103    println!("{sc:x}");
104
105    // Set up the spacecraft dynamics.
106
107    // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
108    // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
109    let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
110
111    // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
112    // We're using the JGM3 model here, which is the default in GMAT.
113    let mut jgm3_meta = MetaFile {
114        uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
115        crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
116    };
117    // And let's download it if we don't have it yet.
118    jgm3_meta.process(true)?;
119
120    // Build the spherical harmonics.
121    // The harmonics must be computed in the body fixed frame.
122    // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
123    let harmonics_21x21 = GravityField::new(
124        GravityFieldData::from_cof(
125            &jgm3_meta.uri,
126            21,
127            21,
128            true,
129            almanac.frame_info(IAU_EARTH_FRAME)?,
130        )
131        .unwrap(),
132    );
133
134    // Include the spherical harmonics into the orbital dynamics.
135    orbital_dyn.accel_models.push(harmonics_21x21);
136
137    // We define the solar radiation pressure, using the default solar flux and accounting only
138    // for the eclipsing caused by the Earth.
139    let srp_dyn = SolarPressure::default_flux(EARTH_J2000, &almanac)?;
140
141    // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
142    // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
143    let dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn);
144
145    println!("{dynamics}");
146
147    // Finally, let's propagate this orbit to the same epoch as above.
148    // The first returned value is the spacecraft state at the final epoch.
149    // The second value is the full trajectory where the step size is variable step used by the propagator.
150    let (future_sc, trajectory) = Propagator::default(dynamics)
151        .with(sc, almanac.clone())
152        .until_epoch_with_traj(future_orbit_tb.epoch)?;
153
154    println!("=== High fidelity propagation ===");
155    println!(
156        "SMA changed by {:.3} km",
157        orbit.sma_km()? - future_sc.orbit.sma_km()?
158    );
159    println!(
160        "ECC changed by {:.6}",
161        orbit.ecc()? - future_sc.orbit.ecc()?
162    );
163    println!(
164        "INC changed by {:.3e} deg",
165        orbit.inc_deg()? - future_sc.orbit.inc_deg()?
166    );
167    println!(
168        "RAAN changed by {:.3} deg",
169        orbit.raan_deg()? - future_sc.orbit.raan_deg()?
170    );
171    println!(
172        "AOP changed by {:.3} deg",
173        orbit.aop_deg()? - future_sc.orbit.aop_deg()?
174    );
175    println!(
176        "TA changed by {:.3} deg",
177        orbit.ta_deg()? - future_sc.orbit.ta_deg()?
178    );
179
180    // We also have access to the full trajectory throughout the propagation.
181    println!("{trajectory}");
182
183    // With the trajectory, let's build a few data products.
184
185    // 1. Export the trajectory as a CCSDS OEM version 2.0 file and as a parquet file, which includes the Keplerian orbital elements.
186
187    trajectory.to_oem_file(
188        "./01_cubesat_hf_prop.oem",
189        "CUBESAT-ID".to_string(),
190        Some("Nyx Space".to_string()),
191        Some("CUBESAT".to_string()),
192        ExportCfg::builder().step(Unit::Minute * 2).build(),
193    )?;
194
195    trajectory.to_parquet_with_cfg(
196        "./01_cubesat_hf_prop.parquet",
197        ExportCfg::builder().step(Unit::Minute * 2).build(),
198    )?;
199
200    // 2. Compare the difference in the radial-intrack-crosstrack frame between the high fidelity
201    // and Keplerian propagation. The RIC frame is commonly used to compute the difference in position
202    // and velocity of different spacecraft.
203    // 3. Compute the azimuth, elevation, range, and range-rate data of that spacecraft as seen from Boulder, CO, USA.
204
205    let boulder_station = GroundStation::from_point(
206        "Boulder, CO, USA".to_string(),
207        40.014984,   // latitude in degrees
208        -105.270546, // longitude in degrees
209        1.6550,      // altitude in kilometers
210        almanac.frame_info(IAU_EARTH_FRAME)?,
211    );
212
213    // We iterate over the trajectory, grabbing a state every two minutes.
214    let mut offset_s = vec![];
215    let mut epoch_str = vec![];
216    let mut ric_x_km = vec![];
217    let mut ric_y_km = vec![];
218    let mut ric_z_km = vec![];
219    let mut ric_vx_km_s = vec![];
220    let mut ric_vy_km_s = vec![];
221    let mut ric_vz_km_s = vec![];
222
223    let mut azimuth_deg = vec![];
224    let mut elevation_deg = vec![];
225    let mut range_km = vec![];
226    let mut range_rate_km_s = vec![];
227    for state in trajectory.every(Unit::Minute * 2) {
228        // Try to compute the Keplerian/two body state just in time.
229        // This method occasionally fails to converge on an appropriate true anomaly
230        // from the mean anomaly. If that happens, we just skip this state.
231        // The high fidelity and Keplerian states diverge continuously, and we're curious
232        // about the divergence in this quick analysis.
233        let this_epoch = state.epoch();
234        match orbit.at_epoch(this_epoch) {
235            Ok(tb_then) => {
236                offset_s.push((this_epoch - orbit.epoch).to_seconds());
237                epoch_str.push(format!("{this_epoch}"));
238                // Compute the two body state just in time.
239                let ric = state.orbit.ric_difference(&tb_then)?;
240                ric_x_km.push(ric.radius_km.x);
241                ric_y_km.push(ric.radius_km.y);
242                ric_z_km.push(ric.radius_km.z);
243                ric_vx_km_s.push(ric.velocity_km_s.x);
244                ric_vy_km_s.push(ric.velocity_km_s.y);
245                ric_vz_km_s.push(ric.velocity_km_s.z);
246
247                // Compute the AER data for each state.
248                let aer = almanac.azimuth_elevation_range_sez(
249                    state.orbit,
250                    boulder_station.to_orbit(this_epoch, &almanac)?,
251                    None,
252                    None,
253                )?;
254                azimuth_deg.push(aer.azimuth_deg);
255                elevation_deg.push(aer.elevation_deg);
256                range_km.push(aer.range_km);
257                range_rate_km_s.push(aer.range_rate_km_s);
258            }
259            Err(e) => warn!("{} {e}", state.epoch()),
260        };
261    }
262
263    // Build the data frames.
264    let ric_df = df!(
265        "Offset (s)" => offset_s.clone(),
266        "Epoch" => epoch_str.clone(),
267        "RIC X (km)" => ric_x_km,
268        "RIC Y (km)" => ric_y_km,
269        "RIC Z (km)" => ric_z_km,
270        "RIC VX (km/s)" => ric_vx_km_s,
271        "RIC VY (km/s)" => ric_vy_km_s,
272        "RIC VZ (km/s)" => ric_vz_km_s,
273    )?;
274
275    println!("RIC difference at start\n{}", ric_df.head(Some(10)));
276    println!("RIC difference at end\n{}", ric_df.tail(Some(10)));
277
278    let aer_df = df!(
279        "Offset (s)" => offset_s.clone(),
280        "Epoch" => epoch_str.clone(),
281        "azimuth (deg)" => azimuth_deg,
282        "elevation (deg)" => elevation_deg,
283        "range (km)" => range_km,
284        "range rate (km/s)" => range_rate_km_s,
285    )?;
286
287    // Finally, let's see when the spacecraft is visible, assuming 15 degrees minimum elevation.
288    let mask = aer_df
289        .column("elevation (deg)")?
290        .gt(&Column::Scalar(ScalarColumn::new(
291            "elevation mask (deg)".into(),
292            Scalar::new(DataType::Float64, AnyValue::Float64(15.0)),
293            offset_s.len(),
294        )))?;
295    let cubesat_visible = aer_df.filter(&mask)?;
296
297    println!("{cubesat_visible}");
298
299    Ok(())
300}

pub fn from_gregorian( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, time_scale: TimeScale, ) -> Epoch

Builds an Epoch from the provided Gregorian date and time in the provided time scale. If invalid date is provided, this function will panic. Use maybe_from_gregorian if unsure.

pub fn from_gregorian_at_midnight( year: i32, month: u8, day: u8, time_scale: TimeScale, ) -> Epoch

Initialize from Gregorian date in UTC at midnight

pub fn from_gregorian_at_noon( year: i32, month: u8, day: u8, time_scale: TimeScale, ) -> Epoch

Initialize from Gregorian date in UTC at noon

pub fn from_gregorian_hms( year: i32, month: u8, day: u8, hour: u8, minute: u8, second: u8, time_scale: TimeScale, ) -> Epoch

Initialize from the Gregorian date and time (without the nanoseconds) in UTC

pub fn from_gregorian_str(s_in: &str) -> Result<Epoch, HifitimeError>

Converts a Gregorian date time in ISO8601 or RFC3339 format into an Epoch, accounting for the time zone designator and the time scale.

§Definition
  1. Time Zone Designator: this is either a Z (lower or upper case) to specify UTC, or an offset in hours and minutes off of UTC, such as +01:00 for UTC plus one hour and zero minutes.
  2. Time system (or time “scale”): UTC, TT, TAI, TDB, ET, etc.

Converts an ISO8601 or RFC3339 datetime representation to an Epoch. If no time scale is specified, then UTC is assumed. A time scale may be specified in addition to the format unless The T which separates the date from the time can be replaced with a single whitespace character (\W). The offset is also optional, cf. the examples below.

§Example
use hifitime::Epoch;
let dt = Epoch::from_gregorian_utc(2017, 1, 14, 0, 31, 55, 0);
assert_eq!(
    dt,
    Epoch::from_gregorian_str("2017-01-14T00:31:55 UTC").unwrap()
);
assert_eq!(
    dt,
    Epoch::from_gregorian_str("2017-01-14T00:31:55.0000 UTC").unwrap()
);
assert_eq!(
    dt,
    Epoch::from_gregorian_str("2017-01-14T00:31:55").unwrap()
);
assert_eq!(
    dt,
    Epoch::from_gregorian_str("2017-01-14 00:31:55").unwrap()
);
// Regression test for #90
assert_eq!(
    Epoch::from_gregorian_utc(2017, 1, 14, 0, 31, 55, 811000000),
    Epoch::from_gregorian_str("2017-01-14 00:31:55.811 UTC").unwrap()
);
assert_eq!(
    Epoch::from_gregorian_utc(2017, 1, 14, 0, 31, 55, 811200000),
    Epoch::from_gregorian_str("2017-01-14 00:31:55.8112 UTC").unwrap()
);
// Example from https://www.w3.org/TR/NOTE-datetime
assert_eq!(
    Epoch::from_gregorian_utc_hms(1994, 11, 5, 13, 15, 30),
    Epoch::from_gregorian_str("1994-11-05T13:15:30Z").unwrap()
);
assert_eq!(
    Epoch::from_gregorian_utc_hms(1994, 11, 5, 13, 15, 30),
    Epoch::from_gregorian_str("1994-11-05T08:15:30-05:00").unwrap()
);
§

impl Epoch

pub const fn from_tai_duration(duration: Duration) -> Epoch

Creates a new Epoch from a Duration as the time difference between this epoch and TAI reference epoch.

pub fn to_duration_since_j1900(&self) -> Duration

pub fn from_tai_parts(centuries: i16, nanoseconds: u64) -> Epoch

Creates a new Epoch from its centuries and nanosecond since the TAI reference epoch.

pub fn from_tai_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the provided TAI seconds since 1900 January 01 at midnight

pub fn from_tai_days(days: f64) -> Epoch

Initialize an Epoch from the provided TAI days since 1900 January 01 at midnight

pub fn from_utc_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided UTC seconds since 1900 January 01 at midnight

pub fn from_utc_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the provided UTC seconds since 1900 January 01 at midnight

pub fn from_utc_days(days: f64) -> Epoch

Initialize an Epoch from the provided UTC days since 1900 January 01 at midnight

pub fn from_gpst_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided duration since 1980 January 6 at midnight

pub fn from_qzsst_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided duration since 1980 January 6 at midnight

pub fn from_gst_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided duration since August 21st 1999 midnight

pub fn from_bdt_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided duration since January 1st midnight

pub fn from_mjd_tai(days: f64) -> Epoch

pub fn from_mjd_in_time_scale(days: f64, time_scale: TimeScale) -> Epoch

pub fn from_mjd_utc(days: f64) -> Epoch

pub fn from_mjd_gpst(days: f64) -> Epoch

pub fn from_mjd_qzsst(days: f64) -> Epoch

pub fn from_mjd_gst(days: f64) -> Epoch

pub fn from_mjd_bdt(days: f64) -> Epoch

pub fn from_jde_tai(days: f64) -> Epoch

pub fn from_jde_in_time_scale(days: f64, time_scale: TimeScale) -> Epoch

pub fn from_jde_utc(days: f64) -> Epoch

pub fn from_jde_gpst(days: f64) -> Epoch

pub fn from_jde_qzsst(days: f64) -> Epoch

pub fn from_jde_gst(days: f64) -> Epoch

pub fn from_jde_bdt(days: f64) -> Epoch

pub fn from_tt_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the provided TT seconds (approximated to 32.184s delta from TAI)

pub fn from_tt_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided TT seconds (approximated to 32.184s delta from TAI)

pub fn from_et_seconds(seconds_since_j2000: f64) -> Epoch

Initialize an Epoch from the Ephemeris Time seconds past 2000 JAN 01 (J2000 reference)

pub fn from_et_duration(duration_since_j2000: Duration) -> Epoch

Initializes an Epoch from the duration between J2000 and the current epoch as per NAIF SPICE.

§Limitation

This method uses a Newton Raphson iteration to find the appropriate TAI duration. This method is only accuracy to a few nanoseconds. Hence, when calling as_et_duration() and re-initializing it with from_et_duration you may have a few nanoseconds of difference (expect less than 10 ns).

§Warning

The et2utc function of NAIF SPICE will assume that there are 9 leap seconds before 01 JAN 1972, as this date introduces 10 leap seconds. At the time of writing, this does not seem to be in line with IERS and the documentation in the leap seconds list.

In order to match SPICE, the as_et_duration() function will manually get rid of that difference.

pub fn from_tdb_seconds(seconds_j2000: f64) -> Epoch

Initialize an Epoch from Dynamic Barycentric Time (TDB) seconds past 2000 JAN 01 midnight (difference than SPICE) NOTE: This uses the ESA algorithm, which is a notch more complicated than the SPICE algorithm, but more precise. In fact, SPICE algorithm is precise +/- 30 microseconds for a century whereas ESA algorithm should be exactly correct.

pub fn from_tdb_duration(duration_since_j2000: Duration) -> Epoch

Initialize from Dynamic Barycentric Time (TDB) (same as SPICE ephemeris time) whose epoch is 2000 JAN 01 noon TAI.

pub fn from_jde_et(days: f64) -> Epoch

Initialize from the JDE days

pub fn from_jde_tdb(days: f64) -> Epoch

Initialize from Dynamic Barycentric Time (TDB) (same as SPICE ephemeris time) in JD days

pub fn from_gpst_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the number of seconds since the GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29).

pub fn from_gpst_days(days: f64) -> Epoch

Initialize an Epoch from the number of days since the GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29).

pub fn from_gpst_nanoseconds(nanoseconds: u64) -> Epoch

Initialize an Epoch from the number of nanoseconds since the GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). This may be useful for time keeping devices that use GPS as a time source.

pub fn from_qzsst_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the number of seconds since the QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29).

pub fn from_qzsst_days(days: f64) -> Epoch

Initialize an Epoch from the number of days since the QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29).

pub fn from_qzsst_nanoseconds(nanoseconds: u64) -> Epoch

Initialize an Epoch from the number of nanoseconds since the QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). This may be useful for time keeping devices that use QZSS as a time source.

pub fn from_gst_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the number of seconds since the GST Time Epoch, starting August 21st 1999 midnight (UTC) (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS).

pub fn from_gst_days(days: f64) -> Epoch

Initialize an Epoch from the number of days since the GST Time Epoch, starting August 21st 1999 midnight (UTC) (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS)

pub fn from_gst_nanoseconds(nanoseconds: u64) -> Epoch

Initialize an Epoch from the number of nanoseconds since the GPS Time Epoch, starting August 21st 1999 midnight (UTC) (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS)

pub fn from_bdt_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the number of seconds since the BDT Time Epoch, starting on January 1st 2006 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS)

pub fn from_bdt_days(days: f64) -> Epoch

Initialize an Epoch from the number of days since the BDT Time Epoch, starting on January 1st 2006 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS)

pub fn from_bdt_nanoseconds(nanoseconds: u64) -> Epoch

Initialize an Epoch from the number of nanoseconds since the BDT Time Epoch, starting on January 1st 2006 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS). This may be useful for time keeping devices that use BDT as a time source.

pub fn from_ptp_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) duration since TAI midnight 1970 January 01. PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.

pub fn from_ptp_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) second timestamp since TAI midnight 1970 January 01. PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.

pub fn from_ptp_nanoseconds(nanoseconds: u64) -> Epoch

Initialize an Epoch from the provided IEEE 1588-2008 (PTPv2) nanoseconds timestamp since TAI midnight 1970 January 01. PTP uses the TAI timescale but with the Unix Epoch for compatibility with unix systems.

pub fn from_unix_duration(duration: Duration) -> Epoch

Initialize an Epoch from the provided duration since UTC midnight 1970 January 01.

pub fn from_unix_seconds(seconds: f64) -> Epoch

Initialize an Epoch from the provided UNIX second timestamp since UTC midnight 1970 January 01.

pub fn from_unix_milliseconds(millisecond: f64) -> Epoch

Initialize an Epoch from the provided UNIX millisecond timestamp since UTC midnight 1970 January 01.

pub fn from_str_with_format( s_in: &str, format: Format, ) -> Result<Epoch, HifitimeError>

Initializes an Epoch from the provided Format.

pub fn from_format_str( s_in: &str, format_str: &str, ) -> Result<Epoch, HifitimeError>

Initializes an Epoch from the Format as a string.

pub fn from_time_of_week( week: u32, nanoseconds: u64, time_scale: TimeScale, ) -> Epoch

Builds an Epoch from given week: elapsed weeks counter into the desired Time scale, and the amount of nanoseconds within that week. For example, this is how GPS vehicles describe a GPST epoch.

Note that this constructor relies on 128 bit integer math and may be slow on embedded devices.

pub fn from_time_of_week_utc(week: u32, nanoseconds: u64) -> Epoch

Builds a UTC Epoch from given week: elapsed weeks counter and “ns” amount of nanoseconds since closest Sunday Midnight.

pub fn from_day_of_year(year: i32, days: f64, time_scale: TimeScale) -> Epoch

Builds an Epoch from the provided year, days in the year, and a time scale.

§Limitations

In the TDB or ET time scales, there may be an error of up to 750 nanoseconds when initializing an Epoch this way. This is because we first initialize the epoch in Gregorian scale and then apply the TDB/ET offset, but that offset actually depends on the precise time.

§Day couting behavior

The day counter starts at 01, in other words, 01 January is day 1 of the counter, as per the GPS specificiations.

§

impl Epoch

pub fn min(&self, other: Epoch) -> Epoch

Returns the minimum of the two epochs.

use hifitime::Epoch;

let e0 = Epoch::from_gregorian_utc_at_midnight(2022, 10, 20);
let e1 = Epoch::from_gregorian_utc_at_midnight(2022, 10, 21);

assert_eq!(e0, e1.min(e0));
assert_eq!(e0, e0.min(e1));

Note: this uses a pointer to self which will be copied immediately because Python requires a pointer. :type other: Epoch :rtype: Epoch

pub fn max(&self, other: Epoch) -> Epoch

Returns the maximum of the two epochs.

use hifitime::Epoch;

let e0 = Epoch::from_gregorian_utc_at_midnight(2022, 10, 20);
let e1 = Epoch::from_gregorian_utc_at_midnight(2022, 10, 21);

assert_eq!(e1, e1.max(e0));
assert_eq!(e1, e0.max(e1));

Note: this uses a pointer to self which will be copied immediately because Python requires a pointer. :type other: Epoch :rtype: Epoch

pub fn floor(&self, duration: Duration) -> Epoch

Floors this epoch to the closest provided duration

§Example
use hifitime::{Epoch, TimeUnits};

let e = Epoch::from_gregorian_tai_hms(2022, 5, 20, 17, 57, 43);
assert_eq!(
    e.floor(1.hours()),
    Epoch::from_gregorian_tai_hms(2022, 5, 20, 17, 0, 0)
);

let e = Epoch::from_gregorian_tai(2022, 10, 3, 17, 44, 29, 898032665);
assert_eq!(
    e.floor(3.minutes()),
    Epoch::from_gregorian_tai_hms(2022, 10, 3, 17, 42, 0)
);

:type duration: Duration :rtype: Epoch

pub fn ceil(&self, duration: Duration) -> Epoch

Ceils this epoch to the closest provided duration in the TAI time scale

§Example
use hifitime::{Epoch, TimeUnits};

let e = Epoch::from_gregorian_tai_hms(2022, 5, 20, 17, 57, 43);
assert_eq!(
    e.ceil(1.hours()),
    Epoch::from_gregorian_tai_hms(2022, 5, 20, 18, 0, 0)
);

// 45 minutes is a multiple of 3 minutes, hence this result
let e = Epoch::from_gregorian_tai(2022, 10, 3, 17, 44, 29, 898032665);
assert_eq!(
    e.ceil(3.minutes()),
    Epoch::from_gregorian_tai_hms(2022, 10, 3, 17, 45, 0)
);

:type duration: Duration :rtype: Epoch

pub fn round(&self, duration: Duration) -> Epoch

Rounds this epoch to the closest provided duration in TAI

§Example
use hifitime::{Epoch, TimeUnits};

let e = Epoch::from_gregorian_tai_hms(2022, 5, 20, 17, 57, 43);
assert_eq!(
    e.round(1.hours()),
    Epoch::from_gregorian_tai_hms(2022, 5, 20, 18, 0, 0)
);

:type duration: Duration :rtype: Epoch

pub fn to_time_of_week(&self) -> (u32, u64)

Converts this epoch into the time of week, represented as a rolling week counter into that time scale and the number of nanoseconds elapsed in current week (since closest Sunday midnight). This is usually how GNSS receivers describe a timestamp. :rtype: tuple

pub fn precise_timescale_conversion( &self, forward: bool, reference_epoch: Epoch, polynomial: Polynomial, target: TimeScale, ) -> Result<Epoch, HifitimeError>

Converts this Epoch into targeted TimeScale using provided Polynomial.

§Input
  • forward: whether this is forward or backward conversion. For example, using GPST-UTC Polynomial
    • GPST->UTC is the forward conversion
    • UTC->GPST is the backward conversion
  • reference_epoch: any reference Epoch for the provided Polynomial.

While we support any time difference, it should remain short in pratice (a day at most, for precise applications).

  • polynomial: that must be valid for this reference Epoch, used in the equation a0 + a1*dt + a2*dt² = GPST-UTC for example.
  • target: targetted TimeScale we will transition to.

Example:

use hifitime::{Epoch, TimeScale, Polynomial, Unit};

// random GPST Epoch for forward conversion to UTC
let t_gpst = Epoch::from_gregorian(2020, 01, 01, 0, 0, 0, 0, TimeScale::GPST);

// Let's say we know the GPST-UTC polynomials for that day,
// They allow precise forward transition from GPST to UTC,
// and precise backward transition from UTC to GPST.
let gpst_utc_polynomials = Polynomial::from_constant_offset_nanoseconds(1.0);

// This is the reference [Epoch] attached to the publication of these polynomials.
// You should use polynomials that remain valid and were provided recently (usually one day at most).
// Example: polynomials were published 1 hour ago.
let gpst_reference = t_gpst - 1.0 * Unit::Hour;

// Forward conversion (to UTC) GPST - a0 + a1 *dt + a2*dt² = UTC
let t_utc = t_gpst.precise_timescale_conversion(true, gpst_reference, gpst_utc_polynomials, TimeScale::UTC)
    .unwrap();

// Verify we did transition to UTC
assert_eq!(t_utc.time_scale, TimeScale::UTC);

// Verify the resulting [Epoch] is the coarse GPST->UTC transition + fine correction
let reversed = t_utc.to_time_scale(TimeScale::GPST) + 1.0 * Unit::Nanosecond;
assert_eq!(reversed, t_gpst);

// Apply the backward transition, from t_utc back to t_gpst.
// The timescale conversion works both ways: (from UTC) GPST = UTC + a0 + a1 *dt + a2*dt²
let backwards = t_utc.precise_timescale_conversion(false, gpst_reference, gpst_utc_polynomials, TimeScale::GPST)
    .unwrap();

assert_eq!(backwards, t_gpst);

// It is important to understand that your reference point does not have to be in the past.
// The only logic that should prevail is to always minimize interpolation gap.
// In other words, if you can access future interpolation information that would minimize the data gap, they should prevail.
// Example: +30' in the future.
let gpst_reference = t_gpst + 30.0 * Unit::Minute;

// Forward conversion (to UTC) but using polynomials that were released 1 hour after t_gpst
let t_utc = t_gpst.precise_timescale_conversion(true, gpst_reference, gpst_utc_polynomials, TimeScale::UTC)
    .unwrap();

// Verifications
assert_eq!(t_utc.time_scale, TimeScale::UTC);

let reversed = t_utc.to_time_scale(TimeScale::GPST) + 1.0 * Unit::Nanosecond;
assert_eq!(reversed, t_gpst);

:type forward: bool :type reference_epoch: Epoch :type polynomial: Polynomial :type target: TimeScale :rtype: Epoch

pub fn weekday_in_time_scale(&self, time_scale: TimeScale) -> Weekday

Returns the weekday in provided time scale ASSUMING that the reference epoch of that time scale is a Monday. You probably do not want to use this. You probably either want weekday() or weekday_utc(). Several time scales do not have a reference day that’s on a Monday, e.g. BDT. :type time_scale: TimeScale :rtype: Weekday

pub fn weekday(&self) -> Weekday

Returns weekday (uses the TAI representation for this calculation). :rtype: Weekday

pub fn weekday_utc(&self) -> Weekday

Returns weekday in UTC timescale :rtype: Weekday

pub fn next(&self, weekday: Weekday) -> Epoch

Returns the next weekday.

use hifitime::prelude::*;

let epoch = Epoch::from_gregorian_utc_at_midnight(1988, 1, 2);
assert_eq!(epoch.weekday_utc(), Weekday::Saturday);
assert_eq!(epoch.next(Weekday::Sunday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 3));
assert_eq!(epoch.next(Weekday::Monday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 4));
assert_eq!(epoch.next(Weekday::Tuesday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 5));
assert_eq!(epoch.next(Weekday::Wednesday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 6));
assert_eq!(epoch.next(Weekday::Thursday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 7));
assert_eq!(epoch.next(Weekday::Friday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 8));
assert_eq!(epoch.next(Weekday::Saturday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 9));

:type weekday: Weekday :rtype: Epoch

pub fn next_weekday_at_midnight(&self, weekday: Weekday) -> Epoch

:type weekday: Weekday :rtype: Epoch

pub fn next_weekday_at_noon(&self, weekday: Weekday) -> Epoch

:type weekday: Weekday :rtype: Epoch

pub fn previous(&self, weekday: Weekday) -> Epoch

Returns the next weekday.

use hifitime::prelude::*;

let epoch = Epoch::from_gregorian_utc_at_midnight(1988, 1, 2);
assert_eq!(epoch.previous(Weekday::Friday), Epoch::from_gregorian_utc_at_midnight(1988, 1, 1));
assert_eq!(epoch.previous(Weekday::Thursday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 31));
assert_eq!(epoch.previous(Weekday::Wednesday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 30));
assert_eq!(epoch.previous(Weekday::Tuesday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 29));
assert_eq!(epoch.previous(Weekday::Monday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 28));
assert_eq!(epoch.previous(Weekday::Sunday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 27));
assert_eq!(epoch.previous(Weekday::Saturday), Epoch::from_gregorian_utc_at_midnight(1987, 12, 26));

:type weekday: Weekday :rtype: Epoch

pub fn previous_weekday_at_midnight(&self, weekday: Weekday) -> Epoch

:type weekday: Weekday :rtype: Epoch

pub fn previous_weekday_at_noon(&self, weekday: Weekday) -> Epoch

:type weekday: Weekday :rtype: Epoch

§

impl Epoch

pub fn with_hms(&self, hours: u64, minutes: u64, seconds: u64) -> Epoch

Returns a copy of self where the time is set to the provided hours, minutes, seconds Invalid number of hours, minutes, and seconds will overflow into their higher unit. Warning: this does not set the subdivisions of second to zero. :type hours: int :type minutes: int :type seconds: int :rtype: Epoch

pub fn with_hms_from(&self, other: Epoch) -> Epoch

Returns a copy of self where the hours, minutes, seconds is set to the time of the provided epoch but the sub-second parts are kept from the current epoch.

:type other: Epoch :rtype: Epoch

use hifitime::prelude::*;

let epoch = Epoch::from_gregorian_utc(2022, 12, 01, 10, 11, 12, 13);
let other_utc = Epoch::from_gregorian_utc(2024, 12, 01, 20, 21, 22, 23);
let other = other_utc.to_time_scale(TimeScale::TDB);

assert_eq!(
    epoch.with_hms_from(other),
    Epoch::from_gregorian_utc(2022, 12, 01, 20, 21, 22, 13)
);

pub fn with_time_from(&self, other: Epoch) -> Epoch

Returns a copy of self where all of the time components (hours, minutes, seconds, and sub-seconds) are set to the time of the provided epoch.

:type other: Epoch :rtype: Epoch

use hifitime::prelude::*;

let epoch = Epoch::from_gregorian_utc(2022, 12, 01, 10, 11, 12, 13);
let other_utc = Epoch::from_gregorian_utc(2024, 12, 01, 20, 21, 22, 23);
// If the other Epoch is in another time scale, it does not matter, it will be converted to the correct time scale.
let other = other_utc.to_time_scale(TimeScale::TDB);

assert_eq!(
    epoch.with_time_from(other),
    Epoch::from_gregorian_utc(2022, 12, 01, 20, 21, 22, 23)
);

pub fn with_hms_strict(&self, hours: u64, minutes: u64, seconds: u64) -> Epoch

Returns a copy of self where the time is set to the provided hours, minutes, seconds Invalid number of hours, minutes, and seconds will overflow into their higher unit. Warning: this will set the subdivisions of seconds to zero. :type hours: int :type minutes: int :type seconds: int :rtype: Epoch

pub fn with_hms_strict_from(&self, other: Epoch) -> Epoch

Returns a copy of self where the time is set to the time of the other epoch but the subseconds are set to zero.

:type other: Epoch :rtype: Epoch

use hifitime::prelude::*;

let epoch = Epoch::from_gregorian_utc(2022, 12, 01, 10, 11, 12, 13);
let other_utc = Epoch::from_gregorian_utc(2024, 12, 01, 20, 21, 22, 23);
let other = other_utc.to_time_scale(TimeScale::TDB);

assert_eq!(
    epoch.with_hms_strict_from(other),
    Epoch::from_gregorian_utc(2022, 12, 01, 20, 21, 22, 0)
);
§

impl Epoch

pub fn now() -> Result<Epoch, HifitimeError>

Initializes a new Epoch from now. WARNING: This assumes that the system time returns the time in UTC (which is the case on Linux) Uses std::time::SystemTime::now or javascript interop under the hood

§

impl Epoch

pub fn from_ut1_duration(duration: Duration, provider: &Ut1Provider) -> Epoch

Initialize an Epoch from the provided UT1 duration since 1900 January 01 at midnight

§Warning

The time scale of this Epoch will be set to TAI! This is to ensure that no additional computations will change the duration since it’s stored in TAI. However, this also means that calling to_duration() on this Epoch will return the TAI duration and not the UT1 duration!

pub fn ut1_offset(&self, provider: &Ut1Provider) -> Option<Duration>

Get the accumulated offset between this epoch and UT1. Assumes the provider’s records are sorted by ascending epoch (enforced in from_eop_data).

§Arguments
  • provider: Borrowed UT1 data source.
§Return
  • Some(Duration) for the last record with record.epoch <= self, otherwise None.

pub fn to_ut1_duration(&self, provider: &Ut1Provider) -> Duration

Returns this time in a Duration past J1900 counted in UT1

pub fn to_ut1(&self, provider: &Ut1Provider) -> Epoch

Returns this time in a Duration past J1900 counted in UT1

§

impl Epoch

pub fn py_from_ut1_duration( _cls: &Bound<'_, PyType>, duration: Duration, provider: PyRef<'_, Ut1Provider>, ) -> Result<Epoch, PyErr>

Initialize a new Epoch from a duration in UT1

:type duration: Duration :type provider: Ut1Provider :rtype: Epoch

pub fn py_ut1_offset( &self, provider: PyRef<'_, Ut1Provider>, ) -> Option<Duration>

Get the accumulated offset between this epoch and UT1.

:type provider: Ut1Provider :rtype: Duration

pub fn py_to_ut1_duration(&self, provider: PyRef<'_, Ut1Provider>) -> Duration

Returns this time in a Duration past J1900 counted in UT1

:type provider: Ut1Provider :rtype: Duration

pub fn py_to_ut1(&self, provider: PyRef<'_, Ut1Provider>) -> Epoch

Convert this epoch to Ut1

:type provider: Ut1Provider :rtype: Epoch

§

impl Epoch

pub fn leap_seconds_with_file( &self, iers_only: bool, provider: LeapSecondsFile, ) -> Option<f64>

Get the accumulated number of leap seconds up to this Epoch from the provided LeapSecondProvider. Returns None if the epoch is before 1960, year at which UTC was defined.

§Why does this function return an Option when the other returns a value

This is to match the iauDat function of SOFA (src/dat.c). That function will return a warning and give up if the start date is before 1960.

:type iers_only: bool :type provider: LeapSecondsFile :rtype: float

pub fn py_to_gregorian( &self, time_scale: Option<TimeScale>, ) -> (i32, u8, u8, u8, u8, u8, u32)

Converts the Epoch to the Gregorian parts in the (optionally) provided time scale as (year, month, day, hour, minute, second).

:type time_scale: TimeScale, optional

:rtype: tuple[int, int, int, int, int, int, int]

§

impl Epoch

pub fn leap_seconds_with<L>(&self, iers_only: bool, provider: L) -> Option<f64>

Get the accumulated number of leap seconds up to this Epoch from the provided LeapSecondProvider. Returns None if the epoch is before 1960, year at which UTC was defined.

§Why does this function return an Option when the other returns a value

This is to match the iauDat function of SOFA (src/dat.c). That function will return a warning and give up if the start date is before 1960.

pub const fn from_duration(duration: Duration, ts: TimeScale) -> Epoch

Creates an epoch from given duration expressed in given timescale, i.e. since the given time scale’s reference epoch.

For example, if the duration is 1 day and the time scale is Ephemeris Time, then this will create an epoch of 2000-01-02 at midnight ET. If the duration is 1 day and the time scale is TAI, this will create an epoch of 1900-01-02 at noon, because the TAI reference epoch in Hifitime is chosen to be the J1900 epoch. In case of ET, TDB Timescales, a duration since J2000 is expected.

§

impl Epoch

pub fn to_time_scale(&self, ts: TimeScale) -> Epoch

Converts self to another time scale

As per the Rust naming convention, this borrows an Epoch and returns an owned Epoch.

:type ts: TimeScale :rtype: Epoch

pub fn leap_seconds_iers(&self) -> i32

Get the accumulated number of leap seconds up to this Epoch accounting only for the IERS leap seconds. :rtype: int

pub fn leap_seconds(&self, iers_only: bool) -> Option<f64>

Get the accumulated number of leap seconds up to this Epoch accounting only for the IERS leap seconds and the SOFA scaling from 1960 to 1972, depending on flag. Returns None if the epoch is before 1960, year at which UTC was defined.

§Why does this function return an Option when the other returns a value

This is to match the iauDat function of SOFA (src/dat.c). That function will return a warning and give up if the start date is before 1960. :type iers_only: bool :rtype: float

pub fn to_isoformat(&self) -> String

The standard ISO format of this epoch (six digits of subseconds) in the current time scale, refer to https://docs.rs/hifitime/latest/hifitime/efmt/format/struct.Format.html for format options. :rtype: str

Examples found in repository?
nyx-core/examples/03_geo_analysis/drift.rs (line 179)
26fn main() -> Result<(), Box<dyn Error>> {
27    pel::init();
28    // Dynamics models require planetary constants and ephemerides to be defined.
29    // Let's start by grabbing those by using ANISE's latest MetaAlmanac.
30    // This will automatically download the DE440s planetary ephemeris,
31    // the daily-updated Earth Orientation Parameters, the high fidelity Moon orientation
32    // parameters (for the Moon Mean Earth and Moon Principal Axes frames), and the PCK11
33    // planetary constants kernels.
34    // For details, refer to https://github.com/nyx-space/anise/blob/master/data/latest.dhall.
35    // Note that we place the Almanac into an Arc so we can clone it cheaply and provide read-only
36    // references to many functions.
37    let almanac = Arc::new(MetaAlmanac::latest().map_err(Box::new)?);
38    // Define the orbit epoch
39    let epoch = Epoch::from_gregorian_utc_hms(2024, 2, 29, 12, 13, 14);
40
41    // Define the orbit.
42    // First we need to fetch the Earth J2000 from information from the Almanac.
43    // This allows the frame to include the gravitational parameters and the shape of the Earth,
44    // defined as a tri-axial ellipoid. Note that this shape can be changed manually or in the Almanac
45    // by loading a different set of planetary constants.
46    let earth_j2000 = almanac.frame_info(EARTH_J2000)?;
47
48    // Placing this GEO bird just above Colorado.
49    // In theory, the eccentricity is zero, but in practice, it's about 1e-5 to 1e-6 at best.
50    let orbit = Orbit::try_keplerian(42164.0, 1e-5, 0., 163.0, 75.0, 0.0, epoch, earth_j2000)?;
51    // Print in in Keplerian form.
52    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    // Nyx is used for high fidelity propagation, not Keplerian propagation as above.
58    // Nyx only propagates Spacecraft at the moment, which allows it to account for acceleration
59    // models such as solar radiation pressure.
60
61    // Let's build a cubesat sized spacecraft, with an SRP area of 10 cm^2 and a mass of 9.6 kg.
62    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    // Set up the spacecraft dynamics.
73
74    // Specify that the orbital dynamics must account for the graviational pull of the Moon and the Sun.
75    // The gravity of the Earth will also be accounted for since the spaceraft in an Earth orbit.
76    let mut orbital_dyn = OrbitalDynamics::point_masses(vec![MOON, SUN]);
77
78    // We want to include the spherical harmonics, so let's download the gravitational data from the Nyx Cloud.
79    // We're using the JGM3 model here, which is the default in GMAT.
80    let mut jgm3_meta = MetaFile {
81        uri: "http://public-data.nyxspace.com/nyx/models/JGM3.cof.gz".to_string(),
82        crc32: Some(0xF446F027), // Specifying the CRC32 avoids redownloading it if it's cached.
83    };
84    // And let's download it if we don't have it yet.
85    jgm3_meta.process(true)?;
86
87    // Build the spherical harmonics.
88    // The harmonics must be computed in the body fixed frame.
89    // We're using the long term prediction of the Earth centered Earth fixed frame, IAU Earth.
90    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    // Include the spherical harmonics into the orbital dynamics.
102    orbital_dyn.accel_models.push(harmonics_21x21);
103
104    // We define the solar radiation pressure, using the default solar flux and accounting only
105    // for the eclipsing caused by the Earth and Moon.
106    let srp_dyn = SolarPressure::new(vec![EARTH_J2000, MOON_J2000], &almanac)?;
107
108    // Finalize setting up the dynamics, specifying the force models (orbital_dyn) separately from the
109    // acceleration models (SRP in this case). Use `from_models` to specify multiple accel models.
110    let dynamics = SpacecraftDynamics::from_model(orbital_dyn, srp_dyn);
111
112    println!("{dynamics}");
113
114    // Finally, let's propagate this orbit to the same epoch as above.
115    // The first returned value is the spacecraft state at the final epoch.
116    // The second value is the full trajectory where the step size is variable step used by the propagator.
117    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    // We also have access to the full trajectory throughout the propagation.
148    println!("{trajectory}");
149
150    println!("Spacecraft params after 3 years without active control:\n{future_sc:x}");
151
152    // With the trajectory, let's build a few data products.
153
154    // 1. Export the trajectory as a parquet file, which includes the Keplerian orbital elements.
155
156    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    // 2. Compute the latitude, longitude, and altitude throughout the trajectory by rotating the spacecraft position into the Earth body fixed frame.
164
165    // We iterate over the trajectory, grabbing a state every two minutes.
166    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        // Convert the GEO bird state into the body fixed frame, and keep track of its latitude, longitude, and altitude.
174        // These define the GEO stationkeeping box.
175
176        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    // Build the station keeping data frame.
204    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    // Create a file to write the Parquet to
214    let file = File::create("./03_geo_lla.parquet").expect("Could not create file");
215
216    // Create a ParquetWriter and write the DataFrame to the file
217    ParquetWriter::new(file).finish(&mut sk_df)?;
218
219    Ok(())
220}

pub fn to_duration_in_time_scale(&self, ts: TimeScale) -> Duration

Returns this epoch with respect to the provided time scale. This is needed to correctly perform duration conversions in dynamical time scales (e.g. TDB). :type ts: TimeScale :rtype: Duration

pub fn to_tai_seconds(&self) -> f64

Returns the number of TAI seconds since J1900 :rtype: float

pub fn to_tai_duration(&self) -> Duration

Returns this time in a Duration past J1900 counted in TAI :rtype: Duration

pub fn to_tai(&self, unit: Unit) -> f64

Returns the epoch as a floating point value in the provided unit :type unit: Unit :rtype: float

pub fn to_tai_parts(&self) -> (i16, u64)

Returns the TAI parts of this duration :rtype: tuple

pub fn to_tai_days(&self) -> f64

Returns the number of days since J1900 in TAI :rtype: float

pub fn to_utc_seconds(&self) -> f64

Returns the number of UTC seconds since the TAI epoch :rtype: float

pub fn to_utc_duration(&self) -> Duration

Returns this time in a Duration past J1900 counted in UTC :rtype: Duration

pub fn to_utc(&self, unit: Unit) -> f64

Returns the number of UTC seconds since the TAI epoch :type unit: Unit :rtype: float

pub fn to_utc_days(&self) -> f64

Returns the number of UTC days since the TAI epoch :rtype: float

pub fn to_mjd_tai_days(&self) -> f64

as_mjd_days creates an Epoch from the provided Modified Julian Date in days as explained here. MJD epoch is Modified Julian Day at 17 November 1858 at midnight. :rtype: float

pub fn to_mjd_tai_seconds(&self) -> f64

Returns the Modified Julian Date in seconds TAI. :rtype: float

pub fn to_mjd_tai(&self, unit: Unit) -> f64

Returns this epoch as a duration in the requested units in MJD TAI :type unit: Unit :rtype: float

pub fn to_mjd_utc_days(&self) -> f64

Returns the Modified Julian Date in days UTC. :rtype: float

pub fn to_mjd_utc(&self, unit: Unit) -> f64

Returns the Modified Julian Date in the provided unit in UTC. :type unit: Unit :rtype: float

pub fn to_mjd_utc_seconds(&self) -> f64

Returns the Modified Julian Date in seconds UTC. :rtype: float

pub fn to_jde_tai_days(&self) -> f64

Returns the Julian days from epoch 01 Jan -4713, 12:00 (noon) as explained in “Fundamentals of astrodynamics and applications”, Vallado et al. 4th edition, page 182, and on Wikipedia. :rtype: float

pub fn to_jde_tai(&self, unit: Unit) -> f64

Returns the Julian Days from epoch 01 Jan -4713 12:00 (noon) in desired Duration::Unit :type unit: Unit :rtype: float

pub fn to_jde_tai_duration(&self) -> Duration

Returns the Julian Days from epoch 01 Jan -4713 12:00 (noon) as a Duration :rtype: Duration

pub fn to_jde_tai_seconds(&self) -> f64

Returns the Julian seconds in TAI. :rtype: float

pub fn to_jde_utc_days(&self) -> f64

Returns the Julian days in UTC. :rtype: float

pub fn to_jde_utc_duration(&self) -> Duration

Returns the Julian days in UTC as a Duration :rtype: Duration

pub fn to_jde_utc_seconds(&self) -> f64

Returns the Julian Days in UTC seconds. :rtype: float

pub fn to_tt_seconds(&self) -> f64

Returns seconds past TAI epoch in Terrestrial Time (TT) (previously called Terrestrial Dynamical Time (TDT)) :rtype: float

pub fn to_tt_duration(&self) -> Duration

Returns Duration past TAI epoch in Terrestrial Time (TT). :rtype: Duration

pub fn to_tt_days(&self) -> f64

Returns days past TAI epoch in Terrestrial Time (TT) (previously called Terrestrial Dynamical Time (TDT)) :rtype: float

pub fn to_tt_centuries_j2k(&self) -> f64

Returns the centuries passed J2000 TT :rtype: float

pub fn to_tt_since_j2k(&self) -> Duration

Returns the duration past J2000 TT :rtype: Duration

pub fn to_jde_tt_days(&self) -> f64

Returns days past Julian epoch in Terrestrial Time (TT) (previously called Terrestrial Dynamical Time (TDT)) :rtype: float

pub fn to_jde_tt_duration(&self) -> Duration

:rtype: Duration

pub fn to_mjd_tt_days(&self) -> f64

Returns days past Modified Julian epoch in Terrestrial Time (TT) (previously called Terrestrial Dynamical Time (TDT)) :rtype: float

pub fn to_mjd_tt_duration(&self) -> Duration

:rtype: Duration

pub fn to_gpst_seconds(&self) -> f64

Returns seconds past GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). :rtype: float

pub fn to_gpst_duration(&self) -> Duration

Returns Duration past GPS time Epoch. :rtype: Duration

pub fn to_gpst_nanoseconds(&self) -> Result<u64, HifitimeError>

Returns nanoseconds past GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). NOTE: This function will return an error if the centuries past GPST time are not zero. :rtype: int

pub fn to_gpst_days(&self) -> f64

Returns days past GPS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). :rtype: float

pub fn to_qzsst_seconds(&self) -> f64

Returns seconds past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). :rtype: float

pub fn to_qzsst_duration(&self) -> Duration

Returns Duration past QZSS time Epoch. :rtype: Duration

pub fn to_qzsst_nanoseconds(&self) -> Result<u64, HifitimeError>

Returns nanoseconds past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). NOTE: This function will return an error if the centuries past QZSST time are not zero. :rtype: int

pub fn to_qzsst_days(&self) -> f64

Returns days past QZSS Time Epoch, defined as UTC midnight of January 5th to 6th 1980 (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS#GPS_Time_.28GPST.29). :rtype: float

pub fn to_gst_seconds(&self) -> f64

Returns seconds past GST (Galileo) Time Epoch :rtype: float

pub fn to_gst_duration(&self) -> Duration

Returns Duration past GST (Galileo) time Epoch. :rtype: Duration

pub fn to_gst_nanoseconds(&self) -> Result<u64, HifitimeError>

Returns nanoseconds past GST (Galileo) Time Epoch, starting on August 21st 1999 Midnight UT (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS). NOTE: This function will return an error if the centuries past GST time are not zero. :rtype: int

pub fn to_gst_days(&self) -> f64

Returns days past GST (Galileo) Time Epoch, starting on August 21st 1999 Midnight UT (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS). :rtype: float

pub fn to_bdt_seconds(&self) -> f64

Returns seconds past BDT (BeiDou) Time Epoch :rtype: float

pub fn to_bdt_duration(&self) -> Duration

Returns Duration past BDT (BeiDou) time Epoch. :rtype: Duration

pub fn to_bdt_days(&self) -> f64

Returns days past BDT (BeiDou) Time Epoch, defined as Jan 01 2006 UTC (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS). :rtype: float

pub fn to_bdt_nanoseconds(&self) -> Result<u64, HifitimeError>

Returns nanoseconds past BDT (BeiDou) Time Epoch, defined as Jan 01 2006 UTC (cf. https://gssc.esa.int/navipedia/index.php/Time_References_in_GNSS). NOTE: This function will return an error if the centuries past GST time are not zero. :rtype: int

pub fn to_unix(&self, unit: Unit) -> f64

Returns the duration since the UNIX epoch in the provided unit. :type unit: Unit :rtype: float

pub fn to_unix_seconds(&self) -> f64

Returns the number seconds since the UNIX epoch defined 01 Jan 1970 midnight UTC. :rtype: float

pub fn to_unix_milliseconds(&self) -> f64

Returns the number milliseconds since the UNIX epoch defined 01 Jan 1970 midnight UTC. :rtype: float

pub fn to_unix_days(&self) -> f64

Returns the number days since the UNIX epoch defined 01 Jan 1970 midnight UTC. :rtype: float

pub fn to_et_seconds(&self) -> f64

Returns the Ephemeris Time seconds past 2000 JAN 01 midnight, matches NASA/NAIF SPICE. :rtype: float

pub fn to_et_duration(&self) -> Duration

Returns the duration between J2000 and the current epoch as per NAIF SPICE.

§Warning

The et2utc function of NAIF SPICE will assume that there are 9 leap seconds before 01 JAN 1972, as this date introduces 10 leap seconds. At the time of writing, this does not seem to be in line with IERS and the documentation in the leap seconds list.

In order to match SPICE, the as_et_duration() function will manually get rid of that difference. :rtype: Duration

pub fn to_tcg_duration(&self) -> Duration

Returns the Geocentric Coordinate Time (TCG) as a Duration its reference epoch (1977-01-01) :rtype: Duration

pub fn to_tcg_seconds(&self) -> f64

Returns the Geocentric Coordinate Time (TCG) as seconds its reference epoch (1977-01-01) :rtype: float

pub fn to_tcb_duration(&self) -> Duration

Returns the Barycentric Coordinate Time (TCB) as a Duration its reference epoch (1977-01-01 + 65.5 µs) :rtype: Duration

pub fn to_tcb_seconds(&self) -> f64

Returns the Barycentric Coordinate Time (TCB) as seconds its reference epoch (1977-01-01 + 65.5 µs) :rtype: float

pub fn to_tdb_duration(&self) -> Duration

Returns the Dynamics Barycentric Time (TDB) as a high precision Duration since J2000

§Algorithm

Given the embedded sine functions in the equation to compute the difference between TDB and TAI from the number of TDB seconds past J2000, one cannot solve the revert the operation analytically. Instead, we iterate until the value no longer changes.

  1. Assume that the TAI duration is in fact the TDB seconds from J2000.
  2. Offset to J2000 because Epoch stores everything in the J1900 but the TDB duration is in J2000.
  3. Compute the offset g due to the TDB computation with the current value of the TDB seconds (defined in step 1).
  4. Subtract that offset to the latest TDB seconds and store this as a new candidate for the true TDB seconds value.
  5. Compute the difference between this candidate and the previous one. If the difference is less than one nanosecond, stop iteration.
  6. Set the new candidate as the TDB seconds since J2000 and loop until step 5 breaks the loop, or we’ve done five iterations.
  7. At this stage, we have a good approximation of the TDB seconds since J2000.
  8. Reverse the algorithm given that approximation: compute the g offset, compute the difference between TDB and TAI, add the TT offset (32.184 s), and offset by the difference between J1900 and J2000.

:rtype: Duration

pub fn to_tdb_seconds(&self) -> f64

Returns the Dynamic Barycentric Time (TDB) (higher fidelity SPICE ephemeris time) whose epoch is 2000 JAN 01 noon TAI (cf. https://gssc.esa.int/navipedia/index.php/Transformations_between_Time_Systems#TDT_-_TDB.2C_TCB) :rtype: float

pub fn to_jde_et_days(&self) -> f64

Returns the Ephemeris Time JDE past epoch :rtype: float

pub fn to_jde_et_duration(&self) -> Duration

:rtype: Duration

pub fn to_jde_et(&self, unit: Unit) -> f64

:type unit: Unit :rtype: float

pub fn to_jde_tdb_duration(&self) -> Duration

:rtype: Duration

pub fn to_jde_tdb_days(&self) -> f64

Returns the Dynamic Barycentric Time (TDB) (higher fidelity SPICE ephemeris time) whose epoch is 2000 JAN 01 noon TAI (cf. https://gssc.esa.int/navipedia/index.php/Transformations_between_Time_Systems#TDT_-_TDB.2C_TCB) :rtype: float

pub fn to_tdb_days_since_j2000(&self) -> f64

Returns the number of days since Dynamic Barycentric Time (TDB) J2000 (used for Archinal et al. rotations) :rtype: float

pub fn to_tdb_centuries_since_j2000(&self) -> f64

Returns the number of centuries since Dynamic Barycentric Time (TDB) J2000 (used for Archinal et al. rotations) :rtype: float

pub fn to_et_days_since_j2000(&self) -> f64

Returns the number of days since Ephemeris Time (ET) J2000 (used for Archinal et al. rotations) :rtype: float

pub fn to_et_centuries_since_j2000(&self) -> f64

Returns the number of centuries since Ephemeris Time (ET) J2000 (used for Archinal et al. rotations) :rtype: float

pub fn duration_in_year(&self) -> Duration

Returns the duration since the start of the year :rtype: Duration

pub fn day_of_year(&self) -> f64

Returns the number of days since the start of the year. :rtype: float

pub fn day_of_month(&self) -> u8

Returns the number of days since the start of the Gregorian month in the current time scale.

§Example
use hifitime::Epoch;
let dt = Epoch::from_gregorian_tai_at_midnight(2025, 7, 3);
assert_eq!(dt.day_of_month(), 3);

:rtype: int

pub fn year(&self) -> i32

Returns the number of Gregorian years of this epoch in the current time scale. :rtype: int

pub fn year_days_of_year(&self) -> (i32, f64)

Returns the year and the days in the year so far (days of year). :rtype: tuple

pub fn hours(&self) -> u64

Returns the hours of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn minutes(&self) -> u64

Returns the minutes of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn seconds(&self) -> u64

Returns the seconds of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn milliseconds(&self) -> u64

Returns the milliseconds of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn microseconds(&self) -> u64

Returns the microseconds of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn nanoseconds(&self) -> u64

Returns the nanoseconds of the Gregorian representation of this epoch in the time scale it was initialized in. :rtype: int

pub fn month_name(&self) -> MonthName

:rtype: MonthName

pub fn to_rfc3339(&self) -> String

Returns this epoch in UTC in the RFC3339 format :rtype: str

Trait Implementations§

§

impl Add<Duration> for Epoch

§

type Output = Epoch

The resulting type after applying the + operator.
§

fn add(self, duration: Duration) -> Epoch

Performs the + operation. Read more
§

impl Add<Unit> for Epoch

§

type Output = Epoch

The resulting type after applying the + operator.
§

fn add(self, unit: Unit) -> Epoch

Performs the + operation. Read more
§

impl Add<f64> for Epoch

WARNING: For speed, there is a possibility to add seconds directly to an Epoch. These will be added in the time scale the Epoch was initialized in. Using this is discouraged and should only be used if you have facing bottlenecks with the units.

§

type Output = Epoch

The resulting type after applying the + operator.
§

fn add(self, seconds: f64) -> Epoch

Performs the + operation. Read more
§

impl AddAssign<Duration> for Epoch

§

fn add_assign(&mut self, duration: Duration)

Performs the += operation. Read more
§

impl AddAssign<Unit> for Epoch

§

fn add_assign(&mut self, unit: Unit)

Performs the += operation. Read more
§

impl Clone for Epoch

§

fn clone(&self) -> Epoch

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
§

impl Copy for Epoch

§

impl Debug for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

The default format of an epoch is in UTC

§

impl Default for Epoch

§

fn default() -> Epoch

Returns the “default value” for a type. Read more
§

impl DerefToPyAny for Epoch

§

impl<'de> Deserialize<'de> for Epoch

Available on crate feature serde and non-kani only.
§

fn deserialize<D>( deserializer: D, ) -> Result<Epoch, <D as Deserializer<'de>>::Error>
where D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
§

impl Display for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Print this epoch in Gregorian in the time scale used at initialization

§

impl Eq for Epoch

§

impl FromStr for Epoch

Available on non-kani only.
§

fn from_str(s_in: &str) -> Result<Epoch, <Epoch as FromStr>::Err>

Attempts to convert a string to an Epoch.

Format identifiers:

  • JD: Julian days
  • MJD: Modified Julian days
  • SEC: Seconds past a given epoch (e.g. SEC 17.2 TAI is 17.2 seconds past TAI Epoch)
§Example
use hifitime::Epoch;
use core::str::FromStr;

assert!(Epoch::from_str("JD 2452312.500372511 TDB").is_ok());
assert!(Epoch::from_str("JD 2452312.500372511 ET").is_ok());
assert!(Epoch::from_str("JD 2452312.500372511 TAI").is_ok());
assert!(Epoch::from_str("MJD 51544.5 TAI").is_ok());
assert!(Epoch::from_str("SEC 0.5 TAI").is_ok());
assert!(Epoch::from_str("SEC 66312032.18493909 TDB").is_ok());
§

type Err = HifitimeError

The associated error which can be returned from parsing.
§

impl Hash for Epoch

§

fn hash<H>(&self, state: &mut H)
where H: Hasher,

Hash normalizes to TAI for consistency with PartialEq.

1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
§

impl<'py> IntoPyObject<'py> for Epoch

§

type Target = Epoch

The Python output type
§

type Output = Bound<'py, <Epoch as IntoPyObject<'py>>::Target>

The smart pointer type to use. Read more
§

type Error = PyErr

The type returned in the event of a conversion error.
§

fn into_pyobject( self, py: Python<'py>, ) -> Result<<Epoch as IntoPyObject<'py>>::Output, <Epoch as IntoPyObject<'py>>::Error>

Performs the conversion.
§

impl LowerExp for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in TDB

§

impl LowerHex for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in TAI

§

impl Octal for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in GPS

§

impl Ord for Epoch

§

fn cmp(&self, other: &Epoch) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
§

impl PartialEq for Epoch

Equality only checks the duration since J1900 match in TAI, because this is how all of the epochs are referenced. Equality compares epochs as points in time. Uses field comparison via to_parts() to bypass Duration::PartialEq’s zero-crossing special case (which treats opposite-sign durations as equal).

§

fn eq(&self, other: &Epoch) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
§

impl PartialOrd for Epoch

§

fn partial_cmp(&self, other: &Epoch) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
§

impl Pointer for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in UNIX

§

impl PyClass for Epoch

§

const NAME: &'static str = "Epoch"

Name of the class. Read more
§

type Frozen = False

Whether the pyclass is frozen. Read more
§

impl PyTypeInfo for Epoch

§

const NAME: &'static str = <Self as ::pyo3::PyClass>::NAME

👎Deprecated since 0.28.0:

prefer using ::type_object(py).name() to get the correct runtime value

Class name.
§

const MODULE: Option<&'static str> = <Self as ::pyo3::impl_::pyclass::PyClassImpl>::MODULE

👎Deprecated since 0.28.0:

prefer using ::type_object(py).module() to get the correct runtime value

Module name, if any.
§

fn type_object_raw(py: Python<'_>) -> *mut PyTypeObject

Returns the PyTypeObject instance for this type.
§

fn type_object(py: Python<'_>) -> Bound<'_, PyType>

Returns the safe abstraction over the type object.
§

fn is_type_of(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type or a subclass of this type.
§

fn is_exact_type_of(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of this type.
§

impl Serialize for Epoch

Available on crate feature serde and non-kani only.
§

fn serialize<S>( &self, serializer: S, ) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where S: Serializer,

Serialize this value into the given Serde serializer. Read more
§

impl Sub for Epoch

§

type Output = Duration

The resulting type after applying the - operator.
§

fn sub(self, other: Epoch) -> Duration

Performs the - operation. Read more
§

impl Sub<Duration> for Epoch

§

type Output = Epoch

The resulting type after applying the - operator.
§

fn sub(self, duration: Duration) -> Epoch

Performs the - operation. Read more
§

impl Sub<Unit> for Epoch

§

type Output = Epoch

The resulting type after applying the - operator.
§

fn sub(self, unit: Unit) -> Epoch

Performs the - operation. Read more
§

impl SubAssign<Duration> for Epoch

§

fn sub_assign(&mut self, duration: Duration)

Performs the -= operation. Read more
§

impl SubAssign<Unit> for Epoch

§

fn sub_assign(&mut self, unit: Unit)

Performs the -= operation. Read more
§

impl UpperExp for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in ET

§

impl UpperHex for Epoch

§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Prints the Epoch in TT

Auto Trait Implementations§

§

impl Freeze for Epoch

§

impl RefUnwindSafe for Epoch

§

impl Send for Epoch

§

impl Sync for Epoch

§

impl Unpin for Epoch

§

impl UnsafeUnpin for Epoch

§

impl UnwindSafe for Epoch

Blanket Implementations§

§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T, Right> ClosedAdd<Right> for T
where T: Add<Right, Output = T> + AddAssign<Right>,

§

impl<T, Right> ClosedAdd<Right> for T
where T: Add<Right, Output = T> + AddAssign<Right>,

§

impl<T, Right> ClosedAddAssign<Right> for T
where T: ClosedAdd<Right> + AddAssign<Right>,

§

impl<T, Right> ClosedAddAssign<Right> for T
where T: ClosedAdd<Right> + AddAssign<Right>,

§

impl<T, Right> ClosedSub<Right> for T
where T: Sub<Right, Output = T> + SubAssign<Right>,

§

impl<T, Right> ClosedSub<Right> for T
where T: Sub<Right, Output = T> + SubAssign<Right>,

§

impl<T, Right> ClosedSubAssign<Right> for T
where T: ClosedSub<Right> + SubAssign<Right>,

§

impl<T, Right> ClosedSubAssign<Right> for T
where T: ClosedSub<Right> + SubAssign<Right>,

§

impl<Q, K> Comparable<K> for Q
where Q: Ord + ?Sized, K: Borrow<Q> + ?Sized,

§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromDhall for T

Source§

fn from_dhall(v: &Value) -> Result<T, Error>

§

impl<'a, 'py, T> FromPyObject<'a, 'py> for T
where T: PyClass + Clone + ExtractPyClassWithClone,

§

type Error = PyClassGuardError<'a, 'py>

The type returned in the event of a conversion error. Read more
§

fn extract( obj: Borrowed<'a, 'py, PyAny>, ) -> Result<T, <T as FromPyObject<'a, 'py>>::Error>

Extracts Self from the bound smart pointer obj. Read more
§

impl<'py, T> FromPyObjectOwned<'py> for T
where T: for<'a> FromPyObject<'a, 'py>,

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

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<'py, T> IntoPyObjectExt<'py> for T
where T: IntoPyObject<'py>,

§

fn into_bound_py_any(self, py: Python<'py>) -> Result<Bound<'py, PyAny>, PyErr>

Converts self into an owned Python object, dropping type information.
§

fn into_py_any(self, py: Python<'py>) -> Result<Py<PyAny>, PyErr>

Converts self into an owned Python object, dropping type information and unbinding it from the 'py lifetime.
§

fn into_pyobject_or_pyerr(self, py: Python<'py>) -> Result<Self::Output, PyErr>

Converts self into a Python object. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
§

impl<T> PyErrArguments for T
where T: for<'py> IntoPyObject<'py> + Send + Sync,

§

fn arguments(self, py: Python<'_>) -> Py<PyAny>

Arguments for exception
§

impl<T> PyTypeCheck for T
where T: PyTypeInfo,

§

const NAME: &'static str = T::NAME

👎Deprecated since 0.27.0:

Use ::classinfo_object() instead and format the type name at runtime. Note that using built-in cast features is often better than manual PyTypeCheck usage.

Name of self. This is used in error messages, for example.
§

fn type_check(object: &Bound<'_, PyAny>) -> bool

Checks if object is an instance of Self, which may include a subtype. Read more
§

fn classinfo_object(py: Python<'_>) -> Bound<'_, PyAny>

Returns the expected type as a possible argument for the isinstance and issubclass function. Read more
§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> RuleType for T
where T: Copy + Debug + Eq + Hash + Ord,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

§

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

Checks if self is actually part of its subset T (and can be converted to it).
§

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

The inclusion map: converts self to the equivalent element of its superset.
§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

§

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

Checks if self is actually part of its subset T (and can be converted to it).
§

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

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToDhall for T
where T: Serialize,

Source§

fn to_dhall(&self, ty: Option<&SimpleType>) -> Result<Value, Error>

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> Ungil for T
where T: Send,

§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more