open-space-toolkit-astrodynamics 15.0.0__py310-none-manylinux2014_x86_64.whl → 15.1.0__py310-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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: open-space-toolkit-astrodynamics
3
- Version: 15.0.0
3
+ Version: 15.1.0
4
4
  Summary: Orbit, attitude, access.
5
5
  Author: Open Space Collective
6
6
  Author-email: contact@open-space-collective.org
@@ -1,7 +1,7 @@
1
1
  ostk/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
2
2
  ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-310-x86_64-linux-gnu.so,sha256=gYM0JjMm4ZqA-zE01ki8UIBkTPFGOOYoRBCeWOddkI4,2631776
3
3
  ostk/astrodynamics/__init__.py,sha256=3gWyqFIbhAfcdeMhmfBPQPlPQTmaOzm-6flkJe745Zk,251
4
- ostk/astrodynamics/__init__.pyi,sha256=tfxDvws_Zrse_YY4O45u-DL4TJT4AAB7fyG_AIOpbIA,32231
4
+ ostk/astrodynamics/__init__.pyi,sha256=bR00BMMwv8B3E2WWEpnVJ_afWHWHr93CUvU5FeHPg3c,32231
5
5
  ostk/astrodynamics/access.pyi,sha256=0GAFCSoMHT3L8wWOxU1-3ELqR7YGyEkt6wlpaL7UXzI,25313
6
6
  ostk/astrodynamics/converters.py,sha256=luPh30qMp9bzEkN7hUccmxlLf7zRp_AzqmBe8IUjPhU,3314
7
7
  ostk/astrodynamics/converters.pyi,sha256=HrZFyizkc6Hv_K38ZKZ80fX_bAxd6keA_NFWNQygvbs,1745
@@ -13,7 +13,7 @@ ostk/astrodynamics/guidance_law.pyi,sha256=pEoAL9CDFWNulOLv6wea3xj1fJI3xFtgx-2eY
13
13
  ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.15,sha256=_YtpT-mc45lP9dFMoUZvxx5Hw8zQZs75QIwmFJS8VEU,101370512
14
14
  ostk/astrodynamics/solver.pyi,sha256=ugi73KdRXPKFvKXFMpYKIwxYUGC2KEMlBiLv0PFcigM,9746
15
15
  ostk/astrodynamics/utilities.py,sha256=mlKL3WrOASrY_pLt7bzUz0XZT7jagzLOZjMOIqjbQ1Y,6999
16
- ostk/astrodynamics/viewer.py,sha256=tmkHe4K4oQ2739ETdPDu8-3YtWhgLtFLMwbenxIu1VU,12670
16
+ ostk/astrodynamics/viewer.py,sha256=SpcvBqXx3CAZpk7UGOPT1ilxywmAmNpgqpuRY11Hwx0,16376
17
17
  ostk/astrodynamics/conjunction/__init__.pyi,sha256=HFvWl8bCmrq3cBkUh5X5RGIh8webmVGxaZdpsz3WN-E,79
18
18
  ostk/astrodynamics/conjunction/message/__init__.pyi,sha256=5H__sg_QUx7ybf9jtVWvXzrUHeK3ECotfhddAdHjJUc,75
19
19
  ostk/astrodynamics/conjunction/message/ccsds.pyi,sha256=3FCWOnInqwI5FKfodR4-xYuiBS0UzemoIXsc1GWPDQI,28690
@@ -38,7 +38,7 @@ ostk/astrodynamics/test/test_import.py,sha256=py_hALBR0IYuUzv9dfgQZzrrLHJIpnyKvt
38
38
  ostk/astrodynamics/test/test_root_solver.py,sha256=hQ8O6g-WP49gZH_H3Rdufv0F0gQorpzJyIcjBGGUQ34,1831
39
39
  ostk/astrodynamics/test/test_trajectory.py,sha256=0sgGPkMFy-u-33z6SF-sTvaBb_NU7OvaeMTVF6wevfY,3148
40
40
  ostk/astrodynamics/test/test_utilities.py,sha256=NNIyzqOxMdsNpK2z0wU0utX06iZNfbMJDE36Upard28,3048
