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 sun for a local date and time.
  7  */
  8 function approximatePositionOfSun(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
  9     var daylightSaving = (isDaylightSaving == true) ? 1 : 0;
 10 
 11     var greenwichDateDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 12     var greenwichDateMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 13     var greenwichDateYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 14     var utHours = paMacros.localCivilTimeToUniversalTime(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 15     var utDays = utHours / 24;
 16     var jdDays = paMacros.civilDateToJulianDate(greenwichDateDay, greenwichDateMonth, greenwichDateYear) + utDays;
 17     var dDays = jdDays - paMacros.civilDateToJulianDate(0, 1, 2010);
 18     var nDeg = 360 * dDays / 365.242191;
 19     var mDeg1 = nDeg + paMacros.sunELong(0, 1, 2010) - paMacros.sunPeri(0, 1, 2010);
 20     var mDeg2 = mDeg1 - 360 * Math.floor(mDeg1 / 360);
 21     var eCDeg = 360 * paMacros.sunEcc(0, 1, 2010) * Math.sin(paUtils.degreesToRadians(mDeg2)) / Math.PI;
 22     var lSDeg1 = nDeg + eCDeg + paMacros.sunELong(0, 1, 2010);
 23     var lSDeg2 = lSDeg1 - 360 * Math.floor(lSDeg1 / 360);
 24     var raDeg = paMacros.ecRA(lSDeg2, 0, 0, 0, 0, 0, greenwichDateDay, greenwichDateMonth, greenwichDateYear);
 25     var raHours = paMacros.decimalDegreesToDegreeHours(raDeg);
 26     var decDeg = paMacros.ecDec(lSDeg2, 0, 0, 0, 0, 0, greenwichDateDay, greenwichDateMonth, greenwichDateYear);
 27 
 28     var sunRAHour = paMacros.decimalHoursHour(raHours);
 29     var sunRAMin = paMacros.decimalHoursMinute(raHours);
 30     var sunRASec = paMacros.decimalHoursSecond(raHours);
 31     var sunDecDeg = paMacros.decimalDegreesDegrees(decDeg);
 32     var sunDecMin = paMacros.decimalDegreesMinutes(decDeg);
 33     var sunDecSec = paMacros.decimalDegreesSeconds(decDeg);
 34 
 35     return [sunRAHour, sunRAMin, sunRASec, sunDecDeg, sunDecMin, sunDecSec];
 36 }
 37 
 38 /**
 39  * Calculate precise position of the sun for a local date and time.
 40  */
 41 function precisePositionOfSun(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
 42     var daylightSaving = (isDaylightSaving == true) ? 1 : 0;
 43 
 44     var gDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 45     var gMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 46     var gYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 47     var sunEclipticLongitudeDeg = paMacros.sunLong(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 48     var raDeg = paMacros.ecRA(sunEclipticLongitudeDeg, 0, 0, 0, 0, 0, gDay, gMonth, gYear);
 49     var raHours = paMacros.decimalDegreesToDegreeHours(raDeg);
 50     var decDeg = paMacros.ecDec(sunEclipticLongitudeDeg, 0, 0, 0, 0, 0, gDay, gMonth, gYear);
 51 
 52     var sunRAHour = paMacros.decimalHoursHour(raHours);
 53     var sunRAMin = paMacros.decimalHoursMinute(raHours);
 54     var sunRASec = paMacros.decimalHoursSecond(raHours);
 55     var sunDecDeg = paMacros.decimalDegreesDegrees(decDeg);
 56     var sunDecMin = paMacros.decimalDegreesMinutes(decDeg);
 57     var sunDecSec = paMacros.decimalDegreesSeconds(decDeg);
 58 
 59     return [sunRAHour, sunRAMin, sunRASec, sunDecDeg, sunDecMin, sunDecSec];
 60 }
 61 
 62 /**
 63  * Calculate distance to the Sun (in km), and angular size.
 64  */
 65 function sunDistanceAndAngularSize(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
 66     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 67 
 68     var gDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 69     var gMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 70     var gYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 71     var trueAnomalyDeg = paMacros.sunTrueAnomaly(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
 72     var trueAnomalyRad = paUtils.degreesToRadians(trueAnomalyDeg);
 73     var eccentricity = paMacros.sunEcc(gDay, gMonth, gYear);
 74     var f = (1 + eccentricity * Math.cos(trueAnomalyRad)) / (1 - eccentricity * eccentricity);
 75     var rKm = 149598500 / f;
 76     var thetaDeg = f * 0.533128;
 77 
 78     var sunDistKm = paUtils.round(rKm, 0);
 79     var sunAngSizeDeg = paMacros.decimalDegreesDegrees(thetaDeg);
 80     var sunAngSizeMin = paMacros.decimalDegreesMinutes(thetaDeg);
 81     var sunAngSizeSec = paMacros.decimalDegreesSeconds(thetaDeg);
 82 
 83     return [sunDistKm, sunAngSizeDeg, sunAngSizeMin, sunAngSizeSec];
 84 }
 85 
 86 /**
 87    * Calculate local sunrise and sunset.
 88  */
 89 function sunriseAndSunset(localDay, localMonth, localYear, isDaylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg) {
 90     var daylightSaving = (isDaylightSaving) ? 1 : 0;
 91 
 92     var localSunriseHours = paMacros.sunriseLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
 93     var localSunsetHours = paMacros.sunsetLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
 94 
 95     var sunRiseSetStatus = paMacros.eSunRS(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
 96 
 97     var adjustedSunriseHours = localSunriseHours + 0.008333;
 98     var adjustedSunsetHours = localSunsetHours + 0.008333;
 99 
100     var azimuthOfSunriseDeg1 = paMacros.sunriseAZ(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
101     var azimuthOfSunsetDeg1 = paMacros.sunsetAZ(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
102 
103     var localSunriseHour = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursHour(adjustedSunriseHours) : 0;
104     var localSunriseMinute = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursMinute(adjustedSunriseHours) : 0;
105 
106     var localSunsetHour = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursHour(adjustedSunsetHours) : 0;
107     var localSunsetMinute = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursMinute(adjustedSunsetHours) : 0;
108 
109     var azimuthOfSunriseDeg = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paUtils.round(azimuthOfSunriseDeg1, 2) : 0;
110     var azimuthOfSunsetDeg = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paUtils.round(azimuthOfSunsetDeg1, 2) : 0;
111 
112     var status = sunRiseSetStatus;
113 
114     return [localSunriseHour, localSunriseMinute, localSunsetHour, localSunsetMinute, azimuthOfSunriseDeg, azimuthOfSunsetDeg, status];
115 }
116 
117 /**
118    * Calculate times of morning and evening twilight.
119  */
120 function morningAndEveningTwilight(localDay, localMonth, localYear, isDaylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType) {
121     var daylightSaving = (isDaylightSaving) ? 1 : 0;
122 
123     var startOfAMTwilightHours = paMacros.twilightAMLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);
124 
125     var endOfPMTwilightHours = paMacros.twilightPMLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);
126 
127     var twilightStatus = paMacros.eTwilight(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);
128 
129     var adjustedAMStartTime = startOfAMTwilightHours + 0.008333;
130     var adjustedPMStartTime = endOfPMTwilightHours + 0.008333;
131 
132     var amTwilightBeginsHour = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursHour(adjustedAMStartTime) : -99;
133     var amTwilightBeginsMin = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursMinute(adjustedAMStartTime) : -99;
134 
135     var pmTwilightEndsHour = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursHour(adjustedPMStartTime) : -99;
136     var pmTwilightEndsMin = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursMinute(adjustedPMStartTime) : -99;
137 
138     var status = twilightStatus;
139 
140     return [amTwilightBeginsHour, amTwilightBeginsMin, pmTwilightEndsHour, pmTwilightEndsMin, status];
141 }
142 
143 /**
144    * Calculate the equation of time. (The difference between the real Sun time and the mean Sun time.)
145  */
146 function equationOfTime(gwdateDay, gwdateMonth, gwdateYear) {
147     var sunLongitudeDeg = paMacros.sunLong(12, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
148     var sunRAHours = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
149     var equivalentUTHours = paMacros.greenwichSiderealTimeToUniversalTime(sunRAHours, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
150     var equationOfTimeHours = equivalentUTHours - 12;
151 
152     var equationOfTimeMin = paMacros.decimalHoursMinute(equationOfTimeHours);
153     var equationOfTimeSec = paMacros.decimalHoursSecond(equationOfTimeHours);
154 
155     return [equationOfTimeMin, equationOfTimeSec];
156 }
157 
158 /**
159  * Calculate solar elongation for a celestial body.
160  *
161  * Solar elongation is the angle between the lines of sight from the Earth to the Sun and from the Earth to the celestial body.
162  */
163 function solarElongation(raHour, raMin, raSec, decDeg, decMin, decSec, gwdateDay, gwdateMonth, gwdateYear) {
164     var sunLongitudeDeg = paMacros.sunLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
165     var sunRAHours = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
166     var sunDecDeg = paMacros.ecDec(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
167     var solarElongationDeg = paMacros.angle(sunRAHours, 0, 0, sunDecDeg, 0, 0, raHour, raMin, raSec, decDeg, decMin, decSec, paTypes.AngleMeasure.Hours);
168 
169     return paUtils.round(solarElongationDeg, 2);
170 }
171 
172 
173 module.exports = {
174     approximatePositionOfSun,
175     precisePositionOfSun,
176     sunDistanceAndAngularSize,
177     sunriseAndSunset,
178     morningAndEveningTwilight,
179     equationOfTime,
180     solarElongation
181 };