Click to fast-forward. Based on the math here. No guarantees of accuracy, this is not legal advice. Made in honor of Pi Day — which I think is borderline numerology, a random and arbitrary mapping, but I respect the ability to create meaning in life ex nihilo.
Earth orbit
<link rel="stylesheet" href="style.css"> | |
<div id="ecliptic"></div> | |
<p>Date: <span id="date"></span><p> | |
<p>Distance from sun: <span id="radius"></span> AU</p> | |
<p>True anomaly: <span id="true-anomaly"></span>°</p> | |
<p>True anomaly: <span id="true-anomaly-pi"></span>π</p> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="script.js" charset="utf-8"></script> |
// ALL GOOD STUFF ADAPTED FROM THIS WONDERFUL OLD SITE: | |
// http://www.stargazing.net/kepler/ellipse.html | |
// angles are in degrees | |
// distances are in a.u. | |
//////////////////////////////////////////// | |
// dates | |
var dayToMs = 24*60*60*1000; | |
var msToDay = 1/dayToMs; | |
// trig | |
var degToRad = (2*Math.PI)/360; | |
var radToDeg = 1/degToRad; | |
function sin(x) { return Math.sin(x * degToRad); } | |
function cos(x) { return Math.cos(x * degToRad); } | |
// "Osculating Elements" | |
var abbrevs = { | |
"i": "Inclination", | |
"o": "Longitude of the Ascending Node", | |
"p": "Longitude of Perihelion", | |
"a": "Mean distance", | |
"n": "Daily motion", | |
"e": "Eccentricity", | |
"L": "Mean Longitude", | |
"JD": "Julian Date" | |
} | |
var earth = { | |
"i": 0.00041, | |
"o": 349.2, | |
"p": 102.8517, | |
"a": 1.0000200, | |
"n": 0.9855796, | |
"e": 0.0166967, | |
"L": 328.40353, | |
"JD": 2450680.5, | |
"datetime": new Date("August 20, 1997 00:00:00"), | |
"radius": 0.000042563739 | |
}; | |
var mars = { | |
"i": 1.84992, | |
"o": 49.5664, | |
"p": 336.0882, | |
"a": 1.5236365, | |
"n": 0.5240613, | |
"e": 0.0934231, | |
"L": 262.42784, | |
"JD": 2450680.5, | |
"datetime": new Date("August 20, 1997 00:00:00"), | |
"radius": 0.00002263 | |
}; | |
var sun = { | |
"radius": 0.004649 | |
} | |
// MATH | |
function meanAnomaly(satellite, time) { | |
var days = (time-satellite.datetime)*msToDay; | |
return ((satellite.n * days + satellite.L - satellite.p) % 360 + 360) % 360; | |
} | |
function trueAnomaly(satellite, time) { | |
var M = meanAnomaly(satellite, time); | |
return M + (180/Math.PI) * | |
( | |
(2 * satellite.e - Math.pow(satellite.e, 3)/4) * Math.sin(degToRad*M) + | |
(5/4 * Math.pow(satellite.e, 2)) * Math.sin(degToRad*M*2) + | |
(13/12 * Math.pow(satellite.e, 3)) * Math.sin(degToRad*M*3) | |
); | |
} | |
function radius(satellite, time) { | |
return satellite.a * (1 - Math.pow(satellite.e, 2)) / (1 + satellite.e * Math.cos(trueAnomaly(satellite, time)*degToRad)); | |
} | |
function coordinates(satellite, time) { | |
var r = radius(satellite, time); | |
var v = trueAnomaly(satellite, time); | |
var o = satellite.o; | |
var p = satellite.p; | |
var i = satellite.i; | |
var X = r * (cos(o) * cos(v+p-o) - sin(o) * sin(v+p-o) * cos(i)); | |
var Y = r * (sin(o) * cos(v+p-o) + cos(o) * sin(v+p-o) * cos(i)); | |
var Z = r * (sin(v+p-o) * sin(i)); | |
return [X,Y,Z]; | |
} | |
// au to pixels | |
var minPx = Math.min(window.innerHeight, window.innerWidth); | |
var auScale = d3.scale.linear().domain([0,2.2]).range([0,minPx]); | |
var sunSel = d3.select("#ecliptic").append("div") | |
.classed('celestial-body', true) | |
.attr('id', 'sun') | |
.style('left', '0') | |
.style('top', '0') | |
.style('width', (auScale(sun.radius)*2)+'px') | |
.style('height', (auScale(sun.radius)*2)+'px'); | |
var earthSel = d3.select("#ecliptic").append("div") | |
.classed('celestial-body', true) | |
.attr('id', 'earth') | |
.style('width', (auScale(earth.radius)*2)+'px') | |
.style('height', (auScale(earth.radius)*2)+'px'); | |
function render(time) { | |
var earthRadius = radius(earth, time); | |
var earthTrueAnomaly = trueAnomaly(earth, time); | |
var earthCoords = coordinates(earth, time); | |
console.log(earthCoords); | |
d3.select("#date").text(time); | |
d3.select("#radius").text(earthRadius); | |
d3.select("#true-anomaly").text(earthTrueAnomaly); | |
d3.select("#true-anomaly-pi").text(earthTrueAnomaly*degToRad/Math.PI); | |
earthSel | |
.style('left', auScale(earthCoords[0]) + 'px') | |
.style('top', auScale(earthCoords[1]) + 'px') | |
} | |
render(new Date()); | |
var realtime = setInterval(function() { render(new Date()); }, 1000); | |
var fastForward; | |
window.onclick = function() { | |
clearInterval(realtime); | |
clearInterval(fastForward); | |
var t = 0; | |
fastForward = setInterval(function() { | |
var newTime = new Date(+new Date() + t*dayToMs); | |
render(newTime); | |
t++; | |
}, 50); | |
} |
html, body { | |
margin: 0; | |
padding: 0; | |
background: black; | |
width: 100%; | |
height: 100%; | |
min-width: 500px; | |
min-height: 500px; | |
color: #333; | |
font-family: courier; | |
font-size: 10px; | |
} | |
p { margin: 0; } | |
#ecliptic { | |
top: 50%; | |
left: 50%; | |
width: 1px; | |
height: 1px; | |
transform: translate(-50%, -50%); | |
-webkit-transform: translate(-50%, -50%); | |
-moz-transform: translate(-50%, -50%); | |
position: relative; | |
} | |
.celestial-body { | |
border: 1px solid rgba(255,255,255,.5); | |
border-radius: 100px; | |
position: absolute; | |
-webkit-transform: translate(-50%, -50%); | |
-moz-transform: translate(-50%, -50%); | |
transform: translate(-50%, -50%); | |
-webkit-box-shadow: 0px 0px 3px 0px rgba(255, 255, 255, 0.75); | |
-moz-box-shadow: 0px 0px 3px 0px rgba(255, 255, 255, 0.75); | |
box-shadow: 0px 0px 3px 0px rgba(255, 255, 255, 0.75); | |
} | |
#sun { | |
background: yellow; | |
} | |
#earth { | |
background: lightblue; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment