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.
Files changed (57) hide show
  1. spherapy-0.3.0/CHANGELOG.md +21 -0
  2. {spherapy-0.2.1/spherapy.egg-info → spherapy-0.3.0}/PKG-INFO +3 -3
  3. {spherapy-0.2.1 → spherapy-0.3.0}/README.md +2 -2
  4. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/_version.py +3 -3
  5. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/orbit.py +26 -8
  6. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/timespan.py +12 -2
  7. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/epoch_u.py +63 -0
  8. {spherapy-0.2.1 → spherapy-0.3.0/spherapy.egg-info}/PKG-INFO +3 -3
  9. {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/test_orbit.py +42 -3
  10. {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/test_timespan.py +75 -0
  11. spherapy-0.2.1/CHANGELOG.md +0 -15
  12. {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  13. {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  14. {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  15. {spherapy-0.2.1 → spherapy-0.3.0}/.github/ISSUE_TEMPLATE/question.yaml +0 -0
  16. {spherapy-0.2.1 → spherapy-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  17. {spherapy-0.2.1 → spherapy-0.3.0}/.github/workflows/pr.yml +0 -0
  18. {spherapy-0.2.1 → spherapy-0.3.0}/.github/workflows/release.yml +0 -0
  19. {spherapy-0.2.1 → spherapy-0.3.0}/.gitignore +0 -0
  20. {spherapy-0.2.1 → spherapy-0.3.0}/.pre-commit-config.yaml +0 -0
  21. {spherapy-0.2.1 → spherapy-0.3.0}/CONTRIBUTING.md +0 -0
  22. {spherapy-0.2.1 → spherapy-0.3.0}/LICENSE.md +0 -0
  23. {spherapy-0.2.1 → spherapy-0.3.0}/docs/README.md +0 -0
  24. {spherapy-0.2.1 → spherapy-0.3.0}/docs/orbit.md +0 -0
  25. {spherapy-0.2.1 → spherapy-0.3.0}/docs/timespan.md +0 -0
  26. {spherapy-0.2.1 → spherapy-0.3.0}/docs/updater.md +0 -0
  27. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.celestrak.md +0 -0
  28. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.constants.md +0 -0
  29. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.credentials.md +0 -0
  30. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.elements_u.md +0 -0
  31. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.epoch_u.md +0 -0
  32. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.exceptions.md +0 -0
  33. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.md +0 -0
  34. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.orbital_u.md +0 -0
  35. {spherapy-0.2.1 → spherapy-0.3.0}/docs/util.spacetrack.md +0 -0
  36. {spherapy-0.2.1 → spherapy-0.3.0}/pyproject.toml +0 -0
  37. {spherapy-0.2.1 → spherapy-0.3.0}/setup.cfg +0 -0
  38. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/__init__.py +0 -0
  39. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/__main__.py +0 -0
  40. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/data/TLEs/25544.tle +0 -0
  41. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/updater.py +0 -0
  42. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/__init__.py +0 -0
  43. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/celestrak.py +0 -0
  44. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/constants.py +0 -0
  45. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/credentials.py +0 -0
  46. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/elements_u.py +0 -0
  47. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/exceptions.py +0 -0
  48. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/orbital_u.py +0 -0
  49. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy/util/spacetrack.py +0 -0
  50. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.conf +0 -0
  51. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/SOURCES.txt +0 -0
  52. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/dependency_links.txt +0 -0
  53. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/entry_points.txt +0 -0
  54. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/requires.txt +0 -0
  55. {spherapy-0.2.1 → spherapy-0.3.0}/spherapy.egg-info/top_level.txt +0 -0
  56. {spherapy-0.2.1 → spherapy-0.3.0}/tests/spherapy/conftest.py +0 -0
  57. {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.2.1
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
- mean_nu=0,
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
- mean_nu=0,
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
- mean_nu=0,
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
- mean_nu=0,
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.2.1'
32
- __version_tuple__ = version_tuple = (0, 2, 1)
31
+ __version__ = version = '0.3.0'
32
+ __version_tuple__ = version_tuple = (0, 3, 0)
33
33
 
34
- __commit_id__ = commit_id = 'ge103d2055'
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
- mean_nu:float=0,
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
- mean_nu: [Optional] mean anomaly in degrees
655
- Default is 0, which represents an orbit that is beginning at periapsis
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 mean_nu > 360 or mean_nu < 0: #noqa: PLR2004
709
- logger.error("Mean anomaly, %s, is out of range, should be 0 < mean_nu < 360", mean_nu)
710
- raise exceptions.OutOfRangeError(f"Mean anomaly, {mean_nu}, is out of range, "
711
- f"should be 0 < mean_nu < 360")
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
- mean_nu * astropy_units.one * astropy_units.deg)
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[[idx]]
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.2.1
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
- mean_nu=0,
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
- mean_nu=0,
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, mean_nu=0)
48
- data['o_analytical_astro'] = orbit.Orbit.fromAnalyticalOrbitalParam(data['t'], a=(6378 + 600), ecc=0, inc=45, raan=0, argp=0, mean_nu=0, astrobodies=True)
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, mean_nu=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
 
@@ -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