| [1719] | 1 | /*
 | 
|---|
 | 2 |  *
 | 
|---|
 | 3 |  * TWOBODY.C
 | 
|---|
 | 4 |  *
 | 
|---|
 | 5 |  *  Computation of planetary position, two-body computation
 | 
|---|
 | 6 |  *
 | 
|---|
 | 7 |  *  Paul Schlyter, 1987-06-15
 | 
|---|
 | 8 |  *
 | 
|---|
 | 9 |  *  Decreased EPSILON from 2E-4 to 3E-8,  1988-12-05
 | 
|---|
 | 10 |  *
 | 
|---|
 | 11 |  *  1990-01-01:  Bug fix in almost parabolic orbits: now the routine
 | 
|---|
 | 12 |  *       doesn't bomb there (an if block was too large)
 | 
|---|
 | 13 |  *
 | 
|---|
 | 14 |  *  2000-12-06:  Donated to Elwood Downey if he wants to use it in XEphem
 | 
|---|
 | 15 |  */
 | 
|---|
 | 16 |  
 | 
|---|
 | 17 |  
 | 
|---|
 | 18 | #include <stdio.h>
 | 
|---|
 | 19 | #include <stdlib.h>
 | 
|---|
 | 20 | #include <math.h>
 | 
|---|
 | 21 |  
 | 
|---|
 | 22 |  
 | 
|---|
 | 23 | /* Constants used when solving Kepler's equation */
 | 
|---|
 | 24 | #define EPSILON   3E-8
 | 
|---|
 | 25 | #define INFINITY  1E+10
 | 
|---|
 | 26 |  
 | 
|---|
 | 27 | /* Math constants */
 | 
|---|
 | 28 | #define PI      3.14159265358979323846
 | 
|---|
 | 29 | #define RADEG   ( 180.0 / PI )
 | 
|---|
 | 30 | #define DEGRAD  ( PI / 180.0 )
 | 
|---|
 | 31 |  
 | 
|---|
 | 32 | /* Trig functions in degrees */
 | 
|---|
 | 33 | #define sind(x)      sin(x*DEGRAD)
 | 
|---|
 | 34 | #define cosd(x)      cos(x*DEGRAD)
 | 
|---|
 | 35 | #define atand(x)     (RADEG*atan(x))
 | 
|---|
 | 36 | #define atan2d(y,x)  (RADEG*atan2(y,x))
 | 
|---|
 | 37 |  
 | 
|---|
 | 38 | /* Gauss' grav.-konstant */
 | 
|---|
 | 39 | #define K    1.720209895E-2
 | 
|---|
 | 40 | #define KD   ( K * 180.0 / PI )
 | 
|---|
 | 41 | #define K2   ( K / 2.0 )
 | 
|---|
 | 42 |  
 | 
|---|
 | 43 |  
 | 
|---|
 | 44 |  
 | 
|---|
 | 45 |  
 | 
|---|
 | 46 | static double cubroot( double x )
 | 
|---|
 | 47 | /* Cubic root */
 | 
|---|
 | 48 | {
 | 
|---|
 | 49 |     double a,b;
 | 
|---|
 | 50 |  
 | 
|---|
 | 51 |     if ( x == 0.0 )
 | 
|---|
 | 52 |         return  0.0;
 | 
|---|
 | 53 |     else
 | 
|---|
 | 54 |     {
 | 
|---|
 | 55 |         a = fabs(x);
 | 
|---|
 | 56 |         b = exp( log(a) / 3.0 );
 | 
|---|
 | 57 |         return  x > 0.0 ? b : -b;
 | 
|---|
 | 58 |     }
 | 
|---|
 | 59 | }  /* cubroot */
 | 
|---|
 | 60 |  
 | 
|---|
 | 61 |  
 | 
|---|
 | 62 | static double rev180( double ang )
 | 
|---|
 | 63 | /* Normalize angle to between +180 and -180 degrees */
 | 
|---|
 | 64 | {
 | 
|---|
 | 65 |     return  ang  -  360.0 * floor(ang*(1.0/360.0) + 0.5);
 | 
|---|
 | 66 | }  /* rev180 */
 | 
