open-space-toolkit-astrodynamics 13.1.0__py310-none-manylinux2014_x86_64.whl → 14.0.0__py310-none-manylinux2014_x86_64.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.
- {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/METADATA +3 -3
- {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/RECORD +52 -20
- {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/WHEEL +1 -1
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-310-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.pyi +785 -0
- ostk/astrodynamics/access.pyi +588 -0
- ostk/astrodynamics/conjunction/__init__.pyi +3 -0
- ostk/astrodynamics/conjunction/message/__init__.pyi +3 -0
- ostk/astrodynamics/conjunction/message/ccsds.pyi +723 -0
- ostk/astrodynamics/converters.pyi +58 -0
- ostk/astrodynamics/data/__init__.pyi +3 -0
- ostk/astrodynamics/data/provider.pyi +22 -0
- ostk/astrodynamics/dynamics.pyi +329 -0
- ostk/astrodynamics/event_condition.pyi +580 -0
- ostk/astrodynamics/flight/__init__.pyi +547 -0
- ostk/astrodynamics/flight/profile/__init__.pyi +102 -0
- ostk/astrodynamics/flight/profile/model.pyi +176 -0
- ostk/astrodynamics/flight/system.pyi +277 -0
- ostk/astrodynamics/guidance_law.pyi +282 -0
- ostk/astrodynamics/{libopen-space-toolkit-astrodynamics.so.13 → libopen-space-toolkit-astrodynamics.so.14} +0 -0
- ostk/astrodynamics/py.typed +0 -0
- ostk/astrodynamics/pytrajectory/__init__.pyi +3 -0
- ostk/astrodynamics/pytrajectory/pystate.py +2 -4
- ostk/astrodynamics/pytrajectory/pystate.pyi +65 -0
- ostk/astrodynamics/solver.pyi +232 -0
- ostk/astrodynamics/test/access/test_generator.py +130 -59
- ostk/astrodynamics/test/access/test_visibility_criterion.py +198 -0
- ostk/astrodynamics/test/data/provider/test_off_nadir.py +58 -0
- ostk/astrodynamics/test/flight/test_maneuver.py +49 -64
- ostk/astrodynamics/test/flight/test_profile.py +4 -2
- ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +24 -11
- ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +9 -1
- ostk/astrodynamics/test/test_display.py +11 -5
- ostk/astrodynamics/test/test_viewer.py +70 -1
- ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_acceleration.py +136 -0
- ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +9 -0
- ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +21 -13
- ostk/astrodynamics/test/trajectory/test_propagator.py +21 -27
- ostk/astrodynamics/test/trajectory/test_segment.py +0 -1
- ostk/astrodynamics/trajectory/__init__.pyi +1796 -0
- ostk/astrodynamics/trajectory/orbit/__init__.pyi +361 -0
- ostk/astrodynamics/trajectory/orbit/message/__init__.pyi +3 -0
- ostk/astrodynamics/trajectory/orbit/message/spacex.pyi +273 -0
- ostk/astrodynamics/trajectory/orbit/model/__init__.pyi +517 -0
- ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi +127 -0
- ostk/astrodynamics/trajectory/orbit/model/kepler.pyi +581 -0
- ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi +333 -0
- ostk/astrodynamics/trajectory/state/__init__.pyi +406 -0
- ostk/astrodynamics/trajectory/state/coordinate_subset.pyi +223 -0
- ostk/astrodynamics/viewer.py +32 -0
- {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/zip-safe +0 -0
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
import pytest
|
4
4
|
|
5
|
+
import numpy as np
|
6
|
+
|
5
7
|
from ostk.mathematics.object import RealInterval
|
6
8
|
|
7
9
|
from ostk.physics.unit import Length
|
@@ -12,7 +14,11 @@ from ostk.physics.time import Duration
|
|
12
14
|
from ostk.physics.time import Instant
|
13
15
|
from ostk.physics.time import Interval
|
14
16
|
from ostk.physics import Environment
|
17
|
+
from ostk.physics.coordinate import Position
|
18
|
+
from ostk.physics.coordinate import Frame
|
19
|
+
from ostk.physics.coordinate.spherical import LLA
|
15
20
|
from ostk.physics.environment.object import Celestial
|
21
|
+
from ostk.physics.environment.object.celestial import Earth
|
16
22
|
|
17
23
|
from ostk.astrodynamics import Trajectory
|
18
24
|
from ostk.astrodynamics.trajectory import Orbit
|
@@ -20,6 +26,8 @@ from ostk.astrodynamics.trajectory.orbit.model import Kepler
|
|
20
26
|
from ostk.astrodynamics.trajectory.orbit.model.kepler import COE
|
21
27
|
from ostk.astrodynamics import Access
|
22
28
|
from ostk.astrodynamics.access import Generator
|
29
|
+
from ostk.astrodynamics.access import AccessTarget
|
30
|
+
from ostk.astrodynamics.access import VisibilityCriterion
|
23
31
|
|
24
32
|
|
25
33
|
@pytest.fixture
|
@@ -36,9 +44,8 @@ def earth(environment: Environment) -> Celestial:
|
|
36
44
|
def generator(environment: Environment) -> Generator:
|
37
45
|
return Generator(
|
38
46
|
environment=environment,
|
39
|
-
aer_filter=lambda aer: True,
|
40
47
|
access_filter=lambda access: True,
|
41
|
-
state_filter=
|
48
|
+
state_filter=None,
|
42
49
|
)
|
43
50
|
|
44
51
|
|
@@ -82,19 +89,106 @@ def to_trajectory(earth: Celestial) -> Trajectory:
|
|
82
89
|
)
|
83
90
|
|
84
91
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
92
|
+
@pytest.fixture
|
93
|
+
def visibility_criterion(environment: Environment) -> VisibilityCriterion:
|
94
|
+
return VisibilityCriterion.from_line_of_sight(environment=environment)
|
95
|
+
|
96
|
+
|
97
|
+
@pytest.fixture
|
98
|
+
def lla() -> LLA:
|
99
|
+
return LLA.vector([0.0, 0.0, 500.0])
|
100
|
+
|
101
|
+
|
102
|
+
@pytest.fixture
|
103
|
+
def access_target(
|
104
|
+
visibility_criterion: VisibilityCriterion,
|
105
|
+
lla: LLA,
|
106
|
+
earth: Earth,
|
107
|
+
) -> AccessTarget:
|
108
|
+
return AccessTarget.from_lla(visibility_criterion, lla, earth)
|
109
|
+
|
110
|
+
|
111
|
+
@pytest.fixture
|
112
|
+
def trajectory_target(
|
113
|
+
visibility_criterion: VisibilityCriterion,
|
114
|
+
from_trajectory: Trajectory,
|
115
|
+
) -> AccessTarget:
|
116
|
+
return AccessTarget.from_trajectory(visibility_criterion, from_trajectory)
|
117
|
+
|
118
|
+
|
119
|
+
class TestAccessTarget:
|
120
|
+
def test_constructor_success(self, access_target: AccessTarget):
|
121
|
+
assert isinstance(access_target, AccessTarget)
|
122
|
+
|
123
|
+
def test_get_type_success(self, access_target: AccessTarget):
|
124
|
+
assert access_target.get_type() == AccessTarget.Type.Fixed
|
125
|
+
|
126
|
+
def test_get_visibility_criterion_success(self, access_target: AccessTarget):
|
127
|
+
assert access_target.get_visibility_criterion() is not None
|
128
|
+
assert isinstance(access_target.get_visibility_criterion(), VisibilityCriterion)
|
129
|
+
|
130
|
+
def test_get_trajectory_success(self, access_target: AccessTarget):
|
131
|
+
assert access_target.get_trajectory() is not None
|
132
|
+
assert isinstance(access_target.get_trajectory(), Trajectory)
|
133
|
+
|
134
|
+
def test_get_position_success(self, access_target: AccessTarget):
|
135
|
+
assert access_target.get_position() is not None
|
136
|
+
assert isinstance(access_target.get_position(), Position)
|
137
|
+
|
138
|
+
def test_get_lla_success(
|
139
|
+
self,
|
140
|
+
access_target: AccessTarget,
|
141
|
+
earth: Earth,
|
142
|
+
):
|
143
|
+
assert access_target.get_lla(earth) is not None
|
144
|
+
assert isinstance(access_target.get_lla(earth), LLA)
|
145
|
+
|
146
|
+
def test_compute_r_sez_ecef_success(
|
147
|
+
self,
|
148
|
+
access_target: AccessTarget,
|
149
|
+
earth: Earth,
|
150
|
+
):
|
151
|
+
assert access_target.compute_r_sez_ecef(earth) is not None
|
152
|
+
assert isinstance(access_target.compute_r_sez_ecef(earth), np.ndarray)
|
153
|
+
|
154
|
+
def test_from_lla_success(
|
155
|
+
self,
|
156
|
+
visibility_criterion: VisibilityCriterion,
|
157
|
+
lla: LLA,
|
158
|
+
earth: Earth,
|
159
|
+
):
|
160
|
+
access_target = AccessTarget.from_lla(visibility_criterion, lla, earth)
|
161
|
+
|
162
|
+
assert access_target is not None
|
163
|
+
assert isinstance(access_target, AccessTarget)
|
164
|
+
|
165
|
+
def test_from_position_success(
|
166
|
+
self,
|
167
|
+
visibility_criterion: VisibilityCriterion,
|
168
|
+
position: Position,
|
169
|
+
):
|
170
|
+
access_target = AccessTarget.from_position(
|
171
|
+
visibility_criterion, position.in_frame(Frame.ITRF(), Instant.J2000())
|
89
172
|
)
|
90
173
|
|
91
|
-
assert
|
92
|
-
assert isinstance(
|
174
|
+
assert access_target is not None
|
175
|
+
assert isinstance(access_target, AccessTarget)
|
93
176
|
|
94
|
-
def
|
177
|
+
def test_from_trajectory_success(
|
178
|
+
self,
|
179
|
+
visibility_criterion: VisibilityCriterion,
|
180
|
+
trajectory: Trajectory,
|
181
|
+
):
|
182
|
+
access_target = AccessTarget.from_trajectory(visibility_criterion, trajectory)
|
183
|
+
|
184
|
+
assert access_target is not None
|
185
|
+
assert isinstance(access_target, AccessTarget)
|
186
|
+
|
187
|
+
|
188
|
+
class TestGenerator:
|
189
|
+
def test_constructor_success_environment(self, environment: Environment):
|
95
190
|
generator = Generator(
|
96
191
|
environment=environment,
|
97
|
-
aer_filter=lambda aer: True,
|
98
192
|
)
|
99
193
|
|
100
194
|
assert generator is not None
|
@@ -142,18 +236,17 @@ class TestGenerator:
|
|
142
236
|
def test_getters_success(self, generator: Generator):
|
143
237
|
assert generator.get_step() == Duration.minutes(1.0)
|
144
238
|
assert generator.get_tolerance() == Duration.microseconds(1.0)
|
145
|
-
assert generator.get_aer_filter() is not None
|
146
239
|
assert generator.get_access_filter() is not None
|
147
|
-
assert generator.get_state_filter() is
|
240
|
+
assert generator.get_state_filter() is None
|
148
241
|
|
149
242
|
def test_get_condition_function_success(
|
150
243
|
self,
|
151
244
|
generator: Generator,
|
152
|
-
|
245
|
+
trajectory_target: AccessTarget,
|
153
246
|
to_trajectory: Trajectory,
|
154
247
|
):
|
155
248
|
condition_function = generator.get_condition_function(
|
156
|
-
|
249
|
+
access_target=trajectory_target,
|
157
250
|
to_trajectory=to_trajectory,
|
158
251
|
)
|
159
252
|
|
@@ -168,7 +261,7 @@ class TestGenerator:
|
|
168
261
|
def test_compute_accesses_success(
|
169
262
|
self,
|
170
263
|
generator: Generator,
|
171
|
-
|
264
|
+
trajectory_target: AccessTarget,
|
172
265
|
to_trajectory: Trajectory,
|
173
266
|
):
|
174
267
|
accesses = generator.compute_accesses(
|
@@ -176,7 +269,7 @@ class TestGenerator:
|
|
176
269
|
Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
|
177
270
|
Instant.date_time(DateTime(2018, 1, 1, 2, 0, 0), Scale.UTC),
|
178
271
|
),
|
179
|
-
|
272
|
+
access_target=trajectory_target,
|
180
273
|
to_trajectory=to_trajectory,
|
181
274
|
)
|
182
275
|
|
@@ -185,15 +278,33 @@ class TestGenerator:
|
|
185
278
|
assert accesses[0] is not None
|
186
279
|
assert isinstance(accesses[0], Access)
|
187
280
|
|
281
|
+
def test_compute_accesses_multiple_targets_success(
|
282
|
+
self,
|
283
|
+
generator: Generator,
|
284
|
+
trajectory_target: AccessTarget,
|
285
|
+
to_trajectory: Trajectory,
|
286
|
+
):
|
287
|
+
accesses = generator.compute_accesses(
|
288
|
+
interval=Interval.closed(
|
289
|
+
Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
|
290
|
+
Instant.date_time(DateTime(2018, 1, 1, 2, 0, 0), Scale.UTC),
|
291
|
+
),
|
292
|
+
access_targets=[trajectory_target],
|
293
|
+
to_trajectory=to_trajectory,
|
294
|
+
)
|
295
|
+
|
296
|
+
assert accesses is not None
|
297
|
+
assert isinstance(accesses, list)
|
298
|
+
assert accesses[0] is not None
|
299
|
+
assert isinstance(accesses[0], list)
|
300
|
+
assert isinstance(accesses[0][0], Access)
|
301
|
+
|
188
302
|
def test_set_step_success(self, generator: Generator):
|
189
303
|
generator.set_step(Duration.seconds(1.0))
|
190
304
|
|
191
305
|
def test_set_tolerance_success(self, generator: Generator):
|
192
306
|
generator.set_tolerance(Duration.seconds(1.0))
|
193
307
|
|
194
|
-
def test_set_aer_filter_success(self, generator: Generator):
|
195
|
-
generator.set_aer_filter(aer_filter=lambda aer: True)
|
196
|
-
|
197
308
|
def test_set_access_filter_success(self, generator: Generator):
|
198
309
|
generator.set_access_filter(access_filter=lambda access: True)
|
199
310
|
|
@@ -206,43 +317,3 @@ class TestGenerator:
|
|
206
317
|
assert generator is not None
|
207
318
|
assert isinstance(generator, Generator)
|
208
319
|
assert generator.is_defined() is False
|
209
|
-
|
210
|
-
def test_aer_ranges_success(self, environment: Environment):
|
211
|
-
# Construct arbitrary AER ranges
|
212
|
-
azimuth_interval = RealInterval.closed(0.0, 360.0)
|
213
|
-
elevation_interval = RealInterval.closed(0.0, 90.0)
|
214
|
-
range_interval = RealInterval.closed(0.0, 7000e3)
|
215
|
-
|
216
|
-
generator = Generator.aer_ranges(
|
217
|
-
azimuth_range=azimuth_interval,
|
218
|
-
elevation_range=elevation_interval,
|
219
|
-
range_range=range_interval,
|
220
|
-
environment=environment,
|
221
|
-
)
|
222
|
-
|
223
|
-
assert generator is not None
|
224
|
-
assert isinstance(generator, Generator)
|
225
|
-
assert generator.is_defined()
|
226
|
-
|
227
|
-
def test_aer_mask_success(self, environment: Environment):
|
228
|
-
# Construct arbitrary anAzimuthElevationMask using python dict
|
229
|
-
an_azimuth_elevation_mask = {
|
230
|
-
0.0: 30.0,
|
231
|
-
90.0: 60.0,
|
232
|
-
180.0: 60.0,
|
233
|
-
270.0: 30.0,
|
234
|
-
359.0: 30.0,
|
235
|
-
}
|
236
|
-
|
237
|
-
# Construct arbitrary aRangerange
|
238
|
-
a_range_range = RealInterval(0.0, 10e4, RealInterval.Type.Closed)
|
239
|
-
|
240
|
-
generator = Generator.aer_mask(
|
241
|
-
azimuth_elevation_mask=an_azimuth_elevation_mask,
|
242
|
-
range_range=a_range_range,
|
243
|
-
environment=environment,
|
244
|
-
)
|
245
|
-
|
246
|
-
assert generator is not None
|
247
|
-
assert isinstance(generator, Generator)
|
248
|
-
assert generator.is_defined()
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
from ostk.mathematics.object import RealInterval
|
7
|
+
|
8
|
+
from ostk.physics import Environment
|
9
|
+
from ostk.physics.time import Instant
|
10
|
+
from ostk.physics.time import DateTime
|
11
|
+
from ostk.physics.time import Scale
|
12
|
+
|
13
|
+
from ostk.astrodynamics.access import VisibilityCriterion
|
14
|
+
|
15
|
+
|
16
|
+
@pytest.fixture()
|
17
|
+
def instant() -> Instant:
|
18
|
+
return Instant.date_time(DateTime(2023, 1, 1, 0, 0, 0), Scale.UTC)
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.fixture
|
22
|
+
def environment() -> Environment:
|
23
|
+
return Environment.default()
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture
|
27
|
+
def azimuth_interval() -> RealInterval:
|
28
|
+
return RealInterval.closed(0.0, 90.0)
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.fixture
|
32
|
+
def elevation_interval() -> RealInterval:
|
33
|
+
return RealInterval.closed(0.0, 45.0)
|
34
|
+
|
35
|
+
|
36
|
+
@pytest.fixture
|
37
|
+
def range_interval() -> RealInterval:
|
38
|
+
return RealInterval.closed(0.0, 1e7)
|
39
|
+
|
40
|
+
|
41
|
+
@pytest.fixture
|
42
|
+
def aer_mask() -> dict[float, float]:
|
43
|
+
return {
|
44
|
+
0.0: 0.0,
|
45
|
+
90.0: 45.0,
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
class TestVisibilityCriterion:
|
50
|
+
def test_aer_interval_constructor(
|
51
|
+
self,
|
52
|
+
azimuth_interval: RealInterval,
|
53
|
+
elevation_interval: RealInterval,
|
54
|
+
range_interval: RealInterval,
|
55
|
+
):
|
56
|
+
aer_interval = VisibilityCriterion.AERInterval(
|
57
|
+
azimuth_interval,
|
58
|
+
elevation_interval,
|
59
|
+
range_interval,
|
60
|
+
)
|
61
|
+
assert aer_interval is not None
|
62
|
+
assert isinstance(aer_interval, VisibilityCriterion.AERInterval)
|
63
|
+
|
64
|
+
def test_aer_mask_constructor(
|
65
|
+
self,
|
66
|
+
aer_mask: dict[float, float],
|
67
|
+
range_interval: RealInterval,
|
68
|
+
):
|
69
|
+
aer_mask_criterion = VisibilityCriterion.AERMask(aer_mask, range_interval)
|
70
|
+
assert aer_mask_criterion is not None
|
71
|
+
assert isinstance(aer_mask_criterion, VisibilityCriterion.AERMask)
|
72
|
+
|
73
|
+
def test_line_of_sight_constructor(
|
74
|
+
self,
|
75
|
+
environment: Environment,
|
76
|
+
):
|
77
|
+
line_of_sight = VisibilityCriterion.LineOfSight(environment)
|
78
|
+
assert line_of_sight is not None
|
79
|
+
assert isinstance(line_of_sight, VisibilityCriterion.LineOfSight)
|
80
|
+
|
81
|
+
def test_elevation_interval_constructor(
|
82
|
+
self,
|
83
|
+
elevation_interval: RealInterval,
|
84
|
+
):
|
85
|
+
elevation_criterion = VisibilityCriterion.ElevationInterval(elevation_interval)
|
86
|
+
assert elevation_criterion is not None
|
87
|
+
assert isinstance(elevation_criterion, VisibilityCriterion.ElevationInterval)
|
88
|
+
|
89
|
+
def test_visibility_criterion_from_aer_interval(
|
90
|
+
self,
|
91
|
+
azimuth_interval: RealInterval,
|
92
|
+
elevation_interval: RealInterval,
|
93
|
+
range_interval: RealInterval,
|
94
|
+
):
|
95
|
+
criterion = VisibilityCriterion.from_aer_interval(
|
96
|
+
azimuth_interval, elevation_interval, range_interval
|
97
|
+
)
|
98
|
+
assert criterion is not None
|
99
|
+
assert isinstance(criterion, VisibilityCriterion)
|
100
|
+
assert criterion.is_aer_interval()
|
101
|
+
|
102
|
+
def test_visibility_criterion_from_aer_mask(
|
103
|
+
self,
|
104
|
+
aer_mask: dict[float, float],
|
105
|
+
range_interval: RealInterval,
|
106
|
+
):
|
107
|
+
criterion = VisibilityCriterion.from_aer_mask(aer_mask, range_interval)
|
108
|
+
assert criterion is not None
|
109
|
+
assert isinstance(criterion, VisibilityCriterion)
|
110
|
+
assert criterion.is_aer_mask()
|
111
|
+
|
112
|
+
def test_visibility_criterion_from_line_of_sight(
|
113
|
+
self,
|
114
|
+
environment: Environment,
|
115
|
+
):
|
116
|
+
criterion = VisibilityCriterion.from_line_of_sight(environment)
|
117
|
+
assert criterion is not None
|
118
|
+
assert isinstance(criterion, VisibilityCriterion)
|
119
|
+
assert criterion.is_line_of_sight()
|
120
|
+
|
121
|
+
def test_visibility_criterion_from_elevation_interval(
|
122
|
+
self, elevation_interval: RealInterval
|
123
|
+
):
|
124
|
+
criterion = VisibilityCriterion.from_elevation_interval(elevation_interval)
|
125
|
+
assert criterion is not None
|
126
|
+
assert isinstance(criterion, VisibilityCriterion)
|
127
|
+
assert criterion.is_elevation_interval()
|
128
|
+
|
129
|
+
def test_aer_interval_is_satisfied(
|
130
|
+
self,
|
131
|
+
azimuth_interval: RealInterval,
|
132
|
+
elevation_interval: RealInterval,
|
133
|
+
range_interval: RealInterval,
|
134
|
+
):
|
135
|
+
aer_interval = VisibilityCriterion.AERInterval(
|
136
|
+
azimuth_interval, elevation_interval, range_interval
|
137
|
+
)
|
138
|
+
assert (
|
139
|
+
aer_interval.is_satisfied(azimuth=np.pi / 4, elevation=np.pi / 8, range=5e6)
|
140
|
+
is True
|
141
|
+
)
|
142
|
+
|
143
|
+
assert (
|
144
|
+
aer_interval.is_satisfied(azimuth=np.pi, elevation=np.pi / 2, range=1e8)
|
145
|
+
is False
|
146
|
+
)
|
147
|
+
|
148
|
+
def test_elevation_interval_is_satisfied(
|
149
|
+
self,
|
150
|
+
elevation_interval: RealInterval,
|
151
|
+
):
|
152
|
+
elevation_criterion = VisibilityCriterion.ElevationInterval(elevation_interval)
|
153
|
+
elevation_valid = np.pi / 8 # 22.5 degrees
|
154
|
+
assert elevation_criterion.is_satisfied(elevation_valid) is True
|
155
|
+
|
156
|
+
elevation_invalid = np.pi / 2 # 90 degrees
|
157
|
+
assert elevation_criterion.is_satisfied(elevation_invalid) is False
|
158
|
+
|
159
|
+
def test_line_of_sight_is_satisfied(
|
160
|
+
self,
|
161
|
+
environment: Environment,
|
162
|
+
):
|
163
|
+
line_of_sight = VisibilityCriterion.LineOfSight(environment)
|
164
|
+
instant = Instant.now()
|
165
|
+
|
166
|
+
from_position = np.array([7000e3, 0.0, 0.0])
|
167
|
+
to_position = np.array([7005e3, 0.0, 0.0])
|
168
|
+
|
169
|
+
assert line_of_sight.is_satisfied(instant, from_position, to_position) is True
|
170
|
+
|
171
|
+
def test_visibility_criterion_type_checks(
|
172
|
+
self,
|
173
|
+
elevation_interval: RealInterval,
|
174
|
+
environment: Environment,
|
175
|
+
):
|
176
|
+
criterion = VisibilityCriterion.from_elevation_interval(elevation_interval)
|
177
|
+
assert criterion.is_elevation_interval() is True
|
178
|
+
assert criterion.is_aer_interval() is False
|
179
|
+
assert criterion.is_line_of_sight() is False
|
180
|
+
|
181
|
+
criterion = VisibilityCriterion.from_line_of_sight(environment)
|
182
|
+
assert criterion.is_line_of_sight() is True
|
183
|
+
|
184
|
+
def test_visibility_criterion_cast(
|
185
|
+
self,
|
186
|
+
azimuth_interval: RealInterval,
|
187
|
+
elevation_interval: RealInterval,
|
188
|
+
range_interval: RealInterval,
|
189
|
+
):
|
190
|
+
criterion = VisibilityCriterion.from_aer_interval(
|
191
|
+
azimuth_interval, elevation_interval, range_interval
|
192
|
+
)
|
193
|
+
aer_interval = criterion.as_aer_interval()
|
194
|
+
assert aer_interval is not None
|
195
|
+
assert isinstance(aer_interval, VisibilityCriterion.AERInterval)
|
196
|
+
|
197
|
+
with pytest.raises(ValueError):
|
198
|
+
criterion.as_line_of_sight()
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
from ostk.physics.time import DateTime
|
6
|
+
from ostk.physics.time import Instant
|
7
|
+
from ostk.physics.time import Scale
|
8
|
+
from ostk.physics.unit import Angle
|
9
|
+
from ostk.physics.coordinate import Frame
|
10
|
+
from ostk.physics.coordinate import Position
|
11
|
+
from ostk.physics.coordinate import Velocity
|
12
|
+
|
13
|
+
from ostk.astrodynamics.trajectory import State
|
14
|
+
from ostk.astrodynamics.data.provider import compute_off_nadir_angles
|
15
|
+
|
16
|
+
|
17
|
+
@pytest.fixture
|
18
|
+
def target_position() -> Position:
|
19
|
+
return Position.meters([0.0, 0.0, 0.0], Frame.GCRF())
|
20
|
+
|
21
|
+
|
22
|
+
@pytest.fixture
|
23
|
+
def state() -> State:
|
24
|
+
instant = Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC)
|
25
|
+
position = Position.meters([0.0, 0.0, 0.0], Frame.GCRF())
|
26
|
+
velocity = Velocity.meters_per_second([0.0, 0.0, 0.0], Frame.GCRF())
|
27
|
+
|
28
|
+
return State(instant=instant, position=position, velocity=velocity)
|
29
|
+
|
30
|
+
|
31
|
+
class TestOffNadirAngles:
|
32
|
+
|
33
|
+
def test_compute_off_nadir_angles(
|
34
|
+
self,
|
35
|
+
state: State,
|
36
|
+
target_position: Position,
|
37
|
+
):
|
38
|
+
along_track: Angle
|
39
|
+
cross_track: Angle
|
40
|
+
off_nadir: Angle
|
41
|
+
along_track, cross_track, off_nadir = compute_off_nadir_angles(
|
42
|
+
state, target_position
|
43
|
+
)
|
44
|
+
|
45
|
+
assert along_track is not None
|
46
|
+
assert cross_track is not None
|
47
|
+
assert off_nadir is not None
|
48
|
+
|
49
|
+
def test_compute_off_nadir_angles_undefined(
|
50
|
+
self,
|
51
|
+
state: State,
|
52
|
+
target_position: Position,
|
53
|
+
):
|
54
|
+
with pytest.raises(RuntimeError):
|
55
|
+
compute_off_nadir_angles(State.undefined(), target_position)
|
56
|
+
|
57
|
+
with pytest.raises(RuntimeError):
|
58
|
+
compute_off_nadir_angles(state, Position.undefined())
|
@@ -12,24 +12,21 @@ from ostk.physics.coordinate import Frame
|
|
12
12
|
from ostk.physics.unit import Mass
|
13
13
|
|
14
14
|
from ostk.astrodynamics.flight import Maneuver
|
15
|
+
from ostk.astrodynamics.trajectory import State
|
15
16
|
from ostk.astrodynamics.dynamics import Tabulated as TabulatedDynamics
|
16
17
|
from ostk.astrodynamics.trajectory.state import CoordinateSubset
|
17
|
-
|
18
|
-
|
19
|
-
from
|
20
|
-
instants as tabulated_instants,
|
21
|
-
contribution_profile as tabulated_contribution_profile,
|
22
|
-
coordinate_subsets as tabulated_coordinate_subsets,
|
23
|
-
)
|
18
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
|
19
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
|
20
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianAcceleration
|
24
21
|
|
25
22
|
|
26
23
|
@pytest.fixture
|
27
24
|
def instants() -> list[Instant]:
|
28
25
|
return [
|
29
26
|
Instant.J2000(),
|
30
|
-
Instant.J2000() + Duration.seconds(
|
31
|
-
Instant.J2000() + Duration.seconds(
|
32
|
-
Instant.J2000() + Duration.seconds(
|
27
|
+
Instant.J2000() + Duration.seconds(30.0),
|
28
|
+
Instant.J2000() + Duration.seconds(35.0),
|
29
|
+
Instant.J2000() + Duration.seconds(37.0),
|
33
30
|
]
|
34
31
|
|
35
32
|
|
@@ -54,13 +51,43 @@ def mass_flow_rate_profile() -> list[float]:
|
|
54
51
|
|
55
52
|
|
56
53
|
@pytest.fixture
|
57
|
-
def
|
54
|
+
def coordinate_subsets() -> list[CoordinateSubset]:
|
55
|
+
return [
|
56
|
+
CartesianPosition.default(),
|
57
|
+
CartesianVelocity.default(),
|
58
|
+
CartesianAcceleration.thrust_acceleration(),
|
59
|
+
CoordinateSubset.mass_flow_rate(),
|
60
|
+
]
|
61
|
+
|
62
|
+
|
63
|
+
@pytest.fixture
|
64
|
+
def states(
|
58
65
|
instants: list[Instant],
|
59
66
|
acceleration_profile: list[np.ndarray],
|
60
|
-
frame: Frame,
|
61
67
|
mass_flow_rate_profile: list[float],
|
68
|
+
frame: Frame,
|
69
|
+
coordinate_subsets: list[CoordinateSubset],
|
70
|
+
) -> list[State]:
|
71
|
+
states = []
|
72
|
+
for instant, acceleration, mass_flow_rate in zip(
|
73
|
+
instants, acceleration_profile, mass_flow_rate_profile
|
74
|
+
):
|
75
|
+
states.append(
|
76
|
+
State(
|
77
|
+
instant,
|
78
|
+
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, *acceleration, mass_flow_rate],
|
79
|
+
frame,
|
80
|
+
coordinate_subsets,
|
81
|
+
)
|
82
|
+
)
|
83
|
+
return states
|
84
|
+
|
85
|
+
|
86
|
+
@pytest.fixture
|
87
|
+
def maneuver(
|
88
|
+
states: list[State],
|
62
89
|
) -> Maneuver:
|
63
|
-
return Maneuver(
|
90
|
+
return Maneuver(states)
|
64
91
|
|
65
92
|
|
66
93
|
@pytest.fixture
|
@@ -97,35 +124,15 @@ class TestManeuver:
|
|
97
124
|
def test_getters(
|
98
125
|
self,
|
99
126
|
maneuver: Maneuver,
|
100
|
-
|
101
|
-
acceleration_profile: list[np.ndarray],
|
102
|
-
mass_flow_rate_profile: list[float],
|
127
|
+
states: list[State],
|
103
128
|
):
|
104
129
|
assert maneuver.is_defined()
|
105
130
|
|
106
|
-
assert maneuver.
|
131
|
+
assert maneuver.get_states() == states
|
107
132
|
|
108
|
-
|
109
|
-
|
110
|
-
assert np.array_equal(
|
111
|
-
computed_acceleration_profile_default_frame[i], acceleration_profile[i]
|
112
|
-
)
|
113
|
-
|
114
|
-
computed_acceleration_profile_non_default_frame = (
|
115
|
-
maneuver.get_acceleration_profile(frame=Frame.ITRF())
|
133
|
+
assert maneuver.get_interval() == Interval.closed(
|
134
|
+
states[0].get_instant(), states[-1].get_instant()
|
116
135
|
)
|
117
|
-
for i in range(len(instants)):
|
118
|
-
assert (
|
119
|
-
np.array_equal(
|
120
|
-
computed_acceleration_profile_non_default_frame[i],
|
121
|
-
acceleration_profile[i],
|
122
|
-
)
|
123
|
-
== False
|
124
|
-
)
|
125
|
-
|
126
|
-
assert maneuver.get_mass_flow_rate_profile() == mass_flow_rate_profile
|
127
|
-
|
128
|
-
assert maneuver.get_interval() == Interval.closed(instants[0], instants[-1])
|
129
136
|
|
130
137
|
def test_calculators(
|
131
138
|
self,
|
@@ -159,7 +166,9 @@ class TestManeuver:
|
|
159
166
|
)
|
160
167
|
|
161
168
|
assert tabulated_dynamics.is_defined()
|
162
|
-
assert tabulated_dynamics.access_instants() ==
|
169
|
+
assert tabulated_dynamics.access_instants() == [
|
170
|
+
state.get_instant() for state in maneuver.get_states()
|
171
|
+
]
|
163
172
|
|
164
173
|
contribution_profile: np.ndarray = (
|
165
174
|
tabulated_dynamics.access_contribution_profile()
|
@@ -175,38 +184,14 @@ class TestManeuver:
|
|
175
184
|
|
176
185
|
assert tabulated_dynamics.access_frame() == frame
|
177
186
|
|
178
|
-
def test_from_tabulated_dynamics(
|
179
|
-
self,
|
180
|
-
tabulated_dynamics: TabulatedDynamics,
|
181
|
-
):
|
182
|
-
maneuver = Maneuver.tabulated_dynamics(tabulated_dynamics)
|
183
|
-
|
184
|
-
assert maneuver.is_defined()
|
185
|
-
assert maneuver.get_instants() == tabulated_dynamics.access_instants()
|
186
|
-
assert (
|
187
|
-
len(maneuver.get_acceleration_profile())
|
188
|
-
== tabulated_dynamics.access_contribution_profile().shape[0]
|
189
|
-
)
|
190
|
-
assert (
|
191
|
-
len(maneuver.get_mass_flow_rate_profile())
|
192
|
-
== tabulated_dynamics.access_contribution_profile().shape[0]
|
193
|
-
)
|
194
|
-
|
195
187
|
def test_from_constant_mass_flow_rate(
|
196
188
|
self,
|
197
|
-
|
198
|
-
acceleration_profile: list[np.ndarray],
|
199
|
-
frame: Frame,
|
189
|
+
states: list[State],
|
200
190
|
):
|
201
191
|
mass_flow_rate: float = -1.0e-5
|
202
192
|
maneuver = Maneuver.constant_mass_flow_rate_profile(
|
203
|
-
|
204
|
-
acceleration_profile=acceleration_profile,
|
205
|
-
frame=frame,
|
193
|
+
states=states,
|
206
194
|
mass_flow_rate=mass_flow_rate,
|
207
195
|
)
|
208
196
|
|
209
197
|
assert maneuver.is_defined()
|
210
|
-
assert maneuver.get_mass_flow_rate_profile() == [
|
211
|
-
mass_flow_rate for _ in range(len(instants))
|
212
|
-
]
|