pg_orrery/src/od_iod.h
Ryan Malloy adfb6949e1 Add range rate fitting, weighted observations, and Gauss angles-only IOD (v0.6.0)
Range rate: topocentric residuals now include an optional 4th component
(dot(Δr, v_ecef) / |Δr|) with OD_RR_SCALE=10.0 for unit balancing.
Controlled via fit_range_rate parameter on tle_from_topocentric().

Weighted observations: per-observation weights applied as √w scaling
to both residuals and Jacobian rows, producing the weighted normal
equations H'WH without explicit W construction. Weights parameter
added to tle_from_eci, tle_from_topocentric, and tle_from_angles.

Gauss angles-only IOD: Vallado Algorithm 52 implementation for
seed-free orbit recovery from 3+ RA/Dec observations. New RA/Dec
residual function with cos(dec) scaling and wrap-around handling.
New tle_from_angles() and tle_from_angles_multi() SQL functions
accepting RA in hours [0,24), Dec in degrees [-90,90].

New standalone test suite: test_od_gauss (17 assertions).
New regression tests: Tests 18-25 covering range rate, weights,
angles-only with/without seed, and error cases.
2026-02-17 17:48:13 -07:00

64 lines
2.0 KiB
C

/*
* od_iod.h -- Initial orbit determination (Gibbs method)
*
* Given 3 position vectors and their times, recover a velocity
* at the middle observation using Gibbs' vector cross-product
* approach (Vallado Algorithm 54). The resulting (r, v) pair
* provides a seed orbit for the DC solver.
*
* Uses WGS-72 mu (398600.8 km^3/s^2) for SGP4 consistency.
*/
#ifndef PG_ORRERY_OD_IOD_H
#define PG_ORRERY_OD_IOD_H
#include "od_math.h"
/*
* IOD result: Keplerian elements at the middle observation epoch.
*/
typedef struct
{
od_keplerian_t kep;
double epoch_jd;
int valid; /* 1 if physically valid orbit */
} od_iod_result_t;
/*
* Gibbs method: recover velocity at r2 from 3 coplanar positions.
*
* pos1, pos2, pos3: TEME position vectors (km)
* jd1, jd2, jd3: Julian dates of each observation
* result: output Keplerian elements at epoch jd2
*
* Returns 0 on success, -1 if vectors are not coplanar (within
* tolerance) or orbit is degenerate.
*
* Coplanarity check: |r1 . (r2 x r3)| / (|r1| * |r2 x r3|) < 0.05
* (about 3 degrees -- generous to handle noise).
*/
int od_gibbs(const double pos1[3], const double pos2[3],
const double pos3[3],
double jd1, double jd2, double jd3,
od_iod_result_t *result);
/*
* Gauss method: recover orbit from 3 angles-only (RA/Dec) observations.
*
* ra[3], dec[3]: right ascension and declination in radians
* jd[3]: Julian dates of each observation
* obs_ecef[3][3]: observer ECEF positions (km) at each epoch
* result: output Keplerian elements at epoch jd[1] (middle obs)
*
* Returns 0 on success, -1 on failure (non-convergence, degenerate).
*
* Implements Vallado Algorithm 52 with iterative refinement of the
* slant range at the middle observation.
*/
int od_gauss(const double ra[3], const double dec[3],
const double jd[3],
const double obs_ecef[3][3],
od_iod_result_t *result);
#endif /* PG_ORRERY_OD_IOD_H */