nrl-tracker 1.2.0__tar.gz → 1.4.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.
Files changed (208) hide show
  1. {nrl_tracker-1.2.0/nrl_tracker.egg-info → nrl_tracker-1.4.0}/PKG-INFO +1 -1
  2. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0/nrl_tracker.egg-info}/PKG-INFO +1 -1
  3. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/nrl_tracker.egg-info/SOURCES.txt +3 -0
  4. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pyproject.toml +1 -1
  5. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/__init__.py +1 -1
  6. nrl_tracker-1.4.0/pytcl/atmosphere/__init__.py +68 -0
  7. nrl_tracker-1.4.0/pytcl/atmosphere/ionosphere.py +512 -0
  8. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/rtree.py +199 -0
  9. nrl_tracker-1.4.0/pytcl/dynamic_estimation/kalman/square_root.py +484 -0
  10. nrl_tracker-1.4.0/pytcl/dynamic_estimation/kalman/sr_ukf.py +302 -0
  11. nrl_tracker-1.4.0/pytcl/dynamic_estimation/kalman/ud_filter.py +404 -0
  12. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/magnetism/__init__.py +7 -0
  13. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/magnetism/wmm.py +260 -23
  14. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/debye.py +132 -26
  15. nrl_tracker-1.2.0/pytcl/atmosphere/__init__.py +0 -37
  16. nrl_tracker-1.2.0/pytcl/dynamic_estimation/kalman/square_root.py +0 -1003
  17. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/CONTRIBUTING.md +0 -0
  18. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/LICENSE +0 -0
  19. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/MANIFEST.in +0 -0
  20. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/README.md +0 -0
  21. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/nrl_tracker.egg-info/dependency_links.txt +0 -0
  22. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/nrl_tracker.egg-info/requires.txt +0 -0
  23. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/nrl_tracker.egg-info/top_level.txt +0 -0
  24. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/__init__.py +0 -0
  25. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/data_association.py +0 -0
  26. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/gating.py +0 -0
  27. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/jpda.py +0 -0
  28. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/three_dimensional/__init__.py +0 -0
  29. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/three_dimensional/assignment.py +0 -0
  30. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/two_dimensional/__init__.py +0 -0
  31. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/two_dimensional/assignment.py +0 -0
  32. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/assignment_algorithms/two_dimensional/kbest.py +0 -0
  33. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/__init__.py +0 -0
  34. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/ephemerides.py +0 -0
  35. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/lambert.py +0 -0
  36. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/orbital_mechanics.py +0 -0
  37. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/reference_frames.py +0 -0
  38. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/relativity.py +0 -0
  39. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/astronomical/time_systems.py +0 -0
  40. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/atmosphere/models.py +0 -0
  41. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/clustering/__init__.py +0 -0
  42. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/clustering/dbscan.py +0 -0
  43. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/clustering/gaussian_mixture.py +0 -0
  44. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/clustering/hierarchical.py +0 -0
  45. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/clustering/kmeans.py +0 -0
  46. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/__init__.py +0 -0
  47. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/base.py +0 -0
  48. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/cluster_set.py +0 -0
  49. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/covertree.py +0 -0
  50. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/kd_tree.py +0 -0
  51. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/measurement_set.py +0 -0
  52. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/track_list.py +0 -0
  53. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/containers/vptree.py +0 -0
  54. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/__init__.py +0 -0
  55. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/conversions/__init__.py +0 -0
  56. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/conversions/geodetic.py +0 -0
  57. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/conversions/spherical.py +0 -0
  58. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/jacobians/__init__.py +0 -0
  59. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/jacobians/jacobians.py +0 -0
  60. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/projections/__init__.py +0 -0
  61. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/projections/projections.py +0 -0
  62. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/rotations/__init__.py +0 -0
  63. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/coordinate_systems/rotations/rotations.py +0 -0
  64. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/core/__init__.py +0 -0
  65. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/core/array_utils.py +0 -0
  66. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/core/constants.py +0 -0
  67. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/core/validation.py +0 -0
  68. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/__init__.py +0 -0
  69. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/batch_estimation/__init__.py +0 -0
  70. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/imm.py +0 -0
  71. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/information_filter.py +0 -0
  72. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/kalman/__init__.py +0 -0
  73. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/kalman/extended.py +0 -0
  74. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/kalman/linear.py +0 -0
  75. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/kalman/unscented.py +0 -0
  76. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/measurement_update/__init__.py +0 -0
  77. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/particle_filters/__init__.py +0 -0
  78. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/particle_filters/bootstrap.py +0 -0
  79. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_estimation/smoothers.py +0 -0
  80. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/__init__.py +0 -0
  81. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/continuous_time/__init__.py +0 -0
  82. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/continuous_time/dynamics.py +0 -0
  83. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/discrete_time/__init__.py +0 -0
  84. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/discrete_time/coordinated_turn.py +0 -0
  85. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/discrete_time/polynomial.py +0 -0
  86. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/discrete_time/singer.py +0 -0
  87. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/process_noise/__init__.py +0 -0
  88. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/process_noise/coordinated_turn.py +0 -0
  89. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/process_noise/polynomial.py +0 -0
  90. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/dynamic_models/process_noise/singer.py +0 -0
  91. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/__init__.py +0 -0
  92. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/clenshaw.py +0 -0
  93. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/egm.py +0 -0
  94. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/models.py +0 -0
  95. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/spherical_harmonics.py +0 -0
  96. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/gravity/tides.py +0 -0
  97. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/logging_config.py +0 -0
  98. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/magnetism/emm.py +0 -0
  99. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/magnetism/igrf.py +0 -0
  100. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/__init__.py +0 -0
  101. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/basic_matrix/__init__.py +0 -0
  102. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/basic_matrix/decompositions.py +0 -0
  103. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/basic_matrix/special_matrices.py +0 -0
  104. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/combinatorics/__init__.py +0 -0
  105. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/combinatorics/combinatorics.py +0 -0
  106. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/continuous_optimization/__init__.py +0 -0
  107. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/geometry/__init__.py +0 -0
  108. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/geometry/geometry.py +0 -0
  109. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/interpolation/__init__.py +0 -0
  110. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/interpolation/interpolation.py +0 -0
  111. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/numerical_integration/__init__.py +0 -0
  112. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/numerical_integration/quadrature.py +0 -0
  113. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/polynomials/__init__.py +0 -0
  114. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/signal_processing/__init__.py +0 -0
  115. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/signal_processing/detection.py +0 -0
  116. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/signal_processing/filters.py +0 -0
  117. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/signal_processing/matched_filter.py +0 -0
  118. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/__init__.py +0 -0
  119. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/bessel.py +0 -0
  120. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/elliptic.py +0 -0
  121. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/error_functions.py +0 -0
  122. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/gamma_functions.py +0 -0
  123. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/hypergeometric.py +0 -0
  124. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/lambert_w.py +0 -0
  125. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/special_functions/marcum_q.py +0 -0
  126. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/statistics/__init__.py +0 -0
  127. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/statistics/distributions.py +0 -0
  128. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/statistics/estimators.py +0 -0
  129. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/transforms/__init__.py +0 -0
  130. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/transforms/fourier.py +0 -0
  131. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/transforms/stft.py +0 -0
  132. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/mathematical_functions/transforms/wavelets.py +0 -0
  133. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/misc/__init__.py +0 -0
  134. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/__init__.py +0 -0
  135. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/geodesy.py +0 -0
  136. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/great_circle.py +0 -0
  137. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/ins.py +0 -0
  138. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/ins_gnss.py +0 -0
  139. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/navigation/rhumb.py +0 -0
  140. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/performance_evaluation/__init__.py +0 -0
  141. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/performance_evaluation/estimation_metrics.py +0 -0
  142. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/performance_evaluation/track_metrics.py +0 -0
  143. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/physical_values/__init__.py +0 -0
  144. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/plotting/__init__.py +0 -0
  145. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/plotting/coordinates.py +0 -0
  146. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/plotting/ellipses.py +0 -0
  147. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/plotting/metrics.py +0 -0
  148. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/plotting/tracks.py +0 -0
  149. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/scheduling/__init__.py +0 -0
  150. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/static_estimation/__init__.py +0 -0
  151. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/static_estimation/least_squares.py +0 -0
  152. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/static_estimation/maximum_likelihood.py +0 -0
  153. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/static_estimation/robust.py +0 -0
  154. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/terrain/__init__.py +0 -0
  155. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/terrain/dem.py +0 -0
  156. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/terrain/loaders.py +0 -0
  157. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/terrain/visibility.py +0 -0
  158. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/trackers/__init__.py +0 -0
  159. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/trackers/hypothesis.py +0 -0
  160. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/trackers/mht.py +0 -0
  161. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/trackers/multi_target.py +0 -0
  162. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/trackers/single_target.py +0 -0
  163. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/pytcl/transponders/__init__.py +0 -0
  164. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/setup.cfg +0 -0
  165. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/__init__.py +0 -0
  166. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/conftest.py +0 -0
  167. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_additional_trees.py +0 -0
  168. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_assignment_algorithms.py +0 -0
  169. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_astronomical.py +0 -0
  170. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_clustering.py +0 -0
  171. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_coordinate_systems.py +0 -0
  172. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_coverage_boost.py +0 -0
  173. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_coverage_boost_2.py +0 -0
  174. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_dynamic_models.py +0 -0
  175. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_egm.py +0 -0
  176. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_emm.py +0 -0
  177. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_ephemerides.py +0 -0
  178. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_gaussian_mixtures.py +0 -0
  179. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_geophysical.py +0 -0
  180. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_great_circle.py +0 -0
  181. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_ins.py +0 -0
  182. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_ins_gnss.py +0 -0
  183. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_kalman_filters.py +0 -0
  184. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_mathematical_functions.py +0 -0
  185. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_maximum_likelihood.py +0 -0
  186. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_mht.py +0 -0
  187. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_performance_evaluation.py +0 -0
  188. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_phase6_specialized.py +0 -0
  189. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_plotting.py +0 -0
  190. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_projections.py +0 -0
  191. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_relativity.py +0 -0
  192. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_rhumb.py +0 -0
  193. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_signal_processing.py +0 -0
  194. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_smoothers.py +0 -0
  195. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_spatial_containers_parametrized.py +0 -0
  196. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_spatial_structures.py +0 -0
  197. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_special_functions_phase12.py +0 -0
  198. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_static_estimation.py +0 -0
  199. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_terrain.py +0 -0
  200. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_terrain_loaders.py +0 -0
  201. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_tides.py +0 -0
  202. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_trackers.py +0 -0
  203. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_tracking_containers.py +0 -0
  204. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_transforms.py +0 -0
  205. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_v030_comprehensive.py +0 -0
  206. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_v030_features.py +0 -0
  207. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/test_validation.py +0 -0
  208. {nrl_tracker-1.2.0 → nrl_tracker-1.4.0}/tests/unit/test_core.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nrl-tracker
