1 const paMacros = require('./pa-macros.js');
  2 const paTypes = require('./pa-types.js');
  3 const paUtils = require('./pa-utils.js');
  4 
  5 /**
  6  * Calculate approximate position of the Moon.
  7  */
  8 function approximatePositionOfMoon(lctHour, lctMin, lctSec, isDaylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear) {
  9     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 10 
 11     var l0 = 91.9293359879052;
 12     var p0 = 130.143076320618;
 13     var n0 = 291.682546643194;
 14     var i = 5.145396;
 15 
 16     var gdateDay = paMacros.localCivilTimeGreenwichDay(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 17     var gdateMonth = paMacros.localCivilTimeGreenwichMonth(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 18     var gdateYear = paMacros.localCivilTimeGreenwichYear(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 19 
 20     var utHours = paMacros.localCivilTimeToUniversalTime(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 21     var dDays = paMacros.civilDateToJulianDate(gdateDay, gdateMonth, gdateYear) - paMacros.civilDateToJulianDate(0.0, 1, 2010) + utHours / 24;
 22     var sunLongDeg = paMacros.sunLong(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 23     var sunMeanAnomalyRad = paMacros.sunMeanAnomaly(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 24     var lmDeg = paMacros.unwindDeg(13.1763966 * dDays + l0);
 25     var mmDeg = paMacros.unwindDeg(lmDeg - 0.1114041 * dDays - p0);
 26     var nDeg = paMacros.unwindDeg(n0 - (0.0529539 * dDays));
 27     var evDeg = 1.2739 * Math.sin(paUtils.degreesToRadians(2.0 * (lmDeg - sunLongDeg) - mmDeg));
 28     var aeDeg = 0.1858 * Math.sin(sunMeanAnomalyRad);
 29     var a3Deg = 0.37 * Math.sin(sunMeanAnomalyRad);
 30     var mmdDeg = mmDeg + evDeg - aeDeg - a3Deg;
 31     var ecDeg = 6.2886 * Math.sin(paUtils.degreesToRadians(mmdDeg));
 32     var a4Deg = 0.214 * Math.sin(2.0 * paUtils.degreesToRadians(mmdDeg));
 33     var ldDeg = lmDeg + evDeg + ecDeg - aeDeg + a4Deg;
 34     var vDeg = 0.6583 * Math.sin(2.0 * paUtils.degreesToRadians(ldDeg - sunLongDeg));
 35     var lddDeg = ldDeg + vDeg;
 36     var ndDeg = nDeg - 0.16 * Math.sin(sunMeanAnomalyRad);
 37     var y = Math.sin(paUtils.degreesToRadians(lddDeg - ndDeg)) * Math.cos(paUtils.degreesToRadians(i));
 38     var x = Math.cos(paUtils.degreesToRadians(lddDeg - ndDeg));
 39 
 40     var moonLongDeg = paMacros.unwindDeg(paMacros.degrees(Math.atan2(y, x)) + ndDeg);
 41     var moonLatDeg = paMacros.degrees(Math.asin(Math.sin(paUtils.degreesToRadians(lddDeg - ndDeg)) * Math.sin(paUtils.degreesToRadians(i))));
 42     var moonRAHours1 = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(moonLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear));
 43     var moonDecDeg1 = paMacros.ecDec(moonLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear);
 44 
 45     var moonRAHour = paMacros.decimalHoursHour(moonRAHours1);
 46     var moonRAMin = paMacros.decimalHoursMinute(moonRAHours1);
 47     var moonRASec = paMacros.decimalHoursSecond(moonRAHours1);
 48     var moonDecDeg = paMacros.decimalDegreesDegrees(moonDecDeg1);
 49     var moonDecMin = paMacros.decimalDegreesMinutes(moonDecDeg1);
 50     var moonDecSec = paMacros.decimalDegreesSeconds(moonDecDeg1);
 51 
 52     return [moonRAHour, moonRAMin, moonRASec, moonDecDeg, moonDecMin, moonDecSec];
 53 }
 54 
 55 /**
 56  * Calculate precise position of the Moon.
 57  */
 58 function precisePositionOfMoon(lctHour, lctMin, lctSec, isDaylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear) {
 59     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 60 
 61     var gdateDay = paMacros.localCivilTimeGreenwichDay(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 62     var gdateMonth = paMacros.localCivilTimeGreenwichMonth(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 63     var gdateYear = paMacros.localCivilTimeGreenwichYear(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 64 
 65     var [moonLongDeg, moonLatDeg, moonHorPara] = paMacros.moonLongLatHP(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 66 
 67     var nutationInLongitudeDeg = paMacros.nutatLong(gdateDay, gdateMonth, gdateYear);
 68     var correctedLongDeg = moonLongDeg + nutationInLongitudeDeg;
 69     var earthMoonDistanceKM = 6378.14 / Math.sin(paUtils.degreesToRadians(moonHorPara));
 70     var moonRAHours1 = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(correctedLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear));
 71     var moonDecDeg1 = paMacros.ecDec(correctedLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear);
 72 
 73     var moonRAHour = paMacros.decimalHoursHour(moonRAHours1);
 74     var moonRAMin = paMacros.decimalHoursMinute(moonRAHours1);
 75     var moonRASec = paMacros.decimalHoursSecond(moonRAHours1);
 76     var moonDecDeg = paMacros.decimalDegreesDegrees(moonDecDeg1);
 77     var moonDecMin = paMacros.decimalDegreesMinutes(moonDecDeg1);
 78     var moonDecSec = paMacros.decimalDegreesSeconds(moonDecDeg1);
 79     var earthMoonDistKM = paUtils.round(earthMoonDistanceKM, 0);
 80     var moonHorParallaxDeg = paUtils.round(moonHorPara, 6);
 81 
 82     return [moonRAHour, moonRAMin, moonRASec, moonDecDeg, moonDecMin, moonDecSec, earthMoonDistKM, moonHorParallaxDeg];
 83 }
 84 
 85 /**
 86  * Calculate Moon phase and position angle of bright limb.
 87  */
 88 function moonPhase(lctHour, lctMin, lctSec, isDaylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear, accuracyLevel) {
 89     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 90 
 91     var gdateDay = paMacros.localCivilTimeGreenwichDay(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 92     var gdateMonth = paMacros.localCivilTimeGreenwichMonth(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 93     var gdateYear = paMacros.localCivilTimeGreenwichYear(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 94 
 95     var sunLongDeg = paMacros.sunLong(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 96     var [moonLongDeg, moonLatDeg, moonHorPara] = paMacros.moonLongLatHP(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
 97     var dRad = paUtils.degreesToRadians(moonLongDeg - sunLongDeg);
 98 
 99     var moonPhase1 = (accuracyLevel == paTypes.AccuracyLevel.Precise) ? paMacros.moonPhase(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear) : (1.0 - Math.cos(dRad)) / 2.0;
100 
101     var sunRARad = paUtils.degreesToRadians(paMacros.ecRA(sunLongDeg, 0, 0, 0, 0, 0, gdateDay, gdateMonth, gdateYear));
102     var moonRARad = paUtils.degreesToRadians(paMacros.ecRA(moonLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear));
103     var sunDecRad = paUtils.degreesToRadians(paMacros.ecDec(sunLongDeg, 0, 0, 0, 0, 0, gdateDay, gdateMonth, gdateYear));
104     var moonDecRad = paUtils.degreesToRadians(paMacros.ecDec(moonLongDeg, 0, 0, moonLatDeg, 0, 0, gdateDay, gdateMonth, gdateYear));
105 
106     var y = Math.cos(sunDecRad) * Math.sin(sunRARad - moonRARad);
107     var x = Math.cos(moonDecRad) * Math.sin(sunDecRad) - Math.sin(moonDecRad) * Math.cos(sunDecRad) * Math.cos(sunRARad - moonRARad);
108 
109     var chiDeg = paMacros.degrees(Math.atan2(y, x));
110 
111     var moonPhase = paUtils.round(moonPhase1, 2);
112     var paBrightLimbDeg = paUtils.round(chiDeg, 2);
113 
114     return [moonPhase, paBrightLimbDeg];
115 }
116 
117 /**
118  * Calculate new moon and full moon instances.
119  */
120 function timesOfNewMoonAndFullMoon(isDaylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear) {
121     var daylightSaving = (isDaylightSaving) ? 1 : 0;
122 
123     var jdOfNewMoonDays = paMacros.newMoon(daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
124     var jdOfFullMoonDays = paMacros.fullMoon(3, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
125 
126     var gDateOfNewMoonDay = paMacros.julianDateDay(jdOfNewMoonDays);
127     var integerDay1 = Math.floor(gDateOfNewMoonDay);
128     var gDateOfNewMoonMonth = paMacros.julianDateMonth(jdOfNewMoonDays);
129     var gDateOfNewMoonYear = paMacros.julianDateYear(jdOfNewMoonDays);
130 
131     var gDateOfFullMoonDay = paMacros.julianDateDay(jdOfFullMoonDays);
132     var integerDay2 = Math.floor(gDateOfFullMoonDay);
133     var gDateOfFullMoonMonth = paMacros.julianDateMonth(jdOfFullMoonDays);
134     var gDateOfFullMoonYear = paMacros.julianDateYear(jdOfFullMoonDays);
135 
136     var utOfNewMoonHours = 24.0 * (gDateOfNewMoonDay - integerDay1);
137     var utOfFullMoonHours = 24.0 * (gDateOfFullMoonDay - integerDay2);
138     var lctOfNewMoonHours = paMacros.universalTimeToLocalCivilTime(utOfNewMoonHours + 0.008333, 0, 0, daylightSaving, zoneCorrectionHours, integerDay1, gDateOfNewMoonMonth, gDateOfNewMoonYear);
139     var lctOfFullMoonHours = paMacros.universalTimeToLocalCivilTime(utOfFullMoonHours + 0.008333, 0, 0, daylightSaving, zoneCorrectionHours, integerDay2, gDateOfFullMoonMonth, gDateOfFullMoonYear);
140 
141     var nmLocalTimeHour = paMacros.decimalHoursHour(lctOfNewMoonHours);
142     var nmLocalTimeMin = paMacros.decimalHoursMinute(lctOfNewMoonHours);
143     var nmLocalDateDay = paMacros.universalTime_LocalCivilDay(utOfNewMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay1, gDateOfNewMoonMonth, gDateOfNewMoonYear);
144     var nmLocalDateMonth = paMacros.universalTime_LocalCivilMonth(utOfNewMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay1, gDateOfNewMoonMonth, gDateOfNewMoonYear);
145     var nmLocalDateYear = paMacros.universalTime_LocalCivilYear(utOfNewMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay1, gDateOfNewMoonMonth, gDateOfNewMoonYear);
146     var fmLocalTimeHour = paMacros.decimalHoursHour(lctOfFullMoonHours);
147     var fmLocalTimeMin = paMacros.decimalHoursMinute(lctOfFullMoonHours);
148     var fmLocalDateDay = paMacros.universalTime_LocalCivilDay(utOfFullMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay2, gDateOfFullMoonMonth, gDateOfFullMoonYear);
149     var fmLocalDateMonth = paMacros.universalTime_LocalCivilMonth(utOfFullMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay2, gDateOfFullMoonMonth, gDateOfFullMoonYear);
150     var fmLocalDateYear = paMacros.universalTime_LocalCivilYear(utOfFullMoonHours, 0, 0, daylightSaving, zoneCorrectionHours, integerDay2, gDateOfFullMoonMonth, gDateOfFullMoonYear);
151 
152     return [nmLocalTimeHour, nmLocalTimeMin, nmLocalDateDay, nmLocalDateMonth, nmLocalDateYear, fmLocalTimeHour, fmLocalTimeMin, fmLocalDateDay, fmLocalDateMonth, fmLocalDateYear];
153 }
154 
155 /**
156  * Calculate Moon's distance, angular diameter, and horizontal parallax.
157  */
158 function moonDistAngDiamHorParallax(lctHour, lctMin, lctSec, isDaylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear) {
159     var daylightSaving = (isDaylightSaving) ? 1 : 0;
160 
161     var moonDistance = paMacros.moonDist(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
162     var moonAngularDiameter = paMacros.moonSize(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
163     var moonHorizontalParallax = paMacros.moonHP(lctHour, lctMin, lctSec, daylightSaving, zoneCorrectionHours, localDateDay, localDateMonth, localDateYear);
164 
165     var earthMoonDist = paUtils.round(moonDistance, 0);
166     var angDiameterDeg = paMacros.decimalDegreesDegrees(moonAngularDiameter + 0.008333);
167     var angDiameterMin = paMacros.decimalDegreesMinutes(moonAngularDiameter + 0.008333);
168     var horParallaxDeg = paMacros.decimalDegreesDegrees(moonHorizontalParallax);
169     var horParallaxMin = paMacros.decimalDegreesMinutes(moonHorizontalParallax);
170     var horParallaxSec = paMacros.decimalDegreesSeconds(moonHorizontalParallax);
171 
172     return [earthMoonDist, angDiameterDeg, angDiameterMin, horParallaxDeg, horParallaxMin, horParallaxSec];
173 }
174 
175 /**
176  * Calculate date/time of local moonrise and moonset.
177  */
178 function moonriseAndMoonset(localDateDay, localDateMonth, localDateYear, isDaylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg) {
179     var daylightSaving = (isDaylightSaving) ? 1 : 0;
180 
181     var localTimeOfMoonriseHours = paMacros.moonRiseLCT(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
182     var [moonRiseLCResult_dy1, moonRiseLCResult_mn1, moonRiseLCResult_yr1] = paMacros.moonRiseLcDMY(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
183     var localAzimuthDeg1 = paMacros.moonRiseAz(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
184 
185     var localTimeOfMoonsetHours = paMacros.moonSetLCT(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
186     var [moonSetLCResult_dy1, moonSetLCResult_mn1, moonSetLCResult_yr1] = paMacros.moonSetLcDMY(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
187     var localAzimuthDeg2 = paMacros.moonSetAz(localDateDay, localDateMonth, localDateYear, daylightSaving, zoneCorrectionHours, geogLongDeg, geogLatDeg);
188 
189     var mrLTHour = paMacros.decimalHoursHour(localTimeOfMoonriseHours + 0.008333);
190     var mrLTMin = paMacros.decimalHoursMinute(localTimeOfMoonriseHours + 0.008333);
191     var mrLocalDateDay = moonRiseLCResult_dy1;
192     var mrLocalDateMonth = moonRiseLCResult_mn1;
193     var mrLocalDateYear = moonRiseLCResult_yr1;
194     var mrAzimuthDeg = paUtils.round(localAzimuthDeg1, 2);
195     var msLTHour = paMacros.decimalHoursHour(localTimeOfMoonsetHours + 0.008333);
196     var msLTMin = paMacros.decimalHoursMinute(localTimeOfMoonsetHours + 0.008333);
197     var msLocalDateDay = moonSetLCResult_dy1;
198     var msLocalDateMonth = moonSetLCResult_mn1;
199     var msLocalDateYear = moonSetLCResult_yr1;
200     var msAzimuthDeg = paUtils.round(localAzimuthDeg2, 2);
201 
202     return [mrLTHour, mrLTMin, mrLocalDateDay, mrLocalDateMonth, mrLocalDateYear, mrAzimuthDeg, msLTHour, msLTMin, msLocalDateDay, msLocalDateMonth, msLocalDateYear, msAzimuthDeg];
203 }
204 
205 
206 module.exports = {
207     approximatePositionOfMoon,
208     precisePositionOfMoon,
209     moonPhase,
210     timesOfNewMoonAndFullMoon,
211     moonDistAngDiamHorParallax,
212     moonriseAndMoonset
213 };