
/* 
 | $Header: /home/dieter/sweph/RCS/swejpl.c,v 1.76 2008/08/26 13:55:36 dieter Exp $
 |
 | Subroutines for reading JPL ephemerides.
 | derived from testeph.f as contained in DE403 distribution July 1995.
 | works with DE200, DE102, DE403, DE404, DE405, DE406. 
 | (attention, DE102 has 1950 reference frame and also DE4* has slightly
 | different reference frame from DE200. With DE4*, use routine 
 | IERS_FK5().)

  Authors: Dieter Koch and Alois Treindl, Astrodienst Zürich

************************************************************/
/* Copyright (C) 1997 - 2008 Astrodienst AG, Switzerland.  All rights reserved.
  
  License conditions
  ------------------

  This file is part of Swiss Ephemeris.

  Swiss Ephemeris is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  

  Swiss Ephemeris is made available by its authors under a dual licensing
  system. The software developer, who uses any part of Swiss Ephemeris
  in his or her software, must choose between one of the two license models,
  which are
  a) GNU public license version 2 or later
  b) Swiss Ephemeris Professional License

  The choice must be made before the software developer distributes software
  containing parts of Swiss Ephemeris to others, and before any public
  service using the developed software is activated.

  If the developer choses the GNU GPL software license, he or she must fulfill
  the conditions of that license, which includes the obligation to place his
  or her whole software project under the GNU GPL or a compatible license.
  See http://www.gnu.org/licenses/old-licenses/gpl-2.0.html

  If the developer choses the Swiss Ephemeris Professional license,
  he must follow the instructions as found in http://www.astro.com/swisseph/ 
  and purchase the Swiss Ephemeris Professional Edition from Astrodienst
  and sign the corresponding license contract.

  The License grants you the right to use, copy, modify and redistribute
  Swiss Ephemeris, but only under certain conditions described in the License.
  Among other things, the License requires that the copyright notices and
  this notice be preserved on all copies.

  Authors of the Swiss Ephemeris: Dieter Koch and Alois Treindl

  The authors of Swiss Ephemeris have no control or influence over any of
  the derived works, i.e. over software or services created by other
  programmers which use Swiss Ephemeris functions.

  The names of the authors or of the copyright holder (Astrodienst) must not
  be used for promoting any software, product or service which uses or contains
  the Swiss Ephemeris. This copyright notice is the ONLY place where the
  names of the authors can legally appear, except in cases where they have
  given special permission in writing.

  The trademarks 'Swiss Ephemeris' and 'Swiss Ephemeris inside' may be used
  for promoting such software, products or services.
*/


#include <string.h>
#include "swephexp.h"
#include "sweph.h"
#include "swejpl.h"

#define DEBUG_DO_SHOW	FALSE

#ifndef NO_JPL
/*
 * local globals
 */
struct jpl_save {
  char *jplfname;
  char *jplfpath;
  FILE *jplfptr;
  short do_reorder;
  double eh_cval[400]; 
  double eh_ss[3], eh_au, eh_emrat;
  int32 eh_denum, eh_ncon, eh_ipt[39];
  char ch_cnam[6*400];
  double pv[78];
  double pvsun[6];
  double buf[1500];
  double pc[18], vc[18], ac[18], jc[18];
  short do_km;
};

static struct jpl_save *FAR js;

static int state (double et, int32 *list, int do_bary, 
		  double *pv, double *pvsun, double *nut, char *serr);
static int interp(double FAR *buf, double t, double intv, int32 ncfin, 
		  int32 ncmin, int32 nain, int32 ifl, double *pv);
static int32 fsizer(char *serr);
static void reorder(char *x, int size, int number);
static int read_const_jpl(double *ss, char *serr);

