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.
Files changed (122) hide show
  1. {satkit-0.2.8 → satkit-0.2.9}/Cargo.toml +1 -1
  2. {satkit-0.2.8/python/satkit.egg-info → satkit-0.2.9}/PKG-INFO +1 -1
  3. {satkit-0.2.8 → satkit-0.2.9}/pyproject.toml +1 -1
  4. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/__init__.pyi +2 -0
  5. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/jplephem.pyi +1 -1
  6. satkit-0.2.9/python/satkit/planets.pyi +28 -0
  7. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/satkit.pyi +51 -0
  8. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/satprop.pyi +6 -3
  9. {satkit-0.2.8 → satkit-0.2.9/python/satkit.egg-info}/PKG-INFO +1 -1
  10. {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/SOURCES.txt +4 -0
  11. satkit-0.2.9/src/frames.rs +52 -0
  12. satkit-0.2.9/src/kepler.rs +312 -0
  13. {satkit-0.2.8 → satkit-0.2.9}/src/lib.rs +4 -0
  14. {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/mod.rs +2 -2
  15. {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/planets.rs +63 -114
  16. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/satstate.rs +12 -12
  17. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/mod.rs +16 -0
  18. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyastrotime.rs +28 -3
  19. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyduration.rs +21 -1
  20. satkit-0.2.9/src/pybindings/pyframes.rs +53 -0
  21. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyitrfcoord.rs +34 -1
  22. satkit-0.2.9/src/pybindings/pykepler.rs +231 -0
  23. satkit-0.2.9/src/pybindings/pylpephem_planets.rs +21 -0
  24. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyquaternion.rs +30 -2
  25. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysatproperties.rs +27 -1
  26. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysatstate.rs +39 -4
  27. satkit-0.2.9/src/pybindings/pytle.rs +242 -0
  28. {satkit-0.2.8 → satkit-0.2.9}/src/solarsystem.rs +19 -0
  29. satkit-0.2.8/src/kepler.rs +0 -159
  30. satkit-0.2.8/src/pybindings/pykepler.rs +0 -98
  31. satkit-0.2.8/src/pybindings/pytle.rs +0 -150
  32. {satkit-0.2.8 → satkit-0.2.9}/LICENSE +0 -0
  33. {satkit-0.2.8 → satkit-0.2.9}/MANIFEST.in +0 -0
  34. {satkit-0.2.8 → satkit-0.2.9}/README.md +0 -0
  35. {satkit-0.2.8 → satkit-0.2.9}/python/docs/source/conf.py +0 -0
  36. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/__init__.py +0 -0
  37. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/density.pyi +0 -0
  38. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/frametransform.pyi +0 -0
  39. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/moon.pyi +0 -0
  40. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/py.typed +0 -0
  41. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/sun.pyi +0 -0
  42. {satkit-0.2.8 → satkit-0.2.9}/python/satkit/utils.pyi +0 -0
  43. {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/dependency_links.txt +0 -0
  44. {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/requires.txt +0 -0
  45. {satkit-0.2.8 → satkit-0.2.9}/python/satkit.egg-info/top_level.txt +0 -0
  46. {satkit-0.2.8 → satkit-0.2.9}/python/test/download_data.py +0 -0
  47. {satkit-0.2.8 → satkit-0.2.9}/python/test/download_from_json.py +0 -0
  48. {satkit-0.2.8 → satkit-0.2.9}/python/test/download_testvecs.py +0 -0
  49. {satkit-0.2.8 → satkit-0.2.9}/python/test/orbitprop_gps_fit.py +0 -0
  50. {satkit-0.2.8 → satkit-0.2.9}/python/test/sp3file.py +0 -0
  51. {satkit-0.2.8 → satkit-0.2.9}/python/test/test.py +0 -0
  52. {satkit-0.2.8 → satkit-0.2.9}/setup.cfg +0 -0
  53. {satkit-0.2.8 → satkit-0.2.9}/src/astrotime.rs +0 -0
  54. {satkit-0.2.8 → satkit-0.2.9}/src/consts.rs +0 -0
  55. {satkit-0.2.8 → satkit-0.2.9}/src/duration.rs +0 -0
  56. {satkit-0.2.8 → satkit-0.2.9}/src/earth_orientation_params.rs +0 -0
  57. {satkit-0.2.8 → satkit-0.2.9}/src/earthgravity.rs +0 -0
  58. {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/ierstable.rs +0 -0
  59. {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/mod.rs +0 -0
  60. {satkit-0.2.8 → satkit-0.2.9}/src/frametransform/qcirs2gcrs.rs +0 -0
  61. {satkit-0.2.8 → satkit-0.2.9}/src/itrfcoord.rs +0 -0
  62. {satkit-0.2.8 → satkit-0.2.9}/src/jplephem.rs +0 -0
  63. {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/moon.rs +0 -0
  64. {satkit-0.2.8 → satkit-0.2.9}/src/lpephem/sun.rs +0 -0
  65. {satkit-0.2.8 → satkit-0.2.9}/src/nrlmsise.rs +0 -0
  66. {satkit-0.2.8 → satkit-0.2.9}/src/ode/harmonic_oscillator.rs +0 -0
  67. {satkit-0.2.8 → satkit-0.2.9}/src/ode/mod.rs +0 -0
  68. {satkit-0.2.8 → satkit-0.2.9}/src/ode/nalgebra.rs +0 -0
  69. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_adaptive.rs +0 -0
  70. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_adaptive_settings.rs +0 -0
  71. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rk_explicit.rs +0 -0
  72. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkf45.rs +0 -0
  73. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkts54.rs +0 -0
  74. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv65.rs +0 -0
  75. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv65_table.rs +0 -0
  76. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv87.rs +0 -0
  77. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv87_table.rs +0 -0
  78. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98.rs +0 -0
  79. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_nointerp.rs +0 -0
  80. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_nointerp_table.rs +0 -0
  81. {satkit-0.2.8 → satkit-0.2.9}/src/ode/rkv98_table.rs +0 -0
  82. {satkit-0.2.8 → satkit-0.2.9}/src/ode/types.rs +0 -0
  83. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/drag.rs +0 -0
  84. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/mod.rs +0 -0
  85. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/point_gravity.rs +0 -0
  86. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/propagator.rs +0 -0
  87. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/satproperties.rs +0 -0
  88. {satkit-0.2.8 → satkit-0.2.9}/src/orbitprop/settings.rs +0 -0
  89. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/mod_utils.rs +0 -0
  90. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyconsts.rs +0 -0
  91. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pydensity.rs +0 -0
  92. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyframetransform.rs +0 -0
  93. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pygravity.rs +0 -0
  94. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyjplephem.rs +0 -0
  95. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pylpephem_moon.rs +0 -0
  96. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pylpephem_sun.rs +0 -0
  97. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pynrlmsise.rs +0 -0
  98. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pypropagate.rs +0 -0
  99. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pypropsettings.rs +0 -0
  100. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysgp4.rs +0 -0
  101. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pysolarsystem.rs +0 -0
  102. {satkit-0.2.8 → satkit-0.2.9}/src/pybindings/pyutils.rs +0 -0
  103. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dpper.rs +0 -0
  104. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dscom.rs +0 -0
  105. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dsinit.rs +0 -0
  106. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/dspace.rs +0 -0
  107. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/getgravconst.rs +0 -0
  108. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/initl.rs +0 -0
  109. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/mod.rs +0 -0
  110. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/satrec.rs +0 -0
  111. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4.rs +0 -0
  112. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4_lowlevel.rs +0 -0
  113. {satkit-0.2.8 → satkit-0.2.9}/src/sgp4/sgp4init.rs +0 -0
  114. {satkit-0.2.8 → satkit-0.2.9}/src/spaceweather.rs +0 -0
  115. {satkit-0.2.8 → satkit-0.2.9}/src/tle.rs +0 -0
  116. {satkit-0.2.8 → satkit-0.2.9}/src/types.rs +0 -0
  117. {satkit-0.2.8 → satkit-0.2.9}/src/utils/datadir.rs +0 -0
  118. {satkit-0.2.8 → satkit-0.2.9}/src/utils/download.rs +0 -0
  119. {satkit-0.2.8 → satkit-0.2.9}/src/utils/mod.rs +0 -0
  120. {satkit-0.2.8 → satkit-0.2.9}/src/utils/skerror.rs +0 -0
  121. {satkit-0.2.8 → satkit-0.2.9}/src/utils/test.rs +0 -0
  122. {satkit-0.2.8 → satkit-0.2.9}/src/utils/update_data.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "satkit"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
  description = "Satellite Toolkit"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satkit
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Satellite Orbital Dynamics Toolkit
5
5
  Author-email: Steven Michael <ssmichael@gmail.com>
6
6
  Maintainer-email: Steven Michael <ssmichael@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "satkit"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  dependencies = ["numpy>=1.20.0"]
9
9
  requires-python = ">= 3.8"
10
10
  authors = [{ name = "Steven Michael", email = "ssmichael@gmail.com" }]
@@ -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"
@@ -1,7 +1,7 @@
1
1
  """
2
2
  High-precision JPL ephemerides for solar-system bodies
3
3
 
4
- For details, see:
4
+ For details, see: https://ssd.jpl.nasa.gov/
5
5
  """
6
6
 
7
7
  from __future__ import annotations
@@ -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 pos(self) -> npt.ArrayLike[np.float64]:
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 vel(self) -> npt.ArrayLike[np.float64]:
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satkit
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Satellite Orbital Dynamics Toolkit
5
5
  Author-email: Steven Michael <ssmichael@gmail.com>
6
6
  Maintainer-email: Steven Michael <ssmichael@gmail.com>
@@ -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;
@@ -4,6 +4,6 @@ pub mod moon;
4
4
  pub mod sun;
5
5
 
6
6
  // This part isn't working yet...
7
- // mod planets;
8
- // pub use planets::barycentric_pos;
7
+ mod planets;
8
+ pub use planets::heliocentric_pos;
9
9