2016-04-05 16:17:36 +03:00

306 lines
9.6 KiB
JavaScript

// http://www.esrl.noaa.gov/gmd/grad/solcalc/azel.html
(function () { 'use strict';
var NOAAcalc = {};
const
PI = Math.PI,
PI_2 = PI/2,
_2PI = 2*PI,
DegToRad = PI/180.,
RadToDeg = 180./PI,
J2000 = 2451545.0; // JD of 2000yr
var
floor = Math.floor,
abs = Math.abs,
sqrt = Math.sqrt;
// all inner angle functions are in degrees!
function sin(deg){return Math.sin(deg * DegToRad);}
function cos(deg){return Math.cos(deg * DegToRad);}
function tan(deg){return Math.tan(deg * DegToRad);}
function asin(deg){return RadToDeg * Math.asin(deg);}
function acos(deg){return RadToDeg * Math.acos(deg);}
function atan(y,x){return RadToDeg * Math.atan2(y,x);}
function d360(angle){
return(angle - floor(angle/360.0)*360);
}
function d180(angle){
var a = d360(angle);
if(a > 180) a -= 360;
return a;
}
function calcJD(d){
var dayMs = 1000 * 60 * 60 * 24,
J1970 = 2440588;
return (d.valueOf()/dayMs - 0.5 + J1970)
}
NOAAcalc.calcJD = calcJD;
function calcTimeJulianCent(jd){
return ((jd - J2000)/36525.0);
}
// argument t in all functions is in Julian cents
function calcGeomMeanAnomalySun(t){
return (357.52911 + t * (35999.05029 - 0.0001537 * t));
}
function calcSunEqOfCenter(t){
var m = calcGeomMeanAnomalySun(t);
var sinm = sin(m);
var sin2m = sin(2*m);
var sin3m = sin(3*m);
return (sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) +
sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289);
}
function calcSunTrueAnomaly(t){
var m = calcGeomMeanAnomalySun(t);
var c = calcSunEqOfCenter(t);
return d360(m + c);
}
function calcEccentricityEarthOrbit(t){
return (0.016708634 - t * (0.000042037 + 0.0000001267 * t));
}
function calcSunRadVector(t){
var v = calcSunTrueAnomaly(t);
var e = calcEccentricityEarthOrbit(t);
var R = (1.000001018 * (1 - e * e)) / (1 + e * cos(v));
return R;
}
function calcMeanObliquityOfEcliptic(t){
var seconds = 21.448 - t*(46.8150 + t*(0.00059 - t*(0.001813)));
return (23.0 + (26.0 + (seconds/60.0))/60.0);
}
function calcObliquityCorrection(t){
var e0 = calcMeanObliquityOfEcliptic(t);
var omega = 125.04 - 1934.136 * t;
var e = e0 + 0.00256 * cos(omega);
return d360(e);
}
function calcGeomMeanLongSun(t){
var L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t);
return d360(L0);
}
function calcSunTrueLong(t){
var l0 = calcGeomMeanLongSun(t);
var c = calcSunEqOfCenter(t);
return d360(l0 + c);
}
function calcSunApparentLong(t){
var o = calcSunTrueLong(t);
var omega = 125.04 - 1934.136 * t;
return d360(o - (0.00569 - 0.00478 * sin(omega)));
}
function calcSunRtAscension(t){
var e = calcObliquityCorrection(t);
var lambda = calcSunApparentLong(t);
var tananum = cos(e) * sin(lambda);
var tanadenom = cos(lambda);
return atan(tananum, tanadenom);
}
function calcSunDeclination(t){
var e = calcObliquityCorrection(t);
var lambda = calcSunApparentLong(t);
var sint = sin(e) * sin(lambda);
return asin(sint);
}
function calcEquationOfTime(t){
var epsilon = calcObliquityCorrection(t);
var l0 = calcGeomMeanLongSun(t);
var e = calcEccentricityEarthOrbit(t);
var m = calcGeomMeanAnomalySun(t);
var y = tan(epsilon/2.0);
y *= y;
var sin2l0 = sin(2.0 * l0);
var sinm = sin(m);
var cos2l0 = cos(2.0 * l0);
var sin4l0 = sin(4.0 * l0);
var sin2m = sin(2.0 * m);
var Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0
- 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return Etime*4.0; // in minutes of time
}
function Refract(zenith){
var refractionCorrection = 0., exoatmElevation = 90. - zenith;
if(exoatmElevation < 85){
var te = tan(exoatmElevation);
if (exoatmElevation > 5){
refractionCorrection = 58.1 / te - 0.07 / (te*te*te) +
0.000086 / (te*te*te*te*te);
} else if (exoatmElevation > -0.575) {
refractionCorrection = 1735.0 + exoatmElevation *
(-518.2 + exoatmElevation * (103.4 +
exoatmElevation * (-12.79 + exoatmElevation * 0.711) ) );
} else {
refractionCorrection = -20.774 / te;
}
refractionCorrection /= 3600.0;
}
return refractionCorrection;
}
NOAAcalc.getSunPosition = function(adate, latitude, longitude){
var JD = calcJD(adate);
var T = calcTimeJulianCent(JD);
//var R = calcSunRadVector(T);
var alpha = calcSunRtAscension(T);
var solarDec = calcSunDeclination(T);
var eqTime = calcEquationOfTime(T);
var solarTimeFix = eqTime + 4.*longitude + adate.getTimezoneOffset();
var trueSolarTime = adate.getHours() * 60. + adate.getMinutes() +
adate.getSeconds()/60. + solarTimeFix;
// in minutes
trueSolarTime -= floor(trueSolarTime/1440)*1440;
var hourAngle = d360(trueSolarTime / 4.0 - 180.0);
var csz = sin(latitude) * sin(solarDec) + cos(latitude) * cos(solarDec) * cos(hourAngle);
if(csz > 1.0){csz = 1.0;} else if(csz < -1.0){csz = -1.0;}
var azimuth, zenith = acos(csz);
var azDenom = cos(latitude) * sin(zenith);
if(abs(azDenom) > 0.001){
var azRad = (sin(latitude) * cos(zenith) - sin(solarDec)) / azDenom;
if(abs(azRad) > 1.0){
if(azRad < 0){azRad = -1.0;} else {azRad = 1.0;}
}
azimuth = acos(azRad);
}else{
if(latitude > 0.0){azimuth = 0.;}else{azimuth = 180.;}
}
var solarZen = zenith - Refract(zenith);
return{
azimuth: azimuth,
altitude: 90. - solarZen,
solarTimeFix: solarTimeFix,
RA: alpha,
Dec: solarDec
};
};
function daynumber(adate){
var Y = adate.getUTCFullYear(), M = adate.getUTCMonth() + 1;
var d = 367 * Y - floor(7*(Y + floor((M+9)/12))/4) + floor(275*M/9) + adate.getUTCDate() - 730530;
d += (adate.getUTCHours() + adate.getUTCMinutes()/60. + adate.getUTCSeconds()/3600.)/24.;
return d;
}
// http://www.stjarnhimlen.se/comp/ppcomp.html
NOAAcalc.getMoonPosition = function(adate, latitude, longitude){
/*
Primary orbital elements of the Moon:
N = 125.1228 - 0.0529538083 * d - longitude of the ascending node
i = 5.1454 - inclination to the ecliptic (plane of the Earth's orbit)
w = 318.0634 + 0.1643573223 * d - argument of perihelion
a = 60.2666 (Earth radii) - semi-major axis, or mean distance from Sun
e = 0.054900 - eccentricity (0=circle, 0-1=ellipse, 1=parabola)
M = 115.3654 + 13.0649929509 * d - mean anomaly (0 at perihelion; increases uniformly with time)
Related orbital elements are:
w1 = N + w - longitude of perihelion
L = M + w1 - mean longitude
q = a*(1-e) - perihelion distance
Q = a*(1+e) - aphelion distance
P = a ^ 1.5 - orbital period (years if a is in AU, astronomical units)
T = Epoch_of_M - (M(deg)/360_deg) / P - time of perihelion
v - true anomaly (angle between position and perihelion)
E - eccentric anomaly
*/
var d = daynumber(adate);
var N = d360(125.1228 - 0.0529538083 * d),
sinN = sin(N), cosN = cos(N),
i = 5.1454,
cosi = cos(i), sini = sin(i),
w = d360(318.0634 + 0.1643573223 * d),
a = 60.2666,
e = 0.054900,
M = d360(115.3654 + 13.0649929509 * d),
E = d360(M + RadToDeg * e * sin(M) * (1.0 + e*cos(M)));
// iterative correction of E
var err = 1, E0=E, iter;
for(iter = 0; iter < 20 && err > 1e-10; ++iter){
var E1 = d360(E - (E - RadToDeg * e * sin(E) - M) / (1 - e * cos(E)));
err = d360(abs(E - E1));
E = E1;
}
var xv = a * (cos(E) - e),
yv = a * sqrt(1.0 - e*e) * sin(E),
v = atan(yv, xv),
r = sqrt(xv*xv + yv*yv);
/*
Compute the planet's position in 3-dimensional space:
xh = r * ( cos(N) * cos(v+w) - sin(N) * sin(v+w) * cos(i) )
yh = r * ( sin(N) * cos(v+w) + cos(N) * sin(v+w) * cos(i) )
zh = r * ( sin(v+w) * sin(i) )
lonecl = atan2( yh, xh )
latecl = atan2( zh, sqrt(xh*xh+yh*yh) )
*/
// calculate sun mean anomaly & longitude; ALL in radians!
var T = calcTimeJulianCent(calcJD(adate));
var Ms = calcSunTrueAnomaly(T), // Mean Anomaly of the Sun
Ls = calcGeomMeanLongSun(T),// Mean Longitude of the Sun
Mm = M, // Mean Anomaly of the Moon
Lm = d360(N + w + M), // Mean longitude of the Moon
D = d360(Lm - Ls), // Mean elongation of the Moon
F = d360(Lm - N); // Argument of latitude for the Moon
var cosvw = cos(v + w), sinvw = sin(v + w),
xh = r * (cosN * cosvw - sinN * sinvw * cosi),
yh = r * (sinN * cosvw + cosN * sinvw * cosi),
zh = r * sinvw * sini,
elon = d360(atan(yh, xh)),
elat = atan(zh, sqrt(xh*xh + yh*yh));
var longAdd =
-1.274 * sin(Mm - 2*D)
+0.658 * sin(2*D)
-0.186 * sin(Ms)
-0.059 * sin(2*Mm - 2*D)
-0.057 * sin(Mm - 2*D + Ms)
+0.053 * sin(Mm + 2*D)
+0.046 * sin(2*D - Ms)
+0.041 * sin(Mm - Ms)
-0.035 * sin(D)
-0.031 * sin(Mm + Ms)
-0.015 * sin(2*F - 2*D)
+0.011 * sin(Mm - 4*D);
var latAdd =
-0.173 * sin(F - 2*D)
-0.055 * sin(Mm - F - 2*D)
-0.046 * sin(Mm + F - 2*D)
+0.033 * sin(F + 2*D)
+0.017 * sin(2*Mm + F);
elon += longAdd;
elat += latAdd;
r -= 0.58 * cos(Mm - 2*D) + 0.46 * cos(2*D);
// Geocentric (Earth-centered) coordinates
var xh = r * cos(elon) * cos(elat),
yh = r * sin(elon) * cos(elat),
zh = r * sin(elat);
var ecl = calcMeanObliquityOfEcliptic(T);
// Equatorial coordinates
var ye = yh * cos(ecl) - zh * sin(ecl),
ze = yh * sin(ecl) + zh * cos(ecl),
RA = d360(atan(ye, xh)),
Dec = atan(ze, sqrt(xh*xh+ye*ye));
// Horizontal coords
var GMST0=(Ls+180),
UT=d-Math.floor(d),
LST = GMST0+UT*360+longitude;
var HA = LST - RA,
x = cos(HA) * cos(Dec),
y = sin(HA) * cos(Dec),
z = sin(Dec),
xhor = x * sin(latitude) - z * cos(latitude),
zhor = x * cos(latitude) + z * sin(latitude),
az = d180(atan(y, xhor)),
zd = 90. - asin(zhor);
var MoonZen = zd - Refract(zd);
return{
azimuth: az,
zenith: MoonZen,
altitude: 90. - MoonZen,
RA: RA,
Dec: Dec
};
}
window.NOAAcalc = NOAAcalc;
}());