Extend od_observation_t with observer_idx so each observation can reference a different ground station. Config now holds an array of observers instead of a single pointer. The existing single-observer tle_from_topocentric() is unchanged (sets observer_idx=0 for all obs). New overload: tle_from_topocentric(topo[], ts[], observer[], int4[], ...) accepts parallel observer_ids array indexing into the observers array. PG function overloading resolves by argument types. Tests 9-11: two-station fit converges, single-station via multi-observer API matches, out-of-range observer_id raises error.
103 lines
3.0 KiB
C
103 lines
3.0 KiB
C
/*
|
|
* 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_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 max_iter; /* iteration limit */
|
|
od_observer_t *observers; /* array of observers (topo mode) */
|
|
int n_observers; /* count (0 for ECI mode) */
|
|
} 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 */
|
|
} 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 */
|