[comp.sources.misc] v09i041: ephem, v4.8, 4 of 5

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (11/28/89)

Posting-number: Volume 9, Issue 41
Submitted-by: ecd@umn-cs.cs.umn.edu@ncs-med.UUCP (Elwood C. Downey)
Archive-name: ephem2/part04

# This is a "shell archive" file; run it with sh.
# This is file 4.
echo x plans.c
cat > plans.c << 'xXx'
#include <stdio.h>
#include <math.h>
#include "astro.h"

#define	TWOPI		(2*PI)
#define	mod2PI(x)	((x) - (int)((x)/TWOPI)*TWOPI)

/* given a modified Julian date, mjd, and a planet, p, find:
 *   lpd0: heliocentric longitude, 
 *   psi0: heliocentric latitude,
 *   rp0:  distance from the sun to the planet, 
 *   rho0: distance from the Earth to the planet,
 *         none corrected for light time, ie, they are the true values for the
 *         given instant.
 *   lam:  geocentric ecliptic longitude, 
 *   bet:  geocentric ecliptic latitude,
 *         each corrected for light time, ie, they are the apparent values as
 *	   seen from the center of the Earth for the given instant.
 *   dia:  angular diameter in arcsec at 1 AU, 
 *   mag:  visual magnitude when 1 AU from sun and earth at 0 phase angle.
 *
 * all angles are in radians, all distances in AU.
 * the mean orbital elements are found by calling pelement(), then mutual
 *   perturbation corrections are applied as necessary.
 *
 * corrections for nutation and abberation must be made by the caller. The RA 
 *   and DEC calculated from the fully-corrected ecliptic coordinates are then
 *   the apparent geocentric coordinates. Further corrections can be made, if
 *   required, for atmospheric refraction and geocentric parallax although the
 *   intrinsic error herein of about 10 arcseconds is usually the dominant
 *   error at this stage.
 * TODO: combine the several intermediate expressions when get a good compiler.
 */
plans (mjd, p, lpd0, psi0, rp0, rho0, lam, bet, dia, mag)
double mjd;
int p;
double *lpd0, *psi0, *rp0, *rho0, *lam, *bet, *dia, *mag;
{
	static double plan[8][9];
	static lastmjd = -10000;
	double dl;	/* perturbation correction for longitude */
	double dr;	/*  "   orbital radius */
	double dml;	/*  "   mean longitude */
	double ds;	/*  "   eccentricity */
	double dm;	/*  "   mean anomaly */
	double da;	/*  "   semi-major axis */
	double dhl;	/*  "   heliocentric longitude */
	double lsn, rsn;	/* true geocentric longitude of sun and sun-earth rad */
	double mas;	/* mean anomaly of the sun */
	double re;	/* radius of earth's orbit */
	double lg;	/* longitude of earth */
	double map[8];	/* array of mean anomalies for each planet */
	double lpd, psi, rp, rho;
	double ll, sll, cll;
	double t;
	double dt;
	int pass;
	int j;
	double s, ma;
	double nu, ea;
	double lp, om;
	double lo, slo, clo;
	double inc, y;
	double spsi, cpsi;
	double rpd;

	/* only need to fill in plan[] once for a given mjd */
	if (mjd != lastmjd) {
	    pelement (mjd, plan);
	    lastmjd = mjd;
	}

	dt = 0;
	t = mjd/36525.;
	sunpos (mjd, &lsn, &rsn);
	masun (mjd, &mas);
        re = rsn;
	lg = lsn+PI;

	/* first find the true position of the planet at mjd.
	 * then repeat a second time for a slightly different time based
	 * on the position found in the first pass to account for light-travel
	 * time.
	 */
	for (pass = 0; pass < 2; pass++) {

	    for (j = 0; j < 8; j++)
		map[j] = degrad(plan[j][0]-plan[j][2]-dt*plan[j][1]);

	    /* set initial corrections to 0.
	     * then modify as necessary for the planet of interest.
	     */
	    dl = 0;
	    dr = 0;
	    dml = 0;
	    ds = 0;
	    dm = 0;
	    da = 0;
	    dhl = 0;

	    switch (p) {

	    case MERCURY:
		p_mercury (map, &dl, &dr);
		break;

	    case VENUS:
		p_venus (t, mas, map, &dl, &dr, &dml, &dm);
		break;

	    case MARS:
		p_mars (mas, map, &dl, &dr, &dml, &dm);
		break;

	    case JUPITER:
		p_jupiter (t, plan[p][3], &dml, &ds, &dm, &da);
		break;

	    case SATURN:
		p_saturn (t, plan[p][3], &dml, &ds, &dm, &da, &dhl);
		break;

	    case URANUS:
		p_uranus (t, plan[p][3], &dl, &dr, &dml, &ds, &dm, &da, &dhl);
		break;

	    case NEPTUNE:
		p_neptune (t, plan[p][3], &dl, &dr, &dml, &ds, &dm, &da, &dhl);
		break;

	    case PLUTO:
		/* no perturbation theory for pluto */
		break;
	    }

	    s = plan[p][3]+ds;
	    ma = map[p]+dm;
	    anomaly (ma, s, &nu, &ea);
	    rp = (plan[p][6]+da)*(1-s*s)/(1+s*cos(nu));
	    lp = raddeg(nu)+plan[p][2]+raddeg(dml-dm);
	    lp = degrad(lp);
	    om = degrad(plan[p][5]);
	    lo = lp-om;
	    slo = sin(lo);
	    clo = cos(lo);
	    inc = degrad(plan[p][4]);
	    rp = rp+dr;
	    spsi = slo*sin(inc);
	    y = slo*cos(inc);
	    psi = asin(spsi)+dhl;
	    spsi = sin(psi);
	    lpd = atan(y/clo)+om+degrad(dl);
	    if (clo<0) lpd += PI;
	    if (lpd>TWOPI) lpd -= TWOPI;
	    cpsi = cos(psi);
	    rpd = rp*cpsi;
	    ll = lpd-lg;
	    rho = sqrt(re*re+rp*rp-2*re*rp*cpsi*cos(ll));

	    /* when we view a planet we see it in the position it occupied
	     * dt hours ago, where rho is the distance between it and earth,
	     * in AU. use this as the new time for the next pass.
	     */
	    dt = rho*5.775518e-3;

	    if (pass == 0) {
		/* save heliocentric coordinates after first pass since, being
		 * true, they are NOT to be corrected for light-travel time.
		 */
		*lpd0 = lpd;
		*psi0 = psi;
		*rp0 = rp;
		*rho0 = rho;
	    }
	}

        sll = sin(ll);
	cll = cos(ll);
        if (p < MARS) 
	    *lam = atan(-1*rpd*sll/(re-rpd*cll))+lg+PI;
	else
	    *lam = atan(re*sll/(rpd-re*cll))+lpd;
	range (lam, TWOPI);
        *bet = atan(rpd*spsi*sin(*lam-lpd)/(cpsi*re*sll));
	*dia = plan[p][7];
	*mag = plan[p][8];
}

/* set auxilliary variables used for jupiter, saturn, uranus, and neptune */
static
aux_jsun (t, j1, j2, j3, j4, j5, j6)
double t;
double *j1, *j2, *j3, *j4, *j5, *j6;
{
        *j1 = t/5+0.1;
        *j2 = mod2PI(4.14473+5.29691e1*t);
        *j3 = mod2PI(4.641118+2.132991e1*t);
        *j4 = mod2PI(4.250177+7.478172*t);
        *j5 = 5 * *j3 - 2 * *j2;
	*j6 = 2 * *j2 - 6 * *j3 + 3 * *j4;
}

/* find the mean anomaly of the sun at mjd.
 * this is the same as that used in sun() but when it was converted to C it
 * was not known it would be required outside that routine.
 * TODO: add an argument to sun() to return mas and eliminate this routine.
 */
static
masun (mjd, mas)
double mjd;
double *mas;
{
	double t, t2;
	double a, b;

	t = mjd/36525;
	t2 = t*t;
	a = 9.999736042e1*t;
	b = 360.*(a-(int)a);
	*mas = degrad (3.5847583e2-(1.5e-4+3.3e-6*t)*t2+b);
}