/* information about eh_ipt[] and buf[]
DE200	DE102		  	DE403
3	3	  ipt[0] 	3	body 0 (mercury) starts at buf[2]
12	15	  ipt[1]	14	body 0, ncf = coefficients per component
4	2	  ipt[2]	4		na = nintervals, tot 14*4*3=168
147	93	  ipt[3]	171	body 1 (venus) starts at buf[170]
12	15	  ipt[4]	10		ncf = coefficients per component
1	1	  ipt[5]	2		total 10*2*3=60
183	138	  ipt[6]	231	body 2 (earth) starts at buf[230]
15	15	  ipt[7]	13		ncf = coefficients per component
2	2	  ipt[8]	2		total 13*2*3=78
273	228	  ipt[9]	309	body 3 (mars) starts at buf[308]
10	10	  ipt[10]	11		ncf = coefficients per component
1	1	  ipt[11]	1		total 11*1*3=33
303	258	  ipt[12]	342	body 4 (jupiter) at buf[341]
9	9	  ipt[13]	8		total 8 * 1 * 3 = 24
1	1	  ipt[14]	1
330	285	  ipt[15]	366	body 5 (saturn) at buf[365]
8	8	  ipt[16]	7		total 7 * 1 * 3 = 21
1	1	  ipt[17]	1
354	309	  ipt[18]	387	body 6 (uranus) at buf[386]
8	8	  ipt[19]	6		total 6 * 1 * 3 = 18
1	1	  ipt[20]	1
378	333	  ipt[21]	405	body 7 (neptune) at buf[404]
6	6	  ipt[22]	6		total 18
1	1	  ipt[23]	1
396	351	  ipt[24]	423	body 8 (pluto) at buf[422]
6	6	  ipt[25]	6		total 18
1	1	  ipt[26]	1
414	369	  ipt[27]	441	body 9 (moon) at buf[440]
12	15	  ipt[28]	13		total 13 * 8 * 3 = 312
8	8	  ipt[29]	8
702	729	  ipt[30]	753	SBARY SUN, starts at buf[752]
15	15	  ipt[31]	11	SBARY SUN, ncf = coeff per component
1	1	  ipt[32]	2		   total 11*2*3=66
747	774	  ipt[33]	819	nutations, starts at buf[818]
10	0	  ipt[34]	10		total 10 * 4 * 2 = 80
4	0	  ipt[35]	4	(nutation only two coordinates)
0	0	  ipt[36]	899	librations, start at buf[898]
0	0	  ipt[37]	10		total 10 * 4 * 3 = 120
0	0	  ipt[38]	4
		
					last element of buf[1017]
  buf[0] contains start jd and buf[1] end jd of segment;
  each segment is 32 days in de403, 64 days in DE102, 32 days in  DE200

  Length of blocks: DE406 = 1456*4=5824 bytes = 728 double
                    DE405 = 2036*4=8144 bytes = 1018 double
		    DE404 = 1456*4=5824 bytes = 728 double
                    DE403 = 2036*4=8144 bytes = 1018 double
		    DE200 = 1652*4=6608 bytes = 826 double
		    DE102 = 1546*4=6184 bytes = 773 double
		    each DE102 record has 53*8=424 fill bytes so that
		    the records have the same length as DE200.
*/

/*
 * This subroutine opens the file jplfname, with a phony record length, 
 * reads the first record, and uses the info to compute ksize, 
 * the number of single precision words in a record. 
 * RETURN: ksize (record size of ephemeris data)
 * jplfptr is opened on return.
 * note 26-aug-2008: now record size is computed by fsizer(), not 
 * set to a fixed value depending as in previous releases. The caller of
 * fsizer() will verify by data comparison whether it computed correctly.
 */
