Phase 1 — Stars, comets, Keplerian propagation: - star_observe() / star_observe_safe(): fixed star alt/az via IAU 1976 precession, equatorial-to-horizontal transform - kepler_propagate(): two-body Keplerian orbit propagation for elliptic, parabolic, and hyperbolic orbits - comet_observe(): observe comets/asteroids from orbital elements - heliocentric type: ecliptic J2000 position (x, y, z in AU) Phase 2 — VSOP87 planets, ELP82B Moon, Sun: - planet_heliocentric(): VSOP87 heliocentric ecliptic J2000 positions for Mercury through Neptune (Bretagnon & Francou, MIT) - planet_observe(): full observation pipeline for any planet - sun_observe(): Sun position from negated Earth VSOP87 - moon_observe(): ELP2000-82B lunar position (Chapront-Touzé, MIT) - Clean-room precession (IAU 2006) and sidereal time (IERS 2010) - elliptic_to_rectangular utility (Stellarium, MIT) All Stellarium extractions are MIT-licensed, thread-safe (static caching removed for PARALLEL SAFE), zero external data files. All 9 regression tests pass (90ms total).
141 lines
3.8 KiB
C
141 lines
3.8 KiB
C
/*
|
|
* sidereal_time.c -- Greenwich sidereal time (mean and apparent)
|
|
*
|
|
* Clean-room implementation from published standards:
|
|
*
|
|
* Mean sidereal time:
|
|
* Capitaine, Guinot & McCarthy (2000), A&A 355, 398-405.
|
|
* IERS Conventions (2010), IERS Technical Note 36, Ch. 5,
|
|
* Eq. 5.29 (GMST as polynomial in UT1).
|
|
*
|
|
* The polynomial form is:
|
|
* GMST(UT1) = theta_0 + theta_1*T + theta_2*T^2 + theta_3*T^3
|
|
* where T is Julian centuries of UT1 from J2000.0, and the
|
|
* result is in seconds of time. Coefficients from Capitaine
|
|
* et al. (2000), Eq. (42), which are the values adopted by
|
|
* the IERS for consistency with IAU 2000/2006 precession.
|
|
*
|
|
* Apparent sidereal time:
|
|
* GAST = GMST + equation_of_equinoxes
|
|
* EqEq = delta_psi * cos(epsilon_A)
|
|
* IERS Conventions (2010), Eq. 5.30.
|
|
*/
|
|
|
|
#include "sidereal_time.h"
|
|
#include "precession.h"
|
|
#include <math.h>
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846
|
|
#endif
|
|
|
|
/* Arcseconds to radians */
|
|
#define ARCSEC_TO_RAD (M_PI / (180.0 * 3600.0))
|
|
|
|
/*
|
|
* get_mean_sidereal_time
|
|
*
|
|
* GMST in radians from the classical polynomial expression.
|
|
*
|
|
* The polynomial (Capitaine et al. 2000, Eq. 42):
|
|
* GMST = 67310.54841
|
|
* + (876600h + 8640184.812866) * T
|
|
* + 0.093104 * T^2
|
|
* - 6.2e-6 * T^3
|
|
*
|
|
* where T = (JD_UT1 - 2451545.0) / 36525.0
|
|
* and the result is in seconds of sidereal time.
|
|
*
|
|
* The constant 876600h = 876600 * 3600 = 3155760000 seconds
|
|
* accounts for complete rotations.
|
|
*
|
|
* To convert seconds of time to radians:
|
|
* 1 day = 86400 seconds of time = 2*pi radians
|
|
* 1 second of time = 2*pi/86400 = pi/43200 radians
|
|
*
|
|
* JDE is accepted for interface consistency but unused here;
|
|
* GMST is fundamentally a UT1 quantity.
|
|
*/
|
|
double
|
|
get_mean_sidereal_time(double JD, double JDE)
|
|
{
|
|
double T, T2, T3;
|
|
double gmst_sec;
|
|
double gmst_rad;
|
|
|
|
(void)JDE; /* GMST depends on UT1, not TDB */
|
|
|
|
T = (JD - 2451545.0) / 36525.0;
|
|
T2 = T * T;
|
|
T3 = T2 * T;
|
|
|
|
/*
|
|
* Polynomial in seconds of sidereal time.
|
|
* The large linear coefficient includes whole rotations
|
|
* accumulated since J2000.0.
|
|
*/
|
|
gmst_sec = 67310.54841
|
|
+ (876600.0 * 3600.0 + 8640184.812866) * T
|
|
+ 0.093104 * T2
|
|
- 6.2e-6 * T3;
|
|
|
|
/* Seconds of time -> radians, then normalize to [0, 2*pi) */
|
|
gmst_rad = fmod(gmst_sec * (M_PI / 43200.0), 2.0 * M_PI);
|
|
if (gmst_rad < 0.0)
|
|
gmst_rad += 2.0 * M_PI;
|
|
|
|
return gmst_rad;
|
|
}
|
|
|
|
|
|
/*
|
|
* get_apparent_sidereal_time
|
|
*
|
|
* GAST = GMST + equation of the equinoxes.
|
|
*
|
|
* The equation of the equinoxes (IERS 2010, Eq. 5.30):
|
|
* EqEq = delta_psi * cos(epsilon_A)
|
|
*
|
|
* where delta_psi is nutation in longitude and epsilon_A is
|
|
* the mean obliquity of the ecliptic, both in arcseconds.
|
|
*
|
|
* For the full IAU 2000A model, there is an additional
|
|
* "complementary terms" correction to EqEq (Eq. 5.31),
|
|
* but it is at the 0.1 mas level and negligible for our
|
|
* accuracy requirements.
|
|
*/
|
|
double
|
|
get_apparent_sidereal_time(double JD, double JDE)
|
|
{
|
|
double gmst;
|
|
double epsilon_A, chi_A, omega_A, psi_A;
|
|
double delta_psi, delta_epsilon;
|
|
double eq_eq;
|
|
double gast;
|
|
|
|
gmst = get_mean_sidereal_time(JD, JDE);
|
|
|
|
/* Get mean obliquity from precession (in arcseconds) */
|
|
get_precession_angles_vondrak(JDE, &epsilon_A, &chi_A, &omega_A, &psi_A);
|
|
|
|
/* Get nutation in longitude (in arcseconds) */
|
|
get_nutation_angles_iau2000b(JDE, &delta_psi, &delta_epsilon);
|
|
|
|
/*
|
|
* Equation of the equinoxes:
|
|
* delta_psi is in arcseconds, epsilon_A is in arcseconds.
|
|
* Convert delta_psi to radians, multiply by cos(epsilon_A in radians),
|
|
* result is in radians.
|
|
*/
|
|
eq_eq = (delta_psi * ARCSEC_TO_RAD) * cos(epsilon_A * ARCSEC_TO_RAD);
|
|
|
|
gast = gmst + eq_eq;
|
|
|
|
/* Normalize to [0, 2*pi) */
|
|
gast = fmod(gast, 2.0 * M_PI);
|
|
if (gast < 0.0)
|
|
gast += 2.0 * M_PI;
|
|
|
|
return gast;
|
|
}
|