satkit 0.2.7__tar.gz → 0.2.8__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 (118) hide show
  1. {satkit-0.2.7 → satkit-0.2.8}/Cargo.toml +1 -1
  2. {satkit-0.2.7/python/satkit.egg-info → satkit-0.2.8}/PKG-INFO +3 -2
  3. {satkit-0.2.7 → satkit-0.2.8}/README.md +2 -1
  4. {satkit-0.2.7 → satkit-0.2.8}/pyproject.toml +1 -1
  5. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/__init__.pyi +1 -0
  6. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/satkit.pyi +47 -0
  7. {satkit-0.2.7 → satkit-0.2.8/python/satkit.egg-info}/PKG-INFO +3 -2
  8. {satkit-0.2.7 → satkit-0.2.8}/python/satkit.egg-info/SOURCES.txt +3 -1
  9. {satkit-0.2.7 → satkit-0.2.8}/python/test/orbitprop_gps_fit.py +46 -11
  10. {satkit-0.2.7 → satkit-0.2.8}/python/test/test.py +38 -0
  11. {satkit-0.2.7 → satkit-0.2.8}/src/astrotime.rs +4 -4
  12. {satkit-0.2.7 → satkit-0.2.8}/src/frametransform/mod.rs +1 -1
  13. {satkit-0.2.7 → satkit-0.2.8}/src/jplephem.rs +18 -18
  14. satkit-0.2.8/src/kepler.rs +159 -0
  15. {satkit-0.2.7 → satkit-0.2.8}/src/lib.rs +2 -0
  16. satkit-0.2.8/src/lpephem/mod.rs +9 -0
  17. satkit-0.2.8/src/lpephem/planets.rs +335 -0
  18. {satkit-0.2.7 → satkit-0.2.8}/src/nrlmsise.rs +5 -1
  19. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rk_adaptive.rs +1 -1
  20. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/propagator.rs +4 -5
  21. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/mod.rs +4 -0
  22. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyastrotime.rs +4 -4
  23. satkit-0.2.8/src/pybindings/pykepler.rs +98 -0
  24. satkit-0.2.8/src/pybindings/pysolarsystem.rs +66 -0
  25. {satkit-0.2.7 → satkit-0.2.8}/src/solarsystem.rs +10 -10
  26. {satkit-0.2.7 → satkit-0.2.8}/src/spaceweather.rs +2 -2
  27. {satkit-0.2.7 → satkit-0.2.8}/src/tle.rs +1 -1
  28. satkit-0.2.7/python/satkit/py.typed +0 -0
  29. satkit-0.2.7/src/lpephem/mod.rs +0 -4
  30. satkit-0.2.7/src/pybindings/pysolarsystem.rs +0 -66
  31. {satkit-0.2.7 → satkit-0.2.8}/LICENSE +0 -0
  32. {satkit-0.2.7 → satkit-0.2.8}/MANIFEST.in +0 -0
  33. {satkit-0.2.7 → satkit-0.2.8}/python/docs/source/conf.py +0 -0
  34. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/__init__.py +0 -0
  35. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/density.pyi +0 -0
  36. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/frametransform.pyi +0 -0
  37. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/jplephem.pyi +0 -0
  38. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/moon.pyi +0 -0
  39. {satkit-0.2.7/python/satkit/astro-data → satkit-0.2.8/python/satkit}/py.typed +0 -0
  40. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/satprop.pyi +0 -0
  41. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/sun.pyi +0 -0
  42. {satkit-0.2.7 → satkit-0.2.8}/python/satkit/utils.pyi +0 -0
  43. {satkit-0.2.7 → satkit-0.2.8}/python/satkit.egg-info/dependency_links.txt +0 -0
  44. {satkit-0.2.7 → satkit-0.2.8}/python/satkit.egg-info/requires.txt +0 -0
  45. {satkit-0.2.7 → satkit-0.2.8}/python/satkit.egg-info/top_level.txt +0 -0
  46. {satkit-0.2.7 → satkit-0.2.8}/python/test/download_data.py +0 -0
  47. {satkit-0.2.7 → satkit-0.2.8}/python/test/download_from_json.py +0 -0
  48. {satkit-0.2.7 → satkit-0.2.8}/python/test/download_testvecs.py +0 -0
  49. {satkit-0.2.7 → satkit-0.2.8}/python/test/sp3file.py +0 -0
  50. {satkit-0.2.7 → satkit-0.2.8}/setup.cfg +0 -0
  51. {satkit-0.2.7 → satkit-0.2.8}/src/consts.rs +0 -0
  52. {satkit-0.2.7 → satkit-0.2.8}/src/duration.rs +0 -0
  53. {satkit-0.2.7 → satkit-0.2.8}/src/earth_orientation_params.rs +0 -0
  54. {satkit-0.2.7 → satkit-0.2.8}/src/earthgravity.rs +0 -0
  55. {satkit-0.2.7 → satkit-0.2.8}/src/frametransform/ierstable.rs +0 -0
  56. {satkit-0.2.7 → satkit-0.2.8}/src/frametransform/qcirs2gcrs.rs +0 -0
  57. {satkit-0.2.7 → satkit-0.2.8}/src/itrfcoord.rs +0 -0
  58. {satkit-0.2.7 → satkit-0.2.8}/src/lpephem/moon.rs +0 -0
  59. {satkit-0.2.7 → satkit-0.2.8}/src/lpephem/sun.rs +0 -0
  60. {satkit-0.2.7 → satkit-0.2.8}/src/ode/harmonic_oscillator.rs +0 -0
  61. {satkit-0.2.7 → satkit-0.2.8}/src/ode/mod.rs +0 -0
  62. {satkit-0.2.7 → satkit-0.2.8}/src/ode/nalgebra.rs +0 -0
  63. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rk_adaptive_settings.rs +0 -0
  64. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rk_explicit.rs +0 -0
  65. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkf45.rs +0 -0
  66. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkts54.rs +0 -0
  67. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv65.rs +0 -0
  68. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv65_table.rs +0 -0
  69. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv87.rs +0 -0
  70. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv87_table.rs +0 -0
  71. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv98.rs +0 -0
  72. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv98_nointerp.rs +0 -0
  73. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv98_nointerp_table.rs +0 -0
  74. {satkit-0.2.7 → satkit-0.2.8}/src/ode/rkv98_table.rs +0 -0
  75. {satkit-0.2.7 → satkit-0.2.8}/src/ode/types.rs +0 -0
  76. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/drag.rs +0 -0
  77. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/mod.rs +0 -0
  78. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/point_gravity.rs +0 -0
  79. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/satproperties.rs +0 -0
  80. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/satstate.rs +0 -0
  81. {satkit-0.2.7 → satkit-0.2.8}/src/orbitprop/settings.rs +0 -0
  82. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/mod_utils.rs +0 -0
  83. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyconsts.rs +0 -0
  84. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pydensity.rs +0 -0
  85. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyduration.rs +0 -0
  86. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyframetransform.rs +0 -0
  87. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pygravity.rs +0 -0
  88. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyitrfcoord.rs +0 -0
  89. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyjplephem.rs +0 -0
  90. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pylpephem_moon.rs +0 -0
  91. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pylpephem_sun.rs +0 -0
  92. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pynrlmsise.rs +0 -0
  93. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pypropagate.rs +0 -0
  94. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pypropsettings.rs +0 -0
  95. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyquaternion.rs +0 -0
  96. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pysatproperties.rs +0 -0
  97. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pysatstate.rs +0 -0
  98. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pysgp4.rs +0 -0
  99. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pytle.rs +0 -0
  100. {satkit-0.2.7 → satkit-0.2.8}/src/pybindings/pyutils.rs +0 -0
  101. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/dpper.rs +0 -0
  102. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/dscom.rs +0 -0
  103. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/dsinit.rs +0 -0
  104. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/dspace.rs +0 -0
  105. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/getgravconst.rs +0 -0
  106. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/initl.rs +0 -0
  107. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/mod.rs +0 -0
  108. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/satrec.rs +0 -0
  109. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/sgp4.rs +0 -0
  110. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/sgp4_lowlevel.rs +0 -0
  111. {satkit-0.2.7 → satkit-0.2.8}/src/sgp4/sgp4init.rs +0 -0
  112. {satkit-0.2.7 → satkit-0.2.8}/src/types.rs +0 -0
  113. {satkit-0.2.7 → satkit-0.2.8}/src/utils/datadir.rs +0 -0
  114. {satkit-0.2.7 → satkit-0.2.8}/src/utils/download.rs +0 -0
  115. {satkit-0.2.7 → satkit-0.2.8}/src/utils/mod.rs +0 -0
  116. {satkit-0.2.7 → satkit-0.2.8}/src/utils/skerror.rs +0 -0
  117. {satkit-0.2.7 → satkit-0.2.8}/src/utils/test.rs +0 -0
  118. {satkit-0.2.7 → satkit-0.2.8}/src/utils/update_data.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "satkit"
