open-space-toolkit-astrodynamics 15.1.0__py311-none-manylinux2014_x86_64.whl → 15.2.1__py311-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.
- {open_space_toolkit_astrodynamics-15.1.0.dist-info → open_space_toolkit_astrodynamics-15.2.1.dist-info}/METADATA +1 -1
- {open_space_toolkit_astrodynamics-15.1.0.dist-info → open_space_toolkit_astrodynamics-15.2.1.dist-info}/RECORD +16 -12
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-311-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.pyi +6 -5
- ostk/astrodynamics/access.pyi +13 -0
- ostk/astrodynamics/estimator.pyi +280 -0
- ostk/astrodynamics/libopen-space-toolkit-astrodynamics.so.15 +0 -0
- ostk/astrodynamics/solver.pyi +209 -1
- ostk/astrodynamics/test/access/test_visibility_criterion.py +3 -0
- ostk/astrodynamics/test/estimator/test_orbit_determination_solver.py +261 -0
- ostk/astrodynamics/test/estimator/test_tle_solver.py +215 -0
- ostk/astrodynamics/test/solvers/test_least_squares_solver.py +334 -0
- ostk/astrodynamics/utilities.py +9 -8
- {open_space_toolkit_astrodynamics-15.1.0.dist-info → open_space_toolkit_astrodynamics-15.2.1.dist-info}/WHEEL +0 -0
- {open_space_toolkit_astrodynamics-15.1.0.dist-info → open_space_toolkit_astrodynamics-15.2.1.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-15.1.0.dist-info → open_space_toolkit_astrodynamics-15.2.1.dist-info}/zip-safe +0 -0
@@ -0,0 +1,334 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
from typing import Callable
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
from ostk.core.type import Real
|
9
|
+
from ostk.core.type import String
|
10
|
+
|
11
|
+
from ostk.physics.time import Instant
|
12
|
+
from ostk.physics.time import Duration
|
13
|
+
from ostk.physics.coordinate import Frame
|
14
|
+
|
15
|
+
from ostk.astrodynamics.solver import LeastSquaresSolver
|
16
|
+
from ostk.astrodynamics.solver import FiniteDifferenceSolver
|
17
|
+
from ostk.astrodynamics.trajectory import State
|
18
|
+
from ostk.astrodynamics.trajectory.state import CoordinateSubset
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.fixture
|
22
|
+
def rms_error() -> float:
|
23
|
+
return 1.0
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture
|
27
|
+
def x_hat() -> np.ndarray:
|
28
|
+
return np.array([1.0, 0.0])
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.fixture
|
32
|
+
def step(
|
33
|
+
rms_error: float,
|
34
|
+
x_hat: np.ndarray,
|
35
|
+
) -> LeastSquaresSolver.Step:
|
36
|
+
return LeastSquaresSolver.Step(
|
37
|
+
rms_error=rms_error,
|
38
|
+
x_hat=x_hat,
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.fixture
|
43
|
+
def termination_criteria() -> str:
|
44
|
+
return "RMS Update Threshold"
|
45
|
+
|
46
|
+
|
47
|
+
@pytest.fixture
|
48
|
+
def estimated_state(coordinate_subsets: list[CoordinateSubset]) -> State:
|
49
|
+
return State(
|
50
|
+
Instant.J2000(),
|
51
|
+
[1.0, 0.0],
|
52
|
+
Frame.GCRF(),
|
53
|
+
coordinate_subsets,
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
@pytest.fixture
|
58
|
+
def estimated_covariance() -> np.ndarray:
|
59
|
+
return np.array([[1.0, 0.0], [0.0, 1.0]])
|
60
|
+
|
61
|
+
|
62
|
+
@pytest.fixture
|
63
|
+
def estimated_frisbee_covariance() -> np.ndarray:
|
64
|
+
return np.array([[1.0, 0.0], [0.0, 1.0]])
|
65
|
+
|
66
|
+
|
67
|
+
@pytest.fixture
|
68
|
+
def observation_count() -> int:
|
69
|
+
return 10
|
70
|
+
|
71
|
+
|
72
|
+
@pytest.fixture
|
73
|
+
def computed_observations(
|
74
|
+
observations: list[State],
|
75
|
+
) -> list[State]:
|
76
|
+
return observations
|
77
|
+
|
78
|
+
|
79
|
+
@pytest.fixture
|
80
|
+
def steps(step: LeastSquaresSolver.Step) -> list[LeastSquaresSolver.Step]:
|
81
|
+
return [step]
|
82
|
+
|
83
|
+
|
84
|
+
@pytest.fixture
|
85
|
+
def analysis(
|
86
|
+
termination_criteria: str,
|
87
|
+
estimated_state: State,
|
88
|
+
estimated_covariance: np.ndarray,
|
89
|
+
estimated_frisbee_covariance: np.ndarray,
|
90
|
+
computed_observations: list[State],
|
91
|
+
steps: list[LeastSquaresSolver.Step],
|
92
|
+
) -> LeastSquaresSolver.Analysis:
|
93
|
+
return LeastSquaresSolver.Analysis(
|
94
|
+
termination_criteria=termination_criteria,
|
95
|
+
estimated_state=estimated_state,
|
96
|
+
estimated_covariance=estimated_covariance,
|
97
|
+
estimated_frisbee_covariance=estimated_frisbee_covariance,
|
98
|
+
computed_observations=computed_observations,
|
99
|
+
steps=steps,
|
100
|
+
)
|
101
|
+
|
102
|
+
|
103
|
+
@pytest.fixture
|
104
|
+
def max_iteration_count() -> int:
|
105
|
+
return 20
|
106
|
+
|
107
|
+
|
108
|
+
@pytest.fixture
|
109
|
+
def rms_update_threshold() -> float:
|
110
|
+
return 1.0
|
111
|
+
|
112
|
+
|
113
|
+
@pytest.fixture
|
114
|
+
def finite_difference_solver() -> FiniteDifferenceSolver:
|
115
|
+
return FiniteDifferenceSolver.default()
|
116
|
+
|
117
|
+
|
118
|
+
@pytest.fixture
|
119
|
+
def least_squares_solver(
|
120
|
+
max_iteration_count: int,
|
121
|
+
rms_update_threshold: float,
|
122
|
+
finite_difference_solver: FiniteDifferenceSolver,
|
123
|
+
) -> LeastSquaresSolver:
|
124
|
+
return LeastSquaresSolver(
|
125
|
+
maximum_iteration_count=max_iteration_count,
|
126
|
+
rms_update_threshold=rms_update_threshold,
|
127
|
+
finite_difference_solver=finite_difference_solver,
|
128
|
+
)
|
129
|
+
|
130
|
+
|
131
|
+
@pytest.fixture
|
132
|
+
def coordinate_subsets() -> list[CoordinateSubset]:
|
133
|
+
return [CoordinateSubset("Position", 1), CoordinateSubset("Velocity", 1)]
|
134
|
+
|
135
|
+
|
136
|
+
@pytest.fixture
|
137
|
+
def initial_guess_sigmas(
|
138
|
+
coordinate_subsets: list[CoordinateSubset],
|
139
|
+
) -> dict[CoordinateSubset, list[float]]:
|
140
|
+
return {
|
141
|
+
coordinate_subsets[0]: [1e-1],
|
142
|
+
coordinate_subsets[1]: [1e-2],
|
143
|
+
}
|
144
|
+
|
145
|
+
|
146
|
+
@pytest.fixture
|
147
|
+
def observation_sigmas(
|
148
|
+
coordinate_subsets: list[CoordinateSubset],
|
149
|
+
) -> dict[CoordinateSubset, list[float]]:
|
150
|
+
return {
|
151
|
+
coordinate_subsets[0]: [1e-1],
|
152
|
+
coordinate_subsets[1]: [1e-2],
|
153
|
+
}
|
154
|
+
|
155
|
+
|
156
|
+
@pytest.fixture
|
157
|
+
def initial_instant() -> Instant:
|
158
|
+
return Instant.J2000()
|
159
|
+
|
160
|
+
|
161
|
+
@pytest.fixture
|
162
|
+
def frame() -> Frame:
|
163
|
+
return Frame.GCRF()
|
164
|
+
|
165
|
+
|
166
|
+
@pytest.fixture
|
167
|
+
def initial_guess(
|
168
|
+
initial_instant: Instant,
|
169
|
+
coordinate_subsets: list[CoordinateSubset],
|
170
|
+
frame: Frame,
|
171
|
+
) -> State:
|
172
|
+
return State(initial_instant, [1.0, 0.0], frame, coordinate_subsets)
|
173
|
+
|
174
|
+
|
175
|
+
@pytest.fixture
|
176
|
+
def observations(
|
177
|
+
initial_instant: Instant,
|
178
|
+
coordinate_subsets: list[CoordinateSubset],
|
179
|
+
frame: Frame,
|
180
|
+
observation_count: int,
|
181
|
+
) -> list[State]:
|
182
|
+
return [
|
183
|
+
State(
|
184
|
+
initial_instant + Duration.seconds(float(x)),
|
185
|
+
[np.cos(x), -np.sin(x)],
|
186
|
+
frame,
|
187
|
+
coordinate_subsets,
|
188
|
+
)
|
189
|
+
for x in range(0, observation_count)
|
190
|
+
]
|
191
|
+
|
192
|
+
|
193
|
+
@pytest.fixture
|
194
|
+
def state_generator() -> Callable:
|
195
|
+
def state_fn(state, instants) -> list[State]:
|
196
|
+
x0: float = state.get_coordinates()[0]
|
197
|
+
v0: float = state.get_coordinates()[1]
|
198
|
+
omega: float = 1.0
|
199
|
+
|
200
|
+
states: list[State] = []
|
201
|
+
|
202
|
+
for instant in instants:
|
203
|
+
t: float = float((instant - state.get_instant()).in_seconds())
|
204
|
+
x: float = x0 * np.cos(omega * t) + v0 / omega * np.sin(omega * t)
|
205
|
+
v: float = -x0 * omega * np.sin(omega * t) + v0 * np.cos(omega * t)
|
206
|
+
|
207
|
+
states.append(
|
208
|
+
State(instant, [x, v], Frame.GCRF(), state.get_coordinate_subsets())
|
209
|
+
)
|
210
|
+
|
211
|
+
return states
|
212
|
+
|
213
|
+
return state_fn
|
214
|
+
|
215
|
+
|
216
|
+
class TestLeastSquaresSolverStep:
|
217
|
+
|
218
|
+
def test_constructor(
|
219
|
+
self,
|
220
|
+
step: LeastSquaresSolver.Step,
|
221
|
+
):
|
222
|
+
assert isinstance(step, LeastSquaresSolver.Step)
|
223
|
+
|
224
|
+
def test_getters(
|
225
|
+
self,
|
226
|
+
step: LeastSquaresSolver.Step,
|
227
|
+
rms_error: float,
|
228
|
+
x_hat: np.ndarray,
|
229
|
+
):
|
230
|
+
assert step.rms_error == rms_error
|
231
|
+
assert np.array_equal(step.x_hat, x_hat)
|
232
|
+
|
233
|
+
|
234
|
+
class TestLeastSquaresSolverAnalysis:
|
235
|
+
|
236
|
+
def test_constructor(
|
237
|
+
self,
|
238
|
+
analysis: LeastSquaresSolver.Analysis,
|
239
|
+
):
|
240
|
+
assert isinstance(analysis, LeastSquaresSolver.Analysis)
|
241
|
+
|
242
|
+
def test_getters(
|
243
|
+
self,
|
244
|
+
analysis: LeastSquaresSolver.Analysis,
|
245
|
+
):
|
246
|
+
assert isinstance(analysis.rms_error, Real)
|
247
|
+
assert isinstance(analysis.observation_count, int)
|
248
|
+
assert isinstance(analysis.iteration_count, int)
|
249
|
+
assert isinstance(analysis.termination_criteria, String)
|
250
|
+
assert isinstance(analysis.estimated_state, State)
|
251
|
+
assert isinstance(analysis.estimated_covariance, np.ndarray)
|
252
|
+
assert isinstance(analysis.estimated_frisbee_covariance, np.ndarray)
|
253
|
+
assert isinstance(analysis.computed_observations, list)
|
254
|
+
assert isinstance(analysis.steps, list)
|
255
|
+
|
256
|
+
def test_compute_residual_states(
|
257
|
+
self,
|
258
|
+
analysis: LeastSquaresSolver.Analysis,
|
259
|
+
observations: list[State],
|
260
|
+
):
|
261
|
+
residuals: list[State] = analysis.compute_residual_states(
|
262
|
+
observations=observations
|
263
|
+
)
|
264
|
+
|
265
|
+
assert isinstance(residuals, list)
|
266
|
+
assert len(residuals) == len(observations)
|
267
|
+
|
268
|
+
|
269
|
+
class TestLeastSquaresSolver:
|
270
|
+
def test_constructor(
|
271
|
+
self,
|
272
|
+
least_squares_solver: LeastSquaresSolver,
|
273
|
+
):
|
274
|
+
assert isinstance(least_squares_solver, LeastSquaresSolver)
|
275
|
+
|
276
|
+
def test_getters(
|
277
|
+
self,
|
278
|
+
least_squares_solver: LeastSquaresSolver,
|
279
|
+
max_iteration_count: int,
|
280
|
+
rms_update_threshold: float,
|
281
|
+
):
|
282
|
+
assert least_squares_solver.get_max_iteration_count() == max_iteration_count
|
283
|
+
assert least_squares_solver.get_rms_update_threshold() == rms_update_threshold
|
284
|
+
assert least_squares_solver.get_finite_difference_solver() is not None
|
285
|
+
|
286
|
+
def test_solve_defaults(
|
287
|
+
self,
|
288
|
+
least_squares_solver: LeastSquaresSolver,
|
289
|
+
initial_guess: State,
|
290
|
+
observations: list[State],
|
291
|
+
state_generator: callable,
|
292
|
+
):
|
293
|
+
analysis = least_squares_solver.solve(
|
294
|
+
initial_guess=initial_guess,
|
295
|
+
observations=observations,
|
296
|
+
state_generator=state_generator,
|
297
|
+
)
|
298
|
+
|
299
|
+
assert analysis is not None
|
300
|
+
|
301
|
+
def test_solve(
|
302
|
+
self,
|
303
|
+
least_squares_solver: LeastSquaresSolver,
|
304
|
+
initial_guess: State,
|
305
|
+
observations: list[State],
|
306
|
+
state_generator: callable,
|
307
|
+
initial_guess_sigmas: dict[CoordinateSubset, list[float]],
|
308
|
+
observation_sigmas: dict[CoordinateSubset, list[float]],
|
309
|
+
):
|
310
|
+
analysis = least_squares_solver.solve(
|
311
|
+
initial_guess=initial_guess,
|
312
|
+
observations=observations,
|
313
|
+
state_generator=state_generator,
|
314
|
+
initial_guess_sigmas=initial_guess_sigmas,
|
315
|
+
observation_sigmas=observation_sigmas,
|
316
|
+
)
|
317
|
+
|
318
|
+
assert analysis is not None
|
319
|
+
|
320
|
+
def test_calculate_empirical_covariance(
|
321
|
+
self,
|
322
|
+
observations: list[State],
|
323
|
+
):
|
324
|
+
covariance: np.ndarray = LeastSquaresSolver.calculate_empirical_covariance(
|
325
|
+
observations
|
326
|
+
)
|
327
|
+
|
328
|
+
assert isinstance(covariance, np.ndarray)
|
329
|
+
assert covariance.shape == (2, 2)
|
330
|
+
|
331
|
+
def test_default(self):
|
332
|
+
default_solver: LeastSquaresSolver = LeastSquaresSolver.default()
|
333
|
+
|
334
|
+
assert isinstance(default_solver, LeastSquaresSolver)
|
ostk/astrodynamics/utilities.py
CHANGED
@@ -5,8 +5,6 @@ from __future__ import annotations
|
|
5
5
|
from datetime import datetime
|
6
6
|
from datetime import timezone
|
7
7
|
|
8
|
-
from .OpenSpaceToolkitAstrodynamicsPy import *
|
9
|
-
|
10
8
|
from ostk.physics import Environment
|
11
9
|
from ostk.physics.time import Scale
|
12
10
|
from ostk.physics.time import Instant
|
@@ -20,13 +18,16 @@ from ostk.physics.coordinate import Frame
|
|
20
18
|
from ostk.physics.environment.object.celestial import Earth
|
21
19
|
from ostk.physics.environment.gravitational import Earth as EarthGravitationalModel
|
22
20
|
|
21
|
+
from ostk.astrodynamics import Trajectory
|
22
|
+
from ostk.astrodynamics.trajectory import State
|
23
|
+
|
23
24
|
|
24
|
-
def lla_from_state(state:
|
25
|
+
def lla_from_state(state: State) -> LLA:
|
25
26
|
"""
|
26
27
|
Return latitude (degrees), longitude (degrees), altitude (meters) float list from a state.
|
27
28
|
|
28
29
|
Args:
|
29
|
-
state (
|
30
|
+
state (State): A state.
|
30
31
|
|
31
32
|
Returns:
|
32
33
|
LLA: The LLA.
|
@@ -115,7 +116,7 @@ def compute_aer(
|
|
115
116
|
|
116
117
|
|
117
118
|
def compute_time_lla_aer_coordinates(
|
118
|
-
state:
|
119
|
+
state: State,
|
119
120
|
from_position: Position,
|
120
121
|
environment: Environment,
|
121
122
|
) -> tuple[datetime, float, float, float, float, float, float]:
|
@@ -123,7 +124,7 @@ def compute_time_lla_aer_coordinates(
|
|
123
124
|
Return [datetime, latitude, longitude, altitude, azimuth, elevation, range] from State and observer Position.
|
124
125
|
|
125
126
|
Args:
|
126
|
-
state (
|
127
|
+
state (State): A state.
|
127
128
|
from_position (Position): An observer position.
|
128
129
|
environment (Environment): An environment.
|
129
130
|
|
@@ -209,7 +210,7 @@ def compute_ground_track(
|
|
209
210
|
|
210
211
|
|
211
212
|
def convert_state(
|
212
|
-
state:
|
213
|
+
state: State,
|
213
214
|
) -> tuple[str, float, float, float, float, float, float, float, float, float]:
|
214
215
|
"""
|
215
216
|
Convert a State into dataframe-ready values.
|
@@ -226,7 +227,7 @@ def convert_state(
|
|
226
227
|
- Altitude [meters] (float)
|
227
228
|
|
228
229
|
Args:
|
229
|
-
state (
|
230
|
+
state (State): A state.
|
230
231
|
|
231
232
|
Returns:
|
232
233
|
tuple[str, float, float, float, float, float, float, float, float, float]: The dataframe-ready values.
|
File without changes
|
File without changes
|
File without changes
|