/* perturbations for mercury */
static
p_mercury (map, dl, dr)
double map[];
double *dl, *dr;
{
	*dl = 2.04e-3*cos(5*map[2-1]-2*map[1-1]+2.1328e-1)+
	     1.03e-3*cos(2*map[2-1]-map[1-1]-2.8046)+
	     9.1e-4*cos(2*map[3]-map[1-1]-6.4582e-1)+
	     7.8e-4*cos(5*map[2-1]-3*map[1-1]+1.7692e-1);

	*dr = 7.525e-6*cos(2*map[3]-map[1-1]+9.25251e-1)+
	     6.802e-6*cos(5*map[2-1]-3*map[1-1]-4.53642)+
	     5.457e-6*cos(2*map[2-1]-2*map[1-1]-1.24246)+
	     3.569e-6*cos(5*map[2-1]-map[1-1]-1.35699);
}

/* ....venus */
static
p_venus (t, mas, map, dl, dr, dml, dm)
double t, mas, map[];
double *dl, *dr, *dml, *dm;
{
	*dml = degrad (7.7e-4*sin(4.1406+t*2.6227));
	*dm = *dml;

	*dl = 3.13e-3*cos(2*mas-2*map[2-1]-2.587)+
	     1.98e-3*cos(3*mas-3*map[2-1]+4.4768e-2)+
	     1.36e-3*cos(mas-map[2-1]-2.0788)+
	     9.6e-4*cos(3*mas-2*map[2-1]-2.3721)+
	     8.2e-4*cos(map[3]-map[2-1]-3.6318);

	*dr = 2.2501e-5*cos(2*mas-2*map[2-1]-1.01592)+
	     1.9045e-5*cos(3*mas-3*map[2-1]+1.61577)+
	     6.887e-6*cos(map[3]-map[2-1]-2.06106)+
	     5.172e-6*cos(mas-map[2-1]-5.08065e-1)+
	     3.62e-6*cos(5*mas-4*map[2-1]-1.81877)+
	     3.283e-6*cos(4*mas-4*map[2-1]+1.10851)+
	     3.074e-6*cos(2*map[3]-2*map[2-1]-9.62846e-1);
}

/* ....mars */
static
p_mars (mas, map, dl, dr, dml, dm)
double mas, map[];
double *dl, *dr, *dml, *dm;
{
	double a;

	a = 3*map[3]-8*map[2]+4*mas;
	*dml = degrad (-1*(1.133e-2*sin(a)+9.33e-3*cos(a)));
	*dm = *dml;

	*dl = 7.05e-3*cos(map[3]-map[2]-8.5448e-1)+
	     6.07e-3*cos(2*map[3]-map[2]-3.2873)+
	     4.45e-3*cos(2*map[3]-2*map[2]-3.3492)+
	     3.88e-3*cos(mas-2*map[2]+3.5771e-1)+
	     2.38e-3*cos(mas-map[2]+6.1256e-1)+
	     2.04e-3*cos(2*mas-3*map[2]+2.7688)+
	     1.77e-3*cos(3*map[2]-map[2-1]-1.0053)+
	     1.36e-3*cos(2*mas-4*map[2]+2.6894)+
	     1.04e-3*cos(map[3]+3.0749e-1);

	*dr = 5.3227e-5*cos(map[3]-map[2]+7.17864e-1)+
	     5.0989e-5*cos(2*map[3]-2*map[2]-1.77997)+
	     3.8278e-5*cos(2*map[3]-map[2]-1.71617)+
	     1.5996e-5*cos(mas-map[2]-9.69618e-1)+
	     1.4764e-5*cos(2*mas-3*map[2]+1.19768)+
	     8.966e-6*cos(map[3]-2*map[2]+7.61225e-1);
	 *dr += 7.914e-6*cos(3*map[3]-2*map[2]-2.43887)+
	     7.004e-6*cos(2*map[3]-3*map[2]-1.79573)+
	     6.62e-6*cos(mas-2*map[2]+1.97575)+
	     4.93e-6*cos(3*map[3]-3*map[2]-1.33069)+
	     4.693e-6*cos(3*mas-5*map[2]+3.32665)+
	     4.571e-6*cos(2*mas-4*map[2]+4.27086)+
	     4.409e-6*cos(3*map[3]-map[2]-2.02158);
}

/* ....jupiter */
static
p_jupiter (t, s, dml, ds, dm, da)
double t, s;
double *dml, *ds, *dm, *da;
{
	double dp;
	double j1, j2, j3, j4, j5, j6, j7;
	double sj3, cj3, s2j3, c2j3;
        double sj5, cj5, s2j5;
	double sj6;
        double sj7, cj7, s2j7, c2j7, s3j7, c3j7, s4j7, c4j7, c5j7;

	aux_jsun (t, &j1, &j2, &j3, &j4, &j5, &j6);
        j7 = j3-j2;
	sj3 = sin(j3);
	cj3 = cos(j3);
        s2j3 = sin(2*j3);
	c2j3 = cos(2*j3);
        sj5 = sin(j5);
	cj5 = cos(j5);
        s2j5 = sin(2*j5);
	sj6 = sin(j6);
        sj7 = sin(j7);
	cj7 = cos(j7);
        s2j7 = sin(2*j7);
	c2j7 = cos(2*j7);
        s3j7 = sin(3*j7);
	c3j7 = cos(3*j7);
        s4j7 = sin(4*j7);
	c4j7 = cos(4*j7);
        c5j7 = cos(5*j7);

	*dml = (3.31364e-1-(1.0281e-2+4.692e-3*j1)*j1)*sj5+
	      (3.228e-3-(6.4436e-2-2.075e-3*j1)*j1)*cj5-
	      (3.083e-3+(2.75e-4-4.89e-4*j1)*j1)*s2j5+
	      2.472e-3*sj6+1.3619e-2*sj7+1.8472e-2*s2j7+6.717e-3*s3j7+
	      2.775e-3*s4j7+6.417e-3*s2j7*sj3+
	      (7.275e-3-1.253e-3*j1)*sj7*sj3+
	      2.439e-3*s3j7*sj3-(3.5681e-2+1.208e-3*j1)*sj7*cj3;
        *dml += -3.767e-3*c2j7*sj3-(3.3839e-2+1.125e-3*j1)*cj7*sj3-
	      4.261e-3*s2j7*cj3+
	      (1.161e-3*j1-6.333e-3)*cj7*cj3+
	      2.178e-3*cj3-6.675e-3*c2j7*cj3-2.664e-3*c3j7*cj3-
	      2.572e-3*sj7*s2j3-3.567e-3*s2j7*s2j3+2.094e-3*cj7*c2j3+
	      3.342e-3*c2j7*c2j3;
	*dml = degrad(*dml);

	*ds = (3606+(130-43*j1)*j1)*sj5+(1289-580*j1)*cj5-6764*sj7*sj3-
	     1110*s2j7*sj3-224*s3j7*sj3-204*sj3+(1284+116*j1)*cj7*sj3+
	     188*c2j7*sj3+(1460+130*j1)*sj7*cj3+224*s2j7*cj3-817*cj3+
	     6074*cj3*cj7+992*c2j7*cj3+
	     508*c3j7*cj3+230*c4j7*cj3+108*c5j7*cj3;
	*ds += -(956+73*j1)*sj7*s2j3+448*s2j7*s2j3+137*s3j7*s2j3+
	     (108*j1-997)*cj7*s2j3+480*c2j7*s2j3+148*c3j7*s2j3+
	     (99*j1-956)*sj7*c2j3+490*s2j7*c2j3+
	     158*s3j7*c2j3+179*c2j3+(1024+75*j1)*cj7*c2j3-
	     437*c2j7*c2j3-132*c3j7*c2j3;
	*ds *= 1e-7;

	dp = (7.192e-3-3.147e-3*j1)*sj5-4.344e-3*sj3+
	     (j1*(1.97e-4*j1-6.75e-4)-2.0428e-2)*cj5+
	     3.4036e-2*cj7*sj3+(7.269e-3+6.72e-4*j1)*sj7*sj3+
	     5.614e-3*c2j7*sj3+2.964e-3*c3j7*sj3+3.7761e-2*sj7*cj3+
	     6.158e-3*s2j7*cj3-
	     6.603e-3*cj7*cj3-5.356e-3*sj7*s2j3+2.722e-3*s2j7*s2j3+
	     4.483e-3*cj7*s2j3-2.642e-3*c2j7*s2j3+4.403e-3*sj7*c2j3-
	     2.536e-3*s2j7*c2j3+5.547e-3*cj7*c2j3-2.689e-3*c2j7*c2j3;

	*dm = *dml-(degrad(dp)/s);

	*da = 205*cj7-263*cj5+693*c2j7+312*c3j7+147*c4j7+299*sj7*sj3+
	     181*c2j7*sj3+204*s2j7*cj3+111*s3j7*cj3-337*cj7*cj3-
	     111*c2j7*cj3;
	*da *= 1e-6;
}