static int32 fsizer(char *serr)
{
  /* Local variables */
  int32 ncon; 
  double emrat;
  int32 numde;
  double au, ss[3];
  int i, kmx, khi, nd;
  int32 ksize, lpt[3];
  char ttl[6*14*3];	
  if ((js->jplfptr = swi_fopen(SEI_FILE_PLANET, js->jplfname, js->jplfpath, serr)) == NULL) {
    return NOT_AVAILABLE;
  }
  /* ttl = ephemeris title, e.g.
   * "JPL Planetary Ephemeris DE404/LE404
   *  Start Epoch: JED=   625296.5-3001 DEC 21 00:00:00
   *  Final Epoch: JED=  2817168.5 3001 JAN 17 00:00:00c */
  fread((void *) &ttl[0], 1, 252, js->jplfptr);
  /* cnam = names of constants */
  fread((void *) js->ch_cnam, 1, 6*400, js->jplfptr);
  /* ss[0] = start epoch of ephemeris
   * ss[1] = end epoch
   * ss[2] = segment size in days */
  fread((void *) &ss[0], sizeof(double), 3, js->jplfptr);
  /* reorder ? */
  if (ss[2] < 1 || ss[2] > 200) 
    js->do_reorder = TRUE;
  else 
    js->do_reorder = 0;
  for (i = 0; i < 3; i++)
    js->eh_ss[i] = ss[i];
  if (js->do_reorder)
    reorder((char *) &js->eh_ss[0], sizeof(double), 3);
  /* plausibility test of these constants. Start and end date must be
   * between -20000 and +20000, segment size >= 1 and <= 200 */
  if (js->eh_ss[0] < -5583942 || js->eh_ss[1] > 9025909 || js->eh_ss[2] < 1 || js->eh_ss[2] > 200) {
    if (serr != NULL)
      sprintf(serr, "alleged ephemeris file (%s) has invalid format.", js->jplfname);
    return(NOT_AVAILABLE);
  }
  /* ncon = number of constants */
  fread((void *) &ncon, sizeof(int32), 1, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &ncon, sizeof(int32), 1);
  /* au = astronomical unit */
  fread((void *) &au, sizeof(double), 1, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &au, sizeof(double), 1);
  /* emrat = earth moon mass ratio */
  fread((void *) &emrat, sizeof(double), 1, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &emrat, sizeof(double), 1);
  /* ipt[i+0]: coefficients of planet i start at buf[ipt[i+0]-1] 
   * ipt[i+1]: number of coefficients (interpolation order - 1)
   * ipt[i+2]: number of intervals in segment */
  fread((void *) &js->eh_ipt[0], sizeof(int32), 36, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &js->eh_ipt[0], sizeof(int32), 36);
  /* numde = number of jpl ephemeris "404" with de404 */
  fread((void *) &numde, sizeof(int32), 1, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &numde, sizeof(int32), 1);
  /* read librations */
  fread(&lpt[0], sizeof(int32), 3, js->jplfptr);
  if (js->do_reorder)
    reorder((char *) &lpt[0], sizeof(int32), 3);
  /* fill librations into eh_ipt[36]..[38] */
  for (i = 0; i < 3; ++i) 
    js->eh_ipt[i + 36] = lpt[i];
  rewind(js->jplfptr);
  /*  find the number of ephemeris coefficients from the pointers */
  /* re-activated this code on 26-aug-2008 */
  kmx = 0;
  khi = 0;
  for (i = 0; i < 13; i++) {
    if (js->eh_ipt[i * 3] > kmx) {
      kmx = js->eh_ipt[i * 3];
      khi = i + 1;
    }
  }
  if (khi == 12) 
    nd = 2;
  else
    nd = 3;
  ksize = (js->eh_ipt[khi * 3 - 3] + nd * js->eh_ipt[khi * 3 - 2] * js->eh_ipt[khi * 3 - 1] - 1L) * 2L;
  /*
   * de102 files give wrong ksize, because they contain 424 empty bytes 
   * per record. Fixed by hand!
   */
  if (ksize == 1546) 
    ksize = 1652;
#if 0		/* we prefer to compute ksize to be comaptible
                   with new DE releases */
  switch (numde) {
    case 403:
    case 405:
    case 410:
    case 413:
    case 414:
    case 418:
    case 421:
      ksize = 2036;
      break;
    case 404:
    case 406:
      ksize = 1456;
      break;
    case 200:
      ksize = 1652;
      break;
    case 102:
      ksize = 1652;     /* de102 is filled with blanks to length of de200 */
      break;
    default:
      if (serr != NULL)
	sprintf(serr,"unknown numde value %d;", numde);
      return ERR;
  }
#endif
  if (ksize < 1000 || ksize > 5000) {
    if (serr != NULL)
      sprintf(serr, "JPL ephemeris file does not provide valid ksize (%d)", ksize);/**/
    return NOT_AVAILABLE;
  }
  return ksize;
} 

/*
 *     This subroutine reads the jpl planetary ephemeris 
 *     and gives the position and velocity of the point 'ntarg' 
 *     with respect to 'ncent'. 
 *     calling sequence parameters: 
 *       et = d.p. julian ephemeris date at which interpolation 
 *            is wanted. 
 *       ** note the entry dpleph for a doubly-dimensioned time ** 
 *          the reason for this option is discussed in the 
 *          subroutine state 
 *     ntarg = integer number of 'target' point. 
 *     ncent = integer number of center point. 
 *            the numbering convention for 'ntarg' and 'ncent' is: 
 *                0 = mercury           7 = neptune 
 *                1 = venus             8 = pluto 
 *                2 = earth             9 = moon 
 *                3 = mars             10 = sun 
 *                4 = jupiter          11 = solar-system barycenter 
 *                5 = saturn           12 = earth-moon barycenter 
 *                6 = uranus           13 = nutations (longitude and obliq) 
 *                                     14 = librations, if on eph file 
 *             (if nutations are wanted, set ntarg = 13. for librations, 
 *              set ntarg = 14. set ncent=0.) 
 *      rrd = output 6-word d.p. array containing position and velocity 
 *            of point 'ntarg' relative to 'ncent'. the units are au and 
 *            au/day. for librations the units are radians and radians 
 *            per day. in the case of nutations the first four words of 
 *            rrd will be set to nutations and rates, having units of 
 *            radians and radians/day. 
 *            The option is available to have the units in km and km/sec. 
 *            For this, set do_km=TRUE (default FALSE). 
 */
