Getting Started
This guide will help you get started with the Tracker Component Library.
Installation
Requirements
Python 3.10 or later
NumPy >= 1.20
SciPy >= 1.7
Install from PyPI
pip install nrl-tracker
Install from Source
git clone https://github.com/nedonatelli/TCL.git
cd TCL
pip install -e ".[dev]"
Optional Dependencies
Install optional features as needed:
# For astronomy features (ephemerides, celestial mechanics)
pip install nrl-tracker[astronomy]
# For geodesy features (coordinate transforms)
pip install nrl-tracker[geodesy]
# For terrain data (GEBCO, Earth2014 via NetCDF)
pip install nrl-tracker[terrain]
# For visualization (Plotly)
pip install nrl-tracker[visualization]
# For optimization (CVXPY)
pip install nrl-tracker[optimization]
# For signal processing (wavelets)
pip install nrl-tracker[signal]
# For GPU acceleration (NVIDIA CUDA)
pip install nrl-tracker[gpu]
# For GPU acceleration (Apple Silicon M1/M2/M3)
pip install nrl-tracker[gpu-apple]
Basic Concepts
State Representation
States are represented as 1D NumPy arrays. For kinematic tracking, common state vectors include:
Constant velocity (2D):
[x, vx, y, vy]Constant acceleration (2D):
[x, vx, ax, y, vy, ay]Singer model (2D):
[x, vx, ax, y, vy, ay](acceleration is correlated)
Covariance matrices are represented as 2D NumPy arrays of shape (n, n).
Motion Models
The library provides state transition matrices (F) and process noise covariance matrices (Q) for various motion models:
from pytcl.dynamic_models import (
f_constant_velocity,
f_constant_acceleration,
f_singer,
q_constant_velocity,
q_constant_acceleration,
q_singer,
)
# Constant velocity model
F_cv = f_constant_velocity(T=1.0, num_dims=2)
Q_cv = q_constant_velocity(T=1.0, sigma_a=1.0, num_dims=2)
# Singer maneuvering model
F_singer = f_singer(T=1.0, tau=10.0, num_dims=2)
Q_singer = q_singer(T=1.0, tau=10.0, sigma_m=1.0, num_dims=2)
Filters
The library provides several filtering algorithms:
Linear Kalman Filter - For linear dynamics and measurements:
from pytcl.dynamic_estimation import kf_predict, kf_update
pred = kf_predict(x, P, F, Q)
upd = kf_update(pred.x, pred.P, z, H, R)
Extended Kalman Filter - For nonlinear dynamics/measurements:
from pytcl.dynamic_estimation import ekf_predict, ekf_update
# F and H are the Jacobian matrices evaluated at the current state
F = F_jacobian(x) # Jacobian of f at x
H = H_jacobian(x) # Jacobian of h at x
pred = ekf_predict(x, P, f_func, F, Q)
upd = ekf_update(pred.x, pred.P, z, h_func, H, R)
Unscented Kalman Filter - For highly nonlinear systems:
from pytcl.dynamic_estimation import ukf_predict, ukf_update
pred = ukf_predict(x, P, f_func, Q)
upd = ukf_update(pred.x, pred.P, z, h_func, R)
Particle Filter - For non-Gaussian distributions:
from pytcl.dynamic_estimation import (
initialize_particles,
bootstrap_pf_step,
)
# Q_sample is a callable that samples process noise
def Q_sample(n_particles, rng=None):
if rng is None:
rng = np.random.default_rng()
return rng.multivariate_normal(np.zeros(n), Q_cov, size=n_particles)
state = initialize_particles(x0, P0, N=1000)
state = bootstrap_pf_step(state.particles, state.weights, z, f, h, Q_sample, R)
Constrained Extended Kalman Filter - For state constraints (e.g., bounded positions):
from pytcl.dynamic_estimation.kalman import (
constrained_ekf_predict,
constrained_ekf_update,
ConstraintFunction,
)
# Define constraint: 0 <= x[0] <= 100 (position within bounds)
def constraint_lower(x):
return -x[0] # g(x) <= 0 means x[0] >= 0
def constraint_upper(x):
return x[0] - 100.0 # g(x) <= 0 means x[0] <= 100
constraint_lower_obj = ConstraintFunction(constraint_lower)
constraint_upper_obj = ConstraintFunction(constraint_upper)
pred = constrained_ekf_predict(x, P, f_func, F_jacobian, Q, constraints=[...])
upd = constrained_ekf_update(pred.x, pred.P, z, h_func, H_jacobian, R, constraints=[...])
Rao-Blackwellized Particle Filter - Hybrid linear/nonlinear filtering:
from pytcl.dynamic_estimation import (
rbpf_predict,
rbpf_update,
RBPFFilter,
)
# For systems with nonlinear dynamics in 'y' and linear dynamics in 'x'
# Each particle maintains its own Kalman filter for the linear part
filter = RBPFFilter(
y0=np.array([...]), # Nonlinear state (particle positions)
x0_fn=lambda y: np.array([...]), # Linear state given y
P0=P_linear,
N_particles=500,
)
filter = rbpf_predict(filter, f_nonlinear, F_linear, Q_linear, Q_nonlinear)
filter = rbpf_update(filter, z, h_func, R)
Coordinate Systems
Convert between coordinate systems:
from pytcl.coordinate_systems import (
cart2sphere,
sphere2cart,
geodetic2ecef,
ecef2geodetic,
)
# Cartesian to spherical
r, az, el = cart2sphere(np.array([[100, 200, 50]]))
# Geodetic to ECEF
x, y, z = geodetic2ecef(lat=40.0, lon=-75.0, alt=100.0)
Atmospheric Models
Get atmospheric density for satellite drag calculations:
from pytcl.atmosphere import nrlmsise00
# NRLMSISE-00 model with solar/geomagnetic activity
density = nrlmsise00.get_density(
altitude_km=400.0,
latitude_deg=45.0,
longitude_deg=-75.0,
year=2024,
day_of_year=100,
hour_utc=12.0,
f107=150.0, # 10.7 cm solar flux (SFU)
f107a=130.0, # 81-day average
kp=3.0, # Geomagnetic activity index
)
print(f"Density: {density:.3e} kg/m³")
# Get atmospheric composition
composition = nrlmsise00.get_composition(
altitude_km=400.0,
latitude_deg=0.0,
longitude_deg=0.0,
year=2024,
day_of_year=100,
hour_utc=12.0,
f107=150.0,
f107a=130.0,
kp=3.0,
)