/Orbitersdk/samples/OrbiterNavigator/OrbiterNavigatorApp/Model/KeplerOrbit.cs

https://bitbucket.org/Mindblast/orbiter-navigator · C# · 453 lines · 226 code · 70 blank · 157 comment · 34 complexity · 6517e3383aa94bade9ef331c66e56507 MD5 · raw file

  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // KeplerOrbit.cs
  4. // Partial Transcript of the Kepler Orbit Simulation Toolkit (KOST) by
  5. // C. J. Plooy and Tim Blaxland
  6. //
  7. // Orbiter Navigator
  8. // Alexander Blaß - 2010
  9. //-----------------------------------------------------------------------------
  10. #endregion
  11. using System;
  12. using System.Collections.Generic;
  13. //using System.Linq;
  14. using System.Text;
  15. using Orbiter_Navigator.VectorMath;
  16. namespace Orbiter_Navigator.Model
  17. {
  18. /// <summary>
  19. /// The KeplerOrbiter class encapsulates data needed to describe a Kepler Orbit ellipse
  20. /// and methods for all kinds of calculations around Kepler Orbits.
  21. /// The code of this class is a partial transcript of the Kepler Orbit Simulation Toolkit (KOST) by
  22. /// C. J. Plooy and Tim Blaxland.
  23. /// </summary>
  24. public class KeplerOrbit
  25. {
  26. /// <summary>
  27. /// Semi-major axis
  28. /// </summary>
  29. public double a;
  30. /// <summary>
  31. /// Eccentricity
  32. /// </summary>
  33. public double e;
  34. /// <summary>
  35. /// Inclination
  36. /// </summary>
  37. public double i;
  38. /// <summary>
  39. /// Longitude of ascending node
  40. /// </summary>
  41. public double theta;
  42. /// <summary>
  43. /// Longitude of periapsis
  44. /// </summary>
  45. public double omegab;
  46. /// <summary>
  47. /// Mean longitude at epoch
  48. /// </summary>
  49. public double L;
  50. /// <summary>
  51. /// Default constructor.
  52. /// </summary>
  53. public KeplerOrbit()
  54. {
  55. this.a = 1.0;
  56. this.e = 0;
  57. this.i = 0;
  58. this.theta = 0;
  59. this.omegab = 0;
  60. this.L = 0;
  61. }
  62. /// <summary>
  63. /// Constructs a KeplerOrbit object from the 6 oskulating elements.
  64. /// </summary>
  65. /// <param name="a">Semi major axis.</param>
  66. /// <param name="e">Eccentricity</param>
  67. /// <param name="i">Inclination</param>
  68. /// <param name="theta">Longitude of ascending node.</param>
  69. /// <param name="omegab">Longitude of periapsis.</param>
  70. /// <param name="L">Mean longitude at epoch.</param>
  71. public KeplerOrbit(double a, double e, double i, double theta, double omegab, double L)
  72. {
  73. this.a = a;
  74. this.e = e;
  75. this.i = i;
  76. this.theta = theta;
  77. this.omegab = omegab;
  78. this.L = L;
  79. }
  80. /// <summary>
  81. /// This method samples the Keplerian orbit and returns an array of vectors depicting the orbit path or shape.
  82. /// The for most interesting points of the an orbit periapsis, apoapsis, ascending node and descending node are
  83. /// calculated and returned in out vectors.
  84. /// </summary>
  85. /// <param name="numPoints">The number of points used to sample the orbit. This is the size of the returned array.</param>
  86. /// <param name="pe">A vector receiving the periapsis position of the orbit.</param>
  87. /// <param name="ap">A vector receiving the apoapsis position. </param>
  88. /// <param name="an">Vector receiving the ascending node position.</param>
  89. /// <param name="dn">Vector receiving the descending node position.</param>
  90. /// <returns>An array of vectors depicting the orbits path.</returns>
  91. public Vector3d[] GetShape(int numPoints, out Vector3d pe, out Vector3d ap, out Vector3d an, out Vector3d dn)
  92. {
  93. Vector3d[] points = new Vector3d[numPoints];
  94. // utility values
  95. double multiplier = this.a * (1.0 - this.e * this.e);
  96. double AgP = this.omegab - this.theta;
  97. pe = new Vector3d(this.a * (1.0 - this.e), 0.0, 0.0);
  98. ap = new Vector3d(-this.a * (1.0 + this.e), 0.0, 0.0);
  99. double TrA;
  100. double absr;
  101. Vector3d direction;
  102. if (numPoints == 1)
  103. {
  104. points[0] = pe;
  105. }
  106. else if (numPoints > 1)
  107. {
  108. double maxTrA, dTrA;
  109. maxTrA = Math.PI;
  110. if (this.e >= 1.0)
  111. {
  112. maxTrA = Math.Acos(-1.0 / this.e);
  113. // Make it a bit smaller to avoid division by zero
  114. maxTrA *= (((double)numPoints) / (numPoints + 1));
  115. }
  116. dTrA = (2 * maxTrA) / (numPoints - 1);
  117. TrA = -maxTrA;
  118. for (int i = 0; i < numPoints; i++)
  119. {
  120. absr = Math.Abs(multiplier / (1.0 + this.e * Math.Cos(TrA)));
  121. direction = new Vector3d(Math.Cos(TrA), Math.Sin(TrA), 0.0);
  122. points[i] = direction * absr;
  123. TrA += dTrA;
  124. }
  125. }
  126. // ascending node
  127. TrA = -AgP;
  128. absr = multiplier / (1.0 + this.e * Math.Cos(TrA));
  129. if (absr <= 0.0)
  130. {
  131. an = new Vector3d(0.0, 0.0, 0.0);
  132. }
  133. else
  134. {
  135. direction = new Vector3d(Math.Cos(TrA), Math.Sin(TrA), 0.0);
  136. an = direction * absr;
  137. }
  138. // descending node
  139. TrA = Math.PI - AgP;
  140. absr = multiplier / (1.0 + this.e * Math.Cos(TrA));
  141. if (absr <= 0.0)
  142. {
  143. dn = new Vector3d(0.0, 0.0, 0.0);
  144. }
  145. else
  146. {
  147. direction = new Vector3d(Math.Cos(TrA), Math.Sin(TrA), 0.0);
  148. dn = direction * absr;
  149. }
  150. Matrixd AgPMat, LANMat, IncMat, transform;
  151. AgPMat = Matrixd.CreateRotationZ(AgP);
  152. IncMat = Matrixd.CreateRotationX(this.i);
  153. LANMat = Matrixd.CreateRotationZ(this.theta);
  154. transform = LANMat * IncMat * AgPMat;
  155. pe = Vector3d.Transform(pe, transform);
  156. ap = Vector3d.Transform(ap, transform);
  157. an = Vector3d.Transform(an, transform);
  158. dn = Vector3d.Transform(dn, transform);
  159. if (numPoints != 0)
  160. for (int i = 0; i < numPoints; i++)
  161. points[i] = Vector3d.Transform(points[i], transform);
  162. return points;
  163. }
  164. /// <summary>
  165. /// Calculates the mean anomaly at a given time.
  166. /// </summary>
  167. /// <param name="mu">The gravitational parameter.</param>
  168. /// <param name="timeSinceEpoch">The time that has passed since the orbits epoch in seconds.</param>
  169. /// <returns>The mean anomaly at the given time.</returns>
  170. public double getMeanAnomalyAtTime(double mu, double timeSinceEpoch)
  171. {
  172. // calculate mean motion
  173. double meanMotion = Math.Sqrt(mu/Math.Pow(Math.Abs(this.a), 3.0));
  174. // calculate change in mean anomaly
  175. double deltaMeanAnomaly = timeSinceEpoch * meanMotion;
  176. // calculate mean anomaly
  177. double meanAnomaly = (this.L - this.omegab + deltaMeanAnomaly) % (2.0 * Math.PI);
  178. if (meanAnomaly < 0)
  179. meanAnomaly += (2.0 * Math.PI);
  180. return meanAnomaly;
  181. }
  182. /// <summary>
  183. /// Calculates the eccentric anomaly for the orbit at a given mean anomaly.
  184. /// The value is estimated using Newtons method.
  185. /// </summary>
  186. /// <param name="meanAnomaly">The mean anomaly</param>
  187. /// <param name="eccentricAnomalyEstimate">A starting value for estimating the eccentric anomaly, the closer this is to the
  188. /// eccentric anomaly the faster the method can calculate the eccentric anomaly using Newtons method.
  189. /// If you don't have a good estimate, the mean anomaly can be used.</param>
  190. /// <param name="maxRelativeError">Maximum relative error between two successive runs of Newtons method. If the values of two successive runs
  191. /// deviate by less than this value, the calculation is ended and the estimate returned.</param>
  192. /// <param name="maxIterations">Maximum iterations for the calculation using Newtons method. This can be used
  193. /// to limit the calculation time of the method. The calculation can be split over severeal calls this way.</param>
  194. /// <returns>The estimated value for the mean anomaly.</returns>
  195. public double getEccentricAnomaly(double meanAnomaly, double eccentricAnomalyEstimate, double maxRelativeError, int maxIterations)
  196. {
  197. double e, meanAnomalyEstimate, relativeError;
  198. if (this.e == 0)
  199. return meanAnomaly;
  200. if (this.e == 1.0) // parabolic orbit - approximate to hyperbolic
  201. e = this.e + 1e-6;
  202. else
  203. e = this.e;
  204. int i = 0;
  205. do
  206. {
  207. if (this.e < 1.0) // eliptical orbit
  208. {
  209. // calulate next estimate of the root of Keplers equation using Newtons method
  210. eccentricAnomalyEstimate = eccentricAnomalyEstimate - (eccentricAnomalyEstimate - e * Math.Sin(eccentricAnomalyEstimate) - meanAnomaly) / (1 - e * Math.Cos(eccentricAnomalyEstimate));
  211. /* calculate estimate of mean anomaly from estimate of eccentric anomaly */
  212. meanAnomalyEstimate = eccentricAnomalyEstimate - e * Math.Sin(eccentricAnomalyEstimate);
  213. }
  214. else // hyperbolic orbit
  215. {
  216. /* sinh is not continuous over the range 0 to 2*pi, so it must be brought into the range -pi to pi */
  217. if (eccentricAnomalyEstimate > Math.PI)
  218. eccentricAnomalyEstimate -= (2.0 * Math.PI);
  219. /* calculate next estimate of the root of Kepler's equation using Newton's method */
  220. eccentricAnomalyEstimate = eccentricAnomalyEstimate - (e * Math.Sinh(eccentricAnomalyEstimate) - eccentricAnomalyEstimate - meanAnomaly) / (e * Math.Cosh(eccentricAnomalyEstimate) - 1);
  221. /* calculate estimate of mean anomaly from estimate of eccentric anomaly */
  222. meanAnomalyEstimate = e * Math.Sinh(eccentricAnomalyEstimate) - eccentricAnomalyEstimate;
  223. }
  224. /* calculate relativeError */
  225. relativeError = 1 - meanAnomalyEstimate / meanAnomaly;
  226. i++;
  227. }
  228. while ((i < maxIterations) && (Math.Abs(relativeError) > Math.Abs(maxRelativeError)));
  229. /* check range is in 0 to 2*pi */
  230. eccentricAnomalyEstimate = eccentricAnomalyEstimate % (2.0 * Math.PI);
  231. if (eccentricAnomalyEstimate < 0)
  232. eccentricAnomalyEstimate += (2.0 * Math.PI);
  233. return eccentricAnomalyEstimate;
  234. }
  235. /// <summary>
  236. /// Calculates the true anomaly for the orbit and a given eccentric anomaly.
  237. /// </summary>
  238. /// <param name="mu">The gravitational parameter.</param>
  239. /// <param name="eccentricAnomaly">The eccentric anomaly.</param>
  240. /// <returns>The true anomaly.</returns>
  241. public double getTrueAnomaly(double mu, double eccentricAnomaly)
  242. {
  243. if (this.e < 1) // elliptical orbit
  244. return (2 * Math.Atan(Math.Sqrt((1 + this.e) / (1 - this.e)) * Math.Tan(eccentricAnomaly / 2)));
  245. else // hyperbolic orbit
  246. return (Math.Acos((Math.Cosh(eccentricAnomaly) - this.e) / (1 - this.e * Math.Cosh(eccentricAnomaly))));
  247. }
  248. /// <summary>
  249. /// Calculates the position and velocity vectors for the orbit at a given true anomaly and gravitational parameter.
  250. /// </summary>
  251. /// <param name="mu">The gravitational parameter</param>
  252. /// <param name="trueAnomaly">The true anomaly.</param>
  253. /// <param name="position">Receives the calculated position.</param>
  254. /// <param name="velocity">Receives the calculated velocity.</param>
  255. public void getStateVector(double mu, double trueAnomaly, out Vector3d position, out Vector3d velocity)
  256. {
  257. /* Pseudocode
  258. *
  259. * calc nodal vector, n
  260. * calc angular momentum vector, h
  261. * calc position vector
  262. * calc argument of position
  263. * calc direction of position vector
  264. * calc length of position vector
  265. * calc velocity vector
  266. * calculate magnitude of velocity vector
  267. * calc components of velocity vector perpendicular and parallel to radius vector
  268. * add to get velocity vector */
  269. Vector3d n; /* unit vector in direction of ascending node */
  270. Vector3d h; /* angular momentum vector */
  271. Vector3d north; /* unit vector pointing ecliptic north */
  272. Vector3d vPro, vO; /* prograde and outward components of velocity vector */
  273. Vector3d tmp;
  274. double argPos; /* argument of position, measured from the longitude of ascending node */
  275. double rPe, vPe; /* radius and velocity at periapsis */
  276. double v2; /* magnitude squared of velocity vector */
  277. double e; /* eccentricity */
  278. e = this.e;
  279. if (e == 1.0) /* parabolic orbit - approximate to hyperbolic orbit */
  280. e += 1e-6;
  281. /* calc nodal vector */
  282. n = new Vector3d(Math.Cos(this.theta), Math.Sin(this.theta), 0.0);
  283. /* ecliptic north vector */
  284. north = new Vector3d(0.0, 0.0, 1.0);
  285. /* calc angular momentum vector, h */
  286. /* projection of h in ecliptic (xy) plane */
  287. h = Vector3d.Cross(n, north);
  288. h *= Math.Sin(this.i);
  289. /* elevation of h */
  290. h.z = Math.Cos(this.i);
  291. h = Vector3d.Normalize(h);
  292. /* calc magnitude of h */
  293. /* calc radius and velocity at periapsis */
  294. if (e < 1.0) /* elliptical orbit */
  295. {
  296. rPe = this.a * (1.0 - e * e) / (1.0 + e);
  297. vPe = Math.Sqrt(mu * (2 / rPe - 1 / this.a));
  298. }
  299. else /* hyperbolic orbit */
  300. {
  301. rPe = Math.Abs(this.a) * (e * e - 1.0) / (1.0 + e);
  302. vPe = Math.Sqrt(mu * (2 / rPe + 1 / Math.Abs(this.a)));
  303. }
  304. /* calc h */
  305. h *= (rPe * vPe);
  306. /* calc position vector */
  307. /* calc argument of position */
  308. argPos = this.omegab - this.theta + trueAnomaly;
  309. /* calc direction of position vector */
  310. tmp = n * Math.Cos(argPos);
  311. position = Vector3d.Normalize(h);
  312. position = Vector3d.Cross(position, n);
  313. position *= Math.Sin(argPos);
  314. position += tmp;
  315. /* calc length of position vector */
  316. if (e < 1.0) /* elliptical orbit */
  317. position *= (this.a * (1.0 - e * e) / (1.0 + e * Math.Cos(trueAnomaly)));
  318. else /* hyperbolic orbit */
  319. position *= (Math.Abs(this.a) * (e * e - 1.0) / (1.0 + e * Math.Cos(trueAnomaly)));
  320. /* calc velocity vector */
  321. /* calculate magnitude of velocity vector */
  322. if (e < 1.0) /* elliptical orbit */
  323. v2 = mu * (2 / position.Length() - 1 / this.a);
  324. else /* hyperbolic orbit */
  325. v2 = mu * (2 / position.Length() + 1 / Math.Abs(this.a));
  326. /* calc components of velocity vector perpendicular and parallel to radius vector:
  327. vPro = (|h|/|pos|) * normal(h x pos)
  328. vO = sqrt(v^2 - |vPro|^2) * normal(pos);
  329. */
  330. vPro = Vector3d.Cross(h, position);
  331. vPro = Vector3d.Normalize(vPro);
  332. vPro *= (h.Length() / position.Length());
  333. vO = Vector3d.Normalize(position);
  334. vO *= (Math.Sqrt(v2 - vPro.LengthSquared()) * Math.Sin(trueAnomaly) / Math.Abs(Math.Sin(trueAnomaly)));
  335. /* add to get velocity vector */
  336. velocity = vPro + vO;
  337. }
  338. /// <summary>
  339. /// Calculates the position and velocity of the body whos orbit is described by this KeplerOrbit object at a given time.
  340. /// </summary>
  341. /// <param name="mu">The gravitational parameter.</param>
  342. /// <param name="timeSinceEpoch">The time in seconds that has passed since the orbits epoch.</param>
  343. /// <param name="position">A vector receiving the relative position of the orbiting body at the provided time.</param>
  344. /// <param name="velocity">A vector receiving the relative velocity of the orbiting body at the provided time.</param>
  345. public void getStateVectorAtTime(double mu, double timeSinceEpoch, out Vector3d position, out Vector3d velocity)
  346. {
  347. // calculate mean anomaly
  348. double meanAnomaly = getMeanAnomalyAtTime(mu, timeSinceEpoch);
  349. // calculate eccentric anomaly
  350. // to get an idea of the practical implications of the maximum relative error passed
  351. // to getEccentricAnomaly:
  352. // neptune radius = ~25000 km
  353. // neptune orbit path length for one revolution = ~28e9 km
  354. // so with 2 * PI * 25000 / 28e9 we get ~0.00000561 which means the maximum error
  355. // would be one neptune radius for neptunes orbit with this value
  356. // thus the value of 0.0000001 used here should calculate neptunes position with a deviation of
  357. // 1/56th of the neptune radius or ~446km
  358. double eccentricAnomaly = getEccentricAnomaly( meanAnomaly, meanAnomaly, 0.0000001, 10);
  359. // calculate true anomaly
  360. double trueAnomaly = getTrueAnomaly(mu, eccentricAnomaly);
  361. // calculate vectors
  362. getStateVector(mu, trueAnomaly, out position, out velocity);
  363. }
  364. public delegate void changedHandler();
  365. public event changedHandler changed;
  366. /// <summary>
  367. /// Updates this Orbit object by copying the values of another one.
  368. /// </summary>
  369. /// <param name="keplerOrbit"></param>
  370. internal void updateFromOrbit(KeplerOrbit keplerOrbit)
  371. {
  372. this.a = keplerOrbit.a;
  373. this.e = keplerOrbit.e;
  374. this.i = keplerOrbit.i;
  375. this.L = keplerOrbit.L;
  376. this.omegab = keplerOrbit.omegab;
  377. this.theta = keplerOrbit.theta;
  378. if (this.changed != null)
  379. this.changed();
  380. }
  381. }
  382. }