3
- Version: 1.2.0
3
+ Version: 1.4.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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: nrl-tracker
3
- Version: 1.2.0
3
+ Version: 1.4.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
@@ -27,6 +27,7 @@ pytcl/astronomical/reference_frames.py
27
27
  pytcl/astronomical/relativity.py
28
28
  pytcl/astronomical/time_systems.py
29
29
  pytcl/atmosphere/__init__.py
30
+ pytcl/atmosphere/ionosphere.py
30
31
  pytcl/atmosphere/models.py
31
32
  pytcl/clustering/__init__.py
32
33
  pytcl/clustering/dbscan.py
@@ -65,6 +66,8 @@ pytcl/dynamic_estimation/kalman/__init__.py
65
66
  pytcl/dynamic_estimation/kalman/extended.py
66
67
  pytcl/dynamic_estimation/kalman/linear.py
67
68
  pytcl/dynamic_estimation/kalman/square_root.py
69
+ pytcl/dynamic_estimation/kalman/sr_ukf.py
70
+ pytcl/dynamic_estimation/kalman/ud_filter.py
68
71
  pytcl/dynamic_estimation/kalman/unscented.py
69
72
  pytcl/dynamic_estimation/measurement_update/__init__.py
70
73
  pytcl/dynamic_estimation/particle_filters/__init__.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nrl-tracker"
