keplemon 3.1.1__cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

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 (76) hide show
  1. keplemon/.DS_Store +0 -0
  2. keplemon/._.DS_Store +0 -0
  3. keplemon/._arm +0 -0
  4. keplemon/._libastrofunc.so +0 -0
  5. keplemon/._libdllmain.so +0 -0
  6. keplemon/._libelops.so +0 -0
  7. keplemon/._libenvconst.so +0 -0
  8. keplemon/._libextephem.so +0 -0
  9. keplemon/._libobs.so +0 -0
  10. keplemon/._libsatstate.so +0 -0
  11. keplemon/._libsensor.so +0 -0
  12. keplemon/._libsgp4prop.so +0 -0
  13. keplemon/._libspvec.so +0 -0
  14. keplemon/._libtimefunc.so +0 -0
  15. keplemon/._libtle.so +0 -0
  16. keplemon/._libvcm.so +0 -0
  17. keplemon/EGM-2008.GEO +2563 -0
  18. keplemon/EGM-96.GEO +2563 -0
  19. keplemon/JPLcon_1950_2050.405 +392370 -0
  20. keplemon/SGP4_Open_License.txt +186 -0
  21. keplemon/WGS84-70.GEO +2563 -0
  22. keplemon/__init__.py +33 -0
  23. keplemon/__init__.pyi +54 -0
  24. keplemon/__main__.py +20 -0
  25. keplemon/_keplemon.abi3.so +0 -0
  26. keplemon/bodies.py +8 -0
  27. keplemon/bodies.pyi +367 -0
  28. keplemon/catalogs.py +5 -0
  29. keplemon/catalogs.pyi +19 -0
  30. keplemon/elements.py +39 -0
  31. keplemon/elements.pyi +521 -0
  32. keplemon/enums.py +21 -0
  33. keplemon/enums.pyi +120 -0
  34. keplemon/estimation.py +9 -0
  35. keplemon/estimation.pyi +200 -0
  36. keplemon/events.py +17 -0
  37. keplemon/events.pyi +103 -0
  38. keplemon/exceptions.py +5 -0
  39. keplemon/libastrofunc.so +0 -0
  40. keplemon/libdllmain.so +0 -0
  41. keplemon/libelops.so +0 -0
  42. keplemon/libenvconst.so +0 -0
  43. keplemon/libextephem.so +0 -0
  44. keplemon/libobs.so +0 -0
  45. keplemon/libsatstate.so +0 -0
  46. keplemon/libsensor.so +0 -0
  47. keplemon/libsgp4prop.so +0 -0
  48. keplemon/libspvec.so +0 -0
  49. keplemon/libtimefunc.so +0 -0
  50. keplemon/libtle.so +0 -0
  51. keplemon/libvcm.so +0 -0
  52. keplemon/propagation.py +15 -0
  53. keplemon/propagation.pyi +50 -0
  54. keplemon/py.typed +0 -0
  55. keplemon/time.py +96 -0
  56. keplemon/time.pyi +293 -0
  57. keplemon/time_constants.dat +19472 -0
  58. keplemon-3.1.1.dist-info/METADATA +21 -0
  59. keplemon-3.1.1.dist-info/RECORD +76 -0
  60. keplemon-3.1.1.dist-info/WHEEL +5 -0
  61. keplemon-3.1.1.dist-info/entry_points.txt +2 -0
  62. keplemon.libs/libastrofunc-d5d29f1a.so +0 -0
  63. keplemon.libs/libdllmain-83b073db.so +0 -0
  64. keplemon.libs/libelops-d6961cbd.so +0 -0
  65. keplemon.libs/libenvconst-5ff8e89b.so +0 -0
  66. keplemon.libs/libextephem-9ddad493.so +0 -0
  67. keplemon.libs/libgfortran-daac5196.so.5 +0 -0
  68. keplemon.libs/libgomp-d22c30c5.so.1 +0 -0
  69. keplemon.libs/libobs-acba28cb.so +0 -0
  70. keplemon.libs/libsatstate-a0706992.so +0 -0
  71. keplemon.libs/libsensor-15ffa85c.so +0 -0
  72. keplemon.libs/libsgp4prop-8300f9d3.so +0 -0
  73. keplemon.libs/libspvec-b3cef8a2.so +0 -0
  74. keplemon.libs/libtimefunc-d4915652.so +0 -0
  75. keplemon.libs/libtle-72004375.so +0 -0
  76. keplemon.libs/libvcm-460d66c8.so +0 -0
