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,851 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import operator
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
import cesiumpy
|
|
13
|
+
|
|
14
|
+
except ImportError:
|
|
15
|
+
...
|
|
16
|
+
|
|
17
|
+
from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
|
|
18
|
+
|
|
19
|
+
from ostk.physics.environment.object import Celestial
|
|
20
|
+
from ostk.physics.unit import Length
|
|
21
|
+
from ostk.physics.unit import Angle
|
|
22
|
+
from ostk.physics.time import Instant, Interval, Duration
|
|
23
|
+
from ostk.physics.coordinate import Position
|
|
24
|
+
from ostk.physics.coordinate import Frame
|
|
25
|
+
from ostk.physics.coordinate.spherical import LLA
|
|
26
|
+
|
|
27
|
+
from ostk.astrodynamics import Trajectory
|
|
28
|
+
from ostk.astrodynamics.flight import Profile
|
|
29
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
30
|
+
from ostk.astrodynamics.trajectory import State
|
|
31
|
+
|
|
32
|
+
from .converters import coerce_to_datetime
|
|
33
|
+
from .utilities import lla_from_position
|
|
34
|
+
from .utilities import lla_from_state
|
|
35
|
+
from .utilities import position_from_lla
|
|
36
|
+
|
|
37
|
+
DEFAULT_SATELLITE_IMAGE: str = (
|
|
38
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII="
|
|
39
|
+
)
|
|
40
|
+
DEFAULT_STEP_DURATION: Duration = Duration.seconds(10.0)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Sensor:
|
|
45
|
+
name: str
|
|
46
|
+
direction: tuple[float, float, float] | np.ndarray
|
|
47
|
+
color: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class ConicSensor(Sensor):
|
|
52
|
+
half_angle: Angle
|
|
53
|
+
length: Length
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class RectangularSensor(Sensor):
|
|
58
|
+
x_half_angle: Angle
|
|
59
|
+
y_half_angle: Angle
|
|
60
|
+
radius: Length
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Viewer:
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
interval: Interval,
|
|
67
|
+
cesium_token: str | None = None,
|
|
68
|
+
width: str = "1500px",
|
|
69
|
+
height: str = "800px",
|
|
70
|
+
zoom_to_entity: bool = True,
|
|
71
|
+
track_entity: bool = True,
|
|
72
|
+
) -> None:
|
|
73
|
+
self._interval: Interval = interval
|
|
74
|
+
|
|
75
|
+
self._viewer: cesiumpy.Viewer = cesiumpy.Viewer(
|
|
76
|
+
width=width,
|
|
77
|
+
height=height,
|
|
78
|
+
clock_view_model=cesiumpy.ClockViewModel(
|
|
79
|
+
clock=cesiumpy.Clock(
|
|
80
|
+
start_time=coerce_to_datetime(interval.get_start()),
|
|
81
|
+
stop_time=coerce_to_datetime(interval.get_end()),
|
|
82
|
+
clock_range=cesiumpy.Clock.Range.CLAMPED,
|
|
83
|
+
can_animate=True,
|
|
84
|
+
should_animate=False,
|
|
85
|
+
)
|
|
86
|
+
),
|
|
87
|
+
fullscreen_button=False,
|
|
88
|
+
home_button=False,
|
|
89
|
+
info_box=False,
|
|
90
|
+
timeline=True,
|
|
91
|
+
navigation_help_button=False,
|
|
92
|
+
navigation_instructions_initially_visible=False,
|
|
93
|
+
scene_mode_picker=False,
|
|
94
|
+
selection_indicator=False,
|
|
95
|
+
scene3d_only=True,
|
|
96
|
+
zoom_to_entity=zoom_to_entity,
|
|
97
|
+
track_entity=track_entity,
|
|
98
|
+
default_access_token=cesium_token,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def interval(self) -> Interval:
|
|
103
|
+
return self._interval
|
|
104
|
+
|
|
105
|
+
def add_orbit(
|
|
106
|
+
self,
|
|
107
|
+
orbit: Orbit,
|
|
108
|
+
step: Duration,
|
|
109
|
+
name: str = "Satellite",
|
|
110
|
+
show_orbital_track: bool = False,
|
|
111
|
+
color: str | None = None,
|
|
112
|
+
image: str | None = None,
|
|
113
|
+
) -> Viewer:
|
|
114
|
+
"""
|
|
115
|
+
Add Orbit to Viewer.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
orbit (Orbit): Orbit to be added.
|
|
119
|
+
step (Duration): Step between two consecutive states.
|
|
120
|
+
name (str, optional): Name of the orbit. Defaults to "Satellite".
|
|
121
|
+
show_orbital_track (bool, optional): Whether to show the orbital track. Defaults to False.
|
|
122
|
+
color (str, optional): Color of the orbit. Defaults to None.
|
|
123
|
+
image (str, optional): Logo to be added. Defaults to None.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Viewer: The Viewer.
|
|
127
|
+
"""
|
|
128
|
+
instants: list[Instant] = self._interval.generate_grid(step)
|
|
129
|
+
states: list[State] = orbit.get_states_at(instants)
|
|
130
|
+
llas: list[LLA] = _generate_llas(states)
|
|
131
|
+
|
|
132
|
+
cesium_positions: cesiumpy.SampledPositionProperty = (
|
|
133
|
+
_generate_sampled_position_from_states(states)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
self._viewer.entities.add(
|
|
137
|
+
cesiumpy.Billboard(
|
|
138
|
+
name=name,
|
|
139
|
+
position=cesium_positions,
|
|
140
|
+
image=image or DEFAULT_SATELLITE_IMAGE,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
self._viewer.entities.add(
|
|
145
|
+
cesiumpy.Label(
|
|
146
|
+
position=cesium_positions,
|
|
147
|
+
text=name,
|
|
148
|
+
scale=1.0,
|
|
149
|
+
fill_color=color or cesiumpy.color.WHITE,
|
|
150
|
+
pixel_offset=[0.0, 20.0],
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if show_orbital_track:
|
|
155
|
+
self._viewer.entities.add(
|
|
156
|
+
cesiumpy.Polyline(
|
|
157
|
+
positions=cesiumpy.entities.cartesian.Cartesian3Array(
|
|
158
|
+
functools.reduce(
|
|
159
|
+
operator.iconcat,
|
|
160
|
+
[
|
|
161
|
+
[
|
|
162
|
+
float(lla.get_longitude().in_degrees()),
|
|
163
|
+
float(lla.get_latitude().in_degrees()),
|
|
164
|
+
float(lla.get_altitude().in_meters()),
|
|
165
|
+
]
|
|
166
|
+
for lla in llas
|
|
167
|
+
],
|
|
168
|
+
[],
|
|
169
|
+
)
|
|
170
|
+
),
|
|
171
|
+
width=1,
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
def add_profile(
|
|
178
|
+
self,
|
|
179
|
+
profile: Profile,
|
|
180
|
+
step: Duration,
|
|
181
|
+
show_orbital_track: bool = False,
|
|
182
|
+
cesium_asset_id: int | None = None,
|
|
183
|
+
sensors: list[Sensor] | None = None,
|
|
184
|
+
show_xyz_axes: bool = False,
|
|
185
|
+
) -> Viewer:
|
|
186
|
+
"""
|
|
187
|
+
Add Profile to Viewer.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
profile (Profile): Profile to be added.
|
|
191
|
+
step (Duration): Step between two consecutive states.
|
|
192
|
+
show_orbital_track (bool, optional): Whether to show the orbital track. Defaults to False.
|
|
193
|
+
cesium_asset_id (int, optional): The Cesium asset ID. Defaults to None.
|
|
194
|
+
sensors (list[Sensor], optional): Sensors to be added to the asset. Defaults to None.
|
|
195
|
+
show_xyz_axes (bool, optional): Whether to show the XYZ axes. Defaults to False.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Viewer: The Viewer.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
instants: list[Instant] = self._interval.generate_grid(step)
|
|
202
|
+
states: list[State] = profile.get_states_at(instants)
|
|
203
|
+
llas: list[LLA] = _generate_llas(states)
|
|
204
|
+
|
|
205
|
+
sensors = sensors or []
|
|
206
|
+
if show_xyz_axes:
|
|
207
|
+
sensors.extend(
|
|
208
|
+
[
|
|
209
|
+
ConicSensor(
|
|
210
|
+
name="x_axis",
|
|
211
|
+
direction=(+1.0, 0.0, 0.0),
|
|
212
|
+
half_angle=Angle.degrees(1.0),
|
|
213
|
+
length=Length.meters(2.0),
|
|
214
|
+
color="red",
|
|
215
|
+
),
|
|
216
|
+
ConicSensor(
|
|
217
|
+
name="y_axis",
|
|
218
|
+
direction=(0.0, +1.0, 0.0),
|
|
219
|
+
half_angle=Angle.degrees(1.0),
|
|
220
|
+
length=Length.meters(2.0),
|
|
221
|
+
color="green",
|
|
222
|
+
),
|
|
223
|
+
ConicSensor(
|
|
224
|
+
name="z_axis",
|
|
225
|
+
direction=(0.0, 0.0, +1.0),
|
|
226
|
+
half_angle=Angle.degrees(1.0),
|
|
227
|
+
length=Length.meters(2.0),
|
|
228
|
+
color="blue",
|
|
229
|
+
),
|
|
230
|
+
]
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
satellite = cesiumpy.Satellite(
|
|
234
|
+
position=_generate_sampled_position_from_llas(instants, llas),
|
|
235
|
+
orientation=_generate_sampled_orientation(states),
|
|
236
|
+
availability=self._get_availability(),
|
|
237
|
+
model=cesiumpy.IonResource(
|
|
238
|
+
asset_id=cesium_asset_id or 0
|
|
239
|
+
), # TBM: Should be made more robust
|
|
240
|
+
sensors=[_cesium_from_ostk_sensor(sensor) for sensor in sensors or []],
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
satellite.render(self._viewer)
|
|
244
|
+
|
|
245
|
+
if show_orbital_track:
|
|
246
|
+
self._viewer.entities.add(
|
|
247
|
+
cesiumpy.Polyline(
|
|
248
|
+
positions=cesiumpy.entities.cartesian.Cartesian3Array(
|
|
249
|
+
functools.reduce(
|
|
250
|
+
operator.iconcat,
|
|
251
|
+
[
|
|
252
|
+
[
|
|
253
|
+
float(lla.get_longitude().in_degrees()),
|
|
254
|
+
float(lla.get_latitude().in_degrees()),
|
|
255
|
+
float(lla.get_altitude().in_meters()),
|
|
256
|
+
]
|
|
257
|
+
for lla in _generate_llas(states)
|
|
258
|
+
],
|
|
259
|
+
[],
|
|
260
|
+
)
|
|
261
|
+
),
|
|
262
|
+
width=1,
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
return self
|
|
267
|
+
|
|
268
|
+
def add_celestial_body_direction(
|
|
269
|
+
self,
|
|
270
|
+
profile_or_trajectory: Profile | Trajectory,
|
|
271
|
+
celestial: Celestial,
|
|
272
|
+
time_step: Duration | None = None,
|
|
273
|
+
color: str | None = None,
|
|
274
|
+
) -> Viewer:
|
|
275
|
+
"""
|
|
276
|
+
Add the celestial direction to the viewer.
|
|
277
|
+
|
|
278
|
+
Args:
|
|
279
|
+
profile_or_trajectory (Profile | Trajectory): The profile or trajectory to be added.
|
|
280
|
+
celestial (Celestial, optional): The celestial body to be used.
|
|
281
|
+
time_step (Duration): The duration of each step in the grid.
|
|
282
|
+
Default to None. If None, the default step duration is used.
|
|
283
|
+
color (str, optional): The color of the celestial body direction.
|
|
284
|
+
Defaults to None. If None, the color depends on the celestial body (for the Earth, Sun and Moon).
|
|
285
|
+
Otherwise, use the default color (RED).
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Viewer: The Viewer.
|
|
289
|
+
"""
|
|
290
|
+
time_step = time_step or DEFAULT_STEP_DURATION
|
|
291
|
+
alpha_color: float = 0.5
|
|
292
|
+
reference_frame: Frame = Frame.GCRF()
|
|
293
|
+
reference_vector: np.ndarray = np.array([0.0, 0.0, 1.0])
|
|
294
|
+
instants: list[Instant] = self._interval.generate_grid(time_step)
|
|
295
|
+
celestial_name: str = str(celestial.access_name())
|
|
296
|
+
|
|
297
|
+
if color is None:
|
|
298
|
+
if celestial_name == "Earth":
|
|
299
|
+
color = cesiumpy.color.BLUE
|
|
300
|
+
elif celestial_name == "Moon":
|
|
301
|
+
color = cesiumpy.color.GREY
|
|
302
|
+
elif celestial_name == "Sun":
|
|
303
|
+
color = cesiumpy.color.YELLOW
|
|
304
|
+
else:
|
|
305
|
+
color = cesiumpy.color.RED
|
|
306
|
+
|
|
307
|
+
# Apply an alpha to the color
|
|
308
|
+
color = color.with_alpha(alpha_color)
|
|
309
|
+
|
|
310
|
+
def _create_celestial_body_direction_state(
|
|
311
|
+
satellite_state: State,
|
|
312
|
+
reference_frame: Frame = reference_frame,
|
|
313
|
+
reference_vector: np.ndarray = reference_vector,
|
|
314
|
+
celestial: Celestial = celestial,
|
|
315
|
+
) -> State:
|
|
316
|
+
state_in_reference_frame: State = satellite_state.in_frame(reference_frame)
|
|
317
|
+
return State(
|
|
318
|
+
instant=state_in_reference_frame.get_instant(),
|
|
319
|
+
position=state_in_reference_frame.get_position(),
|
|
320
|
+
velocity=state_in_reference_frame.get_velocity(),
|
|
321
|
+
attitude=Quaternion.shortest_rotation(
|
|
322
|
+
first_vector=_compute_celestial_direction_from_state(
|
|
323
|
+
state=satellite_state,
|
|
324
|
+
celestial=celestial,
|
|
325
|
+
frame=reference_frame,
|
|
326
|
+
),
|
|
327
|
+
second_vector=reference_vector,
|
|
328
|
+
),
|
|
329
|
+
angular_velocity=np.zeros(3),
|
|
330
|
+
attitude_frame=reference_frame,
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
celestial_direction_states: list[State] = list(
|
|
334
|
+
map(
|
|
335
|
+
_create_celestial_body_direction_state,
|
|
336
|
+
profile_or_trajectory.get_states_at(instants),
|
|
337
|
+
)
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
satellite = cesiumpy.Satellite(
|
|
341
|
+
position=_generate_sampled_position_from_llas(
|
|
342
|
+
instants=instants,
|
|
343
|
+
llas=_generate_llas(celestial_direction_states),
|
|
344
|
+
),
|
|
345
|
+
orientation=_generate_sampled_orientation(celestial_direction_states),
|
|
346
|
+
availability=self._get_availability(),
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
_cesium_from_ostk_sensor(
|
|
350
|
+
ConicSensor(
|
|
351
|
+
name=celestial_name.lower() + "_direction",
|
|
352
|
+
direction=reference_vector,
|
|
353
|
+
# Compute the half angle from the celestial body diameter
|
|
354
|
+
half_angle=Angle.degrees(
|
|
355
|
+
_compute_celestial_angular_diameter_from_states(
|
|
356
|
+
celestial=celestial,
|
|
357
|
+
states=celestial_direction_states,
|
|
358
|
+
).mean()
|
|
359
|
+
/ 2.0
|
|
360
|
+
),
|
|
361
|
+
length=Length.meters(2.0),
|
|
362
|
+
color=color,
|
|
363
|
+
)
|
|
364
|
+
).render(
|
|
365
|
+
viewer=self._viewer,
|
|
366
|
+
satellite=satellite,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return self
|
|
370
|
+
|
|
371
|
+
def add_ground_tracks(
|
|
372
|
+
self,
|
|
373
|
+
profile_or_trajectory: Profile | Trajectory,
|
|
374
|
+
time_step: Duration | None = None,
|
|
375
|
+
show_current_position: bool = True,
|
|
376
|
+
) -> Viewer:
|
|
377
|
+
"""
|
|
378
|
+
Add ground tracks to the viewer.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
profile_or_trajectory (Profile | Trajectory): The profile or trajectory to be added.
|
|
382
|
+
time_step (Duration, optional): The duration of each step in the grid.
|
|
383
|
+
Default to None. If None, the default step duration is used.
|
|
384
|
+
show_current_position (bool, optional): Whether to show the current position as a point. Defaults to True.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
Viewer: The Viewer.
|
|
388
|
+
"""
|
|
389
|
+
time_step = time_step or DEFAULT_STEP_DURATION
|
|
390
|
+
|
|
391
|
+
instants: list[Instant] = self._interval.generate_grid(time_step)
|
|
392
|
+
llas: list[LLA] = []
|
|
393
|
+
ground_track_positions: list[Position] = []
|
|
394
|
+
|
|
395
|
+
for state in profile_or_trajectory.get_states_at(instants):
|
|
396
|
+
satellite_lla: LLA = lla_from_state(state)
|
|
397
|
+
lla: LLA = LLA(
|
|
398
|
+
latitude=satellite_lla.get_latitude(),
|
|
399
|
+
longitude=satellite_lla.get_longitude(),
|
|
400
|
+
altitude=Length.meters(0.0),
|
|
401
|
+
)
|
|
402
|
+
llas.append(lla)
|
|
403
|
+
ground_track_positions.append(position_from_lla(lla))
|
|
404
|
+
|
|
405
|
+
self.add_line(
|
|
406
|
+
positions=ground_track_positions,
|
|
407
|
+
size=1,
|
|
408
|
+
color=cesiumpy.color.GRAY,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
if show_current_position:
|
|
412
|
+
self.add_moving_point(
|
|
413
|
+
instants=instants,
|
|
414
|
+
llas=llas,
|
|
415
|
+
color=cesiumpy.color.DARKORANGE,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return self
|
|
419
|
+
|
|
420
|
+
def add_target(
|
|
421
|
+
self,
|
|
422
|
+
position: Position,
|
|
423
|
+
size: int | None = None,
|
|
424
|
+
color: str | None = None,
|
|
425
|
+
label: str | None = None,
|
|
426
|
+
) -> Viewer:
|
|
427
|
+
"""
|
|
428
|
+
Add target to Viewer.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
position (Position): Target position.
|
|
432
|
+
size (int, optional): Target size. Defaults to None.
|
|
433
|
+
color (str, optional): Target color. Defaults to None.
|
|
434
|
+
label (str, optional): Target label. Defaults to None.
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Viewer: The Viewer.
|
|
438
|
+
"""
|
|
439
|
+
|
|
440
|
+
self._viewer.entities.add(
|
|
441
|
+
cesiumpy.Point(
|
|
442
|
+
position=_cesium_from_ostk_position(position),
|
|
443
|
+
pixel_size=size or 10,
|
|
444
|
+
color=color or cesiumpy.color.YELLOW,
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if label:
|
|
449
|
+
self.add_label(position, label, size, color)
|
|
450
|
+
|
|
451
|
+
return self
|
|
452
|
+
|
|
453
|
+
def add_line(
|
|
454
|
+
self,
|
|
455
|
+
positions: list[Position],
|
|
456
|
+
size: int | None = None,
|
|
457
|
+
color: str | None = None,
|
|
458
|
+
) -> Viewer:
|
|
459
|
+
"""
|
|
460
|
+
Add line to Viewer.
|
|
461
|
+
|
|
462
|
+
Args:
|
|
463
|
+
positions (list[Position]): Line positions.
|
|
464
|
+
size (int, optional): Line size. Defaults to None.
|
|
465
|
+
color (str, optional): Line color. Defaults to None.
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
Viewer: The Viewer.
|
|
469
|
+
"""
|
|
470
|
+
|
|
471
|
+
self._viewer.entities.add(
|
|
472
|
+
cesiumpy.Polyline(
|
|
473
|
+
positions=cesiumpy.entities.cartesian.Cartesian3Array(
|
|
474
|
+
functools.reduce(
|
|
475
|
+
operator.iconcat,
|
|
476
|
+
[
|
|
477
|
+
[
|
|
478
|
+
float(lla.get_longitude().in_degrees()),
|
|
479
|
+
float(lla.get_latitude().in_degrees()),
|
|
480
|
+
10.0,
|
|
481
|
+
]
|
|
482
|
+
for lla in map(lla_from_position, positions)
|
|
483
|
+
],
|
|
484
|
+
[],
|
|
485
|
+
)
|
|
486
|
+
),
|
|
487
|
+
width=size or 1,
|
|
488
|
+
material=color or cesiumpy.color.YELLOW,
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
return self
|
|
493
|
+
|
|
494
|
+
def add_moving_point(
|
|
495
|
+
self,
|
|
496
|
+
instants: list[Instant],
|
|
497
|
+
llas: list[LLA],
|
|
498
|
+
color: str | None = None,
|
|
499
|
+
size: int | None = None,
|
|
500
|
+
) -> Viewer:
|
|
501
|
+
"""
|
|
502
|
+
Add a moving point to the Viewer.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
instants (list[Instant]): The list of instants.
|
|
506
|
+
llas (list[LLA]): The list of Longitude, Latitude, Altitude (LLA) coordinates.
|
|
507
|
+
color (str, optional): The color of the point. Defaults to None. If None, the default color is used.
|
|
508
|
+
size (int, optional): The size of the point. Defaults to None. If None, the default size is used.
|
|
509
|
+
|
|
510
|
+
Returns:
|
|
511
|
+
Viewer: The Viewer.
|
|
512
|
+
"""
|
|
513
|
+
|
|
514
|
+
self._viewer.entities.add(
|
|
515
|
+
cesiumpy.Point(
|
|
516
|
+
position=_generate_sampled_position_from_llas(
|
|
517
|
+
instants=instants,
|
|
518
|
+
llas=llas,
|
|
519
|
+
),
|
|
520
|
+
availability=self._get_availability(),
|
|
521
|
+
color=color,
|
|
522
|
+
pixel_size=size,
|
|
523
|
+
)
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
return self
|
|
527
|
+
|
|
528
|
+
def add_label(
|
|
529
|
+
self,
|
|
530
|
+
position: Position,
|
|
531
|
+
text: str,
|
|
532
|
+
size: int | None = None,
|
|
533
|
+
color: str | None = None,
|
|
534
|
+
) -> Viewer:
|
|
535
|
+
"""
|
|
536
|
+
Add label to Viewer.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
position (Position): Label position.
|
|
540
|
+
text (str): Label text.
|
|
541
|
+
size (int, optional): Label size. Defaults to None.
|
|
542
|
+
color (str, optional): Label color. Defaults to None.
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
Viewer: The Viewer.
|
|
546
|
+
"""
|
|
547
|
+
|
|
548
|
+
self._viewer.entities.add(
|
|
549
|
+
cesiumpy.Label(
|
|
550
|
+
position=_cesium_from_ostk_position(position),
|
|
551
|
+
text=text,
|
|
552
|
+
scale=size or 10,
|
|
553
|
+
fill_color=color or cesiumpy.color.WHITE,
|
|
554
|
+
)
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
return self
|
|
558
|
+
|
|
559
|
+
def render(self) -> str:
|
|
560
|
+
"""
|
|
561
|
+
Render Viewer as HTML string.
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
str: Rendered HTML string.
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
return self._viewer.to_html()
|
|
568
|
+
|
|
569
|
+
def _get_availability(self) -> cesiumpy.TimeIntervalCollection:
|
|
570
|
+
"""
|
|
571
|
+
Get the availability of the viewer.
|
|
572
|
+
|
|
573
|
+
Returns:
|
|
574
|
+
cesiumpy.TimeIntervalCollection: The availability of the viewer.
|
|
575
|
+
"""
|
|
576
|
+
return _cesium_from_ostk_intervals(intervals=[self._interval])
|
|
577
|
+
|
|
578
|
+
def _repr_html_(self) -> str:
|
|
579
|
+
return self.render()
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def _generate_llas(states: list[State]) -> list[LLA]:
|
|
583
|
+
return list(map(lla_from_state, states))
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _generate_sampled_position_from_llas(
|
|
587
|
+
instants: list[Instant],
|
|
588
|
+
llas: list[LLA],
|
|
589
|
+
) -> cesiumpy.SampledPositionProperty:
|
|
590
|
+
"""
|
|
591
|
+
Generate a sampled position property from a list of OSTk LLAs.
|
|
592
|
+
|
|
593
|
+
Args:
|
|
594
|
+
states (list[LLA]): A list of OSTk LLAs.
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
cesiumpy.SampledPositionProperty: Sampled position property.
|
|
598
|
+
"""
|
|
599
|
+
|
|
600
|
+
return cesiumpy.SampledPositionProperty(
|
|
601
|
+
samples=[
|
|
602
|
+
(
|
|
603
|
+
coerce_to_datetime(instant),
|
|
604
|
+
_cesium_from_ostk_lla(lla),
|
|
605
|
+
None,
|
|
606
|
+
)
|
|
607
|
+
for instant, lla in zip(instants, llas)
|
|
608
|
+
],
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def _generate_sampled_orientation(states: list[State]) -> cesiumpy.SampledProperty:
|
|
613
|
+
"""
|
|
614
|
+
Generate a sampled orientation property from a list of OSTk States.
|
|
615
|
+
|
|
616
|
+
Args:
|
|
617
|
+
states (list[State]): A list of OSTk States.
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
cesiumpy.SampledProperty: Sampled orientation property.
|
|
621
|
+
"""
|
|
622
|
+
|
|
623
|
+
return cesiumpy.SampledProperty(
|
|
624
|
+
type=cesiumpy.Quaternion,
|
|
625
|
+
samples=[
|
|
626
|
+
(
|
|
627
|
+
coerce_to_datetime(state.get_instant()),
|
|
628
|
+
_cesium_from_ostk_quaternion(state.in_frame(Frame.ITRF()).get_attitude()),
|
|
629
|
+
None,
|
|
630
|
+
)
|
|
631
|
+
for state in states
|
|
632
|
+
],
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _generate_sampled_position_from_states(
|
|
637
|
+
states: list[State],
|
|
638
|
+
) -> cesiumpy.SampledPositionProperty:
|
|
639
|
+
"""
|
|
640
|
+
Generate a sampled position property from a list of OSTk States.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
states (list[State]): A list of OSTk States.
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
cesiumpy.SampledPositionProperty: Sampled position property.
|
|
647
|
+
"""
|
|
648
|
+
return _generate_sampled_position_from_positions(
|
|
649
|
+
instants=[state.get_instant() for state in states],
|
|
650
|
+
positions=[state.get_position() for state in states],
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def _generate_sampled_position_from_positions(
|
|
655
|
+
instants: list[Instant],
|
|
656
|
+
positions: list[Position],
|
|
657
|
+
) -> cesiumpy.SampledPositionProperty:
|
|
658
|
+
"""
|
|
659
|
+
Generate a sampled position property from a list of OSTk positions and instants.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
instants (list[Instant]): A list of OSTk instants.
|
|
663
|
+
positions (list[Position]): A list of OSTk positions.
|
|
664
|
+
|
|
665
|
+
Returns:
|
|
666
|
+
cesiumpy.SampledPositionProperty: Sampled position property.
|
|
667
|
+
"""
|
|
668
|
+
frame_itrf: Frame = Frame.ITRF()
|
|
669
|
+
|
|
670
|
+
return cesiumpy.SampledPositionProperty(
|
|
671
|
+
samples=[
|
|
672
|
+
(
|
|
673
|
+
coerce_to_datetime(instant),
|
|
674
|
+
_cesium_from_ostk_position(
|
|
675
|
+
position.in_frame(instant=instant, frame=frame_itrf)
|
|
676
|
+
),
|
|
677
|
+
None,
|
|
678
|
+
)
|
|
679
|
+
for instant, position in zip(instants, positions)
|
|
680
|
+
],
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _cesium_from_ostk_position(
|
|
685
|
+
position: Position,
|
|
686
|
+
instant: Instant | None = None,
|
|
687
|
+
) -> cesiumpy.Cartesian3:
|
|
688
|
+
"""
|
|
689
|
+
Convert OSTk Position into Cesium Cartesian3.
|
|
690
|
+
|
|
691
|
+
Args:
|
|
692
|
+
position (Position): Position to convert.
|
|
693
|
+
instant (Instant, optional): Instant at which the position is valid (required if position is not expressed in ECEF).
|
|
694
|
+
|
|
695
|
+
Returns:
|
|
696
|
+
cesiumpy.Cartesian3: Converted position.
|
|
697
|
+
"""
|
|
698
|
+
|
|
699
|
+
return _cesium_from_ostk_lla(lla_from_position(position, instant))
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def _cesium_from_ostk_lla(
|
|
703
|
+
lla: LLA,
|
|
704
|
+
) -> cesiumpy.Cartesian3:
|
|
705
|
+
"""
|
|
706
|
+
Convert OSTk LLA into Cesium Cartesian3.
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
lla (LLA): LLA to convert.
|
|
710
|
+
|
|
711
|
+
Returns:
|
|
712
|
+
cesiumpy.Cartesian3: Converted LLA.
|
|
713
|
+
"""
|
|
714
|
+
|
|
715
|
+
return cesiumpy.Cartesian3.fromDegrees(
|
|
716
|
+
float(lla.get_longitude().in_degrees()),
|
|
717
|
+
float(lla.get_latitude().in_degrees()),
|
|
718
|
+
float(lla.get_altitude().in_meters()),
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def _cesium_from_ostk_quaternion(quaternion: Quaternion) -> cesiumpy.Quaternion:
|
|
723
|
+
"""
|
|
724
|
+
Convert OSTk Quaternion into Cesium Quaternion.
|
|
725
|
+
|
|
726
|
+
Args:
|
|
727
|
+
quaternion (Quaternion): Quaternion to convert.
|
|
728
|
+
|
|
729
|
+
Returns:
|
|
730
|
+
cesiumpy.Quaternion: Converted quaternion.
|
|
731
|
+
"""
|
|
732
|
+
|
|
733
|
+
return cesiumpy.Quaternion(
|
|
734
|
+
float(quaternion.x()),
|
|
735
|
+
float(quaternion.y()),
|
|
736
|
+
float(quaternion.z()),
|
|
737
|
+
float(quaternion.s()),
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def _cesium_from_ostk_sensor(sensor: Sensor) -> cesiumpy.Sensor:
|
|
742
|
+
if isinstance(sensor, ConicSensor):
|
|
743
|
+
return cesiumpy.ConicSensor(
|
|
744
|
+
name=sensor.name,
|
|
745
|
+
direction=cesiumpy.Cartesian3(*sensor.direction),
|
|
746
|
+
half_angle=float(sensor.half_angle.in_radians()),
|
|
747
|
+
length=float(sensor.length.in_meters()),
|
|
748
|
+
material=sensor.color or cesiumpy.color.RED,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
elif isinstance(sensor, RectangularSensor):
|
|
752
|
+
return cesiumpy.RectangularSensor(
|
|
753
|
+
name=sensor.name,
|
|
754
|
+
direction=cesiumpy.Cartesian3(*sensor.direction),
|
|
755
|
+
x_half_angle=float(sensor.x_half_angle.in_radians()),
|
|
756
|
+
y_half_angle=float(sensor.y_half_angle.in_radians()),
|
|
757
|
+
radius=float(sensor.radius.in_meters()),
|
|
758
|
+
material=sensor.color or cesiumpy.color.ORANGE,
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
raise NotImplementedError("{sensor.__name__} is not supported yet.")
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def _compute_celestial_direction_from_state(
|
|
765
|
+
state: State,
|
|
766
|
+
celestial: Celestial,
|
|
767
|
+
frame: Frame | None = None,
|
|
768
|
+
) -> np.ndarray:
|
|
769
|
+
"""
|
|
770
|
+
Compute the direction of a celestial body from a state.
|
|
771
|
+
|
|
772
|
+
Args:
|
|
773
|
+
state (State): The state of the observer.
|
|
774
|
+
celestial (Celestial): The celestial body.
|
|
775
|
+
frame (Frame): The frame in which the celestial body is expressed.
|
|
776
|
+
Defaults to None. If None, the GCRF frame is used.
|
|
777
|
+
|
|
778
|
+
Returns:
|
|
779
|
+
np.ndarray: The direction of the celestial body (in meters).
|
|
780
|
+
"""
|
|
781
|
+
frame = frame or Frame.GCRF()
|
|
782
|
+
return (
|
|
783
|
+
celestial.get_position_in(
|
|
784
|
+
frame=frame,
|
|
785
|
+
instant=state.get_instant(),
|
|
786
|
+
)
|
|
787
|
+
.in_meters()
|
|
788
|
+
.get_coordinates()
|
|
789
|
+
- state.get_position()
|
|
790
|
+
.in_frame(
|
|
791
|
+
frame=frame,
|
|
792
|
+
instant=state.get_instant(),
|
|
793
|
+
)
|
|
794
|
+
.in_meters()
|
|
795
|
+
.get_coordinates()
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
def _compute_celestial_angular_diameter_from_states(
|
|
800
|
+
celestial: Celestial,
|
|
801
|
+
states: list[State],
|
|
802
|
+
) -> np.ndarray:
|
|
803
|
+
"""
|
|
804
|
+
Compute the angular diameter of a celestial body from the states of an observer.
|
|
805
|
+
|
|
806
|
+
Args:
|
|
807
|
+
celestial (Celestial): The celestial body.
|
|
808
|
+
states (list[State]): The states of the observer.
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
np.ndarray: The angular diameter of the celestial body (in degrees).
|
|
812
|
+
|
|
813
|
+
Reference:
|
|
814
|
+
https://en.wikipedia.org/wiki/Angular_diameter
|
|
815
|
+
"""
|
|
816
|
+
celestial_radius_meters: float = float(celestial.get_equatorial_radius().in_meters())
|
|
817
|
+
celestial_to_observer_meters: np.ndarray = np.zeros((3, len(states)))
|
|
818
|
+
|
|
819
|
+
for i, state in enumerate(states):
|
|
820
|
+
celestial_to_observer_meters[:, i] = (
|
|
821
|
+
state.in_frame(celestial.access_frame())
|
|
822
|
+
.get_position()
|
|
823
|
+
.in_meters()
|
|
824
|
+
.get_coordinates()
|
|
825
|
+
)
|
|
826
|
+
distances: np.ndarray = np.linalg.norm(celestial_to_observer_meters, axis=0)
|
|
827
|
+
return np.rad2deg(2 * np.arcsin(celestial_radius_meters / distances))
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
def _cesium_from_ostk_intervals(
|
|
831
|
+
intervals: list[Interval],
|
|
832
|
+
) -> cesiumpy.TimeIntervalCollection:
|
|
833
|
+
"""
|
|
834
|
+
Convert a list of OSTk intervals into Cesium TimeIntervalCollection.
|
|
835
|
+
|
|
836
|
+
Args:
|
|
837
|
+
intervals (list[Interval]): List of OSTk intervals.
|
|
838
|
+
|
|
839
|
+
Returns:
|
|
840
|
+
cesiumpy.TimeIntervalCollection: Converted intervals.
|
|
841
|
+
"""
|
|
842
|
+
|
|
843
|
+
return cesiumpy.TimeIntervalCollection(
|
|
844
|
+
intervals=[
|
|
845
|
+
cesiumpy.TimeInterval(
|
|
846
|
+
start=coerce_to_datetime(interval.get_start()),
|
|
847
|
+
stop=coerce_to_datetime(interval.get_end()),
|
|
848
|
+
)
|
|
849
|
+
for interval in intervals
|
|
850
|
+
],
|
|
851
|
+
)
|