/* ....saturn */
static
p_saturn (t, s, dml, ds, dm, da, dhl)
double t, s;
double *dml, *ds, *dm, *da, *dhl;
{
	double dp;
	double j1, j2, j3, j4, j5, j6, j7, j8;
	double sj3, cj3, s2j3, c2j3, s3j3, c3j3, s4j3, c4j3;
        double sj5, cj5, s2j5, c2j5;
	double sj6;
        double sj7, cj7, s2j7, c2j7, s3j7, c3j7, s4j7, c4j7, c5j7, s5j7;
	double s2j8, c2j8, s3j8, c3j8;

	aux_jsun (t, &j1, &j2, &j3, &j4, &j5, &j6);
        j7 = j3-j2;
	sj3 = sin(j3);
	cj3 = cos(j3);
        s2j3 = sin(2*j3);
	c2j3 = cos(2*j3);
        sj5 = sin(j5);
	cj5 = cos(j5);
        s2j5 = sin(2*j5);
	sj6 = sin(j6);
        sj7 = sin(j7);
	cj7 = cos(j7);
        s2j7 = sin(2*j7);
	c2j7 = cos(2*j7);
        s3j7 = sin(3*j7);
	c3j7 = cos(3*j7);
        s4j7 = sin(4*j7);
	c4j7 = cos(4*j7);
        c5j7 = cos(5*j7);

	s3j3 = sin(3*j3);
	c3j3 = cos(3*j3);
	s4j3 = sin(4*j3);
	c4j3 = cos(4*j3);
	c2j5 = cos(2*j5);
	s5j7 = sin(5*j7);
	j8 = j4-j3;
	s2j8 = sin(2*j8);
	c2j8 = cos(2*j8);
	s3j8 = sin(3*j8);
	c3j8 = cos(3*j8);

	*dml = 7.581e-3*s2j5-7.986e-3*sj6-1.48811e-1*sj7-4.0786e-2*s2j7-
	      (8.14181e-1-(1.815e-2-1.6714e-2*j1)*j1)*sj5-
	      (1.0497e-2-(1.60906e-1-4.1e-3*j1)*j1)*cj5-1.5208e-2*s3j7-
	      6.339e-3*s4j7-6.244e-3*sj3-1.65e-2*s2j7*sj3+
	      (8.931e-3+2.728e-3*j1)*sj7*sj3-5.775e-3*s3j7*sj3+
	      (8.1344e-2+3.206e-3*j1)*cj7*sj3+1.5019e-2*c2j7*sj3;
	*dml += (8.5581e-2+2.494e-3*j1)*sj7*cj3+1.4394e-2*c2j7*cj3+
	      (2.5328e-2-3.117e-3*j1)*cj7*cj3+
	      6.319e-3*c3j7*cj3+6.369e-3*sj7*s2j3+9.156e-3*s2j7*s2j3+
	      7.525e-3*s3j8*s2j3-5.236e-3*cj7*c2j3-7.736e-3*c2j7*c2j3-
	      7.528e-3*c3j8*c2j3;
	*dml = degrad(*dml);

	*ds = (-7927+(2548+91*j1)*j1)*sj5+(13381+(1226-253*j1)*j1)*cj5+
	     (248-121*j1)*s2j5-(305+91*j1)*c2j5+412*s2j7+12415*sj3+
	     (390-617*j1)*sj7*sj3+(165-204*j1)*s2j7*sj3+26599*cj7*sj3-
	     4687*c2j7*sj3-1870*c3j7*sj3-821*c4j7*sj3-
	     377*c5j7*sj3+497*c2j8*sj3+(163-611*j1)*cj3;
	*ds += -12696*sj7*cj3-4200*s2j7*cj3-1503*s3j7*cj3-619*s4j7*cj3-
	     268*s5j7*cj3-(282+1306*j1)*cj7*cj3+(-86+230*j1)*c2j7*cj3+
	     461*s2j8*cj3-350*s2j3+(2211-286*j1)*sj7*s2j3-
	     2208*s2j7*s2j3-568*s3j7*s2j3-346*s4j7*s2j3-
	     (2780+222*j1)*cj7*s2j3+(2022+263*j1)*c2j7*s2j3+248*c3j7*s2j3+
	     242*s3j8*s2j3+467*c3j8*s2j3-490*c2j3-(2842+279*j1)*sj7*c2j3;
	*ds += (128+226*j1)*s2j7*c2j3+224*s3j7*c2j3+
	     (-1594+282*j1)*cj7*c2j3+(2162-207*j1)*c2j7*c2j3+
	     561*c3j7*c2j3+343*c4j7*c2j3+469*s3j8*c2j3-242*c3j8*c2j3-
	     205*sj7*s3j3+262*s3j7*s3j3+208*cj7*c3j3-271*c3j7*c3j3-
	     382*c3j7*s4j3-376*s3j7*c4j3;
	*ds *= 1e-7;

	dp = (7.7108e-2+(7.186e-3-1.533e-3*j1)*j1)*sj5-7.075e-3*sj7+
	     (4.5803e-2-(1.4766e-2+5.36e-4*j1)*j1)*cj5-7.2586e-2*cj3-
	     7.5825e-2*sj7*sj3-2.4839e-2*s2j7*sj3-8.631e-3*s3j7*sj3-
	     1.50383e-1*cj7*cj3+2.6897e-2*c2j7*cj3+1.0053e-2*c3j7*cj3-
	     (1.3597e-2+1.719e-3*j1)*sj7*s2j3+1.1981e-2*s2j7*c2j3;
	dp += -(7.742e-3-1.517e-3*j1)*cj7*s2j3+
	     (1.3586e-2-1.375e-3*j1)*c2j7*c2j3-
	     (1.3667e-2-1.239e-3*j1)*sj7*c2j3+
	     (1.4861e-2+1.136e-3*j1)*cj7*c2j3-
	     (1.3064e-2+1.628e-3*j1)*c2j7*c2j3;

	*dm = *dml-(degrad(dp)/s);

	*da = 572*sj5-1590*s2j7*cj3+2933*cj5-647*s3j7*cj3+33629*cj7-
	     344*s4j7*cj3-3081*c2j7+2885*cj7*cj3-1423*c3j7+
	     (2172+102*j1)*c2j7*cj3-671*c4j7+296*c3j7*cj3-320*c5j7-
	     267*s2j7*s2j3+1098*sj3-778*cj7*s2j3-2812*sj7*sj3;
	*da += 495*c2j7*s2j3+688*s2j7*sj3+250*c3j7*s2j3-393*s3j7*sj3-
	     856*sj7*c2j3-228*s4j7*sj3+441*s2j7*c2j3+2138*cj7*sj3+
	     296*c2j7*c2j3-999*c2j7*sj3+211*c3j7*c2j3-642*c3j7*sj3-
	     427*sj7*s3j3-325*c4j7*sj3+398*s3j7*s3j3-890*cj3+
	     344*cj7*c3j3+2206*sj7*cj3-427*c3j7*c3j3;
	*da *= 1e-6;

	*dhl = 7.47e-4*cj7*sj3+1.069e-3*cj7*cj3+2.108e-3*s2j7*s2j3+
	      1.261e-3*c2j7*s2j3+1.236e-3*s2j7*c2j3-2.075e-3*c2j7*c2j3;
	*dhl = degrad(*dhl);
}