int swi_pleph(double et, int ntarg, int ncent, double *rrd, char *serr)
{
  int i, retc;
  int32 list[12];
  double FAR *pv = js->pv;
  double FAR *pvsun = js->pvsun;
  for (i = 0; i < 6; ++i) 
    rrd[i] = 0.0;
  if (ntarg == ncent) 
      return 0;
  for (i = 0; i < 12; ++i) 
    list[i] = 0;
  /*     check for nutation call */
  if (ntarg == J_NUT) {
    if (js->eh_ipt[34] > 0) {
      list[10] = 2;
      return(state(et, list, FALSE, pv, pvsun, rrd, serr));
    } else {
      if (serr != NULL) 
	sprintf(serr,"No nutations on the JPL ephemeris file;");
      return (NOT_AVAILABLE);
    }
  }
  if (ntarg == J_LIB) {
    if (js->eh_ipt[37] > 0) {
      list[11] = 2;
      if ((retc = state(et, list, FALSE, pv, pvsun, rrd, serr)) != OK)
	return (retc);
      for (i = 0; i < 6; ++i) 
	rrd[i] = pv[i + 60];
      return 0;
    } else {
      if (serr != NULL) 
	sprintf(serr,"No librations on the ephemeris file;");
      return (NOT_AVAILABLE);
    }
  }
  /* set up proper entries in 'list' array for state call */
  if (ntarg < J_SUN) 
    list[ntarg] = 2;
  if (ntarg == J_MOON) 	/* Mooon needs Earth */
    list[J_EARTH] = 2;
  if (ntarg == J_EARTH) 	/* Earth needs Moon */
    list[J_MOON] = 2;
  if (ntarg == J_EMB) 	/* EMB needs Earth */
    list[J_EARTH] = 2;
  if (ncent < J_SUN) 
    list[ncent] = 2;
  if (ncent == J_MOON) 	/* Mooon needs Earth */
    list[J_EARTH] = 2;
  if (ncent == J_EARTH) 	/* Earth needs Moon */
    list[J_MOON] = 2;
  if (ncent == J_EMB) 	/* EMB needs Earth */
    list[J_EARTH] = 2;
  if ((retc = state(et, list, TRUE, pv, pvsun, rrd, serr)) != OK)
    return (retc);
  if (ntarg == J_SUN || ncent == J_SUN) {
    for (i = 0; i < 6; ++i) 
      pv[i + 6*J_SUN] = pvsun[i];
  }
  if (ntarg == J_SBARY || ncent == J_SBARY) {
    for (i = 0; i < 6; ++i) {
      pv[i + 6*J_SBARY] = 0.;
    }
  }
  if (ntarg == J_EMB || ncent == J_EMB) {
    for (i = 0; i < 6; ++i) 
      pv[i + 6*J_EMB] = pv[i + 6*J_EARTH];
  }
  if ((ntarg==J_EARTH && ncent==J_MOON) || (ntarg == J_MOON && ncent==J_EARTH)){
    for (i = 0; i < 6; ++i) 
      pv[i + 6*J_EARTH] = 0.;
    
  } else {
    if (list[J_EARTH] == 2) {
      for (i = 0; i < 6; ++i) 
	pv[i + 6*J_EARTH] -= pv[i + 6*J_MOON] / (js->eh_emrat + 1.);
    }
    if (list[J_MOON] == 2) {
      for (i = 0; i < 6; ++i) {
	pv[i + 6*J_MOON] += pv[i + 6*J_EARTH];
      }
    }
  }
  for (i = 0; i < 6; ++i) 
    rrd[i] = pv[i + ntarg * 6] - pv[i + ncent * 6];
  return OK;
} 

/*
 *  This subroutine differentiates and interpolates a 
 *  set of chebyshev coefficients to give pos, vel, acc, and jerk 
 *  calling sequence parameters: 
 *    input: 
 *     buf   1st location of array of d.p. chebyshev coefficients of position
 *        t   is dp fractional time in interval covered by 
 *            coefficients at which interpolation is wanted, 0 <= t <= 1 
 *     intv   is dp length of whole interval in input time units. 
 *      ncf   number of coefficients per component 
 *      ncm   number of components per set of coefficients 
 *       na   number of sets of coefficients in full array 
 *            (i.e., number of sub-intervals in full interval) 
 *       ifl   int flag: =1 for positions only 
 *                      =2 for pos and vel 
 *                      =3 for pos, vel, and acc 
 *                      =4 for pos, vel, acc, and jerk 
 *    output: 
 *      pv   d.p. interpolated quantities requested. 
 *           assumed dimension is pv(ncm,fl). 
 */
