nrl-tracker 1.11.0__tar.gz → 1.12.0__tar.gz
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.11.0 → nrl_tracker-1.12.0}/CONTRIBUTING.md +7 -7
- {nrl_tracker-1.11.0/nrl_tracker.egg-info → nrl_tracker-1.12.0}/PKG-INFO +4 -4
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/README.md +3 -3
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0/nrl_tracker.egg-info}/PKG-INFO +4 -4
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/nrl_tracker.egg-info/SOURCES.txt +12 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pyproject.toml +1 -1
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/__init__.py +2 -2
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/network_flow.py +172 -60
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/time_systems.py +21 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/cluster_set.py +36 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/conversions/geodetic.py +58 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/array_utils.py +52 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/ekf.py +46 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/kalman.py +16 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/matrix_utils.py +44 -1
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/particle_filter.py +33 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/ukf.py +31 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/utils.py +15 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/magnetism/igrf.py +72 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/magnetism/wmm.py +52 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/basic_matrix/decompositions.py +7 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/basic_matrix/special_matrices.py +31 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/geometry/geometry.py +33 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/interpolation/interpolation.py +83 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/signal_processing/detection.py +31 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/signal_processing/filters.py +56 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/signal_processing/matched_filter.py +32 -1
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/hypergeometric.py +17 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/statistics/estimators.py +71 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/transforms/wavelets.py +25 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/great_circle.py +33 -0
- nrl_tracker-1.12.0/tests/test_debye_comprehensive.py +395 -0
- nrl_tracker-1.12.0/tests/test_detection_comprehensive.py +395 -0
- nrl_tracker-1.12.0/tests/test_distributions_comprehensive.py +371 -0
- nrl_tracker-1.12.0/tests/test_egm_comprehensive.py +354 -0
- nrl_tracker-1.12.0/tests/test_gpu_utils_comprehensive.py +386 -0
- nrl_tracker-1.12.0/tests/test_hypergeometric_comprehensive.py +287 -0
- nrl_tracker-1.12.0/tests/test_hypergeometric_stft_targeted.py +339 -0
- nrl_tracker-1.12.0/tests/test_lambert_comprehensive.py +428 -0
- nrl_tracker-1.12.0/tests/test_maturity_comprehensive.py +312 -0
- nrl_tracker-1.12.0/tests/test_metrics_plotting_comprehensive.py +470 -0
- nrl_tracker-1.12.0/tests/test_network_flow_comprehensive.py +382 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_sgp4.py +4 -2
- nrl_tracker-1.12.0/tests/test_stft_comprehensive.py +370 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/LICENSE +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/MANIFEST.in +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/nrl_tracker.egg-info/dependency_links.txt +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/nrl_tracker.egg-info/requires.txt +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/nrl_tracker.egg-info/top_level.txt +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/data_association.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/dijkstra_min_cost.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/gating.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/jpda.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/nd_assignment.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/network_simplex.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/three_dimensional/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/three_dimensional/assignment.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/two_dimensional/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/two_dimensional/assignment.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/assignment_algorithms/two_dimensional/kbest.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/ephemerides.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/lambert.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/orbital_mechanics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/reference_frames.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/relativity.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/sgp4.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/special_orbits.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/astronomical/tle.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/atmosphere/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/atmosphere/ionosphere.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/atmosphere/models.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/atmosphere/nrlmsise00.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/clustering/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/clustering/dbscan.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/clustering/gaussian_mixture.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/clustering/hierarchical.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/clustering/kmeans.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/base.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/covertree.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/kd_tree.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/measurement_set.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/rtree.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/track_list.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/containers/vptree.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/conversions/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/conversions/spherical.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/jacobians/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/jacobians/jacobians.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/projections/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/projections/projections.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/rotations/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/coordinate_systems/rotations/rotations.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/constants.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/exceptions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/maturity.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/optional_deps.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/core/validation.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/batch_estimation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/imm.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/information_filter.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/constrained.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/extended.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/h_infinity.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/linear.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/matrix_utils.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/square_root.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/sr_ukf.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/types.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/ud_filter.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/kalman/unscented.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/measurement_update/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/particle_filters/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/particle_filters/bootstrap.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/rbpf.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_estimation/smoothers.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/continuous_time/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/continuous_time/dynamics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/discrete_time/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/discrete_time/coordinated_turn.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/discrete_time/polynomial.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/discrete_time/singer.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/process_noise/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/process_noise/coordinated_turn.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/process_noise/polynomial.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/dynamic_models/process_noise/singer.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gpu/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/clenshaw.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/egm.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/models.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/spherical_harmonics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/gravity/tides.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/logging_config.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/magnetism/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/magnetism/emm.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/basic_matrix/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/combinatorics/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/combinatorics/combinatorics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/continuous_optimization/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/geometry/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/interpolation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/numerical_integration/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/numerical_integration/quadrature.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/polynomials/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/signal_processing/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/bessel.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/debye.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/elliptic.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/error_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/gamma_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/lambert_w.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/special_functions/marcum_q.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/statistics/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/statistics/distributions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/transforms/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/transforms/fourier.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/mathematical_functions/transforms/stft.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/misc/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/geodesy.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/ins.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/ins_gnss.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/navigation/rhumb.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/performance_evaluation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/performance_evaluation/estimation_metrics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/performance_evaluation/track_metrics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/physical_values/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/plotting/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/plotting/coordinates.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/plotting/ellipses.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/plotting/metrics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/plotting/tracks.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/scheduling/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/static_estimation/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/static_estimation/least_squares.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/static_estimation/maximum_likelihood.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/static_estimation/robust.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/terrain/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/terrain/dem.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/terrain/loaders.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/terrain/visibility.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/trackers/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/trackers/hypothesis.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/trackers/mht.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/trackers/multi_target.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/trackers/single_target.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/pytcl/transponders/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/setup.cfg +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/__init__.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/conftest.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_additional_trees.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_assignment_algorithms.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_astronomical.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_cfar_detection.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_clustering.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_combinatorics.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_constrained_ekf.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_coordinate_systems.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_coverage_boost.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_coverage_boost_2.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_debye.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_dynamic_models.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_egm.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_elliptic.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_emm.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_ephemerides.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_error_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_exceptions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_gamma_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_gaussian_mixtures.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_gaussian_sum_filter.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_geometry.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_geophysical.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_gpu.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_gpu_utils.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_great_circle.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_h_infinity.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_hypergeometric.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_ins.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_ins_gnss.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_ionosphere.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_jpda.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_kalman_filters.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_marcum_q.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_matched_filter.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_mathematical_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_maximum_likelihood.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_mht.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_nd_assignment.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_network_flow.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_nrlmsise00.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_optional_deps.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_performance_evaluation.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_phase6_specialized.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_plotting.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_projections.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_quadrature.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_rbpf.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_relativity.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_rhumb.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_rotations.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_signal_processing.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_smoothers.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_spatial_containers_parametrized.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_spatial_structures.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_special_functions.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_special_orbits.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_square_root_filters.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_static_estimation.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_terrain.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_terrain_loaders.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_tides.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_tod_mod.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_trackers.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_tracking_containers.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_transforms.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_validation.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/test_wavelets.py +0 -0
- {nrl_tracker-1.11.0 → nrl_tracker-1.12.0}/tests/unit/test_core.py +0 -0
|
@@ -148,8 +148,8 @@ from scipy.io import loadmat
|
|
|
148
148
|
import numpy as np
|
|
149
149
|
|
|
150
150
|
data = loadmat('cart2sphere_reference.mat')
|
|
151
|
-
np.savez('cart2sphere_reference.npz',
|
|
152
|
-
input=data['input'],
|
|
151
|
+
np.savez('cart2sphere_reference.npz',
|
|
152
|
+
input=data['input'],
|
|
153
153
|
expected=data['output'])
|
|
154
154
|
```
|
|
155
155
|
|
|
@@ -176,10 +176,10 @@ When porting a function from the original MATLAB library:
|
|
|
176
176
|
Notes
|
|
177
177
|
-----
|
|
178
178
|
This is a port of Cart2Sphere.m from the MATLAB Tracker Component Library.
|
|
179
|
-
|
|
179
|
+
|
|
180
180
|
References
|
|
181
181
|
----------
|
|
182
|
-
.. [1] Original implementation:
|
|
182
|
+
.. [1] Original implementation:
|
|
183
183
|
https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary
|
|
184
184
|
```
|
|
185
185
|
|
|
@@ -204,13 +204,13 @@ When porting a function from the original MATLAB library:
|
|
|
204
204
|
```bash
|
|
205
205
|
# Format code
|
|
206
206
|
black .
|
|
207
|
-
|
|
207
|
+
|
|
208
208
|
# Lint
|
|
209
209
|
flake8 pytcl tests
|
|
210
|
-
|
|
210
|
+
|
|
211
211
|
# Type check
|
|
212
212
|
mypy pytcl
|
|
213
|
-
|
|
213
|
+
|
|
214
214
|
# Run tests
|
|
215
215
|
pytest
|
|
216
216
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms
|
|
5
5
|
Author: Original: David F. Crouse, Naval Research Laboratory
|
|
6
6
|
Maintainer: Python Port Contributors
|
|
@@ -71,17 +71,17 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
71
71
|
|
|
72
72
|
# Tracker Component Library (Python)
|
|
73
73
|
|
|
74
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
75
75
|
[](https://www.python.org/downloads/)
|
|
76
76
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
77
77
|
[](https://github.com/psf/black)
|
|
78
|
-
[](https://github.com/nedonatelli/TCL)
|
|
79
79
|
[](docs/gap_analysis.rst)
|
|
80
80
|
[](mypy.ini)
|
|
81
81
|
|
|
82
82
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
83
83
|
|
|
84
|
-
**1,070+ functions** | **153 modules** | **
|
|
84
|
+
**1,070+ functions** | **153 modules** | **3,280 tests** | **100% MATLAB parity**
|
|
85
85
|
|
|
86
86
|
## Overview
|
|
87
87
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Tracker Component Library (Python)
|
|
2
2
|
|
|
3
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
4
4
|
[](https://www.python.org/downloads/)
|
|
5
5
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
6
6
|
[](https://github.com/psf/black)
|
|
7
|
-
[](https://github.com/nedonatelli/TCL)
|
|
8
8
|
[](docs/gap_analysis.rst)
|
|
9
9
|
[](mypy.ini)
|
|
10
10
|
|
|
11
11
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
12
12
|
|
|
13
|
-
**1,070+ functions** | **153 modules** | **
|
|
13
|
+
**1,070+ functions** | **153 modules** | **3,280 tests** | **100% MATLAB parity**
|
|
14
14
|
|
|
15
15
|
## Overview
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nrl-tracker
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms
|
|
5
5
|
Author: Original: David F. Crouse, Naval Research Laboratory
|
|
6
6
|
Maintainer: Python Port Contributors
|
|
@@ -71,17 +71,17 @@ Requires-Dist: nrl-tracker[astronomy,dev,geodesy,optimization,signal,visualizati
|
|
|
71
71
|
|
|
72
72
|
# Tracker Component Library (Python)
|
|
73
73
|
|
|
74
|
-
[](https://pypi.org/project/nrl-tracker/)
|
|
75
75
|
[](https://www.python.org/downloads/)
|
|
76
76
|
[](https://en.wikipedia.org/wiki/Public_domain)
|
|
77
77
|
[](https://github.com/psf/black)
|
|
78
|
-
[](https://github.com/nedonatelli/TCL)
|
|
79
79
|
[](docs/gap_analysis.rst)
|
|
80
80
|
[](mypy.ini)
|
|
81
81
|
|
|
82
82
|
A Python port of the [U.S. Naval Research Laboratory's Tracker Component Library](https://github.com/USNavalResearchLaboratory/TrackerComponentLibrary), a comprehensive collection of algorithms for target tracking, estimation, coordinate systems, and related mathematical functions.
|
|
83
83
|
|
|
84
|
-
**1,070+ functions** | **153 modules** | **
|
|
84
|
+
**1,070+ functions** | **153 modules** | **3,280 tests** | **100% MATLAB parity**
|
|
85
85
|
|
|
86
86
|
## Overview
|
|
87
87
|
|
|
@@ -195,8 +195,12 @@ tests/test_coordinate_systems.py
|
|
|
195
195
|
tests/test_coverage_boost.py
|
|
196
196
|
tests/test_coverage_boost_2.py
|
|
197
197
|
tests/test_debye.py
|
|
198
|
+
tests/test_debye_comprehensive.py
|
|
199
|
+
tests/test_detection_comprehensive.py
|
|
200
|
+
tests/test_distributions_comprehensive.py
|
|
198
201
|
tests/test_dynamic_models.py
|
|
199
202
|
tests/test_egm.py
|
|
203
|
+
tests/test_egm_comprehensive.py
|
|
200
204
|
tests/test_elliptic.py
|
|
201
205
|
tests/test_emm.py
|
|
202
206
|
tests/test_ephemerides.py
|
|
@@ -209,21 +213,28 @@ tests/test_geometry.py
|
|
|
209
213
|
tests/test_geophysical.py
|
|
210
214
|
tests/test_gpu.py
|
|
211
215
|
tests/test_gpu_utils.py
|
|
216
|
+
tests/test_gpu_utils_comprehensive.py
|
|
212
217
|
tests/test_great_circle.py
|
|
213
218
|
tests/test_h_infinity.py
|
|
214
219
|
tests/test_hypergeometric.py
|
|
220
|
+
tests/test_hypergeometric_comprehensive.py
|
|
221
|
+
tests/test_hypergeometric_stft_targeted.py
|
|
215
222
|
tests/test_ins.py
|
|
216
223
|
tests/test_ins_gnss.py
|
|
217
224
|
tests/test_ionosphere.py
|
|
218
225
|
tests/test_jpda.py
|
|
219
226
|
tests/test_kalman_filters.py
|
|
227
|
+
tests/test_lambert_comprehensive.py
|
|
220
228
|
tests/test_marcum_q.py
|
|
221
229
|
tests/test_matched_filter.py
|
|
222
230
|
tests/test_mathematical_functions.py
|
|
231
|
+
tests/test_maturity_comprehensive.py
|
|
223
232
|
tests/test_maximum_likelihood.py
|
|
233
|
+
tests/test_metrics_plotting_comprehensive.py
|
|
224
234
|
tests/test_mht.py
|
|
225
235
|
tests/test_nd_assignment.py
|
|
226
236
|
tests/test_network_flow.py
|
|
237
|
+
tests/test_network_flow_comprehensive.py
|
|
227
238
|
tests/test_nrlmsise00.py
|
|
228
239
|
tests/test_optional_deps.py
|
|
229
240
|
tests/test_performance_evaluation.py
|
|
@@ -244,6 +255,7 @@ tests/test_special_functions.py
|
|
|
244
255
|
tests/test_special_orbits.py
|
|
245
256
|
tests/test_square_root_filters.py
|
|
246
257
|
tests/test_static_estimation.py
|
|
258
|
+
tests/test_stft_comprehensive.py
|
|
247
259
|
tests/test_terrain.py
|
|
248
260
|
tests/test_terrain_loaders.py
|
|
249
261
|
tests/test_tides.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nrl-tracker"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.12.0"
|
|
8
8
|
description = "Python port of the U.S. Naval Research Laboratory's Tracker Component Library for target tracking algorithms"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -6,7 +6,7 @@ systems, dynamic models, estimation algorithms, and mathematical functions.
|
|
|
6
6
|
|
|
7
7
|
This is a Python port of the U.S. Naval Research Laboratory's Tracker Component
|
|
8
8
|
Library originally written in MATLAB.
|
|
9
|
-
**Current Version:** 1.
|
|
9
|
+
**Current Version:** 1.12.0 (January 5, 2026)
|
|
10
10
|
**Status:** Production-ready, 2,894 tests passing, 76% line coverage
|
|
11
11
|
Examples
|
|
12
12
|
--------
|
|
@@ -21,7 +21,7 @@ References
|
|
|
21
21
|
no. 5, pp. 18-27, May 2017.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
__version__ = "1.
|
|
24
|
+
__version__ = "1.12.0"
|
|
25
25
|
__author__ = "Python Port Contributors"
|
|
26
26
|
__original_author__ = "David F. Crouse, Naval Research Laboratory"
|
|
27
27
|
|
|
@@ -179,13 +179,19 @@ def min_cost_flow_successive_shortest_paths(
|
|
|
179
179
|
max_iterations: int = 1000,
|
|
180
180
|
) -> MinCostFlowResult:
|
|
181
181
|
"""
|
|
182
|
-
Solve min-cost flow using successive shortest paths.
|
|
182
|
+
Solve min-cost flow using successive shortest paths with cost scaling.
|
|
183
183
|
|
|
184
184
|
Algorithm:
|
|
185
|
-
1.
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
185
|
+
1. Initialize potentials using Bellman-Ford
|
|
186
|
+
2. While there is excess supply:
|
|
187
|
+
- Find shortest path using reduced costs (Dijkstra with potentials)
|
|
188
|
+
- Push unit flow along path
|
|
189
|
+
- Update node potentials
|
|
190
|
+
- Recompute shortest paths to maintain optimality
|
|
191
|
+
|
|
192
|
+
This is the standard min-cost flow algorithm that guarantees optimality
|
|
193
|
+
and convergence. It uses Dijkstra's algorithm with potentials, which
|
|
194
|
+
maintains the dual feasibility (reduced cost property).
|
|
189
195
|
|
|
190
196
|
Parameters
|
|
191
197
|
----------
|
|
@@ -217,100 +223,163 @@ def min_cost_flow_successive_shortest_paths(
|
|
|
217
223
|
|
|
218
224
|
Notes
|
|
219
225
|
-----
|
|
220
|
-
This
|
|
221
|
-
|
|
226
|
+
This implementation uses successive shortest paths with potentials.
|
|
227
|
+
The algorithm is guaranteed to find the optimal solution for any
|
|
228
|
+
feasible min-cost flow problem.
|
|
229
|
+
|
|
230
|
+
For rectangular assignment problems (m < n or m > n), all m units
|
|
231
|
+
of flow must be satisfied. The algorithm ensures this by finding
|
|
232
|
+
augmenting paths until all supply is routed.
|
|
222
233
|
"""
|
|
223
234
|
n_nodes = len(supplies)
|
|
224
235
|
n_edges = len(edges)
|
|
225
236
|
|
|
226
|
-
#
|
|
227
|
-
graph: list[list[tuple[int, int, float]]] = [[] for _ in range(n_nodes)]
|
|
237
|
+
# Initialize flow and residual capacity
|
|
228
238
|
flow = np.zeros(n_edges)
|
|
229
239
|
residual_capacity = np.array([e.capacity for e in edges])
|
|
230
240
|
|
|
241
|
+
# Initialize node potentials using Bellman-Ford from a dummy source
|
|
242
|
+
# This ensures all potentials are finite and maintains dual feasibility
|
|
243
|
+
potential = np.zeros(n_nodes)
|
|
244
|
+
|
|
245
|
+
# Build adjacency list representation
|
|
246
|
+
# Each entry: (to_node, edge_idx, is_reverse, cost)
|
|
247
|
+
graph: list[list[tuple[int, int, int, float]]] = [[] for _ in range(n_nodes)]
|
|
248
|
+
|
|
231
249
|
for edge_idx, edge in enumerate(edges):
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
250
|
+
# Forward edge
|
|
251
|
+
graph[edge.from_node].append((edge.to_node, edge_idx, 0, edge.cost))
|
|
252
|
+
# Reverse edge (for flow cancellation)
|
|
253
|
+
graph[edge.to_node].append((edge.from_node, edge_idx, 1, -edge.cost))
|
|
235
254
|
|
|
236
255
|
current_supplies = supplies.copy()
|
|
237
256
|
iteration = 0
|
|
238
257
|
|
|
258
|
+
# Main algorithm loop
|
|
239
259
|
while iteration < max_iterations:
|
|
240
|
-
# Find
|
|
241
|
-
excess_node =
|
|
260
|
+
# Find excess and deficit nodes
|
|
261
|
+
excess_node = -1
|
|
262
|
+
deficit_node = -1
|
|
263
|
+
|
|
242
264
|
for node in range(n_nodes):
|
|
243
265
|
if current_supplies[node] > 1e-10:
|
|
244
266
|
excess_node = node
|
|
245
267
|
break
|
|
246
268
|
|
|
247
|
-
if excess_node
|
|
248
|
-
break
|
|
269
|
+
if excess_node < 0:
|
|
270
|
+
break # No more excess nodes
|
|
249
271
|
|
|
250
|
-
# Find a node with deficit
|
|
251
|
-
deficit_node = None
|
|
252
272
|
for node in range(n_nodes):
|
|
253
273
|
if current_supplies[node] < -1e-10:
|
|
254
274
|
deficit_node = node
|
|
255
275
|
break
|
|
256
276
|
|
|
257
|
-
if deficit_node
|
|
258
|
-
break
|
|
277
|
+
if deficit_node < 0:
|
|
278
|
+
break # No deficit nodes
|
|
259
279
|
|
|
260
|
-
# Find shortest path using
|
|
280
|
+
# Find shortest path using Dijkstra with potentials
|
|
281
|
+
# Reduced cost: c_reduced(u,v) = c(u,v) + π(u) - π(v)
|
|
261
282
|
dist = np.full(n_nodes, np.inf)
|
|
262
283
|
dist[excess_node] = 0.0
|
|
263
284
|
parent = np.full(n_nodes, -1, dtype=int)
|
|
264
285
|
parent_edge = np.full(n_nodes, -1, dtype=int)
|
|
286
|
+
parent_reverse = np.full(n_nodes, 0, dtype=int)
|
|
287
|
+
visited = np.zeros(n_nodes, dtype=bool)
|
|
288
|
+
|
|
289
|
+
# Dijkstra's algorithm
|
|
290
|
+
for _ in range(n_nodes):
|
|
291
|
+
# Find unvisited node with minimum distance
|
|
292
|
+
u = -1
|
|
293
|
+
min_dist = np.inf
|
|
294
|
+
for node in range(n_nodes):
|
|
295
|
+
if not visited[node] and dist[node] < min_dist:
|
|
296
|
+
u = node
|
|
297
|
+
min_dist = dist[node]
|
|
298
|
+
|
|
299
|
+
if u < 0 or dist[u] == np.inf:
|
|
300
|
+
break
|
|
301
|
+
|
|
302
|
+
visited[u] = True
|
|
265
303
|
|
|
266
|
-
|
|
267
|
-
for
|
|
268
|
-
if
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
304
|
+
# Relax edges from u
|
|
305
|
+
for v, eidx, is_rev, cost in graph[u]:
|
|
306
|
+
if residual_capacity[eidx] > 1e-10:
|
|
307
|
+
# Compute reduced cost
|
|
308
|
+
reduced_cost = cost + potential[u] - potential[v]
|
|
309
|
+
new_dist = dist[u] + reduced_cost
|
|
310
|
+
|
|
311
|
+
if new_dist < dist[v] - 1e-10:
|
|
312
|
+
dist[v] = new_dist
|
|
313
|
+
parent[v] = u
|
|
314
|
+
parent_edge[v] = eidx
|
|
315
|
+
parent_reverse[v] = is_rev
|
|
316
|
+
|
|
317
|
+
if dist[deficit_node] >= np.inf:
|
|
279
318
|
# No path found
|
|
280
319
|
break
|
|
281
320
|
|
|
282
|
-
#
|
|
321
|
+
# Update potentials to maintain dual feasibility
|
|
322
|
+
for node in range(n_nodes):
|
|
323
|
+
if dist[node] < np.inf:
|
|
324
|
+
potential[node] += dist[node]
|
|
325
|
+
|
|
326
|
+
# Extract path by backtracking
|
|
283
327
|
path_edges = []
|
|
328
|
+
path_reverse_flags = []
|
|
284
329
|
node = deficit_node
|
|
285
|
-
|
|
330
|
+
path_length = 0
|
|
331
|
+
visited_set = set()
|
|
332
|
+
|
|
333
|
+
while parent[node] >= 0:
|
|
334
|
+
if path_length >= n_nodes:
|
|
335
|
+
break # Safety check
|
|
336
|
+
if node in visited_set:
|
|
337
|
+
break # Cycle detected
|
|
338
|
+
|
|
339
|
+
visited_set.add(node)
|
|
286
340
|
path_edges.append(parent_edge[node])
|
|
341
|
+
path_reverse_flags.append(parent_reverse[node])
|
|
287
342
|
node = parent[node]
|
|
343
|
+
path_length += 1
|
|
344
|
+
|
|
345
|
+
if not path_edges:
|
|
346
|
+
iteration += 1
|
|
347
|
+
continue
|
|
288
348
|
|
|
289
349
|
path_edges.reverse()
|
|
350
|
+
path_reverse_flags.reverse()
|
|
290
351
|
|
|
291
|
-
# Find
|
|
352
|
+
# Find bottleneck capacity
|
|
292
353
|
min_flow = min(residual_capacity[e] for e in path_edges)
|
|
293
354
|
min_flow = min(
|
|
294
|
-
min_flow,
|
|
355
|
+
min_flow,
|
|
356
|
+
current_supplies[excess_node],
|
|
357
|
+
-current_supplies[deficit_node],
|
|
295
358
|
)
|
|
296
359
|
|
|
297
360
|
# Push flow along path
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
361
|
+
for edge_idx, is_reverse in zip(path_edges, path_reverse_flags):
|
|
362
|
+
if is_reverse == 0:
|
|
363
|
+
# Forward edge: increase flow
|
|
364
|
+
flow[edge_idx] += min_flow
|
|
365
|
+
residual_capacity[edge_idx] -= min_flow
|
|
366
|
+
else:
|
|
367
|
+
# Reverse edge: decrease flow (cancel)
|
|
368
|
+
flow[edge_idx] -= min_flow
|
|
369
|
+
residual_capacity[edge_idx] += min_flow
|
|
303
370
|
|
|
304
371
|
current_supplies[excess_node] -= min_flow
|
|
305
372
|
current_supplies[deficit_node] += min_flow
|
|
306
373
|
|
|
307
374
|
iteration += 1
|
|
308
375
|
|
|
309
|
-
# Compute total cost
|
|
310
|
-
total_cost =
|
|
376
|
+
# Compute total cost: include all flows (including negative which cancel)
|
|
377
|
+
total_cost = 0.0
|
|
378
|
+
for i, edge in enumerate(edges):
|
|
379
|
+
total_cost += flow[i] * edge.cost
|
|
311
380
|
|
|
312
381
|
# Determine status
|
|
313
|
-
if np.allclose(current_supplies, 0):
|
|
382
|
+
if np.allclose(current_supplies, 0, atol=1e-6):
|
|
314
383
|
status = FlowStatus.OPTIMAL
|
|
315
384
|
elif iteration >= max_iterations:
|
|
316
385
|
status = FlowStatus.TIMEOUT
|
|
@@ -409,6 +478,14 @@ def assignment_from_flow_solution(
|
|
|
409
478
|
"""
|
|
410
479
|
Extract assignment from flow network solution.
|
|
411
480
|
|
|
481
|
+
A valid flow solution for assignment should have:
|
|
482
|
+
- Exactly 1 unit of flow from each worker to some task
|
|
483
|
+
- Exactly 1 unit of flow to each task from some worker
|
|
484
|
+
- No negative flows on worker->task edges (those are cancellations)
|
|
485
|
+
|
|
486
|
+
This function extracts the actual assignment by identifying which
|
|
487
|
+
worker->task edges carry the net positive flow.
|
|
488
|
+
|
|
412
489
|
Parameters
|
|
413
490
|
----------
|
|
414
491
|
flow : ndarray
|
|
@@ -427,24 +504,59 @@ def assignment_from_flow_solution(
|
|
|
427
504
|
"""
|
|
428
505
|
m, n = cost_matrix_shape
|
|
429
506
|
assignment = []
|
|
507
|
+
cost = 0.0
|
|
508
|
+
|
|
509
|
+
# Build source node (node 0) and sink node (node m+n+1) indices
|
|
510
|
+
source = 0
|
|
511
|
+
sink = m + n + 1
|
|
512
|
+
|
|
513
|
+
# For a valid assignment solution:
|
|
514
|
+
# - Count flow out of source to each worker
|
|
515
|
+
# - Count flow into sink from each task
|
|
516
|
+
worker_outflow = np.zeros(m)
|
|
517
|
+
task_inflow = np.zeros(n)
|
|
518
|
+
|
|
519
|
+
# Collect all worker->task edges and their flows
|
|
520
|
+
worker_task_edges = []
|
|
430
521
|
|
|
431
522
|
for edge_idx, edge in enumerate(edges):
|
|
432
|
-
# Worker
|
|
433
|
-
if
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
task_idx = edge.to_node - m - 1
|
|
437
|
-
assignment.append([worker_idx, task_idx])
|
|
523
|
+
# Worker edges: from source (0) to worker nodes (1..m)
|
|
524
|
+
if edge.from_node == source and 1 <= edge.to_node <= m:
|
|
525
|
+
worker_id = edge.to_node - 1
|
|
526
|
+
worker_outflow[worker_id] += flow[edge_idx]
|
|
438
527
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
np.sum(
|
|
444
|
-
flow[edge_idx] * edges[edge_idx].cost for edge_idx in range(len(edges))
|
|
445
|
-
)
|
|
446
|
-
)
|
|
528
|
+
# Task edges: from task nodes (m+1..m+n) to sink
|
|
529
|
+
if m + 1 <= edge.from_node <= m + n and edge.to_node == sink:
|
|
530
|
+
task_id = edge.from_node - (m + 1)
|
|
531
|
+
task_inflow[task_id] += flow[edge_idx]
|
|
447
532
|
|
|
533
|
+
# Worker-to-task edges
|
|
534
|
+
if 1 <= edge.from_node <= m and m + 1 <= edge.to_node <= m + n:
|
|
535
|
+
worker_id = edge.from_node - 1
|
|
536
|
+
task_id = edge.to_node - (m + 1)
|
|
537
|
+
if flow[edge_idx] > 0.5: # Positive flow means this edge is used
|
|
538
|
+
worker_task_edges.append(
|
|
539
|
+
{
|
|
540
|
+
"worker": worker_id,
|
|
541
|
+
"task": task_id,
|
|
542
|
+
"flow": flow[edge_idx],
|
|
543
|
+
"cost": edge.cost,
|
|
544
|
+
"edge_idx": edge_idx,
|
|
545
|
+
}
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# For assignment problems, each worker should have exactly 1 outgoing flow
|
|
549
|
+
# and each task should have exactly 1 incoming flow
|
|
550
|
+
# Extract the assignment from worker->task edges with positive flow
|
|
551
|
+
for edge_info in worker_task_edges:
|
|
552
|
+
assignment.append([edge_info["worker"], edge_info["task"]])
|
|
553
|
+
cost += edge_info["flow"] * edge_info["cost"]
|
|
554
|
+
|
|
555
|
+
assignment = (
|
|
556
|
+
np.array(assignment, dtype=np.intp)
|
|
557
|
+
if assignment
|
|
558
|
+
else np.empty((0, 2), dtype=np.intp)
|
|
559
|
+
)
|
|
448
560
|
return assignment, cost
|
|
449
561
|
|
|
450
562
|
|
|
@@ -269,6 +269,13 @@ def mjd_to_jd(mjd: float) -> float:
|
|
|
269
269
|
float
|
|
270
270
|
Julian Date.
|
|
271
271
|
|
|
272
|
+
Examples
|
|
273
|
+
--------
|
|
274
|
+
>>> mjd = 44239.0 # 1980-01-01
|
|
275
|
+
>>> jd = mjd_to_jd(mjd)
|
|
276
|
+
>>> jd
|
|
277
|
+
2444239.5
|
|
278
|
+
|
|
272
279
|
Notes
|
|
273
280
|
-----
|
|
274
281
|
MJD = JD - 2400000.5
|
|
@@ -289,6 +296,13 @@ def jd_to_mjd(jd: float) -> float:
|
|
|
289
296
|
-------
|
|
290
297
|
float
|
|
291
298
|
Modified Julian Date.
|
|
299
|
+
|
|
300
|
+
Examples
|
|
301
|
+
--------
|
|
302
|
+
>>> jd = 2444239.5 # 1980-01-01
|
|
303
|
+
>>> mjd = jd_to_mjd(jd)
|
|
304
|
+
>>> mjd
|
|
305
|
+
44239.0
|
|
292
306
|
"""
|
|
293
307
|
return jd - MJD_OFFSET
|
|
294
308
|
|
|
@@ -328,6 +342,13 @@ def jd_to_unix(jd: float) -> float:
|
|
|
328
342
|
-------
|
|
329
343
|
float
|
|
330
344
|
Unix timestamp.
|
|
345
|
+
|
|
346
|
+
Examples
|
|
347
|
+
--------
|
|
348
|
+
>>> jd = 2440587.5 # 1970-01-01 00:00:00 UTC
|
|
349
|
+
>>> unix_to_jd = jd_to_unix(jd)
|
|
350
|
+
>>> unix_to_jd
|
|
351
|
+
0.0
|
|
331
352
|
"""
|
|
332
353
|
return (jd - JD_UNIX_EPOCH) * 86400.0
|
|
333
354
|
|
|
@@ -94,6 +94,23 @@ def compute_cluster_centroid(
|
|
|
94
94
|
-------
|
|
95
95
|
centroid : ndarray
|
|
96
96
|
Centroid position [x, y].
|
|
97
|
+
|
|
98
|
+
Examples
|
|
99
|
+
--------
|
|
100
|
+
>>> from pytcl.trackers.multi_target import Track
|
|
101
|
+
>>> import numpy as np
|
|
102
|
+
>>> # Create sample tracks with [x, vx, y, vy] state vectors
|
|
103
|
+
>>> track1 = Track(state=np.array([0.0, 1.0, 0.0, 1.0]))
|
|
104
|
+
>>> track2 = Track(state=np.array([2.0, 1.0, 2.0, 1.0]))
|
|
105
|
+
>>> track3 = Track(state=np.array([4.0, 1.0, 4.0, 1.0]))
|
|
106
|
+
>>> tracks = [track1, track2, track3]
|
|
107
|
+
>>> centroid = compute_cluster_centroid(tracks)
|
|
108
|
+
>>> centroid
|
|
109
|
+
array([2., 2.])
|
|
110
|
+
|
|
111
|
+
See Also
|
|
112
|
+
--------
|
|
113
|
+
compute_cluster_covariance : Compute covariance of track positions.
|
|
97
114
|
"""
|
|
98
115
|
track_list = list(tracks)
|
|
99
116
|
if len(track_list) == 0:
|
|
@@ -122,6 +139,25 @@ def compute_cluster_covariance(
|
|
|
122
139
|
-------
|
|
123
140
|
covariance : ndarray
|
|
124
141
|
Position covariance matrix (2x2).
|
|
142
|
+
|
|
143
|
+
Examples
|
|
144
|
+
--------
|
|
145
|
+
>>> from pytcl.trackers.multi_target import Track
|
|
146
|
+
>>> import numpy as np
|
|
147
|
+
>>> # Create collinear tracks (high variance along x-axis)
|
|
148
|
+
>>> track1 = Track(state=np.array([0.0, 1.0, 0.0, 0.0]))
|
|
149
|
+
>>> track2 = Track(state=np.array([1.0, 1.0, 0.0, 0.0]))
|
|
150
|
+
>>> track3 = Track(state=np.array([2.0, 1.0, 0.0, 0.0]))
|
|
151
|
+
>>> tracks = [track1, track2, track3]
|
|
152
|
+
>>> cov = compute_cluster_covariance(tracks)
|
|
153
|
+
>>> cov.shape
|
|
154
|
+
(2, 2)
|
|
155
|
+
>>> cov[0, 0] > cov[1, 1] # More spread in x than y
|
|
156
|
+
True
|
|
157
|
+
|
|
158
|
+
See Also
|
|
159
|
+
--------
|
|
160
|
+
compute_cluster_centroid : Compute centroid of track positions.
|
|
125
161
|
"""
|
|
126
162
|
track_list = list(tracks)
|
|
127
163
|
if len(track_list) < 2:
|