41
- ostk/astrodynamics/test/test_viewer.py,sha256=rfA6daUqtATtaA2daE_kFWNK4oBBHreR5DKeXRKC-j0,6582
41
+ ostk/astrodynamics/test/test_viewer.py,sha256=kDstRH_WKufN_0JPSXttzLMk3Afv_ylcEBfHxc35yrA,8946
42
42
  ostk/astrodynamics/test/access/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
43
43
  ostk/astrodynamics/test/access/test_generator.py,sha256=i7TnM80kF0Q_9KmyoqKt5n1ufg3ZjAIEMPVVds8ZDdI,10315
44
44
  ostk/astrodynamics/test/access/test_visibility_criterion.py,sha256=0Q_lcs6PZC_mTIHDuIPc8adKF02q2EM_ZR6jT5UhnDg,6353
@@ -124,8 +124,8 @@ ostk/astrodynamics/trajectory/orbit/model/kepler.pyi,sha256=09xlx_7dTiivctFDyW_3
124
124
  ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi,sha256=2rxyPPsSCtsEKAShuZQbhZSybK01Q9u1CMieGjwWxN4,14313
125
125
  ostk/astrodynamics/trajectory/state/__init__.pyi,sha256=-sq42oF9YgXSkP1LtPcSu7mmtpjUkEzjydD9VYdIQ6g,17672
126
126
  ostk/astrodynamics/trajectory/state/coordinate_subset.pyi,sha256=iqoM-8RqiLNIsyiQXLfosvGO7ZP_KdQxrUWqiV04E1c,10566
127
- open_space_toolkit_astrodynamics-15.0.0.dist-info/METADATA,sha256=Bf2ectBDzmNRiENq9b31LDEhBBG-bARuZMbfdyJwswM,1913
128
- open_space_toolkit_astrodynamics-15.0.0.dist-info/WHEEL,sha256=esAQ49biZzN3Mxaws3kr8B8Sz08ER9fLpbEMXaWkwRw,110
129
- open_space_toolkit_astrodynamics-15.0.0.dist-info/top_level.txt,sha256=zOR18699uDYnafgarhL8WU_LmTZY_5NVqutv-flp_x4,5
130
- open_space_toolkit_astrodynamics-15.0.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
131
- open_space_toolkit_astrodynamics-15.0.0.dist-info/RECORD,,
127
+ open_space_toolkit_astrodynamics-15.1.0.dist-info/METADATA,sha256=52SOCFxHag6hxYwvZmHaYbAds9Sbi7efOiwqvUWHIj8,1913
128
+ open_space_toolkit_astrodynamics-15.1.0.dist-info/WHEEL,sha256=esAQ49biZzN3Mxaws3kr8B8Sz08ER9fLpbEMXaWkwRw,110
129
+ open_space_toolkit_astrodynamics-15.1.0.dist-info/top_level.txt,sha256=zOR18699uDYnafgarhL8WU_LmTZY_5NVqutv-flp_x4,5
130
+ open_space_toolkit_astrodynamics-15.1.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
131
+ open_space_toolkit_astrodynamics-15.1.0.dist-info/RECORD,,
@@ -19,8 +19,8 @@ from ostk import physics as OpenSpaceToolkitPhysicsPy
19
19
  from ostk.physics import Environment
20
20
  from ostk.physics import Manager
21
21
  from ostk.physics import Unit
22
- from ostk.physics import coordinate
23
22
  import ostk.physics.coordinate
23
+ from ostk.physics import coordinate
24
24
  import ostk.physics.coordinate.spherical
25
25
  from ostk.physics import environment
26
26
  import ostk.physics.environment.object
@@ -36,6 +36,24 @@ def orbit(environment: Environment) -> Orbit:
36
36
  )
37
37
 
38
38
 
39
+ @pytest.fixture
40
+ def orbits(environment: Environment) -> list[Orbit]:
41
+ return [
42
+ Orbit.sun_synchronous(
43
+ epoch=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
44
+ altitude=Length.kilometers(500.0),
45
+ local_time_at_descending_node=Time(14, 0, 0),
46
+ celestial_object=environment.access_celestial_object_with_name("Earth"),
47
+ ),
48
+ Orbit.sun_synchronous(
49
+ epoch=Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC),
50
+ altitude=Length.kilometers(500.0),
51
+ local_time_at_descending_node=Time(12, 0, 0),
52
+ celestial_object=environment.access_celestial_object_with_name("Earth"),
53
+ ),
54
+ ]
55
+
56
+
39
57
  @pytest.fixture
