Phase 3: Planetary moons and Jupiter radio burst prediction

Add observation functions for 19 planetary moons across four systems:
- Galilean moons (Io, Europa, Ganymede, Callisto) via clean-room L1.2 theory
- Saturn moons (Mimas through Hyperion) via TASS 1.7
- Uranus moons (Miranda through Oberon) via GUST86
- Mars moons (Phobos, Deimos) via MarsSat

Add Jupiter decametric radio burst prediction for Radio JOVE operators:
- io_phase_angle() — Io orbital phase from superior conjunction
- jupiter_cml() — System III Central Meridian Longitude with light-time correction
- jupiter_burst_probability() — Carr et al. (1983) source regions A, B, C, D

L1.2 Galilean theory is a clean-room MIT implementation from the published
IMCCE FORTRAN coefficients. All other ephemeris libraries are MIT-licensed
extractions from Stellarium with static caching removed for PARALLEL SAFE.

All 10 regression tests pass. Extension .so grows from 2.4MB to 2.5MB.
This commit is contained in:
Ryan Malloy 2026-02-16 01:55:13 -07:00
parent 0544a78276
commit ad7209d0db
15 changed files with 6224 additions and 2 deletions

View File

@ -7,7 +7,9 @@ OBJS = src/pg_orbit.o src/tle_type.o src/eci_type.o src/observer_type.o \
src/sgp4_funcs.o src/coord_funcs.o src/pass_funcs.o src/gist_tle.o \ src/sgp4_funcs.o src/coord_funcs.o src/pass_funcs.o src/gist_tle.o \
src/star_funcs.o src/kepler_funcs.o \ src/star_funcs.o src/kepler_funcs.o \
src/vsop87.o src/elp82b.o src/elliptic_to_rectangular.o \ src/vsop87.o src/elp82b.o src/elliptic_to_rectangular.o \
src/precession.o src/sidereal_time.o src/planet_funcs.o src/precession.o src/sidereal_time.o src/planet_funcs.o \
src/tass17.o src/gust86.o src/marssat.o src/l12.o \
src/moon_funcs.o src/radio_funcs.o
# sat_code C++ sources (compiled with g++, linked with extern "C" symbols) # sat_code C++ sources (compiled with g++, linked with extern "C" symbols)
SAT_CODE_DIR = lib/sat_code SAT_CODE_DIR = lib/sat_code
@ -21,7 +23,7 @@ OBJS += $(SAT_CODE_OBJS)
# Regression tests # Regression tests
REGRESS = tle_parse sgp4_propagate coord_transforms pass_prediction gist_index convenience \ REGRESS = tle_parse sgp4_propagate coord_transforms pass_prediction gist_index convenience \
star_observe kepler_comet planet_observe star_observe kepler_comet planet_observe moon_observe
REGRESS_OPTS = --inputdir=test REGRESS_OPTS = --inputdir=test
# Need C++ runtime for sat_code # Need C++ runtime for sat_code

View File

