ftp.nice.ch/pub/next/science/astronomy/usat-NeXT.N.bs.tar.gz#/usat/almanacAA/kepler.c

This is kepler.c in view mode; [Download] [Up]

/************************************************************************/
/* kepler.c								*/
/*									*/
/* Program to solve Keplerian orbit given orbital parameters and the	*/
/* time.  Returns Heliocentric equatorial rectangular coordinates of	*/
/* the object.								*/
/************************************************************************/

/***** description
 *
 *	$Id: kepler.c,v 1.4 1993/04/22 19:43:55 craig Exp $
 *
 * This program detects several cases of given orbital elements.
 * If a program for perturbations is pointed to, it is called
 * to calculate all the elements.
 * If there is no program, then the mean longitude is calculated
 * from the mean anomaly and daily motion.
 * If the daily motion is not given, it is calculated
 * by Kepler's law.
 * If the eccentricity is given to be 1.0, it means that
 * meandistance is really the perihelion distance, as in a comet
 * specification, and the orbit is parabolic.
 *
 * Reference: Taff, L.G., "Celestial Mechanics, A Computational
 * Guide for the Practitioner."  Wiley, 1985.
 */

/***** modification history
 *
 *	$Log: kepler.c,v $
 * Revision 1.4  1993/04/22  19:43:55  craig
 * minor changes.
 *
 * Revision 1.3  1993/04/21  21:20:47  craig
 * Changed the path of the satellite.h include.
 * Changed ecnsts to pcnsts.
 *
 * Revision 1.2  1993/04/21  15:16:53  craig
 * First working version.  Ran through indent and converted to ansi.
 * Added hooks for working with the satellite programs.
 *
 *
 */

/***** include files *****/

#include <math.h>
#include "aaproto.h"
#include "satellite.h"

/***** global variables *****/

/* from cnstinit.c */

extern struct orbit earth;		/* orbital elements of the earth */
extern struct MCONSTANTS mcnsts;
extern struct PCONSTANTS pcnsts;

/* from epsiln.c */

extern double eps, coseps, sineps;	/* obliquity of ecliptic */

/***** local function prototypes *****/

int    embofs (double J, double *E, double *W, double *r);

/**********/
/* kepler */
/**********/