40
58
  def profile(orbit: Orbit) -> Profile:
41
59
  return Profile.local_orbital_frame_pointing(
@@ -56,6 +74,8 @@ def interval() -> Interval:
56
74
  def viewer(interval: Interval) -> Viewer:
57
75
  return Viewer(
58
76
  interval=interval,
77
+ zoom_to_entity=False,
78
+ track_entity=False,
59
79
  )
60
80
 
61
81
 
@@ -74,6 +94,50 @@ class TestViewer:
74
94
  assert "var widget = new Cesium.Viewer" in rendered_html
75
95
  assert rendered_html.endswith("</script>")
76
96
 
97
+ def test_add_orbit_success(
98
+ self,
99
+ viewer: Viewer,
100
+ orbit: Orbit,
101
+ ):
102
+ viewer.add_orbit(
103
+ orbit=orbit,
104
+ step=Duration.seconds(5.0),
105
+ show_orbital_track=True,
106
+ )
107
+
108
+ rendered_html: str = viewer.render()
109
+
110
+ assert rendered_html.startswith('<meta charset="utf-8">')
111
+ assert "var widget = new Cesium.Viewer" in rendered_html
112
+ assert "new Cesium.SampledProperty(Cesium.Cartesian3)" in rendered_html
113
+ assert " widget.entities.add({position: widget" in rendered_html
114
+ assert "widget.entities.add({polyline:" in rendered_html
115
+ assert "billboard: {image:" in rendered_html
116
+ assert rendered_html.endswith("</script>")
117
+
118
+ def test_add_orbit_multiple_success(
119
+ self,
120
+ viewer: Viewer,
121
+ orbits: list[Orbit],
122
+ ):
123
+ for i, orbit in enumerate(orbits):
124
+ viewer.add_orbit(
125
+ orbit=orbit,
126
+ step=Duration.seconds(5.0),
127
+ show_orbital_track=True,
128
+ name=f"Satellite {i}",
129
+ )
130
+
131
+ rendered_html: str = viewer.render()
132
+
133
+ assert rendered_html.startswith('<meta charset="utf-8">')
134
+ assert "var widget = new Cesium.Viewer" in rendered_html
135
+ assert "new Cesium.SampledProperty(Cesium.Cartesian3)" in rendered_html
136
+ assert " widget.entities.add({position: widget" in rendered_html
137
+ assert "widget.entities.add({polyline:" in rendered_html
138
+ assert "billboard: {image:" in rendered_html
139
+ assert rendered_html.endswith("</script>")
140
+
77
141
  def test_add_profile_success(
78
142
  self,
79
143
  viewer: Viewer,
@@ -24,11 +24,16 @@ from ostk.physics.coordinate import Frame
24
24
  from ostk.physics.coordinate.spherical import LLA
25
25
 
26
26
  from ostk.astrodynamics.flight import Profile
27
+ from ostk.astrodynamics.trajectory import Orbit
27
28
  from ostk.astrodynamics.trajectory import State
28
29
 
29
30
  from .converters import coerce_to_datetime
30
31
  from .utilities import lla_from_position
31
32
 
33
+ DEFAULT_SATELLITE_IMAGE: str = (
34
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII="
35
+ )
36
+
32
37
 
33
38
  @dataclass
34
39
  class Sensor:
@@ -57,6 +62,8 @@ class Viewer:
57
62
  cesium_token: str | None = None,
58
63
  width: str = "1500px",
59
64
  height: str = "800px",
65
+ zoom_to_entity: bool = True,
66
+ track_entity: bool = True,
60
67
  ) -> None:
61
68
  self._interval: Interval = interval
62
69
 
@@ -81,8 +88,8 @@ class Viewer:
81
88
  scene_mode_picker=False,
82
89
  selection_indicator=False,
83
90
  scene3d_only=True,
84
- zoom_to_entity=True,
85
- track_entity=True,
91
+ zoom_to_entity=zoom_to_entity,
92
+ track_entity=track_entity,
86
93
  default_access_token=cesium_token,
87
94
  )
88
95
 
@@ -90,6 +97,73 @@ class Viewer:
90
97
  def interval(self) -> Interval:
91
98
  return self._interval
92
99
 