@ -120,3 +120,48 @@ CREATE FUNCTION moon_observe(observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_observe(observer, timestamptz) IS COMMENT ON FUNCTION moon_observe(observer, timestamptz) IS
'Observe the Moon via ELP2000-82B from (observer, time). Returns topocentric az/el with geocentric range in km.'; 'Observe the Moon via ELP2000-82B from (observer, time). Returns topocentric az/el with geocentric range in km.';
-- ============================================================
-- Phase 3: Planetary moon observation
-- ============================================================
CREATE FUNCTION galilean_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION galilean_observe(int4, observer, timestamptz) IS
'Observe a Galilean moon of Jupiter via L1.2 theory. Body IDs: 0=Io, 1=Europa, 2=Ganymede, 3=Callisto.';
CREATE FUNCTION saturn_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION saturn_moon_observe(int4, observer, timestamptz) IS
'Observe a Saturn moon via TASS 1.7. Body IDs: 0=Mimas, 1=Enceladus, 2=Tethys, 3=Dione, 4=Rhea, 5=Titan, 6=Iapetus, 7=Hyperion.';
CREATE FUNCTION uranus_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION uranus_moon_observe(int4, observer, timestamptz) IS
'Observe a Uranus moon via GUST86. Body IDs: 0=Miranda, 1=Ariel, 2=Umbriel, 3=Titania, 4=Oberon.';
CREATE FUNCTION mars_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION mars_moon_observe(int4, observer, timestamptz) IS
'Observe a Mars moon via MarsSat. Body IDs: 0=Phobos, 1=Deimos.';
-- ============================================================
-- Phase 3: Jupiter decametric radio burst prediction
-- ============================================================
CREATE FUNCTION io_phase_angle(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION io_phase_angle(timestamptz) IS
'Io orbital phase angle in degrees [0,360). 0=superior conjunction (behind Jupiter). Standard Radio JOVE convention.';
CREATE FUNCTION jupiter_cml(observer, timestamptz) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION jupiter_cml(observer, timestamptz) IS
'Jupiter Central Meridian Longitude, System III (1965.0), in degrees [0,360). Light-time corrected.';
CREATE FUNCTION jupiter_burst_probability(float8, float8) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION jupiter_burst_probability(float8, float8) IS
'Estimated Jupiter decametric burst probability (0-1) from (io_phase_deg, cml_deg). Based on Carr et al. (1983) source regions A, B, C, D.';

View File

@ -636,3 +636,48 @@ CREATE FUNCTION moon_observe(observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE; AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_observe(observer, timestamptz) IS COMMENT ON FUNCTION moon_observe(observer, timestamptz) IS
'Observe the Moon via ELP2000-82B from (observer, time). Returns topocentric az/el with geocentric range in km.'; 'Observe the Moon via ELP2000-82B from (observer, time). Returns topocentric az/el with geocentric range in km.';
-- ============================================================
-- Phase 3: Planetary moon observation
-- ============================================================
CREATE FUNCTION galilean_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION galilean_observe(int4, observer, timestamptz) IS
'Observe a Galilean moon of Jupiter via L1.2 theory. Body IDs: 0=Io, 1=Europa, 2=Ganymede, 3=Callisto.';
CREATE FUNCTION saturn_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION saturn_moon_observe(int4, observer, timestamptz) IS
'Observe a Saturn moon via TASS 1.7. Body IDs: 0=Mimas, 1=Enceladus, 2=Tethys, 3=Dione, 4=Rhea, 5=Titan, 6=Iapetus, 7=Hyperion.';
CREATE FUNCTION uranus_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION uranus_moon_observe(int4, observer, timestamptz) IS
'Observe a Uranus moon via GUST86. Body IDs: 0=Miranda, 1=Ariel, 2=Umbriel, 3=Titania, 4=Oberon.';
CREATE FUNCTION mars_moon_observe(int4, observer, timestamptz) RETURNS topocentric
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION mars_moon_observe(int4, observer, timestamptz) IS
'Observe a Mars moon via MarsSat. Body IDs: 0=Phobos, 1=Deimos.';
-- ============================================================
-- Phase 3: Jupiter decametric radio burst prediction
-- ============================================================
CREATE FUNCTION io_phase_angle(timestamptz) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION io_phase_angle(timestamptz) IS
'Io orbital phase angle in degrees [0,360). 0=superior conjunction (behind Jupiter). Standard Radio JOVE convention.';
CREATE FUNCTION jupiter_cml(observer, timestamptz) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION jupiter_cml(observer, timestamptz) IS
'Jupiter Central Meridian Longitude, System III (1965.0), in degrees [0,360). Light-time corrected.';
CREATE FUNCTION jupiter_burst_probability(float8, float8) RETURNS float8
AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION jupiter_burst_probability(float8, float8) IS
'Estimated Jupiter decametric burst probability (0-1) from (io_phase_deg, cml_deg). Based on Carr et al. (1983) source regions A, B, C, D.';

440
src/gust86.c Normal file
View File

@ -0,0 +1,440 @@
/************************************************************************
COMPUTATION OF THE COORDINATES OF THE URANIAN SATELLITES (GUST86),
version 0.1 (1988,1995) by LASKAR J. and JACOBSON, R. can be found at
ftp://ftp.imcce.fr/pub/ephem/satel/gust86
I (Johannes Gajdosik) have just taken the Fortran code and data
obtained from above and rearranged it into this piece of software.
I can neither allow nor forbid the usage of the GUST86 theory.
The copyright notice below covers not the works of LASKAR J. and JACOBSON, R.,
but just my work, that is the compilation of the GUST86 theory
into the software supplied in this file.
Copyright (c) 2005 Johannes Gajdosik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Derived from Stellarium's GUST86 implementation.
Modified for pg_orbit: removed all static mutable state for thread safety
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
CalcInterpolatedElements for performance; this version computes fresh on
each call which is acceptable for SQL query workloads.
1) Rotate results to "dynamical equinox and ecliptic J2000",
the reference frame of VSOP87 and VSOP87A.
2) units: julian day, AU, rad
3) use EllipticToRectangularN from elliptic_to_rectangular.h
****************************************************************/
#include "gust86.h"
#include "elliptic_to_rectangular.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
static
const double fqn[5] = {4.44519055,
2.492952519,
1.516148111,
0.721718509,
0.46669212};
static
const double fqe[5] = {20.082*M_PI/(180*365.25),
6.217*M_PI/(180*365.25),
2.865*M_PI/(180*365.25),
2.078*M_PI/(180*365.25),
0.386*M_PI/(180*365.25)};
static
const double fqi[5] = {-20.309*M_PI/(180*365.25),
-6.288*M_PI/(180*365.25),
-2.836*M_PI/(180*365.25),
-1.843*M_PI/(180*365.25),
-0.259*M_PI/(180*365.25)};
static
const double phn[5] = {-0.238051,
3.098046,
2.285402,
0.856359,
-0.915592};
static
const double phe[5] = {0.611392,
2.408974,
2.067774,
0.735131,
0.426767};
static
const double phi[5] = {5.702313,
0.395757,
0.589326,
1.746237,
4.206896};
static
void CalcGust86Elem(double t, double elem[30]) {
double an[5], ae[5], ai[5];
int i;
for (i = 0; i < 5; i++) {
an[i] = fmod(fqn[i] * t + phn[i], 2*M_PI);
ae[i] = fmod(fqe[i] * t + phe[i], 2*M_PI);
ai[i] = fmod(fqi[i] * t + phi[i], 2*M_PI);
}
elem[0*6+0] = 4.44352267
- cos(an[0] - an[1] * 3. + an[2] * 2.) * 3.492e-5
+ cos(an[0] * 2. - an[1] * 6. + an[2] * 4.) * 8.47e-6
+ cos(an[0] * 3. - an[1] * 9. + an[2] * 6.) * 1.31e-6
- cos(an[0] - an[1] ) * 5.228e-5
- cos(an[0] * 2. - an[1] * 2. ) * 1.3665e-4;
elem[0*6+1] =
sin(an[0] - an[1] * 3. + an[2] * 2.) * .02547217
- sin(an[0] * 2. - an[1] * 6. + an[2] * 4.) * .00308831
- sin(an[0] * 3. - an[1] * 9. + an[2] * 6.) * 3.181e-4
- sin(an[0] * 4. - an[1] * 12 + an[2] * 8.) * 3.749e-5
- sin(an[0] - an[1] ) * 5.785e-5
- sin(an[0] * 2. - an[1] * 2. ) * 6.232e-5
- sin(an[0] * 3. - an[1] * 3. ) * 2.795e-5
+ t * 4.44519055 - .23805158;
elem[0*6+2] = cos(ae[0]) * .00131238
+ cos(ae[1]) * 7.181e-5
+ cos(ae[2]) * 6.977e-5
+ cos(ae[3]) * 6.75e-6
+ cos(ae[4]) * 6.27e-6
+ cos(an[0]) * 1.941e-4
- cos(-an[0] + an[1] * 2.) * 1.2331e-4
+ cos(an[0] * -2. + an[1] * 3.) * 3.952e-5;
elem[0*6+3] = sin(ae[0]) * .00131238
+ sin(ae[1]) * 7.181e-5
+ sin(ae[2]) * 6.977e-5
+ sin(ae[3]) * 6.75e-6
+ sin(ae[4]) * 6.27e-6
+ sin(an[0]) * 1.941e-4
- sin(-an[0] + an[1] * 2.) * 1.2331e-4
+ sin(an[0] * -2. + an[1] * 3.) * 3.952e-5;
elem[0*6+4] = cos(ai[0]) * .03787171
+ cos(ai[1]) * 2.701e-5
+ cos(ai[2]) * 3.076e-5
+ cos(ai[3]) * 1.218e-5
+ cos(ai[4]) * 5.37e-6;
elem[0*6+5] = sin(ai[0]) * .03787171
+ sin(ai[1]) * 2.701e-5
+ sin(ai[2]) * 3.076e-5
+ sin(ai[3]) * 1.218e-5
+ sin(ai[4]) * 5.37e-6;
elem[1*6+0] = 2.49254257
+ cos(an[0] - an[1] * 3. + an[2] * 2.) * 2.55e-6
- cos( an[1] - an[2] ) * 4.216e-5
- cos( an[1] * 2. - an[2] * 2.) * 1.0256e-4;
elem[1*6+1] =
- sin(an[0] - an[1] * 3. + an[2] * 2.) * .0018605
+ sin(an[0] * 2. - an[1] * 6. + an[2] * 4.) * 2.1999e-4
+ sin(an[0] * 3. - an[1] * 9. + an[2] * 6.) * 2.31e-5
+ sin(an[0] * 4. - an[1] * 12 + an[2] * 8.) * 4.3e-6
- sin( an[1] - an[2] ) * 9.011e-5
- sin( an[1] * 2. - an[2] * 2.) * 9.107e-5
- sin( an[1] * 3. - an[2] * 3.) * 4.275e-5
- sin( an[1] * 2. - an[3] * 2.) * 1.649e-5
+ t * 2.49295252 + 3.09804641;
elem[1*6+2] = cos(ae[0]) * -3.35e-6
+ cos(ae[1]) * .00118763
+ cos(ae[2]) * 8.6159e-4
+ cos(ae[3]) * 7.15e-5
+ cos(ae[4]) * 5.559e-5
- cos(-an[1] + an[2] * 2.) * 8.46e-5
+ cos(an[1] * -2. + an[2] * 3.) * 9.181e-5
+ cos(-an[1] + an[3] * 2.) * 2.003e-5
+ cos(an[1]) * 8.977e-5;
elem[1*6+3] = sin(ae[0]) * -3.35e-6
+ sin(ae[1]) * .00118763
+ sin(ae[2]) * 8.6159e-4
+ sin(ae[3]) * 7.15e-5
+ sin(ae[4]) * 5.559e-5
- sin(-an[1] + an[2] * 2.) * 8.46e-5
+ sin(an[1] * -2. + an[2] * 3.) * 9.181e-5
+ sin(-an[1] + an[3] * 2.) * 2.003e-5
+ sin(an[1]) * 8.977e-5;
elem[1*6+4] = cos(ai[0]) * -1.2175e-4
+ cos(ai[1]) * 3.5825e-4
+ cos(ai[2]) * 2.9008e-4
+ cos(ai[3]) * 9.778e-5
+ cos(ai[4]) * 3.397e-5;
elem[1*6+5] = sin(ai[0]) * -1.2175e-4
+ sin(ai[1]) * 3.5825e-4
+ sin(ai[2]) * 2.9008e-4
+ sin(ai[3]) * 9.778e-5
+ sin(ai[4]) * 3.397e-5;
elem[2*6+0] = 1.5159549
+ cos(an[2] - an[3] * 2. + ae[2]) * 9.74e-6
- cos(an[1] - an[2]) * 1.06e-4
+ cos(an[1] * 2. - an[2] * 2.) * 5.416e-5
- cos(an[2] - an[3]) * 2.359e-5
- cos(an[2] * 2. - an[3] * 2.) * 7.07e-5
- cos(an[2] * 3. - an[3] * 3.) * 3.628e-5;
elem[2*6+1] =
sin(an[0] - an[1] * 3. + an[2] * 2.) * 6.6057e-4
- sin(an[0] * 2. - an[1] * 6. + an[2] * 4.) * 7.651e-5
- sin(an[0] * 3. - an[1] * 9. + an[2] * 6.) * 8.96e-6
- sin(an[0] * 4. - an[1] * 12. + an[2] * 8.) * 2.53e-6
- sin(an[2] - an[3] * 4. + an[4] * 3.) * 5.291e-5
- sin(an[2] - an[3] * 2. + ae[4]) * 7.34e-6
- sin(an[2] - an[3] * 2. + ae[3]) * 1.83e-6
+ sin(an[2] - an[3] * 2. + ae[2]) * 1.4791e-4
+ sin(an[2] - an[3] * 2. + ae[1]) * -7.77e-6
+ sin(an[1] - an[2]) * 9.776e-5
+ sin(an[1] * 2. - an[2] * 2.) * 7.313e-5
+ sin(an[1] * 3. - an[2] * 3.) * 3.471e-5
+ sin(an[1] * 4. - an[2] * 4.) * 1.889e-5
- sin(an[2] - an[3]) * 6.789e-5
- sin(an[2] * 2. - an[3] * 2.) * 8.286e-5
+ sin(an[2] * 3. - an[3] * 3.) * -3.381e-5
- sin(an[2] * 4. - an[3] * 4.) * 1.579e-5
- sin(an[2] - an[4]) * 1.021e-5
- sin(an[2] * 2. - an[4] * 2.) * 1.708e-5
+ t * 1.51614811 + 2.28540169;
elem[2*6+2] = cos(ae[0]) * -2.1e-7
- cos(ae[1]) * 2.2795e-4
+ cos(ae[2]) * .00390469
+ cos(ae[3]) * 3.0917e-4
+ cos(ae[4]) * 2.2192e-4
+ cos(an[1]) * 2.934e-5
+ cos(an[2]) * 2.62e-5
+ cos(-an[1] + an[2] * 2.) * 5.119e-5
- cos(an[1] * -2. + an[2] * 3.) * 1.0386e-4
- cos(an[1] * -3. + an[2] * 4.) * 2.716e-5
+ cos(an[3]) * -1.622e-5
+ cos(-an[2] + an[3] * 2.) * 5.4923e-4
+ cos(an[2] * -2. + an[3] * 3.) * 3.47e-5
+ cos(an[2] * -3. + an[3] * 4.) * 1.281e-5
+ cos(-an[2] + an[4] * 2.) * 2.181e-5
+ cos(an[2]) * 4.625e-5;
elem[2*6+3] = sin(ae[0]) * -2.1e-7
- sin(ae[1]) * 2.2795e-4
+ sin(ae[2]) * .00390469
+ sin(ae[3]) * 3.0917e-4
+ sin(ae[4]) * 2.2192e-4
+ sin(an[1]) * 2.934e-5
+ sin(an[2]) * 2.62e-5
+ sin(-an[1] + an[2] * 2.) * 5.119e-5
- sin(an[1] * -2. + an[2] * 3.) * 1.0386e-4
- sin(an[1] * -3. + an[2] * 4.) * 2.716e-5
+ sin(an[3]) * -1.622e-5
+ sin(-an[2] + an[3] * 2.) * 5.4923e-4
+ sin(an[2] * -2. + an[3] * 3.) * 3.47e-5
+ sin(an[2] * -3. + an[3] * 4.) * 1.281e-5
+ sin(-an[2] + an[4] * 2.) * 2.181e-5
+ sin(an[2]) * 4.625e-5;
elem[2*6+4] = cos(ai[0]) * -1.086e-5
- cos(ai[1]) * 8.151e-5
+ cos(ai[2]) * .00111336
+ cos(ai[3]) * 3.5014e-4
+ cos(ai[4]) * 1.065e-4;
elem[2*6+5] = sin(ai[0]) * -1.086e-5
- sin(ai[1]) * 8.151e-5
+ sin(ai[2]) * .00111336
+ sin(ai[3]) * 3.5014e-4
+ sin(ai[4]) * 1.065e-4;
elem[3*6+0] = .72166316
- cos(an[2] - an[3] * 2. + ae[2]) * 2.64e-6
- cos(an[3] * 2. - an[4] * 3. + ae[4]) * 2.16e-6
+ cos(an[3] * 2. - an[4] * 3. + ae[3]) * 6.45e-6
- cos(an[3] * 2. - an[4] * 3. + ae[2]) * 1.11e-6
+ cos(an[1] - an[3]) * -6.223e-5
- cos(an[2] - an[3]) * 5.613e-5
- cos(an[3] - an[4]) * 3.994e-5
- cos(an[3] * 2. - an[4] * 2.) * 9.185e-5
- cos(an[3] * 3. - an[4] * 3.) * 5.831e-5
- cos(an[3] * 4. - an[4] * 4.) * 3.86e-5
- cos(an[3] * 5. - an[4] * 5.) * 2.618e-5
- cos(an[3] * 6. - an[4] * 6.) * 1.806e-5;
elem[3*6+1] =
sin(an[2] - an[3] * 4. + an[4] * 3.) * 2.061e-5
- sin(an[2] - an[3] * 2. + ae[4]) * 2.07e-6
- sin(an[2] - an[3] * 2. + ae[3]) * 2.88e-6
- sin(an[2] - an[3] * 2. + ae[2]) * 4.079e-5
+ sin(an[2] - an[3] * 2. + ae[1]) * 2.11e-6
- sin(an[3] * 2. - an[4] * 3. + ae[4]) * 5.183e-5
+ sin(an[3] * 2. - an[4] * 3. + ae[3]) * 1.5987e-4
+ sin(an[3] * 2. - an[4] * 3. + ae[2]) * -3.505e-5
- sin(an[3] * 3. - an[4] * 4. + ae[4]) * 1.56e-6
+ sin(an[1] - an[3]) * 4.054e-5
+ sin(an[2] - an[3]) * 4.617e-5
- sin(an[3] - an[4]) * 3.1776e-4
- sin(an[3] * 2. - an[4] * 2.) * 3.0559e-4
- sin(an[3] * 3. - an[4] * 3.) * 1.4836e-4
- sin(an[3] * 4. - an[4] * 4.) * 8.292e-5
+ sin(an[3] * 5. - an[4] * 5.) * -4.998e-5
- sin(an[3] * 6. - an[4] * 6.) * 3.156e-5
- sin(an[3] * 7. - an[4] * 7.) * 2.056e-5
- sin(an[3] * 8. - an[4] * 8.) * 1.369e-5
+ t * .72171851 + .85635879;
elem[3*6+2] = cos(ae[0]) * -2e-8
- cos(ae[1]) * 1.29e-6
- cos(ae[2]) * 3.2451e-4
+ cos(ae[3]) * 9.3281e-4
+ cos(ae[4]) * .00112089
+ cos(an[1]) * 3.386e-5
+ cos(an[3]) * 1.746e-5
+ cos(-an[1] + an[3] * 2.) * 1.658e-5
+ cos(an[2]) * 2.889e-5
- cos(-an[2] + an[3] * 2.) * 3.586e-5
+ cos(an[3]) * -1.786e-5
- cos(an[4]) * 3.21e-5
- cos(-an[3] + an[4] * 2.) * 1.7783e-4
+ cos(an[3] * -2. + an[4] * 3.) * 7.9343e-4
+ cos(an[3] * -3. + an[4] * 4.) * 9.948e-5
+ cos(an[3] * -4. + an[4] * 5.) * 4.483e-5
+ cos(an[3] * -5. + an[4] * 6.) * 2.513e-5
+ cos(an[3] * -6. + an[4] * 7.) * 1.543e-5;
elem[3*6+3] = sin(ae[0]) * -2e-8
- sin(ae[1]) * 1.29e-6
- sin(ae[2]) * 3.2451e-4
+ sin(ae[3]) * 9.3281e-4
+ sin(ae[4]) * .00112089
+ sin(an[1]) * 3.386e-5
+ sin(an[3]) * 1.746e-5
+ sin(-an[1] + an[3] * 2.) * 1.658e-5
+ sin(an[2]) * 2.889e-5
- sin(-an[2] + an[3] * 2.) * 3.586e-5
+ sin(an[3]) * -1.786e-5
- sin(an[4]) * 3.21e-5
- sin(-an[3] + an[4] * 2.) * 1.7783e-4
+ sin(an[3] * -2. + an[4] * 3.) * 7.9343e-4
+ sin(an[3] * -3. + an[4] * 4.) * 9.948e-5
+ sin(an[3] * -4. + an[4] * 5.) * 4.483e-5
+ sin(an[3] * -5. + an[4] * 6.) * 2.513e-5
+ sin(an[3] * -6. + an[4] * 7.) * 1.543e-5;
elem[3*6+4] = cos(ai[0]) * -1.43e-6
- cos(ai[1]) * 1.06e-6
- cos(ai[2]) * 1.4013e-4
+ cos(ai[3]) * 6.8572e-4
+ cos(ai[4]) * 3.7832e-4;
elem[3*6+5] = sin(ai[0]) * -1.43e-6
- sin(ai[1]) * 1.06e-6
- sin(ai[2]) * 1.4013e-4
+ sin(ai[3]) * 6.8572e-4
+ sin(ai[4]) * 3.7832e-4;
elem[4*6+0] = .46658054
+ cos(an[3] * 2. - an[4] * 3. + ae[4]) * 2.08e-6
- cos(an[3] * 2. - an[4] * 3. + ae[3]) * 6.22e-6
+ cos(an[3] * 2. - an[4] * 3. + ae[2]) * 1.07e-6
- cos(an[1] - an[4]) * 4.31e-5
+ cos(an[2] - an[4]) * -3.894e-5
- cos(an[3] - an[4]) * 8.011e-5
+ cos(an[3] * 2. - an[4] * 2.) * 5.906e-5
+ cos(an[3] * 3. - an[4] * 3.) * 3.749e-5
+ cos(an[3] * 4. - an[4] * 4.) * 2.482e-5
+ cos(an[3] * 5. - an[4] * 5.) * 1.684e-5;
elem[4*6+1] =
- sin(an[2] - an[3] * 4. + an[4] * 3.) * 7.82e-6
+ sin(an[3] * 2. - an[4] * 3. + ae[4]) * 5.129e-5
- sin(an[3] * 2. - an[4] * 3. + ae[3]) * 1.5824e-4
+ sin(an[3] * 2. - an[4] * 3. + ae[2]) * 3.451e-5
+ sin(an[1] - an[4]) * 4.751e-5
+ sin(an[2] - an[4]) * 3.896e-5
+ sin(an[3] - an[4]) * 3.5973e-4
+ sin(an[3] * 2. - an[4] * 2.) * 2.8278e-4
+ sin(an[3] * 3. - an[4] * 3.) * 1.386e-4
+ sin(an[3] * 4. - an[4] * 4.) * 7.803e-5
+ sin(an[3] * 5. - an[4] * 5.) * 4.729e-5
+ sin(an[3] * 6. - an[4] * 6.) * 3e-5
+ sin(an[3] * 7. - an[4] * 7.) * 1.962e-5
+ sin(an[3] * 8. - an[4] * 8.) * 1.311e-5
+ t * .46669212 - .9155918;
elem[4*6+2] = cos(ae[1]) * -3.5e-7
+ cos(ae[2]) * 7.453e-5
- cos(ae[3]) * 7.5868e-4
+ cos(ae[4]) * .00139734
+ cos(an[1]) * 3.9e-5
+ cos(-an[1] + an[4] * 2.) * 1.766e-5
+ cos(an[2]) * 3.242e-5
+ cos(an[3]) * 7.975e-5
+ cos(an[4]) * 7.566e-5
+ cos(-an[3] + an[4] * 2.) * 1.3404e-4
- cos(an[3] * -2. + an[4] * 3.) * 9.8726e-4
- cos(an[3] * -3. + an[4] * 4.) * 1.2609e-4
- cos(an[3] * -4. + an[4] * 5.) * 5.742e-5
- cos(an[3] * -5. + an[4] * 6.) * 3.241e-5
- cos(an[3] * -6. + an[4] * 7.) * 1.999e-5
- cos(an[3] * -7. + an[4] * 8.) * 1.294e-5;
elem[4*6+3] = sin(ae[1]) * -3.5e-7
+ sin(ae[2]) * 7.453e-5
- sin(ae[3]) * 7.5868e-4
+ sin(ae[4]) * .00139734
+ sin(an[1]) * 3.9e-5
+ sin(-an[1] + an[4] * 2.) * 1.766e-5
+ sin(an[2]) * 3.242e-5
+ sin(an[3]) * 7.975e-5
+ sin(an[4]) * 7.566e-5
+ sin(-an[3] + an[4] * 2.) * 1.3404e-4
- sin(an[3] * -2. + an[4] * 3.) * 9.8726e-4
- sin(an[3] * -3. + an[4] * 4.) * 1.2609e-4
- sin(an[3] * -4. + an[4] * 5.) * 5.742e-5
- sin(an[3] * -5. + an[4] * 6.) * 3.241e-5
- sin(an[3] * -6. + an[4] * 7.) * 1.999e-5
- sin(an[3] * -7. + an[4] * 8.) * 1.294e-5;
elem[4*6+4] = cos(ai[0]) * -4.4e-7
- cos(ai[1]) * 3.1e-7
+ cos(ai[2]) * 3.689e-5
- cos(ai[3]) * 5.9633e-4
+ cos(ai[4]) * 4.5169e-4;
elem[4*6+5] = sin(ai[0]) * -4.4e-7
- sin(ai[1]) * 3.1e-7
+ sin(ai[2]) * 3.689e-5
- sin(ai[3]) * 5.9633e-4
+ sin(ai[4]) * 4.5169e-4;
}
static
const double gust86_rmu[5] = {
1.291892353675174e-08,
1.291910570526396e-08,
1.291910102284198e-08,
1.291942656265575e-08,
1.291935967091320e-08};
static const double GUST86toVsop87[9] = {
9.753206632086812015e-01, 6.194425668001473004e-02, 2.119257251551559653e-01,
-2.006444610981783542e-01,-1.519328516640849367e-01, 9.678110398294910731e-01,
9.214881523275189928e-02,-9.864478281437795399e-01,-1.357544776485127136e-01
};
void GetGust86Coor(double jd, int body, double *xyz, double *xyzdot) {
double elem[5*6];
double x[6];
const double t = jd - 2444239.5;
CalcGust86Elem(t, elem);
EllipticToRectangularN(gust86_rmu[body], elem + (body * 6), 0.0, x);
xyz[0] = GUST86toVsop87[0]*x[0] + GUST86toVsop87[1]*x[1] + GUST86toVsop87[2]*x[2];
xyz[1] = GUST86toVsop87[3]*x[0] + GUST86toVsop87[4]*x[1] + GUST86toVsop87[5]*x[2];
xyz[2] = GUST86toVsop87[6]*x[0] + GUST86toVsop87[7]*x[1] + GUST86toVsop87[8]*x[2];
if (xyzdot) {
xyzdot[0] = GUST86toVsop87[0]*x[3] + GUST86toVsop87[1]*x[4] + GUST86toVsop87[2]*x[5];
xyzdot[1] = GUST86toVsop87[3]*x[3] + GUST86toVsop87[4]*x[4] + GUST86toVsop87[5]*x[5];
xyzdot[2] = GUST86toVsop87[6]*x[3] + GUST86toVsop87[7]*x[4] + GUST86toVsop87[8]*x[5];
}
}

78
src/gust86.h Normal file
View File

@ -0,0 +1,78 @@
/************************************************************************
COMPUTATION OF THE COORDINATES OF THE URANIAN SATELLITES (GUST86),
version 0.1 (1988,1995) by LASKAR J. and JACOBSON, R. can be found at
ftp://ftp.imcce.fr/pub/ephem/satel/gust86
I (Johannes Gajdosik) have just taken the Fortran code and data
obtained from above and rearranged it into this piece of software.
I can neither allow nor forbid the usage of the GUST86 theory.
The copyright notice below covers not the works of LASKAR J. and JACOBSON, R.,
but just my work, that is the compilation of the GUST86 theory
into the software supplied in this file.
Copyright (c) 2005 Johannes Gajdosik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Derived from Stellarium's GUST86 implementation.
Modified for pg_orbit: removed static mutable state and
CalcInterpolatedElements caching for thread safety
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
1) Rotate results to "dynamical equinox and ecliptic J2000",
the reference frame of VSOP87 and VSOP87A.
2) units: julian day, AU, rad
****************************************************************/
#ifndef PG_ORBIT_GUST86_H
#define PG_ORBIT_GUST86_H
#ifdef __cplusplus
extern "C" {
#endif
#define GUST86_MIRANDA 0
#define GUST86_ARIEL 1
#define GUST86_UMBRIEL 2
#define GUST86_TITANIA 3
#define GUST86_OBERON 4
void GetGust86Coor(double jd, int body, double *xyz, double *xyzdot);
/* Return the rectangular coordinates of the given satellite
and the given julian date jd expressed in dynamical time (TAI+32.184s).
The origin of the xyz-coordinates is the center of Uranus.
The reference frame is "dynamical equinox and ecliptic J2000",
which is the reference frame in VSOP87 and VSOP87A.
body: 0=Miranda, 1=Ariel, 2=Umbriel, 3=Titania, 4=Oberon
xyz[3]: position (AU), relative to Uranus center
xyzdot[3]: velocity (AU/day), may be NULL
*/
#ifdef __cplusplus
}
#endif
#endif

918
src/l12.c Normal file
View File

@ -0,0 +1,918 @@
/************************************************************************
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
Clean-room implementation for pg_orbit.
Positions and velocities of Io, Europa, Ganymede, and Callisto
relative to Jupiter's center, in VSOP87 ecliptic J2000 coordinates.
Reference:
Lainey V., Duriez L., Vienne A.
"New accurate ephemerides for the Galilean satellites of Jupiter"
Astronomy & Astrophysics, 2004
ftp://ftp.imcce.fr/pub/ephem/satel/galilean/L1/L1.2/
The theory expresses each moon's orbit using modified Delaunay
variables {a, L, K, H, Q, P}, where:
a = semi-major axis (AU)
L = mean longitude (radians)
K = e * cos(varpi) (eccentricity vector, real part)
H = e * sin(varpi) (eccentricity vector, imaginary part)
Q = sin(i/2) * cos(Omega) (inclination vector, real part)
P = sin(i/2) * sin(Omega) (inclination vector, imaginary part)
Each variable is computed as a Fourier series in time since the
theory epoch (JD 2433282.5 = 1950 Jan 1.0 TT).
Copyright (c) 2026 Ryan Malloy <ryan@supported.systems>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Thread-safe: all functions are reentrant with no static mutable state.
****************************************************************/
#include "l12.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
#define TWO_PI (2.0 * M_PI)
/* L1.2 theory epoch: JD 2433282.5 (1950 Jan 1.0 TT) */
#define L12_EPOCH_JD 2433282.5
/* Kepler equation convergence threshold */
#define KEPLER_TOL 1.0e-12
/* Maximum series lengths across all four moons */
#define MAX_TERMS_A 38
#define MAX_TERMS_L 36
#define MAX_TERMS_Z 50
#define MAX_TERMS_ZETA 25
/*
* A single Fourier term: amplitude * trig(phase + frequency * t)
*
* For semi-major axis 'a': amplitude * cos(phase + freq * t)
* For mean longitude 'L': amplitude * sin(phase + freq * t)
* For complex eccentricity 'z': amplitude * cos/sin(phase + freq * t)
* For complex inclination 'zeta': amplitude * cos/sin(phase + freq * t)
*/
typedef struct {
double amp;
double phi;
double nu;
} l12_fourier_term;
/*
* All theory data for one Galilean moon.
*/
typedef struct {
double grav_param; /* mu: GM_Jupiter in AU^3/day^2 */
double lon0; /* mean longitude at epoch (rad) */
double lon_rate; /* mean longitude rate (rad/day) */
int n_a; /* number of semi-major axis terms */
int n_l; /* number of mean longitude terms */
int n_z; /* number of eccentricity terms */
int n_zeta; /* number of inclination terms */
l12_fourier_term fa[MAX_TERMS_A];
l12_fourier_term fl[MAX_TERMS_L];
l12_fourier_term fz[MAX_TERMS_Z];
l12_fourier_term fzeta[MAX_TERMS_ZETA];
} l12_moon_data;
/*
* Rotation matrix: L1.2 reference frame -> VSOP87 ecliptic J2000
*
* This is the product of the equatorial-to-ecliptic rotation
* evaluated at J2000, representing the physical orientation of
* Jupiter's Laplacian plane relative to the ecliptic. These nine
* values are an astronomical coordinate transform (physical constant).
*
* Stored row-major: row i, col j = rot_l12_to_vsop87[3*i + j]
*/
static const double rot_l12_to_vsop87[9] = {
9.994327815023905713e-01,
3.039550993390781261e-02,
-1.449924943755843383e-02,
-3.089770442223671880e-02,
9.988822846893227815e-01,
-3.577028369016394015e-02,
1.339578739122566807e-02,
3.619798764705610479e-02,
9.992548516622136737e-01
};
/*
* Convert modified Delaunay elements to position and velocity.
*
* Elements: {a, L, K, H, Q, P} where
* a = semi-major axis
* L = mean longitude
* K = e*cos(varpi), H = e*sin(varpi)
* Q = sin(i/2)*cos(Omega), P = sin(i/2)*sin(Omega)
*
* The mean anomaly in modified Delaunay variables is simply L
* (since the perturbation series already accounts for the
* longitude of periapse through K and H).
*
* Kepler's equation in K,H form:
* E = L + K*sin(E) - H*cos(E)
* where E is the eccentric longitude (not anomaly).
*/
static void
delaunay_to_cartesian(double grav_param, const double elems[6],
double pos[3], double vel[3])
{
double sma, mean_lon, kk, hh, qq, pp;
double mean_mot, ecc_lon, cos_e, sin_e;
double delta, dle, rsm1, inv_r_over_a;
double phi_ecc, psi_fac;
double xp, yp, vxp, vyp;
double f2, one_m_2pp, one_m_2qq, two_pq;
int iter;
sma = elems[0];
mean_lon = elems[1];
kk = elems[2];
hh = elems[3];
qq = elems[4];
pp = elems[5];
/* mean motion from Kepler's third law */
mean_mot = sqrt(grav_param / (sma * sma * sma));
/* solve generalized Kepler equation:
* E = L + K*sin(E) - H*cos(E)
* using Newton-Raphson iteration */
ecc_lon = mean_lon + kk * sin(mean_lon) - hh * cos(mean_lon);
for (iter = 0; iter < 20; iter++) {
cos_e = cos(ecc_lon);
sin_e = sin(ecc_lon);
delta = (mean_lon - ecc_lon + kk * sin_e - hh * cos_e)
/ (1.0 - kk * cos_e - hh * sin_e);
ecc_lon += delta;
if (fabs(delta) <= KEPLER_TOL)
break;
}
cos_e = cos(ecc_lon);
sin_e = sin(ecc_lon);
/* auxiliary quantities */
dle = hh * cos_e - kk * sin_e;
rsm1 = -kk * cos_e - hh * sin_e; /* (r/a - 1) */
inv_r_over_a = 1.0 / (1.0 + rsm1); /* a/r */
/* eccentricity-related factor */
phi_ecc = sqrt(1.0 - kk * kk - hh * hh);
psi_fac = 1.0 / (1.0 + phi_ecc);
/* position in orbital plane */
xp = sma * (cos_e - kk - psi_fac * hh * dle);
yp = sma * (sin_e - hh + psi_fac * kk * dle);
/* velocity in orbital plane */
vxp = mean_mot * inv_r_over_a * sma * (-sin_e - psi_fac * hh * rsm1);
vyp = mean_mot * inv_r_over_a * sma * ( cos_e + psi_fac * kk * rsm1);
/* rotate from orbital plane to 3D using Q, P (inclination vector) */
f2 = 2.0 * sqrt(1.0 - qq * qq - pp * pp);
one_m_2pp = 1.0 - 2.0 * pp * pp;
one_m_2qq = 1.0 - 2.0 * qq * qq;
two_pq = 2.0 * pp * qq;
pos[0] = xp * one_m_2pp + yp * two_pq;
pos[1] = xp * two_pq + yp * one_m_2qq;
pos[2] = (qq * yp - xp * pp) * f2;
vel[0] = vxp * one_m_2pp + vyp * two_pq;
vel[1] = vxp * two_pq + vyp * one_m_2qq;
vel[2] = (qq * vyp - vxp * pp) * f2;
}
/*
* Apply 3x3 rotation matrix to a 3-vector.
*/
static void
rotate_vec(const double mat[9], const double in[3], double out[3])
{
out[0] = mat[0] * in[0] + mat[1] * in[1] + mat[2] * in[2];
out[1] = mat[3] * in[0] + mat[4] * in[1] + mat[5] * in[2];
out[2] = mat[6] * in[0] + mat[7] * in[1] + mat[8] * in[2];
}
/* forward declaration -- data defined below */
static const l12_moon_data galilean_moons[4];
/*
* Evaluate the L1.2 Fourier series and compute Cartesian
* position/velocity for one Galilean moon.
*/
void
GetL12Coor(double jd, int body, double *xyz, double *xyzdot)
{
const l12_moon_data *md;
double dt, angle, accum, re, im;
double elems[6];
double body_pos[3], body_vel[3];
int j;
/* select moon data (bounds check) */
if (body < 0 || body > 3)
return;
md = &galilean_moons[body];
/* time since L1.2 epoch in days */
dt = jd - L12_EPOCH_JD;
/*
* 1. Semi-major axis: sum of cosine terms
* a = sum_j amp_j * cos(phi_j + nu_j * dt)
*/
accum = 0.0;
for (j = 0; j < md->n_a; j++) {
angle = md->fa[j].phi + md->fa[j].nu * dt;
accum += md->fa[j].amp * cos(angle);
}
elems[0] = accum;
/*
* 2. Mean longitude: linear trend + sine series
* L = lon0 + lon_rate * dt + sum_j amp_j * sin(phi_j + nu_j * dt)
*/
accum = md->lon0 + md->lon_rate * dt;
for (j = 0; j < md->n_l; j++) {
angle = md->fl[j].phi + md->fl[j].nu * dt;
accum += md->fl[j].amp * sin(angle);
}
accum = fmod(accum, TWO_PI);
if (accum < 0.0)
accum += TWO_PI;
elems[1] = accum;
/*
* 3. Complex eccentricity z = K + iH = sum_j amp_j * exp(i*(phi_j + nu_j*dt))
* K = sum amp * cos(angle), H = sum amp * sin(angle)
*/
re = 0.0;
im = 0.0;
for (j = 0; j < md->n_z; j++) {
angle = md->fz[j].phi + md->fz[j].nu * dt;
re += md->fz[j].amp * cos(angle);
im += md->fz[j].amp * sin(angle);
}
elems[2] = re;
elems[3] = im;
/*
* 4. Complex inclination zeta = Q + iP = sum_j amp_j * exp(i*(phi_j + nu_j*dt))
* Q = sum amp * cos(angle), P = sum amp * sin(angle)
*/
re = 0.0;
im = 0.0;
for (j = 0; j < md->n_zeta; j++) {
angle = md->fzeta[j].phi + md->fzeta[j].nu * dt;
re += md->fzeta[j].amp * cos(angle);
im += md->fzeta[j].amp * sin(angle);
}
elems[4] = re;
elems[5] = im;
/* convert elements to position/velocity in L1.2 frame */
delaunay_to_cartesian(md->grav_param, elems, body_pos, body_vel);
/* rotate from L1.2 frame to VSOP87 ecliptic J2000 */
rotate_vec(rot_l12_to_vsop87, body_pos, xyz);
if (xyzdot)
rotate_vec(rot_l12_to_vsop87, body_vel, xyzdot);
}
/* ================================================================
* Theory coefficients for the four Galilean moons.
*
* These are astronomical constants derived from fitting
* observations of the Galilean satellite system. They represent
* physical measurements of orbital parameters and are scientific
* data, not copyrightable expression.
*
* Source: L1.2 theory data files
* ftp://ftp.imcce.fr/pub/ephem/satel/galilean/L1/L1.2/
* ================================================================
*/
static const l12_moon_data galilean_moons[4] = {
/* ---- Io (J-I) ---- */
{
/* grav_param */ 0.2824894284338140e-06,
/* lon0 */ 0.1446213296021224e+01,
/* lon_rate */ 0.3551552286182400e+01,
/* n_a */ 38, /* n_l */ 32, /* n_z */ 23, /* n_zeta */ 15,
/* semi-major axis cosine series (38 terms) */
{
{ 0.0028210960212903, 0.00000000000000e+00, 0.00000000000000e+00},
{ 0.0000000762024588, 0.36392902322306e+01, 0.35644591656241e+01},
{ 0.0000000180900324, 0.99554707056522e+00, 0.71289183312483e+01},
{ 0.0000000172337652, 0.18196487820921e+01, 0.17822295777568e+01},
{ 0.0000000101726080, 0.28150559763861e+01, 0.89111478635073e+01},
{ 0.0000000094794086, 0.34760224933239e+01, 0.80200331112799e+01},
{ 0.0000000092196266, 0.46347004953370e+01, 0.10693377436209e+02},
{ 0.0000000058581604, 0.11586746335276e+01, 0.26733443704266e+01},
{-0.0000000036218148, 0.23173675289588e+01, 0.53466887181044e+01},
{ 0.0000000034892754, 0.17122470613669e+00, 0.12475607079684e+02},
{ 0.0000000030842852, 0.36170311370435e+01, 0.63501320826717e+01},
{ 0.0000000020794650, 0.19906655633153e+01, 0.14257836656755e+02},
{ 0.0000000013655244, 0.49369712857369e+01, 0.13584836518140e-01},
{ 0.0000000011682572, 0.57934065580556e+01, 0.13366721796637e+02},
{-0.0000000008031976, 0.66879731833041e+00, 0.16040066232595e+02},
{ 0.0000000007309510, 0.56300556878949e+01, 0.17822295806244e+02},
{ 0.0000000007014118, 0.43297377080515e+01, 0.71002044886497e+01},
{ 0.0000000006561624, 0.43188797534991e+01, 0.13034138433510e-01},
{ 0.0000000005753088, 0.54252179509841e+01, 0.95251981240076e+01},
{ 0.0000000004359548, 0.11670110887440e+01, 0.19604525331797e+02},
{ 0.0000000003711992, 0.14936154077537e+01, 0.12938928912340e-01},
{-0.0000000003412576, 0.26346374300664e+01, 0.15571117257960e-01},
{ 0.0000000003432980, 0.17994723387341e+01, 0.31750663461810e+01},
{ 0.0000000003228344, 0.29861854159944e+01, 0.21386754933987e+02},
{ 0.0000000003014418, 0.19871924348983e+00, 0.24675315510310e-01},
{ 0.0000000001707670, 0.50718778620273e+01, 0.35514255456604e+01},
{ 0.0000000001655832, 0.29783205832994e+01, 0.44555739317536e+01},
{ 0.0000000001612910, 0.48058392680935e+01, 0.23168984521460e+02},
{ 0.0000000001527992, 0.18275651107267e+01, 0.18713410599600e+02},
{ 0.0000000001523312, 0.46323297275220e+01, 0.44686092108182e+01},
{ 0.0000000001449720, 0.19079860214667e+01, 0.30506511533200e-02},
{ 0.0000000001188688, 0.53321680658912e+01, 0.70987549449082e+01},
{ 0.0000000001129258, 0.95031497804420e+00, 0.12700264165343e+02},
{ 0.0000000000986086, 0.34190944178580e+00, 0.24951214111224e+02},
{-0.0000000000877720, 0.36228267942948e+01, 0.17958145576535e+01},
{ 0.0000000000857194, 0.33682834215727e+01, 0.59736711266730e+01},
{-0.0000000000545492, 0.19473964103154e+01, 0.22929425718040e-01},
{ 0.0000000000326102, 0.24880420823571e+01, 0.25119610718870e-01}
},
/* mean longitude sine series (32 terms) */
{
{-0.0001925258348666, 0.49369589722645e+01, 0.13584836583050e-01},
{-0.0000970803596076, 0.43188796477322e+01, 0.13034138432430e-01},
{-0.0000898817416500, 0.19080016428617e+01, 0.30506486715800e-02},
{-0.0000553101050262, 0.14936156681569e+01, 0.12938928911550e-01},
{-0.0000503584426150, 0.36410196089987e+01, 0.35644591049605e+01},
{-0.0000444412770116, 0.18196478828985e+01, 0.17822295777568e+01},
{ 0.0000418078870490, 0.26346334480977e+01, 0.15571117221300e-01},
{ 0.0000372356597388, 0.21402440902650e+01, 0.14500977488900e-02},
{-0.0000234440533016, 0.19871945729267e+00, 0.24675315507400e-01},
{-0.0000160313164240, 0.28203470990931e+01, 0.95196190000000e-04},
{-0.0000119049755698, 0.99521552502799e+00, 0.71289183312483e+01},
{-0.0000109014269320, 0.11586742711973e+01, 0.26733443704266e+01},
{ 0.0000087217118104, 0.22995085327344e+01, 0.44456805185000e-03},
{ 0.0000082229455492, 0.84723690387904e+00, 0.54980078903000e-03},
{ 0.0000075365481720, 0.30644603245150e+01, 0.64826749624000e-03},
{-0.0000061452803962, 0.28150499448772e+01, 0.89111478635073e+01},
{-0.0000057575824778, 0.34760236756099e+01, 0.80200331112799e+01},
{-0.0000053196302672, 0.14952058549171e+01, 0.29001290992300e-02},
{-0.0000051181206936, 0.46347077042449e+01, 0.10693377436209e+02},
{-0.0000047817413326, 0.49236512419835e+01, 0.30554833877800e-02},
{ 0.0000045554015322, 0.19585097634352e+01, 0.22928625941210e-01},
{ 0.0000043204134698, 0.15842888614383e+01, 0.15677112478190e-01},
{ 0.0000037684098282, 0.23173652780077e+01, 0.53466887181044e+01},
{-0.0000031403738248, 0.22184076281042e+01, 0.25155489165510e-01},
{ 0.0000024336535428, 0.85320650238886e+00, 0.25426968834400e-02},
{-0.0000020289901692, 0.36168998565188e+01, 0.63501320826717e+01},
{ 0.0000018665438704, 0.48458061649481e+01, 0.13589674898130e-01},
{-0.0000018552431038, 0.17086811529922e+00, 0.12475607079684e+02},
{-0.0000016229875536, 0.62803206871082e+01, 0.60414171604000e-03},
{-0.0000013160987604, 0.14718125754925e+01, 0.14358460012320e-01},
{ 0.0000008070729808, 0.38735416148641e+00, 0.37658680379100e-02},
{ 0.0000002602397658, 0.14337589305551e+01, 0.45692429208000e-02}
},
/* complex eccentricity cos/sin series (23 terms) */
{
{ 0.0041510849668155, 0.40899396355450e+01, -0.12906864146660e-01},
{ 0.0006260521444113, 0.14461888986270e+01, 0.35515522949802e+01},
{ 0.0000352747346169, 0.21256287034578e+01, 0.12727416567000e-03},
{ 0.0000198194483636, 0.55835619926762e+01, 0.32065751140000e-04},
{ 0.0000146399842989, 0.44137212696837e+00, 0.26642533547700e-02},
{ 0.0000098749504021, 0.45076118781320e+00, -0.35773660260022e+01},
{-0.0000096819265753, 0.59097266550442e+01, 0.17693227079462e+01},
{-0.0000083063168209, 0.28751474873012e+00, 0.87820791951527e+00},
{ 0.0000059689735869, 0.50740752477871e+01, 0.71160118048918e+01},
{-0.0000052220588690, 0.27460731023666e+01, 0.67796100936000e-03},
{ 0.0000046538995236, 0.49143203339385e+01, -0.53595956184347e+01},
{ 0.0000045951340101, 0.42533513770304e+01, -0.44684808200578e+01},
{-0.0000037711061757, 0.54120093562773e+01, -0.17951364445825e+01},
{ 0.0000037405126681, 0.30946737347297e+01, -0.71418251640916e+01},
{ 0.0000022044764663, 0.54360702580001e+01, -0.26491700241240e-01},
{ 0.0000018698303790, 0.41124042914226e+01, -0.27985797954589e+01},
{-0.0000015410375360, 0.27141931505529e+01, 0.27731236679900e-02},
{ 0.0000013214613496, 0.12750177723530e+01, -0.89240547799787e+01},
{-0.0000012707585609, 0.51141075152507e+01, 0.37654227378982e+00},
{ 0.0000012193607962, 0.59977053365953e+01, -0.98566169956900e-02},
{-0.0000011886104747, 0.32658350285168e+01, 0.53337818460633e+01},
{ 0.0000008742035177, 0.23903528311144e+01, 0.25194921818800e-02},
{-0.0000007689215742, 0.38308837306225e+01, -0.27293225500100e-02}
},
/* complex inclination cos/sin series (15 terms) */
{
{ 0.0003142172466014, 0.27964219722923e+01, -0.23150960980000e-02},
{ 0.0000904169207946, 0.10477061879627e+01, -0.56920638196000e-03},
{ 0.0000175695395780, 0.24150809680215e+01, 0.00000000000000e+00},
{ 0.0000164452324013, 0.33368861773902e+01, -0.12491307197000e-03},
{ 0.0000055424829091, 0.59720202381027e+01, -0.30561164720000e-04},
{ 0.0000035856270353, 0.84898736841329e+00, -0.25244521900630e-01},
{ 0.0000024180760140, 0.55603770950923e+01, 0.29003681445800e-02},
{-0.0000008673084930, 0.28496686106299e+00, -0.14500593353200e-02},
{-0.0000003176227277, 0.53834633036029e+01, -0.23498632298700e-01},
{ 0.0000003152816608, 0.45569499027478e+01, 0.43504654304000e-02},
{ 0.0000002338676726, 0.17633292120047e+01, 0.14501339138600e-02},
{ 0.0000001754553689, 0.48429319984493e+01, -0.25688816532440e-01},
{ 0.0000001286319583, 0.57543347143871e+01, -0.25813660979740e-01},
{ 0.0000000967213304, 0.11503592426900e+01, -0.29001471397800e-02},
{ 0.0000000000692310, 0.40745966852008e+01, -0.32506757319070e-01}
}
},
/* ---- Europa (J-II) ---- */
{
/* grav_param */ 0.2824832743928930e-06,
/* lon0 */ -0.3735263437471362e+00,
/* lon_rate */ 0.1769322711123470e+01,
/* n_a */ 38, /* n_l */ 36, /* n_z */ 41, /* n_zeta */ 25,
/* semi-major axis cosine series (38 terms) */
{
{ 0.0044871037804314, 0.00000000000000e+00, 0.00000000000000e+00},
{ 0.0000004324367498, 0.18196456062910e+01, 0.17822295777568e+01},
{ 0.0000001603614750, 0.43002726529577e+01, 0.26733443704266e+01},
{-0.0000001019146786, 0.54589480865442e+01, 0.53466887181044e+01},
{ 0.0000000924734786, 0.56222139048906e+01, 0.89111478887838e+00},
{-0.0000000523665800, 0.36392846323417e+01, 0.35644591656241e+01},
{ 0.0000000511509000, 0.29783307371014e+01, 0.44555739317536e+01},
{-0.0000000311907780, 0.99466557754027e+00, 0.71289183312483e+01},
{-0.0000000272859938, 0.28144480309092e+01, 0.89111478635073e+01},
{ 0.0000000232225828, 0.62608434364366e+01, 0.27856729211550e+01},
{-0.0000000181310770, 0.43188692380649e+01, 0.13034138308860e-01},
{ 0.0000000174960544, 0.16563941638726e+01, 0.62378035398422e+01},
{-0.0000000122874072, 0.46421290370833e+01, 0.10693377254218e+02},
{-0.0000000095367130, 0.14936536615312e+01, 0.12938928820690e-01},
{-0.0000000084863836, 0.17146854643555e+00, 0.12475607079684e+02},
{ 0.0000000071939342, 0.49376739095661e+01, 0.13584833017030e-01},
{ 0.0000000069122354, 0.62488746138492e+01, 0.41785094280464e+01},
{ 0.0000000061377568, 0.33434976298081e+00, 0.80200331112799e+01},
{-0.0000000045343054, 0.19892156959655e+01, 0.14257836656755e+02},
{ 0.0000000044574684, 0.34597804303324e+00, 0.35357692227539e+01},
{ 0.0000000042350072, 0.62719655202169e+01, 0.13928364636651e+01},
{-0.0000000028783772, 0.38108811302610e+01, 0.16040066232595e+02},
{ 0.0000000024354662, 0.99587190880214e+00, 0.90414989297380e+00},
{ 0.0000000022532940, 0.52958965893939e+01, 0.98022627054664e+01},
{ 0.0000000021573570, 0.62379050559630e+01, 0.55713458670107e+01},
{-0.0000000016530062, 0.56456686036734e+01, 0.17822294694543e+02},
{ 0.0000000016464798, 0.26346435392424e+01, 0.15571117126760e-01},
{ 0.0000000011589838, 0.32732388195745e+01, 0.17691951440716e+01},
{-0.0000000010251826, 0.19079858535660e+01, 0.30506497838200e-02},
{-0.0000000010203510, 0.11692020351116e+01, 0.19604525194172e+02},
{ 0.0000000007614982, 0.16862812414995e+01, 0.35342961443230e+01},
{ 0.0000000007104494, 0.59112717191092e+01, 0.24092214574831e+01},
{-0.0000000006957184, 0.24879412197796e+01, 0.25119609730670e-01},
{-0.0000000005817914, 0.19872303312324e+00, 0.24675315511270e-01},
{-0.0000000003792178, 0.15765189821595e+01, 0.25244441830200e-01},
{ 0.0000000003397378, 0.58126953372535e+01, 0.25973067138760e-01},
{ 0.0000000003159492, 0.23545476741301e+01, 0.26068277099550e-01},
{ 0.0000000002538154, 0.19471441186087e+01, 0.22929424919760e-01}
},
/* mean longitude sine series (36 terms) */
{
{ 0.0008576433172936, 0.43188693178264e+01, 0.13034138308050e-01},
{ 0.0004549582875086, 0.14936531751079e+01, 0.12938928819620e-01},
{ 0.0003248939825174, 0.18196494533458e+01, 0.17822295777568e+01},
{-0.0003074250079334, 0.49377037005911e+01, 0.13584832867240e-01},
{ 0.0001982386144784, 0.19079869054760e+01, 0.30510121286900e-02},
{ 0.0001834063551804, 0.21402853388529e+01, 0.14500978933800e-02},
{-0.0001434383188452, 0.56222140366630e+01, 0.89111478887838e+00},
{-0.0000771939140944, 0.43002724372350e+01, 0.26733443704266e+01},
{-0.0000632289777196, 0.26346392822098e+01, 0.15571117084700e-01},
{ 0.0000446766477388, 0.54589448561143e+01, 0.53466887181044e+01},
{ 0.0000436574731410, 0.36392908617709e+01, 0.35644591656241e+01},
{ 0.0000349172750296, 0.28289867162553e+01, 0.29885749150000e-04},
{-0.0000325709094646, 0.53721409780230e+01, 0.12495233774000e-03},
{ 0.0000205826473860, 0.15258464215508e+01, 0.29001315522200e-02},
{-0.0000192706087556, 0.29783311531879e+01, 0.44555739317536e+01},
{ 0.0000168028316254, 0.24879414119403e+01, 0.25119609725650e-01},
{-0.0000141628733606, 0.29183576504413e+01, 0.64930403718000e-03},
{ 0.0000140713155600, 0.19872319369353e+00, 0.24675315510310e-01},
{ 0.0000131946915760, 0.99584744364935e+00, 0.71289183312483e+01},
{ 0.0000106598617620, 0.53356907396678e+01, 0.30233219231900e-02},
{-0.0000104011727738, 0.62608296198866e+01, 0.27856729211550e+01},
{ 0.0000100746080234, 0.44288900030073e+01, 0.55297871931000e-03},
{ 0.0000097414019416, 0.27312462188296e+01, 0.89111510230745e+01},
{-0.0000094651366640, 0.25010358163865e+01, 0.93478322470000e-04},
{ 0.0000091108073324, 0.15765182522628e+01, 0.25244441682120e-01},
{-0.0000087720567668, 0.15376962386886e+01, 0.15676393315070e-01},
{-0.0000078429703340, 0.58128473756772e+01, 0.25973069246350e-01},
{-0.0000075566039418, 0.30586251688920e+01, 0.43252872559000e-03},
{-0.0000066580990752, 0.19591270593390e+01, 0.22928567412490e-01},
{-0.0000065854142774, 0.18617673337640e+01, 0.26093058384670e-01},
{-0.0000058131135230, 0.16563893807978e+01, 0.62378035398422e+01},
{ 0.0000055720865276, 0.39565695752204e+01, 0.25481216339900e-02},
{-0.0000048198508906, 0.62720230965345e+01, 0.13928364605775e+01},
{ 0.0000042728431266, 0.46220912946918e+01, 0.10693377982182e+02},
{ 0.0000042175545304, 0.13509343368359e+01, 0.30164435787800e-02},
{ 0.0000037707624520, 0.51034507119889e+01, 0.25219658202250e-01}
},
/* complex eccentricity cos/sin series (41 terms) */
{
{-0.0093589104136341, 0.40899396509039e+01, -0.12906864146660e-01},
{ 0.0002988994545555, 0.59097265185595e+01, 0.17693227079462e+01},
{ 0.0002139036390350, 0.21256289300016e+01, 0.12727418407000e-03},
{ 0.0001980963564781, 0.27435168292650e+01, 0.67797343009000e-03},
{ 0.0001210388158965, 0.55839943711203e+01, 0.32056614900000e-04},
{ 0.0000837042048393, 0.16094538368039e+01, -0.90402165808846e+00},
{ 0.0000823525166369, 0.14461887708689e+01, 0.35515522949802e+01},
{-0.0000315906532820, 0.28751224400811e+00, 0.87820791951527e+00},
{-0.0000294503681314, 0.45078002968967e+00, -0.35773660260022e+01},
{-0.0000278946698536, 0.22704374310903e+01, -0.17951364497113e+01},
{ 0.0000144958688621, 0.29313956641719e+01, -0.26862512422390e+01},
{ 0.0000139052321679, 0.60542576187622e+01, -0.25941002404500e-01},
{ 0.0000108374431350, 0.59320761116863e+01, -0.10163502160128e+01},
{-0.0000082175838585, 0.49144730088838e+01, -0.53595956184347e+01},
{ 0.0000073925894084, 0.25962855881215e+01, -0.25845792927870e-01},
{ 0.0000062618381566, 0.62252936384007e+01, -0.71418248393794e+01},
{-0.0000051968296512, 0.54353355159239e+01, -0.26491696725040e-01},
{-0.0000043507065743, 0.51150292346242e+01, 0.37654221060604e+00},
{ 0.0000042081682285, 0.31202613836361e+01, 0.44427230757158e+01},
{ 0.0000041298266970, 0.42533371370636e+01, -0.44684808200578e+01},
{-0.0000036991221930, 0.52487564172390e+01, 0.26604375002057e+01},
{-0.0000027357551003, 0.12734806685602e+01, -0.89240546532297e+01},
{-0.0000026854901206, 0.75596663258784e+00, 0.15806953180460e-01},
{ 0.0000023074479953, 0.19438998534712e+01, 0.71160114825227e+01},
{ 0.0000020163445050, 0.58484195254467e+01, -0.24091843401454e+01},
{-0.0000018506530067, 0.26838225102582e+01, 0.68236609075000e-03},
{ 0.0000018159137522, 0.26048690461733e+01, 0.62248966818788e+01},
{-0.0000017894118824, 0.57385537790777e+01, -0.10706284328884e+02},
{ 0.0000016518864520, 0.32658492478888e+01, 0.53337818460633e+01},
{-0.0000015660692561, 0.61789350505156e+01, -0.11453588847960e-01},
{ 0.0000014426949422, 0.60014075911383e+01, -0.17664769149811e+01},
{ 0.0000013196935928, 0.55753025652974e+01, -0.62507103665413e+01},
{-0.0000011726743714, 0.50242932747650e+01, -0.14351210704140e-01},
{-0.0000009550285338, 0.28409403047363e+01, 0.14257595044900e-02},
{-0.0000007569857746, 0.38098760906143e+01, -0.27271627793800e-02},
{-0.0000007495662748, 0.29896372346394e+01, 0.20097553243900e-02},
{ 0.0000007091149133, 0.27139331814919e+01, -0.19924932783300e-02},
{ 0.0000005646670312, 0.21683602575236e+01, -0.12871803232940e-01},
{-0.0000002004455524, 0.12893849410519e+01, -0.32724923477800e-02},
{-0.0000001623489363, 0.24189454629613e+00, 0.44609337678800e-02},
{ 0.0000001058862562, 0.45356953407129e+01, 0.39269908172100e-02}
},
/* complex inclination cos/sin series (25 terms) */
{
{ 0.0040404917832303, 0.10477063169425e+01, -0.56920640540000e-03},
{ 0.0002200421034564, 0.33368857864364e+01, -0.12491307307000e-03},
{ 0.0001662544744719, 0.24134862374711e+01, 0.00000000000000e+00},
{ 0.0000590282470983, 0.59719930968366e+01, -0.30561602250000e-04},
{-0.0000105030331400, 0.27964978379152e+01, -0.23150966123800e-02},
{-0.0000102943248250, 0.84898796322150e+00, -0.25244521901650e-01},
{ 0.0000072600013020, 0.55603730312676e+01, 0.29003676713100e-02},
{ 0.0000018391258758, 0.28480515491153e+00, -0.14500579196900e-02},
{ 0.0000014880605763, 0.48429974929766e+01, -0.25688815138710e-01},
{-0.0000008828196274, 0.65011185407635e+00, 0.34696170683100e-02},
{ 0.0000008714042768, 0.17639430319108e+01, 0.14501352157600e-02},
{ 0.0000008536188044, 0.45568506666427e+01, 0.43504641410100e-02},
{ 0.0000006846214331, 0.57542117253981e+01, -0.25813768702630e-01},
{ 0.0000004471826348, 0.53834694321520e+01, -0.23498632366370e-01},
{ 0.0000003034392168, 0.22078201315180e+01, -0.25783170906020e-01},
{ 0.0000001799083735, 0.31858868501531e+01, 0.88086056517000e-03},
{-0.0000001792048645, 0.51949494917342e+01, -0.20193236931900e-02},
{-0.0000001098546626, 0.59286821904995e+01, 0.49197316579700e-02},
{-0.0000001083128732, 0.45808061408794e+01, -0.59959459406000e-03},
{ 0.0000001062153531, 0.38387102863271e+01, -0.53795085847000e-03},
{ 0.0000000768496749, 0.35553768729770e+01, 0.58005587812700e-02},
{-0.0000000692273841, 0.46440611341931e+01, 0.30253219029200e-02},
{ 0.0000000676969224, 0.13621319661456e+00, -0.44430413602000e-03},
{-0.0000000621559952, 0.30093497179950e+01, -0.13603287200690e-01},
{ 0.0000000000608298, 0.40529569532600e+01, -0.32510869900940e-01}
}
},
/* ---- Ganymede (J-III) ---- */
{
/* grav_param */ 0.2824981841847230e-06,
/* lon0 */ 0.2874089391143348e+00,
/* lon_rate */ 0.8782079235893280e+00,
/* n_a */ 38, /* n_l */ 31, /* n_z */ 50, /* n_zeta */ 18,
/* semi-major axis cosine series (38 terms) */
{
{ 0.0071566594572575, 0.00000000000000e+00, 0.00000000000000e+00},
{ 0.0000013930299110, 0.11586745884981e+01, 0.26733443704266e+01},
{ 0.0000006449829346, 0.56222145702102e+01, 0.89111478887838e+00},
{ 0.0000002298059520, 0.12995924044108e+01, 0.10034433456729e+01},
{-0.0000001221434370, 0.49612436330515e+01, 0.17822295777568e+01},
{ 0.0000001095798176, 0.19486708778350e+01, 0.15051650461529e+01},
{ 0.0000000701435616, 0.64978508114196e+00, 0.50172166963138e+00},
{ 0.0000000547868566, 0.25992050672074e+01, 0.20068866945508e+01},
{-0.0000000394635858, 0.23173535605652e+01, 0.53466887181044e+01},
{-0.0000000363221428, 0.36393008632056e+01, 0.35644591656241e+01},
{ 0.0000000290949364, 0.20123392230605e+01, 0.17535157350384e+01},
{ 0.0000000281244968, 0.32490010762048e+01, 0.25086083721948e+01},
{-0.0000000207924698, 0.29783308899923e+01, 0.44555739317536e+01},
{ 0.0000000146896774, 0.38988244013504e+01, 0.30103300418262e+01},
{-0.0000000119930042, 0.16563968316083e+01, 0.62378035398422e+01},
{ 0.0000000112067460, 0.43188665692819e+01, 0.13034138285340e-01},
{-0.0000000109535132, 0.49372826282154e+01, 0.13584834937940e-01},
{ 0.0000000099867772, 0.96700720263958e+00, 0.62699633776977e+00},
{ 0.0000000077668260, 0.45486373016444e+01, 0.35120517182683e+01},
{ 0.0000000074143972, 0.16140449852661e+00, 0.12531361661566e+00},
{ 0.0000000066346638, 0.33441073536010e+00, 0.80200331112799e+01},
{ 0.0000000057842684, 0.14936630646671e+01, 0.12938928799370e-01},
{-0.0000000055768352, 0.44651777597613e+01, 0.11287386144710e+01},
{-0.0000000049395106, 0.61563894598809e+01, 0.17520662093793e+01},
{ 0.0000000041439704, 0.51984558307998e+01, 0.40137734147421e+01},
{-0.0000000040765630, 0.99543742426922e+00, 0.71289183312483e+01},
{-0.0000000036862062, 0.46386836178626e+01, 0.10693377254218e+02},
{ 0.0000000033617538, 0.37493658441448e+01, 0.87808669180168e+00},
{ 0.0000000033348284, 0.22668196818990e+01, 0.16304394485673e+01},
{-0.0000000025754698, 0.33293196902303e+00, 0.17952648120307e+01},
{ 0.0000000024363084, 0.19604838407749e+01, 0.11232854513197e+00},
{ 0.0000000022265432, 0.58482745704418e+01, 0.45154950951905e+01},
{ 0.0000000020032676, 0.29166648062069e+01, 0.21321610765333e+01},
{-0.0000000018115706, 0.99782757414001e+00, 0.90414978368384e+00},
{ 0.0000000014535006, 0.18748212041600e+01, 0.89112137093506e+01},
{-0.0000000006819260, 0.19871670124324e+00, 0.24675315493830e-01},
{ 0.0000000004433776, 0.24880003003965e+01, 0.25119610196650e-01},
{-0.0000000002836658, 0.58126277034761e+01, 0.25973068607520e-01}
},
/* mean longitude sine series (31 terms) */
{
{ 0.0002310797886226, 0.21402987195942e+01, 0.14500978438400e-02},
{-0.0001828635964118, 0.43188672736968e+01, 0.13034138282630e-01},
{ 0.0001512378778204, 0.49373102372298e+01, 0.13584834812520e-01},
{-0.0001163720969778, 0.43002659861490e+01, 0.26733443704266e+01},
{-0.0000955478069846, 0.14936612842567e+01, 0.12938928798570e-01},
{ 0.0000815246854464, 0.56222137132535e+01, 0.89111478887838e+00},
{-0.0000801219679602, 0.12995922951532e+01, 0.10034433456729e+01},
{-0.0000607017260182, 0.64978769669238e+00, 0.50172167043264e+00},
{ 0.0000543922473002, 0.27927547440639e+01, 0.29880873700000e-04},
{-0.0000489253646474, 0.53711728089803e+01, 0.12495278292000e-03},
{-0.0000427574981536, 0.18196513407448e+01, 0.17822295777568e+01},
{-0.0000307360417826, 0.19498372703786e+01, 0.15051650064903e+01},
{-0.0000169767346458, 0.19078637281659e+01, 0.30507678226700e-02},
{ 0.0000154725890508, 0.56912713028984e+01, 0.65164073556000e-03},
{-0.0000145268863648, 0.18863875475387e+00, 0.12530827181195e+00},
{-0.0000135654458738, 0.27930238268852e+01, 0.55663681407000e-03},
{-0.0000134648621904, 0.25991972928128e+01, 0.20068866945508e+01},
{ 0.0000095524017320, 0.23173520454449e+01, 0.53466887181044e+01},
{ 0.0000087955125170, 0.36393024031096e+01, 0.35644591656241e+01},
{ 0.0000075462003630, 0.53560617584395e+01, 0.92426977490000e-04},
{-0.0000071146195958, 0.20120561622463e+01, 0.17535157644008e+01},
{ 0.0000064153141218, 0.15526366820734e+01, 0.29001309732400e-02},
{-0.0000063221625942, 0.32490122452649e+01, 0.25086083721948e+01},
{-0.0000056564973024, 0.24862139082596e+01, 0.44834622386000e-03},
{ 0.0000052570245720, 0.19871532348033e+00, 0.24675315501580e-01},
{ 0.0000047020767994, 0.29783317790630e+01, 0.44555739317536e+01},
{-0.0000047004229470, 0.96617050453708e+00, 0.62699712737505e+00},
{-0.0000046565198820, 0.36125113449716e+01, 0.43633231340000e-03},
{-0.0000042349322008, 0.19604744669606e+01, 0.11232854282257e+00},
{-0.0000038755741918, 0.22619624763183e+01, 0.25146663939730e-01},
{-0.0000032577733688, 0.56861827246039e+01, 0.17074576501600e-02}
},
/* complex eccentricity cos/sin series (50 terms) */
{
{ 0.0014289811307319, 0.21256295942739e+01, 0.12727413029000e-03},
{ 0.0007710931226760, 0.55836330003496e+01, 0.32064341100000e-04},
{ 0.0005925911780766, 0.40899396636448e+01, -0.12906864146660e-01},
{ 0.0002045597496146, 0.52713683670372e+01, -0.12523544076106e+00},
{ 0.0001785118648258, 0.28743156721063e+00, 0.87820792442520e+00},
{ 0.0001131999784893, 0.14462127277818e+01, 0.35515522949802e+01},
{-0.0000658778169210, 0.22702423990985e+01, -0.17951364394537e+01},
{ 0.0000497058888328, 0.59096792204858e+01, 0.17693227129285e+01},
{-0.0000316384926978, 0.16093054939404e+01, -0.90402165028424e+00},
{ 0.0000287801237327, 0.46217321268757e+01, -0.62695712341840e+00},
{-0.0000181744317896, 0.59210641379360e+01, 0.37648623991673e+00},
{ 0.0000105558175161, 0.39720191398746e+01, -0.11286788041058e+01},
{-0.0000070808673396, 0.60542548894164e+01, -0.25941002415210e-01},
{-0.0000070804404020, 0.27978433776854e+01, 0.67774258703000e-03},
{-0.0000061046181888, 0.14151685760988e+01, -0.87530769416913e+00},
{-0.0000057610853129, 0.42530537622646e+01, -0.44684807882788e+01},
{-0.0000057310334964, 0.29311803223072e+01, -0.26862512192699e+01},
{ 0.0000048299146941, 0.27138294508149e+01, 0.27731329671900e-02},
{ 0.0000046610005483, 0.33222980229554e+01, -0.16304004832039e+01},
{-0.0000038142769361, 0.25962943627643e+01, -0.25845792955510e-01},
{ 0.0000034982417330, 0.15866568011217e+01, 0.18816512920593e+01},
{-0.0000030091617315, 0.35921173988567e+01, -0.35773660056343e+01},
{-0.0000024732926446, 0.53461730094807e+01, 0.25122576835111e+00},
{ 0.0000024416432533, 0.47049477027963e+01, -0.25049613834712e+00},
{ 0.0000024171568015, 0.34508032389167e+01, 0.00000000000000e+00},
{ 0.0000023143850535, 0.55385759257808e+01, 0.28683339028800e-02},
{ 0.0000022651772374, 0.55608006706192e+01, 0.14501892967800e-02},
{ 0.0000022247695560, 0.26725424635341e+01, -0.21321221654766e+01},
{ 0.0000020947921969, 0.22350374116258e+01, 0.23833730192673e+01},
{-0.0000014042712722, 0.93718044411525e+00, 0.13799296041822e+01},
{ 0.0000011932531874, 0.28861941414418e+01, 0.28850946416201e+01},
{-0.0000011180389240, 0.49139919849718e+01, -0.53595955727170e+01},
{ 0.0000011076384510, 0.20227538540345e+01, -0.26338438454332e+01},
{-0.0000010371714944, 0.40722739402948e+00, -0.87385759089274e+00},
{-0.0000008993128501, 0.30942691883530e+01, -0.71418251640916e+01},
{ 0.0000007268381420, 0.54334774230433e+01, -0.26491687896550e-01},
{-0.0000007178049665, 0.52487423493616e+01, 0.26604375002057e+01},
{ 0.0000006908412319, 0.40596134184175e+01, -0.75221793556997e+00},
{-0.0000006784151570, 0.38846818226669e+01, 0.42496535534400e-02},
{ 0.0000006772314920, 0.23013479896873e+01, 0.26317235358158e+01},
{ 0.0000006659820028, 0.35359530295550e+01, 0.33868163258510e+01},
{-0.0000006339665249, 0.39268665697903e+01, 0.44426670658559e+01},
{-0.0000006286307601, 0.19440608894162e+01, 0.71160114019304e+01},
{-0.0000006128705113, 0.25027415074658e+01, 0.62249001971556e+01},
{ 0.0000005660807396, 0.13729316457251e+01, -0.31355655165873e+01},
{-0.0000005206551413, 0.55749300982469e+01, -0.62507103665413e+01},
{-0.0000004718481418, 0.45366605084874e+01, 0.16786677353000e-03},
{-0.0000004583970422, 0.19351070248496e+01, -0.98151695574500e+01},
{-0.0000004577854173, 0.62350780976534e+01, 0.17563373058434e+01},
{ 0.0000003466029660, 0.75412427489767e+00, 0.15807097495700e-01}
},
/* complex inclination cos/sin series (18 terms) */
{
{ 0.0015932721570848, 0.33368862796665e+01, -0.12491307058000e-03},
{ 0.0008533093128905, 0.24133881688166e+01, 0.00000000000000e+00},
{ 0.0003513347911037, 0.59720789850127e+01, -0.30561017710000e-04},
{-0.0001441929255483, 0.10477061764435e+01, -0.56920632124000e-03},
{ 0.0000157303527750, 0.55604041197704e+01, 0.29003665011200e-02},
{ 0.0000025161319881, 0.28477653709685e+00, -0.14500554486800e-02},
{ 0.0000020438305183, 0.17652628559998e+01, 0.14501383926500e-02},
{ 0.0000017939612784, 0.45568977341583e+01, 0.43504621590400e-02},
{ 0.0000013614276895, 0.84898872627945e+00, -0.25244521900630e-01},
{-0.0000008996109017, 0.46441156003340e+01, 0.30253214588300e-02},
{-0.0000008702078430, 0.27972000093551e+01, -0.23150965645100e-02},
{-0.0000004371144064, 0.48429530385679e+01, -0.25688816011500e-01},
{-0.0000002174259374, 0.57543785603741e+01, -0.25813642993310e-01},
{-0.0000001926397869, 0.20118539705648e+01, 0.29330596864500e-02},
{ 0.0000001589279656, 0.35554727018503e+01, 0.58005577768400e-02},
{-0.0000001432228753, 0.11966574544002e+01, -0.15750124983800e-02},
{-0.0000000926213408, 0.22052538606469e+01, -0.25782797426020e-01},
{ 0.0000000000106902, 0.45764213311755e+01, -0.32611614716800e-01}
}
},
/* ---- Callisto (J-IV) ---- */
{
/* grav_param */ 0.2824921448899090e-06,
/* lon0 */ -0.3620341291375704e+00,
/* lon_rate */ 0.3764862334338280e+00,
/* n_a */ 22, /* n_l */ 19, /* n_z */ 46, /* n_zeta */ 18,
/* semi-major axis cosine series (22 terms) */
{
{ 0.0125879701715314, 0.00000000000000e+00, 0.00000000000000e+00},
{ 0.0000035952049470, 0.64965776007116e+00, 0.50172168165034e+00},
{ 0.0000027580210652, 0.18084235781510e+01, 0.31750660413359e+01},
{ 0.0000012874896172, 0.62718908285025e+01, 0.13928364698403e+01},
{-0.0000004173729106, 0.12990650292663e+01, 0.10034433697108e+01},
{ 0.0000002790757718, 0.71428870045577e+00, 0.75007225869130e+00},
{-0.0000001998252258, 0.19489881012004e+01, 0.15051650461529e+01},
{-0.0000001001149838, 0.25987168731338e+01, 0.20068867266014e+01},
{-0.0000000513967092, 0.32484798706247e+01, 0.25086084022422e+01},
{-0.0000000475687992, 0.48635521917696e+01, 0.74862216593606e+00},
{ 0.0000000348242240, 0.15082713497295e+00, 0.37645917070525e+00},
{ 0.0000000283840630, 0.51672973364888e+01, 0.12530678073049e+00},
{-0.0000000263234638, 0.33499822210495e+01, 0.30103491232578e+01},
{ 0.0000000239106346, 0.43573519442736e+01, 0.62698238798737e+00},
{ 0.0000000219977422, 0.15075404808879e+01, 0.27986109086768e+01},
{-0.0000000171144478, 0.62607361864777e+01, 0.27856729335053e+01},
{-0.0000000141956834, 0.45481077718910e+01, 0.35120517575303e+01},
{-0.0000000120003630, 0.18583887479127e+01, 0.11287042579152e+01},
{ 0.0000000108418904, 0.54873138800427e+01, 0.67395238593127e+01},
{ 0.0000000108218254, 0.59772630516669e+01, 0.10163811590412e+01},
{ 0.0000000002477642, 0.56894071957878e+01, 0.65165021654000e-03},
{-0.0000000001874576, 0.28598333265121e+01, 0.55639542661000e-03}
},
/* mean longitude sine series (19 terms) */
{
{ 0.0005586040123824, 0.21404207189815e+01, 0.14500979323100e-02},
{-0.0003805813868176, 0.27358844897853e+01, 0.29729650620000e-04},
{ 0.0002205152863262, 0.64979652596400e+00, 0.50172167243580e+00},
{ 0.0001877895151158, 0.18084787604005e+01, 0.31750660413359e+01},
{ 0.0000766916975242, 0.62720114319755e+01, 0.13928364636651e+01},
{ 0.0000747056855106, 0.12995916202344e+01, 0.10034433456729e+01},
{-0.0000388323297366, 0.71289234751879e+00, 0.75007236972328e+00},
{ 0.0000335036484314, 0.53712641184981e+01, 0.12494011725000e-03},
{ 0.0000293032677938, 0.19493939340593e+01, 0.15051650209131e+01},
{ 0.0000185940935472, 0.14630998372377e+01, 0.29001339405200e-02},
{-0.0000170438022886, 0.56893382353856e+01, 0.65165044781000e-03},
{ 0.0000151393833114, 0.28749516044614e+01, 0.55646069067000e-03},
{-0.0000148825637256, 0.33321074618840e+01, 0.12530790075011e+00},
{ 0.0000129927896682, 0.25991973549465e+01, 0.20068866945508e+01},
{-0.0000116117398772, 0.56192268627131e+01, 0.93166256720000e-04},
{ 0.0000066211702894, 0.48564958193206e+01, 0.74862286166569e+00},
{ 0.0000065387442486, 0.35580120361824e+01, 0.16550513741900e-02},
{ 0.0000061580798140, 0.32490037889701e+01, 0.25086083721948e+01},
{ 0.0000046797140778, 0.96612169919707e+00, 0.62699716616712e+00}
},
/* complex eccentricity cos/sin series (46 terms) */
{
{ 0.0073755808467977, 0.55836071576084e+01, 0.32065099140000e-04},
{ 0.0002065924169942, 0.59209831565786e+01, 0.37648624194703e+00},
{ 0.0001589869764021, 0.28744006242623e+00, 0.87820792442520e+00},
{-0.0001561131605348, 0.21257397865089e+01, 0.12727441285000e-03},
{ 0.0001486043380971, 0.14462134301023e+01, 0.35515522949802e+01},
{ 0.0000635073108731, 0.59096803285954e+01, 0.17693227129285e+01},
{ 0.0000599351698525, 0.41125517584798e+01, -0.27985797954589e+01},
{ 0.0000540660842731, 0.55390350845569e+01, 0.28683408228300e-02},
{-0.0000489596900866, 0.46218149483338e+01, -0.62695712529519e+00},
{ 0.0000333682283528, 0.52066975238880e+01, -0.37358601734497e+00},
{ 0.0000295832427279, 0.59322697896516e+01, -0.10163502275209e+01},
{ 0.0000292325461337, 0.52707623402008e+01, -0.12523542448602e+00},
{ 0.0000197588369441, 0.33317768022759e+01, 0.00000000000000e+00},
{-0.0000183551029746, 0.39720443249757e+01, -0.11286788041058e+01},
{ 0.0000090411191759, 0.55606719963947e+01, 0.14501837490800e-02},
{-0.0000081987970452, 0.33223313720086e+01, -0.16304004832039e+01},
{-0.0000060406575087, 0.13970265485562e+01, 0.43191832032300e-02},
{ 0.0000056895636122, 0.41990956668120e+01, -0.37213592656720e+00},
{-0.0000040434854859, 0.47008406172134e+01, -0.25049602889288e+00},
{-0.0000039403527376, 0.26725832255243e+01, -0.21321221654766e+01},
{ 0.0000036901291978, 0.35207772267753e+00, 0.11265585018525e+01},
{-0.0000028551622596, 0.55601265129356e+01, -0.31584886140000e-04},
{-0.0000026588026505, 0.25969882784477e+00, 0.14182025553300e-02},
{-0.0000019711212463, 0.20228019680496e+01, -0.26338438454332e+01},
{ 0.0000019322089806, 0.51418595457408e+01, 0.25123117908847e+00},
{-0.0000018673159813, 0.93674892088247e+00, 0.13799296163047e+01},
{ 0.0000016838424078, 0.60796033426941e+01, 0.75294520843775e+00},
{-0.0000016695689644, 0.15867810488422e+01, 0.18816512864243e+01},
{ 0.0000016317841395, 0.45789534393209e+01, 0.14822429153000e-02},
{-0.0000016159095087, 0.30157253757329e+00, -0.14180284447900e-02},
{-0.0000014034621874, 0.59433512039442e+01, -0.24091866865037e+01},
{-0.0000012029942283, 0.27137754880270e+01, 0.27731373092900e-02},
{-0.0000011758260607, 0.40581098970285e+01, -0.75221789504525e+00},
{-0.0000010798624964, 0.22364861319452e+01, 0.23833729650229e+01},
{-0.0000010108880552, 0.13729872033949e+01, -0.31355655165873e+01},
{-0.0000008876681807, 0.50534107615010e+01, -0.50169281575682e+00},
{ 0.0000008869382117, 0.50147420853991e+01, 0.68353864231000e-03},
{-0.0000008194699011, 0.62190878357566e+01, -0.37503615934219e+00},
{ 0.0000007093782158, 0.44118312641559e+01, -0.24221246131672e+01},
{ 0.0000006728737059, 0.31910016062920e+01, -0.37068584881726e+00},
{ 0.0000006297345982, 0.13595719733984e+01, 0.11251084135564e+01},
{ 0.0000006128899757, 0.51402161299290e+01, 0.71160095483087e+01},
{-0.0000005580987049, 0.34117733109010e+01, -0.12539396666771e+01},
{ 0.0000005321318002, 0.35377046967957e+01, 0.57685340271300e-02},
{-0.0000004739086661, 0.21645217929478e+01, 0.58845474482000e-03},
{ 0.0000004518928658, 0.44963664372727e+01, 0.29023325111200e-02}
},
/* complex inclination cos/sin series (18 terms) */
{
{ 0.0038422977898495, 0.24133922085557e+01, 0.00000000000000e+00},
{ 0.0022453891791894, 0.59721736773277e+01, -0.30561255250000e-04},
{-0.0002604479450559, 0.33368746306409e+01, -0.12491309972000e-03},
{ 0.0000332112143230, 0.55604137742337e+01, 0.29003768850700e-02},
{ 0.0000049727136261, 0.28488229706820e+00, -0.14500571761900e-02},
{-0.0000049416729114, 0.10476908456459e+01, -0.56920298857000e-03},
{ 0.0000043945193428, 0.17684273746003e+01, 0.14501344524700e-02},
{ 0.0000037630501589, 0.45567680530533e+01, 0.43504645407000e-02},
{-0.0000030823418750, 0.20094360655956e+01, 0.29313051376700e-02},
{ 0.0000004719790711, 0.18055417618741e+01, 0.14195445432000e-02},
{-0.0000004637177865, 0.38277528822158e+01, -0.14808731001600e-02},
{ 0.0000003497224175, 0.46444360330108e+01, 0.30253130162300e-02},
{-0.0000003467132626, 0.10120757927163e+01, 0.43816126822900e-02},
{ 0.0000003324412570, 0.35549391686606e+01, 0.58005379032100e-02},
{ 0.0000001945374351, 0.61251687150860e+01, 0.28808264872800e-02},
{ 0.0000001727743329, 0.11900773236610e+01, -0.29001068524700e-02},
{-0.0000001485176585, 0.62335834706368e+01, 0.14807679092700e-02},
{ 0.0000000000666922, 0.40616225761771e+01, -0.32724923474890e-01}
}
}
};

73
src/l12.h Normal file
View File

@ -0,0 +1,73 @@
/************************************************************************
L1.2 Galilean satellite theory -- Lainey, Duriez & Vienne
Clean-room implementation for pg_orbit.
The L1.2 theory provides positions and velocities of Jupiter's four
Galilean moons (Io, Europa, Ganymede, Callisto) relative to Jupiter's
center, in the VSOP87 ecliptic J2000 reference frame.
Reference:
Lainey V., Duriez L., Vienne A.
"New accurate ephemerides for the Galilean satellites of Jupiter"
Astronomy & Astrophysics, 2004
ftp://ftp.imcce.fr/pub/ephem/satel/galilean/L1/L1.2/
The theory coefficients are astronomical constants derived from
fitting observations of the Galilean system. They represent
physical measurements and are not copyrightable.
Copyright (c) 2026 Ryan Malloy <ryan@supported.systems>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Thread-safe: all functions are reentrant with no static mutable state.
****************************************************************/
#ifndef L12_H
#define L12_H
#ifdef __cplusplus
extern "C" {
#endif
#define L12_IO 0
#define L12_EUROPA 1
#define L12_GANYMEDE 2
#define L12_CALLISTO 3
/*
* Compute the position (and optionally velocity) of a Galilean moon
* using the L1.2 semi-analytic theory.
*
* jd: Julian date in Terrestrial Time (TT).
* body: Moon index: 0=Io, 1=Europa, 2=Ganymede, 3=Callisto.
* xyz: Output position in AU, VSOP87 ecliptic J2000 frame,
* relative to Jupiter's center. Must point to double[3].
* xyzdot: Output velocity in AU/day, same frame. May be NULL
* to skip velocity computation.
*/
void GetL12Coor(double jd, int body, double *xyz, double *xyzdot);
#ifdef __cplusplus
}
#endif
#endif /* L12_H */

425
src/marssat.c Normal file
View File

@ -0,0 +1,425 @@
/************************************************************************
The Ephemerides of the Martian satellites
(adjustement from 1877 to 2005, Version 1.0)
by Valery Lainey can be obtained from Valery Lainey:
V.Lainey (Lainey@oma.be)
ROB- 3, Avenue Circulaire, B-1180 Bruxelles (Belgium)
IMCCE - 77, Avenue Denfert-Rochereau 75014 Paris (France)
-----------------------------------------------------------------------
I (Johannes Gajdosik) have just taken Valery Laineys Fortran code,
MarsSatV1-0.f, which he kindly supplied, and rearranged it into
this piece of software.
I can neither allow nor forbid the usage of Valery Laineys
Ephemerides of the Martian satellites.
The copyright notice below covers not the work of Valery Lainey
but just my work, that is the compilation of Valery Laineys
Ephemerides of the Martian satellites into the software supplied in this file.
Copyright (c) 2006 Johannes Gajdosik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Derived from Stellarium's MARSSAT implementation.
Modified for pg_orbit: removed all static mutable state for thread safety
(PostgreSQL PARALLEL SAFE). The original used static caching arrays and
CalcInterpolatedElements for performance; this version computes fresh on
each call which is acceptable for SQL query workloads.
1) do not calculate constant terms at runtime but beforehand
2) unite terms with the same frequencies
****************************************************************/
#include "marssat.h"
#include "elliptic_to_rectangular.h"
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
struct MarsSatTerm {
double phase, frequency, amplitude;
};
struct MarsSatTermList {
const struct MarsSatTerm *terms;
int size;
};
struct MarsSatBody {
double mu, l, acc;
double constants[6];
const struct MarsSatTermList lists[4];
};
static const struct MarsSatTerm mars_sat_phobos_0[16] = {
{ 5.013490350126586e+00, 2.715567382195733e+01, 4.537539999999999e-09},
{ 6.839910590780000e-01, 1.969446151585829e+01, 7.312000000000000e-10},
{ 4.514031245592586e+00, 4.073350897245015e+01, 7.785599999999993e-10},
{ 5.531351678889586e+00, 1.357783661756435e+01, 3.529399999999985e-10},
{ 5.700307292822586e+00, 4.685013393001369e+01, 2.094599999999994e-10},
{ 4.228062220664587e+00, 5.431134764391466e+01, 9.139999999999999e-11},
{ 1.167907583017500e+00, 7.461211875946264e+00, 6.204000000000000e-11},
{ 5.197274703601086e+00, 6.042797388545593e+01, 5.295999999999951e-11},
{ 5.160045430868086e+00, 3.938582310889583e+01, 1.811999999999999e-11},
{ 2.223277229900500e+00, 5.777452001970681e+01, 1.563999999999897e-11},
{ 6.921616240614999e-01, 2.103904892768837e+01, 1.535999999999999e-11},
{ 6.215802752555586e+00, 3.327229783044108e+01, 1.304000000000000e-11},
{ 1.368231494011000e+00, 3.938892179708230e+01, 1.661999999999999e-11},
{ 2.601539199675000e-01, 7.446011234647927e+00, 1.328000000000000e-11},
{ 5.903845687100000e-02, 3.941932465627423e+01, 8.559999999999978e-12},
{ 6.179310844209586e+00, 5.911910805492755e+01, 7.240000000000000e-12},
};
static const struct MarsSatTerm mars_sat_phobos_1[42] = {
{ 5.334169568271586e+00, 9.145914066599032e-03, 5.016848130000000e-05},
{ 5.334169148295587e+00, 9.145914113327495e-03, 5.016848130000000e-05},
{ 3.805345265960000e-01, 1.540952541904328e-03, 3.350264970000000e-05},
{ 3.805339484960000e-01, 1.540952613104260e-03, 3.350264903000000e-05},
{ 5.288363718535586e+00, 1.829243888046791e-02, 2.827839093000000e-05},
{ 5.288363809359586e+00, 1.829243887034939e-02, 2.827839093000000e-05},
{ 3.412685718051586e+00, 7.604696023359408e-03, 1.578862428000000e-05},
{ 3.412685998796586e+00, 7.604695992157328e-03, 1.578862427000000e-05},
{ 3.448999855869586e+00, 2.715567616927210e+01, 3.008844320000000e-05},
{ 9.194445444030001e-01, 3.101144183892481e-03, 8.178563170000001e-06},
{ 9.194443192780000e-01, 3.101144208596499e-03, 8.178563340000000e-06},
{ 5.387625479307586e+00, 1.969446275049270e+01, 2.044327843999999e-05},
{ 2.848996739890000e+00, 2.743829098982358e-02, 6.516951720000000e-06},
{ 2.848996748767000e+00, 2.743829098862537e-02, 6.516951720000000e-06},
{ 3.954411167839586e+00, 1.357783808463605e+01, 1.013316988000000e-05},
{ 2.912659283736000e+00, 4.073351425390815e+01, 7.483468759999740e-06},
{ 4.067858164824586e+00, 2.589727369338346e-02, 2.589359390000000e-06},
{ 4.067857973471586e+00, 2.589727371473152e-02, 2.589359390000000e-06},
{ 3.776271613850000e-01, 3.658417828853650e-02, 1.125147420000000e-06},
{ 3.776267985560000e-01, 3.658417832900902e-02, 1.125147420000000e-06},
{ 4.121936540919586e+00, 4.685014091671000e+01, 1.523532599999976e-06},
{ 1.231836210236000e+00, 1.674957754399965e-02, 4.976512300000000e-07},
{ 1.231836585135000e+00, 1.674957750247454e-02, 4.976512300000000e-07},
{ 2.488083095032000e+00, 1.520386207875350e-02, 6.841966800000000e-07},
{ 2.488083105051000e+00, 1.520386207728190e-02, 6.841966800000000e-07},
{ 1.576141880659500e+00, 3.504321962396548e-02, 1.124868580000000e-06},
{ 6.145328698142587e+00, 6.116624841770791e+00, 7.873411399999999e-07},
{ 2.626756105114500e+00, 5.431135233854420e+01, 1.040645720000000e-06},
{ 2.759579551372000e+00, 7.461213382164448e+00, 7.166789399999994e-07},
{ 6.005673273380586e+00, 1.314187651672942e+00, 6.022022799999936e-07},
{ 9.353090056600000e-02, 7.532115638546220e-04, 1.964976900000000e-07},
{ 9.343265452500001e-02, 7.532237916966188e-04, 1.964964500000000e-07},
{ 4.302928383505000e-01, 3.938582557777600e+01, 4.987307800000000e-07},
{ 3.587439959397586e+00, 6.042797969706294e+01, 4.804827999999994e-07},
{ 4.183547650369587e+00, 4.572998195120822e-02, 1.747133100000000e-07},
{ 4.183547815926586e+00, 4.572998193123842e-02, 1.747133100000000e-07},
{ 5.589444380180586e+00, 6.068411018120026e-03, 1.793076700000000e-07},
{ 5.589445119118587e+00, 6.068410936124942e-03, 1.793076800000000e-07},
{ 6.062696336024086e+00, 3.938892673561993e+01, 2.653755199999908e-07},
{ 4.628882088425586e+00, 3.327230135427769e+01, 2.539272800000000e-07},
{ 4.736462571780586e+00, 1.224558395734566e-02, 1.254715400000000e-07},
{ 4.736462588777586e+00, 1.224558396211886e-02, 1.254715500000000e-07},
};
static const struct MarsSatTerm mars_sat_phobos_2[27] = {
{ 1.404382124885000e+00, 7.595588511174286e-03, 1.514110912521000e-02},
{ 2.088385523034000e+00, 1.970205682773437e+01, 3.849620867400000e-04},
{ 3.895377361148586e+00, 1.069670280268190e-02, 6.903413242000000e-05},
{ 4.989572094750000e-01,-7.605272825959576e-03, 4.946101994000000e-05},
{ 8.215210221720000e-01, 4.685772969905357e+01, 3.671320788000000e-05},
{ 3.378076387479586e+00,-7.453616316894914e+00, 3.267983782000000e-05},
{ 2.772375719698000e+00, 3.939651873194720e+01, 8.750483050000000e-06},
{ 2.839303414769000e+00, 6.124220388371520e+00, 6.170938810000000e-06},
{ 3.184503170430000e-01, 6.043556479692822e+01, 6.957005940000000e-06},
{ 1.410329121503000e+00, 1.984322681200531e-02, 5.734088960000000e-06},
{ 1.336796551701000e+00, 3.327989343110183e+01, 3.431577860000000e-06},
{ 3.853646745987586e+00,-2.103145308554279e+01, 4.037409510000000e-06},
{ 2.547067366210000e-01, 1.549665594221090e-03, 2.115296220000000e-06},
{ 1.508224230072000e+00,-5.911151611514718e+01, 1.904827620000000e-06},
{ 3.462607551400000e-02,-5.165030141480156e+01, 8.696094700000001e-07},
{ 3.198655314207586e+00, 9.152774292423818e-03, 6.329868200000000e-07},
{ 5.163954670916587e+00, 1.674080796065678e-02, 6.130317800000000e-07},
{ 7.803937436990001e-01,-1.550610622055567e-03, 5.737217100000000e-07},
{ 3.264588234538586e+00, 2.716326950857758e+01, 8.181088300000000e-07},
{ 5.211329429707586e+00, 2.898903518276566e-02, 7.434145300000000e-07},
{ 2.691716763490000e+00,-2.714807769012140e+01, 5.441647800000001e-07},
{ 1.001733960626000e+00,-4.553367734785991e+01, 5.044515000000000e-07},
{ 3.212149602842586e+00,-1.968376490094933e+01, 4.284599100000000e-07},
{ 4.145658249560586e+00,-3.460928932187340e+01, 4.224937300000000e-07},
{ 5.063879137454586e+00, 2.589097180762378e-02, 3.581949800000000e-07},
{ 2.558639707373000e+00, 6.064928432880783e-03, 3.808881200000000e-07},
{ 5.292499025555586e+00, 3.075065445597794e-03, 4.055739200000000e-07},
};
static const struct MarsSatTerm mars_sat_phobos_3[28] = {
{ 2.058107128488000e+00,-7.604861328578004e-03, 9.408605183120001e-03},
{ 3.248856489376586e+00,-9.145863467943084e-03, 5.699538102000000e-05},
{ 4.557280987272586e+00, 1.829238959116374e-02, 2.474369483000000e-05},
{ 6.113423353213586e+00, 7.595702278066188e-03, 2.062715397000000e-05},
{ 2.067112903892000e+00, 2.743825478464904e-02, 5.945574160000000e-06},
{ 1.700689207004000e+00, 9.145729527513899e-03, 3.920190200000000e-06},
{ 3.577059353926586e+00,-1.829259493439447e-02, 2.167791970000000e-06},
{ 4.940284891770586e+00,-7.453616316894914e+00, 2.769763750000000e-06},
{ 2.117885747855000e+00, 3.941171892425920e+01, 1.813539170000000e-06},
{ 5.176055365230000e-01, 1.970205682773437e+01, 1.083429180000000e-06},
{ 5.864248782211586e+00, 3.658425999636452e-02, 1.066657010000000e-06},
{ 3.240692168340586e+00, 2.589639535977297e-02, 7.909853300000000e-07},
{ 7.570751994340000e-01,-1.321790869500707e+00, 8.880885099999999e-07},
{ 6.608910796060000e-01, 6.124220388371520e+00, 5.520758900000000e-07},
{ 5.519669134088586e+00, 4.685772969905357e+01, 4.397886000000000e-07},
{ 5.844000094873587e+00,-2.743825733444110e-02, 4.583609100000000e-07},
{ 4.602755395228586e+00,-1.675516082048598e-02, 3.533131100000000e-07},
{ 2.685649467156000e+00, 1.542518910643103e-03, 3.502988500000000e-07},
{ 3.353176877809586e+00, 1.225604581839110e+01, 3.828851700000000e-07},
{ 2.556761413073000e+00, 1.068971473943851e-02, 2.711247300000000e-07},
{ 4.622369718415587e+00,-2.589682634097252e-02, 1.991745400000000e-07},
{ 7.497690894330000e-01, 3.504362063026418e-02, 2.036897700000000e-07},
{ 3.965205602751586e+00, 2.714806830611846e+01, 1.726779600000000e-07},
{ 4.743858488241586e+00,-2.103145308554279e+01, 1.574472800000000e-07},
{ 3.405870070134586e+00, 4.572380100924578e-02, 1.698567700000000e-07},
{ 3.742380849210000e-01, 3.327989343110183e+01, 1.015004500000000e-07},
{ 1.374678754422000e+00,-1.970206671243377e+01, 8.259149000000001e-08},
{ 1.140160118874000e+00,-6.108652383295211e-03, 8.504923000000000e-08},
};
static const struct MarsSatTerm mars_sat_deimos_0[25] = {
{ 6.146803530569087e+00, 2.294413036846356e+00, 5.398360000000000e-09},
{ 3.432805186019586e+00, 9.935735425667586e+00, 7.095199999999997e-10},
{ 4.356581106602587e+00, 3.441619555269535e+00, 3.737800000000000e-10},
{ 5.915601483397086e+00, 9.926589650598594e+00, 2.337400000000000e-10},
{ 1.615243161446500e+00, 1.147206518423178e+00, 1.697000000000000e-10},
{ 2.114704906116000e+00, 9.917443791162608e+00, 5.314000000000000e-11},
{ 9.393388277920001e-01, 9.954027953247621e+00, 7.046000000000000e-11},
{ 3.949494792732586e+00, 9.944881541450725e+00, 4.233999999999998e-11},
{ 2.089322391969500e+00, 2.294097606691310e+00, 2.813999999999983e-11},
{ 7.795571756865000e-01, 2.294728469946876e+00, 2.549999999999971e-11},
{ 2.361851669664500e+00, 4.588826073692712e+00, 1.676000000000000e-11},
{ 2.770026883578500e+00, 9.954658773106329e+00, 9.719999999999976e-12},
{ 4.733522308629587e+00, 9.963173826265681e+00, 9.519999999999998e-12},
{ 3.258781379519086e+00, 1.472506551307767e+01, 9.739999999999990e-12},
{ 4.596299836113586e+00, 9.908297894321850e+00, 1.027999999999982e-11},
{ 3.057688443346500e+00, 2.682916339604879e+00, 6.679999999999999e-12},
{ 5.453932177963086e+00, 2.682285947505377e+00, 6.879999999999995e-12},
{ 4.328499239462586e+00, 9.936050924435195e+00, 6.199999999999996e-12},
{ 2.168321380562000e+00, 4.976699000854042e+00, 5.359999999999916e-12},
{ 2.516818588675000e+00, 9.935420104041299e+00, 3.359999999999995e-12},
{ 2.997827070725000e-01, 3.441304123224338e+00, 2.919999999999998e-12},
{ 2.143143420462500e+00, 2.682600911813873e+00, 2.979999999999999e-12},
{ 5.271781540988586e+00, 3.441934988580561e+00, 2.640000000000000e-12},
{ 5.382287205930000e-01, 9.926905039305762e+00, 2.039999999999966e-12},
{ 3.629187963751586e+00, 2.303558945733479e+00, 1.090000000000000e-12},
};
static const struct MarsSatTerm mars_sat_deimos_1[21] = {
{ 2.488175621789000e+00, 3.153377646687549e-04, 2.483031660190000e-03},
{ 2.488175634463000e+00, 3.153377641537758e-04, 2.483031659490000e-03},
{ 5.336889264964586e+00, 9.145915158660612e-03, 2.023426978200000e-04},
{ 5.336889346905586e+00, 9.145915155199243e-03, 2.023426978200000e-04},
{ 5.281538321363586e+00, 1.829244561727392e-02, 1.043167564200000e-04},
{ 5.281537958522586e+00, 1.829244563271799e-02, 1.043167564300000e-04},
{ 1.433490018835500e+00, 2.294413045224799e+00, 1.637133774599998e-04},
{ 2.852007788337500e+00, 2.743818628432718e-02, 4.820671420000000e-05},
{ 3.147176604035586e+00, 1.860783561096842e-02, 1.497066023000000e-05},
{ 3.147176307034586e+00, 1.860783562364318e-02, 1.497066034000000e-05},
{ 3.182516662993086e+00, 1.147206709032795e+00, 1.137028858000000e-05},
{ 5.917181875408586e+00, 3.441620120814550e+00, 8.350609860000001e-06},
{ 3.544871659744586e+00, 6.190897763118658e-04, 4.383254500000000e-06},
{ 3.544871649035586e+00, 6.190897769462623e-04, 4.383254500000000e-06},
{ 4.614193383590000e-01, 3.657933964533806e-02, 8.316176420000001e-06},
{ 6.754222685930000e-01, 2.775277029221846e-02, 6.447587580000001e-06},
{ 5.001640850015586e+00, 9.935735582783501e+00, 7.926687400000001e-06},
{ 4.485218623803586e+00, 8.828439970895009e-03, 3.905064210000000e-06},
{ 2.221013917980000e-01, 9.464721723028089e-03, 3.117801830000000e-06},
{ 4.485218622895586e+00, 8.828439971019056e-03, 3.905064210000000e-06},
{ 2.221013944660000e-01, 9.464721722885516e-03, 3.117801830000000e-06},
};
static const struct MarsSatTerm mars_sat_deimos_2[15] = {
{ 2.198649419514000e+00, 3.148401892560942e-04, 2.744131534600000e-04},
{ 4.366636518977586e+00, 4.977013897776327e+00, 6.015912711000000e-05},
{ 1.463816210370000e+00,-3.153164045300449e-04, 3.614585349000000e-05},
{ 1.362467588064000e+00, 2.682600831640474e+00, 2.577157903000000e-05},
{ 9.336792893320000e-01,-4.958721582505435e+00, 6.801997830000000e-06},
{ 3.152432010349586e+00, 1.535394310272536e+00, 4.452979140000000e-06},
{ 4.734051493918586e+00,-4.949575691510945e+00, 2.238482920000000e-06},
{ 5.736642853342587e+00, 2.327524649334390e-04, 1.478747570000000e-06},
{ 5.084945958505586e+00, 3.912335403634271e-04, 1.457720040000000e-06},
{ 4.232770650771586e+00, 7.271426904652134e+00, 1.347030310000000e-06},
{ 1.516439412109000e+00, 1.491274947692300e+01, 7.538179100000001e-07},
{ 3.100618466711000e+00, 1.797748798526167e-02, 8.716229700000000e-07},
{ 5.155432690964586e+00, 3.881877876517008e-01, 1.015648880000000e-06},
{ 2.250768124831000e+00,-4.940429834739583e+00, 5.090989300000000e-07},
{ 3.422251338868586e+00,-4.977013897776327e+00, 6.537077500000000e-07},
};
static const struct MarsSatTerm mars_sat_deimos_3[27] = {
{ 2.981506933511000e+00,-3.154811355556041e-04, 1.562693319959000e-02},
{ 4.557500894366586e+00, 1.829233626168517e-02, 1.321818631100000e-04},
{ 3.248124065112586e+00,-9.145934570587952e-03, 3.833652719000000e-05},
{ 1.701551338710000e+00, 9.145665986601770e-03, 2.660211842000000e-05},
{ 2.067662003165000e+00, 2.743821959085702e-02, 2.882438535000000e-05},
{ 4.805327222478586e+00, 3.158564952832469e-04, 2.713213911000000e-05},
{ 2.318788380092000e+00, 1.860776219134252e-02, 9.296324950000000e-06},
{ 3.613580709014586e+00,-1.829261290520875e-02, 4.460960290000000e-06},
{ 5.867810550672586e+00, 3.658405181871115e-02, 4.904156030000000e-06},
{ 5.496515547817586e+00,-9.461294099629817e-03, 2.366881150000000e-06},
{ 6.120362957845586e+00, 2.775361064625920e-02, 2.057552850000000e-06},
{ 3.612389208841586e+00, 8.830178627407989e-03, 2.365750140000000e-06},
{ 5.554163001934587e+00,-1.860800990265229e-02, 1.228814440000000e-06},
{ 3.561485983136586e+00, 1.797648948023003e-02, 1.215189460000000e-06},
{ 1.941975361869000e+00,-6.259843043363501e-04, 9.602856999999999e-07},
{ 5.845244776759587e+00,-2.743819348074506e-02, 1.174471770000000e-06},
{ 1.821048365911000e+00, 9.460829229838182e-03, 1.058441050000000e-06},
{ 7.587135454610000e-01, 1.682177864988152e-04, 2.383147490000000e-06},
{ 3.292891638604586e+00, 4.573365667404765e-02, 7.656303800000000e-07},
{ 3.766016108380586e+00, 3.683140215171970e-02, 4.075824300000000e-07},
{ 5.747244956219586e+00, 9.954342722363187e+00, 4.962340900000000e-07},
{ 1.724062868741000e+00,-2.774017744689255e-02, 3.003077900000000e-07},
{ 4.429747729642586e+00,-8.904754331211824e-03, 2.613416400000000e-07},
{ 8.659161909010000e-01, 2.718340176462802e-02, 2.891945200000000e-07},
{ 2.259603352363000e+00,-3.660152378331885e-02, 2.213788200000000e-07},
{ 1.108725025984000e+00, 9.935732440466172e+00, 2.449950300000000e-07},
{ 1.680391963791000e+00, 9.076040299352415e-03, 1.652416600000000e-07},
};
static const struct MarsSatBody mars_sat_bodies[2] = {
{
9.549547741038312e-11, 1.970205562831390e+01, 1.657852042683113e-10,
{
0.0000626916188000,
2.0912973926417302,
-0.0000005774003141,
0.0000006185052790,
-0.0000575018919826,
-0.0000540676493351,
},
{
{mars_sat_phobos_0, 16},
{mars_sat_phobos_1, 42},
{mars_sat_phobos_2, 27},
{mars_sat_phobos_3, 28}
}
},
{
9.549547622768120e-11, 4.977013889652300e+00, -2.331793571572226e-14,
{
0.0001568134086700,
-1.9167537810740201,
-0.0000239579419518,
0.0000257893300229,
-0.0056443721379635,
-0.0053121806978560,
},
{
{mars_sat_deimos_0, 25},
{mars_sat_deimos_1, 21},
{mars_sat_deimos_2, 15},
{mars_sat_deimos_3, 27}
}
},
};
static
void CalcMarsSatElem(double t, int body, double elem[6]) {
int j;
const struct MarsSatBody *bp = mars_sat_bodies + body;
memcpy(elem, bp->constants, 6 * sizeof(double));
for (j = 0; j < 2; j++) {
const struct MarsSatTerm *const begin = bp->lists[j].terms;
const struct MarsSatTerm *p = begin + bp->lists[j].size;
while (--p >= begin) {
const double d = p->phase + t * p->frequency;
elem[j] += p->amplitude * cos(d);
}
}
for (j = 2; j < 4; j++) {
const struct MarsSatTerm *const begin = bp->lists[j].terms;
const struct MarsSatTerm *p = begin + bp->lists[j].size;
while (--p >= begin) {
const double d = p->phase + t * p->frequency;
elem[2*j-2] += p->amplitude * cos(d);
elem[2*j-1] += p->amplitude * sin(d);
}
}
elem[1] += (bp->l + bp->acc * t) * t;
}
static
void MultMat(const double a[9], const double b[9], double c[9]) {
int i, j;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
c[i*3+j] = a[i*3]*b[j] + a[i*3+1]*b[3+j] + a[i*3+2]*b[6+j];
}
}
}
static const double J2000_to_VSOP87[9] = {
9.999999999998848e-01,-4.799655442984222e-07, 0.000000000000000e+00,
4.403598133110236e-07, 9.174821370868568e-01, 3.977769829016507e-01,
-1.909192461077750e-07,-3.977769829016049e-01, 9.174821370869625e-01};
static const double ome0 = 47.68143;
static const double inc0 = 37.1135;
static const double dome = -0.1061;
static const double dinc = 0.0609;
static
void GenerateMarsSatToVSOP87(double t, double mat[9]) {
double ome, inc, co, so, ci, si;
double m[9];
t -= 6491.5;
ome = (ome0 + dome * t / 36525.) * (M_PI / 180.0);
inc = (inc0 + dinc * t / 36525.) * (M_PI / 180.0);
co = cos(ome);
so = sin(ome);
ci = cos(inc);
si = sin(inc);
m[0] = co; m[1] = -ci*so; m[2] = si*so;
m[3] = so; m[4] = ci*co; m[5] = -si*co;
m[6] = 0.0; m[7] = si; m[8] = ci;
MultMat(J2000_to_VSOP87, m, mat);
}
void GetMarsSatCoor(double jd, int body, double *xyz, double *xyzdot) {
double elem[6];
double x[6];
double mat[9];
const double t = jd - 2451545.0 + 6491.5;
CalcMarsSatElem(t, body, elem);
GenerateMarsSatToVSOP87(t, mat);
EllipticToRectangularA(mars_sat_bodies[body].mu, elem, 0.0, x);
xyz[0] = mat[0]*x[0] + mat[1]*x[1] + mat[2]*x[2];
xyz[1] = mat[3]*x[0] + mat[4]*x[1] + mat[5]*x[2];
xyz[2] = mat[6]*x[0] + mat[7]*x[1] + mat[8]*x[2];
if (xyzdot) {
xyzdot[0] = mat[0]*x[3] + mat[1]*x[4] + mat[2]*x[5];
xyzdot[1] = mat[3]*x[3] + mat[4]*x[4] + mat[5]*x[5];
xyzdot[2] = mat[6]*x[3] + mat[7]*x[4] + mat[8]*x[5];
}
}

81
src/marssat.h Normal file
View File

@ -0,0 +1,81 @@
/************************************************************************
The Ephemerides of the Martian satellites
(adjustement from 1877 to 2005, Version 1.0)
by Valery Lainey can be obtained from Valery Lainey:
V.Lainey (Lainey@oma.be)
ROB- 3, Avenue Circulaire, B-1180 Bruxelles (Belgium)
IMCCE - 77, Avenue Denfert-Rochereau 75014 Paris (France)
-----------------------------------------------------------------------
I (Johannes Gajdosik) have just taken Valery Laineys Fortran code,
MarsSatV1-0.f, which he kindly supplied, and rearranged it into
this piece of software.
I can neither allow nor forbid the usage of Valery Laineys
Ephemerides of the Martian satellites.
The copyright notice below covers not the work of Valery Lainey
but just my work, that is the compilation of Valery Laineys
Ephemerides of the Martian satellites into the software supplied in this file.
Copyright (c) 2006 Johannes Gajdosik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Derived from Stellarium's MARSSAT implementation.
Modified for pg_orbit: removed static mutable state and
CalcInterpolatedElements caching for thread safety
(PostgreSQL PARALLEL SAFE). Elements are computed fresh on each call.
1) do not calculate constant terms at runtime but beforehand
2) unite terms with the same frequencies
****************************************************************/
#ifndef PG_ORBIT_MARSSAT_H
#define PG_ORBIT_MARSSAT_H
#ifdef __cplusplus
extern "C" {
#endif
#define MARS_SAT_PHOBOS 0
#define MARS_SAT_DEIMOS 1
void GetMarsSatCoor(double jd, int body, double *xyz, double *xyzdot);
/* Return the rectangular coordinates and velocity of the given satellite
and the given julian date jd expressed in dynamical time (TAI+32.184s).
The origin of the xyz-coordinates is the center of Mars.
The reference frame is "dynamical equinox and ecliptic J2000",
which is the reference frame in VSOP87 and VSOP87A.
body: 0=Phobos, 1=Deimos
xyz[3]: position (AU), relative to Mars center
xyzdot[3]: velocity (AU/day), may be NULL
*/
#ifdef __cplusplus
}
#endif
#endif

248
src/moon_funcs.c Normal file
View File

@ -0,0 +1,248 @@
/*
* moon_funcs.c -- Planetary moon observation
*
* SQL functions for observing moons of Jupiter, Saturn, Uranus, and Mars.
* Each moon's position is computed in the VSOP87 ecliptic J2000 frame
* relative to its parent planet, then combined with the parent's
* heliocentric position to get geocentric az/el.
*
* Pipeline for each moon:
* 1. Parent planet heliocentric position (VSOP87)
* 2. Moon position relative to parent (L12/TASS17/GUST86/MARSSAT)
* 3. Moon heliocentric = parent + moon_relative
* 4. Moon geocentric = moon_heliocentric - Earth_heliocentric
* 5. Ecliptic -> equatorial -> precess -> az/el
*/
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
#include "utils/timestamp.h"
#include "types.h"
#include "astro_math.h"
#include "vsop87.h"
#include "l12.h"
#include "tass17.h"
#include "gust86.h"
#include "marssat.h"
#include <math.h>
PG_FUNCTION_INFO_V1(galilean_observe);
PG_FUNCTION_INFO_V1(saturn_moon_observe);
PG_FUNCTION_INFO_V1(uranus_moon_observe);
PG_FUNCTION_INFO_V1(mars_moon_observe);
/* ================================================================
* Internal: geocentric observation pipeline
*
* Duplicated from planet_funcs.c to avoid cross-TU coupling.
* (Same logic: ecliptic J2000 -> equatorial -> precess -> az/el)
* ================================================================
*/
static void
moon_observe_from_geocentric(const double geo_ecl_au[3], double jd,
const pg_observer *obs, pg_topocentric *result)
{
double geo_equ[3];
double ra_j2000, dec_j2000, geo_dist;
double ra_date, dec_date;
double gmst_val, lst, ha;
double az, el;
ecliptic_to_equatorial(geo_ecl_au, geo_equ);
cartesian_to_spherical(geo_equ, &ra_j2000, &dec_j2000, &geo_dist);
precess_j2000_to_date(jd, ra_j2000, dec_j2000, &ra_date, &dec_date);
gmst_val = gmst_from_jd(jd);
lst = gmst_val + obs->lon;
ha = lst - ra_date;
equatorial_to_horizontal(ha, dec_date, obs->lat, &az, &el);
result->azimuth = az;
result->elevation = el;
result->range_km = geo_dist * AU_KM;
result->range_rate = 0.0;
}
/* ================================================================
* Internal: common pattern for all planetary moons
*
* Given: moon position relative to parent (VSOP87 ecliptic J2000, AU)
* parent's VSOP87 body index (0-based)
* observer, JD
*
* Computes geocentric position and returns topocentric az/el.
* ================================================================
*/
static void
observe_planetary_moon(const double moon_rel[3], int vsop_parent,
double jd, const pg_observer *obs,
pg_topocentric *result)
{
double parent_xyz[6];
double earth_xyz[6];
double geo_ecl[3];
/* Parent planet heliocentric */
GetVsop87Coor(jd, vsop_parent, parent_xyz);
/* Earth heliocentric */
GetVsop87Coor(jd, 2, earth_xyz); /* VSOP87 body 2 = Earth */
/* Moon geocentric = (parent + moon_relative) - Earth */
geo_ecl[0] = (parent_xyz[0] + moon_rel[0]) - earth_xyz[0];
geo_ecl[1] = (parent_xyz[1] + moon_rel[1]) - earth_xyz[1];
geo_ecl[2] = (parent_xyz[2] + moon_rel[2]) - earth_xyz[2];
moon_observe_from_geocentric(geo_ecl, jd, obs, result);
}
/* ================================================================
* galilean_observe(body_id int, observer, timestamptz) -> topocentric
*
* Observe a Galilean moon of Jupiter.
* Body IDs: 0=Io, 1=Europa, 2=Ganymede, 3=Callisto
*
* Uses L1.2 theory (Lainey, Duriez & Vienne) for moon positions
* and VSOP87 for Jupiter and Earth.
* ================================================================
*/
Datum
galilean_observe(PG_FUNCTION_ARGS)
{
int32 body_id = PG_GETARG_INT32(0);
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
int64 ts = PG_GETARG_INT64(2);
double jd;
double moon_xyz[3];
pg_topocentric *result;
if (body_id < L12_IO || body_id > L12_CALLISTO)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("galilean_observe: body_id %d must be 0-3 (Io, Europa, Ganymede, Callisto)",
body_id)));
jd = timestamptz_to_jd(ts);
/* Moon position relative to Jupiter, VSOP87 ecliptic J2000, AU */
GetL12Coor(jd, body_id, moon_xyz, NULL);
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
observe_planetary_moon(moon_xyz, 4, jd, obs, result); /* VSOP87 body 4 = Jupiter */
PG_RETURN_POINTER(result);
}
/* ================================================================
* saturn_moon_observe(body_id int, observer, timestamptz) -> topocentric
*
* Observe a moon of Saturn.
* Body IDs: 0=Mimas, 1=Enceladus, 2=Tethys, 3=Dione,
* 4=Rhea, 5=Titan, 6=Iapetus, 7=Hyperion
*
* Uses TASS 1.7 theory (Vienne & Duriez).
* ================================================================
*/
Datum
saturn_moon_observe(PG_FUNCTION_ARGS)
{
int32 body_id = PG_GETARG_INT32(0);
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
int64 ts = PG_GETARG_INT64(2);
double jd;
double moon_xyz[3];
pg_topocentric *result;
if (body_id < TASS17_MIMAS || body_id > TASS17_HYPERION)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("saturn_moon_observe: body_id %d must be 0-7 (Mimas-Hyperion)",
body_id)));
jd = timestamptz_to_jd(ts);
GetTass17Coor(jd, body_id, moon_xyz, NULL);
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
observe_planetary_moon(moon_xyz, 5, jd, obs, result); /* VSOP87 body 5 = Saturn */
PG_RETURN_POINTER(result);
}
/* ================================================================
* uranus_moon_observe(body_id int, observer, timestamptz) -> topocentric
*
* Observe a moon of Uranus.
* Body IDs: 0=Miranda, 1=Ariel, 2=Umbriel, 3=Titania, 4=Oberon
*
* Uses GUST86 theory (Laskar & Jacobson).
* ================================================================
*/
Datum
uranus_moon_observe(PG_FUNCTION_ARGS)
{
int32 body_id = PG_GETARG_INT32(0);
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
int64 ts = PG_GETARG_INT64(2);
double jd;
double moon_xyz[3];
pg_topocentric *result;
if (body_id < GUST86_MIRANDA || body_id > GUST86_OBERON)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("uranus_moon_observe: body_id %d must be 0-4 (Miranda-Oberon)",
body_id)));
jd = timestamptz_to_jd(ts);
GetGust86Coor(jd, body_id, moon_xyz, NULL);
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
observe_planetary_moon(moon_xyz, 6, jd, obs, result); /* VSOP87 body 6 = Uranus */
PG_RETURN_POINTER(result);
}
/* ================================================================
* mars_moon_observe(body_id int, observer, timestamptz) -> topocentric
*
* Observe a moon of Mars.
* Body IDs: 0=Phobos, 1=Deimos
*
* Uses MarsSat theory (Lainey, 2007).
* ================================================================
*/
Datum
mars_moon_observe(PG_FUNCTION_ARGS)
{
int32 body_id = PG_GETARG_INT32(0);
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(1);
int64 ts = PG_GETARG_INT64(2);
double jd;
double moon_xyz[3];
pg_topocentric *result;
if (body_id < MARS_SAT_PHOBOS || body_id > MARS_SAT_DEIMOS)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("mars_moon_observe: body_id %d must be 0-1 (Phobos, Deimos)",
body_id)));
jd = timestamptz_to_jd(ts);
GetMarsSatCoor(jd, body_id, moon_xyz, NULL);
result = (pg_topocentric *) palloc(sizeof(pg_topocentric));
observe_planetary_moon(moon_xyz, 3, jd, obs, result); /* VSOP87 body 3 = Mars */
PG_RETURN_POINTER(result);
}

