open-space-toolkit-astrodynamics 12.2.1__py312-none-manylinux2014_aarch64.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.
Files changed (100) hide show
  1. open_space_toolkit_astrodynamics-12.2.1.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-12.2.1.dist-info/RECORD +100 -0
  3. open_space_toolkit_astrodynamics-12.2.1.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-12.2.1.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-12.2.1.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-aarch64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/converters.py +128 -0
  10. ostk/astrodynamics/dataframe.py +477 -0
  11. ostk/astrodynamics/display.py +220 -0
  12. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.12 +0 -0
  13. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  14. ostk/astrodynamics/pytrajectory/pystate.py +220 -0
  15. ostk/astrodynamics/test/__init__.py +1 -0
  16. ostk/astrodynamics/test/access/__init__.py +1 -0
  17. ostk/astrodynamics/test/access/test_generator.py +248 -0
  18. ostk/astrodynamics/test/conftest.py +119 -0
  19. ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
  20. ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
  21. ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
  22. ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
  23. ostk/astrodynamics/test/dynamics/__init__.py +1 -0
  24. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
  25. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
  26. ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
  27. ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
  28. ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
  29. ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
  30. ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
  31. ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
  32. ostk/astrodynamics/test/dynamics/test_thruster.py +142 -0
  33. ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
  34. ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
  35. ostk/astrodynamics/test/event_condition/test_coe_condition.py +87 -0
  36. ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
  37. ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
  38. ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
  39. ostk/astrodynamics/test/flight/__init__.py +1 -0
  40. ostk/astrodynamics/test/flight/system/__init__.py +1 -0
  41. ostk/astrodynamics/test/flight/system/test_propulsion_system.py +73 -0
  42. ostk/astrodynamics/test/flight/system/test_satellite_system.py +91 -0
  43. ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
  44. ostk/astrodynamics/test/flight/test_maneuver.py +212 -0
  45. ostk/astrodynamics/test/flight/test_profile.py +242 -0
  46. ostk/astrodynamics/test/flight/test_system.py +55 -0
  47. ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +91 -0
  48. ostk/astrodynamics/test/guidance_law/test_qlaw.py +138 -0
  49. ostk/astrodynamics/test/solvers/__init__.py +1 -0
  50. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +181 -0
  51. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +153 -0
  52. ostk/astrodynamics/test/test_access.py +128 -0
  53. ostk/astrodynamics/test/test_converters.py +290 -0
  54. ostk/astrodynamics/test/test_dataframe.py +875 -0
  55. ostk/astrodynamics/test/test_display.py +114 -0
  56. ostk/astrodynamics/test/test_event_condition.py +58 -0
  57. ostk/astrodynamics/test/test_import.py +26 -0
  58. ostk/astrodynamics/test/test_root_solver.py +70 -0
  59. ostk/astrodynamics/test/test_trajectory.py +118 -0
  60. ostk/astrodynamics/test/test_utilities.py +106 -0
  61. ostk/astrodynamics/test/test_viewer.py +129 -0
  62. ostk/astrodynamics/test/trajectory/__init__.py +1 -0
  63. ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
  64. ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
  65. ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
  66. ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
  67. ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
  68. ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
  69. ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
  70. ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
  71. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
  72. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
  73. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
  74. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +180 -0
  75. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
  76. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +331 -0
  77. ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
  78. ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
  79. ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
  80. ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
  81. ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
  82. ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
  83. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
  84. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
  85. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
  86. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
  87. ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
  88. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +46 -0
  89. ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +314 -0
  90. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
  91. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +108 -0
  92. ostk/astrodynamics/test/trajectory/test_model.py +1 -0
  93. ostk/astrodynamics/test/trajectory/test_orbit.py +196 -0
  94. ostk/astrodynamics/test/trajectory/test_propagator.py +458 -0
  95. ostk/astrodynamics/test/trajectory/test_segment.py +305 -0
  96. ostk/astrodynamics/test/trajectory/test_sequence.py +477 -0
  97. ostk/astrodynamics/test/trajectory/test_state.py +467 -0
  98. ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
  99. ostk/astrodynamics/utilities.py +245 -0
  100. ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,477 @@