|---|
 | 67 |  
 | 
|---|
 | 68 |  
 | 
|---|
 | 69 |  
 | 
|---|
 | 70 | static double kepler( double m, double ex )
 | 
|---|
 | 71 | /*
 | 
|---|
 | 72 |  * Solves Kepler's equation
 | 
|---|
 | 73 |  *  m      = mean anomaly
 | 
|---|
 | 74 |  *  ex     = eccentricity
 | 
|---|
 | 75 |  *  kepler = eccentric anomaly
 | 
|---|
 | 76 |  */
 | 
|---|
 | 77 | {
 | 
|---|
 | 78 |     double m1, sinm, cosm, exd, exan, dexan, lim1, adko, adk, denom;
 | 
|---|
 | 79 |     int converged;
 | 
|---|
 | 80 |  
 | 
|---|
 | 81 |     m1 = rev180(m);
 | 
|---|
 | 82 |     sinm = sind(m1);
 | 
|---|
 | 83 |     cosm = cosd(m1);
 | 
|---|
 | 84 |     /* 1st approximation: */
 | 
|---|
 | 85 |     exan = atan2d(sinm,cosm-ex);
 | 
|---|
 | 86 |     if ( ex > 0.008 )
 | 
|---|
 | 87 |     { /* Iteration formula: */
 | 
|---|
 | 88 |         exd = ex * RADEG;
 | 
|---|
 | 89 |         lim1 = 1E-3 / ex;
 | 
|---|
 | 90 |         adko = INFINITY;
 | 
|---|
 | 91 |         denom = 1.0 - ex * cosd(exan);
 | 
|---|
 | 92 |         do
 | 
|---|
 | 93 |         {
 | 
|---|
 | 94 |             dexan = (m1 + exd * sind(exan) - exan) / denom;
 | 
|---|
 | 95 |             exan = exan + dexan;
 | 
|---|
 | 96 |             adk = fabs(dexan);
 | 
|---|
 | 97 |             converged = adk < EPSILON  ||  adk >= adko ;
 | 
|---|
 | 98 |             adko = adk;
 | 
|---|
 | 99 |             if ( !converged  &&  adk > lim1 )
 | 
|---|
 | 100 |                 denom = 1.0 - ex * cosd(exan);
 | 
|---|
 | 101 |         } while ( !converged );
 | 
|---|
 | 102 |     }
 | 
|---|
 | 103 |     return  exan;
 | 
|---|
 | 104 | }  /* kepler */
 | 
|---|
 | 105 |  
 | 
|---|
 | 106 |  
 | 
|---|
 | 107 | static void vr( double *v, double *r, double m, double e, double a )
 | 
|---|
 | 108 | /*
 | 
|---|
 | 109 |  * Elliptic orbits only:
 | 
|---|
 | 110 |  * computes: v = true anomaly   (degrees)
 | 
|---|
 | 111 |  *           r = radius vector  (a.u.)
 | 
|---|
 | 112 |  *   from:   m = mean anomaly   (degrees)
 | 
|---|
 | 113 |  *           e = eccentricity
 | 
|---|
 | 114 |  *           a = semimajor axis (a.u.)
 | 
|---|
 | 115 |  */
 | 
|---|
 | 116 | {
 | 
|---|
 | 117 |     double ean, x, y;
 | 
|---|
 | 118 |  
 | 
|---|
 | 119 |     ean = kepler(m,e);
 | 
|---|
 | 120 |     x = a*(cosd(ean)-e);
 | 
|---|
 | 121 |     y = a*sqrt(1.-e*e)*sind(ean);
 | 
|---|
 | 122 |     *r = sqrt(x*x+y*y);
 | 
|---|
 | 123 |     *v = atan2d(y,x);
 | 
|---|
 | 124 | }  /* vr */
 | 
|---|
 | 125 |  
 | 
|---|
 | 126 |  
 | 
|---|
 | 127 | void vrc( double *v, double *r, double tp, double e, double q )
 | 