/* ....uranus */
static
p_uranus (t, s, dl, dr, dml, ds, dm, da, dhl)
double t, s;
double *dl, *dr, *dml, *ds, *dm, *da, *dhl;
{
	double dp;
	double j1, j2, j3, j4, j5, j6;
	double j8, j9, j10, j11, j12;
	double sj4, cj4, s2j4, c2j4;
	double sj9, cj9, s2j9, c2j9;
	double sj11, cj11;

	aux_jsun (t, &j1, &j2, &j3, &j4, &j5, &j6);

        j8 = mod2PI(1.46205+3.81337*t);
        j9 = 2*j8-j4;
	sj9 = sin(j9);
	cj9 = cos(j9);
        s2j9 = sin(2*j9);
	c2j9 = cos(2*j9);

	j10 = j4-j2;
	j11 = j4-j3;
	j12 = j8-j4;

	*dml = (8.64319e-1-1.583e-3*j1)*sj9+(8.2222e-2-6.833e-3*j1)*cj9+
	      3.6017e-2*s2j9-3.019e-3*c2j9+8.122e-3*sin(j6);
	*dml = degrad(*dml);

	dp = 1.20303e-1*sj9+6.197e-3*s2j9+(1.9472e-2-9.47e-4*j1)*cj9;
	*dm = *dml-(degrad(dp)/s);

	*ds = (163*j1-3349)*sj9+20981*cj9+1311*c2j9;
	*ds *= 1e-7;

	*da = -3.825e-3*cj9;

	*dl = (1.0122e-2-9.88e-4*j1)*sin(j4+j11)+
	     (-3.8581e-2+(2.031e-3-1.91e-3*j1)*j1)*cos(j4+j11)+
	     (3.4964e-2-(1.038e-3-8.68e-4*j1)*j1)*cos(2*j4+j11)+
	     5.594e-3*sin(j4+3*j12)-1.4808e-2*sin(j10)-
	     5.794e-3*sin(j11)+2.347e-3*cos(j11)+9.872e-3*sin(j12)+
	     8.803e-3*sin(2*j12)-4.308e-3*sin(3*j12);

	sj11 = sin(j11);
	cj11 = cos(j11);
	sj4 = sin(j4);
	cj4 = cos(j4);
	s2j4 = sin(2*j4);
	c2j4 = cos(2*j4);
	*dhl = (4.58e-4*sj11-6.42e-4*cj11-5.17e-4*cos(4*j12))*sj4-
	      (3.47e-4*sj11+8.53e-4*cj11+5.17e-4*sin(4*j11))*cj4+
	      4.03e-4*(cos(2*j12)*s2j4+sin(2*j12)*c2j4);
	*dhl = degrad(*dhl);

	*dr = -25948+4985*cos(j10)-1230*cj4+3354*cos(j11)+904*cos(2*j12)+
	     894*(cos(j12)-cos(3*j12))+(5795*cj4-1165*sj4+1388*c2j4)*sj11+
	     (1351*cj4+5702*sj4+1388*s2j4)*cos(j11);
	*dr *= 1e-6;
}

/* ....neptune */
static
p_neptune (t, s, dl, dr, dml, ds, dm, da, dhl)
double t, s;
double *dl, *dr, *dml, *ds, *dm, *da, *dhl;
{
	double dp;
	double j1, j2, j3, j4, j5, j6;
	double j8, j9, j10, j11, j12;
	double sj8, cj8;
	double sj9, cj9, s2j9, c2j9;
	double s2j12, c2j12;

	aux_jsun (t, &j1, &j2, &j3, &j4, &j5, &j6);

        j8 = mod2PI(1.46205+3.81337*t);
        j9 = 2*j8-j4;
	sj9 = sin(j9);
	cj9 = cos(j9);
        s2j9 = sin(2*j9);
	c2j9 = cos(2*j9);

	j10 = j8-j2;
	j11 = j8-j3;
	j12 = j8-j4;

	*dml = (1.089e-3*j1-5.89833e-1)*sj9+(4.658e-3*j1-5.6094e-2)*cj9-
	      2.4286e-2*s2j9;
	*dml = degrad(*dml);

	dp = 2.4039e-2*sj9-2.5303e-2*cj9+6.206e-3*s2j9-5.992e-3*c2j9;

	*dm = *dml-(degrad(dp)/s);

	*ds = 4389*sj9+1129*s2j9+4262*cj9+1089*c2j9;
	*ds *= 1e-7;

	*da = 8189*cj9-817*sj9+781*c2j9;
	*da *= 1e-6;

	s2j12 = sin(2*j12);
	c2j12 = cos(2*j12);
	sj8 = sin(j8);
	cj8 = cos(j8);
	*dl = -9.556e-3*sin(j10)-5.178e-3*sin(j11)+2.572e-3*s2j12-
	     2.972e-3*c2j12*sj8-2.833e-3*s2j12*cj8;

	*dhl = 3.36e-4*c2j12*sj8+3.64e-4*s2j12*cj8;
	*dhl = degrad(*dhl);

	*dr = -40596+4992*cos(j10)+2744*cos(j11)+2044*cos(j12)+1051*c2j12;
	*dr *= 1e-6;
}
xXx
echo x plot.c
cat > plot.c << 'xXx'
/* code to support the plotting capabilities.
 * idea is to let the operator name a plot file and mark some fields for
 * logging. then after each screen update, the logged fields are written to
 * the plot file. later, the file may be plotted (very simplistically by 
 * ephem, for now anyway, or by some other program entirely.).
 * 
 * format of the plot file is one line per coordinate: label,x,y
 * if z was specified, it is a fourth field.
 * x,y,z are plotted using %g format.
 */

#include <stdio.h>
#include <math.h>
#include "screen.h"

extern errno;
extern char *sys_errlist[];
#define	errsys	(sys_errlist[errno])

#define	TRACE(x)	{FILE *fp = fopen("trace","a"); fprintf x; fclose(fp);}

#define	MAXPLTLINES	10	/* max number of labeled lines we can track.
				 * note we can't store more than NFLOGS fields
				 * anyway (see flog.c).
				 */
#define	FNLEN		(14+1)	/* longest filename; plus 1 for \0 */

static char plt_filename[FNLEN] = "ephem.plt";	/* default plot file name */
static FILE *plt_fp;		/* the plot file; == 0 means don't plot */

/* store the label and rcfpack()s for each line to track. */
typedef struct {
    char pl_label;
    int pl_rcpx, pl_rcpy, pl_rcpz;
} PltLine;
static PltLine pltlines[MAXPLTLINES];
static int npltlines;		/* number of pltlines[] in actual use */

static int plt_in_polar;	/*if true plot in polar coords, else cartesian*/

/* picked the Plot label:
 * if on, just turn it off.
 * if off, turn on, define fields or select name of file to plot and do it.
 * TODO: more flexability, more relevance.
 */
plot_setup()
{
	if (plt_fp) {
	    plt_turn_off();
	    plot_prstate (0);
	} else {
	    static char *chcs[4] = {
		"Select fields", "Display a plot file", (char *)0,
		"Begin plotting"
	    };
	    int ff = 0;
    ask:
	    chcs[2] = plt_in_polar ? "Polar coords" : "Cartesian coords";
	    switch (popup(chcs, ff, npltlines > 0 ? 4 : 3)) {
	    case 0: plt_select_fields(); break;
	    case 1: plt_file(); break;
	    case 2: plt_in_polar ^= 1; ff = 2; goto ask;
	    case 3: plt_turn_on(); break;
	    }
	}
}

