open-space-toolkit-astrodynamics 15.0.0__py312-none-manylinux2014_x86_64.whl → 15.2.0__py312-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.0.0.dist-info → open_space_toolkit_astrodynamics-15.2.0.dist-info}/METADATA +1 -1
- {open_space_toolkit_astrodynamics-15.0.0.dist-info → open_space_toolkit_astrodynamics-15.2.0.dist-info}/RECORD +17 -13
- ostk/astrodynamics/OpenSpaceToolkitAstrodynamicsPy.cpython-312-x86_64-linux-gnu.so +0 -0
- ostk/astrodynamics/__init__.pyi +4 -3
- 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/test/test_viewer.py +64 -0
- ostk/astrodynamics/viewer.py +105 -4
- {open_space_toolkit_astrodynamics-15.0.0.dist-info → open_space_toolkit_astrodynamics-15.2.0.dist-info}/WHEEL +0 -0
- {open_space_toolkit_astrodynamics-15.0.0.dist-info → open_space_toolkit_astrodynamics-15.2.0.dist-info}/top_level.txt +0 -0
- {open_space_toolkit_astrodynamics-15.0.0.dist-info → open_space_toolkit_astrodynamics-15.2.0.dist-info}/zip-safe +0 -0
@@ -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)
|
@@ -0,0 +1,215 @@
|
|
1
|
+
# Apache License 2.0
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import pandas as pd
|
6
|
+
|
7
|
+
from ostk.core.type import Real
|
8
|
+
from ostk.core.type import Integer
|
9
|
+
|
10
|
+
from ostk.physics.coordinate import Frame
|
11
|
+
|
12
|
+
from ostk.astrodynamics.solver import LeastSquaresSolver
|
13
|
+
from ostk.astrodynamics.trajectory import State
|
14
|
+
from ostk.astrodynamics.trajectory import StateBuilder
|
15
|
+
from ostk.astrodynamics.trajectory import Orbit
|
16
|
+
from ostk.astrodynamics.trajectory.orbit.model.sgp4 import TLE
|
17
|
+
from ostk.astrodynamics.estimator import TLESolver
|
18
|
+
from ostk.astrodynamics.dataframe import generate_states_from_dataframe
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.fixture
|
22
|
+
def least_squares_solver() -> LeastSquaresSolver:
|
23
|
+
return LeastSquaresSolver.default()
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.fixture
|
27
|
+
def tle_solver(least_squares_solver: LeastSquaresSolver) -> TLESolver:
|
28
|
+
return TLESolver(
|
29
|
+
solver=least_squares_solver,
|
30
|
+
satellite_number=25544, # ISS NORAD ID
|
31
|
+
international_designator="98067A", # ISS Int'l Designator
|
32
|
+
revolution_number=12345,
|
33
|
+
estimate_b_star=True,
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
@pytest.fixture
|
38
|
+
def initial_tle() -> TLE:
|
39
|
+
return TLE(
|
40
|
+
"1 25544U 98067A 22253.00000622 .00000000 00000-0 71655-1 0 02",
|
41
|
+
"2 25544 97.5641 21.8296 0012030 155.5301 309.4836 15.14446734123455",
|
42
|
+
)
|
43
|
+
|
44
|
+
|
45
|
+
@pytest.fixture
|
46
|
+
def initial_state(observations: list[State]) -> State:
|
47
|
+
return observations[0]
|
48
|
+
|
49
|
+
|
50
|
+
@pytest.fixture
|
51
|
+
def initial_state_with_b_star(observations: list[State]) -> tuple[State, float]:
|
52
|
+
return observations[0], 1e-4
|
53
|
+
|
54
|
+
|
55
|
+
@pytest.fixture
|
56
|
+
def observations() -> list[State]:
|
57
|
+
return generate_states_from_dataframe(
|
58
|
+
pd.read_csv(
|
59
|
+
"/app/test/OpenSpaceToolkit/Astrodynamics/Estimator/OrbitDeterminationSolverData/gnss_data.csv"
|
60
|
+
),
|
61
|
+
reference_frame=Frame.ITRF(),
|
62
|
+
)
|
63
|
+
|
64
|
+
|
65
|
+
class TestTLESolver:
|
66
|
+
def test_constructor(
|
67
|
+
self,
|
68
|
+
tle_solver: TLESolver,
|
69
|
+
):
|
70
|
+
assert isinstance(tle_solver, TLESolver)
|
71
|
+
assert tle_solver.access_satellite_number() == 25544
|
72
|
+
assert tle_solver.access_international_designator() == "98067A"
|
73
|
+
assert tle_solver.access_revolution_number() == 12345
|
74
|
+
assert tle_solver.access_estimate_b_star() is True
|
75
|
+
|
76
|
+
def test_constructor_defaults(self):
|
77
|
+
solver = TLESolver()
|
78
|
+
assert solver.access_satellite_number() == 0
|
79
|
+
assert solver.access_international_designator() == "00001A"
|
80
|
+
assert solver.access_revolution_number() == 0
|
81
|
+
assert solver.access_estimate_b_star() is True
|
82
|
+
|
83
|
+
def test_access_methods(self, tle_solver: TLESolver):
|
84
|
+
assert isinstance(tle_solver.access_solver(), LeastSquaresSolver)
|
85
|
+
assert isinstance(tle_solver.access_default_b_star(), Real)
|
86
|
+
assert isinstance(
|
87
|
+
tle_solver.access_first_derivative_mean_motion_divided_by_2(), Real
|
88
|
+
)
|
89
|
+
assert isinstance(
|
90
|
+
tle_solver.access_second_derivative_mean_motion_divided_by_6(), Real
|
91
|
+
)
|
92
|
+
assert isinstance(tle_solver.access_ephemeris_type(), Integer)
|
93
|
+
assert isinstance(tle_solver.access_element_set_number(), Integer)
|
94
|
+
assert isinstance(tle_solver.access_tle_state_builder(), StateBuilder)
|
95
|
+
|
96
|
+
def test_estimate_from_tle(
|
97
|
+
self,
|
98
|
+
tle_solver: TLESolver,
|
99
|
+
initial_tle: TLE,
|
100
|
+
observations: list[State],
|
101
|
+
):
|
102
|
+
analysis: TLESolver.Analysis = tle_solver.estimate(
|
103
|
+
initial_guess=initial_tle,
|
104
|
+
observations=observations,
|
105
|
+
)
|
106
|
+
|
107
|
+
assert isinstance(analysis, TLESolver.Analysis)
|
108
|
+
assert isinstance(analysis.estimated_tle, TLE)
|
109
|
+
assert isinstance(analysis.solver_analysis, LeastSquaresSolver.Analysis)
|
110
|
+
|
111
|
+
assert analysis.solver_analysis.termination_criteria == "RMS Update Threshold"
|
112
|
+
|
113
|
+
def test_estimate_from_state_b_star(
|
114
|
+
self,
|
115
|
+
tle_solver: TLESolver,
|
116
|
+
initial_state: State,
|
117
|
+
observations: list[State],
|
118
|
+
):
|
119
|
+
analysis: TLESolver.Analysis = tle_solver.estimate(
|
120
|
+
initial_guess=(initial_state, 1e-4),
|
121
|
+
observations=observations,
|
122
|
+
)
|
123
|
+
assert isinstance(analysis, TLESolver.Analysis)
|
124
|
+
assert isinstance(analysis.estimated_tle, TLE)
|
125
|
+
assert isinstance(analysis.solver_analysis, LeastSquaresSolver.Analysis)
|
126
|
+
|
127
|
+
assert analysis.solver_analysis.termination_criteria == "RMS Update Threshold"
|
128
|
+
|
129
|
+
def test_estimate_from_state(
|
130
|
+
self,
|
131
|
+
initial_state: State,
|
132
|
+
observations: list[State],
|
133
|
+
):
|
134
|
+
tle_solver_no_b_star = TLESolver(
|
135
|
+
satellite_number=25544,
|
136
|
+
international_designator="98067A",
|
137
|
+
revolution_number=12345,
|
138
|
+
estimate_b_star=False,
|
139
|
+
)
|
140
|
+
analysis: TLESolver.Analysis = tle_solver_no_b_star.estimate(
|
141
|
+
initial_guess=initial_state,
|
142
|
+
observations=observations,
|
143
|
+
)
|
144
|
+
assert isinstance(analysis, TLESolver.Analysis)
|
145
|
+
assert isinstance(analysis.estimated_tle, TLE)
|
146
|
+
assert isinstance(analysis.solver_analysis, LeastSquaresSolver.Analysis)
|
147
|
+
|
148
|
+
assert analysis.solver_analysis.termination_criteria == "RMS Update Threshold"
|
149
|
+
|
150
|
+
def test_estimate_invalid_initial_guess(
|
151
|
+
self,
|
152
|
+
tle_solver: TLESolver,
|
153
|
+
observations: list[State],
|
154
|
+
):
|
155
|
+
with pytest.raises(RuntimeError) as e:
|
156
|
+
tle_solver.estimate(initial_guess="invalid", observations=observations)
|
157
|
+
assert "Initial guess must be a TLE, tuple[State, float], or State." in str(
|
158
|
+
e.value
|
159
|
+
)
|
160
|
+
|
161
|
+
def test_estimate_invalid_state_only(
|
162
|
+
self,
|
163
|
+
tle_solver: TLESolver,
|
164
|
+
initial_state: State,
|
165
|
+
observations: list[State],
|
166
|
+
):
|
167
|
+
with pytest.raises(RuntimeError) as e:
|
168
|
+
tle_solver.estimate(initial_guess=initial_state, observations=observations)
|
169
|
+
assert (
|
170
|
+
"Initial guess must be a TLE or (State, B*) when also estimating B*."
|
171
|
+
in str(e.value)
|
172
|
+
)
|
173
|
+
|
174
|
+
def test_estimate_no_observations(
|
175
|
+
self,
|
176
|
+
tle_solver: TLESolver,
|
177
|
+
initial_state: State,
|
178
|
+
):
|
179
|
+
with pytest.raises(Exception):
|
180
|
+
tle_solver.estimate(initial_guess=initial_state, observations=[])
|
181
|
+
|
182
|
+
def test_estimate_orbit(
|
183
|
+
self,
|
184
|
+
initial_state: State,
|
185
|
+
observations: list[State],
|
186
|
+
):
|
187
|
+
tle_solver_no_b_star = TLESolver(
|
188
|
+
satellite_number=25544,
|
189
|
+
international_designator="98067A",
|
190
|
+
revolution_number=12345,
|
191
|
+
estimate_b_star=False,
|
192
|
+
)
|
193
|
+
orbit: Orbit = tle_solver_no_b_star.estimate_orbit(
|
194
|
+
initial_guess=initial_state,
|
195
|
+
observations=observations,
|
196
|
+
)
|
197
|
+
|
198
|
+
assert isinstance(orbit, Orbit)
|
199
|
+
|
200
|
+
def test_fit_with_different_frames(
|
201
|
+
self,
|
202
|
+
tle_solver: TLESolver,
|
203
|
+
initial_state_with_b_star: tuple[State, float],
|
204
|
+
observations: list[State],
|
205
|
+
):
|
206
|
+
# Convert observations to TEME frame
|
207
|
+
teme_observations = [obs.in_frame(Frame.TEME()) for obs in observations]
|
208
|
+
|
209
|
+
analysis: TLESolver.Analysis = tle_solver.estimate(
|
210
|
+
initial_guess=initial_state_with_b_star, observations=teme_observations
|
211
|
+
)
|
212
|
+
assert isinstance(analysis, TLESolver.Analysis)
|
213
|
+
assert isinstance(analysis.estimated_tle, TLE)
|
214
|
+
|
215
|
+
assert analysis.solver_analysis.termination_criteria == "RMS Update Threshold"
|