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.
Files changed (100) hide show
  1. open_space_toolkit_astrodynamics-13.1.0.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-13.1.0.dist-info/RECORD +100 -0
  3. open_space_toolkit_astrodynamics-13.1.0.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-13.1.0.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-13.1.0.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-313-x86_64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/converters.py +130 -0
  10. ostk/astrodynamics/dataframe.py +479 -0
  11. ostk/astrodynamics/display.py +222 -0
  12. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.13 +0 -0
  13. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  14. ostk/astrodynamics/pytrajectory/pystate.py +251 -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 +253 -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 +78 -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 +201 -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 +205 -0
  94. ostk/astrodynamics/test/trajectory/test_propagator.py +458 -0
  95. ostk/astrodynamics/test/trajectory/test_segment.py +403 -0
  96. ostk/astrodynamics/test/trajectory/test_sequence.py +530 -0
  97. ostk/astrodynamics/test/trajectory/test_state.py +543 -0
  98. ostk/astrodynamics/test/trajectory/test_state_builder.py +171 -0
  99. ostk/astrodynamics/utilities.py +247 -0
  100. ostk/astrodynamics/viewer.py +392 -0
@@ -0,0 +1,251 @@
1
+ # Apache License 2.0
2
+
3
+ # Python-only State functionality
4
+ import re
5
+
6
+ import numpy as np
7
+
8
+ from ostk.physics.coordinate import Frame
9
+ from ostk.physics.time import Instant
10
+
11
+ from ostk.astrodynamics.trajectory import State, StateBuilder
12
+ from ostk.astrodynamics.trajectory.state import CoordinateSubset
13
+ from ostk.astrodynamics.converters import coerce_to_instant
14
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import (
15
+ CartesianPosition,
16
+ CartesianVelocity,
17
+ AttitudeQuaternion,
18
+ AngularVelocity,
19
+ )
20
+
21
+ CANONICAL_FORMAT: str = r"(r|v)_(.*?)_(x|y|z)"
22
+
23
+
24
+ @staticmethod
25
+ def custom_class_generator(frame: Frame, coordinate_subsets: list) -> type:
26
+ """
27
+ Emit a custom class type for States. This is meta-programming syntactic sugar on top of the StateBuilder class.
28
+
29
+ StateType = State.template(frame, coordinate_subsets)
30
+ state = StateType(instant, coordinates)
31
+
32
+ is equivalent to
33
+
34
+ state_builder = StateBuilder(frame, coordinate_subsets)
35
+ state = state_builder.build(instant, coordinates)
36
+ """
37
+
38
+ class StateTemplateType(State):
39
+ state_builder: StateBuilder = StateBuilder(frame, coordinate_subsets)
40
+
41
+ def __init__(self, instant: Instant, coordinates: np.ndarray):
42
+ super().__init__(StateTemplateType.state_builder.build(instant, coordinates))
43
+
44
+ return StateTemplateType
45
+
46
+
47
+ @staticmethod
48
+ def from_dict(data: dict) -> State:
49
+ """
50
+ Create a State from a dictionary.
51
+
52
+ Note: Implicit assumption that ECEF = ITRF, and ECI = GCRF.
53
+
54
+ The dictionary must contain the following:
55
+ - 'timestamp': The timestamp of the state.
56
+ - 'r_ITRF_x'/'rx'/'rx_eci'/'rx_ecef': The x-coordinate of the position.
57
+ - 'r_ITRF_y'/'ry'/'ry_eci'/'ry_ecef': The y-coordinate of the position.
58
+ - 'r_ITRF_z'/'rz'/'rz_eci'/'rz_ecef': The z-coordinate of the position.
59
+ - 'v_ITRF_x'/'vx'/'vx_eci'/'vx_ecef': The x-coordinate of the velocity.
60
+ - 'v_ITRF_y'/'vy'/'vy_eci'/'vy_ecef': The y-coordinate of the velocity.
61
+ - 'v_ITRF_z'/'vz'/'vz_eci'/'vz_ecef': The z-coordinate of the velocity.
62
+ - 'frame': The frame of the state. Required if 'rx', 'ry', 'rz', 'vx', 'vy', 'vz' are provided.
63
+ - 'q_B_ECI_x': The x-coordinate of the quaternion. Optional.
64
+ - 'q_B_ECI_y': The y-coordinate of the quaternion. Optional.
65
+ - 'q_B_ECI_z': The z-coordinate of the quaternion. Optional.
66
+ - 'q_B_ECI_s': The s-coordinate of the quaternion. Optional.
67
+ - 'w_B_ECI_in_B_x': The x-coordinate of the angular velocity. Optional.
68
+ - 'w_B_ECI_in_B_y': The y-coordinate of the angular velocity. Optional.
69
+ - 'w_B_ECI_in_B_z': The z-coordinate of the angular velocity. Optional.
70
+ - 'drag_coefficient'/'cd': The drag coefficient. Optional.
71
+ - 'cross_sectional_area'/'surface_area': The cross-sectional area. Optional.
72
+ - 'mass': The mass. Optional.
73
+
74
+ Args:
75
+ data (dict): The dictionary.
76
+
77
+ Returns:
78
+ State: The State.
79
+ """
80
+
81
+ instant: Instant = coerce_to_instant(data["timestamp"])
82
+
83
+ eci_columns: list[str] = [
84
+ "rx_eci",
85
+ "ry_eci",
86
+ "rz_eci",
87
+ "vx_eci",
88
+ "vy_eci",
89
+ "vz_eci",
90
+ ]
91
+ ecef_columns: list[str] = [
92
+ "rx_ecef",
93
+ "ry_ecef",
94
+ "rz_ecef",
95
+ "vx_ecef",
96
+ "vy_ecef",
97
+ "vz_ecef",
98
+ ]
99
+ generic_columns: list[str] = [
100
+ "rx",
101
+ "ry",
102
+ "rz",
103
+ "vx",
104
+ "vy",
105
+ "vz",
106
+ ]
107
+
108
+ # Replace non-standard position keys with canonical representation
109
+ if all(key in data.keys() for key in ("x_eci", "y_eci", "z_eci")):
110
+ data["rx_eci"] = data["x_eci"]
111
+ data["ry_eci"] = data["y_eci"]
112
+ data["rz_eci"] = data["z_eci"]
113
+
114
+ if all(key in data.keys() for key in ("x_ecef", "y_ecef", "z_ecef")):
115
+ data["rx_ecef"] = data["x_ecef"]
116
+ data["ry_ecef"] = data["y_ecef"]
117
+ data["rz_ecef"] = data["z_ecef"]
118
+
119
+ frame: Frame
120
+ coordinates: np.ndarray
121
+
122
+ coordinate_subsets: list[CoordinateSubset] = [
123
+ CartesianPosition.default(),
124
+ CartesianVelocity.default(),
125
+ ]
126
+
127
+ match_groups: list[re.Match] = [
128
+ re.match(CANONICAL_FORMAT, column) for column in data.keys()
129
+ ]
130
+
131
+ if len(matches := [match for match in match_groups if match is not None]) == 6:
132
+ frame_name: str = matches[0].group(2)
133
+ try:
134
+ frame: Frame = Frame.with_name(frame_name) or getattr(Frame, frame_name)()
135
+ except Exception:
136
+ raise ValueError(f"No frame exists with name [{frame_name}].")
137
+
138
+ coordinates = np.array([data[match.group(0)] for match in matches])
139
+
140
+ elif all(column in data for column in eci_columns):
141
+ frame: Frame = Frame.GCRF()
142
+ coordinates = np.array(
143
+ [
144
+ data["rx_eci"],
145
+ data["ry_eci"],
146
+ data["rz_eci"],
147
+ data["vx_eci"],
148
+ data["vy_eci"],
149
+ data["vz_eci"],
150
+ ]
151
+ )
152
+
153
+ elif all(column in data for column in ecef_columns):
154
+ frame = Frame.ITRF()
155
+ coordinates = np.array(
156
+ [
157
+ data["rx_ecef"],
158
+ data["ry_ecef"],
159
+ data["rz_ecef"],
160
+ data["vx_ecef"],
161
+ data["vy_ecef"],
162
+ data["vz_ecef"],
163
+ ]
164
+ )
165
+
166
+ elif all(column in data for column in generic_columns):
167
+ if "frame" not in data:
168
+ raise ValueError("Frame must be provided for generic columns.")
169
+
170
+ if isinstance(data["frame"], str):
171
+ if Frame.exists(data["frame"]):
172
+ frame = Frame.with_name(data["frame"])
173
+ else:
174
+ raise ValueError(f"No frame exists with name [{data['frame']}].")
175
+ elif isinstance(data["frame"], Frame):
176
+ frame = data["frame"]
177
+ else:
178
+ raise ValueError(f"Invalid frame data [{data['frame']}].")
179
+
180
+ coordinates = np.array(
181
+ [
182
+ data["rx"],
183
+ data["ry"],
184
+ data["rz"],
185
+ data["vx"],
186
+ data["vy"],
187
+ data["vz"],
188
+ ]
189
+ )
190
+ else:
191
+ raise ValueError("Invalid state data.")
192
+
193
+ if all(
194
+ column in data for column in ["q_B_ECI_x", "q_B_ECI_y", "q_B_ECI_z", "q_B_ECI_s"]
195
+ ):
196
+ coordinate_subsets.append(AttitudeQuaternion.default())
197
+ coordinates = np.append(
198
+ coordinates,
199
+ [
200
+ data["q_B_ECI_x"],
201
+ data["q_B_ECI_y"],
202
+ data["q_B_ECI_z"],
203
+ data["q_B_ECI_s"],
204
+ ],
205
+ )
206
+
207
+ if all(
208
+ column in data
209
+ for column in ["w_B_ECI_in_B_x", "w_B_ECI_in_B_y", "w_B_ECI_in_B_z"]
210
+ ):
211
+ coordinate_subsets.append(AngularVelocity.default())
212
+ coordinates = np.append(
213
+ coordinates,
214
+ [
215
+ data["w_B_ECI_in_B_x"],
216
+ data["w_B_ECI_in_B_y"],
217
+ data["w_B_ECI_in_B_z"],
218
+ ],
219
+ )
220
+
221
+ if "drag_coefficient" in data or "cd" in data:
222
+ coordinate_subsets.append(CoordinateSubset.drag_coefficient())
223
+ coordinates = np.append(
224
+ coordinates,
225
+ data.get("drag_coefficient", data.get("cd")),
226
+ )
227
+
228
+ if "cross_sectional_area" in data or "surface_area" in data:
229
+ coordinate_subsets.append(CoordinateSubset.surface_area())
230
+ coordinates = np.append(
231
+ coordinates,
232
+ data.get("cross_sectional_area", data.get("surface_area")),
233
+ )
234
+
235
+ if "mass" in data:
236
+ coordinate_subsets.append(CoordinateSubset.mass())
237
+ coordinates = np.append(
238
+ coordinates,
239
+ data["mass"],
240
+ )
241
+
242
+ return State(
243
+ instant=instant,
244
+ coordinates=coordinates,
245
+ frame=frame,
246
+ coordinate_subsets=coordinate_subsets,
247
+ )
248
+
249
+
250
+ State.from_dict = from_dict
251
+ State.template = custom_class_generator
@@ -0,0 +1 @@
1
+ # Apache License 2.0
@@ -0,0 +1 @@
1
+ # Apache License 2.0
@@ -0,0 +1,248 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ from ostk.mathematics.object import RealInterval
6
+
7
+ from ostk.physics.unit import Length
8
+ from ostk.physics.unit import Angle
9
+ from ostk.physics.time import DateTime
10
+ from ostk.physics.time import Scale
11
+ from ostk.physics.time import Duration
12
+ from ostk.physics.time import Instant
13
+ from ostk.physics.time import Interval
14
+ from ostk.physics import Environment
15
+ from ostk.physics.environment.object import Celestial
16
+
17
+ from ostk.astrodynamics import Trajectory
18
+ from ostk.astrodynamics.trajectory import Orbit
19
+ from ostk.astrodynamics.trajectory.orbit.model import Kepler
20
+ from ostk.astrodynamics.trajectory.orbit.model.kepler import COE
21
+ from ostk.astrodynamics import Access
22
+ from ostk.astrodynamics.access import Generator
23
+
24
+
25
+ @pytest.fixture
26
+ def environment() -> Environment:
27
+ return Environment.default()
28
+
29
+
30
+ @pytest.fixture
31
+ def earth(environment: Environment) -> Celestial:
32
+ return environment.access_celestial_object_with_name("Earth")
33
+
34
+
35
+ @pytest.fixture
36
+ def generator(environment: Environment) -> Generator:
37
+ return Generator(
38
+ environment=environment,
39
+ aer_filter=lambda aer: True,
40
+ access_filter=lambda access: True,
41
+ state_filter=lambda state_1, state_2: True,
42
+ )
43
+
44
+
45
+ @pytest.fixture
46
+ def from_trajectory(earth: Celestial) -> Trajectory:
47
+ return Orbit(
48
+ model=Kepler(
49
+ coe=COE(
50
+ semi_major_axis=Length.kilometers(7000.0),
51
+ eccentricity=0.0,
52
+ inclination=Angle.degrees(45.0),
53
+ raan=Angle.degrees(0.0),
54
+ aop=Angle.degrees(0.0),
55
+ true_anomaly=Angle.degrees(0.0),
56
+ ),
57
+ epoch=Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
58
+ celestial_object=earth,
59
+ perturbation_type=Kepler.PerturbationType.No,
60
+ ),
61
+ celestial_object=earth,
62
+ )
63
+
64
+
65
+ @pytest.fixture
66
+ def to_trajectory(earth: Celestial) -> Trajectory:
67
+ return Orbit(
68
+ model=Kepler(
69
+ coe=COE(
70
+ semi_major_axis=Length.kilometers(7000.0),
71
+ eccentricity=0.0,
72
+ inclination=Angle.degrees(45.0),
73
+ raan=Angle.degrees(180.0),
74
+ aop=Angle.degrees(0.0),
75
+ true_anomaly=Angle.degrees(180.0),
76
+ ),
77
+ epoch=Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
78
+ celestial_object=earth,
79
+ perturbation_type=Kepler.PerturbationType.No,
80
+ ),
81
+ celestial_object=earth,
82
+ )
83
+
84
+
85
+ class TestGenerator:
86
+ def test_constructor_success_environment(self, environment: Environment):
87
+ generator = Generator(
88
+ environment=environment,
89
+ )
90
+
91
+ assert generator is not None
92
+ assert isinstance(generator, Generator)
93
+
94
+ def test_constructor_success_environment_aer_filter(self, environment: Environment):
95
+ generator = Generator(
96
+ environment=environment,
97
+ aer_filter=lambda aer: True,
98
+ )
99
+
100
+ assert generator is not None
101
+ assert isinstance(generator, Generator)
102
+
103
+ def test_constructor_success_environment_access_filter(
104
+ self,
105
+ environment: Environment,
106
+ ):
107
+ generator = Generator(
108
+ environment=environment,
109
+ access_filter=lambda access: True,
110
+ )
111
+
112
+ assert generator is not None
113
+ assert isinstance(generator, Generator)
114
+
115
+ def test_constructor_success_environment_state_filter(
116
+ self,
117
+ environment: Environment,
118
+ ):
119
+ generator = Generator(
120
+ environment=environment,
121
+ state_filter=lambda state_1, state_2: True,
122
+ )
123
+
124
+ assert generator is not None
125
+ assert isinstance(generator, Generator)
126
+
127
+ def test_constructor_success_environment_step_tolerance(
128
+ self,
129
+ environment: Environment,
130
+ ):
131
+ generator = Generator(
132
+ environment=environment,
133
+ step=Duration.seconds(1.0),
134
+ tolerance=Duration.minutes(1.0),
135
+ )
136
+
137
+ assert generator is not None
138
+ assert isinstance(generator, Generator)
139
+ assert generator.get_step() == Duration.seconds(1.0)
140
+ assert generator.get_tolerance() == Duration.minutes(1.0)
141
+
142
+ def test_getters_success(self, generator: Generator):
143
+ assert generator.get_step() == Duration.minutes(1.0)
144
+ assert generator.get_tolerance() == Duration.microseconds(1.0)
145
+ assert generator.get_aer_filter() is not None
146
+ assert generator.get_access_filter() is not None
147
+ assert generator.get_state_filter() is not None
148
+
149
+ def test_get_condition_function_success(
150
+ self,
151
+ generator: Generator,
152
+ from_trajectory: Trajectory,
153
+ to_trajectory: Trajectory,
154
+ ):
155
+ condition_function = generator.get_condition_function(
156
+ from_trajectory=from_trajectory,
157
+ to_trajectory=to_trajectory,
158
+ )
159
+
160
+ assert condition_function is not None
161
+ assert (
162
+ condition_function(
163
+ Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
164
+ )
165
+ is True
166
+ )
167
+
168
+ def test_compute_accesses_success(
169
+ self,
170
+ generator: Generator,
171
+ from_trajectory: Trajectory,
172
+ to_trajectory: Trajectory,
173
+ ):
174
+ accesses = generator.compute_accesses(
175
+ interval=Interval.closed(
176
+ Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
177
+ Instant.date_time(DateTime(2018, 1, 1, 2, 0, 0), Scale.UTC),
178
+ ),
179
+ from_trajectory=from_trajectory,
180
+ to_trajectory=to_trajectory,
181
+ )
182
+
183
+ assert accesses is not None
184
+ assert isinstance(accesses, list)
185
+ assert accesses[0] is not None
186
+ assert isinstance(accesses[0], Access)
187
+
188
+ def test_set_step_success(self, generator: Generator):
189
+ generator.set_step(Duration.seconds(1.0))
190
+
191
+ def test_set_tolerance_success(self, generator: Generator):
192
+ generator.set_tolerance(Duration.seconds(1.0))
193
+
194
+ def test_set_aer_filter_success(self, generator: Generator):
195
+ generator.set_aer_filter(aer_filter=lambda aer: True)
196
+
197
+ def test_set_access_filter_success(self, generator: Generator):
198
+ generator.set_access_filter(access_filter=lambda access: True)
199
+
200
+ def test_set_state_filter_success(self, generator: Generator):
201
+ generator.set_state_filter(state_filter=lambda state_1, state_2: True)
202
+
203
+ def test_undefined_success(self):
204
+ generator = Generator.undefined()
205
+
206
+ assert generator is not None
207
+ assert isinstance(generator, Generator)
208
+ assert generator.is_defined() is False
209
+
210
+ def test_aer_ranges_success(self, environment: Environment):
211
+ # Construct arbitrary AER ranges
212
+ azimuth_interval = RealInterval.closed(0.0, 360.0)
213
+ elevation_interval = RealInterval.closed(0.0, 90.0)
214
+ range_interval = RealInterval.closed(0.0, 7000e3)
215
+
216
+ generator = Generator.aer_ranges(
217
+ azimuth_range=azimuth_interval,
218
+ elevation_range=elevation_interval,
219
+ range_range=range_interval,
220
+ environment=environment,
221
+ )
222
+
223
+ assert generator is not None
224
+ assert isinstance(generator, Generator)
225
+ assert generator.is_defined()
226
+
227
+ def test_aer_mask_success(self, environment: Environment):
228
+ # Construct arbitrary anAzimuthElevationMask using python dict
229
+ an_azimuth_elevation_mask = {
230
+ 0.0: 30.0,
231
+ 90.0: 60.0,
232
+ 180.0: 60.0,
233
+ 270.0: 30.0,
234
+ 359.0: 30.0,
235
+ }
236
+
237
+ # Construct arbitrary aRangerange
238
+ a_range_range = RealInterval(0.0, 10e4, RealInterval.Type.Closed)
239
+
240
+ generator = Generator.aer_mask(
241
+ azimuth_elevation_mask=an_azimuth_elevation_mask,
242
+ range_range=a_range_range,
243
+ environment=environment,
244
+ )
245
+
246
+ assert generator is not None
247
+ assert isinstance(generator, Generator)
248
+ assert generator.is_defined()
@@ -0,0 +1,119 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ from ostk.physics import Environment
6
+ from ostk.physics.unit import Angle
7
+ from ostk.physics.unit import Length
8
+ from ostk.physics.time import Instant
9
+ from ostk.physics.time import DateTime
10
+ from ostk.physics.time import Scale
11
+ from ostk.physics.time import Interval
12
+ from ostk.physics.coordinate import Position
13
+ from ostk.physics.coordinate import Velocity
14
+ from ostk.physics.coordinate import Frame
15
+ from ostk.physics.coordinate.spherical import LLA
16
+ from ostk.physics.coordinate.spherical import AER
17
+
18
+ from ostk.astrodynamics import Trajectory
19
+ from ostk.astrodynamics.trajectory import State
20
+
21
+
22
+ @pytest.fixture
23
+ def environment() -> Environment:
24
+ return Environment.default()
25
+
26
+
27
+ @pytest.fixture
28
+ def scale() -> Scale:
29
+ return Scale.UTC
30
+
31
+
32
+ @pytest.fixture
33
+ def instant_1(scale: Scale) -> Instant:
34
+ return Instant.date_time(DateTime(2023, 1, 8, 13, 45, 34), scale)
35
+
36
+
37
+ @pytest.fixture
38
+ def instant_2(scale: Scale) -> Instant:
39
+ return Instant.date_time(DateTime(2023, 1, 8, 13, 46, 34), scale)
40
+
41
+
42
+ @pytest.fixture
43
+ def instant_3(scale: Scale) -> Instant:
44
+ return Instant.date_time(DateTime(2023, 1, 8, 13, 47, 34), scale)
45
+
46
+
47
+ @pytest.fixture
48
+ def instant(instant_1: Instant) -> Instant:
49
+ return instant_1
50
+
51
+
52
+ @pytest.fixture
53
+ def interval(instant_1: Instant, instant_2: Instant) -> Interval:
54
+ return Interval(instant_1, instant_2, Interval.Type.Closed)
55
+
56
+
57
+ @pytest.fixture
58
+ def frame() -> Frame:
59
+ return Frame.GCRF()
60
+
61
+
62
+ @pytest.fixture
63
+ def position(frame: Frame) -> Position:
64
+ return Position.meters([7000000.0, 0.0, 0.0], frame)
65
+
66
+
67
+ @pytest.fixture
68
+ def velocity(frame: Frame) -> Velocity:
69
+ return Velocity.meters_per_second([7600.0, 0.0, 0.0], frame)
70
+
71
+
72
+ @pytest.fixture
73
+ def state(instant: Instant, position: Position, velocity: Velocity) -> State:
74
+ return State(instant, position, velocity)
75
+
76
+
77
+ @pytest.fixture
78
+ def latitude() -> Angle:
79
+ return Angle(30.0, Angle.Unit.Degree)
80
+
81
+
82
+ @pytest.fixture
83
+ def longitude() -> Angle:
84
+ return Angle(-111.0, Angle.Unit.Degree)
85
+
86
+
87
+ @pytest.fixture
88
+ def altitude() -> Length:
89
+ return Length(10000.0, Length.Unit.Meter)
90
+
91
+
92
+ @pytest.fixture
93
+ def lla(latitude: Angle, longitude: Angle, altitude: Length) -> LLA:
94
+ return LLA(latitude, longitude, altitude)
95
+
96
+
97
+ @pytest.fixture
98
+ def azimuth() -> Angle:
99
+ return Angle(270.0, Angle.Unit.Degree)
100
+
101
+
102
+ @pytest.fixture
103
+ def elevation() -> Angle:
104
+ return Angle(10.45, Angle.Unit.Degree)
105
+
106
+
107
+ @pytest.fixture
108
+ def range() -> Length:
109
+ return Length(100000.0, Length.Unit.Meter)
110
+
111
+
112
+ @pytest.fixture
113
+ def aer(azimuth: Angle, elevation: Angle, range: Length) -> LLA:
114
+ return AER(azimuth, elevation, range)
115
+
116
+
117
+ @pytest.fixture
118
+ def trajectory(position: Position, instant_1: Instant) -> Trajectory:
119
+ return Trajectory.position(position.in_frame(Frame.ITRF(), instant_1))
@@ -0,0 +1 @@
1
+ # Apache License 2.0