open-space-toolkit-astrodynamics 15.4.0__py39-none-manylinux2014_aarch64.whl → 15.6.0__py39-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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: open-space-toolkit-astrodynamics
3
- Version: 15.4.0
3
+ Version: 15.6.0
4
4
  Summary: Orbit, attitude, access.
5
5
  Author: Open Space Collective
6
6
  Author-email: contact@open-space-collective.org
@@ -1,20 +1,20 @@
1
1
  ostk/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
2
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-39-aarch64-linux-gnu.so,sha256=QLYI04ZDx6muGWFWRmR9KURus-nLKTRBP8gSjIG7tCA,2476184
2
+ ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-39-aarch64-linux-gnu.so,sha256=vcPM3aoPtIMghAMoU0ReluVP2nZQzEb9pcT5f-Hz6wk,2476184
3
3
  ostk/astrodynamics/__init__.py,sha256=3gWyqFIbhAfcdeMhmfBPQPlPQTmaOzm-6flkJe745Zk,251
4
- ostk/astrodynamics/__init__.pyi,sha256=V2lKgL6zq7SsKFWOOG7qu9HHp7XoUHf76Hj7hzbNNDE,31428
4
+ ostk/astrodynamics/__init__.pyi,sha256=FGBED8GxIZ9vDefdPyUL7mMkshLqm_l0GFEYrSq3fLA,31428
5
5
  ostk/astrodynamics/access.pyi,sha256=t2CF0TU6_6ow_rkV_I4rVKap7ZIdC4jYKL3WkTDHRXg,25157
6
6
  ostk/astrodynamics/converters.py,sha256=luPh30qMp9bzEkN7hUccmxlLf7zRp_AzqmBe8IUjPhU,3314
7
7
  ostk/astrodynamics/converters.pyi,sha256=HrZFyizkc6Hv_K38ZKZ80fX_bAxd6keA_NFWNQygvbs,1745
8
- ostk/astrodynamics/dataframe.py,sha256=9fXRk7sJl_OrBTCjZC_TFx6JMPE7IDGqv2JgEmGCdgM,18775
8
+ ostk/astrodynamics/dataframe.py,sha256=v-36rBHKdf7kQw1jdvJWrvkR-ZjW5DKRchtTEJWhdxk,25012
9
9
  ostk/astrodynamics/display.py,sha256=LZESZgx2wlrFO4cwAGMb3VPJfdtcjNgCgKFrqot0NYU,6339
10
10
  ostk/astrodynamics/dynamics.pyi,sha256=gZ95KoGex4SB-1z6yMrngkZN1Ir9X6bEmrZgdLxq1ZE,13270
11
11
  ostk/astrodynamics/estimator.pyi,sha256=MnahWzp8aACxrNKWlYRsgQr5zpBxogNr-yPm7hJob5k,14000
12
12
  ostk/astrodynamics/event_condition.pyi,sha256=LXvZCbKnX6kzES7iMG51lJRnIA64YFsU5mUY2RW9fkM,24798
13
13
  ostk/astrodynamics/guidance_law.pyi,sha256=rVmbpV2Y5FsIXejaInxINS67nVHmTIxVEkhaDIn17SA,12171
14
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.15,sha256=OqrxuSJVXeonFcYmPoY9CpE6oRElOqz-Sgd0abbl2R8,3794008
14
+ ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.15,sha256=ri_qOhWpo3qmfclGJC0RgjVaxNRBtCo2hAHiXPBbujA,3798552
15
15
  ostk/astrodynamics/solver.pyi,sha256=sPqyYPXBfFGC24dzzYyFyt01VfMAlWQ5_gh_RpeaBFk,17734
16
16
  ostk/astrodynamics/utilities.py,sha256=y8mr3M46J5z-GhS1oIEnuEJA6otwcyJ9YDhvn_5JxHM,6976
17
- ostk/astrodynamics/viewer.py,sha256=SpcvBqXx3CAZpk7UGOPT1ilxywmAmNpgqpuRY11Hwx0,16376
17
+ ostk/astrodynamics/viewer.py,sha256=-Q4migLFOB9dNzs6TRPIXHNewyV5udsDSpcSma0kv8A,16376
18
18
  ostk/astrodynamics/conjunction/__init__.pyi,sha256=HFvWl8bCmrq3cBkUh5X5RGIh8webmVGxaZdpsz3WN-E,79
19
19
  ostk/astrodynamics/conjunction/message/__init__.pyi,sha256=5H__sg_QUx7ybf9jtVWvXzrUHeK3ECotfhddAdHjJUc,75
20
20
  ostk/astrodynamics/conjunction/message/ccsds.pyi,sha256=1Peto19hRqlD7KHf1cyLP3CT4OAKzwtemqvO6_4FZ0g,28162
@@ -32,7 +32,7 @@ ostk/astrodynamics/test/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3I
32
32
  ostk/astrodynamics/test/conftest.py,sha256=stmQOt7UXjBlXKJzNN6RkS2miv1xXSOX-YNpFhaHTqI,2771
33
33
  ostk/astrodynamics/test/test_access.py,sha256=MCBsUPtuVm7NgHZR0z0DpWnPZ_qBu4aRhLI2PnRNUYs,3940
34
34
  ostk/astrodynamics/test/test_converters.py,sha256=mFpDD0YM8o356lj91GGIwYdMk2ut6xZFV3uYcgZepMY,8744
