/* * od_solver.h -- TLE fitting via weighted least-squares differential correction * * Implements Vallado & Crawford (2008) AIAA 2008-6770: * - Equinoctial element perturbation * - SGP4 as the propagation engine * - Jacobian by finite differencing * - SVD solve via LAPACK dgelss_() * - Tiered step limiting (Vallado 2008 Section V.B) * - Brouwer-to-Kozai Newton-Raphson for TLE output */ #ifndef PG_ORRERY_OD_SOLVER_H #define PG_ORRERY_OD_SOLVER_H #include "norad.h" /* Maximum number of DC iterations */ #define OD_MAX_ITER 25 /* Convergence thresholds (Vallado 2008 Section V.A) */ #define OD_RMS_REL_TOL 0.0002 #define OD_RMS_ABS_TOL 0.0002 /* Number of equinoctial states (6 orbital + optional B*) */ #define OD_NSTATE_6 6 #define OD_NSTATE_7 7 /* * Observation type flag */ typedef enum { OD_OBS_ECI = 0, /* 6-component: x, y, z, vx, vy, vz (km, km/s) */ OD_OBS_TOPO = 1, /* 3-component: az, el, range (rad, rad, km) */ OD_OBS_RADEC = 2 /* 2-component: RA, Dec (radians, equatorial) */ } od_obs_type_t; /* * Single observation for the DC solver */ typedef struct { double jd; /* Julian date of observation */ double data[6]; /* ECI: [x,y,z,vx,vy,vz], topo: [az,el,range,...] */ int observer_idx; /* index into config->observers[] (topo mode) */ } od_observation_t; /* * Observer location (needed for topocentric mode) */ typedef struct { double lat; /* radians */ double lon; /* radians */ double alt_m; /* meters */ double ecef[3]; /* pre-computed ECEF position (km) */ } od_observer_t; /* * Solver configuration */ typedef struct { od_obs_type_t obs_type; /* ECI or topocentric */ int fit_bstar; /* include B* as 7th state */ int fit_range_rate; /* include range_rate in topo residuals */ int max_iter; /* iteration limit */ od_observer_t *observers; /* array of observers (topo mode) */ int n_observers; /* count (0 for ECI mode) */ double *weights; /* per-observation weights (NULL=uniform) */ int n_weights; } od_config_t; /* * Solver result */ typedef struct { tle_t fitted_tle; /* resulting TLE (Kozai mean motion) */ int iterations; /* iterations performed */ double rms_initial; /* RMS residual before fitting (km) */ double rms_final; /* RMS residual after fitting (km) */ int converged; /* 1 if converged, 0 if hit max_iter */ char status[64]; /* human-readable status */ double covariance[28]; /* upper triangle of (H^T H)^{-1}, row-major */ int cov_size; /* 21 (6-state) or 28 (7-state), 0 if failed */ double condition_number; /* s_max / s_min from final SVD */ } od_result_t; /* * Primary entry point: fit a TLE from observations. * * obs: array of N observations * n_obs: number of observations (must be >= 6, or >= 7 if fit_bstar) * seed: initial TLE guess (if NULL, computed from first/last obs) * config: solver configuration * result: output (caller allocates) * * Returns 0 on success, -1 on error. * * Memory: all working arrays allocated via palloc() (when used from * PostgreSQL) or malloc() (standalone). Freed before return. */ int od_fit_tle(const od_observation_t *obs, int n_obs, const tle_t *seed, const od_config_t *config, od_result_t *result); #endif /* PG_ORRERY_OD_SOLVER_H */