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