00001
00002
00003
00004
00005
00006
00007
00008
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
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
00059
00060
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
00123 return M;
00124 }
00125 else if (eccentricity < 0.2)
00126 {
00127
00128 Solution sol = solve_iteration_fixed(SolveKeplerFunc1(eccentricity, M), M, 5);
00129 return sol.first;
00130 }
00131 else if (eccentricity < 0.9)
00132 {
00133
00134
00135 Solution sol = solve_iteration_fixed(SolveKeplerFunc2(eccentricity, M), M, 6);
00136
00137
00138
00139 return sol.first;
00140 }
00141 else if (eccentricity < 1.0)
00142 {
00143
00144
00145
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
00153
00154 return M;
00155 }
00156 else
00157 {
00158
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
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
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
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;
00267 double GM = G * mass;
00268
00269
00270 double a = 1.0 / (2.0 / magR - square(magV) / GM);
00271
00272
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
00280 double E = atan2(ey, ex);
00281 double M = E - e * sin(E);
00282
00283
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
00290 double Om = atan2(L.x, L.z);
00291
00292
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
00303 double T = 2 * PI * sqrt(cube(a) / GM);
00304 T = T / 86400.0;
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;
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
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
00431 }