open-space-toolkit-astrodynamics 16.8.0__py313-none-manylinux2014_aarch64.whl → 16.10.0__py313-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.
- {open_space_toolkit_astrodynamics-16.8.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/METADATA +1 -1
- {open_space_toolkit_astrodynamics-16.8.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/RECORD +22 -17
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-313-aarch64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.pyi +2 -2
- ostk/astrodynamics/conjunction/__init__.pyi +119 -1
- ostk/astrodynamics/conjunction/close_approach.pyi +85 -0
- ostk/astrodynamics/dynamics.pyi +1 -1
- ostk/astrodynamics/flight/__init__.pyi +29 -1
- ostk/astrodynamics/guidance_law.pyi +82 -7
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.16 +0 -0
- ostk/astrodynamics/test/conjunction/close_approach/__init__.py +0 -0
- ostk/astrodynamics/test/conjunction/close_approach/test_generator.py +228 -0
- ostk/astrodynamics/test/conjunction/test_close_approach.py +244 -0
- ostk/astrodynamics/test/dynamics/test_thruster.py +21 -6
- ostk/astrodynamics/test/flight/test_maneuver.py +37 -3
- ostk/astrodynamics/test/guidance_law/test_constant_thrust.py +87 -1
- ostk/astrodynamics/test/guidance_law/test_heterogeneous_guidance_law.py +164 -0
- ostk/astrodynamics/test/trajectory/test_segment.py +107 -2
- ostk/astrodynamics/trajectory/__init__.pyi +24 -0
- {open_space_toolkit_astrodynamics-16.8.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/WHEEL +0 -0
- {open_space_toolkit_astrodynamics-16.8.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-16.8.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/zip-safe +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from ostk.physics.coordinate import Frame
|
|
8
|
+
from ostk.physics.time import Instant
|
|
9
|
+
from ostk.physics.time import Duration
|
|
10
|
+
from ostk.physics.time import Interval
|
|
11
|
+
from ostk.astrodynamics import GuidanceLaw
|
|
12
|
+
from ostk.astrodynamics.guidance_law import HeterogeneousGuidanceLaw
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def interval_1() -> Interval:
|
|
17
|
+
return Interval.closed(
|
|
18
|
+
start_instant=Instant.J2000(),
|
|
19
|
+
end_instant=Instant.J2000() + Duration.seconds(100.0),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def guidance_law_1() -> GuidanceLaw:
|
|
25
|
+
class GuidanceLaw1(GuidanceLaw):
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
name: str,
|
|
30
|
+
):
|
|
31
|
+
super().__init__(name)
|
|
32
|
+
|
|
33
|
+
def calculate_thrust_acceleration_at(
|
|
34
|
+
self,
|
|
35
|
+
instant: Instant,
|
|
36
|
+
position_coordinates: np.array,
|
|
37
|
+
velocity_coordinates: np.array,
|
|
38
|
+
thrust_acceleration: float,
|
|
39
|
+
output_frame: Frame,
|
|
40
|
+
) -> np.array:
|
|
41
|
+
return np.array([1.0, 2.0, 3.0])
|
|
42
|
+
|
|
43
|
+
return GuidanceLaw1("My Guidance Law 1")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.fixture
|
|
47
|
+
def interval_2() -> Interval:
|
|
48
|
+
return Interval.closed(
|
|
49
|
+
start_instant=Instant.J2000() + Duration.seconds(200.0),
|
|
50
|
+
end_instant=Instant.J2000() + Duration.seconds(300.0),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@pytest.fixture
|
|
55
|
+
def guidance_law_2() -> GuidanceLaw:
|
|
56
|
+
class GuidanceLaw2(GuidanceLaw):
|
|
57
|
+
|
|
58
|
+
def __init__(
|
|
59
|
+
self,
|
|
60
|
+
name: str,
|
|
61
|
+
):
|
|
62
|
+
super().__init__(name)
|
|
63
|
+
|
|
64
|
+
def calculate_thrust_acceleration_at(
|
|
65
|
+
self,
|
|
66
|
+
instant: Instant,
|
|
67
|
+
position_coordinates: np.array,
|
|
68
|
+
velocity_coordinates: np.array,
|
|
69
|
+
thrust_acceleration: float,
|
|
70
|
+
output_frame: Frame,
|
|
71
|
+
) -> np.array:
|
|
72
|
+
return np.array([4.0, 5.0, 6.0])
|
|
73
|
+
|
|
74
|
+
return GuidanceLaw2("My Guidance Law 2")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.fixture
|
|
78
|
+
def heterogeneous_guidance_law(
|
|
79
|
+
interval_1: Interval,
|
|
80
|
+
guidance_law_1: GuidanceLaw,
|
|
81
|
+
interval_2: Interval,
|
|
82
|
+
guidance_law_2: GuidanceLaw,
|
|
83
|
+
) -> HeterogeneousGuidanceLaw:
|
|
84
|
+
return HeterogeneousGuidanceLaw(
|
|
85
|
+
guidance_laws_with_intervals=[
|
|
86
|
+
(guidance_law_1, interval_1),
|
|
87
|
+
(guidance_law_2, interval_2),
|
|
88
|
+
],
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TestHeterogeneousGuidanceLaw:
|
|
93
|
+
def test_constructor_and_getters(
|
|
94
|
+
self,
|
|
95
|
+
interval_1: Interval,
|
|
96
|
+
guidance_law_1: GuidanceLaw,
|
|
97
|
+
interval_2: Interval,
|
|
98
|
+
guidance_law_2: GuidanceLaw,
|
|
99
|
+
):
|
|
100
|
+
heterogeneous_guidance_law = HeterogeneousGuidanceLaw(
|
|
101
|
+
guidance_laws_with_intervals=[
|
|
102
|
+
(guidance_law_1, interval_1),
|
|
103
|
+
(guidance_law_2, interval_2),
|
|
104
|
+
],
|
|
105
|
+
)
|
|
106
|
+
assert heterogeneous_guidance_law is not None
|
|
107
|
+
assert isinstance(heterogeneous_guidance_law, HeterogeneousGuidanceLaw)
|
|
108
|
+
assert heterogeneous_guidance_law.get_guidance_laws_with_intervals() == [
|
|
109
|
+
(guidance_law_1, interval_1),
|
|
110
|
+
(guidance_law_2, interval_2),
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
def test_add_guidance_law(
|
|
114
|
+
self,
|
|
115
|
+
interval_1: Interval,
|
|
116
|
+
guidance_law_1: GuidanceLaw,
|
|
117
|
+
interval_2: Interval,
|
|
118
|
+
guidance_law_2: GuidanceLaw,
|
|
119
|
+
):
|
|
120
|
+
heterogeneous_guidance_law = HeterogeneousGuidanceLaw()
|
|
121
|
+
|
|
122
|
+
assert isinstance(heterogeneous_guidance_law, HeterogeneousGuidanceLaw)
|
|
123
|
+
assert heterogeneous_guidance_law.get_guidance_laws_with_intervals() == []
|
|
124
|
+
|
|
125
|
+
heterogeneous_guidance_law.add_guidance_law(guidance_law_1, interval_1)
|
|
126
|
+
assert heterogeneous_guidance_law.get_guidance_laws_with_intervals() == [
|
|
127
|
+
(guidance_law_1, interval_1),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
heterogeneous_guidance_law.add_guidance_law(guidance_law_2, interval_2)
|
|
131
|
+
assert heterogeneous_guidance_law.get_guidance_laws_with_intervals() == [
|
|
132
|
+
(guidance_law_1, interval_1),
|
|
133
|
+
(guidance_law_2, interval_2),
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
@pytest.mark.parametrize(
|
|
137
|
+
(
|
|
138
|
+
"instant",
|
|
139
|
+
"expected_thrust_acceleration",
|
|
140
|
+
),
|
|
141
|
+
[
|
|
142
|
+
(Instant.J2000() - Duration.seconds(50.0), np.array([0.0, 0.0, 0.0])),
|
|
143
|
+
(Instant.J2000() + Duration.seconds(50.0), np.array([1.0, 2.0, 3.0])),
|
|
144
|
+
(Instant.J2000() + Duration.seconds(150.0), np.array([0.0, 0.0, 0.0])),
|
|
145
|
+
(Instant.J2000() + Duration.seconds(250.0), np.array([4.0, 5.0, 6.0])),
|
|
146
|
+
(Instant.J2000() + Duration.seconds(350.0), np.array([0.0, 0.0, 0.0])),
|
|
147
|
+
],
|
|
148
|
+
)
|
|
149
|
+
def test_calculate_thrust_acceleration_at(
|
|
150
|
+
self,
|
|
151
|
+
heterogeneous_guidance_law: HeterogeneousGuidanceLaw,
|
|
152
|
+
instant: Instant,
|
|
153
|
+
expected_thrust_acceleration: np.array,
|
|
154
|
+
):
|
|
155
|
+
assert np.array_equal(
|
|
156
|
+
heterogeneous_guidance_law.calculate_thrust_acceleration_at(
|
|
157
|
+
instant=instant,
|
|
158
|
+
position_coordinates=np.array([0.0, 0.0, 0.0]),
|
|
159
|
+
velocity_coordinates=np.array([0.0, 0.0, 0.0]),
|
|
160
|
+
thrust_acceleration=1.0,
|
|
161
|
+
output_frame=Frame.GCRF(),
|
|
162
|
+
),
|
|
163
|
+
expected_thrust_acceleration,
|
|
164
|
+
)
|
|
@@ -10,6 +10,7 @@ from ostk.physics.time import Scale
|
|
|
10
10
|
from ostk.physics.time import Duration
|
|
11
11
|
from ostk.physics.coordinate import Frame
|
|
12
12
|
from ostk.physics.environment.object.celestial import Earth
|
|
13
|
+
from ostk.physics.unit import Angle
|
|
13
14
|
|
|
14
15
|
from ostk.astrodynamics.flight.system import SatelliteSystem
|
|
15
16
|
from ostk.astrodynamics import Dynamics
|
|
@@ -17,6 +18,7 @@ from ostk.astrodynamics.dynamics import CentralBodyGravity
|
|
|
17
18
|
from ostk.astrodynamics.dynamics import PositionDerivative
|
|
18
19
|
from ostk.astrodynamics.dynamics import Thruster
|
|
19
20
|
from ostk.astrodynamics.guidance_law import ConstantThrust
|
|
21
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
|
|
20
22
|
from ostk.astrodynamics.trajectory import State
|
|
21
23
|
from ostk.astrodynamics.trajectory import Segment
|
|
22
24
|
from ostk.astrodynamics.event_condition import InstantCondition
|
|
@@ -75,6 +77,11 @@ def dynamics(
|
|
|
75
77
|
]
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
@pytest.fixture
|
|
81
|
+
def local_orbital_frame_factory() -> LocalOrbitalFrameFactory:
|
|
82
|
+
return LocalOrbitalFrameFactory.VNC(Frame.GCRF())
|
|
83
|
+
|
|
84
|
+
|
|
78
85
|
@pytest.fixture
|
|
79
86
|
def numerical_solver() -> NumericalSolver:
|
|
80
87
|
return NumericalSolver.default_conditional()
|
|
@@ -122,6 +129,51 @@ def maneuver_segment(
|
|
|
122
129
|
)
|
|
123
130
|
|
|
124
131
|
|
|
132
|
+
@pytest.fixture
|
|
133
|
+
def maximum_allowed_angular_offset() -> Angle:
|
|
134
|
+
return Angle.degrees(180.0)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@pytest.fixture
|
|
138
|
+
def constant_local_orbital_frame_direction_maneuver_segment(
|
|
139
|
+
name: str,
|
|
140
|
+
instant_condition: InstantCondition,
|
|
141
|
+
thruster_dynamics: Thruster,
|
|
142
|
+
dynamics: list,
|
|
143
|
+
numerical_solver: NumericalSolver,
|
|
144
|
+
local_orbital_frame_factory: LocalOrbitalFrameFactory,
|
|
145
|
+
) -> Segment:
|
|
146
|
+
return Segment.constant_local_orbital_frame_direction_maneuver(
|
|
147
|
+
name=name,
|
|
148
|
+
event_condition=instant_condition,
|
|
149
|
+
thruster_dynamics=thruster_dynamics,
|
|
150
|
+
dynamics=dynamics,
|
|
151
|
+
numerical_solver=numerical_solver,
|
|
152
|
+
local_orbital_frame_factory=local_orbital_frame_factory,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@pytest.fixture
|
|
157
|
+
def constant_local_orbital_frame_direction_maneuver_segment_with_maximum_allowed_angular_offset(
|
|
158
|
+
name: str,
|
|
159
|
+
instant_condition: InstantCondition,
|
|
160
|
+
thruster_dynamics: Thruster,
|
|
161
|
+
dynamics: list,
|
|
162
|
+
numerical_solver: NumericalSolver,
|
|
163
|
+
local_orbital_frame_factory: LocalOrbitalFrameFactory,
|
|
164
|
+
maximum_allowed_angular_offset: Angle,
|
|
165
|
+
) -> Segment:
|
|
166
|
+
return Segment.constant_local_orbital_frame_direction_maneuver(
|
|
167
|
+
name=name,
|
|
168
|
+
event_condition=instant_condition,
|
|
169
|
+
thruster_dynamics=thruster_dynamics,
|
|
170
|
+
dynamics=dynamics,
|
|
171
|
+
numerical_solver=numerical_solver,
|
|
172
|
+
local_orbital_frame_factory=local_orbital_frame_factory,
|
|
173
|
+
maximum_allowed_angular_offset=maximum_allowed_angular_offset,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
125
177
|
@pytest.fixture
|
|
126
178
|
def instants(state: State) -> list[Instant]:
|
|
127
179
|
return [state.get_instant(), state.get_instant() + Duration.minutes(1.0)]
|
|
@@ -293,6 +345,41 @@ class TestSegment:
|
|
|
293
345
|
is not None
|
|
294
346
|
)
|
|
295
347
|
|
|
348
|
+
def test_constant_local_orbital_frame_direction_maneuver(
|
|
349
|
+
self,
|
|
350
|
+
name: str,
|
|
351
|
+
instant_condition: InstantCondition,
|
|
352
|
+
thruster_dynamics: ConstantThrust,
|
|
353
|
+
dynamics: list,
|
|
354
|
+
numerical_solver: NumericalSolver,
|
|
355
|
+
local_orbital_frame_factory: LocalOrbitalFrameFactory,
|
|
356
|
+
maximum_allowed_angular_offset: Angle,
|
|
357
|
+
):
|
|
358
|
+
assert (
|
|
359
|
+
Segment.constant_local_orbital_frame_direction_maneuver(
|
|
360
|
+
name=name,
|
|
361
|
+
event_condition=instant_condition,
|
|
362
|
+
thruster_dynamics=thruster_dynamics,
|
|
363
|
+
dynamics=dynamics,
|
|
364
|
+
numerical_solver=numerical_solver,
|
|
365
|
+
local_orbital_frame_factory=local_orbital_frame_factory,
|
|
366
|
+
)
|
|
367
|
+
is not None
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
assert (
|
|
371
|
+
Segment.constant_local_orbital_frame_direction_maneuver(
|
|
372
|
+
name=name,
|
|
373
|
+
event_condition=instant_condition,
|
|
374
|
+
thruster_dynamics=thruster_dynamics,
|
|
375
|
+
dynamics=dynamics,
|
|
376
|
+
numerical_solver=numerical_solver,
|
|
377
|
+
local_orbital_frame_factory=local_orbital_frame_factory,
|
|
378
|
+
maximum_allowed_angular_offset=maximum_allowed_angular_offset,
|
|
379
|
+
)
|
|
380
|
+
is not None
|
|
381
|
+
)
|
|
382
|
+
|
|
296
383
|
def test_create_solution(
|
|
297
384
|
self,
|
|
298
385
|
dynamics: list,
|
|
@@ -310,7 +397,7 @@ class TestSegment:
|
|
|
310
397
|
|
|
311
398
|
assert segment_solution is not None
|
|
312
399
|
|
|
313
|
-
def
|
|
400
|
+
def test_solve_maneuver_segment(
|
|
314
401
|
self,
|
|
315
402
|
state: State,
|
|
316
403
|
end_instant: Instant,
|
|
@@ -318,7 +405,7 @@ class TestSegment:
|
|
|
318
405
|
instants: list[Instant],
|
|
319
406
|
numerical_solver: NumericalSolver,
|
|
320
407
|
):
|
|
321
|
-
solution = maneuver_segment.solve(state)
|
|
408
|
+
solution: Segment.Solution = maneuver_segment.solve(state)
|
|
322
409
|
|
|
323
410
|
assert (
|
|
324
411
|
pytest.approx(
|
|
@@ -400,3 +487,21 @@ class TestSegment:
|
|
|
400
487
|
solution.get_dynamics_acceleration_contribution(
|
|
401
488
|
solution.dynamics[0], state.get_frame()
|
|
402
489
|
)
|
|
490
|
+
|
|
491
|
+
def test_solve_constant_local_orbital_frame_direction_maneuver_segment(
|
|
492
|
+
self,
|
|
493
|
+
state: State,
|
|
494
|
+
constant_local_orbital_frame_direction_maneuver_segment: Segment,
|
|
495
|
+
constant_local_orbital_frame_direction_maneuver_segment_with_maximum_allowed_angular_offset: Segment,
|
|
496
|
+
):
|
|
497
|
+
solution: Segment.Solution = (
|
|
498
|
+
constant_local_orbital_frame_direction_maneuver_segment.solve(state)
|
|
499
|
+
)
|
|
500
|
+
assert solution is not None
|
|
501
|
+
|
|
502
|
+
solution_with_maximum_allowed_angular_offset: Segment.Solution = (
|
|
503
|
+
constant_local_orbital_frame_direction_maneuver_segment_with_maximum_allowed_angular_offset.solve(
|
|
504
|
+
state
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
assert solution_with_maximum_allowed_angular_offset is not None
|
|
@@ -1094,6 +1094,30 @@ class Segment:
|
|
|
1094
1094
|
Segment: The coast segment.
|
|
1095
1095
|
"""
|
|
1096
1096
|
@staticmethod
|
|
1097
|
+
def constant_local_orbital_frame_direction_maneuver(name: ostk.core.type.String, event_condition: typing.Any, thruster_dynamics: typing.Any, dynamics: list[...], numerical_solver: state.NumericalSolver, local_orbital_frame_factory: LocalOrbitalFrameFactory, maximum_allowed_angular_offset: ostk.physics.unit.Angle = ...) -> Segment:
|
|
1098
|
+
"""
|
|
1099
|
+
Create a maneuvering segment that produces maneuvers with a constant direction in the local orbital frame.
|
|
1100
|
+
|
|
1101
|
+
The provided thruster dynamics are used to solve the segment at first. The maneuvers produced by this segement solution
|
|
1102
|
+
are then used to create a new thruster dynamics with a constant direction in the local orbital frame. This new thruster dynamics
|
|
1103
|
+
is then used to actually solve the segment.
|
|
1104
|
+
|
|
1105
|
+
If defined, a runtime error will be thrown if the maximum allowed angular offset between the original thruster dynamics
|
|
1106
|
+
and the mean thrust direction is violated.
|
|
1107
|
+
|
|
1108
|
+
Args:
|
|
1109
|
+
name (str): The name of the segment.
|
|
1110
|
+
event_condition (EventCondition): The event condition.
|
|
1111
|
+
thruster_dynamics (ThrusterDynamics): The thruster dynamics.
|
|
1112
|
+
dynamics (Dynamics): The dynamics.
|
|
1113
|
+
numerical_solver (NumericalSolver): The numerical solver.
|
|
1114
|
+
local_orbital_frame_factory (LocalOrbitalFrameFactory): The local orbital frame factory.
|
|
1115
|
+
maximum_allowed_angular_offset (Angle, optional): The maximum allowed angular offset to consider (if any). Defaults to Angle.undefined().
|
|
1116
|
+
|
|
1117
|
+
Returns:
|
|
1118
|
+
Segment: The maneuver segment.
|
|
1119
|
+
"""
|
|
1120
|
+
@staticmethod
|
|
1097
1121
|
def maneuver(name: ostk.core.type.String, event_condition: typing.Any, thruster_dynamics: typing.Any, dynamics: list[...], numerical_solver: state.NumericalSolver) -> Segment:
|
|
1098
1122
|
"""
|
|
1099
1123
|
Create a maneuver segment.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|