open-space-toolkit-astrodynamics 16.9.0__py312-none-manylinux2014_aarch64.whl → 16.10.0__py312-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.9.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/METADATA +1 -1
- {open_space_toolkit_astrodynamics-16.9.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/RECORD +13 -9
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-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/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
- {open_space_toolkit_astrodynamics-16.9.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/WHEEL +0 -0
- {open_space_toolkit_astrodynamics-16.9.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-16.9.0.dist-info → open_space_toolkit_astrodynamics-16.10.0.dist-info}/zip-safe +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
ostk/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
|
|
2
|
-
ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-aarch64-linux-gnu.so,sha256=
|
|
2
|
+
ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-aarch64-linux-gnu.so,sha256=nx0es1voUWKETyxI32XSQXFvjfFucNhtIvojy2k1T7E,2746776
|
|
3
3
|
ostk/astrodynamics/__init__.py,sha256=3gWyqFIbhAfcdeMhmfBPQPlPQTmaOzm-6flkJe745Zk,251
|
|
4
|
-
ostk/astrodynamics/__init__.pyi,sha256=
|
|
4
|
+
ostk/astrodynamics/__init__.pyi,sha256=4hmkrBmp4i1NvZ_4VYbiTwMNEhAe99dzDdWmxfCk9TQ,32224
|
|
5
5
|
ostk/astrodynamics/access.pyi,sha256=pp2t81kKH4sP4BcGb8nAZHmpwhISjFODMpbZ4ucLFAw,25157
|
|
6
6
|
ostk/astrodynamics/converters.py,sha256=luPh30qMp9bzEkN7hUccmxlLf7zRp_AzqmBe8IUjPhU,3314
|
|
7
7
|
ostk/astrodynamics/converters.pyi,sha256=HrZFyizkc6Hv_K38ZKZ80fX_bAxd6keA_NFWNQygvbs,1745
|
|
@@ -12,11 +12,12 @@ ostk/astrodynamics/eclipse.pyi,sha256=Q8fzdukA7fyxbUM3wjEXAHukT-Ovl4FM00NbjsMPnU
|
|
|
12
12
|
ostk/astrodynamics/estimator.pyi,sha256=MnahWzp8aACxrNKWlYRsgQr5zpBxogNr-yPm7hJob5k,14000
|
|
13
13
|
ostk/astrodynamics/event_condition.pyi,sha256=2c_1Sq7tkYKeAA_aRWBi43KDQXXxu6EMSmUpEWz_Fa4,45814
|
|
14
14
|
ostk/astrodynamics/guidance_law.pyi,sha256=Xtqneu5PLpidOoSH4YIAY3D8rWZ0QH_ESqEdfbiTBVQ,16936
|
|
15
|
-
ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.16,sha256=
|
|
15
|
+
ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.16,sha256=1nMVpUhldGatpn-sRCHcYhF6fQ8P4hwU9DXAmH3jkvQ,4078240
|
|
16
16
|
ostk/astrodynamics/solver.pyi,sha256=dRdR9RLqwSaBBX_ZRmpeBv06WvM_BFNo3WUdNzorYTY,17805
|
|
17
17
|
ostk/astrodynamics/utilities.py,sha256=y8mr3M46J5z-GhS1oIEnuEJA6otwcyJ9YDhvn_5JxHM,6976
|
|
18
18
|
ostk/astrodynamics/viewer.py,sha256=SlKyOWKjaF3V9HFB3I7ZgHy7n_GLeHTWM9q2wXkpxe8,27077
|
|
19
|
-
ostk/astrodynamics/conjunction/__init__.pyi,sha256=
|
|
19
|
+
ostk/astrodynamics/conjunction/__init__.pyi,sha256=TEfTmgCRKr68B1GPJscDOgI130jQf7GLbtoKZPtx_SU,4918
|
|
20
|
+
ostk/astrodynamics/conjunction/close_approach.pyi,sha256=lDGzezf8ELdReQ0TGaqW36gpS9TwBN9QiCNrMi9X8v0,3690
|
|
20
21
|
ostk/astrodynamics/conjunction/message/__init__.pyi,sha256=5H__sg_QUx7ybf9jtVWvXzrUHeK3ECotfhddAdHjJUc,75
|
|
21
22
|
ostk/astrodynamics/conjunction/message/ccsds.pyi,sha256=1Peto19hRqlD7KHf1cyLP3CT4OAKzwtemqvO6_4FZ0g,28162
|
|
22
23
|
ostk/astrodynamics/data/__init__.pyi,sha256=4l_mfVbnU_L7wImwgTCe8fVI81gK_tUmd0z7BY9lLi8,81
|
|
@@ -44,6 +45,9 @@ ostk/astrodynamics/test/test_viewer.py,sha256=6IxHjSrwnLkmLiTTzRcnwAdEeWdIF_b2Kj
|
|
|
44
45
|
ostk/astrodynamics/test/access/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
|
|
45
46
|
ostk/astrodynamics/test/access/test_generator.py,sha256=i7TnM80kF0Q_9KmyoqKt5n1ufg3ZjAIEMPVVds8ZDdI,10315
|
|
46
47
|
ostk/astrodynamics/test/access/test_visibility_criterion.py,sha256=VA6WDQTj3q-f2YGIIkrrNp8G23Nf_0g9nKmfZAgAlWQ,6568
|
|
48
|
+
ostk/astrodynamics/test/conjunction/test_close_approach.py,sha256=vuvVNj2xG8MhM-N5zsAtH7kFciM0V1KMWDd_vNvkCMg,6864
|
|
49
|
+
ostk/astrodynamics/test/conjunction/close_approach/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
50
|
+
ostk/astrodynamics/test/conjunction/close_approach/test_generator.py,sha256=NEDVdhL_Ll2iTo8dCBFuLdE1soFC0FUlx-tuuA8tGIg,6536
|
|
47
51
|
ostk/astrodynamics/test/conjunction/message/ccsds/__init__.py,sha256=epnVn2PwdQkUDZ1msqBRO5nEZIOUBIq-IfK3IlNPijE,21
|
|
48
52
|
ostk/astrodynamics/test/conjunction/message/ccsds/conftest.py,sha256=xyTd24828Ue2HgOWY8UhizIc5mzgRgoH2VSOfVfKCdw,14632
|
|
49
53
|
ostk/astrodynamics/test/conjunction/message/ccsds/test_cdm.py,sha256=ztpnOHI3ZiIV0QwijJ6Si_lIna8jLZF45r1KtPVK4ws,18514
|
|
@@ -140,8 +144,8 @@ ostk/astrodynamics/trajectory/orbit/model/kepler.pyi,sha256=KL-FwPbG4OUkPjhQb1Pa
|
|
|
140
144
|
ostk/astrodynamics/trajectory/orbit/model/sgp4.pyi,sha256=OhFzoPPQHlYy7m3LiZ8TXF89M4uBTfNk6tGsBEp0sjI,14235
|
|
141
145
|
ostk/astrodynamics/trajectory/state/__init__.pyi,sha256=bq__Fii35czVrTeNxc9eQVjXdqwbbQxUdNQWK3vLrMo,17649
|
|
142
146
|
ostk/astrodynamics/trajectory/state/coordinate_subset.pyi,sha256=kYMfMwEjCqO1NepMYFT4QS6kIPBkVL6sGCLeLbogcMw,10176
|
|
143
|
-
open_space_toolkit_astrodynamics-16.
|
|
144
|
-
open_space_toolkit_astrodynamics-16.
|
|
145
|
-
open_space_toolkit_astrodynamics-16.
|
|
146
|
-
open_space_toolkit_astrodynamics-16.
|
|
147
|
-
open_space_toolkit_astrodynamics-16.
|
|
147
|
+
open_space_toolkit_astrodynamics-16.10.0.dist-info/METADATA,sha256=VRCTKInuQXRzjAIDnrl4EOduGokQYU72xGWdlk--r1I,1914
|
|
148
|
+
open_space_toolkit_astrodynamics-16.10.0.dist-info/WHEEL,sha256=R5pNgnt8b9fXaDTmvusPEx11BdzBXjt30_JyyExPv08,111
|
|
149
|
+
open_space_toolkit_astrodynamics-16.10.0.dist-info/top_level.txt,sha256=zOR18699uDYnafgarhL8WU_LmTZY_5NVqutv-flp_x4,5
|
|
150
|
+
open_space_toolkit_astrodynamics-16.10.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
151
|
+
open_space_toolkit_astrodynamics-16.10.0.dist-info/RECORD,,
|
|
Binary file
|
ostk/astrodynamics/__init__.pyi
CHANGED
|
@@ -14,8 +14,8 @@ from ostk import mathematics as OpenSpaceToolkitMathematicsPy
|
|
|
14
14
|
from ostk.mathematics import curve_fitting
|
|
15
15
|
from ostk.mathematics import geometry
|
|
16
16
|
from ostk.mathematics import object
|
|
17
|
-
import ostk.physics
|
|
18
17
|
from ostk import physics as OpenSpaceToolkitPhysicsPy
|
|
18
|
+
import ostk.physics
|
|
19
19
|
from ostk.physics import Environment
|
|
20
20
|
from ostk.physics import Manager
|
|
21
21
|
from ostk.physics import Unit
|
|
@@ -26,8 +26,8 @@ from ostk.physics import environment
|
|
|
26
26
|
import ostk.physics.environment.object
|
|
27
27
|
import ostk.physics.time
|
|
28
28
|
from ostk.physics import time
|
|
29
|
-
import ostk.physics.unit
|
|
30
29
|
from ostk.physics import unit
|
|
30
|
+
import ostk.physics.unit
|
|
31
31
|
import typing
|
|
32
32
|
from . import access
|
|
33
33
|
from . import conjunction
|
|
@@ -1,3 +1,121 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
import ostk.astrodynamics.trajectory
|
|
3
|
+
import ostk.physics.coordinate
|
|
4
|
+
import ostk.physics.time
|
|
5
|
+
import ostk.physics.unit
|
|
6
|
+
import typing
|
|
7
|
+
from . import close_approach
|
|
2
8
|
from . import message
|
|
3
|
-
__all__ = ['message']
|
|
9
|
+
__all__ = ['CloseApproach', 'close_approach', 'message']
|
|
10
|
+
class CloseApproach:
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
Close approach between two objects.
|
|
14
|
+
|
|
15
|
+
This class represents a close approach event between two objects, providing access to the states of both
|
|
16
|
+
objects at the time of closest approach, the miss distance, and the relative state.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
__hash__: typing.ClassVar[None] = None
|
|
20
|
+
@staticmethod
|
|
21
|
+
def undefined() -> CloseApproach:
|
|
22
|
+
"""
|
|
23
|
+
Construct an undefined close approach.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
CloseApproach: An undefined close approach.
|
|
27
|
+
"""
|
|
28
|
+
def __eq__(self, arg0: CloseApproach) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Equal to operator.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
other (CloseApproach): Another close approach.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
bool: True if close approaches are equal.
|
|
37
|
+
"""
|
|
38
|
+
def __init__(self, object_1_state: ostk.astrodynamics.trajectory.State, object_2_state: ostk.astrodynamics.trajectory.State) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Constructor.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
object_1_state (State): The state of Object 1.
|
|
44
|
+
object_2_state (State): The state of Object 2.
|
|
45
|
+
"""
|
|
46
|
+
def __ne__(self, arg0: CloseApproach) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Not equal to operator.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
other (CloseApproach): Another close approach.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
bool: True if close approaches are not equal.
|
|
55
|
+
"""
|
|
56
|
+
def __repr__(self) -> str:
|
|
57
|
+
...
|
|
58
|
+
def __str__(self) -> str:
|
|
59
|
+
...
|
|
60
|
+
def compute_miss_distance_components_in_frame(self, frame: ostk.physics.coordinate.Frame) -> tuple[ostk.physics.unit.Length, ostk.physics.unit.Length, ostk.physics.unit.Length]:
|
|
61
|
+
"""
|
|
62
|
+
Compute the miss distance components in the desired frame.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
frame (Frame): The frame in which to resolve the miss distance components.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
tuple[Length, Length, Length]: The miss distance components (x, y, z).
|
|
69
|
+
"""
|
|
70
|
+
def compute_miss_distance_components_in_local_orbital_frame(self, local_orbital_frame_factory: ostk.astrodynamics.trajectory.LocalOrbitalFrameFactory) -> tuple[ostk.physics.unit.Length, ostk.physics.unit.Length, ostk.physics.unit.Length]:
|
|
71
|
+
"""
|
|
72
|
+
Compute the miss distance components in a local orbital frame (generated from Object 1 state).
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
local_orbital_frame_factory (LocalOrbitalFrameFactory): The local orbital frame factory.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
tuple[Length, Length, Length]: The miss distance components (radial, in-track, cross-track or similar depending on the factory).
|
|
79
|
+
"""
|
|
80
|
+
def get_instant(self) -> ostk.physics.time.Instant:
|
|
81
|
+
"""
|
|
82
|
+
Get the instant of the close approach.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Instant: The instant of closest approach.
|
|
86
|
+
"""
|
|
87
|
+
def get_miss_distance(self) -> ostk.physics.unit.Length:
|
|
88
|
+
"""
|
|
89
|
+
Get the miss distance.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Length: The miss distance between the two objects.
|
|
93
|
+
"""
|
|
94
|
+
def get_object_1_state(self) -> ostk.astrodynamics.trajectory.State:
|
|
95
|
+
"""
|
|
96
|
+
Get the state of Object 1.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
State: The state of Object 1.
|
|
100
|
+
"""
|
|
101
|
+
def get_object_2_state(self) -> ostk.astrodynamics.trajectory.State:
|
|
102
|
+
"""
|
|
103
|
+
Get the state of Object 2.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
State: The state of Object 2.
|
|
107
|
+
"""
|
|
108
|
+
def get_relative_state(self) -> ostk.astrodynamics.trajectory.State:
|
|
109
|
+
"""
|
|
110
|
+
Get the relative state (Object 2 relative to Object 1).
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
State: The relative state.
|
|
114
|
+
"""
|
|
115
|
+
def is_defined(self) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Check if the close approach is defined.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
bool: True if close approach is defined, False otherwise.
|
|
121
|
+
"""
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import ostk.astrodynamics
|
|
3
|
+
import ostk.astrodynamics.conjunction
|
|
4
|
+
import ostk.physics.time
|
|
5
|
+
__all__ = ['Generator']
|
|
6
|
+
class Generator:
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
Compute close approaches to a reference trajectory.
|
|
10
|
+
|
|
11
|
+
This class computes close approach events between a reference trajectory and other object trajectories
|
|
12
|
+
over a specified time interval. It uses a temporal condition solver to identify time periods when objects
|
|
13
|
+
are approaching and then determines the exact time of closest approach.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
@staticmethod
|
|
17
|
+
def undefined() -> Generator:
|
|
18
|
+
"""
|
|
19
|
+
Construct an undefined generator.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
Generator: An undefined generator.
|
|
23
|
+
"""
|
|
24
|
+
def __init__(self, reference_trajectory: ostk.astrodynamics.Trajectory, step: ostk.physics.time.Duration = ..., tolerance: ostk.physics.time.Duration = ...) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Constructor.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
reference_trajectory (Trajectory): The reference trajectory for which to compute close approaches (Object 1).
|
|
30
|
+
step (Duration): The step to use during the close approach search. Defaults to Duration.seconds(15.0).
|
|
31
|
+
tolerance (Duration): The tolerance to use during the close approach search. Defaults to Duration.milliseconds(1.0).
|
|
32
|
+
"""
|
|
33
|
+
def compute_close_approaches(self, trajectory: ostk.astrodynamics.Trajectory, search_interval: ostk.physics.time.Interval) -> list[ostk.astrodynamics.conjunction.CloseApproach]:
|
|
34
|
+
"""
|
|
35
|
+
Compute close approaches between the reference trajectory and another object over a search interval.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
trajectory (Trajectory): The trajectory of the other object (Object 2).
|
|
39
|
+
search_interval (Interval): The interval over which close approaches are searched.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
list[CloseApproach]: Array of close approaches over the search interval (with Object 1 being the reference trajectory).
|
|
43
|
+
"""
|
|
44
|
+
def get_reference_trajectory(self) -> ostk.astrodynamics.Trajectory:
|
|
45
|
+
"""
|
|
46
|
+
Get the reference trajectory.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Trajectory: The reference trajectory.
|
|
50
|
+
"""
|
|
51
|
+
def get_step(self) -> ostk.physics.time.Duration:
|
|
52
|
+
"""
|
|
53
|
+
Get the step.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Duration: The step.
|
|
57
|
+
"""
|
|
58
|
+
def get_tolerance(self) -> ostk.physics.time.Duration:
|
|
59
|
+
"""
|
|
60
|
+
Get the tolerance.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Duration: The tolerance.
|
|
64
|
+
"""
|
|
65
|
+
def is_defined(self) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Check if the generator is defined.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
bool: True if generator is defined, False otherwise.
|
|
71
|
+
"""
|
|
72
|
+
def set_step(self, step: ostk.physics.time.Duration) -> None:
|
|
73
|
+
"""
|
|
74
|
+
Set the step.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
step (Duration): The step.
|
|
78
|
+
"""
|
|
79
|
+
def set_tolerance(self, tolerance: ostk.physics.time.Duration) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Set the tolerance.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
tolerance (Duration): The tolerance.
|
|
85
|
+
"""
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ostk.physics.unit import Length
|
|
6
|
+
from ostk.physics.unit import Angle
|
|
7
|
+
from ostk.physics.time import DateTime
|
|
8
|
+
from ostk.physics.time import Scale
|
|
9
|
+
from ostk.physics.time import Duration
|
|
10
|
+
from ostk.physics.time import Instant
|
|
11
|
+
from ostk.physics.time import Interval
|
|
12
|
+
from ostk.physics import Environment
|
|
13
|
+
from ostk.physics.environment.object import Celestial
|
|
14
|
+
|
|
15
|
+
from ostk.astrodynamics import Trajectory
|
|
16
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
17
|
+
from ostk.astrodynamics.trajectory.orbit.model import Kepler
|
|
18
|
+
from ostk.astrodynamics.trajectory.orbit.model.kepler import COE
|
|
19
|
+
from ostk.astrodynamics.conjunction import CloseApproach
|
|
20
|
+
from ostk.astrodynamics.conjunction.close_approach import Generator
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def environment() -> Environment:
|
|
25
|
+
return Environment.default()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.fixture
|
|
29
|
+
def earth(environment: Environment) -> Celestial:
|
|
30
|
+
return environment.access_celestial_object_with_name("Earth")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture
|
|
34
|
+
def epoch() -> Instant:
|
|
35
|
+
return Instant.date_time(DateTime(2024, 1, 1, 0, 0, 0), Scale.UTC)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture
|
|
39
|
+
def step() -> Duration:
|
|
40
|
+
return Duration.seconds(60.0)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def tolerance() -> Duration:
|
|
45
|
+
return Duration.milliseconds(1.0)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@pytest.fixture
|
|
49
|
+
def reference_trajectory(earth: Celestial, epoch: Instant) -> Trajectory:
|
|
50
|
+
return Orbit(
|
|
51
|
+
model=Kepler(
|
|
52
|
+
coe=COE(
|
|
53
|
+
semi_major_axis=Length.kilometers(7000.0),
|
|
54
|
+
eccentricity=0.0,
|
|
55
|
+
inclination=Angle.degrees(0.0),
|
|
56
|
+
raan=Angle.degrees(0.0),
|
|
57
|
+
aop=Angle.degrees(0.0),
|
|
58
|
+
true_anomaly=Angle.degrees(0.0),
|
|
59
|
+
),
|
|
60
|
+
epoch=epoch,
|
|
61
|
+
celestial_object=earth,
|
|
62
|
+
perturbation_type=Kepler.PerturbationType.No,
|
|
63
|
+
),
|
|
64
|
+
celestial_object=earth,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.fixture
|
|
69
|
+
def other_trajectory(earth: Celestial, epoch: Instant) -> Trajectory:
|
|
70
|
+
return Orbit(
|
|
71
|
+
model=Kepler(
|
|
72
|
+
coe=COE(
|
|
73
|
+
semi_major_axis=Length.kilometers(7000.0),
|
|
74
|
+
eccentricity=0.0,
|
|
75
|
+
inclination=Angle.degrees(90.0),
|
|
76
|
+
raan=Angle.degrees(0.0),
|
|
77
|
+
aop=Angle.degrees(0.0),
|
|
78
|
+
true_anomaly=Angle.degrees(1.0 - 3), # Avoid co-location
|
|
79
|
+
),
|
|
80
|
+
epoch=epoch,
|
|
81
|
+
celestial_object=earth,
|
|
82
|
+
perturbation_type=Kepler.PerturbationType.No,
|
|
83
|
+
),
|
|
84
|
+
celestial_object=earth,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@pytest.fixture
|
|
89
|
+
def generator(
|
|
90
|
+
reference_trajectory: Trajectory,
|
|
91
|
+
step: Duration,
|
|
92
|
+
tolerance: Duration,
|
|
93
|
+
) -> Generator:
|
|
94
|
+
return Generator(
|
|
95
|
+
reference_trajectory=reference_trajectory,
|
|
96
|
+
step=step,
|
|
97
|
+
tolerance=tolerance,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@pytest.fixture
|
|
102
|
+
def search_interval(epoch: Instant) -> Interval:
|
|
103
|
+
return Interval.closed(
|
|
104
|
+
epoch,
|
|
105
|
+
epoch + Duration.hours(3.0),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TestGenerator:
|
|
110
|
+
def test_constructor_success(
|
|
111
|
+
self,
|
|
112
|
+
reference_trajectory: Trajectory,
|
|
113
|
+
step: Duration,
|
|
114
|
+
tolerance: Duration,
|
|
115
|
+
):
|
|
116
|
+
generator = Generator(
|
|
117
|
+
reference_trajectory=reference_trajectory,
|
|
118
|
+
step=step,
|
|
119
|
+
tolerance=tolerance,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
assert generator is not None
|
|
123
|
+
assert isinstance(generator, Generator)
|
|
124
|
+
|
|
125
|
+
def test_constructor_with_defaults_success(
|
|
126
|
+
self,
|
|
127
|
+
reference_trajectory: Trajectory,
|
|
128
|
+
):
|
|
129
|
+
generator = Generator(reference_trajectory=reference_trajectory)
|
|
130
|
+
|
|
131
|
+
assert generator is not None
|
|
132
|
+
assert isinstance(generator, Generator)
|
|
133
|
+
assert generator.is_defined() is True
|
|
134
|
+
|
|
135
|
+
def test_is_defined_success(self, generator: Generator):
|
|
136
|
+
assert generator.is_defined() is True
|
|
137
|
+
|
|
138
|
+
def test_undefined_success(self):
|
|
139
|
+
generator = Generator.undefined()
|
|
140
|
+
|
|
141
|
+
assert generator is not None
|
|
142
|
+
assert isinstance(generator, Generator)
|
|
143
|
+
assert generator.is_defined() is False
|
|
144
|
+
|
|
145
|
+
def test_get_reference_trajectory_success(
|
|
146
|
+
self,
|
|
147
|
+
generator: Generator,
|
|
148
|
+
):
|
|
149
|
+
trajectory = generator.get_reference_trajectory()
|
|
150
|
+
|
|
151
|
+
assert trajectory is not None
|
|
152
|
+
assert isinstance(trajectory, Trajectory)
|
|
153
|
+
|
|
154
|
+
def test_get_step_success(
|
|
155
|
+
self,
|
|
156
|
+
generator: Generator,
|
|
157
|
+
step: Duration,
|
|
158
|
+
):
|
|
159
|
+
result_step = generator.get_step()
|
|
160
|
+
|
|
161
|
+
assert result_step is not None
|
|
162
|
+
assert isinstance(result_step, Duration)
|
|
163
|
+
assert result_step == step
|
|
164
|
+
|
|
165
|
+
def test_get_tolerance_success(
|
|
166
|
+
self,
|
|
167
|
+
generator: Generator,
|
|
168
|
+
tolerance: Duration,
|
|
169
|
+
):
|
|
170
|
+
result_tolerance = generator.get_tolerance()
|
|
171
|
+
|
|
172
|
+
assert result_tolerance is not None
|
|
173
|
+
assert isinstance(result_tolerance, Duration)
|
|
174
|
+
assert result_tolerance == tolerance
|
|
175
|
+
|
|
176
|
+
def test_set_step_success(self, generator: Generator):
|
|
177
|
+
new_step = Duration.seconds(30.0)
|
|
178
|
+
|
|
179
|
+
generator.set_step(step=new_step)
|
|
180
|
+
|
|
181
|
+
assert generator.get_step() == new_step
|
|
182
|
+
|
|
183
|
+
def test_set_tolerance_success(self, generator: Generator):
|
|
184
|
+
new_tolerance = Duration.microseconds(100.0)
|
|
185
|
+
|
|
186
|
+
generator.set_tolerance(tolerance=new_tolerance)
|
|
187
|
+
|
|
188
|
+
assert generator.get_tolerance() == new_tolerance
|
|
189
|
+
|
|
190
|
+
def test_compute_close_approaches_success(
|
|
191
|
+
self,
|
|
192
|
+
generator: Generator,
|
|
193
|
+
other_trajectory: Trajectory,
|
|
194
|
+
search_interval: Interval,
|
|
195
|
+
):
|
|
196
|
+
close_approaches = generator.compute_close_approaches(
|
|
197
|
+
trajectory=other_trajectory,
|
|
198
|
+
search_interval=search_interval,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
assert close_approaches is not None
|
|
202
|
+
assert isinstance(close_approaches, list)
|
|
203
|
+
assert len(close_approaches) > 1
|
|
204
|
+
|
|
205
|
+
for close_approach in close_approaches:
|
|
206
|
+
assert isinstance(close_approach, CloseApproach)
|
|
207
|
+
assert close_approach.is_defined() is True
|
|
208
|
+
assert search_interval.contains_instant(close_approach.get_instant())
|
|
209
|
+
|
|
210
|
+
def test_compute_close_approaches_empty_interval_success(
|
|
211
|
+
self,
|
|
212
|
+
generator: Generator,
|
|
213
|
+
other_trajectory: Trajectory,
|
|
214
|
+
epoch: Instant,
|
|
215
|
+
):
|
|
216
|
+
search_interval = Interval.closed(
|
|
217
|
+
epoch + Duration.hours(10.0),
|
|
218
|
+
epoch + Duration.hours(10.0) + Duration.seconds(1.0),
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
close_approaches = generator.compute_close_approaches(
|
|
222
|
+
trajectory=other_trajectory,
|
|
223
|
+
search_interval=search_interval,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
assert close_approaches is not None
|
|
227
|
+
assert isinstance(close_approaches, list)
|
|
228
|
+
assert len(close_approaches) == 0
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ostk.physics.unit import Length
|
|
6
|
+
from ostk.physics.unit import Angle
|
|
7
|
+
from ostk.physics.time import DateTime
|
|
8
|
+
from ostk.physics.time import Scale
|
|
9
|
+
from ostk.physics.time import Instant
|
|
10
|
+
from ostk.physics.coordinate import Frame
|
|
11
|
+
from ostk.physics import Environment
|
|
12
|
+
from ostk.physics.environment.object import Celestial
|
|
13
|
+
|
|
14
|
+
from ostk.astrodynamics.trajectory import Orbit
|
|
15
|
+
from ostk.astrodynamics.trajectory import State
|
|
16
|
+
from ostk.astrodynamics.trajectory import LocalOrbitalFrameFactory
|
|
17
|
+
from ostk.astrodynamics.trajectory.orbit.model import Kepler
|
|
18
|
+
from ostk.astrodynamics.trajectory.orbit.model.kepler import COE
|
|
19
|
+
from ostk.astrodynamics.conjunction import CloseApproach
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def environment() -> Environment:
|
|
24
|
+
return Environment.default()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def earth(environment: Environment) -> Celestial:
|
|
29
|
+
return environment.access_celestial_object_with_name("Earth")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.fixture
|
|
33
|
+
def instant() -> Instant:
|
|
34
|
+
return Instant.date_time(DateTime(2024, 1, 1, 0, 0, 0), Scale.UTC)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest.fixture
|
|
38
|
+
def gcrf_frame() -> Frame:
|
|
39
|
+
return Frame.GCRF()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.fixture
|
|
43
|
+
def object_1_state(
|
|
44
|
+
instant: Instant,
|
|
45
|
+
earth: Celestial,
|
|
46
|
+
) -> State:
|
|
47
|
+
orbit = Orbit(
|
|
48
|
+
model=Kepler(
|
|
49
|
+
coe=COE(
|
|
50
|
+
semi_major_axis=Length.kilometers(7000.0),
|
|
51
|
+
eccentricity=0.0,
|
|
52
|
+
inclination=Angle.degrees(0.0),
|
|
53
|
+
raan=Angle.degrees(0.0),
|
|
54
|
+
aop=Angle.degrees(0.0),
|
|
55
|
+
true_anomaly=Angle.degrees(0.0),
|
|
56
|
+
),
|
|
57
|
+
epoch=instant,
|
|
58
|
+
celestial_object=earth,
|
|
59
|
+
perturbation_type=Kepler.PerturbationType.No,
|
|
60
|
+
),
|
|
61
|
+
celestial_object=earth,
|
|
62
|
+
)
|
|
63
|
+
return orbit.get_state_at(instant)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.fixture
|
|
67
|
+
def object_2_state(
|
|
68
|
+
instant: Instant,
|
|
69
|
+
earth: Celestial,
|
|
70
|
+
) -> State:
|
|
71
|
+
orbit = Orbit(
|
|
72
|
+
model=Kepler(
|
|
73
|
+
coe=COE(
|
|
74
|
+
semi_major_axis=Length.kilometers(7000.0),
|
|
75
|
+
eccentricity=0.0,
|
|
76
|
+
inclination=Angle.degrees(90.0),
|
|
77
|
+
raan=Angle.degrees(0.0),
|
|
78
|
+
aop=Angle.degrees(0.0),
|
|
79
|
+
true_anomaly=Angle.degrees(1.0),
|
|
80
|
+
),
|
|
81
|
+
epoch=instant,
|
|
82
|
+
celestial_object=earth,
|
|
83
|
+
perturbation_type=Kepler.PerturbationType.No,
|
|
84
|
+
),
|
|
85
|
+
celestial_object=earth,
|
|
86
|
+
)
|
|
87
|
+
return orbit.get_state_at(instant)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@pytest.fixture
|
|
91
|
+
def close_approach(
|
|
92
|
+
object_1_state: State,
|
|
93
|
+
object_2_state: State,
|
|
94
|
+
) -> CloseApproach:
|
|
95
|
+
return CloseApproach(
|
|
96
|
+
object_1_state=object_1_state,
|
|
97
|
+
object_2_state=object_2_state,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class TestCloseApproach:
|
|
102
|
+
def test_constructor_success(
|
|
103
|
+
self,
|
|
104
|
+
object_1_state: State,
|
|
105
|
+
object_2_state: State,
|
|
106
|
+
):
|
|
107
|
+
close_approach = CloseApproach(
|
|
108
|
+
object_1_state=object_1_state,
|
|
109
|
+
object_2_state=object_2_state,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
assert close_approach is not None
|
|
113
|
+
assert isinstance(close_approach, CloseApproach)
|
|
114
|
+
|
|
115
|
+
def test_is_defined_success(
|
|
116
|
+
self,
|
|
117
|
+
close_approach: CloseApproach,
|
|
118
|
+
):
|
|
119
|
+
assert close_approach.is_defined() is True
|
|
120
|
+
|
|
121
|
+
def test_undefined_success(self):
|
|
122
|
+
close_approach = CloseApproach.undefined()
|
|
123
|
+
|
|
124
|
+
assert close_approach is not None
|
|
125
|
+
assert isinstance(close_approach, CloseApproach)
|
|
126
|
+
assert close_approach.is_defined() is False
|
|
127
|
+
|
|
128
|
+
def test_get_object_1_state_success(
|
|
129
|
+
self,
|
|
130
|
+
close_approach: CloseApproach,
|
|
131
|
+
object_1_state: State,
|
|
132
|
+
):
|
|
133
|
+
state = close_approach.get_object_1_state()
|
|
134
|
+
|
|
135
|
+
assert state is not None
|
|
136
|
+
assert isinstance(state, State)
|
|
137
|
+
assert state.get_instant() == object_1_state.get_instant()
|
|
138
|
+
|
|
139
|
+
def test_get_object_2_state_success(
|
|
140
|
+
self,
|
|
141
|
+
close_approach: CloseApproach,
|
|
142
|
+
object_2_state: State,
|
|
143
|
+
):
|
|
144
|
+
state = close_approach.get_object_2_state()
|
|
145
|
+
|
|
146
|
+
assert state is not None
|
|
147
|
+
assert isinstance(state, State)
|
|
148
|
+
assert state.get_instant() == object_2_state.get_instant()
|
|
149
|
+
|
|
150
|
+
def test_get_instant_success(
|
|
151
|
+
self,
|
|
152
|
+
close_approach: CloseApproach,
|
|
153
|
+
instant: Instant,
|
|
154
|
+
):
|
|
155
|
+
ca_instant = close_approach.get_instant()
|
|
156
|
+
|
|
157
|
+
assert ca_instant is not None
|
|
158
|
+
assert isinstance(ca_instant, Instant)
|
|
159
|
+
assert ca_instant == instant
|
|
160
|
+
|
|
161
|
+
def test_get_miss_distance_success(
|
|
162
|
+
self,
|
|
163
|
+
close_approach: CloseApproach,
|
|
164
|
+
):
|
|
165
|
+
miss_distance = close_approach.get_miss_distance()
|
|
166
|
+
|
|
167
|
+
assert miss_distance is not None
|
|
168
|
+
assert isinstance(miss_distance, Length)
|
|
169
|
+
assert miss_distance.in_meters() > 0.0
|
|
170
|
+
|
|
171
|
+
def test_get_relative_state_success(
|
|
172
|
+
self,
|
|
173
|
+
close_approach: CloseApproach,
|
|
174
|
+
):
|
|
175
|
+
relative_state = close_approach.get_relative_state()
|
|
176
|
+
|
|
177
|
+
assert relative_state is not None
|
|
178
|
+
assert isinstance(relative_state, State)
|
|
179
|
+
|
|
180
|
+
def test_compute_miss_distance_components_in_frame_success(
|
|
181
|
+
self,
|
|
182
|
+
close_approach: CloseApproach,
|
|
183
|
+
gcrf_frame: Frame,
|
|
184
|
+
):
|
|
185
|
+
components = close_approach.compute_miss_distance_components_in_frame(
|
|
186
|
+
frame=gcrf_frame
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
assert components is not None
|
|
190
|
+
assert isinstance(components, tuple)
|
|
191
|
+
assert len(components) == 3
|
|
192
|
+
|
|
193
|
+
# Check each component is a Length
|
|
194
|
+
for component in components:
|
|
195
|
+
assert isinstance(component, Length)
|
|
196
|
+
|
|
197
|
+
def test_compute_miss_distance_components_in_local_orbital_frame_success(
|
|
198
|
+
self,
|
|
199
|
+
close_approach: CloseApproach,
|
|
200
|
+
gcrf_frame: Frame,
|
|
201
|
+
):
|
|
202
|
+
qsw_factory = LocalOrbitalFrameFactory.QSW(gcrf_frame)
|
|
203
|
+
|
|
204
|
+
components = (
|
|
205
|
+
close_approach.compute_miss_distance_components_in_local_orbital_frame(
|
|
206
|
+
local_orbital_frame_factory=qsw_factory
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
assert components is not None
|
|
211
|
+
assert isinstance(components, tuple)
|
|
212
|
+
assert len(components) == 3
|
|
213
|
+
|
|
214
|
+
# Check each component is a Length
|
|
215
|
+
for component in components:
|
|
216
|
+
assert isinstance(component, Length)
|
|
217
|
+
|
|
218
|
+
def test_equality_operators_success(
|
|
219
|
+
self,
|
|
220
|
+
object_1_state: State,
|
|
221
|
+
object_2_state: State,
|
|
222
|
+
):
|
|
223
|
+
close_approach_1 = CloseApproach(
|
|
224
|
+
object_1_state=object_1_state,
|
|
225
|
+
object_2_state=object_2_state,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
close_approach_2 = CloseApproach(
|
|
229
|
+
object_1_state=object_1_state,
|
|
230
|
+
object_2_state=object_2_state,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
assert close_approach_1 == close_approach_2
|
|
234
|
+
assert not (close_approach_1 != close_approach_2)
|
|
235
|
+
|
|
236
|
+
def test_string_representation_success(
|
|
237
|
+
self,
|
|
238
|
+
close_approach: CloseApproach,
|
|
239
|
+
):
|
|
240
|
+
string_repr = str(close_approach)
|
|
241
|
+
|
|
242
|
+
assert string_repr is not None
|
|
243
|
+
assert isinstance(string_repr, str)
|
|
244
|
+
assert len(string_repr) > 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|