http://www.srrb.noaa.gov/highlights/sunrise/program.txt http://www.srrb.noaa.gov/highlights/sunrise/calcdetails.html http://www.srrb.noaa.gov/highlights/sunrise/solareqns.PDF 31.1.07 General Solar Position Calculations --------------------------------------------------------- First, the fractional year (g) is calculated, in radians. g=(2pi/365)*( day_of_ year - 1 + (hour-12)/24 ) From g, we can estimate the equation of time (in minutes) and the solar declination angle (in radians). eqtime = 229.18*(0.000075+ 0.001868cos g - 0.032077sin g - 0.014615cos 2g - 0.040849sin 2g decl = 0.006918 - 0.399912cos g + 0.070257sin g - 0.006758cos 2g + 0.000907sin 2g - 0.002697cos 3g + 0.00148sin 3g Next, the true solar time is calculated in the following two equations. First the time offset is found, in minutes, and then the true solar time, in minutes. time_offset = eqtime - 4 *longitude + 60*timezone where eqtime is in minutes, longitude is in degrees, timezone is in hours from UTC (Mountain Standard Time = +7 hours). tst = hr* 60 + mn + sc/60 + time_offset where hr is the hour (0 - 23), mn is the minute (0 - 60), sc is the second (0 - 60). The solar hour angle, in degrees, is: ha = (tst/4) - 180 The solar zenith angle (f) can then be found from the following equation: cos f = sin(lat) sin(decl) + cos(lat ) cos(decl) cos(ha) And the solar azimuth (a, clockwise from north) is: cos(180 - a ) = -(sin(lat) cos f - sin(decl))/(cos(lat) sin f) Sunrise/Sunset Calculations --------------------------------- For the special case of sunrise or sunset, the zenith is set to 90.833° (the approximate correction for atmospheric refraction at sunrise and sunset), and the hour angle becomes: ha= ±arccos( (cos(90.833))/( cos(lat) cos(decl)) - tan(lat) tan(decl) ) where the positive number corresponds to sunrise, negative to sunset. Then the UTC time of sunrise (or sunset) in minutes is: sunrise = 720 + 4(longitude - ha) - eqtime where longitude and hour angle are in degrees and the equation of time is in minutes. Solar noon for a given location is found from the longitude (in degrees) and the equation of time (in minutes): snoon = 720 + 4 *longitude - eqtime //***********************************************************************/ //***********************************************************************/ //* */ //*This section contains subroutines used in calculating solar position */ //* */ //***********************************************************************/ //***********************************************************************/ // Convert radian angle to degrees function radToDeg(angleRad) { return (180.0 * angleRad / Math.PI); } //*********************************************************************/ // Convert degree angle to radians function degToRad(angleDeg) { return (Math.PI * angleDeg / 180.0); } //*********************************************************************/ //***********************************************************************/ //* Name: calcDayOfYear */ //* Type: Function */ //* Purpose: Finds numerical day-of-year from mn, day and lp year info */ //* Arguments: */ //* month: January = 1 */ //* day : 1 - 31 */ //* lpyr : 1 if leap year, 0 if not */ //* Return value: */ //* The numerical day of year */ //***********************************************************************/ function calcDayOfYear(mn, dy, lpyr) { var k = (lpyr ? 1 : 2); var doy = Math.floor((275 * mn)/9) - k * Math.floor((mn + 9)/12) + dy -30; return doy; } //***********************************************************************/ //* Name: calcDayOfWeek */ //* Type: Function */ //* Purpose: Derives weekday from Julian Day */ //* Arguments: */ //* juld : Julian Day */ //* Return value: */ //* String containing name of weekday */ //***********************************************************************/ function calcDayOfWeek(juld) { var A = (juld + 1.5) % 7; var DOW = (A==0)?"Sunday":(A==1)?"Monday":(A==2)?"Tuesday":(A==3)?"Wednesday":(A==4)?"Thursday":(A==5)?"Friday":"Saturday"; return DOW; } //***********************************************************************/ //* Name: calcJD */ //* Type: Function */ //* Purpose: Julian day from calendar day */ //* Arguments: */ //* year : 4 digit year */ //* month: January = 1 */ //* day : 1 - 31 */ //* Return value: */ //* The Julian day corresponding to the date */ //* Note: */ //* Number is returned for start of day. Fractional days should be */ //* added later. */ //***********************************************************************/ function calcJD(year, month, day) { if (month <= 2) { year -= 1; month += 12; } var A = Math.floor(year/100); var B = 2 - A + Math.floor(A/4); var JD = Math.floor(365.25*(year + 4716)) + Math.floor(30.6001*(month+1)) + day + B - 1524.5; return JD; } //***********************************************************************/ //* Name: calcDateFromJD */ //* Type: Function */ //* Purpose: Calendar date from Julian Day */ //* Arguments: */ //* jd : Julian Day */ //* Return value: */ //* String date in the form DD-MONTHNAME-YYYY */ //* Note: */ //***********************************************************************/ function calcDateFromJD(jd) { var z = Math.floor(jd + 0.5); var f = (jd + 0.5) - z; if (z < 2299161) { var A = z; } else { alpha = Math.floor((z - 1867216.25)/36524.25); var A = z + 1 + alpha - Math.floor(alpha/4); } var B = A + 1524; var C = Math.floor((B - 122.1)/365.25); var D = Math.floor(365.25 * C); var E = Math.floor((B - D)/30.6001); var day = B - D - Math.floor(30.6001 * E) + f; var month = (E < 14) ? E - 1 : E - 13; var year = (month > 2) ? C - 4716 : C - 4715; // alert ("date: " + day + "-" + monthList[month-1].name + "-" + year); return (day + "-" + monthList[month-1].name + "-" + year); } //***********************************************************************/ //* Name: calcDayFromJD */ //* Type: Function */ //* Purpose: Calendar day (minus year) from Julian Day */ //* Arguments: */ //* jd : Julian Day */ //* Return value: */ //* String date in the form DD-MONTH */ //***********************************************************************/ function calcDayFromJD(jd) { var z = Math.floor(jd + 0.5); var f = (jd + 0.5) - z; if (z < 2299161) { var A = z; } else { alpha = Math.floor((z - 1867216.25)/36524.25); var A = z + 1 + alpha - Math.floor(alpha/4); } var B = A + 1524; var C = Math.floor((B - 122.1)/365.25); var D = Math.floor(365.25 * C); var E = Math.floor((B - D)/30.6001); var day = B - D - Math.floor(30.6001 * E) + f; var month = (E < 14) ? E - 1 : E - 13; var year = (month > 2) ? C - 4716 : C - 4715; return ((day<10 ? "0" : "") + day + monthList[month-1].abbr); } //***********************************************************************/ //* Name: calcTimeJulianCent */ //* Type: Function */ //* Purpose: convert Julian Day to centuries since J2000.0. */ //* Arguments: */ //* jd : the Julian Day to convert */ //* Return value: */ //* the T value corresponding to the Julian Day */ //***********************************************************************/ function calcTimeJulianCent(jd) { var T = (jd - 2451545.0)/36525.0; return T; } //***********************************************************************/ //* Name: calcJDFromJulianCent */ //* Type: Function */ //* Purpose: convert centuries since J2000.0 to Julian Day. */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* the Julian Day corresponding to the t value */ //***********************************************************************/ function calcJDFromJulianCent(t) { var JD = t * 36525.0 + 2451545.0; return JD; } //***********************************************************************/ //* Name: calGeomMeanLongSun */ //* Type: Function */ //* Purpose: calculate the Geometric Mean Longitude of the Sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* the Geometric Mean Longitude of the Sun in degrees */ //***********************************************************************/ function calcGeomMeanLongSun(t) { var L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t); while(L0 > 360.0) { L0 -= 360.0; } while(L0 < 0.0) { L0 += 360.0; } return L0; // in degrees } //***********************************************************************/ //* Name: calGeomAnomalySun */ //* Type: Function */ //* Purpose: calculate the Geometric Mean Anomaly of the Sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* the Geometric Mean Anomaly of the Sun in degrees */ //***********************************************************************/ function calcGeomMeanAnomalySun(t) { var M = 357.52911 + t * (35999.05029 - 0.0001537 * t); return M; // in degrees } //***********************************************************************/ //* Name: calcEccentricityEarthOrbit */ //* Type: Function */ //* Purpose: calculate the eccentricity of earth's orbit */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* the unitless eccentricity */ //***********************************************************************/ function calcEccentricityEarthOrbit(t) { var e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t); return e; // unitless } //***********************************************************************/ //* Name: calcSunEqOfCenter */ //* Type: Function */ //* Purpose: calculate the equation of center for the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* in degrees */ //***********************************************************************/ function calcSunEqOfCenter(t) { var m = calcGeomMeanAnomalySun(t); var mrad = degToRad(m); var sinm = Math.sin(mrad); var sin2m = Math.sin(mrad+mrad); var sin3m = Math.sin(mrad+mrad+mrad); var C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289; return C; // in degrees } //***********************************************************************/ //* Name: calcSunTrueLong */ //* Type: Function */ //* Purpose: calculate the true longitude of the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun's true longitude in degrees */ //***********************************************************************/ function calcSunTrueLong(t) { var l0 = calcGeomMeanLongSun(t); var c = calcSunEqOfCenter(t); var O = l0 + c; return O; // in degrees } //***********************************************************************/ //* Name: calcSunTrueAnomaly */ //* Type: Function */ //* Purpose: calculate the true anamoly of the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun's true anamoly in degrees */ //***********************************************************************/ function calcSunTrueAnomaly(t) { var m = calcGeomMeanAnomalySun(t); var c = calcSunEqOfCenter(t); var v = m + c; return v; // in degrees } //***********************************************************************/ //* Name: calcSunRadVector */ //* Type: Function */ //* Purpose: calculate the distance to the sun in AU */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun radius vector in AUs */ //***********************************************************************/ function calcSunRadVector(t) { var v = calcSunTrueAnomaly(t); var e = calcEccentricityEarthOrbit(t); var R = (1.000001018 * (1 - e * e)) / (1 + e * Math.cos(degToRad(v))); return R; // in AUs } //***********************************************************************/ //* Name: calcSunApparentLong */ //* Type: Function */ //* Purpose: calculate the apparent longitude of the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun's apparent longitude in degrees */ //***********************************************************************/ function calcSunApparentLong(t) { var o = calcSunTrueLong(t); var omega = 125.04 - 1934.136 * t; var lambda = o - 0.00569 - 0.00478 * Math.sin(degToRad(omega)); return lambda; // in degrees } //***********************************************************************/ //* Name: calcMeanObliquityOfEcliptic */ //* Type: Function */ //* Purpose: calculate the mean obliquity of the ecliptic */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* mean obliquity in degrees */ //***********************************************************************/ function calcMeanObliquityOfEcliptic(t) { var seconds = 21.448 - t*(46.8150 + t*(0.00059 - t*(0.001813))); var e0 = 23.0 + (26.0 + (seconds/60.0))/60.0; return e0; // in degrees } //***********************************************************************/ //* Name: calcObliquityCorrection */ //* Type: Function */ //* Purpose: calculate the corrected obliquity of the ecliptic */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* corrected obliquity in degrees */ //***********************************************************************/ function calcObliquityCorrection(t) { var e0 = calcMeanObliquityOfEcliptic(t); var omega = 125.04 - 1934.136 * t; var e = e0 + 0.00256 * Math.cos(degToRad(omega)); return e; // in degrees } //***********************************************************************/ //* Name: calcSunRtAscension */ //* Type: Function */ //* Purpose: calculate the right ascension of the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun's right ascension in degrees */ //***********************************************************************/ function calcSunRtAscension(t) { var e = calcObliquityCorrection(t); var lambda = calcSunApparentLong(t); var tananum = (Math.cos(degToRad(e)) * Math.sin(degToRad(lambda))); var tanadenom = (Math.cos(degToRad(lambda))); var alpha = radToDeg(Math.atan2(tananum, tanadenom)); return alpha; // in degrees } //***********************************************************************/ //* Name: calcSunDeclination */ //* Type: Function */ //* Purpose: calculate the declination of the sun */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* sun's declination in degrees */ //***********************************************************************/ function calcSunDeclination(t) { var e = calcObliquityCorrection(t); var lambda = calcSunApparentLong(t); var sint = Math.sin(degToRad(e)) * Math.sin(degToRad(lambda)); var theta = radToDeg(Math.asin(sint)); return theta; // in degrees } //***********************************************************************/ //* Name: calcEquationOfTime */ //* Type: Function */ //* Purpose: calculate the difference between true solar time and mean */ //* solar time */ //* Arguments: */ //* t : number of Julian centuries since J2000.0 */ //* Return value: */ //* equation of time in minutes of time */ //***********************************************************************/ function calcEquationOfTime(t) { var epsilon = calcObliquityCorrection(t); var l0 = calcGeomMeanLongSun(t); var e = calcEccentricityEarthOrbit(t); var m = calcGeomMeanAnomalySun(t); var y = Math.tan(degToRad(epsilon)/2.0); y *= y; var sin2l0 = Math.sin(2.0 * degToRad(l0)); var sinm = Math.sin(degToRad(m)); var cos2l0 = Math.cos(2.0 * degToRad(l0)); var sin4l0 = Math.sin(4.0 * degToRad(l0)); var sin2m = Math.sin(2.0 * degToRad(m)); var Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m; return radToDeg(Etime)*4.0; // in minutes of time }