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

OpenGL

开发平台:

Visual C++

  1. // orbit.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 <functional>
  10. #include <algorithm>
  11. #include <cmath>
  12. #include <cassert>
  13. #include <celmath/mathlib.h>
  14. #include <celmath/solve.h>
  15. #include "astro.h"
  16. #include "orbit.h"
  17. #include "body.h"
  18. using namespace std;
  19. // Orbital velocity is computed by differentiation for orbits that don't
  20. // override velocityAtTime().
  21. static const double ORBITAL_VELOCITY_DIFF_DELTA = 1.0 / 1440.0;
  22. EllipticalOrbit::EllipticalOrbit(double _pericenterDistance,
  23.                                  double _eccentricity,
  24.                                  double _inclination,
  25.                                  double _ascendingNode,
  26.                                  double _argOfPeriapsis,
  27.                                  double _meanAnomalyAtEpoch,
  28.                                  double _period,
  29.                                  double _epoch) :
  30.     pericenterDistance(_pericenterDistance),
  31.     eccentricity(_eccentricity),
  32.     inclination(_inclination),
  33.     ascendingNode(_ascendingNode),
  34.     argOfPeriapsis(_argOfPeriapsis),
  35.     meanAnomalyAtEpoch(_meanAnomalyAtEpoch),
  36.     period(_period),
  37.     epoch(_epoch)
  38. {
  39.     orbitPlaneRotation = (Mat3d::zrotation(_ascendingNode) *
  40.                           Mat3d::xrotation(_inclination) *
  41.                           Mat3d::zrotation(_argOfPeriapsis));
  42. }
  43. // Standard iteration for solving Kepler's Equation
  44. struct SolveKeplerFunc1 : public unary_function<double, double>
  45. {
  46.     double ecc;
  47.     double M;
  48.     SolveKeplerFunc1(double _ecc, double _M) : ecc(_ecc), M(_M) {};
  49.     double operator()(double x) const
  50.     {
  51.         return M + ecc * sin(x);
  52.     }
  53. };
  54. // Faster converging iteration for Kepler's Equation; more efficient
  55. // than above for orbits with eccentricities greater than 0.3.  This
  56. // is from Jean Meeus's _Astronomical Algorithms_ (2nd ed), p. 199
  57. struct SolveKeplerFunc2 : public unary_function<double, double>
  58. {
  59.     double ecc;
  60.     double M;
  61.     SolveKeplerFunc2(double _ecc, double _M) : ecc(_ecc), M(_M) {};
  62.     double operator()(double x) const
  63.     {
  64.         return x + (M + ecc * sin(x) - x) / (1 - ecc * cos(x));
  65.     }
  66. };
  67. struct SolveKeplerLaguerreConway : public unary_function<double, double>
  68. {
  69.     double ecc;
  70.     double M;
  71.     SolveKeplerLaguerreConway(double _ecc, double _M) : ecc(_ecc), M(_M) {};
  72.     double operator()(double x) const
  73.     {
  74.         double s = ecc * sin(x);
  75.         double c = ecc * cos(x);
  76.         double f = x - s - M;
  77.         double f1 = 1 - c;
  78.         double f2 = s;
  79.         x += -5 * f / (f1 + sign(f1) * sqrt(abs(16 * f1 * f1 - 20 * f * f2)));
  80.         return x;
  81.     }
  82. };
  83. struct SolveKeplerLaguerreConwayHyp : public unary_function<double, double>
  84. {
  85.     double ecc;
  86.     double M;
  87.     SolveKeplerLaguerreConwayHyp(double _ecc, double _M) : ecc(_ecc), M(_M) {};
  88.     double operator()(double x) const
  89.     {
  90.         double s = ecc * sinh(x);
  91.         double c = ecc * cosh(x);
  92.         double f = s - x - M;
  93.         double f1 = c - 1;
  94.         double f2 = s;
  95.         x += -5 * f / (f1 + sign(f1) * sqrt(abs(16 * f1 * f1 - 20 * f * f2)));
  96.         return x;
  97.     }
  98. };
  99. typedef pair<double, double> Solution;
  100. Vec3d Orbit::velocityAtTime(double tdb) const
  101. {
  102. Point3d p0 = positionAtTime(tdb);
  103. Point3d p1 = positionAtTime(tdb + ORBITAL_VELOCITY_DIFF_DELTA);
  104. return (p1 - p0) * (1.0 / ORBITAL_VELOCITY_DIFF_DELTA);
  105. }
  106. double EllipticalOrbit::eccentricAnomaly(double M) const
  107. {
  108.     if (eccentricity == 0.0)
  109.     {
  110.         // Circular orbit
  111.         return M;
  112.     }
  113.     else if (eccentricity < 0.2)
  114.     {
  115.         // Low eccentricity, so use the standard iteration technique
  116.         Solution sol = solve_iteration_fixed(SolveKeplerFunc1(eccentricity, M), M, 5);
  117.         return sol.first;
  118.     }
  119.     else if (eccentricity < 0.9)
  120.     {
  121.         // Higher eccentricity elliptical orbit; use a more complex but
  122.         // much faster converging iteration.
  123.         Solution sol = solve_iteration_fixed(SolveKeplerFunc2(eccentricity, M), M, 6);
  124.         // Debugging
  125.         // printf("ecc: %f, error: %f masn",
  126.         //        eccentricity, radToDeg(sol.second) * 3600000);
  127.         return sol.first;
  128.     }
  129.     else if (eccentricity < 1.0)
  130.     {
  131.         // Extremely stable Laguerre-Conway method for solving Kepler's
  132.         // equation.  Only use this for high-eccentricity orbits, as it
  133.         // requires more calcuation.
  134.         double E = M + 0.85 * eccentricity * sign(sin(M));
  135.         Solution sol = solve_iteration_fixed(SolveKeplerLaguerreConway(eccentricity, M), E, 8);
  136.         return sol.first;
  137.     }
  138.     else if (eccentricity == 1.0)
  139.     {
  140.         // Nearly parabolic orbit; very common for comets
  141.         // TODO: handle this
  142.         return M;
  143.     }
  144.     else
  145.     {
  146.         // Laguerre-Conway method for hyperbolic (ecc > 1) orbits.
  147.         double E = log(2 * M / eccentricity + 1.85);
  148.         Solution sol = solve_iteration_fixed(SolveKeplerLaguerreConwayHyp(eccentricity, M), E, 30);
  149.         return sol.first;
  150.     }
  151. }
  152. // Compute the position at the specified eccentric
  153. // anomaly E.
  154. Point3d EllipticalOrbit::positionAtE(double E) const
  155. {
  156.     double x, y;
  157.     if (eccentricity < 1.0)
  158.     {
  159.         double a = pericenterDistance / (1.0 - eccentricity);
  160.         x = a * (cos(E) - eccentricity);
  161.         y = a * sqrt(1 - square(eccentricity)) * sin(E);
  162.     }
  163.     else if (eccentricity > 1.0)
  164.     {
  165.         double a = pericenterDistance / (1.0 - eccentricity);
  166.         x = -a * (eccentricity - cosh(E));
  167.         y = -a * sqrt(square(eccentricity) - 1) * sinh(E);
  168.     }
  169.     else
  170.     {
  171.         // TODO: Handle parabolic orbits
  172.         x = 0.0;
  173.         y = 0.0;
  174.     }
  175.     Point3d p = orbitPlaneRotation * Point3d(x, y, 0);
  176.     // Convert to Celestia's internal coordinate system
  177.     return Point3d(p.x, p.z, -p.y);
  178. }
  179. // Compute the velocity at the specified eccentric
  180. // anomaly E.
  181. Vec3d EllipticalOrbit::velocityAtE(double E) const
  182. {
  183.     double x, y;
  184.     if (eccentricity < 1.0)
  185.     {
  186.         double a = pericenterDistance / (1.0 - eccentricity);
  187.         double sinE = sin(E);
  188.         double cosE = cos(E);
  189.         
  190.         x = -a * sinE;
  191.         y =  a * sqrt(1 - square(eccentricity)) * cosE;
  192.         
  193.         double meanMotion = 2.0 * PI / period;
  194.         double edot = meanMotion / (1 - eccentricity * cosE);
  195.         x *= edot;
  196.         y *= edot;
  197.     }
  198.     else if (eccentricity > 1.0)
  199.     {
  200.         double a = pericenterDistance / (1.0 - eccentricity);
  201.         x = -a * (eccentricity - cosh(E));
  202.         y = -a * sqrt(square(eccentricity) - 1) * sinh(E);
  203.     }
  204.     else
  205.     {
  206.         // TODO: Handle parabolic orbits
  207.         x = 0.0;
  208.         y = 0.0;
  209.     }
  210.     Vec3d v = orbitPlaneRotation * Vec3d(x, y, 0);
  211.     // Convert to Celestia's coordinate system
  212.     return Vec3d(v.x, v.z, -v.y);
  213. }
  214. // Return the offset from the center
  215. Point3d EllipticalOrbit::positionAtTime(double t) const
  216. {
  217.     t = t - epoch;
  218.     double meanMotion = 2.0 * PI / period;
  219.     double meanAnomaly = meanAnomalyAtEpoch + t * meanMotion;
  220.     double E = eccentricAnomaly(meanAnomaly);
  221.     return positionAtE(E);
  222. }
  223. Vec3d EllipticalOrbit::velocityAtTime(double t) const
  224. {
  225.     t = t - epoch;
  226.     double meanMotion = 2.0 * PI / period;
  227.     double meanAnomaly = meanAnomalyAtEpoch + t * meanMotion;
  228.     double E = eccentricAnomaly(meanAnomaly);
  229.     return velocityAtE(E);
  230. }
  231. double EllipticalOrbit::getPeriod() const
  232. {
  233.     return period;
  234. }
  235. double EllipticalOrbit::getBoundingRadius() const
  236. {
  237.     // TODO: watch out for unbounded parabolic and hyperbolic orbits
  238.     return pericenterDistance * ((1.0 + eccentricity) / (1.0 - eccentricity));
  239. }
  240. void EllipticalOrbit::sample(double, double t, int nSamples,
  241.                              OrbitSampleProc& proc) const
  242. {
  243.     if (eccentricity >= 1.0)
  244.     {
  245.         double dE = 1 * PI / (double) nSamples;
  246.         for (int i = 0; i < nSamples; i++)
  247.             proc.sample(t, positionAtE(dE * i));
  248.     }
  249.     else
  250.     {
  251.         // Adaptive sampling of the orbit; more samples in regions of high curvature.
  252.         // nSamples is the number of samples that will be used for a perfectly circular
  253.         // orbit. Elliptical orbits will have regions of higher curvature thar require
  254.         // additional sample points.
  255.         double E = 0.0;
  256.         double dE = 2 * PI / (double) nSamples;
  257.         double w = (1 - square(eccentricity));
  258.         double M0 = E - eccentricity * sin(E);
  259.         
  260.         while (E < 2 * PI)
  261.         {
  262.             // Compute the time tag for this sample
  263.             double M = E - eccentricity * sin(E);            // Mean anomaly from ecc anomaly
  264.             double tsamp = t + (M - M0) * period / (2 * PI); // Time from mean anomaly
  265.             
  266.             proc.sample(tsamp, positionAtE(E));
  267.             // Compute the curvature
  268.             double k = w * pow(square(sin(E)) + w * w * square(cos(E)), -1.5);
  269.             // Step amount based on curvature--constrain it so that we don't end up
  270.             // taking too many samples anywhere. Clamping the curvature to 20 effectively
  271.             // limits the numbers of samples to 3*nSamples
  272.             E += dE / max(min(k, 20.0), 1.0);
  273.         }
  274.     }
  275. }
  276. CachingOrbit::CachingOrbit() :
  277. lastTime(-1.0e30),
  278. positionCacheValid(false),
  279. velocityCacheValid(false)
  280. {
  281. }
  282. CachingOrbit::~CachingOrbit()
  283. {
  284. }
  285. Point3d CachingOrbit::positionAtTime(double jd) const
  286. {
  287.     if (jd != lastTime)
  288.     {
  289.         lastTime = jd;
  290.         lastPosition = computePosition(jd);
  291. positionCacheValid = true;
  292. velocityCacheValid = false;
  293.     }
  294. else if (!positionCacheValid)
  295. {
  296. lastPosition = computePosition(jd);
  297. positionCacheValid = true;
  298. }
  299.     return lastPosition;
  300. }
  301. Vec3d CachingOrbit::velocityAtTime(double jd) const
  302. {
  303. if (jd != lastTime)
  304. {
  305. lastVelocity = computeVelocity(jd);
  306. lastTime = jd;  // must be set *after* call to computeVelocity
  307. positionCacheValid = false;
  308. velocityCacheValid = true;
  309. }
  310. else if (!velocityCacheValid)
  311. {
  312. lastVelocity = computeVelocity(jd);
  313. velocityCacheValid = true;
  314. }
  315. return lastVelocity;
  316. }
  317. /*! Calculate the velocity at the specified time (units are
  318.  *  kilometers / Julian day.) The default implementation just
  319.  *  differentiates the position.
  320.  */
  321. Vec3d CachingOrbit::computeVelocity(double jd) const
  322. {
  323. // Compute the velocity by differentiating.
  324. Point3d p0 = positionAtTime(jd);
  325. // Call computePosition() instead of positionAtTime() so that we
  326. // don't affect the cached value. 
  327. // TODO: check the valid ranges of the orbit to make sure that
  328. // jd+dt is still in range.
  329. Point3d p1 = computePosition(jd + ORBITAL_VELOCITY_DIFF_DELTA);
  330. return (p1 - p0) * (1.0 / ORBITAL_VELOCITY_DIFF_DELTA);
  331. }
  332. void CachingOrbit::sample(double start, double t, int nSamples,
  333.                           OrbitSampleProc& proc) const
  334. {
  335.     double dt = t / (double) nSamples;
  336.     for (int i = 0; i < nSamples; i++)
  337.         proc.sample(start + dt * i, positionAtTime(start + dt * i));
  338. }
  339. static EllipticalOrbit* StateVectorToOrbit(const Point3d& position,
  340.                                            const Vec3d& v,
  341.                                            double mass,
  342.                                            double t)
  343. {
  344.     Vec3d R = position - Point3d(0.0, 0.0, 0.0);
  345.     Vec3d L = R ^ v;
  346.     double magR = R.length();
  347.     double magL = L.length();
  348.     double magV = v.length();
  349.     L *= (1.0 / magL);
  350.     Vec3d W = L ^ (R / magR);
  351.     double G = astro::G * 1e-9; // convert from meters to kilometers
  352.     double GM = G * mass;
  353.     // Compute the semimajor axis
  354.     double a = 1.0 / (2.0 / magR - square(magV) / GM);
  355.     // Compute the eccentricity
  356.     double p = square(magL) / GM;
  357.     double q = R * v;
  358.     double ex = 1.0 - magR / a;
  359.     double ey = q / sqrt(a * GM);
  360.     double e = sqrt(ex * ex + ey * ey);
  361.     // Compute the mean anomaly
  362.     double E = atan2(ey, ex);
  363.     double M = E - e * sin(E);
  364.     // Compute the inclination
  365.     double cosi = L * Vec3d(0, 1.0, 0);
  366.     double i = 0.0;
  367.     if (cosi < 1.0)
  368.         i = acos(cosi);
  369.     // Compute the longitude of ascending node
  370.     double Om = atan2(L.x, L.z);
  371.     // Compute the argument of pericenter
  372.     Vec3d U = R / magR;
  373.     double s_nu = (v * U) * sqrt(p / GM);
  374.     double c_nu = (v * W) * sqrt(p / GM) - 1;
  375.     s_nu /= e;
  376.     c_nu /= e;
  377.     Vec3d P = U * c_nu - W * s_nu;
  378.     Vec3d Q = U * s_nu + W * c_nu;
  379.     double om = atan2(P.y, Q.y);
  380.     // Compute the period
  381.     double T = 2 * PI * sqrt(cube(a) / GM);
  382.     T = T / 86400.0; // Convert from seconds to days
  383.     return new EllipticalOrbit(a * (1 - e), e, i, Om, om, M, T, t);
  384. }
  385. MixedOrbit::MixedOrbit(Orbit* orbit, double t0, double t1, double mass) :
  386.     primary(orbit),
  387.     afterApprox(NULL),
  388.     beforeApprox(NULL),
  389.     begin(t0),
  390.     end(t1),
  391.     boundingRadius(0.0)
  392. {
  393.     assert(t1 > t0);
  394.     assert(orbit != NULL);
  395.     double dt = 1.0 / 1440.0; // 1 minute
  396.     Point3d p0 = orbit->positionAtTime(t0);
  397.     Point3d p1 = orbit->positionAtTime(t1);
  398.     Vec3d v0 = (orbit->positionAtTime(t0 + dt) - p0) / (86400 * dt);
  399.     Vec3d v1 = (orbit->positionAtTime(t1 + dt) - p1) / (86400 * dt);
  400.     beforeApprox = StateVectorToOrbit(p0, v0, mass, t0);
  401.     afterApprox = StateVectorToOrbit(p1, v1, mass, t1);
  402.     boundingRadius = beforeApprox->getBoundingRadius();
  403.     if (primary->getBoundingRadius() > boundingRadius)
  404.         boundingRadius = primary->getBoundingRadius();
  405.     if (afterApprox->getBoundingRadius() > boundingRadius)
  406.         boundingRadius = afterApprox->getBoundingRadius();
  407. }
  408. MixedOrbit::~MixedOrbit()
  409. {
  410.     if (primary != NULL)
  411.         delete primary;
  412.     if (beforeApprox != NULL)
  413.         delete beforeApprox;
  414.     if (afterApprox != NULL)
  415.         delete afterApprox;
  416. }
  417. Point3d MixedOrbit::positionAtTime(double jd) const
  418. {
  419.     if (jd < begin)
  420.         return beforeApprox->positionAtTime(jd);
  421.     else if (jd < end)
  422.         return primary->positionAtTime(jd);
  423.     else
  424.         return afterApprox->positionAtTime(jd);
  425. }
  426. Vec3d MixedOrbit::velocityAtTime(double jd) const
  427. {
  428.     if (jd < begin)
  429.         return beforeApprox->velocityAtTime(jd);
  430.     else if (jd < end)
  431.         return primary->velocityAtTime(jd);
  432.     else
  433.         return afterApprox->velocityAtTime(jd);
  434. }
  435. double MixedOrbit::getPeriod() const
  436. {
  437.     return primary->getPeriod();
  438. }
  439. double MixedOrbit::getBoundingRadius() const
  440. {
  441.     return boundingRadius;
  442. }
  443. void MixedOrbit::sample(double t0, double t1, int nSamples,
  444.                         OrbitSampleProc& proc) const
  445. {
  446.     Orbit* o;
  447.     if (t0 < begin)
  448.         o = beforeApprox;
  449.     else if (t0 < end)
  450.         o = primary;
  451.     else
  452.         o = afterApprox;
  453.     o->sample(t0, t1, nSamples, proc);
  454. }
  455. /*** FixedOrbit ***/
  456. FixedOrbit::FixedOrbit(const Point3d& pos) :
  457.     position(pos)
  458. {
  459. }
  460. FixedOrbit::~FixedOrbit()
  461. {
  462. }
  463. Point3d
  464. FixedOrbit::positionAtTime(double /*tjd*/) const
  465. {
  466.     return position;
  467. }
  468. bool
  469. FixedOrbit::isPeriodic() const
  470. {
  471.     return false;
  472. }
  473. double
  474. FixedOrbit::getPeriod() const
  475. {
  476.     return 1.0;
  477. }
  478. double
  479. FixedOrbit::getBoundingRadius() const
  480. {
  481.     return position.distanceFromOrigin() * 1.1;
  482. }
  483. void
  484. FixedOrbit::sample(double, double, int, OrbitSampleProc&) const
  485. {
  486.     /*
  487.     for (int i = 0; i < nSamples; i++)
  488.         proc.sample(t, position);
  489.     */
  490. }
  491. /*** SynchronousOrbit ***/
  492. // TODO: eliminate this class once body-fixed reference frames are implemented
  493. SynchronousOrbit::SynchronousOrbit(const Body& _body,
  494.                                    const Point3d& _position) :
  495.     body(_body),
  496.     position(_position)
  497. {
  498. }
  499. SynchronousOrbit::~SynchronousOrbit()
  500. {
  501. }
  502. Point3d SynchronousOrbit::positionAtTime(double jd) const
  503. {
  504.     Quatd q = body.getEquatorialToBodyFixed(jd);
  505.     return position * q.toMatrix3();
  506. }
  507. double SynchronousOrbit::getPeriod() const
  508. {
  509.     return body.getRotationModel(0.0)->getPeriod();
  510. }
  511. double SynchronousOrbit::getBoundingRadius() const
  512. {
  513.     return position.distanceFromOrigin();
  514. }
  515. void SynchronousOrbit::sample(double, double, int, OrbitSampleProc&) const
  516. {
  517.     // Empty method--we never want to show a synchronous orbit.
  518. }