1 const paMacros = require('./pa-macros.js');
  2 const paTypes = require('./pa-types.js');
  3 const paUtils = require('./pa-utils.js');
  4 
  5 /**
  6  * Convert an Angle (degrees, minutes, and seconds) to Decimal Degrees
  7  */
  8 function angleToDecimalDegrees(degrees, minutes, seconds) {
  9     var a = Math.abs(seconds) / 60;
 10     var b = (Math.abs(minutes) + a) / 60;
 11     var c = Math.abs(degrees) + b;
 12     var d = (degrees < 0 || minutes < 0 || seconds < 0) ? -c : c;
 13 
 14     return d;
 15 }
 16 
 17 /**
 18  * Convert Decimal Degrees to an Angle (degrees, minutes, and seconds)
 19  */
 20 function decimalDegreesToAngle(decimalDegrees) {
 21     var unsignedDecimal = Math.abs(decimalDegrees);
 22     var totalSeconds = unsignedDecimal * 3600;
 23     var seconds2DP = paUtils.round(totalSeconds % 60, 2);
 24     var correctedSeconds = (seconds2DP == 60) ? 0 : seconds2DP;
 25     var correctedRemainder = (seconds2DP == 60) ? totalSeconds + 60 : totalSeconds;
 26     var minutes = Math.floor(correctedRemainder / 60) % 60;
 27     var unsignedDegrees = Math.floor(correctedRemainder / 3600);
 28     var signedDegrees = (decimalDegrees < 0) ? -1 * unsignedDegrees : unsignedDegrees;
 29 
 30     return [signedDegrees, minutes, Math.floor(correctedSeconds)];
 31 }
 32 
 33 /**
 34  * Convert Right Ascension to Hour Angle
 35  */
 36 function rightAscensionToHourAngle(raHours, raMinutes, raSeconds, lctHours, lctMinutes, lctSeconds, isDaylightSavings, zoneCorrection, localDay, localMonth, localYear, geographicalLongitude) {
 37     var daylightSaving = (isDaylightSavings) ? 1 : 0;
 38 
 39     var hourAngle = paMacros.rightAscensionToHourAngle(raHours, raMinutes, raSeconds, lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear, geographicalLongitude);
 40 
 41     var hourAngleHours = paMacros.decimalHoursHour(hourAngle);
 42     var hourAngleMinutes = paMacros.decimalHoursMinute(hourAngle);
 43     var hourAngleSeconds = paMacros.decimalHoursSecond(hourAngle);
 44 
 45     return [hourAngleHours, hourAngleMinutes, hourAngleSeconds];
 46 }
 47 
 48 /**
 49  * Convert Hour Angle to Right Ascension
 50  */
 51 function hourAngleToRightAscension(hourAngleHours, hourAngleMinutes, hourAngleSeconds, lctHours, lctMinutes, lctSeconds, isDaylightSaving, zoneCorrection, localDay, localMonth, localYear, geographicalLongitude) {
 52     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 53 
 54     var rightAscension = paMacros.hourAngleToRightAscension(hourAngleHours, hourAngleMinutes, hourAngleSeconds, lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear, geographicalLongitude);
 55 
 56     var rightAscensionHours = paMacros.decimalHoursHour(rightAscension);
 57     var rightAscensionMinutes = paMacros.decimalHoursMinute(rightAscension);
 58     var rightAscensionSeconds = paMacros.decimalHoursSecond(rightAscension);
 59 
 60     return [rightAscensionHours, rightAscensionMinutes, rightAscensionSeconds];
 61 }
 62 
 63 /**
 64  * Convert Equatorial Coordinates to Horizon Coordinates
 65  */
 66 function equatorialCoordinatesToHorizonCoordinates(hourAngleHours, hourAngleMinutes, hourAngleSeconds, declinationDegrees, declinationMinutes, declinationSeconds, geographicalLatitude) {
 67     var azimuthInDecimalDegrees = paMacros.equatorialCoordinatesToAzimuth(hourAngleHours, hourAngleMinutes, hourAngleSeconds, declinationDegrees, declinationMinutes, declinationSeconds, geographicalLatitude);
 68 
 69     var altitudeInDecimalDegrees = paMacros.equatorialCoordinatesToAltitude(hourAngleHours, hourAngleMinutes, hourAngleSeconds, declinationDegrees, declinationMinutes, declinationSeconds, geographicalLatitude);
 70 
 71     var azimuthDegrees = paMacros.decimalDegreesDegrees(azimuthInDecimalDegrees);
 72     var azimuthMinutes = paMacros.decimalDegreesMinutes(azimuthInDecimalDegrees);
 73     var azimuthSeconds = paMacros.decimalDegreesSeconds(azimuthInDecimalDegrees);
 74 
 75     var altitudeDegrees = paMacros.decimalDegreesDegrees(altitudeInDecimalDegrees);
 76     var altitudeMinutes = paMacros.decimalDegreesMinutes(altitudeInDecimalDegrees);
 77     var altitudeSeconds = paMacros.decimalDegreesSeconds(altitudeInDecimalDegrees);
 78 
 79     return [azimuthDegrees, azimuthMinutes, azimuthSeconds, altitudeDegrees, altitudeMinutes, altitudeSeconds];
 80 }
 81 
 82 /**
 83  * Convert Horizon Coordinates to Equatorial Coordinates
 84  */
 85 function horizonCoordinatesToEquatorialCoordinates(azimuthDegrees, azimuthMinutes, azimuthSeconds, altitudeDegrees, altitudeMinutes, altitudeSeconds, geographicalLatitude) {
 86     var hourAngleInDecimalDegrees = paMacros.horizonCoordinatesToHourAngle(azimuthDegrees, azimuthMinutes, azimuthSeconds, altitudeDegrees, altitudeMinutes, altitudeSeconds, geographicalLatitude);
 87 
 88     var declinationInDecimalDegrees = paMacros.horizonCoordinatesToDeclination(azimuthDegrees, azimuthMinutes, azimuthSeconds, altitudeDegrees, altitudeMinutes, altitudeSeconds, geographicalLatitude);
 89 
 90     var hourAngleHours = paMacros.decimalHoursHour(hourAngleInDecimalDegrees);
 91     var hourAngleMinutes = paMacros.decimalHoursMinute(hourAngleInDecimalDegrees);
 92     var hourAngleSeconds = paMacros.decimalHoursSecond(hourAngleInDecimalDegrees);
 93 
 94     var declinationDegrees = paMacros.decimalDegreesDegrees(declinationInDecimalDegrees);
 95     var declinationMinutes = paMacros.decimalDegreesMinutes(declinationInDecimalDegrees);
 96     var declinationSeconds = paMacros.decimalDegreesSeconds(declinationInDecimalDegrees);
 97 
 98     return [hourAngleHours, hourAngleMinutes, hourAngleSeconds, declinationDegrees, declinationMinutes, declinationSeconds];
 99 }