7
- version = "1.2.0"
7
+ version = "1.4.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 = [
@@ -20,7 +20,7 @@ References
20
20
  no. 5, pp. 18-27, May 2017.
21
21
  """
22
22
 
23
- __version__ = "1.2.0"
23
+ __version__ = "1.4.0"
24
24
  __author__ = "Python Port Contributors"
25
25
  __original_author__ = "David F. Crouse, Naval Research Laboratory"
26
26
 
@@ -0,0 +1,68 @@
1
+ """
2
+ Atmospheric models for tracking applications.
3
+
4
+ This module provides standard atmosphere models used for computing
5
+ temperature, pressure, density, and other properties at various altitudes.
6
+
7
+ Submodules
8
+ ----------
9
+ models : Standard atmosphere models (US76, ISA)
10
+ ionosphere : Ionospheric models for GPS/GNSS corrections
11
+ """
12
+
13
+ from pytcl.atmosphere.ionosphere import (
14
+ DEFAULT_KLOBUCHAR,
15
+ F_L1,
16
+ F_L2,
17
+ IonosphereState,
18
+ KlobucharCoefficients,
19
+ dual_frequency_tec,
20
+ ionospheric_delay_from_tec,
21
+ klobuchar_delay,
22
+ magnetic_latitude,
23
+ scintillation_index,
24
+ simple_iri,
25
+ )
26
+ from pytcl.atmosphere.models import G0 # Constants
27
+ from pytcl.atmosphere.models import (
28
+ GAMMA,
29
+ P0,
30
+ RHO0,
31
+ T0,
32
+ AtmosphereState,
33
+ R,
34
+ altitude_from_pressure,
35
+ isa_atmosphere,
36
+ mach_number,
37
+ true_airspeed_from_mach,
38
+ us_standard_atmosphere_1976,
39
+ )
40
+
41
+ __all__ = [
42
+ # Atmosphere state and models
43
+ "AtmosphereState",
44
+ "us_standard_atmosphere_1976",
45
+ "isa_atmosphere",
46
+ "altitude_from_pressure",
47
+ "mach_number",
48
+ "true_airspeed_from_mach",
49
+ # Atmosphere constants
50
+ "T0",
51
+ "P0",
52
+ "RHO0",
53
+ "G0",
54
+ "R",
55
+ "GAMMA",
56
+ # Ionosphere
57
+ "IonosphereState",
58
+ "KlobucharCoefficients",
59
+ "DEFAULT_KLOBUCHAR",
60
+ "klobuchar_delay",
61
+ "dual_frequency_tec",
62
+ "ionospheric_delay_from_tec",
63
+ "simple_iri",
64
+ "magnetic_latitude",
65
+ "scintillation_index",
66
+ "F_L1",
67
+ "F_L2",
68
+ ]
@@ -0,0 +1,512 @@
1
+ """
2
+ Ionospheric models for radio propagation and navigation applications.
3
+
4
+ This module provides ionospheric models used for computing signal delays,
5
+ electron density profiles, and Total Electron Content (TEC) estimates.
6
+ These are essential for GPS/GNSS corrections and radio wave propagation.
7
+
8
+ Models
9
+ ------
10
+ - Klobuchar: GPS broadcast ionospheric model (single-frequency correction)
11
+ - NeQuick: Galileo ionospheric model placeholder
12
+ - IRI: International Reference Ionosphere simplified model
13
+
14
+ References
15
+ ----------
16
+ .. [1] Klobuchar, J.A. (1987). "Ionospheric Time-Delay Algorithm for
17
+ Single-Frequency GPS Users". IEEE Transactions on Aerospace and
18
+ Electronic Systems, AES-23(3), 325-331.
19
+ .. [2] Nava, B., Coisson, P., & Radicella, S.M. (2008). "A new version
20
+ of the NeQuick ionosphere electron density model". Journal of
21
+ Atmospheric and Solar-Terrestrial Physics, 70(15), 1856-1862.
22
+ """
23
+
24
+ from typing import NamedTuple
25
+
26
+ import numpy as np
27
+ from numpy.typing import ArrayLike, NDArray
28
+
29
+ # Physical constants
30
+ SPEED_OF_LIGHT = 299792458.0 # m/s
31
+ F_L1 = 1575.42e6 # GPS L1 frequency (Hz)
32
+ F_L2 = 1227.60e6 # GPS L2 frequency (Hz)
33
+
34
+
35
+ class IonosphereState(NamedTuple):
36
+ """
37
+ Ionospheric state at a given location and time.
38
+
39
+ Attributes
40
+ ----------
41
+ tec : float or ndarray
42
+ Total Electron Content in TECU (10^16 electrons/m²).
43
+ delay_l1 : float or ndarray
44
+ Ionospheric delay at L1 frequency in meters.
45
+ delay_l2 : float or ndarray
46
+ Ionospheric delay at L2 frequency in meters.
47
+ f_peak : float or ndarray
48
+ Critical frequency of F2 layer in MHz.
49
+ h_peak : float or ndarray
50
+ Height of F2 layer peak in km.
51
+ """
52
+
53
+ tec: float | NDArray[np.float64]
54
+ delay_l1: float | NDArray[np.float64]
55
+ delay_l2: float | NDArray[np.float64]
56
+ f_peak: float | NDArray[np.float64]
57
+ h_peak: float | NDArray[np.float64]
58
+
59
+
60
+ class KlobucharCoefficients(NamedTuple):
61
+ """
62
+ Klobuchar ionospheric model coefficients.
63
+
64
+ These coefficients are broadcast by GPS satellites in the navigation message.
65
+
66
+ Attributes
67
+ ----------
68
+ alpha : ndarray
69
+ Amplitude coefficients (4 values) in seconds.
70
+ beta : ndarray
71
+ Period coefficients (4 values) in seconds.
72
+ """
73
+
74
+ alpha: NDArray[np.float64]
75
+ beta: NDArray[np.float64]
76
+
77
+
78
+ # Default Klobuchar coefficients (typical mid-latitude values)
79
+ DEFAULT_KLOBUCHAR = KlobucharCoefficients(
80
+ alpha=np.array([3.82e-8, 1.49e-8, -5.96e-8, -5.96e-8]),
81
+ beta=np.array([1.43e5, 0.0, -3.28e5, 1.13e5]),
82
+ )
83
+
84
+
85
+ def klobuchar_delay(
86
+ latitude: ArrayLike,
87
+ longitude: ArrayLike,
88
+ elevation: ArrayLike,
89
+ azimuth: ArrayLike,
90
+ gps_time: ArrayLike,
91
+ coefficients: KlobucharCoefficients | None = None,
92
+ ) -> NDArray[np.float64]:
93
+ """
94
+ Compute ionospheric delay using the Klobuchar model.
95
+
96
+ The Klobuchar model is the standard GPS broadcast ionospheric
97
+ correction model. It provides single-frequency ionospheric
98
+ delay estimates accurate to about 50% RMS.
99
+
100
+ Parameters
101
+ ----------
102
+ latitude : array_like
103
+ User geodetic latitude in radians.
104
+ longitude : array_like
105
+ User geodetic longitude in radians.
106
+ elevation : array_like
107
+ Satellite elevation angle in radians.
108
+ azimuth : array_like
109
+ Satellite azimuth angle in radians.
110
+ gps_time : array_like
111
+ GPS time of week in seconds.
112
+ coefficients : KlobucharCoefficients, optional
113
+ Ionospheric coefficients from GPS navigation message.
114
+ If None, uses default mid-latitude values.
115
+
116
+ Returns
117
+ -------
118
+ delay : ndarray
119
+ Ionospheric delay in meters (at L1 frequency).
120
+
121
+ Examples
122
+ --------
123
+ >>> # User at 40°N, 105°W, satellite at 45° elevation
124
+ >>> delay = klobuchar_delay(
125
+ ... np.radians(40), np.radians(-105),
126
+ ... np.radians(45), np.radians(180),
127
+ ... gps_time=43200 # Noon
128
+ ... )
129
+ >>> delay > 0
130
+ True
131
+
132
+ Notes
133
+ -----
134
+ The Klobuchar model assumes a thin-shell ionosphere at 350 km altitude
135
+ and uses a cosine model for diurnal variation. It typically removes
136
+ about 50% of the ionospheric delay.
137
+
138
+ References
139
+ ----------
140
+ .. [1] IS-GPS-200, Interface Specification.
141
+ """
142
+ latitude = np.asarray(latitude, dtype=np.float64)
143
+ longitude = np.asarray(longitude, dtype=np.float64)
144
+ elevation = np.asarray(elevation, dtype=np.float64)
145
+ azimuth = np.asarray(azimuth, dtype=np.float64)
146
+ gps_time = np.asarray(gps_time, dtype=np.float64)
147
+
148
+ if coefficients is None:
149
+ coefficients = DEFAULT_KLOBUCHAR
150
+
151
+ alpha = coefficients.alpha
152
+ beta = coefficients.beta
153
+
154
+ # Semi-circles (GPS convention)
155
+ phi_u = latitude / np.pi # User latitude in semi-circles
156
+ lam_u = longitude / np.pi # User longitude in semi-circles
157
+
158
+ # Earth's central angle (semi-circles)
159
+ psi = 0.0137 / (elevation / np.pi + 0.11) - 0.022
160
+
161
+ # Ionospheric pierce point latitude (semi-circles)
162
+ phi_i = phi_u + psi * np.cos(azimuth)
163
+ phi_i = np.clip(phi_i, -0.416, 0.416)
164
+
165
+ # Ionospheric pierce point longitude (semi-circles)
166
+ lam_i = lam_u + psi * np.sin(azimuth) / np.cos(phi_i * np.pi)
167
+
168
+ # Geomagnetic latitude (semi-circles)
169
+ phi_m = phi_i + 0.064 * np.cos((lam_i - 1.617) * np.pi)
170
+
171
+ # Local time at ionospheric pierce point (seconds)
172
+ t = 43200 * lam_i + gps_time
173
+ t = np.mod(t, 86400)
174
+
175
+ # Obliquity factor
176
+ F = 1.0 + 16.0 * (0.53 - elevation / np.pi) ** 3
177
+
178
+ # Ionospheric delay computation
179
+ # Amplitude
180
+ AMP = alpha[0] + alpha[1] * phi_m + alpha[2] * phi_m**2 + alpha[3] * phi_m**3
181
+ AMP = np.maximum(AMP, 0)
182
+
183
+ # Period
184
+ PER = beta[0] + beta[1] * phi_m + beta[2] * phi_m**2 + beta[3] * phi_m**3
185
+ PER = np.maximum(PER, 72000)
186
+
187
+ # Phase
188
+ x = 2 * np.pi * (t - 50400) / PER
189
+
190
+ # Ionospheric time delay (seconds)
191
+ delay_sec = np.where(
192
+ np.abs(x) < 1.57,
193
+ F * (5e-9 + AMP * (1 - x**2 / 2 + x**4 / 24)),
194
+ F * 5e-9,
195
+ )
196
+
197
+ # Convert to meters
198
+ delay_m = delay_sec * SPEED_OF_LIGHT
199
+
200
+ return delay_m
201
+
202
+
203
+ def dual_frequency_tec(
204
+ pseudorange_l1: ArrayLike,
205
+ pseudorange_l2: ArrayLike,
206
+ ) -> NDArray[np.float64]:
207
+ """
208
+ Compute Total Electron Content from dual-frequency pseudoranges.
209
+
210
+ This method uses the dispersive nature of the ionosphere to
211
+ estimate TEC from the difference in L1 and L2 pseudoranges.
212
+
213
+ Parameters
214
+ ----------
215
+ pseudorange_l1 : array_like
216
+ L1 pseudorange in meters.
217
+ pseudorange_l2 : array_like
218
+ L2 pseudorange in meters.
219
+
220
+ Returns
221
+ -------
222
+ tec : ndarray
223
+ Total Electron Content in TECU (10^16 electrons/m²).
224
+
225
+ Notes
226
+ -----
227
+ The ionospheric delay is proportional to TEC and inversely
228
+ proportional to frequency squared:
229
+ delay = 40.3 * TEC / f²
230
+
231
+ The difference in delays at L1 and L2 gives:
232
+ P2 - P1 = 40.3 * TEC * (1/f1² - 1/f2²)
233
+
234
+ This is the standard dual-frequency ionospheric correction method.
235
+ """
236
+ pseudorange_l1 = np.asarray(pseudorange_l1, dtype=np.float64)
237
+ pseudorange_l2 = np.asarray(pseudorange_l2, dtype=np.float64)
238
+
239
+ # Ionospheric coefficient
240
+ K = 40.3 # m³/s²
241
+
242
+ # Frequency squared terms
243
+ f1_sq = F_L1**2
244
+ f2_sq = F_L2**2
245
+
246
+ # TEC from pseudorange difference
247
+ # P2 - P1 = K * TEC * (1/f2² - 1/f1²) / 10^16
248
+ # Note: negative because f1 > f2
249
+ delta_inv_f_sq = 1 / f2_sq - 1 / f1_sq
250
+ tec = (pseudorange_l2 - pseudorange_l1) / (K * delta_inv_f_sq) / 1e16
251
+
252
+ return tec
253
+
254
+
255
+ def ionospheric_delay_from_tec(
256
+ tec: ArrayLike,
257
+ frequency: float = F_L1,
258
+ ) -> NDArray[np.float64]:
259
+ """
260
+ Compute ionospheric delay from Total Electron Content.
261
+
262
+ Parameters
263
+ ----------
264
+ tec : array_like
265
+ Total Electron Content in TECU (10^16 electrons/m²).
266
+ frequency : float, optional
267
+ Signal frequency in Hz. Default is GPS L1.
268
+
269
+ Returns
270
+ -------
271
+ delay : ndarray
272
+ Ionospheric delay in meters.
273
+
274
+ Notes
275
+ -----
276
+ The ionospheric delay for a signal is:
277
+ delay = 40.3 * TEC * 10^16 / f²
278
+ """
279
+ tec = np.asarray(tec, dtype=np.float64)
280
+
281
+ K = 40.3 # m³/s²
282
+ delay = K * tec * 1e16 / frequency**2
283
+
284
+ return delay
285
+
286
+
287
+ def simple_iri(
288
+ latitude: ArrayLike,
289
+ longitude: ArrayLike,
290
+ altitude: ArrayLike,
291
+ hour: ArrayLike,
292
+ month: int = 6,
293
+ solar_flux: float = 150.0,
294
+ ) -> IonosphereState:
295
+ """
296
+ Simplified International Reference Ionosphere (IRI) model.
297
+
298
+ This provides approximate electron density and TEC values based on
299
+ simplified IRI physics. For accurate predictions, use the full IRI
300
+ model or external services.
301
+
302
+ Parameters
303
+ ----------
304
+ latitude : array_like
305
+ Geodetic latitude in radians.
306
+ longitude : array_like
307
+ Geodetic longitude in radians.
308
+ altitude : array_like
309
+ Altitude in meters.
310
+ hour : array_like
311
+ Local hour (0-24).
312
+ month : int, optional
313
+ Month of year (1-12). Default is 6 (June).
314
+ solar_flux : float, optional
315
+ F10.7 solar flux in SFU. Default is 150 (moderate activity).
316
+
317
+ Returns
318
+ -------
319
+ state : IonosphereState
320
+ Ionospheric state with TEC, delays, and F2 layer parameters.
321
+
322
+ Notes
323
+ -----
324
+ This is a simplified empirical model suitable for educational purposes
325
+ and rough estimates. For operational use, the full IRI-2020 model
326
+ should be employed.
327
+
328
+ Examples
329
+ --------
330
+ >>> state = simple_iri(np.radians(40), np.radians(-105), 300e3, 12)
331
+ >>> state.tec > 0
332
+ True
333
+ """
334
+ latitude = np.asarray(latitude, dtype=np.float64)
335
+ longitude = np.asarray(longitude, dtype=np.float64)
336
+ altitude = np.asarray(altitude, dtype=np.float64)
337
+ hour = np.asarray(hour, dtype=np.float64)
338
+
339
+ # Convert latitude to degrees for calculations
340
+ lat_deg = np.degrees(latitude)
341
+ # lon_deg not used in simplified model but kept for future expansion
342
+
343
+ # Simplified F2 layer critical frequency (foF2) model
344
+ # Based on typical diurnal and latitudinal variations
345
+ lat_factor = np.cos(latitude) ** 0.8
346
+ hour_angle = 2 * np.pi * (hour - 14) / 24 # Peak around 14:00 local
347
+ diurnal = 0.5 * (1 + np.cos(hour_angle))
348
+
349
+ # Solar activity factor
350
+ solar_factor = 0.5 + 0.5 * (solar_flux - 70) / 180
351
+ solar_factor = np.clip(solar_factor, 0.3, 1.2)
352
+
353
+ # Seasonal factor (simplified)
354
+ season_angle = 2 * np.pi * (month - 1) / 12
355
+ season_factor = 1.0 + 0.2 * np.cos(season_angle - np.pi * np.sign(lat_deg))
356
+
357
+ # F2 layer critical frequency (MHz)
358
+ f_peak = 5.0 + 8.0 * lat_factor * diurnal * solar_factor * season_factor
359
+ f_peak = np.maximum(f_peak, 2.0)
360
+
361
+ # F2 layer peak height (km)
362
+ h_peak = 250 + 100 * (1 - lat_factor) + 50 * (1 - diurnal)
363
+
364
+ # Simplified TEC calculation (TECU)
365
+ # TEC roughly scales with foF2 squared
366
+ base_tec = 0.5 * f_peak**2
367
+ tec = base_tec * solar_factor * season_factor
368
+
369
+ # Ionospheric delays
370
+ delay_l1 = ionospheric_delay_from_tec(tec, F_L1)
371
+ delay_l2 = ionospheric_delay_from_tec(tec, F_L2)
372
+
373
+ # Handle scalar vs array output
374
+ if np.ndim(latitude) == 0:
375
+ return IonosphereState(
376
+ tec=float(tec),
377
+ delay_l1=float(delay_l1),
378
+ delay_l2=float(delay_l2),
379
+ f_peak=float(f_peak),
380
+ h_peak=float(h_peak),
381
+ )
382
+
383
+ return IonosphereState(
384
+ tec=tec,
385
+ delay_l1=delay_l1,
386
+ delay_l2=delay_l2,
387
+ f_peak=f_peak,
388
+ h_peak=h_peak,
389
+ )
390
+
391
+
392
+ def magnetic_latitude(
393
+ latitude: ArrayLike,
394
+ longitude: ArrayLike,
395
+ ) -> NDArray[np.float64]:
396
+ """
397
+ Compute approximate geomagnetic latitude.
398
+
399
+ Uses a simple dipole approximation with the magnetic pole at
400
+ approximately 80.5°N, 72.8°W.
401
+
402
+ Parameters
403
+ ----------
404
+ latitude : array_like
405
+ Geodetic latitude in radians.
406
+ longitude : array_like
407
+ Geodetic longitude in radians.
408
+
409
+ Returns
410
+ -------
411
+ mag_lat : ndarray
412
+ Geomagnetic latitude in radians.
413
+ """
414
+ latitude = np.asarray(latitude, dtype=np.float64)
415
+ longitude = np.asarray(longitude, dtype=np.float64)
416
+
417
+ # Approximate magnetic pole location (2020 epoch)
418
+ pole_lat = np.radians(80.5)
419
+ pole_lon = np.radians(-72.8)
420
+
421
+ # Spherical law of cosines for angular distance
422
+ cos_mag_lat = np.sin(latitude) * np.sin(pole_lat) + np.cos(latitude) * np.cos(
423
+ pole_lat
424
+ ) * np.cos(longitude - pole_lon)
425
+
426
+ # Geomagnetic colatitude
427
+ mag_colat = np.arccos(np.clip(cos_mag_lat, -1, 1))
428
+
429
+ # Geomagnetic latitude
430
+ mag_lat = np.pi / 2 - mag_colat
431
+
432
+ return mag_lat
433
+
434
+
435
+ def scintillation_index(
436
+ magnetic_latitude: ArrayLike,
437
+ hour: ArrayLike,
438
+ kp_index: float = 3.0,
439
+ ) -> NDArray[np.float64]:
440
+ """
441
+ Estimate ionospheric scintillation index S4.
442
+
443
+ Provides a rough estimate of amplitude scintillation based on
444
+ geomagnetic latitude, local time, and geomagnetic activity.
445
+
446
+ Parameters
447
+ ----------
448
+ magnetic_latitude : array_like
449
+ Geomagnetic latitude in radians.
450
+ hour : array_like
451
+ Local hour (0-24).
452
+ kp_index : float, optional
453
+ Kp geomagnetic activity index (0-9). Default is 3 (moderate).
454
+
455
+ Returns
456
+ -------
457
+ s4 : ndarray
458
+ S4 amplitude scintillation index (0-1).
459
+
460
+ Notes
461
+ -----
462
+ S4 > 0.3 indicates moderate scintillation.
463
+ S4 > 0.6 indicates strong scintillation that may affect receivers.
464
+ """
465
+ magnetic_latitude = np.asarray(magnetic_latitude, dtype=np.float64)
466
+ hour = np.asarray(hour, dtype=np.float64)
467
+
468
+ # Scintillation is most intense:
469
+ # - At equatorial latitudes (within ±20° of magnetic equator)
470
+ # - At high latitudes (auroral zone, |lat| > 60°)
471
+ # - Post-sunset to midnight (local time 19-24)
472
+ # - During high geomagnetic activity
473
+
474
+ mag_lat_deg = np.abs(np.degrees(magnetic_latitude))
475
+
476
+ # Equatorial contribution
477
+ equatorial = np.exp(-((mag_lat_deg - 15) ** 2) / 200)
478
+
479
+ # Auroral contribution
480
+ auroral = np.exp(-((mag_lat_deg - 70) ** 2) / 100)
481
+
482
+ # Combined latitude factor
483
+ lat_factor = np.maximum(equatorial, 0.3 * auroral)
484
+
485
+ # Local time factor (peak at ~20:00 local)
486
+ hour_angle = 2 * np.pi * (hour - 20) / 24
487
+ time_factor = 0.5 * (1 + np.cos(hour_angle))
488
+
489
+ # Geomagnetic activity factor
490
+ kp_factor = 0.3 + 0.7 * (kp_index / 9)
491
+
492
+ # S4 estimate
493
+ s4 = 0.8 * lat_factor * time_factor * kp_factor
494
+
495
+ return np.clip(s4, 0, 1)
496
+
497
+
498
+ __all__ = [
499
+ "IonosphereState",
500
+ "KlobucharCoefficients",
501
+ "DEFAULT_KLOBUCHAR",
502
+ "klobuchar_delay",
503
+ "dual_frequency_tec",
504
+ "ionospheric_delay_from_tec",
505
+ "simple_iri",
506
+ "magnetic_latitude",
507
+ "scintillation_index",
508
+ # Constants
509
+ "SPEED_OF_LIGHT",
510
+ "F_L1",
511
+ "F_L2",
512
+ ]