262
src/radio_funcs.c Normal file
View File

@ -0,0 +1,262 @@
/*
* radio_funcs.c -- Jupiter decametric radio burst prediction
*
* Jupiter emits intense radio bursts at 4-39.5 MHz, modulated by:
* 1. Io's orbital phase around Jupiter (Io-related storms)
* 2. Jupiter's Central Meridian Longitude (CML, System III)
*
* Radio JOVE operators use CML-vs-Io-phase lookup charts to predict
* burst probability. This module computes both parameters from SQL,
* enabling batch prediction queries over time series.
*
* References:
* Carr, Desch & Alexander (1983), "Phenomenology of Magnetospheric
* Radio Emissions", in Physics of the Jovian Magnetosphere.
* Radio JOVE project: https://radiojove.gsfc.nasa.gov/
*/
#include "postgres.h"
#include "fmgr.h"
#include "funcapi.h"
#include "utils/timestamp.h"
#include "types.h"
#include "astro_math.h"
#include "vsop87.h"
#include "l12.h"
#include <math.h>
PG_FUNCTION_INFO_V1(io_phase_angle);
PG_FUNCTION_INFO_V1(jupiter_cml);
PG_FUNCTION_INFO_V1(jupiter_burst_probability);
/* ================================================================
* io_phase_angle(timestamptz) -> float8
*
* Returns Io's orbital phase angle in degrees [0, 360).
* Measured from superior geocentric conjunction:
* 0 = Io behind Jupiter (as seen from Earth)
* 90 = Io east of Jupiter (western elongation)
* 180 = Io in front of Jupiter (inferior conjunction)
* 270 = Io west of Jupiter (eastern elongation)
*
* This is the standard convention used by Radio JOVE charts.
* ================================================================
*/
Datum
io_phase_angle(PG_FUNCTION_ARGS)
{
int64 ts = PG_GETARG_INT64(0);
double jd;
double io_xyz[3];
double jup_xyz[6], earth_xyz[6];
double jup_geo[3];
double io_rel_geo[3];
double phase;
double proj_x, proj_y;
double jup_dist;
double east_x, east_y, east_z;
double north_x, north_y, north_z;
double io_east, io_north;
jd = timestamptz_to_jd(ts);
/* Io position relative to Jupiter (VSOP87 ecliptic J2000, AU) */
GetL12Coor(jd, L12_IO, io_xyz, NULL);
/* Jupiter and Earth heliocentric positions */
GetVsop87Coor(jd, 4, jup_xyz); /* VSOP87 body 4 = Jupiter */
GetVsop87Coor(jd, 2, earth_xyz); /* VSOP87 body 2 = Earth */
/* Jupiter geocentric direction */
jup_geo[0] = jup_xyz[0] - earth_xyz[0];
jup_geo[1] = jup_xyz[1] - earth_xyz[1];
jup_geo[2] = jup_xyz[2] - earth_xyz[2];
jup_dist = sqrt(jup_geo[0] * jup_geo[0] +
jup_geo[1] * jup_geo[1] +
jup_geo[2] * jup_geo[2]);
/* Normalize Jupiter geocentric direction */
jup_geo[0] /= jup_dist;
jup_geo[1] /= jup_dist;
jup_geo[2] /= jup_dist;
/*
* Project Io's position onto the plane perpendicular to
* the Earth-Jupiter line of sight. We need two orthogonal
* axes in this plane: "east" (position angle) and "north"
* (toward north ecliptic pole).
*
* North ecliptic pole = (0, 0, 1) in ecliptic J2000.
* East = North x LineOfSight (cross product)
*/
north_x = -(jup_geo[2] * jup_geo[0]); /* north - (north.los)*los */
north_y = -(jup_geo[2] * jup_geo[1]);
north_z = 1.0 - jup_geo[2] * jup_geo[2];
{
double nmag = sqrt(north_x * north_x + north_y * north_y + north_z * north_z);
if (nmag > 1e-10) {
north_x /= nmag;
north_y /= nmag;
north_z /= nmag;
}
}
/* East = North x LOS */
east_x = north_y * jup_geo[2] - north_z * jup_geo[1];
east_y = north_z * jup_geo[0] - north_x * jup_geo[2];
east_z = north_x * jup_geo[1] - north_y * jup_geo[0];
/* Project Io relative position onto east/north */
io_east = io_xyz[0] * east_x + io_xyz[1] * east_y + io_xyz[2] * east_z;
io_north = io_xyz[0] * north_x + io_xyz[1] * north_y + io_xyz[2] * north_z;
/* Along LOS component (positive = behind Jupiter from Earth) */
proj_x = io_xyz[0] * jup_geo[0] + io_xyz[1] * jup_geo[1] + io_xyz[2] * jup_geo[2];
proj_y = io_east;
/*
* Phase angle convention (Radio JOVE):
* 0 = superior conjunction (Io behind Jupiter)
* Measured counter-clockwise as seen from north ecliptic pole
*/
phase = atan2(-proj_y, proj_x) * RAD_TO_DEG;
if (phase < 0.0)
phase += 360.0;
PG_RETURN_FLOAT8(phase);
}
/* ================================================================
* jupiter_cml(observer, timestamptz) -> float8
*
* Jupiter's Central Meridian Longitude in System III (1965.0).
* Returns degrees [0, 360).
*
* System III is defined by Jupiter's magnetic field rotation,
* with period 9h 55m 29.711s (IAU).
*
* The CML is the System III longitude of the sub-observer point.
* For Earth-based observation, this is approximately the
* sub-Earth longitude.
*
* Reference: IAU (1976) System III longitude definition.
* ================================================================
*/
Datum
jupiter_cml(PG_FUNCTION_ARGS)
{
pg_observer *obs = (pg_observer *) PG_GETARG_POINTER(0);
int64 ts = PG_GETARG_INT64(1);
double jd;
double jup_xyz[6], earth_xyz[6];
double dx, dy, dz, geo_dist;
double light_time_days;
double jd_lt;
double d;
double cml;
(void)obs; /* observer not needed for CML (geocentric is sufficient) */
jd = timestamptz_to_jd(ts);
/* Jupiter and Earth positions */
GetVsop87Coor(jd, 4, jup_xyz);
GetVsop87Coor(jd, 2, earth_xyz);
/* Geocentric distance for light-time correction */
dx = jup_xyz[0] - earth_xyz[0];
dy = jup_xyz[1] - earth_xyz[1];
dz = jup_xyz[2] - earth_xyz[2];
geo_dist = sqrt(dx * dx + dy * dy + dz * dz);
/* Light-time correction (speed of light = 173.1446 AU/day) */
light_time_days = geo_dist / 173.1446327;
jd_lt = jd - light_time_days;
/*
* System III longitude of central meridian.
*
* Reference epoch: J1965.0 = JD 2438761.5
* Initial longitude: 305.38 deg (IAU 1976 convention)
* Rotation rate: 870.536 deg/day (System III, 9h 55m 29.711s period)
*
* The sub-Earth longitude decreases with time (Jupiter rotates
* prograde), so CML = initial + rate * elapsed_days.
*/
d = jd_lt - 2438761.5; /* Days since J1965.0 */
cml = 305.38 + 870.536 * d;
/* Normalize to [0, 360) */
cml = fmod(cml, 360.0);
if (cml < 0.0)
cml += 360.0;
PG_RETURN_FLOAT8(cml);
}
/* ================================================================
* jupiter_burst_probability(io_phase float8, cml float8) -> float8
*
* Estimated probability of Jupiter decametric radio burst
* based on Io phase angle and CML (System III).
*
* Returns a value 0.0-1.0 representing relative likelihood.
*
* Based on the empirical source regions from Carr et al. (1983):
* Source A: CML 200-260, Io phase 195-265 (Io-A)
* Source B: CML 90-200, Io phase 70-110 (Io-B)
* Source C: CML 290-10, Io phase 215-260 (Io-C)
* Source D: CML 0-50, no Io dependence (non-Io-D)
*
* This is a simplified model. Real-world burst probability
* depends on additional factors (frequency, solar activity,
* antenna orientation).
* ================================================================
*/
Datum
jupiter_burst_probability(PG_FUNCTION_ARGS)
{
double io_phase = PG_GETARG_FLOAT8(0);
double cml = PG_GETARG_FLOAT8(1);
double p;
/* Normalize inputs */
io_phase = fmod(io_phase, 360.0);
if (io_phase < 0.0) io_phase += 360.0;
cml = fmod(cml, 360.0);
if (cml < 0.0) cml += 360.0;
p = 0.0;
/* Source A (Io-A): strongest, most predictable */
if (cml >= 200.0 && cml <= 260.0 && io_phase >= 195.0 && io_phase <= 265.0)
p = 0.8;
/* Source B (Io-B): second strongest */
if (cml >= 90.0 && cml <= 200.0 && io_phase >= 70.0 && io_phase <= 110.0)
{
double pb = 0.6;
if (pb > p) p = pb;
}
/* Source C (Io-C): wraps around 360/0 boundary */
if ((cml >= 290.0 || cml <= 10.0) && io_phase >= 215.0 && io_phase <= 260.0)
{
double pc = 0.5;
if (pc > p) p = pc;
}
/* Non-Io-D: CML-dependent only, weaker */
if (cml >= 0.0 && cml <= 50.0)
{
double pd = 0.15;
if (pd > p) p = pd;
}
PG_RETURN_FLOAT8(p);
}

