Source code for pytcl.dynamic_models.process_noise.singer

"""
Process noise covariance matrices for Singer acceleration model.

This module provides functions to construct process noise covariance matrices
(Q matrices) for the Singer acceleration motion model. The Singer model
treats target acceleration as a first-order Gauss-Markov process, making it
well-suited for tracking maneuvering targets with random accelerations.

The Singer model is characterized by:
- A maneuver time constant (tau) that controls how quickly acceleration
  correlations decay
- An RMS maneuver level (sigma_m) that sets the expected acceleration magnitude
- State vector [position, velocity, acceleration] per dimension

The model dynamics are:
    da/dt = -a/tau + w(t)

where w(t) is white noise with spectral density 2*sigma_m²/tau.

Available functions:
- ``q_singer``: Generic N-dimensional Singer process noise
- ``q_singer_2d``: 2D Singer model (6x6 state)
- ``q_singer_3d``: 3D Singer model (9x9 state)

These Q matrices are designed to work with the corresponding state transition
matrices in :mod:`pytcl.dynamic_models.singer`.

Examples
--------
Create process noise for tracking a maneuvering aircraft:

>>> from pytcl.dynamic_models.process_noise import q_singer
>>> Q = q_singer(T=1.0, tau=20.0, sigma_m=3.0)  # tau=20s, 3g maneuvers
>>> Q.shape
(3, 3)

For 3D tracking:

>>> Q = q_singer_3d(T=0.1, tau=10.0, sigma_m=2.0)
>>> Q.shape
(9, 9)

See Also
--------
pytcl.dynamic_models.singer : State transition matrices
pytcl.dynamic_models.process_noise.constant_acceleration : CA process noise

References
----------
.. [1] Singer, R.A., "Estimating Optimal Tracking Filter Performance for
       Manned Maneuvering Targets", IEEE Trans. Aerospace and Electronic
       Systems, Vol. AES-6, No. 4, July 1970, pp. 473-483.
.. [2] Bar-Shalom, Y., Li, X.R., and Kirubarajan, T., "Estimation with
       Applications to Tracking and Navigation", Wiley, 2001, Chapter 6.
"""

import numpy as np
from numpy.typing import NDArray


[docs] def q_singer( T: float, tau: float, sigma_m: float, num_dims: int = 1, ) -> NDArray[np.floating]: """ Create process noise covariance matrix for Singer acceleration model. Parameters ---------- T : float Time step in seconds. tau : float Maneuver time constant in seconds. sigma_m : float Standard deviation of target acceleration [m/s²]. This is the RMS maneuver level. num_dims : int, optional Number of spatial dimensions (default: 1). Returns ------- Q : ndarray Process noise covariance matrix of shape (3*num_dims, 3*num_dims). Examples -------- >>> Q = q_singer(T=1.0, tau=10.0, sigma_m=1.0) >>> Q.shape (3, 3) Notes ----- The Singer model assumes acceleration is a first-order Gauss-Markov process: da/dt = -a/tau + w(t) where w(t) is white noise with spectral density 2*sigma_m²/tau. The discrete-time process noise covariance is computed by integrating the continuous-time dynamics. See Also -------- f_singer : State transition matrix for Singer model. References ---------- .. [1] Singer, R.A., "Estimating Optimal Tracking Filter Performance for Manned Maneuvering Targets", IEEE Trans. AES, 1970. """ # Use the standard formulas from Bar-Shalom, Li, Kirubarajan # "Estimation with Applications to Tracking" alpha = np.exp(-T / tau) alpha2 = alpha * alpha tau2 = tau * tau tau3 = tau2 * tau tau4 = tau3 * tau tau5 = tau4 * tau T2 = T * T T3 = T2 * T q_c = 2 * sigma_m**2 / tau Q_1d = np.zeros((3, 3), dtype=np.float64) Q_1d[0, 0] = q_c * ( tau5 / 2 * (1 - alpha2 + 2 * T / tau * alpha2) - tau4 * T * (1 - alpha) ** 2 - tau3 * T2 * (1 - alpha) + tau2 * T3 / 3 ) Q_1d[0, 1] = q_c * ( tau4 / 2 * (alpha2 - 1 + 2 * T / tau - 2 * T / tau * alpha) + tau3 * T * (1 - alpha) - tau2 * T2 / 2 ) Q_1d[1, 0] = Q_1d[0, 1] Q_1d[0, 2] = q_c * tau3 / 2 * (1 - alpha) ** 2 Q_1d[2, 0] = Q_1d[0, 2] Q_1d[1, 1] = q_c * tau3 / 2 * (4 * alpha - alpha2 - 3 + 2 * T / tau) Q_1d[1, 2] = q_c * tau2 / 2 * (1 - 2 * alpha + alpha2) Q_1d[2, 1] = Q_1d[1, 2] Q_1d[2, 2] = q_c * tau / 2 * (1 - alpha2) if num_dims == 1: return Q_1d # Build block diagonal for multiple dimensions n = 3 Q = np.zeros((n * num_dims, n * num_dims), dtype=np.float64) for d in range(num_dims): start = d * n end = start + n Q[start:end, start:end] = Q_1d return Q
[docs] def q_singer_2d(T: float, tau: float, sigma_m: float) -> NDArray[np.floating]: """ Create process noise covariance for 2D Singer model. Parameters ---------- T : float Time step in seconds. tau : float Maneuver time constant in seconds. sigma_m : float Standard deviation of target acceleration [m/s²]. Returns ------- Q : ndarray Process noise covariance matrix of shape (6, 6). """ return q_singer(T=T, tau=tau, sigma_m=sigma_m, num_dims=2)
[docs] def q_singer_3d(T: float, tau: float, sigma_m: float) -> NDArray[np.floating]: """ Create process noise covariance for 3D Singer model. Parameters ---------- T : float Time step in seconds. tau : float Maneuver time constant in seconds. sigma_m : float Standard deviation of target acceleration [m/s²]. Returns ------- Q : ndarray Process noise covariance matrix of shape (9, 9). """ return q_singer(T=T, tau=tau, sigma_m=sigma_m, num_dims=3)
__all__ = [ "q_singer", "q_singer_2d", "q_singer_3d", ]