spherapy 0.2.1__tar.gz → 0.3.0__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.
- spherapy-0.3.0/CHANGELOG.md +21 -0
- {spherapy-0.2.1/spherapy.egg-info → spherapy-0.3.0}/PKG-INFO +3 -3
- {spherapy-0.2.1 → spherapy-0.3.0}/README.md +2 -2
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/_version.py +3 -3
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/orbit.py +26 -8
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/timespan.py +12 -2
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/epoch_u.py +63 -0
- {spherapy-0.2.1 → spherapy-0.3.0/spherapy.egg-info}/PKG-INFO +3 -3
- {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/test_orbit.py +42 -3
- {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/test_timespan.py +75 -0
- spherapy-0.2.1/CHANGELOG.md +0 -15
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/question.yaml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/workflows/pr.yml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.github/workflows/release.yml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.gitignore +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/.pre-commit-config.yaml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/CONTRIBUTING.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/LICENSE.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/README.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/orbit.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/timespan.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/updater.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.celestrak.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.constants.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.credentials.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.elements_u.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.epoch_u.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.exceptions.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.orbital_u.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.spacetrack.md +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/pyproject.toml +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/setup.cfg +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/__init__.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/__main__.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/data/TLEs/25544.tle +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/updater.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/__init__.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/celestrak.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/constants.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/credentials.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/elements_u.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/exceptions.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/orbital_u.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/spacetrack.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.conf +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/SOURCES.txt +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/dependency_links.txt +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/entry_points.txt +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/requires.txt +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/top_level.txt +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/conftest.py +0 -0
- {spherapy-0.2.1 → spherapy-0.3.0}/tests/utils/test_epoch_u.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The change log should include
|
|
2
|
+
- theme of the upversion
|
|
3
|
+
- changes in API
|
|
4
|
+
- changes in model behaviour
|
|
5
|
+
|
|
6
|
+
# v0.3.0
|
|
7
|
+
- renamed fromAnalyticalOrbitalParams() argument mean_nu to true_nu to reflect the underlying api call to hapsira.
|
|
8
|
+
- added an epoch argument for the orbital parameters of fromAnalyticalOrbitalParams(). Note the default epoch for fromAnalyticalOrbitalParams() is now the first timestep of the TimeSpan rather than astropy's J2000. This is a breaking change and thus existing calls to fromAnalyticalOrbitalParams() will almost certainly now produce different results. The previous functionality can be obtained by choosing datetime.datetime.fromisoformat('2000-01-01T12:00:00+00:00') as the epoch.
|
|
9
|
+
- adding typing overloads to the TimeSpan.__getitem__() function which specify a stronger typing constraints dependent on the type of the argument.
|
|
10
|
+
- fixed TimeSpan.__getitem__() adding extra layer of list nesting when index was of type list.
|
|
11
|
+
|
|
12
|
+
# v0.2.1
|
|
13
|
+
- minor bug fixes to the API for pulling TLEs from Celestrak
|
|
14
|
+
|
|
15
|
+
# v0.2.0
|
|
16
|
+
- initial release to pypi
|
|
17
|
+
- timespan object
|
|
18
|
+
- orbit object
|
|
19
|
+
- orbit object creation from: TLE, propagated orbital parameters, analytical orbital parameters, list of positions
|
|
20
|
+
- TLE updater, pulls requested TLEs from spacetrack, stores all TLEs for that satellite in a saved file
|
|
21
|
+
- TLE updater falls back to celestrak if no credentials provided
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spherapy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: An orbital propagator wrapper and TLE fetcher
|
|
5
5
|
Author-email: Robert Mearns <rz.mearns@gmail.com>
|
|
6
6
|
Maintainer-email: Robert Mearns <rz.mearns@gmail.com>
|
|
@@ -87,7 +87,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
87
87
|
inc=0,
|
|
88
88
|
raan=0,
|
|
89
89
|
argp=0,
|
|
90
|
-
|
|
90
|
+
true_nu=0,
|
|
91
91
|
name='My Analytical Orbit',
|
|
92
92
|
astrobodies=True)
|
|
93
93
|
```
|
|
@@ -115,7 +115,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
115
115
|
inc=0,
|
|
116
116
|
raan=0,
|
|
117
117
|
argp=0,
|
|
118
|
-
|
|
118
|
+
true_nu=0,
|
|
119
119
|
name='My Analytical Orbit',
|
|
120
120
|
astrobodies=True)
|
|
121
121
|
```
|
|
@@ -53,7 +53,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
53
53
|
inc=0,
|
|
54
54
|
raan=0,
|
|
55
55
|
argp=0,
|
|
56
|
-
|
|
56
|
+
true_nu=0,
|
|
57
57
|
name='My Analytical Orbit',
|
|
58
58
|
astrobodies=True)
|
|
59
59
|
```
|
|
@@ -81,7 +81,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
81
81
|
inc=0,
|
|
82
82
|
raan=0,
|
|
83
83
|
argp=0,
|
|
84
|
-
|
|
84
|
+
true_nu=0,
|
|
85
85
|
name='My Analytical Orbit',
|
|
86
86
|
astrobodies=True)
|
|
87
87
|
```
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.3.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gad44a3cde'
|
|
@@ -626,7 +626,8 @@ class Orbit:
|
|
|
626
626
|
inc:float=0,
|
|
627
627
|
raan:float=0,
|
|
628
628
|
argp:float=0,
|
|
629
|
-
|
|
629
|
+
true_nu:float=0,
|
|
630
|
+
epoch:dt.datetime|None=None,
|
|
630
631
|
name:str='Analytical',
|
|
631
632
|
astrobodies:bool=True,
|
|
632
633
|
unsafe:bool=False) -> 'Orbit':
|
|
@@ -651,8 +652,11 @@ class Orbit:
|
|
|
651
652
|
argp: [Optional] argument of the perigee in degrees
|
|
652
653
|
Default is 0, which represents an orbit with its semimajor axis in
|
|
653
654
|
the plane of the Earth's equator
|
|
654
|
-
|
|
655
|
-
Default is 0, which represents an orbit that is beginning
|
|
655
|
+
true_nu: [Optional] true anomaly at epoch in degrees
|
|
656
|
+
Default is 0, which represents an orbit that is beginning
|
|
657
|
+
at periapsis on the epoch
|
|
658
|
+
epoch: [Optional] epoch for orbit
|
|
659
|
+
Default is the first timestep of the timespan
|
|
656
660
|
name: [Optional] string giving the name of the orbit
|
|
657
661
|
Default is 'Analytical'
|
|
658
662
|
astrobodies: [Optional] Flag to calculate Sun and Moon positions at timestamps
|
|
@@ -705,13 +709,25 @@ class Orbit:
|
|
|
705
709
|
raise exceptions.OutOfRangeError(f"Argument of periapsis, {raan}, is out of "
|
|
706
710
|
f"range, should be 0 < argp < 360")
|
|
707
711
|
|
|
708
|
-
if
|
|
709
|
-
logger.error("
|
|
710
|
-
raise exceptions.OutOfRangeError(f"
|
|
711
|
-
f"should be 0 <
|
|
712
|
+
if true_nu > 360 or true_nu < 0: #noqa: PLR2004
|
|
713
|
+
logger.error("True anomaly, %s, is out of range, should be 0 < true_nu < 360", true_nu)
|
|
714
|
+
raise exceptions.OutOfRangeError(f"True anomaly, {true_nu}, is out of range, "
|
|
715
|
+
f"should be 0 < true_nu < 360")
|
|
716
|
+
|
|
717
|
+
if epoch is None:
|
|
718
|
+
epoch = timespan[0]
|
|
719
|
+
if isinstance(epoch, dt.datetime):
|
|
720
|
+
astropy_epoch = astropyTime(epoch, scale='utc')
|
|
721
|
+
else:
|
|
722
|
+
logger.error("epoch, %s must be of type dt.datetime|astropyTime but"
|
|
723
|
+
" is of type %s", epoch, type(epoch))
|
|
724
|
+
raise TypeError(f"epoch, {epoch} must be of type "
|
|
725
|
+
f"dt.datetime|astropyTime but is of type {type(epoch)}")
|
|
712
726
|
|
|
713
727
|
attr_dct = _createEmptyOrbitAttrDict()
|
|
714
728
|
|
|
729
|
+
|
|
730
|
+
|
|
715
731
|
logger.info("Creating analytical orbit")
|
|
716
732
|
orb = hapsiraOrbit.from_classical(central_body,
|
|
717
733
|
a * astropy_units.one * astropy_units.km,
|
|
@@ -719,7 +735,9 @@ class Orbit:
|
|
|
719
735
|
inc * astropy_units.one * astropy_units.deg,
|
|
720
736
|
raan * astropy_units.one * astropy_units.deg,
|
|
721
737
|
argp * astropy_units.one * astropy_units.deg,
|
|
722
|
-
|
|
738
|
+
true_nu * astropy_units.one * astropy_units.deg,
|
|
739
|
+
astropy_epoch
|
|
740
|
+
)
|
|
723
741
|
|
|
724
742
|
|
|
725
743
|
logger.info("Creating ephemeris for orbit, using timespan")
|
|
@@ -6,7 +6,7 @@ This module provides:
|
|
|
6
6
|
import datetime as dt
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
-
from typing import cast
|
|
9
|
+
from typing import cast, overload
|
|
10
10
|
from typing_extensions import Self
|
|
11
11
|
|
|
12
12
|
from astropy.time import Time as astropyTime
|
|
@@ -112,6 +112,16 @@ class TimeSpan:
|
|
|
112
112
|
"""Returns the internal _timearr when the TimeSpan is called."""
|
|
113
113
|
return self._timearr
|
|
114
114
|
|
|
115
|
+
@overload
|
|
116
|
+
def __getitem__(self, idx: None) -> np.ndarray[tuple[int], np.dtype[np.datetime64]]: ...
|
|
117
|
+
|
|
118
|
+
@overload
|
|
119
|
+
def __getitem__(self, idx: int | np.integer) -> dt.datetime: ...
|
|
120
|
+
|
|
121
|
+
@overload
|
|
122
|
+
def __getitem__(self, idx: slice | list[int] | tuple[int, ...] | np.ndarray) \
|
|
123
|
+
-> np.ndarray[tuple[int], np.dtype[np.datetime64]]: ...
|
|
124
|
+
|
|
115
125
|
def __getitem__(self, idx:None|int|np.integer|tuple|list|np.ndarray|slice=None) \
|
|
116
126
|
-> None|dt.datetime|np.ndarray[tuple[int], np.dtype[np.datetime64]]: # noqa: PLR0911
|
|
117
127
|
"""Returns an index or slice of the TimeSpan as an array of datetime objects."""
|
|
@@ -124,7 +134,7 @@ class TimeSpan:
|
|
|
124
134
|
return self._timearr[idx[0]:idx[1]]
|
|
125
135
|
return self._timearr[idx[0]:idx[1]:idx[2]]
|
|
126
136
|
if isinstance(idx, list):
|
|
127
|
-
return self._timearr[
|
|
137
|
+
return self._timearr[idx]
|
|
128
138
|
if isinstance(idx, np.ndarray):
|
|
129
139
|
return self._timearr[idx]
|
|
130
140
|
if isinstance(idx,slice):
|
|
@@ -147,3 +147,66 @@ def getStoredEpochs(tle_path:pathlib.Path) -> None|tuple[dt.datetime, dt.datetim
|
|
|
147
147
|
last_epoch_dt = epoch2datetime(last_tle_line_1['fields'][3])
|
|
148
148
|
|
|
149
149
|
return (first_epoch_dt, last_epoch_dt)
|
|
150
|
+
|
|
151
|
+
def getAllStoredEpochs(tle_path:pathlib.Path) -> None|list[dt.datetime]:
|
|
152
|
+
"""Return the all TLE epochs for tle_path.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
tle_path: tle file
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
list of datetime object equivalent to the TLE epoch for each TLE
|
|
159
|
+
None if no spacetrack tle stored for sat_id
|
|
160
|
+
"""
|
|
161
|
+
if not tle_path.exists():
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
with tle_path.open('r') as fp:
|
|
165
|
+
lines = fp.readlines()
|
|
166
|
+
|
|
167
|
+
tle_epochs_dt = []
|
|
168
|
+
for ii in range(1,len(lines),3):
|
|
169
|
+
tle_line = elements_u.split3LELineIntoFields(lines[ii])
|
|
170
|
+
tle_epochs_dt.append(epoch2datetime(tle_line['fields'][3]))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
return tle_epochs_dt
|
|
174
|
+
|
|
175
|
+
def getStoredTLEByIdx(tle_path:pathlib.Path,
|
|
176
|
+
idx_list:int|list[int],
|
|
177
|
+
string_only:bool=True) \
|
|
178
|
+
-> list[dict[int, elements_u.ElementsLineDict]]|list[str]|None:
|
|
179
|
+
"""Return the TLE at the Idx within tle_path specifed by idx_list.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
tle_path: tle file
|
|
183
|
+
idx_list: list of TLE indices within tle_path
|
|
184
|
+
string_only: if true, return only the raw TLE string,
|
|
185
|
+
if false returns a dict of each field within each line
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
list of tle_data
|
|
189
|
+
"""
|
|
190
|
+
if not tle_path.exists():
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
if isinstance(idx_list, int):
|
|
194
|
+
idx_list = [idx_list]
|
|
195
|
+
|
|
196
|
+
with tle_path.open('r') as fp:
|
|
197
|
+
lines = fp.readlines()
|
|
198
|
+
|
|
199
|
+
tles = []
|
|
200
|
+
for idx in idx_list:
|
|
201
|
+
tle_line_0 = elements_u.split3LELineIntoFields(lines[idx*3+0])
|
|
202
|
+
tle_line_1 = elements_u.split3LELineIntoFields(lines[idx*3+1])
|
|
203
|
+
tle_line_2 = elements_u.split3LELineIntoFields(lines[idx*3+2])
|
|
204
|
+
|
|
205
|
+
if string_only:
|
|
206
|
+
tle_data = f"{tle_line_0['line_str']}{tle_line_1['line_str']}{tle_line_2['line_str']}"
|
|
207
|
+
else:
|
|
208
|
+
tle_data = {0:tle_line_0, 1:tle_line_1, 2:tle_line_2} #type:ignore
|
|
209
|
+
|
|
210
|
+
tles.append(tle_data)
|
|
211
|
+
|
|
212
|
+
return tles
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spherapy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: An orbital propagator wrapper and TLE fetcher
|
|
5
5
|
Author-email: Robert Mearns <rz.mearns@gmail.com>
|
|
6
6
|
Maintainer-email: Robert Mearns <rz.mearns@gmail.com>
|
|
@@ -87,7 +87,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
87
87
|
inc=0,
|
|
88
88
|
raan=0,
|
|
89
89
|
argp=0,
|
|
90
|
-
|
|
90
|
+
true_nu=0,
|
|
91
91
|
name='My Analytical Orbit',
|
|
92
92
|
astrobodies=True)
|
|
93
93
|
```
|
|
@@ -115,7 +115,7 @@ o = spherapy.orbit.Orbit.fromAnalyticalOrbitalParam(timespan, body='Earth',
|
|
|
115
115
|
inc=0,
|
|
116
116
|
raan=0,
|
|
117
117
|
argp=0,
|
|
118
|
-
|
|
118
|
+
true_nu=0,
|
|
119
119
|
name='My Analytical Orbit',
|
|
120
120
|
astrobodies=True)
|
|
121
121
|
```
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from numpy.typing import NDArray
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
from pytest_check import check
|
|
3
5
|
import pathlib
|
|
@@ -44,8 +46,8 @@ def propagationData(pytestFillPackagedData:None) -> dict: #noqa: ARG001
|
|
|
44
46
|
data['o_ftle_astro'] = orbit.Orbit.fromPropagatedOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0, mean_nu=0, astrobodies=True)
|
|
45
47
|
|
|
46
48
|
# Analytic from orbital param
|
|
47
|
-
data['o_analytical'] = orbit.Orbit.fromAnalyticalOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0,
|
|
48
|
-
data['o_analytical_astro'] = orbit.Orbit.fromAnalyticalOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0,
|
|
49
|
+
data['o_analytical'] = orbit.Orbit.fromAnalyticalOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0, true_nu=0)
|
|
50
|
+
data['o_analytical_astro'] = orbit.Orbit.fromAnalyticalOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0, true_nu=0, astrobodies=True)
|
|
49
51
|
|
|
50
52
|
# From list of position
|
|
51
53
|
data['o_poslist'] = orbit.Orbit.fromListOfPositions(data['t'], data['pos'])
|
|
@@ -554,10 +556,47 @@ def test_analyticalValidity():
|
|
|
554
556
|
t0 = dt.datetime(2021, 1, 1, 0, 0, 1)
|
|
555
557
|
t = timespan.TimeSpan(t0, '1S', '180M')
|
|
556
558
|
a = 6378+600
|
|
557
|
-
o = orbit.Orbit.fromAnalyticalOrbitalParam(t, a=a, ecc=0, inc=45, raan=0, argp=0,
|
|
559
|
+
o = orbit.Orbit.fromAnalyticalOrbitalParam(t, a=a, ecc=0, inc=45, raan=0, argp=0, true_nu=0)
|
|
558
560
|
|
|
559
561
|
# Circular orbit should have the same semi-major for its duration
|
|
560
562
|
check.is_true(np.all(np.isclose(np.linalg.norm(o.pos, axis=1), a)))
|
|
561
563
|
# Circular orbit should have the same orbital speed for its duration
|
|
562
564
|
speed = orbital_u.calcOrbitalVel(a * 1e3, np.array((a * 1e3, 0, 0)))
|
|
563
565
|
check.is_true(np.all(np.isclose(np.linalg.norm(o.vel, axis=1), speed)))
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def test_fromAnalyticalOrbitalParams_epoch():
|
|
569
|
+
'''
|
|
570
|
+
Test that the epoch argument is set to the correct default value
|
|
571
|
+
'''
|
|
572
|
+
|
|
573
|
+
t0 = dt.datetime.fromisoformat('2026-03-20T14:46:00+00:00') # this is the vernal equinox so sun should be at raan
|
|
574
|
+
t = timespan.TimeSpan(t0, '1S', '1S')
|
|
575
|
+
o = orbit.Orbit.fromAnalyticalOrbitalParam(t, a=6378+600, ecc=0.4, inc=45, raan=0, argp=0, true_nu=0)
|
|
576
|
+
|
|
577
|
+
def unit_vector(v : NDArray) -> NDArray:
|
|
578
|
+
return v/np.linalg.norm(v)
|
|
579
|
+
|
|
580
|
+
# an orbit with raan=argp=true_nu=0 with epoch at vernal equinox should be at the sub solar point
|
|
581
|
+
check.is_true(np.all(np.isclose(unit_vector(o.pos[0]), unit_vector(o.sun_pos[0]), atol=0.01)))
|
|
582
|
+
|
|
583
|
+
def test_fromAnalyiticalOrbitalParams_epoch_timespan_sampling():
|
|
584
|
+
'''
|
|
585
|
+
test that the epoch is kept consistent over different timespans when specified
|
|
586
|
+
'''
|
|
587
|
+
|
|
588
|
+
t0 = dt.datetime.fromisoformat('2026-01-12T23:34:18+00:00')
|
|
589
|
+
|
|
590
|
+
ts1 = timespan.TimeSpan(t0, '1M', '180M')
|
|
591
|
+
ts2 = timespan.TimeSpan(t0 + dt.timedelta(minutes=1), '2M', '180M')
|
|
592
|
+
|
|
593
|
+
o1 = orbit.Orbit.fromAnalyticalOrbitalParam(ts1, a=6378+400, ecc=0.2, inc=10, raan=18, argp=76, true_nu=23, epoch=ts1[0])
|
|
594
|
+
o2 = orbit.Orbit.fromAnalyticalOrbitalParam(ts2, a=6378+400, ecc=0.2, inc=10, raan=18, argp=76, true_nu=23, epoch=ts1[0])
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
check.is_true(
|
|
598
|
+
np.all(np.isclose(
|
|
599
|
+
o1.pos[1::2,:],
|
|
600
|
+
o2.pos[:-1,:],
|
|
601
|
+
atol=0.01
|
|
602
|
+
)))
|
|
@@ -61,6 +61,81 @@ def test_init():
|
|
|
61
61
|
check.equal(first_decade[-1], dt.datetime(2006,9,8, tzinfo=dt.timezone.utc))
|
|
62
62
|
check.equal(minimum_ts[-1], t0.replace(tzinfo=dt.timezone.utc) + dt.timedelta(seconds=1))
|
|
63
63
|
|
|
64
|
+
def test_getitem():
|
|
65
|
+
t0 = dt.datetime(2013,5,17,11,45,23, tzinfo=dt.timezone.utc)
|
|
66
|
+
ts = TimeSpan(t0, '1S', '1M')
|
|
67
|
+
|
|
68
|
+
# NONE
|
|
69
|
+
item = ts[None]
|
|
70
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
71
|
+
check.is_true(np.array_equal(item, ts._timearr))
|
|
72
|
+
|
|
73
|
+
# int
|
|
74
|
+
item = ts[1]
|
|
75
|
+
check.is_true(isinstance(item, dt.datetime))
|
|
76
|
+
check.equal(item, t0 + dt.timedelta(seconds=1))
|
|
77
|
+
|
|
78
|
+
with check.raises(IndexError):
|
|
79
|
+
item = ts[70]
|
|
80
|
+
|
|
81
|
+
# slice
|
|
82
|
+
item = ts[1:4]
|
|
83
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
84
|
+
check.equal(len(item), 3)
|
|
85
|
+
for rdt, edt in zip(item, [t0 + dt.timedelta(seconds=i) for i in range(1,4)]):
|
|
86
|
+
check.equal(rdt, edt)
|
|
87
|
+
|
|
88
|
+
# check that slice correctly crop the interval to valid range
|
|
89
|
+
item = ts[30:82]
|
|
90
|
+
check.equal(len(item), 31)
|
|
91
|
+
|
|
92
|
+
# list[int]
|
|
93
|
+
item = ts[[3,7,4]]
|
|
94
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
95
|
+
check.equal(len(item), 3)
|
|
96
|
+
for rdt, edt in zip(item, [t0 + dt.timedelta(seconds=i) for i in [3,7,4]]):
|
|
97
|
+
check.equal(rdt, edt)
|
|
98
|
+
|
|
99
|
+
with check.raises(IndexError):
|
|
100
|
+
item = ts[[1,70]]
|
|
101
|
+
|
|
102
|
+
# np.ndarray
|
|
103
|
+
item = ts[np.array([3,7,4])]
|
|
104
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
105
|
+
check.equal(len(item), 3)
|
|
106
|
+
for rdt, edt in zip(item, [t0 + dt.timedelta(seconds=i) for i in [3,7,4]]):
|
|
107
|
+
check.equal(rdt, edt)
|
|
108
|
+
|
|
109
|
+
with check.raises(IndexError):
|
|
110
|
+
item = ts[np.ndarray([1,70])]
|
|
111
|
+
|
|
112
|
+
# 2-tuple
|
|
113
|
+
item = ts[(5,20)]
|
|
114
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
115
|
+
check.equal(len(item), 15)
|
|
116
|
+
for rdt, edt in zip(item, [t0 + dt.timedelta(seconds=i) for i in range(5,20)]):
|
|
117
|
+
check.equal(rdt, edt)
|
|
118
|
+
|
|
119
|
+
# check that 2-tuple correctly crop the interval to valid range
|
|
120
|
+
item = ts[(55,65)]
|
|
121
|
+
check.equal(len(item), 6)
|
|
122
|
+
|
|
123
|
+
# 3-tuple
|
|
124
|
+
item = ts[(5,20,3)]
|
|
125
|
+
check.is_true(isinstance(item, np.ndarray))
|
|
126
|
+
check.equal(len(item), 5)
|
|
127
|
+
for rdt, edt in zip(item, [t0 + dt.timedelta(seconds=i) for i in range(5,20,3)]):
|
|
128
|
+
check.equal(rdt, edt)
|
|
129
|
+
|
|
130
|
+
# check that 2-tuple correctly crop the interval to valid range
|
|
131
|
+
item = ts[(44, 62, 8)]
|
|
132
|
+
check.equal(len(item), 3)
|
|
133
|
+
|
|
134
|
+
with check.raises(TypeError):
|
|
135
|
+
item = ts[0.0]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
64
139
|
def test_len():
|
|
65
140
|
t0 = dt.datetime(2000,1,1,12,4,5)
|
|
66
141
|
|
spherapy-0.2.1/CHANGELOG.md
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
The change log should include
|
|
2
|
-
- theme of the upversion
|
|
3
|
-
- changes in API
|
|
4
|
-
- changes in model behaviour
|
|
5
|
-
|
|
6
|
-
# v0.2.1
|
|
7
|
-
- minor bug fixes to the API for pulling TLEs from Celestrak
|
|
8
|
-
|
|
9
|
-
# v0.2.0
|
|
10
|
-
- initial release to pypi
|
|
11
|
-
- timespan object
|
|
12
|
-
- orbit object
|
|
13
|
-
- orbit object creation from: TLE, propagated orbital parameters, analytical orbital parameters, list of positions
|
|
14
|
-
- TLE updater, pulls requested TLEs from spacetrack, stores all TLEs for that satellite in a saved file
|
|
15
|
-
- TLE updater falls back to celestrak if no credentials provided
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|