keplemon/__init__.py ADDED
@@ -0,0 +1,33 @@
1
+ # flake8: noqa
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import logging
6
+ from importlib import resources
7
+
8
+ _logger = logging.getLogger(__name__)
9
+
10
+
11
+ def _set_asset_directory() -> None:
12
+
13
+ asset_dir = os.getenv("SAAL_ASSET_DIRECTORY")
14
+ if asset_dir is None:
15
+ pkg_dir = resources.files("keplemon")
16
+ _logger.debug("Setting SAAL_ASSET_DIRECTORY to %s", pkg_dir)
17
+ os.environ.setdefault("SAAL_ASSET_DIRECTORY", str(pkg_dir))
18
+ elif not os.path.exists(asset_dir):
19
+ raise FileNotFoundError(f"SAAL_ASSET_DIRECTORY '{asset_dir}' does not exist.")
20
+
21
+
22
+ _set_asset_directory()
23
+
24
+ from keplemon._keplemon import ( # type: ignore
25
+ get_thread_count,
26
+ set_thread_count,
27
+ )
28
+
29
+
30
+ __all__ = [
31
+ "get_thread_count",
32
+ "set_thread_count",
33
+ ]
keplemon/__init__.pyi ADDED
@@ -0,0 +1,54 @@
1
+ # flake8: noqa
2
+ from pathlib import Path
3
+
4
+ def get_thread_count() -> int:
5
+ """
6
+ Returns:
7
+ Number of cores allocated for use by KepLemon
8
+ """
9
+ ...
10
+
11
+ def set_thread_count(n: int) -> None:
12
+ """
13
+ Set the number of cores allocated for use by KepLemon
14
+
15
+ !!! warning
16
+ This function must be called before any other functions in the library
17
+
18
+ Args:
19
+ n: Number of cores to allocate
20
+ """
21
+ ...
22
+
23
+ #: Path to the time constants file
24
+ TIME_CONSTANTS_PATH: Path
25
+ """
26
+ Path to the default time constants file required by the SAAL binaries
27
+
28
+ !!! warning
29
+ This path should never be modified and is only exposed to allow inspection of current data.
30
+ """
31
+
32
+ #: Path to the parent directory of the package
33
+ PACKAGE_DIRECTORY: Path
34
+ """Path to the parent directory of the package"""
35
+
36
+ #: Path to the assets directory containing supporting data files for the package
37
+ ASSETS_DIRECTORY: Path
38
+ """Path to the assets directory containing supporting data files for the package"""
39
+
40
+ def set_license_file_path(path: str) -> None:
41
+ """
42
+ Set the path to the license file required by the SAAL binaries
43
+
44
+ Args:
45
+ path: Path to the SGP4 license file
46
+ """
47
+ ...
48
+
49
+ def get_license_file_path() -> str:
50
+ """
51
+ Returns:
52
+ Path to the SGP4 license file
53
+ """
54
+ ...
keplemon/__main__.py ADDED
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+ import click
3
+ from pathlib import Path
4
+ from importlib import resources
5
+ from keplemon.time import request_time_constants_update
6
+
7
+
8
+ @click.command()
9
+ @click.option(
10
+ "--update-eop",
11
+ help="Update time constants and EOP data (global or path/to/output/file)",
12
+ type=click.Path(exists=False),
13
+ )
14
+ def cli(update_eop: Path | None) -> None:
15
+ if update_eop is not None:
16
+ if update_eop == "global":
17
+ time_constants_path = resources.files("keplemon") / "time_constants.dat"
18
+ print("Requesting time constants and EOP data from USNO...")
19
+ request_time_constants_update(str(time_constants_path))
20
+ print(f"Updated time constants at {update_eop}")
Binary file
keplemon/bodies.py ADDED
@@ -0,0 +1,8 @@
1
+ from keplemon._keplemon.bodies import ( # type: ignore
2
+ Satellite,
3
+ Constellation,
4
+ Sensor,
5
+ Observatory,
6
+ )
7
+
8
+ __all__ = ["Satellite", "Constellation", "Sensor", "Observatory"]
keplemon/bodies.pyi ADDED
@@ -0,0 +1,367 @@
1
+ # flake8: noqa
2
+ from typing import Optional
3
+
4
+ from keplemon.elements import (
5
+ TLE,
6
+ CartesianState,
7
+ Ephemeris,
8
+ KeplerianState,
9
+ GeodeticPosition,
10
+ OrbitPlotData,
11
+ TopocentricElements,
12
+ RelativeState,
13
+ BoreToBodyAngles,
14
+ )
15
+ from keplemon.catalogs import TLECatalog
16
+ from keplemon.time import Epoch, TimeSpan
17
+ from keplemon.events import CloseApproach, CloseApproachReport, HorizonAccessReport, FieldOfViewReport
18
+ from keplemon.propagation import ForceProperties
19
+ from keplemon.enums import ReferenceFrame
20
+
21
+ class Satellite:
22
+
23
+ id: str
24
+ """Unique identifier for the satellite."""
25
+
26
+ norad_id: int
27
+ """Number corresponding to the satellite's NORAD catalog ID.
28
+ """
29
+
30
+ force_properties: ForceProperties
31
+ """Force properties of the satellite used for propagation"""
32
+
33
+ name: Optional[str]
34
+ """Human-readable name of the satellite"""
35
+
36
+ keplerian_state: Optional[KeplerianState]
37
+ """Keplerian state of the satellite at the epoch of the TLE, if available"""
38
+
39
+ geodetic_position: Optional[GeodeticPosition]
40
+ """Geodetic position of the satellite at the epoch of the TLE, if available"""
41
+
42
+ def __init__(self) -> None: ...
43
+ @classmethod
44
+ def from_tle(cls, tle: TLE) -> Satellite:
45
+ """
46
+ Instantiate a Satellite from a legacy TLE
47
+
48
+ Args:
49
+ tle: Two-line element set for the satellite
50
+ """
51
+ ...
52
+
53
+ def get_close_approach(
54
+ self,
55
+ other: Satellite,
56
+ start: Epoch,
57
+ end: Epoch,
58
+ distance_threshold: float,
59
+ ) -> Optional[CloseApproach]: ...
60
+ def get_ephemeris(
61
+ self,
62
+ start: Epoch,
63
+ end: Epoch,
64
+ step: TimeSpan,
65
+ ) -> Ephemeris: ...
66
+ def get_state_at_epoch(self, epoch: Epoch) -> CartesianState: ...
67
+ def to_tle(self) -> Optional[TLE]:
68
+ """
69
+ Returns:
70
+ Satellite as a two-line element set or None if no state is loaded
71
+
72
+ """
73
+ ...
74
+
75
+ def get_relative_state_at_epoch(self, other: Satellite, epoch: Epoch) -> Optional[RelativeState]:
76
+ """
77
+ Calculate the relative state between this satellite and another satellite at a given epoch.
78
+
79
+ Args:
80
+ other: Secondary satellite to calculate the relative state against
81
+ epoch: UTC epoch at which the relative state will be calculated
82
+ """
83
+ ...
84
+
85
+ def get_body_angles_at_epoch(self, other: Satellite, epoch: Epoch) -> Optional[BoreToBodyAngles]:
86
+ """
87
+ Calculate the bore-to-body angles between this satellite and another satellite at a given epoch.
88
+
89
+ Args:
90
+ other: Secondary satellite to calculate the bore-to-body angles against
91
+ epoch: UTC epoch at which the bore-to-body angles will be calculated
92
+ """
93
+ ...
94
+
95
+ def get_plot_data(self, start: Epoch, end: Epoch, step: TimeSpan) -> Optional[OrbitPlotData]: ...
96
+ def get_observatory_access_report(
97
+ self,
98
+ observatories: list[Observatory],
99
+ start: Epoch,
100
+ end: Epoch,
101
+ min_el: float,
102
+ min_duration: TimeSpan,
103
+ ) -> Optional[HorizonAccessReport]:
104
+ """
105
+ Calculate horizon access from multiple observatories to this satellite.
106
+
107
+ Args:
108
+ observatories: List of observatories to check for horizon access
109
+ start: UTC epoch of the start of the report
110
+ end: UTC epoch of the end of the report
111
+ min_el: Minimum elevation angle in **_degrees_**
112
+ min_duration: Minimum duration of access
113
+
114
+ Returns:
115
+ Horizon access report containing accesses from all observatories to the satellite,
116
+ or None if the satellite ephemeris cannot be generated
117
+ """
118
+ ...
119
+
120
+ class Constellation:
121
+
122
+ count: int
123
+ """Number of satellites in the constellation"""
124
+
125
+ name: Optional[str]
126
+ """Human-readable name of the constellation"""
127
+
128
+ def __init__(self) -> None: ...
129
+ def get_plot_data(self, start: Epoch, end: Epoch, step: TimeSpan) -> dict[str, OrbitPlotData]: ...
130
+ @classmethod
131
+ def from_tle_catalog(cls, tle_catalog: TLECatalog) -> Constellation:
132
+ """
133
+ Instantiate a Constellation from a TLE catalog
134
+
135
+ Args:
136
+ tle_catalog: TLE catalog for the constellation
137
+ """
138
+ ...
139
+
140
+ def get_states_at_epoch(self, epoch: Epoch) -> dict[int, CartesianState]:
141
+ """
142
+ Args:
143
+ epoch: UTC epoch at which the states will be calculated
144
+
145
+ Returns:
146
+ (satellite_id, state) dictionary for the constellation at the given epoch
147
+ """
148
+ ...
149
+
150
+ def get_ephemeris(
151
+ self,
152
+ start: Epoch,
153
+ end: Epoch,
154
+ step: TimeSpan,
155
+ ) -> dict[str, Ephemeris]:
156
+ """
157
+ Args:
158
+ start: UTC epoch of the start of the ephemeris
159
+ end: UTC epoch of the end of the ephemeris
160
+ step: Time step for the ephemeris
161
+
162
+ Returns:
163
+ (satellite_id, ephemeris) dictionary for the constellation
164
+ """
165
+ ...
166
+
167
+ def get_ca_report_vs_one(
168
+ self,
169
+ other: Satellite,
170
+ start: Epoch,
171
+ end: Epoch,
172
+ distance_threshold: float,
173
+ ) -> CloseApproachReport:
174
+ """
175
+ Calculate close approaches between the constellation and a given satellite.
176
+
177
+ Args:
178
+ other: Satellite to compare against
179
+ start: UTC epoch of the start of the close approach report
180
+ end: UTC epoch of the end of the close approach report
181
+ distance_threshold: Distance threshold for close approach screening in **_kilometers_**
182
+
183
+ Returns:
184
+ Close approach report for the constellation vs. the given satellite
185
+ """
186
+ ...
187
+
188
+ def get_ca_report_vs_many(
189
+ self,
190
+ start: Epoch,
191
+ end: Epoch,
192
+ distance_threshold: float,
193
+ ) -> CloseApproachReport:
194
+ """
195
+ Calculate close approaches among satellites in the calling constellation.
196
+
197
+ !!! warning
198
+ This is a long-running operation when the constellation is large.
199
+
200
+ Args:
201
+ start: UTC epoch of the start of the close approach report
202
+ end: UTC epoch of the end of the close approach report
203
+ distance_threshold: Distance threshold for close approach screening in **_kilometers_**
204
+
205
+ Returns:
206
+ Close approach report for the constellation vs. all other satellites
207
+ """
208
+ ...
209
+
210
+ def __getitem__(self, satellite_id: str) -> Satellite: ...
211
+ def __setitem__(self, satellite_id: str, sat: Satellite) -> None: ...
212
+ def get_horizon_access_report(
213
+ self,
214
+ site: Observatory,
215
+ start: Epoch,
216
+ end: Epoch,
217
+ min_el: float,
218
+ min_duration: TimeSpan,
219
+ ) -> HorizonAccessReport:
220
+ """
221
+ Calculate horizon access to a given observatory.
222
+
223
+ Args:
224
+ site: Observatory to check for horizon access
225
+ start: UTC epoch of the start of the report
226
+ end: UTC epoch of the end of the report
227
+ min_el: Minimum elevation angle in **_degrees_**
228
+ min_duration: Minimum duration of access
229
+
230
+ Returns:
231
+ Horizon access report for the constellation from the observatory
232
+ """
233
+ ...
234
+
235
+ class Sensor:
236
+ """
237
+ Args:
238
+ name: Identifier of the sensor
239
+ angular_noise: Angular noise in **_degrees_**
240
+ """
241
+
242
+ id: str
243
+ """Unique identifier for the sensor."""
244
+ name: Optional[str]
245
+ angular_noise: float
246
+ range_noise: Optional[float]
247
+ """Range noise in **_kilometers_**"""
248
+
249
+ range_rate_noise: Optional[float]
250
+ """Range rate noise in **_kilometers per second_**"""
251
+
252
+ angular_rate_noise: Optional[float]
253
+ """Angular rate noise in **_degrees per second_**"""
254
+ def __init__(self, angular_noise: float) -> None: ...
255
+
256
+ class Observatory:
257
+ """
258
+ Args:
259
+ latitude: Latitude in **_degrees_**
260
+ longitude: Longitude in **_degrees_**
261
+ altitude: Altitude in **_kilometers_**
262
+ """
263
+
264
+ name: str
265
+ id: str
266
+ """Unique identifier for the observatory."""
267
+ latitude: float
268
+ longitude: float
269
+ altitude: float
270
+ sensors: list[Sensor]
271
+ """List of sensors at the observatory"""
272
+ def __init__(
273
+ self,
274
+ latitude: float,
275
+ longitude: float,
276
+ altitude: float,
277
+ ) -> None: ...
278
+ def get_state_at_epoch(self, epoch: Epoch) -> CartesianState:
279
+ """
280
+ Args:
281
+ epoch: UTC epoch of the state
282
+
283
+ Returns:
284
+ TEME Cartesian state of the observatory in **_kilometers_** and **_kilometers per second_**
285
+ """
286
+ ...
287
+
288
+ @classmethod
289
+ def from_cartesian_state(cls, state: CartesianState) -> Observatory:
290
+ """
291
+ Create an observatory from a Cartesian state.
292
+
293
+ Args:
294
+ state: Cartesian state of the observatory
295
+
296
+ """
297
+ ...
298
+
299
+ def get_theta(self, epoch: Epoch) -> float:
300
+ """
301
+ Calculate the Greenwich angle plus the observatory longitude at a given epoch.
302
+
303
+ Args:
304
+ epoch: UTC epoch for the calculation
305
+
306
+ Returns:
307
+ Greenwich angle plus the observatory longitude in **_radians_**
308
+ """
309
+ ...
310
+
311
+ def get_horizon_access_report(
312
+ self,
313
+ satellite: Satellite,
314
+ start: Epoch,
315
+ end: Epoch,
316
+ min_el: float,
317
+ min_duration: TimeSpan,
318
+ ) -> HorizonAccessReport:
319
+ """
320
+ Calculate horizon access for a satellite from the observatory.
321
+
322
+ Args:
323
+ satellite: Satellite to check for horizon access
324
+ start: UTC epoch of the start of the report
325
+ end: UTC epoch of the end of the report
326
+ min_el: Minimum elevation angle in **_degrees_**
327
+ min_duration: Minimum duration of access in **_seconds_**
328
+
329
+ Returns:
330
+ Horizon access report for the satellite from the observatory
331
+ """
332
+ ...
333
+
334
+ def get_field_of_view_report(
335
+ self,
336
+ epoch: Epoch,
337
+ sensor_direction: TopocentricElements,
338
+ angular_threshold: float,
339
+ sats: Constellation,
340
+ reference_frame: ReferenceFrame,
341
+ ) -> FieldOfViewReport:
342
+ """
343
+ Calculate satellites in the field of view from a given time and direction.
344
+
345
+ Args:
346
+ epoch: UTC epoch of the report
347
+ sensor_direction: Topocentric direction the sensor is pointing
348
+ angular_threshold: Angular threshold in **_degrees_**
349
+ sats: Constellation of satellites to check for being in the field of view
350
+ reference_frame: Reference frame of the output direction elements
351
+ """
352
+ ...
353
+
354
+ def get_topocentric_to_satellite(
355
+ self,
356
+ epoch: Epoch,
357
+ sat: Satellite,
358
+ reference_frame: ReferenceFrame,
359
+ ) -> TopocentricElements:
360
+ """
361
+ Get the topocentric elements of a satellite as seen from the observatory.
362
+ Args:
363
+ epoch: UTC epoch of the observation
364
+ sat: Satellite to observe
365
+ reference_frame: Reference frame of the output direction elements
366
+ """
367
+ ...
keplemon/catalogs.py ADDED
@@ -0,0 +1,5 @@
1
+ from keplemon._keplemon.catalogs import ( # type: ignore
2
+ TLECatalog,
3
+ )
4
+
5
+ __all__ = ["TLECatalog"]
keplemon/catalogs.pyi ADDED
@@ -0,0 +1,19 @@
1
+ # flake8: noqa
2
+ from typing import Optional
3
+
4
+ from keplemon.elements import TLE, OrbitPlotData
5
+
6
+ class TLECatalog:
7
+ count: int
8
+ name: Optional[str]
9
+ def __init__(self) -> None: ...
10
+ @classmethod
11
+ def from_tle_file(cls, filename: str) -> TLECatalog: ...
12
+ def add(self, tle: TLE) -> None: ...
13
+ def get(self, satellite_id: str) -> TLE: ...
14
+ def remove(self, satellite_id: str) -> None: ...
15
+ def keys(self) -> list[str]: ...
16
+ def get_count(self) -> int: ...
17
+ def clear(self) -> None: ...
18
+ def __getitem__(self, satellite_id: str) -> TLE: ...
19
+ def get_plot_data(self) -> OrbitPlotData: ...
keplemon/elements.py ADDED
@@ -0,0 +1,39 @@
1
+ from keplemon._keplemon.elements import ( # type: ignore
2
+ TLE,
3
+ KeplerianState,
4
+ CartesianState,
5
+ CartesianVector,
6
+ KeplerianElements,
7
+ Ephemeris,
8
+ SphericalVector,
9
+ TopocentricElements,
10
+ HorizonState,
11
+ HorizonElements,
12
+ GeodeticPosition,
13
+ OrbitPlotData,
14
+ OrbitPlotState,
15
+ RelativeState,
16
+ BoreToBodyAngles,
17
+ TopocentricState,
18
+ EquinoctialElements,
19
+ )
20
+
21
+ __all__ = [
22
+ "TLE",
23
+ "KeplerianState",
24
+ "CartesianState",
25
+ "CartesianVector",
26
+ "KeplerianElements",
27
+ "Ephemeris",
28
+ "SphericalVector",
29
+ "TopocentricElements",
30
+ "HorizonState",
31
+ "HorizonElements",
32
+ "GeodeticPosition",
33
+ "OrbitPlotData",
34
+ "OrbitPlotState",
35
+ "RelativeState",
36
+ "BoreToBodyAngles",
37
+ "TopocentricState",
38
+ "EquinoctialElements",
39
+ ]