|---|
 | 128 | /*
 | 
|---|
 | 129 |  * Elliptic, hyperbolic and near-parabolic orbits:
 | 
|---|
 | 130 |  * computes: v  = true anomaly  (degrees)
 | 
|---|
 | 131 |  *           r  = radius vector (a.u.)
 | 
|---|
 | 132 |  *   from:   tp = time from perihelion (days)
 | 
|---|
 | 133 |  *           e  = eccentricity
 | 
|---|
 | 134 |  *           q  = perihelion distance (a.u.)
 | 
|---|
 | 135 |  */
 | 
|---|
 | 136 | {
 | 
|---|
 | 137 |  
 | 
|---|
 | 138 |     double lambda;
 | 
|---|
 | 139 |  
 | 
|---|
 | 140 |     double a, b, w, w2, w4, c, c1, c2, c3, c5, a0, a1, a2,
 | 
|---|
 | 141 |            a3, m, n, g, adgg, adgg2, gs, dg;
 | 
|---|
 | 142 |  
 | 
|---|
 | 143 |     if ( tp == 0.0 )  /* In perihelion */
 | 
|---|
 | 144 |     {
 | 
|---|
 | 145 |         *v = 0.0;
 | 
|---|
 | 146 |         *r = q;
 | 
|---|
 | 147 |         return;
 | 
|---|
 | 148 |     }
 | 
|---|
 | 149 |  
 | 
|---|
 | 150 |  
 | 
|---|
 | 151 |     lambda = (1.0-e) / (1.0+e);
 | 
|---|
 | 152 |  
 | 
|---|
 | 153 |     if ( fabs(lambda) < 0.01 )
 | 
|---|
 | 154 |     {  /* Near-parabolic orbits */
 | 
|---|
 | 155 |         a = K2 * sqrt((1.0+e)/(q*q*q)) * tp;
 | 
|---|
 | 156 |         b = sqrt( 1.0 + 2.25*a*a );
 | 
|---|
 | 157 |         w = cubroot( b + 1.5*a ) - cubroot( b - 1.5*a );
 | 
|---|
 | 158 |  
 | 
|---|
 | 159 |         /* Test if it's accuate enough to compute this as a near-parabolic orbit */
 | 
|---|
 | 160 |         if ( fabs(w*w*lambda) > 0.2 )
 | 
|---|
 | 161 |         {
 | 
|---|
 | 162 |             if ( fabs(lambda) < 0.0002 )
 | 
|---|
 | 163 |             {
 | 
|---|
 | 164 |                 /* Sorry, but we cannot compute this at all -- we must give up!
 | 
|---|
 | 165 |                  *
 | 
|---|
 | 166 |                  * This happens very rarely, in orbits having an eccentricity
 | 
|---|
 | 167 |                  * some 2% away from 1.0 AND if the body is very very far from
 | 
|---|
 | 168 |                  * perihelion.  E.g. a Kreutz sun-grazing comet having
 | 
|---|
 | 169 |                  * eccentricity near 0.98 or 1.02, and being outside
 | 
|---|
 | 170 |                  * the orbit of Pluto.  For any reasonable orbit this will
 | 
|---|
 | 171 |                  * never happen in practice.
 | 
|---|
 | 172 |                  *
 | 
|---|
 | 173 |                  * You might want to code a more graceful error exit here though.
 | 
|---|
 | 174 |                  *
 | 
|---|
 | 175 |                  */
 | 
|---|
 | 176 |                 printf( "\nNear-parabolic orbit: inaccurate result."
 | 
|---|
 | 177 |                         "\n  e = %f, lambda = %f, w = %f", e, lambda, w );
 | 
|---|
 | 178 |                 exit(1);
 | 
|---|
 | 179 |             }
 | 
|---|
 | 180 |             else
 | 
|---|
 | 181 |             {
 | 
|---|
 | 182 |                 /* We cannot compute this as a near-parabolic orbit, so let's
 | 
|---|
 | 183 |                    compute it as an elliptic or hyperbolic orbit instead. */
 | 
|---|
 | 184 |                 goto ellipse_hyperbola;
 | 
|---|
 | 185 |             }
 | 
|---|
 | 186 |         }
 | 
|---|
 | 187 |  
 | 
|---|
 | 188 |         /* Go ahead computing the near-parabolic case */
 | 
|---|
 | 189 |         c = 1.0 + 1.0 / (w*w);
 | 
|---|
 | 190 |         c1 = 1.0 / c;
 | 
|---|
 | 191 |         c2 = c1*c1;
 | 
|---|
 | 192 |         c3 = c1*c2;
 | 
|---|
 | 193 |         c5 = c3*c2;
 | 
|---|
 | 194 |         w2 = w*w;
 | 
|---|
 | 195 |         w4 = w2*w2;
 | 
|---|
 | 196 |         a0 = w;
 | 
|---|
 | 197 |         a1 = 2.0 * w * (0.33333333 + 0.2*w2) * c1;
 | 
|---|
 | 198 |         a2 = 0.2 * w * (7.0 + 0.14285714 * (33.0*w2+7.4*w4)) * c3;
 | 
|---|
 | 199 |         a3 = 0.022857143 * (108.0 + 37.177777*w2 + 5.1111111*w4) * c5;
 | 
|---|
 | 200 |         w = (( lambda * a3 + a2 ) * lambda + a1 ) * lambda + a0;
 | 
|---|
 | 201 |         w2 = w*w;
 | 
|---|
 | 202 |         *v = 2.0 * atand(w);
 | 
|---|
 | 203 |         *r = q * (1+w2) / ( 1.0 + w2*lambda );
 | 
|---|
 | 204 |         return;  /* Near-parabolic orbit */
 | 
|---|
 | 205 |     }
 | 
|---|
 | 206 |  
 | 
|---|
 | 207 |  
 | 
|---|
 | 208 | ellipse_hyperbola:
 | 
|---|
 | 209 |  
 | 
|---|
 | 210 |     if ( lambda > 0.0 )
 | 
|---|
 | 211 |     {   /* Elliptic orbit: */
 | 
|---|
 | 212 |         a = q / (1.0-e);            /* Semi-major axis */
 | 
|---|
 | 213 |         m = KD * tp / sqrt(a*a*a);  /* Mean Anomaly */
 | 
|---|
 | 214 |         vr( v, r, m, e, a );        /* Solve Kepler's equation, etc */
 | 
|---|
 | 215 |     }
 | 
|---|
 | 216 |     else
 | 
|---|
 | 217 |     {   /* Hyperbolic orbit: */
 | 
|---|
 | 218 |         a = q / (e-1.0);            /* Semi-major axis */
 | 
|---|
 | 219 |         n = K * tp / sqrt(a*a*a);   /* "Daily motion" */
 | 
|---|
 | 220 |         g = n/e;
 | 
|---|
 | 221 |         adgg = INFINITY;
 | 
|---|
 | 222 |         do  
 | 
|---|
 | 223 |         {
 | 
|---|
 | 224 |             adgg2 = adgg;
 | 
|---|
 | 225 |             gs = sqrt(g*g+1.0);
 | 
|---|
 | 226 |             dg = -( e*g - log(g+gs) - n ) / ( e - 1.0/gs );
 | 
|---|
 | 227 |             g = g + dg;
 | 
|---|
 | 228 |             adgg = fabs(dg/g);
 | 
|---|
 | 229 |         } while ( adgg < adgg2  &&  adgg > 1E-5 );
 | 
|---|
 | 230 |         gs = sqrt(g*g+1.0);
 | 
|---|
 | 231 |         *v = 2.0 * atand( sqrt( (e+1.0)/(e-1.0) ) * g / (gs+1.0) );
 | 
|---|
 | 232 |         *r = q * (1.0+e) / ( 1.0 + e*cosd(*v) );
 | 
|---|
 | 233 |     }
 | 
|---|
 | 234 |  
 | 
|---|
 | 235 | } /* vrc */
 | 
|---|
 | 236 | 
 | 
|---|
 | 237 | /* For RCS Only -- Do Not Edit */
 | 
|---|
 | 238 | static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: twobody.c,v $ $Date: 2001-10-22 12:08:28 $ $Revision: 1.1 $ $Name: not supported by cvs2svn $"};
 | 
|---|