static int interp(double FAR *buf, double t, double intv, int32 ncfin, 
		  int32 ncmin, int32 nain, int32 ifl, double *pv)
{
  /* Initialized data */
  static int FAR np, nv;
  static int FAR nac;
  static int FAR njk;
  static double FAR twot = 0.;
  double FAR *pc = js->pc;
  double FAR *vc = js->vc;
  double FAR *ac = js->ac;
  double FAR *jc = js->jc;
  int ncf = (int) ncfin;
  int ncm = (int) ncmin;
  int na = (int) nain;
  /* Local variables */
  double temp;
  int i, j, ni;
  double tc;
  double dt1, bma;
  double bma2, bma3;
  /*
   | get correct sub-interval number for this set of coefficients and then
   | get normalized chebyshev time within that subinterval. 
   */
  if (t >= 0)
    dt1 = floor(t);
  else
    dt1 = -floor(-t);
  temp = na * t;
  ni = (int) (temp - dt1);
  /* tc is the normalized chebyshev time (-1 <= tc <= 1) */
  tc = (fmod(temp, 1.0) + dt1) * 2. - 1.;
  /*
   *  check to see whether chebyshev time has changed, 
   *  and compute new polynomial values if it has. 
   *  (the element pc(2) is the value of t1(tc) and hence 
   *  contains the value of tc on the previous call.) 
   */
  if (tc != pc[1]) {
    np = 2;
    nv = 3;
    nac = 4;
    njk = 5;
    pc[1] = tc;
    twot = tc + tc;
  }
  /*
   *  be sure that at least 'ncf' polynomials have been evaluated 
   *  and are stored in the array 'pc'. 
   */
  if (np < ncf) {
    for (i = np; i < ncf; ++i) 
      pc[i] = twot * pc[i - 1] - pc[i - 2];
    np = ncf;
  }
  /*  interpolate to get position for each component */
  for (i = 0; i < ncm; ++i) {
    pv[i] = 0.;
    for (j = ncf-1; j >= 0; --j) 
      pv[i] += pc[j] * buf[j + (i + ni * ncm) * ncf];
  }
  if (ifl <= 1) 
    return 0;
  /*
   *       if velocity interpolation is wanted, be sure enough 
   *       derivative polynomials have been generated and stored. 
   */
  bma = (na + na) / intv;
  vc[2] = twot + twot;
  if (nv < ncf) {
    for (i = nv; i < ncf; ++i) 
      vc[i] = twot * vc[i - 1] + pc[i - 1] + pc[i - 1] - vc[i - 2];
    nv = ncf;
  }
  /*       interpolate to get velocity for each component */
  for (i = 0; i < ncm; ++i) {
    pv[i + ncm] = 0.;
    for (j = ncf-1; j >= 1; --j) 
      pv[i + ncm] += vc[j] * buf[j + (i + ni * ncm) * ncf];
    pv[i + ncm] *= bma;
  }
  if (ifl == 2) 
    return 0;
  /*       check acceleration polynomial values, and */
  /*       re-do if necessary */
  bma2 = bma * bma;
  ac[3] = pc[1] * 24.;
  if (nac < ncf) {
    nac = ncf;
    for (i = nac; i < ncf; ++i) 
      ac[i] = twot * ac[i - 1] + vc[i - 1] * 4. - ac[i - 2];
  }
  /*       get acceleration for each component */
  for (i = 0; i < ncm; ++i) {
    pv[i + ncm * 2] = 0.;
    for (j = ncf-1; j >= 2; --j) 
      pv[i + ncm * 2] += ac[j] * buf[j + (i + ni * ncm) * ncf];
    pv[i + ncm * 2] *= bma2;
  }
  if (ifl == 3) 
      return 0;
  /*       check jerk polynomial values, and */
  /*       re-do if necessary */
  bma3 = bma * bma2;
  jc[4] = pc[1] * 192.;
  if (njk < ncf) {
    njk = ncf;
    for (i = njk; i < ncf; ++i) 
      jc[i] = twot * jc[i - 1] + ac[i - 1] * 6. - jc[i - 2];
  }
  /*       get jerk for each component */
  for (i = 0; i < ncm; ++i) {
    pv[i + ncm * 3] = 0.;
    for (j = ncf-1; j >= 3; --j) 
      pv[i + ncm * 3] += jc[j] * buf[j + (i + ni * ncm) * ncf];
    pv[i + ncm * 3] *= bma3;
  }
  return 0;
}