int    kepler (double J, struct orbit *e, double *rect, double *polar)
{
    int    k;

    double alat, E, M, W, temp;
    double epoch, inclination, ascnode, argperih;
    double meandistance, dailymotion, eccent, meananomaly;
    double r, coso, sino, cosa;

    /* Compute orbital elements if a program for doing so is supplied */

    if (e->oelmnt)
    {
	k = (*(e->oelmnt)) (e, J);

	if (k == -1)
	{
	    goto dobs;
	}
    }
    else if (e->celmnt)
    {					/* call B & S algorithm */
dobs:
	(*(e->celmnt)) (J, polar);
	polar[0] = modtp (polar[0]);
	E = polar[0];			/* longitude */
	e->L = E;
	W = polar[1];			/* latitude */
	r = polar[2];			/* radius */
	e->r = r;
	e->epoch = J;
	e->equinox = J;
	goto kepdon;
    }

    /* Decant the parameters from the data structure */

    epoch = e->epoch;
    inclination = e->i;
    ascnode = e->W * mcnsts.de2ra;
    argperih = e->w;
    meandistance = e->a;		/* semimajor axis */
    dailymotion = e->dm;
    eccent = e->ecc;
    meananomaly = e->M;

    /* Check for parabolic orbit. */

    if (eccent == 1.0)
    {
	/* meandistance = perihelion distance, q epoch = perihelion
	   passage date */

	temp = meandistance * sqrt (meandistance);
	W = (J - epoch) * pcnsts.k * 3. / sqrt (2.) / temp;

	E = 0.0;
	M = 1.0;

	while (fabs (M) > 1.0e-11)
	{
	    temp = E * E;
	    temp = (2.0 * E * temp + W) / (3.0 * (1.0 + temp));
	    M = temp - E;

	    if (temp != 0.0)
	    {
		M /= temp;
	    }

	    E = temp;
	}

	r = meandistance * (1.0 + E * E);
	M = atan (E);
	M = 2.0 * M;
	alat = M + mcnsts.de2ra * argperih;

	goto parabcon;
    }

    if (eccent > 1.0)
    {
	/* The equation of the hyperbola in polar coordinates r, theta is
	   r = a(e^2 - 1)/(1 + e cos(theta)) so the perihelion distance q
	   = a(e-1), the "mean distance"  a = q/(e-1). */

	meandistance = meandistance / (eccent - 1.0);
	temp = meandistance * sqrt (meandistance);
	W = (J - epoch) * 0.01720209895 / temp;

	/* solve M = -E + e sinh E */

	E = W / (eccent - 1.0);
	M = 1.0;

	while (fabs (M) > 1.0e-11)
	{
	    M = -E + eccent * sinh (E) - W;
	    E += M / (1.0 - eccent * cosh (E));
	}

	r = meandistance * (-1.0 + eccent * cosh (E));
	temp = (eccent + 1.0) / (eccent - 1.0);
	M = sqrt (temp) * tanh (0.5 * E);
	M = 2.0 * atan (M);
	alat = M + mcnsts.de2ra * argperih;

	goto parabcon;
    }

    /* Calculate the daily motion, if it is not given.  */

    if (dailymotion == 0.0)
    {
	/* Assumes object in heliocentric orbit is massless. */

	dailymotion = pcnsts.k * 180. / mcnsts.pi / (e->a * sqrt (e->a));
    }

    dailymotion *= (J - epoch);

    /* M is proportional to the area swept out by the radius vector of a
       circular orbit during the time between perihelion passage and
       Julian date J. It is the mean anomaly at time J. */

    M = mcnsts.de2ra * (meananomaly + dailymotion);
    M = modtp (M);

    /* If mean longitude was calculated, adjust it also for motion since
       epoch of elements. */

    if (e->L)
    {
	e->L += dailymotion;
	e->L = mod360 (e->L);
    }

    /* By Kepler's second law, M must be equal to the area swept out in
       the same time by an elliptical orbit of same total area. Integrate
       the ellipse expressed in polar coordinates r = a(1-e^2)/(1 + e
       cosW) with respect to the angle W to get an expression for the area
       swept out by the radius vector.  The area is given by the mean
       anomaly; the angle is solved numerically.
    
    The answer is obtained in two steps.  We first solve Kepler's equation
       M = E - eccent*sin(E) for the eccentric anomaly E.  Then there is a
       closed form solution for W in terms of E. */

    E = M;				/* Initial guess is same as
					   circular orbit. */
    temp = 1.0;

    do
    {
	/* The approximate area swept out in the ellipse */
	/* ...minus the area swept out in the circle (M) */

	temp = E - eccent * sin (E) - M;

	/* ...should be zero.  Use the derivative of the error to converge
	   to solution by Newton's method. */

	E -= temp / (1.0 - eccent * cos (E));
    }
    while (fabs (temp) > 1.0e-11);

    /* The exact formula for the area in the ellipse is
       2.0*atan(c2*tan(0.5*W)) - c1*eccent*sin(W)/(1+e*cos(W)) where c1 =
       sqrt( 1.0 - eccent*eccent ) c2 = sqrt( (1.0-eccent)/(1.0+eccent) ).
       Substituting the following value of W yields the exact solution. */

    temp = sqrt ((1.0 + eccent) / (1.0 - eccent));
    W = 2.0 * atan (temp * tan (0.5 * E));

    /* The true anomaly. */

    W = modtp (W);

    meananomaly *= mcnsts.de2ra;

    /* Orbital longitude measured from node (argument of latitude) */

    if (e->L)
    {
	alat = (e->L) * mcnsts.de2ra + W - meananomaly - ascnode;
    }
    else
    {
	alat = W + mcnsts.de2ra * argperih;	/* mean longitude not given */
    }

    /* From the equation of the ellipse, get the radius from central focus
       to the object. */

    r = meandistance * (1.0 - eccent * eccent) / (1.0 + eccent * cos (W));

parabcon:

    /* The heliocentric ecliptic longitude of the object is given by tan(
       longitude - ascnode )  =  cos( inclination ) * tan( alat ). */

    coso = cos (alat);
    sino = sin (alat);
    inclination *= mcnsts.de2ra;
    W = sino * cos (inclination);
    E = matan2 (W, coso) + ascnode;

    /* The ecliptic latitude of the object */

    W = sino * sin (inclination);
    W = asin (W);

    /* Apply perturbations, if a program is supplied. */

    if (e->celmnt)
    {
	e->L = E;
	e->r = r;
	(*(e->celmnt)) (e);
	E = e->L;
	r = e->r;
	W += e->plat;
    }
    
    /* If earth, Adjust from earth-moon barycenter to earth by AA page E2
       unless orbital elements are calculated by formula. (The Meeus
       perturbation formulas include this term for the moon.) */

    if ((e == &earth) && (e->oelmnt == 0))
    {
	embofs (J, &E, &W, &r);		/* see below */
    }

    /* Output the polar cooordinates */

    polar[0] = E;			/* longitude */
    polar[1] = W;			/* latitude */
    polar[2] = r;			/* radius */

kepdon:

    /* Convert to rectangular coordinates, using the perturbed latitude. */

    rect[2] = r * sin (W);
    cosa = cos (W);
    rect[1] = r * cosa * sin (E);
    rect[0] = r * cosa * cos (E);

    /* Convert from heliocentric ecliptic rectangular to heliocentric
       equatorial rectangular coordinates by rotating eps radians about
       the x axis. */

    epsiln (e->equinox);
    W = coseps * rect[1] - sineps * rect[2];
    M = sineps * rect[1] + coseps * rect[2];
    rect[1] = W;
    rect[2] = M;

    /* Precess the position to ecliptic and equinox of J2000.0 if not
       already there. */

    precess (rect, e->equinox, 1);

    return (0);
}