3
- version = "0.2.7"
3
+ version = "0.2.8"
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.7
3
+ Version: 0.2.8
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>
@@ -21,6 +21,7 @@ Requires-Dist: numpy>=1.20.0
21
21
 
22
22
  An accurate, high-performance satellite orbital kinematics toolkit, written in Rust with a sensible interface.
23
23
  <br/>
24
+ Also includes python bindings for *all* functions via via [pyo3](https://pyo3.rs/)
24
25
 
25
26
  ### Github
26
27
 
@@ -48,7 +49,7 @@ An accurate, high-performance satellite orbital kinematics toolkit, written in R
48
49
  - **Native Rust** bindings
49
50
  - **Python bindings** for compiled rust code ... speed of Rust with convenience of Python<br/>
50
51
  Install with `pip install satkit`<br/>
51
- PyPi includes binaries for windows, macos (Intel & arm), and linux
52
+ PyPi includes binary packages for windows, macos (Intel & ARM), and linux. Python documentation is at: <https://satellite-toolkit.readthedocs.io/latest/>
52
53
 
53
54
  ## Features
54
55
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  An accurate, high-performance satellite orbital kinematics toolkit, written in Rust with a sensible interface.
4
4
  <br/>
5
+ Also includes python bindings for *all* functions via via [pyo3](https://pyo3.rs/)
5
6
 
6
7
  ### Github
7
8
 
@@ -29,7 +30,7 @@ An accurate, high-performance satellite orbital kinematics toolkit, written in R
29
30
  - **Native Rust** bindings
30
31
  - **Python bindings** for compiled rust code ... speed of Rust with convenience of Python<br/>
31
32
  Install with `pip install satkit`<br/>
32
- PyPi includes binaries for windows, macos (Intel & arm), and linux
33
+ PyPi includes binary packages for windows, macos (Intel & ARM), and linux. Python documentation is at: <https://satellite-toolkit.readthedocs.io/latest/>
33
34
 
34
35
  ## Features
35
36
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "satkit"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  dependencies = ["numpy>=1.20.0"]
9
9
  requires-python = ">= 3.8"
10
10
  authors = [{ name = "Steven Michael", email = "ssmichael@gmail.com" }]
@@ -21,6 +21,7 @@ __all__ = [
21
21
  "solarsystem",
22
22
  "TLE",
23
23
  "itrfcoord",
24
+ "kepler",
24
25
  "consts",
25
26
  "frametransform",
26
27
  "jplephem",
@@ -1050,6 +1050,53 @@ class quaternion:
1050
1050
  quaternion: Quaternion representing interpolation between self and other
1051
1051
  """
1052
1052
 
1053
+ class kepler:
1054
+ """ Represent Keplerian element sets and convert between cartesian
1055
+
1056
+
1057
+ Notes:
1058
+ * This class is used to represent Keplerian elements and convert between Cartesian coordinates
1059
+ * The class uses the semi-major axis (a), not the semiparameter
1060
+ * All angle units are radians
1061
+ * All length units are meters
1062
+ * All velocity units are meters / second
1063
+ """
1064
+
1065
+ def __init__(self, *args):
1066
+ """Create Keplerian element set object from input elements
1067
+
1068
+ # Args:
1069
+ a (float) : Semi-major axis, meters
1070
+ e (float) : Eccentricity, unitless
1071
+ i (float) : Inclination, radians
1072
+ raan (float) : Right ascension of ascending node, radians
1073
+ argp (float) : Argument of perigee, radians
1074
+ nu (float) : True anomaly, radians
1075
+
1076
+ # Returns:
1077
+ satkit.kepler: Keplerian element set object
1078
+ """
1079
+
1080
+ def to_pv(self) -> typing.Tuple[npt.ArrayLike[np.float64], npt.ArrayLike[np.float64]]:
1081
+ """Convert Keplerian element set to position and velocity vectors
1082
+
1083
+ Returns:
1084
+ tuple[npt.ArrayLike[np.float64], npt.ArrayLike[np.float64]]: Tuple with two elements representing the position and velocity vectors
1085
+ """
1086
+
1087
+ def from_pv(
1088
+ pos: npt.ArrayLike[np.float64], vel: npt.ArrayLike[np.float64]
1089
+ ) -> kepler:
1090
+ """Create Keplerian element set from input position and velocity vectors
1091
+
1092
+ Args:
1093
+ pos (npt.ArrayLike[np.float64]): 3-element array representing position vector
1094
+ vel (npt.ArrayLike[np.float64]): 3-element array representing velocity vector
1095
+
1096
+ Returns:
1097
+ satkit.kepler: Keplerian element set object
1098
+ """
1099
+
1053
1100
  class itrfcoord:
1054
1101
  """ Representation of a coordinate in the International Terrestrial Reference Frame (ITRF)
1055
1102
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satkit
3
- Version: 0.2.7
3
+ Version: 0.2.8
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>
@@ -21,6 +21,7 @@ Requires-Dist: numpy>=1.20.0
21
21
 
22
22
  An accurate, high-performance satellite orbital kinematics toolkit, written in Rust with a sensible interface.
23
23
  <br/>
24
+ Also includes python bindings for *all* functions via via [pyo3](https://pyo3.rs/)
24
25
 
25
26
  ### Github
26
27
 
@@ -48,7 +49,7 @@ An accurate, high-performance satellite orbital kinematics toolkit, written in R
48
49
  - **Native Rust** bindings
49
50
  - **Python bindings** for compiled rust code ... speed of Rust with convenience of Python<br/>
50
51
  Install with `pip install satkit`<br/>
51
- PyPi includes binaries for windows, macos (Intel & arm), and linux
52
+ PyPi includes binary packages for windows, macos (Intel & ARM), and linux. Python documentation is at: <https://satellite-toolkit.readthedocs.io/latest/>
52
53
 
53
54
  ## Features
54
55
 
@@ -20,7 +20,6 @@ python/satkit.egg-info/SOURCES.txt
20
20
  python/satkit.egg-info/dependency_links.txt
21
21
  python/satkit.egg-info/requires.txt
22
22
  python/satkit.egg-info/top_level.txt
23
- python/satkit/astro-data/py.typed
24
23
  python/test/download_data.py
25
24
  python/test/download_from_json.py
26
25
  python/test/download_testvecs.py
@@ -34,6 +33,7 @@ src/earth_orientation_params.rs
34
33
  src/earthgravity.rs
35
34
  src/itrfcoord.rs
36
35
  src/jplephem.rs
36
+ src/kepler.rs
37
37
  src/lib.rs
38
38
  src/nrlmsise.rs
39
39
  src/solarsystem.rs
@@ -45,6 +45,7 @@ src/frametransform/mod.rs
45
45
  src/frametransform/qcirs2gcrs.rs
46
46
  src/lpephem/mod.rs
47
47
  src/lpephem/moon.rs
48
+ src/lpephem/planets.rs
48
49
  src/lpephem/sun.rs
49
50
  src/ode/harmonic_oscillator.rs
50
51
  src/ode/mod.rs
@@ -80,6 +81,7 @@ src/pybindings/pyframetransform.rs
80
81
  src/pybindings/pygravity.rs
81
82
  src/pybindings/pyitrfcoord.rs
82
83
  src/pybindings/pyjplephem.rs
84
+ src/pybindings/pykepler.rs
83
85
  src/pybindings/pylpephem_moon.rs
84
86
  src/pybindings/pylpephem_sun.rs
85
87
  src/pybindings/pynrlmsise.rs
@@ -1,3 +1,4 @@
1
+ # %%
1
2
  """
2
3
  This function reads an SP3 file containing precise GPS locaions in the ITRF frame
3
4
 
@@ -24,16 +25,30 @@ import os
24
25
  basedir = os.getenv(
25
26
  "SATKIT_TESTVEC_ROOT", default="../.." + os.path.sep + "satkit-testvecs"
26
27
  )
27
- fname = (
28
- basedir
29
- + os.path.sep
30
- + "orbitprop"
31
- + os.path.sep
32
- + "ESA0OPSFIN_20233640000_01D_05M_ORB.SP3"
33
- )
34
28
 
35
- # Get positions and times from SP3 file
36
- [pitrf, timearr] = read_sp3file(fname)
29
+ sp3names = ['mid22630.sp3', 'mid22631.sp3', 'mid22632.sp3',
30
+ 'mid22633.sp3', 'mid22634.sp3', 'mid22635.sp3',
31
+ 'mid22636.sp3']
32
+
33
+ sp3names = ['mid22630.sp3', 'mid22631.sp3', 'mid22632.sp3']
34
+ pitrf = np.zeros((0,3), np.float64)
35
+ timearr = np.array([])
36
+
37
+ for sp3name in sp3names:
38
+ fname = (
39
+ basedir
40
+ + os.path.sep
41
+ + "orbitprop"
42
+ + os.path.sep
43
+ + sp3name
44
+ )
45
+ [pitrf1, timearray1] = read_sp3file(fname)
46
+ pitrf = np.append(pitrf, pitrf1, axis=0)
47
+ timearr = np.append(timearr, timearray1)
48
+
49
+ print(pitrf.shape)
50
+ print(timearr.shape)
51
+ print(timearr[0], timearr[-1])
37
52
 
38
53
  # Rotate positions to the GCRF frame
39
54
  pgcrf = np.stack(
@@ -42,13 +57,12 @@ pgcrf = np.stack(
42
57
  ),
43
58
  axis=0,
44
59
  )
45
-
46
60
  # Crude estimation of initial velocity
47
61
  vgcrf = (pgcrf[1, :] - pgcrf[0, :]) / (timearr[1] - timearr[0]).seconds()
48
62
 
49
63
  # Initial state for non-linear least squares is initial velocity
50
64
  # and susceptibility to radiation pressuer : Cr A / m
51
- v0 = np.array([vgcrf[0], vgcrf[1], vgcrf[2], 1 * 10**2 / 5000])
65
+ v0 = np.array([vgcrf[0], vgcrf[1], vgcrf[2], 0.01])
52
66
 
53
67
 
54
68
  def minfunc(v):
@@ -68,6 +82,7 @@ def minfunc(v):
68
82
  propsettings=settings,
69
83
  satproperties=satprops,
70
84
  )
85
+
71
86
  return np.sum(np.sum((res["pos"] - pgcrf) ** 2, axis=1), axis=0)
72
87
 
73
88
 
@@ -75,3 +90,23 @@ def minfunc(v):
75
90
  # by tuning initial velocity and CrAoverM
76
91
  r = minimize(minfunc, v0, method="Nelder-Mead")
77
92
  print(r)
93
+
94
+ # %%
95
+ import plotly.graph_objects as go
96
+
97
+ settings = sk.satprop.propsettings()
98
+ settings.use_jplephem = False
99
+ satprops = sk.satprop.satproperties_static()
100
+ satprops.craoverm = r.x[3]
101
+ res = sk.satprop.propagate(
102
+ pgcrf[0,:], r.x[0:3], timearr[0],
103
+ stoptime=timearr[-1], dt=sk.duration(minutes=5),
104
+ propsettings=settings, satproperties=satprops
105
+ )
106
+ perr = res['pos'] - pgcrf
107
+ fig = go.Figure()
108
+ fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,0], mode='lines', name='X'))
109
+ fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,1], mode='lines', name='Y'))
110
+ fig.add_trace(go.Scatter(x=[t.datetime() for t in timearr], y=perr[:,2], mode='lines', name='Z'))
111
+
112
+ # %%
@@ -121,6 +121,44 @@ class TestTime:
121
121
  assert minute == 0
122
122
  assert sec == 0
123
123
 
124
+ class TestKepler:
125
+ def test_kepler_from_pv(self):
126
+ """
127
+ Test creation of Kepler elements from position and velocity
128
+ """
129
+
130
+ # Test case from Vallado, example 2-6
131
+ r = np.array([6524.834, 6862.875, 6448.296])*1.0e3
132
+ v = np.array([4.901327, 5.533756, -1.976341])*1.0e3
133
+ kep = sk.kepler.from_pv(r, v)
134
+ rad2deg = 180.0/m.pi
135
+ print(kep)
136
+ assert kep.a == pytest.approx(36127343, 1.0e-3)
137
+ assert kep.eccen == pytest.approx(0.83285, 1.0e-5)
138
+ assert kep.inclination*rad2deg == pytest.approx(87.87, 1.0e-3)
139
+ assert kep.raan*rad2deg == pytest.approx(227.89, 1.0e-3)
140
+ assert kep.w*rad2deg == pytest.approx(53.38, 1.0e-3)
141
+ assert kep.nu*rad2deg == pytest.approx(92.335, 1.0e-3)
142
+
143
+ def test_kepler_to_pv(self):
144
+ """
145
+ Test conversion of Kepler elements to position and velocity
146
+ """
147
+ p = 11067790
148
+ eccen = 0.83285
149
+ incl = 87.87*m.pi/180
150
+ raan = 227.89*m.pi/180
151
+ w = 53.38*m.pi/180
152
+ nu = 92.335*m.pi/180
153
+
154
+ a = p/(1-eccen**2)
155
+ kep = sk.kepler(a, eccen, incl, raan, w, nu)
156
+ pos, vel = kep.to_pv()
157
+ assert pos == pytest.approx(np.array([6525.368, 6861.532, 6449.119])*1.0e3, 1.0e-3)
158
+ assert vel == pytest.approx(np.array([4.902279, 5.533140, -1.975710])*1.0e3, 1.0e-3)
159
+
160
+
161
+
124
162
  class TestJPLEphem:
125
163
  def test_jplephem_testvecs(self):
126
164
  """
@@ -423,7 +423,7 @@ impl AstroTime {
423
423
  /// # Returns
424
424
  ///
425
425
  /// * AstroTime Object
426
- pub fn from_date(year: u32, month: u32, day: u32) -> AstroTime {
426
+ pub fn from_date(year: i32, month: u32, day: u32) -> AstroTime {
427
427
  AstroTime::from_mjd(date2mjd_utc(year, month, day) as f64, Scale::UTC)
428
428
  }
429
429
 
@@ -500,7 +500,7 @@ impl AstroTime {
500
500
  /// * AstroTime object
501
501
  #[inline]
502
502
  pub fn from_datetime(
503
- year: u32,
503
+ year: i32,
504
504
  month: u32,
505
505
  day: u32,
506
506
  hour: u32,
@@ -526,7 +526,7 @@ impl AstroTime {
526
526
  ///
527
527
  /// * AstroTime object
528
528
  pub fn from_datetime_with_scale(
529
- year: u32,
529
+ year: i32,
530
530
  month: u32,
531
531
  day: u32,
532
532
  hour: u32,
@@ -649,7 +649,7 @@ fn mjd_utc2date(mjd_utc: f64) -> (u32, u32, u32) {
649
649
  (year as u32, month as u32, day as u32)
650
650
  }
651
651
 
652
- fn date2mjd_utc(year: u32, month: u32, day: u32) -> i32 {
652
+ fn date2mjd_utc(year: i32, month: u32, day: u32) -> i32 {
653
653
  // Chapter 15 "Calendars" section 15.11.3 of the
654
654
  // Explanatory Suppliment to the Astronomical Almanac
655
655
  // Algorithm 3
@@ -210,7 +210,7 @@ pub fn qgcrf2itrf_approx(tm: &AstroTime) -> Quat {
210
210
  // Neglecting polar motion
211
211
  let qitrf2tod_approx: Quat = qrot_zcoord(-gast(tm));
212
212
 
213
- qmod2gcrf(tm) * qtod2mod_approx(tm) * qitrf2tod_approx
213
+ (qmod2gcrf(tm) * qtod2mod_approx(tm) * qitrf2tod_approx).conjugate()
214
214
  }
215
215
 
216
216
  ///
@@ -38,17 +38,17 @@ impl TryFrom<i32> for SolarSystem {
38
38
  type Error = ();
39
39
  fn try_from(v: i32) -> Result<Self, Self::Error> {
40
40
  match v {
41
- x if x == SolarSystem::MERCURY as i32 => Ok(SolarSystem::MERCURY),
42
- x if x == SolarSystem::VENUS as i32 => Ok(SolarSystem::VENUS),
41
+ x if x == SolarSystem::Mercury as i32 => Ok(SolarSystem::Mercury),
42
+ x if x == SolarSystem::Venus as i32 => Ok(SolarSystem::Venus),
43
43
  x if x == SolarSystem::EMB as i32 => Ok(SolarSystem::EMB),
44
- x if x == SolarSystem::MARS as i32 => Ok(SolarSystem::MARS),
45
- x if x == SolarSystem::JUPITER as i32 => Ok(SolarSystem::JUPITER),
46
- x if x == SolarSystem::SATURN as i32 => Ok(SolarSystem::SATURN),
47
- x if x == SolarSystem::URANUS as i32 => Ok(SolarSystem::URANUS),
48
- x if x == SolarSystem::NEPTUNE as i32 => Ok(SolarSystem::NEPTUNE),
49
- x if x == SolarSystem::PLUTO as i32 => Ok(SolarSystem::PLUTO),
50
- x if x == SolarSystem::MOON as i32 => Ok(SolarSystem::MOON),
51
- x if x == SolarSystem::SUN as i32 => Ok(SolarSystem::SUN),
44
+ x if x == SolarSystem::Mars as i32 => Ok(SolarSystem::Mars),
45
+ x if x == SolarSystem::Jupiter as i32 => Ok(SolarSystem::Jupiter),
46
+ x if x == SolarSystem::Saturn as i32 => Ok(SolarSystem::Saturn),
47
+ x if x == SolarSystem::Uranus as i32 => Ok(SolarSystem::Uranus),
48
+ x if x == SolarSystem::Neptune as i32 => Ok(SolarSystem::Neptune),
49
+ x if x == SolarSystem::Pluto as i32 => Ok(SolarSystem::Pluto),
50
+ x if x == SolarSystem::Moon as i32 => Ok(SolarSystem::Moon),
51
+ x if x == SolarSystem::Sun as i32 => Ok(SolarSystem::Sun),
52
52
  _ => Err(()),
53
53
  }
54
54
  }
@@ -130,7 +130,7 @@ impl JPLEphem {
130
130
  /// let t = AstroTime::from_datetime(2021, 3, 1, 12, 0, 0.0);
131
131
  ///
132
132
  /// // Find geocentric moon position at this time in the GCRF frame
133
- /// let p = jplephem::geocentric_pos(SolarSystem::MOON, &t).unwrap();
133
+ /// let p = jplephem::geocentric_pos(SolarSystem::Moon, &t).unwrap();
134
134
  /// println!("p = {}", p);
135
135
  /// ```
136
136
  ///
@@ -453,11 +453,11 @@ impl JPLEphem {
453
453
  /// 3-vector of cartesian Geocentric position in meters
454
454
  ///
455
455
  fn geocentric_pos(&self, body: SolarSystem, tm: &AstroTime) -> SKResult<Vec3> {
456
- if body == SolarSystem::MOON {
456
+ if body == SolarSystem::Moon {
457
457
  return self.barycentric_pos(body, tm);
458
458
  } else {
459
459
  let emb: Vec3 = self.barycentric_pos(SolarSystem::EMB, tm)?;
460
- let moon: Vec3 = self.barycentric_pos(SolarSystem::MOON, tm)?;
460
+ let moon: Vec3 = self.barycentric_pos(SolarSystem::Moon, tm)?;
461
461
  let b: Vec3 = self.barycentric_pos(body, tm)?;
462
462
 
463
463
  // Compute the position of the body relative to the Earth-moon
@@ -483,11 +483,11 @@ impl JPLEphem {
483
483
  /// Note: velocity is relative to Earth
484
484
  ///
485
485
  fn geocentric_state(&self, body: SolarSystem, tm: &AstroTime) -> SKResult<(Vec3, Vec3)> {
486
- if body == SolarSystem::MOON {
486
+ if body == SolarSystem::Moon {
487
487
  return self.barycentric_state(body, tm);
488
488
  } else {
489
489
  let emb: (Vec3, Vec3) = self.barycentric_state(SolarSystem::EMB, tm)?;
490
- let moon: (Vec3, Vec3) = self.barycentric_state(SolarSystem::MOON, tm)?;
490
+ let moon: (Vec3, Vec3) = self.barycentric_state(SolarSystem::Moon, tm)?;
491
491
  let b: (Vec3, Vec3) = self.barycentric_state(body, tm)?;
492
492
 
493
493
  // Compute the position of the body relative to the Earth-moon
@@ -613,7 +613,7 @@ mod tests {
613
613
 
614
614
  let tm = AstroTime::from_jd(2451545.0, Scale::TT);
615
615
  //let tm = &AstroTime::from_jd(2451545.0, Scale::UTC);
616
- let (_, _): (Vec3, Vec3) = jpl.geocentric_state(SolarSystem::MOON, &tm).unwrap();
616
+ let (_, _): (Vec3, Vec3) = jpl.geocentric_state(SolarSystem::Moon, &tm).unwrap();
617
617
  println!("au = {:.20}", jpl._au);
618
618
  }
619
619
 
@@ -672,7 +672,7 @@ mod tests {
672
672
  let (_mpos, mvel): (Vec3, Vec3) = jplephem_singleton()
673
673
  .as_ref()
674
674
  .unwrap()
675
- .geocentric_state(SolarSystem::MOON, &tm)
675
+ .geocentric_state(SolarSystem::Moon, &tm)
676
676
  .unwrap();
677
677
  // Scale Earth velocity
678
678
  tvel = tvel - mvel / (1.0 + jplephem_singleton().as_ref().unwrap().emrat);
@@ -682,7 +682,7 @@ mod tests {
682
682
  let (_mpos, mvel): (Vec3, Vec3) = jplephem_singleton()
683
683
  .as_ref()
684
684
  .unwrap()
685
- .geocentric_state(SolarSystem::MOON, &tm)
685
+ .geocentric_state(SolarSystem::Moon, &tm)
686
686
  .unwrap();
687
687
  //Scale Earth velocity
688
688
  svel = svel - mvel / (1.0 + jplephem_singleton().as_ref().unwrap().emrat);
@@ -0,0 +1,159 @@
1
+ //! Keplerian orbital elements module
2
+ //!
3
+
4
+ // External library imports
5
+ use nalgebra::{Vector3, UnitQuaternion};
6
+ type Vec3 = Vector3<f64>;
7
+ type Quat = UnitQuaternion<f64>;
8
+
9
+ /// Keplerian Orbital Elements
10
+ ///
11
+ /// The 6 Keplerian orbital elements are:
12
+ /// a: semi-major axis, meters
13
+ /// eccen: Eccentricity
14
+ /// incl: Inclination, radians
15
+ /// RAAN: Right Ascension of the Ascending Node, radians
16
+ /// w: Argument of Perigee, radians
17
+ /// nu: True Anomaly, radians
18
+ #[derive(Debug, Clone)]
19
+ pub struct Kepler {
20
+ pub a: f64,
21
+ pub eccen: f64,
22
+ pub incl: f64,
23
+ pub raan: f64,
24
+ pub w: f64,
25
+ pub nu: f64, // True anomaly
26
+ }
27
+
28
+ impl Kepler {
29
+ /// Create a new Keplerian orbital element object
30
+ ///
31
+ /// # Arguments
32
+ ///
33
+ /// * `a` - Semi-major axis, meters
34
+ /// * `e` - Eccentricity
35
+ /// * `i` - Inclination, radians
36
+ /// * `raan` - Right Ascension of the Ascending Node, radians
37
+ /// * `argp` - Argument of Perigee, radians
38
+ /// * `nu` - True Anomaly, radians
39
+ ///
40
+ /// # Returns
41
+ ///
42
+ /// * `Kepler` - A new Keplerian orbital element object
43
+ pub fn new(a: f64, e: f64, i: f64, raan: f64, argp: f64, nu: f64) -> Kepler {
44
+ Kepler {
45
+ a: a,
46
+ eccen: e,
47
+ incl: i,
48
+ raan: raan,
49
+ w: argp,
50
+ nu: nu,
51
+ }
52
+ }
53
+
54
+ /// Convert Cartesian coordinates to Keplerian orbital elements
55
+ ///
56
+ /// # Arguments
57
+ ///
58
+ /// * `r` - Position vector, meters
59
+ /// * `v` - Velocity vector, meters/second
60
+ ///
61
+ /// # Returns
62
+ ///
63
+ /// * `Kepler` - A new Keplerian orbital element object
64
+ ///
65
+ pub fn from_pv(r: Vec3, v: Vec3) -> Kepler {
66
+ let h = r.cross(&v);
67
+ let n = Vec3::z_axis().cross(&h);
68
+ let e =
69
+ ((v.norm_squared() -
70
+ crate::consts::MU_EARTH / r.norm()) * r - r.dot(&v) * v) / crate::consts::MU_EARTH;
71
+ let eccen = e.norm();
72
+ let xi = v.norm().powi(2) / 2.0 - crate::consts::MU_EARTH / r.norm();
73
+ let a = -crate::consts::MU_EARTH / (2.0 * xi);
74
+ let incl = (h.z/h.norm()).acos();
75
+ let mut raan = (n.x/n.norm()).acos();
76
+ if n.y < 0.0 {
77
+ raan = 2.0 * std::f64::consts::PI - raan;
78
+ }
79
+ let mut w = (n.dot(&e)/n.norm()/e.norm()).acos();
80
+ if e.z < 0.0 {
81
+ w = 2.0 * std::f64::consts::PI - w;
82
+ }
83
+ let mut nu = (r.dot(&e)/r.norm()/e.norm()).acos();
84
+ if r.dot(&v) < 0.0 {
85
+ nu = 2.0 * std::f64::consts::PI - nu;
86
+ }
87
+ Kepler::new(a, eccen, incl, raan, w, nu)
88
+ }
89
+
90
+ /// Convert Keplerian orbital elements to Cartesian coordinates
91
+ ///
92
+ /// # Returns
93
+ ///
94
+ /// * `(Vec3, Vec3)` - Position and velocity vectors, meters and meters/second
95
+ ///
96
+ pub fn to_pv(&self) -> (Vec3, Vec3) {
97
+ let p = self.a * (1.0 - self.eccen.powi(2));
98
+ let r = p / (1.0 + self.eccen * self.nu.cos());
99
+ let r_pqw = Vec3::new(r * self.nu.cos(), r * self.nu.sin(), 0.0);
100
+ let v_pqw = Vec3::new(
101
+ -self.nu.sin(),
102
+ self.eccen + self.nu.cos(),
103
+ 0.0) * (crate::consts::MU_EARTH/p).sqrt();
104
+ let q = Quat::from_axis_angle(&Vec3::z_axis(), self.raan) *
105
+ Quat::from_axis_angle(&Vec3::x_axis(), self.incl) *
106
+ Quat::from_axis_angle(&Vec3::z_axis(), self.w);
107
+ (q * r_pqw, q * v_pqw)
108
+ }
109
+ }
110
+
111
+ impl std::fmt::Display for Kepler {
112
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
113
+ write!(f,
114
+ "Keplerian Elements:\n a = {:.0} m\n e = {:.3}\n i = {:.3} rad\n",
115
+ self.a, self.eccen, self.incl)?;
116
+ write!(f, " Ω = {:.3} rad\n ω = {:.3} rad\n ν = {:.3} rad\n", self.raan, self.w, self.nu)
117
+ }
118
+ }
119
+
120
+ #[cfg(test)]
121
+ mod tests {
122
+ use super::*;
123
+
124
+ #[test]
125
+ fn test_topv() {
126
+ // Example 2-6 from Vallado
127
+ let p = 11067790.0;
128
+ let eccen = 0.83285_f64;
129
+ let incl = 87.87_f64.to_radians();
130
+ let raan = 227.89_f64.to_radians();
131
+ let w = 53.38_f64.to_radians();
132
+ let nu = 92.335_f64.to_radians();
133
+
134
+ let a = p / (1.0 - eccen.powi(2));
135
+
136
+ let k = Kepler::new(a, eccen, incl, raan, w, nu);
137
+ let (r, v) = k.to_pv();
138
+ // Note: values below are not incorrect in the book, but are
139
+ // corrected in the online errata
140
+ // See: https://celestrak.org/software/vallado/ErrataVer4.pdf
141
+ assert!((r*1.0e-3 - Vec3::new(6525.368, 6861.532, 6449.119)).norm() < 1e-3);
142
+ assert!((v*1.0e-3 - Vec3::new(4.902279, 5.533140, -1.975710)).norm() < 1e-3);
143
+ }
144
+
145
+ #[test]
146
+ fn test_frompv() {
147
+ // Vallado example 2-5
148
+ let r = Vec3::new(6524.834, 6862.875, 6448.296)*1.0e3;
149
+ let v = Vec3::new(4.901327, 5.533756, -1.976341)*1.0e3;
150
+ let k = Kepler::from_pv(r, v);
151
+ assert!((k.a - 36127343_f64).abs() < 1.0e3);
152
+ assert!((k.eccen - 0.83285).abs() < 1e-3);
153
+ assert!((k.incl - 87.87_f64.to_radians()).abs() < 1e-3);
154
+ assert!((k.raan - 227.89_f64.to_radians()).abs() < 1e-3);
155
+ assert!((k.w - 53.38_f64.to_radians()).abs() < 1e-3);
156
+ assert!((k.nu - 92.335_f64.to_radians()).abs() < 1e-3);
157
+ }
158
+ }
159
+
@@ -61,6 +61,8 @@ pub mod spaceweather;
61
61
  pub mod tle;
62
62
  /// Utility functions
63
63
  pub mod utils;
64
+ /// Keplerian orbital elements
65
+ pub mod kepler;
64
66
 
65
67
  // Integrate ordinary differential equations
66
68
  mod ode;
@@ -0,0 +1,9 @@
1
+ /// Lunar calculations
2
+ pub mod moon;
3
+ /// Solar calculations
4
+ pub mod sun;
5
+
6
+ // This part isn't working yet...
7
+ // mod planets;
8
+ // pub use planets::barycentric_pos;
9
+