/*
 | ********** state ********************
 | this subroutine reads and interpolates the jpl planetary ephemeris file 
 |  calling sequence parameters: 
 |  input: 
 |     et    dp julian ephemeris epoch at which interpolation is wanted.
 |     list  12-word integer array specifying what interpolation 
 |           is wanted for each of the bodies on the file. 
 |                      list(i)=0, no interpolation for body i 
 |                             =1, position only 
 |                             =2, position and velocity 
 |            the designation of the astronomical bodies by i is: 
 |                      i = 0: mercury 
 |                        = 1: venus 
 |                        = 2: earth-moon barycenter, NOT earth! 
 |                        = 3: mars 
 |                        = 4: jupiter 
 |                        = 5: saturn 
 |                        = 6: uranus 
 |                        = 7: neptune 
 |                        = 8: pluto 
 |                        = 9: geocentric moon 
 |                        =10: nutations in longitude and obliquity 
 |                        =11: lunar librations (if on file) 
 |            If called with list = NULL, only the header records are read and
 |            stored in the global areas.
 |  do_bary   short, if true, barycentric, if false, heliocentric.
 |              only the 9 planets 0..8 are affected by it.
 |  output: 
 |       pv   dp 6 x 11 array that will contain requested interpolated 
 |            quantities.  the body specified by list(i) will have its 
 |            state in the array starting at pv(1,i).  (on any given 
 |            call, only those words in 'pv' which are affected by the 
 |            first 10 'list' entries (and by list(11) if librations are 
 |            on the file) are set.  the rest of the 'pv' array 
 |            is untouched.)  the order of components starting in 
 |            pv is: x,y,z,dx,dy,dz. 
 |            all output vectors are referenced to the earth mean 
 |            equator and equinox of epoch. the moon state is always 
 |            geocentric; the other nine states are either heliocentric 
 |            or solar-system barycentric, depending on the setting of 
 |            common flags (see below). 
 |            lunar librations, if on file, are put into pv(k,10) if 
 |            list(11) is 1 or 2. 
 |    pvsun   dp 6-word array containing the barycentric position and 
 |            velocity of the sun.
 |      nut   dp 4-word array that will contain nutations and rates, 
 |            depending on the setting of list(10).  the order of 
 |            quantities in nut is: 
 |                     d psi  (nutation in longitude) 
 |                     d epsilon (nutation in obliquity) 
 |                     d psi dot 
 |                     d epsilon dot 
 |  globals used:
 |    do_km   logical flag defining physical units of the output states. 
 |            TRUE = return km and km/sec, FALSE = return au and au/day 
 |            default value = FALSE  (km determines time unit 
 |            for nutations and librations.  angle unit is always radians.)
 */
