open-space-toolkit-astrodynamics 9.4.1__py38-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-9.4.1.dist-info/METADATA +30 -0
- open_space_toolkit_astrodynamics-9.4.1.dist-info/RECORD +98 -0
- open_space_toolkit_astrodynamics-9.4.1.dist-info/WHEEL +5 -0
- open_space_toolkit_astrodynamics-9.4.1.dist-info/top_level.txt +1 -0
- open_space_toolkit_astrodynamics-9.4.1.dist-info/zip-safe +1 -0
- ostk/__init__.py +1 -0
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-38-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.py +11 -0
- ostk/astrodynamics/converters.py +185 -0
- ostk/astrodynamics/display.py +220 -0
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.9 +0 -0
- ostk/astrodynamics/pytrajectory/__init__.py +1 -0
- ostk/astrodynamics/pytrajectory/pystate.py +36 -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 +153 -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 +387 -0
- ostk/astrodynamics/test/test_display.py +115 -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 +40 -0
- ostk/astrodynamics/test/test_utilities.py +121 -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 +167 -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 +72 -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 +76 -0
- ostk/astrodynamics/test/trajectory/test_model.py +1 -0
- ostk/astrodynamics/test/trajectory/test_orbit.py +174 -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 +237 -0
- ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
- ostk/astrodynamics/utilities.py +158 -0
- ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,477 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from ostk.mathematics.geometry.d3.object import Composite
|
8
|
+
from ostk.mathematics.geometry.d3.object import Cuboid
|
9
|
+
from ostk.mathematics.geometry.d3.object import Point
|
10
|
+
|
11
|
+
from ostk.physics import Environment
|
12
|
+
from ostk.physics.coordinate import Frame
|
13
|
+
from ostk.physics.environment.atmospheric import Earth as EarthAtmosphericModel
|
14
|
+
from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
|
15
|
+
from ostk.physics.environment.magnetic import Earth as EarthMagneticModel
|
16
|
+
from ostk.physics.environment.object.celestial import Earth
|
17
|
+
from ostk.physics.time import DateTime
|
18
|
+
from ostk.physics.time import Duration
|
19
|
+
from ostk.physics.time import Instant
|
20
|
+
from ostk.physics.time import Scale
|
21
|
+
from ostk.physics.unit import Derived
|
22
|
+
from ostk.physics.unit import Length
|
23
|
+
from ostk.physics.unit import Mass
|
24
|
+
|
25
|
+
from ostk.astrodynamics.event_condition import COECondition
|
26
|
+
from ostk.astrodynamics.event_condition import InstantCondition
|
27
|
+
from ostk.astrodynamics.event_condition import RealCondition
|
28
|
+
from ostk.astrodynamics import Dynamics
|
29
|
+
from ostk.astrodynamics import EventCondition
|
30
|
+
from ostk.astrodynamics.dynamics import Thruster
|
31
|
+
from ostk.astrodynamics.guidance_law import ConstantThrust
|
32
|
+
from ostk.astrodynamics.flight.system import PropulsionSystem
|
33
|
+
from ostk.astrodynamics.flight.system import SatelliteSystem
|
34
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameDirection
|
35
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
|
36
|
+
from ostk.astrodynamics.trajectory import Segment
|
37
|
+
from ostk.astrodynamics.trajectory import Sequence
|
38
|
+
from ostk.astrodynamics.trajectory import State
|
39
|
+
from ostk.astrodynamics.trajectory.state import CoordinateBroker
|
40
|
+
from ostk.astrodynamics.trajectory.state import CoordinateSubset
|
41
|
+
from ostk.astrodynamics.trajectory.state import NumericalSolver
|
42
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
|
43
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
|
44
|
+
|
45
|
+
|
46
|
+
@pytest.fixture
|
47
|
+
def propulsion_system() -> PropulsionSystem:
|
48
|
+
return PropulsionSystem(
|
49
|
+
1.0,
|
50
|
+
1500.0,
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
@pytest.fixture
|
55
|
+
def dry_mass() -> Mass:
|
56
|
+
return Mass.kilograms(100.0)
|
57
|
+
|
58
|
+
|
59
|
+
@pytest.fixture
|
60
|
+
def wet_mass() -> Mass:
|
61
|
+
return Mass.kilograms(10.0)
|
62
|
+
|
63
|
+
|
64
|
+
@pytest.fixture
|
65
|
+
def cross_sectional_surface_area() -> float:
|
66
|
+
return 1.0
|
67
|
+
|
68
|
+
|
69
|
+
@pytest.fixture
|
70
|
+
def drag_coefficient() -> float:
|
71
|
+
return 2.2
|
72
|
+
|
73
|
+
|
74
|
+
@pytest.fixture
|
75
|
+
def satellite_system(
|
76
|
+
dry_mass: Mass,
|
77
|
+
cross_sectional_surface_area: float,
|
78
|
+
drag_coefficient: float,
|
79
|
+
propulsion_system: PropulsionSystem,
|
80
|
+
) -> SatelliteSystem:
|
81
|
+
satellite_geometry = Composite(
|
82
|
+
Cuboid(
|
83
|
+
Point(0.0, 0.0, 0.0),
|
84
|
+
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
|
85
|
+
[1.0, 0.0, 0.0],
|
86
|
+
)
|
87
|
+
)
|
88
|
+
inertia_tensor = np.ndarray(shape=(3, 3))
|
89
|
+
|
90
|
+
return SatelliteSystem(
|
91
|
+
dry_mass,
|
92
|
+
satellite_geometry,
|
93
|
+
inertia_tensor,
|
94
|
+
cross_sectional_surface_area,
|
95
|
+
drag_coefficient,
|
96
|
+
propulsion_system,
|
97
|
+
)
|
98
|
+
|
99
|
+
|
100
|
+
@pytest.fixture
|
101
|
+
def environment() -> Environment:
|
102
|
+
return Environment(
|
103
|
+
Instant.J2000(),
|
104
|
+
[
|
105
|
+
Earth.from_models(
|
106
|
+
EarthGravitationalModel(EarthGravitationalModel.Type.Spherical),
|
107
|
+
EarthMagneticModel(EarthMagneticModel.Type.Undefined),
|
108
|
+
EarthAtmosphericModel(EarthAtmosphericModel.Type.Exponential),
|
109
|
+
)
|
110
|
+
],
|
111
|
+
)
|
112
|
+
|
113
|
+
|
114
|
+
@pytest.fixture
|
115
|
+
def coordinate_broker() -> CoordinateBroker:
|
116
|
+
return CoordinateBroker(
|
117
|
+
[
|
118
|
+
CartesianPosition.default(),
|
119
|
+
CartesianVelocity.default(),
|
120
|
+
CoordinateSubset.mass(),
|
121
|
+
CoordinateSubset.surface_area(),
|
122
|
+
CoordinateSubset.drag_coefficient(),
|
123
|
+
]
|
124
|
+
)
|
125
|
+
|
126
|
+
|
127
|
+
@pytest.fixture
|
128
|
+
def cartesian_coordinates() -> list[float]:
|
129
|
+
return [
|
130
|
+
717094.039086306,
|
131
|
+
-6872433.2241124,
|
132
|
+
46175.9696673281,
|
133
|
+
-970.650826004612,
|
134
|
+
-45.4598114773158,
|
135
|
+
7529.82424886455,
|
136
|
+
]
|
137
|
+
|
138
|
+
|
139
|
+
@pytest.fixture
|
140
|
+
def state(
|
141
|
+
cartesian_coordinates: list[float],
|
142
|
+
dry_mass: Mass,
|
143
|
+
wet_mass: Mass,
|
144
|
+
cross_sectional_surface_area: float,
|
145
|
+
drag_coefficient: float,
|
146
|
+
coordinate_broker: CoordinateBroker,
|
147
|
+
) -> State:
|
148
|
+
frame: Frame = Frame.GCRF()
|
149
|
+
instant: Instant = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
|
150
|
+
coordinates = [
|
151
|
+
*cartesian_coordinates,
|
152
|
+
dry_mass.in_kilograms() + wet_mass.in_kilograms(),
|
153
|
+
cross_sectional_surface_area,
|
154
|
+
drag_coefficient,
|
155
|
+
]
|
156
|
+
|
157
|
+
return State(
|
158
|
+
instant,
|
159
|
+
[
|
160
|
+
717094.039086306,
|
161
|
+
-6872433.2241124,
|
162
|
+
46175.9696673281,
|
163
|
+
-970.650826004612,
|
164
|
+
-45.4598114773158,
|
165
|
+
7529.82424886455,
|
166
|
+
dry_mass.in_kilograms() + wet_mass.in_kilograms(),
|
167
|
+
cross_sectional_surface_area,
|
168
|
+
drag_coefficient,
|
169
|
+
],
|
170
|
+
frame,
|
171
|
+
coordinate_broker,
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
@pytest.fixture
|
176
|
+
def dynamics(environment: Environment) -> list:
|
177
|
+
return Dynamics.from_environment(environment)
|
178
|
+
|
179
|
+
|
180
|
+
@pytest.fixture
|
181
|
+
def numerical_solver() -> NumericalSolver:
|
182
|
+
return NumericalSolver.default_conditional()
|
183
|
+
|
184
|
+
|
185
|
+
@pytest.fixture
|
186
|
+
def duration() -> Duration:
|
187
|
+
return Duration.minutes(5.0)
|
188
|
+
|
189
|
+
|
190
|
+
@pytest.fixture
|
191
|
+
def instant_condition(state: State, duration: Duration) -> InstantCondition:
|
192
|
+
return InstantCondition(
|
193
|
+
RealCondition.Criterion.AnyCrossing, state.get_instant() + duration
|
194
|
+
)
|
195
|
+
|
196
|
+
|
197
|
+
@pytest.fixture
|
198
|
+
def sma() -> Length:
|
199
|
+
return Length.kilometers(6907.000)
|
200
|
+
|
201
|
+
|
202
|
+
@pytest.fixture
|
203
|
+
def gravitational_parameter() -> Derived:
|
204
|
+
return EarthGravitationalModel.spherical.gravitational_parameter
|
205
|
+
|
206
|
+
|
207
|
+
@pytest.fixture
|
208
|
+
def constant_thrust() -> ConstantThrust:
|
209
|
+
return ConstantThrust(
|
210
|
+
thrust_direction=LocalOrbitalFrameDirection(
|
211
|
+
vector=[1.0, 0.0, 0.0],
|
212
|
+
local_orbital_frame_factory=LocalOrbitalFrameFactory.VNC(Frame.GCRF()),
|
213
|
+
)
|
214
|
+
)
|
215
|
+
|
216
|
+
|
217
|
+
@pytest.fixture
|
218
|
+
def thruster_dynamics(
|
219
|
+
satellite_system: SatelliteSystem, constant_thrust: ConstantThrust
|
220
|
+
) -> ConstantThrust:
|
221
|
+
return Thruster(
|
222
|
+
satellite_system=satellite_system,
|
223
|
+
guidance_law=constant_thrust,
|
224
|
+
)
|
225
|
+
|
226
|
+
|
227
|
+
@pytest.fixture
|
228
|
+
def sma_target(sma: Length) -> EventCondition.Target:
|
229
|
+
return EventCondition.Target(sma)
|
230
|
+
|
231
|
+
|
232
|
+
@pytest.fixture
|
233
|
+
def sma_condition(
|
234
|
+
sma_target: EventCondition.Target, gravitational_parameter: Derived
|
235
|
+
) -> COECondition:
|
236
|
+
return COECondition.semi_major_axis(
|
237
|
+
criterion=RealCondition.Criterion.AnyCrossing,
|
238
|
+
frame=Frame.GCRF(),
|
239
|
+
semi_major_axis=sma_target,
|
240
|
+
gravitational_parameter=gravitational_parameter,
|
241
|
+
)
|
242
|
+
|
243
|
+
|
244
|
+
@pytest.fixture
|
245
|
+
def coast_duration_segment(
|
246
|
+
instant_condition: InstantCondition,
|
247
|
+
dynamics: list[Dynamics],
|
248
|
+
numerical_solver: NumericalSolver,
|
249
|
+
):
|
250
|
+
return Segment.coast(
|
251
|
+
name="duration coast",
|
252
|
+
event_condition=instant_condition,
|
253
|
+
dynamics=dynamics,
|
254
|
+
numerical_solver=numerical_solver,
|
255
|
+
)
|
256
|
+
|
257
|
+
|
258
|
+
@pytest.fixture
|
259
|
+
def coast_sma_segment(
|
260
|
+
sma_condition: COECondition,
|
261
|
+
dynamics: list[Dynamics],
|
262
|
+
numerical_solver: NumericalSolver,
|
263
|
+
):
|
264
|
+
return Segment.coast(
|
265
|
+
name="sma coast",
|
266
|
+
event_condition=sma_condition,
|
267
|
+
dynamics=dynamics,
|
268
|
+
numerical_solver=numerical_solver,
|
269
|
+
)
|
270
|
+
|
271
|
+
|
272
|
+
@pytest.fixture
|
273
|
+
def thrust_segment(
|
274
|
+
sma_condition: RealCondition,
|
275
|
+
thruster_dynamics: Thruster,
|
276
|
+
dynamics: list[Dynamics],
|
277
|
+
numerical_solver: NumericalSolver,
|
278
|
+
):
|
279
|
+
return Segment.maneuver(
|
280
|
+
name="duration thrust",
|
281
|
+
event_condition=sma_condition,
|
282
|
+
thruster_dynamics=thruster_dynamics,
|
283
|
+
dynamics=dynamics,
|
284
|
+
numerical_solver=numerical_solver,
|
285
|
+
)
|
286
|
+
|
287
|
+
|
288
|
+
@pytest.fixture
|
289
|
+
def segments(
|
290
|
+
coast_duration_segment: Segment,
|
291
|
+
thrust_segment: Segment,
|
292
|
+
) -> list[Segment]:
|
293
|
+
return [coast_duration_segment, thrust_segment]
|
294
|
+
|
295
|
+
|
296
|
+
@pytest.fixture
|
297
|
+
def maximum_propagation_duration() -> Duration:
|
298
|
+
return Duration.days(2.0)
|
299
|
+
|
300
|
+
|
301
|
+
@pytest.fixture
|
302
|
+
def repetition_count() -> int:
|
303
|
+
return 1
|
304
|
+
|
305
|
+
|
306
|
+
@pytest.fixture
|
307
|
+
def sequence(
|
308
|
+
segments: list[Segment],
|
309
|
+
numerical_solver: NumericalSolver,
|
310
|
+
dynamics: list,
|
311
|
+
maximum_propagation_duration: Duration,
|
312
|
+
):
|
313
|
+
return Sequence(
|
314
|
+
segments=segments,
|
315
|
+
dynamics=dynamics,
|
316
|
+
numerical_solver=numerical_solver,
|
317
|
+
maximum_propagation_duration=maximum_propagation_duration,
|
318
|
+
)
|
319
|
+
|
320
|
+
|
321
|
+
@pytest.fixture
|
322
|
+
def segment_solution(
|
323
|
+
dynamics: list,
|
324
|
+
state: State,
|
325
|
+
):
|
326
|
+
return Segment.Solution(
|
327
|
+
name="A Segment Solution",
|
328
|
+
dynamics=dynamics,
|
329
|
+
states=[
|
330
|
+
state,
|
331
|
+
],
|
332
|
+
condition_is_satisfied=True,
|
333
|
+
segment_type=Segment.Type.Maneuver,
|
334
|
+
)
|
335
|
+
|
336
|
+
|
337
|
+
@pytest.fixture
|
338
|
+
def instants(state: State) -> list[Instant]:
|
339
|
+
return [state.get_instant(), state.get_instant() + Duration.minutes(1.0)]
|
340
|
+
|
341
|
+
|
342
|
+
class TestSequence:
|
343
|
+
def test_get_segments(
|
344
|
+
self,
|
345
|
+
sequence: Sequence,
|
346
|
+
segments: list[Segment],
|
347
|
+
):
|
348
|
+
assert len(sequence.get_segments()) == len(segments)
|
349
|
+
|
350
|
+
def test_get_numerical_solver(
|
351
|
+
self,
|
352
|
+
sequence: Sequence,
|
353
|
+
numerical_solver: NumericalSolver,
|
354
|
+
):
|
355
|
+
assert sequence.get_numerical_solver() == numerical_solver
|
356
|
+
|
357
|
+
def test_get_dynamics(
|
358
|
+
self,
|
359
|
+
sequence: Sequence,
|
360
|
+
dynamics: list,
|
361
|
+
):
|
362
|
+
assert len(sequence.get_dynamics()) == len(dynamics)
|
363
|
+
|
364
|
+
def test_get_maximum_propagation_duration(
|
365
|
+
self,
|
366
|
+
sequence: Sequence,
|
367
|
+
maximum_propagation_duration: Duration,
|
368
|
+
):
|
369
|
+
assert sequence.get_maximum_propagation_duration() == maximum_propagation_duration
|
370
|
+
|
371
|
+
def test_add_segment(
|
372
|
+
self,
|
373
|
+
sequence: Sequence,
|
374
|
+
coast_duration_segment: Segment,
|
375
|
+
):
|
376
|
+
segments_count: int = len(sequence.get_segments())
|
377
|
+
|
378
|
+
sequence.add_segment(coast_duration_segment)
|
379
|
+
|
380
|
+
assert len(sequence.get_segments()) == segments_count + 1
|
381
|
+
|
382
|
+
segments_count = len(sequence.get_segments())
|
383
|
+
|
384
|
+
sequence.add_segments([coast_duration_segment, coast_duration_segment])
|
385
|
+
|
386
|
+
assert len(sequence.get_segments()) == segments_count + 2
|
387
|
+
|
388
|
+
def test_add_coast_segment(
|
389
|
+
self,
|
390
|
+
sequence: Sequence,
|
391
|
+
instant_condition: InstantCondition,
|
392
|
+
):
|
393
|
+
segments_count: int = len(sequence.get_segments())
|
394
|
+
|
395
|
+
sequence.add_coast_segment(instant_condition)
|
396
|
+
|
397
|
+
assert len(sequence.get_segments()) == segments_count + 1
|
398
|
+
|
399
|
+
def test_add_maneuver_segment(
|
400
|
+
self,
|
401
|
+
sequence: Sequence,
|
402
|
+
instant_condition: InstantCondition,
|
403
|
+
thruster_dynamics: Thruster,
|
404
|
+
):
|
405
|
+
segments_count: int = len(sequence.get_segments())
|
406
|
+
|
407
|
+
sequence.add_maneuver_segment(instant_condition, thruster_dynamics)
|
408
|
+
|
409
|
+
assert len(sequence.get_segments()) == segments_count + 1
|
410
|
+
|
411
|
+
def test_create_sequence_solution(
|
412
|
+
self,
|
413
|
+
segment_solution: Segment.Solution,
|
414
|
+
):
|
415
|
+
solution: Sequence.Solution = Sequence.Solution(
|
416
|
+
segment_solutions=[
|
417
|
+
segment_solution,
|
418
|
+
],
|
419
|
+
execution_is_complete=True,
|
420
|
+
)
|
421
|
+
|
422
|
+
assert solution is not None
|
423
|
+
assert len(solution.segment_solutions) == 1
|
424
|
+
assert solution.execution_is_complete
|
425
|
+
|
426
|
+
def test_solve(
|
427
|
+
self,
|
428
|
+
state: State,
|
429
|
+
repetition_count: int,
|
430
|
+
sequence: Sequence,
|
431
|
+
segments: list[Segment],
|
432
|
+
instants: list[Instant],
|
433
|
+
numerical_solver: NumericalSolver,
|
434
|
+
):
|
435
|
+
solution = sequence.solve(
|
436
|
+
state=state,
|
437
|
+
repetition_count=repetition_count,
|
438
|
+
)
|
439
|
+
|
440
|
+
assert len(solution.segment_solutions) == len(segments)
|
441
|
+
|
442
|
+
assert solution.execution_is_complete
|
443
|
+
|
444
|
+
assert solution.access_start_instant() is not None
|
445
|
+
assert solution.access_end_instant() is not None
|
446
|
+
|
447
|
+
assert solution.get_states() is not None
|
448
|
+
assert solution.get_initial_mass() is not None
|
449
|
+
assert solution.get_final_mass() is not None
|
450
|
+
assert solution.get_propagation_duration() is not None
|
451
|
+
|
452
|
+
assert solution.compute_delta_mass() is not None
|
453
|
+
assert solution.compute_delta_v(1500.0) is not None
|
454
|
+
|
455
|
+
propagated_states = solution.calculate_states_at(
|
456
|
+
instants,
|
457
|
+
numerical_solver,
|
458
|
+
)
|
459
|
+
|
460
|
+
assert propagated_states is not None
|
461
|
+
assert len(propagated_states) == len(instants)
|
462
|
+
|
463
|
+
def test_solve_to_condition(
|
464
|
+
self,
|
465
|
+
state: State,
|
466
|
+
sequence: Sequence,
|
467
|
+
instant_condition: InstantCondition,
|
468
|
+
):
|
469
|
+
assert sequence.solve_to_condition(state, instant_condition) is not None
|
470
|
+
assert (
|
471
|
+
sequence.solve_to_condition(
|
472
|
+
state=state,
|
473
|
+
event_condition=instant_condition,
|
474
|
+
maximum_propagation_duration_limit=Duration.hours(1.0),
|
475
|
+
)
|
476
|
+
is not None
|
477
|
+
)
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
|
8
|
+
|
9
|
+
from ostk.physics.time import Instant
|
10
|
+
from ostk.physics.time import DateTime
|
11
|
+
from ostk.physics.time import Scale
|
12
|
+
from ostk.physics.coordinate import Position
|
13
|
+
from ostk.physics.coordinate import Velocity
|
14
|
+
from ostk.physics.coordinate import Frame
|
15
|
+
|
16
|
+
from ostk.astrodynamics.trajectory import State
|
17
|
+
from ostk.astrodynamics.trajectory.state import CoordinateBroker
|
18
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import (
|
19
|
+
CartesianPosition,
|
20
|
+
CartesianVelocity,
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
@pytest.fixture()
|
25
|
+
def instant() -> Instant:
|
26
|
+
return Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
|
27
|
+
|
28
|
+
|
29
|
+
@pytest.fixture
|
30
|
+
def frame() -> Frame:
|
31
|
+
return Frame.GCRF()
|
32
|
+
|
33
|
+
|
34
|
+
@pytest.fixture()
|
35
|
+
def position(frame: Frame) -> Position:
|
36
|
+
return Position.meters([6371000.0, 0.0, 0.0], frame)
|
37
|
+
|
38
|
+
|
39
|
+
@pytest.fixture()
|
40
|
+
def velocity(frame: Frame) -> Velocity:
|
41
|
+
return Velocity.meters_per_second([7600.0, 0.0, 0.0], frame)
|
42
|
+
|
43
|
+
|
44
|
+
@pytest.fixture()
|
45
|
+
def attitude() -> Quaternion:
|
46
|
+
return Quaternion([0.0, 0.0, 0.0, 1.0], Quaternion.Format.XYZS)
|
47
|
+
|
48
|
+
|
49
|
+
@pytest.fixture()
|
50
|
+
def angular_velocity() -> np.ndarray:
|
51
|
+
return np.array([-1.0, -2.0, -3.0])
|
52
|
+
|
53
|
+
|
54
|
+
@pytest.fixture
|
55
|
+
def state(
|
56
|
+
instant: Instant,
|
57
|
+
position: Position,
|
58
|
+
velocity: Velocity,
|
59
|
+
) -> State:
|
60
|
+
return State(instant, position, velocity)
|
61
|
+
|
62
|
+
|
63
|
+
@pytest.fixture
|
64
|
+
def profile_state(
|
65
|
+
instant: Instant,
|
66
|
+
position: Position,
|
67
|
+
velocity: Velocity,
|
68
|
+
attitude: Quaternion,
|
69
|
+
angular_velocity: np.ndarray,
|
70
|
+
frame: Frame,
|
71
|
+
) -> State:
|
72
|
+
return State(instant, position, velocity, attitude, angular_velocity, frame)
|
73
|
+
|
74
|
+
|
75
|
+
@pytest.fixture
|
76
|
+
def coordinate_broker() -> CoordinateBroker:
|
77
|
+
return CoordinateBroker([CartesianPosition.default(), CartesianVelocity.default()])
|
78
|
+
|
79
|
+
|
80
|
+
class TestState:
|
81
|
+
def test_constructor_position_velocity(
|
82
|
+
self,
|
83
|
+
instant: Instant,
|
84
|
+
position: Position,
|
85
|
+
velocity: Velocity,
|
86
|
+
):
|
87
|
+
state = State(instant, position, velocity)
|
88
|
+
assert state is not None
|
89
|
+
assert isinstance(state, State)
|
90
|
+
assert state.is_defined()
|
91
|
+
|
92
|
+
def test_constructor_position_velocity_attitude_angular_velocity(
|
93
|
+
self,
|
94
|
+
instant: Instant,
|
95
|
+
frame: Frame,
|
96
|
+
position: Position,
|
97
|
+
velocity: Velocity,
|
98
|
+
attitude: Quaternion,
|
99
|
+
angular_velocity: np.ndarray,
|
100
|
+
):
|
101
|
+
state = State(instant, position, velocity, attitude, angular_velocity, frame)
|
102
|
+
assert state is not None
|
103
|
+
assert isinstance(state, State)
|
104
|
+
assert state.is_defined()
|
105
|
+
|
106
|
+
def test_explicit_constructor(
|
107
|
+
self,
|
108
|
+
instant: Instant,
|
109
|
+
position: Position,
|
110
|
+
velocity: Velocity,
|
111
|
+
frame: Frame,
|
112
|
+
coordinate_broker: CoordinateBroker,
|
113
|
+
):
|
114
|
+
state = State(instant, position, velocity)
|
115
|
+
assert state is not None
|
116
|
+
assert isinstance(state, State)
|
117
|
+
assert state.is_defined()
|
118
|
+
|
119
|
+
state = State(
|
120
|
+
instant,
|
121
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
122
|
+
frame,
|
123
|
+
coordinate_broker,
|
124
|
+
)
|
125
|
+
|
126
|
+
assert state is not None
|
127
|
+
assert isinstance(state, State)
|
128
|
+
assert state.is_defined()
|
129
|
+
|
130
|
+
def test_subsets_constructor(
|
131
|
+
self,
|
132
|
+
instant: Instant,
|
133
|
+
position: Position,
|
134
|
+
velocity: Velocity,
|
135
|
+
frame: Frame,
|
136
|
+
):
|
137
|
+
state = State(
|
138
|
+
instant,
|
139
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
140
|
+
frame,
|
141
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
142
|
+
)
|
143
|
+
|
144
|
+
assert state is not None
|
145
|
+
assert isinstance(state, State)
|
146
|
+
assert state.is_defined()
|
147
|
+
|
148
|
+
def test_custom_state_class_generator(
|
149
|
+
self,
|
150
|
+
instant: Instant,
|
151
|
+
position: Position,
|
152
|
+
velocity: Velocity,
|
153
|
+
frame: Frame,
|
154
|
+
):
|
155
|
+
state = State(
|
156
|
+
instant,
|
157
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
158
|
+
frame,
|
159
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
160
|
+
)
|
161
|
+
|
162
|
+
MySuperFunState: type = State.template(
|
163
|
+
frame,
|
164
|
+
[CartesianPosition.default(), CartesianVelocity.default()],
|
165
|
+
)
|
166
|
+
|
167
|
+
custom_state: MySuperFunState = MySuperFunState(
|
168
|
+
instant,
|
169
|
+
np.append(position.get_coordinates(), velocity.get_coordinates()),
|
170
|
+
)
|
171
|
+
|
172
|
+
assert custom_state is not None
|
173
|
+
assert isinstance(custom_state, MySuperFunState)
|
174
|
+
assert isinstance(state, State)
|
175
|
+
assert custom_state.is_defined()
|
176
|
+
|
177
|
+
assert custom_state == state
|
178
|
+
assert custom_state is not state
|
179
|
+
|
180
|
+
def test_comparators(self, state: State):
|
181
|
+
assert (state == state) is True
|
182
|
+
assert (state != state) is False
|
183
|
+
|
184
|
+
def test_operators(self, state: State):
|
185
|
+
assert isinstance(state + state, State)
|
186
|
+
assert isinstance(state - state, State)
|
187
|
+
|
188
|
+
def test_getters(
|
189
|
+
self,
|
190
|
+
profile_state: State,
|
191
|
+
instant: Instant,
|
192
|
+
position: Position,
|
193
|
+
velocity: Velocity,
|
194
|
+
attitude: Quaternion,
|
195
|
+
angular_velocity: np.ndarray,
|
196
|
+
frame: Frame,
|
197
|
+
):
|
198
|
+
assert profile_state.get_instant() == instant
|
199
|
+
assert profile_state.get_position() == position
|
200
|
+
assert profile_state.get_velocity() == velocity
|
201
|
+
assert profile_state.get_attitude() == attitude
|
202
|
+
assert np.all(profile_state.get_angular_velocity() == angular_velocity)
|
203
|
+
assert profile_state.has_subset(CartesianPosition.default())
|
204
|
+
assert profile_state.has_subset(CartesianVelocity.default())
|
205
|
+
assert profile_state.get_frame() == frame
|
206
|
+
assert profile_state.get_coordinates() is not None
|
207
|
+
assert profile_state.get_coordinate_subsets() is not None
|
208
|
+
|
209
|
+
def test_in_frame(
|
210
|
+
self,
|
211
|
+
state: State,
|
212
|
+
frame: Frame,
|
213
|
+
):
|
214
|
+
assert state.in_frame(frame) == state
|
215
|
+
assert state.in_frame(Frame.ITRF()) != state
|
216
|
+
|
217
|
+
def test_extract_coordinate(
|
218
|
+
self,
|
219
|
+
state: State,
|
220
|
+
):
|
221
|
+
position_coordinates = state.extract_coordinate(CartesianPosition.default())
|
222
|
+
velocity_coordinates = state.extract_coordinate(CartesianVelocity.default())
|
223
|
+
|
224
|
+
assert len(position_coordinates) == 3
|
225
|
+
assert len(velocity_coordinates) == 3
|
226
|
+
assert (position_coordinates == state.get_position().get_coordinates()).all()
|
227
|
+
assert (velocity_coordinates == state.get_velocity().get_coordinates()).all()
|
228
|
+
|
229
|
+
def test_extract_coordinates(
|
230
|
+
self,
|
231
|
+
state: State,
|
232
|
+
):
|
233
|
+
pv_coordinates = state.extract_coordinates(
|
234
|
+
[CartesianPosition.default(), CartesianVelocity.default()]
|
235
|
+
)
|
236
|
+
assert len(pv_coordinates) == 6
|
237
|
+
assert (pv_coordinates == state.get_coordinates()).all()
|