/* write the active plotfields to the current plot file, if one is open. */
plot()
{
	if (plt_fp) {
	    PltLine *plp;
	    double x, y, z;
	    for (plp = &pltlines[npltlines]; --plp >= pltlines; ) {
		if (flog_get (plp->pl_rcpx, &x) == 0 
			&& flog_get (plp->pl_rcpy, &y) == 0) {
		    fprintf (plt_fp, "%c,%.12g,%.12g", plp->pl_label, x, y);
		    if (flog_get (plp->pl_rcpz, &z) == 0)
			fprintf (plt_fp, ",%.12g", z);
		    fprintf (plt_fp, "\n");
		}
	    }
	}
}

plot_prstate (force)
int force;
{
	static last;
	int this = plt_fp != 0;

	if (force || this != last) {
	    f_string (R_PLOT, C_PLOTV, this ? " on" : "off");
	    last = this;
	}
}

plot_ison()
{
	return (plt_fp != 0);
}

static
plt_reset()
{
	PltLine *plp;

	for (plp = &pltlines[npltlines]; --plp >= pltlines; ) {
	    (void) flog_delete (plp->pl_rcpx);
	    (void) flog_delete (plp->pl_rcpy);
	    (void) flog_delete (plp->pl_rcpz);
	    plp->pl_rcpx = plp->pl_rcpy = plp->pl_rcpz = 0;
	}
	npltlines = 0;
}

/* let operator select the fields he wants to plot.
 * register them with flog and keep rcfpack() in pltlines[] array.
 */
static
plt_select_fields()
{
	static char hlp[] = "move and RETURN to select a field, or q to quit";
	static char sry[] = "Sorry; can not log any more fields.";
	int r = R_UT, c = C_UTV; /* TODO: start where main was? */
	char buf[64];
	int rcp;
	int i;

	plt_reset();
	for (i = 0; i < MAXPLTLINES; i++) {
	    sprintf (buf, "select x field for line %d", i+1);
	    rcp = sel_fld (r, c, alt_menumask()|F_PLT, buf, hlp);
	    if (!rcp)
		break;
	    pltlines[i].pl_rcpx = rcp;
	    if (flog_add (rcp) < 0) {
		f_msg (sry);
		break;
	    }

	    sprintf (buf, "select y field for line %d", i+1);
	    r = unpackr (rcp); c = unpackc (rcp);
	    rcp = sel_fld (r, c, alt_menumask()|F_PLT, buf, hlp);
	    if (!rcp) {
		(void) flog_delete (pltlines[i].pl_rcpx);
		break;
	    }
	    if (flog_add (rcp) < 0) {
		(void) flog_delete (pltlines[i].pl_rcpx);
		f_msg (sry);
		break;
	    }
	    pltlines[i].pl_rcpy = rcp;

	    sprintf (buf, "select z field for line %d", i+1);
	    r = unpackr (rcp); c = unpackc (rcp);
	    rcp = sel_fld (r, c, alt_menumask()|F_PLT, buf, hlp);
	    if (rcp) {
		if (flog_add (rcp) < 0) {
		    (void) flog_delete (pltlines[i].pl_rcpx);
		    (void) flog_delete (pltlines[i].pl_rcpy);
		    f_msg (sry);
		    break;
		}
		pltlines[i].pl_rcpz = rcp;
		r = unpackr (rcp); c = unpackc (rcp);
	    }

	    do {
		sprintf (buf, "enter a one-character label for line %d: ", i+1);
		f_prompt (buf);
	    } while (read_line (buf, 1) != 1);
	    pltlines[i].pl_label = *buf;
	}
	npltlines = i;
}

static
plt_turn_off ()
{
	fclose (plt_fp);
	plt_fp = 0;
	plot_prstate(0);
}

/* turn on plotting.
 * establish a file to use (and thereby set plt_fp, the plotting_is_on flag).
 * also check that there is a srch function if it is being plotted.
 */
static
plt_turn_on ()
{
	int sf = rcfpack(R_SRCH, C_SRCH, 0);
	char fn[FNLEN], fnq[64];
	char *optype;
	int n;
	PltLine *plp;

	/* insure there is a valid srch function if we are to plot it */
	for (plp = &pltlines[npltlines]; --plp >= pltlines; )
	    if ((plp->pl_rcpx == sf || plp->pl_rcpy == sf || plp->pl_rcpz == sf)
		    && !prog_isgood()) {
		f_msg ("Plotting search function but it is not defined.");
		return;
	    }

	/* prompt for file name, giving current as default */
	sprintf (fnq, "file to write <%s>: ", plt_filename);
	f_prompt (fnq);
	n = read_line (fn, sizeof(fn)-1);

	/* leave plotting off if type END.
	 * reuse same fn if just type \n
	 */
	if (n < 0)
	    return;
	if (n > 0)
	    strcpy (plt_filename, fn);

	/* give option to append if file already exists */
	optype = "w";
	if (access (plt_filename, 2) == 0) {
	    while (1) {
		f_prompt ("files exists; append or overwrite (a/o)?: ");
		n = read_char();
		if (n == 'a') {
		    optype = "a";
		    break;
		}
		if (n == 'o')
		    break;
	    }
	}

	/* plotting is on if file opens ok */
	plt_fp = fopen (plt_filename, optype);
	if (!plt_fp) {
	    char buf[NC];
	    sprintf (buf, "can not open %s: %s", plt_filename, errsys);
	    f_prompt (buf);
	    (void)read_char();
	}
	plot_prstate (0);
}

/* ask operator for a file to plot. if it's ok, do it.
 */
static
plt_file ()
{
	char fn[FNLEN], fnq[64];
	FILE *pfp;
	int n;

	/* prompt for file name, giving current as default */
	sprintf (fnq, "file to read <%s>: ", plt_filename);
	f_prompt (fnq);
	n = read_line (fn, sizeof(fn)-1);

	/* forget it if type END.
	 * reuse same fn if just type \n
	 */
	if (n < 0)
	    return;
	if (n > 0)
	    strcpy (plt_filename, fn);

	/* do the plot if file opens ok */
	pfp = fopen (plt_filename, "r");
	if (pfp) {
	    if (plt_in_polar)
		plot_polar (pfp);
	    else
		plot_cartesian (pfp);
	    fclose (pfp);
	} else {
	    char buf[NC];
	    sprintf (buf, "can not open %s: %s", plt_filename, errsys);
	    f_prompt (buf);
	    (void)read_char();
	}
}

/* plot the given file on the screen in cartesian coords.
 * TODO: add z tags somehow
 * N.B. do whatever you like but redraw the screen when done.
 */
static
plot_cartesian (pfp)
FILE *pfp;
{
	static char fmt[] = "%c,%F,%F";
	double x, y;	/* N.B. be sure these match what your scanf's %F wants*/
	double minx, maxx, miny, maxy;
	char buf[128];
	int npts = 0;
	char c;

	/* find ranges and number of points */
	while (fgets (buf, sizeof(buf), pfp)) {
	    sscanf (buf, fmt, &c, &x, &y);
	    if (npts++ == 0) {
		maxx = minx = x;
		maxy = miny = y;
	    } else {
		if (x > maxx) maxx = x;
		else if (x < minx) minx = x;
		if (y > maxy) maxy = y;
		else if (y < miny) miny = y;
	    }
	}

#define	SMALL	(1e-10)
	if (npts < 2 || fabs(minx-maxx) < SMALL || fabs(miny-maxy) < SMALL)
	    f_prompt ("At least two different points required to plot.");
	else {
	    /* read file again, this time plotting */
	    rewind (pfp);
	    c_erase();
	    while (fgets (buf, sizeof(buf), pfp)) {
		int row, col;
		sscanf (buf, fmt, &c, &x, &y);
		row = NR-(int)((NR-1)*(y-miny)/(maxy-miny)+0.5);
		col =  1+(int)((NC-1)*(x-minx)/(maxx-minx)+0.5);
		if (row == NR && col == NC)
		    col--;	/* avoid lower right scrolling corner */
		f_char (row, col, c);
	    }

	    /* label axes */
	    f_double (1, 1, "%.2g", maxy);
	    f_double (NR-1, 1, "%.2g", miny);
	    f_double (NR, 1, "%.2g", minx);
	    f_double (NR, NC-10, "%.2g", maxx);
	}

	/* hit any key to resume... */
	(void) read_char();
	redraw_screen (2);	/* full redraw */
}