35
- ostk/astrodynamics/test/test_dataframe.py,sha256=IJMOODzTVYtiPxekmltH6lOMArPXbHwSEAl-jg0Ab2g,28414
35
+ ostk/astrodynamics/test/test_dataframe.py,sha256=NddmwnV8R1QgTD_iLXn_h0xK2kP5Qk22Oh93hF6KjT0,38300
36
36
  ostk/astrodynamics/test/test_display.py,sha256=6giw_i08YMOSRgqNpdVWtzDu6fFalEVKz_Uisu3N_X8,3870
37
37
  ostk/astrodynamics/test/test_event_condition.py,sha256=fQ-rVYpQsa7xacxRMBOQDyn61CuAGX1QBOGZxGWR0_s,2204
38
38
  ostk/astrodynamics/test/test_import.py,sha256=py_hALBR0IYuUzv9dfgQZzrrLHJIpnyKvt3Oi1XBqCg,1251
@@ -111,7 +111,7 @@ ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py,sha256=ryMfYH
111
111
  ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
112
112
  ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py,sha256=jkyRr5NJQMaSpI3ey7cKcH0E-BlalaUlf_mZPYTSow8,11482
113
113
  ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py,sha256=ho5FBfB3IvIjfQl7EYoIN1KBuH9lu76IiNMuweq8cqg,2844
114
- ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py,sha256=uSvwLSZ332IreBkHD6PmAF2TFE6g_-o4tRLu9mR-IDg,1580
114
+ ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py,sha256=UnJM4sYsE6-s6tETDI3aR846Zd3u2j75RfrGlpoKLyE,1691
115
115
  ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py,sha256=0uh87wFRRF7UNh4cSI6WWAcAEF13TY4_VRBJ_gifnyY,9415
116
116
  ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py,sha256=xjyGREKpxDuT8gjtBPkV4osfW-6UyWv9hgvBXZVb_wk,625
117
117
  ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py,sha256=UEu9ApzQLmT87eeISw6_gcHTlX-4b2scIvHz-uE1p_c,393
@@ -126,10 +126,10 @@ ostk/astrodynamics/trajectory/orbit/model/__init__.pyi,sha256=DYgjafm4rMV9Cqzg6d
126
126
  ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi,sha256=t1jbWsK8JgXkx-l_WSg96xfEG9fsddZ_CjqLpC3GBVY,5874
127
127
  ostk/astrodynamics/trajectory/orbit/model/kepler.pyi,sha256=KZH_m7mqwXNKFTu4zLRMl_pG7ADQ9Zo6j3FYuVNnLwQ,26955
128
128
  ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi,sha256=OhFzoPPQHlYy7m3LiZ8TXF89M4uBTfNk6tGsBEp0sjI,14235
129
- ostk/astrodynamics/trajectory/state/__init__.pyi,sha256=_NrkgYTo9YQ3vv8y76PHJ8_1DFoYRvkrGGIxtDoXlcc,17348
129
+ ostk/astrodynamics/trajectory/state/__init__.pyi,sha256=bq__Fii35czVrTeNxc9eQVjXdqwbbQxUdNQWK3vLrMo,17649
130
130
  ostk/astrodynamics/trajectory/state/coordinate_subset.pyi,sha256=kYMfMwEjCqO1NepMYFT4QS6kIPBkVL6sGCLeLbogcMw,10176
131
- open_space_toolkit_astrodynamics-15.4.0.dist-info/METADATA,sha256=IkSkTep__oIwwyZtQvdDdS1KkPOOKRZ0qEWlPMmyz2Q,1913
132
- open_space_toolkit_astrodynamics-15.4.0.dist-info/WHEEL,sha256=HKTDQRaBjeful1LuAej8fX2VXGhDWeCDzK_M-yn2ypk,110
133
- open_space_toolkit_astrodynamics-15.4.0.dist-info/top_level.txt,sha256=zOR18699uDYnafgarhL8WU_LmTZY_5NVqutv-flp_x4,5
134
- open_space_toolkit_astrodynamics-15.4.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
135
- open_space_toolkit_astrodynamics-15.4.0.dist-info/RECORD,,
131
+ open_space_toolkit_astrodynamics-15.6.0.dist-info/METADATA,sha256=5Nm6f5thffoDOhAlypysgUY8KnkhTGONPBbGR8LSJOI,1913
132
+ open_space_toolkit_astrodynamics-15.6.0.dist-info/WHEEL,sha256=HKTDQRaBjeful1LuAej8fX2VXGhDWeCDzK_M-yn2ypk,110
133
+ open_space_toolkit_astrodynamics-15.6.0.dist-info/top_level.txt,sha256=zOR18699uDYnafgarhL8WU_LmTZY_5NVqutv-flp_x4,5
134
+ open_space_toolkit_astrodynamics-15.6.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
135
+ open_space_toolkit_astrodynamics-15.6.0.dist-info/RECORD,,
@@ -5,8 +5,8 @@ from ostk.astrodynamics.trajectory import State as PyState
5
5
  from ostk import core as OpenSpaceToolkitCorePy
6
6
  from ostk.core import container
7
7
  from ostk.core import filesystem
8
- from ostk.core import type
9
8
  import ostk.core.type
9
+ from ostk.core import type
10
10
  from ostk import io as OpenSpaceToolkitIOPy
11
11
  from ostk.io import URL
12
12
  from ostk.io import ip
@@ -26,8 +26,8 @@ from ostk.physics import environment
26
26
  import ostk.physics.environment.object
27
27
  from ostk.physics import time
28
28
  import ostk.physics.time
29
- import ostk.physics.unit
30
29
  from ostk.physics import unit
