open-space-toolkit-astrodynamics 16.2.0__py313-none-manylinux2014_x86_64.whl → 16.4.0__py313-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-16.2.0.dist-info → open_space_toolkit_astrodynamics-16.4.0.dist-info}/METADATA +1 -1
- {open_space_toolkit_astrodynamics-16.2.0.dist-info → open_space_toolkit_astrodynamics-16.4.0.dist-info}/RECORD +21 -16
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-313-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.pyi +5 -5
- ostk/astrodynamics/flight/__init__.pyi +18 -9
- ostk/astrodynamics/flight/profile/model.pyi +19 -0
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.16 +0 -0
- ostk/astrodynamics/test/flight/profile/model/test_tabulated_profile.py +115 -0
- ostk/astrodynamics/test/flight/test_profile.py +4 -0
- ostk/astrodynamics/test/test_display.py +0 -2
- ostk/astrodynamics/test/test_trajectory.py +28 -7
- ostk/astrodynamics/test/test_viewer.py +56 -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/trajectory/__init__.pyi +10 -9
- ostk/astrodynamics/trajectory/model.pyi +259 -0
- ostk/astrodynamics/viewer.py +316 -14
- {open_space_toolkit_astrodynamics-16.2.0.dist-info → open_space_toolkit_astrodynamics-16.4.0.dist-info}/WHEEL +0 -0
- {open_space_toolkit_astrodynamics-16.2.0.dist-info → open_space_toolkit_astrodynamics-16.4.0.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-16.2.0.dist-info → open_space_toolkit_astrodynamics-16.4.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
import ostk.astrodynamics.trajectory
|
3
|
+
import ostk.mathematics.curve_fitting
|
4
|
+
import ostk.physics.coordinate.spherical
|
5
|
+
import ostk.physics.environment.object
|
6
|
+
import ostk.physics.time
|
7
|
+
import ostk.physics.unit
|
8
|
+
import typing
|
9
|
+
__all__ = ['Nadir', 'Tabulated', 'TargetScan']
|
10
|
+
class Nadir(ostk.astrodynamics.trajectory.Model):
|
11
|
+
"""
|
12
|
+
|
13
|
+
Nadir trajectory model.
|
14
|
+
|
15
|
+
This model represents a trajectory that follows the nadir direction of an orbit.
|
16
|
+
|
17
|
+
"""
|
18
|
+
__hash__: typing.ClassVar[None] = None
|
19
|
+
def __eq__(self, arg0: Nadir) -> bool:
|
20
|
+
...
|
21
|
+
def __init__(self, orbit: ostk.astrodynamics.trajectory.Orbit, step_size: ostk.physics.time.Duration = ...) -> None:
|
22
|
+
"""
|
23
|
+
Construct a `Nadir` object from an orbit.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
orbit (Orbit): The orbit.
|
27
|
+
step_size (Duration): The step size for the trajectory. Defaults to 1e-2 seconds.
|
28
|
+
|
29
|
+
Returns:
|
30
|
+
Nadir: The `Nadir` object.
|
31
|
+
"""
|
32
|
+
def __ne__(self, arg0: Nadir) -> bool:
|
33
|
+
...
|
34
|
+
def __repr__(self) -> str:
|
35
|
+
...
|
36
|
+
def __str__(self) -> str:
|
37
|
+
...
|
38
|
+
def calculate_state_at(self, instant: ostk.physics.time.Instant) -> ostk.astrodynamics.trajectory.State:
|
39
|
+
"""
|
40
|
+
Calculate the state at a given instant.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
instant (Instant): The instant.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
State: The state at the given instant.
|
47
|
+
"""
|
48
|
+
def get_orbit(self) -> ostk.astrodynamics.trajectory.Orbit:
|
49
|
+
"""
|
50
|
+
Get the orbit of the nadir model.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
Orbit: The orbit of the nadir model.
|
54
|
+
"""
|
55
|
+
def get_step_size(self) -> ostk.physics.time.Duration:
|
56
|
+
"""
|
57
|
+
Get the step size of the nadir model.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Duration: The step size of the nadir model.
|
61
|
+
"""
|
62
|
+
def is_defined(self) -> bool:
|
63
|
+
"""
|
64
|
+
Check if the model is defined.
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
bool: True if the model is defined, False otherwise.
|
68
|
+
"""
|
69
|
+
class Tabulated(ostk.astrodynamics.trajectory.Model):
|
70
|
+
"""
|
71
|
+
|
72
|
+
A trajectory model defined by a set of states.
|
73
|
+
|
74
|
+
|
75
|
+
"""
|
76
|
+
def __init__(self, states: list[ostk.astrodynamics.trajectory.State], interpolation_type: ostk.mathematics.curve_fitting.Interpolator.Type = ...) -> None:
|
77
|
+
"""
|
78
|
+
Constructor.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
states (Array[State]): The states of the model.
|
82
|
+
interpolation_type (Interpolator.Type): The type of interpolation to use. Defaults to Linear.
|
83
|
+
"""
|
84
|
+
def __repr__(self) -> str:
|
85
|
+
"""
|
86
|
+
Convert the model to a string.
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
str: The string representation of the model.
|
90
|
+
"""
|
91
|
+
def __str__(self) -> str:
|
92
|
+
"""
|
93
|
+
Convert the model to a string.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
str: The string representation of the model.
|
97
|
+
"""
|
98
|
+
def calculate_state_at(self, instant: ostk.physics.time.Instant) -> ostk.astrodynamics.trajectory.State:
|
99
|
+
"""
|
100
|
+
Calculate the state of the model at a specific instant.
|
101
|
+
|
102
|
+
Args:
|
103
|
+
instant (Instant): The instant at which to calculate the state.
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
State: The state of the model at the specified instant.
|
107
|
+
"""
|
108
|
+
def calculate_states_at(self, instants: list[ostk.physics.time.Instant]) -> list[ostk.astrodynamics.trajectory.State]:
|
109
|
+
"""
|
110
|
+
Calculate the states of the model at the specified instants.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
instants (list[Instant]): The instants at which to calculate the states.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
list[State]: The states of the model at the specified instants.
|
117
|
+
"""
|
118
|
+
def get_first_state(self) -> ostk.astrodynamics.trajectory.State:
|
119
|
+
"""
|
120
|
+
Get the first state of the model.
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
State: The first state of the model.
|
124
|
+
"""
|
125
|
+
def get_interpolation_type(self) -> ostk.mathematics.curve_fitting.Interpolator.Type:
|
126
|
+
"""
|
127
|
+
Get the interpolation type of the model.
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Interpolator.Type: The interpolation type of the model.
|
131
|
+
"""
|
132
|
+
def get_interval(self) -> ostk.physics.time.Interval:
|
133
|
+
"""
|
134
|
+
Get the interval of the model.
|
135
|
+
|
136
|
+
Returns:
|
137
|
+
Interval: The interval of the model.
|
138
|
+
"""
|
139
|
+
def get_last_state(self) -> ostk.astrodynamics.trajectory.State:
|
140
|
+
"""
|
141
|
+
Get the last state of the model.
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
State: The last state of the model.
|
145
|
+
"""
|
146
|
+
def is_defined(self) -> bool:
|
147
|
+
"""
|
148
|
+
Check if the model is defined.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
bool: True if the model is defined, False otherwise.
|
152
|
+
"""
|
153
|
+
class TargetScan(ostk.astrodynamics.trajectory.Model):
|
154
|
+
"""
|
155
|
+
|
156
|
+
TargetScan trajectory model.
|
157
|
+
|
158
|
+
This model represents a trajectory that scans between two target locations on a celestial body.
|
159
|
+
|
160
|
+
"""
|
161
|
+
__hash__: typing.ClassVar[None] = None
|
162
|
+
@staticmethod
|
163
|
+
def from_ground_speed(start_lla: ostk.physics.coordinate.spherical.LLA, end_lla: ostk.physics.coordinate.spherical.LLA, ground_speed: ostk.physics.unit.Derived, start_instant: ostk.physics.time.Instant, celestial: ostk.physics.environment.object.Celestial = ..., step_size: ostk.physics.time.Duration = ...) -> TargetScan:
|
164
|
+
"""
|
165
|
+
Construct a `TargetScan` object from ground speed.
|
166
|
+
|
167
|
+
Args:
|
168
|
+
start_lla (LLA): The starting location.
|
169
|
+
end_lla (LLA): The ending location.
|
170
|
+
ground_speed (Derived): The ground speed.
|
171
|
+
start_instant (Instant): The starting instant.
|
172
|
+
celestial (Celestial): The celestial body.
|
173
|
+
step_size (Duration): The step size for the trajectory.
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
TargetScan: The `TargetScan` object.
|
177
|
+
"""
|
178
|
+
def __eq__(self, arg0: TargetScan) -> bool:
|
179
|
+
...
|
180
|
+
def __init__(self, start_lla: ostk.physics.coordinate.spherical.LLA, end_lla: ostk.physics.coordinate.spherical.LLA, start_instant: ostk.physics.time.Instant, end_instant: ostk.physics.time.Instant, celestial: ostk.physics.environment.object.Celestial = ..., step_size: ostk.physics.time.Duration = ...) -> None:
|
181
|
+
"""
|
182
|
+
Construct a `TargetScan` object.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
start_lla (LLA): The starting location.
|
186
|
+
end_lla (LLA): The ending location.
|
187
|
+
start_instant (Instant): The starting instant.
|
188
|
+
end_instant (Instant): The ending instant.
|
189
|
+
celestial (Celestial): The celestial body. Defaults to Earth.WGS84().
|
190
|
+
step_size (Duration): The step size for the trajectory. Defaults to 1e-2 seconds.
|
191
|
+
|
192
|
+
Returns:
|
193
|
+
TargetScan: The `TargetScan` object.
|
194
|
+
"""
|
195
|
+
def __ne__(self, arg0: TargetScan) -> bool:
|
196
|
+
...
|
197
|
+
def __repr__(self) -> str:
|
198
|
+
...
|
199
|
+
def __str__(self) -> str:
|
200
|
+
...
|
201
|
+
def calculate_state_at(self, instant: ostk.physics.time.Instant) -> ostk.astrodynamics.trajectory.State:
|
202
|
+
"""
|
203
|
+
Calculate the state at a given instant.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
instant (Instant): The instant.
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
State: The state at the given instant.
|
210
|
+
"""
|
211
|
+
def get_celestial(self) -> ostk.physics.environment.object.Celestial:
|
212
|
+
"""
|
213
|
+
Get the celestial object of the target scan.
|
214
|
+
|
215
|
+
Returns:
|
216
|
+
Celestial: The celestial object.
|
217
|
+
"""
|
218
|
+
def get_end_instant(self) -> ostk.physics.time.Instant:
|
219
|
+
"""
|
220
|
+
Get the ending instant of the target scan.
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
Instant: The ending instant.
|
224
|
+
"""
|
225
|
+
def get_end_lla(self) -> ostk.physics.coordinate.spherical.LLA:
|
226
|
+
"""
|
227
|
+
Get the ending LLA of the target scan.
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
LLA: The ending LLA.
|
231
|
+
"""
|
232
|
+
def get_start_instant(self) -> ostk.physics.time.Instant:
|
233
|
+
"""
|
234
|
+
Get the starting instant of the target scan.
|
235
|
+
|
236
|
+
Returns:
|
237
|
+
Instant: The starting instant.
|
238
|
+
"""
|
239
|
+
def get_start_lla(self) -> ostk.physics.coordinate.spherical.LLA:
|
240
|
+
"""
|
241
|
+
Get the starting LLA of the target scan.
|
242
|
+
|
243
|
+
Returns:
|
244
|
+
LLA: The starting LLA.
|
245
|
+
"""
|
246
|
+
def get_step_size(self) -> ostk.physics.time.Duration:
|
247
|
+
"""
|
248
|
+
Get the step size of the target scan.
|
249
|
+
|
250
|
+
Returns:
|
251
|
+
Duration: The step size.
|
252
|
+
"""
|
253
|
+
def is_defined(self) -> bool:
|
254
|
+
"""
|
255
|
+
Check if the model is defined.
|
256
|
+
|
257
|
+
Returns:
|
258
|
+
bool: True if the model is defined, False otherwise.
|
259
|
+
"""
|
ostk/astrodynamics/viewer.py
CHANGED
@@ -16,6 +16,7 @@ except ImportError:
|
|
16
16
|
|
17
17
|
from ostk.mathematics.geometry.d3.transformation.rotation import Quaternion
|
18
18
|
|
19
|
+
from ostk.physics.environment.object import Celestial
|
19
20
|
from ostk.physics.unit import Length
|
20
21
|
from ostk.physics.unit import Angle
|
21
22
|
from ostk.physics.time import Instant, Interval, Duration
|
@@ -23,16 +24,20 @@ from ostk.physics.coordinate import Position
|
|
23
24
|
from ostk.physics.coordinate import Frame
|
24
25
|
from ostk.physics.coordinate.spherical import LLA
|
25
26
|
|
27
|
+
from ostk.astrodynamics import Trajectory
|
26
28
|
from ostk.astrodynamics.flight import Profile
|
27
29
|
from ostk.astrodynamics.trajectory import Orbit
|
28
30
|
from ostk.astrodynamics.trajectory import State
|
29
31
|
|
30
32
|
from .converters import coerce_to_datetime
|
31
33
|
from .utilities import lla_from_position
|
34
|
+
from .utilities import lla_from_state
|
35
|
+
from .utilities import position_from_lla
|
32
36
|
|
33
37
|
DEFAULT_SATELLITE_IMAGE: str = (
|
34
38
|
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII="
|
35
39
|
)
|
40
|
+
DEFAULT_STEP_DURATION: Duration = Duration.seconds(10.0)
|
36
41
|
|
37
42
|
|
38
43
|
@dataclass
|
@@ -228,14 +233,7 @@ class Viewer:
|
|
228
233
|
satellite = cesiumpy.Satellite(
|
229
234
|
position=_generate_sampled_position_from_llas(instants, llas),
|
230
235
|
orientation=_generate_sampled_orientation(states),
|
231
|
-
availability=
|
232
|
-
intervals=[
|
233
|
-
cesiumpy.TimeInterval(
|
234
|
-
start=coerce_to_datetime(self._interval.get_start()),
|
235
|
-
stop=coerce_to_datetime(self._interval.get_end()),
|
236
|
-
),
|
237
|
-
],
|
238
|
-
),
|
236
|
+
availability=self._get_availability(),
|
239
237
|
model=cesiumpy.IonResource(
|
240
238
|
asset_id=cesium_asset_id or 0
|
241
239
|
), # TBM: Should be made more robust
|
@@ -267,6 +265,158 @@ class Viewer:
|
|
267
265
|
|
268
266
|
return self
|
269
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
|
+
|
270
420
|
def add_target(
|
271
421
|
self,
|
272
422
|
position: Position,
|
@@ -341,6 +491,40 @@ class Viewer:
|
|
341
491
|
|
342
492
|
return self
|
343
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
|
+
|
344
528
|
def add_label(
|
345
529
|
self,
|
346
530
|
position: Position,
|
@@ -382,14 +566,21 @@ class Viewer:
|
|
382
566
|
|
383
567
|
return self._viewer.to_html()
|
384
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
|
+
|
385
578
|
def _repr_html_(self) -> str:
|
386
579
|
return self.render()
|
387
580
|
|
388
581
|
|
389
582
|
def _generate_llas(states: list[State]) -> list[LLA]:
|
390
|
-
return
|
391
|
-
lla_from_position(state.get_position(), state.get_instant()) for state in states
|
392
|
-
]
|
583
|
+
return list(map(lla_from_state, states))
|
393
584
|
|
394
585
|
|
395
586
|
def _generate_sampled_position_from_llas(
|
@@ -454,17 +645,38 @@ def _generate_sampled_position_from_states(
|
|
454
645
|
Returns:
|
455
646
|
cesiumpy.SampledPositionProperty: Sampled position property.
|
456
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()
|
457
669
|
|
458
670
|
return cesiumpy.SampledPositionProperty(
|
459
671
|
samples=[
|
460
672
|
(
|
461
|
-
coerce_to_datetime(
|
673
|
+
coerce_to_datetime(instant),
|
462
674
|
_cesium_from_ostk_position(
|
463
|
-
position
|
675
|
+
position.in_frame(instant=instant, frame=frame_itrf)
|
464
676
|
),
|
465
677
|
None,
|
466
678
|
)
|
467
|
-
for
|
679
|
+
for instant, position in zip(instants, positions)
|
468
680
|
],
|
469
681
|
)
|
470
682
|
|
@@ -547,3 +759,93 @@ def _cesium_from_ostk_sensor(sensor: Sensor) -> cesiumpy.Sensor:
|
|
547
759
|
)
|
548
760
|
|
549
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
|
+
)
|
File without changes
|
File without changes
|
File without changes
|