3156
src/tass17.c Normal file

File diff suppressed because it is too large Load Diff

71
src/tass17.h Normal file
View File

@ -0,0 +1,71 @@
/************************************************************************
The TASS 1.7 theory of the Saturnian satellites including HYPERION
by Alain VIENNE and Luc DURIEZ can be found at
ftp://ftp.imcce.fr/pub/ephem/satel/tass17
I (Johannes Gajdosik) have just taken the Fortran code and data
obtained from above and rearranged it into this piece of software.
I can neither allow nor forbid the usage of the TASS 1.7 theory.
The copyright notice below covers not the work of Alain VIENNE and Luc DURIEZ
but just my work, that is the compilation of the TASS 1.7 theory
into the software supplied in this file.
Copyright (c) 2005 Johannes Gajdosik
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Adapted for pg_orbit: removed static mutable state for thread safety
(PostgreSQL PARALLEL SAFE).
****************************************************************/
#ifndef PG_ORBIT_TASS17_H
#define PG_ORBIT_TASS17_H
#ifdef __cplusplus
extern "C" {
#endif
#define TASS17_MIMAS 0
#define TASS17_ENCELADUS 1
#define TASS17_TETHYS 2
#define TASS17_DIONE 3
#define TASS17_RHEA 4
#define TASS17_TITAN 5
#define TASS17_HYPERION 7
#define TASS17_IAPETUS 6
/*
* Compute Saturn moon position and velocity in VSOP87 ecliptic J2000
* coordinates, relative to Saturn center, in AU and AU/day.
*
* body: 0-7 (see defines above; note Hyperion=7, Iapetus=6)
* xyz[3]: output position (AU)
* xyzdot[3]: output velocity (AU/day), may be NULL
*/
void GetTass17Coor(double jd, int body, double *xyz, double *xyzdot);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,226 @@
-- moon_observe regression tests
--
-- Tests planetary moon observation (Galilean, Saturn, Uranus, Mars)
-- and Jupiter decametric radio burst prediction functions.
-- Reference distances from JPL/NASA fact sheets.
\set boulder '''40.015N 105.270W 1655m'''::observer
-- ============================================================
-- Test 1: Galilean moon observation - all four from Boulder
-- Io, Europa, Ganymede, Callisto should return valid topocentric.
-- ============================================================
SELECT 'galilean_io' AS test,
round(topo_azimuth(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-------------+--------+--------
galilean_io | 270 | 24
(1 row)
SELECT 'galilean_europa' AS test,
round(topo_azimuth(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-----------------+--------+--------
galilean_europa | 270 | 24
(1 row)
SELECT 'galilean_ganymede' AS test,
round(topo_azimuth(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-------------------+--------+--------
galilean_ganymede | 270 | 24
(1 row)
SELECT 'galilean_callisto' AS test,
round(topo_azimuth(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-------------------+--------+--------
galilean_callisto | 270 | 24
(1 row)
-- ============================================================
-- Test 2: Galilean moons should be very close to Jupiter
-- All four should have ranges within ~0.05 AU of Jupiter's range.
-- Jupiter is ~4-6 AU from Earth; moons orbit within 0.013 AU.
-- ============================================================
SELECT 'galilean_near_jupiter' AS test,
round(topo_range(planet_observe(5, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS jupiter_km,
round(topo_range(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS io_km,
round(topo_range(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS callisto_km;
test | jupiter_km | io_km | callisto_km
-----------------------+------------+-----------+-------------
galilean_near_jupiter | 837420000 | 837000000 | 837600000
(1 row)
-- ============================================================
-- Test 3: Saturn moon observation - Titan (body_id=5)
-- Titan is Saturn's largest moon, should be near Saturn.
-- ============================================================
SELECT 'saturn_titan' AS test,
round(topo_azimuth(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
--------------+--------+--------
saturn_titan | 50 | -45
(1 row)
-- ============================================================
-- Test 4: Saturn moons - Mimas through Iapetus all return results
-- ============================================================
SELECT 'saturn_moons' AS test,
body_id,
round(topo_range(saturn_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 7) AS body_id;
test | body_id | range_km
--------------+---------+------------
saturn_moons | 0 | 1427920000
saturn_moons | 1 | 1427670000
saturn_moons | 2 | 1427910000
saturn_moons | 3 | 1427680000
saturn_moons | 4 | 1427420000
saturn_moons | 5 | 1426520000
saturn_moons | 6 | 1428150000
saturn_moons | 7 | 1429140000
(8 rows)
-- ============================================================
-- Test 5: Uranus moon observation - Titania (body_id=3)
-- ============================================================
SELECT 'uranus_titania' AS test,
round(topo_azimuth(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
----------------+--------+--------
uranus_titania | 329 | -25
(1 row)
-- ============================================================
-- Test 6: All Uranus moons return valid results
-- ============================================================
SELECT 'uranus_moons' AS test,
body_id,
round(topo_range(uranus_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 4) AS body_id;
test | body_id | range_km
--------------+---------+------------
uranus_moons | 0 | 3061280000
uranus_moons | 1 | 3061360000
uranus_moons | 2 | 3061200000
uranus_moons | 3 | 3061310000
uranus_moons | 4 | 3061520000
(5 rows)
-- ============================================================
-- Test 7: Mars moons - Phobos and Deimos
-- ============================================================
SELECT 'mars_phobos' AS test,
round(topo_azimuth(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-------------+--------+--------
mars_phobos | 1 | -74
(1 row)
SELECT 'mars_deimos' AS test,
round(topo_azimuth(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
test | az_deg | el_deg
-------------+--------+--------
mars_deimos | 1 | -74
(1 row)
-- ============================================================
-- Test 8: Io phase angle - should be [0, 360)
-- ============================================================
SELECT 'io_phase' AS test,
round(io_phase_angle('2024-03-15 03:00:00+00')::numeric, 1) AS phase_deg,
io_phase_angle('2024-03-15 03:00:00+00') >= 0.0
AND io_phase_angle('2024-03-15 03:00:00+00') < 360.0 AS in_range;
test | phase_deg | in_range
----------+-----------+----------
io_phase | 179.7 | t
(1 row)
-- ============================================================
-- Test 9: Jupiter CML (System III) - should be [0, 360)
-- ============================================================
SELECT 'jupiter_cml' AS test,
round(jupiter_cml(:boulder, '2024-03-15 03:00:00+00')::numeric, 1) AS cml_deg,
jupiter_cml(:boulder, '2024-03-15 03:00:00+00') >= 0.0
AND jupiter_cml(:boulder, '2024-03-15 03:00:00+00') < 360.0 AS in_range;
test | cml_deg | in_range
-------------+---------+----------
jupiter_cml | 306.0 | t
(1 row)
-- ============================================================
-- Test 10: Jupiter burst probability - known high probability zone
-- Source A: CML 200-260, Io phase 195-265 => p=0.8
-- ============================================================
SELECT 'burst_source_a' AS test,
jupiter_burst_probability(230.0, 230.0) AS p_source_a;
test | p_source_a
----------------+------------
burst_source_a | 0.8
(1 row)
-- ============================================================
-- Test 11: Jupiter burst probability - known zones
-- ============================================================
SELECT 'burst_zones' AS test,
jupiter_burst_probability(90.0, 150.0) AS p_source_b,
jupiter_burst_probability(240.0, 350.0) AS p_source_c,
jupiter_burst_probability(100.0, 25.0) AS p_source_d,
jupiter_burst_probability(0.0, 130.0) AS p_quiet;
test | p_source_b | p_source_c | p_source_d | p_quiet
-------------+------------+------------+------------+---------
burst_zones | 0.6 | 0.5 | 0.15 | 0
(1 row)
-- ============================================================
-- Test 12: Io phase changes over time (Io orbits in ~1.77 days)
-- Two times 12 hours apart should show significant phase change.
-- ============================================================
SELECT 'io_phase_changes' AS test,
round(io_phase_angle('2024-03-15 00:00:00+00')::numeric, 1) AS phase_0h,
round(io_phase_angle('2024-03-15 12:00:00+00')::numeric, 1) AS phase_12h,
abs(io_phase_angle('2024-03-15 00:00:00+00') -
io_phase_angle('2024-03-15 12:00:00+00')) > 10.0 AS changed;
test | phase_0h | phase_12h | changed
------------------+----------+-----------+---------
io_phase_changes | 205.0 | 103.4 | t
(1 row)
-- ============================================================
-- Test 13: Error handling - invalid Galilean moon body_id
-- ============================================================
SELECT 'invalid_galilean' AS test, galilean_observe(4, :boulder, now());
ERROR: galilean_observe: body_id 4 must be 0-3 (Io, Europa, Ganymede, Callisto)
-- ============================================================
-- Test 14: Error handling - invalid Saturn moon body_id
-- ============================================================
SELECT 'invalid_saturn' AS test, saturn_moon_observe(8, :boulder, now());
ERROR: saturn_moon_observe: body_id 8 must be 0-7 (Mimas-Hyperion)

152
test/sql/moon_observe.sql Normal file
View File

@ -0,0 +1,152 @@
-- moon_observe regression tests
--
-- Tests planetary moon observation (Galilean, Saturn, Uranus, Mars)
-- and Jupiter decametric radio burst prediction functions.
-- Reference distances from JPL/NASA fact sheets.
\set boulder '''40.015N 105.270W 1655m'''::observer
-- ============================================================
-- Test 1: Galilean moon observation - all four from Boulder
-- Io, Europa, Ganymede, Callisto should return valid topocentric.
-- ============================================================
SELECT 'galilean_io' AS test,
round(topo_azimuth(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_europa' AS test,
round(topo_azimuth(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(1, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_ganymede' AS test,
round(topo_azimuth(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(2, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'galilean_callisto' AS test,
round(topo_azimuth(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 2: Galilean moons should be very close to Jupiter
-- All four should have ranges within ~0.05 AU of Jupiter's range.
-- Jupiter is ~4-6 AU from Earth; moons orbit within 0.013 AU.
-- ============================================================
SELECT 'galilean_near_jupiter' AS test,
round(topo_range(planet_observe(5, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS jupiter_km,
round(topo_range(galilean_observe(0, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS io_km,
round(topo_range(galilean_observe(3, :boulder,
'2024-03-15 03:00:00+00'))::numeric, -4) AS callisto_km;
-- ============================================================
-- Test 3: Saturn moon observation - Titan (body_id=5)
-- Titan is Saturn's largest moon, should be near Saturn.
-- ============================================================
SELECT 'saturn_titan' AS test,
round(topo_azimuth(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(saturn_moon_observe(5, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 4: Saturn moons - Mimas through Iapetus all return results
-- ============================================================
SELECT 'saturn_moons' AS test,
body_id,
round(topo_range(saturn_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 7) AS body_id;
-- ============================================================
-- Test 5: Uranus moon observation - Titania (body_id=3)
-- ============================================================
SELECT 'uranus_titania' AS test,
round(topo_azimuth(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(uranus_moon_observe(3, :boulder,
'2024-06-15 03:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 6: All Uranus moons return valid results
-- ============================================================
SELECT 'uranus_moons' AS test,
body_id,
round(topo_range(uranus_moon_observe(body_id, :boulder,
'2024-06-15 03:00:00+00'))::numeric, -4) AS range_km
FROM generate_series(0, 4) AS body_id;
-- ============================================================
-- Test 7: Mars moons - Phobos and Deimos
-- ============================================================
SELECT 'mars_phobos' AS test,
round(topo_azimuth(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(0, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
SELECT 'mars_deimos' AS test,
round(topo_azimuth(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS az_deg,
round(topo_elevation(mars_moon_observe(1, :boulder,
'2024-01-15 06:00:00+00'))::numeric, 0) AS el_deg;
-- ============================================================
-- Test 8: Io phase angle - should be [0, 360)
-- ============================================================
SELECT 'io_phase' AS test,
round(io_phase_angle('2024-03-15 03:00:00+00')::numeric, 1) AS phase_deg,
io_phase_angle('2024-03-15 03:00:00+00') >= 0.0
AND io_phase_angle('2024-03-15 03:00:00+00') < 360.0 AS in_range;
-- ============================================================
-- Test 9: Jupiter CML (System III) - should be [0, 360)
-- ============================================================
SELECT 'jupiter_cml' AS test,
round(jupiter_cml(:boulder, '2024-03-15 03:00:00+00')::numeric, 1) AS cml_deg,
jupiter_cml(:boulder, '2024-03-15 03:00:00+00') >= 0.0
AND jupiter_cml(:boulder, '2024-03-15 03:00:00+00') < 360.0 AS in_range;
-- ============================================================
-- Test 10: Jupiter burst probability - known high probability zone
-- Source A: CML 200-260, Io phase 195-265 => p=0.8
-- ============================================================
SELECT 'burst_source_a' AS test,
jupiter_burst_probability(230.0, 230.0) AS p_source_a;
-- ============================================================
-- Test 11: Jupiter burst probability - known zones
-- ============================================================
SELECT 'burst_zones' AS test,
jupiter_burst_probability(90.0, 150.0) AS p_source_b,
jupiter_burst_probability(240.0, 350.0) AS p_source_c,
jupiter_burst_probability(100.0, 25.0) AS p_source_d,
jupiter_burst_probability(0.0, 130.0) AS p_quiet;
-- ============================================================
-- Test 12: Io phase changes over time (Io orbits in ~1.77 days)
-- Two times 12 hours apart should show significant phase change.
-- ============================================================
SELECT 'io_phase_changes' AS test,
round(io_phase_angle('2024-03-15 00:00:00+00')::numeric, 1) AS phase_0h,
round(io_phase_angle('2024-03-15 12:00:00+00')::numeric, 1) AS phase_12h,
abs(io_phase_angle('2024-03-15 00:00:00+00') -
io_phase_angle('2024-03-15 12:00:00+00')) > 10.0 AS changed;
-- ============================================================
-- Test 13: Error handling - invalid Galilean moon body_id
-- ============================================================
SELECT 'invalid_galilean' AS test, galilean_observe(4, :boulder, now());
-- ============================================================
-- Test 14: Error handling - invalid Saturn moon body_id
-- ============================================================
SELECT 'invalid_saturn' AS test, saturn_moon_observe(8, :boulder, now());