1
+ # Copyright © Loft Orbital Solutions Inc.
2
+
3
+ import numpy as np
4
+
5
+ import pandas as pd
6
+
7
+ from ostk.mathematics.curve_fitting import Interpolator
8
+
9
+ from ostk.physics.environment.object import Celestial
10
+ from ostk.physics.environment.object.celestial import Earth
11
+ from ostk.physics.time import Instant
12
+ from ostk.physics.coordinate import Frame
13
+
14
+ from ostk.astrodynamics.converters import coerce_to_datetime
15
+ from ostk.astrodynamics.converters import coerce_to_instant
16
+ from ostk.astrodynamics.trajectory import State
17
+ from ostk.astrodynamics.trajectory import StateBuilder
18
+ from ostk.astrodynamics.trajectory import Orbit
19
+ from ostk.astrodynamics.trajectory.state import CoordinateSubset
20
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
21
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
22
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import AttitudeQuaternion
23
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import AngularVelocity
24
+ from ostk.astrodynamics.trajectory.orbit.model import Tabulated as TabulatedOrbit
25
+ from ostk.astrodynamics.flight import Profile
26
+ from ostk.astrodynamics.flight.profile.model import Tabulated as TabulatedProfile
27
+
28
+
29
+ DEFAULT_REFERENCE_FRAME: Frame = Frame.GCRF()
30
+ DEFAULT_TIME_COLUMN: str = "Timestamp"
31
+ DEFAULT_POSITION_COLUMNS_FORMAT: list[str] = [
32
+ "r_{frame}_x",
33
+ "r_{frame}_y",
34
+ "r_{frame}_z",
35
+ ]
36
+ DEFAULT_VELOCITY_COLUMNS_FORMAT: list[str] = [
37
+ "v_{frame}_x",
38
+ "v_{frame}_y",
39
+ "v_{frame}_z",
40
+ ]
41
+ DEFAULT_ATTITUDE_COLUMNS_FORMAT: list[str] = [
42
+ "q_B_{frame}_x",
43
+ "q_B_{frame}_y",
44
+ "q_B_{frame}_z",
45
+ "q_B_{frame}_s",
46
+ ]
47
+ DEFAULT_ANGULAR_VELOCITY_COLUMNS_FORMAT: list[str] = [
48
+ "w_B_{frame}_in_B_x",
49
+ "w_B_{frame}_in_B_y",
50
+ "w_B_{frame}_in_B_z",
51
+ ]
52
+ CARTESIAN_POSITION_SUBSET: CartesianPosition = CartesianPosition.default()
53
+ CARTESIAN_VELOCITY_SUBSET: CartesianVelocity = CartesianVelocity.default()
54
+ ATTITUDE_QUATERNION_SUBSET: AttitudeQuaternion = AttitudeQuaternion.default()
55
+ ANGULAR_VELOCITY_SUBSET: AngularVelocity = AngularVelocity.default()
56
+
57
+ DEFAULT_INTERPOLATION_TYPE: Interpolator.Type = Interpolator.Type.BarycentricRational
58
+
59
+
60
+ def generate_column_names(
61
+ reference_frame: Frame,
62
+ time_column: str | None = None,
63
+ position_columns: list[str] | None = None,
64
+ velocity_columns: list[str] | None = None,
65
+ attitude_columns: list[str] | None = None,
66
+ angular_velocity_columns: list[str] | None = None,
67
+ ) -> tuple[str, list[str], list[str], list[str], list[str]]:
68
+ """
69
+ Generate column names for a DataFrame containing orbit data.
70
+
71
+ Args:
72
+ reference_frame (Frame): Reference frame.
73
+ time_column (str | None): Name of the column containing the time data in [UTC].
74
+ position_columns (list[str] | None): List of column names containing the position data in [m].
75
+ velocity_columns (list[str] | None): List of column names containing the velocity data in [m/s].
76
+ attitude_columns (list[str] | None): List of column names containing the attitude data in [x, y, z, s] form.
77
+ angular_velocity_columns (list[str] | None): List of column names containing the angular velocity data in [rad/s].
78
+
79
+ Returns:
80
+ tuple[str, list[str], list[str], list[str], list[str]: Tuple containing the column names.
81
+ """
82
+ reference_frame_name: str = reference_frame.get_name()
83
+
84
+ return (
85
+ time_column or DEFAULT_TIME_COLUMN,
86
+ position_columns
87
+ or [
88
+ column.format(frame=reference_frame_name)
89
+ for column in DEFAULT_POSITION_COLUMNS_FORMAT
90
+ ],
91
+ velocity_columns
92
+ or [
93
+ column.format(frame=reference_frame_name)
94
+ for column in DEFAULT_VELOCITY_COLUMNS_FORMAT
95
+ ],
96
+ attitude_columns
97
+ or [
98
+ column.format(frame=reference_frame_name)
99
+ for column in DEFAULT_ATTITUDE_COLUMNS_FORMAT
100
+ ],
101
+ angular_velocity_columns
102
+ or [
103
+ column.format(frame=reference_frame_name)
104
+ for column in DEFAULT_ANGULAR_VELOCITY_COLUMNS_FORMAT
105
+ ],
106
+ )
107
+
108
+
109
+ def generate_states_from_dataframe(
110
+ dataframe: pd.DataFrame,
111
+ reference_frame: Frame | None = None,
112
+ time_column: str | None = None,
113
+ position_columns: list[str] | None = None,
114
+ velocity_columns: list[str] | None = None,
115
+ attitude_columns: list[str] | None = None,
116
+ angular_velocity_columns: list[str] | None = None,
117
+ output_frame: Frame | None = None,
118
+ ) -> list[State]:
119
+ """
120
+ Generate a list of OSTk States from a Pandas DataFrame.
121
+
122
+ Args:
123
+ dataframe (pd.DataFrame): Pandas DataFrame containing the orbit data.
124
+ reference_frame (Frame | None, optional): Reference frame of the states in the dataframe.
125
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
126
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
127
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
128
+ attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
129
+ angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
130
+ output_frame (Frame | None, optional): Output frame for the states.
131
+
132
+ Returns:
133
+ list[State]: List of OSTk States.
134
+ """
135
+
136
+ reference_frame = reference_frame or DEFAULT_REFERENCE_FRAME
137
+
138
+ output_frame = output_frame or reference_frame
139
+
140
+ (
141
+ time_column,
142
+ position_columns,
143
+ velocity_columns,
144
+ attitude_columns,
145
+ angular_velocity_columns,
146
+ ) = generate_column_names(
147
+ reference_frame=reference_frame,
148
+ time_column=time_column,
149
+ position_columns=position_columns,
150
+ velocity_columns=velocity_columns,
151
+ attitude_columns=attitude_columns,
152
+ angular_velocity_columns=angular_velocity_columns,
153
+ )
154
+
155
+ if not set(position_columns).issubset(dataframe.columns):
156
+ raise ValueError("Position columns are not present in the DataFrame.")
157
+
158
+ if not set(velocity_columns).issubset(dataframe.columns):
159
+ raise ValueError("Velocity columns are not present in the DataFrame.")
160
+
161
+ # check if the dataframe contains a timestamp column
162
+ has_timestamp: bool = time_column in dataframe.columns
163
+
164
+ # check if the dataframe contains attitude
165
+ has_attitude: bool = set(attitude_columns).issubset(dataframe.columns)
166
+
167
+ # check if the dataframe contains angular velocity
168
+ has_angular_velocity: bool = set(angular_velocity_columns).issubset(dataframe.columns)
169
+
170
+ coordinate_subsets = [
171
+ CARTESIAN_POSITION_SUBSET,
172
+ CARTESIAN_VELOCITY_SUBSET,
173
+ ]
174
+
175
+ if has_attitude:
176
+ coordinate_subsets.extend([ATTITUDE_QUATERNION_SUBSET])
177
+
178
+ if has_angular_velocity:
179
+ coordinate_subsets.extend([ANGULAR_VELOCITY_SUBSET])
180
+
181
+ state_builder: StateBuilder = StateBuilder(
182
+ frame=reference_frame,
183
+ coordinate_subsets=coordinate_subsets,
184
+ )
185
+
186
+ states: list[State] = []
187
+ for index, row in dataframe.to_dict("index").items():
188
+
189
+ coordinates: list[float] = [
190
+ *[row[column] for column in position_columns],
191
+ *[row[column] for column in velocity_columns],
192
+ ]
193
+
194
+ if has_attitude:
195
+ coordinates.extend([row[column] for column in attitude_columns])
196
+
197
+ if has_angular_velocity:
198
+ coordinates.extend([row[column] for column in angular_velocity_columns])
199
+
200
+ states.append(
201
+ state_builder.build(
202
+ instant=coerce_to_instant(
203
+ row[time_column] if has_timestamp else index,
204
+ ),
205
+ coordinates=coordinates,
206
+ ).in_frame(output_frame)
207
+ )
208
+
209
+ return states
210
+
211
+
212
+ def generate_dataframe_from_states(
213
+ states: list[State],
214
+ reference_frame: Frame | None = None,
215
+ time_column: str | None = None,
216
+ position_columns: list[str] | None = None,
217
+ velocity_columns: list[str] | None = None,
218
+ attitude_columns: list[str] | None = None,
219
+ angular_velocity_columns: list[str] | None = None,
220
+ set_time_index: bool = True,
221
+ ) -> pd.DataFrame:
222
+ """
223
+ Generate a Pandas DataFrame from a list of OSTk States.
224
+
225
+ Args:
226
+ states (list[State]): List of OSTk States.
227
+ reference_frame (Frame | None, optional): The desired reference frame.
228
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
229
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
230
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
231
+ attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
232
+ angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
233
+ set_time_index (bool, optional): Whether to set the time column as the index. Defaults to True.
234
+
235
+ Returns:
236
+ pd.DataFrame: Pandas DataFrame containing the orbit data.
237
+ """
238
+ has_attitude: bool = states[0].has_subset(ATTITUDE_QUATERNION_SUBSET)
239
+ has_angular_velocity: bool = states[0].has_subset(ANGULAR_VELOCITY_SUBSET)
240
+
241
+ reference_frame = reference_frame or DEFAULT_REFERENCE_FRAME
242
+
243
+ (
244
+ time_column,
245
+ position_columns,
246
+ velocity_columns,
247
+ attitude_columns,
248
+ angular_velocity_columns,
249
+ ) = generate_column_names(
250
+ reference_frame=reference_frame,
251
+ time_column=time_column,
252
+ position_columns=position_columns,
253
+ velocity_columns=velocity_columns,
254
+ attitude_columns=attitude_columns,
255
+ angular_velocity_columns=angular_velocity_columns,
256
+ )
257
+
258
+ if len(position_columns) != 3:
259
+ raise ValueError("Position columns must be a list of length 3.")
260
+
261
+ if len(velocity_columns) != 3:
262
+ raise ValueError("Velocity columns must be a list of length 3.")
263
+
264
+ if len(attitude_columns) != 4:
265
+ raise ValueError("Attitude columns must be a list of length 4.")
266
+
267
+ if len(angular_velocity_columns) != 3:
268
+ raise ValueError("Angular velocity columns must be a list of length 3.")
269
+
270
+ data: list[dict] = []
271
+
272
+ for state in states:
273
+ state = state.in_frame(reference_frame)
274
+
275
+ datum: dict = {
276
+ time_column: coerce_to_datetime(state.get_instant()),
277
+ **_get_entry_from_state(state, position_columns, CARTESIAN_POSITION_SUBSET),
278
+ **_get_entry_from_state(state, velocity_columns, CARTESIAN_VELOCITY_SUBSET),
279
+ }
280
+
281
+ if has_attitude:
282
+ datum.update(
283
+ _get_entry_from_state(state, attitude_columns, ATTITUDE_QUATERNION_SUBSET)
284
+ )
285
+ if has_angular_velocity:
286
+ datum.update(
287
+ _get_entry_from_state(
288
+ state, angular_velocity_columns, ANGULAR_VELOCITY_SUBSET
289
+ )
290
+ )
291
+
292
+ data.append(datum)
293
+
294
+ dataframe: pd.DataFrame = pd.DataFrame(data)
295
+ if set_time_index:
296
+ dataframe.set_index(time_column, inplace=True)
297
+
298
+ return dataframe
299
+
300
+
301
+ def generate_orbit_from_dataframe(
302
+ dataframe: pd.DataFrame,
303
+ central_body: Celestial = Earth.default(),
304
+ reference_frame: Frame | None = None,
305
+ time_column: str | None = None,
306
+ position_columns: list[str] | None = None,
307
+ velocity_columns: list[str] | None = None,
308
+ initial_revolution_number: int = 1,
309
+ interpolation_type: Interpolator.Type | None = None,
310
+ output_frame: Frame | None = None,
311
+ ) -> Orbit:
312
+ """
313
+ Generate an OSTk Orbit from a Pandas DataFrame.
314
+
315
+ Args:
316
+ dataframe (pd.DataFrame): Pandas DataFrame containing the orbit data.
317
+ central_body (Celestial, optional): Celestial object around which the Orbit is defined. Defaults to Earth.
318
+ reference_frame (Frame | None, optional): Reference frame.
319
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
320
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
321
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
322
+ initial_revolution_number (int, optional): Initial revolution number. Defaults to 1.
323
+ interpolation_type (Interpolator.Type | None, optional): Interpolation type.
324
+ output_frame (Frame | None, optional): Output frame for the states.
325
+
326
+ Returns:
327
+ Orbit: OSTk Orbit.
328
+ """
329
+
330
+ return Orbit(
331
+ model=TabulatedOrbit(
332
+ states=generate_states_from_dataframe(
333
+ dataframe=dataframe,
334
+ time_column=time_column,
335
+ position_columns=position_columns,
336
+ velocity_columns=velocity_columns,
337
+ reference_frame=reference_frame,
338
+ output_frame=output_frame,
339
+ ),
340
+ initial_revolution_number=initial_revolution_number,
341
+ interpolation_type=interpolation_type or DEFAULT_INTERPOLATION_TYPE,
342
+ ),
343
+ celestial_object=central_body,
344
+ )
345
+
346
+
347
+ def generate_dataframe_from_orbit(
348
+ orbit: Orbit,
349
+ instants: list[Instant],
350
+ reference_frame: Frame | None = None,
351
+ time_column: str | None = None,
352
+ position_columns: list[str] | None = None,
353
+ velocity_columns: list[str] | None = None,
354
+ set_time_index: bool = True,
355
+ ) -> pd.DataFrame:
356
+ """
357
+ Generate a Pandas DataFrame from an OSTk Orbit.
358
+
359
+ Args:
360
+ orbit (Orbit): OSTk Orbit.
361
+ instants (list[Instant]): List of instants at which the Orbit is to be evaluated.
362
+ reference_frame (Frame | None, optional): Reference frame.
363
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
364
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
365
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
366
+ set_time_index (bool, optional): Whether to set the time column as the DataFrame index. Defaults to True.
367
+
368
+ Returns:
369
+ pd.DataFrame: Pandas DataFrame containing the Orbit data.
370
+ """
371
+
372
+ states: list[State] = orbit.get_states_at(instants)
373
+
374
+ return generate_dataframe_from_states(
375
+ states,
376
+ reference_frame=reference_frame,
377
+ time_column=time_column,
378
+ position_columns=position_columns,
379
+ velocity_columns=velocity_columns,
380
+ set_time_index=set_time_index,
381
+ )
382
+
383
+
384
+ def generate_profile_from_dataframe(
385
+ dataframe: pd.DataFrame,
386
+ reference_frame: Frame | None = None,
387
+ time_column: str | None = None,
388
+ position_columns: list[str] | None = None,
389
+ velocity_columns: list[str] | None = None,
390
+ attitude_columns: list[str] | None = None,
391
+ angular_velocity_columns: list[str] | None = None,
392
+ output_frame: Frame | None = None,
393
+ ) -> Profile:
394
+ """
395
+ Generate an OSTk Profile from a Pandas DataFrame.
396
+
397
+ Args:
398
+ dataframe (pd.DataFrame): Pandas DataFrame containing the Profile data.
399
+ reference_frame (Frame | None, optional): Reference frame.
400
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
401
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
402
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
403
+ attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
404
+ angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
405
+ output_frame (Frame | None, optional): Output frame for the states.
406
+
407
+ Returns:
408
+ Profile: OSTk Profile.
409
+ """
410
+
411
+ return Profile(
412
+ model=TabulatedProfile(
413
+ states=generate_states_from_dataframe(
414
+ dataframe=dataframe,
415
+ reference_frame=reference_frame,
416
+ time_column=time_column,
417
+ position_columns=position_columns,
418
+ velocity_columns=velocity_columns,
419
+ attitude_columns=attitude_columns,
420
+ angular_velocity_columns=angular_velocity_columns,
421
+ output_frame=output_frame,
422
+ ),
423
+ ),
424
+ )
425
+
426
+
427
+ def generate_dataframe_from_profile(
428
+ profile: Profile,
429
+ instants: list[Instant],
430
+ reference_frame: Frame | None = None,
431
+ time_column: str | None = None,
432
+ position_columns: list[str] | None = None,
433
+ velocity_columns: list[str] | None = None,
434
+ attitude_columns: list[str] | None = None,
435
+ angular_velocity_columns: list[str] | None = None,
436
+ set_time_index: bool = True,
437
+ ) -> pd.DataFrame:
438
+ """
439
+ Generate a Pandas DataFrame from an OSTk Profile.
440
+
441
+ Args:
442
+ profile (Profile): OSTk Profile.
443
+ instants (list[Instant]): List of instants at which the Profile is to be evaluated.
444
+ reference_frame (Frame | None, optional): Reference frame.
445
+ time_column (str | None, optional): Name of the column containing the time data in [UTC].
446
+ position_columns (list[str] | None, optional): List of column names containing the position data in [m].
447
+ velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
448
+ attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
449
+ angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
450
+ set_time_index (bool, optional): Whether to set the time column as the DataFrame index. Defaults to True.
451
+
452
+ Returns:
453
+ pd.DataFrame: Pandas DataFrame containing the Profile data.
454
+ """
455
+
456
+ states: list[State] = profile.get_states_at(instants)
457
+
458
+ return generate_dataframe_from_states(
459
+ states,
460
+ reference_frame=reference_frame,
461
+ time_column=time_column,
462
+ position_columns=position_columns,
463
+ velocity_columns=velocity_columns,
464
+ attitude_columns=attitude_columns,
465
+ angular_velocity_columns=angular_velocity_columns,
466
+ set_time_index=set_time_index,
467
+ )
468
+
469
+
470
+ def _get_entry_from_state(
471
+ state: State, columns: list[str], subset: CoordinateSubset
472
+ ) -> dict:
473
+ coordinates: np.ndarray = state.extract_coordinate(subset)
474
+ return {
475
+ column.format(frame=state.get_frame().get_name()): coordinates[idx]
476
+ for idx, column in enumerate(columns)
477
+ }
@@ -0,0 +1,220 @@
1
+ # Apache License 2.0
2
+
3
+ from typing import Any
4
+
5
+ import pandas as pd
6
+
7
+ import plotly.graph_objs as go
8
+
9
+ from ostk.mathematics.object import RealInterval
10
+
11
+ from ostk.physics.coordinate import Position
12
+ from ostk.physics.coordinate import Frame
13
+ from ostk.physics.coordinate.spherical import LLA
14
+ from ostk.physics.environment.object import Celestial
15
+ from ostk.physics.time import Duration
16
+ from ostk.physics.time import Instant
17
+ from ostk.physics.unit import Length
18
+
19
+ from ostk.astrodynamics import Access
20
+ from ostk.astrodynamics import Trajectory
21
+
22
+
23
+ DEFAULT_COLOR: str = "rgba(255, 0, 0, 0.5)"
24
+
25
+ DEFAULT_LAYOUT_2D: go.Layout = go.Layout(
26
+ title=None,
27
+ showlegend=False,
28
+ height=1000,
29
+ geo=go.layout.Geo(
30
+ showland=True,
31
+ landcolor="rgb(243, 243, 243)",
32
+ countrycolor="rgb(204, 204, 204)",
33
+ lonaxis=dict(
34
+ showgrid=True,
35
+ gridcolor="rgb(102, 102, 102)",
36
+ gridwidth=0.1,
37
+ ),
38
+ lataxis=dict(
39
+ showgrid=True,
40
+ gridcolor="rgb(102, 102, 102)",
41
+ gridwidth=0.1,
42
+ ),
43
+ ),
44
+ )
45
+
46
+ DEFAULT_LAYOUT_3D: go.Layout = go.Layout(
47
+ title=None,
48
+ showlegend=False,
49
+ height=1000,
50
+ geo=go.layout.Geo(
51
+ showland=True,
52
+ showlakes=True,
53
+ showcountries=False,
54
+ showocean=True,
55
+ countrywidth=0.0,
56
+ landcolor="rgb(100, 100, 100)",
57
+ lakecolor="rgb(240, 240, 240)",
58
+ oceancolor="rgb(240, 240, 240)",
59
+ projection=dict(
60
+ type="orthographic",
61
+ rotation=dict(
62
+ lon=0,
63
+ lat=0,
64
+ roll=0,
65
+ ),
66
+ ),
67
+ lonaxis=dict(
68
+ showgrid=True,
69
+ gridcolor="rgb(102, 102, 102)",
70
+ gridwidth=0.5,
71
+ ),
72
+ lataxis=dict(
73
+ showgrid=True,
74
+ gridcolor="rgb(102, 102, 102)",
75
+ gridwidth=0.5,
76
+ ),
77
+ ),
78
+ )
79
+
80
+
81
+ def convert_to_ground_track_plotting_data(
82
+ longitude: list, latitude: list, color: str = DEFAULT_COLOR
83
+ ) -> go.Scattergeo:
84
+ return go.Scattergeo(
85
+ lon=longitude,
86
+ lat=latitude,
87
+ mode="lines",
88
+ line=dict(
89
+ width=1,
90
+ color=color,
91
+ ),
92
+ )
93
+
94
+
95
+ def create_plotly_figure(data: Any, layout: go.Layout) -> go.Figure:
96
+ return go.Figure(
97
+ data=data,
98
+ layout=layout,
99
+ )
100
+
101
+
102
+ def create_2d_map(data: Any) -> go.Figure:
103
+ return create_plotly_figure(data, DEFAULT_LAYOUT_2D)
104
+
105
+
106
+ def create_3d_globe(data: Any) -> go.Figure:
107
+ return create_plotly_figure(data, DEFAULT_LAYOUT_3D)
108
+
109
+
110
+ class AccessesPlot:
111
+ """
112
+ Accesses plot. Display a 2D world map, with 1 ground station and
113
+ multiple satellites, highlighting the accesses.
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ earth: Celestial,
119
+ interval: RealInterval,
120
+ trajectory_step: Duration,
121
+ access_step: Duration,
122
+ ground_station_lla: LLA,
123
+ color: str,
124
+ ):
125
+ self._earth = earth
126
+ self._trajectory_grid: list[Instant] = interval.generate_grid(trajectory_step)
127
+ self._access_step: Instant = access_step
128
+ self._ned_frame: Frame = self._earth.get_frame_at(
129
+ ground_station_lla, self._earth.FrameType.NED
130
+ )
131
+ self._ground_station_position_ned: Position = Position(
132
+ [0.0, 0.0, 0.0],
133
+ Length.Unit.Meter,
134
+ self._ned_frame,
135
+ )
136
+ self._data = []
137
+ self._data.append(
138
+ dict(
139
+ type="scattergeo",
140
+ lon=[float(ground_station_lla.get_longitude().in_degrees())],
141
+ lat=[float(ground_station_lla.get_latitude().in_degrees())],
142
+ mode="markers",
143
+ marker=dict(
144
+ size=10,
145
+ color=color,
146
+ ),
147
+ )
148
+ )
149
+
150
+ def _generate_and_append_data(
151
+ self, data: list[list[float]], trajectory: Trajectory, grid: list[Instant]
152
+ ) -> None:
153
+ for state in trajectory.get_states_at(grid):
154
+ lla: LLA = LLA.cartesian(
155
+ state.get_position()
156
+ .in_frame(Frame.ITRF(), state.get_instant())
157
+ .get_coordinates(),
158
+ self._earth.get_equatorial_radius(),
159
+ self._earth.get_flattening(),
160
+ )
161
+ data.append(
162
+ [
163
+ float(lla.get_longitude().in_degrees()),
164
+ float(lla.get_latitude().in_degrees()),
165
+ ]
166
+ )
167
+
168
+ def _append_line(
169
+ self, df: pd.DataFrame, width: int, rgb: list[int], alpha: float
170
+ ) -> None:
171
+ self._data.append(
172
+ dict(
173
+ type="scattergeo",
174
+ lon=df["Longitude"],
175
+ lat=df["Latitude"],
176
+ mode="lines",
177
+ line=dict(
178
+ width=width,
179
+ color=f"rgba({str(rgb[0])},{str(rgb[1])},{str(rgb[2])},{str(alpha)})",
180
+ ),
181
+ )
182
+ )
183
+
184
+ def add_satellite(
185
+ self, trajectory: Trajectory, accesses: list[Access], rgb: list[int]
186
+ ) -> None:
187
+ """
188
+ Add a satellite trajectory to the plot, including a highligh ot the interference
189
+ accesses.
190
+ """
191
+
192
+ # Satellite trajectory
193
+ satellite_trajectory_data: list[list[float]] = []
194
+ self._generate_and_append_data(
195
+ satellite_trajectory_data, trajectory, self._trajectory_grid
196
+ )
197
+ satellite_trajectory_df: pd.Dataframe = pd.DataFrame(
198
+ satellite_trajectory_data, columns=["Longitude", "Latitude"]
199
+ )
200
+ self._append_line(satellite_trajectory_df, 1, rgb, 0.1)
201
+
202
+ # Satellite accesses
203
+ for access in accesses:
204
+ satellite_access_data: list[list[float]] = []
205
+ self._generate_and_append_data(
206
+ satellite_access_data,
207
+ trajectory,
208
+ access.get_interval().generate_grid(self._access_step),
209
+ )
210
+ satellite_access_df: pd.Dataframe = pd.DataFrame(
211
+ satellite_access_data, columns=["Longitude", "Latitude"]
212
+ )
213
+ self._append_line(satellite_access_df, 2, rgb, 1.0)
214
+
215
+ def show(self):
216
+ """
217
+ Display the figure.
218
+ """
219
+ figure = create_2d_map(data=self._data)
220
+ figure.show()
@@ -0,0 +1 @@
1
+ # Apache License 2.0