Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

customorbit.cpp

Go to the documentation of this file.
00001 // customorbit.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <cassert>
00011 #include <vector>
00012 #include <fstream>
00013 #include <iomanip>
00014 #include <celmath/mathlib.h>
00015 #include "astro.h"
00016 #include "customorbit.h"
00017 #include "vsop87.h"
00018 #include "jpleph.h"
00019 
00020 using namespace std;
00021 
00022 
00023 #define TWOPI 6.28318530717958647692
00024 #define LPEJ 0.23509484  // Longitude of perihelion of Jupiter
00025 
00026 // These are required because the orbits of the Jovian and Saturnian
00027 // satellites are computed in units of their parent planets' radii.
00028 static const double JupiterRadius = 71398.0;
00029 static const double SaturnRadius = 60330.0;
00030 
00031 // The expressions for custom orbits are complex, so the bounding radii are
00032 // generally must computed from mean orbital elements.  It's important that
00033 // a sphere with the bounding radius completely enclose the orbit, so we
00034 // multiply by this factor to make the bounding radius a bit larger than
00035 // the apocenter distance computed from the mean elements.
00036 static const double BoundingRadiusSlack = 1.2;
00037 
00038 static bool jplephInitialized = false;
00039 static JPLEphemeris* jpleph = NULL;
00040 
00041 
00042 double gPlanetElements[8][9];
00043 double gElements[8][23] = {
00044         {   /*     mercury... */
00045 
00046             178.179078, 415.2057519,    3.011e-4,       0.0,
00047             75.899697,  1.5554889,      2.947e-4,       0.0,
00048             .20561421,  2.046e-5,       3e-8,           0.0,
00049             7.002881,   1.8608e-3,      -1.83e-5,       0.0,
00050             47.145944,  1.1852083,      1.739e-4,       0.0,
00051             .3870986,   6.74,           -0.42
00052         },
00053 
00054         {   /*     venus... */
00055 
00056             342.767053, 162.5533664,    3.097e-4,       0.0,
00057             130.163833, 1.4080361,      -9.764e-4,      0.0,
00058             6.82069e-3, -4.774e-5,      9.1e-8,         0.0,
00059             3.393631,   1.0058e-3,      -1e-6,          0.0,
00060             75.779647,  .89985,         4.1e-4,         0.0,
00061             .7233316,   16.92,          -4.4
00062         },
00063 
00064         {   /*     mars... */
00065 
00066             293.737334, 53.17137642,    3.107e-4,       0.0,
00067             3.34218203e2, 1.8407584,    1.299e-4,       -1.19e-6,
00068             9.33129e-2, 9.2064e-5,      7.7e-8,         0.0,
00069             1.850333,   -6.75e-4,       1.26e-5,        0.0,
00070             48.786442,  .7709917,       -1.4e-6,        -5.33e-6,
00071             1.5236883,  9.36,           -1.52
00072         },
00073 
00074         {   /*     jupiter... */
00075 
00076             238.049257, 8.434172183,    3.347e-4,       -1.65e-6,
00077             1.2720972e1, 1.6099617,     1.05627e-3,     -3.43e-6,
00078             4.833475e-2, 1.6418e-4,     -4.676e-7,      -1.7e-9,
00079             1.308736,   -5.6961e-3,     3.9e-6,         0.0,
00080             99.443414,  1.01053,        3.5222e-4,      -8.51e-6,
00081             5.202561,   196.74,         -9.4
00082         },
00083 
00084         {   /*     saturn... */
00085 
00086             266.564377, 3.398638567,    3.245e-4,       -5.8e-6,
00087             9.1098214e1, 1.9584158,     8.2636e-4,      4.61e-6,
00088             5.589232e-2, -3.455e-4,     -7.28e-7,       7.4e-10,
00089             2.492519,   -3.9189e-3,     -1.549e-5,      4e-8,
00090             112.790414, .8731951,       -1.5218e-4,     -5.31e-6,
00091             9.554747,   165.6,          -8.88
00092         },
00093 
00094         {   /*     uranus... */
00095 
00096             244.19747,  1.194065406,    3.16e-4,        -6e-7,
00097             1.71548692e2, 1.4844328,    2.372e-4,       -6.1e-7,
00098             4.63444e-2, -2.658e-5,      7.7e-8,         0.0,
00099             .772464,    6.253e-4,       3.95e-5,        0.0,
00100             73.477111,  .4986678,       1.3117e-3,      0.0,
00101             19.21814,   65.8,           -7.19
00102         },
00103 
00104         {   /*     neptune... */
00105 
00106             84.457994,  .6107942056,    3.205e-4,       -6e-7,
00107             4.6727364e1, 1.4245744,     3.9082e-4,      -6.05e-7,
00108             8.99704e-3, 6.33e-6,        -2e-9,          0.0,
00109             1.779242,   -9.5436e-3,     -9.1e-6,        0.0,
00110             130.681389, 1.098935,       2.4987e-4,      -4.718e-6,
00111             30.10957,   62.2,           -6.87
00112         },
00113 
00114         {   /*     pluto...(osculating 1984 jan 21) */
00115 
00116             95.3113544, .3980332167,    0.0,            0.0,
00117             224.017,    0.0,            0.0,            0.0,
00118             .25515,     0.0,            0.0,            0.0,
00119             17.1329,    0.0,            0.0,            0.0,
00120             110.191,    0.0,            0.0,            0.0,
00121             39.8151,    8.2,            -1.0
00122         }
00123 };
00124 
00125 
00126 // Useful version of trig functions which operate on values in degrees instead
00127 // of radians.
00128 static double sinD(double theta)
00129 {
00130     return sin(degToRad(theta));
00131 }
00132 
00133 static double cosD(double theta)
00134 {
00135     return cos(degToRad(theta));
00136 }
00137 
00138 
00139 static double Obliquity(double t)
00140 {
00141     // Parameter t represents the Julian centuries elapsed since 1900.
00142     // In other words, t = (jd - 2415020.0) / 36525.0
00143 
00144     return degToRad(2.345229444E1 - ((((-1.81E-3*t)+5.9E-3)*t+4.6845E1)*t)/3600.0);
00145 }
00146 
00147 static void Nutation(double t, double &deps, double& dpsi)
00148 {
00149     // Parameter t represents the Julian centuries elapsed since 1900.
00150     // In other words, t = (jd - 2415020.0) / 36525.0
00151 
00152     double ls, ld;      // sun's mean longitude, moon's mean longitude
00153     double ms, md;      // sun's mean anomaly, moon's mean anomaly
00154     double nm;      // longitude of moon's ascending node
00155     double t2;
00156     double tls, tnm, tld;       // twice above
00157     double a, b;
00158 
00159     t2 = t*t;
00160 
00161     a = 100.0021358*t;
00162     b = 360.*(a-(int)a);
00163     ls = 279.697+.000303*t2+b;
00164 
00165     a = 1336.855231*t;
00166     b = 360.*(a-(int)a);
00167     ld = 270.434-.001133*t2+b;
00168 
00169     a = 99.99736056000026*t;
00170     b = 360.*(a-(int)a);
00171     ms = 358.476-.00015*t2+b;
00172 
00173     a = 13255523.59*t;
00174     b = 360.*(a-(int)a);
00175     md = 296.105+.009192*t2+b;
00176 
00177     a = 5.372616667*t;
00178     b = 360.*(a-(int)a);
00179     nm = 259.183+.002078*t2-b;
00180 
00181     //convert to radian forms for use with trig functions.
00182     tls = 2*degToRad(ls);
00183     nm = degToRad(nm);
00184     tnm = 2*degToRad(nm);
00185     ms = degToRad(ms);
00186     tld = 2*degToRad(ld);
00187     md = degToRad(md);
00188 
00189     // find delta psi and eps, in arcseconds.
00190     dpsi = (-17.2327-.01737*t)*sin(nm)+(-1.2729-.00013*t)*sin(tls)
00191         +.2088*sin(tnm)-.2037*sin(tld)+(.1261-.00031*t)*sin(ms)
00192         +.0675*sin(md)-(.0497-.00012*t)*sin(tls+ms)
00193         -.0342*sin(tld-nm)-.0261*sin(tld+md)+.0214*sin(tls-ms)
00194         -.0149*sin(tls-tld+md)+.0124*sin(tls-nm)+.0114*sin(tld-md);
00195     deps = (9.21+.00091*t)*cos(nm)+(.5522-.00029*t)*cos(tls)
00196         -.0904*cos(tnm)+.0884*cos(tld)+.0216*cos(tls+ms)
00197         +.0183*cos(tld-nm)+.0113*cos(tld+md)-.0093*cos(tls-ms)
00198         -.0066*cos(tls-nm);
00199 
00200     // convert to radians.
00201     dpsi = degToRad(dpsi/3600);
00202     deps = degToRad(deps/3600);
00203 }
00204 
00205 static void EclipticToEquatorial(double t, double fEclLat, double fEclLon,
00206                                  double& RA, double& dec)
00207 {
00208     // Parameter t represents the Julian centuries elapsed since 1900.
00209     // In other words, t = (jd - 2415020.0) / 36525.0
00210 
00211     double seps, ceps;  // sin and cos of mean obliquity
00212     double sx, cx, sy, cy, ty;
00213     double eps;
00214     double deps, dpsi;
00215 
00216     t = (astro::J2000 - 2415020.0) / 36525.0;
00217     t = 0;
00218     eps = Obliquity(t);         // mean obliquity for date
00219     Nutation(t, deps, dpsi);
00220     eps += deps;
00221     seps = sin(eps);
00222     ceps = cos(eps);
00223 
00224     sy = sin(fEclLat);
00225     cy = cos(fEclLat);                          // always non-negative
00226     if (fabs(cy)<1e-20)
00227         cy = 1e-20;             // insure > 0
00228     ty = sy/cy;
00229     cx = cos(fEclLon);
00230     sx = sin(fEclLon);
00231     dec = asin((sy*ceps)+(cy*seps*sx));
00232     RA = atan(((sx*ceps)-(ty*seps))/cx);
00233     if (cx<0)
00234         RA += PI;               // account for atan quad ambiguity
00235     RA = pfmod(RA, TWOPI);
00236 }
00237 
00238 
00239 // Convert equatorial coordinates from one epoch to another.  Method is from
00240 // Chapter 21 of Meeus's _Astronomical Algorithms_
00241 void EpochConvert(double jdFrom, double jdTo,
00242                   double a0, double d0,
00243                   double& a, double& d)
00244 {
00245     double T = (jdFrom - astro::J2000) / 36525.0;
00246     double t = (jdTo - jdFrom) / 36525.0;
00247             
00248     double zeta = (2306.2181 + 1.39656 * T - 0.000139 * T * T) * t +
00249         (0.30188 - 0.000344 * T) * t * t + 0.017998 * t * t * t;
00250     double z = (2306.2181 + 1.39656 * T - 0.000139 * T * T) * t +
00251         (1.09468 + 0.000066 * T) * t * t + 0.018203 * t * t * t;
00252     double theta = (2004.3109 - 0.85330 * T - 0.000217 * T * T) * t -
00253         (0.42665 + 0.000217 * T) * t * t - 0.041833 * t * t * t;
00254     zeta  = degToRad(zeta / 3600.0);
00255     z     = degToRad(z / 3600.0);
00256     theta = degToRad(theta / 3600.0);
00257 
00258     double A = cos(d0) * sin(a0 + zeta);
00259     double B = cos(theta) * cos(d0) * cos(a0 + zeta) -
00260         sin(theta) * sin(d0);
00261     double C = sin(theta) * cos(d0) * cos(a0 + zeta) +
00262         cos(theta) * sin(d0);
00263 
00264     a = atan2(A, B) + z;
00265     d = asin(C);
00266 }
00267 
00268 
00269 double meanAnomalySun(double t)
00270 {
00271     double t2, a, b;
00272 
00273         t2 = t*t;
00274         a = 9.999736042e1*t;
00275         b = 360*(a - (int)a);
00276         
00277     return degToRad(3.5847583e2 - (1.5e-4 + 3.3e-6*t)*t2 + b);
00278 }
00279 
00280 void auxJSun(double t, double* x1, double* x2, double* x3, double* x4,
00281              double* x5, double* x6)
00282 {
00283     *x1 = t/5+0.1;
00284     *x2 = pfmod(4.14473+5.29691e1*t, TWOPI);
00285     *x3 = pfmod(4.641118+2.132991e1*t, TWOPI);
00286     *x4 = pfmod(4.250177+7.478172*t, TWOPI);
00287     *x5 = 5 * *x3 - 2 * *x2;
00288     *x6 = 2 * *x2 - 6 * *x3 + 3 * *x4;
00289 }
00290 
00291 void computePlanetElements(double t, vector<int> pList)
00292 {
00293     // Parameter t represents the Julian centuries elapsed since 1900.
00294     // In other words, t = (jd - 2415020.0) / 36525.0
00295 
00296     double *ep, *pp;
00297     double aa;
00298     int planet;
00299 
00300     for(unsigned i = 0; i < pList.size(); i++)
00301     {
00302         planet = pList[i];
00303         ep = gElements[planet];
00304         pp = gPlanetElements[planet];
00305         aa = ep[1]*t;
00306         pp[0] = ep[0] + 360*(aa-(int)aa) + (ep[3]*t + ep[2])*t*t;
00307         *pp = pfmod(*pp, 360.0);
00308         pp[1] = (ep[1]*9.856263e-3) + (ep[2] + ep[3])/36525;
00309 
00310         for(unsigned j = 4; j < 20; j += 4)
00311             pp[j/4+1] = ((ep[j+3]*t + ep[j+2])*t + ep[j+1])*t + ep[j+0];
00312 
00313         pp[6] = ep[20];
00314         pp[7] = ep[21];
00315         pp[8] = ep[22];
00316     }
00317 }
00318 
00319 void computePlanetCoords(int p, double map, double da, double dhl, double dl,
00320                          double dm, double dml, double dr, double ds,
00321                          double& eclLong, double& eclLat, double& distance)
00322 {
00323     double s, ma, nu, ea, lp, om, lo, slo, clo, inc, spsi, y;
00324 
00325     s = gPlanetElements[p][3] + ds;
00326     ma = map + dm;
00327     astro::anomaly(ma, s, nu, ea);
00328     distance = (gPlanetElements[p][6] + da)*(1 - s*s)/(1 + s*cos(nu));
00329     lp = radToDeg(nu) + gPlanetElements[p][2] + radToDeg(dml - dm);
00330     lp = degToRad(lp);
00331     om = degToRad(gPlanetElements[p][5]);
00332     lo = lp - om;
00333     slo = sin(lo);
00334     clo = cos(lo);
00335     inc = degToRad(gPlanetElements[p][4]);
00336     distance += dr;
00337     spsi = slo*sin(inc);
00338     y = slo*cos(inc);
00339     eclLat = asin(spsi) + dhl;
00340     spsi = sin(eclLat);
00341     eclLong = atan(y/clo) + om + degToRad(dl);
00342     if (clo < 0)
00343         eclLong += PI;
00344     eclLong = pfmod(eclLong, TWOPI);
00345     distance *= KM_PER_AU;
00346 }
00347 
00348 void ComputeGalileanElements(double t,
00349                              double& l1, double& l2, double& l3, double& l4,
00350                              double& p1, double& p2, double& p3, double& p4,
00351                              double& w1, double& w2, double& w3, double& w4,
00352                              double& gamma, double& phi, double& psi,
00353                              double& G, double& Gp)
00354 {
00355     // Parameter t is Julian days, epoch 1950.0.
00356     l1 = 1.8513962 + 3.551552269981*t;
00357     l2 = 3.0670952 + 1.769322724929*t;
00358     l3 = 2.1041485 + 0.87820795239*t;
00359     l4 = 1.473836 + 0.37648621522*t;
00360 
00361     p1 = 1.69451 + 2.8167146e-3*t;
00362     p2 = 2.702927 + 8.248962e-4*t;
00363     p3 = 3.28443 + 1.24396e-4*t;
00364     p4 = 5.851859 + 3.21e-5*t;
00365 
00366     w1 = 5.451267 - 2.3176901e-3*t;
00367     w2 = 1.753028 - 5.695121e-4*t;
00368     w3 = 2.080331 - 1.25263e-4*t;
00369     w4 = 5.630757 - 3.07063e-5*t;
00370 
00371     gamma = 5.7653e-3*sin(2.85674 + 1.8347e-5*t) + 6.002e-4*sin(0.60189 - 2.82274e-4*t);
00372     phi = 3.485014 + 3.033241e-3*t;
00373     psi = 5.524285 - 3.63e-8*t;
00374     G = 0.527745 + 1.45023893e-3*t + gamma;
00375     Gp = 0.5581306 + 5.83982523e-4*t;
00376 }
00377 
00378 
00379 
00381 
00382 class MercuryOrbit : public CachingOrbit
00383 {
00384     Point3d computePosition(double jd) const
00385     {
00386     const int p = 0;  //Planet 0
00387     vector<int> pList;
00388     double t;
00389     double map[4];
00390     double dl, dr, dml, ds, dm, da, dhl;
00391     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
00392 
00393     dl = dr = dml = ds = dm = da = dhl = 0.0;
00394 
00395     // Calculate the Julian centuries elapsed since 1900
00396     t = (jd - 2415020.0)/36525.0;
00397 
00398     // Specify which planets we must compute elements for
00399     pList.push_back(0);
00400     pList.push_back(1);
00401     pList.push_back(3);
00402     computePlanetElements(t, pList);
00403 
00404     // Compute necessary planet mean anomalies
00405     map[0] = degToRad(gPlanetElements[0][0] - gPlanetElements[0][2]);
00406     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
00407     map[2] = 0.0;
00408     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
00409 
00410     // Compute perturbations
00411     dl = 2.04e-3*cos(5*map[1]-2*map[0]+2.1328e-1)+
00412          1.03e-3*cos(2*map[1]-map[0]-2.8046)+
00413          9.1e-4*cos(2*map[3]-map[0]-6.4582e-1)+
00414          7.8e-4*cos(5*map[1]-3*map[0]+1.7692e-1);
00415 
00416     dr = 7.525e-6*cos(2*map[3]-map[0]+9.25251e-1)+
00417          6.802e-6*cos(5*map[1]-3*map[0]-4.53642)+
00418          5.457e-6*cos(2*map[1]-2*map[0]-1.24246)+
00419          3.569e-6*cos(5*map[1]-map[0]-1.35699);
00420 
00421     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
00422                         eclLong, eclLat, distance);
00423 
00424     // Corrections for internal coordinate system
00425     eclLat -= (PI/2);
00426     eclLong += PI;
00427 
00428     return Point3d(cos(eclLong) * sin(eclLat) * distance,
00429                    cos(eclLat) * distance,
00430                    -sin(eclLong) * sin(eclLat) * distance);
00431     };
00432 
00433     double getPeriod() const
00434     {
00435         return 87.9522;
00436     };
00437 
00438     double getBoundingRadius() const
00439     {
00440         return 6.98e+7 * BoundingRadiusSlack;
00441     };
00442 };
00443 
00444 class VenusOrbit : public CachingOrbit
00445 {
00446     Point3d computePosition(double jd) const
00447     {
00448     const int p = 1;  //Planet 1
00449     vector<int> pList;
00450     double t;
00451     double map[4], mas;
00452     double dl, dr, dml, ds, dm, da, dhl;
00453     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
00454 
00455     dl = dr = dml = ds = dm = da = dhl = 0.0;
00456 
00457     //Calculate the Julian centuries elapsed since 1900
00458         t = (jd - 2415020.0)/36525.0;
00459 
00460     mas = meanAnomalySun(t);
00461 
00462     //Specify which planets we must compute elements for
00463     pList.push_back(1);
00464     pList.push_back(3);
00465     computePlanetElements(t, pList);
00466 
00467     //Compute necessary planet mean anomalies
00468     map[0] = 0.0;
00469     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
00470     map[2] = 0.0;
00471     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
00472 
00473     //Compute perturbations
00474     dml = degToRad(7.7e-4*sin(4.1406+t*2.6227));
00475     dm = dml;
00476 
00477         dl = 3.13e-3*cos(2*mas-2*map[1]-2.587)+
00478              1.98e-3*cos(3*mas-3*map[1]+4.4768e-2)+
00479              1.36e-3*cos(mas-map[1]-2.0788)+
00480              9.6e-4*cos(3*mas-2*map[1]-2.3721)+
00481              8.2e-4*cos(map[3]-map[1]-3.6318);
00482 
00483         dr = 2.2501e-5*cos(2*mas-2*map[1]-1.01592)+
00484              1.9045e-5*cos(3*mas-3*map[1]+1.61577)+
00485              6.887e-6*cos(map[3]-map[1]-2.06106)+
00486              5.172e-6*cos(mas-map[1]-5.08065e-1)+
00487              3.62e-6*cos(5*mas-4*map[1]-1.81877)+
00488              3.283e-6*cos(4*mas-4*map[1]+1.10851)+
00489              3.074e-6*cos(2*map[3]-2*map[1]-9.62846e-1);
00490 
00491     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
00492                         eclLong, eclLat, distance);
00493 
00494     //Corrections for internal coordinate system
00495     eclLat -= (PI/2);
00496     eclLong += PI;
00497 
00498     return Point3d(cos(eclLong) * sin(eclLat) * distance,
00499                    cos(eclLat) * distance,
00500                    -sin(eclLong) * sin(eclLat) * distance);
00501     };
00502 
00503     double getPeriod() const
00504     {
00505         return 224.7018;
00506     };
00507 
00508     double getBoundingRadius() const
00509     {
00510         return 1.089e+8 * BoundingRadiusSlack;
00511     };
00512 };
00513 
00514 class EarthOrbit : public CachingOrbit
00515 {
00516     Point3d computePosition(double jd) const
00517     {
00518         double t, t2;
00519         double ls, ms;    // mean longitude and mean anomaly
00520         double s, nu, ea; // eccentricity, true anomaly, eccentric anomaly
00521         double a, b, a1, b1, c1, d1, e1, h1, dl, dr;
00522         double eclLong, distance;
00523 
00524         // Calculate the Julian centuries elapsed since 1900
00525         t = (jd - 2415020.0)/36525.0;
00526 
00527         t2 = t*t;
00528         a = 100.0021359*t;
00529         b = 360.*(a-(int)a);
00530         ls = 279.69668+.0003025*t2+b;
00531         ms = meanAnomalySun(t);
00532         s = .016751-.0000418*t-1.26e-07*t2;
00533         astro::anomaly(degToRad(ms), s, nu, ea);
00534         a = 62.55209472000015*t;
00535         b = 360*(a-(int)a);
00536         a1 = degToRad(153.23+b);
00537         a = 125.1041894*t;
00538         b = 360*(a-(int)a);
00539         b1 = degToRad(216.57+b);
00540         a = 91.56766028*t;
00541         b = 360*(a-(int)a);
00542         c1 = degToRad(312.69+b);
00543         a = 1236.853095*t;
00544         b = 360*(a-(int)a);
00545         d1 = degToRad(350.74-.00144*t2+b);
00546         e1 = degToRad(231.19+20.2*t);
00547         a = 183.1353208*t;
00548         b = 360*(a-(int)a);
00549         h1 = degToRad(353.4+b);
00550         dl = .00134*cos(a1)+.00154*cos(b1)+.002*cos(c1)+.00179*sin(d1)+
00551             .00178*sin(e1);
00552         dr = 5.43e-06*sin(a1)+1.575e-05*sin(b1)+1.627e-05*sin(c1)+
00553             3.076e-05*cos(d1)+9.27e-06*sin(h1);
00554 
00555         eclLong = nu+degToRad(ls-ms+dl) + PI;
00556         eclLong = pfmod(eclLong, TWOPI);
00557         distance = KM_PER_AU * (1.0000002*(1-s*cos(ea))+dr);
00558 
00559         // Correction for internal coordinate system
00560         eclLong += PI;
00561         
00562         return Point3d(-cos(eclLong) * distance,
00563                        0,
00564                        sin(eclLong) * distance);
00565     };
00566 
00567     double getPeriod() const
00568     {
00569         return 365.25;
00570     };
00571 
00572     double getBoundingRadius() const
00573     {
00574         return 1.52e+8 * BoundingRadiusSlack;
00575     };
00576 };
00577 
00578 class LunarOrbit : public CachingOrbit
00579 {
00580     Point3d computePosition(double jd) const
00581     {
00582         double jd19, t, t2;
00583         double ld, ms, md, de, f, n, hp;
00584         double a, sa, sn, b, sb, c, sc, e, e2, l, g, w1, w2;
00585         double m1, m2, m3, m4, m5, m6;
00586         double eclLon, eclLat, horzPar, distance;
00587         double RA, dec;
00588 
00589         // Computation requires an abbreviated Julian day:
00590         // epoch January 0.5, 1900.
00591         jd19 = jd - 2415020.0;
00592         t = jd19/36525;
00593         t2 = t*t;
00594 
00595         m1 = jd19/27.32158213;
00596         m1 = 360.0*(m1-(int)m1);
00597         m2 = jd19/365.2596407;
00598         m2 = 360.0*(m2-(int)m2);
00599         m3 = jd19/27.55455094;
00600         m3 = 360.0*(m3-(int)m3);
00601         m4 = jd19/29.53058868;
00602         m4 = 360.0*(m4-(int)m4);
00603         m5 = jd19/27.21222039;
00604         m5 = 360.0*(m5-(int)m5);
00605         m6 = jd19/6798.363307;
00606         m6 = 360.0*(m6-(int)m6);
00607 
00608         ld = 270.434164+m1-(.001133-.0000019*t)*t2;
00609         ms = 358.475833+m2-(.00015+.0000033*t)*t2;
00610         md = 296.104608+m3+(.009192+.0000144*t)*t2;
00611         de = 350.737486+m4-(.001436-.0000019*t)*t2;
00612         f = 11.250889+m5-(.003211+.0000003*t)*t2;
00613         n = 259.183275-m6+(.002078+.000022*t)*t2;
00614 
00615         a = degToRad(51.2+20.2*t);
00616         sa = sin(a);
00617         sn = sin(degToRad(n));
00618         b = 346.56+(132.87-.0091731*t)*t;
00619         sb = .003964*sin(degToRad(b));
00620         c = degToRad(n+275.05-2.3*t);
00621         sc = sin(c);
00622         ld = ld+.000233*sa+sb+.001964*sn;
00623         ms = ms-.001778*sa;
00624         md = md+.000817*sa+sb+.002541*sn;
00625         f = f+sb-.024691*sn-.004328*sc;
00626         de = de+.002011*sa+sb+.001964*sn;
00627         e = 1-(.002495+7.52e-06*t)*t;
00628         e2 = e*e;
00629 
00630         ld = degToRad(ld);
00631         ms = degToRad(ms);
00632         n = degToRad(n);
00633         de = degToRad(de);
00634         f = degToRad(f);
00635         md = degToRad(md);
00636 
00637         l = 6.28875*sin(md)+1.27402*sin(2*de-md)+.658309*sin(2*de)+
00638             .213616*sin(2*md)-e*.185596*sin(ms)-.114336*sin(2*f)+
00639             .058793*sin(2*(de-md))+.057212*e*sin(2*de-ms-md)+
00640             .05332*sin(2*de+md)+.045874*e*sin(2*de-ms)+.041024*e*sin(md-ms);
00641         l = l-.034718*sin(de)-e*.030465*sin(ms+md)+.015326*sin(2*(de-f))-
00642             .012528*sin(2*f+md)-.01098*sin(2*f-md)+.010674*sin(4*de-md)+
00643             .010034*sin(3*md)+.008548*sin(4*de-2*md)-e*.00791*sin(ms-md+2*de)-
00644             e*.006783*sin(2*de+ms);
00645         l = l+.005162*sin(md-de)+e*.005*sin(ms+de)+.003862*sin(4*de)+
00646             e*.004049*sin(md-ms+2*de)+.003996*sin(2*(md+de))+
00647             .003665*sin(2*de-3*md)+e*.002695*sin(2*md-ms)+
00648             .002602*sin(md-2*(f+de))+e*.002396*sin(2*(de-md)-ms)-
00649             .002349*sin(md+de);
00650         l = l+e2*.002249*sin(2*(de-ms))-e*.002125*sin(2*md+ms)-
00651             e2*.002079*sin(2*ms)+e2*.002059*sin(2*(de-ms)-md)-
00652             .001773*sin(md+2*(de-f))-.001595*sin(2*(f+de))+
00653             e*.00122*sin(4*de-ms-md)-.00111*sin(2*(md+f))+.000892*sin(md-3*de);
00654         l = l-e*.000811*sin(ms+md+2*de)+e*.000761*sin(4*de-ms-2*md)+
00655             e2*.000704*sin(md-2*(ms+de))+e*.000693*sin(ms-2*(md-de))+
00656             e*.000598*sin(2*(de-f)-ms)+.00055*sin(md+4*de)+.000538*sin(4*md)+
00657             e*.000521*sin(4*de-ms)+.000486*sin(2*md-de);
00658         l = l+e2*.000717*sin(md-2*ms);
00659         eclLon = ld+degToRad(l);
00660         eclLon = pfmod(eclLon, TWOPI);
00661 
00662         g = 5.12819*sin(f)+.280606*sin(md+f)+.277693*sin(md-f)+
00663             .173238*sin(2*de-f)+.055413*sin(2*de+f-md)+.046272*sin(2*de-f-md)+
00664             .032573*sin(2*de+f)+.017198*sin(2*md+f)+.009267*sin(2*de+md-f)+
00665             .008823*sin(2*md-f)+e*.008247*sin(2*de-ms-f);
00666         g = g+.004323*sin(2*(de-md)-f)+.0042*sin(2*de+f+md)+
00667             e*.003372*sin(f-ms-2*de)+e*.002472*sin(2*de+f-ms-md)+
00668             e*.002222*sin(2*de+f-ms)+e*.002072*sin(2*de-f-ms-md)+
00669             e*.001877*sin(f-ms+md)+.001828*sin(4*de-f-md)-e*.001803*sin(f+ms)-
00670             .00175*sin(3*f);
00671         g = g+e*.00157*sin(md-ms-f)-.001487*sin(f+de)-e*.001481*sin(f+ms+md)+
00672             e*.001417*sin(f-ms-md)+e*.00135*sin(f-ms)+.00133*sin(f-de)+
00673             .001106*sin(f+3*md)+.00102*sin(4*de-f)+.000833*sin(f+4*de-md)+
00674             .000781*sin(md-3*f)+.00067*sin(f+4*de-2*md);
00675         g = g+.000606*sin(2*de-3*f)+.000597*sin(2*(de+md)-f)+
00676             e*.000492*sin(2*de+md-ms-f)+.00045*sin(2*(md-de)-f)+
00677             .000439*sin(3*md-f)+.000423*sin(f+2*(de+md))+
00678             .000422*sin(2*de-f-3*md)-e*.000367*sin(ms+f+2*de-md)-
00679             e*.000353*sin(ms+f+2*de)+.000331*sin(f+4*de);
00680         g = g+e*.000317*sin(2*de+f-ms+md)+e2*.000306*sin(2*(de-ms)-f)-
00681             .000283*sin(md+3*f);
00682         w1 = .0004664*cos(n);
00683         w2 = .0000754*cos(c);
00684         eclLat = degToRad(g)*(1-w1-w2);
00685 
00686         hp = .950724+.051818*cos(md)+.009531*cos(2*de-md)+.007843*cos(2*de)+
00687          .002824*cos(2*md)+.000857*cos(2*de+md)+e*.000533*cos(2*de-ms)+
00688          e*.000401*cos(2*de-md-ms)+e*.00032*cos(md-ms)-.000271*cos(de)-
00689          e*.000264*cos(ms+md)-.000198*cos(2*f-md);
00690         hp = hp+.000173*cos(3*md)+.000167*cos(4*de-md)-e*.000111*cos(ms)+
00691          .000103*cos(4*de-2*md)-.000084*cos(2*md-2*de)-
00692          e*.000083*cos(2*de+ms)+.000079*cos(2*de+2*md)+.000072*cos(4*de)+
00693          e*.000064*cos(2*de-ms+md)-e*.000063*cos(2*de+ms-md)+
00694          e*.000041*cos(ms+de);
00695         hp = hp+e*.000035*cos(2*md-ms)-.000033*cos(3*md-2*de)-
00696          .00003*cos(md+de)-.000029*cos(2*(f-de))-e*.000029*cos(2*md+ms)+
00697          e2*.000026*cos(2*(de-ms))-.000023*cos(2*(f-de)+md)+
00698          e*.000019*cos(4*de-ms-md);
00699         horzPar = degToRad(hp);
00700 
00701         // At this point we have values of ecliptic longitude, latitude and
00702         // horizontal parallax (eclLong, eclLat, horzPar) in radians.
00703 
00704         // Now compute distance using horizontal parallax.
00705         distance = 6378.14 / sin(horzPar);
00706 
00707         // Finally convert eclLat, eclLon to RA, Dec.
00708         EclipticToEquatorial(t, eclLat, eclLon, RA, dec);
00709 
00710         // RA and Dec are referred to the equinox of date; we want to use
00711         // the J2000 equinox instead.  A better idea would be to directly 
00712         // compute the position of the Moon in this coordinate system, but
00713         // this was easier.
00714         EpochConvert(jd, astro::J2000, RA, dec, RA, dec);
00715 
00716         // Corrections for internal coordinate system
00717         dec -= (PI/2);
00718         RA += PI;
00719 
00720         return Point3d(cos(RA) * sin(dec) * distance,
00721                        cos(dec) * distance,
00722                        -sin(RA) * sin(dec) * distance);
00723     };
00724 
00725     double getPeriod() const
00726     {
00727         return 27.321661;
00728     };
00729 
00730     double getBoundingRadius() const
00731     {
00732         return 405504 * BoundingRadiusSlack;
00733     };
00734 };
00735 
00736 class MarsOrbit : public CachingOrbit
00737 {
00738     Point3d computePosition(double jd) const
00739     {
00740     const int p = 2;  //Planet 2
00741     vector<int> pList;
00742     double t;
00743     double map[4], mas, a;
00744     double dl, dr, dml, ds, dm, da, dhl;
00745     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
00746 
00747     dl = dr = dml = ds = dm = da = dhl = 0.0;
00748 
00749     //Calculate the Julian centuries elapsed since 1900
00750         t = (jd - 2415020.0)/36525.0;
00751 
00752     mas = meanAnomalySun(t);
00753 
00754     //Specify which planets we must compute elements for
00755     pList.push_back(1);
00756     pList.push_back(2);
00757     pList.push_back(3);
00758     computePlanetElements(t, pList);
00759 
00760     //Compute necessary planet mean anomalies
00761     map[0] = 0.0;
00762     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
00763     map[2] = degToRad(gPlanetElements[2][0] - gPlanetElements[2][2]);
00764     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
00765 
00766     //Compute perturbations
00767     a = 3*map[3]-8*map[2]+4*mas;
00768     dml = degToRad(-1*(1.133e-2*sin(a)+9.33e-3*cos(a)));
00769     dm = dml;
00770     dl = 7.05e-3*cos(map[3]-map[2]-8.5448e-1)+
00771              6.07e-3*cos(2*map[3]-map[2]-3.2873)+
00772              4.45e-3*cos(2*map[3]-2*map[2]-3.3492)+
00773              3.88e-3*cos(mas-2*map[2]+3.5771e-1)+
00774              2.38e-3*cos(mas-map[2]+6.1256e-1)+
00775              2.04e-3*cos(2*mas-3*map[2]+2.7688)+
00776              1.77e-3*cos(3*map[2]-map[2-1]-1.0053)+
00777              1.36e-3*cos(2*mas-4*map[2]+2.6894)+
00778              1.04e-3*cos(map[3]+3.0749e-1);
00779 
00780     dr = 5.3227e-5*cos(map[3]-map[2]+7.17864e-1)+
00781              5.0989e-5*cos(2*map[3]-2*map[2]-1.77997)+
00782              3.8278e-5*cos(2*map[3]-map[2]-1.71617)+
00783              1.5996e-5*cos(mas-map[2]-9.69618e-1)+
00784              1.4764e-5*cos(2*mas-3*map[2]+1.19768)+
00785              8.966e-6*cos(map[3]-2*map[2]+7.61225e-1);
00786     dr += 7.914e-6*cos(3*map[3]-2*map[2]-2.43887)+
00787              7.004e-6*cos(2*map[3]-3*map[2]-1.79573)+
00788              6.62e-6*cos(mas-2*map[2]+1.97575)+
00789              4.93e-6*cos(3*map[3]-3*map[2]-1.33069)+
00790              4.693e-6*cos(3*mas-5*map[2]+3.32665)+
00791              4.571e-6*cos(2*mas-4*map[2]+4.27086)+
00792              4.409e-6*cos(3*map[3]-map[2]-2.02158);
00793 
00794     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
00795                         eclLong, eclLat, distance);
00796 
00797     //Corrections for internal coordinate system
00798     eclLat -= (PI/2);
00799     eclLong += PI;
00800 
00801     return Point3d(cos(eclLong) * sin(eclLat) * distance,
00802                    cos(eclLat) * distance,
00803                    -sin(eclLong) * sin(eclLat) * distance);
00804     };
00805 
00806     double getPeriod() const
00807     {
00808         return 689.998725;
00809     };
00810 
00811     double getBoundingRadius() const
00812     {
00813         return 2.49e+8 * BoundingRadiusSlack;
00814     };
00815 };
00816 
00817 class JupiterOrbit : public CachingOrbit
00818 {
00819     Point3d computePosition(double jd) const
00820     {
00821     const int p = 3;  //Planet 3
00822     vector<int> pList(1, p);
00823     double t, map;
00824     double dl, dr, dml, ds, dm, da, dhl, s;
00825     double dp;
00826     double x1, x2, x3, x4, x5, x6, x7;
00827     double sx3, cx3, s2x3, c2x3;
00828     double sx5, cx5, s2x5;
00829     double sx6;
00830     double sx7, cx7, s2x7, c2x7, s3x7, c3x7, s4x7, c4x7, c5x7;
00831     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
00832 
00833     dl = dr = dml = ds = dm = da = dhl = 0.0;
00834 
00835     //Calculate the Julian centuries elapsed since 1900
00836         t = (jd - 2415020.0)/36525.0;
00837 
00838     computePlanetElements(t, pList);
00839 
00840     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
00841 
00842     //Compute perturbations
00843     s = gPlanetElements[p][3];
00844     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
00845     x7 = x3-x2;
00846     sx3 = sin(x3);
00847     cx3 = cos(x3);
00848     s2x3 = sin(2*x3);
00849     c2x3 = cos(2*x3);
00850     sx5 = sin(x5);
00851     cx5 = cos(x5);
00852     s2x5 = sin(2*x5);
00853     sx6 = sin(x6);
00854     sx7 = sin(x7);
00855     cx7 = cos(x7);
00856     s2x7 = sin(2*x7);
00857     c2x7 = cos(2*x7);
00858     s3x7 = sin(3*x7);
00859     c3x7 = cos(3*x7);
00860     s4x7 = sin(4*x7);
00861     c4x7 = cos(4*x7);
00862     c5x7 = cos(5*x7);
00863     dml = (3.31364e-1-(1.0281e-2+4.692e-3*x1)*x1)*sx5+
00864               (3.228e-3-(6.4436e-2-2.075e-3*x1)*x1)*cx5-
00865               (3.083e-3+(2.75e-4-4.89e-4*x1)*x1)*s2x5+
00866               2.472e-3*sx6+1.3619e-2*sx7+1.8472e-2*s2x7+6.717e-3*s3x7+
00867               2.775e-3*s4x7+6.417e-3*s2x7*sx3+
00868               (7.275e-3-1.253e-3*x1)*sx7*sx3+
00869               2.439e-3*s3x7*sx3-(3.5681e-2+1.208e-3*x1)*sx7*cx3;
00870     dml += -3.767e-3*c2x7*sx3-(3.3839e-2+1.125e-3*x1)*cx7*sx3-
00871               4.261e-3*s2x7*cx3+
00872               (1.161e-3*x1-6.333e-3)*cx7*cx3+
00873               2.178e-3*cx3-6.675e-3*c2x7*cx3-2.664e-3*c3x7*cx3-
00874               2.572e-3*sx7*s2x3-3.567e-3*s2x7*s2x3+2.094e-3*cx7*c2x3+
00875               3.342e-3*c2x7*c2x3;
00876     dml = degToRad(dml);
00877     ds = (3606+(130-43*x1)*x1)*sx5+(1289-580*x1)*cx5-6764*sx7*sx3-
00878              1110*s2x7*sx3-224*s3x7*sx3-204*sx3+(1284+116*x1)*cx7*sx3+
00879              188*c2x7*sx3+(1460+130*x1)*sx7*cx3+224*s2x7*cx3-817*cx3+
00880              6074*cx3*cx7+992*c2x7*cx3+
00881              508*c3x7*cx3+230*c4x7*cx3+108*c5x7*cx3;
00882     ds += -(956+73*x1)*sx7*s2x3+448*s2x7*s2x3+137*s3x7*s2x3+
00883              (108*x1-997)*cx7*s2x3+480*c2x7*s2x3+148*c3x7*s2x3+
00884              (99*x1-956)*sx7*c2x3+490*s2x7*c2x3+
00885              158*s3x7*c2x3+179*c2x3+(1024+75*x1)*cx7*c2x3-
00886              437*c2x7*c2x3-132*c3x7*c2x3;
00887     ds *= 1e-7;
00888     dp = (7.192e-3-3.147e-3*x1)*sx5-4.344e-3*sx3+
00889              (x1*(1.97e-4*x1-6.75e-4)-2.0428e-2)*cx5+
00890              3.4036e-2*cx7*sx3+(7.269e-3+6.72e-4*x1)*sx7*sx3+
00891              5.614e-3*c2x7*sx3+2.964e-3*c3x7*sx3+3.7761e-2*sx7*cx3+
00892              6.158e-3*s2x7*cx3-
00893              6.603e-3*cx7*cx3-5.356e-3*sx7*s2x3+2.722e-3*s2x7*s2x3+
00894              4.483e-3*cx7*s2x3-2.642e-3*c2x7*s2x3+4.403e-3*sx7*c2x3-
00895              2.536e-3*s2x7*c2x3+5.547e-3*cx7*c2x3-2.689e-3*c2x7*c2x3;
00896     dm = dml-(degToRad(dp)/s);
00897     da = 205*cx7-263*cx5+693*c2x7+312*c3x7+147*c4x7+299*sx7*sx3+
00898              181*c2x7*sx3+204*s2x7*cx3+111*s3x7*cx3-337*cx7*cx3-
00899              111*c2x7*cx3;
00900     da *= 1e-6;
00901 
00902     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
00903                         eclLong, eclLat, distance);
00904 
00905     //Corrections for internal coordinate system
00906     eclLat -= (PI/2);
00907     eclLong += PI;
00908 
00909     return Point3d(cos(eclLong) * sin(eclLat) * distance,
00910                    cos(eclLat) * distance,
00911                    -sin(eclLong) * sin(eclLat) * distance);
00912     };
00913 
00914     double getPeriod() const
00915     {
00916         return 4332.66855;
00917     };
00918 
00919     double getBoundingRadius() const
00920     {
00921         return 8.16e+8 * BoundingRadiusSlack;
00922     };
00923 };
00924 
00925 class SaturnOrbit : public CachingOrbit
00926 {
00927     Point3d computePosition(double jd) const
00928     {
00929     const int p = 4;  //Planet 4
00930     vector<int> pList(1, p);
00931     double t, map;
00932     double dl, dr, dml, ds, dm, da, dhl, s;
00933     double dp;
00934     double x1, x2, x3, x4, x5, x6, x7, x8;
00935     double sx3, cx3, s2x3, c2x3, s3x3, c3x3, s4x3, c4x3;
00936     double sx5, cx5, s2x5, c2x5;
00937     double sx6;
00938     double sx7, cx7, s2x7, c2x7, s3x7, c3x7, s4x7, c4x7, c5x7, s5x7;
00939     double s2x8, c2x8, s3x8, c3x8;
00940     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
00941 
00942     dl = dr = dml = ds = dm = da = dhl = 0.0;
00943 
00944     //Calculate the Julian centuries elapsed since 1900
00945         t = (jd - 2415020.0)/36525.0;
00946 
00947     computePlanetElements(t, pList);
00948 
00949     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
00950 
00951     //Compute perturbations
00952     s = gPlanetElements[p][3];
00953     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
00954     x7 = x3-x2;
00955     sx3 = sin(x3);
00956     cx3 = cos(x3);
00957     s2x3 = sin(2*x3);
00958     c2x3 = cos(2*x3);
00959     sx5 = sin(x5);
00960     cx5 = cos(x5);
00961     s2x5 = sin(2*x5);
00962     sx6 = sin(x6);
00963     sx7 = sin(x7);
00964     cx7 = cos(x7);
00965     s2x7 = sin(2*x7);
00966     c2x7 = cos(2*x7);
00967     s3x7 = sin(3*x7);
00968     c3x7 = cos(3*x7);
00969     s4x7 = sin(4*x7);
00970     c4x7 = cos(4*x7);
00971     c5x7 = cos(5*x7);
00972     s3x3 = sin(3*x3);
00973     c3x3 = cos(3*x3);
00974     s4x3 = sin(4*x3);
00975     c4x3 = cos(4*x3);
00976     c2x5 = cos(2*x5);
00977     s5x7 = sin(5*x7);
00978     x8 = x4-x3;
00979     s2x8 = sin(2*x8);
00980     c2x8 = cos(2*x8);
00981     s3x8 = sin(3*x8);
00982     c3x8 = cos(3*x8);
00983     dml = 7.581e-3*s2x5-7.986e-3*sx6-1.48811e-1*sx7-4.0786e-2*s2x7-
00984               (8.14181e-1-(1.815e-2-1.6714e-2*x1)*x1)*sx5-
00985               (1.0497e-2-(1.60906e-1-4.1e-3*x1)*x1)*cx5-1.5208e-2*s3x7-
00986               6.339e-3*s4x7-6.244e-3*sx3-1.65e-2*s2x7*sx3+
00987               (8.931e-3+2.728e-3*x1)*sx7*sx3-5.775e-3*s3x7*sx3+
00988               (8.1344e-2+3.206e-3*x1)*cx7*sx3+1.5019e-2*c2x7*sx3;
00989     dml += (8.5581e-2+2.494e-3*x1)*sx7*cx3+1.4394e-2*c2x7*cx3+
00990               (2.5328e-2-3.117e-3*x1)*cx7*cx3+
00991               6.319e-3*c3x7*cx3+6.369e-3*sx7*s2x3+9.156e-3*s2x7*s2x3+
00992               7.525e-3*s3x8*s2x3-5.236e-3*cx7*c2x3-7.736e-3*c2x7*c2x3-
00993               7.528e-3*c3x8*c2x3;
00994     dml = degToRad(dml);
00995     ds = (-7927+(2548+91*x1)*x1)*sx5+(13381+(1226-253*x1)*x1)*cx5+
00996              (248-121*x1)*s2x5-(305+91*x1)*c2x5+412*s2x7+12415*sx3+
00997              (390-617*x1)*sx7*sx3+(165-204*x1)*s2x7*sx3+26599*cx7*sx3-
00998              4687*c2x7*sx3-1870*c3x7*sx3-821*c4x7*sx3-
00999              377*c5x7*sx3+497*c2x8*sx3+(163-611*x1)*cx3;
01000     ds += -12696*sx7*cx3-4200*s2x7*cx3-1503*s3x7*cx3-619*s4x7*cx3-
01001              268*s5x7*cx3-(282+1306*x1)*cx7*cx3+(-86+230*x1)*c2x7*cx3+
01002              461*s2x8*cx3-350*s2x3+(2211-286*x1)*sx7*s2x3-
01003              2208*s2x7*s2x3-568*s3x7*s2x3-346*s4x7*s2x3-
01004              (2780+222*x1)*cx7*s2x3+(2022+263*x1)*c2x7*s2x3+248*c3x7*s2x3+
01005              242*s3x8*s2x3+467*c3x8*s2x3-490*c2x3-(2842+279*x1)*sx7*c2x3;
01006     ds += (128+226*x1)*s2x7*c2x3+224*s3x7*c2x3+
01007              (-1594+282*x1)*cx7*c2x3+(2162-207*x1)*c2x7*c2x3+
01008              561*c3x7*c2x3+343*c4x7*c2x3+469*s3x8*c2x3-242*c3x8*c2x3-
01009              205*sx7*s3x3+262*s3x7*s3x3+208*cx7*c3x3-271*c3x7*c3x3-
01010              382*c3x7*s4x3-376*s3x7*c4x3;
01011     ds *= 1e-7;
01012     dp = (7.7108e-2+(7.186e-3-1.533e-3*x1)*x1)*sx5-7.075e-3*sx7+
01013              (4.5803e-2-(1.4766e-2+5.36e-4*x1)*x1)*cx5-7.2586e-2*cx3-
01014              7.5825e-2*sx7*sx3-2.4839e-2*s2x7*sx3-8.631e-3*s3x7*sx3-
01015              1.50383e-1*cx7*cx3+2.6897e-2*c2x7*cx3+1.0053e-2*c3x7*cx3-
01016              (1.3597e-2+1.719e-3*x1)*sx7*s2x3+1.1981e-2*s2x7*c2x3;
01017     dp += -(7.742e-3-1.517e-3*x1)*cx7*s2x3+
01018              (1.3586e-2-1.375e-3*x1)*c2x7*c2x3-
01019              (1.3667e-2-1.239e-3*x1)*sx7*c2x3+
01020              (1.4861e-2+1.136e-3*x1)*cx7*c2x3-
01021              (1.3064e-2+1.628e-3*x1)*c2x7*c2x3;
01022     dm = dml-(degToRad(dp)/s);
01023     da = 572*sx5-1590*s2x7*cx3+2933*cx5-647*s3x7*cx3+33629*cx7-
01024              344*s4x7*cx3-3081*c2x7+2885*cx7*cx3-1423*c3x7+
01025              (2172+102*x1)*c2x7*cx3-671*c4x7+296*c3x7*cx3-320*c5x7-
01026              267*s2x7*s2x3+1098*sx3-778*cx7*s2x3-2812*sx7*sx3;
01027     da += 495*c2x7*s2x3+688*s2x7*sx3+250*c3x7*s2x3-393*s3x7*sx3-
01028              856*sx7*c2x3-228*s4x7*sx3+441*s2x7*c2x3+2138*cx7*sx3+
01029              296*c2x7*c2x3-999*c2x7*sx3+211*c3x7*c2x3-642*c3x7*sx3-
01030              427*sx7*s3x3-325*c4x7*sx3+398*s3x7*s3x3-890*cx3+
01031              344*cx7*c3x3+2206*sx7*cx3-427*c3x7*c3x3;
01032     da *= 1e-6;
01033     dhl = 7.47e-4*cx7*sx3+1.069e-3*cx7*cx3+2.108e-3*s2x7*s2x3+
01034               1.261e-3*c2x7*s2x3+1.236e-3*s2x7*c2x3-2.075e-3*c2x7*c2x3;
01035     dhl = degToRad(dhl);
01036 
01037     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
01038                         eclLong, eclLat, distance);
01039 
01040     //Corrections for internal coordinate system
01041     eclLat -= (PI/2);
01042     eclLong += PI;
01043 
01044     return Point3d(cos(eclLong) * sin(eclLat) * distance,
01045                    cos(eclLat) * distance,
01046                    -sin(eclLong) * sin(eclLat) * distance);
01047     };
01048 
01049     double getPeriod() const
01050     {
01051         return 10759.42493;
01052     };
01053 
01054     double getBoundingRadius() const
01055     {
01056         return 1.50e+9 * BoundingRadiusSlack;
01057     };
01058 };
01059 
01060 class UranusOrbit : public CachingOrbit
01061 {
01062     Point3d computePosition(double jd) const
01063     {
01064     const int p = 5;  //Planet 5
01065     vector<int> pList(1, p);
01066     double t, map;
01067     double dl, dr, dml, ds, dm, da, dhl, s;
01068     double dp;
01069     double x1, x2, x3, x4, x5, x6;
01070     double x8, x9, x10, x11, x12;
01071     double sx4, cx4, s2x4, c2x4;
01072     double sx9, cx9, s2x9, c2x9;
01073     double sx11, cx11;
01074     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
01075 
01076     dl = dr = dml = ds = dm = da = dhl = 0.0;
01077 
01078     //Calculate the Julian centuries elapsed since 1900
01079         t = (jd - 2415020.0)/36525.0;
01080 
01081     computePlanetElements(t, pList);
01082 
01083     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
01084 
01085     //Compute perturbations
01086     s = gPlanetElements[p][3];
01087     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
01088     x8 = pfmod(1.46205+3.81337*t, TWOPI);
01089     x9 = 2*x8-x4;
01090     sx9 = sin(x9);
01091     cx9 = cos(x9);
01092     s2x9 = sin(2*x9);
01093     c2x9 = cos(2*x9);
01094     x10 = x4-x2;
01095     x11 = x4-x3;
01096     x12 = x8-x4;
01097     dml = (8.64319e-1-1.583e-3*x1)*sx9+(8.2222e-2-6.833e-3*x1)*cx9+
01098               3.6017e-2*s2x9-3.019e-3*c2x9+8.122e-3*sin(x6);
01099     dml = degToRad(dml);
01100     dp = 1.20303e-1*sx9+6.197e-3*s2x9+(1.9472e-2-9.47e-4*x1)*cx9;
01101     dm = dml-(degToRad(dp)/s);
01102     ds = (163*x1-3349)*sx9+20981*cx9+1311*c2x9;
01103     ds *= 1e-7;
01104     da = -3.825e-3*cx9;
01105     dl = (1.0122e-2-9.88e-4*x1)*sin(x4+x11)+
01106              (-3.8581e-2+(2.031e-3-1.91e-3*x1)*x1)*cos(x4+x11)+
01107              (3.4964e-2-(1.038e-3-8.68e-4*x1)*x1)*cos(2*x4+x11)+
01108              5.594e-3*sin(x4+3*x12)-1.4808e-2*sin(x10)-
01109              5.794e-3*sin(x11)+2.347e-3*cos(x11)+9.872e-3*sin(x12)+
01110              8.803e-3*sin(2*x12)-4.308e-3*sin(3*x12);
01111     sx11 = sin(x11);
01112     cx11 = cos(x11);
01113     sx4 = sin(x4);
01114     cx4 = cos(x4);
01115     s2x4 = sin(2*x4);
01116     c2x4 = cos(2*x4);
01117     dhl = (4.58e-4*sx11-6.42e-4*cx11-5.17e-4*cos(4*x12))*sx4-
01118               (3.47e-4*sx11+8.53e-4*cx11+5.17e-4*sin(4*x11))*cx4+
01119               4.03e-4*(cos(2*x12)*s2x4+sin(2*x12)*c2x4);
01120     dhl = degToRad(dhl);
01121 
01122     dr = -25948+4985*cos(x10)-1230*cx4+3354*cos(x11)+904*cos(2*x12)+
01123              894*(cos(x12)-cos(3*x12))+(5795*cx4-1165*sx4+1388*c2x4)*sx11+
01124              (1351*cx4+5702*sx4+1388*s2x4)*cos(x11);
01125     dr *= 1e-6;
01126 
01127     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
01128                         eclLong, eclLat, distance);
01129 
01130     //Corrections for internal coordinate system
01131     eclLat -= (PI/2);
01132     eclLong += PI;
01133 
01134     return Point3d(cos(eclLong) * sin(eclLat) * distance,
01135                    cos(eclLat) * distance,
01136                    -sin(eclLong) * sin(eclLat) * distance);
01137     };
01138 
01139     double getPeriod() const
01140     {
01141         return 30686.07698;
01142     };
01143 
01144     double getBoundingRadius() const
01145     {
01146         return 3.01e+9 * BoundingRadiusSlack;
01147     };
01148 };
01149 
01150 class NeptuneOrbit : public CachingOrbit
01151 {
01152     Point3d computePosition(double jd) const
01153     {
01154     const int p = 6;  //Planet 6
01155     vector<int> pList(1, p);
01156     double t, map;
01157     double dl, dr, dml, ds, dm, da, dhl, s;
01158     double dp;
01159     double x1, x2, x3, x4, x5, x6;
01160     double x8, x9, x10, x11, x12;
01161     double sx8, cx8;
01162     double sx9, cx9, s2x9, c2x9;
01163     double s2x12, c2x12;
01164     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
01165 
01166     dl = dr = dml = ds = dm = da = dhl = 0.0;
01167 
01168     //Calculate the Julian centuries elapsed since 1900
01169         t = (jd - 2415020.0)/36525.0;
01170 
01171     computePlanetElements(t, pList);
01172 
01173     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
01174 
01175     //Compute perturbations
01176     s = gPlanetElements[p][3];
01177         auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
01178     x8 = pfmod(1.46205+3.81337*t, TWOPI);
01179     x9 = 2*x8-x4;
01180         sx9 = sin(x9);
01181         cx9 = cos(x9);
01182     s2x9 = sin(2*x9);
01183         c2x9 = cos(2*x9);
01184         x10 = x8-x2;
01185         x11 = x8-x3;
01186         x12 = x8-x4;
01187         dml = (1.089e-3*x1-5.89833e-1)*sx9+(4.658e-3*x1-5.6094e-2)*cx9-
01188               2.4286e-2*s2x9;
01189         dml = degToRad(dml);
01190         dp = 2.4039e-2*sx9-2.5303e-2*cx9+6.206e-3*s2x9-5.992e-3*c2x9;
01191         dm = dml-(degToRad(dp)/s);
01192         ds = 4389*sx9+1129*s2x9+4262*cx9+1089*c2x9;
01193         ds *= 1e-7;
01194         da = 8189*cx9-817*sx9+781*c2x9;
01195         da *= 1e-6;
01196         s2x12 = sin(2*x12);
01197         c2x12 = cos(2*x12);
01198         sx8 = sin(x8);
01199         cx8 = cos(x8);
01200         dl = -9.556e-3*sin(x10)-5.178e-3*sin(x11)+2.572e-3*s2x12-
01201              2.972e-3*c2x12*sx8-2.833e-3*s2x12*cx8;
01202         dhl = 3.36e-4*c2x12*sx8+3.64e-4*s2x12*cx8;
01203         dhl = degToRad(dhl);
01204         dr = -40596+4992*cos(x10)+2744*cos(x11)+2044*cos(x12)+1051*c2x12;
01205         dr *= 1e-6;
01206 
01207     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
01208                         eclLong, eclLat, distance);
01209 
01210     //Corrections for internal coordinate system
01211     eclLat -= (PI/2);
01212     eclLong += PI;
01213 
01214     return Point3d(cos(eclLong) * sin(eclLat) * distance,
01215                    cos(eclLat) * distance,
01216                    -sin(eclLong) * sin(eclLat) * distance);
01217     };
01218 
01219     double getPeriod() const
01220     {
01221         return 60190.64325;
01222     };
01223 
01224     double getBoundingRadius() const
01225     {
01226         return 4.54e+9 * BoundingRadiusSlack;
01227     };
01228 };
01229 
01230 class PlutoOrbit : public CachingOrbit
01231 {
01232     Point3d computePosition(double jd) const
01233     {
01234     const int p = 7;  //Planet 7
01235     vector<int> pList(1, p);
01236     double t, map;
01237     double dl, dr, dml, ds, dm, da, dhl;
01238     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
01239 
01240     dl = dr = dml = ds = dm = da = dhl = 0.0;
01241 
01242     //Calculate the Julian centuries elapsed since 1900
01243     t = (jd - 2415020.0)/36525.0;
01244 
01245     computePlanetElements(t, pList);
01246 
01247     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
01248 
01249     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
01250                         eclLong, eclLat, distance);
01251 
01252     //Corrections for internal coordinate system
01253     eclLat -= PI / 2;
01254     eclLong += PI;
01255 
01256     return Point3d(cos(eclLong) * sin(eclLat) * distance,
01257                    cos(eclLat) * distance,
01258                    -sin(eclLong) * sin(eclLat) * distance);
01259     };
01260 
01261     double getPeriod() const
01262     {
01263         return 90779.235;
01264     };
01265 
01266     double getBoundingRadius() const
01267     {
01268         return 7.38e+9 * BoundingRadiusSlack;
01269     };
01270 };
01271 
01272 
01273 // Compute for mean anomaly M the point on the ellipse with
01274 // semimajor axis a and eccentricity e.  This helper function assumes
01275 // a low eccentricity; orbit.cpp has functions appropriate for solving
01276 // Kepler's equation for larger values of e.
01277 static Point3d ellipsePosition(double a, double e, double M)
01278 {
01279     // Solve Kepler's equation--for a low eccentricity orbit, just a few
01280     // iterations is enough.
01281     double E = M;
01282     for (int k = 0; k < 3; k++)
01283         E = M + e * sin(E);
01284 
01285     return Point3d(a * (cos(E) - e),
01286                    0.0,
01287                    a * sqrt(1 - square(e)) * -sin(E));
01288 }
01289 
01290 
01291 class PhobosOrbit : public CachingOrbit
01292 {
01293     Point3d computePosition(double jd) const
01294     {
01295         double epoch = 2433283.0 - 0.5; // 00:00 1 Jan 1950
01296         double a     = 9380.0;
01297         double e     = 0.0151;
01298         double w0    = 150.247;
01299         double M0    =  92.474;
01300         double i     =   1.075;
01301         double node0 = 164.931;
01302         double n     = 1128.8444155;
01303         double Pw    =  1.131;
01304         double Pnode =  2.262;
01305 
01306         double refplane_RA  = 317.724;
01307         double refplane_Dec =  52.924;
01308         double marspole_RA  = 317.681;
01309         double marspole_Dec =  52.886;
01310 
01311         double t = jd - epoch;
01312         t += 10.5 / 1440.0;     // light time correction?
01313         double T = t / 365.25;
01314 
01315         double dnode = 360.0 / Pnode;
01316         double dw    = 360.0 / Pw;
01317         double node = degToRad(node0 + T * dnode);
01318         double w    = degToRad(w0 + T * dw - T * dnode);
01319         double M    = degToRad(M0 + t * n  - T * dw);
01320 
01321         Point3d p = ellipsePosition(a, e, M);
01322 
01323         // Orientation of the orbital plane with respect to the Laplacian plane
01324         Mat3d Rorbit     = (Mat3d::yrotation(node) *
01325                             Mat3d::xrotation(degToRad(i)) *
01326                             Mat3d::yrotation(w));
01327 
01328         // Rotate to the Earth's equatorial plane
01329         double N = degToRad(refplane_RA);
01330         double J = degToRad(90 - refplane_Dec);
01331         Mat3d RLaplacian = (Mat3d::yrotation( N) *
01332                             Mat3d::xrotation( J) *
01333                             Mat3d::yrotation(-N));
01334 
01335         // Rotate to the Martian equatorial plane
01336         N = degToRad(marspole_RA);
01337         J = degToRad(90 - marspole_Dec);
01338         Mat3d RMars_eq   = (Mat3d::yrotation( N) *
01339                             Mat3d::xrotation(-J) *
01340                             Mat3d::yrotation(-N));
01341 
01342         return RMars_eq * (RLaplacian * (Rorbit * p));
01343     }
01344     
01345     double getPeriod() const
01346     {
01347         return 0.319;
01348     }
01349 
01350     double getBoundingRadius() const
01351     {
01352         return 9380 * BoundingRadiusSlack;
01353     }
01354 };
01355 
01356 
01357 class DeimosOrbit : public CachingOrbit
01358 {
01359     Point3d computePosition(double jd) const
01360     {
01361         double epoch = 2433283.0 - 0.5;
01362         double a     = 23460.0;
01363         double e     = 0.0002;
01364         double w0    = 290.496;
01365         double M0    = 296.230;
01366         double i     = 1.793;
01367         double node0 = 339.600;
01368         double n     = 285.1618919;
01369         double Pw    = 26.892;
01370         double Pnode = 54.536;
01371 
01372         double refplane_RA  = 316.700;
01373         double refplane_Dec =  53.564;
01374         double marspole_RA  = 317.681;
01375         double marspole_Dec =  52.886;
01376 
01377         double t = jd - epoch;
01378         t += 10.5 / 1440.0;     // light time correction?
01379         double T = t / 365.25;
01380 
01381         double dnode = 360.0 / Pnode;
01382         double dw    = 360.0 / Pw;
01383         double node = degToRad(node0 + T * dnode);
01384         double w    = degToRad(w0 + T * dw - T * dnode);
01385         double M    = degToRad(M0 + t * n  - T * dw);
01386 
01387         Point3d p = ellipsePosition(a, e, M);
01388 
01389         // Orientation of the orbital plane with respect to the Laplacian plane
01390         Mat3d Rorbit     = (Mat3d::yrotation(node) *
01391                             Mat3d::xrotation(degToRad(i)) *
01392                             Mat3d::yrotation(w));
01393 
01394         // Rotate to the Earth's equatorial plane
01395         double N = degToRad(refplane_RA);
01396         double J = degToRad(90 - refplane_Dec);
01397         Mat3d RLaplacian = (Mat3d::yrotation( N) *
01398                             Mat3d::xrotation( J) *
01399                             Mat3d::yrotation(-N));
01400 
01401         // Rotate to the Martian equatorial plane
01402         N = degToRad(marspole_RA);
01403         J = degToRad(90 - marspole_Dec);
01404         Mat3d RMars_eq   = (Mat3d::yrotation( N) *
01405                             Mat3d::xrotation(-J) *
01406                             Mat3d::yrotation(-N));
01407 
01408         return RMars_eq * (RLaplacian * (Rorbit * p));
01409     }
01410 
01411 #if 0
01412     // More accurate orbit calculation for Mars from _Explanatory
01413     // Supplement to the Astronomical Almanac_  There's still a bug in
01414     // this routine however.
01415     Point3d computePosition(double jd) const
01416     {
01417         double d = jd - 2441266.5;  // days since 11 Nov 1971
01418         double D = d / 365.25;      // years
01419 
01420         double a = 1.56828e-4 * KM_PER_AU;
01421         double n = 285.161888;
01422         double e = 0.0004;
01423         double gamma = degToRad(1.79);
01424         double theta = degToRad(240.38 - 0.01801 * d);
01425 
01426         double h = degToRad(196.55 - 0.01801 * d);
01427         double L = degToRad(28.96 + n * d - 0.27 * sin(h));
01428         double P = degToRad(111.7 + 0.01798 * d);
01429 
01430         // N and J give the orientation of the Laplacian plane with respect
01431         // to the Earth's equator and equinox.
01432         //    N - longitude of ascending node
01433         //    J - inclination
01434         double N = degToRad(46.37 - 0.0014 * D);
01435         double J = degToRad(36.62 + 0.0008 * D);
01436 
01437         // Compute the mean anomaly
01438         double M = L - P;
01439         
01440         // Solve Kepler's equation--for a low eccentricity orbit, just a few
01441         // iterations is enough.
01442         double E = M;
01443         for (int i = 0; i < 3; i++)
01444             E = M + e * sin(E);
01445 
01446         // Compute the position in the orbital plane (y = 0)
01447         double x = a * (cos(E) - e);
01448         double z = a * sqrt(1 - square(e)) * -sin(E);
01449 
01450         // Orientation of the orbital plane with respect to the Laplacian plane
01451         Mat3d Rorbit     = (Mat3d::yrotation(theta) *
01452                             Mat3d::xrotation(gamma) *
01453                             Mat3d::yrotation(P - theta));
01454 
01455         Mat3d RLaplacian = (Mat3d::yrotation( N) *
01456                             Mat3d::xrotation(-J) *
01457                             Mat3d::yrotation(-N));
01458 
01459         double marspole_RA  = 317.681;
01460         double marspole_Dec =  52.886;
01461         double Nm = degToRad(marspole_RA + 90);
01462         double Jm = degToRad(90 - marspole_Dec);
01463         Mat3d RMars_eq   = (Mat3d::yrotation( Nm) *
01464                             Mat3d::xrotation( Jm) *
01465                             Mat3d::yrotation(-Nm));
01466 
01467         // Celestia wants the position of a satellite with respect to the
01468         // equatorial plane of the planet it orbits.
01469 
01470         // to Laplacian...
01471         Point3d p = Rorbit * Point3d(x, 0.0, z);
01472 
01473         // to Earth equatorial...
01474         p = RLaplacian * p;
01475 
01476         // to Mars equatorial...
01477         return RMars_eq * p;
01478     }
01479 #endif
01480 
01481     double getPeriod() const
01482     {
01483         return 1.262441;
01484     }
01485 
01486     double getBoundingRadius() const
01487     {
01488         return 23462 * BoundingRadiusSlack;
01489     }
01490 };
01491 
01492 
01493 // static const double JupAscendingNode = degToRad(20.453422);
01494 static const double JupAscendingNode = degToRad(22.203);
01495 
01496 class IoOrbit : public CachingOrbit
01497 {
01498     Point3d computePosition(double jd) const
01499     {
01500     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
01501     double t;
01502     double l1, l2, l3, l4;
01503     double p1, p2, p3, p4;
01504     double w1, w2, w3, w4;
01505     double gamma, phi, psi, G, Gp;
01506     double sigma, L, B, R;
01507     double T, P;
01508 
01509     // Epoch for Galilean satellites is 1976.0 Aug 10
01510     t = jd - 2443000.5;
01511 
01512     ComputeGalileanElements(t,
01513                             l1, l2, l3, l4,
01514                             p1, p2, p3, p4,
01515                             w1, w2, w3, w4,
01516                             gamma, phi, psi, G, Gp);
01517 
01518     // Calculate periodic terms for longitude
01519     sigma = 0.47259*sin(2*(l1 - l2)) - 0.03478*sin(p3 - p4)
01520           + 0.01081*sin(l2 - 2*l3 + p3) + 7.38e-3*sin(phi)
01521           + 7.13e-3*sin(l2 - 2*l3 + p2) - 6.74e-3*sin(p1 + p3 - 2*LPEJ - 2*G)
01522           + 6.66e-3*sin(l2 - 2*l3 + p4) + 4.45e-3*sin(l1 - p3)
01523           - 3.54e-3*sin(l1 - l2) - 3.17e-3*sin(2*(psi - LPEJ))
01524           + 2.65e-3*sin(l1 - p4) - 1.86e-3*sin(G)
01525           + 1.62e-3*sin(p2 - p3) + 1.58e-3*sin(4*(l1 - l2))
01526           - 1.55e-3*sin(l1 - l3) - 1.38e-3*sin(psi + w3 - 2*LPEJ - 2*G)
01527           - 1.15e-3*sin(2*(l1 - 2*l2 + w2)) + 8.9e-4*sin(p2 - p4)
01528           + 8.5e-4*sin(l1 + p3 - 2*LPEJ - 2*G) + 8.3e-4*sin(w2 - w3)
01529           + 5.3e-4*sin(psi - w2);
01530     sigma = pfmod(sigma, 360.0);
01531     sigma = degToRad(sigma);
01532     L = l1 + sigma;
01533 
01534     // Calculate periodic terms for the tangent of the latitude
01535     B = 6.393e-4*sin(L - w1) + 1.825e-4*sin(L - w2)
01536       + 3.29e-5*sin(L - w3) - 3.11e-5*sin(L - psi)
01537       + 9.3e-6*sin(L - w4) + 7.5e-6*sin(3*L - 4*l2 - 1.9927*sigma + w2)
01538       + 4.6e-6*sin(L + psi - 2*LPEJ - 2*G);
01539     B = atan(B);
01540 
01541     // Calculate the periodic terms for distance
01542     R = -4.1339e-3*cos(2*(l1 - l2)) - 3.87e-5*cos(l1 - p3)
01543       - 2.14e-5*cos(l1 - p4) + 1.7e-5*cos(l1 - l2)
01544       - 1.31e-5*cos(4*(l1 - l2)) + 1.06e-5*cos(l1 - l3)
01545       - 6.6e-6*cos(l1 + p3 - 2*LPEJ - 2*G);
01546     R = 5.90569 * JupiterRadius * (1 + R);
01547 
01548     T = (jd - 2433282.423) / 36525.0;
01549     P = 1.3966626*T + 3.088e-4*T*T;
01550     L += degToRad(P);
01551 
01552     L += JupAscendingNode;
01553 
01554     // Corrections for internal coordinate system
01555     B -= (PI/2);
01556     L += PI;
01557 
01558     return Point3d(cos(L) * sin(B) * R,
01559                    cos(B) * R,
01560                    -sin(L) * sin(B) * R);
01561     };
01562 
01563     double getPeriod() const
01564     {
01565         return 1.769138;
01566     };
01567 
01568     double getBoundingRadius() const
01569     {
01570         return 423329 * BoundingRadiusSlack;
01571     };
01572 };
01573 
01574 class EuropaOrbit : public CachingOrbit
01575 {
01576     Point3d computePosition(double jd) const
01577     {
01578     // Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
01579     double t;
01580     double l1, l2, l3, l4;
01581     double p1, p2, p3, p4;
01582     double w1, w2, w3, w4;
01583     double gamma, phi, psi, G, Gp;
01584     double sigma, L, B, R;
01585     double T, P;
01586 
01587     // Epoch for Galilean satellites is 1976 Aug 10
01588     t = jd - 2443000.5;
01589 
01590     ComputeGalileanElements(t,
01591                             l1, l2, l3, l4,
01592                             p1, p2, p3, p4,
01593                             w1, w2, w3, w4,
01594                             gamma, phi, psi, G, Gp);
01595 
01596     // Calculate periodic terms for longitude
01597     sigma = 1.06476*sin(2*(l2 - l3)) + 0.04256*sin(l1 - 2*l2 + p3)
01598           + 0.03581*sin(l2 - p3) + 0.02395*sin(l1 - 2*l2 + p4)
01599           + 0.01984*sin(l2 - p4) - 0.01778*sin(phi)
01600           + 0.01654*sin(l2 - p2) + 0.01334*sin(l2 - 2*l3 + p2)
01601           + 0.01294*sin(p3 - p4) - 0.01142*sin(l2 - l3)
01602           - 0.01057*sin(G) - 7.75e-3*sin(2*(psi - LPEJ))
01603           + 5.24e-3*sin(2*(l1 - l2)) - 4.6e-3*sin(l1 - l3)
01604           + 3.16e-3*sin(psi - 2*G + w3 - 2*LPEJ) - 2.03e-3*sin(p1 + p3 - 2*LPEJ - 2*G)
01605           + 1.46e-3*sin(psi - w3) - 1.45e-3*sin(2*G)
01606           + 1.25e-3*sin(psi - w4) - 1.15e-3*sin(l1 - 2*l3 + p3)
01607           - 9.4e-4*sin(2*(l2 - w2)) + 8.6e-4*sin(2*(l1 - 2*l2 + w2))
01608           - 8.6e-4*sin(5*Gp - 2*G + 0.9115) - 7.8e-4*sin(l2 - l4)
01609           - 6.4e-4*sin(3*l3 - 7*l4 + 4*p4) + 6.4e-4*sin(p1 - p4)
01610           - 6.3e-4*sin(l1 - 2*l3 + p4) + 5.8e-4*sin(w3 - w4)
01611           + 5.6e-4*sin(2*(psi - LPEJ - G)) + 5.6e-4*sin(2*(l2 - l4))
01612           + 5.5e-4*sin(2*(l1 - l3)) + 5.2e-4*sin(3*l3 - 7*l4 + p3 +3*p4)
01613           - 4.3e-4*sin(l1 - p3) + 4.1e-4*sin(5*(l2 - l3))
01614           + 4.1e-4*sin(p4 - LPEJ) + 3.2e-4*sin(w2 - w3)
01615           + 3.2e-4*sin(2*(l3 - G - LPEJ));
01616     sigma = pfmod(sigma, 360.0);
01617     sigma = degToRad(sigma);
01618     L = l2 + sigma;
01619 
01620     // Calculate periodic terms for the tangent of the latitude
01621     B = 8.1004e-3*sin(L - w2) + 4.512e-4*sin(L - w3)
01622       - 3.284e-4*sin(L - psi) + 1.160e-4*sin(L - w4)
01623       + 2.72e-5*sin(l1 - 2*l3 + 1.0146*sigma + w2) - 1.44e-5*sin(L - w1)
01624       + 1.43e-5*sin(L + psi - 2*LPEJ - 2*G) + 3.5e-6*sin(L - psi + G)
01625       - 2.8e-6*sin(l1 - 2*l3 + 1.0146*sigma + w3);
01626     B = atan(B);
01627 
01628     // Calculate the periodic terms for distance
01629     R = 9.3848e-3*cos(l1 - l2) - 3.116e-4*cos(l2 - p3)
01630       - 1.744e-4*cos(l2 - p4) - 1.442e-4*cos(l2 - p2)
01631       + 5.53e-5*cos(l2 - l3) + 5.23e-5*cos(l1 - l3)
01632       - 2.9e-5*cos(2*(l1 - l2)) + 1.64e-5*cos(2*(l2 - w2))
01633       + 1.07e-5*cos(l1 - 2*l3 + p3) - 1.02e-5*cos(l2 - p1)
01634       - 9.1e-6*cos(2*(l1 - l3));
01635     R = 9.39657 * JupiterRadius * (1 + R);
01636 
01637     T = (jd - 2433282.423) / 36525.0;
01638     P = 1.3966626*T + 3.088e-4*T*T;
01639     L += degToRad(P);
01640 
01641     L += JupAscendingNode;
01642 
01643     // Corrections for internal coordinate system
01644     B -= (PI/2);
01645     L += PI;
01646 
01647     return Point3d(cos(L) * sin(B) * R,
01648                    cos(B) * R,
01649                    -sin(L) * sin(B) * R);
01650     };
01651 
01652     double getPeriod() const
01653     {
01654         return 3.5511810791;
01655     };
01656 
01657     double getBoundingRadius() const
01658     {
01659         return 678000 * BoundingRadiusSlack;
01660     };
01661 };
01662 
01663 class GanymedeOrbit : public CachingOrbit
01664 {
01665     Point3d computePosition(double jd) const
01666     {
01667     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
01668     double t;
01669     double l1, l2, l3, l4;
01670     double p1, p2, p3, p4;
01671     double w1, w2, w3, w4;
01672     double gamma, phi, psi, G, Gp;
01673     double sigma, L, B, R;
01674     double T, P;
01675 
01676     //Epoch for Galilean satellites is 1976 Aug 10
01677     t = jd - 2443000.5;
01678 
01679     ComputeGalileanElements(t,
01680                             l1, l2, l3, l4,
01681                             p1, p2, p3, p4,
01682                             w1, w2, w3, w4,
01683                             gamma, phi, psi, G, Gp);
01684 
01685     //Calculate periodic terms for longitude
01686     sigma = 0.1649*sin(l3 - p3) + 0.09081*sin(l3 - p4)
01687           - 0.06907*sin(l2 - l3) + 0.03784*sin(p3 - p4)
01688           + 0.01846*sin(2*(l3 - l4)) - 0.01340*sin(G)
01689           - 0.01014*sin(2*(psi - LPEJ)) + 7.04e-3*sin(l2 - 2*l3 + p3)
01690           - 6.2e-3*sin(l2 - 2*l3 + p2) - 5.41e-3*sin(l3 - l4)
01691           + 3.81e-3*sin(l2 - 2*l3 + p4) + 2.35e-3*sin(psi - w3)
01692           + 1.98e-3*sin(psi - w4) + 1.76e-3*sin(phi)
01693           + 1.3e-3*sin(3*(l3 - l4)) + 1.25e-3*sin(l1 - l3)
01694           - 1.19e-3*sin(5*Gp - 2*G + 0.9115) + 1.09e-3*sin(l1 - l2)
01695           - 1.0e-3*sin(3*l3 - 7*l4 + 4*p4) + 9.1e-4*sin(w3 - w4)
01696           + 8.0e-4*sin(3*l3 - 7*l4 + p3 + 3*p4) - 7.5e-4*sin(2*l2 - 3*l3 + p3)
01697           + 7.2e-4*sin(p1 + p3 - 2*LPEJ - 2*G) + 6.9e-4*sin(p4 - LPEJ)
01698           - 5.8e-4*sin(2*l3 - 3*l4 + p4) - 5.7e-4*sin(l3 - 2*l4 + p4)
01699           + 5.6e-4*sin(l3 + p3 - 2*LPEJ - 2*G) - 5.2e-4*sin(l2 - 2*l3 + p1)
01700           - 5.0e-4*sin(p2 - p3) + 4.8e-4*sin(l3 - 2*l4 + p3)
01701           - 4.5e-4*sin(2*l2 - 3*l3 + p4) - 4.1e-4*sin(p2 - p4)
01702           - 3.8e-4*sin(2*G) - 3.7e-4*sin(p3 - p4 + w3 - w4)
01703           - 3.2e-4*sin(3*l3 - 7*l4 + 2*p3 + 2*p4) + 3.0e-4*sin(4*(l3 - l4))
01704           + 2.9e-4*sin(l3 + p4 - 2*LPEJ - 2*G) - 2.8e-4*sin(w3 + psi - 2*LPEJ - 2*G)
01705           + 2.6e-4*sin(l3 - LPEJ - G) + 2.4e-4*sin(l2 - 3*l3 + 2*l4)
01706           + 2.1e-4*sin(2*(l3 - LPEJ - G)) - 2.1e-4*sin(l3 - p2)
01707           + 1.7e-4*sin(l3 - p3);
01708     sigma = pfmod(sigma, 360.0);
01709     sigma = degToRad(sigma);
01710     L = l3 + sigma;
01711 
01712     //Calculate periodic terms for the tangent of the latitude
01713     B = 3.2402e-3*sin(L - w3) - 1.6911e-3*sin(L - psi)
01714       + 6.847e-4*sin(L - w4) - 2.797e-4*sin(L - w2)
01715       + 3.21e-5*sin(L + psi - 2*LPEJ - 2*G) + 5.1e-6*sin(L - psi + G)
01716       - 4.5e-6*sin(L - psi - G) - 4.5e-6*sin(L + psi - 2*LPEJ)
01717       + 3.7e-6*sin(L + psi - 2*LPEJ - 3*G) + 3.0e-6*sin(2*l2 - 3*L + 4.03*sigma + w2)
01718       - 2.1e-6*sin(2*l2 - 3*L + 4.03*sigma + w3);
01719     B = atan(B);
01720 
01721     //Calculate the periodic terms for distance
01722     R = -1.4388e-3*cos(l3 - p3) - 7.919e-4*cos(l3 - p4)
01723       + 6.342e-4*cos(l2 - l3) - 1.761e-4*cos(2*(l3 - l4))
01724       + 2.94e-5*cos(l3 - l4) - 1.56e-5*cos(3*(l3 - l4))
01725       + 1.56e-5*cos(l1 - l3) - 1.53e-5*cos(l1 - l2)
01726       + 7.0e-6*cos(2*l2 - 3*l3 + p3) - 5.1e-6*cos(l3 + p3 - 2*LPEJ - 2*G);
01727     R = 14.98832 * JupiterRadius * (1 + R);
01728 
01729     T = (jd - 2433282.423) / 36525.0;
01730     P = 1.3966626*T + 3.088e-4*T*T;
01731     L += degToRad(P);
01732 
01733     L += JupAscendingNode;
01734 
01735     //Corrections for internal coordinate system
01736     B -= (PI/2);
01737     L += PI;
01738 
01739     return Point3d(cos(L) * sin(B) * R,
01740                    cos(B) * R,
01741                    -sin(L) * sin(B) * R);
01742     };
01743 
01744     double getPeriod() const
01745     {
01746         return 7.154553;
01747     };
01748 
01749     double getBoundingRadius() const
01750     {
01751         return 1070000 * BoundingRadiusSlack;
01752     };
01753 };
01754 
01755 class CallistoOrbit : public CachingOrbit
01756 {
01757     Point3d computePosition(double jd) const
01758     {
01759     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
01760     double t;
01761     double l1, l2, l3, l4;
01762     double p1, p2, p3, p4;
01763     double w1, w2, w3, w4;
01764     double gamma, phi, psi, G, Gp;
01765     double sigma, L, B, R;
01766     double T, P;
01767 
01768     //Epoch for Galilean satellites is 1976 Aug 10
01769     t = jd - 2443000.5;
01770 
01771     ComputeGalileanElements(t,
01772                             l1, l2, l3, l4,
01773                             p1, p2, p3, p4,
01774                             w1, w2, w3, w4,
01775                             gamma, phi, psi, G, Gp);
01776 
01777     //Calculate periodic terms for longitude
01778     sigma =
01779         0.84287*sin(l4 - p4)
01780         + 0.03431*sin(p4 - p3)
01781         - 0.03305*sin(2*(psi - LPEJ))
01782         - 0.03211*sin(G)
01783         - 0.01862*sin(l4 - p3)
01784         + 0.01186*sin(psi - w4)
01785         + 6.23e-3*sin(l4 + p4 - 2*G - 2*LPEJ)
01786         + 3.87e-3*sin(2*(l4 - p4))
01787         - 2.84e-3*sin(5*Gp - 2*G + 0.9115)
01788         - 2.34e-3*sin(2*(psi - p4))
01789         - 2.23e-3*sin(l3 - l4)
01790         - 2.08e-3*sin(l4 - LPEJ)
01791         + 1.78e-3*sin(psi + w4 - 2*p4)
01792         + 1.34e-3*sin(p4 - LPEJ)
01793         + 1.25e-3*sin(2*(l4 - G - LPEJ))
01794         - 1.17e-3*sin(2*G)
01795         - 1.12e-3*sin(2*(l3 - l4))
01796         + 1.07e-3*sin(3*l3 - 7*l4 + 4*p4)
01797         + 1.02e-3*sin(l4 - G - LPEJ)
01798         + 9.6e-4*sin(2*l4 - psi - w4)
01799         + 8.7e-4*sin(2*(psi - w4))
01800         - 8.5e-4*sin(3*l3 - 7*l4 + p3 + 3*p4)
01801         + 8.5e-4*sin(l3 - 2*l4 + p4)
01802         - 8.1e-4*sin(2*(l4 - psi))
01803         + 7.1e-4*sin(l4 + p4 - 2*LPEJ - 3*G)
01804         + 6.1e-4*sin(l1 - l4)
01805         - 5.6e-4*sin(psi - w3)
01806         - 5.4e-4*sin(l3 - 2*l4 + p3)
01807         + 5.1e-4*sin(l2 - l4)
01808         + 4.2e-4*sin(2*(psi - G - LPEJ))
01809         + 3.9e-4*sin(2*(p4 - w4))
01810         + 3.6e-4*sin(psi + LPEJ - p4 - w4)
01811         + 3.5e-4*sin(2*Gp - G + 3.2877)
01812         - 3.5e-4*sin(l4 - p4 + 2*LPEJ - 2*psi)
01813         - 3.2e-4*sin(l4 + p4 - 2*LPEJ - G)
01814         + 3.0e-4*sin(2*Gp - 2*G + 2.6032)
01815         + 2.9e-4*sin(3*l3 - 7*l4 + 2*p3 + 2*p4)
01816         + 2.8e-4*sin(l4 - p4 + 2*psi - 2*LPEJ)
01817         - 2.8e-4*sin(2*(l4 - w4))
01818         - 2.7e-4*sin(p3 - p4 + w3 - w4)
01819         - 2.6e-4*sin(5*Gp - 3*G + 3.2877)
01820         + 2.5e-4*sin(w4 - w3)
01821         - 2.5e-4*sin(l2 - 3*l3 + 2*l4)
01822         - 2.3e-4*sin(3*(l3 - l4))
01823         + 2.1e-4*sin(2*l4 - 2*LPEJ - 3*G)
01824         - 2.1e-4*sin(2*l3 - 3*l4 + p4)
01825         + 1.9e-4*sin(l4 - p4 - G)
01826         - 1.9e-4*sin(2*l4 - p3 - p4)
01827         - 1.8e-4*sin(l4 - p4 + G)
01828         - 1.6e-4*sin(l4 + p3 - 2*LPEJ - 2*G);
01829     sigma = pfmod(sigma, 360.0);
01830     sigma = degToRad(sigma);
01831     L = l4 + sigma;
01832 
01833     //Calculate periodic terms for the tangent of the latitude
01834     B =
01835         - 7.6579e-3 * sin(L - psi)
01836         + 4.4134e-3 * sin(L - w4)
01837         - 5.112e-4  * sin(L - w3)
01838         + 7.73e-5   * sin(L + psi - 2*LPEJ - 2*G)
01839         + 1.04e-5   * sin(L - psi + G)
01840         - 1.02e-5   * sin(L - psi - G)
01841         + 8.8e-6    * sin(L + psi - 2*LPEJ - 3*G)
01842         - 3.8e-6    * sin(L + psi - 2*LPEJ - G);
01843     B = atan(B);
01844 
01845     //Calculate the periodic terms for distance
01846     R =
01847         - 7.3546e-3 * cos(l4 - p4)
01848         + 1.621e-4  * cos(l4 - p3)
01849         + 9.74e-5   * cos(l3 - l4)
01850         - 5.43e-5   * cos(l4 + p4 - 2*LPEJ - 2*G)
01851         - 2.71e-5   * cos(2*(l4 - p4))
01852         + 1.82e-5   * cos(l4 - LPEJ)
01853         + 1.77e-5   * cos(2*(l3 - l4))
01854         - 1.67e-5   * cos(2*l4 - psi - w4)
01855         + 1.67e-5   * cos(psi - w4)
01856         - 1.55e-5   * cos(2*(l4 - LPEJ - G))
01857         + 1.42e-5   * cos(2*(l4 - psi))
01858         + 1.05e-5   * cos(l1 - l4)
01859         + 9.2e-6    * cos(l2 - l4)
01860         - 8.9e-6    * cos(l4 - LPEJ -G)
01861         - 6.2e-6    * cos(l4 + p4 - 2*LPEJ - 3*G)
01862         + 4.8e-6    * cos(2*(l4 - w4));
01863     
01864     R = 26.36273 * JupiterRadius * (1 + R);
01865 
01866     T = (jd - 2433282.423) / 36525.0;
01867     P = 1.3966626*T + 3.088e-4*T*T;
01868     L += degToRad(P);
01869 
01870     L += JupAscendingNode;
01871 
01872     //Corrections for internal coordinate system
01873     B -= (PI/2);
01874     L += PI;
01875 
01876     return Point3d(cos(L) * sin(B) * R,
01877                    cos(B) * R,
01878                    -sin(L) * sin(B) * R);
01879     };
01880 
01881     double getPeriod() const
01882     {
01883         return 16.689018;
01884     };
01885 
01886     double getBoundingRadius() const
01887     {
01888         return 1890000 * BoundingRadiusSlack;
01889     };
01890 };
01891 
01892 
01893 static const double SatAscendingNode = 168.8112;
01894 static const double SatTilt = 28.0817;
01895 // static const double SatAscendingNode = 169.530;
01896 // static const double SatTilt = 28.049;
01897 
01898 // Calculations for the orbits of Mimas, Enceladus, Tethys, Dione, Rhea,
01899 // Titan, Hyperion, and Iapetus are from Jean Meeus's Astronomical Algorithms,
01900 // and were originally derived by Gerard Dourneau.
01901 
01902 void ComputeSaturnianElements(double t,
01903                               double& t1, double& t2, double& t3,
01904                               double& t4, double& t5, double& t6,
01905                               double& t7, double& t8, double& t9,
01906                               double& t10, double& t11,
01907                               double& W0, double& W1, double& W2,
01908                               double& W3, double& W4, double& W5,
01909                               double& W6, double& W7, double& W8)
01910 {
01911     t1 = t - 2411093.0;
01912     t2 = t1 / 365.25;
01913     t3 = (t - 2433282.423) / 365.25 + 1950.0;
01914     t4 = t - 2411368.0;
01915     t5 = t4 / 365.25;
01916     t6 = t - 2415020.0;
01917     t7 = t6 / 36525;
01918     t8 = t6 / 365.25;
01919     t9 = (t - 2442000.5) / 365.25;
01920     t10 = t - 2409786.0;
01921     t11 = t10 / 36525;
01922 
01923     W0 = 5.095 * (t3 - 1866.39);
01924     W1 = 74.4 + 32.39 * t2;
01925     W2 = 134.3 + 92.62 * t2;
01926     W3 = 42.0 - 0.5118 * t5;
01927     W4 = 276.59 + 0.5118 * t5;
01928     W5 = 267.2635 + 1222.1136 * t7;
01929     W6 = 175.4762 + 1221.5515 * t7;
01930     W7 = 2.4891 + 0.002435 * t7;
01931     W8 = 113.35 - 0.2597 * t7;
01932 }
01933 
01934 
01935 static Point3d SaturnMoonPosition(double lam, double gam, double Om, double r)
01936 {
01937     double u = lam - Om;
01938     double w = Om - SatAscendingNode;
01939 
01940     u = degToRad(u);
01941     w = degToRad(w);
01942     gam = -degToRad(gam);
01943     r = r * SaturnRadius;
01944 
01945     // Corrections for Celestia's coordinate system
01946     u = -u;
01947     w = -w;
01948 
01949     double x = r * (cos(u) * cos(w) - sin(u) * sin(w) * cos(gam));
01950     double y = r * sin(u) * sin(gam);
01951     double z = r * (sin(u) * cos(w) * cos(gam) + cos(u) * sin(w));
01952 
01953     return Point3d(x, y, z);
01954 }
01955 
01956 
01957 static void OuterSaturnMoonParams(double a, double e, double i,
01958                                   double Om_, double M, double lam_,
01959                                   double& lam, double& gam,
01960                                   double& r, double& w)
01961 {
01962     double s1 = sinD(SatTilt);
01963     double c1 = cosD(SatTilt);
01964     double e_2 = e * e;
01965     double e_3 = e_2 * e;
01966     double e_4 = e_3 * e;
01967     double e_5 = e_4 * e;
01968     double C = (2 * e - 0.25 * e_3 + 0.0520833333 * e_5) * sinD(M) +
01969         (1.25 * e_2 - 0.458333333 * e_4) * sinD(2 * M) +
01970         (1.083333333 * e_3 - 0.671875 * e_5) * sinD(3 * M) +
01971         1.072917 * e_4 * sinD(4 * M) + 1.142708 * e_5 * sinD(5 * M);
01972     double g = Om_ - SatAscendingNode;
01973     double a1 = sinD(i) * sinD(g);
01974     double a2 = c1 * sinD(i) * cosD(g) - s1 * cosD(i);
01975     double u = radToDeg(atan2(a1, a2));
01976     double h = c1 * sinD(i) - s1 * cosD(i) * cosD(g);
01977     double psi = radToDeg(atan2(s1 * sinD(g), h));
01978 
01979     C = radToDeg(C);
01980     lam = lam_ + C + u - g - psi;
01981     gam = radToDeg(asin(sqrt(square(a1) + square(a2))));
01982     r = a * (1 - e * e) / (1 + e * cosD(M + C));
01983     w = SatAscendingNode + u;
01984 }
01985 
01986 
01987 class MimasOrbit : public CachingOrbit
01988 {
01989     Point3d computePosition(double jd) const
01990     {
01991         // Computation will yield latitude(L), longitude(B) and distance(R)
01992         // relative to Saturn.
01993         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
01994         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
01995 
01996         ComputeSaturnianElements(jd,
01997                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
01998                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
01999 
02000         double L = 127.64 + 381.994497 * t1 - 43.57 * sinD(W0) -
02001             0.720 * sinD( 3 * W0) - 0.02144 * sinD(5 * W0);
02002         double p = 106.1 + 365.549 * t2;
02003         double M = L - p;
02004         double C = 2.18287 * sinD(M) + 0.025988 * sinD(2 * M) +
02005             0.00043 * sinD(3 * M);
02006         double lam = L + C;
02007         double r = 3.06879 / (1 + 0.01905 * cosD(M + C));
02008         double gam = 1.563;
02009         double Om = 54.5 - 365.072 * t2;
02010 
02011         return SaturnMoonPosition(lam, gam, Om, r);
02012     };
02013 
02014     double getPeriod() const
02015     {
02016         return 0.9424218;
02017     };
02018 
02019     double getBoundingRadius() const
02020     {
02021         return 189000 * BoundingRadiusSlack;
02022     };
02023 };
02024 
02025 
02026 class EnceladusOrbit : public CachingOrbit
02027 {
02028     Point3d computePosition(double jd) const
02029     {
02030         // Computation will yield latitude(L), longitude(B) and distance(R)
02031         // relative to Saturn.
02032         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02033         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02034 
02035         ComputeSaturnianElements(jd,
02036                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02037                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02038 
02039         double L = 200.317 + 262.7319002 * t1 + 0.25667 * sinD(W1) +
02040             0.20883 * sinD(W2);
02041         double p = 309.107 + 123.44121 * t2;
02042         double M = L - p;
02043         double C = 0.55577 * sinD(M) + 0.00168 * sinD(2 * M);
02044         double lam = L + C;
02045         double r = 3.94118 / (1 + 0.00485 * cosD(M + C));
02046         double gam = 0.0262;
02047         double Om = 348 - 151.95 * t2;
02048 
02049         return SaturnMoonPosition(lam, gam, Om, r);
02050     };
02051 
02052     double getPeriod() const
02053     {
02054         return 1.370218;
02055     };
02056 
02057     double getBoundingRadius() const
02058     {
02059         return 239000 * BoundingRadiusSlack;
02060     };
02061 };
02062 
02063 
02064 class TethysOrbit : public CachingOrbit
02065 {
02066     Point3d computePosition(double jd) const
02067     {
02068         // Computation will yield latitude(L), longitude(B) and distance(R)
02069         // relative to Saturn.
02070         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02071         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02072 
02073         ComputeSaturnianElements(jd,
02074                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02075                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02076 
02077         double lam = 285.306 + 190.69791226 * t1 + 2.063 * sinD(W0) +
02078             0.03409 * sinD(3 * W0) + 0.001015 * sinD(5 * W0);
02079         double r = 4.880998;
02080         double gam = 1.0976;
02081         double Om = 111.33 - 72.2441 * t2;
02082 
02083         return SaturnMoonPosition(lam, gam, Om, r);
02084     };
02085 
02086     double getPeriod() const
02087     {
02088         return 1.887802;
02089     };
02090 
02091     double getBoundingRadius() const
02092     {
02093         return 295000 * BoundingRadiusSlack;
02094     };
02095 };
02096 
02097 
02098 class DioneOrbit : public CachingOrbit
02099 {
02100     Point3d computePosition(double jd) const
02101     {
02102         // Computation will yield latitude(L), longitude(B) and distance(R)
02103         // relative to Saturn.
02104         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02105         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02106 
02107         ComputeSaturnianElements(jd,
02108                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02109                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02110 
02111         double L = 254.712 + 131.53493193 * t1 - 0.0215 * sinD(W1) -
02112             0.01733 * sinD(W2);
02113         double p = 174.8 + 30.820 * t2;
02114         double M = L - p;
02115         double C = 0.24717 * sinD(M) + 0.00033 * sinD(2 * M);
02116         double lam = L + C;
02117         double r = 6.24871 / (1 + 0.002157 * cosD(M + C));
02118         double gam = 0.0139;
02119         double Om = 232 - 30.27 * t2;
02120         // cout << "Dione: " << pfmod(lam, 360.0) << ',' << gam << ',' << pfmod(Om, 360.0) << ',' << r << '\n';
02121 
02122         return SaturnMoonPosition(lam, gam, Om, r);
02123     };
02124 
02125     double getPeriod() const
02126     {
02127         return 2.736915;
02128     };
02129 
02130     double getBoundingRadius() const
02131     {
02132         return 378000 * BoundingRadiusSlack;
02133     };
02134 };
02135 
02136 
02137 class RheaOrbit : public CachingOrbit
02138 {
02139     Point3d computePosition(double jd) const
02140     {
02141         // Computation will yield latitude(L), longitude(B) and distance(R)
02142         // relative to Saturn.
02143         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02144         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02145 
02146         ComputeSaturnianElements(jd,
02147                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02148                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02149         double e1 = 0.05589 - 0.000346 * t7;
02150 
02151         double p_ = 342.7 + 10.057 * t2;
02152         double a1 = 0.000265 * sinD(p_) + 0.01 * sinD(W4);
02153         double a2 = 0.000265 * cosD(p_) + 0.01 * cosD(W4);
02154         double e = sqrt(square(a1) + square(a2));
02155         double p = radToDeg(atan2(a1, a2));
02156         double N = 345 - 10.057 * t2;
02157         double lam_ = 359.244 + 79.69004720 * t1 + 0.086754 * sinD(N);
02158         double i = 28.0362 + 0.346898 * cosD(N) + 0.01930 * cosD(W3);
02159         double Om = 168.8034 + 0.736936 * sinD(N) + 0.041 * sinD(W3);
02160         double a = 8.725924;
02161 
02162         double lam, gam, r, w;
02163         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
02164                               lam, gam, r, w);
02165         // cout << "Rhea (intermediate): " << e << ',' << pfmod(lam_, 360.0) << ',' << pfmod(i, 360.0) << ',' << pfmod(Om, 360.0) << '\n';
02166         // cout << "Rhea: " << pfmod(lam, 360.0) << ',' << gam << ',' << pfmod(w, 360.0) << ',' << r << '\n';
02167 
02168         return SaturnMoonPosition(lam, gam, w, r);
02169     };
02170 
02171     double getPeriod() const
02172     {
02173         return 4.517500;
02174     };
02175 
02176     double getBoundingRadius() const
02177     {
02178         return 528000 * BoundingRadiusSlack;
02179     };
02180 };
02181 
02182 
02183 class TitanOrbit : public CachingOrbit
02184 {
02185     Point3d computePosition(double jd) const
02186     {
02187         // Computation will yield latitude(L), longitude(B) and distance(R)
02188         // relative to Saturn.
02189         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02190         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02191 
02192         ComputeSaturnianElements(jd,
02193                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02194                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02195         double e1 = 0.05589 - 0.000346 * t7;
02196 
02197         double L = 261.1582 + 22.57697855 * t4 + 0.074025 * sinD(W3);
02198         double i_ = 27.45141 + 0.295999 * cosD(W3);
02199         double Om_ = 168.66925 + 0.628808 * sinD(W3);
02200         double a1 = sinD(W7) * sinD(Om_ - W8);
02201         double a2 = cosD(W7) * sinD(i_) - sinD(W7) * cosD(i_) * cosD(Om_ - W8);
02202         double g0 = 102.8623;
02203         double psi = radToDeg(atan2(a1, a2));
02204         double s = sqrt(square(a1) + square(a2));
02205         double g = W4 - Om_ - psi;
02206 
02207         // Three successive approximations will always be enough
02208         double om;
02209         for (int n = 0; n < 3; n++)
02210         {
02211             om = W4 + 0.37515 * (sinD(2 * g) - sinD(2 * g0));
02212             g = om - Om_ - psi;
02213         }
02214 
02215         double e_ = 0.029092 + 0.00019048 * (cosD(2 * g) - cosD(2 * g0));
02216         double q = 2 * (W5 - om);
02217         double b1 = sinD(i_) * sinD(Om_ - W8);
02218         double b2 = cosD(W7) * sinD(i_) * cosD(Om_ - W8) - sinD(W7) * cosD(i_);
02219         double theta = radToDeg(atan2(b1, b2)) + W8;
02220         double e = e_ + 0.002778797 * e_ * cosD(q);
02221         double p = om + 0.159215 * sinD(q);
02222         double u = 2 * W5 - 2 * theta + psi;
02223         double h = 0.9375 * square(e_) * sinD(q) + 0.1875 * square(s) * sinD(2 * (W5 - theta));
02224         double lam_ = L - 0.254744 * (e1 * sinD(W6) + 0.75 * square(e1) * sinD(2 * W6) + h);
02225         double i = i_ + 0.031843 * s * cosD(u);
02226         double Om = Om_ + (0.031843 * s * sinD(u)) / sinD(i_);
02227         double a = 20.216193;
02228 
02229         double lam, gam, r, w;
02230         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
02231                               lam, gam, r, w);
02232 
02233         return SaturnMoonPosition(lam, gam, w, r);
02234     };
02235 
02236     double getPeriod() const
02237     {
02238         return 15.94544758;
02239     };
02240 
02241     double getBoundingRadius() const
02242     {
02243         return 1260000 * BoundingRadiusSlack;
02244     };
02245 };
02246 
02247 
02248 class HyperionOrbit : public CachingOrbit
02249 {
02250     Point3d computePosition(double jd) const
02251     {
02252         // Computation will yield latitude(L), longitude(B) and distance(R)
02253         // relative to Saturn.
02254         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02255         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02256 
02257         ComputeSaturnianElements(jd,
02258                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02259                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02260         double eta = 92.39 + 0.5621071 * t6;
02261         double zeta = 148.19 - 19.18 * t8;
02262         double theta = 184.8 - 35.41 * t9;
02263         double theta_ = theta - 7.5;
02264         double as = 176 + 12.22 * t8;
02265         double bs = 8 + 24.44 * t8;
02266         double cs = bs + 5;
02267         double om = 68.898 - 18.67088 * t8;
02268         double phi = 2 * (om - W5);
02269         double chi = 94.9 - 2.292 * t8;
02270         double a = 24.50601 -
02271             0.08686 * cosD(eta) -
02272             0.00166 * cosD(zeta + eta) +
02273             0.00175 * cosD(zeta - eta);
02274         double e = 0.103458 -
02275             0.004099 * cosD(eta) -
02276             0.000167 * cosD(zeta + eta) +
02277             0.000235 * cosD(zeta - eta) +
02278             0.02303 * cosD(zeta) -
02279             0.00212 * cosD(2 * zeta) +
02280             0.000151 * cosD(3 * zeta) +
02281             0.00013 * sinD(phi);
02282         double p = om +
02283             0.15648 * sinD(chi) -
02284             0.4457 * sinD(eta) -
02285             0.2657 * sinD(zeta + eta) -
02286             0.3573 * sinD(zeta - eta) -
02287             12.872 * sinD(zeta) +
02288             1.668 * sinD(2 * zeta) -
02289             0.2419 * sinD(3 * zeta) - 
02290             0.07 * sinD(phi);
02291         double lam_ = 177.047 +
02292             16.91993829 * t6 +
02293             0.15648 * sinD(chi) +
02294             9.142 * sinD(eta) +
02295             0.007 * sinD(2 * eta) -
02296             0.014 * sinD(3 * eta) +
02297             0.2275 * sinD(zeta + eta) +
02298             0.2112 * sinD(zeta - eta) -
02299             0.26 * sinD(zeta) -
02300             0.0098 * sinD(2 * zeta) -
02301             0.013 * sinD(as) +
02302             0.017 * sinD(bs) -
02303             0.0303 * sinD(phi);
02304         double i = 27.3347 + 0.643486 * cosD(chi) + 0.315 * cosD(W3) +
02305             0.018 * cosD(theta) - 0.018 * cosD(cs);
02306         double Om = 168.6812 + 1.40136 * cosD(chi) + 0.68599 * sinD(W3) -
02307             0.0392 * sinD(cs) + 0.0366 * sinD(theta_);
02308 
02309         double lam, gam, r, w;
02310         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
02311                               lam, gam, r, w);
02312 
02313         return SaturnMoonPosition(lam, gam, w, r);
02314     };
02315 
02316     double getPeriod() const
02317     {
02318         return 21.276609;
02319     };
02320 
02321     double getBoundingRadius() const
02322     {
02323         return 1640000 * BoundingRadiusSlack;
02324     };
02325 };
02326 
02327 
02328 class IapetusOrbit : public CachingOrbit
02329 {
02330     Point3d computePosition(double jd) const
02331     {
02332         // Computation will yield latitude(L), longitude(B) and distance(R)
02333         // relative to Saturn.
02334         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
02335         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
02336 
02337         ComputeSaturnianElements(jd,
02338                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
02339                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
02340         double L = 261.1582 + 22.57697855 * t4;
02341         double om_ = 91.796 + 0.562 * t7;
02342         double psi = 4.367 - 0.195 * t7;
02343         double theta = 146.819 - 3.198 * t7;
02344         double phi = 60.470 + 1.521 * t7;
02345         double Phi = 205.055 - 2.091 * t7;
02346         double e_ = 0.028298 + 0.001156 * t11;
02347         double om0 = 352.91 + 11.71 * t11;
02348         double mu = 76.3852 + 4.53795125 * t10;
02349         double i_ = 18.4602 - 0.9518 * t11 - 0.072 * square(t11) +
02350             0.0054 * cube(t11);
02351         double Om_ = 143.198 - 3.919 * t11 + 0.116 * square(t11) +
02352             0.008 * cube(t11);
02353         double l = mu - om0;
02354         double g = om0 - Om_ - psi;
02355         double g1 = om0 - Om_ - phi;
02356         double ls = W5 - om_;
02357         double gs = om_ - theta;
02358         double lT = L - W4;
02359         double gT = W4 - Phi;
02360         double u1 = 2 * (l + g - (ls + gs));
02361         double u2 = l + g1 - (lT + gT);
02362         double u3 = l + 2 * (g - (ls + gs));
02363         double u4 = lT + gT - g1;
02364         double u5 = 2 * (ls + gs);
02365 
02366         double a = 58.935028 + 0.004638 * cosD(u1) + 0.058222 * cosD(u2);
02367         double e = e_ -
02368             0.0014097 * cosD(g1 - gT) +
02369             0.0003733 * cosD(u5 - 2 * g) +
02370             0.0001180 * cosD(u3) +
02371             0.0002408 * cosD(l) +
02372             0.0002849 * cosD(l + u2) +
02373             0.0006190 * cosD(u4);
02374         double W = 0.08077 * sinD(g1 - gT) +
02375             0.02139 * sinD(u5 - 2 * g) -
02376             0.00676 * sinD(u3) +
02377             0.01380 * sinD(l) +
02378             0.01632 * sinD(l + u2) +
02379             0.03547 * sinD(u4);
02380         double p = om0 + W / e_;
02381         double lam_ = mu - 
02382             0.04299 * sinD(u2) -
02383             0.00789 * sinD(u1) -
02384             0.06312 * sinD(ls) -
02385             0.00295 * sinD(2 * ls) -
02386             0.02231 * sinD(u5) +
02387             0.00650 * sinD(u5 + psi);
02388         double sum = l + g1 + lT + gT + phi;
02389         double i = i_ +
02390             0.04204 * cosD(u5 + psi) +
02391             0.00235 * cosD(sum) +
02392             0.00360 * cosD(u2 + phi);
02393         double w_ = 0.04204 * sinD(u5 + psi) +
02394             0.00235 * sinD(sum) +
02395             0.00358 * sinD(u2 + phi);
02396         double Om = Om_ + w_ / sinD(i_);
02397 
02398         double lam, gam, r, w;
02399         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
02400                               lam, gam, r, w);
02401 
02402         return SaturnMoonPosition(lam, gam, w, r);
02403     };
02404 
02405     double getPeriod() const
02406     {
02407         return 79.330183;
02408     };
02409 
02410     double getBoundingRadius() const
02411     {
02412         return 3660000 * BoundingRadiusSlack;
02413     };
02414 };
02415 
02416 
02417 class PhoebeOrbit : public CachingOrbit
02418 {
02419     Point3d computePosition(double jd) const
02420     {
02421         double t = jd - 2433282.5;
02422         double T = t / 365.25;
02423 
02424         double a = astro::AUtoKilometers(0.0865752f) / SaturnRadius;
02425         double lam_ = 277.872 - 0.6541068 * t - 90;
02426         double e = 0.16326;
02427         double pi = 280.165 - 0.19586 * T;
02428         double i = 173.949 - 0.020 * T;
02429         double Om = 245.998 - 0.41353 * T;
02430 
02431         double lam, gam, r, w;
02432         OuterSaturnMoonParams(a, e, i, Om, lam_ - pi, lam_,
02433                               lam, gam, r, w);
02434 
02435         return SaturnMoonPosition(lam, gam, w, r);
02436     };
02437 
02438     double getPeriod() const
02439     {
02440         return 548.2122790;
02441     };
02442 
02443     double getBoundingRadius() const
02444     {
02445         return 15100000 * BoundingRadiusSlack;
02446     };
02447 };
02448 
02449 
02450 class UranianSatelliteOrbit : public CachingOrbit
02451 {
02452 private:
02453     double a;
02454     double n;
02455     double L0;
02456     double L1;
02457     double *L_k, *L_theta, *L_phi;
02458     int LTerms;
02459     double *z_k, *z_theta, *z_phi;
02460     int zTerms;
02461     double *zeta_k, *zeta_theta, *zeta_phi;
02462     int zetaTerms;
02463 
02464 public:
02465     UranianSatelliteOrbit(double _a,
02466                           double _n,
02467                           double _L0, double _L1,
02468                           int _LTerms, int _zTerms, int _zetaTerms,
02469                           double* _L_k, double* _L_theta, double* _L_phi,
02470                           double* _z_k, double* _z_theta, double* _z_phi,
02471                           double* _zeta_k, double* _zeta_theta,
02472                           double* _zeta_phi) :
02473         a(_a), n(_n), L0(_L0), L1(_L1),
02474         LTerms(_LTerms), zTerms(_zTerms), zetaTerms(_zetaTerms),
02475         L_k(_L_k), L_theta(_L_theta), L_phi(_L_phi),
02476         z_k(_z_k), z_theta(_z_theta), z_phi(_z_phi),
02477         zeta_k(_zeta_k), zeta_theta(_zeta_theta), zeta_phi(_zeta_phi)
02478     {
02479     };
02480 
02481 public:
02482     double getPeriod() const
02483     {
02484         return 2 * PI / n;
02485     }
02486 
02487     double getBoundingRadius() const
02488     {
02489         // Not quite correct, but should work since e is pretty low
02490         // for most of the Uranian moons.
02491         return a * BoundingRadiusSlack;
02492     }
02493 
02494     Point3d computePosition(double jd) const
02495     {
02496         double t = jd - 2444239.5;
02497         int i;
02498 
02499         double L = L0 + L1 * t;
02500         for (i = 0; i < LTerms; i++)
02501             L += L_k[i] * sin(L_theta[i] * t + L_phi[i]);
02502 
02503         double a0 = 0.0;
02504         double a1 = 0.0;
02505         for (i = 0; i < zTerms; i++)
02506         {
02507             double w = z_theta[i] * t + z_phi[i];
02508             a0 += z_k[i] * cos(w);
02509             a1 += z_k[i] * sin(w);
02510         }
02511 
02512         double b0 = 0.0;
02513         double b1 = 0.0;
02514         for (i = 0; i < zetaTerms; i++)
02515         {
02516             double w = zeta_theta[i] * t + zeta_phi[i];
02517             b0 += zeta_k[i] * cos(w);
02518             b1 += zeta_k[i] * sin(w);
02519         }
02520 
02521         double e = sqrt(square(a0) + square(a1));
02522         double p = atan2(a1, a0);
02523         double gamma = 2.0 * asin(sqrt(square(b0) + square(b1)));
02524         double theta = atan2(b1, b0);
02525 
02526         L += degToRad(174.99);
02527 
02528         // Now that we have all the orbital elements, compute the position
02529         double M = L - p;
02530 
02531         // Iterate a few times to compute the eccentric anomaly from the
02532         // mean anomaly.
02533         double ecc = M;
02534         for (i = 0; i < 4; i++)
02535             ecc = M + e * sin(ecc);
02536 
02537         double x = a * (cos(ecc) - e);
02538         double z = a * sqrt(1 - square(e)) * -sin(ecc);
02539 
02540         Mat3d R = (Mat3d::yrotation(theta) *
02541                    Mat3d::xrotation(gamma) *
02542                    Mat3d::yrotation(p - theta));
02543         return R * Point3d(x, 0, z);
02544     }
02545 };
02546 
02547 
02548 static double uran_n[5] =
02549 { 4.44352267, 2.49254257, 1.51595490, 0.72166316, 0.46658054 };
02550 static double uran_a[5] =
02551 { 129800, 191200, 266000, 435800, 583600 };
02552 static double uran_L0[5] =
02553 { -0.23805158, 3.09804641, 2.28540169, 0.85635879, -0.91559180 };
02554 static double uran_L1[5] =
02555 { 4.44519055, 2.49295252, 1.51614811, 0.72171851, 0.46669212 };
02556 static double uran_L_k[5][3] = {
02557 {  0.02547217, -0.00308831, -3.181e-4 },
02558 { -1.86050e-3, 2.1999e-4, 0 },
02559 { 6.6057e-4, 0, 0 },
02560 { 0, 0, 0 },
02561 { 0, 0, 0 }
02562 };
02563 static double uran_L_theta[5][3] = {
02564 { -2.18167e-4, -4.36336e-4, -6.54502e-4 },
02565 { -2.18167e-4, -4.36336e-4, 0 },
02566 { -2.18167e-4, 0, 0 },
02567 { 0, 0, 0 },
02568 { 0, 0, 0 }
02569 };
02570 static double uran_L_phi[5][3] = {
02571 { 1.32, 2.64, 3.97 },
02572 { 1.32, 2.64, 0 },
02573 { 1.32, 0, 0 },
02574 { 0, 0, 0 },
02575 { 0, 0, 0 },   
02576 };
02577 static double uran_z_k[5][5] = {
02578 { 1.31238e-3, -1.2331e-4, -1.9410e-4, 0, 0 },
02579 { 1.18763e-3, 8.6159e-4, 0, 0, 0 },
02580 { -2.2795e-4, 3.90496e-3, 3.0917e-4, 2.2192e-4, 5.4923e-4 },
02581 { 9.3281e-4, 1.12089e-3, 7.9343e-4, 0, 0 },
02582 { -7.5868e-4, 1.39734e-3, -9.8726e-4, 0, 0 }
02583 };
02584 static double uran_z_theta[5][5] = {
02585 { 1.5273e-4, 0.08606, 0.709, 0, 0 },
02586 { 4.727824e-5, 2.179316e-5, 0, 0, 0 },
02587 { 4.727824e-5, 2.179132e-5, 1.580524e-5, 2.9363068e-6, -0.01157 },
02588 { 1.580524e-5, 2.9363068e-6, -6.9008e-3, 0, 0 },
02589 { 1.580524e-5, 2.9363068e-6, -6.9008e-3, 0, 0 }
02590 };
02591 static double uran_z_phi[5][5] = {
02592 { 0.61, 0.15, 6.04, 0, 0 },
02593 { 2.41, 2.07, 0, 0, 0 },
02594 { 2.41, 2.07, 0.74, 0.43, 5.71 },
02595 { 0.74, 0.43, 1.82, 0, 0 },
02596 { 0.74, 0.43, 1.82, 0, 0 }
02597 };
02598 static double uran_zeta_k[5][2] = {
02599 { 0.03787171, 0 },
02600 { 3.5825e-4, 2.9008e-4 },
02601 { 1.11336e-3, 3.5014e-4 },
02602 { 6.8572e-4, 3.7832e-4 },
02603 { -5.9633e-4, 4.5169e-4 }
02604 };
02605 static double uran_zeta_theta[5][2] = {
02606 { -1.54449e-4, 0 },
02607 { -4.782474e-5, -2.156628e-5 },
02608 { -2.156628e-5, -1.401373e-5 },
02609 { -1.401373e-5, -1.9713918e-6 },
02610 { -1.401373e-5, -1.9713918e-6 }
02611 };
02612 static double uran_zeta_phi[5][2] = {
02613 { 5.70, 0 },
02614 { 0.40, 0.59 },
02615 { 0.59, 1.75 },
02616 { 1.75, 4.21 },
02617 { 1.75, 4.21 },
02618 };
02619 
02620 static UranianSatelliteOrbit* CreateUranianSatelliteOrbit(int n)
02621 {
02622     assert(n >= 1 && n <= 5);
02623     n--;
02624 
02625     return new UranianSatelliteOrbit(uran_a[n], uran_n[n],
02626                                      uran_L0[n], uran_L1[n],
02627                                      3, 5, 2,
02628                                      uran_L_k[n], uran_L_theta[n],
02629                                      uran_L_phi[n], uran_z_k[n],
02630                                      uran_z_theta[n], uran_z_phi[n],
02631                                      uran_zeta_k[n], uran_zeta_theta[n],
02632                                      uran_zeta_phi[n]);
02633 };
02634 
02635 
02636 class TritonOrbit : public CachingOrbit
02637 {
02638     Point3d computePosition(double jd) const
02639     {
02640         // Pole of the fixed reference plane
02641         //double t = jd - 2433282.5;
02642         double epoch = 2433282.5;
02643         double t = jd - epoch;
02644         double T = (jd - 2451545.0) / 36525;
02645         double N = degToRad(359.28 + 54.308 * T);
02646         double refplane_RA  = degToRad(298.72 +
02647                                        2.58 * sin(N) - 0.04 * sin(2 * N));
02648         double refplane_Dec = degToRad(42.63 -
02649                                        1.90 * cos(N) - 0.01 * cos(2 * N));
02650         double nepPole_RA = degToRad(299.36);
02651         double nepPole_Dec = degToRad(43.46);
02652 
02653         double a = 354800;
02654         double e = 0.0000;
02655         double n = degToRad(61.2588532);
02656         double L0 = degToRad(200.913);
02657         double L  = L0 + n * t;
02658         double gamma = degToRad(158.996);
02659         double theta = degToRad(151.401 + 0.57806 * t / 365.25);
02660 
02661         double E = L - theta;
02662         Point3d p(a * cos(E), 0.0, a * -sin(E));
02663 
02664         // Orientation of the orbital plane with respect to the Laplacian plane
02665         Mat3d Rorbit     = (Mat3d::yrotation(theta) *
02666                             Mat3d::xrotation(gamma));
02667 
02668         // Rotate to the Earth's equatorial plane
02669         double Nr = degToRad(refplane_RA);
02670         double Jr = degToRad(90 - refplane_Dec);
02671         Mat3d Rrefplane = (Mat3d::yrotation( Nr) *
02672                            Mat3d::xrotation( Jr) *
02673                            Mat3d::yrotation(-Nr));
02674 
02675         // Rotate to the Neptunian equatorial plane
02676         double Nn = degToRad(nepPole_RA);
02677         double Jn = degToRad(90 - nepPole_Dec);
02678         Mat3d RNept_eq   = (Mat3d::yrotation( Nn) *
02679                             Mat3d::xrotation(-Jn) *
02680                             Mat3d::yrotation(-Nn));
02681 
02682         return RNept_eq * (Rrefplane * (Rorbit * p));
02683     }
02684     
02685     double getPeriod() const
02686     {
02687         return 5.877;
02688     }
02689 
02690     double getBoundingRadius() const
02691     {
02692         return 354800 * BoundingRadiusSlack;
02693     }
02694 };
02695 
02696 
02697 class JPLEphOrbit : public CachingOrbit
02698 {
02699 public:
02700     JPLEphOrbit(const JPLEphemeris& e, JPLEphemItem item,
02701                 double _period, double _boundingRadius) :
02702         ephem(e),
02703         body(item),
02704         period(_period),
02705         boundingRadius(_boundingRadius)
02706     {
02707     };
02708 
02709     double getPeriod() const
02710     {
02711         return period;
02712     }
02713 
02714     double getBoundingRadius() const
02715     {
02716         return boundingRadius;
02717     }
02718 
02719     Point3d computePosition(double jd) const
02720     {
02721         Point3d baryPos = ephem.getPlanetPosition(body, jd);
02722 
02723         // Convert from solar system barycentric position to heliocentric
02724         // position.
02725         Point3d sunPos = ephem.getPlanetPosition(JPLEph_Sun, jd);
02726         Point3d heliocentricPos = Point3d(0.0, 0.0, 0.0) + (baryPos - sunPos);
02727 
02728         // Rotate from the J2000 mean equator to the ecliptic
02729         // 23 deg 26' 21".448
02730         double J2000_equator = degToRad(23.4392911);
02731         heliocentricPos = heliocentricPos * Mat3d::xrotation(J2000_equator);
02732 
02733         return Point3d(heliocentricPos.x,
02734                        heliocentricPos.z,
02735                        -heliocentricPos.y);
02736     }
02737 
02738 private:
02739     const JPLEphemeris& ephem;
02740     JPLEphemItem body;
02741     double period;
02742     double boundingRadius;
02743 };
02744 
02745 
02746 static Orbit* CreateJPLEphOrbit(JPLEphemItem body,
02747                                 double period,
02748                                 double boundingRadius)
02749 {
02750     Orbit* o = new JPLEphOrbit(*jpleph, body, period, boundingRadius);
02751     return new MixedOrbit(o,
02752                           jpleph->getStartDate(),
02753                           jpleph->getEndDate(),
02754                           astro::SolarMass);
02755 }
02756 
02757 
02758 static double yearToJD(int year)
02759 {
02760     return (double) astro::Date(year, 1, 1);
02761 }
02762 
02763 
02764 Orbit* GetCustomOrbit(const string& name)
02765 {
02766     // Attempt to load JPL ephemeris data if we haven't tried already
02767     if (!jplephInitialized)
02768     {
02769         jplephInitialized = true;
02770         ifstream in("data/jpleph.dat", ios::in | ios::binary);
02771         if (in.good())
02772             jpleph = JPLEphemeris::load(in);
02773         if (jpleph != NULL)
02774         {
02775             clog << "Loaded DE" << jpleph->getDENumber() <<
02776                 " ephemeris. Valid from JD" <<
02777                 setprecision(8) <<
02778                 jpleph->getStartDate() << " to JD" <<
02779                 jpleph->getEndDate() << '\n';
02780         }
02781     }
02782 
02783     if (name == "mercury")
02784         return new MixedOrbit(new MercuryOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02785     if (name == "venus")
02786         return new MixedOrbit(new VenusOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02787     if (name == "earth")
02788         return new MixedOrbit(new EarthOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02789     if (name == "moon")
02790         return new MixedOrbit(new LunarOrbit(), yearToJD(-2000), yearToJD(4000), astro::EarthMass + astro::LunarMass);
02791     if (name == "mars")
02792         return new MixedOrbit(new MarsOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02793     if (name == "jupiter")
02794         return new MixedOrbit(new JupiterOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02795     if (name == "saturn")
02796         return new MixedOrbit(new SaturnOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02797     if (name == "uranus")
02798         return new MixedOrbit(new UranusOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02799     if (name == "neptune")
02800         return new MixedOrbit(new NeptuneOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02801     if (name == "pluto")
02802         return new MixedOrbit(new PlutoOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
02803     if (name == "mercury-jpl")
02804         return CreateJPLEphOrbit(JPLEph_Mercury,  0.2408  * 365.25, 6.0e7);
02805     if (name == "venus-jpl")
02806         return CreateJPLEphOrbit(JPLEph_Venus,    0.6152  * 365.25, 1.0e8);
02807     if (name == "earth-jpl")
02808         return CreateJPLEphOrbit(JPLEph_EarthMoonBary,      365.25, 1.6e8);
02809     if (name == "mars-jpl")
02810         return CreateJPLEphOrbit(JPLEph_Mars,      1.8809 * 365.25, 2.4e8);
02811     if (name == "jupiter-jpl")
02812         return CreateJPLEphOrbit(JPLEph_Jupiter,  11.86   * 365.25, 8.0e8);
02813     if (name == "saturn-jpl")
02814         return CreateJPLEphOrbit(JPLEph_Saturn,   29.4577 * 365.25, 1.5e9);
02815     if (name == "uranus-jpl")
02816         return CreateJPLEphOrbit(JPLEph_Uranus,   84.0139 * 365.25, 3.0e9);
02817     if (name == "neptune-jpl")
02818         return CreateJPLEphOrbit(JPLEph_Neptune, 164.793  * 365.25, 4.7e9);
02819     if (name == "pluto-jpl")
02820         return CreateJPLEphOrbit(JPLEph_Pluto,   248.54   * 365.25, 6.0e9);
02821     if (name == "moon-jpl")
02822         return CreateJPLEphOrbit(JPLEph_Moon,     27.321661,        5.0e5);
02823     if (name == "phobos")
02824         return new PhobosOrbit();
02825     if (name == "deimos")
02826         return new DeimosOrbit();
02827     if (name == "io")
02828         return new IoOrbit();
02829     if (name == "europa")
02830         return new EuropaOrbit();
02831     if (name == "ganymede")
02832         return new GanymedeOrbit();
02833     if (name == "callisto")
02834         return new CallistoOrbit();
02835     if (name == "mimas")
02836         return new MimasOrbit();
02837     if (name == "enceladus")
02838         return new EnceladusOrbit();
02839     if (name == "tethys")
02840         return new TethysOrbit();
02841     if (name == "dione")
02842         return new DioneOrbit();
02843     if (name == "rhea")
02844         return new RheaOrbit();
02845     if (name == "titan")
02846         return new TitanOrbit();
02847     if (name == "hyperion")
02848         return new HyperionOrbit();
02849     if (name == "iapetus")
02850         return new IapetusOrbit();
02851     if (name == "phoebe")
02852         return new PhoebeOrbit();
02853     if (name == "miranda")
02854         return CreateUranianSatelliteOrbit(1);
02855     if (name == "ariel")
02856         return CreateUranianSatelliteOrbit(2);
02857     if (name == "umbriel")
02858         return CreateUranianSatelliteOrbit(3);
02859     if (name == "titania")
02860         return CreateUranianSatelliteOrbit(4);
02861     if (name == "oberon")
02862         return CreateUranianSatelliteOrbit(5);
02863     if (name == "triton")
02864         return new TritonOrbit();
02865     else
02866         return CreateVSOP87Orbit(name);
02867 }

Generated on Sat Jan 14 22:30:27 2006 for Celestia by  doxygen 1.4.1