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