1 /*
  2  * Astro.js - Static functions to support astronomical calculations
  3  *
  4  * Copyright © 2014-2015, 2018, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 // !data astro
 21 
 22 /*
 23  * These routines were derived from a public domain set of JavaScript
 24  * functions for positional astronomy by John Walker of Fourmilab,
 25  * September 1999.
 26  */
 27 
 28 var ilib = require("./ilib.js");
 29 var Utils = require("./Utils.js");
 30 var SearchUtils = require("./SearchUtils.js");
 31 
 32 var GregorianDate = require("./GregorianDate.js");
 33 var RataDie = require("./RataDie.js");
 34 var GregRataDie = require("./GregRataDie.js");
 35 
 36 var Astro = {};
 37 
 38 /**
 39  * Load in all the data needed for astrological calculations.
 40  *
 41  * @private
 42  * @param {boolean} sync
 43  * @param {*} loadParams
 44  * @param {function(*)|undefined} callback
 45  */
 46 Astro.initAstro = function(sync, loadParams, callback) {
 47     if (!ilib.data.astro) {
 48         Utils.loadData({
 49             object: "Astro",
 50             name: "astro.json", // countries in their own language
 51             locale: "-", // only need to load the root file
 52             nonLocale: true,
 53             sync: sync,
 54             loadParams: loadParams,
 55             callback: ilib.bind(this, function(astroData) {
 56                 /**
 57                  * @type {{
 58                  *      _EquinoxpTerms:Array.<number>,
 59                  *      _JDE0tab1000:Array.<number>,
 60                  *      _JDE0tab2000:Array.<number>,
 61                  *      _deltaTtab:Array.<number>,
 62                  *      _oterms:Array.<number>,
 63                  *      _nutArgMult:Array.<number>,
 64                  *      _nutArgCoeff:Array.<number>,
 65                  *      _nutCoeffA:Array.<number>,
 66                  *      _nutCoeffB:Array.<number>,
 67                  *      _coeff19th:Array.<number>,
 68                  *      _coeff18th:Array.<number>,
 69                  *      _solarLongCoeff:Array.<number>,
 70                  *      _solarLongMultipliers:Array.<number>,
 71                  *      _solarLongAddends:Array.<number>,
 72                  *      _meanMoonCoeff:Array.<number>,
 73                  *      _elongationCoeff:Array.<number>,
 74                  *      _solarAnomalyCoeff:Array.<number>,
 75                  *      _lunarAnomalyCoeff:Array.<number>,
 76                  *      _moonFromNodeCoeff:Array.<number>,
 77                  *      _eCoeff:Array.<number>,
 78                  *      _lunarElongationLongCoeff:Array.<number>,
 79                  *      _solarAnomalyLongCoeff:Array.<number>,
 80                  *      _lunarAnomalyLongCoeff:Array.<number>,
 81                  *      _moonFromNodeLongCoeff:Array.<number>,
 82                  *      _sineCoeff:Array.<number>,
 83                  *      _nmApproxCoeff:Array.<number>,
 84                  *      _nmCapECoeff:Array.<number>,
 85                  *      _nmSolarAnomalyCoeff:Array.<number>,
 86                  *      _nmLunarAnomalyCoeff:Array.<number>,
 87                  *      _nmMoonArgumentCoeff:Array.<number>,
 88                  *      _nmCapOmegaCoeff:Array.<number>,
 89                  *      _nmEFactor:Array.<number>,
 90                  *      _nmSolarCoeff:Array.<number>,
 91                  *      _nmLunarCoeff:Array.<number>,
 92                  *      _nmMoonCoeff:Array.<number>,
 93                  *      _nmSineCoeff:Array.<number>,
 94                  *      _nmAddConst:Array.<number>,
 95                  *      _nmAddCoeff:Array.<number>,
 96                  *      _nmAddFactor:Array.<number>,
 97                  *      _nmExtra:Array.<number>
 98                  *  }}
 99                  */
100                  ilib.data.astro = astroData;
101                 if (callback && typeof(callback) === 'function') {
102                     callback(astroData);
103                 }
104             })
105         });
106     } else {
107         if (callback && typeof(callback) === 'function') {
108             callback(ilib.data.astro);
109         }
110     }
111 };
112 
113 /**
114  * Convert degrees to radians.
115  *
116  * @static
117  * @protected
118  * @param {number} d angle in degrees
119  * @return {number} angle in radians
120  */
121 Astro._dtr = function(d) {
122     return (d * Math.PI) / 180.0;
123 };
124 
125 /**
126  * Convert radians to degrees.
127  *
128  * @static
129  * @protected
130  * @param {number} r angle in radians
131  * @return {number} angle in degrees
132  */
133 Astro._rtd = function(r) {
134     return (r * 180.0) / Math.PI;
135 };
136 
137 /**
138  * Return the cosine of an angle given in degrees.
139  * @static
140  * @protected
141  * @param {number} d angle in degrees
142  * @return {number} cosine of the angle.
143  */
144 Astro._dcos = function(d) {
145     return Math.cos(Astro._dtr(d));
146 };
147 
148 /**
149  * Return the sine of an angle given in degrees.
150  * @static
151  * @protected
152  * @param {number} d angle in degrees
153  * @return {number} sine of the angle.
154  */
155 Astro._dsin = function(d) {
156     return Math.sin(Astro._dtr(d));
157 };
158 
159 /**
160  * Return the tan of an angle given in degrees.
161  * @static
162  * @protected
163  * @param {number} d angle in degrees
164  * @return {number} tan of the angle.
165  */
166 Astro._dtan = function(d) {
167     return Math.tan(Astro._dtr(d));
168 };
169 
170 /**
171  * Range reduce angle in degrees.
172  *
173  * @static
174  * @param {number} a angle to reduce
175  * @return {number} the reduced angle
176  */
177 Astro._fixangle = function(a) {
178     return a - 360.0 * (Math.floor(a / 360.0));
179 };
180 
181 /**
182  * Range reduce angle in radians.
183  *
184  * @static
185  * @protected
186  * @param {number} a angle to reduce
187  * @return {number} the reduced angle
188  */
189 Astro._fixangr = function(a) {
190     return a - (2 * Math.PI) * (Math.floor(a / (2 * Math.PI)));
191 };
192 
193 /**
194  * Determine the Julian Ephemeris Day of an equinox or solstice.  The "which"
195  * argument selects the item to be computed:
196  *
197  * <ul>
198  * <li>0   March equinox
199  * <li>1   June solstice
200  * <li>2   September equinox
201  * <li>3   December solstice
202  * </ul>
203  *
204  * @static
205  * @protected
206  * @param {number} year Gregorian year to calculate for
207  * @param {number} which Which equinox or solstice to calculate
208  */
209 Astro._equinox = function(year, which) {
210     var deltaL, i, j, JDE0, JDE, JDE0tab, S, T, W, Y;
211 
212     /*  Initialize terms for mean equinox and solstices.  We
213         have two sets: one for years prior to 1000 and a second
214         for subsequent years.  */
215 
216     if (year < 1000) {
217         JDE0tab = ilib.data.astro._JDE0tab1000;
218         Y = year / 1000;
219     } else {
220         JDE0tab = ilib.data.astro._JDE0tab2000;
221         Y = (year - 2000) / 1000;
222     }
223 
224     JDE0 = JDE0tab[which][0] + (JDE0tab[which][1] * Y)
225             + (JDE0tab[which][2] * Y * Y) + (JDE0tab[which][3] * Y * Y * Y)
226             + (JDE0tab[which][4] * Y * Y * Y * Y);
227 
228     //document.debug.log.value += "JDE0 = " + JDE0 + "\n";
229 
230     T = (JDE0 - 2451545.0) / 36525;
231     //document.debug.log.value += "T = " + T + "\n";
232     W = (35999.373 * T) - 2.47;
233     //document.debug.log.value += "W = " + W + "\n";
234     deltaL = 1 + (0.0334 * Astro._dcos(W)) + (0.0007 * Astro._dcos(2 * W));
235     //document.debug.log.value += "deltaL = " + deltaL + "\n";
236 
237     //  Sum the periodic terms for time T
238 
239     S = 0;
240     j = 0;
241     for (i = 0; i < 24; i++) {
242         S += ilib.data.astro._EquinoxpTerms[j]
243                 * Astro._dcos(ilib.data.astro._EquinoxpTerms[j + 1] + (ilib.data.astro._EquinoxpTerms[j + 2] * T));
244         j += 3;
245     }
246 
247     //document.debug.log.value += "S = " + S + "\n";
248     //document.debug.log.value += "Corr = " + ((S * 0.00001) / deltaL) + "\n";
249 
250     JDE = JDE0 + ((S * 0.00001) / deltaL);
251 
252     return JDE;
253 };
254 
255 /*
256  * The table of observed Delta T values at the beginning of
257  * years from 1620 through 2014 as found in astro.json is taken from
258  * http://www.staff.science.uu.nl/~gent0113/deltat/deltat.htm
259  * and
260  * ftp://maia.usno.navy.mil/ser7/deltat.data
261  */
262 
263 /**
264  * Determine the difference, in seconds, between dynamical time and universal time.
265  *
266  * @static
267  * @protected
268  * @param {number} year to calculate the difference for
269  * @return {number} difference in seconds between dynamical time and universal time
270  */
271 Astro._deltat = function (year) {
272     var dt, f, i, t;
273 
274     if ((year >= 1620) && (year <= 2014)) {
275         i = Math.floor(year - 1620);
276         f = (year - 1620) - i; /* Fractional part of year */
277         dt = ilib.data.astro._deltaTtab[i] + ((ilib.data.astro._deltaTtab[i + 1] - ilib.data.astro._deltaTtab[i]) * f);
278     } else {
279         t = (year - 2000) / 100;
280         if (year < 948) {
281             dt = 2177 + (497 * t) + (44.1 * t * t);
282         } else {
283             dt = 102 + (102 * t) + (25.3 * t * t);
284             if ((year > 2000) && (year < 2100)) {
285                 dt += 0.37 * (year - 2100);
286             }
287         }
288     }
289     return dt;
290 };
291 
292 /**
293  * Calculate the obliquity of the ecliptic for a given
294  * Julian date.  This uses Laskar's tenth-degree
295  * polynomial fit (J. Laskar, Astronomy and
296  * Astrophysics, Vol. 157, page 68 [1986]) which is
297  * accurate to within 0.01 arc second between AD 1000
298  * and AD 3000, and within a few seconds of arc for
299  * +/-10000 years around AD 2000.  If we're outside the
300  * range in which this fit is valid (deep time) we
301  * simply return the J2000 value of the obliquity, which
302  * happens to be almost precisely the mean.
303  *
304  * @static
305  * @protected
306  * @param {number} jd Julian Day to calculate the obliquity for
307  * @return {number} the obliquity
308  */
309 Astro._obliqeq = function (jd) {
310     var eps, u, v, i;
311 
312      v = u = (jd - 2451545.0) / 3652500.0;
313 
314      eps = 23 + (26 / 60.0) + (21.448 / 3600.0);
315 
316      if (Math.abs(u) < 1.0) {
317          for (i = 0; i < 10; i++) {
318              eps += (ilib.data.astro._oterms[i] / 3600.0) * v;
319              v *= u;
320          }
321      }
322      return eps;
323 };
324 
325 /**
326  * Return the position of the sun.  We return
327  * intermediate values because they are useful in a
328  * variety of other contexts.
329  * @static
330  * @protected
331  * @param {number} jd find the position of sun on this Julian Day
332  * @return {Object} the position of the sun and many intermediate
333  * values
334  */
335 Astro._sunpos = function(jd) {
336     var ret = {},
337         T, T2, T3, Omega, epsilon, epsilon0;
338 
339     T = (jd - 2451545.0) / 36525.0;
340     //document.debug.log.value += "Sunpos.  T = " + T + "\n";
341     T2 = T * T;
342     T3 = T * T2;
343     ret.meanLongitude = Astro._fixangle(280.46646 + 36000.76983 * T + 0.0003032 * T2);
344     //document.debug.log.value += "ret.meanLongitude = " + ret.meanLongitude + "\n";
345     ret.meanAnomaly = Astro._fixangle(357.52911 + (35999.05029 * T) - 0.0001537 * T2 - 0.00000048 * T3);
346     //document.debug.log.value += "ret.meanAnomaly = " + ret.meanAnomaly + "\n";
347     ret.eccentricity = 0.016708634 - 0.000042037 * T - 0.0000001267 * T2;
348     //document.debug.log.value += "e = " + e + "\n";
349     ret.equationOfCenter = ((1.914602 - 0.004817 * T - 0.000014 * T2) * Astro._dsin(ret.meanAnomaly))
350             + ((0.019993 - 0.000101 * T) * Astro._dsin(2 * ret.meanAnomaly))
351             + (0.000289 * Astro._dsin(3 * ret.meanAnomaly));
352     //document.debug.log.value += "ret.equationOfCenter = " + ret.equationOfCenter + "\n";
353     ret.sunLongitude = ret.meanLongitude + ret.equationOfCenter;
354     //document.debug.log.value += "ret.sunLongitude = " + ret.sunLongitude + "\n";
355     //ret.sunAnomaly = ret.meanAnomaly + ret.equationOfCenter;
356     //document.debug.log.value += "ret.sunAnomaly = " + ret.sunAnomaly + "\n";
357     // ret.sunRadius = (1.000001018 * (1 - (ret.eccentricity * ret.eccentricity))) / (1 + (ret.eccentricity * Astro._dcos(ret.sunAnomaly)));
358     //document.debug.log.value += "ret.sunRadius = " + ret.sunRadius + "\n";
359     Omega = 125.04 - (1934.136 * T);
360     //document.debug.log.value += "Omega = " + Omega + "\n";
361     ret.apparentLong = ret.sunLongitude + (-0.00569) + (-0.00478 * Astro._dsin(Omega));
362     //document.debug.log.value += "ret.apparentLong = " + ret.apparentLong + "\n";
363     epsilon0 = Astro._obliqeq(jd);
364     //document.debug.log.value += "epsilon0 = " + epsilon0 + "\n";
365     epsilon = epsilon0 + (0.00256 * Astro._dcos(Omega));
366     //document.debug.log.value += "epsilon = " + epsilon + "\n";
367     //ret.rightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon0) * Astro._dsin(ret.sunLongitude), Astro._dcos(ret.sunLongitude))));
368     //document.debug.log.value += "ret.rightAscension = " + ret.rightAscension + "\n";
369     // ret.declination = Astro._rtd(Math.asin(Astro._dsin(epsilon0) * Astro._dsin(ret.sunLongitude)));
370     ////document.debug.log.value += "ret.declination = " + ret.declination + "\n";
371     ret.inclination = Astro._fixangle(23.4392911 - 0.013004167 * T - 0.00000016389 * T2 + 0.0000005036 * T3);
372     ret.apparentRightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon) * Astro._dsin(ret.apparentLong), Astro._dcos(ret.apparentLong))));
373     //document.debug.log.value += "ret.apparentRightAscension = " + ret.apparentRightAscension + "\n";
374     //ret.apparentDeclination = Astro._rtd(Math.asin(Astro._dsin(epsilon) * Astro._dsin(ret.apparentLong)));
375     //document.debug.log.value += "ret.apparentDecliation = " + ret.apparentDecliation + "\n";
376 
377     // Angular quantities are expressed in decimal degrees
378     return ret;
379 };
380 
381 /**
382  * Calculate the nutation in longitude, deltaPsi, and obliquity,
383  * deltaEpsilon for a given Julian date jd. Results are returned as an object
384  * giving deltaPsi and deltaEpsilon in degrees.
385  *
386  * @static
387  * @protected
388  * @param {number} jd calculate the nutation of this Julian Day
389  * @return {Object} the deltaPsi and deltaEpsilon of the nutation
390  */
391 Astro._nutation = function(jd) {
392     var i, j,
393         t = (jd - 2451545.0) / 36525.0,
394         t2, t3, to10,
395         ta = [],
396         dp = 0,
397         de = 0,
398         ang,
399         ret = {};
400 
401     t3 = t * (t2 = t * t);
402 
403     /*
404      * Calculate angles. The correspondence between the elements of our array
405      * and the terms cited in Meeus are:
406      *
407      * ta[0] = D ta[0] = M ta[2] = M' ta[3] = F ta[4] = \Omega
408      *
409      */
410 
411     ta[0] = Astro._dtr(297.850363 + 445267.11148 * t - 0.0019142 * t2 + t3 / 189474.0);
412     ta[1] = Astro._dtr(357.52772 + 35999.05034 * t - 0.0001603 * t2 - t3 / 300000.0);
413     ta[2] = Astro._dtr(134.96298 + 477198.867398 * t + 0.0086972 * t2 + t3 / 56250.0);
414     ta[3] = Astro._dtr(93.27191 + 483202.017538 * t - 0.0036825 * t2 + t3 / 327270);
415     ta[4] = Astro._dtr(125.04452 - 1934.136261 * t + 0.0020708 * t2 + t3 / 450000.0);
416 
417     /*
418      * Range reduce the angles in case the sine and cosine functions don't do it
419      * as accurately or quickly.
420      */
421 
422     for (i = 0; i < 5; i++) {
423         ta[i] = Astro._fixangr(ta[i]);
424     }
425 
426     to10 = t / 10.0;
427     for (i = 0; i < 63; i++) {
428         ang = 0;
429         for (j = 0; j < 5; j++) {
430             if (ilib.data.astro._nutArgMult[(i * 5) + j] != 0) {
431                 ang += ilib.data.astro._nutArgMult[(i * 5) + j] * ta[j];
432             }
433         }
434         dp += (ilib.data.astro._nutArgCoeff[(i * 4) + 0] + ilib.data.astro._nutArgCoeff[(i * 4) + 1] * to10) * Math.sin(ang);
435         de += (ilib.data.astro._nutArgCoeff[(i * 4) + 2] + ilib.data.astro._nutArgCoeff[(i * 4) + 3] * to10) * Math.cos(ang);
436     }
437 
438     /*
439      * Return the result, converting from ten thousandths of arc seconds to
440      * radians in the process.
441      */
442 
443     ret.deltaPsi = dp / (3600.0 * 10000.0);
444     ret.deltaEpsilon = de / (3600.0 * 10000.0);
445 
446     return ret;
447 };
448 
449 /**
450  * Returns the equation of time as a fraction of a day.
451  *
452  * @static
453  * @protected
454  * @param {number} jd the Julian Day of the day to calculate for
455  * @return {number} the equation of time for the given day
456  */
457 Astro._equationOfTime = function(jd) {
458     var alpha, deltaPsi, E, epsilon, L0, tau, pos;
459 
460     // 2451545.0 is the Julian day of J2000 epoch
461     // 365250.0 is the number of days in a Julian millenium
462     tau = (jd - 2451545.0) / 365250.0;
463     //console.log("equationOfTime.  tau = " + tau);
464     L0 = 280.4664567 + (360007.6982779 * tau) + (0.03032028 * tau * tau)
465             + ((tau * tau * tau) / 49931)
466             + (-((tau * tau * tau * tau) / 15300))
467             + (-((tau * tau * tau * tau * tau) / 2000000));
468     //console.log("L0 = " + L0);
469     L0 = Astro._fixangle(L0);
470     //console.log("L0 = " + L0);
471     pos = Astro._sunpos(jd);
472     alpha = pos.apparentRightAscension;
473     //console.log("alpha = " + alpha);
474     var nut = Astro._nutation(jd);
475     deltaPsi = nut.deltaPsi;
476     //console.log("deltaPsi = " + deltaPsi);
477     epsilon = Astro._obliqeq(jd) + nut.deltaEpsilon;
478     //console.log("epsilon = " + epsilon);
479     //console.log("L0 - 0.0057183 = " + (L0 - 0.0057183));
480     //console.log("L0 - 0.0057183 - alpha = " + (L0 - 0.0057183 - alpha));
481     //console.log("deltaPsi * cos(epsilon) = " + deltaPsi * Astro._dcos(epsilon));
482 
483     E = L0 - 0.0057183 - alpha + deltaPsi * Astro._dcos(epsilon);
484     // if alpha and L0 are in different quadrants, then renormalize
485     // so that the difference between them is in the right range
486     if (E > 180) {
487         E -= 360;
488     }
489     //console.log("E = " + E);
490     // E = E - 20.0 * (Math.floor(E / 20.0));
491     E = E * 4;
492     //console.log("Efixed = " + E);
493     E = E / (24 * 60);
494     //console.log("Eday = " + E);
495 
496     return E;
497 };
498 
499 /**
500  * @private
501  * @static
502  */
503 Astro._poly = function(x, coefficients) {
504     var result = coefficients[0];
505     var xpow = x;
506     for (var i = 1; i < coefficients.length; i++) {
507         result += coefficients[i] * xpow;
508         xpow *= x;
509     }
510     return result;
511 };
512 
513 /**
514  * Calculate the UTC RD from the local RD given "zone" number of minutes
515  * worth of offset.
516  *
517  * @static
518  * @protected
519  * @param {number} local RD of the locale time, given in any calendar
520  * @param {number} zone number of minutes of offset from UTC for the time zone
521  * @return {number} the UTC equivalent of the local RD
522  */
523 Astro._universalFromLocal = function(local, zone) {
524     return local - zone / 1440;
525 };
526 
527 /**
528  * Calculate the local RD from the UTC RD given "zone" number of minutes
529  * worth of offset.
530  *
531  * @static
532  * @protected
533  * @param {number} local RD of the locale time, given in any calendar
534  * @param {number} zone number of minutes of offset from UTC for the time zone
535  * @return {number} the UTC equivalent of the local RD
536  */
537 Astro._localFromUniversal = function(local, zone) {
538     return local + zone / 1440;
539 };
540 
541 /**
542  * @private
543  * @static
544  * @param {number} c julian centuries of the date to calculate
545  * @return {number} the aberration
546  */
547 Astro._aberration = function(c) {
548     return 9.74e-05 * Astro._dcos(177.63 + 35999.01847999999 * c) - 0.005575;
549 };
550 
551 /**
552  * @private
553  *
554 ilib.data.astro._nutCoeffA = [124.90, -1934.134, 0.002063];
555 ilib.data.astro._nutCoeffB q= [201.11, 72001.5377, 0.00057];
556 */
557 
558 /**
559  * @private
560  * @static
561  * @param {number} c julian centuries of the date to calculate
562  * @return {number} the nutation for the given julian century in radians
563  */
564 Astro._nutation2 = function(c) {
565     var a = Astro._poly(c, ilib.data.astro._nutCoeffA);
566     var b = Astro._poly(c, ilib.data.astro._nutCoeffB);
567     // return -0.0000834 * Astro._dsin(a) - 0.0000064 * Astro._dsin(b);
568     return -0.004778 * Astro._dsin(a) - 0.0003667 * Astro._dsin(b);
569 };
570 
571 /**
572  * @static
573  * @private
574  */
575 Astro._ephemerisCorrection = function(jd) {
576     var year = GregorianDate._calcYear(jd - 1721424.5);
577 
578     if (1988 <= year && year <= 2019) {
579         return (year - 1933) / 86400;
580     }
581 
582     if (1800 <= year && year <= 1987) {
583         var jul1 = new GregRataDie({
584             year: year,
585             month: 7,
586             day: 1,
587             hour: 0,
588             minute: 0,
589             second: 0
590         });
591         // 693596 is the rd of Jan 1, 1900
592         var theta = (jul1.getRataDie() - 693596) / 36525;
593         return Astro._poly(theta, (1900 <= year) ? ilib.data.astro._coeff19th : ilib.data.astro._coeff18th);
594     }
595 
596     if (1620 <= year && year <= 1799) {
597         year -= 1600;
598         return (196.58333 - 4.0675 * year + 0.0219167 * year * year) / 86400;
599     }
600 
601     // 660724 is the rd of Jan 1, 1810
602     var jan1 = new GregRataDie({
603         year: year,
604         month: 1,
605         day: 1,
606         hour: 0,
607         minute: 0,
608         second: 0
609     });
610     // var x = 0.5 + (jan1.getRataDie() - 660724);
611     var x = 0.5 + (jan1.getRataDie() - 660724);
612 
613     return ((x * x / 41048480) - 15) / 86400;
614 };
615 
616 /**
617  * @static
618  * @private
619  */
620 Astro._ephemerisFromUniversal = function(jd) {
621     return jd + Astro._ephemerisCorrection(jd);
622 };
623 
624 /**
625  * @static
626  * @private
627  */
628 Astro._universalFromEphemeris = function(jd) {
629     return jd - Astro._ephemerisCorrection(jd);
630 };
631 
632 /**
633  * @static
634  * @private
635  */
636 Astro._julianCenturies = function(jd) {
637     // 2451545.0 is the Julian day of J2000 epoch
638     // 730119.5 is the Gregorian RD of J2000 epoch
639     // 36525.0 is the number of days in a Julian century
640     return (Astro._ephemerisFromUniversal(jd) - 2451545.0) / 36525.0;
641 };
642 
643 /**
644  * Calculate the solar longitude
645  *
646  * @static
647  * @protected
648  * @param {number} jd julian day of the date to calculate the longitude for
649  * @return {number} the solar longitude in degrees
650  */
651 Astro._solarLongitude = function(jd) {
652     var c = Astro._julianCenturies(jd),
653         longitude = 0,
654         len = ilib.data.astro._solarLongCoeff.length;
655 
656     for (var i = 0; i < len; i++) {
657         longitude += ilib.data.astro._solarLongCoeff[i] *
658             Astro._dsin(ilib.data.astro._solarLongAddends[i] + ilib.data.astro._solarLongMultipliers[i] * c);
659     }
660     longitude *= 5.729577951308232e-06;
661     longitude += 282.77718340000001 + 36000.769537439999 * c;
662     longitude += Astro._aberration(c) + Astro._nutation2(c);
663     return Astro._fixangle(longitude);
664 };
665 
666 /**
667  * @static
668  * @protected
669  * @param {number} jd
670  * @return {number}
671  */
672 Astro._lunarLongitude = function (jd) {
673     var c = Astro._julianCenturies(jd),
674         meanMoon = Astro._fixangle(Astro._poly(c, ilib.data.astro._meanMoonCoeff)),
675         elongation = Astro._fixangle(Astro._poly(c, ilib.data.astro._elongationCoeff)),
676         solarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._solarAnomalyCoeff)),
677         lunarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._lunarAnomalyCoeff)),
678         moonNode = Astro._fixangle(Astro._poly(c, ilib.data.astro._moonFromNodeCoeff)),
679         e = Astro._poly(c, ilib.data.astro._eCoeff);
680 
681     var sum = 0;
682     for (var i = 0; i < ilib.data.astro._lunarElongationLongCoeff.length; i++) {
683         var x = ilib.data.astro._solarAnomalyLongCoeff[i];
684 
685         sum += ilib.data.astro._sineCoeff[i] * Math.pow(e, Math.abs(x)) *
686             Astro._dsin(ilib.data.astro._lunarElongationLongCoeff[i] * elongation + x * solarAnomaly +
687                 ilib.data.astro._lunarAnomalyLongCoeff[i] * lunarAnomaly +
688                 ilib.data.astro._moonFromNodeLongCoeff[i] * moonNode);
689     }
690     var longitude = sum / 1000000;
691     var venus = 3958.0 / 1000000 * Astro._dsin(119.75 + c * 131.84899999999999);
692     var jupiter = 318.0 / 1000000 * Astro._dsin(53.090000000000003 + c * 479264.28999999998);
693     var flatEarth = 1962.0 / 1000000 * Astro._dsin(meanMoon - moonNode);
694 
695     return Astro._fixangle(meanMoon + longitude + venus + jupiter + flatEarth + Astro._nutation2(c));
696 };
697 
698 /**
699  * @static
700  * @protected
701  * @param {number} n
702  * @return {number} julian day of the n'th new moon
703  */
704 Astro._newMoonTime = function(n) {
705     var k = n - 24724;
706     var c = k / 1236.8499999999999;
707     var approx = Astro._poly(c, ilib.data.astro._nmApproxCoeff);
708     var capE = Astro._poly(c, ilib.data.astro._nmCapECoeff);
709     var solarAnomaly = Astro._poly(c, ilib.data.astro._nmSolarAnomalyCoeff);
710     var lunarAnomaly = Astro._poly(c, ilib.data.astro._nmLunarAnomalyCoeff);
711     var moonArgument = Astro._poly(c, ilib.data.astro._nmMoonArgumentCoeff);
712     var capOmega = Astro._poly(c, ilib.data.astro._nmCapOmegaCoeff);
713     var correction = -0.00017 * Astro._dsin(capOmega);
714     for (var i = 0; i < ilib.data.astro._nmSineCoeff.length; i++) {
715         correction = correction + ilib.data.astro._nmSineCoeff[i] * Math.pow(capE, ilib.data.astro._nmEFactor[i]) *
716         Astro._dsin(ilib.data.astro._nmSolarCoeff[i] * solarAnomaly +
717                 ilib.data.astro._nmLunarCoeff[i] * lunarAnomaly +
718                 ilib.data.astro._nmMoonCoeff[i] * moonArgument);
719     }
720     var additional = 0;
721     for (var i = 0; i < ilib.data.astro._nmAddConst.length; i++) {
722         additional = additional + ilib.data.astro._nmAddFactor[i] *
723         Astro._dsin(ilib.data.astro._nmAddConst[i] + ilib.data.astro._nmAddCoeff[i] * k);
724     }
725     var extra = 0.000325 * Astro._dsin(Astro._poly(c, ilib.data.astro._nmExtra));
726     return Astro._universalFromEphemeris(approx + correction + extra + additional + RataDie.gregorianEpoch);
727 };
728 
729 /**
730  * @static
731  * @protected
732  * @param {number} jd
733  * @return {number}
734  */
735 Astro._lunarSolarAngle = function(jd) {
736     var lunar = Astro._lunarLongitude(jd);
737     var solar = Astro._solarLongitude(jd)
738     return Astro._fixangle(lunar - solar);
739 };
740 
741 /**
742  * @static
743  * @protected
744  * @param {number} jd
745  * @return {number}
746  */
747 Astro._newMoonBefore = function (jd) {
748     var phase = Astro._lunarSolarAngle(jd);
749     // 11.450086114414322 is the julian day of the 0th full moon
750     // 29.530588853000001 is the average length of a month
751     var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360) - 1;
752     var current, last;
753     current = last = Astro._newMoonTime(guess);
754     while (current < jd) {
755         guess++;
756         last = current;
757         current = Astro._newMoonTime(guess);
758     }
759     return last;
760 };
761 
762 /**
763  * @static
764  * @protected
765  * @param {number} jd
766  * @return {number}
767  */
768 Astro._newMoonAtOrAfter = function (jd) {
769     var phase = Astro._lunarSolarAngle(jd);
770     // 11.450086114414322 is the julian day of the 0th full moon
771     // 29.530588853000001 is the average length of a month
772     var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360);
773     var current;
774     while ((current = Astro._newMoonTime(guess)) < jd) {
775         guess++;
776     }
777     return current;
778 };
779 
780 /**
781  * @static
782  * @protected
783  * @param {number} jd JD to calculate from
784  * @param {number} longitude longitude to seek
785  * @returns {number} the JD of the next time that the solar longitude
786  * is a multiple of the given longitude
787  */
788 Astro._nextSolarLongitude = function(jd, longitude) {
789     var rate = 365.242189 / 360.0;
790     var tau = jd + rate * Astro._fixangle(longitude - Astro._solarLongitude(jd));
791     var start = Math.max(jd, tau - 5.0);
792     var end = tau + 5.0;
793 
794     return SearchUtils.bisectionSearch(0, start, end, 1e-6, function (l) {
795         return 180 - Astro._fixangle(Astro._solarLongitude(l) - longitude);
796     });
797 };
798 
799 /**
800  * Floor the julian day to midnight of the current julian day.
801  *
802  * @static
803  * @protected
804  * @param {number} jd the julian to round
805  * @return {number} the jd floored to the midnight of the julian day
806  */
807 Astro._floorToJD = function(jd) {
808     return Math.floor(jd - 0.5) + 0.5;
809 };
810 
811 /**
812  * Floor the julian day to midnight of the current julian day.
813  *
814  * @static
815  * @protected
816  * @param {number} jd the julian to round
817  * @return {number} the jd floored to the midnight of the julian day
818  */
819 Astro._ceilToJD = function(jd) {
820     return Math.ceil(jd + 0.5) - 0.5;
821 };
822 
823 module.exports = Astro;
824