/********************************************************/
/* embofs						*/
/*							*/
/* Adjust position from Earth-Moon barycenter to Earth	*/
/********************************************************/

/*
 * J = Julian day number
 * E = Earth's ecliptic longitude (radians)
 * W = Earth's ecliptic latitude (radians)
 * r = Earth's distance to the Sun (au)
 */

int    embofs (double J, double *E, double *W, double *r)
{
    double T, M, a, L, B, p;
    double smp, cmp, s2mp, c2mp, s2d, c2d, sf, cf;
    double s2f, sx, cx, xyz[3];

    /* Short series for position of the Moon */

    T = (J - pcnsts.J1900) / pcnsts.dapcen;

    /* Mean anomaly of moon (MP) */

    a = ((1.44e-5 * T + 0.009192) * T + 477198.8491) * T + 296.104608;
    a *= mcnsts.de2ra;
    smp = sin (a);
    cmp = cos (a);
    s2mp = 2.0 * smp * cmp;		/* sin(2MP) */
    c2mp = cmp * cmp - smp * smp;	/* cos(2MP) */

    /* Mean elongation of moon (D) */

    a = ((1.9e-6 * T - 0.001436) * T + 445267.1142) * T + 350.737486;
    a = 2.0 * mcnsts.de2ra * a;
    s2d = sin (a);
    c2d = cos (a);

    /* Mean distance of moon from its ascending node (F) */

    a = ((-3.e-7 * T - 0.003211) * T + 483202.0251) * T + 11.250889;
    a *= mcnsts.de2ra;
    sf = sin (a);
    cf = cos (a);
    s2f = 2.0 * sf * cf;		/* sin(2F) */
    sx = s2d * cmp - c2d * smp;		/* sin(2D - MP) */
    cx = c2d * cmp + s2d * smp;		/* cos(2D - MP) */

    /* Mean longitude of moon (LP) */

    L = ((1.9e-6 * T - 0.001133) * T + 481267.8831) * T + 270.434164;

    /* Mean anomaly of sun (M) */

    M = ((-3.3e-6 * T - 1.50e-4) * T + 35999.0498) * T + 358.475833;

    /* Ecliptic longitude of the moon */

    L = L
	+ 6.288750 * smp
	+ 1.274018 * sx
	+ 0.658309 * s2d
	+ 0.213616 * s2mp
	- 0.185596 * sin (mcnsts.de2ra * M)
	- 0.114336 * s2f;

    /* Ecliptic latitude of the moon */

    a = smp * cf;
    sx = cmp * sf;
    B = 5.128189 * sf
	+ 0.280606 * (a + sx)		/* sin(MP+F) */
	+ 0.277693 * (a - sx)		/* sin(MP-F) */
	+ 0.173238 * (s2d * cf - c2d * sf);	/* sin(2D-F) */
    B *= mcnsts.de2ra;

    /* Parallax of the moon */

    p = 0.950724
	+ 0.051818 * cmp
	+ 0.009531 * cx
	+ 0.007843 * c2d
	+ 0.002824 * c2mp;
    p *= mcnsts.de2ra;

    /* Elongation of Moon from Sun */

    L = mcnsts.de2ra * mod360 (L) - (*E - mcnsts.pi);

    /* The Moon's position has to be precessed to J2000 */

    /* Distance in au */

    a = 4.263523e-5 / sin (p);

    /* Convert to rectangular ecliptic coordinates */

    xyz[2] = a * sin (B);
    cx = cos (B);
    xyz[1] = a * cx * sin (L);
    xyz[0] = a * cx * cos (L);

    /* Convert to equatorial */

    epsiln (J);
    sf = coseps * xyz[1] - sineps * xyz[2];
    cf = sineps * xyz[1] + coseps * xyz[2];
    xyz[1] = sf;
    xyz[2] = cf;

    /* Precess the position
     * to ecliptic and equinox of J2000.0
     */

    precess (xyz, J, 1);

    /* Rotate back to ecliptic coordinates */

    sf = coseps * xyz[1] + sineps * xyz[2];
    cf = -sineps * xyz[1] + coseps * xyz[2];
    xyz[1] = sf;
    xyz[2] = cf;

    /* Convert to polar coordinates */

    L = matan2 (xyz[1], xyz[0]);
    B = asin (xyz[2] / a);

    /* Distance from Earth to Earth-Moon barycenter */

    a = 5.18041e-7 / p;

    /* Adjust the coordinates of the Earth */

    sx = a / *r;			/* chord = R * dTheta */
    *E += sx * sin (L);
    *W -= sx * B;
    *r += a * cos (L);

    return (0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.