open-space-toolkit-astrodynamics 17.2.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 (151) hide show
  1. open_space_toolkit_astrodynamics-17.2.0.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-17.2.0.dist-info/RECORD +151 -0
  3. open_space_toolkit_astrodynamics-17.2.0.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-17.2.0.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-17.2.0.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-x86_64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/__init__.pyi +720 -0
  10. ostk/astrodynamics/access.pyi +577 -0
  11. ostk/astrodynamics/conjunction/__init__.pyi +121 -0
  12. ostk/astrodynamics/conjunction/close_approach.pyi +89 -0
  13. ostk/astrodynamics/conjunction/message/__init__.pyi +3 -0
  14. ostk/astrodynamics/conjunction/message/ccsds.pyi +705 -0
  15. ostk/astrodynamics/converters.py +130 -0
  16. ostk/astrodynamics/converters.pyi +58 -0
  17. ostk/astrodynamics/data/__init__.pyi +3 -0
  18. ostk/astrodynamics/data/provider.pyi +22 -0
  19. ostk/astrodynamics/dataframe.py +597 -0
  20. ostk/astrodynamics/display.py +281 -0
  21. ostk/astrodynamics/dynamics.pyi +311 -0
  22. ostk/astrodynamics/eclipse.pyi +70 -0
  23. ostk/astrodynamics/estimator.pyi +268 -0
  24. ostk/astrodynamics/event_condition.pyi +910 -0
  25. ostk/astrodynamics/flight/__init__.pyi +626 -0
  26. ostk/astrodynamics/flight/profile/__init__.pyi +99 -0
  27. ostk/astrodynamics/flight/profile/model.pyi +179 -0
  28. ostk/astrodynamics/flight/system.pyi +268 -0
  29. ostk/astrodynamics/guidance_law.pyi +416 -0
  30. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.17 +0 -0
  31. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  32. ostk/astrodynamics/pytrajectory/__init__.pyi +3 -0
  33. ostk/astrodynamics/pytrajectory/pystate.py +263 -0
  34. ostk/astrodynamics/pytrajectory/pystate.pyi +66 -0
  35. ostk/astrodynamics/solver.pyi +432 -0
  36. ostk/astrodynamics/test/__init__.py +1 -0
  37. ostk/astrodynamics/test/access/__init__.py +1 -0
  38. ostk/astrodynamics/test/access/test_generator.py +319 -0
  39. ostk/astrodynamics/test/access/test_visibility_criterion.py +201 -0
  40. ostk/astrodynamics/test/conftest.py +119 -0
  41. ostk/astrodynamics/test/conjunction/close_approach/__init__.py +0 -0
  42. ostk/astrodynamics/test/conjunction/close_approach/test_generator.py +228 -0
  43. ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
  44. ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
  45. ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
  46. ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
  47. ostk/astrodynamics/test/conjunction/test_close_approach.py +244 -0
  48. ostk/astrodynamics/test/data/provider/test_off_nadir.py +58 -0
  49. ostk/astrodynamics/test/dynamics/__init__.py +1 -0
  50. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
  51. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
  52. ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
  53. ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
  54. ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
  55. ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
  56. ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
  57. ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
  58. ostk/astrodynamics/test/dynamics/test_thruster.py +157 -0
  59. ostk/astrodynamics/test/eclipse/__init__.py +1 -0
  60. ostk/astrodynamics/test/eclipse/test_generator.py +138 -0
  61. ostk/astrodynamics/test/estimator/test_orbit_determination_solver.py +261 -0
  62. ostk/astrodynamics/test/estimator/test_tle_solver.py +216 -0
  63. ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
  64. ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
  65. ostk/astrodynamics/test/event_condition/test_brouwer_lyddane_mean_long_condition.py +135 -0
  66. ostk/astrodynamics/test/event_condition/test_coe_condition.py +135 -0
  67. ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
  68. ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
  69. ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
  70. ostk/astrodynamics/test/flight/__init__.py +1 -0
  71. ostk/astrodynamics/test/flight/profile/model/test_tabulated_profile.py +115 -0
  72. ostk/astrodynamics/test/flight/system/__init__.py +1 -0
  73. ostk/astrodynamics/test/flight/system/test_propulsion_system.py +64 -0
  74. ostk/astrodynamics/test/flight/system/test_satellite_system.py +83 -0
  75. ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
  76. ostk/astrodynamics/test/flight/test_maneuver.py +231 -0
  77. ostk/astrodynamics/test/flight/test_profile.py +293 -0
  78. ostk/astrodynamics/test/flight/test_system.py +45 -0
  79. ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +177 -0
  80. ostk/astrodynamics/test/guidance_law/test_guidance_law.py +60 -0
  81. ostk/astrodynamics/test/guidance_law/test_heterogeneous_guidance_law.py +164 -0
  82. ostk/astrodynamics/test/guidance_law/test_qlaw.py +209 -0
  83. ostk/astrodynamics/test/solvers/__init__.py +1 -0
  84. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +196 -0
  85. ostk/astrodynamics/test/solvers/test_least_squares_solver.py +334 -0
  86. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +161 -0
  87. ostk/astrodynamics/test/test_access.py +128 -0
  88. ostk/astrodynamics/test/test_converters.py +290 -0
  89. ostk/astrodynamics/test/test_dataframe.py +1355 -0
  90. ostk/astrodynamics/test/test_display.py +184 -0
  91. ostk/astrodynamics/test/test_event_condition.py +80 -0
  92. ostk/astrodynamics/test/test_import.py +26 -0
  93. ostk/astrodynamics/test/test_root_solver.py +70 -0
  94. ostk/astrodynamics/test/test_trajectory.py +126 -0
  95. ostk/astrodynamics/test/test_utilities.py +338 -0
  96. ostk/astrodynamics/test/test_viewer.py +318 -0
  97. ostk/astrodynamics/test/trajectory/__init__.py +1 -0
  98. ostk/astrodynamics/test/trajectory/model/test_nadir_trajectory.py +87 -0
  99. ostk/astrodynamics/test/trajectory/model/test_tabulated_trajectory.py +303 -0
  100. ostk/astrodynamics/test/trajectory/model/test_target_scan_trajectory.py +126 -0
  101. ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
  102. ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
  103. ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
  104. ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
  105. ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
  106. ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
  107. ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
  108. ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
  109. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
  110. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
  111. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
  112. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +305 -0
  113. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
  114. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +337 -0
  115. ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
  116. ostk/astrodynamics/test/trajectory/orbit/models/test_modified_equinoctial.py +142 -0
  117. ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
  118. ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
  119. ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
  120. ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
  121. ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
  122. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
  123. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
  124. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_acceleration.py +136 -0
  125. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
  126. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
  127. ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
  128. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +58 -0
  129. ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +316 -0
  130. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
  131. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +119 -0
  132. ostk/astrodynamics/test/trajectory/test_model.py +1 -0
  133. ostk/astrodynamics/test/trajectory/test_orbit.py +212 -0
  134. ostk/astrodynamics/test/trajectory/test_propagator.py +452 -0
  135. ostk/astrodynamics/test/trajectory/test_segment.py +694 -0
  136. ostk/astrodynamics/test/trajectory/test_sequence.py +550 -0
  137. ostk/astrodynamics/test/trajectory/test_state.py +629 -0
  138. ostk/astrodynamics/test/trajectory/test_state_builder.py +172 -0
  139. ostk/astrodynamics/trajectory/__init__.pyi +1982 -0
  140. ostk/astrodynamics/trajectory/model.pyi +259 -0
  141. ostk/astrodynamics/trajectory/orbit/__init__.pyi +349 -0
  142. ostk/astrodynamics/trajectory/orbit/message/__init__.pyi +3 -0
  143. ostk/astrodynamics/trajectory/orbit/message/spacex.pyi +264 -0
  144. ostk/astrodynamics/trajectory/orbit/model/__init__.pyi +648 -0
  145. ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi +121 -0
  146. ostk/astrodynamics/trajectory/orbit/model/kepler.pyi +709 -0
  147. ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi +330 -0
  148. ostk/astrodynamics/trajectory/state/__init__.pyi +402 -0
  149. ostk/astrodynamics/trajectory/state/coordinate_subset.pyi +208 -0
  150. ostk/astrodynamics/utilities.py +396 -0
  151. ostk/astrodynamics/viewer.py +851 -0
