open-space-toolkit-astrodynamics 15.0.0__py313-none-manylinux2014_x86_64.whl → 15.2.0__py313-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.
@@ -3,7 +3,7 @@ import numpy
3
3
  import ostk.core.type
4
4
  import ostk.physics.time
5
5
  import typing
6
- __all__ = ['FiniteDifferenceSolver', 'TemporalConditionSolver']
6
+ __all__ = ['FiniteDifferenceSolver', 'LeastSquaresSolver', 'TemporalConditionSolver']
7
7
  class FiniteDifferenceSolver:
8
8
  """
9
9
 
@@ -166,6 +166,214 @@ class FiniteDifferenceSolver:
166
166
  Returns:
167
167
  FiniteDifferenceSolver.Type: The type.
168
168
  """
169
+ class LeastSquaresSolver:
170
+ """
171
+
172
+ Class to solve non-linear least squares problems.
173
+
174
+ """
175
+ class Analysis:
176
+ """
177
+
178
+ Class representing the analysis of the least squares solver.
179
+
180
+ """
181
+ @staticmethod
182
+ def _pybind11_conduit_v1_(*args, **kwargs):
183
+ ...
184
+ def __init__(self, termination_criteria: ostk.core.type.String, estimated_state: typing.Any, estimated_covariance: numpy.ndarray[numpy.float64[m, n]], estimated_frisbee_covariance: numpy.ndarray[numpy.float64[m, n]], computed_observations: list[...], steps: list[LeastSquaresSolver.Step]) -> None:
185
+ """
186
+ Constructor.
187
+
188
+ Args:
189
+ termination_criteria (str): The termination criteria.
190
+ estimated_state (State): The estimated state.
191
+ estimated_covariance (np.ndarray): The estimated covariance matrix.
192
+ estimated_frisbee_covariance (np.ndarray): The estimated Frisbee covariance matrix.
193
+ computed_observations (list[State]): The computed observations of the final iteration.
194
+ steps (list[LeastSquaresSolver.Step]): The steps.
195
+ """
196
+ def __repr__(self) -> str:
197
+ ...
198
+ def __str__(self) -> str:
199
+ ...
200
+ def compute_residual_states(self, observations: list[...]) -> list[...]:
201
+ """
202
+ Compute the residual states.
203
+
204
+ Args:
205
+ observations (list[State]): The observations.
206
+
207
+ Returns:
208
+ list[State]: The residuals.
209
+ """
210
+ @property
211
+ def computed_observations(self) -> list[...]:
212
+ """
213
+ The computed observations of the final iteration.
214
+
215
+ :type: np.ndarray
216
+ """
217
+ @property
218
+ def estimated_covariance(self) -> numpy.ndarray[numpy.float64[m, n]]:
219
+ """
220
+ The estimated covariance matrix.
221
+
222
+ :type: np.ndarray
223
+ """
224
+ @property
225
+ def estimated_frisbee_covariance(self) -> numpy.ndarray[numpy.float64[m, n]]:
226
+ """
227
+ The estimated Frisbee covariance matrix.
228
+
229
+ :type: np.ndarray
230
+ """
231
+ @property
232
+ def estimated_state(self) -> ...:
233
+ """
234
+ The estimated state.
235
+
236
+ :type: State
237
+ """
238
+ @property
239
+ def iteration_count(self) -> int:
240
+ """
241
+ The iteration count.
242
+
243
+ :type: int
244
+ """
245
+ @property
246
+ def observation_count(self) -> int:
247
+ """
248
+ The observation count.
249
+
250
+ :type: int
251
+ """
252
+ @property
253
+ def rms_error(self) -> ostk.core.type.Real:
254
+ """
255
+ The RMS error.
256
+
257
+ :type: float
258
+ """
259
+ @property
260
+ def steps(self) -> list[LeastSquaresSolver.Step]:
261
+ """
262
+ The steps.
263
+
264
+ :type: list[LeastSquaresSolver.Step]
265
+ """
266
+ @property
267
+ def termination_criteria(self) -> ostk.core.type.String:
268
+ """
269
+ The termination criteria.
270
+
271
+ :type: str
272
+ """
273
+ class Step:
274
+ """
275
+
276
+ Class representing a step in the least squares solver.
277
+
278
+ """
279
+ @staticmethod
280
+ def _pybind11_conduit_v1_(*args, **kwargs):
281
+ ...
282
+ def __init__(self, rms_error: ostk.core.type.Real, x_hat: numpy.ndarray[numpy.float64[m, 1]]) -> None:
283
+ """
284
+ Constructor.
285
+
286
+ Args:
287
+ rms_error (float): The RMS error.
288
+ x_hat (np.ndarray): The X hat vector.
289
+ """
290
+ def __repr__(self) -> str:
291
+ ...
292
+ def __str__(self) -> str:
293
+ ...
294
+ @property
295
+ def rms_error(self) -> ostk.core.type.Real:
296
+ """
297
+ The RMS error.
298
+
299
+ :type: float
300
+ """
301
+ @property
302
+ def x_hat(self) -> numpy.ndarray[numpy.float64[m, 1]]:
303
+ """
304
+ The X hat vector.
305
+
306
+ :type: np.ndarray
307
+ """
308
+ @staticmethod
309
+ def _pybind11_conduit_v1_(*args, **kwargs):
310
+ ...
311
+ @staticmethod
312
+ def calculate_empirical_covariance(residuals: list[...]) -> numpy.ndarray[numpy.float64[m, n]]:
313
+ """
314
+ Calculate the empirical covariance matrix from an array of state residuals.
315
+
316
+ Args:
317
+ residuals (list[State]): A list of state residuals.
318
+
319
+ Returns:
320
+ np.ndarray: The empirical covariance matrix.
321
+
322
+ Throws:
323
+ ostk::core::error::runtime::Undefined: If the residual array is empty.
324
+ """
325
+ @staticmethod
326
+ def default() -> LeastSquaresSolver:
327
+ """
328
+ Create a default instance of LeastSquaresSolver.
329
+
330
+ Returns:
331
+ LeastSquaresSolver: A default instance of LeastSquaresSolver.
332
+ """
333
+ def __init__(self, maximum_iteration_count: int, rms_update_threshold: ostk.core.type.Real, finite_difference_solver: FiniteDifferenceSolver = ...) -> None:
334
+ """
335
+ Constructor.
336
+
337
+ Args:
338
+ maximum_iteration_count (int): Maximum number of iterations.
339
+ rms_update_threshold (float): Minimum RMS threshold.
340
+ finite_difference_solver (FiniteDifferenceSolver): Finite difference solver. Defaults to FiniteDifferenceSolver.Default().
341
+ """
342
+ def get_finite_difference_solver(self) -> FiniteDifferenceSolver:
343
+ """
344
+ Get the finite difference solver.
345
+
346
+ Returns:
347
+ FiniteDifferenceSolver: The finite difference solver.
348
+ """
349
+ def get_max_iteration_count(self) -> int:
350
+ """
351
+ Get the maximum iteration count.
352
+
353
+ Returns:
354
+ int: The maximum iteration count.
355
+ """
356
+ def get_rms_update_threshold(self) -> ostk.core.type.Real:
357
+ """
358
+ Get the RMS update threshold.
359
+
360
+ Returns:
361
+ float: The RMS update threshold.
362
+ """
363
+ def solve(self, initial_guess: typing.Any, observations: list[...], state_generator: typing.Callable[[..., list[ostk.physics.time.Instant]], list[...]], initial_guess_sigmas: dict[..., numpy.ndarray[numpy.float64[m, 1]]] = {}, observation_sigmas: dict[..., numpy.ndarray[numpy.float64[m, 1]]] = {}) -> LeastSquaresSolver.Analysis:
364
+ """
365
+ Solve the non-linear least squares problem.
366
+
367
+ Args:
368
+ initial_guess (State): Initial guess state (the Estimated State is of the same domain as this state).
369
+ observations (list[State]): List of observations.
370
+ state_generator (callable[list[State],[State, list[Instant]]]): Function to generate states.
371
+ initial_guess_sigmas (dict[CoordinateSubset, np.ndarray], optional): Dictionary of sigmas for initial guess.
372
+ observation_sigmas (dict[CoordinateSubset, np.ndarray], optional): Dictionary of sigmas for observations.
373
+
374
+ Returns:
375
+ LeastSquaresSolver::Analysis: The analysis of the estimate.
376
+ """
169
377
  class TemporalConditionSolver:
170
378
  """
171
379
 
@@ -9,6 +9,7 @@ from ostk.physics import Environment
9
9
  from ostk.physics.time import Instant
10
10
  from ostk.physics.time import DateTime
11
11
  from ostk.physics.time import Scale
12
+ from ostk.physics.unit import Angle
12
13
 
13
14
  from ostk.astrodynamics.access import VisibilityCriterion
14
15
 
@@ -152,9 +153,11 @@ class TestVisibilityCriterion:
152
153
  elevation_criterion = VisibilityCriterion.ElevationInterval(elevation_interval)
153
154
  elevation_valid = np.pi / 8 # 22.5 degrees
154
155
  assert elevation_criterion.is_satisfied(elevation_valid) is True
156
+ assert elevation_criterion.is_satisfied(Angle.radians(elevation_valid)) is True
155
157
 
156
158
  elevation_invalid = np.pi / 2 # 90 degrees
157
159
  assert elevation_criterion.is_satisfied(elevation_invalid) is False
160
+ assert elevation_criterion.is_satisfied(Angle.radians(elevation_invalid)) is False
158
161
 
159
162
  def test_line_of_sight_is_satisfied(
160
163
  self,
@@ -0,0 +1,261 @@
1
+ # Apache License 2.0
2
+
3
+ import pytest
4
+
5
+ import pandas as pd
6
+
7
+ import numpy as np
8
+
9
+ from ostk.physics import Environment
10
+ from ostk.physics.time import Instant
11
+ from ostk.physics.coordinate import Frame
12
+ from ostk.physics.environment.object.celestial import Earth
13
+
14
+ from ostk.astrodynamics.solver import LeastSquaresSolver
15
+ from ostk.astrodynamics.trajectory import State
16
+ from ostk.astrodynamics.trajectory import Orbit
17
+ from ostk.astrodynamics.trajectory import Propagator
18
+ from ostk.astrodynamics.trajectory.state import NumericalSolver
19
+ from ostk.astrodynamics.trajectory.state import CoordinateSubset
20
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianPosition
21
+ from ostk.astrodynamics.trajectory.state.coordinate_subset import CartesianVelocity
22
+ from ostk.astrodynamics.estimator import OrbitDeterminationSolver
23
+ from ostk.astrodynamics.dataframe import generate_states_from_dataframe
24
+
25
+
26
+ @pytest.fixture
27
+ def environment() -> Environment:
28
+ return Environment(central_celestial_object=Earth.EGM96(10, 10), objects=[])
29
+
30
+
31
+ @pytest.fixture
32
+ def numerical_solver() -> NumericalSolver:
33
+ return NumericalSolver.default()
34
+
35
+
36
+ @pytest.fixture
37
+ def least_squares_solver() -> LeastSquaresSolver:
38
+ return LeastSquaresSolver.default()
39
+
40
+
41
+ @pytest.fixture
42
+ def orbit_determination_solver(
43
+ environment: Environment,
44
+ numerical_solver: NumericalSolver,
45
+ least_squares_solver: LeastSquaresSolver,
46
+ ) -> OrbitDeterminationSolver:
47
+ return OrbitDeterminationSolver(
48
+ environment=environment,
49
+ numerical_solver=numerical_solver,
50
+ solver=least_squares_solver,
51
+ )
52
+
53
+
54
+ @pytest.fixture
55
+ def coordinate_subsets() -> list[CoordinateSubset]:
56
+ return [
57
+ CartesianPosition.default(),
58
+ CartesianVelocity.default(),
59
+ ]
60
+
61
+
62
+ @pytest.fixture
63
+ def initial_guess(
64
+ observations: list[State],
65
+ ) -> State:
66
+ return observations[0]
67
+
68
+
69
+ @pytest.fixture
70
+ def observations() -> list[State]:
71
+ return generate_states_from_dataframe(
72
+ pd.read_csv(
73
+ "/app/test/OpenSpaceToolkit/Astrodynamics/Estimator/OrbitDeterminationSolverData/gnss_data.csv"
74
+ ),
75
+ reference_frame=Frame.ITRF(),
76
+ )
77
+
78
+
79
+ @pytest.fixture
80
+ def initial_guess_sigmas(
81
+ coordinate_subsets: list[CoordinateSubset],
82
+ ) -> dict[CoordinateSubset, list[float]]:
83
+ return {
84
+ coordinate_subsets[0]: [1e-1, 1e-1, 1e-1],
85
+ coordinate_subsets[1]: [1e-2, 1e-2, 1e-2],
86
+ }
87
+
88
+
89
+ @pytest.fixture
90
+ def observation_sigmas(
91
+ coordinate_subsets: list[CoordinateSubset],
92
+ ) -> dict[CoordinateSubset, list[float]]:
93
+ return {
94
+ coordinate_subsets[0]: [1e-1, 1e-1, 1e-1],
95
+ coordinate_subsets[1]: [1e-2, 1e-2, 1e-2],
96
+ }
97
+
98
+
99
+ @pytest.fixture
100
+ def rms_error() -> float:
101
+ return 1.0
102
+
103
+
104
+ @pytest.fixture
105
+ def x_hat() -> np.ndarray:
106
+ return np.array([1.0, 0.0])
107
+
108
+
109
+ @pytest.fixture
110
+ def step(
111
+ rms_error: float,
112
+ x_hat: np.ndarray,
113
+ ) -> LeastSquaresSolver.Step:
114
+ return LeastSquaresSolver.Step(
115
+ rms_error=rms_error,
116
+ x_hat=x_hat,
117
+ )
118
+
119
+
120
+ @pytest.fixture
121
+ def observation_count() -> int:
122
+ return 5
123
+
124
+
125
+ @pytest.fixture
126
+ def termination_criteria() -> str:
127
+ return "RMS Update Threshold"
128
+
129
+
130
+ @pytest.fixture
131
+ def estimated_state() -> State:
132
+ return State(
133
+ Instant.J2000(),
134
+ [1.0, 0.0],
135
+ Frame.GCRF(),
136
+ [CoordinateSubset("Position", 1), CoordinateSubset("Velocity", 1)],
137
+ )
138
+
139
+
140
+ @pytest.fixture
141
+ def estimated_covariance() -> np.ndarray:
142
+ return np.array([[1.0, 0.0], [0.0, 1.0]])
143
+
144
+
145
+ @pytest.fixture
146
+ def estimated_frisbee_covariance() -> np.ndarray:
147
+ return np.array([[1.0, 0.0], [0.0, 1.0]])
148
+
149
+
150
+ @pytest.fixture
151
+ def computed_observations(
152
+ observations: list[State],
153
+ ) -> list[State]:
154
+ return observations
155
+
156
+
157
+ @pytest.fixture
158
+ def steps(step: LeastSquaresSolver.Step) -> list[LeastSquaresSolver.Step]:
159
+ return [step]
160
+
161
+
162
+ @pytest.fixture
163
+ def solver_analysis(
164
+ termination_criteria: str,
165
+ estimated_state: State,
166
+ estimated_covariance: np.ndarray,
167
+ estimated_frisbee_covariance: np.ndarray,
168
+ computed_observations: list[State],
169
+ steps: list[LeastSquaresSolver.Step],
170
+ ) -> LeastSquaresSolver.Analysis:
171
+ return LeastSquaresSolver.Analysis(
172
+ termination_criteria=termination_criteria,
173
+ estimated_state=estimated_state,
174
+ estimated_covariance=estimated_covariance,
175
+ estimated_frisbee_covariance=estimated_frisbee_covariance,
176
+ computed_observations=computed_observations,
177
+ steps=steps,
178
+ )
179
+
180
+
181
+ @pytest.fixture
182
+ def analysis(
183
+ initial_guess: State,
184
+ solver_analysis: LeastSquaresSolver.Analysis,
185
+ ) -> OrbitDeterminationSolver.Analysis:
186
+ return OrbitDeterminationSolver.Analysis(
187
+ estimated_state=initial_guess,
188
+ solver_analysis=solver_analysis,
189
+ )
190
+
191
+
192
+ class TestOrbitDeterminationSolverAnalysis:
193
+ def test_constructor(
194
+ self,
195
+ analysis: OrbitDeterminationSolver.Analysis,
196
+ ):
197
+ assert isinstance(analysis, OrbitDeterminationSolver.Analysis)
198
+
199
+ def test_properties(
200
+ self,
201
+ analysis: OrbitDeterminationSolver.Analysis,
202
+ ):
203
+ assert isinstance(analysis.estimated_state, State)
204
+ assert isinstance(analysis.solver_analysis, LeastSquaresSolver.Analysis)
205
+
206
+
207
+ class TestOrbitDeterminationSolver:
208
+ def test_constructor(
209
+ self,
210
+ orbit_determination_solver: OrbitDeterminationSolver,
211
+ ):
212
+ assert isinstance(orbit_determination_solver, OrbitDeterminationSolver)
213
+
214
+ def test_access_methods(
215
+ self,
216
+ orbit_determination_solver: OrbitDeterminationSolver,
217
+ ):
218
+ assert isinstance(orbit_determination_solver.access_environment(), Environment)
219
+ assert isinstance(orbit_determination_solver.access_propagator(), Propagator)
220
+ assert isinstance(orbit_determination_solver.access_solver(), LeastSquaresSolver)
221
+ assert isinstance(orbit_determination_solver.access_estimation_frame(), Frame)
222
+
223
+ def test_estimate(
224
+ self,
225
+ orbit_determination_solver: OrbitDeterminationSolver,
226
+ initial_guess: State,
227
+ observations: list[State],
228
+ coordinate_subsets: list[CoordinateSubset],
229
+ initial_guess_sigmas: dict[CoordinateSubset, list[float]],
230
+ observation_sigmas: dict[CoordinateSubset, list[float]],
231
+ ):
232
+ analysis: OrbitDeterminationSolver.Analysis = orbit_determination_solver.estimate(
233
+ initial_guess=initial_guess,
234
+ observations=observations,
235
+ estimation_coordinate_subsets=coordinate_subsets,
236
+ initial_guess_sigmas=initial_guess_sigmas,
237
+ observation_sigmas=observation_sigmas,
238
+ )
239
+
240
+ assert isinstance(analysis, OrbitDeterminationSolver.Analysis)
241
+ assert isinstance(analysis.estimated_state, State)
242
+ assert isinstance(analysis.solver_analysis, LeastSquaresSolver.Analysis)
243
+
244
+ def test_estimate_orbit(
245
+ self,
246
+ orbit_determination_solver: OrbitDeterminationSolver,
247
+ initial_guess: State,
248
+ observations: list[State],
249
+ coordinate_subsets: list[CoordinateSubset],
250
+ initial_guess_sigmas: dict[CoordinateSubset, list[float]],
251
+ observation_sigmas: dict[CoordinateSubset, list[float]],
252
+ ):
253
+ orbit: Orbit = orbit_determination_solver.estimate_orbit(
254
+ initial_guess=initial_guess,
255
+ observations=observations,
256
+ estimation_coordinate_subsets=coordinate_subsets,
257
+ initial_guess_sigmas=initial_guess_sigmas,
258
+ observation_sigmas=observation_sigmas,
259
+ )
260
+
261
+ assert isinstance(orbit, Orbit)