100 
101 /**
102  * Calculate Mean Obliquity of the Ecliptic for a Greenwich Date
103  */
104 function meanObliquityOfTheEcliptic(greenwichDay, greenwichMonth, greenwichYear) {
105     var jd = paMacros.civilDateToJulianDate(greenwichDay, greenwichMonth, greenwichYear);
106     var mjd = jd - 2451545;
107     var t = mjd / 36525;
108     var de1 = t * (46.815 + t * (0.0006 - (t * 0.00181)));
109     var de2 = de1 / 3600;
110 
111     return 23.439292 - de2;
112 }
113 
114 /**
115  * Convert Ecliptic Coordinates to Equatorial Coordinates
116  */
117 function eclipticCoordinateToEquatorialCoordinate(eclipticLongitudeDegrees, eclipticLongitudeMinutes, eclipticLongitudeSeconds, eclipticLatitudeDegrees, eclipticLatitudeMinutes, eclipticLatitudeSeconds, greenwichDay, greenwichMonth, greenwichYear) {
118     var eclonDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(eclipticLongitudeDegrees, eclipticLongitudeMinutes, eclipticLongitudeSeconds);
119     var eclatDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(eclipticLatitudeDegrees, eclipticLatitudeMinutes, eclipticLatitudeSeconds);
120     var eclonRad = paUtils.degreesToRadians(eclonDeg);
121     var eclatRad = paUtils.degreesToRadians(eclatDeg);
122     var obliqDeg = paMacros.obliq(greenwichDay, greenwichMonth, greenwichYear);
123     var obliqRad = paUtils.degreesToRadians(obliqDeg);
124     var sinDec = Math.sin(eclatRad) * Math.cos(obliqRad) + Math.cos(eclatRad) * Math.sin(obliqRad) * Math.sin(eclonRad);
125     var decRad = Math.asin(sinDec);
126     var decDeg = paMacros.degrees(decRad);
127     var y = Math.sin(eclonRad) * Math.cos(obliqRad) - Math.tan(eclatRad) * Math.sin(obliqRad);
128     var x = Math.cos(eclonRad);
129     var raRad = Math.atan2(y, x);
130     var raDeg1 = paMacros.degrees(raRad);
131     var raDeg2 = raDeg1 - 360 * Math.floor(raDeg1 / 360);
132     var raHours = paMacros.decimalDegreesToDegreeHours(raDeg2);
133 
134     var outRAHours = paMacros.decimalHoursHour(raHours);
135     var outRAMinutes = paMacros.decimalHoursMinute(raHours);
136     var outRASeconds = paMacros.decimalHoursSecond(raHours);
137     var outDecDegrees = paMacros.decimalDegreesDegrees(decDeg);
138     var outDecMinutes = paMacros.decimalDegreesMinutes(decDeg);
139     var outDecSeconds = paMacros.decimalDegreesSeconds(decDeg);
140 
141     return [outRAHours, outRAMinutes, outRASeconds, outDecDegrees, outDecMinutes, outDecSeconds];
142 }
143 
144 /**
145  * Convert Equatorial Coordinates to Ecliptic Coordinates
146  */
147 function equatorialCoordinateToEclipticCoordinate(raHours, raMinutes, raSeconds, decDegrees, decMinutes, decSeconds, gwDay, gwMonth, gwYear) {
148     var raDeg = paMacros.degreeHoursToDecimalDegrees(paMacros.HMStoDH(raHours, raMinutes, raSeconds));
149     var decDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(decDegrees, decMinutes, decSeconds);
150     var raRad = paUtils.degreesToRadians(raDeg);
151     var decRad = paUtils.degreesToRadians(decDeg);
152     var obliqDeg = paMacros.obliq(gwDay, gwMonth, gwYear);
153     var obliqRad = paUtils.degreesToRadians(obliqDeg);
154     var sinEclLat = Math.sin(decRad) * Math.cos(obliqRad) - Math.cos(decRad) * Math.sin(obliqRad) * Math.sin(raRad);
155     var eclLatRad = Math.asin(sinEclLat);
156     var eclLatDeg = paMacros.degrees(eclLatRad);
157     var y = Math.sin(raRad) * Math.cos(obliqRad) + Math.tan(decRad) * Math.sin(obliqRad);
158     var x = Math.cos(raRad);
159     var eclLongRad = Math.atan2(y, x);
160     var eclLongDeg1 = paMacros.degrees(eclLongRad);
161     var eclLongDeg2 = eclLongDeg1 - 360 * Math.floor(eclLongDeg1 / 360);
162 
163     var outEclLongDeg = paMacros.decimalDegreesDegrees(eclLongDeg2);
164     var outEclLongMin = paMacros.decimalDegreesMinutes(eclLongDeg2);
165     var outEclLongSec = paMacros.decimalDegreesSeconds(eclLongDeg2);
166     var outEclLatDeg = paMacros.decimalDegreesDegrees(eclLatDeg);
167     var outEclLatMin = paMacros.decimalDegreesMinutes(eclLatDeg);
168     var outEclLatSec = paMacros.decimalDegreesSeconds(eclLatDeg);
169 
170     return [outEclLongDeg, outEclLongMin, outEclLongSec, outEclLatDeg, outEclLatMin, outEclLatSec];
171 }
172 
173 /**
174  * 
175  * Convert Equatorial Coordinates to Galactic Coordinates
176  */
177 function equatorialCoordinateToGalacticCoordinate(raHours, raMinutes, raSeconds, decDegrees, decMinutes, decSeconds) {
178     var raDeg = paMacros.degreeHoursToDecimalDegrees(paMacros.HMStoDH(raHours, raMinutes, raSeconds));
179     var decDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(decDegrees, decMinutes, decSeconds);
180     var raRad = paUtils.degreesToRadians(raDeg);
181     var decRad = paUtils.degreesToRadians(decDeg);
182     var sinB = Math.cos(decRad) * Math.cos(paUtils.degreesToRadians(27.4)) * Math.cos(raRad - paUtils.degreesToRadians(192.25)) + Math.sin(decRad) * Math.sin(paUtils.degreesToRadians(27.4));
183     var bRadians = Math.asin(sinB);
184     var bDeg = paMacros.degrees(bRadians);
185     var y = Math.sin(decRad) - sinB * Math.sin(paUtils.degreesToRadians(27.4));
186     var x = Math.cos(decRad) * Math.sin(raRad - paUtils.degreesToRadians(192.25)) * Math.cos(paUtils.degreesToRadians(27.4));
187     var longDeg1 = paMacros.degrees(Math.atan2(y, x)) + 33;
188     var longDeg2 = longDeg1 - 360 * Math.floor(longDeg1 / 360);
189 
190     var galLongDeg = paMacros.decimalDegreesDegrees(longDeg2);
191     var galLongMin = paMacros.decimalDegreesMinutes(longDeg2);
192     var galLongSec = paMacros.decimalDegreesSeconds(longDeg2);
193     var galLatDeg = paMacros.decimalDegreesDegrees(bDeg);
194     var galLatMin = paMacros.decimalDegreesMinutes(bDeg);
195     var galLatSec = paMacros.decimalDegreesSeconds(bDeg);
196 
197     return [galLongDeg, galLongMin, galLongSec, galLatDeg, galLatMin, galLatSec];
198 }
199 
200 /**
201  * Convert Galactic Coordinates to Equatorial Coordinates
202  */
203 function galacticCoordinateToEquatorialCoordinate(galLongDeg, galLongMin, galLongSec, galLatDeg, galLatMin, galLatSec) {
204     var glongDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(galLongDeg, galLongMin, galLongSec);
205     var glatDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(galLatDeg, galLatMin, galLatSec);
206     var glongRad = paUtils.degreesToRadians(glongDeg);
207     var glatRad = paUtils.degreesToRadians(glatDeg);
208     var sinDec = Math.cos(glatRad) * Math.cos(paUtils.degreesToRadians(27.4)) * Math.sin(glongRad - paUtils.degreesToRadians(33.0)) + Math.sin(glatRad) * Math.sin(paUtils.degreesToRadians(27.4));
209     var decRadians = Math.asin(sinDec);
210     var decDeg = paMacros.degrees(decRadians);
211     var y = Math.cos(glatRad) * Math.cos(glongRad - paUtils.degreesToRadians(33.0));
212     var x = Math.sin(glatRad) * Math.cos(paUtils.degreesToRadians(27.4)) - Math.cos(glatRad) * Math.sin(paUtils.degreesToRadians(27.4)) * Math.sin(glongRad - paUtils.degreesToRadians(33.0));
213 
214     var raDeg1 = paMacros.degrees(Math.atan2(y, x)) + 192.25;
215     var raDeg2 = raDeg1 - 360 * Math.floor(raDeg1 / 360);
216     var raHours1 = paMacros.decimalDegreesToDegreeHours(raDeg2);
217 
218     var raHours = paMacros.decimalHoursHour(raHours1);
219     var raMinutes = paMacros.decimalHoursMinute(raHours1);
220     var raSeconds = paMacros.decimalHoursSecond(raHours1);
221     var decDegrees = paMacros.decimalDegreesDegrees(decDeg);
222     var decMinutes = paMacros.decimalDegreesMinutes(decDeg);
223     var decSeconds = paMacros.decimalDegreesSeconds(decDeg);
224 
225     return [raHours, raMinutes, raSeconds, decDegrees, decMinutes, decSeconds];
226 }
227 
228 /**
229  * Calculate the angle between two celestial objects
230  */
231 function angleBetweenTwoObjects(raLong1HourDeg, raLong1Min, raLong1Sec, decLat1Deg, decLat1Min, decLat1Sec, raLong2HourDeg, raLong2Min, raLong2Sec, decLat2Deg, decLat2Min, decLat2Sec, hourOrDegree) {
232     var raLong1Decimal = (hourOrDegree == paTypes.AngleMeasure.Hours) ? paMacros.HMStoDH(raLong1HourDeg, raLong1Min, raLong1Sec) : paMacros.degreesMinutesSecondsToDecimalDegrees(raLong1HourDeg, raLong1Min, raLong1Sec);
233     var raLong1Deg = (hourOrDegree == paTypes.AngleMeasure.Hours) ? paMacros.degreeHoursToDecimalDegrees(raLong1Decimal) : raLong1Decimal;
234 
235     var raLong1Rad = paUtils.degreesToRadians(raLong1Deg);
236     var decLat1Deg1 = paMacros.degreesMinutesSecondsToDecimalDegrees(decLat1Deg, decLat1Min, decLat1Sec);
237     var decLat1Rad = paUtils.degreesToRadians(decLat1Deg1);
238 
239     var raLong2Decimal = (hourOrDegree == paTypes.AngleMeasure.Hours) ? paMacros.HMStoDH(raLong2HourDeg, raLong2Min, raLong2Sec) : paMacros.degreesMinutesSecondsToDecimalDegrees(raLong2HourDeg, raLong2Min, raLong2Sec);
240     var raLong2Deg = (hourOrDegree == paTypes.AngleMeasure.Hours) ? paMacros.degreeHoursToDecimalDegrees(raLong2Decimal) : raLong2Decimal;
241     var raLong2Rad = paUtils.degreesToRadians(raLong2Deg);
242     var decLat2Deg1 = paMacros.degreesMinutesSecondsToDecimalDegrees(decLat2Deg, decLat2Min, decLat2Sec);
243     var decLat2Rad = paUtils.degreesToRadians(decLat2Deg1);
244 
245     var cosD = Math.sin(decLat1Rad) * Math.sin(decLat2Rad) + Math.cos(decLat1Rad) * Math.cos(decLat2Rad) * Math.cos(raLong1Rad - raLong2Rad);
246     var dRad = Math.acos(cosD);
247     var dDeg = paMacros.degrees(dRad);
248 
249     var angleDeg = paMacros.decimalDegreesDegrees(dDeg);
250     var angleMin = paMacros.decimalDegreesMinutes(dDeg);
251     var angleSec = paMacros.decimalDegreesSeconds(dDeg);
252 
253     return [angleDeg, angleMin, angleSec];
254 }
255 
256 /**
257  * Calculate rising and setting times for an object.
258  */
259 function risingAndSetting(raHours, raMinutes, raSeconds, decDeg, decMin, decSec, gwDateDay, gwDateMonth, gwDateYear, geogLongDeg, geogLatDeg, vertShiftDeg) {
260     var raHours1 = paMacros.HMStoDH(raHours, raMinutes, raSeconds);
261     var decRad = paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(decDeg, decMin, decSec));
262     var verticalDisplRadians = paUtils.degreesToRadians(vertShiftDeg);
263     var geoLatRadians = paUtils.degreesToRadians(geogLatDeg);
264     var cosH = -(Math.sin(verticalDisplRadians) + Math.sin(geoLatRadians) * Math.sin(decRad)) / (Math.cos(geoLatRadians) * Math.cos(decRad));
265     var hHours = paMacros.decimalDegreesToDegreeHours(paMacros.degrees(Math.acos(cosH)));
266     var lstRiseHours = (raHours1 - hHours) - 24 * Math.floor((raHours1 - hHours) / 24);
267     var lstSetHours = (raHours1 + hHours) - 24 * Math.floor((raHours1 + hHours) / 24);
268     var aDeg = paMacros.degrees(Math.acos((Math.sin(decRad) + Math.sin(verticalDisplRadians) * Math.sin(geoLatRadians)) / (Math.cos(verticalDisplRadians) * Math.cos(geoLatRadians))));
269     var azRiseDeg = aDeg - 360 * Math.floor(aDeg / 360);
270     var azSetDeg = (360 - aDeg) - 360 * Math.floor((360 - aDeg) / 360);
271     var utRiseHours1 = paMacros.greenwichSiderealTimeToUniversalTime(paMacros.localSiderealTimeToGreenwichSiderealTime(lstRiseHours, 0, 0, geogLongDeg), 0, 0, gwDateDay, gwDateMonth, gwDateYear);
272     var utSetHours1 = paMacros.greenwichSiderealTimeToUniversalTime(paMacros.localSiderealTimeToGreenwichSiderealTime(lstSetHours, 0, 0, geogLongDeg), 0, 0, gwDateDay, gwDateMonth, gwDateYear);
273     var utRiseAdjustedHours = utRiseHours1 + 0.008333;
274     var utSetAdjustedHours = utSetHours1 + 0.008333;
275 
276     var riseSetStatus = paTypes.RiseSetStatus.OK;
277     if (cosH > 1)
278         riseSetStatus = paTypes.RiseSetStatus.NeverRises;
279     if (cosH < -1)
280         riseSetStatus = paTypes.RiseSetStatus.Circumpolar;
281 
282     var utRiseHour = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paMacros.decimalHoursHour(utRiseAdjustedHours) : 0;
283     var utRiseMin = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paMacros.decimalHoursMinute(utRiseAdjustedHours) : 0;
284     var utSetHour = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paMacros.decimalHoursHour(utSetAdjustedHours) : 0;
285     var utSetMin = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paMacros.decimalHoursMinute(utSetAdjustedHours) : 0;
286     var azRise = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paUtils.round(azRiseDeg, 2) : 0;
287     var azSet = (riseSetStatus == paTypes.RiseSetStatus.OK) ? paUtils.round(azSetDeg, 2) : 0;
288 
289     return [riseSetStatus, utRiseHour, utRiseMin, utSetHour, utSetMin, azRise, azSet];
290 }
291 
292 /**
293  * Calculate precession (corrected coordinates between two epochs)
294  */
295 function correctForPrecession(raHour, raMinutes, raSeconds, decDeg, decMinutes, decSeconds, epoch1Day, epoch1Month, epoch1Year, epoch2Day, epoch2Month, epoch2Year) {
296     var ra1Rad = paUtils.degreesToRadians(paMacros.degreeHoursToDecimalDegrees(paMacros.HMStoDH(raHour, raMinutes, raSeconds)));
297     var dec1Rad = paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(decDeg, decMinutes, decSeconds));
298     var tCenturies = (paMacros.civilDateToJulianDate(epoch1Day, epoch1Month, epoch1Year) - 2415020) / 36525;
299     var mSec = 3.07234 + (0.00186 * tCenturies);
300     var nArcsec = 20.0468 - (0.0085 * tCenturies);
301     var nYears = (paMacros.civilDateToJulianDate(epoch2Day, epoch2Month, epoch2Year) - paMacros.civilDateToJulianDate(epoch1Day, epoch1Month, epoch1Year)) / 365.25;
302     var s1Hours = ((mSec + (nArcsec * Math.sin(ra1Rad) * Math.tan(dec1Rad) / 15)) * nYears) / 3600;
303     var ra2Hours = paMacros.HMStoDH(raHour, raMinutes, raSeconds) + s1Hours;
304     var s2Deg = (nArcsec * Math.cos(ra1Rad) * nYears) / 3600;
305     var dec2Deg = paMacros.degreesMinutesSecondsToDecimalDegrees(decDeg, decMinutes, decSeconds) + s2Deg;
306 
307     var correctedRAHour = paMacros.decimalHoursHour(ra2Hours);
308     var correctedRAMinutes = paMacros.decimalHoursMinute(ra2Hours);
309     var correctedRASeconds = paMacros.decimalHoursSecond(ra2Hours);
310     var correctedDecDeg = paMacros.decimalDegreesDegrees(dec2Deg);
311     var correctedDecMinutes = paMacros.decimalDegreesMinutes(dec2Deg);
312     var correctedDecSeconds = paMacros.decimalDegreesSeconds(dec2Deg);
313 
314     return [correctedRAHour, correctedRAMinutes, correctedRASeconds, correctedDecDeg, correctedDecMinutes, correctedDecSeconds];
315 }
316 
317 /**
318  * Calculate nutation for two values: ecliptic longitude and obliquity, for a Greenwich date.
319  */
320 function nutationInEclipticLongitudeAndObliquity(greenwichDay, greenwichMonth, greenwichYear) {
321     var jdDays = paMacros.civilDateToJulianDate(greenwichDay, greenwichMonth, greenwichYear);
322     var tCenturies = (jdDays - 2415020) / 36525;
323     var aDeg = 100.0021358 * tCenturies;
324     var l1Deg = 279.6967 + (0.000303 * tCenturies * tCenturies);
325     var lDeg1 = l1Deg + 360 * (aDeg - Math.floor(aDeg));
326     var lDeg2 = lDeg1 - 360 * Math.floor(lDeg1 / 360);
327     var lRad = paUtils.degreesToRadians(lDeg2);
328     var bDeg = 5.372617 * tCenturies;
329     var nDeg1 = 259.1833 - 360 * (bDeg - Math.floor(bDeg));
330     var nDeg2 = nDeg1 - 360 * (Math.floor(nDeg1 / 360));
331     var nRad = paUtils.degreesToRadians(nDeg2);
332     var nutInLongArcsec = -17.2 * Math.sin(nRad) - 1.3 * Math.sin(2 * lRad);
333     var nutInOblArcsec = 9.2 * Math.cos(nRad) + 0.5 * Math.cos(2 * lRad);
334 
335     var nutInLongDeg = nutInLongArcsec / 3600;
336     var nutInOblDeg = nutInOblArcsec / 3600;
337 
338     return [nutInLongDeg, nutInOblDeg];
339 }
340 
341 /**
342  * Correct ecliptic coordinates for the effects of aberration.
343  */
344 function correctForAberration(utHour, utMinutes, utSeconds, gwDay, gwMonth, gwYear, trueEclLongDeg, trueEclLongMin, trueEclLongSec, trueEclLatDeg, trueEclLatMin, trueEclLatSec) {
345     var trueLongDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(trueEclLongDeg, trueEclLongMin, trueEclLongSec);
346     var trueLatDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(trueEclLatDeg, trueEclLatMin, trueEclLatSec);
347     var sunTrueLongDeg = paMacros.sunLong(utHour, utMinutes, utSeconds, 0, 0, gwDay, gwMonth, gwYear);
348     var dlongArcsec = -20.5 * Math.cos(paUtils.degreesToRadians(sunTrueLongDeg - trueLongDeg)) / Math.cos(paUtils.degreesToRadians(trueLatDeg));
349     var dlatArcsec = -20.5 * Math.sin(paUtils.degreesToRadians(sunTrueLongDeg - trueLongDeg)) * Math.sin(paUtils.degreesToRadians(trueLatDeg));
350     var apparentLongDeg = trueLongDeg + (dlongArcsec / 3600);
351     var apparentLatDeg = trueLatDeg + (dlatArcsec / 3600);
352 
353     var apparentEclLongDeg = paMacros.decimalDegreesDegrees(apparentLongDeg);
354     var apparentEclLongMin = paMacros.decimalDegreesMinutes(apparentLongDeg);
355     var apparentEclLongSec = paMacros.decimalDegreesSeconds(apparentLongDeg);
356     var apparentEclLatDeg = paMacros.decimalDegreesDegrees(apparentLatDeg);
357     var apparentEclLatMin = paMacros.decimalDegreesMinutes(apparentLatDeg);
358     var apparentEclLatSec = paMacros.decimalDegreesSeconds(apparentLatDeg);
359 
360     return [apparentEclLongDeg, apparentEclLongMin, apparentEclLongSec, apparentEclLatDeg, apparentEclLatMin, apparentEclLatSec];
361 }
362 
363 /**
364  * Calculate corrected RA/Dec, accounting for atmospheric refraction.
365  */
366 function atmosphericRefraction(trueRAHour, trueRAMin, trueRASec, trueDecDeg, trueDecMin, trueDecSec, coordinateType, geogLongDeg, geogLatDeg, daylightSavingHours, timezoneHours, lcdDay, lcdMonth, lcdYear, lctHour, lctMin, lctSec, atmosphericPressureMbar, atmosphericTemperatureCelsius) {
367     var haHour = paMacros.rightAscensionToHourAngle(trueRAHour, trueRAMin, trueRASec, lctHour, lctMin, lctSec, daylightSavingHours, timezoneHours, lcdDay, lcdMonth, lcdYear, geogLongDeg);
368     var azimuthDeg = paMacros.equatorialCoordinatesToAzimuth(haHour, 0, 0, trueDecDeg, trueDecMin, trueDecSec, geogLatDeg);
369     var altitudeDeg = paMacros.equatorialCoordinatesToAltitude(haHour, 0, 0, trueDecDeg, trueDecMin, trueDecSec, geogLatDeg);
370     var correctedAltitudeDeg = paMacros.refract(altitudeDeg, coordinateType, atmosphericPressureMbar, atmosphericTemperatureCelsius);
371 
372     var correctedHAHour = paMacros.horizonCoordinatesToHourAngle(azimuthDeg, 0, 0, correctedAltitudeDeg, 0, 0, geogLatDeg);
373     var correctedRAHour1 = paMacros.hourAngleToRightAscension(correctedHAHour, 0, 0, lctHour, lctMin, lctSec, daylightSavingHours, timezoneHours, lcdDay, lcdMonth, lcdYear, geogLongDeg);
374     var correctedDecDeg1 = paMacros.horizonCoordinatesToDeclination(azimuthDeg, 0, 0, correctedAltitudeDeg, 0, 0, geogLatDeg);
375 
376     var correctedRAHour = paMacros.decimalHoursHour(correctedRAHour1);
377     var correctedRAMin = paMacros.decimalHoursMinute(correctedRAHour1);
378     var correctedRASec = paMacros.decimalHoursSecond(correctedRAHour1);
379     var correctedDecDeg = paMacros.decimalDegreesDegrees(correctedDecDeg1);
380     var correctedDecMin = paMacros.decimalDegreesMinutes(correctedDecDeg1);
381     var correctedDecSec = paMacros.decimalDegreesSeconds(correctedDecDeg1);
382 
383     return [correctedRAHour, correctedRAMin, correctedRASec, correctedDecDeg, correctedDecMin, correctedDecSec];
384 }
385 
386 /**
387  * Calculate corrected RA/Dec, accounting for geocentric parallax.
388  */
389 function correctionsForGeocentricParallax(raHour, raMin, raSec, decDeg, decMin, decSec, coordinateType, equatorialHorParallaxDeg, geogLongDeg, geogLatDeg, heightM, daylightSaving, timezoneHours, lcdDay, lcdMonth, lcdYear, lctHour, lctMin, lctSec) {
390     var haHours = paMacros.rightAscensionToHourAngle(raHour, raMin, raSec, lctHour, lctMin, lctSec, daylightSaving, timezoneHours, lcdDay, lcdMonth, lcdYear, geogLongDeg);
391 
392     var correctedHAHours = paMacros.parallaxHA(haHours, 0, 0, decDeg, decMin, decSec, coordinateType, geogLatDeg, heightM, equatorialHorParallaxDeg);
393 
394     var correctedRAHours = paMacros.hourAngleToRightAscension(correctedHAHours, 0, 0, lctHour, lctMin, lctSec, daylightSaving, timezoneHours, lcdDay, lcdMonth, lcdYear, geogLongDeg);
395 
396     var correctedDecDeg1 = paMacros.parallaxDec(haHours, 0, 0, decDeg, decMin, decSec, coordinateType, geogLatDeg, heightM, equatorialHorParallaxDeg);
397 
398     var correctedRAHour = paMacros.decimalHoursHour(correctedRAHours);
399     var correctedRAMin = paMacros.decimalHoursMinute(correctedRAHours);
400     var correctedRASec = paMacros.decimalHoursSecond(correctedRAHours);
401     var correctedDecDeg = paMacros.decimalDegreesDegrees(correctedDecDeg1);
402     var correctedDecMin = paMacros.decimalDegreesMinutes(correctedDecDeg1);
403     var correctedDecSec = paMacros.decimalDegreesSeconds(correctedDecDeg1);
404 
405     return [correctedRAHour, correctedRAMin, correctedRASec, correctedDecDeg, correctedDecMin, correctedDecSec];
406 }
407 
408 /**
409  * 
410  * Calculate heliographic coordinates for a given Greenwich date, with a given heliographic position angle and heliographic displacement in arc minutes.
411  */
412 function heliographicCoordinates(helioPositionAngleDeg, helioDisplacementArcmin, gwdateDay, gwdateMonth, gwdateYear) {
413     var julianDateDays = paMacros.civilDateToJulianDate(gwdateDay, gwdateMonth, gwdateYear);
414     var tCenturies = (julianDateDays - 2415020) / 36525;
415     var longAscNodeDeg = paMacros.degreesMinutesSecondsToDecimalDegrees(74, 22, 0) + (84 * tCenturies / 60);
416     var sunLongDeg = paMacros.sunLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
417     var y = Math.sin(paUtils.degreesToRadians(longAscNodeDeg - sunLongDeg)) * Math.cos(paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(7, 15, 0)));
418     var x = -Math.cos(paUtils.degreesToRadians(longAscNodeDeg - sunLongDeg));
419     var aDeg = paMacros.degrees(Math.atan2(y, x));
420     var mDeg1 = 360 - (360 * (julianDateDays - 2398220) / 25.38);
421     var mDeg2 = mDeg1 - 360 * Math.floor(mDeg1 / 360);
422     var l0Deg1 = mDeg2 + aDeg;
423     var b0Rad = Math.asin(Math.sin(paUtils.degreesToRadians(sunLongDeg - longAscNodeDeg)) * Math.sin(paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(7, 15, 0))));
424     var theta1Rad = Math.atan(-Math.cos(paUtils.degreesToRadians(sunLongDeg)) * Math.tan(paUtils.degreesToRadians(paMacros.obliq(gwdateDay, gwdateMonth, gwdateYear))));
425     var theta2Rad = Math.atan(-Math.cos(paUtils.degreesToRadians(longAscNodeDeg - sunLongDeg)) * Math.tan(paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(7, 15, 0))));
426     var pDeg = paMacros.degrees(theta1Rad + theta2Rad);
427     var rho1Deg = helioDisplacementArcmin / 60;
428     var rhoRad = Math.asin(2 * rho1Deg / paMacros.sunDia(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear)) - paUtils.degreesToRadians(rho1Deg);
429     var bRad = Math.asin(Math.sin(b0Rad) * Math.cos(rhoRad) + Math.cos(b0Rad) * Math.sin(rhoRad) * Math.cos(paUtils.degreesToRadians(pDeg - helioPositionAngleDeg)));
430     var bDeg = paMacros.degrees(bRad);
431     var lDeg1 = paMacros.degrees(Math.asin(Math.sin(rhoRad) * Math.sin(paUtils.degreesToRadians(pDeg - helioPositionAngleDeg)) / Math.cos(bRad))) + l0Deg1;
432     var lDeg2 = lDeg1 - 360 * Math.floor(lDeg1 / 360);
433 
434     var helioLongDeg = paUtils.round(lDeg2, 2);
435     var helioLatDeg = paUtils.round(bDeg, 2);
436 
437     return [helioLongDeg, helioLatDeg];
438 }
439 
440 /**
441  * Calculate carrington rotation number for a Greenwich date
442  */
443 function carringtonRotationNumber(gwdateDay, gwdateMonth, gwdateYear) {
444     var julianDateDays = paMacros.civilDateToJulianDate(gwdateDay, gwdateMonth, gwdateYear);
445 
446     var crn = 1690 + paUtils.round((julianDateDays - 2444235.34) / 27.2753, 0);
447 
448     return crn;
449 }
450 
451 /**
452  * Calculate selenographic (lunar) coordinates (sub-Earth)
453  */
454 function selenographicCoordinates1(gwdateDay, gwdateMonth, gwdateYear) {
455     var julianDateDays = paMacros.civilDateToJulianDate(gwdateDay, gwdateMonth, gwdateYear);
456     var tCenturies = (julianDateDays - 2451545) / 36525;
457     var longAscNodeDeg = 125.044522 - 1934.136261 * tCenturies;
458     var f1 = 93.27191 + 483202.0175 * tCenturies;
459     var f2 = f1 - 360 * Math.floor(f1 / 360);
460     var geocentricMoonLongDeg = paMacros.moonLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
461     var geocentricMoonLatRad = paUtils.degreesToRadians(paMacros.moonLat(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
462     var inclinationRad = paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(1, 32, 32.7));
463     var nodeLongRad = paUtils.degreesToRadians(longAscNodeDeg - geocentricMoonLongDeg);
464     var sinBe = -Math.cos(inclinationRad) * Math.sin(geocentricMoonLatRad) + Math.sin(inclinationRad) * Math.cos(geocentricMoonLatRad) * Math.sin(nodeLongRad);
465     var subEarthLatDeg = paMacros.degrees(Math.asin(sinBe));
466     var aRad = Math.atan2((-Math.sin(geocentricMoonLatRad) * Math.sin(inclinationRad) - Math.cos(geocentricMoonLatRad) * Math.cos(inclinationRad) * Math.sin(nodeLongRad)), (Math.cos(geocentricMoonLatRad) * Math.cos(nodeLongRad)));
467     var aDeg = paMacros.degrees(aRad);
468     var subEarthLongDeg1 = aDeg - f2;
469     var subEarthLongDeg2 = subEarthLongDeg1 - 360 * Math.floor(subEarthLongDeg1 / 360);
470     var subEarthLongDeg3 = (subEarthLongDeg2 > 180) ? subEarthLongDeg2 - 360 : subEarthLongDeg2;
471     var c1Rad = Math.atan(Math.cos(nodeLongRad) * Math.sin(inclinationRad) / (Math.cos(geocentricMoonLatRad) * Math.cos(inclinationRad) + Math.sin(geocentricMoonLatRad) * Math.sin(inclinationRad) * Math.sin(nodeLongRad)));
472     var obliquityRad = paUtils.degreesToRadians(paMacros.obliq(gwdateDay, gwdateMonth, gwdateYear));
473     var c2Rad = Math.atan(Math.sin(obliquityRad) * Math.cos(paUtils.degreesToRadians(geocentricMoonLongDeg)) / (Math.sin(obliquityRad) * Math.sin(geocentricMoonLatRad) * Math.sin(paUtils.degreesToRadians(geocentricMoonLongDeg)) - Math.cos(obliquityRad) * Math.cos(geocentricMoonLatRad)));
474     var cDeg = paMacros.degrees(c1Rad + c2Rad);
475 
476     var subEarthLongitude = paUtils.round(subEarthLongDeg3, 2);
477     var subEarthLatitude = paUtils.round(subEarthLatDeg, 2);
478     var positionAngleOfPole = paUtils.round(cDeg, 2);
479 
480     return [subEarthLongitude, subEarthLatitude, positionAngleOfPole];
481 }
482 
483 /**
484  * Calculate selenographic (lunar) coordinates (sub-Solar)
485  */
486 function selenographicCoordinates2(gwdateDay, gwdateMonth, gwdateYear) {
487     var julianDateDays = paMacros.civilDateToJulianDate(gwdateDay, gwdateMonth, gwdateYear);
488     var tCenturies = (julianDateDays - 2451545) / 36525;
489     var longAscNodeDeg = 125.044522 - 1934.136261 * tCenturies;
490     var f1 = 93.27191 + 483202.0175 * tCenturies;
491     var f2 = f1 - 360 * Math.floor(f1 / 360);
492     var sunGeocentricLongDeg = paMacros.sunLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
493     var moonEquHorParallaxArcMin = paMacros.moonHP(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear) * 60;
494     var sunEarthDistAU = paMacros.sunDist(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
495     var geocentricMoonLatRad = paUtils.degreesToRadians(paMacros.moonLat(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
496     var geocentricMoonLongDeg = paMacros.moonLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
497     var adjustedMoonLongDeg = sunGeocentricLongDeg + 180 + (26.4 * Math.cos(geocentricMoonLatRad) * Math.sin(paUtils.degreesToRadians(sunGeocentricLongDeg - geocentricMoonLongDeg)) / (moonEquHorParallaxArcMin * sunEarthDistAU));
498     var adjustedMoonLatRad = 0.14666 * geocentricMoonLatRad / (moonEquHorParallaxArcMin * sunEarthDistAU);
499     var inclinationRad = paUtils.degreesToRadians(paMacros.degreesMinutesSecondsToDecimalDegrees(1, 32, 32.7));
500     var nodeLongRad = paUtils.degreesToRadians(longAscNodeDeg - adjustedMoonLongDeg);
501     var sinBs = -Math.cos(inclinationRad) * Math.sin(adjustedMoonLatRad) + Math.sin(inclinationRad) * Math.cos(adjustedMoonLatRad) * Math.sin(nodeLongRad);
502     var subSolarLatDeg = paMacros.degrees(Math.asin(sinBs));
503     var aRad = Math.atan2((-Math.sin(adjustedMoonLatRad) * Math.sin(inclinationRad) - Math.cos(adjustedMoonLatRad) * Math.cos(inclinationRad) * Math.sin(nodeLongRad)), (Math.cos(adjustedMoonLatRad) * Math.cos(nodeLongRad)));
504     var aDeg = paMacros.degrees(aRad);
505     var subSolarLongDeg1 = aDeg - f2;
506     var subSolarLongDeg2 = subSolarLongDeg1 - 360 * Math.floor(subSolarLongDeg1 / 360);
507     var subSolarLongDeg3 = (subSolarLongDeg2 > 180) ? subSolarLongDeg2 - 360 : subSolarLongDeg2;
508     var subSolarColongDeg = 90 - subSolarLongDeg3;
509 
510     var subSolarLongitude = paUtils.round(subSolarLongDeg3, 2);
511     var subSolarColongitude = paUtils.round(subSolarColongDeg, 2);
512     var subSolarLatitude = paUtils.round(subSolarLatDeg, 2);
513 
514     return [subSolarLongitude, subSolarColongitude, subSolarLatitude];
515 }
516 
517 
518 module.exports = {
519     angleToDecimalDegrees,
520     decimalDegreesToAngle,
521     rightAscensionToHourAngle,
522     hourAngleToRightAscension,
523     equatorialCoordinatesToHorizonCoordinates,
524     horizonCoordinatesToEquatorialCoordinates,
525     meanObliquityOfTheEcliptic,
526     eclipticCoordinateToEquatorialCoordinate,
527     equatorialCoordinateToEclipticCoordinate,
528     equatorialCoordinateToGalacticCoordinate,
529     galacticCoordinateToEquatorialCoordinate,
530     angleBetweenTwoObjects,
531     risingAndSetting,
532     correctForPrecession,
533     nutationInEclipticLongitudeAndObliquity,
534     correctForAberration,
535     atmosphericRefraction,
536     correctionsForGeocentricParallax,
537     heliographicCoordinates,
538     carringtonRotationNumber,
539     selenographicCoordinates1,
540     selenographicCoordinates2
541 };