123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See Ephemeris.mli *)typeplanet=Mercury|Venus|Mars|Jupiter|Saturnletplanets=[Mercury;Venus;Mars;Jupiter;Saturn]letplanet_name(p:planet):string=matchpwithMercury->"Mercury"|Venus->"Venus"|Mars->"Mars"|Jupiter->"Jupiter"|Saturn->"Saturn"letrad=Celestial.degrees(*****************************************************************************)(* The planets: Kepler's ellipses *)(*****************************************************************************)(* Standish's table 1: a (AU), e, I, L, varpi, Omega (degrees) at
* J2000, then their rates per century. The Earth's is the Earth-Moon
* barycentre's, 4700 km from the Earth's centre: 6 arcsec of the Sun's
* position. *)typeelements={a:float;e:float;i:float;l:float;varpi:float;omega:float}letelements(p:planetoption):elements*elements=matchpwith|SomeMercury->({a=0.38709927;e=0.20563593;i=7.00497902;l=252.25032350;varpi=77.45779628;omega=48.33076593},{a=0.00000037;e=0.00001906;i=-0.00594749;l=149472.67411175;varpi=0.16047689;omega=-0.12534081})|SomeVenus->({a=0.72333566;e=0.00677672;i=3.39467605;l=181.97909950;varpi=131.60246718;omega=76.67984255},{a=0.00000390;e=-0.00004107;i=-0.00078890;l=58517.81538729;varpi=0.00268329;omega=-0.27769418})|None->({a=1.00000261;e=0.01671123;i=-0.00001531;l=100.46457166;varpi=102.93768193;omega=0.0},{a=0.00000562;e=-0.00004392;i=-0.01294668;l=35999.37244981;varpi=0.32327364;omega=0.0})|SomeMars->({a=1.52371034;e=0.09339410;i=1.84969142;l=-4.55343205;varpi=-23.94362959;omega=49.55953891},{a=0.00001847;e=0.00007882;i=-0.00813131;l=19140.30268499;varpi=0.44441088;omega=-0.29257343})|SomeJupiter->({a=5.20288700;e=0.04838624;i=1.30439695;l=34.39644051;varpi=14.72847983;omega=100.47390909},{a=-0.00011607;e=-0.00013253;i=-0.00183714;l=3034.74612775;varpi=0.21252668;omega=0.20469106})|SomeSaturn->({a=9.53667594;e=0.05386179;i=2.48599187;l=49.95424423;varpi=92.59887831;omega=113.66242448},{a=-0.00125060;e=-0.00050991;i=0.00193609;l=1222.49362201;varpi=-0.41897216;omega=-0.28867794})(* Newton's method on f(E) = E - e sin E - m, from E = m + e sin m:
* a handful of steps for the planets' small eccentricities *)letkepler~(e:float)(m:float):float=letrecgoe_anomalyn=letd=(e_anomaly-.(e*.sine_anomaly)-.m)/.(1.-.(e*.cose_anomaly))inlete_anomaly=e_anomaly-.dinifFloat.absd<1e-12||n=0thene_anomalyelsegoe_anomaly(n-1)ingo(m+.(e*.sinm))20letheliocentric(p:planetoption)(jd:float):float*float*float=lett=Celestial.centuriesjdinletel0,rate=elementspinleta=el0.a+.(rate.a*.t)ande=el0.e+.(rate.e*.t)inleti=rad(el0.i+.(rate.i*.t))inletl=rad(el0.l+.(rate.l*.t))inletvarpi=rad(el0.varpi+.(rate.varpi*.t))inletomega=rad(el0.omega+.(rate.omega*.t))in(* the argument of the perihelion, from the node; and the mean
* anomaly, from the perihelion *)letw=varpi-.omegainletm=Float.rem(l-.varpi)(2.*.Float.pi)inletea=kepler~emin(* on the ellipse, the Sun at the origin, x towards the perihelion *)letx'=a*.(cosea-.e)andy'=a*.sqrt(1.-.(e*.e))*.sineain(* rotated by w about the orbit's pole, tilted by i about the node
* line, turned by omega about the ecliptic's pole *)letcw=coswandsw=sinwandco=cosomegaandso=sinomegaandci=cosiandsi=siniin((((cw*.co)-.(sw*.so*.ci))*.x')+.((-.(sw*.co)-.(cw*.so*.ci))*.y'),(((cw*.so)+.(sw*.co*.ci))*.x')+.((-.(sw*.so)+.(cw*.co*.ci))*.y'),(sw*.si*.x')+.(cw*.si*.y'))(* J2000's ecliptic to J2000's equator: a tilt by the obliquity of 2000
* about the x axis, the equinox *)letequatorial_of_ecliptic((x,y,z):float*float*float):Celestial.equatorial=leteps=Celestial.obliquityCelestial.j2000inCelestial.of_vector(x,(coseps*.y)-.(sineps*.z),(sineps*.y)+.(coseps*.z))letplanet(p:planet)(jd:float):Celestial.equatorial=letx,y,z=heliocentric(Somep)jdandex,ey,ez=heliocentricNonejdinequatorial_of_ecliptic(x-.ex,y-.ey,z-.ez)letsun(jd:float):Celestial.equatorial=letex,ey,ez=heliocentricNonejdinequatorial_of_ecliptic(-.ex,-.ey,-.ez)(*****************************************************************************)(* The Moon: a mean motion and its disturbances *)(*****************************************************************************)letmoon_ecliptic(jd:float):float*float=lett=Celestial.centuriesjdinletsab=sin(rad(a+.(b*.t)))inletlon=218.32+.(481267.881*.t)+.(6.29*.s134.9477198.85)(* the equation of the centre *)-.(1.27*.s259.2(-413335.38))(* the evection *)+.(0.66*.s235.7890534.23)(* the variation *)+.(0.21*.s269.9954397.70)-.(0.19*.s357.535999.05)(* the annual equation *)-.(0.11*.s186.6966404.05)inletlat=(5.13*.s93.3483202.03)+.(0.28*.s228.2960400.87)-.(0.28*.s318.36003.18)-.(0.17*.s217.6(-407332.20))in(Celestial.normalize(radlon),radlat)letmoon(jd:float):Celestial.equatorial=letlon,lat=moon_eclipticjdinCelestial.of_ecliptic~obliquity:(Celestial.obliquityjd)lonlat(* the phase angle (Sun - Moon - Earth) is nearly 180 deg less the
* elongation (Sun - Earth - Moon), the Sun being 400 times further *)letilluminated~(sun:Celestial.equatorial)(moon:Celestial.equatorial):float=letelongation=Celestial.separationsunmoonin(1.-.coselongation)/.2.