/* plot the given file on the screen in polar coords.
 * first numberic field in plot file is r, second is theta in degrees.
 * TODO: add z tags somehow
 * N.B. do whatever you like but redraw the screen when done.
 */
static
plot_polar (pfp)
FILE *pfp;
{
	static char fmt[] = "%c,%F,%F";
	double r, th;	/* N.B. be sure these match what your scanf's %F wants*/
	double maxr;
	char buf[128];
	int npts = 0;
	char c;

	/* find ranges and number of points */
	while (fgets (buf, sizeof(buf), pfp)) {
	    sscanf (buf, fmt, &c, &r, &th);
	    if (npts++ == 0)
		maxr = r;
	    else
		if (r > maxr)
		    maxr = r;
	}

	if (npts < 2)
	    f_prompt ("At least two points required to plot.");
	else {
	    /* read file again, this time plotting */
	    rewind (pfp);
	    c_erase();
	    while (fgets (buf, sizeof(buf), pfp)) {
		int row, col;
		double x, y;
		sscanf (buf, fmt, &c, &r, &th);
		x = r * cos(th/57.2958);	/* degs to rads */
		y = r * sin(th/57.2958);
		row = NR-(int)((NR-1)*(y+maxr)/(2.0*maxr)+0.5);
		col =  1+(int)((NC-1)*(x+maxr)/(2.0*maxr)/ASPECT+0.5);
		if (row == NR && col == NC)
		    col--;	/* avoid lower right scrolling corner */
		f_char (row, col, c);
	    }

	    /* label radius */
	    f_double (NR/2, NC-10, "%.4g", maxr);
	}

	/* hit any key to resume... */
	(void) read_char();
	redraw_screen (2);	/* full redraw */
}
xXx
echo x popup.c
cat > popup.c << 'xXx'
/* put up a one-line menu consisting of the given fields and let op move
 * between them with the same methods as sel_fld().
 * return index of which he picked, or -1 if hit END.
 */

#include <stdio.h>
#include "screen.h"

#define	FLDGAP	2	/* inter-field gap */
#define	MAXFLDS	32	/* max number of fields we can handle */

static char pup[] = "Select: ";

/* put up an array of strings on prompt line and let op pick one.
 * start with field fn.
 * N.B. we do not do much error/bounds checking.
 */
popup (fields, fn, nfields)
char *fields[];
int fn;
int nfields;
{
	int fcols[MAXFLDS];	/* column to use for each field */
	int i;

	if (nfields > MAXFLDS)
	    return (-1);

    again:
	/* erase the prompt line; we are going to take it over */
	c_pos (R_PROMPT, C_PROMPT);
	c_eol();

	/* compute starting column for each field */
	fcols[0] = sizeof(pup);
	for (i = 1; i < nfields; i++)
	    fcols[i] = fcols[i-1] + strlen (fields[i-1]) + FLDGAP;

	/* draw each field, with comma after all but last */
	c_pos (R_PROMPT, 1);
	fputs (pup, stdout);
	for (i = 0; i < nfields; i++) {
	    c_pos (R_PROMPT, fcols[i]);
	    printf (i < nfields-1 ? "%s," : "%s", fields[i]);
	}

	/* let op choose one now; begin at fn.
	 */
	i = fn;
	while (1) {
	    c_pos (R_PROMPT, fcols[i]);
	    switch (read_char()) {
	    case END: return (-1);
	    case REDRAW: redraw_screen(2); goto again;
	    case VERSION: version(); goto again;
	    case '\r': return (i);
	    case 'h':
		if (--i < 0)
		    i = nfields - 1;
		break;
	    case 'l':
		if (++i >= nfields)
		    i = 0;
		break;
	    }
	}
}
xXx
echo x precess.c
cat > precess.c << 'xXx'
#include <stdio.h>
#include <math.h>
#include "astro.h"

/* corrects ra and dec, both in radians, for precession from epoch 1 to epoch 2.
 * the epochs are given by their modified JDs, mjd1 and mjd2, respectively.
 * N.B. ra and dec are modifed IN PLACE.
 */
precess (mjd1, mjd2, ra, dec)
double mjd1, mjd2;	/* initial and final epoch modified JDs */
double *ra, *dec;	/* ra/dec for mjd1 in, for mjd2 out */
{
	static double lastmjd1, lastmjd2;
	static double m, n, nyrs;
	double dra, ddec;	/* ra and dec corrections */

	if (mjd1 != lastmjd1 || mjd2 != lastmjd2) {
	    double t1, t2; /* Julian centuries of 36525 days since Jan .5 1900 */
	    double m1, n1; /* "constants" for t1 */
	    double m2, n2; /* "constants" for t2 */
	    t1 = mjd1/36525.;
	    t2 = mjd2/36525.;
	    m1 = 3.07234+(1.86e-3*t1);
	    n1 = 20.0468-(8.5e-3*t1);
	    m2 = 3.07234+(1.86e-3*t2);
	    n2 = 20.0468-(8.5e-3*t2);
	    m = (m1+m2)/2;	/* average m for the two epochs */
	    n = (n1+n2)/2;	/* average n for the two epochs */
	    nyrs = (mjd2-mjd1)/365.2425;
	    lastmjd1 = mjd1;
	    lastmjd2 = mjd2;
	}

	dra = (m+(n*sin(*ra)*tan(*dec)/15))*7.272205e-5*nyrs;
	ddec = n*cos(*ra)*4.848137e-6*nyrs;
	*ra += dra;
	*dec += ddec;
	range (ra, 2*PI);
}
xXx
echo x refract.c
cat > refract.c << 'xXx'
#include <stdio.h>
#include <math.h>
#include "astro.h"

/* correct the true altitude, ta, for refraction to the apparent altitude, aa,
 * each in radians, given the local atmospheric pressure, pr, in mbars, and
 * the temperature, tr, in degrees C.
 */
refract (pr, tr, ta, aa)
double pr, tr;
double ta;
double *aa;
{
	double r;	/* refraction correction*/

        if (ta >= degrad(15.)) {
	    /* model for altitudes at least 15 degrees above horizon */
            r = 7.888888e-5*pr/((273+tr)*tan(ta));
	} else if (ta > degrad(-5.)) {
	    /* hairier model for altitudes at least -5 and below 15 degrees */
	    double a, b, tadeg = raddeg(ta);
	    a = ((2e-5*tadeg+1.96e-2)*tadeg+1.594e-1)*pr;
	    b = (273+tr)*((8.45e-2*tadeg+5.05e-1)*tadeg+1);
	    r = degrad(a/b);
	} else {
	    /* do nothing if more than 5 degrees below horizon.
	     */
	    r = 0;
	}

	*aa  =  ta + r;
}

/* correct the apparent altitude, aa, for refraction to the true altitude, ta,
 * each in radians, given the local atmospheric pressure, pr, in mbars, and
 * the temperature, tr, in degrees C.
 */
unrefract (pr, tr, aa, ta)
double pr, tr;
double aa;
double *ta;
{
	double err;
	double appar;
	double true;

	/* iterative solution: search for the true that refracts to the
	 *   given apparent.
	 * since refract() is discontinuous at -5 degrees, there is a range
	 *   of apparent altitudes between about -4.5 and -5 degrees that are
	 *   not invertable (the graph of ap vs. true has a vertical step at
	 *   true = -5). thus, the iteration just oscillates if it gets into
	 *   this region. if this happens the iteration is forced to abort.
	 *   of course, this makes unrefract() discontinuous too.
	 */
	true = aa;
	do {
	    refract (pr, tr, true, &appar);
	    err = appar - aa;
	    true -= err;
	} while (fabs(err) >= 1e-6 && true > degrad(-5));

	*ta = true;
}
xXx
echo x riset.c
cat > riset.c << 'xXx'
#include <stdio.h>
#include <math.h>
#include "astro.h"