static int state(double et, int32 *list, int do_bary, 
	  double *pv, double *pvsun, double *nut, char *serr)
{
  int i, j, k;
  int32 flen, nseg, nb;
  double FAR *buf = js->buf;
  double aufac, s, t, intv, ts[4];
  int32 nrecl, ksize;
  int32 nr;
  double et_mn, et_fr;
  int32 FAR *ipt = js->eh_ipt;
  char ch_ttl[252];
  static int32 irecsz;
  static int32 nrl, lpt[3], ncoeffs;
  if (js->jplfptr == NULL) {
    ksize = fsizer(serr); /* the number of single precision words in a record */
    nrecl = 4;
    if (ksize == NOT_AVAILABLE)
      return NOT_AVAILABLE;
    irecsz = nrecl * ksize; 	/* record size in bytes */
    ncoeffs = ksize / 2;	/* # of coefficients, doubles */
    /* ttl = ephemeris title, e.g.
     * "JPL Planetary Ephemeris DE404/LE404
     *  Start Epoch: JED=   625296.5-3001 DEC 21 00:00:00
     *  Final Epoch: JED=  2817168.5 3001 JAN 17 00:00:00c */
    fread((void *) ch_ttl, 1, 252, js->jplfptr);
    /* cnam = names of constants */
    fread((void *) js->ch_cnam, 1, 2400, js->jplfptr);
    /* ss[0] = start epoch of ephemeris
     * ss[1] = end epoch
     * ss[2] = segment size in days */
    fread((void *) &js->eh_ss[0], sizeof(double), 3, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_ss[0], sizeof(double), 3);
    /* ncon = number of constants */
    fread((void *) &js->eh_ncon, sizeof(int32), 1, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_ncon, sizeof(int32), 1);
    /* au = astronomical unit */
    fread((void *) &js->eh_au, sizeof(double), 1, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_au, sizeof(double), 1);
    /* emrat = earth moon mass ratio */
    fread((void *) &js->eh_emrat, sizeof(double), 1, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_emrat, sizeof(double), 1);
    /* ipt[i+0]: coefficients of planet i start at buf[ipt[i+0]-1] 
     * ipt[i+1]: number of coefficients (interpolation order - 1)
     * ipt[i+2]: number of intervals in segment */
    fread((void *) &ipt[0], sizeof(int32), 36, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &ipt[0], sizeof(int32), 36);
    /* numde = number of jpl ephemeris "404" with de404 */
    fread((void *) &js->eh_denum, sizeof(int32), 1, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_denum, sizeof(int32), 1);
    fread((void *) &lpt[0], sizeof(int32), 3, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &lpt[0], sizeof(int32), 3);
    /* cval[]:  other constants in next record */
    fseek(js->jplfptr, 1L * irecsz, 0);
    fread((void *) &js->eh_cval[0], sizeof(double), 400, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &js->eh_cval[0], sizeof(double), 400);
    /* new 26-aug-2008: verify correct block size */
#if 0
    sp = strstr(js->ch_cnam, "EMRAT ");
    if (sp == NULL) {
      if (serr != NULL) 
	sprintf(serr, "JPL ephemeris file strange, constant name 'EMRAT' missing");
      return ERR;
    }
    i = (sp - js->ch_cnam);
    if (i % 6 != 0) {
      if (serr != NULL) 
	sprintf(serr, "JPL ephemeris file strange, constant name 'EMRAT' not at multiple of 6");
      return ERR;
    }
    i = i / 6;	/* position of EMRAT in constant array eh_cval */
    if (js->eh_cval[i] != js->eh_emrat) {
      if (serr != NULL) 
	sprintf(serr, "JPL ephemeris file error, record size failed EMRAT check");
      return ERR;
    }
#endif
    for (i = 0; i < 3; ++i) 
      ipt[i + 36] = lpt[i];
    nrl = 0;
    /* is file length correct? */
    /* file length */
    fseek(js->jplfptr, 0L, SEEK_END);
    flen = ftell(js->jplfptr);
    /* # of segments in file */
    nseg = (int32) ((js->eh_ss[1] - js->eh_ss[0]) / js->eh_ss[2]);	
    /* sum of all cheby coeffs of all planets and segments */
    for(i = 0, nb = 0; i < 13; i++) {		
      k = 3;
      if (i == 11)
	k = 2;
      nb += (ipt[i*3+1] * ipt[i*3+2]) * k * nseg;
    }
    /* add start and end epochs of segments */
    nb += 2 * nseg; 
    /* doubles to bytes */
    nb *= 8;
    /* add size of header and constants section */
    nb += 2 * ksize * nrecl;
#if 0
    printf("hallo %d %d\n", nb, flen);
    printf("hallo %d %d\n", nb-flen, ksize);
#endif
    if (flen != nb 
      /* some of our files are one record too long */
      && flen - nb != ksize * nrecl) {
      if (serr != NULL) {
	sprintf(serr, "JPL ephemeris file is mutilated; length = %d instead of %d.", flen, nb);
	if (strlen(serr) + strlen(js->jplfname) < AS_MAXCH - 1)
	  sprintf(serr, "JPL ephemeris file %s is mutilated; length = %d instead of %d.", js->jplfname, flen, nb);
      }
      return(NOT_AVAILABLE);
    }
    /* check if start and end dates in segments are the same as in 
     * file header */
    fseek(js->jplfptr, 2L * irecsz, 0);
    fread((void *) &ts[0], sizeof(double), 2, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &ts[0], sizeof(double), 2);
    fseek(js->jplfptr, (nseg + 2 - 1) * irecsz, 0);
    fread((void *) &ts[2], sizeof(double), 2, js->jplfptr);
    if (js->do_reorder)
      reorder((char *) &ts[2], sizeof(double), 2);
    if (ts[0] != js->eh_ss[0] || ts[3] != js->eh_ss[1]) {
      if (serr != NULL)
	strcpy(serr, "JPL ephemeris file is corrupt; start/end date check failed.");
      return NOT_AVAILABLE;
    }
  }
  if (list == NULL) 
    return 0;
  s = et - .5;
  et_mn = floor(s);
  et_fr = s - et_mn;	/* fraction of days since previous midnight */
  et_mn += .5;	/* midnight before epoch */
  /*       error return for epoch out of range */
  if (et < js->eh_ss[0] || et > js->eh_ss[1]) {
    if (serr != NULL) 
      sprintf(serr,"jd %f outside JPL eph. range %.2f .. %.2f;", et, js->eh_ss[0], js->eh_ss[1]);
    return BEYOND_EPH_LIMITS;
  }
  /*       calculate record # and relative time in interval */
  nr = (int32) ((et_mn - js->eh_ss[0]) / js->eh_ss[2]) + 2;
  if (et_mn == js->eh_ss[1]) 
    --nr;	/* end point of ephemeris, use last record */
  t = (et_mn - ((nr - 2) * js->eh_ss[2] + js->eh_ss[0]) + et_fr) / js->eh_ss[2];
  /* read correct record if not in core */
  if (nr != nrl) {
    nrl = nr;
    if (fseek(js->jplfptr, nr * irecsz, 0) != 0) {
      if (serr != NULL) 
	sprintf(serr, "Read error in JPL eph. at %f\n", et);
      return NOT_AVAILABLE;
    }
    for (k = 1; k <= ncoeffs; ++k) {
      if ( fread((void *) &buf[k - 1], sizeof(double), 1, js->jplfptr) != 1) {
	if (serr != NULL) 
	  sprintf(serr, "Read error in JPL eph. at %f\n", et);
	return NOT_AVAILABLE;
      }
      if (js->do_reorder)
	reorder((char *) &buf[k-1], sizeof(double), 1);
    }
  }
  if (js->do_km) {
    intv = js->eh_ss[2] * 86400.;
    aufac = 1.;
  } else {
    intv = js->eh_ss[2];
    aufac = 1. / js->eh_au;
  }
  /*   interpolate ssbary sun */
  interp(&buf[(int) ipt[30] - 1], t, intv, ipt[31], 3L, ipt[32], 2L, pvsun);
  for (i = 0; i < 6; ++i) {
    pvsun[i] *= aufac;
  }
  /*   check and interpolate whichever bodies are requested */
  for (i = 0; i < 10; ++i) {
    if (list[i] > 0) {
      interp(&buf[(int) ipt[i * 3] - 1], t, intv, ipt[i * 3 + 1], 3L,
	     ipt[i * 3 + 2], list[i], &pv[i * 6]);
      for (j = 0; j < 6; ++j) {
	if (i < 9 && ! do_bary) {
	  pv[j + i * 6] = pv[j + i * 6] * aufac - pvsun[j];
	} else {
	  pv[j + i * 6] *= aufac;
	}
      }
    }
  }
  /*       do nutations if requested (and if on file) */
  if (list[10] > 0 && ipt[34] > 0) {
    interp(&buf[(int) ipt[33] - 1], t, intv, ipt[34], 2L, ipt[35],
	     list[10], nut);
  }
  /*       get librations if requested (and if on file) */
  if (list[11] > 0 && ipt[37] > 0) {
    interp(&buf[(int) ipt[36] - 1], t, intv, ipt[37], 3L, ipt[38], list[1],
	    &pv[60]);
  }
  return OK;
} 

