satkit 0.2.8__tar.gz → 0.2.9__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.2.8 → satkit-0.2.9}/Cargo.toml +1 -1
- {satkit-0.2.8/python/satkit.egg-info → satkit-0.2.9}/PKG-INFO +1 -1
- {satkit-0.2.8 → satkit-0.2.9}/pyproject.toml +1 -1
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/__init__.pyi +2 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/jplephem.pyi +1 -1
- satkit-0.2.9/python/satkit/planets.pyi +28 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/satkit.pyi +51 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/satprop.pyi +6 -3
- {satkit-0.2.8 → satkit-0.2.9/python/satkit.egg-info}/PKG-INFO +1 -1
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/SOURCES.txt +4 -0
- satkit-0.2.9/src/frames.rs +52 -0
- satkit-0.2.9/src/kepler.rs +312 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/lib.rs +4 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/mod.rs +2 -2
- {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/planets.rs +63 -114
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/satstate.rs +12 -12
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/mod.rs +16 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyastrotime.rs +28 -3
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyduration.rs +21 -1
- satkit-0.2.9/src/pybindings/pyframes.rs +53 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyitrfcoord.rs +34 -1
- satkit-0.2.9/src/pybindings/pykepler.rs +231 -0
- satkit-0.2.9/src/pybindings/pylpephem_planets.rs +21 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyquaternion.rs +30 -2
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysatproperties.rs +27 -1
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysatstate.rs +39 -4
- satkit-0.2.9/src/pybindings/pytle.rs +242 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/solarsystem.rs +19 -0
- satkit-0.2.8/src/kepler.rs +0 -159
- satkit-0.2.8/src/pybindings/pykepler.rs +0 -98
- satkit-0.2.8/src/pybindings/pytle.rs +0 -150
- {satkit-0.2.8 → satkit-0.2.9}/LICENSE +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/MANIFEST.in +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/README.md +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/docs/source/conf.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/__init__.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/density.pyi +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/frametransform.pyi +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/moon.pyi +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/py.typed +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/sun.pyi +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit/utils.pyi +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/dependency_links.txt +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/requires.txt +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/top_level.txt +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/download_data.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/download_from_json.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/download_testvecs.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/orbitprop_gps_fit.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/sp3file.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/python/test/test.py +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/setup.cfg +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/astrotime.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/consts.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/duration.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/earth_orientation_params.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/earthgravity.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/ierstable.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/mod.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/qcirs2gcrs.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/itrfcoord.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/jplephem.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/moon.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/sun.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/nrlmsise.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/harmonic_oscillator.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/mod.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/nalgebra.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_adaptive.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_adaptive_settings.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_explicit.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkf45.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkts54.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv65.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv65_table.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv87.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv87_table.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_nointerp.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_nointerp_table.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_table.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/ode/types.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/drag.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/mod.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/point_gravity.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/propagator.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/satproperties.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/settings.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/mod_utils.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyconsts.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pydensity.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyframetransform.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pygravity.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyjplephem.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pylpephem_moon.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pylpephem_sun.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pynrlmsise.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pypropagate.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pypropsettings.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysgp4.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysolarsystem.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyutils.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dpper.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dscom.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dsinit.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dspace.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/getgravconst.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/initl.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/mod.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/satrec.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4_lowlevel.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4init.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/spaceweather.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/tle.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/types.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/datadir.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/download.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/mod.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/skerror.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/test.rs +0 -0
- {satkit-0.2.8 → satkit-0.2.9}/src/utils/update_data.rs +0 -0
|
@@ -8,6 +8,7 @@ from . import sun
|
|
|
8
8
|
from . import satprop
|
|
9
9
|
from . import density
|
|
10
10
|
from . import utils
|
|
11
|
+
from . import planets
|
|
11
12
|
|
|
12
13
|
__all__ = [
|
|
13
14
|
"time",
|
|
@@ -28,6 +29,7 @@ __all__ = [
|
|
|
28
29
|
"utils",
|
|
29
30
|
"moon",
|
|
30
31
|
"sun",
|
|
32
|
+
"planets",
|
|
31
33
|
"satprop",
|
|
32
34
|
"density",
|
|
33
35
|
"utils"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Low-precision planetary ephemerides from https://ssd.jpl.nasa.gov/?planet_pos
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
import typing
|
|
7
|
+
import numpy.typing as npt
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
import satkit
|
|
11
|
+
|
|
12
|
+
def heliocentric_pos(body: satkit.solarsystem, tm: satkit.time|list[satkit.time]|npt.ArrayLike[satkit.time]) -> npt.ArrayLike[np.float64]:
|
|
13
|
+
"""Return the position of the given body in the Heliocentric coordinate system (origin is the Sun)
|
|
14
|
+
|
|
15
|
+
Note: This function is only valid for the Sun and the 8 planets
|
|
16
|
+
|
|
17
|
+
Note: This is less accurate than using the jpl ephemeris, but involves fewer calculations
|
|
18
|
+
|
|
19
|
+
Note: See https://ssd.jpl.nasa.gov/?planet_pos for more information and accuracy details
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
body (satkit.solarsystem): Solar system body for which to return position
|
|
23
|
+
tm (satkit.time|numpy.ndarray|list): Time[s] at which to return position
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
numpy.ndarray: 3-vector of Cartesian position in meters, with the origin at the Sun.
|
|
27
|
+
If input is list or numpy array of N times, then r will be Nx3 array
|
|
28
|
+
"""
|
|
@@ -1073,6 +1073,16 @@ class kepler:
|
|
|
1073
1073
|
argp (float) : Argument of perigee, radians
|
|
1074
1074
|
nu (float) : True anomaly, radians
|
|
1075
1075
|
|
|
1076
|
+
# Keyword Args:
|
|
1077
|
+
true_anomaly (float) : True anomaly, radians
|
|
1078
|
+
mean_anomaly (float) : Mean anomaly, radians
|
|
1079
|
+
eccentric_anomaly (float) : Eccentric anomaly, radians
|
|
1080
|
+
|
|
1081
|
+
# Notes:
|
|
1082
|
+
* If "nu" is provided (6th argument), it will be used as the true anomaly
|
|
1083
|
+
* Anomaly may also be set via keyword arguments; if so, the there should only be
|
|
1084
|
+
5 input arguments
|
|
1085
|
+
|
|
1076
1086
|
# Returns:
|
|
1077
1087
|
satkit.kepler: Keplerian element set object
|
|
1078
1088
|
"""
|
|
@@ -1084,6 +1094,47 @@ class kepler:
|
|
|
1084
1094
|
tuple[npt.ArrayLike[np.float64], npt.ArrayLike[np.float64]]: Tuple with two elements representing the position and velocity vectors
|
|
1085
1095
|
"""
|
|
1086
1096
|
|
|
1097
|
+
def propagate(self, dt: duration|float) -> kepler:
|
|
1098
|
+
"""Propagate Keplerian element set by input duration
|
|
1099
|
+
|
|
1100
|
+
Args:
|
|
1101
|
+
dt (duration | float): Duration by which to propagate the Keplerian element set
|
|
1102
|
+
If float, value is seconds
|
|
1103
|
+
|
|
1104
|
+
Returns:
|
|
1105
|
+
satkit.kepler: Keplerian element set object after propagation
|
|
1106
|
+
"""
|
|
1107
|
+
|
|
1108
|
+
@property
|
|
1109
|
+
def mean_motion(self) -> float:
|
|
1110
|
+
"""Mean motion, radians / second
|
|
1111
|
+
"""
|
|
1112
|
+
|
|
1113
|
+
@property
|
|
1114
|
+
def true_anomaly(self) -> float:
|
|
1115
|
+
"""True anomaly, radians
|
|
1116
|
+
"""
|
|
1117
|
+
|
|
1118
|
+
@property
|
|
1119
|
+
def eccentric_anomaly(self) -> float:
|
|
1120
|
+
"""Eccentric anomaly, radians
|
|
1121
|
+
"""
|
|
1122
|
+
|
|
1123
|
+
@property
|
|
1124
|
+
def mean_anomaly(self) -> float:
|
|
1125
|
+
"""Mean anomaly, radians
|
|
1126
|
+
"""
|
|
1127
|
+
|
|
1128
|
+
@property
|
|
1129
|
+
def mean_motion(self) -> float:
|
|
1130
|
+
"""Mean motion, radians / second
|
|
1131
|
+
"""
|
|
1132
|
+
|
|
1133
|
+
@property
|
|
1134
|
+
def period(self) -> float:
|
|
1135
|
+
"""Orbital period, seconds
|
|
1136
|
+
"""
|
|
1137
|
+
|
|
1087
1138
|
def from_pv(
|
|
1088
1139
|
pos: npt.ArrayLike[np.float64], vel: npt.ArrayLike[np.float64]
|
|
1089
1140
|
) -> kepler:
|
|
@@ -113,7 +113,7 @@ class satstate:
|
|
|
113
113
|
"""
|
|
114
114
|
|
|
115
115
|
@property
|
|
116
|
-
def
|
|
116
|
+
def pos_gcrf(self) -> npt.ArrayLike[np.float64]:
|
|
117
117
|
"""state position in meters in GCRF frame
|
|
118
118
|
|
|
119
119
|
Returns:
|
|
@@ -121,7 +121,7 @@ class satstate:
|
|
|
121
121
|
"""
|
|
122
122
|
|
|
123
123
|
@property
|
|
124
|
-
def
|
|
124
|
+
def vel_gcrf(self) -> npt.ArrayLike[np.float64]:
|
|
125
125
|
"""Return this state velocity in meters / second in GCRF
|
|
126
126
|
|
|
127
127
|
Returns:
|
|
@@ -154,9 +154,12 @@ class satstate:
|
|
|
154
154
|
satkit.time: Time instant of this state
|
|
155
155
|
"""
|
|
156
156
|
|
|
157
|
-
def propagate(self, time: satkit.time, propsettings=None) -> satstate:
|
|
157
|
+
def propagate(self, time: satkit.time|satkit.duration, propsettings=None) -> satstate:
|
|
158
158
|
"""Propagate this state to a new time, specified by the "time" input, updating the position, the velocity, and the covariance if set
|
|
159
159
|
|
|
160
|
+
Args:
|
|
161
|
+
time (satkit.time|satkit.duration): Time or duration from current time to which to propagate the state
|
|
162
|
+
|
|
160
163
|
Keyword Arguments:
|
|
161
164
|
propsettings: satkit.satprop.propsettings object describing settings to use in the propagation. If omitted, default is used
|
|
162
165
|
|
|
@@ -10,6 +10,7 @@ python/satkit/density.pyi
|
|
|
10
10
|
python/satkit/frametransform.pyi
|
|
11
11
|
python/satkit/jplephem.pyi
|
|
12
12
|
python/satkit/moon.pyi
|
|
13
|
+
python/satkit/planets.pyi
|
|
13
14
|
python/satkit/py.typed
|
|
14
15
|
python/satkit/satkit.pyi
|
|
15
16
|
python/satkit/satprop.pyi
|
|
@@ -31,6 +32,7 @@ src/consts.rs
|
|
|
31
32
|
src/duration.rs
|
|
32
33
|
src/earth_orientation_params.rs
|
|
33
34
|
src/earthgravity.rs
|
|
35
|
+
src/frames.rs
|
|
34
36
|
src/itrfcoord.rs
|
|
35
37
|
src/jplephem.rs
|
|
36
38
|
src/kepler.rs
|
|
@@ -77,12 +79,14 @@ src/pybindings/pyastrotime.rs
|
|
|
77
79
|
src/pybindings/pyconsts.rs
|
|
78
80
|
src/pybindings/pydensity.rs
|
|
79
81
|
src/pybindings/pyduration.rs
|
|
82
|
+
src/pybindings/pyframes.rs
|
|
80
83
|
src/pybindings/pyframetransform.rs
|
|
81
84
|
src/pybindings/pygravity.rs
|
|
82
85
|
src/pybindings/pyitrfcoord.rs
|
|
83
86
|
src/pybindings/pyjplephem.rs
|
|
84
87
|
src/pybindings/pykepler.rs
|
|
85
88
|
src/pybindings/pylpephem_moon.rs
|
|
89
|
+
src/pybindings/pylpephem_planets.rs
|
|
86
90
|
src/pybindings/pylpephem_sun.rs
|
|
87
91
|
src/pybindings/pynrlmsise.rs
|
|
88
92
|
src/pybindings/pypropagate.rs
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
#[derive(Clone, Debug)]
|
|
4
|
+
pub enum Frame {
|
|
5
|
+
/// International Terrestrial Reference Frame
|
|
6
|
+
ITRF,
|
|
7
|
+
/// Terrestrial Intermediate Reference System
|
|
8
|
+
TIRS,
|
|
9
|
+
/// Celestial Intermediate Reference System
|
|
10
|
+
CIRS,
|
|
11
|
+
/// Geocentric Celestial Reference Frame
|
|
12
|
+
GCRF,
|
|
13
|
+
/// True Equator Mean Equinox
|
|
14
|
+
TEME,
|
|
15
|
+
/// Earth Mean Equator 2000
|
|
16
|
+
EME2000,
|
|
17
|
+
/// International Celestial Reference Frame
|
|
18
|
+
ICRF,
|
|
19
|
+
/// Local Vertical Local Horizontal
|
|
20
|
+
LVLH,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl std::fmt::Display for Frame {
|
|
24
|
+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
25
|
+
match self {
|
|
26
|
+
Frame::ITRF => write!(f, "ITRF"),
|
|
27
|
+
Frame::TIRS => write!(f, "TIRS"),
|
|
28
|
+
Frame::CIRS => write!(f, "CIRS"),
|
|
29
|
+
Frame::GCRF => write!(f, "GCRF"),
|
|
30
|
+
Frame::TEME => write!(f, "TEME"),
|
|
31
|
+
Frame::EME2000 => write!(f, "EME2000"),
|
|
32
|
+
Frame::ICRF => write!(f, "ICRF"),
|
|
33
|
+
Frame::LVLH => write!(f, "LVLH"),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
impl Frame {
|
|
39
|
+
pub fn from_str(s: &str) -> Result<Self, &'static str> {
|
|
40
|
+
match s {
|
|
41
|
+
"ITRF" => Ok(Frame::ITRF),
|
|
42
|
+
"TIRS" => Ok(Frame::TIRS),
|
|
43
|
+
"CIRS" => Ok(Frame::CIRS),
|
|
44
|
+
"GCRF" => Ok(Frame::GCRF),
|
|
45
|
+
"TEME" => Ok(Frame::TEME),
|
|
46
|
+
"EME2000" => Ok(Frame::EME2000),
|
|
47
|
+
"ICRF" => Ok(Frame::ICRF),
|
|
48
|
+
"LVLH" => Ok(Frame::LVLH),
|
|
49
|
+
_ => Err("Invalid frame"),
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
//! Keplerian orbital elements module
|
|
2
|
+
//!
|
|
3
|
+
|
|
4
|
+
use thiserror::Error;
|
|
5
|
+
use crate::SKResult;
|
|
6
|
+
|
|
7
|
+
#[derive(Debug, Error)]
|
|
8
|
+
pub enum KeplerError {
|
|
9
|
+
#[error("Eccentricity Out of Bounds {x}")]
|
|
10
|
+
EccenOutOfBound{x: f64}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/// Keplerian element can be defined by multiple
|
|
14
|
+
/// types of "anomalies", which describe the position
|
|
15
|
+
/// of the satellite orbiting the central body within the orbital plane
|
|
16
|
+
///
|
|
17
|
+
/// These are:
|
|
18
|
+
///
|
|
19
|
+
/// * `True Anomaly` - Denoted ν, is the Periapsis-Earth-Satellite
|
|
20
|
+
/// angle in the orbital plane
|
|
21
|
+
///
|
|
22
|
+
/// * `Mean Anomaly` - Denoted M, this does not have a great geographical
|
|
23
|
+
/// representation, but is an angle that increases monotonically in time
|
|
24
|
+
/// between 0 and 2π over the course of a single orbit.
|
|
25
|
+
///
|
|
26
|
+
/// * `Eccentric Anomaly` - Denoted E, is the Periaps-C-B
|
|
27
|
+
/// angle in the orbital plane, wehre "C" is the center of the orbital
|
|
28
|
+
/// ellipse, and "B" is a point on the auxilliary circle (the circle
|
|
29
|
+
/// bounding the orbital ellipse) along a line from the satellite
|
|
30
|
+
/// and perpendicular to the semimajor axis. The eccentric anomaly is
|
|
31
|
+
/// a useful prerequisite to compute the mean anomaly
|
|
32
|
+
///
|
|
33
|
+
pub enum Anomaly {
|
|
34
|
+
Mean(f64),
|
|
35
|
+
True(f64),
|
|
36
|
+
Eccentric(f64)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// External library imports
|
|
40
|
+
use nalgebra::{Vector3, UnitQuaternion};
|
|
41
|
+
type Vec3 = Vector3<f64>;
|
|
42
|
+
type Quat = UnitQuaternion<f64>;
|
|
43
|
+
|
|
44
|
+
/// Keplerian Orbital Elements
|
|
45
|
+
///
|
|
46
|
+
/// The 6 Keplerian orbital elements are:
|
|
47
|
+
/// a: semi-major axis, meters
|
|
48
|
+
/// eccen: Eccentricity
|
|
49
|
+
/// incl: Inclination, radians
|
|
50
|
+
/// RAAN: Right Ascension of the Ascending Node, radians
|
|
51
|
+
/// w: Argument of Perigee, radians
|
|
52
|
+
/// an: Anomaly of given type, radians
|
|
53
|
+
#[derive(Debug, Clone)]
|
|
54
|
+
pub struct Kepler {
|
|
55
|
+
pub a: f64,
|
|
56
|
+
pub eccen: f64,
|
|
57
|
+
pub incl: f64,
|
|
58
|
+
pub raan: f64,
|
|
59
|
+
pub w: f64,
|
|
60
|
+
pub nu: f64, // True anomaly
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Convert mean to eccentric anomaly
|
|
64
|
+
// iterative solution required
|
|
65
|
+
fn mean2eccentric(m: f64, eccen: f64) -> f64
|
|
66
|
+
{
|
|
67
|
+
use std::f64::consts::PI;
|
|
68
|
+
#[allow(non_snake_case)]
|
|
69
|
+
let mut E = match (m > PI) || ((m < 0.0) && (m > -PI)) {
|
|
70
|
+
true => m - eccen,
|
|
71
|
+
false => m + eccen
|
|
72
|
+
};
|
|
73
|
+
loop {
|
|
74
|
+
let de = (m - E + eccen*E.sin()) / (1.0 - eccen*E.cos());
|
|
75
|
+
E += de;
|
|
76
|
+
if de.abs() < 1.0e-6 {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
E
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn eccentric2true(ea: f64, eccen: f64) -> f64 {
|
|
84
|
+
f64::atan2(
|
|
85
|
+
ea.sin() * (1.0 - eccen.powi(2)).sqrt(),
|
|
86
|
+
ea.cos() - eccen
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
fn mean2true(ma: f64, eccen: f64) -> f64 {
|
|
91
|
+
eccentric2true(mean2eccentric(ma, eccen), eccen)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fn to_trueanomaly(an: Anomaly, eccen: f64) -> f64 {
|
|
95
|
+
match an {
|
|
96
|
+
Anomaly::True(v) => v,
|
|
97
|
+
Anomaly::Mean(ma) => {
|
|
98
|
+
mean2true(ma, eccen)
|
|
99
|
+
}
|
|
100
|
+
Anomaly::Eccentric(ea) => {
|
|
101
|
+
eccentric2true(ea, eccen)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
impl Kepler {
|
|
107
|
+
/// Create a new Keplerian orbital element object
|
|
108
|
+
///
|
|
109
|
+
/// # Arguments
|
|
110
|
+
///
|
|
111
|
+
/// * `a` - Semi-major axis, meters
|
|
112
|
+
/// * `e` - Eccentricity
|
|
113
|
+
/// * `i` - Inclination, radians
|
|
114
|
+
/// * `raan` - Right Ascension of the Ascending Node, radians
|
|
115
|
+
/// * `argp` - Argument of Perigee, radians
|
|
116
|
+
/// * `anomaly` - Anomaly type representing location of satellite along the
|
|
117
|
+
/// orbital plane
|
|
118
|
+
///
|
|
119
|
+
/// # Returns
|
|
120
|
+
///
|
|
121
|
+
/// * `Kepler` - A new Keplerian orbital element object
|
|
122
|
+
pub fn new(a: f64, eccen: f64, i: f64, raan: f64, argp: f64, an: Anomaly) -> Kepler {
|
|
123
|
+
Kepler {
|
|
124
|
+
a: a,
|
|
125
|
+
eccen: eccen,
|
|
126
|
+
incl: i,
|
|
127
|
+
raan: raan,
|
|
128
|
+
w: argp,
|
|
129
|
+
nu: to_trueanomaly(an, eccen)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/// Return the semiparameter of the satellite orbit
|
|
134
|
+
///
|
|
135
|
+
/// The semiparameter is also known as the semi-latus rectum
|
|
136
|
+
/// # Returns
|
|
137
|
+
///
|
|
138
|
+
/// * `f64` - Semiparameter, meters
|
|
139
|
+
pub fn semiparameter(&self) -> f64 {
|
|
140
|
+
self.a * (1.0 - self.eccen.powi(2))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/// Propagate the orbit forward (or backward) in time
|
|
144
|
+
/// by givend duration
|
|
145
|
+
///
|
|
146
|
+
/// # Arguments
|
|
147
|
+
///
|
|
148
|
+
/// * `dt` - `satkit.Duration` object representing the time to propagate
|
|
149
|
+
///
|
|
150
|
+
/// # Returns
|
|
151
|
+
///
|
|
152
|
+
/// * `Kepler` - A new Keplerian orbital element object
|
|
153
|
+
pub fn propagate(&self, dt: &crate::Duration) -> Kepler {
|
|
154
|
+
let n = self.mean_motion();
|
|
155
|
+
let ma = self.mean_anomaly() + n*dt.seconds();
|
|
156
|
+
let nu = mean2true(ma, self.eccen);
|
|
157
|
+
Kepler {
|
|
158
|
+
a: self.a,
|
|
159
|
+
eccen: self.eccen,
|
|
160
|
+
incl: self.incl,
|
|
161
|
+
raan: self.raan,
|
|
162
|
+
w: self.w,
|
|
163
|
+
nu: nu
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Return the eccentric anomaly of the satellite in radians
|
|
168
|
+
pub fn eccentric_anomaly(&self) -> f64 {
|
|
169
|
+
f64::atan2(
|
|
170
|
+
self.nu.sin()*(1.0-self.eccen.powi(2)).sqrt(),
|
|
171
|
+
1.0 + self.eccen * self.nu.cos())
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Return the mean anomaly of the satellite in radians
|
|
175
|
+
pub fn mean_anomaly(&self) -> f64 {
|
|
176
|
+
let ea = self.eccentric_anomaly();
|
|
177
|
+
ea - self.eccen*ea.sin()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// Return the true anomaly of the satellite in radians
|
|
181
|
+
pub fn true_anomaly(&self) -> f64 {
|
|
182
|
+
self.nu
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/// Return the mean motion of the satellite in radians/second
|
|
186
|
+
///
|
|
187
|
+
/// # Returns
|
|
188
|
+
///
|
|
189
|
+
/// * `f64` - Mean motion, radians/second
|
|
190
|
+
pub fn mean_motion(&self) -> f64 {
|
|
191
|
+
(crate::consts::MU_EARTH / self.a.powi(3)).sqrt()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/// Return the period of the satellite in seconds
|
|
195
|
+
///
|
|
196
|
+
/// # Returns
|
|
197
|
+
///
|
|
198
|
+
/// * `f64` - Period, seconds
|
|
199
|
+
pub fn period(&self) -> f64 {
|
|
200
|
+
2.0 * std::f64::consts::PI / self.mean_motion()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
/// Convert Cartesian coordinates to Keplerian orbital elements
|
|
205
|
+
///
|
|
206
|
+
/// # Arguments
|
|
207
|
+
///
|
|
208
|
+
/// * `r` - Position vector, meters
|
|
209
|
+
/// * `v` - Velocity vector, meters/second
|
|
210
|
+
///
|
|
211
|
+
/// # Returns
|
|
212
|
+
///
|
|
213
|
+
/// * `Kepler` - A new Keplerian orbital element object
|
|
214
|
+
///
|
|
215
|
+
pub fn from_pv(r: Vec3, v: Vec3) -> SKResult<Kepler> {
|
|
216
|
+
let h = r.cross(&v);
|
|
217
|
+
let n = Vec3::z_axis().cross(&h);
|
|
218
|
+
let e =
|
|
219
|
+
((v.norm_squared() -
|
|
220
|
+
crate::consts::MU_EARTH / r.norm()) * r - r.dot(&v) * v) / crate::consts::MU_EARTH;
|
|
221
|
+
let eccen = e.norm();
|
|
222
|
+
if eccen >= 1.0 {
|
|
223
|
+
return Err(Box::new(KeplerError::EccenOutOfBound { x: eccen }));
|
|
224
|
+
}
|
|
225
|
+
let xi = v.norm().powi(2) / 2.0 - crate::consts::MU_EARTH / r.norm();
|
|
226
|
+
let a = -crate::consts::MU_EARTH / (2.0 * xi);
|
|
227
|
+
let incl = (h.z/h.norm()).acos();
|
|
228
|
+
let mut raan = (n.x/n.norm()).acos();
|
|
229
|
+
if n.y < 0.0 {
|
|
230
|
+
raan = 2.0 * std::f64::consts::PI - raan;
|
|
231
|
+
}
|
|
232
|
+
let mut w = (n.dot(&e)/n.norm()/e.norm()).acos();
|
|
233
|
+
if e.z < 0.0 {
|
|
234
|
+
w = 2.0 * std::f64::consts::PI - w;
|
|
235
|
+
}
|
|
236
|
+
let mut nu = (r.dot(&e)/r.norm()/e.norm()).acos();
|
|
237
|
+
if r.dot(&v) < 0.0 {
|
|
238
|
+
nu = 2.0 * std::f64::consts::PI - nu;
|
|
239
|
+
}
|
|
240
|
+
Ok(Kepler::new(a, eccen, incl, raan, w, Anomaly::True(nu)))
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/// Convert Keplerian orbital elements to Cartesian coordinates
|
|
244
|
+
///
|
|
245
|
+
/// # Returns
|
|
246
|
+
///
|
|
247
|
+
/// * `(Vec3, Vec3)` - Position and velocity vectors, meters and meters/second
|
|
248
|
+
///
|
|
249
|
+
pub fn to_pv(&self) -> (Vec3, Vec3) {
|
|
250
|
+
let p = self.a * (1.0 - self.eccen.powi(2));
|
|
251
|
+
let r = p / (1.0 + self.eccen * self.nu.cos());
|
|
252
|
+
let r_pqw = Vec3::new(r * self.nu.cos(), r * self.nu.sin(), 0.0);
|
|
253
|
+
let v_pqw = Vec3::new(
|
|
254
|
+
-self.nu.sin(),
|
|
255
|
+
self.eccen + self.nu.cos(),
|
|
256
|
+
0.0) * (crate::consts::MU_EARTH/p).sqrt();
|
|
257
|
+
let q = Quat::from_axis_angle(&Vec3::z_axis(), self.raan) *
|
|
258
|
+
Quat::from_axis_angle(&Vec3::x_axis(), self.incl) *
|
|
259
|
+
Quat::from_axis_angle(&Vec3::z_axis(), self.w);
|
|
260
|
+
(q * r_pqw, q * v_pqw)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
impl std::fmt::Display for Kepler {
|
|
265
|
+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
266
|
+
write!(f,
|
|
267
|
+
"Keplerian Elements:\n a = {:.0} m\n e = {:.3}\n i = {:.3} rad\n",
|
|
268
|
+
self.a, self.eccen, self.incl)?;
|
|
269
|
+
write!(f, " Ω = {:.3} rad\n ω = {:.3} rad\n ν = {:.3} rad\n", self.raan, self.w, self.nu)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[cfg(test)]
|
|
274
|
+
mod tests {
|
|
275
|
+
use super::*;
|
|
276
|
+
|
|
277
|
+
#[test]
|
|
278
|
+
fn test_topv() {
|
|
279
|
+
// Example 2-6 from Vallado
|
|
280
|
+
let p = 11067790.0;
|
|
281
|
+
let eccen = 0.83285_f64;
|
|
282
|
+
let incl = 87.87_f64.to_radians();
|
|
283
|
+
let raan = 227.89_f64.to_radians();
|
|
284
|
+
let w = 53.38_f64.to_radians();
|
|
285
|
+
let nu = 92.335_f64.to_radians();
|
|
286
|
+
|
|
287
|
+
let a = p / (1.0 - eccen.powi(2));
|
|
288
|
+
|
|
289
|
+
let k = Kepler::new(a, eccen, incl, raan, w, Anomaly::True(nu));
|
|
290
|
+
let (r, v) = k.to_pv();
|
|
291
|
+
// Note: values below are not incorrect in the book, but are
|
|
292
|
+
// corrected in the online errata
|
|
293
|
+
// See: https://celestrak.org/software/vallado/ErrataVer4.pdf
|
|
294
|
+
assert!((r*1.0e-3 - Vec3::new(6525.368, 6861.532, 6449.119)).norm() < 1e-3);
|
|
295
|
+
assert!((v*1.0e-3 - Vec3::new(4.902279, 5.533140, -1.975710)).norm() < 1e-3);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
#[test]
|
|
299
|
+
fn test_frompv() {
|
|
300
|
+
// Vallado example 2-5
|
|
301
|
+
let r = Vec3::new(6524.834, 6862.875, 6448.296)*1.0e3;
|
|
302
|
+
let v = Vec3::new(4.901327, 5.533756, -1.976341)*1.0e3;
|
|
303
|
+
let k = Kepler::from_pv(r, v).unwrap();
|
|
304
|
+
assert!((k.a - 36127343_f64).abs() < 1.0e3);
|
|
305
|
+
assert!((k.eccen - 0.83285).abs() < 1e-3);
|
|
306
|
+
assert!((k.incl - 87.87_f64.to_radians()).abs() < 1e-3);
|
|
307
|
+
assert!((k.raan - 227.89_f64.to_radians()).abs() < 1e-3);
|
|
308
|
+
assert!((k.w - 53.38_f64.to_radians()).abs() < 1e-3);
|
|
309
|
+
assert!((k.nu - 92.335_f64.to_radians()).abs() < 1e-3);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
@@ -64,6 +64,9 @@ pub mod utils;
|
|
|
64
64
|
/// Keplerian orbital elements
|
|
65
65
|
pub mod kepler;
|
|
66
66
|
|
|
67
|
+
/// Coordinate frames
|
|
68
|
+
mod frames;
|
|
69
|
+
|
|
67
70
|
// Integrate ordinary differential equations
|
|
68
71
|
mod ode;
|
|
69
72
|
|
|
@@ -78,6 +81,7 @@ pub use solarsystem::SolarSystem;
|
|
|
78
81
|
pub use tle::TLE;
|
|
79
82
|
pub use utils::SKErr;
|
|
80
83
|
pub use utils::SKResult;
|
|
84
|
+
pub use frames::Frame;
|
|
81
85
|
|
|
82
86
|
#[cfg(feature = "pybindings")]
|
|
83
87
|
pub mod pybindings;
|