/* given the true geocentric ra and dec of an object, in radians, the observer's
 *   latitude in radians, and a horizon displacement correction, dis, in radians
 *   find the local sidereal times and azimuths of rising and setting, lstr/s
 *   and azr/s, in radians, respectively.
 * dis is the vertical displacement from the true position of the horizon. it
 *   is positive if the apparent position is higher than the true position.
 *   said another way, it is positive if the shift causes the object to spend
 *   longer above the horizon. for example, atmospheric refraction is typically
 *   assumed to produce a vertical shift of 34 arc minutes at the horizon; dis
 *   would then take on the value +9.89e-3 (radians). On the other hand, if
 *   your horizon has hills such that your apparent horizon is, say, 1 degree
 *   above sea level, you would allow for this by setting dis to -1.75e-2
 *   (radians).
 * status: 0: normal; 1: never rises; -1: circumpolar; 2: trouble.
 */
riset (ra, dec, lat, dis, lstr, lsts, azr, azs, status)
double ra, dec;
double lat, dis;
double *lstr, *lsts;
double *azr, *azs;
int *status;
{
	static double lastlat = 0, slat = 0, clat = 1.0;
	double dec1, sdec, cdec, tdec;
	double psi, spsi, cpsi;
	double h, dh, ch;	/* hr angle, delta and cos */

	/* avoid needless sin/cos since latitude doesn't change often */
        if (lat != lastlat) {
	    clat = cos(lat);
	    slat = sin(lat);
	    lastlat = lat;
	}

	/* can't cope with objects very near the celestial pole nor if we 
	 * are located very near the earth's poles.
	 */
	cdec = cos(dec);
        if (fabs(cdec*clat) < 1e-10) {
	    /* trouble */
	    *status = 2;
	    return;
	}

        cpsi = slat/cdec;
        if (cpsi>1.0) cpsi = 1.0;
        else if (cpsi<-1.0) cpsi = -1.0;
        psi = acos(cpsi);
	spsi = sin(psi);

        dh = dis*spsi;
	dec1 = dec + (dis*cpsi);
        sdec = sin(dec1);
	tdec = tan(dec1);

        ch = (-1*slat*tdec)/clat;

        if (ch < -1) {
	    /* circumpolar */
	    *status = -1;
	    return;
	}
        if (ch > 1) {
	    /* never rises */
	    *status = 1;
	    return;
	}

        *status = 0;
	h = acos(ch)+dh;

        *lstr = 24+radhr(ra-h);
	*lsts = radhr(ra+h);
	range (lstr, 24.0);
	range (lsts, 24.0);

	*azr = acos(sdec/clat);
	range (azr, 2*PI);
        *azs = 2*PI - *azr;
}
xXx
echo x riset_c.c
cat > riset_c.c << 'xXx'
/* find rise and set circumstances, ie, riset_cir() and related functions. */

#include <stdio.h>
#include <math.h>
#include "astro.h"
#include "circum.h"
#include "screen.h"	/* just for SUN and MOON */

#define	TRACE(x)	{FILE *fp = fopen("trace","a"); fprintf x; fclose(fp);}

#define	STDREF	degrad(34./60.)	/* nominal horizon refraction amount */
#define	TWIREF	degrad(18.)	/* twilight horizon displacement */

/* find where and when a body, p, will rise and set and
 *   its transit circumstances. all times are local, angles rads e of n.
 * return 0 if just returned same stuff as previous call, else 1 if new.
 * status is set from the RS_* #defines in circum.h.
 * also used to find astro twilight by calling with dis of 18 degrees.
 */
riset_cir (p, np, hzn, ltr, lts, ltt, azr, azs, altt, status)
int p;		/* one of the body defines in astro.h or screen.h */
Now *np;
int hzn;	/* STDHZN or ADPHZN or TWILIGHT */
double *ltr, *lts; /* local rise and set times */
double *ltt;	/* local transit time */
double *azr, *azs; /* local rise and set azimuths, rads e of n */
double *altt;	/* local altitude at transit */
int *status;	/* one or more of the RS_* defines */
{
	typedef struct {
	    Now l_now;
	    double l_ltr, l_lts, l_ltt, l_azr, l_azs, l_altt;
	    int l_hzn;
	    int l_status;
	} Last;
	/* must be in same order as the astro.h/screen.h #define's */
	static Last last[NOBJ];
	Last *lp;
	int new;

	lp = last + p;
	if (same_cir (np, &lp->l_now) && same_lday (np, &lp->l_now)
						&& lp->l_hzn == hzn) {
	    *ltr = lp->l_ltr;
	    *lts = lp->l_lts;
	    *ltt = lp->l_ltt;
	    *azr = lp->l_azr;
	    *azs = lp->l_azs;
	    *altt = lp->l_altt;
	    *status = lp->l_status;
	    new = 0;
	} else {
	    *status = 0;
	    iterative_riset (p, np, hzn, ltr, lts, ltt, azr, azs, altt, status);
	    lp->l_ltr = *ltr;
	    lp->l_lts = *lts;
	    lp->l_ltt = *ltt;
	    lp->l_azr = *azr;
	    lp->l_azs = *azs;
	    lp->l_altt = *altt;
	    lp->l_status = *status;
	    lp->l_hzn = hzn;
	    lp->l_now = *np;
	    new = 1;
	}
	return (new);
}

static
iterative_riset (p, np, hzn, ltr, lts, ltt, azr, azs, altt, status)
int p;
Now *np;
int hzn;
double *ltr, *lts, *ltt;	/* local times of rise, set and transit */
double *azr, *azs, *altt;/* local azimuths of rise, set and transit altitude */
int *status;
{
#define	MAXPASSES	6
	double lstr, lsts, lstt;	/* local sidereal times of rising/setting */
	double mjd0;		/* mjd estimates of rise/set event */
	double lnoon;		/* mjd of local noon */
	double x;		/* discarded tmp value */
	Now n;			/* just used to call now_lst() */
	double lst;		/* lst at local noon */
	double diff, lastdiff;	/* iterative improvement to mjd0 */
	int pass;
	int rss;

	/* first approximation is to find rise/set times of a fixed object
	 * in its position at local noon.
	 */
	lnoon = mjd_day(mjd - tz/24.0) + (12.0 + tz)/24.0; /*mjd of local noon*/
	n.n_mjd = lnoon;
	n.n_lng = lng;
	now_lst (&n, &lst);	/* lst at local noon */
	mjd0 = lnoon;
	stationary_riset (p,mjd0,np,hzn,&lstr,&lsts,&lstt,&x,&x,&x,&rss);
    chkrss:
	switch (rss) {
	case  0:  break;
	case  1: *status = RS_NEVERUP; return;
	case -1: *status = RS_CIRCUMPOLAR; goto transit;
	default: *status = RS_ERROR; return;
	}

	/* find a better approximation to the rising circumstances based on
	 * more passes, each using a "fixed" object at the location at
	 * previous approximation of the rise time.
	 */
	lastdiff = 1000.0;
	for (pass = 1; pass < MAXPASSES; pass++) {
	    diff = (lstr - lst)*SIDRATE; /* next guess at rise time wrt noon */
	    if (diff > 12.0)
		diff -= 24.0*SIDRATE;	/* not tomorrow, today */
	    else if (diff < -12.0)
		diff += 24.0*SIDRATE;	/* not yesterday, today */
	    mjd0 = lnoon + diff/24.0;	/* next guess at mjd of rise */
	    stationary_riset (p,mjd0,np,hzn,&lstr,&x,&x,azr,&x,&x,&rss);
	    if (rss != 0) goto chkrss;
	    if (fabs (diff - lastdiff) < 0.25/60.0)
		break; 			/* converged to better than 15 secs */
	    lastdiff = diff;
	}
	if (pass == MAXPASSES)
	    *status |= RS_NORISE;	/* didn't converge - no rise today */
	else {
	    *ltr = 12.0 + diff;
	    if (p != MOON &&
		    (*ltr <= 24.0*(1.0-SIDRATE) || *ltr >= 24.0*SIDRATE))
		*status |= RS_2RISES;
	}

	/* find a better approximation to the setting circumstances based on
	 * more passes, each using a "fixed" object at the location at
	 * previous approximation of the set time.
	 */
	lastdiff = 1000.0;
	for (pass = 1; pass < MAXPASSES; pass++) {
	    diff = (lsts - lst)*SIDRATE; /* next guess at set time wrt noon */
	    if (diff > 12.0)
		diff -= 24.0*SIDRATE;	/* not tomorrow, today */
	    else if (diff < -12.0)
		diff += 24.0*SIDRATE;	/* not yesterday, today */
	    mjd0 = lnoon + diff/24.0;	/* next guess at mjd of set */
	    stationary_riset (p,mjd0,np,hzn,&x,&lsts,&x,&x,azs,&x,&rss);
	    if (rss != 0) goto chkrss;
	    if (fabs (diff - lastdiff) < 0.25/60.0)
		break; 			/* converged to better than 15 secs */
	    lastdiff = diff;
	}
	if (pass == MAXPASSES)
	    *status |= RS_NOSET;	/* didn't converge - no set today */
	else {
	    *lts = 12.0 + diff;
	    if (p != MOON &&
		    (*lts <= 24.0*(1.0-SIDRATE) || *lts >= 24.0*SIDRATE))
		*status |= RS_2SETS;
	}

    transit:
	/* find a better approximation to the transit circumstances based on
	 * more passes, each using a "fixed" object at the location at
	 * previous approximation of the transit time.
	 */
	lastdiff = 1000.0;
	for (pass = 1; pass < MAXPASSES; pass++) {
	    diff = (lstt - lst)*SIDRATE; /*next guess at transit time wrt noon*/
	    if (diff > 12.0)
		diff -= 24.0*SIDRATE;	/* not tomorrow, today */
	    else if (diff < -12.0)
		diff += 24.0*SIDRATE;	/* not yesterday, today */
	    mjd0 = lnoon + diff/24.0;	/* next guess at mjd of set */
	    stationary_riset (p,mjd0,np,hzn,&x,&x,&lstt,&x,&x,altt,&rss);
	    if (fabs (diff - lastdiff) < 0.25/60.0)
		break; 			/* converged to better than 15 secs */
	    lastdiff = diff;
	}
	if (pass == MAXPASSES || rss != 0)
	    *status |= RS_NOTRANS;	/* didn't converge - no transit today */
	else {
	    *ltt = 12.0 + diff;
	    if (p != MOON &&
		    (*ltt <= 24.0*(1.0-SIDRATE) || *ltt >= 24.0*SIDRATE))
		*status |= RS_2TRANS;
	}
}