@@ -0,0 +1,629 @@
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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: dict = {
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
+ data: dict = {
358
+ "timestamp": datetime.now(timezone.utc).isoformat(),
359
+ "rx_eci": 7000.0,
360
+ "ry_eci": 0.0,
361
+ "rz_eci": 0.0,
362
+ "vx_eci": 0.0,
363
+ "vy_eci": 7.5,
364
+ "vz_eci": 0.0,
365
+ "drag_coefficient": None,
366
+ }
367
+
368
+ state: State = State.from_dict(data)
369
+
370
+ assert state is not None
371
+ assert isinstance(state, State)
372
+ assert state.get_size() == 6
373
+
374
+ def test_from_dict_with_surface_area(self):
375
+ data: dict = {
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
+ "surface_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
+ data: dict = {
393
+ "timestamp": datetime.now(timezone.utc).isoformat(),
394
+ "rx_eci": 7000.0,
395
+ "ry_eci": 0.0,
396
+ "rz_eci": 0.0,
397
+ "vx_eci": 0.0,
398
+ "vy_eci": 7.5,
399
+ "vz_eci": 0.0,
400
+ "cross_sectional_area": 2.2,
401
+ }
402
+
403
+ state: State = State.from_dict(data)
404
+
405
+ assert state is not None
406
+ assert isinstance(state, State)
407
+ assert state.get_size() == 7
408
+
409
+ data: dict = {
410
+ "timestamp": datetime.now(timezone.utc).isoformat(),
411
+ "rx_eci": 7000.0,
412
+ "ry_eci": 0.0,
413
+ "rz_eci": 0.0,
414
+ "vx_eci": 0.0,
415
+ "vy_eci": 7.5,
416
+ "vz_eci": 0.0,
417
+ "cross_sectional_area": None,
418
+ }
419
+
420
+ state: State = State.from_dict(data)
421
+
422
+ assert state is not None
423
+ assert isinstance(state, State)
424
+ assert state.get_size() == 6
425
+
426
+ def test_from_dict_with_mass(self):
427
+ data: dict = {
428
+ "timestamp": datetime.now(timezone.utc).isoformat(),
429
+ "rx_eci": 7000.0,
430
+ "ry_eci": 0.0,
431
+ "rz_eci": 0.0,
432
+ "vx_eci": 0.0,
433
+ "vy_eci": 7.5,
434
+ "vz_eci": 0.0,
435
+ "mass": 2.2,
436
+ }
437
+
438
+ state: State = State.from_dict(data)
439
+
440
+ assert state is not None
441
+ assert isinstance(state, State)
442
+ assert state.get_size() == 7
443
+
444
+ data: dict = {
445
+ "timestamp": datetime.now(timezone.utc).isoformat(),
446
+ "rx_eci": 7000.0,
447
+ "ry_eci": 0.0,
448
+ "rz_eci": 0.0,
449
+ "vx_eci": 0.0,
450
+ "vy_eci": 7.5,
451
+ "vz_eci": 0.0,
452
+ "mass": None,
453
+ }
454
+
455
+ state: State = State.from_dict(data)
456
+
457
+ assert state is not None
458
+ assert isinstance(state, State)
459
+ assert state.get_size() == 6
460
+
461
+ def test_from_dict_with_ballistic_coefficient(self):
462
+ data: dict = {
463
+ "timestamp": datetime.now(timezone.utc).isoformat(),
464
+ "rx_eci": 7000.0,
465
+ "ry_eci": 0.0,
466
+ "rz_eci": 0.0,
467
+ "vx_eci": 0.0,
468
+ "vy_eci": 7.5,
469
+ "vz_eci": 0.0,
470
+ "ballistic_coefficient": 2.2,
471
+ }
472
+
473
+ state: State = State.from_dict(data)
474
+
475
+ assert state is not None
476
+ assert isinstance(state, State)
477
+ assert state.get_size() == 7
478
+
479
+ data: dict = {
480
+ "timestamp": datetime.now(timezone.utc).isoformat(),
481
+ "rx_eci": 7000.0,
482
+ "ry_eci": 0.0,
483
+ "rz_eci": 0.0,
484
+ "vx_eci": 0.0,
485
+ "vy_eci": 7.5,
486
+ "vz_eci": 0.0,
487
+ "ballistic_coefficient": None,
488
+ }
489
+
490
+ state: State = State.from_dict(data)
491
+
492
+ assert state is not None
493
+ assert isinstance(state, State)
494
+ assert state.get_size() == 6
495
+
496
+ @pytest.mark.parametrize(
497
+ ("data", "expected_length", "expected_frame"),
498
+ [
499
+ (
500
+ {
501
+ "timestamp": datetime.now(timezone.utc).isoformat(),
502
+ "r_GCRF_x": 1.0,
503
+ "r_GCRF_y": 2.0,
504
+ "r_GCRF_z": 3.0,
505
+ "v_GCRF_x": 4.0,
506
+ "v_GCRF_y": 5.0,
507
+ "v_GCRF_z": 6.0,
508
+ },
509
+ 6,
510
+ Frame.GCRF(),
511
+ ),
512
+ (
513
+ {
514
+ "timestamp": datetime.now(timezone.utc).isoformat(),
515
+ "r_ITRF_x": 1.0,
516
+ "r_ITRF_y": 2.0,
517
+ "r_ITRF_z": 3.0,
518
+ "v_ITRF_x": 4.0,
519
+ "v_ITRF_y": 5.0,
520
+ "v_ITRF_z": 6.0,
521
+ },
522
+ 6,
523
+ Frame.ITRF(),
524
+ ),
525
+ ],
526
+ )
527
+ def test_from_dict_cannonical_success(
528
+ self, data: dict, expected_length: int, expected_frame: Frame
529
+ ):
530
+ state: State = State.from_dict(data)
531
+
532
+ assert state is not None
533
+ assert isinstance(state, State)
534
+
535
+ assert state.get_size() == expected_length
536
+ assert state.get_frame() == expected_frame
537
+
538
+ @pytest.mark.parametrize(
539
+ ("data", "expected_failure_message"),
540
+ [
541
+ (
542
+ {
543
+ "timestamp": datetime.now(timezone.utc).isoformat(),
544
+ "r_GCRF_x": 1.0,
545
+ "r_GCRF_y": 2.0,
546
+ "r_GCRF_z": 3.0,
547
+ "v_GCRF_x": 4.0,
548
+ "v_GCRF_y": 5.0,
549
+ },
550
+ "Invalid state data.",
551
+ ),
552
+ (
553
+ {
554
+ "timestamp": datetime.now(timezone.utc).isoformat(),
555
+ "r_TEST_x": 1.0,
556
+ "r_TEST_y": 2.0,
557
+ "r_TEST_z": 3.0,
558
+ "v_TEST_x": 4.0,
559
+ "v_TEST_y": 5.0,
560
+ "v_TEST_z": 6.0,
561
+ },
562
+ "No frame exists with name \\[TEST\\].",
563
+ ),
564
+ ],
565
+ )
566
+ def test_from_dict_cannonical_failure(
567
+ self, data: dict, expected_failure_message: str
568
+ ):
569
+ with pytest.raises(ValueError, match=expected_failure_message):
570
+ State.from_dict(data)
571
+
572
+ def test_comparators(self, state: State):
573
+ assert (state == state) is True
574
+ assert (state != state) is False
575
+
576
+ def test_operators(self, state: State):
577
+ assert isinstance(state + state, State)
578
+ assert isinstance(state - state, State)
579
+
580
+ def test_getters(
581
+ self,
582
+ profile_state: State,
583
+ instant: Instant,
584
+ position: Position,
585
+ velocity: Velocity,
586
+ attitude: Quaternion,
587
+ angular_velocity: np.ndarray,
588
+ frame: Frame,
589
+ ):
590
+ assert profile_state.get_instant() == instant
591
+ assert profile_state.get_position() == position
592
+ assert profile_state.get_velocity() == velocity
593
+ assert profile_state.get_attitude() == attitude
594
+ assert np.all(profile_state.get_angular_velocity() == angular_velocity)
595
+ assert profile_state.has_subset(CartesianPosition.default())
596
+ assert profile_state.has_subset(CartesianVelocity.default())
597
+ assert profile_state.get_frame() == frame
598
+ assert profile_state.get_coordinates() is not None
599
+ assert profile_state.get_coordinate_subsets() is not None
600
+
601
+ def test_in_frame(
602
+ self,
603
+ state: State,
604
+ frame: Frame,
605
+ ):
606
+ assert state.in_frame(frame) == state
607
+ assert state.in_frame(Frame.ITRF()) != state
608
+
609
+ def test_extract_coordinate(
610
+ self,
611
+ state: State,
612
+ ):
613
+ position_coordinates = state.extract_coordinate(CartesianPosition.default())
614
+ velocity_coordinates = state.extract_coordinate(CartesianVelocity.default())
615
+
616
+ assert len(position_coordinates) == 3
617
+ assert len(velocity_coordinates) == 3
618
+ assert (position_coordinates == state.get_position().get_coordinates()).all()
619
+ assert (velocity_coordinates == state.get_velocity().get_coordinates()).all()
620
+
621
+ def test_extract_coordinates(
622
+ self,
623
+ state: State,
624
+ ):
625
+ pv_coordinates = state.extract_coordinates(
626
+ [CartesianPosition.default(), CartesianVelocity.default()]
627
+ )
628
+ assert len(pv_coordinates) == 6
629
+ assert (pv_coordinates == state.get_coordinates()).all()