open-space-toolkit-astrodynamics 13.1.0__py313-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 (100) hide show
  1. open_space_toolkit_astrodynamics-13.1.0.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-13.1.0.dist-info/RECORD +100 -0
  3. open_space_toolkit_astrodynamics-13.1.0.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-13.1.0.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-13.1.0.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-313-x86_64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/converters.py +130 -0
  10. ostk/astrodynamics/dataframe.py +479 -0
  11. ostk/astrodynamics/display.py +222 -0
  12. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.13 +0 -0
  13. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  14. ostk/astrodynamics/pytrajectory/pystate.py +251 -0
  15. ostk/astrodynamics/test/__init__.py +1 -0
  16. ostk/astrodynamics/test/access/__init__.py +1 -0
  17. ostk/astrodynamics/test/access/test_generator.py +248 -0
  18. ostk/astrodynamics/test/conftest.py +119 -0
  19. ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
  20. ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
  21. ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
  22. ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
  23. ostk/astrodynamics/test/dynamics/__init__.py +1 -0
  24. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
  25. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
  26. ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
  27. ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
  28. ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
  29. ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
  30. ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
  31. ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
  32. ostk/astrodynamics/test/dynamics/test_thruster.py +142 -0
  33. ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
  34. ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
  35. ostk/astrodynamics/test/event_condition/test_coe_condition.py +87 -0
  36. ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
  37. ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
  38. ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
  39. ostk/astrodynamics/test/flight/__init__.py +1 -0
  40. ostk/astrodynamics/test/flight/system/__init__.py +1 -0
  41. ostk/astrodynamics/test/flight/system/test_propulsion_system.py +73 -0
  42. ostk/astrodynamics/test/flight/system/test_satellite_system.py +91 -0
  43. ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
  44. ostk/astrodynamics/test/flight/test_maneuver.py +212 -0
  45. ostk/astrodynamics/test/flight/test_profile.py +253 -0
  46. ostk/astrodynamics/test/flight/test_system.py +55 -0
  47. ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +91 -0
  48. ostk/astrodynamics/test/guidance_law/test_qlaw.py +138 -0
  49. ostk/astrodynamics/test/solvers/__init__.py +1 -0
  50. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +181 -0
  51. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +153 -0
  52. ostk/astrodynamics/test/test_access.py +128 -0
  53. ostk/astrodynamics/test/test_converters.py +290 -0
  54. ostk/astrodynamics/test/test_dataframe.py +875 -0
  55. ostk/astrodynamics/test/test_display.py +114 -0
  56. ostk/astrodynamics/test/test_event_condition.py +78 -0
  57. ostk/astrodynamics/test/test_import.py +26 -0
  58. ostk/astrodynamics/test/test_root_solver.py +70 -0
  59. ostk/astrodynamics/test/test_trajectory.py +118 -0
  60. ostk/astrodynamics/test/test_utilities.py +106 -0
  61. ostk/astrodynamics/test/test_viewer.py +129 -0
  62. ostk/astrodynamics/test/trajectory/__init__.py +1 -0
  63. ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
  64. ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
  65. ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
  66. ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
  67. ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
  68. ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
  69. ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
  70. ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
  71. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
  72. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
  73. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
  74. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +201 -0
  75. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
  76. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +331 -0
  77. ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
  78. ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
  79. ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
  80. ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
  81. ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
  82. ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
  83. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
  84. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
  85. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
  86. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
  87. ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
  88. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +46 -0
  89. ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +314 -0
  90. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
  91. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +108 -0
  92. ostk/astrodynamics/test/trajectory/test_model.py +1 -0
  93. ostk/astrodynamics/test/trajectory/test_orbit.py +205 -0
  94. ostk/astrodynamics/test/trajectory/test_propagator.py +458 -0
  95. ostk/astrodynamics/test/trajectory/test_segment.py +403 -0
  96. ostk/astrodynamics/test/trajectory/test_sequence.py +530 -0
  97. ostk/astrodynamics/test/trajectory/test_state.py +543 -0
  98. ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
  99. ostk/astrodynamics/utilities.py +247 -0
  100. ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,543 @@
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
+ @pytest.mark.parametrize(
411
+ ("data", "expected_length", "expected_frame"),
412
+ [
413
+ (
414
+ {
415
+ "timestamp": datetime.now(timezone.utc).isoformat(),
416
+ "r_GCRF_x": 1.0,
417
+ "r_GCRF_y": 2.0,
418
+ "r_GCRF_z": 3.0,
419
+ "v_GCRF_x": 4.0,
420
+ "v_GCRF_y": 5.0,
421
+ "v_GCRF_z": 6.0,
422
+ },
423
+ 6,
424
+ Frame.GCRF(),
425
+ ),
426
+ (
427
+ {
428
+ "timestamp": datetime.now(timezone.utc).isoformat(),
429
+ "r_ITRF_x": 1.0,
430
+ "r_ITRF_y": 2.0,
431
+ "r_ITRF_z": 3.0,
432
+ "v_ITRF_x": 4.0,
433
+ "v_ITRF_y": 5.0,
434
+ "v_ITRF_z": 6.0,
435
+ },
436
+ 6,
437
+ Frame.ITRF(),
438
+ ),
439
+ ],
440
+ )
441
+ def test_from_dict_cannonical_success(
442
+ self, data: dict, expected_length: int, expected_frame: Frame
443
+ ):
444
+ state: State = State.from_dict(data)
445
+
446
+ assert state is not None
447
+ assert isinstance(state, State)
448
+
449
+ assert state.get_size() == expected_length
450
+ assert state.get_frame() == expected_frame
451
+
452
+ @pytest.mark.parametrize(
453
+ ("data", "expected_failure_message"),
454
+ [
455
+ (
456
+ {
457
+ "timestamp": datetime.now(timezone.utc).isoformat(),
458
+ "r_GCRF_x": 1.0,
459
+ "r_GCRF_y": 2.0,
460
+ "r_GCRF_z": 3.0,
461
+ "v_GCRF_x": 4.0,
462
+ "v_GCRF_y": 5.0,
463
+ },
464
+ "Invalid state data.",
465
+ ),
466
+ (
467
+ {
468
+ "timestamp": datetime.now(timezone.utc).isoformat(),
469
+ "r_TEST_x": 1.0,
470
+ "r_TEST_y": 2.0,
471
+ "r_TEST_z": 3.0,
472
+ "v_TEST_x": 4.0,
473
+ "v_TEST_y": 5.0,
474
+ "v_TEST_z": 6.0,
475
+ },
476
+ "No frame exists with name \\[TEST\\].",
477
+ ),
478
+ ],
479
+ )
480
+ def test_from_dict_cannonical_failure(
481
+ self, data: dict, expected_failure_message: str
482
+ ):
483
+ with pytest.raises(ValueError, match=expected_failure_message):
484
+ State.from_dict(data)
485
+
486
+ def test_comparators(self, state: State):
487
+ assert (state == state) is True
488
+ assert (state != state) is False
489
+
490
+ def test_operators(self, state: State):
491
+ assert isinstance(state + state, State)
492
+ assert isinstance(state - state, State)
493
+
494
+ def test_getters(
495
+ self,
496
+ profile_state: State,
497
+ instant: Instant,
498
+ position: Position,
499
+ velocity: Velocity,
500
+ attitude: Quaternion,
501
+ angular_velocity: np.ndarray,
502
+ frame: Frame,
503
+ ):
504
+ assert profile_state.get_instant() == instant
505
+ assert profile_state.get_position() == position
506
+ assert profile_state.get_velocity() == velocity
507
+ assert profile_state.get_attitude() == attitude
508
+ assert np.all(profile_state.get_angular_velocity() == angular_velocity)
509
+ assert profile_state.has_subset(CartesianPosition.default())
510
+ assert profile_state.has_subset(CartesianVelocity.default())
511
+ assert profile_state.get_frame() == frame
512
+ assert profile_state.get_coordinates() is not None
513
+ assert profile_state.get_coordinate_subsets() is not None
514
+
515
+ def test_in_frame(
516
+ self,
517
+ state: State,
518
+ frame: Frame,
519
+ ):
520
+ assert state.in_frame(frame) == state
521
+ assert state.in_frame(Frame.ITRF()) != state
522
+
523
+ def test_extract_coordinate(
524
+ self,
525
+ state: State,
526
+ ):
527
+ position_coordinates = state.extract_coordinate(CartesianPosition.default())
528
+ velocity_coordinates = state.extract_coordinate(CartesianVelocity.default())
529
+
530
+ assert len(position_coordinates) == 3
531
+ assert len(velocity_coordinates) == 3
532
+ assert (position_coordinates == state.get_position().get_coordinates()).all()
533
+ assert (velocity_coordinates == state.get_velocity().get_coordinates()).all()
534
+
535
+ def test_extract_coordinates(
536
+ self,
537
+ state: State,
538
+ ):
539
+ pv_coordinates = state.extract_coordinates(
540
+ [CartesianPosition.default(), CartesianVelocity.default()]
541
+ )
542
+ assert len(pv_coordinates) == 6
543
+ 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()