open-space-toolkit-astrodynamics 17.2.0__py312-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 (151) hide show
  1. open_space_toolkit_astrodynamics-17.2.0.dist-info/METADATA +30 -0
  2. open_space_toolkit_astrodynamics-17.2.0.dist-info/RECORD +151 -0
  3. open_space_toolkit_astrodynamics-17.2.0.dist-info/WHEEL +5 -0
  4. open_space_toolkit_astrodynamics-17.2.0.dist-info/top_level.txt +1 -0
  5. open_space_toolkit_astrodynamics-17.2.0.dist-info/zip-safe +1 -0
  6. ostk/__init__.py +1 -0
  7. ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-x86_64-linux-gnu.so +0 -0
  8. ostk/astrodynamics/__init__.py +11 -0
  9. ostk/astrodynamics/__init__.pyi +720 -0
  10. ostk/astrodynamics/access.pyi +577 -0
  11. ostk/astrodynamics/conjunction/__init__.pyi +121 -0
  12. ostk/astrodynamics/conjunction/close_approach.pyi +89 -0
  13. ostk/astrodynamics/conjunction/message/__init__.pyi +3 -0
  14. ostk/astrodynamics/conjunction/message/ccsds.pyi +705 -0
  15. ostk/astrodynamics/converters.py +130 -0
  16. ostk/astrodynamics/converters.pyi +58 -0
  17. ostk/astrodynamics/data/__init__.pyi +3 -0
  18. ostk/astrodynamics/data/provider.pyi +22 -0
  19. ostk/astrodynamics/dataframe.py +597 -0
  20. ostk/astrodynamics/display.py +281 -0
  21. ostk/astrodynamics/dynamics.pyi +311 -0
  22. ostk/astrodynamics/eclipse.pyi +70 -0
  23. ostk/astrodynamics/estimator.pyi +268 -0
  24. ostk/astrodynamics/event_condition.pyi +910 -0
  25. ostk/astrodynamics/flight/__init__.pyi +626 -0
  26. ostk/astrodynamics/flight/profile/__init__.pyi +99 -0
  27. ostk/astrodynamics/flight/profile/model.pyi +179 -0
  28. ostk/astrodynamics/flight/system.pyi +268 -0
  29. ostk/astrodynamics/guidance_law.pyi +416 -0
  30. ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.17 +0 -0
  31. ostk/astrodynamics/pytrajectory/__init__.py +1 -0
  32. ostk/astrodynamics/pytrajectory/__init__.pyi +3 -0
  33. ostk/astrodynamics/pytrajectory/pystate.py +263 -0
  34. ostk/astrodynamics/pytrajectory/pystate.pyi +66 -0
  35. ostk/astrodynamics/solver.pyi +432 -0
  36. ostk/astrodynamics/test/__init__.py +1 -0
  37. ostk/astrodynamics/test/access/__init__.py +1 -0
  38. ostk/astrodynamics/test/access/test_generator.py +319 -0
  39. ostk/astrodynamics/test/access/test_visibility_criterion.py +201 -0
  40. ostk/astrodynamics/test/conftest.py +119 -0
  41. ostk/astrodynamics/test/conjunction/close_approach/__init__.py +0 -0
  42. ostk/astrodynamics/test/conjunction/close_approach/test_generator.py +228 -0
  43. ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py +1 -0
  44. ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py +325 -0
  45. ostk/astrodynamics/test/conjunction/message/ccsds/data/cdm.json +303 -0
  46. ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py +416 -0
  47. ostk/astrodynamics/test/conjunction/test_close_approach.py +244 -0
  48. ostk/astrodynamics/test/data/provider/test_off_nadir.py +58 -0
  49. ostk/astrodynamics/test/dynamics/__init__.py +1 -0
  50. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity.csv +565 -0
  51. ostk/astrodynamics/test/dynamics/data/Tabulated_Earth_Gravity_Truth.csv +100 -0
  52. ostk/astrodynamics/test/dynamics/test_atmospheric_drag.py +128 -0
  53. ostk/astrodynamics/test/dynamics/test_central_body_gravity.py +58 -0
  54. ostk/astrodynamics/test/dynamics/test_dynamics.py +50 -0
  55. ostk/astrodynamics/test/dynamics/test_position_derivative.py +51 -0
  56. ostk/astrodynamics/test/dynamics/test_tabulated.py +138 -0
  57. ostk/astrodynamics/test/dynamics/test_third_body_gravity.py +67 -0
  58. ostk/astrodynamics/test/dynamics/test_thruster.py +157 -0
  59. ostk/astrodynamics/test/eclipse/__init__.py +1 -0
  60. ostk/astrodynamics/test/eclipse/test_generator.py +138 -0
  61. ostk/astrodynamics/test/estimator/test_orbit_determination_solver.py +261 -0
  62. ostk/astrodynamics/test/estimator/test_tle_solver.py +216 -0
  63. ostk/astrodynamics/test/event_condition/test_angular_condition.py +113 -0
  64. ostk/astrodynamics/test/event_condition/test_boolean_condition.py +55 -0
  65. ostk/astrodynamics/test/event_condition/test_brouwer_lyddane_mean_long_condition.py +135 -0
  66. ostk/astrodynamics/test/event_condition/test_coe_condition.py +135 -0
  67. ostk/astrodynamics/test/event_condition/test_instant_condition.py +48 -0
  68. ostk/astrodynamics/test/event_condition/test_logical_condition.py +120 -0
  69. ostk/astrodynamics/test/event_condition/test_real_condition.py +50 -0
  70. ostk/astrodynamics/test/flight/__init__.py +1 -0
  71. ostk/astrodynamics/test/flight/profile/model/test_tabulated_profile.py +115 -0
  72. ostk/astrodynamics/test/flight/system/__init__.py +1 -0
  73. ostk/astrodynamics/test/flight/system/test_propulsion_system.py +64 -0
  74. ostk/astrodynamics/test/flight/system/test_satellite_system.py +83 -0
  75. ostk/astrodynamics/test/flight/system/test_satellite_system_builder.py +71 -0
  76. ostk/astrodynamics/test/flight/test_maneuver.py +231 -0
  77. ostk/astrodynamics/test/flight/test_profile.py +293 -0
  78. ostk/astrodynamics/test/flight/test_system.py +45 -0
  79. ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +177 -0
  80. ostk/astrodynamics/test/guidance_law/test_guidance_law.py +60 -0
  81. ostk/astrodynamics/test/guidance_law/test_heterogeneous_guidance_law.py +164 -0
  82. ostk/astrodynamics/test/guidance_law/test_qlaw.py +209 -0
  83. ostk/astrodynamics/test/solvers/__init__.py +1 -0
  84. ostk/astrodynamics/test/solvers/test_finite_difference_solver.py +196 -0
  85. ostk/astrodynamics/test/solvers/test_least_squares_solver.py +334 -0
  86. ostk/astrodynamics/test/solvers/test_temporal_condition_solver.py +161 -0
  87. ostk/astrodynamics/test/test_access.py +128 -0
  88. ostk/astrodynamics/test/test_converters.py +290 -0
  89. ostk/astrodynamics/test/test_dataframe.py +1355 -0
  90. ostk/astrodynamics/test/test_display.py +184 -0
  91. ostk/astrodynamics/test/test_event_condition.py +80 -0
  92. ostk/astrodynamics/test/test_import.py +26 -0
  93. ostk/astrodynamics/test/test_root_solver.py +70 -0
  94. ostk/astrodynamics/test/test_trajectory.py +126 -0
  95. ostk/astrodynamics/test/test_utilities.py +338 -0
  96. ostk/astrodynamics/test/test_viewer.py +318 -0
  97. ostk/astrodynamics/test/trajectory/__init__.py +1 -0
  98. ostk/astrodynamics/test/trajectory/model/test_nadir_trajectory.py +87 -0
  99. ostk/astrodynamics/test/trajectory/model/test_tabulated_trajectory.py +303 -0
  100. ostk/astrodynamics/test/trajectory/model/test_target_scan_trajectory.py +126 -0
  101. ostk/astrodynamics/test/trajectory/orbit/__init__.py +1 -0
  102. ostk/astrodynamics/test/trajectory/orbit/message/__init__.py +1 -0
  103. ostk/astrodynamics/test/trajectory/orbit/message/spacex/__init__.py +1 -0
  104. ostk/astrodynamics/test/trajectory/orbit/message/spacex/conftest.py +18 -0
  105. ostk/astrodynamics/test/trajectory/orbit/message/spacex/data/opm_1.yaml +44 -0
  106. ostk/astrodynamics/test/trajectory/orbit/message/spacex/test_opm.py +108 -0
  107. ostk/astrodynamics/test/trajectory/orbit/models/__init__.py +1 -0
  108. ostk/astrodynamics/test/trajectory/orbit/models/kepler/__init__.py +1 -0
  109. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean.py +65 -0
  110. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_long.py +102 -0
  111. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_brouwer_lyddane_mean_short.py +102 -0
  112. ostk/astrodynamics/test/trajectory/orbit/models/kepler/test_coe.py +305 -0
  113. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/__init__.py +1 -0
  114. ostk/astrodynamics/test/trajectory/orbit/models/sgp4/test_tle.py +337 -0
  115. ostk/astrodynamics/test/trajectory/orbit/models/test_kepler.py +130 -0
  116. ostk/astrodynamics/test/trajectory/orbit/models/test_modified_equinoctial.py +142 -0
  117. ostk/astrodynamics/test/trajectory/orbit/models/test_propagated.py +234 -0
  118. ostk/astrodynamics/test/trajectory/orbit/models/test_sgp4.py +1 -0
  119. ostk/astrodynamics/test/trajectory/orbit/models/test_tabulated.py +380 -0
  120. ostk/astrodynamics/test/trajectory/orbit/test_model.py +1 -0
  121. ostk/astrodynamics/test/trajectory/orbit/test_pass.py +75 -0
  122. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_angular_velocity.py +30 -0
  123. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_attitude_quaternion.py +18 -0
  124. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_acceleration.py +136 -0
  125. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_position.py +107 -0
  126. ostk/astrodynamics/test/trajectory/state/coordinate_subset/test_cartesian_velocity.py +115 -0
  127. ostk/astrodynamics/test/trajectory/state/test_coordinate_broker.py +84 -0
  128. ostk/astrodynamics/test/trajectory/state/test_coordinate_subset.py +58 -0
  129. ostk/astrodynamics/test/trajectory/state/test_numerical_solver.py +316 -0
  130. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_direction.py +81 -0
  131. ostk/astrodynamics/test/trajectory/test_local_orbital_frame_factory.py +119 -0
  132. ostk/astrodynamics/test/trajectory/test_model.py +1 -0
  133. ostk/astrodynamics/test/trajectory/test_orbit.py +212 -0
  134. ostk/astrodynamics/test/trajectory/test_propagator.py +452 -0
  135. ostk/astrodynamics/test/trajectory/test_segment.py +694 -0
  136. ostk/astrodynamics/test/trajectory/test_sequence.py +550 -0
  137. ostk/astrodynamics/test/trajectory/test_state.py +629 -0
  138. ostk/astrodynamics/test/trajectory/test_state_builder.py +172 -0
  139. ostk/astrodynamics/trajectory/__init__.pyi +1982 -0
  140. ostk/astrodynamics/trajectory/model.pyi +259 -0
  141. ostk/astrodynamics/trajectory/orbit/__init__.pyi +349 -0
  142. ostk/astrodynamics/trajectory/orbit/message/__init__.pyi +3 -0
  143. ostk/astrodynamics/trajectory/orbit/message/spacex.pyi +264 -0
  144. ostk/astrodynamics/trajectory/orbit/model/__init__.pyi +648 -0
  145. ostk/astrodynamics/trajectory/orbit/model/brouwerLyddaneMean.pyi +121 -0
  146. ostk/astrodynamics/trajectory/orbit/model/kepler.pyi +709 -0
  147. ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi +330 -0
  148. ostk/astrodynamics/trajectory/state/__init__.pyi +402 -0
  149. ostk/astrodynamics/trajectory/state/coordinate_subset.pyi +208 -0
  150. ostk/astrodynamics/utilities.py +396 -0
  151. ostk/astrodynamics/viewer.py +851 -0
