customorbit.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:103k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // customorbit.cpp
  2. //
  3. // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. #include <cassert>
  10. #include <vector>
  11. #include <fstream>
  12. #include <iomanip>
  13. #include <celmath/mathlib.h>
  14. #include <celmath/quaternion.h>
  15. #include "astro.h"
  16. #include "customorbit.h"
  17. #include "vsop87.h"
  18. #include "jpleph.h"
  19. using namespace std;
  20. #define TWOPI 6.28318530717958647692
  21. #define LPEJ 0.23509484  // Longitude of perihelion of Jupiter
  22. // These are required because the orbits of the Jovian and Saturnian
  23. // satellites are computed in units of their parent planets' radii.
  24. static const double JupiterRadius = 71398.0;
  25. static const double SaturnRadius = 60330.0;
  26. // The expressions for custom orbits are complex, so the bounding radii are
  27. // generally must computed from mean orbital elements.  It's important that
  28. // a sphere with the bounding radius completely enclose the orbit, so we
  29. // multiply by this factor to make the bounding radius a bit larger than
  30. // the apocenter distance computed from the mean elements.
  31. static const double BoundingRadiusSlack = 1.2;
  32. static bool jplephInitialized = false;
  33. static JPLEphemeris* jpleph = NULL;
  34. double gPlanetElements[8][9];
  35. double gElements[8][23] = {
  36. {   /*     mercury... */
  37.     178.179078, 415.2057519, 3.011e-4, 0.0,
  38.     75.899697, 1.5554889, 2.947e-4, 0.0,
  39.     .20561421, 2.046e-5, 3e-8, 0.0,
  40.     7.002881, 1.8608e-3, -1.83e-5, 0.0,
  41.     47.145944, 1.1852083, 1.739e-4, 0.0,
  42.     .3870986, 6.74,  -0.42
  43. },
  44. {   /*     venus... */
  45.     342.767053, 162.5533664, 3.097e-4, 0.0,
  46.     130.163833, 1.4080361, -9.764e-4, 0.0,
  47.     6.82069e-3, -4.774e-5, 9.1e-8, 0.0,
  48.     3.393631, 1.0058e-3, -1e-6, 0.0,
  49.     75.779647, .89985, 4.1e-4, 0.0,
  50.     .7233316, 16.92, -4.4
  51. },
  52. {   /*     mars... */
  53.     293.737334, 53.17137642, 3.107e-4, 0.0,
  54.     3.34218203e2, 1.8407584, 1.299e-4, -1.19e-6,
  55.     9.33129e-2, 9.2064e-5, 7.7e-8, 0.0,
  56.     1.850333, -6.75e-4, 1.26e-5, 0.0,
  57.     48.786442, .7709917, -1.4e-6, -5.33e-6,
  58.     1.5236883, 9.36, -1.52
  59. },
  60. {   /*     jupiter... */
  61.     238.049257, 8.434172183, 3.347e-4, -1.65e-6,
  62.     1.2720972e1, 1.6099617, 1.05627e-3, -3.43e-6,
  63.     4.833475e-2, 1.6418e-4, -4.676e-7, -1.7e-9,
  64.     1.308736, -5.6961e-3, 3.9e-6, 0.0,
  65.     99.443414, 1.01053, 3.5222e-4, -8.51e-6,
  66.     5.202561, 196.74, -9.4
  67. },
  68. {   /*     saturn... */
  69.     266.564377, 3.398638567, 3.245e-4, -5.8e-6,
  70.     9.1098214e1, 1.9584158, 8.2636e-4, 4.61e-6,
  71.     5.589232e-2, -3.455e-4, -7.28e-7, 7.4e-10,
  72.     2.492519, -3.9189e-3, -1.549e-5, 4e-8,
  73.     112.790414, .8731951, -1.5218e-4, -5.31e-6,
  74.     9.554747, 165.6, -8.88
  75. },
  76. {   /*     uranus... */
  77.     244.19747, 1.194065406, 3.16e-4, -6e-7,
  78.     1.71548692e2, 1.4844328, 2.372e-4, -6.1e-7,
  79.     4.63444e-2, -2.658e-5, 7.7e-8, 0.0,
  80.     .772464, 6.253e-4, 3.95e-5, 0.0,
  81.     73.477111, .4986678, 1.3117e-3, 0.0,
  82.     19.21814, 65.8, -7.19
  83. },
  84. {   /*     neptune... */
  85.     84.457994, .6107942056, 3.205e-4, -6e-7,
  86.     4.6727364e1, 1.4245744, 3.9082e-4, -6.05e-7,
  87.     8.99704e-3, 6.33e-6, -2e-9, 0.0,
  88.     1.779242, -9.5436e-3, -9.1e-6, 0.0,
  89.     130.681389, 1.098935, 2.4987e-4, -4.718e-6,
  90.     30.10957, 62.2, -6.87
  91. },
  92. {   /*     pluto...(osculating 1984 jan 21) */
  93.     95.3113544, .3980332167, 0.0, 0.0,
  94.     224.017, 0.0, 0.0, 0.0,
  95.     .25515, 0.0, 0.0, 0.0,
  96.     17.1329, 0.0, 0.0, 0.0,
  97.     110.191, 0.0, 0.0, 0.0,
  98.     39.8151, 8.2, -1.0
  99. }
  100. };
  101. // Useful version of trig functions which operate on values in degrees instead
  102. // of radians.
  103. static double sinD(double theta)
  104. {
  105.     return sin(degToRad(theta));
  106. }
  107. static double cosD(double theta)
  108. {
  109.     return cos(degToRad(theta));
  110. }
  111. static double Obliquity(double t)
  112. {
  113.     // Parameter t represents the Julian centuries elapsed since 1900.
  114.     // In other words, t = (jd - 2415020.0) / 36525.0
  115.     return degToRad(2.345229444E1 - ((((-1.81E-3*t)+5.9E-3)*t+4.6845E1)*t)/3600.0);
  116. }
  117. static void Nutation(double t, double &deps, double& dpsi)
  118. {
  119.     // Parameter t represents the Julian centuries elapsed since 1900.
  120.     // In other words, t = (jd - 2415020.0) / 36525.0
  121.     double ls, ld; // sun's mean longitude, moon's mean longitude
  122.     double ms, md; // sun's mean anomaly, moon's mean anomaly
  123.     double nm;     // longitude of moon's ascending node
  124.     double t2;
  125.     double tls, tnm, tld; // twice above
  126.     double a, b;
  127.     t2 = t*t;
  128.     a = 100.0021358*t;
  129.     b = 360.*(a-(int)a);
  130.     ls = 279.697+.000303*t2+b;
  131.     a = 1336.855231*t;
  132.     b = 360.*(a-(int)a);
  133.     ld = 270.434-.001133*t2+b;
  134.     a = 99.99736056000026*t;
  135.     b = 360.*(a-(int)a);
  136.     ms = 358.476-.00015*t2+b;
  137.     a = 13255523.59*t;
  138.     b = 360.*(a-(int)a);
  139.     md = 296.105+.009192*t2+b;
  140.     a = 5.372616667*t;
  141.     b = 360.*(a-(int)a);
  142.     nm = 259.183+.002078*t2-b;
  143.     //convert to radian forms for use with trig functions.
  144.     tls = 2*degToRad(ls);
  145.     nm = degToRad(nm);
  146.     tnm = 2*degToRad(nm);
  147.     ms = degToRad(ms);
  148.     tld = 2*degToRad(ld);
  149.     md = degToRad(md);
  150.     // find delta psi and eps, in arcseconds.
  151.     dpsi = (-17.2327-.01737*t)*sin(nm)+(-1.2729-.00013*t)*sin(tls)
  152.         +.2088*sin(tnm)-.2037*sin(tld)+(.1261-.00031*t)*sin(ms)
  153.         +.0675*sin(md)-(.0497-.00012*t)*sin(tls+ms)
  154.         -.0342*sin(tld-nm)-.0261*sin(tld+md)+.0214*sin(tls-ms)
  155.         -.0149*sin(tls-tld+md)+.0124*sin(tls-nm)+.0114*sin(tld-md);
  156.     deps = (9.21+.00091*t)*cos(nm)+(.5522-.00029*t)*cos(tls)
  157.         -.0904*cos(tnm)+.0884*cos(tld)+.0216*cos(tls+ms)
  158.         +.0183*cos(tld-nm)+.0113*cos(tld+md)-.0093*cos(tls-ms)
  159.         -.0066*cos(tls-nm);
  160.     // convert to radians.
  161.     dpsi = degToRad(dpsi/3600);
  162.     deps = degToRad(deps/3600);
  163. }
  164. static void EclipticToEquatorial(double t, double fEclLat, double fEclLon,
  165.                                  double& RA, double& dec)
  166. {
  167.     // Parameter t represents the Julian centuries elapsed since 1900.
  168.     // In other words, t = (jd - 2415020.0) / 36525.0
  169.     double seps, ceps; // sin and cos of mean obliquity
  170.     double sx, cx, sy, cy, ty;
  171.     double eps;
  172.     double deps, dpsi;
  173.     t = (astro::J2000 - 2415020.0) / 36525.0;
  174.     t = 0;
  175.     eps = Obliquity(t); // mean obliquity for date
  176.     Nutation(t, deps, dpsi);
  177.     eps += deps;
  178.     seps = sin(eps);
  179.     ceps = cos(eps);
  180.     sy = sin(fEclLat);
  181.     cy = cos(fEclLat); // always non-negative
  182.     if (fabs(cy)<1e-20)
  183.         cy = 1e-20; // insure > 0
  184.     ty = sy/cy;
  185.     cx = cos(fEclLon);
  186.     sx = sin(fEclLon);
  187.     dec = asin((sy*ceps)+(cy*seps*sx));
  188.     RA = atan(((sx*ceps)-(ty*seps))/cx);
  189.     if (cx<0)
  190.         RA += PI; // account for atan quad ambiguity
  191.     RA = pfmod(RA, TWOPI);
  192. }
  193. // Convert equatorial coordinates from one epoch to another.  Method is from
  194. // Chapter 21 of Meeus's _Astronomical Algorithms_
  195. void EpochConvert(double jdFrom, double jdTo,
  196.                   double a0, double d0,
  197.                   double& a, double& d)
  198. {
  199.     double T = (jdFrom - astro::J2000) / 36525.0;
  200.     double t = (jdTo - jdFrom) / 36525.0;
  201.     double zeta = (2306.2181 + 1.39656 * T - 0.000139 * T * T) * t +
  202.         (0.30188 - 0.000344 * T) * t * t + 0.017998 * t * t * t;
  203.     double z = (2306.2181 + 1.39656 * T - 0.000139 * T * T) * t +
  204.         (1.09468 + 0.000066 * T) * t * t + 0.018203 * t * t * t;
  205.     double theta = (2004.3109 - 0.85330 * T - 0.000217 * T * T) * t -
  206.         (0.42665 + 0.000217 * T) * t * t - 0.041833 * t * t * t;
  207.     zeta  = degToRad(zeta / 3600.0);
  208.     z     = degToRad(z / 3600.0);
  209.     theta = degToRad(theta / 3600.0);
  210.     double A = cos(d0) * sin(a0 + zeta);
  211.     double B = cos(theta) * cos(d0) * cos(a0 + zeta) -
  212.         sin(theta) * sin(d0);
  213.     double C = sin(theta) * cos(d0) * cos(a0 + zeta) +
  214.         cos(theta) * sin(d0);
  215.     a = atan2(A, B) + z;
  216.     d = asin(C);
  217. }
  218. double meanAnomalySun(double t)
  219. {
  220.     double t2, a, b;
  221. t2 = t*t;
  222. a = 9.999736042e1*t;
  223. b = 360*(a - (int)a);
  224.     return degToRad(3.5847583e2 - (1.5e-4 + 3.3e-6*t)*t2 + b);
  225. }
  226. void auxJSun(double t, double* x1, double* x2, double* x3, double* x4,
  227.              double* x5, double* x6)
  228. {
  229.     *x1 = t/5+0.1;
  230.     *x2 = pfmod(4.14473+5.29691e1*t, TWOPI);
  231.     *x3 = pfmod(4.641118+2.132991e1*t, TWOPI);
  232.     *x4 = pfmod(4.250177+7.478172*t, TWOPI);
  233.     *x5 = 5 * *x3 - 2 * *x2;
  234.     *x6 = 2 * *x2 - 6 * *x3 + 3 * *x4;
  235. }
  236. void computePlanetElements(double t, vector<int> pList)
  237. {
  238.     // Parameter t represents the Julian centuries elapsed since 1900.
  239.     // In other words, t = (jd - 2415020.0) / 36525.0
  240.     double *ep, *pp;
  241.     double aa;
  242.     int planet;
  243.     for(unsigned i = 0; i < pList.size(); i++)
  244.     {
  245.         planet = pList[i];
  246.         ep = gElements[planet];
  247.         pp = gPlanetElements[planet];
  248.         aa = ep[1]*t;
  249.         pp[0] = ep[0] + 360*(aa-(int)aa) + (ep[3]*t + ep[2])*t*t;
  250.         *pp = pfmod(*pp, 360.0);
  251.         pp[1] = (ep[1]*9.856263e-3) + (ep[2] + ep[3])/36525;
  252.         for(unsigned j = 4; j < 20; j += 4)
  253.             pp[j/4+1] = ((ep[j+3]*t + ep[j+2])*t + ep[j+1])*t + ep[j+0];
  254.         pp[6] = ep[20];
  255.         pp[7] = ep[21];
  256.         pp[8] = ep[22];
  257.     }
  258. }
  259. void computePlanetCoords(int p, double map, double da, double dhl, double dl,
  260.                          double dm, double dml, double dr, double ds,
  261.                          double& eclLong, double& eclLat, double& distance)
  262. {
  263.     double s, ma, nu, ea, lp, om, lo, slo, clo, inc, spsi, y;
  264.     s = gPlanetElements[p][3] + ds;
  265.     ma = map + dm;
  266.     astro::anomaly(ma, s, nu, ea);
  267.     distance = (gPlanetElements[p][6] + da)*(1 - s*s)/(1 + s*cos(nu));
  268.     lp = radToDeg(nu) + gPlanetElements[p][2] + radToDeg(dml - dm);
  269.     lp = degToRad(lp);
  270.     om = degToRad(gPlanetElements[p][5]);
  271.     lo = lp - om;
  272.     slo = sin(lo);
  273.     clo = cos(lo);
  274.     inc = degToRad(gPlanetElements[p][4]);
  275.     distance += dr;
  276.     spsi = slo*sin(inc);
  277.     y = slo*cos(inc);
  278.     eclLat = asin(spsi) + dhl;
  279.     spsi = sin(eclLat);
  280.     eclLong = atan(y/clo) + om + degToRad(dl);
  281.     if (clo < 0)
  282.         eclLong += PI;
  283.     eclLong = pfmod(eclLong, TWOPI);
  284.     distance *= KM_PER_AU;
  285. }
  286. void ComputeGalileanElements(double t,
  287.                              double& l1, double& l2, double& l3, double& l4,
  288.                              double& p1, double& p2, double& p3, double& p4,
  289.                              double& w1, double& w2, double& w3, double& w4,
  290.                              double& gamma, double& phi, double& psi,
  291.                              double& G, double& Gp)
  292. {
  293.     // Parameter t is Julian days, epoch 1950.0.
  294.     l1 = 1.8513962 + 3.551552269981*t;
  295.     l2 = 3.0670952 + 1.769322724929*t;
  296.     l3 = 2.1041485 + 0.87820795239*t;
  297.     l4 = 1.473836 + 0.37648621522*t;
  298.     p1 = 1.69451 + 2.8167146e-3*t;
  299.     p2 = 2.702927 + 8.248962e-4*t;
  300.     p3 = 3.28443 + 1.24396e-4*t;
  301.     p4 = 5.851859 + 3.21e-5*t;
  302.     w1 = 5.451267 - 2.3176901e-3*t;
  303.     w2 = 1.753028 - 5.695121e-4*t;
  304.     w3 = 2.080331 - 1.25263e-4*t;
  305.     w4 = 5.630757 - 3.07063e-5*t;
  306.     gamma = 5.7653e-3*sin(2.85674 + 1.8347e-5*t) + 6.002e-4*sin(0.60189 - 2.82274e-4*t);
  307.     phi = 3.485014 + 3.033241e-3*t;
  308.     psi = 5.524285 - 3.63e-8*t;
  309.     G = 0.527745 + 1.45023893e-3*t + gamma;
  310.     Gp = 0.5581306 + 5.83982523e-4*t;
  311. }
  312. //////////////////////////////////////////////////////////////////////////////
  313. class MercuryOrbit : public CachingOrbit
  314. {
  315.  public:
  316.     virtual ~MercuryOrbit() {};
  317.     Point3d computePosition(double jd) const
  318.     {
  319.     const int p = 0;  //Planet 0
  320.     vector<int> pList;
  321.     double t;
  322.     double map[4];
  323.     double dl, dr, dml, ds, dm, da, dhl;
  324.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  325.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  326.     // Calculate the Julian centuries elapsed since 1900
  327.     t = (jd - 2415020.0)/36525.0;
  328.     // Specify which planets we must compute elements for
  329.     pList.push_back(0);
  330.     pList.push_back(1);
  331.     pList.push_back(3);
  332.     computePlanetElements(t, pList);
  333.     // Compute necessary planet mean anomalies
  334.     map[0] = degToRad(gPlanetElements[0][0] - gPlanetElements[0][2]);
  335.     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
  336.     map[2] = 0.0;
  337.     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
  338.     // Compute perturbations
  339.     dl = 2.04e-3*cos(5*map[1]-2*map[0]+2.1328e-1)+
  340.          1.03e-3*cos(2*map[1]-map[0]-2.8046)+
  341.          9.1e-4*cos(2*map[3]-map[0]-6.4582e-1)+
  342.          7.8e-4*cos(5*map[1]-3*map[0]+1.7692e-1);
  343.     dr = 7.525e-6*cos(2*map[3]-map[0]+9.25251e-1)+
  344.          6.802e-6*cos(5*map[1]-3*map[0]-4.53642)+
  345.          5.457e-6*cos(2*map[1]-2*map[0]-1.24246)+
  346.          3.569e-6*cos(5*map[1]-map[0]-1.35699);
  347.     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
  348.                         eclLong, eclLat, distance);
  349.     // Corrections for internal coordinate system
  350.     eclLat -= (PI/2);
  351.     eclLong += PI;
  352.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  353.                    cos(eclLat) * distance,
  354.                    -sin(eclLong) * sin(eclLat) * distance);
  355.     };
  356.     double getPeriod() const
  357.     {
  358.         return 87.9522;
  359.     };
  360.     double getBoundingRadius() const
  361.     {
  362.         return 6.98e+7 * BoundingRadiusSlack;
  363.     };
  364. };
  365. class VenusOrbit : public CachingOrbit
  366. {
  367.  public:
  368.     virtual ~VenusOrbit() {};
  369.     Point3d computePosition(double jd) const
  370.     {
  371.     const int p = 1;  //Planet 1
  372.     vector<int> pList;
  373.     double t;
  374.     double map[4], mas;
  375.     double dl, dr, dml, ds, dm, da, dhl;
  376.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  377.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  378.     //Calculate the Julian centuries elapsed since 1900
  379. t = (jd - 2415020.0)/36525.0;
  380.     mas = meanAnomalySun(t);
  381.     //Specify which planets we must compute elements for
  382.     pList.push_back(1);
  383.     pList.push_back(3);
  384.     computePlanetElements(t, pList);
  385.     //Compute necessary planet mean anomalies
  386.     map[0] = 0.0;
  387.     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
  388.     map[2] = 0.0;
  389.     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
  390.     //Compute perturbations
  391.     dml = degToRad(7.7e-4*sin(4.1406+t*2.6227));
  392.     dm = dml;
  393. dl = 3.13e-3*cos(2*mas-2*map[1]-2.587)+
  394.      1.98e-3*cos(3*mas-3*map[1]+4.4768e-2)+
  395.      1.36e-3*cos(mas-map[1]-2.0788)+
  396.      9.6e-4*cos(3*mas-2*map[1]-2.3721)+
  397.      8.2e-4*cos(map[3]-map[1]-3.6318);
  398. dr = 2.2501e-5*cos(2*mas-2*map[1]-1.01592)+
  399.      1.9045e-5*cos(3*mas-3*map[1]+1.61577)+
  400.      6.887e-6*cos(map[3]-map[1]-2.06106)+
  401.      5.172e-6*cos(mas-map[1]-5.08065e-1)+
  402.      3.62e-6*cos(5*mas-4*map[1]-1.81877)+
  403.      3.283e-6*cos(4*mas-4*map[1]+1.10851)+
  404.      3.074e-6*cos(2*map[3]-2*map[1]-9.62846e-1);
  405.     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
  406.                         eclLong, eclLat, distance);
  407.     //Corrections for internal coordinate system
  408.     eclLat -= (PI/2);
  409.     eclLong += PI;
  410.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  411.                    cos(eclLat) * distance,
  412.                    -sin(eclLong) * sin(eclLat) * distance);
  413.     };
  414.     double getPeriod() const
  415.     {
  416.         return 224.7018;
  417.     };
  418.     double getBoundingRadius() const
  419.     {
  420.         return 1.089e+8 * BoundingRadiusSlack;
  421.     };
  422. };
  423. class EarthOrbit : public CachingOrbit
  424. {
  425.  public:
  426.     virtual ~EarthOrbit() {};
  427.     Point3d computePosition(double jd) const
  428.     {
  429.     double t, t2;
  430. double ls, ms;    // mean longitude and mean anomaly
  431. double s, nu, ea; // eccentricity, true anomaly, eccentric anomaly
  432. double a, b, a1, b1, c1, d1, e1, h1, dl, dr;
  433.         double eclLong, distance;
  434.         // Calculate the Julian centuries elapsed since 1900
  435. t = (jd - 2415020.0)/36525.0;
  436. t2 = t*t;
  437. a = 100.0021359*t;
  438. b = 360.*(a-(int)a);
  439. ls = 279.69668+.0003025*t2+b;
  440.         ms = meanAnomalySun(t);
  441. s = .016751-.0000418*t-1.26e-07*t2;
  442.         astro::anomaly(degToRad(ms), s, nu, ea);
  443. a = 62.55209472000015*t;
  444. b = 360*(a-(int)a);
  445. a1 = degToRad(153.23+b);
  446. a = 125.1041894*t;
  447. b = 360*(a-(int)a);
  448. b1 = degToRad(216.57+b);
  449. a = 91.56766028*t;
  450. b = 360*(a-(int)a);
  451. c1 = degToRad(312.69+b);
  452. a = 1236.853095*t;
  453. b = 360*(a-(int)a);
  454. d1 = degToRad(350.74-.00144*t2+b);
  455. e1 = degToRad(231.19+20.2*t);
  456. a = 183.1353208*t;
  457. b = 360*(a-(int)a);
  458. h1 = degToRad(353.4+b);
  459. dl = .00134*cos(a1)+.00154*cos(b1)+.002*cos(c1)+.00179*sin(d1)+
  460.             .00178*sin(e1);
  461. dr = 5.43e-06*sin(a1)+1.575e-05*sin(b1)+1.627e-05*sin(c1)+
  462.             3.076e-05*cos(d1)+9.27e-06*sin(h1);
  463.         eclLong = nu+degToRad(ls-ms+dl) + PI;
  464.         eclLong = pfmod(eclLong, TWOPI);
  465.         distance = KM_PER_AU * (1.0000002*(1-s*cos(ea))+dr);
  466.         // Correction for internal coordinate system
  467.         eclLong += PI;
  468.         return Point3d(-cos(eclLong) * distance,
  469.                        0,
  470.                        sin(eclLong) * distance);
  471.     };
  472.     double getPeriod() const
  473.     {
  474.         return 365.25;
  475.     };
  476.     double getBoundingRadius() const
  477.     {
  478.         return 1.52e+8 * BoundingRadiusSlack;
  479.     };
  480. };
  481. class LunarOrbit : public CachingOrbit
  482. {
  483.  public:
  484.     virtual ~LunarOrbit() {};
  485.     Point3d computePosition(double jd) const
  486.     {
  487. double jd19, t, t2;
  488. double ld, ms, md, de, f, n, hp;
  489. double a, sa, sn, b, sb, c, sc, e, e2, l, g, w1, w2;
  490. double m1, m2, m3, m4, m5, m6;
  491.         double eclLon, eclLat, horzPar, distance;
  492.         double RA, dec;
  493.         // Computation requires an abbreviated Julian day:
  494.         // epoch January 0.5, 1900.
  495.         jd19 = jd - 2415020.0;
  496. t = jd19/36525;
  497. t2 = t*t;
  498. m1 = jd19/27.32158213;
  499. m1 = 360.0*(m1-(int)m1);
  500. m2 = jd19/365.2596407;
  501. m2 = 360.0*(m2-(int)m2);
  502. m3 = jd19/27.55455094;
  503. m3 = 360.0*(m3-(int)m3);
  504. m4 = jd19/29.53058868;
  505. m4 = 360.0*(m4-(int)m4);
  506. m5 = jd19/27.21222039;
  507. m5 = 360.0*(m5-(int)m5);
  508. m6 = jd19/6798.363307;
  509. m6 = 360.0*(m6-(int)m6);
  510. ld = 270.434164+m1-(.001133-.0000019*t)*t2;
  511. ms = 358.475833+m2-(.00015+.0000033*t)*t2;
  512. md = 296.104608+m3+(.009192+.0000144*t)*t2;
  513. de = 350.737486+m4-(.001436-.0000019*t)*t2;
  514. f = 11.250889+m5-(.003211+.0000003*t)*t2;
  515. n = 259.183275-m6+(.002078+.000022*t)*t2;
  516. a = degToRad(51.2+20.2*t);
  517. sa = sin(a);
  518. sn = sin(degToRad(n));
  519. b = 346.56+(132.87-.0091731*t)*t;
  520. sb = .003964*sin(degToRad(b));
  521. c = degToRad(n+275.05-2.3*t);
  522. sc = sin(c);
  523. ld = ld+.000233*sa+sb+.001964*sn;
  524. ms = ms-.001778*sa;
  525. md = md+.000817*sa+sb+.002541*sn;
  526. f = f+sb-.024691*sn-.004328*sc;
  527. de = de+.002011*sa+sb+.001964*sn;
  528. e = 1-(.002495+7.52e-06*t)*t;
  529. e2 = e*e;
  530. ld = degToRad(ld);
  531. ms = degToRad(ms);
  532. n = degToRad(n);
  533. de = degToRad(de);
  534. f = degToRad(f);
  535. md = degToRad(md);
  536. l = 6.28875*sin(md)+1.27402*sin(2*de-md)+.658309*sin(2*de)+
  537.     .213616*sin(2*md)-e*.185596*sin(ms)-.114336*sin(2*f)+
  538.     .058793*sin(2*(de-md))+.057212*e*sin(2*de-ms-md)+
  539.     .05332*sin(2*de+md)+.045874*e*sin(2*de-ms)+.041024*e*sin(md-ms);
  540. l = l-.034718*sin(de)-e*.030465*sin(ms+md)+.015326*sin(2*(de-f))-
  541.     .012528*sin(2*f+md)-.01098*sin(2*f-md)+.010674*sin(4*de-md)+
  542.     .010034*sin(3*md)+.008548*sin(4*de-2*md)-e*.00791*sin(ms-md+2*de)-
  543.     e*.006783*sin(2*de+ms);
  544. l = l+.005162*sin(md-de)+e*.005*sin(ms+de)+.003862*sin(4*de)+
  545.     e*.004049*sin(md-ms+2*de)+.003996*sin(2*(md+de))+
  546.     .003665*sin(2*de-3*md)+e*.002695*sin(2*md-ms)+
  547.     .002602*sin(md-2*(f+de))+e*.002396*sin(2*(de-md)-ms)-
  548.     .002349*sin(md+de);
  549. l = l+e2*.002249*sin(2*(de-ms))-e*.002125*sin(2*md+ms)-
  550.     e2*.002079*sin(2*ms)+e2*.002059*sin(2*(de-ms)-md)-
  551.     .001773*sin(md+2*(de-f))-.001595*sin(2*(f+de))+
  552.     e*.00122*sin(4*de-ms-md)-.00111*sin(2*(md+f))+.000892*sin(md-3*de);
  553. l = l-e*.000811*sin(ms+md+2*de)+e*.000761*sin(4*de-ms-2*md)+
  554.             e2*.000704*sin(md-2*(ms+de))+e*.000693*sin(ms-2*(md-de))+
  555.             e*.000598*sin(2*(de-f)-ms)+.00055*sin(md+4*de)+.000538*sin(4*md)+
  556.             e*.000521*sin(4*de-ms)+.000486*sin(2*md-de);
  557. l = l+e2*.000717*sin(md-2*ms);
  558.         eclLon = ld+degToRad(l);
  559.         eclLon = pfmod(eclLon, TWOPI);
  560. g = 5.12819*sin(f)+.280606*sin(md+f)+.277693*sin(md-f)+
  561.     .173238*sin(2*de-f)+.055413*sin(2*de+f-md)+.046272*sin(2*de-f-md)+
  562.     .032573*sin(2*de+f)+.017198*sin(2*md+f)+.009267*sin(2*de+md-f)+
  563.     .008823*sin(2*md-f)+e*.008247*sin(2*de-ms-f);
  564. g = g+.004323*sin(2*(de-md)-f)+.0042*sin(2*de+f+md)+
  565.     e*.003372*sin(f-ms-2*de)+e*.002472*sin(2*de+f-ms-md)+
  566.     e*.002222*sin(2*de+f-ms)+e*.002072*sin(2*de-f-ms-md)+
  567.     e*.001877*sin(f-ms+md)+.001828*sin(4*de-f-md)-e*.001803*sin(f+ms)-
  568.     .00175*sin(3*f);
  569. g = g+e*.00157*sin(md-ms-f)-.001487*sin(f+de)-e*.001481*sin(f+ms+md)+
  570.             e*.001417*sin(f-ms-md)+e*.00135*sin(f-ms)+.00133*sin(f-de)+
  571.             .001106*sin(f+3*md)+.00102*sin(4*de-f)+.000833*sin(f+4*de-md)+
  572.             .000781*sin(md-3*f)+.00067*sin(f+4*de-2*md);
  573. g = g+.000606*sin(2*de-3*f)+.000597*sin(2*(de+md)-f)+
  574.     e*.000492*sin(2*de+md-ms-f)+.00045*sin(2*(md-de)-f)+
  575.     .000439*sin(3*md-f)+.000423*sin(f+2*(de+md))+
  576.     .000422*sin(2*de-f-3*md)-e*.000367*sin(ms+f+2*de-md)-
  577.     e*.000353*sin(ms+f+2*de)+.000331*sin(f+4*de);
  578. g = g+e*.000317*sin(2*de+f-ms+md)+e2*.000306*sin(2*(de-ms)-f)-
  579.     .000283*sin(md+3*f);
  580. w1 = .0004664*cos(n);
  581. w2 = .0000754*cos(c);
  582. eclLat = degToRad(g)*(1-w1-w2);
  583. hp = .950724+.051818*cos(md)+.009531*cos(2*de-md)+.007843*cos(2*de)+
  584.          .002824*cos(2*md)+.000857*cos(2*de+md)+e*.000533*cos(2*de-ms)+
  585.          e*.000401*cos(2*de-md-ms)+e*.00032*cos(md-ms)-.000271*cos(de)-
  586.          e*.000264*cos(ms+md)-.000198*cos(2*f-md);
  587. hp = hp+.000173*cos(3*md)+.000167*cos(4*de-md)-e*.000111*cos(ms)+
  588.          .000103*cos(4*de-2*md)-.000084*cos(2*md-2*de)-
  589.          e*.000083*cos(2*de+ms)+.000079*cos(2*de+2*md)+.000072*cos(4*de)+
  590.          e*.000064*cos(2*de-ms+md)-e*.000063*cos(2*de+ms-md)+
  591.          e*.000041*cos(ms+de);
  592. hp = hp+e*.000035*cos(2*md-ms)-.000033*cos(3*md-2*de)-
  593.          .00003*cos(md+de)-.000029*cos(2*(f-de))-e*.000029*cos(2*md+ms)+
  594.          e2*.000026*cos(2*(de-ms))-.000023*cos(2*(f-de)+md)+
  595.          e*.000019*cos(4*de-ms-md);
  596. horzPar = degToRad(hp);
  597.         // At this point we have values of ecliptic longitude, latitude and
  598.         // horizontal parallax (eclLong, eclLat, horzPar) in radians.
  599.         // Now compute distance using horizontal parallax.
  600.         distance = 6378.14 / sin(horzPar);
  601. #if 1
  602.         // Finally convert eclLat, eclLon to RA, Dec.
  603.         EclipticToEquatorial(t, eclLat, eclLon, RA, dec);
  604.         // RA and Dec are referred to the equinox of date; we want to use
  605.         // the J2000 equinox instead.  A better idea would be to directly
  606.         // compute the position of the Moon in this coordinate system, but
  607.         // this was easier.
  608.         EpochConvert(jd, astro::J2000, RA, dec, RA, dec);
  609.         // Corrections for internal coordinate system
  610.         dec -= (PI/2);
  611.         RA += PI;
  612.         return Point3d(cos(RA) * sin(dec) * distance,
  613.                        cos(dec) * distance,
  614.                        -sin(RA) * sin(dec) * distance);
  615. #else
  616.         // Skip the conversion and return ecliptical coordinates
  617.         double x = distance * cos(eclLat) * cos(eclLon);
  618.         double y = distance * cos(eclLat) * sin(eclLon);
  619.         double z = distance * sin(eclLat);
  620.         return Point3d(x, z, -y);
  621. #endif
  622.     };
  623.     double getPeriod() const
  624.     {
  625.         return 27.321661;
  626.     };
  627.     double getBoundingRadius() const
  628.     {
  629.         return 405504 * BoundingRadiusSlack;
  630.     };
  631. };
  632. class MarsOrbit : public CachingOrbit
  633. {
  634.  public:
  635.     virtual ~MarsOrbit() {};
  636.     Point3d computePosition(double jd) const
  637.     {
  638.     const int p = 2;  //Planet 2
  639.     vector<int> pList;
  640.     double t;
  641.     double map[4], mas, a;
  642.     double dl, dr, dml, ds, dm, da, dhl;
  643.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  644.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  645.     //Calculate the Julian centuries elapsed since 1900
  646. t = (jd - 2415020.0)/36525.0;
  647.     mas = meanAnomalySun(t);
  648.     //Specify which planets we must compute elements for
  649.     pList.push_back(1);
  650.     pList.push_back(2);
  651.     pList.push_back(3);
  652.     computePlanetElements(t, pList);
  653.     //Compute necessary planet mean anomalies
  654.     map[0] = 0.0;
  655.     map[1] = degToRad(gPlanetElements[1][0] - gPlanetElements[1][2]);
  656.     map[2] = degToRad(gPlanetElements[2][0] - gPlanetElements[2][2]);
  657.     map[3] = degToRad(gPlanetElements[3][0] - gPlanetElements[3][2]);
  658.     //Compute perturbations
  659.     a = 3*map[3]-8*map[2]+4*mas;
  660.     dml = degToRad(-1*(1.133e-2*sin(a)+9.33e-3*cos(a)));
  661.     dm = dml;
  662.     dl = 7.05e-3*cos(map[3]-map[2]-8.5448e-1)+
  663.      6.07e-3*cos(2*map[3]-map[2]-3.2873)+
  664.      4.45e-3*cos(2*map[3]-2*map[2]-3.3492)+
  665.      3.88e-3*cos(mas-2*map[2]+3.5771e-1)+
  666.      2.38e-3*cos(mas-map[2]+6.1256e-1)+
  667.      2.04e-3*cos(2*mas-3*map[2]+2.7688)+
  668.      1.77e-3*cos(3*map[2]-map[2-1]-1.0053)+
  669.      1.36e-3*cos(2*mas-4*map[2]+2.6894)+
  670.      1.04e-3*cos(map[3]+3.0749e-1);
  671.     dr = 5.3227e-5*cos(map[3]-map[2]+7.17864e-1)+
  672.      5.0989e-5*cos(2*map[3]-2*map[2]-1.77997)+
  673.      3.8278e-5*cos(2*map[3]-map[2]-1.71617)+
  674.      1.5996e-5*cos(mas-map[2]-9.69618e-1)+
  675.      1.4764e-5*cos(2*mas-3*map[2]+1.19768)+
  676.      8.966e-6*cos(map[3]-2*map[2]+7.61225e-1);
  677.     dr += 7.914e-6*cos(3*map[3]-2*map[2]-2.43887)+
  678.      7.004e-6*cos(2*map[3]-3*map[2]-1.79573)+
  679.      6.62e-6*cos(mas-2*map[2]+1.97575)+
  680.      4.93e-6*cos(3*map[3]-3*map[2]-1.33069)+
  681.      4.693e-6*cos(3*mas-5*map[2]+3.32665)+
  682.      4.571e-6*cos(2*mas-4*map[2]+4.27086)+
  683.      4.409e-6*cos(3*map[3]-map[2]-2.02158);
  684.     computePlanetCoords(p, map[p], da, dhl, dl, dm, dml, dr, ds,
  685.                         eclLong, eclLat, distance);
  686.     //Corrections for internal coordinate system
  687.     eclLat -= (PI/2);
  688.     eclLong += PI;
  689.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  690.                    cos(eclLat) * distance,
  691.                    -sin(eclLong) * sin(eclLat) * distance);
  692.     };
  693.     double getPeriod() const
  694.     {
  695.         return 689.998725;
  696.     };
  697.     double getBoundingRadius() const
  698.     {
  699.         return 2.49e+8 * BoundingRadiusSlack;
  700.     };
  701. };
  702. class JupiterOrbit : public CachingOrbit
  703. {
  704.  public:
  705.     virtual ~JupiterOrbit() {};
  706.     Point3d computePosition(double jd) const
  707.     {
  708.     const int p = 3;  //Planet 3
  709.     vector<int> pList(1, p);
  710.     double t, map;
  711.     double dl, dr, dml, ds, dm, da, dhl, s;
  712.     double dp;
  713.     double x1, x2, x3, x4, x5, x6, x7;
  714.     double sx3, cx3, s2x3, c2x3;
  715.     double sx5, cx5, s2x5;
  716.     double sx6;
  717.     double sx7, cx7, s2x7, c2x7, s3x7, c3x7, s4x7, c4x7, c5x7;
  718.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  719.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  720.     //Calculate the Julian centuries elapsed since 1900
  721. t = (jd - 2415020.0)/36525.0;
  722.     computePlanetElements(t, pList);
  723.     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
  724.     //Compute perturbations
  725.     s = gPlanetElements[p][3];
  726.     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
  727.     x7 = x3-x2;
  728.     sx3 = sin(x3);
  729.     cx3 = cos(x3);
  730.     s2x3 = sin(2*x3);
  731.     c2x3 = cos(2*x3);
  732.     sx5 = sin(x5);
  733.     cx5 = cos(x5);
  734.     s2x5 = sin(2*x5);
  735.     sx6 = sin(x6);
  736.     sx7 = sin(x7);
  737.     cx7 = cos(x7);
  738.     s2x7 = sin(2*x7);
  739.     c2x7 = cos(2*x7);
  740.     s3x7 = sin(3*x7);
  741.     c3x7 = cos(3*x7);
  742.     s4x7 = sin(4*x7);
  743.     c4x7 = cos(4*x7);
  744.     c5x7 = cos(5*x7);
  745.     dml = (3.31364e-1-(1.0281e-2+4.692e-3*x1)*x1)*sx5+
  746.       (3.228e-3-(6.4436e-2-2.075e-3*x1)*x1)*cx5-
  747.       (3.083e-3+(2.75e-4-4.89e-4*x1)*x1)*s2x5+
  748.       2.472e-3*sx6+1.3619e-2*sx7+1.8472e-2*s2x7+6.717e-3*s3x7+
  749.       2.775e-3*s4x7+6.417e-3*s2x7*sx3+
  750.       (7.275e-3-1.253e-3*x1)*sx7*sx3+
  751.       2.439e-3*s3x7*sx3-(3.5681e-2+1.208e-3*x1)*sx7*cx3;
  752.     dml += -3.767e-3*c2x7*sx3-(3.3839e-2+1.125e-3*x1)*cx7*sx3-
  753.       4.261e-3*s2x7*cx3+
  754.       (1.161e-3*x1-6.333e-3)*cx7*cx3+
  755.       2.178e-3*cx3-6.675e-3*c2x7*cx3-2.664e-3*c3x7*cx3-
  756.       2.572e-3*sx7*s2x3-3.567e-3*s2x7*s2x3+2.094e-3*cx7*c2x3+
  757.       3.342e-3*c2x7*c2x3;
  758.     dml = degToRad(dml);
  759.     ds = (3606+(130-43*x1)*x1)*sx5+(1289-580*x1)*cx5-6764*sx7*sx3-
  760.      1110*s2x7*sx3-224*s3x7*sx3-204*sx3+(1284+116*x1)*cx7*sx3+
  761.      188*c2x7*sx3+(1460+130*x1)*sx7*cx3+224*s2x7*cx3-817*cx3+
  762.      6074*cx3*cx7+992*c2x7*cx3+
  763.      508*c3x7*cx3+230*c4x7*cx3+108*c5x7*cx3;
  764.     ds += -(956+73*x1)*sx7*s2x3+448*s2x7*s2x3+137*s3x7*s2x3+
  765.      (108*x1-997)*cx7*s2x3+480*c2x7*s2x3+148*c3x7*s2x3+
  766.      (99*x1-956)*sx7*c2x3+490*s2x7*c2x3+
  767.      158*s3x7*c2x3+179*c2x3+(1024+75*x1)*cx7*c2x3-
  768.      437*c2x7*c2x3-132*c3x7*c2x3;
  769.     ds *= 1e-7;
  770.     dp = (7.192e-3-3.147e-3*x1)*sx5-4.344e-3*sx3+
  771.      (x1*(1.97e-4*x1-6.75e-4)-2.0428e-2)*cx5+
  772.      3.4036e-2*cx7*sx3+(7.269e-3+6.72e-4*x1)*sx7*sx3+
  773.      5.614e-3*c2x7*sx3+2.964e-3*c3x7*sx3+3.7761e-2*sx7*cx3+
  774.      6.158e-3*s2x7*cx3-
  775.      6.603e-3*cx7*cx3-5.356e-3*sx7*s2x3+2.722e-3*s2x7*s2x3+
  776.      4.483e-3*cx7*s2x3-2.642e-3*c2x7*s2x3+4.403e-3*sx7*c2x3-
  777.      2.536e-3*s2x7*c2x3+5.547e-3*cx7*c2x3-2.689e-3*c2x7*c2x3;
  778.     dm = dml-(degToRad(dp)/s);
  779.     da = 205*cx7-263*cx5+693*c2x7+312*c3x7+147*c4x7+299*sx7*sx3+
  780.      181*c2x7*sx3+204*s2x7*cx3+111*s3x7*cx3-337*cx7*cx3-
  781.      111*c2x7*cx3;
  782.     da *= 1e-6;
  783.     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
  784.                         eclLong, eclLat, distance);
  785.     //Corrections for internal coordinate system
  786.     eclLat -= (PI/2);
  787.     eclLong += PI;
  788.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  789.                    cos(eclLat) * distance,
  790.                    -sin(eclLong) * sin(eclLat) * distance);
  791.     };
  792.     double getPeriod() const
  793.     {
  794.         return 4332.66855;
  795.     };
  796.     double getBoundingRadius() const
  797.     {
  798.         return 8.16e+8 * BoundingRadiusSlack;
  799.     };
  800. };
  801. class SaturnOrbit : public CachingOrbit
  802. {
  803.  public:
  804.     virtual ~SaturnOrbit() {};
  805.     Point3d computePosition(double jd) const
  806.     {
  807.     const int p = 4;  //Planet 4
  808.     vector<int> pList(1, p);
  809.     double t, map;
  810.     double dl, dr, dml, ds, dm, da, dhl, s;
  811.     double dp;
  812.     double x1, x2, x3, x4, x5, x6, x7, x8;
  813.     double sx3, cx3, s2x3, c2x3, s3x3, c3x3, s4x3, c4x3;
  814.     double sx5, cx5, s2x5, c2x5;
  815.     double sx6;
  816.     double sx7, cx7, s2x7, c2x7, s3x7, c3x7, s4x7, c4x7, c5x7, s5x7;
  817.     double s2x8, c2x8, s3x8, c3x8;
  818.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  819.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  820.     //Calculate the Julian centuries elapsed since 1900
  821. t = (jd - 2415020.0)/36525.0;
  822.     computePlanetElements(t, pList);
  823.     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
  824.     //Compute perturbations
  825.     s = gPlanetElements[p][3];
  826.     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
  827.     x7 = x3-x2;
  828.     sx3 = sin(x3);
  829.     cx3 = cos(x3);
  830.     s2x3 = sin(2*x3);
  831.     c2x3 = cos(2*x3);
  832.     sx5 = sin(x5);
  833.     cx5 = cos(x5);
  834.     s2x5 = sin(2*x5);
  835.     sx6 = sin(x6);
  836.     sx7 = sin(x7);
  837.     cx7 = cos(x7);
  838.     s2x7 = sin(2*x7);
  839.     c2x7 = cos(2*x7);
  840.     s3x7 = sin(3*x7);
  841.     c3x7 = cos(3*x7);
  842.     s4x7 = sin(4*x7);
  843.     c4x7 = cos(4*x7);
  844.     c5x7 = cos(5*x7);
  845.     s3x3 = sin(3*x3);
  846.     c3x3 = cos(3*x3);
  847.     s4x3 = sin(4*x3);
  848.     c4x3 = cos(4*x3);
  849.     c2x5 = cos(2*x5);
  850.     s5x7 = sin(5*x7);
  851.     x8 = x4-x3;
  852.     s2x8 = sin(2*x8);
  853.     c2x8 = cos(2*x8);
  854.     s3x8 = sin(3*x8);
  855.     c3x8 = cos(3*x8);
  856.     dml = 7.581e-3*s2x5-7.986e-3*sx6-1.48811e-1*sx7-4.0786e-2*s2x7-
  857.       (8.14181e-1-(1.815e-2-1.6714e-2*x1)*x1)*sx5-
  858.       (1.0497e-2-(1.60906e-1-4.1e-3*x1)*x1)*cx5-1.5208e-2*s3x7-
  859.       6.339e-3*s4x7-6.244e-3*sx3-1.65e-2*s2x7*sx3+
  860.       (8.931e-3+2.728e-3*x1)*sx7*sx3-5.775e-3*s3x7*sx3+
  861.       (8.1344e-2+3.206e-3*x1)*cx7*sx3+1.5019e-2*c2x7*sx3;
  862.     dml += (8.5581e-2+2.494e-3*x1)*sx7*cx3+1.4394e-2*c2x7*cx3+
  863.       (2.5328e-2-3.117e-3*x1)*cx7*cx3+
  864.       6.319e-3*c3x7*cx3+6.369e-3*sx7*s2x3+9.156e-3*s2x7*s2x3+
  865.       7.525e-3*s3x8*s2x3-5.236e-3*cx7*c2x3-7.736e-3*c2x7*c2x3-
  866.       7.528e-3*c3x8*c2x3;
  867.     dml = degToRad(dml);
  868.     ds = (-7927+(2548+91*x1)*x1)*sx5+(13381+(1226-253*x1)*x1)*cx5+
  869.      (248-121*x1)*s2x5-(305+91*x1)*c2x5+412*s2x7+12415*sx3+
  870.      (390-617*x1)*sx7*sx3+(165-204*x1)*s2x7*sx3+26599*cx7*sx3-
  871.      4687*c2x7*sx3-1870*c3x7*sx3-821*c4x7*sx3-
  872.      377*c5x7*sx3+497*c2x8*sx3+(163-611*x1)*cx3;
  873.     ds += -12696*sx7*cx3-4200*s2x7*cx3-1503*s3x7*cx3-619*s4x7*cx3-
  874.      268*s5x7*cx3-(282+1306*x1)*cx7*cx3+(-86+230*x1)*c2x7*cx3+
  875.      461*s2x8*cx3-350*s2x3+(2211-286*x1)*sx7*s2x3-
  876.      2208*s2x7*s2x3-568*s3x7*s2x3-346*s4x7*s2x3-
  877.      (2780+222*x1)*cx7*s2x3+(2022+263*x1)*c2x7*s2x3+248*c3x7*s2x3+
  878.      242*s3x8*s2x3+467*c3x8*s2x3-490*c2x3-(2842+279*x1)*sx7*c2x3;
  879.     ds += (128+226*x1)*s2x7*c2x3+224*s3x7*c2x3+
  880.      (-1594+282*x1)*cx7*c2x3+(2162-207*x1)*c2x7*c2x3+
  881.      561*c3x7*c2x3+343*c4x7*c2x3+469*s3x8*c2x3-242*c3x8*c2x3-
  882.      205*sx7*s3x3+262*s3x7*s3x3+208*cx7*c3x3-271*c3x7*c3x3-
  883.      382*c3x7*s4x3-376*s3x7*c4x3;
  884.     ds *= 1e-7;
  885.     dp = (7.7108e-2+(7.186e-3-1.533e-3*x1)*x1)*sx5-7.075e-3*sx7+
  886.      (4.5803e-2-(1.4766e-2+5.36e-4*x1)*x1)*cx5-7.2586e-2*cx3-
  887.      7.5825e-2*sx7*sx3-2.4839e-2*s2x7*sx3-8.631e-3*s3x7*sx3-
  888.      1.50383e-1*cx7*cx3+2.6897e-2*c2x7*cx3+1.0053e-2*c3x7*cx3-
  889.      (1.3597e-2+1.719e-3*x1)*sx7*s2x3+1.1981e-2*s2x7*c2x3;
  890.     dp += -(7.742e-3-1.517e-3*x1)*cx7*s2x3+
  891.      (1.3586e-2-1.375e-3*x1)*c2x7*c2x3-
  892.      (1.3667e-2-1.239e-3*x1)*sx7*c2x3+
  893.      (1.4861e-2+1.136e-3*x1)*cx7*c2x3-
  894.      (1.3064e-2+1.628e-3*x1)*c2x7*c2x3;
  895.     dm = dml-(degToRad(dp)/s);
  896.     da = 572*sx5-1590*s2x7*cx3+2933*cx5-647*s3x7*cx3+33629*cx7-
  897.      344*s4x7*cx3-3081*c2x7+2885*cx7*cx3-1423*c3x7+
  898.      (2172+102*x1)*c2x7*cx3-671*c4x7+296*c3x7*cx3-320*c5x7-
  899.      267*s2x7*s2x3+1098*sx3-778*cx7*s2x3-2812*sx7*sx3;
  900.     da += 495*c2x7*s2x3+688*s2x7*sx3+250*c3x7*s2x3-393*s3x7*sx3-
  901.      856*sx7*c2x3-228*s4x7*sx3+441*s2x7*c2x3+2138*cx7*sx3+
  902.      296*c2x7*c2x3-999*c2x7*sx3+211*c3x7*c2x3-642*c3x7*sx3-
  903.      427*sx7*s3x3-325*c4x7*sx3+398*s3x7*s3x3-890*cx3+
  904.      344*cx7*c3x3+2206*sx7*cx3-427*c3x7*c3x3;
  905.     da *= 1e-6;
  906.     dhl = 7.47e-4*cx7*sx3+1.069e-3*cx7*cx3+2.108e-3*s2x7*s2x3+
  907.       1.261e-3*c2x7*s2x3+1.236e-3*s2x7*c2x3-2.075e-3*c2x7*c2x3;
  908.     dhl = degToRad(dhl);
  909.     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
  910.                         eclLong, eclLat, distance);
  911.     //Corrections for internal coordinate system
  912.     eclLat -= (PI/2);
  913.     eclLong += PI;
  914.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  915.                    cos(eclLat) * distance,
  916.                    -sin(eclLong) * sin(eclLat) * distance);
  917.     };
  918.     double getPeriod() const
  919.     {
  920.         return 10759.42493;
  921.     };
  922.     double getBoundingRadius() const
  923.     {
  924.         return 1.50e+9 * BoundingRadiusSlack;
  925.     };
  926. };
  927. class UranusOrbit : public CachingOrbit
  928. {
  929.  public:
  930.     virtual ~UranusOrbit() {};
  931.     Point3d computePosition(double jd) const
  932.     {
  933.     const int p = 5;  //Planet 5
  934.     vector<int> pList(1, p);
  935.     double t, map;
  936.     double dl, dr, dml, ds, dm, da, dhl, s;
  937.     double dp;
  938.     double x1, x2, x3, x4, x5, x6;
  939.     double x8, x9, x10, x11, x12;
  940.     double sx4, cx4, s2x4, c2x4;
  941.     double sx9, cx9, s2x9, c2x9;
  942.     double sx11, cx11;
  943.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  944.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  945.     //Calculate the Julian centuries elapsed since 1900
  946. t = (jd - 2415020.0)/36525.0;
  947.     computePlanetElements(t, pList);
  948.     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
  949.     //Compute perturbations
  950.     s = gPlanetElements[p][3];
  951.     auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
  952.     x8 = pfmod(1.46205+3.81337*t, TWOPI);
  953.     x9 = 2*x8-x4;
  954.     sx9 = sin(x9);
  955.     cx9 = cos(x9);
  956.     s2x9 = sin(2*x9);
  957.     c2x9 = cos(2*x9);
  958.     x10 = x4-x2;
  959.     x11 = x4-x3;
  960.     x12 = x8-x4;
  961.     dml = (8.64319e-1-1.583e-3*x1)*sx9+(8.2222e-2-6.833e-3*x1)*cx9+
  962.       3.6017e-2*s2x9-3.019e-3*c2x9+8.122e-3*sin(x6);
  963.     dml = degToRad(dml);
  964.     dp = 1.20303e-1*sx9+6.197e-3*s2x9+(1.9472e-2-9.47e-4*x1)*cx9;
  965.     dm = dml-(degToRad(dp)/s);
  966.     ds = (163*x1-3349)*sx9+20981*cx9+1311*c2x9;
  967.     ds *= 1e-7;
  968.     da = -3.825e-3*cx9;
  969.     dl = (1.0122e-2-9.88e-4*x1)*sin(x4+x11)+
  970.      (-3.8581e-2+(2.031e-3-1.91e-3*x1)*x1)*cos(x4+x11)+
  971.      (3.4964e-2-(1.038e-3-8.68e-4*x1)*x1)*cos(2*x4+x11)+
  972.      5.594e-3*sin(x4+3*x12)-1.4808e-2*sin(x10)-
  973.      5.794e-3*sin(x11)+2.347e-3*cos(x11)+9.872e-3*sin(x12)+
  974.      8.803e-3*sin(2*x12)-4.308e-3*sin(3*x12);
  975.     sx11 = sin(x11);
  976.     cx11 = cos(x11);
  977.     sx4 = sin(x4);
  978.     cx4 = cos(x4);
  979.     s2x4 = sin(2*x4);
  980.     c2x4 = cos(2*x4);
  981.     dhl = (4.58e-4*sx11-6.42e-4*cx11-5.17e-4*cos(4*x12))*sx4-
  982.       (3.47e-4*sx11+8.53e-4*cx11+5.17e-4*sin(4*x11))*cx4+
  983.       4.03e-4*(cos(2*x12)*s2x4+sin(2*x12)*c2x4);
  984.     dhl = degToRad(dhl);
  985.     dr = -25948+4985*cos(x10)-1230*cx4+3354*cos(x11)+904*cos(2*x12)+
  986.      894*(cos(x12)-cos(3*x12))+(5795*cx4-1165*sx4+1388*c2x4)*sx11+
  987.      (1351*cx4+5702*sx4+1388*s2x4)*cos(x11);
  988.     dr *= 1e-6;
  989.     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
  990.                         eclLong, eclLat, distance);
  991.     //Corrections for internal coordinate system
  992.     eclLat -= (PI/2);
  993.     eclLong += PI;
  994.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  995.                    cos(eclLat) * distance,
  996.                    -sin(eclLong) * sin(eclLat) * distance);
  997.     };
  998.     double getPeriod() const
  999.     {
  1000.         return 30686.07698;
  1001.     };
  1002.     double getBoundingRadius() const
  1003.     {
  1004.         return 3.01e+9 * BoundingRadiusSlack;
  1005.     };
  1006. };
  1007. class NeptuneOrbit : public CachingOrbit
  1008. {
  1009.  public:
  1010.     virtual ~NeptuneOrbit() {};
  1011.     Point3d computePosition(double jd) const
  1012.     {
  1013.     const int p = 6;  //Planet 6
  1014.     vector<int> pList(1, p);
  1015.     double t, map;
  1016.     double dl, dr, dml, ds, dm, da, dhl, s;
  1017.     double dp;
  1018.     double x1, x2, x3, x4, x5, x6;
  1019.     double x8, x9, x10, x11, x12;
  1020.     double sx8, cx8;
  1021.     double sx9, cx9, s2x9, c2x9;
  1022.     double s2x12, c2x12;
  1023.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  1024.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  1025.     //Calculate the Julian centuries elapsed since 1900
  1026. t = (jd - 2415020.0)/36525.0;
  1027.     computePlanetElements(t, pList);
  1028.     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
  1029.     //Compute perturbations
  1030.     s = gPlanetElements[p][3];
  1031. auxJSun(t, &x1, &x2, &x3, &x4, &x5, &x6);
  1032.     x8 = pfmod(1.46205+3.81337*t, TWOPI);
  1033.     x9 = 2*x8-x4;
  1034. sx9 = sin(x9);
  1035. cx9 = cos(x9);
  1036.     s2x9 = sin(2*x9);
  1037. c2x9 = cos(2*x9);
  1038. x10 = x8-x2;
  1039. x11 = x8-x3;
  1040. x12 = x8-x4;
  1041. dml = (1.089e-3*x1-5.89833e-1)*sx9+(4.658e-3*x1-5.6094e-2)*cx9-
  1042.       2.4286e-2*s2x9;
  1043. dml = degToRad(dml);
  1044. dp = 2.4039e-2*sx9-2.5303e-2*cx9+6.206e-3*s2x9-5.992e-3*c2x9;
  1045. dm = dml-(degToRad(dp)/s);
  1046. ds = 4389*sx9+1129*s2x9+4262*cx9+1089*c2x9;
  1047. ds *= 1e-7;
  1048. da = 8189*cx9-817*sx9+781*c2x9;
  1049. da *= 1e-6;
  1050. s2x12 = sin(2*x12);
  1051. c2x12 = cos(2*x12);
  1052. sx8 = sin(x8);
  1053. cx8 = cos(x8);
  1054. dl = -9.556e-3*sin(x10)-5.178e-3*sin(x11)+2.572e-3*s2x12-
  1055.      2.972e-3*c2x12*sx8-2.833e-3*s2x12*cx8;
  1056. dhl = 3.36e-4*c2x12*sx8+3.64e-4*s2x12*cx8;
  1057. dhl = degToRad(dhl);
  1058. dr = -40596+4992*cos(x10)+2744*cos(x11)+2044*cos(x12)+1051*c2x12;
  1059. dr *= 1e-6;
  1060.     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
  1061.                         eclLong, eclLat, distance);
  1062.     //Corrections for internal coordinate system
  1063.     eclLat -= (PI/2);
  1064.     eclLong += PI;
  1065.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  1066.                    cos(eclLat) * distance,
  1067.                    -sin(eclLong) * sin(eclLat) * distance);
  1068.     };
  1069.     double getPeriod() const
  1070.     {
  1071.         return 60190.64325;
  1072.     };
  1073.     double getBoundingRadius() const
  1074.     {
  1075.         return 4.54e+9 * BoundingRadiusSlack;
  1076.     };
  1077. };
  1078. class PlutoOrbit : public CachingOrbit
  1079. {
  1080.  public:
  1081.     virtual ~PlutoOrbit() {};
  1082.     Point3d computePosition(double jd) const
  1083.     {
  1084.     const int p = 7;  //Planet 7
  1085.     vector<int> pList(1, p);
  1086.     double t, map;
  1087.     double dl, dr, dml, ds, dm, da, dhl;
  1088.     double eclLong, eclLat, distance;    //heliocentric longitude, latitude, distance
  1089.     dl = dr = dml = ds = dm = da = dhl = 0.0;
  1090.     //Calculate the Julian centuries elapsed since 1900
  1091.     t = (jd - 2415020.0)/36525.0;
  1092.     computePlanetElements(t, pList);
  1093.     map = degToRad(gPlanetElements[p][0] - gPlanetElements[p][2]);
  1094.     computePlanetCoords(p, map, da, dhl, dl, dm, dml, dr, ds,
  1095.                         eclLong, eclLat, distance);
  1096.     //Corrections for internal coordinate system
  1097.     eclLat -= PI / 2;
  1098.     eclLong += PI;
  1099.     return Point3d(cos(eclLong) * sin(eclLat) * distance,
  1100.                    cos(eclLat) * distance,
  1101.                    -sin(eclLong) * sin(eclLat) * distance);
  1102.     };
  1103.     double getPeriod() const
  1104.     {
  1105.         return 90779.235;
  1106.     };
  1107.     double getBoundingRadius() const
  1108.     {
  1109.         return 7.38e+9 * BoundingRadiusSlack;
  1110.     };
  1111. };
  1112. // Compute for mean anomaly M the point on the ellipse with
  1113. // semimajor axis a and eccentricity e.  This helper function assumes
  1114. // a low eccentricity; orbit.cpp has functions appropriate for solving
  1115. // Kepler's equation for larger values of e.
  1116. static Point3d ellipsePosition(double a, double e, double M)
  1117. {
  1118.     // Solve Kepler's equation--for a low eccentricity orbit, just a few
  1119.     // iterations is enough.
  1120.     double E = M;
  1121.     for (int k = 0; k < 3; k++)
  1122.         E = M + e * sin(E);
  1123.     return Point3d(a * (cos(E) - e),
  1124.                    0.0,
  1125.                    a * sqrt(1 - square(e)) * -sin(E));
  1126. }
  1127. class PhobosOrbit : public CachingOrbit
  1128. {
  1129.  public:
  1130.     virtual ~PhobosOrbit() {};
  1131.     Point3d computePosition(double jd) const
  1132.     {
  1133.         double epoch = 2433283.0 - 0.5; // 00:00 1 Jan 1950
  1134.         double a     = 9380.0;
  1135.         double e     = 0.0151;
  1136.         double w0    = 150.247;
  1137.         double M0    =  92.474;
  1138.         double i     =   1.075;
  1139.         double node0 = 164.931;
  1140.         double n     = 1128.8444155;
  1141.         double Pw    =  1.131;
  1142.         double Pnode =  2.262;
  1143.         double refplane_RA  = 317.724;
  1144.         double refplane_Dec =  52.924;
  1145.         double marspole_RA  = 317.681;
  1146.         double marspole_Dec =  52.886;
  1147.         double t = jd - epoch;
  1148.         t += 10.5 / 1440.0;     // light time correction?
  1149.         double T = t / 365.25;
  1150.         double dnode = 360.0 / Pnode;
  1151.         double dw    = 360.0 / Pw;
  1152.         double node = degToRad(node0 + T * dnode);
  1153.         double w    = degToRad(w0 + T * dw - T * dnode);
  1154.         double M    = degToRad(M0 + t * n  - T * dw);
  1155.         Point3d p = ellipsePosition(a, e, M);
  1156.         // Orientation of the orbital plane with respect to the Laplacian plane
  1157.         Mat3d Rorbit     = (Mat3d::yrotation(node) *
  1158.                             Mat3d::xrotation(degToRad(i)) *
  1159.                             Mat3d::yrotation(w));
  1160.         // Rotate to the Earth's equatorial plane
  1161.         double N = degToRad(refplane_RA);
  1162.         double J = degToRad(90 - refplane_Dec);
  1163.         Mat3d RLaplacian = (Mat3d::yrotation( N) *
  1164.                             Mat3d::xrotation( J) *
  1165.                             Mat3d::yrotation(-N));
  1166.         // Rotate to the Martian equatorial plane
  1167.         N = degToRad(marspole_RA);
  1168.         J = degToRad(90 - marspole_Dec);
  1169.         Mat3d RMars_eq   = (Mat3d::yrotation( N) *
  1170.                             Mat3d::xrotation(-J) *
  1171.                             Mat3d::yrotation(-N));
  1172.         return RMars_eq * (RLaplacian * (Rorbit * p));
  1173.     }
  1174.     double getPeriod() const
  1175.     {
  1176.         return 0.319;
  1177.     }
  1178.     double getBoundingRadius() const
  1179.     {
  1180.         return 9380 * BoundingRadiusSlack;
  1181.     }
  1182. };
  1183. class DeimosOrbit : public CachingOrbit
  1184. {
  1185.  public:
  1186.     virtual ~DeimosOrbit() {};
  1187.     Point3d computePosition(double jd) const
  1188.     {
  1189.         double epoch = 2433283.0 - 0.5;
  1190.         double a     = 23460.0;
  1191.         double e     = 0.0002;
  1192.         double w0    = 290.496;
  1193.         double M0    = 296.230;
  1194.         double i     = 1.793;
  1195.         double node0 = 339.600;
  1196.         double n     = 285.1618919;
  1197.         double Pw    = 26.892;
  1198.         double Pnode = 54.536;
  1199.         double refplane_RA  = 316.700;
  1200.         double refplane_Dec =  53.564;
  1201.         double marspole_RA  = 317.681;
  1202.         double marspole_Dec =  52.886;
  1203.         double t = jd - epoch;
  1204.         t += 10.5 / 1440.0;     // light time correction?
  1205.         double T = t / 365.25;
  1206.         double dnode = 360.0 / Pnode;
  1207.         double dw    = 360.0 / Pw;
  1208.         double node = degToRad(node0 + T * dnode);
  1209.         double w    = degToRad(w0 + T * dw - T * dnode);
  1210.         double M    = degToRad(M0 + t * n  - T * dw);
  1211.         Point3d p = ellipsePosition(a, e, M);
  1212.         // Orientation of the orbital plane with respect to the Laplacian plane
  1213.         Mat3d Rorbit     = (Mat3d::yrotation(node) *
  1214.                             Mat3d::xrotation(degToRad(i)) *
  1215.                             Mat3d::yrotation(w));
  1216.         // Rotate to the Earth's equatorial plane
  1217.         double N = degToRad(refplane_RA);
  1218.         double J = degToRad(90 - refplane_Dec);
  1219.         Mat3d RLaplacian = (Mat3d::yrotation( N) *
  1220.                             Mat3d::xrotation( J) *
  1221.                             Mat3d::yrotation(-N));
  1222.         // Rotate to the Martian equatorial plane
  1223.         N = degToRad(marspole_RA);
  1224.         J = degToRad(90 - marspole_Dec);
  1225.         Mat3d RMars_eq   = (Mat3d::yrotation( N) *
  1226.                             Mat3d::xrotation(-J) *
  1227.                             Mat3d::yrotation(-N));
  1228.         return RMars_eq * (RLaplacian * (Rorbit * p));
  1229.     }
  1230. #if 0
  1231.     // More accurate orbit calculation for Mars from _Explanatory
  1232.     // Supplement to the Astronomical Almanac_  There's still a bug in
  1233.     // this routine however.
  1234.     Point3d computePosition(double jd) const
  1235.     {
  1236.         double d = jd - 2441266.5;  // days since 11 Nov 1971
  1237.         double D = d / 365.25;      // years
  1238.         double a = 1.56828e-4 * KM_PER_AU;
  1239.         double n = 285.161888;
  1240.         double e = 0.0004;
  1241.         double gamma = degToRad(1.79);
  1242.         double theta = degToRad(240.38 - 0.01801 * d);
  1243.         double h = degToRad(196.55 - 0.01801 * d);
  1244.         double L = degToRad(28.96 + n * d - 0.27 * sin(h));
  1245.         double P = degToRad(111.7 + 0.01798 * d);
  1246.         // N and J give the orientation of the Laplacian plane with respect
  1247.         // to the Earth's equator and equinox.
  1248.         //    N - longitude of ascending node
  1249.         //    J - inclination
  1250.         double N = degToRad(46.37 - 0.0014 * D);
  1251.         double J = degToRad(36.62 + 0.0008 * D);
  1252.         // Compute the mean anomaly
  1253.         double M = L - P;
  1254.         // Solve Kepler's equation--for a low eccentricity orbit, just a few
  1255.         // iterations is enough.
  1256.         double E = M;
  1257.         for (int i = 0; i < 3; i++)
  1258.             E = M + e * sin(E);
  1259.         // Compute the position in the orbital plane (y = 0)
  1260.         double x = a * (cos(E) - e);
  1261.         double z = a * sqrt(1 - square(e)) * -sin(E);
  1262.         // Orientation of the orbital plane with respect to the Laplacian plane
  1263.         Mat3d Rorbit     = (Mat3d::yrotation(theta) *
  1264.                             Mat3d::xrotation(gamma) *
  1265.                             Mat3d::yrotation(P - theta));
  1266.         Mat3d RLaplacian = (Mat3d::yrotation( N) *
  1267.                             Mat3d::xrotation(-J) *
  1268.                             Mat3d::yrotation(-N));
  1269.         double marspole_RA  = 317.681;
  1270.         double marspole_Dec =  52.886;
  1271.         double Nm = degToRad(marspole_RA + 90);
  1272.         double Jm = degToRad(90 - marspole_Dec);
  1273.         Mat3d RMars_eq   = (Mat3d::yrotation( Nm) *
  1274.                             Mat3d::xrotation( Jm) *
  1275.                             Mat3d::yrotation(-Nm));
  1276.         // Celestia wants the position of a satellite with respect to the
  1277.         // equatorial plane of the planet it orbits.
  1278.         // to Laplacian...
  1279.         Point3d p = Rorbit * Point3d(x, 0.0, z);
  1280.         // to Earth equatorial...
  1281.         p = RLaplacian * p;
  1282.         // to Mars equatorial...
  1283.         return RMars_eq * p;
  1284.     }
  1285. #endif
  1286.     double getPeriod() const
  1287.     {
  1288.         return 1.262441;
  1289.     }
  1290.     double getBoundingRadius() const
  1291.     {
  1292.         return 23462 * BoundingRadiusSlack;
  1293.     }
  1294. };
  1295. // static const double JupAscendingNode = degToRad(20.453422);
  1296. static const double JupAscendingNode = degToRad(22.203);
  1297. class IoOrbit : public CachingOrbit
  1298. {
  1299.  public:
  1300.     virtual ~IoOrbit() {};
  1301.     Point3d computePosition(double jd) const
  1302.     {
  1303.     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
  1304.     double t;
  1305.     double l1, l2, l3, l4;
  1306.     double p1, p2, p3, p4;
  1307.     double w1, w2, w3, w4;
  1308.     double gamma, phi, psi, G, Gp;
  1309.     double sigma, L, B, R;
  1310.     double T, P;
  1311.     // Epoch for Galilean satellites is 1976.0 Aug 10
  1312.     t = jd - 2443000.5;
  1313.     ComputeGalileanElements(t,
  1314.                             l1, l2, l3, l4,
  1315.                             p1, p2, p3, p4,
  1316.                             w1, w2, w3, w4,
  1317.                             gamma, phi, psi, G, Gp);
  1318.     // Calculate periodic terms for longitude
  1319.     sigma = 0.47259*sin(2*(l1 - l2)) - 0.03478*sin(p3 - p4)
  1320.           + 0.01081*sin(l2 - 2*l3 + p3) + 7.38e-3*sin(phi)
  1321.           + 7.13e-3*sin(l2 - 2*l3 + p2) - 6.74e-3*sin(p1 + p3 - 2*LPEJ - 2*G)
  1322.           + 6.66e-3*sin(l2 - 2*l3 + p4) + 4.45e-3*sin(l1 - p3)
  1323.           - 3.54e-3*sin(l1 - l2) - 3.17e-3*sin(2*(psi - LPEJ))
  1324.           + 2.65e-3*sin(l1 - p4) - 1.86e-3*sin(G)
  1325.           + 1.62e-3*sin(p2 - p3) + 1.58e-3*sin(4*(l1 - l2))
  1326.           - 1.55e-3*sin(l1 - l3) - 1.38e-3*sin(psi + w3 - 2*LPEJ - 2*G)
  1327.           - 1.15e-3*sin(2*(l1 - 2*l2 + w2)) + 8.9e-4*sin(p2 - p4)
  1328.           + 8.5e-4*sin(l1 + p3 - 2*LPEJ - 2*G) + 8.3e-4*sin(w2 - w3)
  1329.           + 5.3e-4*sin(psi - w2);
  1330.     sigma = pfmod(sigma, 360.0);
  1331.     sigma = degToRad(sigma);
  1332.     L = l1 + sigma;
  1333.     // Calculate periodic terms for the tangent of the latitude
  1334.     B = 6.393e-4*sin(L - w1) + 1.825e-4*sin(L - w2)
  1335.       + 3.29e-5*sin(L - w3) - 3.11e-5*sin(L - psi)
  1336.       + 9.3e-6*sin(L - w4) + 7.5e-6*sin(3*L - 4*l2 - 1.9927*sigma + w2)
  1337.       + 4.6e-6*sin(L + psi - 2*LPEJ - 2*G);
  1338.     B = atan(B);
  1339.     // Calculate the periodic terms for distance
  1340.     R = -4.1339e-3*cos(2*(l1 - l2)) - 3.87e-5*cos(l1 - p3)
  1341.       - 2.14e-5*cos(l1 - p4) + 1.7e-5*cos(l1 - l2)
  1342.       - 1.31e-5*cos(4*(l1 - l2)) + 1.06e-5*cos(l1 - l3)
  1343.       - 6.6e-6*cos(l1 + p3 - 2*LPEJ - 2*G);
  1344.     R = 5.90569 * JupiterRadius * (1 + R);
  1345.     T = (jd - 2433282.423) / 36525.0;
  1346.     P = 1.3966626*T + 3.088e-4*T*T;
  1347.     L += degToRad(P);
  1348.     L += JupAscendingNode;
  1349.     // Corrections for internal coordinate system
  1350.     B -= (PI/2);
  1351.     L += PI;
  1352.     return Point3d(cos(L) * sin(B) * R,
  1353.                    cos(B) * R,
  1354.                    -sin(L) * sin(B) * R);
  1355.     };
  1356.     double getPeriod() const
  1357.     {
  1358.         return 1.769138;
  1359.     };
  1360.     double getBoundingRadius() const
  1361.     {
  1362.         return 423329 * BoundingRadiusSlack;
  1363.     };
  1364. };
  1365. class EuropaOrbit : public CachingOrbit
  1366. {
  1367.  public:
  1368.     virtual ~EuropaOrbit() {};
  1369.     Point3d computePosition(double jd) const
  1370.     {
  1371.     // Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
  1372.     double t;
  1373.     double l1, l2, l3, l4;
  1374.     double p1, p2, p3, p4;
  1375.     double w1, w2, w3, w4;
  1376.     double gamma, phi, psi, G, Gp;
  1377.     double sigma, L, B, R;
  1378.     double T, P;
  1379.     // Epoch for Galilean satellites is 1976 Aug 10
  1380.     t = jd - 2443000.5;
  1381.     ComputeGalileanElements(t,
  1382.                             l1, l2, l3, l4,
  1383.                             p1, p2, p3, p4,
  1384.                             w1, w2, w3, w4,
  1385.                             gamma, phi, psi, G, Gp);
  1386.     // Calculate periodic terms for longitude
  1387.     sigma = 1.06476*sin(2*(l2 - l3)) + 0.04256*sin(l1 - 2*l2 + p3)
  1388.           + 0.03581*sin(l2 - p3) + 0.02395*sin(l1 - 2*l2 + p4)
  1389.           + 0.01984*sin(l2 - p4) - 0.01778*sin(phi)
  1390.           + 0.01654*sin(l2 - p2) + 0.01334*sin(l2 - 2*l3 + p2)
  1391.           + 0.01294*sin(p3 - p4) - 0.01142*sin(l2 - l3)
  1392.           - 0.01057*sin(G) - 7.75e-3*sin(2*(psi - LPEJ))
  1393.           + 5.24e-3*sin(2*(l1 - l2)) - 4.6e-3*sin(l1 - l3)
  1394.           + 3.16e-3*sin(psi - 2*G + w3 - 2*LPEJ) - 2.03e-3*sin(p1 + p3 - 2*LPEJ - 2*G)
  1395.           + 1.46e-3*sin(psi - w3) - 1.45e-3*sin(2*G)
  1396.           + 1.25e-3*sin(psi - w4) - 1.15e-3*sin(l1 - 2*l3 + p3)
  1397.           - 9.4e-4*sin(2*(l2 - w2)) + 8.6e-4*sin(2*(l1 - 2*l2 + w2))
  1398.           - 8.6e-4*sin(5*Gp - 2*G + 0.9115) - 7.8e-4*sin(l2 - l4)
  1399.           - 6.4e-4*sin(3*l3 - 7*l4 + 4*p4) + 6.4e-4*sin(p1 - p4)
  1400.           - 6.3e-4*sin(l1 - 2*l3 + p4) + 5.8e-4*sin(w3 - w4)
  1401.           + 5.6e-4*sin(2*(psi - LPEJ - G)) + 5.6e-4*sin(2*(l2 - l4))
  1402.           + 5.5e-4*sin(2*(l1 - l3)) + 5.2e-4*sin(3*l3 - 7*l4 + p3 +3*p4)
  1403.           - 4.3e-4*sin(l1 - p3) + 4.1e-4*sin(5*(l2 - l3))
  1404.           + 4.1e-4*sin(p4 - LPEJ) + 3.2e-4*sin(w2 - w3)
  1405.           + 3.2e-4*sin(2*(l3 - G - LPEJ));
  1406.     sigma = pfmod(sigma, 360.0);
  1407.     sigma = degToRad(sigma);
  1408.     L = l2 + sigma;
  1409.     // Calculate periodic terms for the tangent of the latitude
  1410.     B = 8.1004e-3*sin(L - w2) + 4.512e-4*sin(L - w3)
  1411.       - 3.284e-4*sin(L - psi) + 1.160e-4*sin(L - w4)
  1412.       + 2.72e-5*sin(l1 - 2*l3 + 1.0146*sigma + w2) - 1.44e-5*sin(L - w1)
  1413.       + 1.43e-5*sin(L + psi - 2*LPEJ - 2*G) + 3.5e-6*sin(L - psi + G)
  1414.       - 2.8e-6*sin(l1 - 2*l3 + 1.0146*sigma + w3);
  1415.     B = atan(B);
  1416.     // Calculate the periodic terms for distance
  1417.     R = 9.3848e-3*cos(l1 - l2) - 3.116e-4*cos(l2 - p3)
  1418.       - 1.744e-4*cos(l2 - p4) - 1.442e-4*cos(l2 - p2)
  1419.       + 5.53e-5*cos(l2 - l3) + 5.23e-5*cos(l1 - l3)
  1420.       - 2.9e-5*cos(2*(l1 - l2)) + 1.64e-5*cos(2*(l2 - w2))
  1421.       + 1.07e-5*cos(l1 - 2*l3 + p3) - 1.02e-5*cos(l2 - p1)
  1422.       - 9.1e-6*cos(2*(l1 - l3));
  1423.     R = 9.39657 * JupiterRadius * (1 + R);
  1424.     T = (jd - 2433282.423) / 36525.0;
  1425.     P = 1.3966626*T + 3.088e-4*T*T;
  1426.     L += degToRad(P);
  1427.     L += JupAscendingNode;
  1428.     // Corrections for internal coordinate system
  1429.     B -= (PI/2);
  1430.     L += PI;
  1431.     return Point3d(cos(L) * sin(B) * R,
  1432.                    cos(B) * R,
  1433.                    -sin(L) * sin(B) * R);
  1434.     };
  1435.     double getPeriod() const
  1436.     {
  1437.         return 3.5511810791;
  1438.     };
  1439.     double getBoundingRadius() const
  1440.     {
  1441.         return 678000 * BoundingRadiusSlack;
  1442.     };
  1443. };
  1444. class GanymedeOrbit : public CachingOrbit
  1445. {
  1446.  public:
  1447.     virtual ~GanymedeOrbit() {};
  1448.     Point3d computePosition(double jd) const
  1449.     {
  1450.     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
  1451.     double t;
  1452.     double l1, l2, l3, l4;
  1453.     double p1, p2, p3, p4;
  1454.     double w1, w2, w3, w4;
  1455.     double gamma, phi, psi, G, Gp;
  1456.     double sigma, L, B, R;
  1457.     double T, P;
  1458.     //Epoch for Galilean satellites is 1976 Aug 10
  1459.     t = jd - 2443000.5;
  1460.     ComputeGalileanElements(t,
  1461.                             l1, l2, l3, l4,
  1462.                             p1, p2, p3, p4,
  1463.                             w1, w2, w3, w4,
  1464.                             gamma, phi, psi, G, Gp);
  1465.     //Calculate periodic terms for longitude
  1466.     sigma = 0.1649*sin(l3 - p3) + 0.09081*sin(l3 - p4)
  1467.           - 0.06907*sin(l2 - l3) + 0.03784*sin(p3 - p4)
  1468.           + 0.01846*sin(2*(l3 - l4)) - 0.01340*sin(G)
  1469.           - 0.01014*sin(2*(psi - LPEJ)) + 7.04e-3*sin(l2 - 2*l3 + p3)
  1470.           - 6.2e-3*sin(l2 - 2*l3 + p2) - 5.41e-3*sin(l3 - l4)
  1471.           + 3.81e-3*sin(l2 - 2*l3 + p4) + 2.35e-3*sin(psi - w3)
  1472.           + 1.98e-3*sin(psi - w4) + 1.76e-3*sin(phi)
  1473.           + 1.3e-3*sin(3*(l3 - l4)) + 1.25e-3*sin(l1 - l3)
  1474.           - 1.19e-3*sin(5*Gp - 2*G + 0.9115) + 1.09e-3*sin(l1 - l2)
  1475.           - 1.0e-3*sin(3*l3 - 7*l4 + 4*p4) + 9.1e-4*sin(w3 - w4)
  1476.           + 8.0e-4*sin(3*l3 - 7*l4 + p3 + 3*p4) - 7.5e-4*sin(2*l2 - 3*l3 + p3)
  1477.           + 7.2e-4*sin(p1 + p3 - 2*LPEJ - 2*G) + 6.9e-4*sin(p4 - LPEJ)
  1478.           - 5.8e-4*sin(2*l3 - 3*l4 + p4) - 5.7e-4*sin(l3 - 2*l4 + p4)
  1479.           + 5.6e-4*sin(l3 + p3 - 2*LPEJ - 2*G) - 5.2e-4*sin(l2 - 2*l3 + p1)
  1480.           - 5.0e-4*sin(p2 - p3) + 4.8e-4*sin(l3 - 2*l4 + p3)
  1481.           - 4.5e-4*sin(2*l2 - 3*l3 + p4) - 4.1e-4*sin(p2 - p4)
  1482.           - 3.8e-4*sin(2*G) - 3.7e-4*sin(p3 - p4 + w3 - w4)
  1483.           - 3.2e-4*sin(3*l3 - 7*l4 + 2*p3 + 2*p4) + 3.0e-4*sin(4*(l3 - l4))
  1484.           + 2.9e-4*sin(l3 + p4 - 2*LPEJ - 2*G) - 2.8e-4*sin(w3 + psi - 2*LPEJ - 2*G)
  1485.           + 2.6e-4*sin(l3 - LPEJ - G) + 2.4e-4*sin(l2 - 3*l3 + 2*l4)
  1486.           + 2.1e-4*sin(2*(l3 - LPEJ - G)) - 2.1e-4*sin(l3 - p2)
  1487.           + 1.7e-4*sin(l3 - p3);
  1488.     sigma = pfmod(sigma, 360.0);
  1489.     sigma = degToRad(sigma);
  1490.     L = l3 + sigma;
  1491.     //Calculate periodic terms for the tangent of the latitude
  1492.     B = 3.2402e-3*sin(L - w3) - 1.6911e-3*sin(L - psi)
  1493.       + 6.847e-4*sin(L - w4) - 2.797e-4*sin(L - w2)
  1494.       + 3.21e-5*sin(L + psi - 2*LPEJ - 2*G) + 5.1e-6*sin(L - psi + G)
  1495.       - 4.5e-6*sin(L - psi - G) - 4.5e-6*sin(L + psi - 2*LPEJ)
  1496.       + 3.7e-6*sin(L + psi - 2*LPEJ - 3*G) + 3.0e-6*sin(2*l2 - 3*L + 4.03*sigma + w2)
  1497.       - 2.1e-6*sin(2*l2 - 3*L + 4.03*sigma + w3);
  1498.     B = atan(B);
  1499.     //Calculate the periodic terms for distance
  1500.     R = -1.4388e-3*cos(l3 - p3) - 7.919e-4*cos(l3 - p4)
  1501.       + 6.342e-4*cos(l2 - l3) - 1.761e-4*cos(2*(l3 - l4))
  1502.       + 2.94e-5*cos(l3 - l4) - 1.56e-5*cos(3*(l3 - l4))
  1503.       + 1.56e-5*cos(l1 - l3) - 1.53e-5*cos(l1 - l2)
  1504.       + 7.0e-6*cos(2*l2 - 3*l3 + p3) - 5.1e-6*cos(l3 + p3 - 2*LPEJ - 2*G);
  1505.     R = 14.98832 * JupiterRadius * (1 + R);
  1506.     T = (jd - 2433282.423) / 36525.0;
  1507.     P = 1.3966626*T + 3.088e-4*T*T;
  1508.     L += degToRad(P);
  1509.     L += JupAscendingNode;
  1510.     //Corrections for internal coordinate system
  1511.     B -= (PI/2);
  1512.     L += PI;
  1513.     return Point3d(cos(L) * sin(B) * R,
  1514.                    cos(B) * R,
  1515.                    -sin(L) * sin(B) * R);
  1516.     };
  1517.     double getPeriod() const
  1518.     {
  1519.         return 7.154553;
  1520.     };
  1521.     double getBoundingRadius() const
  1522.     {
  1523.         return 1070000 * BoundingRadiusSlack;
  1524.     };
  1525. };
  1526. class CallistoOrbit : public CachingOrbit
  1527. {
  1528.  public:
  1529.     virtual ~CallistoOrbit() {};
  1530.     Point3d computePosition(double jd) const
  1531.     {
  1532.     //Computation will yield latitude(L), longitude(B) and distance(R) relative to Jupiter
  1533.     double t;
  1534.     double l1, l2, l3, l4;
  1535.     double p1, p2, p3, p4;
  1536.     double w1, w2, w3, w4;
  1537.     double gamma, phi, psi, G, Gp;
  1538.     double sigma, L, B, R;
  1539.     double T, P;
  1540.     //Epoch for Galilean satellites is 1976 Aug 10
  1541.     t = jd - 2443000.5;
  1542.     ComputeGalileanElements(t,
  1543.                             l1, l2, l3, l4,
  1544.                             p1, p2, p3, p4,
  1545.                             w1, w2, w3, w4,
  1546.                             gamma, phi, psi, G, Gp);
  1547.     //Calculate periodic terms for longitude
  1548.     sigma =
  1549.         0.84287*sin(l4 - p4)
  1550.         + 0.03431*sin(p4 - p3)
  1551.         - 0.03305*sin(2*(psi - LPEJ))
  1552.         - 0.03211*sin(G)
  1553.         - 0.01862*sin(l4 - p3)
  1554.         + 0.01186*sin(psi - w4)
  1555.         + 6.23e-3*sin(l4 + p4 - 2*G - 2*LPEJ)
  1556.         + 3.87e-3*sin(2*(l4 - p4))
  1557.         - 2.84e-3*sin(5*Gp - 2*G + 0.9115)
  1558.         - 2.34e-3*sin(2*(psi - p4))
  1559.         - 2.23e-3*sin(l3 - l4)
  1560.         - 2.08e-3*sin(l4 - LPEJ)
  1561.         + 1.78e-3*sin(psi + w4 - 2*p4)
  1562.         + 1.34e-3*sin(p4 - LPEJ)
  1563.         + 1.25e-3*sin(2*(l4 - G - LPEJ))
  1564.         - 1.17e-3*sin(2*G)
  1565.         - 1.12e-3*sin(2*(l3 - l4))
  1566.         + 1.07e-3*sin(3*l3 - 7*l4 + 4*p4)
  1567.         + 1.02e-3*sin(l4 - G - LPEJ)
  1568.         + 9.6e-4*sin(2*l4 - psi - w4)
  1569.         + 8.7e-4*sin(2*(psi - w4))
  1570.         - 8.5e-4*sin(3*l3 - 7*l4 + p3 + 3*p4)
  1571.         + 8.5e-4*sin(l3 - 2*l4 + p4)
  1572.         - 8.1e-4*sin(2*(l4 - psi))
  1573.         + 7.1e-4*sin(l4 + p4 - 2*LPEJ - 3*G)
  1574.         + 6.1e-4*sin(l1 - l4)
  1575.         - 5.6e-4*sin(psi - w3)
  1576.         - 5.4e-4*sin(l3 - 2*l4 + p3)
  1577.         + 5.1e-4*sin(l2 - l4)
  1578.         + 4.2e-4*sin(2*(psi - G - LPEJ))
  1579.         + 3.9e-4*sin(2*(p4 - w4))
  1580.         + 3.6e-4*sin(psi + LPEJ - p4 - w4)
  1581.         + 3.5e-4*sin(2*Gp - G + 3.2877)
  1582.         - 3.5e-4*sin(l4 - p4 + 2*LPEJ - 2*psi)
  1583.         - 3.2e-4*sin(l4 + p4 - 2*LPEJ - G)
  1584.         + 3.0e-4*sin(2*Gp - 2*G + 2.6032)
  1585.         + 2.9e-4*sin(3*l3 - 7*l4 + 2*p3 + 2*p4)
  1586.         + 2.8e-4*sin(l4 - p4 + 2*psi - 2*LPEJ)
  1587.         - 2.8e-4*sin(2*(l4 - w4))
  1588.         - 2.7e-4*sin(p3 - p4 + w3 - w4)
  1589.         - 2.6e-4*sin(5*Gp - 3*G + 3.2877)
  1590.         + 2.5e-4*sin(w4 - w3)
  1591.         - 2.5e-4*sin(l2 - 3*l3 + 2*l4)
  1592.         - 2.3e-4*sin(3*(l3 - l4))
  1593.         + 2.1e-4*sin(2*l4 - 2*LPEJ - 3*G)
  1594.         - 2.1e-4*sin(2*l3 - 3*l4 + p4)
  1595.         + 1.9e-4*sin(l4 - p4 - G)
  1596.         - 1.9e-4*sin(2*l4 - p3 - p4)
  1597.         - 1.8e-4*sin(l4 - p4 + G)
  1598.         - 1.6e-4*sin(l4 + p3 - 2*LPEJ - 2*G);
  1599.     sigma = pfmod(sigma, 360.0);
  1600.     sigma = degToRad(sigma);
  1601.     L = l4 + sigma;
  1602.     //Calculate periodic terms for the tangent of the latitude
  1603.     B =
  1604.         - 7.6579e-3 * sin(L - psi)
  1605.         + 4.4134e-3 * sin(L - w4)
  1606.         - 5.112e-4  * sin(L - w3)
  1607.         + 7.73e-5   * sin(L + psi - 2*LPEJ - 2*G)
  1608.         + 1.04e-5   * sin(L - psi + G)
  1609.         - 1.02e-5   * sin(L - psi - G)
  1610.         + 8.8e-6    * sin(L + psi - 2*LPEJ - 3*G)
  1611.         - 3.8e-6    * sin(L + psi - 2*LPEJ - G);
  1612.     B = atan(B);
  1613.     //Calculate the periodic terms for distance
  1614.     R =
  1615.         - 7.3546e-3 * cos(l4 - p4)
  1616.         + 1.621e-4  * cos(l4 - p3)
  1617.         + 9.74e-5   * cos(l3 - l4)
  1618.         - 5.43e-5   * cos(l4 + p4 - 2*LPEJ - 2*G)
  1619.         - 2.71e-5   * cos(2*(l4 - p4))
  1620.         + 1.82e-5   * cos(l4 - LPEJ)
  1621.         + 1.77e-5   * cos(2*(l3 - l4))
  1622.         - 1.67e-5   * cos(2*l4 - psi - w4)
  1623.         + 1.67e-5   * cos(psi - w4)
  1624.         - 1.55e-5   * cos(2*(l4 - LPEJ - G))
  1625.         + 1.42e-5   * cos(2*(l4 - psi))
  1626.         + 1.05e-5   * cos(l1 - l4)
  1627.         + 9.2e-6    * cos(l2 - l4)
  1628.         - 8.9e-6    * cos(l4 - LPEJ -G)
  1629.         - 6.2e-6    * cos(l4 + p4 - 2*LPEJ - 3*G)
  1630.         + 4.8e-6    * cos(2*(l4 - w4));
  1631.     R = 26.36273 * JupiterRadius * (1 + R);
  1632.     T = (jd - 2433282.423) / 36525.0;
  1633.     P = 1.3966626*T + 3.088e-4*T*T;
  1634.     L += degToRad(P);
  1635.     L += JupAscendingNode;
  1636.     //Corrections for internal coordinate system
  1637.     B -= (PI/2);
  1638.     L += PI;
  1639.     return Point3d(cos(L) * sin(B) * R,
  1640.                    cos(B) * R,
  1641.                    -sin(L) * sin(B) * R);
  1642.     };
  1643.     double getPeriod() const
  1644.     {
  1645.         return 16.689018;
  1646.     };
  1647.     double getBoundingRadius() const
  1648.     {
  1649.         return 1890000 * BoundingRadiusSlack;
  1650.     };
  1651. };
  1652. static const double SatAscendingNode = 168.8112;
  1653. static const double SatTilt = 28.0817;
  1654. // static const double SatAscendingNode = 169.530;
  1655. // static const double SatTilt = 28.049;
  1656. // Calculations for the orbits of Mimas, Enceladus, Tethys, Dione, Rhea,
  1657. // Titan, Hyperion, and Iapetus are from Jean Meeus's Astronomical Algorithms,
  1658. // and were originally derived by Gerard Dourneau.
  1659. void ComputeSaturnianElements(double t,
  1660.                               double& t1, double& t2, double& t3,
  1661.                               double& t4, double& t5, double& t6,
  1662.                               double& t7, double& t8, double& t9,
  1663.                               double& t10, double& t11,
  1664.                               double& W0, double& W1, double& W2,
  1665.                               double& W3, double& W4, double& W5,
  1666.                               double& W6, double& W7, double& W8)
  1667. {
  1668.     t1 = t - 2411093.0;
  1669.     t2 = t1 / 365.25;
  1670.     t3 = (t - 2433282.423) / 365.25 + 1950.0;
  1671.     t4 = t - 2411368.0;
  1672.     t5 = t4 / 365.25;
  1673.     t6 = t - 2415020.0;
  1674.     t7 = t6 / 36525;
  1675.     t8 = t6 / 365.25;
  1676.     t9 = (t - 2442000.5) / 365.25;
  1677.     t10 = t - 2409786.0;
  1678.     t11 = t10 / 36525;
  1679.     W0 = 5.095 * (t3 - 1866.39);
  1680.     W1 = 74.4 + 32.39 * t2;
  1681.     W2 = 134.3 + 92.62 * t2;
  1682.     W3 = 42.0 - 0.5118 * t5;
  1683.     W4 = 276.59 + 0.5118 * t5;
  1684.     W5 = 267.2635 + 1222.1136 * t7;
  1685.     W6 = 175.4762 + 1221.5515 * t7;
  1686.     W7 = 2.4891 + 0.002435 * t7;
  1687.     W8 = 113.35 - 0.2597 * t7;
  1688. }
  1689. static Point3d SaturnMoonPosition(double lam, double gam, double Om, double r)
  1690. {
  1691.     double u = lam - Om;
  1692.     double w = Om - SatAscendingNode;
  1693.     u = degToRad(u);
  1694.     w = degToRad(w);
  1695.     gam = -degToRad(gam);
  1696.     r = r * SaturnRadius;
  1697.     // Corrections for Celestia's coordinate system
  1698.     u = -u;
  1699.     w = -w;
  1700.     double x = r * (cos(u) * cos(w) - sin(u) * sin(w) * cos(gam));
  1701.     double y = r * sin(u) * sin(gam);
  1702.     double z = r * (sin(u) * cos(w) * cos(gam) + cos(u) * sin(w));
  1703.     return Point3d(x, y, z);
  1704. }
  1705. static void OuterSaturnMoonParams(double a, double e, double i,
  1706.                                   double Om_, double M, double lam_,
  1707.                                   double& lam, double& gam,
  1708.                                   double& r, double& w)
  1709. {
  1710.     double s1 = sinD(SatTilt);
  1711.     double c1 = cosD(SatTilt);
  1712.     double e_2 = e * e;
  1713.     double e_3 = e_2 * e;
  1714.     double e_4 = e_3 * e;
  1715.     double e_5 = e_4 * e;
  1716.     double C = (2 * e - 0.25 * e_3 + 0.0520833333 * e_5) * sinD(M) +
  1717.         (1.25 * e_2 - 0.458333333 * e_4) * sinD(2 * M) +
  1718.         (1.083333333 * e_3 - 0.671875 * e_5) * sinD(3 * M) +
  1719.         1.072917 * e_4 * sinD(4 * M) + 1.142708 * e_5 * sinD(5 * M);
  1720.     double g = Om_ - SatAscendingNode;
  1721.     double a1 = sinD(i) * sinD(g);
  1722.     double a2 = c1 * sinD(i) * cosD(g) - s1 * cosD(i);
  1723.     double u = radToDeg(atan2(a1, a2));
  1724.     double h = c1 * sinD(i) - s1 * cosD(i) * cosD(g);
  1725.     double psi = radToDeg(atan2(s1 * sinD(g), h));
  1726.     C = radToDeg(C);
  1727.     lam = lam_ + C + u - g - psi;
  1728.     gam = radToDeg(asin(sqrt(square(a1) + square(a2))));
  1729.     r = a * (1 - e * e) / (1 + e * cosD(M + C));
  1730.     w = SatAscendingNode + u;
  1731. }
  1732. class MimasOrbit : public CachingOrbit
  1733. {
  1734.  public:
  1735.     virtual ~MimasOrbit() {};
  1736.     Point3d computePosition(double jd) const
  1737.     {
  1738.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1739.         // relative to Saturn.
  1740.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1741.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1742.         ComputeSaturnianElements(jd,
  1743.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1744.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1745.         double L = 127.64 + 381.994497 * t1 - 43.57 * sinD(W0) -
  1746.             0.720 * sinD( 3 * W0) - 0.02144 * sinD(5 * W0);
  1747.         double p = 106.1 + 365.549 * t2;
  1748.         double M = L - p;
  1749.         double C = 2.18287 * sinD(M) + 0.025988 * sinD(2 * M) +
  1750.             0.00043 * sinD(3 * M);
  1751.         double lam = L + C;
  1752.         double r = 3.06879 / (1 + 0.01905 * cosD(M + C));
  1753.         double gam = 1.563;
  1754.         double Om = 54.5 - 365.072 * t2;
  1755.         return SaturnMoonPosition(lam, gam, Om, r);
  1756.     };
  1757.     double getPeriod() const
  1758.     {
  1759. return 0.9424218;
  1760.     };
  1761.     double getBoundingRadius() const
  1762.     {
  1763.         return 189000 * BoundingRadiusSlack;
  1764.     };
  1765. };
  1766. class EnceladusOrbit : public CachingOrbit
  1767. {
  1768.  public:
  1769.     virtual ~EnceladusOrbit() {};
  1770.     Point3d computePosition(double jd) const
  1771.     {
  1772.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1773.         // relative to Saturn.
  1774.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1775.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1776.         ComputeSaturnianElements(jd,
  1777.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1778.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1779.         double L = 200.317 + 262.7319002 * t1 + 0.25667 * sinD(W1) +
  1780.             0.20883 * sinD(W2);
  1781.         double p = 309.107 + 123.44121 * t2;
  1782.         double M = L - p;
  1783.         double C = 0.55577 * sinD(M) + 0.00168 * sinD(2 * M);
  1784.         double lam = L + C;
  1785.         double r = 3.94118 / (1 + 0.00485 * cosD(M + C));
  1786.         double gam = 0.0262;
  1787.         double Om = 348 - 151.95 * t2;
  1788.         return SaturnMoonPosition(lam, gam, Om, r);
  1789.     };
  1790.     double getPeriod() const
  1791.     {
  1792. return 1.370218;
  1793.     };
  1794.     double getBoundingRadius() const
  1795.     {
  1796.         return 239000 * BoundingRadiusSlack;
  1797.     };
  1798. };
  1799. class TethysOrbit : public CachingOrbit
  1800. {
  1801.  public:
  1802.     virtual ~TethysOrbit() {};
  1803.     Point3d computePosition(double jd) const
  1804.     {
  1805.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1806.         // relative to Saturn.
  1807.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1808.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1809.         ComputeSaturnianElements(jd,
  1810.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1811.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1812.         double lam = 285.306 + 190.69791226 * t1 + 2.063 * sinD(W0) +
  1813.             0.03409 * sinD(3 * W0) + 0.001015 * sinD(5 * W0);
  1814.         double r = 4.880998;
  1815.         double gam = 1.0976;
  1816.         double Om = 111.33 - 72.2441 * t2;
  1817.         return SaturnMoonPosition(lam, gam, Om, r);
  1818.     };
  1819.     double getPeriod() const
  1820.     {
  1821.         return 1.887802;
  1822.     };
  1823.     double getBoundingRadius() const
  1824.     {
  1825.         return 295000 * BoundingRadiusSlack;
  1826.     };
  1827. };
  1828. class DioneOrbit : public CachingOrbit
  1829. {
  1830.  public:
  1831.     virtual ~DioneOrbit() {};
  1832.     Point3d computePosition(double jd) const
  1833.     {
  1834.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1835.         // relative to Saturn.
  1836.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1837.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1838.         ComputeSaturnianElements(jd,
  1839.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1840.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1841.         double L = 254.712 + 131.53493193 * t1 - 0.0215 * sinD(W1) -
  1842.             0.01733 * sinD(W2);
  1843.         double p = 174.8 + 30.820 * t2;
  1844.         double M = L - p;
  1845.         double C = 0.24717 * sinD(M) + 0.00033 * sinD(2 * M);
  1846.         double lam = L + C;
  1847.         double r = 6.24871 / (1 + 0.002157 * cosD(M + C));
  1848.         double gam = 0.0139;
  1849.         double Om = 232 - 30.27 * t2;
  1850.         // cout << "Dione: " << pfmod(lam, 360.0) << ',' << gam << ',' << pfmod(Om, 360.0) << ',' << r << 'n';
  1851.         return SaturnMoonPosition(lam, gam, Om, r);
  1852.     };
  1853.     double getPeriod() const
  1854.     {
  1855. return 2.736915;
  1856.     };
  1857.     double getBoundingRadius() const
  1858.     {
  1859.         return 378000 * BoundingRadiusSlack;
  1860.     };
  1861. };
  1862. class RheaOrbit : public CachingOrbit
  1863. {
  1864.  public:
  1865.     virtual ~RheaOrbit() {};
  1866.     Point3d computePosition(double jd) const
  1867.     {
  1868.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1869.         // relative to Saturn.
  1870.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1871.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1872.         ComputeSaturnianElements(jd,
  1873.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1874.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1875.         /*double e1 = 0.05589 - 0.000346 * t7;  Unused*/
  1876.         double p_ = 342.7 + 10.057 * t2;
  1877.         double a1 = 0.000265 * sinD(p_) + 0.01 * sinD(W4);
  1878.         double a2 = 0.000265 * cosD(p_) + 0.01 * cosD(W4);
  1879.         double e = sqrt(square(a1) + square(a2));
  1880.         double p = radToDeg(atan2(a1, a2));
  1881.         double N = 345 - 10.057 * t2;
  1882.         double lam_ = 359.244 + 79.69004720 * t1 + 0.086754 * sinD(N);
  1883.         double i = 28.0362 + 0.346898 * cosD(N) + 0.01930 * cosD(W3);
  1884.         double Om = 168.8034 + 0.736936 * sinD(N) + 0.041 * sinD(W3);
  1885.         double a = 8.725924;
  1886.         double lam, gam, r, w;
  1887.         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
  1888.                               lam, gam, r, w);
  1889.         // cout << "Rhea (intermediate): " << e << ',' << pfmod(lam_, 360.0) << ',' << pfmod(i, 360.0) << ',' << pfmod(Om, 360.0) << 'n';
  1890.         // cout << "Rhea: " << pfmod(lam, 360.0) << ',' << gam << ',' << pfmod(w, 360.0) << ',' << r << 'n';
  1891.         return SaturnMoonPosition(lam, gam, w, r);
  1892.     };
  1893.     double getPeriod() const
  1894.     {
  1895.         return 4.517500;
  1896.     };
  1897.     double getBoundingRadius() const
  1898.     {
  1899.         return 528000 * BoundingRadiusSlack;
  1900.     };
  1901. };
  1902. class TitanOrbit : public CachingOrbit
  1903. {
  1904.  public:
  1905.     virtual ~TitanOrbit() {};
  1906.     Point3d computePosition(double jd) const
  1907.     {
  1908.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1909.         // relative to Saturn.
  1910.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1911.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1912.         ComputeSaturnianElements(jd,
  1913.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1914.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1915.         double e1 = 0.05589 - 0.000346 * t7;
  1916.         double L = 261.1582 + 22.57697855 * t4 + 0.074025 * sinD(W3);
  1917.         double i_ = 27.45141 + 0.295999 * cosD(W3);
  1918.         double Om_ = 168.66925 + 0.628808 * sinD(W3);
  1919.         double a1 = sinD(W7) * sinD(Om_ - W8);
  1920.         double a2 = cosD(W7) * sinD(i_) - sinD(W7) * cosD(i_) * cosD(Om_ - W8);
  1921.         double g0 = 102.8623;
  1922.         double psi = radToDeg(atan2(a1, a2));
  1923.         double s = sqrt(square(a1) + square(a2));
  1924.         double g = W4 - Om_ - psi;
  1925.         // Three successive approximations will always be enough
  1926.         double om;
  1927.         for (int n = 0; n < 3; n++)
  1928.         {
  1929.             om = W4 + 0.37515 * (sinD(2 * g) - sinD(2 * g0));
  1930.             g = om - Om_ - psi;
  1931.         }
  1932.         double e_ = 0.029092 + 0.00019048 * (cosD(2 * g) - cosD(2 * g0));
  1933.         double q = 2 * (W5 - om);
  1934.         double b1 = sinD(i_) * sinD(Om_ - W8);
  1935.         double b2 = cosD(W7) * sinD(i_) * cosD(Om_ - W8) - sinD(W7) * cosD(i_);
  1936.         double theta = radToDeg(atan2(b1, b2)) + W8;
  1937.         double e = e_ + 0.002778797 * e_ * cosD(q);
  1938.         double p = om + 0.159215 * sinD(q);
  1939.         double u = 2 * W5 - 2 * theta + psi;
  1940.         double h = 0.9375 * square(e_) * sinD(q) + 0.1875 * square(s) * sinD(2 * (W5 - theta));
  1941.         double lam_ = L - 0.254744 * (e1 * sinD(W6) + 0.75 * square(e1) * sinD(2 * W6) + h);
  1942.         double i = i_ + 0.031843 * s * cosD(u);
  1943.         double Om = Om_ + (0.031843 * s * sinD(u)) / sinD(i_);
  1944.         double a = 20.216193;
  1945.         double lam, gam, r, w;
  1946.         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
  1947.                               lam, gam, r, w);
  1948.         return SaturnMoonPosition(lam, gam, w, r);
  1949.     };
  1950.     double getPeriod() const
  1951.     {
  1952.         return 15.94544758;
  1953.     };
  1954.     double getBoundingRadius() const
  1955.     {
  1956.         return 1260000 * BoundingRadiusSlack;
  1957.     };
  1958. };
  1959. class HyperionOrbit : public CachingOrbit
  1960. {
  1961.  public:
  1962.     virtual ~HyperionOrbit() {};
  1963.     Point3d computePosition(double jd) const
  1964.     {
  1965.         // Computation will yield latitude(L), longitude(B) and distance(R)
  1966.         // relative to Saturn.
  1967.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  1968.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  1969.         ComputeSaturnianElements(jd,
  1970.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  1971.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  1972.         double eta = 92.39 + 0.5621071 * t6;
  1973.         double zeta = 148.19 - 19.18 * t8;
  1974.         double theta = 184.8 - 35.41 * t9;
  1975.         double theta_ = theta - 7.5;
  1976.         double as = 176 + 12.22 * t8;
  1977.         double bs = 8 + 24.44 * t8;
  1978.         double cs = bs + 5;
  1979.         double om = 68.898 - 18.67088 * t8;
  1980.         double phi = 2 * (om - W5);
  1981.         double chi = 94.9 - 2.292 * t8;
  1982.         double a = 24.50601 -
  1983.             0.08686 * cosD(eta) -
  1984.             0.00166 * cosD(zeta + eta) +
  1985.             0.00175 * cosD(zeta - eta);
  1986.         double e = 0.103458 -
  1987.             0.004099 * cosD(eta) -
  1988.             0.000167 * cosD(zeta + eta) +
  1989.             0.000235 * cosD(zeta - eta) +
  1990.             0.02303 * cosD(zeta) -
  1991.             0.00212 * cosD(2 * zeta) +
  1992.             0.000151 * cosD(3 * zeta) +
  1993.             0.00013 * sinD(phi);
  1994.         double p = om +
  1995.             0.15648 * sinD(chi) -
  1996.             0.4457 * sinD(eta) -
  1997.             0.2657 * sinD(zeta + eta) -
  1998.             0.3573 * sinD(zeta - eta) -
  1999.             12.872 * sinD(zeta) +
  2000.             1.668 * sinD(2 * zeta) -
  2001.             0.2419 * sinD(3 * zeta) -
  2002.             0.07 * sinD(phi);
  2003.         double lam_ = 177.047 +
  2004.             16.91993829 * t6 +
  2005.             0.15648 * sinD(chi) +
  2006.             9.142 * sinD(eta) +
  2007.             0.007 * sinD(2 * eta) -
  2008.             0.014 * sinD(3 * eta) +
  2009.             0.2275 * sinD(zeta + eta) +
  2010.             0.2112 * sinD(zeta - eta) -
  2011.             0.26 * sinD(zeta) -
  2012.             0.0098 * sinD(2 * zeta) -
  2013.             0.013 * sinD(as) +
  2014.             0.017 * sinD(bs) -
  2015.             0.0303 * sinD(phi);
  2016.         double i = 27.3347 + 0.643486 * cosD(chi) + 0.315 * cosD(W3) +
  2017.             0.018 * cosD(theta) - 0.018 * cosD(cs);
  2018.         double Om = 168.6812 + 1.40136 * cosD(chi) + 0.68599 * sinD(W3) -
  2019.             0.0392 * sinD(cs) + 0.0366 * sinD(theta_);
  2020.         double lam, gam, r, w;
  2021.         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
  2022.                               lam, gam, r, w);
  2023.         return SaturnMoonPosition(lam, gam, w, r);
  2024.     };
  2025.     double getPeriod() const
  2026.     {
  2027.         return 21.276609;
  2028.     };
  2029.     double getBoundingRadius() const
  2030.     {
  2031.         return 1640000 * BoundingRadiusSlack;
  2032.     };
  2033. };
  2034. class IapetusOrbit : public CachingOrbit
  2035. {
  2036.  public:
  2037.     virtual ~IapetusOrbit() {};
  2038.     Point3d computePosition(double jd) const
  2039.     {
  2040.         // Computation will yield latitude(L), longitude(B) and distance(R)
  2041.         // relative to Saturn.
  2042.         double t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11;
  2043.         double W0, W1, W2, W3, W4, W5, W6, W7, W8;
  2044.         ComputeSaturnianElements(jd,
  2045.                                  t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11,
  2046.                                  W0, W1, W2, W3, W4, W5, W6, W7, W8);
  2047.         double L = 261.1582 + 22.57697855 * t4;
  2048.         double om_ = 91.796 + 0.562 * t7;
  2049.         double psi = 4.367 - 0.195 * t7;
  2050.         double theta = 146.819 - 3.198 * t7;
  2051.         double phi = 60.470 + 1.521 * t7;
  2052.         double Phi = 205.055 - 2.091 * t7;
  2053.         double e_ = 0.028298 + 0.001156 * t11;
  2054.         double om0 = 352.91 + 11.71 * t11;
  2055.         double mu = 76.3852 + 4.53795125 * t10;
  2056.         double i_ = 18.4602 - 0.9518 * t11 - 0.072 * square(t11) +
  2057.             0.0054 * cube(t11);
  2058.         double Om_ = 143.198 - 3.919 * t11 + 0.116 * square(t11) +
  2059.             0.008 * cube(t11);
  2060.         double l = mu - om0;
  2061.         double g = om0 - Om_ - psi;
  2062.         double g1 = om0 - Om_ - phi;
  2063.         double ls = W5 - om_;
  2064.         double gs = om_ - theta;
  2065.         double lT = L - W4;
  2066.         double gT = W4 - Phi;
  2067.         double u1 = 2 * (l + g - (ls + gs));
  2068.         double u2 = l + g1 - (lT + gT);
  2069.         double u3 = l + 2 * (g - (ls + gs));
  2070.         double u4 = lT + gT - g1;
  2071.         double u5 = 2 * (ls + gs);
  2072.         double a = 58.935028 + 0.004638 * cosD(u1) + 0.058222 * cosD(u2);
  2073.         double e = e_ -
  2074.             0.0014097 * cosD(g1 - gT) +
  2075.             0.0003733 * cosD(u5 - 2 * g) +
  2076.             0.0001180 * cosD(u3) +
  2077.             0.0002408 * cosD(l) +
  2078.             0.0002849 * cosD(l + u2) +
  2079.             0.0006190 * cosD(u4);
  2080.         double W = 0.08077 * sinD(g1 - gT) +
  2081.             0.02139 * sinD(u5 - 2 * g) -
  2082.             0.00676 * sinD(u3) +
  2083.             0.01380 * sinD(l) +
  2084.             0.01632 * sinD(l + u2) +
  2085.             0.03547 * sinD(u4);
  2086.         double p = om0 + W / e_;
  2087.         double lam_ = mu -
  2088.             0.04299 * sinD(u2) -
  2089.             0.00789 * sinD(u1) -
  2090.             0.06312 * sinD(ls) -
  2091.             0.00295 * sinD(2 * ls) -
  2092.             0.02231 * sinD(u5) +
  2093.             0.00650 * sinD(u5 + psi);
  2094.         double sum = l + g1 + lT + gT + phi;
  2095.         double i = i_ +
  2096.             0.04204 * cosD(u5 + psi) +
  2097.             0.00235 * cosD(sum) +
  2098.             0.00360 * cosD(u2 + phi);
  2099.         double w_ = 0.04204 * sinD(u5 + psi) +
  2100.             0.00235 * sinD(sum) +
  2101.             0.00358 * sinD(u2 + phi);
  2102.         double Om = Om_ + w_ / sinD(i_);
  2103.         double lam, gam, r, w;
  2104.         OuterSaturnMoonParams(a, e, i, Om, lam_ - p, lam_,
  2105.                               lam, gam, r, w);
  2106.         return SaturnMoonPosition(lam, gam, w, r);
  2107.     };
  2108.     double getPeriod() const
  2109.     {
  2110.         return 79.330183;
  2111.     };
  2112.     double getBoundingRadius() const
  2113.     {
  2114.         return 3660000 * BoundingRadiusSlack;
  2115.     };
  2116. };
  2117. class PhoebeOrbit : public CachingOrbit
  2118. {
  2119.  public:
  2120.     virtual ~PhoebeOrbit() {};
  2121.     Point3d computePosition(double jd) const
  2122.     {
  2123.         double t = jd - 2433282.5;
  2124.         double T = t / 365.25;
  2125.         double a = astro::AUtoKilometers(0.0865752f) / SaturnRadius;
  2126.         double lam_ = 277.872 - 0.6541068 * t - 90;
  2127.         double e = 0.16326;
  2128.         double pi = 280.165 - 0.19586 * T;
  2129.         double i = 173.949 - 0.020 * T;
  2130.         double Om = 245.998 - 0.41353 * T;
  2131.         double lam, gam, r, w;
  2132.         OuterSaturnMoonParams(a, e, i, Om, lam_ - pi, lam_,
  2133.                               lam, gam, r, w);
  2134.         return SaturnMoonPosition(lam, gam, w, r);
  2135.     };
  2136.     double getPeriod() const
  2137.     {
  2138.         return 548.2122790;
  2139.     };
  2140.     double getBoundingRadius() const
  2141.     {
  2142.         return 15100000 * BoundingRadiusSlack;
  2143.     };
  2144. };
  2145. class UranianSatelliteOrbit : public CachingOrbit
  2146. {
  2147.  private:
  2148.     double a;
  2149.     double n;
  2150.     double L0;
  2151.     double L1;
  2152.     double *L_k, *L_theta, *L_phi;
  2153.     int LTerms;
  2154.     double *z_k, *z_theta, *z_phi;
  2155.     int zTerms;
  2156.     double *zeta_k, *zeta_theta, *zeta_phi;
  2157.     int zetaTerms;
  2158.  public:
  2159.     UranianSatelliteOrbit(double _a,
  2160.                           double _n,
  2161.                           double _L0, double _L1,
  2162.                           int _LTerms, int _zTerms, int _zetaTerms,
  2163.                           double* _L_k, double* _L_theta, double* _L_phi,
  2164.                           double* _z_k, double* _z_theta, double* _z_phi,
  2165.                           double* _zeta_k, double* _zeta_theta,
  2166.                           double* _zeta_phi) :
  2167.         a(_a), n(_n), L0(_L0), L1(_L1),
  2168.         L_k(_L_k), L_theta(_L_theta), L_phi(_L_phi),
  2169.         LTerms(_LTerms),
  2170.         z_k(_z_k), z_theta(_z_theta), z_phi(_z_phi),
  2171.         zTerms(_zTerms),
  2172.         zeta_k(_zeta_k), zeta_theta(_zeta_theta), zeta_phi(_zeta_phi),
  2173.         zetaTerms(_zetaTerms)
  2174.     {
  2175.     };
  2176.     virtual ~UranianSatelliteOrbit() {};
  2177.     double getPeriod() const
  2178.     {
  2179.         return 2 * PI / n;
  2180.     }
  2181.     double getBoundingRadius() const
  2182.     {
  2183.         // Not quite correct, but should work since e is pretty low
  2184.         // for most of the Uranian moons.
  2185.         return a * BoundingRadiusSlack;
  2186.     }
  2187.     Point3d computePosition(double jd) const
  2188.     {
  2189.         double t = jd - 2444239.5;
  2190.         int i;
  2191.         double L = L0 + L1 * t;
  2192.         for (i = 0; i < LTerms; i++)
  2193.             L += L_k[i] * sin(L_theta[i] * t + L_phi[i]);
  2194.         double a0 = 0.0;
  2195.         double a1 = 0.0;
  2196.         for (i = 0; i < zTerms; i++)
  2197.         {
  2198.             double w = z_theta[i] * t + z_phi[i];
  2199.             a0 += z_k[i] * cos(w);
  2200.             a1 += z_k[i] * sin(w);
  2201.         }
  2202.         double b0 = 0.0;
  2203.         double b1 = 0.0;
  2204.         for (i = 0; i < zetaTerms; i++)
  2205.         {
  2206.             double w = zeta_theta[i] * t + zeta_phi[i];
  2207.             b0 += zeta_k[i] * cos(w);
  2208.             b1 += zeta_k[i] * sin(w);
  2209.         }
  2210.         double e = sqrt(square(a0) + square(a1));
  2211.         double p = atan2(a1, a0);
  2212.         double gamma = 2.0 * asin(sqrt(square(b0) + square(b1)));
  2213.         double theta = atan2(b1, b0);
  2214.         L += degToRad(174.99);
  2215.         // Now that we have all the orbital elements, compute the position
  2216.         double M = L - p;
  2217.         // Iterate a few times to compute the eccentric anomaly from the
  2218.         // mean anomaly.
  2219.         double ecc = M;
  2220.         for (i = 0; i < 4; i++)
  2221.             ecc = M + e * sin(ecc);
  2222.         double x = a * (cos(ecc) - e);
  2223.         double z = a * sqrt(1 - square(e)) * -sin(ecc);
  2224.         Mat3d R = (Mat3d::yrotation(theta) *
  2225.                    Mat3d::xrotation(gamma) *
  2226.                    Mat3d::yrotation(p - theta));
  2227.         return R * Point3d(x, 0, z);
  2228.     }
  2229. };
  2230. static double uran_n[5] =
  2231. { 4.44352267, 2.49254257, 1.51595490, 0.72166316, 0.46658054 };
  2232. static double uran_a[5] =
  2233. { 129800, 191200, 266000, 435800, 583600 };
  2234. static double uran_L0[5] =
  2235. { -0.23805158, 3.09804641, 2.28540169, 0.85635879, -0.91559180 };
  2236. static double uran_L1[5] =
  2237. { 4.44519055, 2.49295252, 1.51614811, 0.72171851, 0.46669212 };
  2238. static double uran_L_k[5][3] = {
  2239. {  0.02547217, -0.00308831, -3.181e-4 },
  2240. { -1.86050e-3, 2.1999e-4, 0 },
  2241. { 6.6057e-4, 0, 0 },
  2242. { 0, 0, 0 },
  2243. { 0, 0, 0 }
  2244. };
  2245. static double uran_L_theta[5][3] = {
  2246. { -2.18167e-4, -4.36336e-4, -6.54502e-4 },
  2247. { -2.18167e-4, -4.36336e-4, 0 },
  2248. { -2.18167e-4, 0, 0 },
  2249. { 0, 0, 0 },
  2250. { 0, 0, 0 }
  2251. };
  2252. static double uran_L_phi[5][3] = {
  2253. { 1.32, 2.64, 3.97 },
  2254. { 1.32, 2.64, 0 },
  2255. { 1.32, 0, 0 },
  2256. { 0, 0, 0 },
  2257. { 0, 0, 0 },
  2258. };
  2259. static double uran_z_k[5][5] = {
  2260. { 1.31238e-3, -1.2331e-4, -1.9410e-4, 0, 0 },
  2261. { 1.18763e-3, 8.6159e-4, 0, 0, 0 },
  2262. { -2.2795e-4, 3.90496e-3, 3.0917e-4, 2.2192e-4, 5.4923e-4 },
  2263. { 9.3281e-4, 1.12089e-3, 7.9343e-4, 0, 0 },
  2264. { -7.5868e-4, 1.39734e-3, -9.8726e-4, 0, 0 }
  2265. };
  2266. static double uran_z_theta[5][5] = {
  2267. { 1.5273e-4, 0.08606, 0.709, 0, 0 },
  2268. { 4.727824e-5, 2.179316e-5, 0, 0, 0 },
  2269. { 4.727824e-5, 2.179132e-5, 1.580524e-5, 2.9363068e-6, -0.01157 },
  2270. { 1.580524e-5, 2.9363068e-6, -6.9008e-3, 0, 0 },
  2271. { 1.580524e-5, 2.9363068e-6, -6.9008e-3, 0, 0 }
  2272. };
  2273. static double uran_z_phi[5][5] = {
  2274. { 0.61, 0.15, 6.04, 0, 0 },
  2275. { 2.41, 2.07, 0, 0, 0 },
  2276. { 2.41, 2.07, 0.74, 0.43, 5.71 },
  2277. { 0.74, 0.43, 1.82, 0, 0 },
  2278. { 0.74, 0.43, 1.82, 0, 0 }
  2279. };
  2280. static double uran_zeta_k[5][2] = {
  2281. { 0.03787171, 0 },
  2282. { 3.5825e-4, 2.9008e-4 },
  2283. { 1.11336e-3, 3.5014e-4 },
  2284. { 6.8572e-4, 3.7832e-4 },
  2285. { -5.9633e-4, 4.5169e-4 }
  2286. };
  2287. static double uran_zeta_theta[5][2] = {
  2288. { -1.54449e-4, 0 },
  2289. { -4.782474e-5, -2.156628e-5 },
  2290. { -2.156628e-5, -1.401373e-5 },
  2291. { -1.401373e-5, -1.9713918e-6 },
  2292. { -1.401373e-5, -1.9713918e-6 }
  2293. };
  2294. static double uran_zeta_phi[5][2] = {
  2295. { 5.70, 0 },
  2296. { 0.40, 0.59 },
  2297. { 0.59, 1.75 },
  2298. { 1.75, 4.21 },
  2299. { 1.75, 4.21 },
  2300. };
  2301. static UranianSatelliteOrbit* CreateUranianSatelliteOrbit(int n)
  2302. {
  2303.     assert(n >= 1 && n <= 5);
  2304.     n--;
  2305.     return new UranianSatelliteOrbit(uran_a[n], uran_n[n],
  2306.                                      uran_L0[n], uran_L1[n],
  2307.                                      3, 5, 2,
  2308.                                      uran_L_k[n], uran_L_theta[n],
  2309.                                      uran_L_phi[n], uran_z_k[n],
  2310.                                      uran_z_theta[n], uran_z_phi[n],
  2311.                                      uran_zeta_k[n], uran_zeta_theta[n],
  2312.                                      uran_zeta_phi[n]);
  2313. };
  2314. /*! Orbit of Triton, from Seidelmann, _Explanatory Supplement to the
  2315.  *  Astronomical Almanac_ (1992), p.373-374. The position of Triton
  2316.  *  is calculated in Neptunocentric coordinates referred to the 
  2317.  *  Earth equator/equinox of J2000.0.
  2318.  */
  2319. class TritonOrbit : public CachingOrbit
  2320. {
  2321.  public:
  2322.     virtual ~TritonOrbit() {};
  2323.     Point3d computePosition(double jd) const
  2324.     {
  2325.         double epoch = 2433282.5;
  2326.         double t = jd - epoch;
  2327.         // Compute the position of Triton in its orbital plane
  2328.         double a = 354800;                // Semi-major axis (488.49")
  2329.         double n = degToRad(61.2588532);  // mean motion
  2330.         double L0 = degToRad(200.913);
  2331.         double L  = L0 + n * t;
  2332.         double E = L;   // Triton's orbit is circular, so E = mean anomaly
  2333.         Point3d p(a * cos(E), a * sin(E), 0.0);
  2334.         // Transform to the invariable plane:
  2335.         //   gamma is the inclination of the orbital plane on the invariable plane
  2336.         //   theta is the angle from the intersection of the invariable plane
  2337.         //      with the Earth equatorial plane of 1950.0 to the ascending node
  2338.         //      of the orbit on the invariable plane.
  2339.         double gamma = degToRad(158.996);
  2340.         double theta = degToRad(151.401 + 0.57806 * t / 365.25);
  2341.         Quatd toInvariable = Quatd::xrotation(-gamma) * Quatd::zrotation(-theta);
  2342.         // Compute the RA and declination of the pole of the fixed reference plane
  2343.         // (epoch is J2000.0)
  2344.         double T = (jd - astro::J2000) / 36525;
  2345.         double N = degToRad(359.28 + 54.308 * T);
  2346.         double refplane_RA  = 298.72 + 2.58 * sin(N) - 0.04 * sin(2 * N);
  2347.         double refplane_Dec =  42.63 - 1.90 * cos(N) - 0.01 * cos(2 * N);
  2348.         // Rotate to the Earth's equatorial plane
  2349.         double Nr = degToRad(refplane_RA - 90.0);
  2350.         double Jr = degToRad(90.0 - refplane_Dec);
  2351.         Quatd toEarthEq = Quatd::xrotation(Jr) * Quatd::zrotation(Nr);
  2352.         Quatd q = toEarthEq * toInvariable;
  2353.         //Quatd q = toInvariable * toEarthEq;
  2354.         p = q.toMatrix3() * p;
  2355.         // Convert to Celestia's coordinate system
  2356.         return Point3d(p.x, p.z, -p.y);
  2357.     }
  2358.     double getPeriod() const
  2359.     {
  2360.         return 5.877;
  2361.     }
  2362.     double getBoundingRadius() const
  2363.     {
  2364.         return 354800 * BoundingRadiusSlack;
  2365.     }
  2366. };
  2367. /*! Ephemeris for Helene, Telesto, and Calypso, from
  2368.  *  "An upgraded theory for Helene, Telesto, and Calypso"
  2369.  *  Oberti P., Vienne A., 2002, A&A
  2370.  *  Translated to C++ by Chris Laurel from FORTRAN source available at:
  2371.  *    ftp://ftp.imcce.fr/pub/ephem/satel/htc20
  2372.  *
  2373.  *  Coordinates are Saturnocentric and referred to the ecliptic 
  2374.  *     and equinox of J2000.0.
  2375.  */    
  2376. static const double HeleneTerms[24*5] =
  2377. {
  2378.     0,0,0,0,1,
  2379.     1,0,0,0,1,
  2380.     1,1,0,0,1,
  2381.     0,0,1,0,1,
  2382.     1,0,1,0,1,
  2383.     0,0,2,0,1,
  2384.     1,0,2,0,1,
  2385.     0,0,3,0,1,
  2386.     0,1,0,0,-1,
  2387.     1,1,0,0,-1,
  2388.     0,0,1,0,-1,
  2389.     1,0,1,0,-1,
  2390.     0,0,2,0,-1,
  2391.     1,0,2,0,-1,
  2392.     0,0,3,0,-1,
  2393.     1,0,0,1,0,
  2394.     0,1,0,0,1,
  2395.     1,0,3,0,1,
  2396.     1,0,3,0,-1,
  2397.     1,1,1,0,1,
  2398.     1,1,-1,0,1,
  2399.     0,0,0,1,0,
  2400.     0,1,1,0,1,
  2401.     0,1,-1,0,1,
  2402. };
  2403. static const double HeleneAmps[24*6] = 
  2404.     -0.002396,-0.000399,0.000442,0.001278,-0.004939,0.002466,
  2405.      0.000557,-0.002152,0.001074,0.005500,0.000916,-0.001015,-0.000003,
  2406.      0.,0.,0.000003,-0.000011,0.000006,-0.000066,0.000265,-0.000133,
  2407.      -0.000676,-0.000107,0.000122,-0.000295,-0.000047,0.000053,
  2408.      0.000151,-0.000607,0.000303,0.000015,0.000017,-0.000010,-0.000044,
  2409.      0.000033,-0.000013,-0.000019,0.000014,-0.000006,-0.000035,
  2410.      -0.000038,0.000023,0.000002,0.,0.,-0.000002,0.000004,-0.000002,
  2411.      -0.000002,0.000008,-0.000004,0.,0.,0.,0.000009,0.,-0.000002,0.,0.,
  2412.      0.,-0.000067,0.000264,-0.000132,-0.000677,-0.000110,0.000123,
  2413.      0.000294,0.000048,-0.000053,-0.000154,0.000608,-0.000304,0.000015,
  2414.      0.000016,-0.000010,-0.000044,0.000033,-0.000013,0.000019,
  2415.      -0.000014,0.000006,0.000035,0.000038,-0.000023,0.000002,0.,0.,
  2416.      -0.000002,0.000004,-0.000002,0.,0.000005,0.000010,0.,0.,0.,0.,
  2417.      0.000002,0.,-0.000013,-0.000002,0.000002,0.,0.000002,0.,-0.000004,
  2418.      -0.000002,0.,0.,-0.000002,0.,0.000004,0.000002,0.,0.,0.,0.,
  2419.      -0.000003,0.,0.,0.,0.,0.,-0.000003,0.,0.,0.,0.,0.,0.,0.000005,
  2420.      0.000010,0.,0.,0.,0.,0.000003,0.,0.,0.,0.,0.,0.000003,0.
  2421. };
  2422. static const double TelestoTerms[12*5] =
  2423. {
  2424.     1,0,0,1,0,
  2425.     0,0,0,0,1,
  2426.     1,0,0,0,1,
  2427.     1,1,0,0,1,
  2428.     0,0,1,0,1,
  2429.     1,0,1,0,1,
  2430.     1,1,0,0,-1,
  2431.     0,0,1,0,-1,
  2432.     1,0,1,0,-1,
  2433.     0,1,0,0,1,
  2434.     0,1,0,0,-1,
  2435.     0,0,0,1,0
  2436. };
  2437. static const double TelestoAmps[12*6] =
  2438. {
  2439.     0.000002,0.000010,0.000019,0.,0.,0.,
  2440.     -0.001933,-0.000253,0.000320,0.001237,-0.005767,0.002904,
  2441.     0.000372,-0.001733,0.000873,0.006432,0.000842,-0.001066,
  2442.     -0.000002,0.,0.,0.000003,-0.000014,0.000007,
  2443.     -0.000006,0.000029,-0.000015,-0.000108,-0.000014,0.000018,
  2444.     -0.000033,-0.000004,0.000005,0.000020,-0.000097,0.000049,
  2445.     0.000007,0.,0.,0.,0.,0.,
  2446.     -0.000006,0.000029,-0.000015,-0.000108,-0.000014,0.000018,
  2447.     0.000032,0.000004,-0.000005,-0.000021,0.000097,-0.000049,
  2448.     0.,0.000002,0.,-0.000016,-0.000002,0.000003,
  2449.     0.,0.000007,-0.000003,0.,0.,0.,
  2450.     0.,0.,0.,0.000002,0.000010,0.000019
  2451. };
  2452. static const double CalypsoTerms[24*5] =
  2453. {
  2454.     1,0,0,1,0,
  2455.     0,0,0,0,1,
  2456.     0,1,0,0,1,
  2457.     1,0,0,0,1,
  2458.     1,1,0,0,1,
  2459.     0,0,1,0,1,
  2460.     1,0,1,0,1,
  2461.     0,0,2,0,1,
  2462.     0,1,0,0,-1,
  2463.     0,0,1,0,-1,
  2464.     1,0,1,0,-1,
  2465.     0,0,2,0,-1,
  2466.     1,0,2,0,1,
  2467.     1,1,0,0,-1,
  2468.     1,0,2,0,-1,
  2469.     0,0,1,1,0,
  2470.     0,0,1,-1,0,
  2471.     0,0,0,1,0,
  2472.     0,1,1,0,-1,
  2473.     0,1,-1,0,-1,
  2474.     1,1,1,0,-1,
  2475.     1,1,-1,0,-1,
  2476.     1,0,1,1,0,
  2477.     1,0,1,-1,0
  2478. };
  2479. static const double CalypsoAmps[24*6] =
  2480. {
  2481.     0.000005,0.000027,0.000052,0.,0.,0.,0.000651,0.001615,
  2482.     -0.000910,-0.006145,0.002170,-0.000542,-0.000011,0.000004,0.,0.,
  2483.     0.,0.,-0.001846,0.000652,-0.000163,-0.002166,-0.005375,0.003030,
  2484.     -0.000004,-0.000010,0.000006,0.,0.,0.,-0.000077,0.000028,
  2485.     -0.000007,-0.000092,-0.000225,0.000127,-0.000028,-0.000067,
  2486.     0.000038,0.000257,-0.000092,0.000023,-0.000002,0.,0.,0.000004,
  2487.     -0.000006,0.000003,-0.000004,0.,0.,-0.000009,-0.000022,0.000012,
  2488.     -0.000078,0.000027,-0.000007,-0.000089,-0.000225,0.000127,
  2489.     0.000027,0.000068,-0.000038,-0.000257,0.000089,-0.000022,
  2490.     -0.000002,0.,0.,0.000004,-0.000006,0.000003,0.,-0.000002,0.,
  2491.     0.000007,0.000003,-0.000002,0.,0.000003,-0.000002,-0.000025,
  2492.     0.000009,-0.000002,0.,0.000002,0.,-0.000007,-0.000003,0.000002,
  2493.     0.,0.,-0.000002,0.,0.,0.,0.,0.,-0.000002,0.,0.,0.,0.,0.,0.,
  2494.     0.000005,0.000027,0.000052,0.,0.,0.,0.000002,0.,0.,0.,0.,0.,
  2495.     0.000002,0.,0.,0.,0.,0.,0.,-0.000002,0.,0.,0.,0.,0.,-0.000002,0.,
  2496.     0.,0.,0.,0.,0.,0.000002,0.,0.,0.,0.,0.,-0.000002
  2497. };
  2498. struct HTC20Angles
  2499. {
  2500.     double nu1;
  2501.     double nu2;
  2502.     double nu3;
  2503.     double lambda;
  2504.     double phi1;
  2505.     double phi2;
  2506.     double phi3;
  2507.     double theta;
  2508. };
  2509. static const HTC20Angles HeleneAngles =
  2510.     2.29427177, 
  2511.     -0.00802443,
  2512.     2.29714724,
  2513.     2.29571726,
  2514.     3.27342548,
  2515.     1.30770422,
  2516.     0.77232982,
  2517.     3.07410251
  2518. };
  2519. static const HTC20Angles TelestoAngles =
  2520. {
  2521.     3.32489098,
  2522.     -0.00948045,
  2523.     3.33170385,
  2524.     3.32830561,
  2525.     6.24233590,
  2526.     4.62624497,
  2527.     0.04769409,
  2528.     3.24465053
  2529. };
  2530. static const HTC20Angles CalypsoAngles =
  2531. {
  2532.     -3.32489617,
  2533.     0.00946761,
  2534.     -3.33170262,
  2535.     3.32830561,
  2536.     5.41384760,
  2537.     1.36874776,
  2538.     5.64157287,
  2539.     3.25074880
  2540. };
  2541. class HTC20Orbit : public CachingOrbit
  2542. {
  2543.  public:
  2544.     HTC20Orbit(int _nTerms, const double* _args, const double* _amplitudes,
  2545.                const HTC20Angles& _angles,
  2546.                double _period, double _boundingRadius) :
  2547.         nTerms(_nTerms),
  2548.         args(_args),
  2549.         amplitudes(_amplitudes),
  2550.         angles(_angles),
  2551.         period(_period),
  2552.         boundingRadius(_boundingRadius)
  2553.     {
  2554.     }
  2555.     virtual ~HTC20Orbit() {};
  2556.     Point3d computePosition(double jd) const
  2557.     {
  2558.         double t = jd - astro::J2000 - (4156.0 / 86400.0);
  2559.         Point3d pos(0.0, 0.0, 0.0);
  2560.         for (int i = 0; i < nTerms; i++)
  2561.         {
  2562.             const double* row = args + i * 5;
  2563.             double ang = (row[1] * (angles.nu1 * t + angles.phi1) +
  2564.                           row[2] * (angles.nu2 * t + angles.phi2) +
  2565.                           row[3] * (angles.nu3 * t + angles.phi3) +
  2566.                           row[4] * (angles.lambda * t + angles.theta));
  2567.             double u = row[0] == 0.0 ? cos(ang) : sin(ang);
  2568.             pos += Vec3d(amplitudes[i * 6], amplitudes[i * 6 + 1], amplitudes[i * 6 + 2]) * u;
  2569.         }
  2570.         // Convert to Celestia's coordinate system
  2571.         return Point3d(pos.x, pos.z, -pos.y)  * astro::AUtoKilometers(1.0);
  2572.     }
  2573.     double getPeriod() const
  2574.     {
  2575.         return period;
  2576.     }
  2577.     double getBoundingRadius() const
  2578.     {
  2579.         return 354800 * BoundingRadiusSlack;
  2580.     }
  2581.  private:
  2582.     int nTerms;
  2583.     const double* args;
  2584.     const double* amplitudes;
  2585.     HTC20Angles angles;
  2586.     double period;
  2587.     double boundingRadius;
  2588.  public:
  2589.      static HTC20Orbit* CreateHeleneOrbit()
  2590.      {
  2591.          return new HTC20Orbit(24, HeleneTerms, HeleneAmps, HeleneAngles, 2.736915, 380000);
  2592.      }
  2593.      static HTC20Orbit* CreateTelestoOrbit()
  2594.      {
  2595.          return new HTC20Orbit(12, TelestoTerms, TelestoAmps, TelestoAngles, 1.887802, 300000);
  2596.      }
  2597.      static HTC20Orbit* CreateCalypsoOrbit()
  2598.      {
  2599.          return new HTC20Orbit(24, CalypsoTerms, CalypsoAmps, CalypsoAngles, 1.887803, 300000);
  2600.      }
  2601. };
  2602. class JPLEphOrbit : public CachingOrbit
  2603. {
  2604.  public:
  2605.     JPLEphOrbit(const JPLEphemeris& e,
  2606.                 JPLEphemItem _target,
  2607.                 JPLEphemItem _center,
  2608.                 double _period, double _boundingRadius) :
  2609.         ephem(e),
  2610.         target(_target),
  2611.         center(_center),
  2612.         period(_period),
  2613.         boundingRadius(_boundingRadius)
  2614.     {
  2615.     };
  2616.     virtual ~JPLEphOrbit() {};
  2617.     double getPeriod() const
  2618.     {
  2619.         return period;
  2620.     }
  2621.     double getBoundingRadius() const
  2622.     {
  2623.         return boundingRadius;
  2624.     }
  2625.     Point3d computePosition(double tjd) const
  2626.     {
  2627.         // Get the position relative to the Earth (for the Moon) or
  2628.         // the solar system barycenter.
  2629.         Point3d pos = ephem.getPlanetPosition(target, tjd);
  2630.         if (center == JPLEph_SSB && target != JPLEph_Moon)
  2631.         {
  2632.             // No translation necessary
  2633.         }
  2634.         else if (center == JPLEph_Earth && target == JPLEph_Moon)
  2635.         {
  2636.             // No translation necessary
  2637.         }
  2638.         else
  2639.         {
  2640.             Point3d centerPos = ephem.getPlanetPosition(center, tjd);
  2641.             if (target == JPLEph_Moon)
  2642.             {
  2643.                 pos += (ephem.getPlanetPosition(JPLEph_Earth, tjd) - Point3d(0.0, 0.0, 0.0));
  2644.             }
  2645.             if (center == JPLEph_Moon)
  2646.             {
  2647.                 centerPos += (ephem.getPlanetPosition(JPLEph_Earth, tjd) - Point3d(0.0, 0.0, 0.0));
  2648.             }
  2649.             // Compute the position of target relative to the center
  2650.             pos = Point3d(pos.x - centerPos.x,
  2651.                           pos.y - centerPos.y,
  2652.                           pos.z - centerPos.z);
  2653.         }
  2654.         // Rotate from the J2000 mean equator to the ecliptic
  2655.         pos = pos * Mat3d::xrotation(astro::J2000Obliquity);
  2656.         // Convert to Celestia's coordinate system
  2657.         return Point3d(pos.x, pos.z, -pos.y);
  2658.     }
  2659.  private:
  2660.     const JPLEphemeris& ephem;
  2661.     JPLEphemItem target;
  2662.     JPLEphemItem center;
  2663.     double period;
  2664.     double boundingRadius;
  2665. };
  2666. static Orbit* CreateJPLEphOrbit(JPLEphemItem target,
  2667.                                 JPLEphemItem center,
  2668.                                 double period,
  2669.                                 double boundingRadius)
  2670. {
  2671.     if (jpleph == NULL)
  2672.         return NULL;
  2673.     Orbit* o = new JPLEphOrbit(*jpleph, target, center, period, boundingRadius);
  2674.     return new MixedOrbit(o,
  2675.                           jpleph->getStartDate(),
  2676.                           jpleph->getEndDate(),
  2677.                           astro::SolarMass);
  2678. }
  2679. static double yearToJD(int year)
  2680. {
  2681.     return (double) astro::Date(year, 1, 1);
  2682. }
  2683. Orbit* GetCustomOrbit(const string& name)
  2684. {
  2685.     // Attempt to load JPL ephemeris data if we haven't tried already
  2686.     if (!jplephInitialized)
  2687.     {
  2688.         jplephInitialized = true;
  2689.         ifstream in("data/jpleph.dat", ios::in | ios::binary);
  2690.         if (in.good())
  2691.             jpleph = JPLEphemeris::load(in);
  2692.         if (jpleph != NULL)
  2693.         {
  2694.             clog << "Loaded DE" << jpleph->getDENumber() <<
  2695.                 " ephemeris. Valid from JD" <<
  2696.                 setprecision(8) <<
  2697.                 jpleph->getStartDate() << " to JD" <<
  2698.                 jpleph->getEndDate() << 'n';
  2699.         }
  2700.     }
  2701.     if (name == "mercury")
  2702.         return new MixedOrbit(new MercuryOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2703.     if (name == "venus")
  2704.         return new MixedOrbit(new VenusOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2705.     if (name == "earth")
  2706.         return new MixedOrbit(new EarthOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2707.     if (name == "moon")
  2708.         return new MixedOrbit(new LunarOrbit(), yearToJD(-2000), yearToJD(4000), astro::EarthMass + astro::LunarMass);
  2709.     if (name == "mars")
  2710.         return new MixedOrbit(new MarsOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2711.     if (name == "jupiter")
  2712.         return new MixedOrbit(new JupiterOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2713.     if (name == "saturn")
  2714.         return new MixedOrbit(new SaturnOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2715.     if (name == "uranus")
  2716.         return new MixedOrbit(new UranusOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2717.     if (name == "neptune")
  2718.         return new MixedOrbit(new NeptuneOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2719.     if (name == "pluto")
  2720.         return new MixedOrbit(new PlutoOrbit(), yearToJD(-4000), yearToJD(4000), astro::SolarMass);
  2721.     // Two styles of custom orbit name are permitted for JPL ephemeris orbits.
  2722.     // The preferred is <ephemeris>-<object>, e.g. jpl-mercury. But the reverse
  2723.     // form is still supported for backward compatibility.
  2724.     if (name == "jpl-mercury-sun" || name == "mercury-jpl")
  2725.         return CreateJPLEphOrbit(JPLEph_Mercury, JPLEph_Sun,  0.2408  * 365.25, 6.0e7);
  2726.     if (name == "jpl-venus-sun" || name == "venus-jpl")
  2727.         return CreateJPLEphOrbit(JPLEph_Venus,   JPLEph_Sun,  0.6152  * 365.25, 1.0e8);
  2728.     if (name == "jpl-earth-sun" || name == "earth-jpl")
  2729.         return CreateJPLEphOrbit(JPLEph_Earth,   JPLEph_Sun,            365.25, 1.6e8);
  2730.     if (name == "jpl-mars-sun" || name == "mars-jpl")
  2731.         return CreateJPLEphOrbit(JPLEph_Mars,    JPLEph_Sun,  1.8809 * 365.25, 2.4e8);
  2732.     if (name == "jpl-jupiter-sun" || name == "jupiter-jpl")
  2733.         return CreateJPLEphOrbit(JPLEph_Jupiter, JPLEph_Sun, 11.86   * 365.25, 8.0e8);
  2734.     if (name == "jpl-saturn-sun" || name == "saturn-jpl")
  2735.         return CreateJPLEphOrbit(JPLEph_Saturn,  JPLEph_Sun, 29.4577 * 365.25, 1.5e9);
  2736.     if (name == "jpl-uranus-sun" || name == "uranus-jpl")
  2737.         return CreateJPLEphOrbit(JPLEph_Uranus,  JPLEph_Sun, 84.0139 * 365.25, 3.0e9);
  2738.     if (name == "jpl-neptune-sun" || name == "neptune-jpl")
  2739.         return CreateJPLEphOrbit(JPLEph_Neptune, JPLEph_Sun, 164.793  * 365.25, 4.7e9);
  2740.     if (name == "jpl-pluto-sun" || name == "pluto-jpl")
  2741.         return CreateJPLEphOrbit(JPLEph_Pluto,   JPLEph_Sun, 248.54   * 365.25, 6.0e9);
  2742.     if (name == "jpl-mercury-ssb")
  2743.         return CreateJPLEphOrbit(JPLEph_Mercury, JPLEph_SSB,  0.2408  * 365.25, 6.0e7);
  2744.     if (name == "jpl-venus-ssb")
  2745.         return CreateJPLEphOrbit(JPLEph_Venus,   JPLEph_SSB,  0.6152  * 365.25, 1.0e8);
  2746.     if (name == "jpl-earth-ssb")
  2747.         return CreateJPLEphOrbit(JPLEph_Earth,   JPLEph_SSB,            365.25, 1.6e8);
  2748.     if (name == "jpl-mars-ssb")
  2749.         return CreateJPLEphOrbit(JPLEph_Mars,    JPLEph_SSB,  1.8809 * 365.25, 2.4e8);
  2750.     if (name == "jpl-jupiter-ssb")
  2751.         return CreateJPLEphOrbit(JPLEph_Jupiter, JPLEph_SSB, 11.86   * 365.25, 8.0e8);
  2752.     if (name == "jpl-saturn-ssb")
  2753.         return CreateJPLEphOrbit(JPLEph_Saturn,  JPLEph_SSB, 29.4577 * 365.25, 1.5e9);
  2754.     if (name == "jpl-uranus-ssb")
  2755.         return CreateJPLEphOrbit(JPLEph_Uranus,  JPLEph_SSB, 84.0139 * 365.25, 3.0e9);
  2756.     if (name == "jpl-neptune-ssb")
  2757.         return CreateJPLEphOrbit(JPLEph_Neptune, JPLEph_SSB, 164.793  * 365.25, 4.7e9);
  2758.     if (name == "jpl-pluto-ssb")
  2759.         return CreateJPLEphOrbit(JPLEph_Pluto,   JPLEph_SSB, 248.54   * 365.25, 6.0e9);
  2760.     // JPL ephemerides for Earth-Moon system
  2761.     if (name == "jpl-emb-sun") // Earth-Moon barycenter, heliocentric
  2762.         return CreateJPLEphOrbit(JPLEph_EarthMoonBary, JPLEph_Sun,      365.25, 1.6e8);
  2763.     if (name == "jpl-emb-ssb") // Earth-Moon barycenter, relative to ssb
  2764.         return CreateJPLEphOrbit(JPLEph_EarthMoonBary, JPLEph_SSB,      365.25, 1.6e8);
  2765.     if (name == "jpl-moon-emb") // Moon, barycentric
  2766.         return CreateJPLEphOrbit(JPLEph_Moon, JPLEph_EarthMoonBary,      27.321661,        5.0e5);
  2767.     if (name == "jpl-moon-earth") // Moon, geocentric
  2768.         return CreateJPLEphOrbit(JPLEph_Moon,    JPLEph_Earth,           27.321661,        5.0e5);
  2769.     if (name == "jpl-earth-emb") // Earth, barycentric
  2770.         return CreateJPLEphOrbit(JPLEph_Earth,   JPLEph_EarthMoonBary,   27.321,           1.0e5);
  2771.                                  
  2772.     if (name == "jpl-sun-ssb")   // Position of Sun relative to SSB
  2773.         return CreateJPLEphOrbit(JPLEph_Sun,     JPLEph_SSB,             11.861773 * 365.25, 2000000);
  2774.     // HTC2.0 ephemeris for Saturnian satellites in Lagrange points of Tethys and Dione
  2775.     if (name == "htc20-helene")
  2776.         return HTC20Orbit::CreateHeleneOrbit();
  2777.     if (name == "htc20-telesto")
  2778.         return HTC20Orbit::CreateTelestoOrbit();
  2779.     if (name == "htc20-calypso")
  2780.         return HTC20Orbit::CreateCalypsoOrbit();
  2781.     if (name == "phobos")
  2782.         return new PhobosOrbit();
  2783.     if (name == "deimos")
  2784.         return new DeimosOrbit();
  2785.     if (name == "io")
  2786.         return new IoOrbit();
  2787.     if (name == "europa")
  2788.         return new EuropaOrbit();
  2789.     if (name == "ganymede")
  2790.         return new GanymedeOrbit();
  2791.     if (name == "callisto")
  2792.         return new CallistoOrbit();
  2793.     if (name == "mimas")
  2794.         return new MimasOrbit();
  2795.     if (name == "enceladus")
  2796.         return new EnceladusOrbit();
  2797.     if (name == "tethys")
  2798.         return new TethysOrbit();
  2799.     if (name == "dione")
  2800.         return new DioneOrbit();
  2801.     if (name == "rhea")
  2802.         return new RheaOrbit();
  2803.     if (name == "titan")
  2804.         return new TitanOrbit();
  2805.     if (name == "hyperion")
  2806.         return new HyperionOrbit();
  2807.     if (name == "iapetus")
  2808.         return new IapetusOrbit();
  2809.     if (name == "phoebe")
  2810.         return new PhoebeOrbit();
  2811.     if (name == "miranda")
  2812.         return CreateUranianSatelliteOrbit(1);
  2813.     if (name == "ariel")
  2814.         return CreateUranianSatelliteOrbit(2);
  2815.     if (name == "umbriel")
  2816.         return CreateUranianSatelliteOrbit(3);
  2817.     if (name == "titania")
  2818.         return CreateUranianSatelliteOrbit(4);
  2819.     if (name == "oberon")
  2820.         return CreateUranianSatelliteOrbit(5);
  2821.     if (name == "triton")
  2822.         return new TritonOrbit();
  2823.     else
  2824.         return CreateVSOP87Orbit(name);
  2825. }