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

orbit.cpp

Go to the documentation of this file.
00001 // orbit.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <functional>
00011 #include <algorithm>
00012 #include <cmath>
00013 #include <cassert>
00014 #include <celmath/mathlib.h>
00015 #include <celmath/solve.h>
00016 #include "astro.h"
00017 #include "orbit.h"
00018 #include "body.h"
00019 
00020 using namespace std;
00021 
00022 
00023 EllipticalOrbit::EllipticalOrbit(double _pericenterDistance,
00024                                  double _eccentricity,
00025                                  double _inclination,
00026                                  double _ascendingNode,
00027                                  double _argOfPeriapsis,
00028                                  double _meanAnomalyAtEpoch,
00029                                  double _period,
00030                                  double _epoch) :
00031     pericenterDistance(_pericenterDistance),
00032     eccentricity(_eccentricity),
00033     inclination(_inclination),
00034     ascendingNode(_ascendingNode),
00035     argOfPeriapsis(_argOfPeriapsis),
00036     meanAnomalyAtEpoch(_meanAnomalyAtEpoch),
00037     period(_period),
00038     epoch(_epoch)
00039 {
00040 }
00041 
00042 
00043 // Standard iteration for solving Kepler's Equation
00044 struct SolveKeplerFunc1 : public unary_function<double, double>
00045 {
00046     double ecc;
00047     double M;
00048 
00049     SolveKeplerFunc1(double _ecc, double _M) : ecc(_ecc), M(_M) {};
00050 
00051     double operator()(double x) const
00052     {
00053         return M + ecc * sin(x);
00054     }
00055 };
00056 
00057 
00058 // Faster converging iteration for Kepler's Equation; more efficient
00059 // than above for orbits with eccentricities greater than 0.3.  This
00060 // is from Jean Meeus's _Astronomical Algorithms_ (2nd ed), p. 199
00061 struct SolveKeplerFunc2 : public unary_function<double, double>
00062 {
00063     double ecc;
00064     double M;
00065 
00066     SolveKeplerFunc2(double _ecc, double _M) : ecc(_ecc), M(_M) {};
00067 
00068     double operator()(double x) const
00069     {
00070         return x + (M + ecc * sin(x) - x) / (1 - ecc * cos(x));
00071     }
00072 };
00073 
00074 
00075 struct SolveKeplerLaguerreConway : public unary_function<double, double>
00076 {
00077     double ecc;
00078     double M;
00079 
00080     SolveKeplerLaguerreConway(double _ecc, double _M) : ecc(_ecc), M(_M) {};
00081 
00082     double operator()(double x) const
00083     {
00084         double s = ecc * sin(x);
00085         double c = ecc * cos(x);
00086         double f = x - s - M;
00087         double f1 = 1 - c;
00088         double f2 = s;
00089         x += -5 * f / (f1 + sign(f1) * sqrt(abs(16 * f1 * f1 - 20 * f * f2)));
00090 
00091         return x;
00092     }
00093 };
00094 
00095 struct SolveKeplerLaguerreConwayHyp : public unary_function<double, double>
00096 {
00097     double ecc;
00098     double M;
00099 
00100     SolveKeplerLaguerreConwayHyp(double _ecc, double _M) : ecc(_ecc), M(_M) {};
00101 
00102     double operator()(double x) const
00103     {
00104         double s = ecc * sinh(x);
00105         double c = ecc * cosh(x);
00106         double f = s - x - M;
00107         double f1 = c - 1;
00108         double f2 = s;
00109         x += -5 * f / (f1 + sign(f1) * sqrt(abs(16 * f1 * f1 - 20 * f * f2)));
00110 
00111         return x;
00112     }
00113 };
00114 
00115 typedef pair<double, double> Solution;
00116 
00117 
00118 double EllipticalOrbit::eccentricAnomaly(double M) const
00119 {
00120     if (eccentricity == 0.0)
00121     {
00122         // Circular orbit
00123         return M;
00124     }
00125     else if (eccentricity < 0.2)
00126     {
00127         // Low eccentricity, so use the standard iteration technique
00128         Solution sol = solve_iteration_fixed(SolveKeplerFunc1(eccentricity, M), M, 5);
00129         return sol.first;
00130     }
00131     else if (eccentricity < 0.9)
00132     {
00133         // Higher eccentricity elliptical orbit; use a more complex but
00134         // much faster converging iteration.
00135         Solution sol = solve_iteration_fixed(SolveKeplerFunc2(eccentricity, M), M, 6);
00136         // Debugging
00137         // printf("ecc: %f, error: %f mas\n",
00138         //        eccentricity, radToDeg(sol.second) * 3600000);
00139         return sol.first;
00140     }
00141     else if (eccentricity < 1.0)
00142     {
00143         // Extremely stable Laguerre-Conway method for solving Kepler's
00144         // equation.  Only use this for high-eccentricity orbits, as it
00145         // requires more calcuation.
00146         double E = M + 0.85 * eccentricity * sign(sin(M));
00147         Solution sol = solve_iteration_fixed(SolveKeplerLaguerreConway(eccentricity, M), E, 8);
00148         return sol.first;
00149     }
00150     else if (eccentricity == 1.0)
00151     {
00152         // Nearly parabolic orbit; very common for comets
00153         // TODO: handle this
00154         return M;
00155     }
00156     else
00157     {
00158         // Laguerre-Conway method for hyperbolic (ecc > 1) orbits.
00159         double E = log(2 * M / eccentricity + 1.85);
00160         Solution sol = solve_iteration_fixed(SolveKeplerLaguerreConwayHyp(eccentricity, M), E, 30);
00161         return sol.first;
00162     }
00163 }
00164 
00165 
00166 Point3d EllipticalOrbit::positionAtE(double E) const
00167 {
00168     double x, z;
00169 
00170     if (eccentricity < 1.0)
00171     {
00172         double a = pericenterDistance / (1.0 - eccentricity);
00173         x = a * (cos(E) - eccentricity);
00174         z = a * sqrt(1 - square(eccentricity)) * -sin(E);
00175     }
00176     else if (eccentricity > 1.0)
00177     {
00178         double a = pericenterDistance / (1.0 - eccentricity);
00179         x = -a * (eccentricity - cosh(E));
00180         z = -a * sqrt(square(eccentricity) - 1) * -sinh(E);
00181     }
00182     else
00183     {
00184         // TODO: Handle parabolic orbits
00185         x = 0.0;
00186         z = 0.0;
00187     }
00188 
00189     Mat3d R = (Mat3d::yrotation(ascendingNode) *
00190                Mat3d::xrotation(inclination) *
00191                Mat3d::yrotation(argOfPeriapsis));
00192 
00193     return R * Point3d(x, 0, z);
00194 }
00195 
00196 
00197 // Return the offset from the center
00198 Point3d EllipticalOrbit::positionAtTime(double t) const
00199 {
00200     t = t - epoch;
00201     double meanMotion = 2.0 * PI / period;
00202     double meanAnomaly = meanAnomalyAtEpoch + t * meanMotion;
00203     double E = eccentricAnomaly(meanAnomaly);
00204     
00205     return positionAtE(E);
00206 }
00207 
00208 
00209 double EllipticalOrbit::getPeriod() const
00210 {
00211     return period;
00212 }
00213 
00214 
00215 double EllipticalOrbit::getBoundingRadius() const
00216 {
00217     // TODO: watch out for unbounded parabolic and hyperbolic orbits
00218     return pericenterDistance * ((1.0 + eccentricity) / (1.0 - eccentricity));
00219 }
00220 
00221 
00222 void EllipticalOrbit::sample(double start, double t, int nSamples,
00223                              OrbitSampleProc& proc) const
00224 {
00225     double dE = 2 * PI / (double) nSamples;
00226     for (int i = 0; i < nSamples; i++)
00227         proc.sample(t, positionAtE(dE * i));
00228 }
00229 
00230 
00231 
00232 Point3d CachingOrbit::positionAtTime(double jd) const
00233 {
00234     if (jd != lastTime)
00235     {
00236         lastTime = jd;
00237         lastPosition = computePosition(jd);
00238     }
00239     return lastPosition;
00240 }
00241 
00242 
00243 void CachingOrbit::sample(double start, double t, int nSamples,
00244                           OrbitSampleProc& proc) const
00245 {
00246     double dt = t / (double) nSamples;
00247     for (int i = 0; i < nSamples; i++)
00248         proc.sample(start + dt * i, positionAtTime(start + dt * i));
00249 }
00250 
00251 
00252 static EllipticalOrbit* StateVectorToOrbit(const Point3d& position,
00253                                            const Vec3d& v,
00254                                            double mass,
00255                                            double t)
00256 {
00257     Vec3d R = position - Point3d(0.0, 0.0, 0.0);
00258     Vec3d L = R ^ v;
00259     double magR = R.length();
00260     double magL = L.length();
00261     double magV = v.length();
00262     L *= (1.0 / magL);
00263 
00264     Vec3d W = L ^ (R / magR);
00265 
00266     double G = astro::G * 1e-9; // convert from meters to kilometers
00267     double GM = G * mass;
00268 
00269     // Compute the semimajor axis
00270     double a = 1.0 / (2.0 / magR - square(magV) / GM);
00271 
00272     // Compute the eccentricity
00273     double p = square(magL) / GM;
00274     double q = R * v;
00275     double ex = 1.0 - magR / a;
00276     double ey = q / sqrt(a * GM);
00277     double e = sqrt(ex * ex + ey * ey);
00278 
00279     // Compute the mean anomaly
00280     double E = atan2(ey, ex);
00281     double M = E - e * sin(E);
00282     
00283     // Compute the inclination
00284     double cosi = L * Vec3d(0, 1.0, 0);
00285     double i = 0.0;
00286     if (cosi < 1.0)
00287         i = acos(cosi);
00288 
00289     // Compute the longitude of ascending node
00290     double Om = atan2(L.x, L.z);
00291 
00292     // Compute the argument of pericenter
00293     Vec3d U = R / magR;
00294     double s_nu = (v * U) * sqrt(p / GM);
00295     double c_nu = (v * W) * sqrt(p / GM) - 1;
00296     s_nu /= e;
00297     c_nu /= e;
00298     Vec3d P = U * c_nu - W * s_nu;
00299     Vec3d Q = U * s_nu + W * c_nu;
00300     double om = atan2(P.y, Q.y);
00301 
00302     // Compute the period
00303     double T = 2 * PI * sqrt(cube(a) / GM);
00304     T = T / 86400.0; // Convert from seconds to days
00305 
00306 #if 0
00307     cout << "a: " << astro::kilometersToAU(a) << '\n';
00308     cout << "e: " << e << '\n';
00309     cout << "i: " << radToDeg(i) << '\n';
00310     cout << "Om: " << radToDeg(Om) << '\n';
00311     cout << "om: " << radToDeg(om) << '\n';
00312     cout << "M: " << radToDeg(M) << '\n';
00313     cout << "T: " << T << '\n';
00314 #endif
00315 
00316     return new EllipticalOrbit(a * (1 - e), e, i, Om, om, M, T, t);
00317 }
00318 
00319 
00320 MixedOrbit::MixedOrbit(Orbit* orbit, double t0, double t1, double mass) :
00321     primary(orbit),
00322     afterApprox(NULL),
00323     beforeApprox(NULL),
00324     begin(t0),
00325     end(t1),
00326     boundingRadius(0.0)
00327 {
00328     assert(t1 > t0);
00329     assert(orbit != NULL);
00330     
00331     double dt = 1.0 / 1440.0; // 1 minute
00332     Point3d p0 = orbit->positionAtTime(t0);
00333     Point3d p1 = orbit->positionAtTime(t1);
00334     Vec3d v0 = (orbit->positionAtTime(t0 + dt) - p0) / (86400 * dt);
00335     Vec3d v1 = (orbit->positionAtTime(t1 + dt) - p1) / (86400 * dt);
00336     beforeApprox = StateVectorToOrbit(p0, v0, mass, t0);
00337     afterApprox = StateVectorToOrbit(p1, v1, mass, t1);
00338 
00339     boundingRadius = beforeApprox->getBoundingRadius();
00340     if (primary->getBoundingRadius() > boundingRadius)
00341         boundingRadius = primary->getBoundingRadius();
00342     if (afterApprox->getBoundingRadius() > boundingRadius)
00343         boundingRadius = afterApprox->getBoundingRadius();
00344 }
00345 
00346 MixedOrbit::~MixedOrbit()
00347 {
00348     if (primary != NULL)
00349         delete primary;
00350     if (beforeApprox != NULL)
00351         delete beforeApprox;
00352     if (afterApprox != NULL)
00353         delete afterApprox;
00354 }
00355 
00356 
00357 Point3d MixedOrbit::positionAtTime(double jd) const
00358 {
00359     if (jd < begin)
00360         return beforeApprox->positionAtTime(jd);
00361     else if (jd < end)
00362         return primary->positionAtTime(jd);
00363     else
00364         return afterApprox->positionAtTime(jd);
00365 }
00366 
00367 
00368 double MixedOrbit::getPeriod() const
00369 {
00370     return primary->getPeriod();
00371 }
00372 
00373 
00374 double MixedOrbit::getBoundingRadius() const
00375 {
00376     return boundingRadius;
00377 }
00378 
00379 
00380 void MixedOrbit::sample(double t0, double t1, int nSamples,
00381                         OrbitSampleProc& proc) const
00382 {
00383     Orbit* o;
00384     if (t0 < begin)
00385         o = beforeApprox;
00386     else if (t0 < end)
00387         o = primary;
00388     else
00389         o = afterApprox;
00390     o->sample(t0, t1, nSamples, proc);
00391 }
00392 
00393 
00394 
00395 SynchronousOrbit::SynchronousOrbit(const Body& _body,
00396                                    const Point3d& _position) :
00397     body(_body),
00398     position(_position)
00399 {
00400 }
00401 
00402 
00403 SynchronousOrbit::~SynchronousOrbit()
00404 {
00405 }
00406 
00407 
00408 Point3d SynchronousOrbit::positionAtTime(double jd) const
00409 {
00410     //Quatd q = body.getEclipticalToGeographic(jd);
00411     Quatd q = body.getEquatorialToGeographic(jd);
00412     return position * q.toMatrix3();
00413 }
00414 
00415 
00416 double SynchronousOrbit::getPeriod() const
00417 {
00418     return body.getRotationElements().period;
00419 }
00420 
00421 
00422 double SynchronousOrbit::getBoundingRadius() const
00423 {
00424     return position.distanceFromOrigin();
00425 }
00426 
00427 
00428 void SynchronousOrbit::sample(double, double, int, OrbitSampleProc&) const
00429 {
00430     // Empty method--we never want to show a synchronous orbit.
00431 }

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