open-space-toolkit-astrodynamics 12.2.1__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.
- open_space_toolkit_astrodynamics-12.2.1.dist-info/METADATA +30 -0
- open_space_toolkit_astrodynamics-12.2.1.dist-info/RECORD +100 -0
- open_space_toolkit_astrodynamics-12.2.1.dist-info/WHEEL +5 -0
- open_space_toolkit_astrodynamics-12.2.1.dist-info/top_level.txt +1 -0
- open_space_toolkit_astrodynamics-12.2.1.dist-info/zip-safe +1 -0
- ostk/__init__.py +1 -0
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.py +11 -0
- ostk/astrodynamics/converters.py +128 -0
- ostk/astrodynamics/dataframe.py +477 -0
- ostk/astrodynamics/display.py +220 -0
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.12 +0 -0
- ostk/astrodynamics/pytrajectory/__init__.py +1 -0
- ostk/astrodynamics/pytrajectory/pystate.py +220 -0
- ostk/astrodynamics/test/__init__.py +1 -0
- ostk/astrodynamics/test/access/__init__.py +1 -0
- ostk/astrodynamics/test/access/test_generator.py +248 -0
- ostk/astrodynamics/test/conftest.py +119 -0
- ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
- ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
- ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
- ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
- ostk/astrodynamics/test/dynamics/__init__.py +1 -0
- ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
- ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
- ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
- ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
- ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
- ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
- ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
- ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
- ostk/astrodynamics/test/dynamics/test_thruster.py +142 -0
- ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
- ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
- ostk/astrodynamics/test/event_condition/test_coe_condition.py +87 -0
- ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
- ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
- ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
- ostk/astrodynamics/test/flight/__init__.py +1 -0
- ostk/astrodynamics/test/flight/system/__init__.py +1 -0
- ostk/astrodynamics/test/flight/system/test_propulsion_system.py +73 -0
- ostk/astrodynamics/test/flight/system/test_satellite_system.py +91 -0
- ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
- ostk/astrodynamics/test/flight/test_maneuver.py +212 -0
- ostk/astrodynamics/test/flight/test_profile.py +242 -0
- ostk/astrodynamics/test/flight/test_system.py +55 -0
- ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +91 -0
- ostk/astrodynamics/test/guidance_law/test_qlaw.py +138 -0
- ostk/astrodynamics/test/solvers/__init__.py +1 -0
- ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +181 -0
- ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +153 -0
- ostk/astrodynamics/test/test_access.py +128 -0
- ostk/astrodynamics/test/test_converters.py +290 -0
- ostk/astrodynamics/test/test_dataframe.py +875 -0
- ostk/astrodynamics/test/test_display.py +114 -0
- ostk/astrodynamics/test/test_event_condition.py +58 -0
- ostk/astrodynamics/test/test_import.py +26 -0
- ostk/astrodynamics/test/test_root_solver.py +70 -0
- ostk/astrodynamics/test/test_trajectory.py +118 -0
- ostk/astrodynamics/test/test_utilities.py +106 -0
- ostk/astrodynamics/test/test_viewer.py +129 -0
- ostk/astrodynamics/test/trajectory/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
- ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
- ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
- ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
- ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
- ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
- ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +180 -0
- ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +331 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
- ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
- ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
- ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
- ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
- ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
- ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
- ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +46 -0
- ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +314 -0
- ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
- ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +108 -0
- ostk/astrodynamics/test/trajectory/test_model.py +1 -0
- ostk/astrodynamics/test/trajectory/test_orbit.py +196 -0
- ostk/astrodynamics/test/trajectory/test_propagator.py +458 -0
- ostk/astrodynamics/test/trajectory/test_segment.py +305 -0
- ostk/astrodynamics/test/trajectory/test_sequence.py +477 -0
- ostk/astrodynamics/test/trajectory/test_state.py +467 -0
- ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
- ostk/astrodynamics/utilities.py +245 -0
- ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,467 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
from datetime import datetime, timezone
|
6
|
+
|
7
|
+
import numpy as np
|
8
|
+
|
9
|
+
from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
|
10
|
+
|
11
|
+
from ostk.physics.time import Instant
|
12
|
+
from ostk.physics.time import DateTime
|
13
|
+
from ostk.physics.time import Scale
|
14
|
+
from ostk.physics.coordinate import Position
|
15
|
+
from ostk.physics.coordinate import Velocity
|
16
|
+
from ostk.physics.coordinate import Frame
|
17
|
+
|
18
|
+
from ostk.astrodynamics.trajectory import State
|
19
|
+
from ostk.astrodynamics.trajectory.state import CoordinateBroker
|
20
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import (
|
21
|
+
CartesianPosition,
|
22
|
+
CartesianVelocity,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture()
|
27
|
+
def instant() -> Instant:
|
28
|
+
return Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.fixture
|
32
|
+
def frame() -> Frame:
|
33
|
+
return Frame.GCRF()
|
34
|
+
|
35
|
+
|
36
|
+
@pytest.fixture()
|
37
|
+
def position(frame: Frame) -> Position:
|
38
|
+
return Position.meters([6371000.0, 0.0, 0.0], frame)
|
39
|
+
|
40
|
+
|
41
|
+
@pytest.fixture()
|
42
|
+
def velocity(frame: Frame) -> Velocity:
|
43
|
+
return Velocity.meters_per_second([7600.0, 0.0, 0.0], frame)
|
44
|
+
|
45
|
+
|
46
|
+
@pytest.fixture()
|
47
|
+
def attitude() -> Quaternion:
|
48
|
+
return Quaternion([0.0, 0.0, 0.0, 1.0], Quaternion.Format.XYZS)
|
49
|
+
|
50
|
+
|
51
|
+
@pytest.fixture()
|
52
|
+
def angular_velocity() -> np.ndarray:
|
53
|
+
return np.array([-1.0, -2.0, -3.0])
|
54
|
+
|
55
|
+
|
56
|
+
@pytest.fixture
|
57
|
+
def state(
|
58
|
+
instant: Instant,
|
59
|
+
position: Position,
|
60
|
+
velocity: Velocity,
|
61
|
+
) -> State:
|
62
|
+
return State(instant, position, velocity)
|
63
|
+
|
64
|
+
|
65
|
+
@pytest.fixture
|
66
|
+
def profile_state(
|
67
|
+
instant: Instant,
|
68
|
+
position: Position,
|
69
|
+
velocity: Velocity,
|
70
|
+
attitude: Quaternion,
|
71
|
+
angular_velocity: np.ndarray,
|
72
|
+
frame: Frame,
|
73
|
+
) -> State:
|
74
|
+
return State(instant, position, velocity, attitude, angular_velocity, frame)
|
75
|
+
|
76
|
+
|
77
|
+
@pytest.fixture
|
78
|
+
def coordinate_broker() -> CoordinateBroker:
|
79
|
+
return CoordinateBroker([CartesianPosition.default(), CartesianVelocity.default()])
|
80
|
+
|
81
|
+
|
82
|
+
class TestState:
|
83
|
+
def test_constructor_position_velocity(
|
84
|
+
self,
|
85
|
+
instant: Instant,
|
86
|
+
position: Position,
|
87
|
+
velocity: Velocity,
|
88
|
+
):
|
89
|
+
state = State(instant, position, velocity)
|
90
|
+
assert state is not None
|
91
|
+
assert isinstance(state, State)
|
92
|
+
assert state.is_defined()
|
93
|
+
|
94
|
+
def test_constructor_position_velocity_attitude_angular_velocity(
|
95
|
+
self,
|
96
|
+
instant: Instant,
|
97
|
+
frame: Frame,
|
98
|
+
position: Position,
|
99
|
+
velocity: Velocity,
|
100
|
+
attitude: Quaternion,
|
101
|
+
angular_velocity: np.ndarray,
|
102
|
+
):
|
103
|
+
state = State(instant, position, velocity, attitude, angular_velocity, frame)
|
104
|
+
assert state is not None
|
105
|
+
assert isinstance(state, State)
|
106
|
+
assert state.is_defined()
|
107
|
+
|
108
|
+
def test_explicit_constructor(
|
109
|
+
self,
|
110
|
+
instant: Instant,
|
111
|
+
position: Position,
|
112
|
+
velocity: Velocity,
|
113
|
+
frame: Frame,
|
114
|
+
coordinate_broker: CoordinateBroker,
|
115
|
+
):
|
116
|
+
state = State(instant, position, velocity)
|
117
|
+
assert state is not None
|
118
|
+
assert isinstance(state, State)
|
119
|
+
assert state.is_defined()
|
120
|
+
|
121
|
+
state = State(
|
122
|
+
instant,
|
123
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
124
|
+
frame,
|
125
|
+
coordinate_broker,
|
126
|
+
)
|
127
|
+
|
128
|
+
assert state is not None
|
129
|
+
assert isinstance(state, State)
|
130
|
+
assert state.is_defined()
|
131
|
+
|
132
|
+
def test_subsets_constructor(
|
133
|
+
self,
|
134
|
+
instant: Instant,
|
135
|
+
position: Position,
|
136
|
+
velocity: Velocity,
|
137
|
+
frame: Frame,
|
138
|
+
):
|
139
|
+
state = State(
|
140
|
+
instant,
|
141
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
142
|
+
frame,
|
143
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
144
|
+
)
|
145
|
+
|
146
|
+
assert state is not None
|
147
|
+
assert isinstance(state, State)
|
148
|
+
assert state.is_defined()
|
149
|
+
|
150
|
+
def test_custom_state_class_generator(
|
151
|
+
self,
|
152
|
+
instant: Instant,
|
153
|
+
position: Position,
|
154
|
+
velocity: Velocity,
|
155
|
+
frame: Frame,
|
156
|
+
):
|
157
|
+
state = State(
|
158
|
+
instant,
|
159
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
160
|
+
frame,
|
161
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
162
|
+
)
|
163
|
+
|
164
|
+
MySuperFunState: type = State.template(
|
165
|
+
frame,
|
166
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
167
|
+
)
|
168
|
+
|
169
|
+
custom_state: MySuperFunState = MySuperFunState(
|
170
|
+
instant,
|
171
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
172
|
+
)
|
173
|
+
|
174
|
+
assert custom_state is not None
|
175
|
+
assert isinstance(custom_state, MySuperFunState)
|
176
|
+
assert isinstance(state, State)
|
177
|
+
assert custom_state.is_defined()
|
178
|
+
|
179
|
+
assert custom_state == state
|
180
|
+
assert custom_state is not state
|
181
|
+
|
182
|
+
def test_from_dict_with_eci_coordinates(self):
|
183
|
+
data = {
|
184
|
+
"timestamp": datetime.now(timezone.utc),
|
185
|
+
"rx_eci": 7000.0,
|
186
|
+
"ry_eci": 0.0,
|
187
|
+
"rz_eci": 0.0,
|
188
|
+
"vx_eci": 0.0,
|
189
|
+
"vy_eci": 7.5,
|
190
|
+
"vz_eci": 0.0,
|
191
|
+
}
|
192
|
+
|
193
|
+
state: State = State.from_dict(data)
|
194
|
+
|
195
|
+
assert state is not None
|
196
|
+
assert isinstance(state, State)
|
197
|
+
assert state.get_frame() == Frame.GCRF()
|
198
|
+
assert state.get_size() == 6
|
199
|
+
|
200
|
+
def test_from_dict_with_ecef_coordinates(self):
|
201
|
+
data = {
|
202
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
203
|
+
"rx_ecef": 7000.0,
|
204
|
+
"ry_ecef": 0.0,
|
205
|
+
"rz_ecef": 0.0,
|
206
|
+
"vx_ecef": 0.0,
|
207
|
+
"vy_ecef": 7.5,
|
208
|
+
"vz_ecef": 0.0,
|
209
|
+
}
|
210
|
+
|
211
|
+
state: State = State.from_dict(data)
|
212
|
+
|
213
|
+
assert state is not None
|
214
|
+
assert isinstance(state, State)
|
215
|
+
assert state.get_frame() == Frame.ITRF()
|
216
|
+
assert state.get_size() == 6
|
217
|
+
|
218
|
+
def test_from_dict_with_generic_coordinates(self):
|
219
|
+
data = {
|
220
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
221
|
+
"rx": 7000.0,
|
222
|
+
"ry": 0.0,
|
223
|
+
"rz": 0.0,
|
224
|
+
"vx": 0.0,
|
225
|
+
"vy": 7.5,
|
226
|
+
"vz": 0.0,
|
227
|
+
"frame": "GCRF",
|
228
|
+
}
|
229
|
+
|
230
|
+
state: State = State.from_dict(data)
|
231
|
+
|
232
|
+
assert state is not None
|
233
|
+
assert isinstance(state, State)
|
234
|
+
assert state.get_frame() == Frame.GCRF()
|
235
|
+
assert state.get_size() == 6
|
236
|
+
|
237
|
+
with pytest.raises(
|
238
|
+
ValueError, match="Frame must be provided for generic columns."
|
239
|
+
):
|
240
|
+
data = {
|
241
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
242
|
+
"rx": 7000.0,
|
243
|
+
"ry": 0.0,
|
244
|
+
"rz": 0.0,
|
245
|
+
"vx": 0.0,
|
246
|
+
"vy": 7.5,
|
247
|
+
"vz": 0.0,
|
248
|
+
}
|
249
|
+
|
250
|
+
State.from_dict(data)
|
251
|
+
|
252
|
+
with pytest.raises(ValueError, match="No frame exists with name \\[RANDOM\\]."):
|
253
|
+
data = {
|
254
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
255
|
+
"rx": 7000.0,
|
256
|
+
"ry": 0.0,
|
257
|
+
"rz": 0.0,
|
258
|
+
"vx": 0.0,
|
259
|
+
"vy": 7.5,
|
260
|
+
"vz": 0.0,
|
261
|
+
"frame": "RANDOM",
|
262
|
+
}
|
263
|
+
|
264
|
+
State.from_dict(data)
|
265
|
+
|
266
|
+
with pytest.raises(ValueError, match="Invalid frame data \\[123\\]"):
|
267
|
+
data = {
|
268
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
269
|
+
"rx": 7000.0,
|
270
|
+
"ry": 0.0,
|
271
|
+
"rz": 0.0,
|
272
|
+
"vx": 0.0,
|
273
|
+
"vy": 7.5,
|
274
|
+
"vz": 0.0,
|
275
|
+
"frame": 123,
|
276
|
+
}
|
277
|
+
|
278
|
+
State.from_dict(data)
|
279
|
+
|
280
|
+
def test_from_dict_with_attitude_quaternion(self):
|
281
|
+
data = {
|
282
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
283
|
+
"rx_eci": 7000.0,
|
284
|
+
"ry_eci": 0.0,
|
285
|
+
"rz_eci": 0.0,
|
286
|
+
"vx_eci": 0.0,
|
287
|
+
"vy_eci": 7.5,
|
288
|
+
"vz_eci": 0.0,
|
289
|
+
"q_B_ECI_x": 0.0,
|
290
|
+
"q_B_ECI_y": 0.0,
|
291
|
+
"q_B_ECI_z": 0.0,
|
292
|
+
"q_B_ECI_s": 1.0,
|
293
|
+
}
|
294
|
+
|
295
|
+
state: State = State.from_dict(data)
|
296
|
+
|
297
|
+
assert state is not None
|
298
|
+
assert isinstance(state, State)
|
299
|
+
assert state.get_frame() == Frame.GCRF()
|
300
|
+
assert state.get_size() == 10
|
301
|
+
|
302
|
+
def test_from_dict_with_angular_velocity(self):
|
303
|
+
data = {
|
304
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
305
|
+
"rx_eci": 7000.0,
|
306
|
+
"ry_eci": 0.0,
|
307
|
+
"rz_eci": 0.0,
|
308
|
+
"vx_eci": 0.0,
|
309
|
+
"vy_eci": 7.5,
|
310
|
+
"vz_eci": 0.0,
|
311
|
+
"w_B_ECI_in_B_x": 0.1,
|
312
|
+
"w_B_ECI_in_B_y": 0.2,
|
313
|
+
"w_B_ECI_in_B_z": 0.3,
|
314
|
+
}
|
315
|
+
|
316
|
+
state: State = State.from_dict(data)
|
317
|
+
|
318
|
+
assert state is not None
|
319
|
+
assert isinstance(state, State)
|
320
|
+
assert state.get_size() == 9
|
321
|
+
|
322
|
+
def test_from_dict_with_drag_coefficient(self):
|
323
|
+
data = {
|
324
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
325
|
+
"rx_eci": 7000.0,
|
326
|
+
"ry_eci": 0.0,
|
327
|
+
"rz_eci": 0.0,
|
328
|
+
"vx_eci": 0.0,
|
329
|
+
"vy_eci": 7.5,
|
330
|
+
"vz_eci": 0.0,
|
331
|
+
"drag_coefficient": 2.2,
|
332
|
+
}
|
333
|
+
|
334
|
+
state: State = State.from_dict(data)
|
335
|
+
|
336
|
+
assert state is not None
|
337
|
+
assert isinstance(state, State)
|
338
|
+
assert state.get_size() == 7
|
339
|
+
|
340
|
+
data = {
|
341
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
342
|
+
"rx_eci": 7000.0,
|
343
|
+
"ry_eci": 0.0,
|
344
|
+
"rz_eci": 0.0,
|
345
|
+
"vx_eci": 0.0,
|
346
|
+
"vy_eci": 7.5,
|
347
|
+
"vz_eci": 0.0,
|
348
|
+
"cd": 2.2,
|
349
|
+
}
|
350
|
+
|
351
|
+
state: State = State.from_dict(data)
|
352
|
+
|
353
|
+
assert state is not None
|
354
|
+
assert isinstance(state, State)
|
355
|
+
assert state.get_size() == 7
|
356
|
+
|
357
|
+
def test_from_dict_with_surface_area(self):
|
358
|
+
data = {
|
359
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
360
|
+
"rx_eci": 7000.0,
|
361
|
+
"ry_eci": 0.0,
|
362
|
+
"rz_eci": 0.0,
|
363
|
+
"vx_eci": 0.0,
|
364
|
+
"vy_eci": 7.5,
|
365
|
+
"vz_eci": 0.0,
|
366
|
+
"surface_area": 2.2,
|
367
|
+
}
|
368
|
+
|
369
|
+
state: State = State.from_dict(data)
|
370
|
+
|
371
|
+
assert state is not None
|
372
|
+
assert isinstance(state, State)
|
373
|
+
assert state.get_size() == 7
|
374
|
+
|
375
|
+
data = {
|
376
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
377
|
+
"rx_eci": 7000.0,
|
378
|
+
"ry_eci": 0.0,
|
379
|
+
"rz_eci": 0.0,
|
380
|
+
"vx_eci": 0.0,
|
381
|
+
"vy_eci": 7.5,
|
382
|
+
"vz_eci": 0.0,
|
383
|
+
"cross_sectional_area": 2.2,
|
384
|
+
}
|
385
|
+
|
386
|
+
state: State = State.from_dict(data)
|
387
|
+
|
388
|
+
assert state is not None
|
389
|
+
assert isinstance(state, State)
|
390
|
+
assert state.get_size() == 7
|
391
|
+
|
392
|
+
def test_from_dict_with_mass(self):
|
393
|
+
data = {
|
394
|
+
"timestamp": datetime.now(timezone.utc).isoformat(),
|
395
|
+
"rx_eci": 7000.0,
|
396
|
+
"ry_eci": 0.0,
|
397
|
+
"rz_eci": 0.0,
|
398
|
+
"vx_eci": 0.0,
|
399
|
+
"vy_eci": 7.5,
|
400
|
+
"vz_eci": 0.0,
|
401
|
+
"mass": 2.2,
|
402
|
+
}
|
403
|
+
|
404
|
+
state: State = State.from_dict(data)
|
405
|
+
|
406
|
+
assert state is not None
|
407
|
+
assert isinstance(state, State)
|
408
|
+
assert state.get_size() == 7
|
409
|
+
|
410
|
+
def test_comparators(self, state: State):
|
411
|
+
assert (state == state) is True
|
412
|
+
assert (state != state) is False
|
413
|
+
|
414
|
+
def test_operators(self, state: State):
|
415
|
+
assert isinstance(state + state, State)
|
416
|
+
assert isinstance(state - state, State)
|
417
|
+
|
418
|
+
def test_getters(
|
419
|
+
self,
|
420
|
+
profile_state: State,
|
421
|
+
instant: Instant,
|
422
|
+
position: Position,
|
423
|
+
velocity: Velocity,
|
424
|
+
attitude: Quaternion,
|
425
|
+
angular_velocity: np.ndarray,
|
426
|
+
frame: Frame,
|
427
|
+
):
|
428
|
+
assert profile_state.get_instant() == instant
|
429
|
+
assert profile_state.get_position() == position
|
430
|
+
assert profile_state.get_velocity() == velocity
|
431
|
+
assert profile_state.get_attitude() == attitude
|
432
|
+
assert np.all(profile_state.get_angular_velocity() == angular_velocity)
|
433
|
+
assert profile_state.has_subset(CartesianPosition.default())
|
434
|
+
assert profile_state.has_subset(CartesianVelocity.default())
|
435
|
+
assert profile_state.get_frame() == frame
|
436
|
+
assert profile_state.get_coordinates() is not None
|
437
|
+
assert profile_state.get_coordinate_subsets() is not None
|
438
|
+
|
439
|
+
def test_in_frame(
|
440
|
+
self,
|
441
|
+
state: State,
|
442
|
+
frame: Frame,
|
443
|
+
):
|
444
|
+
assert state.in_frame(frame) == state
|
445
|
+
assert state.in_frame(Frame.ITRF()) != state
|
446
|
+
|
447
|
+
def test_extract_coordinate(
|
448
|
+
self,
|
449
|
+
state: State,
|
450
|
+
):
|
451
|
+
position_coordinates = state.extract_coordinate(CartesianPosition.default())
|
452
|
+
velocity_coordinates = state.extract_coordinate(CartesianVelocity.default())
|
453
|
+
|
454
|
+
assert len(position_coordinates) == 3
|
455
|
+
assert len(velocity_coordinates) == 3
|
456
|
+
assert (position_coordinates == state.get_position().get_coordinates()).all()
|
457
|
+
assert (velocity_coordinates == state.get_velocity().get_coordinates()).all()
|
458
|
+
|
459
|
+
def test_extract_coordinates(
|
460
|
+
self,
|
461
|
+
state: State,
|
462
|
+
):
|
463
|
+
pv_coordinates = state.extract_coordinates(
|
464
|
+
[CartesianPosition.default(), CartesianVelocity.default()]
|
465
|
+
)
|
466
|
+
assert len(pv_coordinates) == 6
|
467
|
+
assert (pv_coordinates == state.get_coordinates()).all()
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from ostk.physics.time import Instant
|
8
|
+
from ostk.physics.time import DateTime
|
9
|
+
from ostk.physics.time import Scale
|
10
|
+
from ostk.physics.coordinate import Frame
|
11
|
+
|
12
|
+
from ostk.astrodynamics.trajectory import (
|
13
|
+
State,
|
14
|
+
StateBuilder,
|
15
|
+
)
|
16
|
+
from ostk.astrodynamics.trajectory.state import (
|
17
|
+
CoordinateBroker,
|
18
|
+
CoordinateSubset,
|
19
|
+
)
|
20
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import (
|
21
|
+
CartesianPosition,
|
22
|
+
CartesianVelocity,
|
23
|
+
)
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture()
|
27
|
+
def instant() -> Instant:
|
28
|
+
return Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.fixture
|
32
|
+
def frame() -> Frame:
|
33
|
+
return Frame.GCRF()
|
34
|
+
|
35
|
+
|
36
|
+
@pytest.fixture
|
37
|
+
def coordinate_subsets() -> list[CoordinateSubset]:
|
38
|
+
return [CartesianPosition.default(), CartesianVelocity.default()]
|
39
|
+
|
40
|
+
|
41
|
+
@pytest.fixture
|
42
|
+
def coordinates() -> list[float]:
|
43
|
+
return [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
44
|
+
|
45
|
+
|
46
|
+
@pytest.fixture
|
47
|
+
def coordinate_broker(
|
48
|
+
coordinate_subsets: list[CoordinateSubset],
|
49
|
+
) -> CoordinateBroker:
|
50
|
+
return CoordinateBroker(coordinate_subsets)
|
51
|
+
|
52
|
+
|
53
|
+
@pytest.fixture
|
54
|
+
def state(
|
55
|
+
instant: Instant,
|
56
|
+
coordinates: list[float],
|
57
|
+
frame: Frame,
|
58
|
+
coordinate_broker: CoordinateBroker,
|
59
|
+
) -> State:
|
60
|
+
return State(instant, coordinates, frame, coordinate_broker)
|
61
|
+
|
62
|
+
|
63
|
+
@pytest.fixture
|
64
|
+
def state_builder(frame: Frame, coordinate_broker: CoordinateBroker) -> State:
|
65
|
+
return StateBuilder(frame, coordinate_broker)
|
66
|
+
|
67
|
+
|
68
|
+
class TestStateBuilder:
|
69
|
+
def test_broker_constructor(
|
70
|
+
self,
|
71
|
+
frame: Frame,
|
72
|
+
coordinate_broker: CoordinateBroker,
|
73
|
+
):
|
74
|
+
builder = StateBuilder(frame, coordinate_broker)
|
75
|
+
assert builder is not None
|
76
|
+
assert isinstance(builder, StateBuilder)
|
77
|
+
assert builder.is_defined()
|
78
|
+
|
79
|
+
def test_subsets_constructor(
|
80
|
+
self,
|
81
|
+
frame: Frame,
|
82
|
+
coordinate_subsets: list[CoordinateSubset],
|
83
|
+
):
|
84
|
+
builder = StateBuilder(frame, coordinate_subsets)
|
85
|
+
assert builder is not None
|
86
|
+
assert isinstance(builder, StateBuilder)
|
87
|
+
assert builder.is_defined()
|
88
|
+
|
89
|
+
def test_state_constructor(
|
90
|
+
self,
|
91
|
+
state: State,
|
92
|
+
):
|
93
|
+
builder = StateBuilder(state)
|
94
|
+
assert builder is not None
|
95
|
+
assert isinstance(builder, StateBuilder)
|
96
|
+
assert builder.is_defined()
|
97
|
+
|
98
|
+
def test_comparators(self, state_builder: StateBuilder):
|
99
|
+
assert (state_builder == state_builder) is True
|
100
|
+
assert (state_builder != state_builder) is False
|
101
|
+
|
102
|
+
def test_operators(
|
103
|
+
self,
|
104
|
+
state_builder: StateBuilder,
|
105
|
+
):
|
106
|
+
added_builder: StateBuilder = state_builder + CoordinateSubset.mass()
|
107
|
+
assert isinstance(added_builder, StateBuilder)
|
108
|
+
assert state_builder != added_builder
|
109
|
+
|
110
|
+
subtracted_builder: StateBuilder = state_builder - CartesianPosition.default()
|
111
|
+
assert isinstance(subtracted_builder, StateBuilder)
|
112
|
+
assert state_builder != subtracted_builder
|
113
|
+
|
114
|
+
def test_build(
|
115
|
+
self,
|
116
|
+
instant: Instant,
|
117
|
+
state_builder: StateBuilder,
|
118
|
+
):
|
119
|
+
coordinates = [1, 2, 3, 1, 2, 3]
|
120
|
+
state: State = state_builder.build(instant, coordinates)
|
121
|
+
|
122
|
+
assert state is not None
|
123
|
+
assert isinstance(state, State)
|
124
|
+
assert state.is_defined()
|
125
|
+
assert state.get_instant() == instant
|
126
|
+
assert (state.get_coordinates() == coordinates).all()
|
127
|
+
assert state.get_frame() == state_builder.get_frame()
|
128
|
+
assert state.get_coordinate_subsets() == state_builder.get_coordinate_subsets()
|
129
|
+
|
130
|
+
def test_reduce(
|
131
|
+
self,
|
132
|
+
state: State,
|
133
|
+
):
|
134
|
+
builder = StateBuilder(state.get_frame(), [CartesianPosition.default()])
|
135
|
+
reduced_state: State = builder.reduce(state)
|
136
|
+
|
137
|
+
assert isinstance(reduced_state, State)
|
138
|
+
assert state != reduced_state
|
139
|
+
|
140
|
+
def test_expand(
|
141
|
+
self,
|
142
|
+
state: State,
|
143
|
+
):
|
144
|
+
builder = StateBuilder(
|
145
|
+
state.get_frame(),
|
146
|
+
[
|
147
|
+
CartesianPosition.default(),
|
148
|
+
CartesianVelocity.default(),
|
149
|
+
CoordinateSubset.mass(),
|
150
|
+
],
|
151
|
+
)
|
152
|
+
default_state: State = State(
|
153
|
+
state.get_instant(),
|
154
|
+
[100],
|
155
|
+
state.get_frame(),
|
156
|
+
CoordinateBroker([CoordinateSubset.mass()]),
|
157
|
+
)
|
158
|
+
expanded_state: State = builder.expand(state, default_state)
|
159
|
+
|
160
|
+
assert isinstance(expanded_state, State)
|
161
|
+
assert state != expanded_state
|
162
|
+
assert default_state != expanded_state
|
163
|
+
|
164
|
+
def test_getters(
|
165
|
+
self,
|
166
|
+
state_builder: StateBuilder,
|
167
|
+
frame: Frame,
|
168
|
+
coordinate_broker: CoordinateBroker,
|
169
|
+
):
|
170
|
+
assert state_builder.get_frame() == frame
|
171
|
+
assert state_builder.get_coordinate_subsets() == coordinate_broker.get_subsets()
|