60#define DEFAULT_ALPHA 1e-3
62#define DEFAULT_BETA 2.0
64#define DEFAULT_KAPPA 1.0
66#define SRUKF_EPS 1e-12
241 if (!ukf || !ukf->
ws)
269 free(ws->downdate_work);
284 if (N == 0 || M == 0)
288 if (ukf->
ws && ukf->
ws->N == N && ukf->
ws->M == M)
337 if (!ws->Xsig || !ws->Ysig_N || !ws->x_pred || !ws->S_tmp || !ws->Ysig_M ||
338 !ws->y_mean || !ws->Pxz || !ws->K || !ws->innov || !ws->x_new ||
339 !ws->S_new || !ws->dx || !ws->tmp1 || !ws->Dev_N || !ws->Dev_M ||
340 !ws->qr_work_N || !ws->qr_work_M || !ws->Syy || !ws->tau_N ||
341 !ws->tau_M || !ws->downdate_work || !ws->dev0_N || !ws->dev0_M) {
365 if (ukf->
ws->N == N && ukf->
ws->M == M)
396 if (isnan(v) || isinf(v))
475 diag_report(
"compute_weights: denominator too small (n + lambda ≈ 0)");
481 ukf->
wm[i] = 1.0 / (2.0 * denom);
486 ukf->
wc[i] = ukf->
wm[i];
508 if (fabs((
double)(n + lambda)) <
SRUKF_EPS) {
569 if (!ukf || !ukf->
Rsqrt)
577 if (!ukf || !ukf->
x || !x_out)
589 if (!ukf || !ukf->
x || !x_in)
601 if (!ukf || !ukf->
S || !S_out)
613 if (!ukf || !ukf->
S || !S_in)
625 if (!ukf || !ukf->
x || !ukf->
S)
716 if (N <= 0 || M <= 0)
735 if (!Qsqrt || !Rsqrt)
747 if (srukf_init(ukf, (
int)Qsqrt->
n_rows, (
int)Rsqrt->
n_rows, Qsqrt, Rsqrt) !=
759 if (!ukf || !Qsqrt || !Rsqrt)
782 if (!new_Qsqrt || !new_Rsqrt) {
807 ukf->
Qsqrt = new_Qsqrt;
808 ukf->
Rsqrt = new_Rsqrt;
866 if (!x || !S || !Xsig)
879 diag_report(
"generate_sigma_points: gamma <= 0 (N + lambda <= 0)");
941 if (!Xsig || !func || !Ysig)
944 if (Ysig->
data == NULL)
960 func(&col_in, &col_out, user);
1016 if (!S || !v || !work)
1061 work[i] = c * wi - s * Sij;
1101 if (!Ysig || !mean || !wc || !Dev)
1171 if (!Dev || !Noise_sqrt || !S || !work || !tau)
1177 if (Noise_sqrt->
n_rows != dim || Noise_sqrt->
n_cols != dim)
1216 diag_report(
"QR factorization (SRUKF_GEQRF) failed");
1242 if (wc0_negative && dev0) {
1273 if (!Ysig || !wm || !mean)
1327 if (!Xsig || !Ysig || !x_mean || !y_mean || !weights || !Pxz)
1410 if (!ukf || !f || !x_in || !S_in || !x_out || !S_out)
1412 if (!ukf->
Qsqrt || !ukf->
wm || !ukf->
wc || !ukf->
ws)
1435 diag_report(
"predict: sigma point generation failed");
1441 diag_report(
"predict: sigma point propagation failed");
1447 diag_report(
"predict: callback f produced NaN or Inf");
1454 diag_report(
"predict: compute_weighted_mean failed");
1461 diag_report(
"predict: compute_weighted_deviations failed");
1467 bool wc0_negative = (ukf->
wc[0] < 0.0);
1478 ws->downdate_work, wc0_negative, dev0);
1480 diag_report(
"predict: sqrt_from_deviations (QR/downdate) failed");
1495 if (!ukf || !x || !S)
1518 if (!ukf || !ukf->
x || !ukf->
S)
1623 if (!ukf || !h || !z || !x_in || !S_in || !x_out || !S_out)
1625 if (!ukf->
Rsqrt || !ukf->
wm || !ukf->
wc || !ukf->
ws)
1664 diag_report(
"correct: callback h produced NaN or Inf");
1683 bool wc0_negative = (ukf->
wc[0] < 0.0);
1687 dev0_M_buf = ws->dev0_M;
1693 ws->tau_M, ws->downdate_work,
1694 wc0_negative, dev0_M_buf);
1699 bool Syy_zero =
true;
1777 diag_report(
"correct: Cholesky downdate failed, matrix not SPD");
1794 if (!ukf || !x || !S || !z)
1800 if (N == 0 || M == 0)
1820 if (!ukf || !ukf->
x || !ukf->
S)
static srukf_return srukf_predict_core(const srukf *ukf, const srukf_mat *x_in, const srukf_mat *S_in, void(*f)(const srukf_mat *, srukf_mat *, void *), void *user, srukf_mat *x_out, srukf_mat *S_out)
Core predict implementation.
static bool is_numeric_valid(const srukf_mat *M)
Check if matrix contains any NaN or Inf values.
static srukf_return srukf_sqrt_from_deviations_ex(const srukf_mat *Dev, const srukf_mat *Noise_sqrt, srukf_mat *S, srukf_mat *work, srukf_value *tau, srukf_value *downdate_work, bool wc0_negative, const srukf_value *dev0)
Compute sqrt-covariance from weighted deviations via QR.
static srukf_return alloc_vector(srukf_value **vec, srukf_index len)
Allocate a vector of doubles.
srukf_index srukf_state_dim(const srukf *ukf)
Get the state dimension N.
static srukf_return srukf_compute_weights(srukf *ukf, const srukf_index n)
Compute sigma point weights.
#define SRUKF_EPS
Numerical tolerance for near-zero checks.
srukf_index srukf_meas_dim(const srukf *ukf)
Get the measurement dimension M.
srukf_return srukf_set_noise(srukf *ukf, const srukf_mat *Qsqrt, const srukf_mat *Rsqrt)
Set the noise sqrt-covariance matrices.
static void diag_report(const char *msg)
Report a diagnostic message.
srukf * srukf_create(int N, int M)
Create a filter with uninitialized noise matrices.
static srukf_return srukf_correct_core(const srukf *ukf, const srukf_mat *x_in, const srukf_mat *S_in, srukf_mat *z, void(*h)(const srukf_mat *, srukf_mat *, void *), void *user, srukf_mat *x_out, srukf_mat *S_out)
Core correct implementation.
srukf_return srukf_predict_to(srukf *ukf, srukf_mat *x, srukf_mat *S, void(*f)(const srukf_mat *, srukf_mat *, void *), void *user)
Transactional predict: operate on external buffers.
static srukf_return generate_sigma_points_from(const srukf_mat *x, const srukf_mat *S, srukf_value lambda, srukf_mat *Xsig)
Generate sigma points from state and sqrt-covariance.
srukf * srukf_create_from_noise(const srukf_mat *Qsqrt, const srukf_mat *Rsqrt)
Create a filter from noise covariance matrices.
void srukf_free(srukf *ukf)
Free all memory allocated for the filter.
srukf_return srukf_correct_to(srukf *ukf, srukf_mat *x, srukf_mat *S, srukf_mat *z, void(*h)(const srukf_mat *, srukf_mat *, void *), void *user)
Transactional correct: operate on external buffers.
srukf_return srukf_predict(srukf *ukf, void(*f)(const srukf_mat *, srukf_mat *, void *), void *user)
Process model callback type.
srukf_return srukf_alloc_workspace(srukf *ukf)
Pre-allocate workspace.
srukf_return srukf_reset(srukf *ukf, srukf_value init_std)
Reset filter to initial conditions.
srukf_return srukf_set_sqrt_cov(srukf *ukf, const srukf_mat *S_in)
Set the sqrt-covariance matrix.
static void srukf_mat_column_view(srukf_mat *V, const srukf_mat *M, srukf_index k)
Create a column view into a matrix (zero-copy)
static srukf_return compute_weighted_deviations(const srukf_mat *Ysig, const srukf_mat *mean, const srukf_value *wc, srukf_mat *Dev)
Compute weighted deviations from sigma points.
static srukf_diag_fn g_diag_callback
static srukf_return chol_downdate_rank1(srukf_mat *S, const srukf_value *v, srukf_value *work)
Cholesky rank-1 downdate.
srukf_return srukf_get_sqrt_cov(const srukf *ukf, srukf_mat *S_out)
Get the sqrt-covariance matrix.
srukf_return srukf_get_state(const srukf *ukf, srukf_mat *x_out)
Get the current state estimate.
#define DEFAULT_ALPHA
Default sigma point spread parameter.
void srukf_set_diag_callback(srukf_diag_fn fn)
Set the global diagnostic callback.
void srukf_mat_free(srukf_mat *mat)
Free a matrix and its data.
srukf_return srukf_set_state(srukf *ukf, const srukf_mat *x_in)
Set the state estimate.
static srukf_return ensure_workspace(srukf *ukf)
Ensure workspace is allocated (lazy allocation)
void srukf_free_workspace(srukf *ukf)
Free workspace to reclaim memory.
static srukf_return compute_weighted_mean(const srukf_mat *Ysig, const srukf_value *wm, srukf_mat *mean)
Compute weighted mean from sigma points.
static srukf_return compute_cross_covariance(const srukf_mat *Xsig, const srukf_mat *Ysig, const srukf_mat *x_mean, const srukf_mat *y_mean, const srukf_value *weights, srukf_mat *Pxz)
Compute cross-covariance between state and measurement.
srukf_mat * srukf_mat_alloc(srukf_index rows, srukf_index cols, int alloc_data)
Allocate a matrix.
#define DEFAULT_KAPPA
Default secondary scaling parameter.
srukf_return srukf_set_scale(srukf *ukf, srukf_value alpha, srukf_value beta, srukf_value kappa)
Set the UKF scaling parameters.
#define DEFAULT_BETA
Default prior distribution parameter (optimal for Gaussian)
static srukf_return propagate_sigma_points(const srukf_mat *Xsig, srukf_mat *Ysig, void(*func)(const srukf_mat *, srukf_mat *, void *), void *user)
Propagate sigma points through a function.
srukf_return srukf_correct(srukf *ukf, srukf_mat *z, void(*h)(const srukf_mat *, srukf_mat *, void *), void *user)
Correct step: incorporate measurement.
Square-Root Unscented Kalman Filter (SR-UKF) Library.
#define SRUKF_MAT_ALLOC(rows, cols)
Allocate a matrix with data buffer.
#define SRUKF_LEADING_DIM(A)
Get leading dimension for BLAS/LAPACK (always n_rows for col-major)
struct srukf_workspace srukf_workspace
Opaque workspace structure.
#define SRUKF_SET_TYPE(A, t)
Set a type flag on a matrix.
double srukf_value
Scalar value type.
void(* srukf_diag_fn)(const char *msg)
Diagnostic callback function type.
#define SRUKF_IS_TYPE(A, t)
Test if a type flag is set.
#define SRUKF_MAT_ALLOC_NO_DATA(rows, cols)
Allocate a matrix struct only (data pointer will be NULL)
#define SRUKF_ENTRY(A, i, j)
Element access macro (column-major)
#define SRUKF_CBLAS_LAYOUT
BLAS layout constant.
#define SRUKF_LAPACK_LAYOUT
LAPACK layout constant.
size_t srukf_index
Scalar index type.
srukf_return
Function return codes.
@ SRUKF_RETURN_MATH_ERROR
@ SRUKF_RETURN_PARAMETER_ERROR
Square-Root Unscented Kalman Filter.