/* 
 *  this entry obtains the constants from the ephemeris file 
 *  call state to initialize the ephemeris and read in the constants 
 */
static int read_const_jpl(double *ss,  char *serr)
{
  int i, retc;
  retc = state(0.0, NULL, FALSE, NULL, NULL, NULL, serr);
  if (retc != OK)
    return (retc);
  for (i = 0; i < 3; i++)
    ss[i] = js->eh_ss[i];
#if DEBUG_DO_SHOW
  {
    static char FAR *bname[] = {
	"Mercury", "Venus", "EMB", "Mars", "Jupiter", "Saturn", 
	"Uranus", "Neptune", "Pluto", "Moon", "SunBary", "Nut", "Libr"};
    int j, k;
    int32 nb, nc;
    printf(" JPL TEST-EPHEMERIS program.  Version October 1995.\n");
    for (i = 0; i < 13; i++) {
      j = i * 3;
      k = 3;
      if (i == 11) k = 2;
      nb = js->eh_ipt[j+1] * js->eh_ipt[j+2] * k; 
      nc = (int32) (nb * 36525L / js->eh_ss[2] * 8L);
      printf("%s\t%d\tipt[%d]\t%3ld %2ld %2ld,\t",
	bname[i], i, j, js->eh_ipt[j], js->eh_ipt[j+1], js->eh_ipt[j+2]);
      printf("%3ld double, bytes per century = %6ld\n", nb, nc);
      fflush(stdout);
    }
    printf("%16.2f %16.2f %16.2f\n", js->eh_ss[0], js->eh_ss[1], js->eh_ss[2]);
    for (i = 0; i < js->eh_ncon; ++i) 
      printf("%.6s\t%24.16f\n", js->ch_cnam + i * 6, js->eh_cval[i]);
    fflush(stdout);
  }
#endif
  return OK;
} 

static void reorder(char *x, int size, int number) 
{
  int i, j;
  char s[8];
  char *sp1 = x;
  char *sp2 = &s[0];
  for (i = 0; i < number; i++) {
    for (j = 0; j < size; j++) 
      *(sp2 + j) = *(sp1 + size - j - 1);
    for (j = 0; j < size; j++) 
      *(sp1 + j) = *(sp2 + j);
    sp1 += size;
  }
}

void swi_close_jpl_file(void)
{
  if (js != NULL) {
    if (js->jplfptr != NULL)
      fclose(js->jplfptr);
    if (js->jplfname != NULL) 
      FREE((void *) js->jplfname);
    if (js->jplfpath != NULL) 
      FREE((void *) js->jplfpath);
    FREE((void *) js);
    js = NULL;
  }
}

int swi_open_jpl_file(double *ss, char *fname, char *fpath, char *serr)
{
  int retc = OK;
  /* if open, return */
  if (js != NULL && js->jplfptr != NULL)
    return OK;
  if ((js = (struct jpl_save *) CALLOC(1, sizeof(struct jpl_save))) == NULL
    || (js->jplfname = MALLOC(strlen(fname)+1)) == NULL
    || (js->jplfpath = MALLOC(strlen(fpath)+1)) == NULL
    ) {
    if (serr != NULL)
      strcpy(serr, "error in malloc() with JPL ephemeris.");
    return ERR;
  }
  strcpy(js->jplfname, fname);
  strcpy(js->jplfpath, fpath);
  retc = read_const_jpl(ss, serr);
  if (retc != OK) 
    swi_close_jpl_file();
  else {
    /* intializations for function interpol() */
    js->pc[0] = 1;
    js->pc[1] = 2;
    js->vc[1] = 1;
    js->ac[2] = 4;
    js->jc[3] = 24;
  }
  return retc;
}

int32 swi_get_jpl_denum()
{
  return js->eh_denum;
}
#endif	/* NO_JPL */