100
+ def add_orbit(
101
+ self,
102
+ orbit: Orbit,
103
+ step: Duration,
104
+ name: str = "Satellite",
105
+ show_orbital_track: bool = False,
106
+ color: str | None = None,
107
+ image: str | None = None,
108
+ ) -> None:
109
+ """
110
+ Add Orbit to Viewer.
111
+
112
+ Args:
113
+ orbit (Orbit): Orbit to be added.
114
+ step (Duration): Step between two consecutive states.
115
+ name (str, optional): Name of the orbit. Defaults to "Satellite".
116
+ show_orbital_track (bool, optional): Whether to show the orbital track. Defaults to False.
117
+ color (str, optional): Color of the orbit. Defaults to None.
118
+ image (str, optional): Logo to be added. Defaults to None.
119
+ """
120
+ instants: list[Instant] = self._interval.generate_grid(step)
121
+ states: list[State] = orbit.get_states_at(instants)
122
+ llas: list[LLA] = _generate_llas(states)
123
+
124
+ cesium_positions: cesiumpy.SampledPositionProperty = (
125
+ _generate_sampled_position_from_states(states)
126
+ )
127
+
128
+ self._viewer.entities.add(
129
+ cesiumpy.Billboard(
130
+ name=name,
131
+ position=cesium_positions,
132
+ image=image or DEFAULT_SATELLITE_IMAGE,
133
+ )
134
+ )
135
+
136
+ self._viewer.entities.add(
137
+ cesiumpy.Label(
138
+ position=cesium_positions,
139
+ text=name,
140
+ scale=1.0,
141
+ fill_color=color or cesiumpy.color.WHITE,
142
+ pixel_offset=[0.0, 20.0],
143
+ )
144
+ )
145
+
146
+ if show_orbital_track:
147
+ self._viewer.entities.add(
148
+ cesiumpy.Polyline(
149
+ positions=cesiumpy.entities.cartesian.Cartesian3Array(
150
+ functools.reduce(
151
+ operator.iconcat,
152
+ [
153
+ [
154
+ float(lla.get_longitude().in_degrees()),
155
+ float(lla.get_latitude().in_degrees()),
156
+ float(lla.get_altitude().in_meters()),
157
+ ]
158
+ for lla in llas
159
+ ],
160
+ [],
161
+ )
162
+ ),
163
+ width=1,
164
+ )
165
+ )
166
+
93
167
  def add_profile(
94
168
  self,
95
169
  profile: Profile,
@@ -144,7 +218,7 @@ class Viewer:
144
218
  )
145
219
 
146
220
  satellite = cesiumpy.Satellite(
147
- position=_generate_sampled_position(instants, llas),
221
+ position=_generate_sampled_position_from_llas(instants, llas),
148
222
  orientation=_generate_sampled_orientation(states),
149
223
  availability=cesiumpy.TimeIntervalCollection(
150
224
  intervals=[
@@ -294,7 +368,7 @@ def _generate_llas(states: list[State]) -> list[LLA]:
294
368
  ]
295
369
 
296
370
 
297
- def _generate_sampled_position(
371
+ def _generate_sampled_position_from_llas(
298
372
  instants: list[Instant],
299
373
  llas: list[LLA],
300
374
  ) -> cesiumpy.SampledPositionProperty:
@@ -344,6 +418,33 @@ def _generate_sampled_orientation(states: list[State]) -> cesiumpy.SampledProper
344
418
  )
345
419
 
346
420
 
421
+ def _generate_sampled_position_from_states(
422
+ states: list[State],
423
+ ) -> cesiumpy.SampledPositionProperty:
424
+ """
425
+ Generate a sampled position property from a list of OSTk States.
426
+
427
+ Args:
428
+ states (list[State]): A list of OSTk States.
429
+
430
+ Returns:
431
+ cesiumpy.SampledPositionProperty: Sampled position property.
432
+ """
433
+
434
+ return cesiumpy.SampledPositionProperty(
435
+ samples=[
436
+ (
437
+ coerce_to_datetime(state.get_instant()),
438
+ _cesium_from_ostk_position(
439
+ position=state.in_frame(Frame.ITRF()).get_position()
440
+ ),
441
+ None,
442
+ )
443
+ for state in states
444
+ ],
445
+ )
446
+
447
+
347
448
  def _cesium_from_ostk_position(
348
449
  position: Position,
349
450
  instant: Instant | None = None,