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,338 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from ostk.mathematics.curve_fitting import Interpolator
|
|
8
|
+
|
|
9
|
+
from ostk.physics import Environment
|
|
10
|
+
from ostk.physics.environment.object.celestial import Earth
|
|
11
|
+
from ostk.physics.time import Instant
|
|
12
|
+
from ostk.physics.time import Interval
|
|
13
|
+
from ostk.physics.time import DateTime
|
|
14
|
+
from ostk.physics.time import Scale
|
|
15
|
+
from ostk.physics.coordinate import Position
|
|
16
|
+
from ostk.physics.coordinate import Velocity
|
|
17
|
+
from ostk.physics.coordinate import Frame
|
|
18
|
+
from ostk.physics.coordinate.spherical import LLA
|
|
19
|
+
from ostk.physics.coordinate.spherical import AER
|
|
20
|
+
|
|
21
|
+
from ostk.astrodynamics import utilities
|
|
22
|
+
from ostk.astrodynamics import Trajectory
|
|
23
|
+
from ostk.astrodynamics.trajectory import State
|
|
24
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
25
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
|
|
26
|
+
from ostk.astrodynamics.trajectory.orbit.model import Tabulated as TabulatedOrbit
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@pytest.fixture
|
|
30
|
+
def instant() -> Instant:
|
|
31
|
+
return Instant.date_time(DateTime.parse("2024-01-29T00:00:00"), Scale.UTC)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def frame() -> Frame:
|
|
36
|
+
return Frame.GCRF()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def position_1(frame: Frame) -> Position:
|
|
41
|
+
return Position.meters([7000000.0, 0.0, 0.0], frame)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.fixture
|
|
45
|
+
def velocity_1(frame: Frame) -> Velocity:
|
|
46
|
+
return Velocity.meters_per_second([0.0, 7500.0, 0.0], frame)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.fixture
|
|
50
|
+
def position_2(frame: Frame) -> Position:
|
|
51
|
+
return Position.meters([7000100.0, 0.0, 0.0], frame)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.fixture
|
|
55
|
+
def velocity_2(frame: Frame) -> Velocity:
|
|
56
|
+
return Velocity.meters_per_second([0.0, 7500.0, 0.0], frame)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.fixture
|
|
60
|
+
def state_1(
|
|
61
|
+
instant: Instant,
|
|
62
|
+
position_1: Position,
|
|
63
|
+
velocity_1: Velocity,
|
|
64
|
+
) -> State:
|
|
65
|
+
return State(instant=instant, position=position_1, velocity=velocity_1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.fixture
|
|
69
|
+
def state_2(
|
|
70
|
+
instant: Instant,
|
|
71
|
+
position_2: Position,
|
|
72
|
+
velocity_2: Velocity,
|
|
73
|
+
) -> State:
|
|
74
|
+
return State(instant=instant, position=position_2, velocity=velocity_2)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.fixture
|
|
78
|
+
def candidate_states(state_1: State) -> list[State]:
|
|
79
|
+
return [state_1, state_1, state_1]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@pytest.fixture
|
|
83
|
+
def reference_states(state_2: State) -> list[State]:
|
|
84
|
+
return [state_2, state_2, state_2]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@pytest.fixture
|
|
88
|
+
def orbit(reference_states: list[State]) -> Orbit:
|
|
89
|
+
return Orbit(
|
|
90
|
+
model=TabulatedOrbit(
|
|
91
|
+
states=reference_states,
|
|
92
|
+
initial_revolution_number=1,
|
|
93
|
+
interpolation_type=Interpolator.Type.Linear,
|
|
94
|
+
),
|
|
95
|
+
celestial_object=Earth.default(),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TestUtility:
|
|
100
|
+
def test_lla_from_state(
|
|
101
|
+
self,
|
|
102
|
+
state: State,
|
|
103
|
+
):
|
|
104
|
+
lla: LLA = utilities.lla_from_state(state)
|
|
105
|
+
|
|
106
|
+
assert lla is not None
|
|
107
|
+
assert isinstance(lla, LLA)
|
|
108
|
+
assert lla.is_defined()
|
|
109
|
+
|
|
110
|
+
def test_lla_from_position(
|
|
111
|
+
self,
|
|
112
|
+
instant: Instant,
|
|
113
|
+
position: Position,
|
|
114
|
+
):
|
|
115
|
+
lla: LLA = utilities.lla_from_position(position, instant)
|
|
116
|
+
|
|
117
|
+
assert lla is not None
|
|
118
|
+
assert isinstance(lla, LLA)
|
|
119
|
+
assert lla.is_defined()
|
|
120
|
+
|
|
121
|
+
def test_position_from_lla(
|
|
122
|
+
self,
|
|
123
|
+
lla: LLA,
|
|
124
|
+
):
|
|
125
|
+
position: Position = utilities.position_from_lla(lla)
|
|
126
|
+
|
|
127
|
+
assert position is not None
|
|
128
|
+
assert isinstance(position, Position)
|
|
129
|
+
assert position.is_defined()
|
|
130
|
+
|
|
131
|
+
def test_compute_aer(
|
|
132
|
+
self,
|
|
133
|
+
instant: Instant,
|
|
134
|
+
position: Position,
|
|
135
|
+
environment: Environment,
|
|
136
|
+
):
|
|
137
|
+
aer: AER = utilities.compute_aer(instant, position, position, environment)
|
|
138
|
+
|
|
139
|
+
assert aer is not None
|
|
140
|
+
assert isinstance(aer, AER)
|
|
141
|
+
|
|
142
|
+
def test_compute_time_lla_aer_coordinates(
|
|
143
|
+
self,
|
|
144
|
+
state: State,
|
|
145
|
+
position: Position,
|
|
146
|
+
environment: Environment,
|
|
147
|
+
):
|
|
148
|
+
time_lla_aer: tuple[datetime, float, float, float, float, float, float] = (
|
|
149
|
+
utilities.compute_time_lla_aer_coordinates(state, position, environment)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
assert time_lla_aer is not None
|
|
153
|
+
assert len(time_lla_aer) == 7
|
|
154
|
+
assert isinstance(time_lla_aer[0], datetime)
|
|
155
|
+
for i in range(1, 7):
|
|
156
|
+
assert isinstance(time_lla_aer[i], float)
|
|
157
|
+
|
|
158
|
+
def test_compute_trajectory_geometry(
|
|
159
|
+
self,
|
|
160
|
+
interval: Interval,
|
|
161
|
+
trajectory: Trajectory,
|
|
162
|
+
):
|
|
163
|
+
output: list[LLA] = utilities.compute_trajectory_geometry(trajectory, interval)
|
|
164
|
+
|
|
165
|
+
assert output is not None
|
|
166
|
+
assert len(output) == 2
|
|
167
|
+
assert isinstance(output[0], LLA)
|
|
168
|
+
|
|
169
|
+
def test_compute_ground_track(
|
|
170
|
+
self,
|
|
171
|
+
interval: Interval,
|
|
172
|
+
trajectory: Trajectory,
|
|
173
|
+
):
|
|
174
|
+
output: list[LLA] = utilities.compute_ground_track(trajectory, interval)
|
|
175
|
+
|
|
176
|
+
assert output is not None
|
|
177
|
+
assert len(output) == 2
|
|
178
|
+
assert isinstance(output[0], LLA)
|
|
179
|
+
assert output[0].get_altitude().in_meters() <= 15.0
|
|
180
|
+
|
|
181
|
+
def test_convert_state(self, state: State):
|
|
182
|
+
output: tuple[
|
|
183
|
+
str, float, float, float, float, float, float, float, float, float
|
|
184
|
+
] = utilities.convert_state(state)
|
|
185
|
+
|
|
186
|
+
assert output is not None
|
|
187
|
+
assert len(output) == 11
|
|
188
|
+
assert isinstance(output[0], str)
|
|
189
|
+
for i in range(1, 11):
|
|
190
|
+
assert isinstance(output[i], float)
|
|
191
|
+
|
|
192
|
+
def test_compute_residuals_identical_states(
|
|
193
|
+
self,
|
|
194
|
+
candidate_states: list[State],
|
|
195
|
+
) -> None:
|
|
196
|
+
residuals = utilities.compute_residuals(
|
|
197
|
+
candidate_states=candidate_states,
|
|
198
|
+
reference_states=candidate_states,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
assert len(residuals) == len(candidate_states)
|
|
202
|
+
for residual in residuals:
|
|
203
|
+
assert isinstance(residual, utilities.Residual)
|
|
204
|
+
assert residual.dr == pytest.approx(0.0, abs=1e-10)
|
|
205
|
+
assert residual.dv == pytest.approx(0.0, abs=1e-10)
|
|
206
|
+
assert residual.dr_x == pytest.approx(0.0, abs=1e-10)
|
|
207
|
+
assert residual.dr_y == pytest.approx(0.0, abs=1e-10)
|
|
208
|
+
assert residual.dr_z == pytest.approx(0.0, abs=1e-10)
|
|
209
|
+
assert residual.dv_x == pytest.approx(0.0, abs=1e-10)
|
|
210
|
+
assert residual.dv_y == pytest.approx(0.0, abs=1e-10)
|
|
211
|
+
assert residual.dv_z == pytest.approx(0.0, abs=1e-10)
|
|
212
|
+
|
|
213
|
+
def test_compute_residuals_with_local_orbital_frame_factory(
|
|
214
|
+
self,
|
|
215
|
+
candidate_states: list[State],
|
|
216
|
+
reference_states: list[State],
|
|
217
|
+
) -> None:
|
|
218
|
+
residuals = utilities.compute_residuals(
|
|
219
|
+
candidate_states=candidate_states,
|
|
220
|
+
reference_states=reference_states,
|
|
221
|
+
local_orbital_frame_factory_or_frame=LocalOrbitalFrameFactory.VNC(
|
|
222
|
+
Frame.GCRF()
|
|
223
|
+
),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
assert len(residuals) == len(candidate_states)
|
|
227
|
+
# Expected difference: reference_states - candidate_states = 7000000 - 7000100 = -100m in cross track-direction
|
|
228
|
+
for residual in residuals:
|
|
229
|
+
assert isinstance(residual, utilities.Residual)
|
|
230
|
+
assert residual.dr == pytest.approx(100.0, rel=1e-6)
|
|
231
|
+
assert residual.dr_x == pytest.approx(0.0, rel=1e-6)
|
|
232
|
+
assert residual.dr_y == pytest.approx(0.0, abs=1e-10)
|
|
233
|
+
assert residual.dr_z == pytest.approx(-100.0, abs=1e-10)
|
|
234
|
+
assert residual.dv == pytest.approx(0.0, abs=1e-10)
|
|
235
|
+
|
|
236
|
+
def test_compute_residuals_for_orbit_identical_orbit_and_states(
|
|
237
|
+
self,
|
|
238
|
+
orbit: Orbit,
|
|
239
|
+
reference_states: list[State],
|
|
240
|
+
) -> None:
|
|
241
|
+
result = utilities.compute_residuals_for_orbit(
|
|
242
|
+
candidate_orbit=orbit,
|
|
243
|
+
reference_states=reference_states,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
assert len(result) == len(reference_states)
|
|
247
|
+
for entry in result:
|
|
248
|
+
assert isinstance(entry, utilities.Residual)
|
|
249
|
+
assert entry.timestamp is not None
|
|
250
|
+
assert entry.dr_x is not None
|
|
251
|
+
assert entry.dr_y is not None
|
|
252
|
+
assert entry.dr_z is not None
|
|
253
|
+
assert entry.dv_x is not None
|
|
254
|
+
assert entry.dv_y is not None
|
|
255
|
+
assert entry.dv_z is not None
|
|
256
|
+
assert isinstance(entry.timestamp, datetime)
|
|
257
|
+
|
|
258
|
+
def test_compute_residuals_for_orbit_identical_orbit_and_states_with_local_orbital_frame_factory(
|
|
259
|
+
self,
|
|
260
|
+
orbit: Orbit,
|
|
261
|
+
reference_states: list[State],
|
|
262
|
+
) -> None:
|
|
263
|
+
result = utilities.compute_residuals_for_orbit(
|
|
264
|
+
candidate_orbit=orbit,
|
|
265
|
+
reference_states=reference_states,
|
|
266
|
+
local_orbital_frame_factory_or_frame=LocalOrbitalFrameFactory.VNC(
|
|
267
|
+
Frame.GCRF()
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
assert len(result) == len(reference_states)
|
|
272
|
+
for entry in result:
|
|
273
|
+
assert isinstance(entry, utilities.Residual)
|
|
274
|
+
assert entry.timestamp is not None
|
|
275
|
+
assert entry.dr_x is not None
|
|
276
|
+
assert entry.dr_y is not None
|
|
277
|
+
assert entry.dr_z is not None
|
|
278
|
+
assert entry.dv_x is not None
|
|
279
|
+
assert entry.dv_y is not None
|
|
280
|
+
assert entry.dv_z is not None
|
|
281
|
+
assert isinstance(entry.timestamp, datetime)
|
|
282
|
+
|
|
283
|
+
def test_compute_residuals_for_orbits_identical_orbits(
|
|
284
|
+
self,
|
|
285
|
+
orbit: Orbit,
|
|
286
|
+
reference_states: list[State],
|
|
287
|
+
) -> None:
|
|
288
|
+
result: list[utilities.Residual] = utilities.compute_residuals_for_orbits(
|
|
289
|
+
candidate_orbit=orbit,
|
|
290
|
+
reference_orbit=orbit,
|
|
291
|
+
instants=[state.get_instant() for state in reference_states],
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
assert len(result) == len(reference_states)
|
|
295
|
+
for entry in result:
|
|
296
|
+
assert isinstance(entry, utilities.Residual)
|
|
297
|
+
assert entry.timestamp is not None
|
|
298
|
+
assert entry.dr is not None
|
|
299
|
+
assert entry.dr_x is not None
|
|
300
|
+
assert entry.dr_y is not None
|
|
301
|
+
assert entry.dr_z is not None
|
|
302
|
+
assert entry.dv is not None
|
|
303
|
+
assert entry.dv_x is not None
|
|
304
|
+
assert entry.dv_y is not None
|
|
305
|
+
assert entry.dv_z is not None
|
|
306
|
+
assert isinstance(entry.timestamp, datetime)
|
|
307
|
+
assert isinstance(entry.dr, float)
|
|
308
|
+
assert isinstance(entry.dv, float)
|
|
309
|
+
|
|
310
|
+
def test_compute_residuals_for_orbits_with_local_orbital_frame_factory(
|
|
311
|
+
self,
|
|
312
|
+
orbit: Orbit,
|
|
313
|
+
reference_states: list[State],
|
|
314
|
+
) -> None:
|
|
315
|
+
result: list[utilities.Residual] = utilities.compute_residuals_for_orbits(
|
|
316
|
+
candidate_orbit=orbit,
|
|
317
|
+
reference_orbit=orbit,
|
|
318
|
+
instants=[state.get_instant() for state in reference_states],
|
|
319
|
+
local_orbital_frame_factory_or_frame=LocalOrbitalFrameFactory.VNC(
|
|
320
|
+
Frame.GCRF()
|
|
321
|
+
),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
assert len(result) == len(reference_states)
|
|
325
|
+
for entry in result:
|
|
326
|
+
assert isinstance(entry, utilities.Residual)
|
|
327
|
+
assert entry.timestamp is not None
|
|
328
|
+
assert entry.dr is not None
|
|
329
|
+
assert entry.dr_x is not None
|
|
330
|
+
assert entry.dr_y is not None
|
|
331
|
+
assert entry.dr_z is not None
|
|
332
|
+
assert entry.dv is not None
|
|
333
|
+
assert entry.dv_x is not None
|
|
334
|
+
assert entry.dv_y is not None
|
|
335
|
+
assert entry.dv_z is not None
|
|
336
|
+
assert isinstance(entry.timestamp, datetime)
|
|
337
|
+
assert isinstance(entry.dr, float)
|
|
338
|
+
assert isinstance(entry.dv, float)
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ostk.physics import Environment
|
|
6
|
+
from ostk.physics.unit import Length
|
|
7
|
+
from ostk.physics.unit import Angle
|
|
8
|
+
from ostk.physics.time import Instant
|
|
9
|
+
from ostk.physics.time import Interval
|
|
10
|
+
from ostk.physics.time import Duration
|
|
11
|
+
from ostk.physics.time import DateTime
|
|
12
|
+
from ostk.physics.time import Time
|
|
13
|
+
from ostk.physics.time import Scale
|
|
14
|
+
from ostk.physics.unit import Length
|
|
15
|
+
from ostk.physics.coordinate import Position
|
|
16
|
+
from ostk.physics.coordinate import Frame
|
|
17
|
+
|
|
18
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
19
|
+
from ostk.astrodynamics.flight import Profile
|
|
20
|
+
from ostk.astrodynamics.viewer import Viewer
|
|
21
|
+
from ostk.astrodynamics.viewer import ConicSensor
|
|
22
|
+
from ostk.astrodynamics.viewer import _compute_celestial_angular_diameter_from_states
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def environment() -> Environment:
|
|
27
|
+
return Environment.default()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.fixture
|
|
31
|
+
def orbit(environment: Environment) -> Orbit:
|
|
32
|
+
return Orbit.sun_synchronous(
|
|
33
|
+
epoch=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
|
|
34
|
+
altitude=Length.kilometers(500.0),
|
|
35
|
+
local_time_at_descending_node=Time(14, 0, 0),
|
|
36
|
+
celestial_object=environment.access_celestial_object_with_name("Earth"),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@pytest.fixture
|
|
41
|
+
def orbits(environment: Environment) -> list[Orbit]:
|
|
42
|
+
return [
|
|
43
|
+
Orbit.sun_synchronous(
|
|
44
|
+
epoch=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
|
|
45
|
+
altitude=Length.kilometers(500.0),
|
|
46
|
+
local_time_at_descending_node=Time(14, 0, 0),
|
|
47
|
+
celestial_object=environment.access_celestial_object_with_name("Earth"),
|
|
48
|
+
),
|
|
49
|
+
Orbit.sun_synchronous(
|
|
50
|
+
epoch=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
|
|
51
|
+
altitude=Length.kilometers(500.0),
|
|
52
|
+
local_time_at_descending_node=Time(12, 0, 0),
|
|
53
|
+
celestial_object=environment.access_celestial_object_with_name("Earth"),
|
|
54
|
+
),
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.fixture
|
|
59
|
+
def profile(orbit: Orbit) -> Profile:
|
|
60
|
+
return Profile.local_orbital_frame_pointing(
|
|
61
|
+
orbit=orbit,
|
|
62
|
+
orbital_frame_type=Orbit.FrameType.VVLH,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def interval() -> Interval:
|
|
68
|
+
return Interval.closed(
|
|
69
|
+
start_instant=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
|
|
70
|
+
end_instant=Instant.date_time(DateTime(2020, 1, 1, 0, 10, 0), Scale.UTC),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@pytest.fixture
|
|
75
|
+
def viewer(interval: Interval) -> Viewer:
|
|
76
|
+
return Viewer(
|
|
77
|
+
interval=interval,
|
|
78
|
+
zoom_to_entity=False,
|
|
79
|
+
track_entity=False,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestViewer:
|
|
84
|
+
def test_constructor_success(self, interval: Interval):
|
|
85
|
+
viewer = Viewer(
|
|
86
|
+
interval=interval,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assert viewer is not None
|
|
90
|
+
assert viewer.interval == interval
|
|
91
|
+
|
|
92
|
+
rendered_html: str = viewer.render()
|
|
93
|
+
|
|
94
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
95
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
96
|
+
assert rendered_html.endswith("</script>")
|
|
97
|
+
|
|
98
|
+
def test_add_orbit_success(
|
|
99
|
+
self,
|
|
100
|
+
viewer: Viewer,
|
|
101
|
+
orbit: Orbit,
|
|
102
|
+
):
|
|
103
|
+
viewer.add_orbit(
|
|
104
|
+
orbit=orbit,
|
|
105
|
+
step=Duration.seconds(5.0),
|
|
106
|
+
show_orbital_track=True,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
rendered_html: str = viewer.render()
|
|
110
|
+
|
|
111
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
112
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
113
|
+
assert "new Cesium.SampledProperty(Cesium.Cartesian3)" in rendered_html
|
|
114
|
+
assert " widget.entities.add({position: widget" in rendered_html
|
|
115
|
+
assert "widget.entities.add({polyline:" in rendered_html
|
|
116
|
+
assert "billboard: {image:" in rendered_html
|
|
117
|
+
assert rendered_html.endswith("</script>")
|
|
118
|
+
|
|
119
|
+
def test_add_orbit_multiple_success(
|
|
120
|
+
self,
|
|
121
|
+
viewer: Viewer,
|
|
122
|
+
orbits: list[Orbit],
|
|
123
|
+
):
|
|
124
|
+
for i, orbit in enumerate(orbits):
|
|
125
|
+
viewer.add_orbit(
|
|
126
|
+
orbit=orbit,
|
|
127
|
+
step=Duration.seconds(5.0),
|
|
128
|
+
show_orbital_track=True,
|
|
129
|
+
name=f"Satellite {i}",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
rendered_html: str = viewer.render()
|
|
133
|
+
|
|
134
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
135
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
136
|
+
assert "new Cesium.SampledProperty(Cesium.Cartesian3)" in rendered_html
|
|
137
|
+
assert " widget.entities.add({position: widget" in rendered_html
|
|
138
|
+
assert "widget.entities.add({polyline:" in rendered_html
|
|
139
|
+
assert "billboard: {image:" in rendered_html
|
|
140
|
+
assert rendered_html.endswith("</script>")
|
|
141
|
+
|
|
142
|
+
def test_add_profile_success(
|
|
143
|
+
self,
|
|
144
|
+
viewer: Viewer,
|
|
145
|
+
profile: Profile,
|
|
146
|
+
):
|
|
147
|
+
viewer.add_profile(
|
|
148
|
+
profile=profile,
|
|
149
|
+
step=Duration.seconds(30.0),
|
|
150
|
+
show_orbital_track=True,
|
|
151
|
+
cesium_asset_id=123,
|
|
152
|
+
sensors=[
|
|
153
|
+
ConicSensor(
|
|
154
|
+
name="star_tracker",
|
|
155
|
+
direction=(1.0, 0.0, 0.0),
|
|
156
|
+
half_angle=Angle.degrees(8.0),
|
|
157
|
+
length=Length.meters(0.1),
|
|
158
|
+
color="red",
|
|
159
|
+
),
|
|
160
|
+
],
|
|
161
|
+
show_xyz_axes=True,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
rendered_html: str = viewer.render()
|
|
165
|
+
|
|
166
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
167
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
168
|
+
assert " widget.entities.add({position: widget" in rendered_html
|
|
169
|
+
assert "Cesium.IonResource.fromAssetId(123)" in rendered_html
|
|
170
|
+
assert "widget.entities.add({polyline:" in rendered_html
|
|
171
|
+
assert (
|
|
172
|
+
"widget.entities.add({position: widget.star_tracker_position" in rendered_html
|
|
173
|
+
)
|
|
174
|
+
assert rendered_html.endswith("</script>")
|
|
175
|
+
|
|
176
|
+
@pytest.mark.parametrize(
|
|
177
|
+
"celestial_body_name",
|
|
178
|
+
["Earth", "Moon", "Sun"],
|
|
179
|
+
)
|
|
180
|
+
def test_add_celestial_body_direction_success(
|
|
181
|
+
self,
|
|
182
|
+
viewer: Viewer,
|
|
183
|
+
orbit: Orbit,
|
|
184
|
+
celestial_body_name: str,
|
|
185
|
+
environment: Environment,
|
|
186
|
+
):
|
|
187
|
+
viewer.add_celestial_body_direction(
|
|
188
|
+
profile_or_trajectory=orbit,
|
|
189
|
+
time_step=Duration.seconds(30.0),
|
|
190
|
+
celestial=environment.access_celestial_object_with_name(celestial_body_name),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
rendered_html: str = viewer.render()
|
|
194
|
+
|
|
195
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
196
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
197
|
+
assert " widget.entities.add({position: widget" in rendered_html
|
|
198
|
+
assert (
|
|
199
|
+
f"widget.entities.add({{position: widget.{celestial_body_name.lower()}_direction_position"
|
|
200
|
+
in rendered_html
|
|
201
|
+
)
|
|
202
|
+
assert rendered_html.endswith("</script>")
|
|
203
|
+
|
|
204
|
+
def test_add_ground_tracks_success(
|
|
205
|
+
self,
|
|
206
|
+
viewer: Viewer,
|
|
207
|
+
orbit: Orbit,
|
|
208
|
+
):
|
|
209
|
+
viewer.add_ground_tracks(profile_or_trajectory=orbit)
|
|
210
|
+
|
|
211
|
+
rendered_html: str = viewer.render()
|
|
212
|
+
|
|
213
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
214
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
215
|
+
assert "widget.entities.add({polyline:" in rendered_html
|
|
216
|
+
assert rendered_html.endswith("</script>")
|
|
217
|
+
|
|
218
|
+
def test_add_target_success(
|
|
219
|
+
self,
|
|
220
|
+
viewer: Viewer,
|
|
221
|
+
):
|
|
222
|
+
viewer.add_target(
|
|
223
|
+
position=Position.meters([1.0, 2.0, 3.0], Frame.ITRF()),
|
|
224
|
+
size=123,
|
|
225
|
+
color="red",
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
rendered_html: str = viewer.render()
|
|
229
|
+
|
|
230
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
231
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
232
|
+
assert (
|
|
233
|
+
"widget.entities.add({position: Cesium.Cartesian3.fromDegrees(63.43494882292201, 18.22447811510915, -6376045.535225509), point: {pixelSize: 123.0, color: Cesium.Color.RED}});"
|
|
234
|
+
in rendered_html
|
|
235
|
+
)
|
|
236
|
+
assert rendered_html.endswith("</script>")
|
|
237
|
+
|
|
238
|
+
def test_add_target_with_label_success(
|
|
239
|
+
self,
|
|
240
|
+
viewer: Viewer,
|
|
241
|
+
):
|
|
242
|
+
viewer.add_target(
|
|
243
|
+
position=Position.meters([1.0, 2.0, 3.0], Frame.ITRF()),
|
|
244
|
+
size=123,
|
|
245
|
+
color="red",
|
|
246
|
+
label="TEST",
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
rendered_html: str = viewer.render()
|
|
250
|
+
|
|
251
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
252
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
253
|
+
assert (
|
|
254
|
+
"widget.entities.add({position: Cesium.Cartesian3.fromDegrees(63.43494882292201, 18.22447811510915, -6376045.535225509), point: {pixelSize: 123.0, color: Cesium.Color.RED}});"
|
|
255
|
+
in rendered_html
|
|
256
|
+
)
|
|
257
|
+
assert (
|
|
258
|
+
'widget.entities.add({position: Cesium.Cartesian3.fromDegrees(63.43494882292201, 18.22447811510915, -6376045.535225509), label: {text: "TEST", fillColor: Cesium.Color.RED, scale: 123.0}});'
|
|
259
|
+
in rendered_html
|
|
260
|
+
)
|
|
261
|
+
assert rendered_html.endswith("</script>")
|
|
262
|
+
|
|
263
|
+
def test_add_line_success(
|
|
264
|
+
self,
|
|
265
|
+
viewer: Viewer,
|
|
266
|
+
):
|
|
267
|
+
viewer.add_line(
|
|
268
|
+
positions=[
|
|
269
|
+
Position.meters([1.0, 2.0, 3.0], Frame.ITRF()),
|
|
270
|
+
Position.meters([4.0, 5.0, 6.0], Frame.ITRF()),
|
|
271
|
+
],
|
|
272
|
+
size=10.0,
|
|
273
|
+
color="red",
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
rendered_html: str = viewer.render()
|
|
277
|
+
|
|
278
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
279
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
280
|
+
assert (
|
|
281
|
+
"widget.entities.add({polyline: {positions: Cesium.Cartesian3.fromDegreesArrayHeights([63.43494882292201, 18.22447811510915, 10.0, 51.34019174590991, 10.165199393640696, 10.0]), width: 10.0, material: Cesium.Color.RED}});"
|
|
282
|
+
in rendered_html
|
|
283
|
+
)
|
|
284
|
+
assert rendered_html.endswith("</script>")
|
|
285
|
+
|
|
286
|
+
def test_add_label_success(
|
|
287
|
+
self,
|
|
288
|
+
viewer: Viewer,
|
|
289
|
+
):
|
|
290
|
+
viewer.add_label(
|
|
291
|
+
position=Position.meters([6671000.0, 0.0, 0.0], Frame.ITRF()),
|
|
292
|
+
text="Hello, World!",
|
|
293
|
+
size=1.0,
|
|
294
|
+
color="red",
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
rendered_html: str = viewer.render()
|
|
298
|
+
|
|
299
|
+
assert rendered_html.startswith('<meta charset="utf-8">')
|
|
300
|
+
assert "var widget = new Cesium.Viewer" in rendered_html
|
|
301
|
+
assert (
|
|
302
|
+
'widget.entities.add({position: Cesium.Cartesian3.fromDegrees(0.0, 0.0, 292863.0000000001), label: {text: "Hello, World!", fillColor: Cesium.Color.RED, scale: 1.0}});'
|
|
303
|
+
in rendered_html
|
|
304
|
+
)
|
|
305
|
+
assert rendered_html.endswith("</script>")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def test_compute_celestial_angular_diameter_from_states_success(
|
|
309
|
+
orbit: Orbit,
|
|
310
|
+
interval: Interval,
|
|
311
|
+
environment: Environment,
|
|
312
|
+
) -> None:
|
|
313
|
+
assert _compute_celestial_angular_diameter_from_states(
|
|
314
|
+
celestial=environment.access_celestial_object_with_name("Sun"),
|
|
315
|
+
states=orbit.get_states_at(
|
|
316
|
+
interval.generate_grid(Duration.seconds(30.0)),
|
|
317
|
+
),
|
|
318
|
+
).mean() == pytest.approx(0.54, rel=1e-2)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Apache License 2.0
|