satkit 0.20.0__tar.gz → 0.20.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {satkit-0.20.0 → satkit-0.20.2}/Cargo.toml +3 -2
- {satkit-0.20.0 → satkit-0.20.2}/MANIFEST.in +1 -0
- {satkit-0.20.0 → satkit-0.20.2}/PKG-INFO +1 -1
- satkit-0.20.2/benches/hotpaths.rs +189 -0
- {satkit-0.20.0 → satkit-0.20.2}/pyproject.toml +1 -1
- {satkit-0.20.0 → satkit-0.20.2}/python/Cargo.toml +4 -4
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit.egg-info/PKG-INFO +1 -1
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit.egg-info/SOURCES.txt +1 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pypropsettings.rs +1 -4
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pysgp4.rs +4 -4
- {satkit-0.20.0 → satkit-0.20.2}/src/kepler.rs +21 -12
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/tides.rs +6 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/instant.rs +2 -2
- {satkit-0.20.0 → satkit-0.20.2}/src/time/timescale.rs +1 -1
- {satkit-0.20.0 → satkit-0.20.2}/LICENSE-APACHE +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/LICENSE-MIT +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/README.md +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/build.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/build.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/__init__.py +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/__init__.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/density.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/frametransform.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/jplephem.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/moon.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/planets.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/py.typed +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/satkit.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/spaceweather.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/sun.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit/utils.pyi +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit.egg-info/dependency_links.txt +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit.egg-info/requires.txt +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/satkit.egg-info/top_level.txt +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/lib.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/mod_utils.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyconsts.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pydensity.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyduration.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyframes.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyframetransform.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pygravity.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyinstant.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyitrfcoord.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyjplephem.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pykepler.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pylambert.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pylpephem_moon.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pylpephem_planets.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pylpephem_sun.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pynrlmsise.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyomm.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pypropagate.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pypropresult.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyquaternion.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pysatproperties.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pysatstate.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pysolarsystem.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyspaceweather.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pythrust.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pytle.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pytlefitstatus.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/python/src/pyutils.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/setup.cfg +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/consts.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/earth_orientation_params.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/earthgravity.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frames.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frametransform/dispatch.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frametransform/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frametransform/ierstable.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frametransform/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/frametransform/qcirs2gcrs.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/itrfcoord.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/jplephem.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lambert.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lib.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lpephem/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lpephem/moon.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lpephem/planets.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/lpephem/sun.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/mathtypes.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/nrlmsise.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/omm/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/omm/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/omm/xml.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/drag.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/ode/gauss_jackson.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/ode/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/point_gravity.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/precomputed.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/propagator.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/relativity.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/satproperties.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/satstate.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/settings.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/orbitprop/thrust.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/dpper.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/dscom.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/dsinit.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/dspace.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/getgravconst.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/initl.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/satrec.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/sgp4_impl.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/sgp4_lowlevel.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/sgp4/sgp4init.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/solar_cycle_forecast.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/solarsystem.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/spaceweather.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/chrono.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/duration.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/instant_err.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/instant_ops.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/instantparse.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/tests.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/timelike.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/time/weekday.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/tle/error.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/tle/fitting.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/tle/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/datadir.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/download.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/mod.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/singleton.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/test.rs +0 -0
- {satkit-0.20.0 → satkit-0.20.2}/src/utils/update_data.rs +0 -0
|
@@ -3,7 +3,7 @@ members = [".", "python"]
|
|
|
3
3
|
|
|
4
4
|
[package]
|
|
5
5
|
name = "satkit"
|
|
6
|
-
version = "0.20.
|
|
6
|
+
version = "0.20.2"
|
|
7
7
|
edition = "2021"
|
|
8
8
|
description = "Satellite Toolkit"
|
|
9
9
|
readme = "README.md"
|
|
@@ -28,7 +28,7 @@ ureq = { version = "3.1.2", optional = true }
|
|
|
28
28
|
process_path = "0.1.4"
|
|
29
29
|
serde = { version = "1.0", features = ["derive"] }
|
|
30
30
|
serde_json = "1.0"
|
|
31
|
-
quick-xml = { version = "0.
|
|
31
|
+
quick-xml = { version = "0.41", features = ["serialize"], optional = true }
|
|
32
32
|
chrono = { version = "0.4", optional = true }
|
|
33
33
|
|
|
34
34
|
[dev-dependencies]
|
|
@@ -37,6 +37,7 @@ rand = "0.9.2"
|
|
|
37
37
|
approx = "0.5"
|
|
38
38
|
rand_distr = "0.5.1"
|
|
39
39
|
criterion = "0.8.2"
|
|
40
|
+
proptest = "1"
|
|
40
41
|
|
|
41
42
|
[[bench]]
|
|
42
43
|
name = "hotpaths"
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
//! Criterion benchmarks for satkit hot paths
|
|
2
|
+
//!
|
|
3
|
+
//! Covers the per-call costs that dominate real workloads: high-precision
|
|
4
|
+
//! propagation (per-regime and per-integrator), SGP4, frame transforms,
|
|
5
|
+
//! spherical-harmonic gravity, JPL ephemeris lookup, and the NRLMSISE-00
|
|
6
|
+
//! density model.
|
|
7
|
+
//!
|
|
8
|
+
//! Data files (EOP, gravity models, JPL ephemeris, space weather) are
|
|
9
|
+
//! resolved through the normal `satkit::utils::datadir()` discovery, same
|
|
10
|
+
//! as the test suite. Run with `cargo bench`.
|
|
11
|
+
|
|
12
|
+
use criterion::{criterion_group, criterion_main, Criterion};
|
|
13
|
+
use std::hint::black_box;
|
|
14
|
+
|
|
15
|
+
use satkit::consts;
|
|
16
|
+
use satkit::frametransform;
|
|
17
|
+
use satkit::mathtypes::{Matrix6, Matrix67, Vector3, Vector6};
|
|
18
|
+
use satkit::orbitprop::{propagate, Integrator, PropSettings, SatPropertiesSimple};
|
|
19
|
+
use satkit::{Duration, Instant};
|
|
20
|
+
|
|
21
|
+
fn epoch() -> Instant {
|
|
22
|
+
Instant::from_rfc3339("2024-01-01T00:00:00Z").unwrap()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/// Circular orbit state at radius `r` (meters) with inclination `incl` (radians)
|
|
26
|
+
fn circular_state(r: f64, incl: f64) -> Vector6 {
|
|
27
|
+
let v = (consts::MU_EARTH / r).sqrt();
|
|
28
|
+
numeris::vector![r, 0.0, 0.0, 0.0, v * incl.cos(), v * incl.sin()]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ISS_INCL_RAD: f64 = 51.6 * std::f64::consts::PI / 180.0;
|
|
32
|
+
|
|
33
|
+
fn bench_propagation(c: &mut Criterion) {
|
|
34
|
+
let mut group = c.benchmark_group("propagation");
|
|
35
|
+
group
|
|
36
|
+
.sample_size(10)
|
|
37
|
+
.warm_up_time(std::time::Duration::from_secs(2))
|
|
38
|
+
.measurement_time(std::time::Duration::from_secs(20));
|
|
39
|
+
|
|
40
|
+
let t0 = epoch();
|
|
41
|
+
let t1 = t0 + Duration::from_days(1.0);
|
|
42
|
+
|
|
43
|
+
// LEO with drag + full default force model, adaptive RKV 9(8)
|
|
44
|
+
let leo = circular_state(consts::WGS84_A + 500.0e3, ISS_INCL_RAD);
|
|
45
|
+
let satprops = SatPropertiesSimple::new(0.02, 0.01);
|
|
46
|
+
let settings = PropSettings::default();
|
|
47
|
+
group.bench_function("leo_drag_1day_rkv98", |b| {
|
|
48
|
+
b.iter(|| propagate(black_box(&leo), &t0, &t1, &settings, Some(&satprops)).unwrap())
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// GEO, sun/moon gravity dominated, adaptive RKV 9(8)
|
|
52
|
+
let geo = circular_state(consts::GEO_R, 0.0);
|
|
53
|
+
group.bench_function("geo_1day_rkv98", |b| {
|
|
54
|
+
b.iter(|| propagate(black_box(&geo), &t0, &t1, &settings, None).unwrap())
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// GEO, fixed-step Gauss-Jackson 8
|
|
58
|
+
let settings_gj8 = PropSettings {
|
|
59
|
+
integrator: Integrator::GaussJackson8,
|
|
60
|
+
gj_step_seconds: 300.0,
|
|
61
|
+
..PropSettings::default()
|
|
62
|
+
};
|
|
63
|
+
group.bench_function("geo_1day_gj8", |b| {
|
|
64
|
+
b.iter(|| propagate(black_box(&geo), &t0, &t1, &settings_gj8, None).unwrap())
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// LEO with 6x6 state transition matrix (C = 7 state columns)
|
|
68
|
+
let mut leo_stm = Matrix67::zeros();
|
|
69
|
+
leo_stm.set_block(0, 0, &leo);
|
|
70
|
+
leo_stm.set_block(0, 1, &Matrix6::eye());
|
|
71
|
+
group.bench_function("leo_stm_1day_rkv98", |b| {
|
|
72
|
+
b.iter(|| propagate(black_box(&leo_stm), &t0, &t1, &settings, Some(&satprops)).unwrap())
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
group.finish();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
fn bench_sgp4(c: &mut Criterion) {
|
|
79
|
+
let mut group = c.benchmark_group("sgp4");
|
|
80
|
+
|
|
81
|
+
let line0 = "0 INTELSAT 902";
|
|
82
|
+
let line1 = "1 26900U 01039A 06106.74503247 .00000045 00000-0 10000-3 0 8290";
|
|
83
|
+
let line2 = "2 26900 0.0164 266.5378 0003319 86.1794 182.2590 1.00273847 16981";
|
|
84
|
+
let tle = satkit::TLE::load_3line(line0, line1, line2).unwrap();
|
|
85
|
+
|
|
86
|
+
// 1 day of output at 1-minute cadence
|
|
87
|
+
let times: Vec<Instant> = (0..1441)
|
|
88
|
+
.map(|i| tle.epoch + Duration::from_seconds(60.0 * i as f64))
|
|
89
|
+
.collect();
|
|
90
|
+
|
|
91
|
+
// Includes per-TLE SGP4 initialization (fresh TLE each iteration)
|
|
92
|
+
group.bench_function("init_plus_1day_1min", |b| {
|
|
93
|
+
b.iter(|| {
|
|
94
|
+
let mut t = tle.clone();
|
|
95
|
+
satkit::sgp4::sgp4(&mut t, black_box(times.as_slice())).unwrap()
|
|
96
|
+
})
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Cached init state (same TLE reused), measures the propagation only
|
|
100
|
+
let mut warm = tle.clone();
|
|
101
|
+
let _ = satkit::sgp4::sgp4(&mut warm, ×[..1]).unwrap();
|
|
102
|
+
group.bench_function("cached_1day_1min", |b| {
|
|
103
|
+
b.iter(|| satkit::sgp4::sgp4(&mut warm, black_box(times.as_slice())).unwrap())
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
group.finish();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
fn bench_frametransform(c: &mut Criterion) {
|
|
110
|
+
let mut group = c.benchmark_group("frametransform");
|
|
111
|
+
let tm = epoch();
|
|
112
|
+
|
|
113
|
+
// Warm the EOP singleton so first-load cost isn't measured
|
|
114
|
+
let _ = frametransform::qgcrf2itrf(&tm);
|
|
115
|
+
|
|
116
|
+
group.bench_function("qgcrf2itrf", |b| {
|
|
117
|
+
b.iter(|| frametransform::qgcrf2itrf(black_box(&tm)))
|
|
118
|
+
});
|
|
119
|
+
group.bench_function("qgcrf2itrf_approx", |b| {
|
|
120
|
+
b.iter(|| frametransform::qgcrf2itrf_approx(black_box(&tm)))
|
|
121
|
+
});
|
|
122
|
+
group.bench_function("qteme2itrf", |b| {
|
|
123
|
+
b.iter(|| frametransform::qteme2itrf(black_box(&tm)))
|
|
124
|
+
});
|
|
125
|
+
group.bench_function("gmst", |b| b.iter(|| frametransform::gmst(black_box(&tm))));
|
|
126
|
+
|
|
127
|
+
group.finish();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fn bench_earthgravity(c: &mut Criterion) {
|
|
131
|
+
let mut group = c.benchmark_group("earthgravity");
|
|
132
|
+
|
|
133
|
+
// LEO position in ITRF (slightly off-axis to exercise all terms)
|
|
134
|
+
let pos: Vector3 = numeris::vector![consts::WGS84_A + 400.0e3, 1000.0e3, 2000.0e3];
|
|
135
|
+
let gravity = satkit::earthgravity::jgm3();
|
|
136
|
+
|
|
137
|
+
group.bench_function("accel_deg4", |b| {
|
|
138
|
+
b.iter(|| gravity.accel(black_box(&pos), 4, 4))
|
|
139
|
+
});
|
|
140
|
+
group.bench_function("accel_deg16", |b| {
|
|
141
|
+
b.iter(|| gravity.accel(black_box(&pos), 16, 16))
|
|
142
|
+
});
|
|
143
|
+
group.bench_function("accel_and_partials_deg4", |b| {
|
|
144
|
+
b.iter(|| gravity.accel_and_partials(black_box(&pos), 4, 4))
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
group.finish();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
fn bench_jplephem(c: &mut Criterion) {
|
|
151
|
+
let mut group = c.benchmark_group("jplephem");
|
|
152
|
+
let tm = epoch();
|
|
153
|
+
|
|
154
|
+
// Warm the ephemeris singleton
|
|
155
|
+
let _ = satkit::jplephem::geocentric_pos(satkit::SolarSystem::Moon, &tm);
|
|
156
|
+
|
|
157
|
+
group.bench_function("moon_geocentric_pos", |b| {
|
|
158
|
+
b.iter(|| satkit::jplephem::geocentric_pos(satkit::SolarSystem::Moon, black_box(&tm)))
|
|
159
|
+
});
|
|
160
|
+
group.bench_function("sun_geocentric_state", |b| {
|
|
161
|
+
b.iter(|| satkit::jplephem::geocentric_state(satkit::SolarSystem::Sun, black_box(&tm)))
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
group.finish();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fn bench_nrlmsise(c: &mut Criterion) {
|
|
168
|
+
let mut group = c.benchmark_group("nrlmsise");
|
|
169
|
+
let tm = epoch();
|
|
170
|
+
|
|
171
|
+
group.bench_function("density_400km", |b| {
|
|
172
|
+
b.iter(|| {
|
|
173
|
+
satkit::nrlmsise::nrlmsise(black_box(400.0), Some(0.5), Some(0.5), Some(&tm), true)
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
group.finish();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
criterion_group!(
|
|
181
|
+
benches,
|
|
182
|
+
bench_propagation,
|
|
183
|
+
bench_sgp4,
|
|
184
|
+
bench_frametransform,
|
|
185
|
+
bench_earthgravity,
|
|
186
|
+
bench_jplephem,
|
|
187
|
+
bench_nrlmsise
|
|
188
|
+
);
|
|
189
|
+
criterion_main!(benches);
|
|
@@ -9,7 +9,7 @@ requires-python = ">= 3.10"
|
|
|
9
9
|
authors = [{ name = "Steven Michael", email = "ssmichael@gmail.com" }]
|
|
10
10
|
maintainers = [{ name = "Steven Michael", email = "ssmichael@gmail.com" }]
|
|
11
11
|
readme = "README.md"
|
|
12
|
-
version = "0.20.
|
|
12
|
+
version = "0.20.2"
|
|
13
13
|
license = "MIT OR Apache-2.0"
|
|
14
14
|
description = "Satellite Orbital Dynamics Toolkit"
|
|
15
15
|
keywords = [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "satkit-python"
|
|
3
|
-
version = "0.20.
|
|
3
|
+
version = "0.20.2"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
publish = false
|
|
6
6
|
|
|
@@ -10,8 +10,8 @@ crate-type = ["cdylib"]
|
|
|
10
10
|
|
|
11
11
|
[dependencies]
|
|
12
12
|
satkit = { path = "..", features = ["download"] }
|
|
13
|
-
pyo3 = { version = "0.
|
|
14
|
-
numpy = "0.
|
|
13
|
+
pyo3 = { version = "0.29", features = ["extension-module", "anyhow"] }
|
|
14
|
+
numpy = "0.29"
|
|
15
15
|
numeris = { version = "0.5.14", features = ["serde", "ode"] }
|
|
16
16
|
anyhow = "1"
|
|
17
17
|
serde = { version = "1.0", features = ["derive"] }
|
|
@@ -19,4 +19,4 @@ serde-pickle = "1.2.0"
|
|
|
19
19
|
process_path = "0.1.4"
|
|
20
20
|
|
|
21
21
|
[build-dependencies]
|
|
22
|
-
pyo3-build-config = "0.
|
|
22
|
+
pyo3-build-config = "0.29"
|
|
@@ -394,10 +394,7 @@ impl PyPropSettings {
|
|
|
394
394
|
Some(d.0.as_seconds())
|
|
395
395
|
} else if let Ok(secs) = obj.extract::<f64>() {
|
|
396
396
|
Some(secs)
|
|
397
|
-
} else if let Ok(delta) = {
|
|
398
|
-
#[allow(deprecated)]
|
|
399
|
-
obj.downcast::<PyDelta>()
|
|
400
|
-
} {
|
|
397
|
+
} else if let Ok(delta) = obj.cast::<PyDelta>() {
|
|
401
398
|
Some(
|
|
402
399
|
delta.get_days() as f64 * 86400.0
|
|
403
400
|
+ delta.get_seconds() as f64
|
|
@@ -391,7 +391,7 @@ pub fn sgp4(
|
|
|
391
391
|
// TLE sources keep a handle to the originating Python object so the
|
|
392
392
|
// cached SGP4 init state can be written back afterward.
|
|
393
393
|
enum Sgp4Source {
|
|
394
|
-
Tle(Py<PyTLE>, satkit::TLE),
|
|
394
|
+
Tle(Py<PyTLE>, Box<satkit::TLE>),
|
|
395
395
|
Omm(Box<satkit::omm::OMM>),
|
|
396
396
|
}
|
|
397
397
|
|
|
@@ -402,7 +402,7 @@ pub fn sgp4(
|
|
|
402
402
|
let pytle: Py<PyTLE> = item.extract().map_err(|e| {
|
|
403
403
|
pyo3::exceptions::PyValueError::new_err(format!("Invalid TLE: {}", e))
|
|
404
404
|
})?;
|
|
405
|
-
let rtle = pytle.borrow(item.py()).0.clone();
|
|
405
|
+
let rtle = Box::new(pytle.borrow(item.py()).0.clone());
|
|
406
406
|
Ok(Sgp4Source::Tle(pytle, rtle))
|
|
407
407
|
} else if item.is_instance_of::<PyDict>() {
|
|
408
408
|
let dict: &Bound<'_, PyDict> = item.cast().map_err(|e| {
|
|
@@ -428,7 +428,7 @@ pub fn sgp4(
|
|
|
428
428
|
.map(|src| -> Result<psgp4::SGP4State> {
|
|
429
429
|
match src {
|
|
430
430
|
Sgp4Source::Tle(_, rtle) => {
|
|
431
|
-
Ok(psgp4::sgp4_full(rtle, tmarray.as_slice(), gc, om)?)
|
|
431
|
+
Ok(psgp4::sgp4_full(rtle.as_mut(), tmarray.as_slice(), gc, om)?)
|
|
432
432
|
}
|
|
433
433
|
Sgp4Source::Omm(omm) => {
|
|
434
434
|
Ok(psgp4::sgp4_full(omm.as_mut(), tmarray.as_slice(), gc, om)?)
|
|
@@ -441,7 +441,7 @@ pub fn sgp4(
|
|
|
441
441
|
// Write the TLEs back to preserve their cached SGP4 init state
|
|
442
442
|
for src in &sources {
|
|
443
443
|
if let Sgp4Source::Tle(pytle, rtle) = src {
|
|
444
|
-
pytle.borrow_mut(tle.py()).0 = rtle.clone();
|
|
444
|
+
pytle.borrow_mut(tle.py()).0 = rtle.as_ref().clone();
|
|
445
445
|
}
|
|
446
446
|
}
|
|
447
447
|
|
|
@@ -76,23 +76,32 @@ pub struct Kepler {
|
|
|
76
76
|
// Convert mean to eccentric anomaly
|
|
77
77
|
// iterative solution required
|
|
78
78
|
fn mean2eccentric(m: f64, eccen: f64) -> f64 {
|
|
79
|
-
use std::f64::consts::
|
|
79
|
+
use std::f64::consts::TAU;
|
|
80
|
+
// Range-reduce the mean anomaly to [0, 2π). Kepler's equation shifts by
|
|
81
|
+
// 2πk in E and M together, so the solution for the reduced M is shifted
|
|
82
|
+
// back at the end. Without this, an unwrapped M (e.g. after propagating
|
|
83
|
+
// multiple revolutions) puts the naive initial guess in a near-flat region
|
|
84
|
+
// of the equation at high eccentricity, where Newton's trajectory turns
|
|
85
|
+
// chaotic and can exhaust the iteration cap with a wildly wrong root.
|
|
86
|
+
let k = (m / TAU).floor();
|
|
87
|
+
let mr = m - k * TAU;
|
|
88
|
+
|
|
89
|
+
// Danby (1987) initial guess: E₀ = M + 0.85·e·sign(sin M). Together with
|
|
90
|
+
// the range reduction this keeps plain Newton convergent in < ~10
|
|
91
|
+
// iterations for all eccentricities below 1, including e > 0.9.
|
|
80
92
|
#[allow(non_snake_case)]
|
|
81
|
-
let mut E =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// (e >= 1, where the step can go non-finite) cannot spin forever.
|
|
88
|
-
for _ in 0..30 {
|
|
89
|
-
let de = eccen.mul_add(E.sin(), m - E) / eccen.mul_add(-E.cos(), 1.0);
|
|
93
|
+
let mut E = mr + 0.85 * eccen * if mr.sin() >= 0.0 { 1.0 } else { -1.0 };
|
|
94
|
+
|
|
95
|
+
// Cap the iteration count so a pathological eccentricity (e >= 1, where
|
|
96
|
+
// the step can go non-finite) cannot spin forever.
|
|
97
|
+
for _ in 0..50 {
|
|
98
|
+
let de = eccen.mul_add(E.sin(), mr - E) / eccen.mul_add(-E.cos(), 1.0);
|
|
90
99
|
E += de;
|
|
91
|
-
if de.abs() < 1.0e-
|
|
100
|
+
if de.abs() < 1.0e-13 {
|
|
92
101
|
break;
|
|
93
102
|
}
|
|
94
103
|
}
|
|
95
|
-
E
|
|
104
|
+
E + k * TAU
|
|
96
105
|
}
|
|
97
106
|
|
|
98
107
|
fn eccentric2true(ea: f64, eccen: f64) -> f64 {
|
|
@@ -51,6 +51,9 @@ pub struct TideDeltas {
|
|
|
51
51
|
// Index convention: K_RE[n][m], K_IM[n][m].
|
|
52
52
|
// Entries outside the (n,m) range listed in Table 6.3 are zero.
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
54
|
+
// The k_22 Love number (0.30102) coincidentally resembles log10(2); it is a
|
|
55
|
+
// physical constant from Table 6.3, not a math constant.
|
|
56
|
+
#[allow(clippy::approx_constant)]
|
|
54
57
|
const K_RE: [[f64; 4]; 4] = [
|
|
55
58
|
[0.0, 0.0, 0.0, 0.0],
|
|
56
59
|
[0.0, 0.0, 0.0, 0.0],
|
|
@@ -71,7 +74,10 @@ const K_PLUS: [f64; 3] = [-0.00089, -0.00080, -0.00057];
|
|
|
71
74
|
// Fully-normalized associated-Legendre normalization factors
|
|
72
75
|
// N_nm = sqrt((2 - δ_0m)(2n+1)(n-m)!/(n+m)!). Constants chosen to allow
|
|
73
76
|
// the recursion-free closed-form evaluation of P̄_nm used here.
|
|
77
|
+
// Written to full precision deliberately (clippy flags a digit beyond f64's
|
|
78
|
+
// round-trip length on N21; keeping the mathematical value verbatim).
|
|
74
79
|
// ---------------------------------------------------------------------------
|
|
80
|
+
#[allow(clippy::excessive_precision)]
|
|
75
81
|
const N20: f64 = 2.2360679774997896; // sqrt(5)
|
|
76
82
|
const N21: f64 = 1.2909944487358056; // sqrt(5/3)
|
|
77
83
|
const N22: f64 = 0.6454972243679028; // sqrt(5/12)
|
|
@@ -320,7 +320,7 @@ impl Instant {
|
|
|
320
320
|
TimeScale::UT1 => {
|
|
321
321
|
// This will be approximately correct for computing ut1
|
|
322
322
|
let eop = crate::earth_orientation_params::eop_from_mjd_utc_or_zero(mjd);
|
|
323
|
-
let dut1 = eop[0]
|
|
323
|
+
let dut1 = eop[0];
|
|
324
324
|
Self::from_mjd_with_scale(mjd - dut1 / 86_400.0, TimeScale::UTC)
|
|
325
325
|
}
|
|
326
326
|
TimeScale::GPS => {
|
|
@@ -415,7 +415,7 @@ impl Instant {
|
|
|
415
415
|
TimeScale::UT1 => {
|
|
416
416
|
let mjd_utc = self.as_mjd_utc();
|
|
417
417
|
let eop = crate::earth_orientation_params::eop_from_mjd_utc_or_zero(mjd_utc);
|
|
418
|
-
let dut1 = eop[0]
|
|
418
|
+
let dut1 = eop[0];
|
|
419
419
|
mjd_utc + dut1 / 86_400.0
|
|
420
420
|
}
|
|
421
421
|
TimeScale::TAI => (self.raw - Self::MJD_EPOCH.raw) as f64 / 86_400_000_000.0,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|