Source: pa-sun.js

const paMacros = require('./pa-macros.js');
const paTypes = require('./pa-types.js');
const paUtils = require('./pa-utils.js');

/**
 * Calculate approximate position of the sun for a local date and time.
 */
function approximatePositionOfSun(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
    var daylightSaving = (isDaylightSaving == true) ? 1 : 0;

    var greenwichDateDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var greenwichDateMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var greenwichDateYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var utHours = paMacros.localCivilTimeToUniversalTime(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var utDays = utHours / 24;
    var jdDays = paMacros.civilDateToJulianDate(greenwichDateDay, greenwichDateMonth, greenwichDateYear) + utDays;
    var dDays = jdDays - paMacros.civilDateToJulianDate(0, 1, 2010);
    var nDeg = 360 * dDays / 365.242191;
    var mDeg1 = nDeg + paMacros.sunELong(0, 1, 2010) - paMacros.sunPeri(0, 1, 2010);
    var mDeg2 = mDeg1 - 360 * Math.floor(mDeg1 / 360);
    var eCDeg = 360 * paMacros.sunEcc(0, 1, 2010) * Math.sin(paUtils.degreesToRadians(mDeg2)) / Math.PI;
    var lSDeg1 = nDeg + eCDeg + paMacros.sunELong(0, 1, 2010);
    var lSDeg2 = lSDeg1 - 360 * Math.floor(lSDeg1 / 360);
    var raDeg = paMacros.ecRA(lSDeg2, 0, 0, 0, 0, 0, greenwichDateDay, greenwichDateMonth, greenwichDateYear);
    var raHours = paMacros.decimalDegreesToDegreeHours(raDeg);
    var decDeg = paMacros.ecDec(lSDeg2, 0, 0, 0, 0, 0, greenwichDateDay, greenwichDateMonth, greenwichDateYear);

    var sunRAHour = paMacros.decimalHoursHour(raHours);
    var sunRAMin = paMacros.decimalHoursMinute(raHours);
    var sunRASec = paMacros.decimalHoursSecond(raHours);
    var sunDecDeg = paMacros.decimalDegreesDegrees(decDeg);
    var sunDecMin = paMacros.decimalDegreesMinutes(decDeg);
    var sunDecSec = paMacros.decimalDegreesSeconds(decDeg);

    return [sunRAHour, sunRAMin, sunRASec, sunDecDeg, sunDecMin, sunDecSec];
}

/**
 * Calculate precise position of the sun for a local date and time.
 */
function precisePositionOfSun(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
    var daylightSaving = (isDaylightSaving == true) ? 1 : 0;

    var gDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var gMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var gYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var sunEclipticLongitudeDeg = paMacros.sunLong(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var raDeg = paMacros.ecRA(sunEclipticLongitudeDeg, 0, 0, 0, 0, 0, gDay, gMonth, gYear);
    var raHours = paMacros.decimalDegreesToDegreeHours(raDeg);
    var decDeg = paMacros.ecDec(sunEclipticLongitudeDeg, 0, 0, 0, 0, 0, gDay, gMonth, gYear);

    var sunRAHour = paMacros.decimalHoursHour(raHours);
    var sunRAMin = paMacros.decimalHoursMinute(raHours);
    var sunRASec = paMacros.decimalHoursSecond(raHours);
    var sunDecDeg = paMacros.decimalDegreesDegrees(decDeg);
    var sunDecMin = paMacros.decimalDegreesMinutes(decDeg);
    var sunDecSec = paMacros.decimalDegreesSeconds(decDeg);

    return [sunRAHour, sunRAMin, sunRASec, sunDecDeg, sunDecMin, sunDecSec];
}

/**
 * Calculate distance to the Sun (in km), and angular size.
 */
function sunDistanceAndAngularSize(lctHours, lctMinutes, lctSeconds, localDay, localMonth, localYear, isDaylightSaving, zoneCorrection) {
    var daylightSaving = (isDaylightSaving) ? 1 : 0;

    var gDay = paMacros.localCivilTimeGreenwichDay(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var gMonth = paMacros.localCivilTimeGreenwichMonth(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var gYear = paMacros.localCivilTimeGreenwichYear(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var trueAnomalyDeg = paMacros.sunTrueAnomaly(lctHours, lctMinutes, lctSeconds, daylightSaving, zoneCorrection, localDay, localMonth, localYear);
    var trueAnomalyRad = paUtils.degreesToRadians(trueAnomalyDeg);
    var eccentricity = paMacros.sunEcc(gDay, gMonth, gYear);
    var f = (1 + eccentricity * Math.cos(trueAnomalyRad)) / (1 - eccentricity * eccentricity);
    var rKm = 149598500 / f;
    var thetaDeg = f * 0.533128;

    var sunDistKm = paUtils.round(rKm, 0);
    var sunAngSizeDeg = paMacros.decimalDegreesDegrees(thetaDeg);
    var sunAngSizeMin = paMacros.decimalDegreesMinutes(thetaDeg);
    var sunAngSizeSec = paMacros.decimalDegreesSeconds(thetaDeg);

    return [sunDistKm, sunAngSizeDeg, sunAngSizeMin, sunAngSizeSec];
}

/**
   * Calculate local sunrise and sunset.
 */
function sunriseAndSunset(localDay, localMonth, localYear, isDaylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg) {
    var daylightSaving = (isDaylightSaving) ? 1 : 0;

    var localSunriseHours = paMacros.sunriseLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
    var localSunsetHours = paMacros.sunsetLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);

    var sunRiseSetStatus = paMacros.eSunRS(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);

    var adjustedSunriseHours = localSunriseHours + 0.008333;
    var adjustedSunsetHours = localSunsetHours + 0.008333;

    var azimuthOfSunriseDeg1 = paMacros.sunriseAZ(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);
    var azimuthOfSunsetDeg1 = paMacros.sunsetAZ(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg);

    var localSunriseHour = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursHour(adjustedSunriseHours) : 0;
    var localSunriseMinute = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursMinute(adjustedSunriseHours) : 0;

    var localSunsetHour = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursHour(adjustedSunsetHours) : 0;
    var localSunsetMinute = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paMacros.decimalHoursMinute(adjustedSunsetHours) : 0;

    var azimuthOfSunriseDeg = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paUtils.round(azimuthOfSunriseDeg1, 2) : 0;
    var azimuthOfSunsetDeg = (sunRiseSetStatus == paTypes.RiseSetCalcStatus.OK) ? paUtils.round(azimuthOfSunsetDeg1, 2) : 0;

    var status = sunRiseSetStatus;

    return [localSunriseHour, localSunriseMinute, localSunsetHour, localSunsetMinute, azimuthOfSunriseDeg, azimuthOfSunsetDeg, status];
}

/**
   * Calculate times of morning and evening twilight.
 */
function morningAndEveningTwilight(localDay, localMonth, localYear, isDaylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType) {
    var daylightSaving = (isDaylightSaving) ? 1 : 0;

    var startOfAMTwilightHours = paMacros.twilightAMLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);

    var endOfPMTwilightHours = paMacros.twilightPMLCT(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);

    var twilightStatus = paMacros.eTwilight(localDay, localMonth, localYear, daylightSaving, zoneCorrection, geographicalLongDeg, geographicalLatDeg, twilightType);

    var adjustedAMStartTime = startOfAMTwilightHours + 0.008333;
    var adjustedPMStartTime = endOfPMTwilightHours + 0.008333;

    var amTwilightBeginsHour = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursHour(adjustedAMStartTime) : -99;
    var amTwilightBeginsMin = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursMinute(adjustedAMStartTime) : -99;

    var pmTwilightEndsHour = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursHour(adjustedPMStartTime) : -99;
    var pmTwilightEndsMin = (twilightStatus == paTypes.TwilightStatus.OK) ? paMacros.decimalHoursMinute(adjustedPMStartTime) : -99;

    var status = twilightStatus;

    return [amTwilightBeginsHour, amTwilightBeginsMin, pmTwilightEndsHour, pmTwilightEndsMin, status];
}

/**
   * Calculate the equation of time. (The difference between the real Sun time and the mean Sun time.)
 */
function equationOfTime(gwdateDay, gwdateMonth, gwdateYear) {
    var sunLongitudeDeg = paMacros.sunLong(12, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
    var sunRAHours = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
    var equivalentUTHours = paMacros.greenwichSiderealTimeToUniversalTime(sunRAHours, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
    var equationOfTimeHours = equivalentUTHours - 12;

    var equationOfTimeMin = paMacros.decimalHoursMinute(equationOfTimeHours);
    var equationOfTimeSec = paMacros.decimalHoursSecond(equationOfTimeHours);

    return [equationOfTimeMin, equationOfTimeSec];
}

/**
 * Calculate solar elongation for a celestial body.
 *
 * Solar elongation is the angle between the lines of sight from the Earth to the Sun and from the Earth to the celestial body.
 */
function solarElongation(raHour, raMin, raSec, decDeg, decMin, decSec, gwdateDay, gwdateMonth, gwdateYear) {
    var sunLongitudeDeg = paMacros.sunLong(0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
    var sunRAHours = paMacros.decimalDegreesToDegreeHours(paMacros.ecRA(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear));
    var sunDecDeg = paMacros.ecDec(sunLongitudeDeg, 0, 0, 0, 0, 0, gwdateDay, gwdateMonth, gwdateYear);
    var solarElongationDeg = paMacros.angle(sunRAHours, 0, 0, sunDecDeg, 0, 0, raHour, raMin, raSec, decDeg, decMin, decSec, paTypes.AngleMeasure.Hours);

    return paUtils.round(solarElongationDeg, 2);
}


module.exports = {
    approximatePositionOfSun,
    precisePositionOfSun,
    sunDistanceAndAngularSize,
    sunriseAndSunset,
    morningAndEveningTwilight,
    equationOfTime,
    solarElongation
};