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.
- open_space_toolkit_astrodynamics-17.2.0.dist-info/METADATA +30 -0
- open_space_toolkit_astrodynamics-17.2.0.dist-info/RECORD +151 -0
- open_space_toolkit_astrodynamics-17.2.0.dist-info/WHEEL +5 -0
- open_space_toolkit_astrodynamics-17.2.0.dist-info/top_level.txt +1 -0
- open_space_toolkit_astrodynamics-17.2.0.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/__init__.pyi +720 -0
- ostk/astrodynamics/access.pyi +577 -0
- ostk/astrodynamics/conjunction/__init__.pyi +121 -0
- ostk/astrodynamics/conjunction/close_approach.pyi +89 -0
- ostk/astrodynamics/conjunction/message/__init__.pyi +3 -0
- ostk/astrodynamics/conjunction/message/ccsds.pyi +705 -0
- ostk/astrodynamics/converters.py +130 -0
- ostk/astrodynamics/converters.pyi +58 -0
- ostk/astrodynamics/data/__init__.pyi +3 -0
- ostk/astrodynamics/data/provider.pyi +22 -0
- ostk/astrodynamics/dataframe.py +597 -0
- ostk/astrodynamics/display.py +281 -0
- ostk/astrodynamics/dynamics.pyi +311 -0
- ostk/astrodynamics/eclipse.pyi +70 -0
- ostk/astrodynamics/estimator.pyi +268 -0
- ostk/astrodynamics/event_condition.pyi +910 -0
- ostk/astrodynamics/flight/__init__.pyi +626 -0
- ostk/astrodynamics/flight/profile/__init__.pyi +99 -0
- ostk/astrodynamics/flight/profile/model.pyi +179 -0
- ostk/astrodynamics/flight/system.pyi +268 -0
- ostk/astrodynamics/guidance_law.pyi +416 -0
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.17 +0 -0
- ostk/astrodynamics/pytrajectory/__init__.py +1 -0
- ostk/astrodynamics/pytrajectory/__init__.pyi +3 -0
- ostk/astrodynamics/pytrajectory/pystate.py +263 -0
- ostk/astrodynamics/pytrajectory/pystate.pyi +66 -0
- ostk/astrodynamics/solver.pyi +432 -0
- ostk/astrodynamics/test/__init__.py +1 -0
- ostk/astrodynamics/test/access/__init__.py +1 -0
- ostk/astrodynamics/test/access/test_generator.py +319 -0
- ostk/astrodynamics/test/access/test_visibility_criterion.py +201 -0
- ostk/astrodynamics/test/conftest.py +119 -0
- ostk/astrodynamics/test/conjunction/close_approach/__init__.py +0 -0
- ostk/astrodynamics/test/conjunction/close_approach/test_generator.py +228 -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/conjunction/test_close_approach.py +244 -0
- ostk/astrodynamics/test/data/provider/test_off_nadir.py +58 -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 +157 -0
- ostk/astrodynamics/test/eclipse/__init__.py +1 -0
- ostk/astrodynamics/test/eclipse/test_generator.py +138 -0
- ostk/astrodynamics/test/estimator/test_orbit_determination_solver.py +261 -0
- ostk/astrodynamics/test/estimator/test_tle_solver.py +216 -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_brouwer_lyddane_mean_long_condition.py +135 -0
- ostk/astrodynamics/test/event_condition/test_coe_condition.py +135 -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/profile/model/test_tabulated_profile.py +115 -0
- ostk/astrodynamics/test/flight/system/__init__.py +1 -0
- ostk/astrodynamics/test/flight/system/test_propulsion_system.py +64 -0
- ostk/astrodynamics/test/flight/system/test_satellite_system.py +83 -0
- ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
- ostk/astrodynamics/test/flight/test_maneuver.py +231 -0
- ostk/astrodynamics/test/flight/test_profile.py +293 -0
- ostk/astrodynamics/test/flight/test_system.py +45 -0
- ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +177 -0
- ostk/astrodynamics/test/guidance_law/test_guidance_law.py +60 -0
- ostk/astrodynamics/test/guidance_law/test_heterogeneous_guidance_law.py +164 -0
- ostk/astrodynamics/test/guidance_law/test_qlaw.py +209 -0
- ostk/astrodynamics/test/solvers/__init__.py +1 -0
- ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +196 -0
- ostk/astrodynamics/test/solvers/test_least_squares_solver.py +334 -0
- ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +161 -0
- ostk/astrodynamics/test/test_access.py +128 -0
- ostk/astrodynamics/test/test_converters.py +290 -0
- ostk/astrodynamics/test/test_dataframe.py +1355 -0
- ostk/astrodynamics/test/test_display.py +184 -0
- ostk/astrodynamics/test/test_event_condition.py +80 -0
- ostk/astrodynamics/test/test_import.py +26 -0
- ostk/astrodynamics/test/test_root_solver.py +70 -0
- ostk/astrodynamics/test/test_trajectory.py +126 -0
- ostk/astrodynamics/test/test_utilities.py +338 -0
- ostk/astrodynamics/test/test_viewer.py +318 -0
- ostk/astrodynamics/test/trajectory/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/model/test_nadir_trajectory.py +87 -0
- ostk/astrodynamics/test/trajectory/model/test_tabulated_trajectory.py +303 -0
- ostk/astrodynamics/test/trajectory/model/test_target_scan_trajectory.py +126 -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 +305 -0
- ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
- ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +337 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
- ostk/astrodynamics/test/trajectory/orbit/models/test_modified_equinoctial.py +142 -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_acceleration.py +136 -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 +58 -0
- ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +316 -0
- ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
- ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +119 -0
- ostk/astrodynamics/test/trajectory/test_model.py +1 -0
- ostk/astrodynamics/test/trajectory/test_orbit.py +212 -0
- ostk/astrodynamics/test/trajectory/test_propagator.py +452 -0
- ostk/astrodynamics/test/trajectory/test_segment.py +694 -0
- ostk/astrodynamics/test/trajectory/test_sequence.py +550 -0
- ostk/astrodynamics/test/trajectory/test_state.py +629 -0
- ostk/astrodynamics/test/trajectory/test_state_builder.py +172 -0
- ostk/astrodynamics/trajectory/__init__.pyi +1982 -0
- ostk/astrodynamics/trajectory/model.pyi +259 -0
- ostk/astrodynamics/trajectory/orbit/__init__.pyi +349 -0
- ostk/astrodynamics/trajectory/orbit/message/__init__.pyi +3 -0
- ostk/astrodynamics/trajectory/orbit/message/spacex.pyi +264 -0
- ostk/astrodynamics/trajectory/orbit/model/__init__.pyi +648 -0
- ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi +121 -0
- ostk/astrodynamics/trajectory/orbit/model/kepler.pyi +709 -0
- ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi +330 -0
- ostk/astrodynamics/trajectory/state/__init__.pyi +402 -0
- ostk/astrodynamics/trajectory/state/coordinate_subset.pyi +208 -0
- ostk/astrodynamics/utilities.py +396 -0
- ostk/astrodynamics/viewer.py +851 -0
|
@@ -0,0 +1,231 @@
|
|
|
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 Interval
|
|
9
|
+
from ostk.physics.time import Duration
|
|
10
|
+
from ostk.physics.coordinate import Frame
|
|
11
|
+
from ostk.physics.unit import Mass
|
|
12
|
+
from ostk.physics.unit import Angle
|
|
13
|
+
|
|
14
|
+
from ostk.astrodynamics.dynamics import Tabulated as TabulatedDynamics
|
|
15
|
+
from ostk.astrodynamics.flight import Maneuver
|
|
16
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameDirection
|
|
17
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
|
|
18
|
+
from ostk.astrodynamics.trajectory import State
|
|
19
|
+
from ostk.astrodynamics.trajectory.state import CoordinateSubset
|
|
20
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
|
|
21
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
|
|
22
|
+
from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianAcceleration
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def instants() -> list[Instant]:
|
|
27
|
+
return [
|
|
28
|
+
Instant.J2000(),
|
|
29
|
+
Instant.J2000() + Duration.seconds(30.0),
|
|
30
|
+
Instant.J2000() + Duration.seconds(35.0),
|
|
31
|
+
Instant.J2000() + Duration.seconds(37.0),
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def acceleration_profile() -> list[np.ndarray]:
|
|
37
|
+
return [
|
|
38
|
+
np.array([1.0e-3, 0.0e-3, 0.0e-3]),
|
|
39
|
+
np.array([0.0e-3, 1.0e-3, 0.0e-3]),
|
|
40
|
+
np.array([0.0e-3, 0.0e-3, 1.0e-3]),
|
|
41
|
+
np.array([1.0e-3, 1.0e-3, 1.0e-3]),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.fixture
|
|
46
|
+
def frame() -> Frame:
|
|
47
|
+
return Frame.GCRF()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.fixture
|
|
51
|
+
def mass_flow_rate_profile() -> list[float]:
|
|
52
|
+
return [-1.0e-5, -1.1e-5, -0.9e-5, -1.0e-5]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@pytest.fixture
|
|
56
|
+
def coordinate_subsets() -> list[CoordinateSubset]:
|
|
57
|
+
return [
|
|
58
|
+
CartesianPosition.default(),
|
|
59
|
+
CartesianVelocity.default(),
|
|
60
|
+
CartesianAcceleration.thrust_acceleration(),
|
|
61
|
+
CoordinateSubset.mass_flow_rate(),
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.fixture
|
|
66
|
+
def states(
|
|
67
|
+
instants: list[Instant],
|
|
68
|
+
acceleration_profile: list[np.ndarray],
|
|
69
|
+
mass_flow_rate_profile: list[float],
|
|
70
|
+
frame: Frame,
|
|
71
|
+
coordinate_subsets: list[CoordinateSubset],
|
|
72
|
+
) -> list[State]:
|
|
73
|
+
states = []
|
|
74
|
+
for instant, acceleration, mass_flow_rate in zip(
|
|
75
|
+
instants, acceleration_profile, mass_flow_rate_profile
|
|
76
|
+
):
|
|
77
|
+
states.append(
|
|
78
|
+
State(
|
|
79
|
+
instant,
|
|
80
|
+
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, *acceleration, mass_flow_rate],
|
|
81
|
+
frame,
|
|
82
|
+
coordinate_subsets,
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
return states
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.fixture
|
|
89
|
+
def maneuver(
|
|
90
|
+
states: list[State],
|
|
91
|
+
) -> Maneuver:
|
|
92
|
+
return Maneuver(states)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@pytest.fixture
|
|
96
|
+
def tabulated_dynamics(
|
|
97
|
+
tabulated_instants: list[Instant],
|
|
98
|
+
tabulated_contribution_profile: np.ndarray,
|
|
99
|
+
tabulated_coordinate_subsets: list[CoordinateSubset],
|
|
100
|
+
frame: Frame,
|
|
101
|
+
) -> TabulatedDynamics:
|
|
102
|
+
return TabulatedDynamics(
|
|
103
|
+
tabulated_instants,
|
|
104
|
+
tabulated_contribution_profile,
|
|
105
|
+
tabulated_coordinate_subsets,
|
|
106
|
+
frame,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class TestManeuver:
|
|
111
|
+
def test_constructor(
|
|
112
|
+
self,
|
|
113
|
+
maneuver: Maneuver,
|
|
114
|
+
):
|
|
115
|
+
assert maneuver is not None
|
|
116
|
+
assert isinstance(maneuver, Maneuver)
|
|
117
|
+
assert maneuver.is_defined()
|
|
118
|
+
|
|
119
|
+
def test_comparators(
|
|
120
|
+
self,
|
|
121
|
+
maneuver: Maneuver,
|
|
122
|
+
):
|
|
123
|
+
assert (maneuver == maneuver) is True
|
|
124
|
+
assert (maneuver != maneuver) is False
|
|
125
|
+
|
|
126
|
+
def test_getters(
|
|
127
|
+
self,
|
|
128
|
+
maneuver: Maneuver,
|
|
129
|
+
states: list[State],
|
|
130
|
+
):
|
|
131
|
+
assert maneuver.is_defined()
|
|
132
|
+
|
|
133
|
+
assert maneuver.get_states() == states
|
|
134
|
+
|
|
135
|
+
assert maneuver.get_interval() == Interval.closed(
|
|
136
|
+
states[0].get_instant(), states[-1].get_instant()
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def test_calculators(
|
|
140
|
+
self,
|
|
141
|
+
maneuver: Maneuver,
|
|
142
|
+
):
|
|
143
|
+
assert maneuver.calculate_delta_v() is not None
|
|
144
|
+
assert maneuver.calculate_delta_mass().in_kilograms() is not None
|
|
145
|
+
assert (
|
|
146
|
+
maneuver.calculate_average_thrust(
|
|
147
|
+
initial_spacecraft_mass=Mass(100.0, Mass.Unit.Kilogram)
|
|
148
|
+
)
|
|
149
|
+
is not None
|
|
150
|
+
)
|
|
151
|
+
assert (
|
|
152
|
+
maneuver.calculate_average_specific_impulse(
|
|
153
|
+
initial_spacecraft_mass=Mass(100.0, Mass.Unit.Kilogram)
|
|
154
|
+
)
|
|
155
|
+
is not None
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def test_to_tabulated_dynamics(
|
|
159
|
+
self,
|
|
160
|
+
maneuver: Maneuver,
|
|
161
|
+
instants: list[Instant],
|
|
162
|
+
acceleration_profile: list[np.ndarray],
|
|
163
|
+
frame: Frame,
|
|
164
|
+
mass_flow_rate_profile: list[float],
|
|
165
|
+
):
|
|
166
|
+
tabulated_dynamics: TabulatedDynamics = maneuver.to_tabulated_dynamics(
|
|
167
|
+
frame=frame
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
assert tabulated_dynamics.is_defined()
|
|
171
|
+
assert tabulated_dynamics.access_instants() == [
|
|
172
|
+
state.get_instant() for state in maneuver.get_states()
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
contribution_profile: np.ndarray = (
|
|
176
|
+
tabulated_dynamics.access_contribution_profile()
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
for i in range(len(instants)):
|
|
180
|
+
assert contribution_profile[i][0:3] == pytest.approx(
|
|
181
|
+
acceleration_profile[i], rel=1e-15
|
|
182
|
+
)
|
|
183
|
+
assert contribution_profile[i][3] == pytest.approx(
|
|
184
|
+
mass_flow_rate_profile[i], rel=1e-15
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
assert tabulated_dynamics.access_frame() == frame
|
|
188
|
+
|
|
189
|
+
def test_calculate_mean_thrust_direction_and_maximum_angular_offset(
|
|
190
|
+
self,
|
|
191
|
+
maneuver: Maneuver,
|
|
192
|
+
):
|
|
193
|
+
mean_thrust_direction_and_maximum_angular_offset = (
|
|
194
|
+
maneuver.calculate_mean_thrust_direction_and_maximum_angular_offset(
|
|
195
|
+
local_orbital_frame_factory=LocalOrbitalFrameFactory.TNW(Frame.GCRF()),
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
assert isinstance(
|
|
199
|
+
mean_thrust_direction_and_maximum_angular_offset[0],
|
|
200
|
+
LocalOrbitalFrameDirection,
|
|
201
|
+
)
|
|
202
|
+
assert isinstance(mean_thrust_direction_and_maximum_angular_offset[1], Angle)
|
|
203
|
+
|
|
204
|
+
def test_to_constant_local_orbital_frame_direction_maneuver(
|
|
205
|
+
self,
|
|
206
|
+
maneuver: Maneuver,
|
|
207
|
+
):
|
|
208
|
+
maneuver: Maneuver = maneuver.to_constant_local_orbital_frame_direction_maneuver(
|
|
209
|
+
local_orbital_frame_factory=LocalOrbitalFrameFactory.TNW(Frame.GCRF()),
|
|
210
|
+
)
|
|
211
|
+
assert maneuver.is_defined()
|
|
212
|
+
|
|
213
|
+
maneuver_with_maximum_allowed_angular_offset: Maneuver = (
|
|
214
|
+
maneuver.to_constant_local_orbital_frame_direction_maneuver(
|
|
215
|
+
local_orbital_frame_factory=LocalOrbitalFrameFactory.TNW(Frame.GCRF()),
|
|
216
|
+
maximum_allowed_angular_offset=Angle.degrees(180.0),
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
assert maneuver_with_maximum_allowed_angular_offset.is_defined()
|
|
220
|
+
|
|
221
|
+
def test_from_constant_mass_flow_rate(
|
|
222
|
+
self,
|
|
223
|
+
states: list[State],
|
|
224
|
+
):
|
|
225
|
+
mass_flow_rate: float = -1.0e-5
|
|
226
|
+
maneuver: Maneuver = Maneuver.constant_mass_flow_rate_profile(
|
|
227
|
+
states=states,
|
|
228
|
+
mass_flow_rate=mass_flow_rate,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
assert maneuver.is_defined()
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
|
|
8
|
+
from ostk.mathematics.curve_fitting import Interpolator
|
|
9
|
+
|
|
10
|
+
from ostk.physics import Environment
|
|
11
|
+
from ostk.physics.time import DateTime
|
|
12
|
+
from ostk.physics.time import Duration
|
|
13
|
+
from ostk.physics.time import Time
|
|
14
|
+
from ostk.physics.time import Scale
|
|
15
|
+
from ostk.physics.time import Instant
|
|
16
|
+
from ostk.physics.unit import Length
|
|
17
|
+
from ostk.physics.unit import Angle
|
|
18
|
+
from ostk.physics.coordinate import Transform
|
|
19
|
+
from ostk.physics.coordinate import Position
|
|
20
|
+
from ostk.physics.coordinate import Velocity
|
|
21
|
+
from ostk.physics.coordinate import Frame
|
|
22
|
+
from ostk.physics.coordinate import Axes
|
|
23
|
+
from ostk.physics.coordinate.frame.provider import Dynamic as DynamicProvider
|
|
24
|
+
|
|
25
|
+
from ostk.astrodynamics import Trajectory
|
|
26
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
27
|
+
from ostk.astrodynamics.trajectory import State
|
|
28
|
+
from ostk.astrodynamics.flight import Profile
|
|
29
|
+
from ostk.astrodynamics.flight.profile.model import Transform as TransformModel
|
|
30
|
+
from ostk.astrodynamics.flight.profile.model import Tabulated as TabulatedModel
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture
|
|
34
|
+
def instant() -> Instant:
|
|
35
|
+
return Instant.date_time(DateTime(2020, 1, 1, 0, 0, 30), Scale.UTC)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def environment() -> Environment:
|
|
40
|
+
return Environment.default()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def orbit(instant: Instant, environment: Environment) -> Orbit:
|
|
45
|
+
return Orbit.sun_synchronous(
|
|
46
|
+
epoch=instant,
|
|
47
|
+
altitude=Length.kilometers(500.0),
|
|
48
|
+
local_time_at_descending_node=Time(14, 0, 0),
|
|
49
|
+
celestial_object=environment.access_celestial_object_with_name("Earth"),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.fixture
|
|
54
|
+
def transform_model() -> TransformModel:
|
|
55
|
+
def dynamic_provider_generator(instant: Instant):
|
|
56
|
+
return Transform.identity(instant)
|
|
57
|
+
|
|
58
|
+
return TransformModel(
|
|
59
|
+
dynamic_provider=DynamicProvider(dynamic_provider_generator),
|
|
60
|
+
frame=Frame.GCRF(),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.fixture
|
|
65
|
+
def tabulated_model() -> TabulatedModel:
|
|
66
|
+
return TabulatedModel(
|
|
67
|
+
states=[
|
|
68
|
+
State(
|
|
69
|
+
instant=Instant.date_time(datetime(2020, 1, 1, 0, 0, 0), Scale.UTC),
|
|
70
|
+
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
|
|
71
|
+
velocity=Velocity.meters_per_second((0.0, 0.0, 0.0), Frame.GCRF()),
|
|
72
|
+
attitude=Quaternion.unit(),
|
|
73
|
+
angular_velocity=(0.0, 0.0, 0.0),
|
|
74
|
+
attitude_frame=Frame.GCRF(),
|
|
75
|
+
),
|
|
76
|
+
State(
|
|
77
|
+
instant=Instant.date_time(datetime(2020, 1, 1, 0, 1, 0), Scale.UTC),
|
|
78
|
+
position=Position.meters((0.0, 0.0, 0.0), Frame.GCRF()),
|
|
79
|
+
velocity=Velocity.meters_per_second((0.0, 0.0, 0.0), Frame.GCRF()),
|
|
80
|
+
attitude=Quaternion.unit(),
|
|
81
|
+
angular_velocity=(0.0, 0.0, 0.0),
|
|
82
|
+
attitude_frame=Frame.GCRF(),
|
|
83
|
+
),
|
|
84
|
+
],
|
|
85
|
+
interpolator_type=Interpolator.Type.Linear,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@pytest.fixture(
|
|
90
|
+
params=[
|
|
91
|
+
"transform_model",
|
|
92
|
+
"tabulated_model",
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
def profile(request) -> Profile:
|
|
96
|
+
model: TransformModel | TabulatedModel = request.getfixturevalue(request.param)
|
|
97
|
+
return Profile(model=model)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# TODO: Add test for target_sliding_ground_velocity
|
|
101
|
+
@pytest.fixture(
|
|
102
|
+
params=[
|
|
103
|
+
# Axis-based constructors
|
|
104
|
+
Profile.Target(Profile.TargetType.GeocentricNadir, Profile.Axis.X),
|
|
105
|
+
Profile.TrajectoryTarget.target_position(
|
|
106
|
+
Trajectory.position(Position.meters((7000000.0, 0.0, 0.0), Frame.ITRF())),
|
|
107
|
+
Profile.Axis.X,
|
|
108
|
+
),
|
|
109
|
+
Profile.TrajectoryTarget.target_velocity(
|
|
110
|
+
Trajectory.position(Position.meters((7000000.0, 0.0, 0.0), Frame.ITRF())),
|
|
111
|
+
Profile.Axis.X,
|
|
112
|
+
),
|
|
113
|
+
Profile.OrientationProfileTarget(
|
|
114
|
+
[
|
|
115
|
+
(Instant.J2000(), [1.0, 0.0, 0.0]),
|
|
116
|
+
(Instant.J2000() + Duration.minutes(1.0), [1.0, 0.0, 0.0]),
|
|
117
|
+
(Instant.J2000() + Duration.minutes(2.0), [1.0, 0.0, 0.0]),
|
|
118
|
+
(Instant.J2000() + Duration.minutes(3.0), [1.0, 0.0, 0.0]),
|
|
119
|
+
],
|
|
120
|
+
Profile.Axis.X,
|
|
121
|
+
False,
|
|
122
|
+
Interpolator.Type.Linear,
|
|
123
|
+
),
|
|
124
|
+
Profile.CustomTarget(
|
|
125
|
+
lambda state: [1.0, 0.0, 0.0],
|
|
126
|
+
Profile.Axis.X,
|
|
127
|
+
),
|
|
128
|
+
# Vector3d-based constructors
|
|
129
|
+
Profile.Target(Profile.TargetType.GeocentricNadir, [1.0, 0.0, 0.0]),
|
|
130
|
+
Profile.TrajectoryTarget.target_position(
|
|
131
|
+
Trajectory.position(Position.meters((7000000.0, 0.0, 0.0), Frame.ITRF())),
|
|
132
|
+
[1.0, 0.0, 0.0],
|
|
133
|
+
),
|
|
134
|
+
Profile.TrajectoryTarget.target_velocity(
|
|
135
|
+
Trajectory.position(Position.meters((7000000.0, 0.0, 0.0), Frame.ITRF())),
|
|
136
|
+
[1.0, 0.0, 0.0],
|
|
137
|
+
),
|
|
138
|
+
Profile.OrientationProfileTarget(
|
|
139
|
+
[
|
|
140
|
+
(Instant.J2000(), [1.0, 0.0, 0.0]),
|
|
141
|
+
(Instant.J2000() + Duration.minutes(1.0), [1.0, 0.0, 0.0]),
|
|
142
|
+
(Instant.J2000() + Duration.minutes(2.0), [1.0, 0.0, 0.0]),
|
|
143
|
+
(Instant.J2000() + Duration.minutes(3.0), [1.0, 0.0, 0.0]),
|
|
144
|
+
],
|
|
145
|
+
[1.0, 0.0, 0.0],
|
|
146
|
+
Interpolator.Type.Linear,
|
|
147
|
+
),
|
|
148
|
+
Profile.CustomTarget(
|
|
149
|
+
lambda state: [1.0, 0.0, 0.0],
|
|
150
|
+
[0.0, 0.0, 1.0],
|
|
151
|
+
),
|
|
152
|
+
]
|
|
153
|
+
)
|
|
154
|
+
def alignment_target(request) -> Profile.Target:
|
|
155
|
+
return request.param
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pytest.fixture
|
|
159
|
+
def clocking_target() -> Profile.Target:
|
|
160
|
+
return Profile.Target(Profile.TargetType.VelocityECI, Profile.Axis.Y)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class TestProfile:
|
|
164
|
+
def test_constructors(self, profile: Profile):
|
|
165
|
+
assert profile is not None
|
|
166
|
+
assert isinstance(profile, Profile)
|
|
167
|
+
|
|
168
|
+
def test_profile_target(self, alignment_target: Profile.Target):
|
|
169
|
+
assert alignment_target is not None
|
|
170
|
+
assert isinstance(alignment_target, Profile.Target)
|
|
171
|
+
|
|
172
|
+
def test_access_model(self, profile: Profile):
|
|
173
|
+
model = profile.access_model()
|
|
174
|
+
|
|
175
|
+
assert model is not None
|
|
176
|
+
|
|
177
|
+
if model.is_transform():
|
|
178
|
+
assert model.as_transform() is not None
|
|
179
|
+
|
|
180
|
+
if model.is_tabulated():
|
|
181
|
+
assert model.as_tabulated() is not None
|
|
182
|
+
|
|
183
|
+
def test_get_state_at(self, profile: Profile, instant: Instant):
|
|
184
|
+
state: State = profile.get_state_at(instant)
|
|
185
|
+
|
|
186
|
+
assert state is not None
|
|
187
|
+
assert isinstance(state, State)
|
|
188
|
+
state.is_defined()
|
|
189
|
+
|
|
190
|
+
def test_get_states_at(self, profile: Profile, instant: Instant):
|
|
191
|
+
states = profile.get_states_at([instant, instant])
|
|
192
|
+
|
|
193
|
+
assert states is not None
|
|
194
|
+
|
|
195
|
+
def test_get_axes_at(self, profile: Profile, instant: Instant):
|
|
196
|
+
axes = profile.get_axes_at(instant)
|
|
197
|
+
|
|
198
|
+
assert axes is not None
|
|
199
|
+
assert isinstance(axes, Axes)
|
|
200
|
+
|
|
201
|
+
def test_construct_body_frame(self, profile: Profile):
|
|
202
|
+
frame_name: str = "test_construct_body_frame"
|
|
203
|
+
|
|
204
|
+
if Frame.exists(frame_name):
|
|
205
|
+
Frame.destruct(frame_name)
|
|
206
|
+
|
|
207
|
+
frame: Frame = profile.construct_body_frame(frame_name)
|
|
208
|
+
assert frame is not None
|
|
209
|
+
|
|
210
|
+
frame_2: Frame = profile.construct_body_frame(frame_name, True)
|
|
211
|
+
assert frame_2 is not None
|
|
212
|
+
|
|
213
|
+
def test_undefined(self):
|
|
214
|
+
profile: Profile = Profile.undefined()
|
|
215
|
+
|
|
216
|
+
assert profile is not None
|
|
217
|
+
assert isinstance(profile, Profile)
|
|
218
|
+
assert profile.is_defined() is False
|
|
219
|
+
|
|
220
|
+
def test_inertial_pointing(self):
|
|
221
|
+
quaternion: Quaternion = Quaternion([0.0, 0.0, 0.0, 1.0], Quaternion.Format.XYZS)
|
|
222
|
+
|
|
223
|
+
trajectory: Trajectory = Trajectory.position(
|
|
224
|
+
Position.meters((0.0, 0.0, 0.0), Frame.ITRF())
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
profile: Profile = Profile.inertial_pointing(trajectory, quaternion)
|
|
228
|
+
|
|
229
|
+
assert profile is not None
|
|
230
|
+
assert isinstance(profile, Profile)
|
|
231
|
+
assert profile.is_defined()
|
|
232
|
+
|
|
233
|
+
def test_local_orbital_frame_pointing(
|
|
234
|
+
self,
|
|
235
|
+
orbit: Orbit,
|
|
236
|
+
):
|
|
237
|
+
profile: Profile = Profile.local_orbital_frame_pointing(
|
|
238
|
+
orbit, Orbit.FrameType.VVLH
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
assert profile is not None
|
|
242
|
+
assert isinstance(profile, Profile)
|
|
243
|
+
assert profile.is_defined()
|
|
244
|
+
|
|
245
|
+
def test_align_and_constrain(
|
|
246
|
+
self,
|
|
247
|
+
orbit: Orbit,
|
|
248
|
+
instant: Instant,
|
|
249
|
+
alignment_target: Profile.Target,
|
|
250
|
+
clocking_target: Profile.Target,
|
|
251
|
+
):
|
|
252
|
+
orientation = Profile.align_and_constrain(
|
|
253
|
+
alignment_target=alignment_target,
|
|
254
|
+
clocking_target=clocking_target,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
assert orientation is not None
|
|
258
|
+
assert orientation(orbit.get_state_at(instant)) is not None
|
|
259
|
+
|
|
260
|
+
def test_custom_pointing(
|
|
261
|
+
self,
|
|
262
|
+
orbit: Orbit,
|
|
263
|
+
alignment_target: Profile.Target,
|
|
264
|
+
clocking_target: Profile.Target,
|
|
265
|
+
):
|
|
266
|
+
profile = Profile.custom_pointing(
|
|
267
|
+
orbit=orbit,
|
|
268
|
+
orientation_generator=Profile.align_and_constrain(
|
|
269
|
+
alignment_target, clocking_target
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
assert profile is not None
|
|
274
|
+
assert profile.is_defined()
|
|
275
|
+
|
|
276
|
+
profile = Profile.custom_pointing(
|
|
277
|
+
orbit=orbit,
|
|
278
|
+
alignment_target=alignment_target,
|
|
279
|
+
clocking_target=clocking_target,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
assert profile is not None
|
|
283
|
+
assert profile.is_defined()
|
|
284
|
+
|
|
285
|
+
profile = Profile.custom_pointing(
|
|
286
|
+
orbit=orbit,
|
|
287
|
+
alignment_target=alignment_target,
|
|
288
|
+
clocking_target=clocking_target,
|
|
289
|
+
angular_offset=Angle.degrees(90.0),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
assert profile is not None
|
|
293
|
+
assert profile.is_defined()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ostk.mathematics.geometry.d3.object import Cuboid
|
|
6
|
+
from ostk.mathematics.geometry.d3.object import Composite
|
|
7
|
+
from ostk.mathematics.geometry.d3.object import Point
|
|
8
|
+
from ostk.physics.unit import Mass
|
|
9
|
+
from ostk.astrodynamics.flight import System
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture
|
|
13
|
+
def system_default_inputs():
|
|
14
|
+
mass = Mass(90.0, Mass.Unit.Kilogram)
|
|
15
|
+
geometry = Composite(
|
|
16
|
+
Cuboid(
|
|
17
|
+
Point(0.0, 0.0, 0.0),
|
|
18
|
+
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
|
|
19
|
+
[1.0, 0.0, 0.0],
|
|
20
|
+
)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return (mass, geometry)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.fixture
|
|
27
|
+
def system(system_default_inputs) -> System:
|
|
28
|
+
return System(*system_default_inputs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestSatelliteSystem:
|
|
32
|
+
def test_constructors(self, system: System):
|
|
33
|
+
assert system is not None
|
|
34
|
+
assert isinstance(system, System)
|
|
35
|
+
assert system.is_defined()
|
|
36
|
+
|
|
37
|
+
def test_comparators(self, system: System):
|
|
38
|
+
assert (system == system) is True
|
|
39
|
+
assert (system != system) is False
|
|
40
|
+
|
|
41
|
+
def test_getters(self, system_default_inputs, system: System):
|
|
42
|
+
(mass, geometry) = system_default_inputs
|
|
43
|
+
|
|
44
|
+
assert system.get_mass() == mass
|
|
45
|
+
assert system.get_geometry() == geometry
|