OrbitalElements.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. 'use strict';
  2. var ns = require('ns');
  3. var Utils = require('./Utils');
  4. var THREE = require('./Three.shim');
  5. var CMath = require('./Math');
  6. var maxIterationsForEccentricAnomaly = 10;
  7. var maxDE = 1e-15;
  8. var solveEccentricAnomaly = function(f, x0, maxIter) {
  9. var x = 0;
  10. var x2 = x0;
  11. for (var i = 0; i < maxIter; i++) {
  12. x = x2;
  13. x2 = f(x);
  14. }
  15. return x2;
  16. }
  17. var solveKepler = function(e, M) {
  18. return function(x) {
  19. return x + (M + e * Math.sin(x) - x) / (1 - e * Math.cos(x));
  20. };
  21. };
  22. var solveKeplerLaguerreConway = function(e, M) {
  23. return function(x) {
  24. var s = e * Math.sin(x);
  25. var c = e * Math.cos(x);
  26. var f = x - s - M;
  27. var f1 = 1 - c;
  28. var f2 = s;
  29. x += -5 * f / (f1 + CMath.sign(f1) * Math.sqrt(Math.abs(16 * f1 * f1 - 20 * f * f2)));
  30. return x;
  31. };
  32. };
  33. var solveKeplerLaguerreConwayHyp = function(e, M) {
  34. return function(x) {
  35. var s = e * CMath.sinh(x);
  36. var c = e * CMath.cosh(x);
  37. var f = x - s - M;
  38. var f1 = c - 1;
  39. var f2 = s;
  40. x += -5 * f / (f1 + CMath.sign(f1) * Math.sqrt(Math.abs(16 * f1 * f1 - 20 * f * f2)));
  41. return x;
  42. };
  43. };
  44. module.exports = {
  45. setDefaultOrbit : function(orbitalElements, calculator) {
  46. this.orbitalElements = orbitalElements;
  47. if(orbitalElements && orbitalElements.epoch) {
  48. this.epochCorrection = ns.getEpochTime(orbitalElements.epoch);
  49. }
  50. this.calculator = calculator;
  51. },
  52. setName : function(name){
  53. this.name = name;
  54. },
  55. calculateVelocity : function(timeEpoch, relativeTo, isFromDelta) {
  56. if(!this.orbitalElements) return new THREE.Vector3(0,0,0);
  57. var eclipticVelocity;
  58. if ( isFromDelta ) {
  59. var pos1 = this.calculatePosition(timeEpoch);
  60. var pos2 = this.calculatePosition(timeEpoch + 60);
  61. eclipticVelocity = pos2.sub(pos1).multiplyScalar(1/60);
  62. } else {
  63. //vis viva to calculate speed (not velocity, i.e not a vector)
  64. var el = this.calculateElements(timeEpoch);
  65. var speed = Math.sqrt(ns.G * require('./SolarSystem').getBody(relativeTo).mass * ((2 / (el.r)) - (1 / (el.a))));
  66. //now calculate velocity orientation, that is, a vector tangent to the orbital ellipse
  67. var k = el.r / el.a;
  68. var o = ((2 - (2 * el.e * el.e)) / (k * (2-k)))-1;
  69. //floating point imprecision
  70. o = o > 1 ? 1 : o;
  71. var alpha = Math.PI - Math.acos(o);
  72. alpha = el.v < 0 ? (2 * Math.PI) - alpha : alpha;
  73. var velocityAngle = el.v + (alpha / 2);
  74. //velocity vector in the plane of the orbit
  75. var orbitalVelocity = new THREE.Vector3(Math.cos(velocityAngle), Math.sin(velocityAngle)).setLength(speed);
  76. var velocityEls = Utils.extend({}, el, {pos:orbitalVelocity, v:null, r:null});
  77. eclipticVelocity = this.getPositionFromElements(velocityEls);
  78. }
  79. //var diff = eclipticVelocityFromDelta.sub(eclipticVelocity);console.log(diff.length());
  80. return eclipticVelocity;
  81. },
  82. calculatePosition : function(timeEpoch) {
  83. if(!this.orbitalElements) return new THREE.Vector3(0,0,0);
  84. var computed = this.calculateElements(timeEpoch);
  85. var pos = this.getPositionFromElements(computed);
  86. return pos;
  87. },
  88. solveEccentricAnomaly : function(e, M){
  89. if (e == 0.0) {
  90. return M;
  91. } else if (e < 0.9) {
  92. var sol = solveEccentricAnomaly(solveKepler(e, M), M, 6);
  93. return sol;
  94. } else if (e < 1.0) {
  95. var E = M + 0.85 * e * ((Math.sin(M) >= 0.0) ? 1 : -1);
  96. var sol = solveEccentricAnomaly(solveKeplerLaguerreConway(e, M), E, 8);
  97. return sol;
  98. } else if (e == 1.0) {
  99. return M;
  100. } else {
  101. var E = Math.log(2 * M / e + 1.85);
  102. var sol = solveEccentricAnomaly(solveKeplerLaguerreConwayHyp(e, M), E, 30);
  103. return sol;
  104. }
  105. },
  106. calculateElements : function(timeEpoch, forcedOrbitalElements) {
  107. if(!forcedOrbitalElements && !this.orbitalElements) return null;
  108. var orbitalElements = forcedOrbitalElements || this.orbitalElements;
  109. /*
  110. Epoch : J2000
  111. a Semi-major axis
  112. e Eccentricity
  113. i Inclination
  114. o Longitude of Ascending Node (Ω)
  115. w Argument of periapsis (ω)
  116. E Eccentric Anomaly
  117. T Time at perihelion
  118. M Mean anomaly
  119. l Mean Longitude
  120. lp longitude of periapsis
  121. r distance du centre
  122. v true anomaly
  123. P Sidereal period (mean value)
  124. Pw Argument of periapsis precession period (mean value)
  125. Pn Longitude of the ascending node precession period (mean value)
  126. */
  127. if (this.epochCorrection) {
  128. timeEpoch -= this.epochCorrection;
  129. }
  130. var tDays = timeEpoch / ns.DAY;
  131. var T = tDays / ns.CENTURY ;
  132. //console.log(T);
  133. var computed = {
  134. t : timeEpoch
  135. };
  136. if(this.calculator && !forcedOrbitalElements) {
  137. var realorbit = this.calculator(T);
  138. Utils.extend(computed, realorbit);
  139. } else {
  140. if (orbitalElements.base) {
  141. var variation;
  142. for(var el in orbitalElements.base) {
  143. //cy : variation by century.
  144. //day : variation by day.
  145. variation = orbitalElements.cy ? orbitalElements.cy[el] : (orbitalElements.day[el] * ns.CENTURY);
  146. variation = variation || 0;
  147. computed[el] = orbitalElements.base[el] + (variation * T);
  148. }
  149. } else {
  150. computed = Utils.extend({}, orbitalElements);
  151. }
  152. if (undefined === computed.w) {
  153. computed.w = computed.lp - computed.o;
  154. }
  155. if (undefined === computed.M) {
  156. computed.M = computed.l - computed.lp;
  157. }
  158. computed.a = computed.a * ns.KM;//was in km, set it in m
  159. }
  160. computed.i = ns.DEG_TO_RAD * computed.i;
  161. computed.o = ns.DEG_TO_RAD * computed.o;
  162. computed.w = ns.DEG_TO_RAD * computed.w;
  163. computed.M = ns.DEG_TO_RAD * computed.M;
  164. computed.E = this.solveEccentricAnomaly(computed.e, computed.M);
  165. computed.E = computed.E % ns.CIRCLE;
  166. computed.i = computed.i % ns.CIRCLE;
  167. computed.o = computed.o % ns.CIRCLE;
  168. computed.w = computed.w % ns.CIRCLE;
  169. computed.M = computed.M % ns.CIRCLE;
  170. //in the plane of the orbit
  171. computed.pos = new THREE.Vector3(computed.a * (Math.cos(computed.E) - computed.e), computed.a * (Math.sqrt(1 - (computed.e*computed.e))) * Math.sin(computed.E));
  172. computed.r = computed.pos.length();
  173. computed.v = Math.atan2(computed.pos.y, computed.pos.x);
  174. if(orbitalElements.relativeTo) {
  175. var relativeTo = require('./SolarSystem').getBody(orbitalElements.relativeTo);
  176. if(relativeTo.tilt) {
  177. computed.tilt = -relativeTo.tilt * ns.DEG_TO_RAD;
  178. }
  179. };
  180. return computed;
  181. },
  182. getPositionFromElements : function(computed) {
  183. if(!computed) return new THREE.Vector3(0,0,0);
  184. var a1 = new THREE.Euler(computed.tilt || 0, 0, computed.o, 'XYZ');
  185. var q1 = new THREE.Quaternion().setFromEuler(a1);
  186. var a2 = new THREE.Euler(computed.i, 0, computed.w, 'XYZ');
  187. var q2 = new THREE.Quaternion().setFromEuler(a2);
  188. var planeQuat = new THREE.Quaternion().multiplyQuaternions(q1, q2);
  189. computed.pos.applyQuaternion(planeQuat);
  190. return computed.pos;
  191. },
  192. calculatePeriod : function(elements, relativeTo) {
  193. var period;
  194. if(this.orbitalElements && this.orbitalElements.day && this.orbitalElements.day.M) {
  195. period = 360 / this.orbitalElements.day.M ;
  196. }else if(require('./SolarSystem').getBody(relativeTo) && require('./SolarSystem').getBody(relativeTo).k && elements) {
  197. period = 2 * Math.PI * Math.sqrt(Math.pow(elements.a/(ns.AU*1000), 3)) / require('./SolarSystem').getBody(relativeTo).k;
  198. }
  199. period *= ns.DAY;//in seconds
  200. return period;
  201. }
  202. };