@@ -0,0 +1,212 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ from ostk.physics.environment.object.celestial import Earth
6
+ from ostk.physics.environment.object import Celestial
7
+ from ostk.physics.unit import Length, Angle
8
+ from ostk.physics.time import Scale, Instant, DateTime, Time, Duration, Interval
9
+
10
+ from ostk.astrodynamics.trajectory import Orbit, State
11
+ from ostk.astrodynamics.trajectory.orbit import Pass
12
+ from ostk.astrodynamics.trajectory.orbit import Pass
13
+ from ostk.astrodynamics.trajectory.orbit.model import SGP4
14
+ from ostk.astrodynamics.trajectory.orbit.model.sgp4 import TLE
15
+
16
+
17
+ @pytest.fixture
18
+ def earth() -> Earth:
19
+ return Earth.default()
20
+
21
+
22
+ @pytest.fixture
23
+ def epoch() -> Instant:
24
+ return Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
25
+
26
+
27
+ @pytest.fixture
28
+ def orbit(earth: Earth, epoch: Instant):
29
+ return Orbit.sun_synchronous(epoch, Length.kilometers(500.0), Time.midnight(), earth)
30
+
31
+
32
+ @pytest.fixture
33
+ def states(orbit: Orbit, epoch: Instant) -> list[State]:
34
+ instants: list[Instant] = Interval.closed(
35
+ epoch, epoch + Duration.days(1.0)
36
+ ).generate_grid(Duration.seconds(20.0))
37
+
38
+ return orbit.get_states_at(instants)
39
+
40
+
41
+ class TestOrbit:
42
+ def test_constructors(self, earth):
43
+ # Construct Two-Line Element set
44
+ tle = TLE(
45
+ "1 25544U 98067A 18231.17878740 .00000187 00000-0 10196-4 0 9994",
46
+ "2 25544 51.6447 64.7824 0005971 73.1467 36.4366 15.53848234128316",
47
+ )
48
+
49
+ # Construct orbit using SGP4 model
50
+ orbit = Orbit(SGP4(tle), earth)
51
+
52
+ assert orbit is not None
53
+ assert isinstance(orbit, Orbit)
54
+ assert orbit.is_defined()
55
+
56
+ # Construct get state at current epoch
57
+ state: State = orbit.get_state_at(Instant.now())
58
+
59
+ assert state is not None
60
+ assert isinstance(state, State)
61
+
62
+ def test_access_celestial_object(self, orbit: Orbit):
63
+ celestial_object = orbit.access_celestial_object()
64
+
65
+ assert celestial_object is not None
66
+ assert isinstance(celestial_object, Celestial)
67
+
68
+ @pytest.mark.parametrize(
69
+ "frame_type",
70
+ [
71
+ (Orbit.FrameType.NED),
72
+ (Orbit.FrameType.LVLH),
73
+ (Orbit.FrameType.VVLH),
74
+ (Orbit.FrameType.LVLHGD),
75
+ (Orbit.FrameType.LVLHGDGT),
76
+ (Orbit.FrameType.QSW),
77
+ (Orbit.FrameType.TNW),
78
+ (Orbit.FrameType.VNC),
79
+ ],
80
+ )
81
+ def test_get_orbital_frame(self, orbit: Orbit, frame_type: Orbit.FrameType):
82
+ assert orbit.get_orbital_frame(frame_type) is not None
83
+
84
+ def test_get_revolution_number_at(self, orbit: Orbit):
85
+ assert (
86
+ orbit.get_revolution_number_at(
87
+ Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
88
+ )
89
+ == 1
90
+ )
91
+
92
+ def test_get_pass_at(self, orbit: Orbit):
93
+ pass_ = orbit.get_pass_at(
94
+ Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
95
+ )
96
+
97
+ assert pass_ is not None
98
+ assert isinstance(pass_, Pass)
99
+ assert pass_.is_defined()
100
+
101
+ def test_get_pass_with_revolution_number(self, orbit: Orbit):
102
+ pass_ = orbit.get_pass_with_revolution_number(1)
103
+
104
+ assert pass_ is not None
105
+ assert isinstance(pass_, Pass)
106
+ assert pass_.is_defined()
107
+
108
+ assert (
109
+ orbit.get_pass_with_revolution_number(2, Duration.minutes(10.0)) is not None
110
+ )
111
+
112
+ def test_get_passes_within_interval(self, orbit: Orbit):
113
+ passes: list[Pass] = orbit.get_passes_within_interval(
114
+ Interval.closed(
115
+ Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
116
+ Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC),
117
+ )
118
+ )
119
+
120
+ assert len(passes) > 0
121
+
122
+ def test_undefined(self):
123
+ assert Orbit.undefined().is_defined() is False
124
+
125
+ def test_circular(self, earth):
126
+ epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
127
+ altitude = Length.kilometers(500.0)
128
+ inclination = Angle.degrees(45.0)
129
+
130
+ orbit: Orbit = Orbit.circular(epoch, altitude, inclination, earth)
131
+
132
+ assert orbit is not None
133
+ assert isinstance(orbit, Orbit)
134
+ assert orbit.is_defined()
135
+
136
+ def test_equatorial(self, earth):
137
+ epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
138
+ apoapsis_altitude = Length.kilometers(500.1)
139
+ periapsis_altitude = Length.kilometers(499.9)
140
+
141
+ orbit: Orbit = Orbit.equatorial(
142
+ epoch, apoapsis_altitude, periapsis_altitude, earth
143
+ )
144
+
145
+ assert orbit is not None
146
+ assert isinstance(orbit, Orbit)
147
+ assert orbit.is_defined()
148
+
149
+ def test_circular_equatorial(self, earth):
150
+ epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
151
+ altitude = Length.kilometers(500.0)
152
+
153
+ orbit: Orbit = Orbit.circular_equatorial(epoch, altitude, earth)
154
+
155
+ assert orbit is not None
156
+ assert isinstance(orbit, Orbit)
157
+ assert orbit.is_defined()
158
+
159
+ def test_geo_synchronous(self, earth):
160
+ epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
161
+ inclination = Angle.degrees(45.0)
162
+ longitude = Angle.degrees(45.0)
163
+
164
+ orbit: Orbit = Orbit.geo_synchronous(epoch, inclination, longitude, earth)
165
+
166
+ assert orbit is not None
167
+ assert isinstance(orbit, Orbit)
168
+ assert orbit.is_defined()
169
+
170
+ def test_sun_synchronous(self, earth):
171
+ epoch = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
172
+ altitude = Length.kilometers(500.0)
173
+ local_time_at_descending_node = Time.midnight()
174
+
175
+ orbit: Orbit = Orbit.sun_synchronous(
176
+ epoch, altitude, local_time_at_descending_node, earth
177
+ )
178
+
179
+ assert orbit is not None
180
+ assert isinstance(orbit, Orbit)
181
+ assert orbit.is_defined()
182
+
183
+ assert Orbit.sun_synchronous(
184
+ epoch=epoch,
185
+ altitude=altitude,
186
+ local_time_at_descending_node=local_time_at_descending_node,
187
+ celestial_object=earth,
188
+ argument_of_latitude=Angle.degrees(50.0),
189
+ ).is_defined()
190
+
191
+ def test_frozen(self, earth, epoch):
192
+ altitude = Length.kilometers(500.0)
193
+
194
+ orbit: Orbit = Orbit.frozen(epoch, altitude, earth)
195
+
196
+ assert orbit is not None
197
+ assert isinstance(orbit, Orbit)
198
+ assert orbit.is_defined()
199
+
200
+ def test_compute_passes(self, states: list[State]):
201
+ passes: list[tuple[int, Pass]] = Orbit.compute_passes(states, 1)
202
+ assert passes is not None
203
+
204
+ def test_compute_passes_with_model(self, orbit: Orbit):
205
+ passes: list[tuple[int, Pass]] = Orbit.compute_passes_with_model(
206
+ model=orbit.access_kepler_model(),
207
+ start_instant=Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC),
208
+ end_instant=Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC),
209
+ initial_revolution_number=1,
210
+ )
211
+
212
+ assert len(passes) > 0
@@ -0,0 +1,452 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ import numpy as np
6
+
7
+ from ostk.mathematics.curve_fitting import Interpolator
8
+ from ostk.mathematics.geometry.d3.object import Cuboid
9
+ from ostk.mathematics.geometry.d3.object import Composite
10
+ from ostk.mathematics.geometry.d3.object import Point
11
+
12
+ from ostk.physics import Environment
13
+ from ostk.physics.unit import Mass
14
+ from ostk.physics.time import Instant
15
+ from ostk.physics.time import DateTime
16
+ from ostk.physics.time import Scale
17
+ from ostk.physics.time import Duration
18
+ from ostk.physics.coordinate import Frame
19
+ from ostk.physics.environment.object.celestial import Earth, Sun
20
+ from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
21
+ from ostk.physics.environment.magnetic import Earth as EarthMagneticModel
22
+ from ostk.physics.environment.atmospheric import Earth as EarthAtmosphericModel
23
+
24
+ from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
25
+ from ostk.astrodynamics.trajectory import LocalOrbitalFrameDirection
26
+ from ostk.astrodynamics.trajectory.state import CoordinateSubset
27
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
28
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
29
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianAcceleration
30
+ from ostk.astrodynamics.trajectory.state import CoordinateBroker
31
+ from ostk.astrodynamics.trajectory.state import NumericalSolver
32
+ from ostk.astrodynamics import Dynamics
33
+ from ostk.astrodynamics.dynamics import Thruster
34
+ from ostk.astrodynamics.dynamics import CentralBodyGravity
35
+ from ostk.astrodynamics.dynamics import PositionDerivative
36
+ from ostk.astrodynamics.dynamics import AtmosphericDrag
37
+ from ostk.astrodynamics.event_condition import InstantCondition
38
+ from ostk.astrodynamics.guidance_law import ConstantThrust
39
+ from ostk.astrodynamics.flight import Maneuver
40
+ from ostk.astrodynamics.flight.system import PropulsionSystem
41
+ from ostk.astrodynamics.flight.system import SatelliteSystem
42
+ from ostk.astrodynamics.trajectory import State
43
+ from ostk.astrodynamics.trajectory import Propagator
44
+
45
+
46
+ @pytest.fixture
47
+ def propulsion_system() -> PropulsionSystem:
48
+ return PropulsionSystem(
49
+ 1.0,
50
+ 150.0,
51
+ )
52
+
53
+
54
+ @pytest.fixture
55
+ def satellite_system(propulsion_system: PropulsionSystem) -> SatelliteSystem:
56
+ mass = Mass(90.0, Mass.Unit.Kilogram)
57
+ satellite_geometry = Composite(
58
+ Cuboid(
59
+ Point(0.0, 0.0, 0.0),
60
+ [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
61
+ [1.0, 0.0, 0.0],
62
+ )
63
+ )
64
+ inertia_tensor = np.identity(3)
65
+ surface_area = 0.8
66
+ drag_coefficient = 0.0
67
+
68
+ return SatelliteSystem(
69
+ mass,
70
+ satellite_geometry,
71
+ inertia_tensor,
72
+ surface_area,
73
+ drag_coefficient,
74
+ propulsion_system,
75
+ )
76
+
77
+
78
+ @pytest.fixture
79
+ def earth() -> Earth:
80
+ return Earth.from_models(
81
+ EarthGravitationalModel(EarthGravitationalModel.Type.EGM96),
82
+ EarthMagneticModel(EarthMagneticModel.Type.Undefined),
83
+ EarthAtmosphericModel(EarthAtmosphericModel.Type.Exponential),
84
+ )
85
+
86
+
87
+ @pytest.fixture
88
+ def environment(earth) -> Environment:
89
+ sun = Sun.default()
90
+
91
+ return Environment(Instant.J2000(), [earth, sun])
92
+
93
+
94
+ @pytest.fixture
95
+ def coordinate_broker_7d():
96
+ return CoordinateBroker(
97
+ [
98
+ CartesianPosition.default(),
99
+ CartesianVelocity.default(),
100
+ CoordinateSubset.mass(),
101
+ ]
102
+ )
103
+
104
+
105
+ @pytest.fixture
106
+ def coordinate_broker_9d():
107
+ return CoordinateBroker(
108
+ [
109
+ CartesianPosition.default(),
110
+ CartesianVelocity.default(),
111
+ CoordinateSubset.mass(),
112
+ CoordinateSubset.surface_area(),
113
+ CoordinateSubset.drag_coefficient(),
114
+ ]
115
+ )
116
+
117
+
118
+ @pytest.fixture
119
+ def state(
120
+ satellite_system: SatelliteSystem, coordinate_broker_7d: CoordinateBroker
121
+ ) -> State:
122
+ instant: Instant = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
123
+
124
+ propellant_mass: float = 10.0
125
+
126
+ coordinates: list = [
127
+ 7500000.0,
128
+ 0.0,
129
+ 0.0,
130
+ 0.0,
131
+ 5335.865450622126,
132
+ 5335.865450622126,
133
+ satellite_system.get_mass().in_kilograms() + propellant_mass,
134
+ ]
135
+
136
+ return State(instant, coordinates, Frame.GCRF(), coordinate_broker_7d)
137
+
138
+
139
+ @pytest.fixture
140
+ def state_low_altitude(
141
+ satellite_system: SatelliteSystem, coordinate_broker_9d: CoordinateBroker
142
+ ) -> State:
143
+ instant: Instant = Instant.date_time(DateTime(2018, 1, 1, 0, 0, 0), Scale.UTC)
144
+
145
+ propellant_mass: float = 10.0
146
+ area: float = satellite_system.get_cross_sectional_surface_area()
147
+ cd: float = satellite_system.get_drag_coefficient()
148
+
149
+ coordinates: list = [
150
+ 7000000.0,
151
+ 0.0,
152
+ 0.0,
153
+ 0.0,
154
+ 5335.865450622126,
155
+ 5335.865450622126,
156
+ satellite_system.get_mass().in_kilograms() + propellant_mass,
157
+ area,
158
+ cd,
159
+ ]
160
+
161
+ return State(instant, coordinates, Frame.GCRF(), coordinate_broker_9d)
162
+
163
+
164
+ @pytest.fixture
165
+ def central_body_gravity() -> CentralBodyGravity:
166
+ return CentralBodyGravity(Earth.WGS84(20, 0))
167
+
168
+
169
+ @pytest.fixture
170
+ def atmospheric_drag(environment, satellite_system) -> AtmosphericDrag:
171
+ return AtmosphericDrag(environment.access_celestial_object_with_name("Earth"))
172
+
173
+
174
+ @pytest.fixture
175
+ def position_derivative() -> PositionDerivative:
176
+ return PositionDerivative()
177
+
178
+
179
+ @pytest.fixture
180
+ def local_orbital_frame_direction() -> LocalOrbitalFrameDirection:
181
+ return LocalOrbitalFrameDirection(
182
+ [1.0, 0.0, 0.0],
183
+ LocalOrbitalFrameFactory.VNC(Frame.GCRF()),
184
+ )
185
+
186
+
187
+ @pytest.fixture
188
+ def thrust_dynamics(
189
+ satellite_system: SatelliteSystem,
190
+ local_orbital_frame_direction: LocalOrbitalFrameDirection,
191
+ ) -> Thruster:
192
+ return Thruster(
193
+ satellite_system=satellite_system,
194
+ guidance_law=ConstantThrust(local_orbital_frame_direction),
195
+ )
196
+
197
+
198
+ @pytest.fixture
199
+ def dynamics(
200
+ position_derivative: PositionDerivative,
201
+ central_body_gravity: CentralBodyGravity,
202
+ ) -> list:
203
+ return [position_derivative, central_body_gravity]
204
+
205
+
206
+ @pytest.fixture
207
+ def numerical_solver() -> NumericalSolver:
208
+ return NumericalSolver(
209
+ NumericalSolver.LogType.NoLog,
210
+ NumericalSolver.StepperType.RungeKuttaFehlberg78,
211
+ 5.0,
212
+ 1.0e-15,
213
+ 1.0e-15,
214
+ )
215
+
216
+
217
+ @pytest.fixture
218
+ def conditional_numerical_solver() -> NumericalSolver:
219
+ return NumericalSolver(
220
+ NumericalSolver.LogType.NoLog,
221
+ NumericalSolver.StepperType.RungeKuttaDopri5,
222
+ 5.0,
223
+ 1.0e-15,
224
+ 1.0e-15,
225
+ )
226
+
227
+
228
+ @pytest.fixture
229
+ def event_condition(state: State) -> InstantCondition:
230
+ return InstantCondition(
231
+ InstantCondition.Criterion.StrictlyPositive,
232
+ state.get_instant() + Duration.seconds(42.0),
233
+ )
234
+
235
+
236
+ @pytest.fixture
237
+ def maneuver_states(frame: Frame) -> list[State]:
238
+ return [
239
+ State(
240
+ Instant.J2000() + Duration.seconds(float(i)),
241
+ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -1e-3],
242
+ frame,
243
+ [
244
+ CartesianPosition.default(),
245
+ CartesianVelocity.default(),
246
+ CartesianAcceleration.thrust_acceleration(),
247
+ CoordinateSubset.mass_flow_rate(),
248
+ ],
249
+ )
250
+ for i in range(0, 100, 10)
251
+ ]
252
+
253
+
254
+ @pytest.fixture
255
+ def maneuver(
256
+ maneuver_states: list[State],
257
+ ) -> Maneuver:
258
+ return Maneuver(states=maneuver_states)
259
+
260
+
261
+ @pytest.fixture
262
+ def propagator(numerical_solver: NumericalSolver, dynamics: list[Dynamics]) -> Propagator:
263
+ return Propagator(numerical_solver, dynamics)
264
+
265
+
266
+ @pytest.fixture
267
+ def propagator_with_maneuvers(
268
+ numerical_solver: NumericalSolver,
269
+ dynamics: list[Dynamics],
270
+ maneuver: Maneuver,
271
+ ) -> Propagator:
272
+ return Propagator(numerical_solver, dynamics, [maneuver], Interpolator.Type.Linear)
273
+
274
+
275
+ class TestPropagator:
276
+ def test_constructors(
277
+ self, propagator: Propagator, propagator_with_maneuvers: Propagator
278
+ ):
279
+ assert propagator is not None
280
+ assert isinstance(propagator, Propagator)
281
+ assert propagator.is_defined()
282
+
283
+ assert propagator_with_maneuvers is not None
284
+ assert isinstance(propagator_with_maneuvers, Propagator)
285
+ assert propagator_with_maneuvers.is_defined()
286
+
287
+ def test_comparators(
288
+ self, propagator: Propagator, propagator_with_maneuvers: Propagator
289
+ ):
290
+ assert propagator == propagator
291
+ assert propagator != propagator_with_maneuvers
292
+
293
+ def test_access_numerical_solver(
294
+ self, propagator: Propagator, numerical_solver: NumericalSolver
295
+ ):
296
+ assert propagator.access_numerical_solver() == numerical_solver
297
+
298
+ def test_get_dynamics(self, propagator: Propagator, dynamics: list):
299
+ assert propagator.get_dynamics() == dynamics
300
+
301
+ def test_set_dynamics(self, propagator: Propagator, dynamics: list):
302
+ assert len(propagator.get_dynamics()) == 2
303
+
304
+ propagator.set_dynamics(dynamics + dynamics)
305
+
306
+ assert len(propagator.get_dynamics()) == 4
307
+
308
+ def test_add_dynamics(
309
+ self, propagator: Propagator, central_body_gravity: CentralBodyGravity
310
+ ):
311
+ assert len(propagator.get_dynamics()) == 2
312
+
313
+ propagator.add_dynamics(central_body_gravity)
314
+ propagator.add_dynamics(central_body_gravity)
315
+
316
+ assert len(propagator.get_dynamics()) == 4
317
+
318
+ def test_clear_dynamics(self, propagator: Propagator):
319
+ assert len(propagator.get_dynamics()) >= 1
320
+
321
+ propagator.clear_dynamics()
322
+
323
+ assert len(propagator.get_dynamics()) == 0
324
+
325
+ def test_add_maneuver(
326
+ self,
327
+ propagator: Propagator,
328
+ maneuver: Maneuver,
329
+ ):
330
+ assert len(propagator.get_dynamics()) == 2
331
+
332
+ propagator.add_maneuver(maneuver)
333
+
334
+ assert len(propagator.get_dynamics()) == 3
335
+
336
+ def test_calculate_state_at(self, propagator: Propagator, state: State):
337
+ instant: Instant = Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC)
338
+
339
+ propagator_state = propagator.calculate_state_at(state, instant)
340
+
341
+ propagator_state_position_ref = np.array(
342
+ [6265892.25765909, 3024770.94961259, 3024359.72137468]
343
+ )
344
+ propagator_state_velocity_ref = np.array(
345
+ [-3974.49168221, 4468.16996776, 4466.19232746]
346
+ )
347
+
348
+ propagator_state_position = propagator_state.get_position().get_coordinates()
349
+ propagator_state_velocity = propagator_state.get_velocity().get_coordinates()
350
+
351
+ assert all(
352
+ [
353
+ round(propagator_state_position[i], 8)
354
+ == round(propagator_state_position_ref[i], 8)
355
+ for i in range(0, len(propagator_state_position_ref))
356
+ ]
357
+ )
358
+ assert all(
359
+ [
360
+ round(propagator_state_velocity[i], 8)
361
+ == round(propagator_state_velocity_ref[i], 8)
362
+ for i in range(0, len(propagator_state_velocity_ref))
363
+ ]
364
+ )
365
+ assert propagator_state.get_instant() == instant
366
+
367
+ def test_calculate_state_to_condition(
368
+ self,
369
+ conditional_numerical_solver: NumericalSolver,
370
+ dynamics: list[Dynamics],
371
+ state: State,
372
+ event_condition: InstantCondition,
373
+ ):
374
+ propagator: Propagator = Propagator(conditional_numerical_solver, dynamics)
375
+
376
+ instant: Instant = Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC)
377
+
378
+ solution = propagator.calculate_state_to_condition(
379
+ state=state,
380
+ instant=instant,
381
+ event_condition=event_condition,
382
+ )
383
+
384
+ assert solution.condition_is_satisfied
385
+ assert pytest.approx(42.0, abs=1e-3) == float(
386
+ (solution.state.get_instant() - state.get_instant()).in_seconds()
387
+ )
388
+
389
+ def test_calculate_states_at(self, propagator: Propagator, state: State):
390
+ instant_array = [
391
+ Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC),
392
+ Instant.date_time(DateTime(2018, 1, 1, 0, 20, 0), Scale.UTC),
393
+ ]
394
+
395
+ _ = propagator.calculate_states_at(state, instant_array)
396
+
397
+ with pytest.raises(RuntimeError):
398
+ instant_array.reverse()
399
+ propagator.calculate_states_at(state, instant_array)
400
+
401
+ def test_calculate_states_at_with_drag(
402
+ self,
403
+ numerical_solver: NumericalSolver,
404
+ dynamics: list[Dynamics],
405
+ atmospheric_drag: AtmosphericDrag,
406
+ state_low_altitude: State,
407
+ ):
408
+ propagator: Propagator = Propagator(
409
+ numerical_solver,
410
+ dynamics + [atmospheric_drag],
411
+ )
412
+
413
+ instant_array = [
414
+ Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC),
415
+ Instant.date_time(DateTime(2018, 1, 1, 0, 20, 0), Scale.UTC),
416
+ Instant.date_time(DateTime(2018, 1, 1, 0, 30, 0), Scale.UTC),
417
+ Instant.date_time(DateTime(2018, 1, 1, 0, 40, 0), Scale.UTC),
418
+ ]
419
+
420
+ _ = propagator.calculate_states_at(state_low_altitude, instant_array)
421
+
422
+ def test_calculate_states_at_with_thrust(
423
+ self,
424
+ numerical_solver: NumericalSolver,
425
+ dynamics: list[Dynamics],
426
+ thrust_dynamics: Thruster,
427
+ state: State,
428
+ ):
429
+ propagator: Propagator = Propagator(
430
+ numerical_solver,
431
+ dynamics + [thrust_dynamics],
432
+ )
433
+
434
+ instant_array = [
435
+ Instant.date_time(DateTime(2018, 1, 1, 0, 10, 0), Scale.UTC),
436
+ Instant.date_time(DateTime(2018, 1, 1, 0, 20, 0), Scale.UTC),
437
+ Instant.date_time(DateTime(2018, 1, 1, 0, 30, 0), Scale.UTC),
438
+ Instant.date_time(DateTime(2018, 1, 1, 0, 40, 0), Scale.UTC),
439
+ ]
440
+
441
+ _ = propagator.calculate_states_at(state, instant_array)
442
+
443
+ def test_from_environment(
444
+ self,
445
+ numerical_solver: NumericalSolver,
446
+ environment: Environment,
447
+ ):
448
+ assert Propagator.from_environment(numerical_solver, environment) is not None
449
+
450
+ def test_default(self, environment: Environment):
451
+ assert Propagator.default()
452
+ assert Propagator.default(environment) is not None