nrl-tracker 0.21.4__py3-none-any.whl → 1.7.5__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.
Files changed (95) hide show
  1. {nrl_tracker-0.21.4.dist-info → nrl_tracker-1.7.5.dist-info}/METADATA +57 -10
  2. nrl_tracker-1.7.5.dist-info/RECORD +165 -0
  3. pytcl/__init__.py +4 -3
  4. pytcl/assignment_algorithms/__init__.py +28 -0
  5. pytcl/assignment_algorithms/data_association.py +2 -7
  6. pytcl/assignment_algorithms/gating.py +10 -10
  7. pytcl/assignment_algorithms/jpda.py +40 -40
  8. pytcl/assignment_algorithms/nd_assignment.py +379 -0
  9. pytcl/assignment_algorithms/network_flow.py +371 -0
  10. pytcl/assignment_algorithms/three_dimensional/assignment.py +3 -3
  11. pytcl/astronomical/__init__.py +162 -8
  12. pytcl/astronomical/ephemerides.py +533 -0
  13. pytcl/astronomical/reference_frames.py +865 -56
  14. pytcl/astronomical/relativity.py +473 -0
  15. pytcl/astronomical/sgp4.py +710 -0
  16. pytcl/astronomical/special_orbits.py +532 -0
  17. pytcl/astronomical/tle.py +558 -0
  18. pytcl/atmosphere/__init__.py +45 -3
  19. pytcl/atmosphere/ionosphere.py +512 -0
  20. pytcl/atmosphere/nrlmsise00.py +809 -0
  21. pytcl/clustering/dbscan.py +2 -2
  22. pytcl/clustering/gaussian_mixture.py +3 -3
  23. pytcl/clustering/hierarchical.py +15 -15
  24. pytcl/clustering/kmeans.py +4 -4
  25. pytcl/containers/__init__.py +28 -21
  26. pytcl/containers/base.py +219 -0
  27. pytcl/containers/cluster_set.py +2 -1
  28. pytcl/containers/covertree.py +26 -29
  29. pytcl/containers/kd_tree.py +94 -29
  30. pytcl/containers/measurement_set.py +1 -9
  31. pytcl/containers/rtree.py +200 -1
  32. pytcl/containers/vptree.py +21 -28
  33. pytcl/coordinate_systems/conversions/geodetic.py +272 -5
  34. pytcl/coordinate_systems/jacobians/jacobians.py +2 -2
  35. pytcl/coordinate_systems/projections/__init__.py +4 -2
  36. pytcl/coordinate_systems/projections/projections.py +2 -2
  37. pytcl/coordinate_systems/rotations/rotations.py +10 -6
  38. pytcl/core/__init__.py +18 -0
  39. pytcl/core/validation.py +333 -2
  40. pytcl/dynamic_estimation/__init__.py +26 -0
  41. pytcl/dynamic_estimation/gaussian_sum_filter.py +434 -0
  42. pytcl/dynamic_estimation/imm.py +15 -18
  43. pytcl/dynamic_estimation/kalman/__init__.py +30 -0
  44. pytcl/dynamic_estimation/kalman/constrained.py +382 -0
  45. pytcl/dynamic_estimation/kalman/extended.py +9 -12
  46. pytcl/dynamic_estimation/kalman/h_infinity.py +613 -0
  47. pytcl/dynamic_estimation/kalman/square_root.py +60 -573
  48. pytcl/dynamic_estimation/kalman/sr_ukf.py +302 -0
  49. pytcl/dynamic_estimation/kalman/ud_filter.py +410 -0
  50. pytcl/dynamic_estimation/kalman/unscented.py +9 -10
  51. pytcl/dynamic_estimation/particle_filters/bootstrap.py +15 -15
  52. pytcl/dynamic_estimation/rbpf.py +589 -0
  53. pytcl/dynamic_estimation/smoothers.py +1 -5
  54. pytcl/dynamic_models/discrete_time/__init__.py +1 -5
  55. pytcl/dynamic_models/process_noise/__init__.py +1 -5
  56. pytcl/gravity/egm.py +13 -0
  57. pytcl/gravity/spherical_harmonics.py +98 -37
  58. pytcl/gravity/tides.py +6 -6
  59. pytcl/logging_config.py +328 -0
  60. pytcl/magnetism/__init__.py +10 -14
  61. pytcl/magnetism/emm.py +10 -3
  62. pytcl/magnetism/wmm.py +260 -23
  63. pytcl/mathematical_functions/combinatorics/combinatorics.py +5 -5
  64. pytcl/mathematical_functions/geometry/geometry.py +5 -5
  65. pytcl/mathematical_functions/interpolation/__init__.py +2 -2
  66. pytcl/mathematical_functions/numerical_integration/quadrature.py +6 -6
  67. pytcl/mathematical_functions/signal_processing/detection.py +24 -24
  68. pytcl/mathematical_functions/signal_processing/filters.py +14 -14
  69. pytcl/mathematical_functions/signal_processing/matched_filter.py +12 -12
  70. pytcl/mathematical_functions/special_functions/__init__.py +2 -2
  71. pytcl/mathematical_functions/special_functions/bessel.py +15 -3
  72. pytcl/mathematical_functions/special_functions/debye.py +136 -26
  73. pytcl/mathematical_functions/special_functions/error_functions.py +3 -1
  74. pytcl/mathematical_functions/special_functions/gamma_functions.py +4 -4
  75. pytcl/mathematical_functions/special_functions/hypergeometric.py +81 -15
  76. pytcl/mathematical_functions/transforms/fourier.py +8 -8
  77. pytcl/mathematical_functions/transforms/stft.py +12 -12
  78. pytcl/mathematical_functions/transforms/wavelets.py +9 -9
  79. pytcl/navigation/__init__.py +14 -10
  80. pytcl/navigation/geodesy.py +246 -160
  81. pytcl/navigation/great_circle.py +101 -19
  82. pytcl/navigation/ins.py +1 -5
  83. pytcl/plotting/coordinates.py +7 -7
  84. pytcl/plotting/tracks.py +2 -2
  85. pytcl/static_estimation/maximum_likelihood.py +16 -14
  86. pytcl/static_estimation/robust.py +5 -5
  87. pytcl/terrain/loaders.py +5 -5
  88. pytcl/trackers/__init__.py +3 -14
  89. pytcl/trackers/hypothesis.py +1 -1
  90. pytcl/trackers/mht.py +9 -9
  91. pytcl/trackers/multi_target.py +2 -5
  92. nrl_tracker-0.21.4.dist-info/RECORD +0 -148
  93. {nrl_tracker-0.21.4.dist-info → nrl_tracker-1.7.5.dist-info}/LICENSE +0 -0
  94. {nrl_tracker-0.21.4.dist-info → nrl_tracker-1.7.5.dist-info}/WHEEL +0 -0
  95. {nrl_tracker-0.21.4.dist-info → nrl_tracker-1.7.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,532 @@
1
+ """
2
+ Special orbit cases: parabolic and advanced hyperbolic orbits.
3
+
4
+ This module extends orbital_mechanics.py with handling for edge cases:
5
+ - Parabolic orbits (e = 1, unbounded trajectory with zero energy)
6
+ - Advanced hyperbolic orbit calculations (escape trajectories)
7
+ - Unified orbit type detection and handling
8
+
9
+ References
10
+ ----------
11
+ .. [1] Vallado, D. A., "Fundamentals of Astrodynamics and Applications,"
12
+ 4th ed., Microcosm Press, 2013.
13
+ .. [2] Curtis, H. D., "Orbital Mechanics for Engineering Students,"
14
+ 3rd ed., Butterworth-Heinemann, 2014.
15
+ .. [3] Battin, R. H., "An Introduction to the Mathematics and Methods
16
+ of Astrodynamics," 2nd ed., AIAA, 1999.
17
+ """
18
+
19
+ from enum import Enum
20
+ from typing import NamedTuple
21
+
22
+ import numpy as np
23
+ from numpy.typing import NDArray
24
+
25
+
26
+ class OrbitType(Enum):
27
+ """Classification of orbit types based on eccentricity."""
28
+
29
+ CIRCULAR = 0 # e = 0
30
+ ELLIPTICAL = 1 # 0 < e < 1
31
+ PARABOLIC = 2 # e = 1 (boundary case)
32
+ HYPERBOLIC = 3 # e > 1
33
+
34
+
35
+ class ParabolicElements(NamedTuple):
36
+ """Parabolic (escape) orbit elements.
37
+
38
+ For a parabolic orbit (e=1), the semi-major axis is infinite.
39
+ Instead, we use the periapsis distance and orientation parameters.
40
+
41
+ Attributes
42
+ ----------
43
+ rp : float
44
+ Periapsis distance (km). Also called pericenter or closest approach.
45
+ i : float
46
+ Inclination (radians), 0 to pi.
47
+ raan : float
48
+ Right ascension of ascending node (radians), 0 to 2*pi.
49
+ omega : float
50
+ Argument of periapsis (radians), 0 to 2*pi.
51
+ nu : float
52
+ True anomaly (radians), typically in [-pi, pi] for parabolic orbits.
53
+ """
54
+
55
+ rp: float
56
+ i: float
57
+ raan: float
58
+ omega: float
59
+ nu: float
60
+
61
+
62
+ def classify_orbit(e: float, tol: float = 1e-9) -> OrbitType:
63
+ """
64
+ Classify orbit type based on eccentricity.
65
+
66
+ Parameters
67
+ ----------
68
+ e : float
69
+ Eccentricity value.
70
+ tol : float, optional
71
+ Tolerance for parabolic classification (default 1e-9).
72
+ Orbits with abs(e - 1) < tol are classified as parabolic.
73
+
74
+ Returns
75
+ -------
76
+ OrbitType
77
+ Classified orbit type.
78
+
79
+ Raises
80
+ ------
81
+ ValueError
82
+ If eccentricity is negative or NaN.
83
+ """
84
+ if np.isnan(e) or e < 0:
85
+ raise ValueError(f"Eccentricity must be non-negative, got {e}")
86
+
87
+ if e < tol:
88
+ return OrbitType.CIRCULAR
89
+ elif abs(e - 1.0) < tol:
90
+ return OrbitType.PARABOLIC
91
+ elif e < 1 - tol:
92
+ return OrbitType.ELLIPTICAL
93
+ else:
94
+ return OrbitType.HYPERBOLIC
95
+
96
+
97
+ def mean_to_parabolic_anomaly(
98
+ M: float,
99
+ tol: float = 1e-12,
100
+ max_iter: int = 100,
101
+ ) -> float:
102
+ """
103
+ Solve parabolic Kepler's equation: M = D + (1/3)*D^3.
104
+
105
+ For parabolic orbits (e=1), the "anomaly" is the parabolic anomaly D,
106
+ related to true anomaly by: D = tan(nu/2).
107
+
108
+ The equation relates mean anomaly to parabolic anomaly:
109
+ M = D + (1/3)*D^3
110
+
111
+ This is solved numerically using Newton-Raphson iteration.
112
+
113
+ Parameters
114
+ ----------
115
+ M : float
116
+ Mean anomaly (radians).
117
+ tol : float, optional
118
+ Convergence tolerance (default 1e-12).
119
+ max_iter : int, optional
120
+ Maximum iterations (default 100).
121
+
122
+ Returns
123
+ -------
124
+ D : float
125
+ Parabolic anomaly (the parameter D such that tan(nu/2) = D).
126
+
127
+ Notes
128
+ -----
129
+ For parabolic orbits, mean anomaly relates to time as:
130
+ M = sqrt(mu/rp^3) * t where rp is periapsis distance and mu is GM.
131
+
132
+ The solution D satisfies: D + (1/3)*D^3 = M
133
+ """
134
+ # Newton-Raphson for parabolic anomaly
135
+ # f(D) = D + (1/3)*D^3 - M = 0
136
+ # f'(D) = 1 + D^2
137
+
138
+ D = M # Initial guess
139
+
140
+ for _ in range(max_iter):
141
+ f = D + (1.0 / 3.0) * D**3 - M
142
+ f_prime = 1.0 + D**2
143
+ delta = f / f_prime
144
+ D = D - delta
145
+
146
+ if abs(delta) < tol:
147
+ return D
148
+
149
+ raise ValueError(
150
+ f"Parabolic Kepler's equation did not converge after {max_iter} iterations"
151
+ )
152
+
153
+
154
+ def parabolic_anomaly_to_true_anomaly(D: float) -> float:
155
+ """
156
+ Convert parabolic anomaly to true anomaly.
157
+
158
+ For parabolic orbits, the parabolic anomaly D relates to true anomaly by:
159
+ tan(nu/2) = D
160
+
161
+ Parameters
162
+ ----------
163
+ D : float
164
+ Parabolic anomaly (the parameter such that tan(nu/2) = D).
165
+
166
+ Returns
167
+ -------
168
+ nu : float
169
+ True anomaly (radians), in [-pi, pi].
170
+ """
171
+ return 2.0 * np.arctan(D)
172
+
173
+
174
+ def true_anomaly_to_parabolic_anomaly(nu: float) -> float:
175
+ """
176
+ Convert true anomaly to parabolic anomaly.
177
+
178
+ Parameters
179
+ ----------
180
+ nu : float
181
+ True anomaly (radians).
182
+
183
+ Returns
184
+ -------
185
+ D : float
186
+ Parabolic anomaly.
187
+ """
188
+ return np.tan(nu / 2.0)
189
+
190
+
191
+ def mean_to_true_anomaly_parabolic(M: float, tol: float = 1e-12) -> float:
192
+ """
193
+ Direct conversion from mean to true anomaly for parabolic orbits.
194
+
195
+ Parameters
196
+ ----------
197
+ M : float
198
+ Mean anomaly (radians).
199
+ tol : float, optional
200
+ Convergence tolerance (default 1e-12).
201
+
202
+ Returns
203
+ -------
204
+ nu : float
205
+ True anomaly (radians).
206
+ """
207
+ D = mean_to_parabolic_anomaly(M, tol=tol)
208
+ return parabolic_anomaly_to_true_anomaly(D)
209
+
210
+
211
+ def radius_parabolic(rp: float, nu: float) -> float:
212
+ """
213
+ Compute radius for parabolic orbit.
214
+
215
+ For a parabolic orbit with periapsis distance rp and true anomaly nu:
216
+ r = 2*rp / (1 + cos(nu))
217
+
218
+ This formula is consistent with the general conic section equation
219
+ with e=1: r = p/(1 + e*cos(nu)) where p = 2*rp (semi-latus rectum).
220
+
221
+ Parameters
222
+ ----------
223
+ rp : float
224
+ Periapsis distance (km).
225
+ nu : float
226
+ True anomaly (radians).
227
+
228
+ Returns
229
+ -------
230
+ r : float
231
+ Orbital radius (km).
232
+
233
+ Raises
234
+ ------
235
+ ValueError
236
+ If radius would be negative (nu near +pi for parabolic orbit).
237
+ """
238
+ denom = 1.0 + np.cos(nu)
239
+
240
+ if denom <= 0:
241
+ raise ValueError(
242
+ f"Parabolic orbit undefined at true anomaly nu={np.degrees(nu):.2f}°"
243
+ )
244
+
245
+ r = 2.0 * rp / denom
246
+
247
+ if r < 0:
248
+ raise ValueError(f"Computed radius is negative: r={r}")
249
+
250
+ return r
251
+
252
+
253
+ def velocity_parabolic(mu: float, rp: float, nu: float) -> float:
254
+ """
255
+ Compute velocity magnitude for parabolic orbit.
256
+
257
+ For a parabolic orbit (e=1), the specific orbital energy is zero,
258
+ and the velocity relates to radius by:
259
+ v = sqrt(2*mu/r)
260
+
261
+ Parameters
262
+ ----------
263
+ mu : float
264
+ Standard gravitational parameter (km^3/s^2).
265
+ rp : float
266
+ Periapsis distance (km).
267
+ nu : float
268
+ True anomaly (radians).
269
+
270
+ Returns
271
+ -------
272
+ v : float
273
+ Velocity magnitude (km/s).
274
+ """
275
+ r = radius_parabolic(rp, nu)
276
+ return np.sqrt(2.0 * mu / r)
277
+
278
+
279
+ def hyperbolic_anomaly_to_true_anomaly(H: float, e: float) -> float:
280
+ """
281
+ Convert hyperbolic anomaly to true anomaly.
282
+
283
+ For hyperbolic orbits (e > 1), hyperbolic anomaly H relates to true anomaly by:
284
+ tan(nu/2) = sqrt((e+1)/(e-1)) * tanh(H/2)
285
+
286
+ Parameters
287
+ ----------
288
+ H : float
289
+ Hyperbolic anomaly (radians).
290
+ e : float
291
+ Eccentricity (e > 1 for hyperbolic).
292
+
293
+ Returns
294
+ -------
295
+ nu : float
296
+ True anomaly (radians).
297
+
298
+ Raises
299
+ ------
300
+ ValueError
301
+ If eccentricity is not hyperbolic (e <= 1).
302
+ """
303
+ if e <= 1:
304
+ raise ValueError(f"Eccentricity must be > 1 for hyperbolic orbits, got {e}")
305
+
306
+ nu = 2.0 * np.arctan(np.sqrt((e + 1.0) / (e - 1.0)) * np.tanh(H / 2.0))
307
+
308
+ return nu
309
+
310
+
311
+ def true_anomaly_to_hyperbolic_anomaly(nu: float, e: float) -> float:
312
+ """
313
+ Convert true anomaly to hyperbolic anomaly.
314
+
315
+ Parameters
316
+ ----------
317
+ nu : float
318
+ True anomaly (radians).
319
+ e : float
320
+ Eccentricity (e > 1 for hyperbolic).
321
+
322
+ Returns
323
+ -------
324
+ H : float
325
+ Hyperbolic anomaly (radians).
326
+
327
+ Raises
328
+ ------
329
+ ValueError
330
+ If eccentricity is not hyperbolic.
331
+ """
332
+ if e <= 1:
333
+ raise ValueError(f"Eccentricity must be > 1 for hyperbolic orbits, got {e}")
334
+
335
+ H = 2.0 * np.arctanh(np.sqrt((e - 1.0) / (e + 1.0)) * np.tan(nu / 2.0))
336
+
337
+ return H
338
+
339
+
340
+ def escape_velocity_at_radius(mu: float, r: float) -> float:
341
+ """
342
+ Compute escape velocity at a given radius.
343
+
344
+ Escape velocity is the minimum velocity needed to reach infinity
345
+ with zero velocity, corresponding to a parabolic orbit:
346
+ v_esc = sqrt(2*mu/r)
347
+
348
+ Parameters
349
+ ----------
350
+ mu : float
351
+ Standard gravitational parameter (km^3/s^2).
352
+ r : float
353
+ Orbital radius (km).
354
+
355
+ Returns
356
+ -------
357
+ v_esc : float
358
+ Escape velocity (km/s).
359
+ """
360
+ return np.sqrt(2.0 * mu / r)
361
+
362
+
363
+ def hyperbolic_excess_velocity(mu: float, a: float) -> float:
364
+ """
365
+ Compute hyperbolic excess velocity.
366
+
367
+ For a hyperbolic orbit with semi-major axis a (negative for hyperbolic),
368
+ the excess velocity at infinity is:
369
+ v_inf = sqrt(-mu/a)
370
+
371
+ Parameters
372
+ ----------
373
+ mu : float
374
+ Standard gravitational parameter (km^3/s^2).
375
+ a : float
376
+ Semi-major axis (km). Must be negative for hyperbolic orbits.
377
+
378
+ Returns
379
+ -------
380
+ v_inf : float
381
+ Hyperbolic excess velocity (km/s).
382
+
383
+ Raises
384
+ ------
385
+ ValueError
386
+ If semi-major axis is not negative.
387
+ """
388
+ if a >= 0:
389
+ raise ValueError(
390
+ f"Semi-major axis must be negative for hyperbolic orbits, got {a}"
391
+ )
392
+
393
+ v_inf = np.sqrt(-mu / a)
394
+ return v_inf
395
+
396
+
397
+ def hyperbolic_asymptote_angle(e: float) -> float:
398
+ """
399
+ Compute the asymptote angle for a hyperbolic orbit.
400
+
401
+ For a hyperbolic orbit with eccentricity e, the true anomaly
402
+ asymptotically approaches ±nu_inf where:
403
+ cos(nu_inf) = -1/e
404
+
405
+ The asymptote angle is nu_inf.
406
+
407
+ Parameters
408
+ ----------
409
+ e : float
410
+ Eccentricity (e > 1 for hyperbolic).
411
+
412
+ Returns
413
+ -------
414
+ nu_inf : float
415
+ Asymptote angle (radians), in (0, pi).
416
+
417
+ Raises
418
+ ------
419
+ ValueError
420
+ If eccentricity is not hyperbolic.
421
+ """
422
+ if e <= 1:
423
+ raise ValueError(f"Eccentricity must be > 1 for hyperbolic orbits, got {e}")
424
+
425
+ nu_inf = np.arccos(-1.0 / e)
426
+ return nu_inf
427
+
428
+
429
+ def hyperbolic_deflection_angle(e: float) -> float:
430
+ """
431
+ Compute the deflection angle for a hyperbolic orbit.
432
+
433
+ The deflection angle is the angle through which the velocity vector
434
+ is deflected from its asymptotic direction:
435
+ delta = pi - 2*nu_inf = pi - 2*arccos(-1/e)
436
+
437
+ Parameters
438
+ ----------
439
+ e : float
440
+ Eccentricity (e > 1 for hyperbolic).
441
+
442
+ Returns
443
+ -------
444
+ delta : float
445
+ Deflection angle (radians), in (0, pi).
446
+
447
+ Raises
448
+ ------
449
+ ValueError
450
+ If eccentricity is not hyperbolic.
451
+ """
452
+ if e <= 1:
453
+ raise ValueError(f"Eccentricity must be > 1 for hyperbolic orbits, got {e}")
454
+
455
+ nu_inf = hyperbolic_asymptote_angle(e)
456
+ delta = np.pi - 2.0 * nu_inf
457
+
458
+ return delta
459
+
460
+
461
+ def semi_major_axis_from_energy(mu: float, specific_energy: float) -> float:
462
+ """
463
+ Compute semi-major axis from specific orbital energy.
464
+
465
+ The specific orbital energy relates to semi-major axis by:
466
+ epsilon = -mu / (2*a)
467
+
468
+ Rearranging: a = -mu / (2*epsilon)
469
+
470
+ Parameters
471
+ ----------
472
+ mu : float
473
+ Standard gravitational parameter (km^3/s^2).
474
+ specific_energy : float
475
+ Specific orbital energy (km^2/s^2).
476
+
477
+ Returns
478
+ -------
479
+ a : float
480
+ Semi-major axis (km).
481
+ - a > 0 for elliptical orbits (epsilon < 0)
482
+ - a < 0 for hyperbolic orbits (epsilon > 0)
483
+ - a → ∞ for parabolic orbits (epsilon = 0)
484
+
485
+ Raises
486
+ ------
487
+ ValueError
488
+ If specific energy is exactly zero (parabolic case).
489
+ """
490
+ if abs(specific_energy) < 1e-15:
491
+ raise ValueError(
492
+ "Specific energy is zero (parabolic orbit); use alternative methods"
493
+ )
494
+
495
+ a = -mu / (2.0 * specific_energy)
496
+ return a
497
+
498
+
499
+ def eccentricity_vector(
500
+ r: NDArray[np.floating],
501
+ v: NDArray[np.floating],
502
+ mu: float,
503
+ ) -> NDArray[np.floating]:
504
+ """
505
+ Compute eccentricity vector from position and velocity.
506
+
507
+ The eccentricity vector e is defined as:
508
+ e = (v^2/mu - 1/r) * r - (r·v/mu) * v
509
+
510
+ This works for all orbit types: elliptical, parabolic, and hyperbolic.
511
+
512
+ Parameters
513
+ ----------
514
+ r : ndarray
515
+ Position vector (km), shape (3,).
516
+ v : ndarray
517
+ Velocity vector (km/s), shape (3,).
518
+ mu : float
519
+ Standard gravitational parameter (km^3/s^2).
520
+
521
+ Returns
522
+ -------
523
+ e : ndarray
524
+ Eccentricity vector, shape (3,).
525
+ """
526
+ r_mag = np.linalg.norm(r)
527
+ v_mag = np.linalg.norm(v)
528
+ rv_dot = np.dot(r, v)
529
+
530
+ e_vec = (v_mag**2 / mu - 1.0 / r_mag) * r - (rv_dot / mu) * v
531
+
532
+ return e_vec