static
stationary_riset (p, mjd0, np, hzn, lstr, lsts, lstt, azr, azs, altt, status)
int p;
double mjd0;
Now *np;
int hzn;
double *lstr, *lsts, *lstt;
double *azr, *azs, *altt;
int *status;
{
	double dis;
	Now n;
	Sky s;

	switch (hzn) {
	case STDHZN:
	    /* nominal atmospheric refraction.
	     * then add nominal moon or sun semi-diameter, as appropriate.
	     */
	    dis = STDREF;
	    break;
	case TWILIGHT:
	    if (p != SUN) {
		c_erase();
		printf ("BUG 1!\n\r");
		exit(1);
	    }
	    dis = TWIREF;
	    break;
	default:
	    /* horizon displacement is refraction plus object's semi-diameter */
	    unrefract (pressure, temp, 0.0, &dis);
	    dis = -dis;
	    n = *np;
	    n.n_mjd = mjd0;
	    (void) body_cir (p, 0.0, &n, &s);
	    dis += degrad(s.s_size/3600./2.0);
	}

	switch (p) {
	case SUN:
	    if (hzn == STDHZN)
		dis += degrad (32./60./2.);
	    fixedsunriset (mjd0,np,dis,lstr,lsts,lstt,azr,azs,altt,status);
	    break;
	case MOON:
	    if (hzn == STDHZN)
		dis += degrad (32./60./2.);
	    fixedmoonriset (mjd0,np,dis,lstr,lsts,lstt,azr,azs,altt,status);
	    break;
	default:
	    if (hzn == STDHZN) {
		/* assume planets have negligible diameters.
		 * also, need to set s if hzn was STDHZN.
		 */
		n = *np;
		n.n_mjd = mjd0;
		(void) body_cir (p, 0.0, &n, &s);
	    }
	    riset (s.s_ra, s.s_dec, lat, dis, lstr, lsts, azr, azs, status);
	    transit (s.s_ra, s.s_dec, np, lstt, altt);
	}
}

/* find the local rise/set sidereal times and azimuths of an object fixed
 * at the ra/dec of the sun on the given mjd time as seen from lat.
 * times are for the upper limb. dis should account for refraction and
 *   sun's semi-diameter; we add corrections for nutation and aberration.
 */
static
fixedsunriset (mjd0, np, dis, lstr, lsts, lstt, azr, azs, altt, status)
double mjd0;
Now *np;
double dis;
double *lstr, *lsts, *lstt;
double *azr, *azs, *altt;
int *status;
{
	double lsn, rsn;
	double deps, dpsi;
	double r, d;

	/* find ecliptic position of sun at mjd */
	sunpos (mjd0, &lsn, &rsn);

	/* allow for 20.4" aberation and nutation */
	nutation (mjd0, &deps, &dpsi);
        lsn += dpsi - degrad(20.4/3600.0);

	/* convert ecliptic to equatorial coords */
	ecl_eq (mjd0, 0.0, lsn, &r, &d);

	/* find circumstances for given horizon displacement */
	riset (r, d, lat, dis, lstr, lsts, azr, azs, status);
	transit (r, d, np, lstt, altt);
}

/* find the local sidereal rise/set times and azimuths of an object fixed
 * at the ra/dec of the moon on the given mjd time as seen from lat.
 * times are for the upper limb. dis should account for refraction and moon's
 *    semi-diameter; we add corrections for parallax, and nutation.
 * accuracy is to nearest minute.
 */
static
fixedmoonriset (mjd0, np, dis, lstr, lsts, lstt, azr, azs, altt, status)
double mjd0;
Now *np;
double dis;
double *lstr, *lsts, *lstt;
double *azr, *azs, *altt;
int *status;
{
	double lam, bet, hp;
	double deps, dpsi;
	double r, d;
	double ha;

	/* find geocentric ecliptic location and equatorial horizontal parallax
	 * of moon at mjd.
	 */
	moon (mjd0, &lam, &bet, &hp);

	/* allow for nutation */
	nutation (mjd0, &deps, &dpsi);
        lam += dpsi;

	/* convert ecliptic to equatorial coords */
	ecl_eq (mjd0, bet, lam, &r, &d);

	/* find local sidereal times of rise/set times/azimuths for given
	 * equatorial coords, allowing for
	 * .27249*sin(hp)   parallax (hp is radius of earth from moon;
	 *                  .27249 is radius of moon from earth where
	 *                  the ratio is the dia_moon/dia_earth).
	 * hp               nominal angular earth radius. subtract because
	 *                  tangential line-of-sight makes moon appear lower
	 *                  as move out from center of earth.
	 */
	dis += .27249*sin(hp) - hp;
	riset (r, d, lat, dis, lstr, lsts, azr, azs, status);

	/* account for parallax on the meridian (0 hour-angle).
	 * TODO: is this really right? other stuff too? better way?? help!
	 */
	ta_par (0.0, d, lat, height, hp, &ha, &d);
	transit (r+ha, d, np, lstt, altt);
}

/* find when and how hi object at (r,d) is when it transits. */
static
transit (r, d, np, lstt, altt)
double r, d;	/* ra and dec, rads */
Now *np;	/* for refraction info */
double *lstt;	/* local sidereal time of transit */
double *altt;	/* local, refracted, altitude at time of transit */
{
	*lstt = radhr(r);
	*altt = PI/2 - lat + d;
	if (*altt > PI/2)
	    *altt = PI - *altt;
	refract (pressure, temp, *altt, altt);
}
xXx