30
+ import ostk.physics.unit
31
31
  import typing
32
32
  from . import access
33
33
  from . import conjunction
@@ -51,10 +51,17 @@ DEFAULT_ANGULAR_VELOCITY_COLUMNS_FORMAT: list[str] = [
51
51
  "w_B_{frame}_in_B_y",
52
52
  "w_B_{frame}_in_B_z",
53
53
  ]
54
+ DEFAULT_MASS_COLUMN: str = "mass"
55
+ DEFAULT_DRAG_COEFFICIENT_COLUMN: str = "drag_coefficient"
56
+ DEFAULT_SURFACE_AREA_COLUMN: str = "surface_area"
57
+
54
58
  CARTESIAN_POSITION_SUBSET: CartesianPosition = CartesianPosition.default()
55
59
  CARTESIAN_VELOCITY_SUBSET: CartesianVelocity = CartesianVelocity.default()
56
60
  ATTITUDE_QUATERNION_SUBSET: AttitudeQuaternion = AttitudeQuaternion.default()
57
61
  ANGULAR_VELOCITY_SUBSET: AngularVelocity = AngularVelocity.default()
62
+ MASS_SUBSET: CoordinateSubset = CoordinateSubset.mass()
63
+ DRAG_COEFFICIENT_SUBSET: CoordinateSubset = CoordinateSubset.drag_coefficient()
64
+ SURFACE_AREA_SUBSET: CoordinateSubset = CoordinateSubset.surface_area()
58
65
 
59
66
  DEFAULT_INTERPOLATION_TYPE: Interpolator.Type = Interpolator.Type.BarycentricRational
60
67
 
@@ -66,7 +73,10 @@ def generate_column_names(
66
73
  velocity_columns: list[str] | None = None,
67
74
  attitude_columns: list[str] | None = None,
68
75
  angular_velocity_columns: list[str] | None = None,
69
- ) -> tuple[str, list[str], list[str], list[str], list[str]]:
76
+ mass_column: str | None = None,
77
+ drag_coefficient_column: str | None = None,
78
+ surface_area_column: str | None = None,
79
+ ) -> tuple[str, list[str], list[str], list[str], list[str], str, str, str]:
70
80
  """
71
81
  Generate column names for a DataFrame containing orbit data.
72
82
 
@@ -77,6 +87,9 @@ def generate_column_names(
77
87
  velocity_columns (list[str] | None): List of column names containing the velocity data in [m/s].
78
88
  attitude_columns (list[str] | None): List of column names containing the attitude data in [x, y, z, s] form.
79
89
  angular_velocity_columns (list[str] | None): List of column names containing the angular velocity data in [rad/s].
90
+ mass_column (str | None): Name of the column containing the mass data in [kg]
91
+ drag_coefficient_column (str | None): Name of the column containing the drag coefficient data.
92
+ surface_area_column (str | None): Name of the column containing the surface area data in [m^2].
80
93
 
81
94
  Returns:
82
95
  tuple[str, list[str], list[str], list[str], list[str]: Tuple containing the column names.
@@ -105,6 +118,9 @@ def generate_column_names(
105
118
  column.format(frame=reference_frame_name)
106
119
  for column in DEFAULT_ANGULAR_VELOCITY_COLUMNS_FORMAT
107
120
  ],
121
+ mass_column or DEFAULT_MASS_COLUMN,
122
+ drag_coefficient_column or DEFAULT_DRAG_COEFFICIENT_COLUMN,
123
+ surface_area_column or DEFAULT_SURFACE_AREA_COLUMN,
108
124
  )
109
125
 
110
126
 
@@ -116,6 +132,9 @@ def generate_states_from_dataframe(
116
132
  velocity_columns: list[str] | None = None,
117
133
  attitude_columns: list[str] | None = None,
118
134
  angular_velocity_columns: list[str] | None = None,
135
+ mass_column: str | None = None,
136
+ drag_coefficient_column: str | None = None,
137
+ surface_area_column: str | None = None,
119
138
  output_frame: Frame | None = None,
120
139
  ) -> list[State]:
121
140
  """
@@ -129,6 +148,9 @@ def generate_states_from_dataframe(
129
148
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
130
149
  attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
131
150
  angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
151
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
152
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
153
+ surface_area_column (str | None, optional): Name of the column containing the surface area data in m^2].
132
154
  output_frame (Frame | None, optional): Output frame for the states.
133
155
 
134
156
  Returns:
@@ -145,6 +167,9 @@ def generate_states_from_dataframe(
145
167
  velocity_columns,
146
168
  attitude_columns,
147
169
  angular_velocity_columns,
170
+ mass_column,
171
+ drag_coefficient_column,
172
+ surface_area_column,
148
173
  ) = generate_column_names(
149
174
  reference_frame=reference_frame,
150
175
  time_column=time_column,
@@ -152,6 +177,9 @@ def generate_states_from_dataframe(
152
177
  velocity_columns=velocity_columns,
153
178
  attitude_columns=attitude_columns,
154
179
  angular_velocity_columns=angular_velocity_columns,
180
+ mass_column=mass_column,
181
+ drag_coefficient_column=drag_coefficient_column,
182
+ surface_area_column=surface_area_column,
155
183
  )
156
184
 
157
185
  if not set(position_columns).issubset(dataframe.columns):
@@ -169,6 +197,15 @@ def generate_states_from_dataframe(
169
197
  # check if the dataframe contains angular velocity
170
198
  has_angular_velocity: bool = set(angular_velocity_columns).issubset(dataframe.columns)
171
199
 
200
+ # check if the dataframe contains mass
201
+ has_mass: bool = mass_column in dataframe.columns
202
+
203
+ # check if the dataframe contains drag coefficient
204
+ has_drag_coefficient: bool = drag_coefficient_column in dataframe.columns
205
+
206
+ # check if the dataframe contains surface area
207
+ has_surface_area: bool = surface_area_column in dataframe.columns
208
+
172
209
  coordinate_subsets = [
173
210
  CARTESIAN_POSITION_SUBSET,
174
211
  CARTESIAN_VELOCITY_SUBSET,
@@ -180,6 +217,15 @@ def generate_states_from_dataframe(
180
217
  if has_angular_velocity:
181
218
  coordinate_subsets.extend([ANGULAR_VELOCITY_SUBSET])
182
219
 
220
+ if has_mass:
221
+ coordinate_subsets.append(MASS_SUBSET)
222
+
223
+ if has_drag_coefficient:
224
+ coordinate_subsets.append(DRAG_COEFFICIENT_SUBSET)
225
+
226
+ if has_surface_area:
227
+ coordinate_subsets.append(SURFACE_AREA_SUBSET)
228
+
183
229
  state_builder: StateBuilder = StateBuilder(
184
230
  frame=reference_frame,
185
231
  coordinate_subsets=coordinate_subsets,
@@ -199,6 +245,15 @@ def generate_states_from_dataframe(
199
245
  if has_angular_velocity:
200
246
  coordinates.extend([row[column] for column in angular_velocity_columns])
201
247
 
248
+ if has_mass:
249
+ coordinates.append(row[mass_column])
250
+
251
+ if has_drag_coefficient:
252
+ coordinates.append(row[drag_coefficient_column])
253
+
254
+ if has_surface_area:
255
+ coordinates.append(row[surface_area_column])
256
+
202
257
  states.append(
203
258
  state_builder.build(
204
259
  instant=coerce_to_instant(
@@ -219,6 +274,9 @@ def generate_dataframe_from_states(
219
274
  velocity_columns: list[str] | None = None,
220
275
  attitude_columns: list[str] | None = None,
221
276
  angular_velocity_columns: list[str] | None = None,
277
+ mass_column: str | None = None,
278
+ drag_coefficient_column: str | None = None,
279
+ surface_area_column: str | None = None,
222
280
  set_time_index: bool = True,
223
281
  ) -> pd.DataFrame:
224
282
  """
@@ -232,6 +290,9 @@ def generate_dataframe_from_states(
232
290
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
233
291
  attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
234
292
  angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
293
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
294
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
295
+ surface_area_column (str | None, optional): Name of the column containing the surface area data in [m^2].
235
296
  set_time_index (bool, optional): Whether to set the time column as the index. Defaults to True.
236
297
 
237
298
  Returns:
@@ -239,6 +300,9 @@ def generate_dataframe_from_states(
239
300
  """
240
301
  has_attitude: bool = states[0].has_subset(ATTITUDE_QUATERNION_SUBSET)
241
302
  has_angular_velocity: bool = states[0].has_subset(ANGULAR_VELOCITY_SUBSET)
303
+ has_mass: bool = states[0].has_subset(MASS_SUBSET)
304
+ has_drag_coefficient: bool = states[0].has_subset(DRAG_COEFFICIENT_SUBSET)
305
+ has_surface_area: bool = states[0].has_subset(SURFACE_AREA_SUBSET)
242
306
 
243
307
  reference_frame = reference_frame or DEFAULT_REFERENCE_FRAME
244
308
 
@@ -248,6 +312,9 @@ def generate_dataframe_from_states(
248
312
  velocity_columns,
249
313
  attitude_columns,
250
314
  angular_velocity_columns,
315
+ mass_column,
316
+ drag_coefficient_column,
317
+ surface_area_column,
251
318
  ) = generate_column_names(
252
319
  reference_frame=reference_frame,
253
320
  time_column=time_column,
@@ -255,6 +322,9 @@ def generate_dataframe_from_states(
255
322
  velocity_columns=velocity_columns,
256
323
  attitude_columns=attitude_columns,
257
324
  angular_velocity_columns=angular_velocity_columns,
325
+ mass_column=mass_column,
326
+ drag_coefficient_column=drag_coefficient_column,
327
+ surface_area_column=surface_area_column,
258
328
  )
259
329
 
260
330
  if len(position_columns) != 3:
@@ -290,6 +360,18 @@ def generate_dataframe_from_states(
290
360
  state, angular_velocity_columns, ANGULAR_VELOCITY_SUBSET
291
361
  )
292
362
  )
363
+ if has_mass:
364
+ datum.update(**_get_entry_from_state(state, [mass_column], MASS_SUBSET))
365
+ if has_drag_coefficient:
366
+ datum.update(
367
+ **_get_entry_from_state(
368
+ state, [drag_coefficient_column], DRAG_COEFFICIENT_SUBSET
369
+ )
370
+ )
371
+ if has_surface_area:
372
+ datum.update(
373
+ **_get_entry_from_state(state, [surface_area_column], SURFACE_AREA_SUBSET)
374
+ )
293
375
 
294
376
  data.append(datum)
295
377
 
@@ -307,6 +389,9 @@ def generate_orbit_from_dataframe(
307
389
  time_column: str | None = None,
308
390
  position_columns: list[str] | None = None,
309
391
  velocity_columns: list[str] | None = None,
392
+ mass_column: str | None = None,
393
+ drag_coefficient_column: str | None = None,
394
+ surface_area_column: str | None = None,
310
395
  initial_revolution_number: int = 1,
311
396
  interpolation_type: Interpolator.Type | None = None,
312
397
  output_frame: Frame | None = None,
@@ -321,6 +406,9 @@ def generate_orbit_from_dataframe(
321
406
  time_column (str | None, optional): Name of the column containing the time data in [UTC].
322
407
  position_columns (list[str] | None, optional): List of column names containing the position data in [m].
323
408
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
409
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
410
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
411
+ surface_area_column (str | None, optional): Name of the column containing the surface area data in [m^2].
324
412
  initial_revolution_number (int, optional): Initial revolution number. Defaults to 1.
325
413
  interpolation_type (Interpolator.Type | None, optional): Interpolation type.
326
414
  output_frame (Frame | None, optional): Output frame for the states.
@@ -336,6 +424,9 @@ def generate_orbit_from_dataframe(
336
424
  time_column=time_column,
337
425
  position_columns=position_columns,
338
426
  velocity_columns=velocity_columns,
427
+ mass_column=mass_column,
428
+ drag_coefficient_column=drag_coefficient_column,
429
+ surface_area_column=surface_area_column,
339
430
  reference_frame=reference_frame,
340
431
  output_frame=output_frame,
341
432
  ),
@@ -353,6 +444,9 @@ def generate_dataframe_from_orbit(
353
444
  time_column: str | None = None,
354
445
  position_columns: list[str] | None = None,
355
446
  velocity_columns: list[str] | None = None,
447
+ mass_column: str | None = None,
448
+ drag_coefficient_column: str | None = None,
449
+ surface_area_column: str | None = None,
356
450
  set_time_index: bool = True,
357
451
  ) -> pd.DataFrame:
358
452
  """
@@ -365,6 +459,9 @@ def generate_dataframe_from_orbit(
365
459
  time_column (str | None, optional): Name of the column containing the time data in [UTC].
366
460
  position_columns (list[str] | None, optional): List of column names containing the position data in [m].
367
461
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
462
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
463
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
464
+ surface_area_column (str | None, optional): Name of the column containing the surface area data in [m^2].
368
465
  set_time_index (bool, optional): Whether to set the time column as the DataFrame index. Defaults to True.
369
466
 
370
467
  Returns:
@@ -379,6 +476,9 @@ def generate_dataframe_from_orbit(
379
476
  time_column=time_column,
380
477
  position_columns=position_columns,
381
478
  velocity_columns=velocity_columns,
479
+ mass_column=mass_column,
480
+ drag_coefficient_column=drag_coefficient_column,
481
+ surface_area_column=surface_area_column,
382
482
  set_time_index=set_time_index,
383
483
  )
384
484
 
@@ -391,6 +491,9 @@ def generate_profile_from_dataframe(
391
491
  velocity_columns: list[str] | None = None,
392
492
  attitude_columns: list[str] | None = None,
393
493
  angular_velocity_columns: list[str] | None = None,
494
+ mass_column: str | None = None,
495
+ drag_coefficient_column: str | None = None,
496
+ surface_area_column: str | None = None,
394
497
  output_frame: Frame | None = None,
395
498
  ) -> Profile:
396
499
  """
@@ -404,6 +507,9 @@ def generate_profile_from_dataframe(
404
507
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
405
508
  attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
406
509
  angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
510
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
511
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
512
+ surface_area_column (str | None, optional): Name of the column containing the surface area data [m^2].
407
513
  output_frame (Frame | None, optional): Output frame for the states.
408
514
 
409
515
  Returns:
@@ -420,6 +526,9 @@ def generate_profile_from_dataframe(
420
526
  velocity_columns=velocity_columns,
421
527
  attitude_columns=attitude_columns,
422
528
  angular_velocity_columns=angular_velocity_columns,
529
+ mass_column=mass_column,
530
+ drag_coefficient_column=drag_coefficient_column,
531
+ surface_area_column=surface_area_column,
423
532
  output_frame=output_frame,
424
533
  ),
425
534
  ),
@@ -435,6 +544,9 @@ def generate_dataframe_from_profile(
435
544
  velocity_columns: list[str] | None = None,
436
545
  attitude_columns: list[str] | None = None,
437
546
  angular_velocity_columns: list[str] | None = None,
547
+ mass_column: str | None = None,
548
+ drag_coefficient_column: str | None = None,
549
+ surface_area_column: str | None = None,
438
550
  set_time_index: bool = True,
439
551
  ) -> pd.DataFrame:
440
552
  """
@@ -449,6 +561,9 @@ def generate_dataframe_from_profile(
449
561
  velocity_columns (list[str] | None, optional): List of column names containing the velocity data in [m/s].
450
562
  attitude_columns (list[str] | None, optional): List of column names containing the attitude data in [x, y, z, s] form.
451
563
  angular_velocity_columns (list[str] | None, optional): List of column names containing the angular velocity data in [rad/s].
564
+ mass_column (str | None, optional): Name of the column containing the mass data in [kg].
565
+ drag_coefficient_column (str | None, optional): Name of the column containing the drag coefficient data.
566
+ surface_area_column (str | None, optional): Name of the column containing the surface area data [m^2].
452
567
  set_time_index (bool, optional): Whether to set the time column as the DataFrame index. Defaults to True.
453
568
 
454
569
  Returns:
@@ -465,6 +580,9 @@ def generate_dataframe_from_profile(
465
580
  velocity_columns=velocity_columns,
466
581
  attitude_columns=attitude_columns,
467
582
  angular_velocity_columns=angular_velocity_columns,
583
+ mass_column=mass_column,
584
+ drag_coefficient_column=drag_coefficient_column,
585
+ surface_area_column=surface_area_column,
468
586
  set_time_index=set_time_index,
469
587
  )
470
588
 
@@ -20,9 +20,10 @@ from ostk.physics.coordinate.frame.provider.iau import Theory
20
20
 
21
21
  from ostk.astrodynamics.converters import coerce_to_datetime
22
22
  from ostk.astrodynamics.trajectory import State
23
- from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
23
+ from ostk.astrodynamics.trajectory.state import CoordinateSubset
24
24
  from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
25
25
  from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
26
+ from ostk.astrodynamics.trajectory import StateBuilder
26
27
  from ostk.astrodynamics.flight import Profile
27
28
 
28
29
  from ostk.astrodynamics.dataframe import generate_states_from_dataframe
@@ -194,6 +195,76 @@ class TestOrbitDataframe:
194
195
  orbit_dataframe.set_index("Timestamp", inplace=True)
195
196
  return orbit_dataframe
196
197
 
198
+ @pytest.fixture
199
+ def orbit_state_with_properties(
200
+ self,
201
+ instant: Instant,
202
+ position: Position,
203
+ velocity: Velocity,
204
+ ) -> State:
205
+ state_builder = StateBuilder(
206
+ frame=Frame.GCRF(),
207
+ coordinate_subsets=[
208
+ CartesianPosition.default(),
209
+ CartesianVelocity.default(),
210
+ CoordinateSubset.mass(),
211
+ CoordinateSubset.drag_coefficient(),
212
+ CoordinateSubset.surface_area(),
213
+ ],
214
+ )
215
+
216
+ return state_builder.build(
217
+ instant=instant,
218
+ coordinates=[
219
+ *position.get_coordinates(),
220
+ *velocity.get_coordinates(),
221
+ 100.0, # mass in kg
222
+ 2.2, # drag coefficient
223
+ 10.5, # surface area in m²
224
+ ],
225
+ )
226
+
227
+ @pytest.fixture
228
+ def orbit_states_with_properties(
229
+ self, orbit_state_with_properties: State
230
+ ) -> list[State]:
231
+ return [
232
+ orbit_state_with_properties,
233
+ orbit_state_with_properties,
234
+ orbit_state_with_properties,
235
+ ]
236
+
237
+ @pytest.fixture
238
+ def orbit_dataframe_with_properties(self, instant: Instant) -> pd.DataFrame:
239
+ return pd.DataFrame(
240
+ [
241
+ {
242
+ "Timestamp": coerce_to_datetime(instant),
243
+ "r_GCRF_x": 1.0,
244
+ "r_GCRF_y": 2.0,
245
+ "r_GCRF_z": 3.0,
246
+ "v_GCRF_x": 4.0,
247
+ "v_GCRF_y": 5.0,
248
+ "v_GCRF_z": 6.0,
249
+ "mass": 100.0,
250
+ "drag_coefficient": 2.2,
251
+ "surface_area": 10.5,
252
+ },
253
+ {
254
+ "Timestamp": coerce_to_datetime(instant + Duration.minutes(1.0)),
255
+ "r_GCRF_x": 11.0,
256
+ "r_GCRF_y": 12.0,
257
+ "r_GCRF_z": 13.0,
258
+ "v_GCRF_x": 14.0,
259
+ "v_GCRF_y": 15.0,
260
+ "v_GCRF_z": 16.0,
261
+ "mass": 99.5,
262
+ "drag_coefficient": 2.2,
263
+ "surface_area": 10.5,
264
+ },
265
+ ]
266
+ )
267
+
197
268
  def test_generate_orbit_states_from_dataframe_defaults_success(
198
269
  self,
199
270
  orbit_dataframe: pd.DataFrame,
@@ -416,6 +487,63 @@ class TestOrbitDataframe:
416
487
  "w_3",
417
488
  ]
418
489
 
490
+ def test_generate_states_from_dataframe_with_properties_success(
491
+ self,
492
+ orbit_dataframe_with_properties: pd.DataFrame,
493
+ ):
494
+ states: list[State] = generate_states_from_dataframe(
495
+ dataframe=orbit_dataframe_with_properties,
496
+ reference_frame=Frame.GCRF(),
497
+ )
498
+
499
+ for state in states:
500
+ assert (
501
+ len(state.get_coordinates()) == 9
502
+ ) # 3 position + 3 velocity + mass + drag_coefficient + surface_area
503
+ assert state.has_subset(CoordinateSubset.mass())
504
+ assert state.has_subset(CoordinateSubset.drag_coefficient())
505
+ assert state.has_subset(CoordinateSubset.surface_area())
506
+
507
+ def test_generate_dataframe_from_states_with_properties_success(
508
+ self,
509
+ orbit_states_with_properties: list[State],
510
+ ):
511
+ generated_dataframe: pd.DataFrame = generate_dataframe_from_states(
512
+ states=orbit_states_with_properties,
513
+ )
514
+
515
+ assert "mass" in generated_dataframe.columns
516
+ assert "drag_coefficient" in generated_dataframe.columns
517
+ assert "surface_area" in generated_dataframe.columns
518
+
519
+ # Verify the values are correct
520
+ assert generated_dataframe["mass"].iloc[0] == 100.0
521
+ assert generated_dataframe["drag_coefficient"].iloc[0] == 2.2
522
+ assert generated_dataframe["surface_area"].iloc[0] == 10.5
523
+
524
+ def test_generate_dataframe_from_states_with_custom_property_columns(
525
+ self,
526
+ orbit_states_with_properties: list[State],
527
+ ):
528
+ generated_dataframe: pd.DataFrame = generate_dataframe_from_states(
529
+ states=orbit_states_with_properties,
530
+ time_column="t",
531
+ position_columns=["r_1", "r_2", "r_3"],
532
+ velocity_columns=["v_1", "v_2", "v_3"],
533
+ mass_column="spacecraft_mass",
534
+ drag_coefficient_column="cd",
535
+ surface_area_column="area",
536
+ )
537
+
538
+ assert "spacecraft_mass" in generated_dataframe.columns
539
+ assert "cd" in generated_dataframe.columns
540
+ assert "area" in generated_dataframe.columns
541
+
542
+ # Verify the values are correct
543
+ assert generated_dataframe["spacecraft_mass"].iloc[0] == 100.0
544
+ assert generated_dataframe["cd"].iloc[0] == 2.2
545
+ assert generated_dataframe["area"].iloc[0] == 10.5
546
+
419
547
 
420
548
  class TestProfileDataframe:
421
549
  @pytest.fixture
@@ -427,7 +555,10 @@ class TestProfileDataframe:
427
555
  return Instant.date_time(DateTime(2020, 1, 1, 0, 0, 0), Scale.UTC)
428
556
 
429
557
  @pytest.fixture
430
- def dataframe(self, epoch: Instant) -> pd.DataFrame:
558
+ def dataframe(
559
+ self,
560
+ epoch: Instant,
561
+ ) -> pd.DataFrame:
431
562
  return pd.DataFrame(
432
563
  [
433
564
  {
@@ -466,16 +597,78 @@ class TestProfileDataframe:
466
597
  )
467
598
 
468
599
  @pytest.fixture
469
- def profile(self, dataframe: pd.DataFrame) -> Profile:
600
+ def profile(
601
+ self,
602
+ dataframe: pd.DataFrame,
603
+ ) -> Profile:
470
604
  return generate_profile_from_dataframe(
471
605
  dataframe=dataframe,
472
606
  )
473
607
 
474
608
  @pytest.fixture
475
- def dataframe_indexed_timestamp(self, dataframe: pd.DataFrame) -> pd.DataFrame:
609
+ def dataframe_indexed_timestamp(
610
+ self,
611
+ dataframe: pd.DataFrame,
612
+ ) -> pd.DataFrame:
476
613
  dataframe.set_index("Timestamp", inplace=True)
477
614
  return dataframe
478
615
 
616
+ @pytest.fixture
617
+ def dataframe_with_properties(
618
+ self,
619
+ epoch: Instant,
620
+ ) -> pd.DataFrame:
621
+ return pd.DataFrame(
622
+ [
623
+ {
624
+ "Timestamp": coerce_to_datetime(epoch),
625
+ "r_GCRF_x": 1.0,
626
+ "r_GCRF_y": 2.0,
627
+ "r_GCRF_z": 3.0,
628
+ "v_GCRF_x": 4.0,
629
+ "v_GCRF_y": 5.0,
630
+ "v_GCRF_z": 6.0,
631
+ "q_B_GCRF_x": 0.0,
632
+ "q_B_GCRF_y": 0.0,
633
+ "q_B_GCRF_z": 0.0,
634
+ "q_B_GCRF_s": 1.0,
635
+ "w_B_GCRF_in_B_x": 0.0,
636
+ "w_B_GCRF_in_B_y": 0.0,
637
+ "w_B_GCRF_in_B_z": 0.0,
638
+ "mass": 100.0,
639
+ "drag_coefficient": 2.2,
640
+ "surface_area": 10.5,
641
+ },
642
+ {
643
+ "Timestamp": coerce_to_datetime(epoch + Duration.minutes(1.0)),
644
+ "r_GCRF_x": 11.0,
645
+ "r_GCRF_y": 12.0,
646
+ "r_GCRF_z": 13.0,
647
+ "v_GCRF_x": 14.0,
648
+ "v_GCRF_y": 15.0,
649
+ "v_GCRF_z": 16.0,
650
+ "q_B_GCRF_x": 0.0,
651
+ "q_B_GCRF_y": 0.0,
652
+ "q_B_GCRF_z": 1.0,
653
+ "q_B_GCRF_s": 0.0,
654
+ "w_B_GCRF_in_B_x": 1.0,
655
+ "w_B_GCRF_in_B_y": 1.0,
656
+ "w_B_GCRF_in_B_z": 1.0,
657
+ "mass": 99.5,
658
+ "drag_coefficient": 2.2,
659
+ "surface_area": 10.5,
660
+ },
661
+ ]
662
+ )
663
+
664
+ @pytest.fixture
665
+ def dataframe_with_properties_indexed_timestamp(
666
+ self,
667
+ dataframe_with_properties: pd.DataFrame,
668
+ ) -> pd.DataFrame:
669
+ dataframe_with_properties.set_index("Timestamp", inplace=True)
670
+ return dataframe_with_properties
671
+
479
672
  def test_generate_profile_from_dataframe_success(
480
673
  self,
481
674
  epoch: Instant,
@@ -873,3 +1066,87 @@ class TestProfileDataframe:
873
1066
  "w_2",
874
1067
  "w_3",
875
1068
  ]
1069
+
1070
+ def test_generate_profile_from_dataframe_with_properties_success(
1071
+ self,
1072
+ epoch: Instant,
1073
+ dataframe_with_properties: pd.DataFrame,
1074
+ ):
1075
+ profile: Profile = generate_profile_from_dataframe(
1076
+ dataframe=dataframe_with_properties,
1077
+ )
1078
+
1079
+ assert profile is not None
1080
+ state = profile.get_state_at(epoch)
1081
+
1082
+ assert state.has_subset(CoordinateSubset.mass())
1083
+ assert state.has_subset(CoordinateSubset.drag_coefficient())
1084
+ assert state.has_subset(CoordinateSubset.surface_area())
1085
+
1086
+ # Verify extracted coordinates are correct
1087
+ assert state.extract_coordinate(CoordinateSubset.mass())[0] == 100.0
1088
+ assert state.extract_coordinate(CoordinateSubset.drag_coefficient())[0] == 2.2
1089
+ assert state.extract_coordinate(CoordinateSubset.surface_area())[0] == 10.5
1090
+
1091
+ def test_generate_dataframe_from_profile_with_properties_success(
1092
+ self,
1093
+ epoch: Instant,
1094
+ dataframe_with_properties_indexed_timestamp: pd.DataFrame,
1095
+ ):
1096
+ # Create a profile with properties
1097
+ profile_with_props = generate_profile_from_dataframe(
1098
+ dataframe=dataframe_with_properties_indexed_timestamp,
1099
+ )
1100
+
1101
+ generated_dataframe: pd.DataFrame = generate_dataframe_from_profile(
1102
+ profile=profile_with_props,
1103
+ instants=[
1104
+ epoch,
1105
+ epoch + Duration.minutes(1.0),
1106
+ ],
1107
+ )
1108
+
1109
+ assert "mass" in generated_dataframe.columns
1110
+ assert "drag_coefficient" in generated_dataframe.columns
1111
+ assert "surface_area" in generated_dataframe.columns
1112
+
1113
+ # Verify the values
1114
+ assert generated_dataframe["mass"].iloc[0] == 100.0
1115
+ assert generated_dataframe["drag_coefficient"].iloc[0] == 2.2
1116
+ assert generated_dataframe["surface_area"].iloc[0] == 10.5
1117
+
1118
+ def test_generate_dataframe_from_profile_with_custom_property_columns(
1119
+ self,
1120
+ epoch: Instant,
1121
+ dataframe_with_properties_indexed_timestamp: pd.DataFrame,
1122
+ ):
1123
+ # Create a profile with properties
1124
+ profile_with_props = generate_profile_from_dataframe(
1125
+ dataframe=dataframe_with_properties_indexed_timestamp,
1126
+ )
1127
+
1128
+ generated_dataframe: pd.DataFrame = generate_dataframe_from_profile(
1129
+ profile=profile_with_props,
1130
+ instants=[
1131
+ epoch,
1132
+ epoch + Duration.minutes(1.0),
1133
+ ],
1134
+ time_column="t",
1135
+ position_columns=["r_1", "r_2", "r_3"],
1136
+ velocity_columns=["v_1", "v_2", "v_3"],
1137
+ attitude_columns=["q_1", "q_2", "q_3", "q_4"],
1138
+ angular_velocity_columns=["w_1", "w_2", "w_3"],
1139
+ mass_column="spacecraft_mass",
1140
+ drag_coefficient_column="cd",
1141
+ surface_area_column="area",
1142
+ set_time_index=False,
1143
+ )
1144
+
1145
+ assert "spacecraft_mass" in generated_dataframe.columns
1146
+ assert "cd" in generated_dataframe.columns
1147
+ assert "area" in generated_dataframe.columns
1148
+
1149
+ # Verify the values
1150
+ assert generated_dataframe["spacecraft_mass"].iloc[0] == 100.0
1151
+ assert generated_dataframe["cd"].iloc[0] == 2.2
1152
+ assert generated_dataframe["area"].iloc[0] == 10.5
@@ -53,3 +53,6 @@ class TestCoordinateSubset:
53
53
 
54
54
  def test_mass_flow_rate(self):
55
55
  assert CoordinateSubset.mass_flow_rate() is not None
56
+
57
+ def test_ballistic_coefficient(self):
58
+ assert CoordinateSubset.ballistic_coefficient() is not None
@@ -110,6 +110,14 @@ class CoordinateSubset:
110
110
 
111
111
  """
112
112
  @staticmethod
113
+ def ballistic_coefficient() -> CoordinateSubset:
114
+ """
115
+ Get the ballistic coefficient coordinate subset.
116
+
117
+ Returns:
118
+ CoordinateSubset: The ballistic coefficient coordinate subset.
119
+ """
120
+ @staticmethod
113
121
  def drag_coefficient() -> CoordinateSubset:
114
122
  """
115
123
  Get the drag coefficient coordinate subset.
@@ -205,14 +205,14 @@ class Viewer:
205
205
  direction=(0.0, +1.0, 0.0),
206
206
  half_angle=Angle.degrees(1.0),
207
207
  length=Length.meters(2.0),
208
- color="blue",
208
+ color="green",
209
209
  ),
210
210
  ConicSensor(
211
211
  name="z_axis",
212
212
  direction=(0.0, 0.0, +1.0),
213
213
  half_angle=Angle.degrees(1.0),
214
214
  length=Length.meters(2.0),
215
- color="green",
215
+ color="blue",
216
216
  ),
217
217
  ]
218
218
  )