The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
*+
*  Name:
*     palDmoon

*  Purpose:
*     Approximate geocentric position and velocity of the Moon

*  Language:
*     Starlink ANSI C

*  Type of Module:
*     Library routine

*  Invocation:
*     void palDmoon( double date, double pv[6] );

*  Arguments:
*     date = double (Given)
*        TDB as a Modified Julian Date (JD-2400000.5)
*     pv = double [6] (Returned)
*        Moon x,y,z,xdot,ydot,zdot, mean equator and
*        equinox of date (AU, AU/s)

*  Description:
*      Calculate the approximate geocentric position of the Moon
*      using a full implementation of the algorithm published by
*      Meeus (l'Astronomie, June 1984, p348).

*  Authors:
*     TIMJ: Tim Jenness (JAC, Hawaii)
*     PTW: Patrick T. Wallace
*     {enter_new_authors_here}

*  Notes:
*     - Meeus quotes accuracies of 10 arcsec in longitude, 3 arcsec in
*       latitude and 0.2 arcsec in HP (equivalent to about 20 km in
*       distance).  Comparison with JPL DE200 over the interval
*       1960-2025 gives RMS errors of 3.7 arcsec and 83 mas/hour in
*       longitude, 2.3 arcsec and 48 mas/hour in latitude, 11 km
*       and 81 mm/s in distance.  The maximum errors over the same
*       interval are 18 arcsec and 0.50 arcsec/hour in longitude,
*       11 arcsec and 0.24 arcsec/hour in latitude, 40 km and 0.29 m/s
*       in distance.
*     - The original algorithm is expressed in terms of the obsolete
*       timescale Ephemeris Time.  Either TDB or TT can be used, but
*       not UT without incurring significant errors (30 arcsec at
*       the present time) due to the Moon's 0.5 arcsec/sec movement.
*     - The algorithm is based on pre IAU 1976 standards.  However,
*       the result has been moved onto the new (FK5) equinox, an
*       adjustment which is in any case much smaller than the
*       intrinsic accuracy of the procedure.
*     - Velocity is obtained by a complete analytical differentiation
*       of the Meeus model.

*  History:
*     2012-03-07 (TIMJ):
*        Initial version based on a direct port of the SLA/F code.
*        Adapted with permission from the Fortran SLALIB library.
*     {enter_further_changes_here}

*  Copyright:
*     Copyright (C) 1998 Rutherford Appleton Laboratory
*     Copyright (C) 2012 Science and Technology Facilities Council.
*     All Rights Reserved.

*  Licence:
*     This program is free software; you can redistribute it and/or
*     modify it under the terms of the GNU General Public License as
*     published by the Free Software Foundation; either version 3 of
*     the License, or (at your option) any later version.
*
*     This program is distributed in the hope that it will be
*     useful, but WITHOUT ANY WARRANTY; without even the implied
*     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
*     PURPOSE. See the GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
*     MA 02110-1301, USA.

*  Bugs:
*     {note_any_bugs_here}
*-
*/

#include "pal.h"
#include "pal1sofa.h"
#include "palmac.h"

/* Autoconf can give us -DPIC */
#undef PIC

void palDmoon( double date, double pv[6] ) {

  /*  Seconds per Julian century (86400*36525) */
  const double CJ = 3155760000.0;

  /*  Julian epoch of B1950 */
  const double B1950 = 1949.9997904423;

  /*  Earth equatorial radius in AU ( = 6378.137 / 149597870 ) */
  const double ERADAU=4.2635212653763e-5;

  double T,THETA,SINOM,COSOM,DOMCOM,WA,DWA,WB,DWB,WOM,
    DWOM,SINWOM,COSWOM,V,DV,COEFF,EMN,EMPN,DN,FN,EN,
    DEN,DTHETA,FTHETA,EL,DEL,B,DB,BF,DBF,P,DP,SP,R,
    DR,X,Y,Z,XD,YD,ZD,SEL,CEL,SB,CB,RCB,RBD,W,EPJ,
    EQCOR,EPS,SINEPS,COSEPS,ES,EC;
  double ELP, DELP;
  double EM, DEM, EMP, DEMP, D, DD, F, DF, OM, DOM, E, DESQ, ESQ, DE;
  int N,I;

  /*
   *  Coefficients for fundamental arguments
   *
   *   at J1900:  T**0, T**1, T**2, T**3
   *   at epoch:  T**0, T**1
   *
   *  Units are degrees for position and Julian centuries for time
   *
   */

  /*  Moon's mean longitude */
  const double ELP0=270.434164;
  const double ELP1=481267.8831;
  const double ELP2=-0.001133;
  const double ELP3=0.0000019;

  /*  Sun's mean anomaly */
  const double EM0=358.475833;
  const double EM1=35999.0498;
  const double EM2=-0.000150;
  const double EM3=-0.0000033;

  /*  Moon's mean anomaly */
  const double EMP0=296.104608;
  const double EMP1=477198.8491;
  const double EMP2=0.009192;
  const double EMP3=0.0000144;

  /*  Moon's mean elongation */
  const double D0=350.737486;
  const double D1=445267.1142;
  const double D2=-0.001436;
  const double D3=0.0000019;

  /*  Mean distance of the Moon from its ascending node */
  const double F0=11.250889;
  const double F1=483202.0251;
  const double F2=-0.003211;
  const double F3=-0.0000003;

  /*  Longitude of the Moon's ascending node */
  const double OM0=259.183275;
  const double OM1=-1934.1420;
  const double OM2=0.002078;
  const double OM3=0.0000022;

  /*  Coefficients for (dimensionless) E factor */
  const double E1=-0.002495;
  const double E2=-0.00000752;

  /*  Coefficients for periodic variations etc */
  const double PAC=0.000233;
  const double PA0=51.2;
  const double PA1=20.2;
  const double PBC=-0.001778;
  const double PCC=0.000817;
  const double PDC=0.002011;
  const double PEC=0.003964;
  const double PE0=346.560;
  const double PE1=132.870;
  const double PE2=-0.0091731;
  const double PFC=0.001964;
  const double PGC=0.002541;
  const double PHC=0.001964;
  const double PIC=-0.024691;
  const double PJC=-0.004328;
  const double PJ0=275.05;
  const double PJ1=-2.30;
  const double CW1=0.0004664;
  const double CW2=0.0000754;

  /*
   *  Coefficients for Moon position
   *
   *   Tx(N)       = coefficient of L, B or P term (deg)
   *   ITx(N,1-5)  = coefficients of M, M', D, F, E**n in argument
   */
#define NL 50
#define NB 45
#define NP 31

  /*
   *  Longitude
   */
  const double TL[NL] = {
    6.28875,1.274018,.658309,.213616,-.185596,
    -.114336,.058793,.057212,.05332,.045874,.041024,-.034718,-.030465,
    .015326,-.012528,-.01098,.010674,.010034,.008548,-.00791,-.006783,
    .005162,.005,.004049,.003996,.003862,.003665,.002695,.002602,
    .002396,-.002349,.002249,-.002125,-.002079,.002059,-.001773,
    -.001595,.00122,-.00111,8.92e-4,-8.11e-4,7.61e-4,7.17e-4,7.04e-4,
    6.93e-4,5.98e-4,5.5e-4,5.38e-4,5.21e-4,4.86e-4
  };

  const int ITL[NL][5] = {
    /* M   M'  D   F   n */
    { +0, +1, +0, +0, 0 },
    { +0, -1, +2, +0, 0 },
    { +0, +0, +2, +0, 0 },
    { +0, +2, +0, +0, 0 },
    { +1, +0, +0, +0, 1 },
    { +0, +0, +0, +2, 0 },
    { +0, -2, +2, +0, 0 },
    { -1, -1, +2, +0, 1 },
    { +0, +1, +2, +0, 0 },
    { -1, +0, +2, +0, 1 },
    { -1, +1, +0, +0, 1 },
    { +0, +0, +1, +0, 0 },
    { +1, +1, +0, +0, 1 },
    { +0, +0, +2, -2, 0 },
    { +0, +1, +0, +2, 0 },
    { +0, -1, +0, +2, 0 },
    { +0, -1, +4, +0, 0 },
    { +0, +3, +0, +0, 0 },
    { +0, -2, +4, +0, 0 },
    { +1, -1, +2, +0, 1 },
    { +1, +0, +2, +0, 1 },
    { +0, +1, -1, +0, 0 },
    { +1, +0, +1, +0, 1 },
    { -1, +1, +2, +0, 1 },
    { +0, +2, +2, +0, 0 },
    { +0, +0, +4, +0, 0 },
    { +0, -3, +2, +0, 0 },
    { -1, +2, +0, +0, 1 },
    { +0, +1, -2, -2, 0 },
    { -1, -2, +2, +0, 1 },
    { +0, +1, +1, +0, 0 },
    { -2, +0, +2, +0, 2 },
    { +1, +2, +0, +0, 1 },
    { +2, +0, +0, +0, 2 },
    { -2, -1, +2, +0, 2 },
    { +0, +1, +2, -2, 0 },
    { +0, +0, +2, +2, 0 },
    { -1, -1, +4, +0, 1 },
    { +0, +2, +0, +2, 0 },
    { +0, +1, -3, +0, 0 },
    { +1, +1, +2, +0, 1 },
    { -1, -2, +4, +0, 1 },
    { -2, +1, +0, +0, 2 },
    { -2, +1, -2, +0, 2 },
    { +1, -2, +2, +0, 1 },
    { -1, +0, +2, -2, 1 },
    { +0, +1, +4, +0, 0 },
    { +0, +4, +0, +0, 0 },
    { -1, +0, +4, +0, 1 },
    { +0, +2, -1, +0, 0 }
  };

  /*
   *  Latitude
   */
  const double TB[NB] = {
    5.128189,.280606,.277693,.173238,.055413,
    .046272,.032573,.017198,.009267,.008823,.008247,.004323,.0042,
    .003372,.002472,.002222,.002072,.001877,.001828,-.001803,-.00175,
    .00157,-.001487,-.001481,.001417,.00135,.00133,.001106,.00102,
    8.33e-4,7.81e-4,6.7e-4,6.06e-4,5.97e-4,4.92e-4,4.5e-4,4.39e-4,
    4.23e-4,4.22e-4,-3.67e-4,-3.53e-4,3.31e-4,3.17e-4,3.06e-4,
    -2.83e-4
  };

  const int ITB[NB][5] = {
    /* M   M'  D   F   n */
    { +0, +0, +0, +1, 0 },
    { +0, +1, +0, +1, 0 },
    { +0, +1, +0, -1, 0 },
    { +0, +0, +2, -1, 0 },
    { +0, -1, +2, +1, 0 },
    { +0, -1, +2, -1, 0 },
    { +0, +0, +2, +1, 0 },
    { +0, +2, +0, +1, 0 },
    { +0, +1, +2, -1, 0 },
    { +0, +2, +0, -1, 0 },
    { -1, +0, +2, -1, 1 },
    { +0, -2, +2, -1, 0 },
    { +0, +1, +2, +1, 0 },
    { -1, +0, -2, +1, 1 },
    { -1, -1, +2, +1, 1 },
    { -1, +0, +2, +1, 1 },
    { -1, -1, +2, -1, 1 },
    { -1, +1, +0, +1, 1 },
    { +0, -1, +4, -1, 0 },
    { +1, +0, +0, +1, 1 },
    { +0, +0, +0, +3, 0 },
    { -1, +1, +0, -1, 1 },
    { +0, +0, +1, +1, 0 },
    { +1, +1, +0, +1, 1 },
    { -1, -1, +0, +1, 1 },
    { -1, +0, +0, +1, 1 },
    { +0, +0, -1, +1, 0 },
    { +0, +3, +0, +1, 0 },
    { +0, +0, +4, -1, 0 },
    { +0, -1, +4, +1, 0 },
    { +0, +1, +0, -3, 0 },
    { +0, -2, +4, +1, 0 },
    { +0, +0, +2, -3, 0 },
    { +0, +2, +2, -1, 0 },
    { -1, +1, +2, -1, 1 },
    { +0, +2, -2, -1, 0 },
    { +0, +3, +0, -1, 0 },
    { +0, +2, +2, +1, 0 },
    { +0, -3, +2, -1, 0 },
    { +1, -1, +2, +1, 1 },
    { +1, +0, +2, +1, 1 },
    { +0, +0, +4, +1, 0 },
    { -1, +1, +2, +1, 1 },
    { -2, +0, +2, -1, 2 },
    { +0, +1, +0, +3, 0 }
  };

  /*
   *  Parallax
   */
  const double TP[NP] = {
    .950724,.051818,.009531,.007843,.002824,
    8.57e-4,5.33e-4,4.01e-4,3.2e-4,-2.71e-4,-2.64e-4,-1.98e-4,1.73e-4,
    1.67e-4,-1.11e-4,1.03e-4,-8.4e-5,-8.3e-5,7.9e-5,7.2e-5,6.4e-5,
    -6.3e-5,4.1e-5,3.5e-5,-3.3e-5,-3e-5,-2.9e-5,-2.9e-5,2.6e-5,
    -2.3e-5,1.9e-5
  };

  const int ITP[NP][5] = {
    /* M   M'  D   F   n */
    { +0, +0, +0, +0, 0 },
    { +0, +1, +0, +0, 0 },
    { +0, -1, +2, +0, 0 },
    { +0, +0, +2, +0, 0 },
    { +0, +2, +0, +0, 0 },
    { +0, +1, +2, +0, 0 },
    { -1, +0, +2, +0, 1 },
    { -1, -1, +2, +0, 1 },
    { -1, +1, +0, +0, 1 },
    { +0, +0, +1, +0, 0 },
    { +1, +1, +0, +0, 1 },
    { +0, -1, +0, +2, 0 },
    { +0, +3, +0, +0, 0 },
    { +0, -1, +4, +0, 0 },
    { +1, +0, +0, +0, 1 },
    { +0, -2, +4, +0, 0 },
    { +0, +2, -2, +0, 0 },
    { +1, +0, +2, +0, 1 },
    { +0, +2, +2, +0, 0 },
    { +0, +0, +4, +0, 0 },
    { -1, +1, +2, +0, 1 },
    { +1, -1, +2, +0, 1 },
    { +1, +0, +1, +0, 1 },
    { -1, +2, +0, +0, 1 },
    { +0, +3, -2, +0, 0 },
    { +0, +1, +1, +0, 0 },
    { +0, +0, -2, +2, 0 },
    { +1, +2, +0, +0, 1 },
    { -2, +0, +2, +0, 2 },
    { +0, +1, -2, +2, 0 },
    { -1, -1, +4, +0, 1 }
  };

  /*  Centuries since J1900 */
  T=(date-15019.5)/36525.;

  /*
   *  Fundamental arguments (radians) and derivatives (radians per
   *  Julian century) for the current epoch
   */

  /*  Moon's mean longitude */
  ELP=PAL__DD2R*fmod(ELP0+(ELP1+(ELP2+ELP3*T)*T)*T,360.);
  DELP=PAL__DD2R*(ELP1+(2.*ELP2+3*ELP3*T)*T);

  /*  Sun's mean anomaly */
  EM=PAL__DD2R*fmod(EM0+(EM1+(EM2+EM3*T)*T)*T,360.);
  DEM=PAL__DD2R*(EM1+(2.*EM2+3*EM3*T)*T);

  /*  Moon's mean anomaly */
  EMP=PAL__DD2R*fmod(EMP0+(EMP1+(EMP2+EMP3*T)*T)*T,360.);
  DEMP=PAL__DD2R*(EMP1+(2.*EMP2+3*EMP3*T)*T);

  /*  Moon's mean elongation */
  D=PAL__DD2R*fmod(D0+(D1+(D2+D3*T)*T)*T,360.);
  DD=PAL__DD2R*(D1+(2.*D2+3.*D3*T)*T);

  /*  Mean distance of the Moon from its ascending node */
  F=PAL__DD2R*fmod(F0+(F1+(F2+F3*T)*T)*T,360.);
  DF=PAL__DD2R*(F1+(2.*F2+3.*F3*T)*T);

  /*  Longitude of the Moon's ascending node */
  OM=PAL__DD2R*fmod(OM0+(OM1+(OM2+OM3*T)*T)*T,360.);
  DOM=PAL__DD2R*(OM1+(2.*OM2+3.*OM3*T)*T);
  SINOM=sin(OM);
  COSOM=cos(OM);
  DOMCOM=DOM*COSOM;

  /*  Add the periodic variations */
  THETA=PAL__DD2R*(PA0+PA1*T);
  WA=sin(THETA);
  DWA=PAL__DD2R*PA1*cos(THETA);
  THETA=PAL__DD2R*(PE0+(PE1+PE2*T)*T);
  WB=PEC*sin(THETA);
  DWB=PAL__DD2R*PEC*(PE1+2.*PE2*T)*cos(THETA);
  ELP=ELP+PAL__DD2R*(PAC*WA+WB+PFC*SINOM);
  DELP=DELP+PAL__DD2R*(PAC*DWA+DWB+PFC*DOMCOM);
  EM=EM+PAL__DD2R*PBC*WA;
  DEM=DEM+PAL__DD2R*PBC*DWA;
  EMP=EMP+PAL__DD2R*(PCC*WA+WB+PGC*SINOM);
  DEMP=DEMP+PAL__DD2R*(PCC*DWA+DWB+PGC*DOMCOM);
  D=D+PAL__DD2R*(PDC*WA+WB+PHC*SINOM);
  DD=DD+PAL__DD2R*(PDC*DWA+DWB+PHC*DOMCOM);
  WOM=OM+PAL__DD2R*(PJ0+PJ1*T);
  DWOM=DOM+PAL__DD2R*PJ1;
  SINWOM=sin(WOM);
  COSWOM=cos(WOM);
  F=F+PAL__DD2R*(WB+PIC*SINOM+PJC*SINWOM);
  DF=DF+PAL__DD2R*(DWB+PIC*DOMCOM+PJC*DWOM*COSWOM);

  /*  E-factor, and square */
  E=1.+(E1+E2*T)*T;
  DE=E1+2.*E2*T;
  ESQ=E*E;
  DESQ=2.*E*DE;

  /*
   *  Series expansions
   */

  /*  Longitude */
  V=0.;
  DV=0.;
  for (N=NL-1; N>=0; N--) { /* DO N=NL, 1, -1 */
    COEFF=TL[N];
    EMN=(double)(ITL[N][0]);
    EMPN=(double)(ITL[N][1]);
    DN=(double)(ITL[N][2]);
    FN=(double)(ITL[N][3]);
    I=ITL[N][4];
    if (I == 0) {
      EN=1.;
      DEN=0.;
    } else if (I == 1) {
      EN=E;
      DEN=DE;
    } else {
      EN=ESQ;
      DEN=DESQ;
    }
    THETA=EMN*EM+EMPN*EMP+DN*D+FN*F;
    DTHETA=EMN*DEM+EMPN*DEMP+DN*DD+FN*DF;
    FTHETA=sin(THETA);
    V=V+COEFF*FTHETA*EN;
    DV=DV+COEFF*(cos(THETA)*DTHETA*EN+FTHETA*DEN);
  }
  EL=ELP+PAL__DD2R*V;
  DEL=(DELP+PAL__DD2R*DV)/CJ;

  /*  Latitude */
  V=0.;
  DV=0.;
  for (N=NB-1; N>=0; N--) { /* DO N=NB,1,-1 */
    COEFF=TB[N];
    EMN=(double)(ITB[N][0]);
    EMPN=(double)(ITB[N][1]);
    DN=(double)(ITB[N][2]);
    FN=(double)(ITB[N][3]);
    I=ITB[N][4];
    if (I == 0 ) {
      EN=1.;
      DEN=0.;
    } else if (I == 1) {
      EN=E;
      DEN=DE;
    } else {
      EN=ESQ;
      DEN=DESQ;
    }
    THETA=EMN*EM+EMPN*EMP+DN*D+FN*F;
    DTHETA=EMN*DEM+EMPN*DEMP+DN*DD+FN*DF;
    FTHETA=sin(THETA);
    V=V+COEFF*FTHETA*EN;
    DV=DV+COEFF*(cos(THETA)*DTHETA*EN+FTHETA*DEN);
  }
  BF=1.-CW1*COSOM-CW2*COSWOM;
  DBF=CW1*DOM*SINOM+CW2*DWOM*SINWOM;
  B=PAL__DD2R*V*BF;
  DB=PAL__DD2R*(DV*BF+V*DBF)/CJ;

  /*  Parallax */
  V=0.;
  DV=0.;
  for (N=NP-1; N>=0; N--) { /* DO N=NP,1,-1 */
    COEFF=TP[N];
    EMN=(double)(ITP[N][0]);
    EMPN=(double)(ITP[N][1]);
    DN=(double)(ITP[N][2]);
    FN=(double)(ITP[N][3]);
    I=ITP[N][4];
    if (I == 0) {
      EN=1.;
      DEN=0.;
    } else if (I == 1) {
      EN=E;
      DEN=DE;
    } else {
      EN=ESQ;
      DEN=DESQ;
    }
    THETA=EMN*EM+EMPN*EMP+DN*D+FN*F;
    DTHETA=EMN*DEM+EMPN*DEMP+DN*DD+FN*DF;
    FTHETA=cos(THETA);
    V=V+COEFF*FTHETA*EN;
    DV=DV+COEFF*(-sin(THETA)*DTHETA*EN+FTHETA*DEN);
  }
  P=PAL__DD2R*V;
  DP=PAL__DD2R*DV/CJ;

  /*
   *  Transformation into final form
   */

  /*  Parallax to distance (AU, AU/sec) */
  SP=sin(P);
  R=ERADAU/SP;
  DR=-R*DP*cos(P)/SP;

  /*  Longitude, latitude to x,y,z (AU) */
  SEL=sin(EL);
  CEL=cos(EL);
  SB=sin(B);
  CB=cos(B);
  RCB=R*CB;
  RBD=R*DB;
  W=RBD*SB-CB*DR;
  X=RCB*CEL;
  Y=RCB*SEL;
  Z=R*SB;
  XD=-Y*DEL-W*CEL;
  YD=X*DEL-W*SEL;
  ZD=RBD*CB+SB*DR;

  /*  Julian centuries since J2000 */
  T=(date-51544.5)/36525.;

  /*  Fricke equinox correction */
  EPJ=2000.+T*100.;
  EQCOR=PAL__DS2R*(0.035+0.00085*(EPJ-B1950));

  /*  Mean obliquity (IAU 1976) */
  EPS=PAL__DAS2R*(84381.448+(-46.8150+(-0.00059+0.001813*T)*T)*T);

  /*  To the equatorial system, mean of date, FK5 system */
  SINEPS=sin(EPS);
  COSEPS=cos(EPS);
  ES=EQCOR*SINEPS;
  EC=EQCOR*COSEPS;
  pv[0]=X-EC*Y+ES*Z;
  pv[1]=EQCOR*X+Y*COSEPS-Z*SINEPS;
  pv[2]=Y*SINEPS+Z*COSEPS;
  pv[3]=XD-EC*YD+ES*ZD;
  pv[4]=EQCOR*XD+YD*COSEPS-ZD*SINEPS;
  pv[5]=YD*SINEPS+ZD*COSEPS;

}