open-space-toolkit-astrodynamics 13.1.0__py312-none-manylinux2014_x86_64.whl → 14.0.0__py312-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.
Files changed (52) hide show
  1. {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/METADATA +3 -3
  2. {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/RECORD +52 -20
  3. {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/WHEEL +1 -1
  4. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-x86_64-linux-gnu.so +0 -0
  5. ostk/astrodynamics/__init__.pyi +785 -0
  6. ostk/astrodynamics/access.pyi +588 -0
  7. ostk/astrodynamics/conjunction/__init__.pyi +3 -0
  8. ostk/astrodynamics/conjunction/message/__init__.pyi +3 -0
  9. ostk/astrodynamics/conjunction/message/ccsds.pyi +723 -0
  10. ostk/astrodynamics/converters.pyi +58 -0
  11. ostk/astrodynamics/data/__init__.pyi +3 -0
  12. ostk/astrodynamics/data/provider.pyi +22 -0
  13. ostk/astrodynamics/dynamics.pyi +329 -0
  14. ostk/astrodynamics/event_condition.pyi +580 -0
  15. ostk/astrodynamics/flight/__init__.pyi +547 -0
  16. ostk/astrodynamics/flight/profile/__init__.pyi +102 -0
  17. ostk/astrodynamics/flight/profile/model.pyi +176 -0
  18. ostk/astrodynamics/flight/system.pyi +277 -0
  19. ostk/astrodynamics/guidance_law.pyi +282 -0
  20. ostk/astrodynamics/{libopen-space-toolkit-astrodynamics.so.13 → libopen-space-toolkit-astrodynamics.so.14} +0 -0
  21. ostk/astrodynamics/py.typed +0 -0
  22. ostk/astrodynamics/pytrajectory/__init__.pyi +3 -0
  23. ostk/astrodynamics/pytrajectory/pystate.py +2 -4
  24. ostk/astrodynamics/pytrajectory/pystate.pyi +65 -0
  25. ostk/astrodynamics/solver.pyi +232 -0
  26. ostk/astrodynamics/test/access/test_generator.py +130 -59
  27. ostk/astrodynamics/test/access/test_visibility_criterion.py +198 -0
  28. ostk/astrodynamics/test/data/provider/test_off_nadir.py +58 -0
  29. ostk/astrodynamics/test/flight/test_maneuver.py +49 -64
  30. ostk/astrodynamics/test/flight/test_profile.py +4 -2
  31. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +24 -11
  32. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +9 -1
  33. ostk/astrodynamics/test/test_display.py +11 -5
  34. ostk/astrodynamics/test/test_viewer.py +70 -1
  35. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_acceleration.py +136 -0
  36. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +9 -0
  37. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +21 -13
  38. ostk/astrodynamics/test/trajectory/test_propagator.py +21 -27
  39. ostk/astrodynamics/test/trajectory/test_segment.py +0 -1
  40. ostk/astrodynamics/trajectory/__init__.pyi +1796 -0
  41. ostk/astrodynamics/trajectory/orbit/__init__.pyi +361 -0
  42. ostk/astrodynamics/trajectory/orbit/message/__init__.pyi +3 -0
  43. ostk/astrodynamics/trajectory/orbit/message/spacex.pyi +273 -0
  44. ostk/astrodynamics/trajectory/orbit/model/__init__.pyi +517 -0
  45. ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi +127 -0
  46. ostk/astrodynamics/trajectory/orbit/model/kepler.pyi +581 -0
  47. ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi +333 -0
  48. ostk/astrodynamics/trajectory/state/__init__.pyi +406 -0
  49. ostk/astrodynamics/trajectory/state/coordinate_subset.pyi +223 -0
  50. ostk/astrodynamics/viewer.py +32 -0
  51. {open_space_toolkit_astrodynamics-13.1.0.dist-info → open_space_toolkit_astrodynamics-14.0.0.dist-info}/top_level.txt +0 -0
  52. {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=lambda state_1, state_2: True,
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
- class TestGenerator:
86
- def test_constructor_success_environment(self, environment: Environment):
87
- generator = Generator(
88
- environment=environment,
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 generator is not None
92
- assert isinstance(generator, Generator)
174
+ assert access_target is not None
175
+ assert isinstance(access_target, AccessTarget)
93
176
 
94
- def test_constructor_success_environment_aer_filter(self, environment: Environment):
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 not None
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
- from_trajectory: Trajectory,
245
+ trajectory_target: AccessTarget,
153
246
  to_trajectory: Trajectory,
154
247
  ):
155
248
  condition_function = generator.get_condition_function(
156
- from_trajectory=from_trajectory,
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
- from_trajectory: Trajectory,
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
- from_trajectory=from_trajectory,
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 ..dynamics.test_tabulated import (
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(1.0),
31
- Instant.J2000() + Duration.seconds(5.0),
32
- Instant.J2000() + Duration.seconds(7.0),
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 maneuver(
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(instants, acceleration_profile, frame, mass_flow_rate_profile)
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
- instants: list[Instant],
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.get_instants() == instants
131
+ assert maneuver.get_states() == states
107
132
 
108
- computed_acceleration_profile_default_frame = maneuver.get_acceleration_profile()
109
- for i in range(len(instants)):
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() == maneuver.get_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
- instants: list[Instant],
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
- instants=instants,
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
- ]