nrl-tracker 0.22.5__py3-none-any.whl → 1.8.0__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-0.22.5.dist-info → nrl_tracker-1.8.0.dist-info}/METADATA +57 -10
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.8.0.dist-info}/RECORD +86 -69
- pytcl/__init__.py +4 -3
- pytcl/assignment_algorithms/__init__.py +28 -0
- pytcl/assignment_algorithms/dijkstra_min_cost.py +184 -0
- pytcl/assignment_algorithms/gating.py +10 -10
- pytcl/assignment_algorithms/jpda.py +40 -40
- pytcl/assignment_algorithms/nd_assignment.py +379 -0
- pytcl/assignment_algorithms/network_flow.py +464 -0
- pytcl/assignment_algorithms/network_simplex.py +167 -0
- pytcl/assignment_algorithms/three_dimensional/assignment.py +3 -3
- pytcl/astronomical/__init__.py +104 -3
- pytcl/astronomical/ephemerides.py +14 -11
- pytcl/astronomical/reference_frames.py +865 -56
- pytcl/astronomical/relativity.py +6 -5
- pytcl/astronomical/sgp4.py +710 -0
- pytcl/astronomical/special_orbits.py +532 -0
- pytcl/astronomical/tle.py +558 -0
- pytcl/atmosphere/__init__.py +43 -1
- pytcl/atmosphere/ionosphere.py +512 -0
- pytcl/atmosphere/nrlmsise00.py +809 -0
- pytcl/clustering/dbscan.py +2 -2
- pytcl/clustering/gaussian_mixture.py +3 -3
- pytcl/clustering/hierarchical.py +15 -15
- pytcl/clustering/kmeans.py +4 -4
- pytcl/containers/__init__.py +24 -0
- pytcl/containers/base.py +219 -0
- pytcl/containers/cluster_set.py +12 -2
- pytcl/containers/covertree.py +26 -29
- pytcl/containers/kd_tree.py +94 -29
- pytcl/containers/rtree.py +200 -1
- pytcl/containers/vptree.py +21 -28
- pytcl/coordinate_systems/conversions/geodetic.py +272 -5
- pytcl/coordinate_systems/jacobians/jacobians.py +2 -2
- pytcl/coordinate_systems/projections/__init__.py +1 -1
- pytcl/coordinate_systems/projections/projections.py +2 -2
- pytcl/coordinate_systems/rotations/rotations.py +10 -6
- pytcl/core/__init__.py +18 -0
- pytcl/core/validation.py +333 -2
- pytcl/dynamic_estimation/__init__.py +26 -0
- pytcl/dynamic_estimation/gaussian_sum_filter.py +434 -0
- pytcl/dynamic_estimation/imm.py +14 -14
- pytcl/dynamic_estimation/kalman/__init__.py +30 -0
- pytcl/dynamic_estimation/kalman/constrained.py +382 -0
- pytcl/dynamic_estimation/kalman/extended.py +8 -8
- pytcl/dynamic_estimation/kalman/h_infinity.py +613 -0
- pytcl/dynamic_estimation/kalman/square_root.py +60 -573
- pytcl/dynamic_estimation/kalman/sr_ukf.py +302 -0
- pytcl/dynamic_estimation/kalman/ud_filter.py +410 -0
- pytcl/dynamic_estimation/kalman/unscented.py +8 -6
- pytcl/dynamic_estimation/particle_filters/bootstrap.py +15 -15
- pytcl/dynamic_estimation/rbpf.py +589 -0
- pytcl/gravity/egm.py +13 -0
- pytcl/gravity/spherical_harmonics.py +98 -37
- pytcl/gravity/tides.py +6 -6
- pytcl/logging_config.py +328 -0
- pytcl/magnetism/__init__.py +7 -0
- pytcl/magnetism/emm.py +10 -3
- pytcl/magnetism/wmm.py +260 -23
- pytcl/mathematical_functions/combinatorics/combinatorics.py +5 -5
- pytcl/mathematical_functions/geometry/geometry.py +5 -5
- pytcl/mathematical_functions/numerical_integration/quadrature.py +6 -6
- pytcl/mathematical_functions/signal_processing/detection.py +24 -24
- pytcl/mathematical_functions/signal_processing/filters.py +14 -14
- pytcl/mathematical_functions/signal_processing/matched_filter.py +12 -12
- pytcl/mathematical_functions/special_functions/bessel.py +15 -3
- pytcl/mathematical_functions/special_functions/debye.py +136 -26
- pytcl/mathematical_functions/special_functions/error_functions.py +3 -1
- pytcl/mathematical_functions/special_functions/gamma_functions.py +4 -4
- pytcl/mathematical_functions/special_functions/hypergeometric.py +81 -15
- pytcl/mathematical_functions/transforms/fourier.py +8 -8
- pytcl/mathematical_functions/transforms/stft.py +12 -12
- pytcl/mathematical_functions/transforms/wavelets.py +9 -9
- pytcl/navigation/geodesy.py +246 -160
- pytcl/navigation/great_circle.py +101 -19
- pytcl/plotting/coordinates.py +7 -7
- pytcl/plotting/tracks.py +2 -2
- pytcl/static_estimation/maximum_likelihood.py +16 -14
- pytcl/static_estimation/robust.py +5 -5
- pytcl/terrain/loaders.py +5 -5
- pytcl/trackers/hypothesis.py +1 -1
- pytcl/trackers/mht.py +9 -9
- pytcl/trackers/multi_target.py +1 -1
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.8.0.dist-info}/LICENSE +0 -0
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.8.0.dist-info}/WHEEL +0 -0
- {nrl_tracker-0.22.5.dist-info → nrl_tracker-1.8.0.dist-info}/top_level.txt +0 -0
pytcl/navigation/great_circle.py
CHANGED
|
@@ -10,11 +10,20 @@ computing the shortest path on a sphere, including:
|
|
|
10
10
|
- TDOA localization on a sphere
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
import logging
|
|
14
|
+
from functools import lru_cache
|
|
15
|
+
from typing import Any, NamedTuple, Optional, Tuple
|
|
14
16
|
|
|
15
17
|
import numpy as np
|
|
16
18
|
from numpy.typing import NDArray
|
|
17
19
|
|
|
20
|
+
# Module logger
|
|
21
|
+
_logger = logging.getLogger("pytcl.navigation.great_circle")
|
|
22
|
+
|
|
23
|
+
# Cache configuration for great circle calculations
|
|
24
|
+
_GC_CACHE_DECIMALS = 10 # ~0.01mm precision at Earth's surface
|
|
25
|
+
_GC_CACHE_MAXSIZE = 256 # Max cached coordinate pairs
|
|
26
|
+
|
|
18
27
|
|
|
19
28
|
class GreatCircleResult(NamedTuple):
|
|
20
29
|
"""
|
|
@@ -96,6 +105,50 @@ class CrossTrackResult(NamedTuple):
|
|
|
96
105
|
EARTH_RADIUS = 6371000.0
|
|
97
106
|
|
|
98
107
|
|
|
108
|
+
def _quantize_coord(val: float) -> float:
|
|
109
|
+
"""Quantize coordinate value for cache key compatibility."""
|
|
110
|
+
return round(val, _GC_CACHE_DECIMALS)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@lru_cache(maxsize=_GC_CACHE_MAXSIZE)
|
|
114
|
+
def _gc_distance_cached(
|
|
115
|
+
lat1_q: float,
|
|
116
|
+
lon1_q: float,
|
|
117
|
+
lat2_q: float,
|
|
118
|
+
lon2_q: float,
|
|
119
|
+
) -> float:
|
|
120
|
+
"""Cached great circle distance computation (internal).
|
|
121
|
+
|
|
122
|
+
Uses haversine formula for numerical stability.
|
|
123
|
+
Returns angular distance in radians.
|
|
124
|
+
"""
|
|
125
|
+
dlat = lat2_q - lat1_q
|
|
126
|
+
dlon = lon2_q - lon1_q
|
|
127
|
+
|
|
128
|
+
a = np.sin(dlat / 2) ** 2 + np.cos(lat1_q) * np.cos(lat2_q) * np.sin(dlon / 2) ** 2
|
|
129
|
+
return 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@lru_cache(maxsize=_GC_CACHE_MAXSIZE)
|
|
133
|
+
def _gc_azimuth_cached(
|
|
134
|
+
lat1_q: float,
|
|
135
|
+
lon1_q: float,
|
|
136
|
+
lat2_q: float,
|
|
137
|
+
lon2_q: float,
|
|
138
|
+
) -> float:
|
|
139
|
+
"""Cached great circle azimuth computation (internal).
|
|
140
|
+
|
|
141
|
+
Returns azimuth in radians [0, 2π).
|
|
142
|
+
"""
|
|
143
|
+
dlon = lon2_q - lon1_q
|
|
144
|
+
|
|
145
|
+
x = np.sin(dlon) * np.cos(lat2_q)
|
|
146
|
+
y = np.cos(lat1_q) * np.sin(lat2_q) - np.sin(lat1_q) * np.cos(lat2_q) * np.cos(dlon)
|
|
147
|
+
|
|
148
|
+
azimuth = np.arctan2(x, y)
|
|
149
|
+
return azimuth % (2 * np.pi)
|
|
150
|
+
|
|
151
|
+
|
|
99
152
|
def great_circle_distance(
|
|
100
153
|
lat1: float,
|
|
101
154
|
lon1: float,
|
|
@@ -107,6 +160,7 @@ def great_circle_distance(
|
|
|
107
160
|
Compute great circle distance between two points.
|
|
108
161
|
|
|
109
162
|
Uses the haversine formula for numerical stability at small distances.
|
|
163
|
+
Results are cached for repeated queries with the same coordinates.
|
|
110
164
|
|
|
111
165
|
Parameters
|
|
112
166
|
----------
|
|
@@ -131,13 +185,14 @@ def great_circle_distance(
|
|
|
131
185
|
>>> dist = great_circle_distance(lat1, lon1, lat2, lon2)
|
|
132
186
|
>>> print(f"Distance: {dist/1000:.0f} km")
|
|
133
187
|
"""
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
188
|
+
# Use cached angular distance computation
|
|
189
|
+
angular_dist = _gc_distance_cached(
|
|
190
|
+
_quantize_coord(lat1),
|
|
191
|
+
_quantize_coord(lon1),
|
|
192
|
+
_quantize_coord(lat2),
|
|
193
|
+
_quantize_coord(lon2),
|
|
194
|
+
)
|
|
195
|
+
return radius * angular_dist
|
|
141
196
|
|
|
142
197
|
|
|
143
198
|
def great_circle_azimuth(
|
|
@@ -149,6 +204,8 @@ def great_circle_azimuth(
|
|
|
149
204
|
"""
|
|
150
205
|
Compute initial azimuth (bearing) from point 1 to point 2.
|
|
151
206
|
|
|
207
|
+
Results are cached for repeated queries with the same coordinates.
|
|
208
|
+
|
|
152
209
|
Parameters
|
|
153
210
|
----------
|
|
154
211
|
lat1, lon1 : float
|
|
@@ -170,15 +227,12 @@ def great_circle_azimuth(
|
|
|
170
227
|
>>> az = great_circle_azimuth(lat1, lon1, lat2, lon2)
|
|
171
228
|
>>> print(f"Initial bearing: {np.degrees(az):.1f}°")
|
|
172
229
|
"""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# Normalize to [0, 2π)
|
|
181
|
-
return azimuth % (2 * np.pi)
|
|
230
|
+
return _gc_azimuth_cached(
|
|
231
|
+
_quantize_coord(lat1),
|
|
232
|
+
_quantize_coord(lon1),
|
|
233
|
+
_quantize_coord(lat2),
|
|
234
|
+
_quantize_coord(lon2),
|
|
235
|
+
)
|
|
182
236
|
|
|
183
237
|
|
|
184
238
|
def great_circle_inverse(
|
|
@@ -451,7 +505,7 @@ def great_circle_intersect(
|
|
|
451
505
|
"""
|
|
452
506
|
|
|
453
507
|
# Convert to Cartesian unit vectors
|
|
454
|
-
def to_cartesian(lat, lon):
|
|
508
|
+
def to_cartesian(lat: Any, lon: Any) -> NDArray[np.float64]:
|
|
455
509
|
return np.array(
|
|
456
510
|
[np.cos(lat) * np.cos(lon), np.cos(lat) * np.sin(lon), np.sin(lat)]
|
|
457
511
|
)
|
|
@@ -607,7 +661,7 @@ def great_circle_tdoa_loc(
|
|
|
607
661
|
delta_d12 = delta_r12 / radius
|
|
608
662
|
delta_d13 = delta_r13 / radius
|
|
609
663
|
|
|
610
|
-
def objective(lat, lon):
|
|
664
|
+
def objective(lat: Any, lon: Any) -> Any:
|
|
611
665
|
"""Objective function: difference between computed and observed TDOAs."""
|
|
612
666
|
d1 = great_circle_distance(lat, lon, lat1, lon1, radius=1.0)
|
|
613
667
|
d2 = great_circle_distance(lat, lon, lat2, lon2, radius=1.0)
|
|
@@ -771,6 +825,31 @@ def destination_point(
|
|
|
771
825
|
return WaypointResult(float(lat2), float(lon2))
|
|
772
826
|
|
|
773
827
|
|
|
828
|
+
def clear_great_circle_cache() -> None:
|
|
829
|
+
"""Clear all great circle computation caches.
|
|
830
|
+
|
|
831
|
+
This can be useful to free memory after processing large datasets
|
|
832
|
+
or when cache statistics are being monitored.
|
|
833
|
+
"""
|
|
834
|
+
_gc_distance_cached.cache_clear()
|
|
835
|
+
_gc_azimuth_cached.cache_clear()
|
|
836
|
+
_logger.debug("Great circle caches cleared")
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def get_cache_info() -> dict[str, Any]:
|
|
840
|
+
"""Get cache statistics for great circle computations.
|
|
841
|
+
|
|
842
|
+
Returns
|
|
843
|
+
-------
|
|
844
|
+
dict[str, Any]
|
|
845
|
+
Dictionary with cache statistics for distance and azimuth caches.
|
|
846
|
+
"""
|
|
847
|
+
return {
|
|
848
|
+
"distance": _gc_distance_cached.cache_info()._asdict(),
|
|
849
|
+
"azimuth": _gc_azimuth_cached.cache_info()._asdict(),
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
|
|
774
853
|
__all__ = [
|
|
775
854
|
# Constants
|
|
776
855
|
"EARTH_RADIUS",
|
|
@@ -796,4 +875,7 @@ __all__ = [
|
|
|
796
875
|
"great_circle_path_intersect",
|
|
797
876
|
# TDOA
|
|
798
877
|
"great_circle_tdoa_loc",
|
|
878
|
+
# Cache management
|
|
879
|
+
"clear_great_circle_cache",
|
|
880
|
+
"get_cache_info",
|
|
799
881
|
]
|
pytcl/plotting/coordinates.py
CHANGED
|
@@ -5,10 +5,10 @@ This module provides functions for visualizing coordinate systems,
|
|
|
5
5
|
rotations, and transformations in 2D and 3D.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import List, Optional, Tuple
|
|
8
|
+
from typing import Any, List, Optional, Tuple
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
|
-
from numpy.typing import ArrayLike
|
|
11
|
+
from numpy.typing import ArrayLike, NDArray
|
|
12
12
|
|
|
13
13
|
try:
|
|
14
14
|
import plotly.graph_objects as go
|
|
@@ -180,17 +180,17 @@ def plot_euler_angles(
|
|
|
180
180
|
angles = np.asarray(angles)
|
|
181
181
|
|
|
182
182
|
# Create rotation matrices for each axis
|
|
183
|
-
def rotx(a):
|
|
183
|
+
def rotx(a: Any) -> NDArray[np.float64]:
|
|
184
184
|
return np.array(
|
|
185
185
|
[[1, 0, 0], [0, np.cos(a), -np.sin(a)], [0, np.sin(a), np.cos(a)]]
|
|
186
186
|
)
|
|
187
187
|
|
|
188
|
-
def roty(a):
|
|
188
|
+
def roty(a: Any) -> NDArray[np.float64]:
|
|
189
189
|
return np.array(
|
|
190
190
|
[[np.cos(a), 0, np.sin(a)], [0, 1, 0], [-np.sin(a), 0, np.cos(a)]]
|
|
191
191
|
)
|
|
192
192
|
|
|
193
|
-
def rotz(a):
|
|
193
|
+
def rotz(a: Any) -> NDArray[np.float64]:
|
|
194
194
|
return np.array(
|
|
195
195
|
[[np.cos(a), -np.sin(a), 0], [np.sin(a), np.cos(a), 0], [0, 0, 1]]
|
|
196
196
|
)
|
|
@@ -293,7 +293,7 @@ def plot_quaternion_interpolation(
|
|
|
293
293
|
q_end = q_end / np.linalg.norm(q_end)
|
|
294
294
|
|
|
295
295
|
# SLERP interpolation
|
|
296
|
-
def quat_slerp(q1, q2, t):
|
|
296
|
+
def quat_slerp(q1: Any, q2: Any, t: Any) -> NDArray[np.float64]:
|
|
297
297
|
dot = np.dot(q1, q2)
|
|
298
298
|
if dot < 0:
|
|
299
299
|
q2 = -q2
|
|
@@ -303,7 +303,7 @@ def plot_quaternion_interpolation(
|
|
|
303
303
|
theta = np.arccos(dot)
|
|
304
304
|
return (np.sin((1 - t) * theta) * q1 + np.sin(t * theta) * q2) / np.sin(theta)
|
|
305
305
|
|
|
306
|
-
def quat_to_rotmat(q):
|
|
306
|
+
def quat_to_rotmat(q: Any) -> NDArray[np.float64]:
|
|
307
307
|
w, x, y, z = q
|
|
308
308
|
return np.array(
|
|
309
309
|
[
|
pytcl/plotting/tracks.py
CHANGED
|
@@ -196,7 +196,7 @@ def plot_tracking_result(
|
|
|
196
196
|
covariances: Optional[List[ArrayLike]] = None,
|
|
197
197
|
x_idx: int = 0,
|
|
198
198
|
y_idx: int = 2,
|
|
199
|
-
cov_xy_idx: tuple = (0, 2),
|
|
199
|
+
cov_xy_idx: tuple[int, int] = (0, 2),
|
|
200
200
|
ellipse_interval: int = 5,
|
|
201
201
|
n_std: float = 2.0,
|
|
202
202
|
title: str = "Tracking Result",
|
|
@@ -596,7 +596,7 @@ def create_animated_tracking(
|
|
|
596
596
|
covariances: Optional[List[ArrayLike]] = None,
|
|
597
597
|
x_idx: int = 0,
|
|
598
598
|
y_idx: int = 2,
|
|
599
|
-
cov_xy_idx: tuple = (0, 2),
|
|
599
|
+
cov_xy_idx: tuple[int, int] = (0, 2),
|
|
600
600
|
n_std: float = 2.0,
|
|
601
601
|
frame_duration: int = 100,
|
|
602
602
|
title: str = "Animated Tracking",
|
|
@@ -12,7 +12,7 @@ References
|
|
|
12
12
|
Wiley, 2001.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from typing import Callable, NamedTuple, Optional
|
|
15
|
+
from typing import Any, Callable, NamedTuple, Optional
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
from numpy.typing import ArrayLike, NDArray
|
|
@@ -75,10 +75,10 @@ class CRBResult(NamedTuple):
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
def fisher_information_numerical(
|
|
78
|
-
log_likelihood: Callable[[
|
|
78
|
+
log_likelihood: Callable[[np.ndarray[Any, Any]], float],
|
|
79
79
|
theta: ArrayLike,
|
|
80
80
|
h: float = 1e-5,
|
|
81
|
-
) ->
|
|
81
|
+
) -> np.ndarray[Any, Any]:
|
|
82
82
|
"""
|
|
83
83
|
Compute Fisher information matrix numerically.
|
|
84
84
|
|
|
@@ -189,11 +189,13 @@ def fisher_information_gaussian(
|
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
def fisher_information_exponential_family(
|
|
192
|
-
sufficient_stats: Callable[
|
|
192
|
+
sufficient_stats: Callable[
|
|
193
|
+
[np.ndarray[Any, Any], np.ndarray[Any, Any]], np.ndarray[Any, Any]
|
|
194
|
+
],
|
|
193
195
|
theta: ArrayLike,
|
|
194
196
|
data: ArrayLike,
|
|
195
197
|
h: float = 1e-5,
|
|
196
|
-
) ->
|
|
198
|
+
) -> np.ndarray[Any, Any]:
|
|
197
199
|
"""
|
|
198
200
|
Fisher information for exponential family distributions.
|
|
199
201
|
|
|
@@ -232,10 +234,10 @@ def fisher_information_exponential_family(
|
|
|
232
234
|
|
|
233
235
|
|
|
234
236
|
def observed_fisher_information(
|
|
235
|
-
log_likelihood: Callable[[
|
|
237
|
+
log_likelihood: Callable[[np.ndarray[Any, Any]], float],
|
|
236
238
|
theta: ArrayLike,
|
|
237
239
|
h: float = 1e-5,
|
|
238
|
-
) ->
|
|
240
|
+
) -> np.ndarray[Any, Any]:
|
|
239
241
|
"""
|
|
240
242
|
Compute observed Fisher information (negative Hessian).
|
|
241
243
|
|
|
@@ -409,10 +411,10 @@ def efficiency(
|
|
|
409
411
|
|
|
410
412
|
|
|
411
413
|
def mle_newton_raphson(
|
|
412
|
-
log_likelihood: Callable[[
|
|
413
|
-
score: Callable[[
|
|
414
|
+
log_likelihood: Callable[[np.ndarray[Any, Any]], float],
|
|
415
|
+
score: Callable[[np.ndarray[Any, Any]], np.ndarray[Any, Any]],
|
|
414
416
|
theta_init: ArrayLike,
|
|
415
|
-
hessian: Optional[Callable[[
|
|
417
|
+
hessian: Optional[Callable[[np.ndarray[Any, Any]], np.ndarray[Any, Any]]] = None,
|
|
416
418
|
max_iter: int = 100,
|
|
417
419
|
tol: float = 1e-8,
|
|
418
420
|
h: float = 1e-5,
|
|
@@ -460,7 +462,7 @@ def mle_newton_raphson(
|
|
|
460
462
|
|
|
461
463
|
converged = False
|
|
462
464
|
|
|
463
|
-
def numerical_hessian(t:
|
|
465
|
+
def numerical_hessian(t: np.ndarray[Any, Any]) -> np.ndarray[Any, Any]:
|
|
464
466
|
H = np.zeros((n_params, n_params))
|
|
465
467
|
for i in range(n_params):
|
|
466
468
|
for j in range(i, n_params):
|
|
@@ -532,9 +534,9 @@ def mle_newton_raphson(
|
|
|
532
534
|
|
|
533
535
|
|
|
534
536
|
def mle_scoring(
|
|
535
|
-
log_likelihood: Callable[[
|
|
536
|
-
score: Callable[[
|
|
537
|
-
fisher_info: Callable[[
|
|
537
|
+
log_likelihood: Callable[[np.ndarray[Any, Any]], float],
|
|
538
|
+
score: Callable[[np.ndarray[Any, Any]], np.ndarray[Any, Any]],
|
|
539
|
+
fisher_info: Callable[[np.ndarray[Any, Any]], np.ndarray[Any, Any]],
|
|
538
540
|
theta_init: ArrayLike,
|
|
539
541
|
max_iter: int = 100,
|
|
540
542
|
tol: float = 1e-8,
|
|
@@ -12,7 +12,7 @@ References
|
|
|
12
12
|
Cartography," Communications of the ACM, 1981.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from typing import Callable, NamedTuple, Optional
|
|
15
|
+
from typing import Any, Callable, NamedTuple, Optional
|
|
16
16
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
from numpy.typing import ArrayLike, NDArray
|
|
@@ -313,8 +313,8 @@ def tau_scale(
|
|
|
313
313
|
def irls(
|
|
314
314
|
A: ArrayLike,
|
|
315
315
|
b: ArrayLike,
|
|
316
|
-
weight_func: Callable[[
|
|
317
|
-
scale_func: Callable[[
|
|
316
|
+
weight_func: Callable[[np.ndarray[Any, Any]], np.ndarray[Any, Any]] = huber_weight,
|
|
317
|
+
scale_func: Callable[[np.ndarray[Any, Any]], float] = mad,
|
|
318
318
|
max_iter: int = 50,
|
|
319
319
|
tol: float = 1e-6,
|
|
320
320
|
) -> RobustResult:
|
|
@@ -455,7 +455,7 @@ def huber_regression(
|
|
|
455
455
|
Gaussian errors and resistance to outliers.
|
|
456
456
|
"""
|
|
457
457
|
|
|
458
|
-
def weight_func(r:
|
|
458
|
+
def weight_func(r: np.ndarray[Any, Any]) -> np.ndarray[Any, Any]:
|
|
459
459
|
return huber_weight(r, c)
|
|
460
460
|
|
|
461
461
|
return irls(A, b, weight_func=weight_func, max_iter=max_iter, tol=tol)
|
|
@@ -504,7 +504,7 @@ def tukey_regression(
|
|
|
504
504
|
Huber for gross outliers.
|
|
505
505
|
"""
|
|
506
506
|
|
|
507
|
-
def weight_func(r:
|
|
507
|
+
def weight_func(r: np.ndarray[Any, Any]) -> np.ndarray[Any, Any]:
|
|
508
508
|
return tukey_weight(r, c)
|
|
509
509
|
|
|
510
510
|
return irls(A, b, weight_func=weight_func, max_iter=max_iter, tol=tol)
|
pytcl/terrain/loaders.py
CHANGED
|
@@ -21,7 +21,7 @@ References
|
|
|
21
21
|
import os
|
|
22
22
|
from functools import lru_cache
|
|
23
23
|
from pathlib import Path
|
|
24
|
-
from typing import
|
|
24
|
+
from typing import Any, NamedTuple, Optional
|
|
25
25
|
|
|
26
26
|
import numpy as np
|
|
27
27
|
from numpy.typing import NDArray
|
|
@@ -31,7 +31,7 @@ from .dem import DEMGrid
|
|
|
31
31
|
# Model parameters
|
|
32
32
|
_GEBCO_BASE_URL = "https://www.gebco.net/data-products/gridded-bathymetry-data"
|
|
33
33
|
|
|
34
|
-
GEBCO_PARAMETERS:
|
|
34
|
+
GEBCO_PARAMETERS: dict[str, dict[str, Any]] = {
|
|
35
35
|
"GEBCO2024": {
|
|
36
36
|
"resolution_arcsec": 15.0,
|
|
37
37
|
"n_lat": 43200,
|
|
@@ -58,7 +58,7 @@ GEBCO_PARAMETERS: Dict[str, Dict] = {
|
|
|
58
58
|
},
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
EARTH2014_PARAMETERS:
|
|
61
|
+
EARTH2014_PARAMETERS: dict[str, dict[str, Any]] = {
|
|
62
62
|
"SUR": {
|
|
63
63
|
"description": "Physical surface (topography, ice surface, 0 over oceans)",
|
|
64
64
|
"file_pattern": "Earth2014.SUR2014.1min.geod.bin",
|
|
@@ -275,7 +275,7 @@ def parse_gebco_netcdf(
|
|
|
275
275
|
lat_max: Optional[float] = None,
|
|
276
276
|
lon_min: Optional[float] = None,
|
|
277
277
|
lon_max: Optional[float] = None,
|
|
278
|
-
) ->
|
|
278
|
+
) -> tuple[NDArray[np.floating], float, float, float, float]:
|
|
279
279
|
"""Parse GEBCO NetCDF file and extract region.
|
|
280
280
|
|
|
281
281
|
Parameters
|
|
@@ -369,7 +369,7 @@ def parse_earth2014_binary(
|
|
|
369
369
|
lat_max: Optional[float] = None,
|
|
370
370
|
lon_min: Optional[float] = None,
|
|
371
371
|
lon_max: Optional[float] = None,
|
|
372
|
-
) ->
|
|
372
|
+
) -> tuple[NDArray[np.floating], float, float, float, float]:
|
|
373
373
|
"""Parse Earth2014 binary file and extract region.
|
|
374
374
|
|
|
375
375
|
Earth2014 files are stored as int16 big-endian binary data,
|
pytcl/trackers/hypothesis.py
CHANGED
pytcl/trackers/mht.py
CHANGED
|
@@ -15,7 +15,7 @@ References
|
|
|
15
15
|
IEEE Trans. Automatic Control, 1979.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from typing import Callable, Dict, List, NamedTuple, Optional
|
|
18
|
+
from typing import Callable, Dict, List, NamedTuple, Optional
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
from numpy.typing import ArrayLike, NDArray
|
|
@@ -322,8 +322,8 @@ class MHTTracker:
|
|
|
322
322
|
def _predict_tracks(
|
|
323
323
|
self,
|
|
324
324
|
tracks: Dict[int, MHTTrack],
|
|
325
|
-
F: NDArray,
|
|
326
|
-
Q: NDArray,
|
|
325
|
+
F: NDArray[np.floating],
|
|
326
|
+
Q: NDArray[np.floating],
|
|
327
327
|
) -> Dict[int, MHTTrack]:
|
|
328
328
|
"""Predict all tracks forward in time."""
|
|
329
329
|
predicted = {}
|
|
@@ -352,8 +352,8 @@ class MHTTracker:
|
|
|
352
352
|
def _compute_gating_and_likelihoods(
|
|
353
353
|
self,
|
|
354
354
|
tracks: Dict[int, MHTTrack],
|
|
355
|
-
Z: NDArray,
|
|
356
|
-
) -> tuple[
|
|
355
|
+
Z: NDArray[np.floating],
|
|
356
|
+
) -> tuple[set[tuple[int, int]], dict[tuple[int, int], float]]:
|
|
357
357
|
"""Compute gating matrix and likelihood values."""
|
|
358
358
|
gated = set()
|
|
359
359
|
likelihood_matrix = {}
|
|
@@ -387,8 +387,8 @@ class MHTTracker:
|
|
|
387
387
|
self,
|
|
388
388
|
association: Dict[int, int],
|
|
389
389
|
tracks: Dict[int, MHTTrack],
|
|
390
|
-
Z: NDArray,
|
|
391
|
-
likelihood_matrix:
|
|
390
|
+
Z: NDArray[np.floating],
|
|
391
|
+
likelihood_matrix: dict[tuple[int, int], float],
|
|
392
392
|
) -> float:
|
|
393
393
|
"""Compute likelihood of a joint association."""
|
|
394
394
|
likelihood = 1.0
|
|
@@ -417,7 +417,7 @@ class MHTTracker:
|
|
|
417
417
|
def _update_track(
|
|
418
418
|
self,
|
|
419
419
|
track: MHTTrack,
|
|
420
|
-
measurement: NDArray,
|
|
420
|
+
measurement: NDArray[np.floating],
|
|
421
421
|
meas_idx: int,
|
|
422
422
|
) -> MHTTrack:
|
|
423
423
|
"""Update a track with a measurement."""
|
|
@@ -502,7 +502,7 @@ class MHTTracker:
|
|
|
502
502
|
|
|
503
503
|
def _initiate_track(
|
|
504
504
|
self,
|
|
505
|
-
measurement: NDArray,
|
|
505
|
+
measurement: NDArray[np.floating],
|
|
506
506
|
meas_idx: int,
|
|
507
507
|
) -> MHTTrack:
|
|
508
508
|
"""Initiate a new track from a measurement."""
|
pytcl/trackers/multi_target.py
CHANGED
|
@@ -225,7 +225,7 @@ class MultiTargetTracker:
|
|
|
225
225
|
track.covariance = F @ track.covariance @ F.T + Q
|
|
226
226
|
track.time = self._time
|
|
227
227
|
|
|
228
|
-
def _associate(self, Z: NDArray[np.float64]) -> dict:
|
|
228
|
+
def _associate(self, Z: NDArray[np.float64]) -> dict[int, int]:
|
|
229
229
|
"""
|
|
230
230
|
Associate measurements to tracks using GNN.
|
|
231
231
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|