nrl-tracker 1.8.0__py3-none-any.whl → 1.9.1__py3-none-any.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.
- {nrl_tracker-1.8.0.dist-info → nrl_tracker-1.9.1.dist-info}/METADATA +2 -2
- {nrl_tracker-1.8.0.dist-info → nrl_tracker-1.9.1.dist-info}/RECORD +32 -28
- pytcl/__init__.py +3 -3
- pytcl/assignment_algorithms/dijkstra_min_cost.py +0 -1
- pytcl/assignment_algorithms/network_simplex.py +0 -2
- pytcl/astronomical/ephemerides.py +8 -4
- pytcl/astronomical/relativity.py +20 -0
- pytcl/containers/__init__.py +19 -8
- pytcl/containers/base.py +82 -9
- pytcl/containers/covertree.py +14 -21
- pytcl/containers/kd_tree.py +18 -45
- pytcl/containers/rtree.py +43 -4
- pytcl/containers/vptree.py +14 -21
- pytcl/core/__init__.py +59 -2
- pytcl/core/constants.py +59 -0
- pytcl/core/exceptions.py +865 -0
- pytcl/core/optional_deps.py +531 -0
- pytcl/core/validation.py +4 -6
- pytcl/dynamic_estimation/kalman/matrix_utils.py +427 -0
- pytcl/dynamic_estimation/kalman/square_root.py +20 -213
- pytcl/dynamic_estimation/kalman/sr_ukf.py +5 -5
- pytcl/dynamic_estimation/kalman/types.py +98 -0
- pytcl/mathematical_functions/signal_processing/detection.py +19 -0
- pytcl/mathematical_functions/transforms/wavelets.py +7 -6
- pytcl/plotting/coordinates.py +25 -27
- pytcl/plotting/ellipses.py +14 -16
- pytcl/plotting/metrics.py +7 -5
- pytcl/plotting/tracks.py +8 -7
- pytcl/terrain/loaders.py +10 -6
- {nrl_tracker-1.8.0.dist-info → nrl_tracker-1.9.1.dist-info}/LICENSE +0 -0
- {nrl_tracker-1.8.0.dist-info → nrl_tracker-1.9.1.dist-info}/WHEEL +0 -0
- {nrl_tracker-1.8.0.dist-info → nrl_tracker-1.9.1.dist-info}/top_level.txt +0 -0
pytcl/core/exceptions.py
ADDED
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Custom exception hierarchy for the Tracker Component Library.
|
|
3
|
+
|
|
4
|
+
This module provides a structured exception hierarchy for consistent error
|
|
5
|
+
handling across the library. Custom exceptions enable more specific error
|
|
6
|
+
catching, better error messages, and improved debugging.
|
|
7
|
+
|
|
8
|
+
Exception Hierarchy
|
|
9
|
+
-------------------
|
|
10
|
+
TCLError (base)
|
|
11
|
+
├── ValidationError (input validation failures)
|
|
12
|
+
│ ├── DimensionError (array shape/dimension mismatches)
|
|
13
|
+
│ ├── ParameterError (invalid parameter values)
|
|
14
|
+
│ └── RangeError (out-of-range values)
|
|
15
|
+
├── ComputationError (numerical computation failures)
|
|
16
|
+
│ ├── ConvergenceError (iterative algorithm non-convergence)
|
|
17
|
+
│ ├── NumericalError (numerical stability issues)
|
|
18
|
+
│ └── SingularMatrixError (singular/non-invertible matrix)
|
|
19
|
+
├── StateError (object state violations)
|
|
20
|
+
│ ├── UninitializedError (object not initialized)
|
|
21
|
+
│ └── EmptyContainerError (container has no elements)
|
|
22
|
+
├── ConfigurationError (configuration/setup issues)
|
|
23
|
+
│ ├── MethodError (invalid method/algorithm selection)
|
|
24
|
+
│ └── DependencyError (missing optional dependency)
|
|
25
|
+
└── DataError (data format/structure issues)
|
|
26
|
+
├── FormatError (invalid data format)
|
|
27
|
+
└── ParseError (data parsing failures)
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
Catching specific exception types:
|
|
32
|
+
|
|
33
|
+
>>> from pytcl.core.exceptions import ConvergenceError, ParameterError
|
|
34
|
+
>>> try:
|
|
35
|
+
... result = solve_kepler(M=1.5, e=1.5) # Invalid eccentricity
|
|
36
|
+
... except ParameterError as e:
|
|
37
|
+
... print(f"Invalid parameter: {e}")
|
|
38
|
+
|
|
39
|
+
Catching all TCL errors:
|
|
40
|
+
|
|
41
|
+
>>> from pytcl.core.exceptions import TCLError
|
|
42
|
+
>>> try:
|
|
43
|
+
... result = compute_orbit(...)
|
|
44
|
+
... except TCLError as e:
|
|
45
|
+
... print(f"TCL error: {e}")
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from typing import Any, Optional, Sequence
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TCLError(Exception):
|
|
52
|
+
"""
|
|
53
|
+
Base exception for all Tracker Component Library errors.
|
|
54
|
+
|
|
55
|
+
All custom exceptions in the library inherit from this class,
|
|
56
|
+
allowing users to catch all TCL-specific errors with a single
|
|
57
|
+
except clause.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
message : str
|
|
62
|
+
Human-readable error message.
|
|
63
|
+
details : dict, optional
|
|
64
|
+
Additional context about the error (parameter values, etc.).
|
|
65
|
+
|
|
66
|
+
Examples
|
|
67
|
+
--------
|
|
68
|
+
>>> raise TCLError("Something went wrong", details={"value": 42})
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, message: str, details: Optional[dict[str, Any]] = None):
|
|
72
|
+
self.message = message
|
|
73
|
+
self.details = details or {}
|
|
74
|
+
super().__init__(message)
|
|
75
|
+
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
if self.details:
|
|
78
|
+
detail_str = ", ".join(f"{k}={v}" for k, v in self.details.items())
|
|
79
|
+
return f"{self.message} ({detail_str})"
|
|
80
|
+
return self.message
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# =============================================================================
|
|
84
|
+
# Validation Errors
|
|
85
|
+
# =============================================================================
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ValidationError(TCLError, ValueError):
|
|
89
|
+
"""
|
|
90
|
+
Base exception for input validation failures.
|
|
91
|
+
|
|
92
|
+
Raised when function inputs fail validation checks. This extends
|
|
93
|
+
both TCLError (for TCL-specific catching) and ValueError (for
|
|
94
|
+
compatibility with code expecting standard Python exceptions).
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
message : str
|
|
99
|
+
Description of the validation failure.
|
|
100
|
+
parameter : str, optional
|
|
101
|
+
Name of the parameter that failed validation.
|
|
102
|
+
expected : str, optional
|
|
103
|
+
Description of what was expected.
|
|
104
|
+
actual : Any, optional
|
|
105
|
+
The actual value that was provided.
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
>>> raise ValidationError(
|
|
110
|
+
... "Invalid matrix dimensions",
|
|
111
|
+
... parameter="P",
|
|
112
|
+
... expected="3x3 matrix",
|
|
113
|
+
... actual="2x4 array"
|
|
114
|
+
... )
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
message: str,
|
|
120
|
+
parameter: Optional[str] = None,
|
|
121
|
+
expected: Optional[str] = None,
|
|
122
|
+
actual: Optional[Any] = None,
|
|
123
|
+
**kwargs: Any,
|
|
124
|
+
):
|
|
125
|
+
details: dict[str, Any] = {}
|
|
126
|
+
if parameter is not None:
|
|
127
|
+
details["parameter"] = parameter
|
|
128
|
+
if expected is not None:
|
|
129
|
+
details["expected"] = expected
|
|
130
|
+
if actual is not None:
|
|
131
|
+
details["actual"] = actual
|
|
132
|
+
details.update(kwargs)
|
|
133
|
+
super().__init__(message, details)
|
|
134
|
+
self.parameter = parameter
|
|
135
|
+
self.expected = expected
|
|
136
|
+
self.actual = actual
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class DimensionError(ValidationError):
|
|
140
|
+
"""
|
|
141
|
+
Exception for array dimension and shape mismatches.
|
|
142
|
+
|
|
143
|
+
Raised when array dimensions or shapes don't match requirements
|
|
144
|
+
or aren't compatible with each other.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
message : str
|
|
149
|
+
Description of the dimension mismatch.
|
|
150
|
+
expected_shape : tuple, optional
|
|
151
|
+
Expected shape.
|
|
152
|
+
actual_shape : tuple, optional
|
|
153
|
+
Actual shape.
|
|
154
|
+
parameter : str, optional
|
|
155
|
+
Name of the parameter.
|
|
156
|
+
|
|
157
|
+
Examples
|
|
158
|
+
--------
|
|
159
|
+
>>> raise DimensionError(
|
|
160
|
+
... "Covariance matrix must be 3x3",
|
|
161
|
+
... expected_shape=(3, 3),
|
|
162
|
+
... actual_shape=(2, 4),
|
|
163
|
+
... parameter="P"
|
|
164
|
+
... )
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
message: str,
|
|
170
|
+
expected_shape: Optional[tuple[int, ...]] = None,
|
|
171
|
+
actual_shape: Optional[tuple[int, ...]] = None,
|
|
172
|
+
parameter: Optional[str] = None,
|
|
173
|
+
**kwargs: Any,
|
|
174
|
+
):
|
|
175
|
+
details: dict[str, Any] = {}
|
|
176
|
+
if expected_shape is not None:
|
|
177
|
+
details["expected_shape"] = expected_shape
|
|
178
|
+
if actual_shape is not None:
|
|
179
|
+
details["actual_shape"] = actual_shape
|
|
180
|
+
super().__init__(message, parameter=parameter, **details, **kwargs)
|
|
181
|
+
self.expected_shape = expected_shape
|
|
182
|
+
self.actual_shape = actual_shape
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class ParameterError(ValidationError):
|
|
186
|
+
"""
|
|
187
|
+
Exception for invalid parameter values.
|
|
188
|
+
|
|
189
|
+
Raised when a parameter value violates constraints (type, value
|
|
190
|
+
range, allowed values, etc.).
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
message : str
|
|
195
|
+
Description of the invalid parameter.
|
|
196
|
+
parameter : str, optional
|
|
197
|
+
Name of the invalid parameter.
|
|
198
|
+
value : Any, optional
|
|
199
|
+
The invalid value.
|
|
200
|
+
constraint : str, optional
|
|
201
|
+
Description of the constraint that was violated.
|
|
202
|
+
|
|
203
|
+
Examples
|
|
204
|
+
--------
|
|
205
|
+
>>> raise ParameterError(
|
|
206
|
+
... "Variance must be positive",
|
|
207
|
+
... parameter="variance",
|
|
208
|
+
... value=-1.0,
|
|
209
|
+
... constraint="must be > 0"
|
|
210
|
+
... )
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
def __init__(
|
|
214
|
+
self,
|
|
215
|
+
message: str,
|
|
216
|
+
parameter: Optional[str] = None,
|
|
217
|
+
value: Optional[Any] = None,
|
|
218
|
+
constraint: Optional[str] = None,
|
|
219
|
+
**kwargs: Any,
|
|
220
|
+
):
|
|
221
|
+
details: dict[str, Any] = {}
|
|
222
|
+
if value is not None:
|
|
223
|
+
details["value"] = value
|
|
224
|
+
if constraint is not None:
|
|
225
|
+
details["constraint"] = constraint
|
|
226
|
+
super().__init__(message, parameter=parameter, **details, **kwargs)
|
|
227
|
+
self.value = value
|
|
228
|
+
self.constraint = constraint
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class RangeError(ValidationError):
|
|
232
|
+
"""
|
|
233
|
+
Exception for out-of-range values.
|
|
234
|
+
|
|
235
|
+
Raised when a numeric value falls outside an allowed range.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
message : str
|
|
240
|
+
Description of the range violation.
|
|
241
|
+
parameter : str, optional
|
|
242
|
+
Name of the parameter.
|
|
243
|
+
value : float, optional
|
|
244
|
+
The out-of-range value.
|
|
245
|
+
min_value : float, optional
|
|
246
|
+
Minimum allowed value.
|
|
247
|
+
max_value : float, optional
|
|
248
|
+
Maximum allowed value.
|
|
249
|
+
|
|
250
|
+
Examples
|
|
251
|
+
--------
|
|
252
|
+
>>> raise RangeError(
|
|
253
|
+
... "Eccentricity must be in [0, 1) for elliptic orbits",
|
|
254
|
+
... parameter="e",
|
|
255
|
+
... value=1.5,
|
|
256
|
+
... min_value=0.0,
|
|
257
|
+
... max_value=1.0
|
|
258
|
+
... )
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
def __init__(
|
|
262
|
+
self,
|
|
263
|
+
message: str,
|
|
264
|
+
parameter: Optional[str] = None,
|
|
265
|
+
value: Optional[float] = None,
|
|
266
|
+
min_value: Optional[float] = None,
|
|
267
|
+
max_value: Optional[float] = None,
|
|
268
|
+
**kwargs: Any,
|
|
269
|
+
):
|
|
270
|
+
details: dict[str, Any] = {}
|
|
271
|
+
if value is not None:
|
|
272
|
+
details["value"] = value
|
|
273
|
+
if min_value is not None:
|
|
274
|
+
details["min"] = min_value
|
|
275
|
+
if max_value is not None:
|
|
276
|
+
details["max"] = max_value
|
|
277
|
+
super().__init__(message, parameter=parameter, **details, **kwargs)
|
|
278
|
+
self.value = value
|
|
279
|
+
self.min_value = min_value
|
|
280
|
+
self.max_value = max_value
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# =============================================================================
|
|
284
|
+
# Computation Errors
|
|
285
|
+
# =============================================================================
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class ComputationError(TCLError, RuntimeError):
|
|
289
|
+
"""
|
|
290
|
+
Base exception for numerical computation failures.
|
|
291
|
+
|
|
292
|
+
Raised when a numerical algorithm fails to produce a valid result.
|
|
293
|
+
This extends RuntimeError for compatibility with code that catches
|
|
294
|
+
standard computation errors.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
message : str
|
|
299
|
+
Description of the computation failure.
|
|
300
|
+
algorithm : str, optional
|
|
301
|
+
Name of the algorithm that failed.
|
|
302
|
+
|
|
303
|
+
Examples
|
|
304
|
+
--------
|
|
305
|
+
>>> raise ComputationError(
|
|
306
|
+
... "Failed to compute eigenvalues",
|
|
307
|
+
... algorithm="numpy.linalg.eig"
|
|
308
|
+
... )
|
|
309
|
+
"""
|
|
310
|
+
|
|
311
|
+
def __init__(
|
|
312
|
+
self,
|
|
313
|
+
message: str,
|
|
314
|
+
algorithm: Optional[str] = None,
|
|
315
|
+
**kwargs: Any,
|
|
316
|
+
):
|
|
317
|
+
details: dict[str, Any] = {}
|
|
318
|
+
if algorithm is not None:
|
|
319
|
+
details["algorithm"] = algorithm
|
|
320
|
+
details.update(kwargs)
|
|
321
|
+
super().__init__(message, details)
|
|
322
|
+
self.algorithm = algorithm
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class ConvergenceError(ComputationError):
|
|
326
|
+
"""
|
|
327
|
+
Exception for iterative algorithm convergence failures.
|
|
328
|
+
|
|
329
|
+
Raised when an iterative algorithm fails to converge within
|
|
330
|
+
the maximum number of iterations.
|
|
331
|
+
|
|
332
|
+
Parameters
|
|
333
|
+
----------
|
|
334
|
+
message : str
|
|
335
|
+
Description of the convergence failure.
|
|
336
|
+
algorithm : str, optional
|
|
337
|
+
Name of the algorithm.
|
|
338
|
+
iterations : int, optional
|
|
339
|
+
Number of iterations performed.
|
|
340
|
+
max_iterations : int, optional
|
|
341
|
+
Maximum iterations allowed.
|
|
342
|
+
residual : float, optional
|
|
343
|
+
Final residual or error value.
|
|
344
|
+
tolerance : float, optional
|
|
345
|
+
Convergence tolerance.
|
|
346
|
+
|
|
347
|
+
Examples
|
|
348
|
+
--------
|
|
349
|
+
>>> raise ConvergenceError(
|
|
350
|
+
... "Kepler's equation did not converge",
|
|
351
|
+
... algorithm="Newton-Raphson",
|
|
352
|
+
... iterations=100,
|
|
353
|
+
... max_iterations=100,
|
|
354
|
+
... residual=1e-5,
|
|
355
|
+
... tolerance=1e-12
|
|
356
|
+
... )
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
def __init__(
|
|
360
|
+
self,
|
|
361
|
+
message: str,
|
|
362
|
+
algorithm: Optional[str] = None,
|
|
363
|
+
iterations: Optional[int] = None,
|
|
364
|
+
max_iterations: Optional[int] = None,
|
|
365
|
+
residual: Optional[float] = None,
|
|
366
|
+
tolerance: Optional[float] = None,
|
|
367
|
+
**kwargs: Any,
|
|
368
|
+
):
|
|
369
|
+
details: dict[str, Any] = {}
|
|
370
|
+
if iterations is not None:
|
|
371
|
+
details["iterations"] = iterations
|
|
372
|
+
if max_iterations is not None:
|
|
373
|
+
details["max_iterations"] = max_iterations
|
|
374
|
+
if residual is not None:
|
|
375
|
+
details["residual"] = residual
|
|
376
|
+
if tolerance is not None:
|
|
377
|
+
details["tolerance"] = tolerance
|
|
378
|
+
super().__init__(message, algorithm=algorithm, **details, **kwargs)
|
|
379
|
+
self.iterations = iterations
|
|
380
|
+
self.max_iterations = max_iterations
|
|
381
|
+
self.residual = residual
|
|
382
|
+
self.tolerance = tolerance
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class NumericalError(ComputationError):
|
|
386
|
+
"""
|
|
387
|
+
Exception for numerical stability issues.
|
|
388
|
+
|
|
389
|
+
Raised when numerical operations fail due to precision issues,
|
|
390
|
+
overflow, underflow, or ill-conditioned computations.
|
|
391
|
+
|
|
392
|
+
Parameters
|
|
393
|
+
----------
|
|
394
|
+
message : str
|
|
395
|
+
Description of the numerical issue.
|
|
396
|
+
operation : str, optional
|
|
397
|
+
The operation that failed.
|
|
398
|
+
condition_number : float, optional
|
|
399
|
+
Matrix condition number (if applicable).
|
|
400
|
+
|
|
401
|
+
Examples
|
|
402
|
+
--------
|
|
403
|
+
>>> raise NumericalError(
|
|
404
|
+
... "Matrix is ill-conditioned",
|
|
405
|
+
... operation="matrix inversion",
|
|
406
|
+
... condition_number=1e16
|
|
407
|
+
... )
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
def __init__(
|
|
411
|
+
self,
|
|
412
|
+
message: str,
|
|
413
|
+
operation: Optional[str] = None,
|
|
414
|
+
condition_number: Optional[float] = None,
|
|
415
|
+
**kwargs: Any,
|
|
416
|
+
):
|
|
417
|
+
details: dict[str, Any] = {}
|
|
418
|
+
if operation is not None:
|
|
419
|
+
details["operation"] = operation
|
|
420
|
+
if condition_number is not None:
|
|
421
|
+
details["condition_number"] = condition_number
|
|
422
|
+
super().__init__(message, **details, **kwargs)
|
|
423
|
+
self.operation = operation
|
|
424
|
+
self.condition_number = condition_number
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
class SingularMatrixError(ComputationError):
|
|
428
|
+
"""
|
|
429
|
+
Exception for singular or non-invertible matrix operations.
|
|
430
|
+
|
|
431
|
+
Raised when a matrix operation requires an invertible matrix
|
|
432
|
+
but the matrix is singular or nearly singular.
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
message : str
|
|
437
|
+
Description of the singularity issue.
|
|
438
|
+
matrix_name : str, optional
|
|
439
|
+
Name of the singular matrix.
|
|
440
|
+
determinant : float, optional
|
|
441
|
+
Determinant value (near zero).
|
|
442
|
+
|
|
443
|
+
Examples
|
|
444
|
+
--------
|
|
445
|
+
>>> raise SingularMatrixError(
|
|
446
|
+
... "Covariance matrix is singular",
|
|
447
|
+
... matrix_name="P",
|
|
448
|
+
... determinant=1e-20
|
|
449
|
+
... )
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
def __init__(
|
|
453
|
+
self,
|
|
454
|
+
message: str,
|
|
455
|
+
matrix_name: Optional[str] = None,
|
|
456
|
+
determinant: Optional[float] = None,
|
|
457
|
+
**kwargs: Any,
|
|
458
|
+
):
|
|
459
|
+
details: dict[str, Any] = {}
|
|
460
|
+
if matrix_name is not None:
|
|
461
|
+
details["matrix"] = matrix_name
|
|
462
|
+
if determinant is not None:
|
|
463
|
+
details["determinant"] = determinant
|
|
464
|
+
super().__init__(message, **details, **kwargs)
|
|
465
|
+
self.matrix_name = matrix_name
|
|
466
|
+
self.determinant = determinant
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
# =============================================================================
|
|
470
|
+
# State Errors
|
|
471
|
+
# =============================================================================
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
class StateError(TCLError):
|
|
475
|
+
"""
|
|
476
|
+
Base exception for object state violations.
|
|
477
|
+
|
|
478
|
+
Raised when an operation is attempted on an object that is not
|
|
479
|
+
in a valid state for that operation.
|
|
480
|
+
|
|
481
|
+
Parameters
|
|
482
|
+
----------
|
|
483
|
+
message : str
|
|
484
|
+
Description of the state violation.
|
|
485
|
+
object_type : str, optional
|
|
486
|
+
Type of the object.
|
|
487
|
+
current_state : str, optional
|
|
488
|
+
Description of the current state.
|
|
489
|
+
required_state : str, optional
|
|
490
|
+
Description of the required state.
|
|
491
|
+
|
|
492
|
+
Examples
|
|
493
|
+
--------
|
|
494
|
+
>>> raise StateError(
|
|
495
|
+
... "Cannot update without prediction",
|
|
496
|
+
... object_type="KalmanFilter",
|
|
497
|
+
... current_state="uninitialized",
|
|
498
|
+
... required_state="predicted"
|
|
499
|
+
... )
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
def __init__(
|
|
503
|
+
self,
|
|
504
|
+
message: str,
|
|
505
|
+
object_type: Optional[str] = None,
|
|
506
|
+
current_state: Optional[str] = None,
|
|
507
|
+
required_state: Optional[str] = None,
|
|
508
|
+
**kwargs: Any,
|
|
509
|
+
):
|
|
510
|
+
details: dict[str, Any] = {}
|
|
511
|
+
if object_type is not None:
|
|
512
|
+
details["object_type"] = object_type
|
|
513
|
+
if current_state is not None:
|
|
514
|
+
details["current_state"] = current_state
|
|
515
|
+
if required_state is not None:
|
|
516
|
+
details["required_state"] = required_state
|
|
517
|
+
details.update(kwargs)
|
|
518
|
+
super().__init__(message, details)
|
|
519
|
+
self.object_type = object_type
|
|
520
|
+
self.current_state = current_state
|
|
521
|
+
self.required_state = required_state
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
class UninitializedError(StateError):
|
|
525
|
+
"""
|
|
526
|
+
Exception for uninitialized object access.
|
|
527
|
+
|
|
528
|
+
Raised when an operation requires an initialized object but
|
|
529
|
+
the object hasn't been properly initialized.
|
|
530
|
+
|
|
531
|
+
Parameters
|
|
532
|
+
----------
|
|
533
|
+
message : str
|
|
534
|
+
Description of the issue.
|
|
535
|
+
object_type : str, optional
|
|
536
|
+
Type of the uninitialized object.
|
|
537
|
+
required_initialization : str, optional
|
|
538
|
+
What initialization is required.
|
|
539
|
+
|
|
540
|
+
Examples
|
|
541
|
+
--------
|
|
542
|
+
>>> raise UninitializedError(
|
|
543
|
+
... "Tracker not initialized",
|
|
544
|
+
... object_type="SingleTargetTracker",
|
|
545
|
+
... required_initialization="call initialize() first"
|
|
546
|
+
... )
|
|
547
|
+
"""
|
|
548
|
+
|
|
549
|
+
def __init__(
|
|
550
|
+
self,
|
|
551
|
+
message: str,
|
|
552
|
+
object_type: Optional[str] = None,
|
|
553
|
+
required_initialization: Optional[str] = None,
|
|
554
|
+
**kwargs: Any,
|
|
555
|
+
):
|
|
556
|
+
super().__init__(
|
|
557
|
+
message,
|
|
558
|
+
object_type=object_type,
|
|
559
|
+
current_state="uninitialized",
|
|
560
|
+
required_state=required_initialization,
|
|
561
|
+
**kwargs,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class EmptyContainerError(StateError):
|
|
566
|
+
"""
|
|
567
|
+
Exception for empty container operations.
|
|
568
|
+
|
|
569
|
+
Raised when an operation requires a non-empty container but
|
|
570
|
+
the container has no elements.
|
|
571
|
+
|
|
572
|
+
Parameters
|
|
573
|
+
----------
|
|
574
|
+
message : str
|
|
575
|
+
Description of the issue.
|
|
576
|
+
container_type : str, optional
|
|
577
|
+
Type of the empty container.
|
|
578
|
+
operation : str, optional
|
|
579
|
+
The operation that requires non-empty container.
|
|
580
|
+
|
|
581
|
+
Examples
|
|
582
|
+
--------
|
|
583
|
+
>>> raise EmptyContainerError(
|
|
584
|
+
... "Cannot query empty RTree",
|
|
585
|
+
... container_type="RTree",
|
|
586
|
+
... operation="nearest neighbor query"
|
|
587
|
+
... )
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
def __init__(
|
|
591
|
+
self,
|
|
592
|
+
message: str,
|
|
593
|
+
container_type: Optional[str] = None,
|
|
594
|
+
operation: Optional[str] = None,
|
|
595
|
+
**kwargs: Any,
|
|
596
|
+
):
|
|
597
|
+
details: dict[str, Any] = {}
|
|
598
|
+
if operation is not None:
|
|
599
|
+
details["operation"] = operation
|
|
600
|
+
super().__init__(
|
|
601
|
+
message,
|
|
602
|
+
object_type=container_type,
|
|
603
|
+
current_state="empty",
|
|
604
|
+
**details,
|
|
605
|
+
**kwargs,
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
# =============================================================================
|
|
610
|
+
# Configuration Errors
|
|
611
|
+
# =============================================================================
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
class ConfigurationError(TCLError):
|
|
615
|
+
"""
|
|
616
|
+
Base exception for configuration and setup issues.
|
|
617
|
+
|
|
618
|
+
Raised when there are problems with algorithm configuration,
|
|
619
|
+
method selection, or dependency availability.
|
|
620
|
+
|
|
621
|
+
Parameters
|
|
622
|
+
----------
|
|
623
|
+
message : str
|
|
624
|
+
Description of the configuration issue.
|
|
625
|
+
|
|
626
|
+
Examples
|
|
627
|
+
--------
|
|
628
|
+
>>> raise ConfigurationError("Invalid filter configuration")
|
|
629
|
+
"""
|
|
630
|
+
|
|
631
|
+
pass
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class MethodError(ConfigurationError, ValueError):
|
|
635
|
+
"""
|
|
636
|
+
Exception for invalid method or algorithm selection.
|
|
637
|
+
|
|
638
|
+
Raised when an unknown or unsupported method/algorithm is specified.
|
|
639
|
+
|
|
640
|
+
Parameters
|
|
641
|
+
----------
|
|
642
|
+
message : str
|
|
643
|
+
Description of the issue.
|
|
644
|
+
method : str, optional
|
|
645
|
+
The invalid method name.
|
|
646
|
+
valid_methods : sequence of str, optional
|
|
647
|
+
List of valid method names.
|
|
648
|
+
|
|
649
|
+
Examples
|
|
650
|
+
--------
|
|
651
|
+
>>> raise MethodError(
|
|
652
|
+
... "Unknown assignment method",
|
|
653
|
+
... method="invalid_method",
|
|
654
|
+
... valid_methods=["hungarian", "auction", "greedy"]
|
|
655
|
+
... )
|
|
656
|
+
"""
|
|
657
|
+
|
|
658
|
+
def __init__(
|
|
659
|
+
self,
|
|
660
|
+
message: str,
|
|
661
|
+
method: Optional[str] = None,
|
|
662
|
+
valid_methods: Optional[Sequence[str]] = None,
|
|
663
|
+
**kwargs: Any,
|
|
664
|
+
):
|
|
665
|
+
details: dict[str, Any] = {}
|
|
666
|
+
if method is not None:
|
|
667
|
+
details["method"] = method
|
|
668
|
+
if valid_methods is not None:
|
|
669
|
+
details["valid_methods"] = list(valid_methods)
|
|
670
|
+
super().__init__(message, details)
|
|
671
|
+
self.method = method
|
|
672
|
+
self.valid_methods = valid_methods
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
class DependencyError(ConfigurationError, ImportError):
|
|
676
|
+
"""
|
|
677
|
+
Exception for missing optional dependencies.
|
|
678
|
+
|
|
679
|
+
Raised when an optional dependency is required but not installed.
|
|
680
|
+
|
|
681
|
+
Parameters
|
|
682
|
+
----------
|
|
683
|
+
message : str
|
|
684
|
+
Description of the missing dependency.
|
|
685
|
+
package : str, optional
|
|
686
|
+
Name of the missing package.
|
|
687
|
+
feature : str, optional
|
|
688
|
+
Feature that requires the dependency.
|
|
689
|
+
install_command : str, optional
|
|
690
|
+
Command to install the dependency.
|
|
691
|
+
|
|
692
|
+
Examples
|
|
693
|
+
--------
|
|
694
|
+
>>> raise DependencyError(
|
|
695
|
+
... "plotly is required for interactive plotting",
|
|
696
|
+
... package="plotly",
|
|
697
|
+
... feature="3D visualization",
|
|
698
|
+
... install_command="pip install plotly"
|
|
699
|
+
... )
|
|
700
|
+
"""
|
|
701
|
+
|
|
702
|
+
def __init__(
|
|
703
|
+
self,
|
|
704
|
+
message: str,
|
|
705
|
+
package: Optional[str] = None,
|
|
706
|
+
feature: Optional[str] = None,
|
|
707
|
+
install_command: Optional[str] = None,
|
|
708
|
+
**kwargs: Any,
|
|
709
|
+
):
|
|
710
|
+
details: dict[str, Any] = {}
|
|
711
|
+
if package is not None:
|
|
712
|
+
details["package"] = package
|
|
713
|
+
if feature is not None:
|
|
714
|
+
details["feature"] = feature
|
|
715
|
+
if install_command is not None:
|
|
716
|
+
details["install"] = install_command
|
|
717
|
+
super().__init__(message, details)
|
|
718
|
+
self.package = package
|
|
719
|
+
self.feature = feature
|
|
720
|
+
self.install_command = install_command
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
# =============================================================================
|
|
724
|
+
# Data Errors
|
|
725
|
+
# =============================================================================
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class DataError(TCLError):
|
|
729
|
+
"""
|
|
730
|
+
Base exception for data format and structure issues.
|
|
731
|
+
|
|
732
|
+
Raised when input data has format or structural problems.
|
|
733
|
+
|
|
734
|
+
Parameters
|
|
735
|
+
----------
|
|
736
|
+
message : str
|
|
737
|
+
Description of the data issue.
|
|
738
|
+
|
|
739
|
+
Examples
|
|
740
|
+
--------
|
|
741
|
+
>>> raise DataError("Invalid input data format")
|
|
742
|
+
"""
|
|
743
|
+
|
|
744
|
+
pass
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
class FormatError(DataError, ValueError):
|
|
748
|
+
"""
|
|
749
|
+
Exception for invalid data format.
|
|
750
|
+
|
|
751
|
+
Raised when data doesn't conform to the expected format.
|
|
752
|
+
|
|
753
|
+
Parameters
|
|
754
|
+
----------
|
|
755
|
+
message : str
|
|
756
|
+
Description of the format issue.
|
|
757
|
+
expected_format : str, optional
|
|
758
|
+
Description of the expected format.
|
|
759
|
+
actual_format : str, optional
|
|
760
|
+
Description of the actual format.
|
|
761
|
+
|
|
762
|
+
Examples
|
|
763
|
+
--------
|
|
764
|
+
>>> raise FormatError(
|
|
765
|
+
... "Invalid TLE format",
|
|
766
|
+
... expected_format="69 characters per line",
|
|
767
|
+
... actual_format="line 1 has 65 characters"
|
|
768
|
+
... )
|
|
769
|
+
"""
|
|
770
|
+
|
|
771
|
+
def __init__(
|
|
772
|
+
self,
|
|
773
|
+
message: str,
|
|
774
|
+
expected_format: Optional[str] = None,
|
|
775
|
+
actual_format: Optional[str] = None,
|
|
776
|
+
**kwargs: Any,
|
|
777
|
+
):
|
|
778
|
+
details: dict[str, Any] = {}
|
|
779
|
+
if expected_format is not None:
|
|
780
|
+
details["expected"] = expected_format
|
|
781
|
+
if actual_format is not None:
|
|
782
|
+
details["actual"] = actual_format
|
|
783
|
+
super().__init__(message, details)
|
|
784
|
+
self.expected_format = expected_format
|
|
785
|
+
self.actual_format = actual_format
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
class ParseError(DataError, ValueError):
|
|
789
|
+
"""
|
|
790
|
+
Exception for data parsing failures.
|
|
791
|
+
|
|
792
|
+
Raised when data cannot be parsed or interpreted.
|
|
793
|
+
|
|
794
|
+
Parameters
|
|
795
|
+
----------
|
|
796
|
+
message : str
|
|
797
|
+
Description of the parsing failure.
|
|
798
|
+
data_type : str, optional
|
|
799
|
+
Type of data being parsed.
|
|
800
|
+
position : int or tuple, optional
|
|
801
|
+
Position where parsing failed.
|
|
802
|
+
reason : str, optional
|
|
803
|
+
Reason for the parsing failure.
|
|
804
|
+
|
|
805
|
+
Examples
|
|
806
|
+
--------
|
|
807
|
+
>>> raise ParseError(
|
|
808
|
+
... "Failed to parse TLE checksum",
|
|
809
|
+
... data_type="TLE",
|
|
810
|
+
... position=68,
|
|
811
|
+
... reason="invalid checksum digit"
|
|
812
|
+
... )
|
|
813
|
+
"""
|
|
814
|
+
|
|
815
|
+
def __init__(
|
|
816
|
+
self,
|
|
817
|
+
message: str,
|
|
818
|
+
data_type: Optional[str] = None,
|
|
819
|
+
position: Optional[int | tuple[int, ...]] = None,
|
|
820
|
+
reason: Optional[str] = None,
|
|
821
|
+
**kwargs: Any,
|
|
822
|
+
):
|
|
823
|
+
details: dict[str, Any] = {}
|
|
824
|
+
if data_type is not None:
|
|
825
|
+
details["data_type"] = data_type
|
|
826
|
+
if position is not None:
|
|
827
|
+
details["position"] = position
|
|
828
|
+
if reason is not None:
|
|
829
|
+
details["reason"] = reason
|
|
830
|
+
super().__init__(message, details)
|
|
831
|
+
self.data_type = data_type
|
|
832
|
+
self.position = position
|
|
833
|
+
self.reason = reason
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
# =============================================================================
|
|
837
|
+
# Exports
|
|
838
|
+
# =============================================================================
|
|
839
|
+
|
|
840
|
+
__all__ = [
|
|
841
|
+
# Base exception
|
|
842
|
+
"TCLError",
|
|
843
|
+
# Validation errors
|
|
844
|
+
"ValidationError",
|
|
845
|
+
"DimensionError",
|
|
846
|
+
"ParameterError",
|
|
847
|
+
"RangeError",
|
|
848
|
+
# Computation errors
|
|
849
|
+
"ComputationError",
|
|
850
|
+
"ConvergenceError",
|
|
851
|
+
"NumericalError",
|
|
852
|
+
"SingularMatrixError",
|
|
853
|
+
# State errors
|
|
854
|
+
"StateError",
|
|
855
|
+
"UninitializedError",
|
|
856
|
+
"EmptyContainerError",
|
|
857
|
+
# Configuration errors
|
|
858
|
+
"ConfigurationError",
|
|
859
|
+
"MethodError",
|
|
860
|
+
"DependencyError",
|
|
861
|
+
# Data errors
|
|
862
|
+
"DataError",
|
|
863
|
+
"FormatError",
|
|
864
|
+
"ParseError",
|
|
865
|
+
]
|