This is the main functionality of observability.date, and also just about the simplest part to code. Calculating the altitude and azimuth of a celestial object from its Right Ascension and Declination just requires a calculation of the local sidereal time, to put into the equations
sin(altitude)=sin(latitude)*sin(δ) + cos(latitude)*cos(δ)*cos(LHA) cos(azimuth)=(sin(δ)-sin(altitude)*sin(latitude))/(cos(altitude)*cos(latitude))
where LHA is the local hour angle in degrees, obtained by subtracting the Right Ascension from the local sidereal time, and δ is the declination of the object. The local sidereal time is calculated using the method described at http://aa.usno.navy.mil/faq/docs/GAST.php, neglecting the correction for nutation which amounts to only 1.1s at most.
In calculating the altitude of objects other than the Sun, I neglect the effect of refraction, which delays their setting and advances their rising by a couple of minutes. As the tool is for finding the best times to observe things, their movements very close to the horizon are not particularly interesting.
When an object's coordinates are resolved from its name, observability.date also retrieves the proper motions in Right Ascension and Declination if known. It uses these to determine the RA and dec at the epoch of the observation, taken as the start of the year.
Precession is then calculated to convert the RA and dec (at epoch 2000.0 if the proper motion is negligible or not measured) from equinox 2000.0 to the equinox at Jan 1 of the year of the selected date. Proper motion typically has a negligible effect, even over many centuries. Precession is a small effect but still amounts to a 50 arcsec shift per year in the coordinate system, which for J2000 coordinates observed in 2018 is 15 arcmin or 1 minute in RA.
From the altitude of the object I calculate the airmass that it is at. The simplest equation for airmass assumes a plane parallel atmosphere and is X=sec(z), where z is the zenith distance. I correct for the curvature of the Earth using the equation of Rozenberg (1966), which is
X = (cos(z) + 0.25e-11cos(z))-1
To calculate sunset and sunrise times, observability.date accounts for refraction and the elevation of the observing site by determining when the altitude of the centre of the Sun's disc is (-0.83 - (elevation)0.5/60) °, where elevation is in metres above sea level. The radius of the solar disc is 0.25° and refraction raises the apparent elevation by 0.58°. The altitude correction term implies that, at 3600m above sea level, the zenith distance of the horizon is increased by 1°.
Once the Sun is below the horizon, refraction no longer matters, and observability.date does not account for elevation either, so the boundaries of civil, nautical and astronomical twilights are just calculated as the times when the altitude of the Sun reaches -6, -12 and -18°.
The equations for calculating the time at which the Sun reaches a given altitude are then:
MST = n - (longitude/360) M = (357.5291 + 0.98560028*MST)%360 C=1.9148*sin(M) + 0.2*sin(2M) + 0.0003*sin(3M) λ=(M+C+180+102.9372)%360 J_transit = jd2000 + 0.5 + MST + (0.0053*sin(M)) - (0.0069*sin(2λ)) sin(δ) = sin(λ) * sin(23.44) cos(ω) = (sin(el) - sin(δ)*sin(latitude)) / (cos(latitude) * cos(δ)) J_event = J_transit ± ω/360
Everything on observability.date is night-centric, so it calculates sunset and the starts of twilights using the relevant value of n for the date of the start of the night, while the ends of the twilights and the time of sunrise are calculated using n+1.
This is by far the most complicated thing that observability.date calculates. A really precise calculation of the Moon's position requires the calculation of hundreds of periodic terms to account for all the myriad gravitational forces acting on the Moon. Meeus gives a somewhat simplified set of calculations in chapter 45 of Astronomical Algorithms, with 59 periodic terms in longitude and 30 in latitude. I simplify this further by taking the first 13 periodic terms in the Moon's longitude and the first 10 in latitude.
An exact calculation of moonrise and moonset needs to be done iteratively, as its RA and dec are changing constantly. observability.date calculates the Moon's position every two minutes throughout the 24 hour period it is looking at, and from this, it finds the times at which its altitude changes from positive to negative or vice versa. It then linearly interpolates to estimate the time at which the altitude was zero.
Lunar calculations are made firstly in the geocentric frame and then corrected to the topocentric position. Refraction is then accounted for, and finally the rise and set times determined using the horizon corrected for the elevation of the observing location. Moon rise and set times should be accurate to within a couple of minutes.
The times of full moons throughout the year are calculated using the method described in Meeus's chapter 47, but using only the first 14 of 25 periodic correction terms, and omitting the planetary corrections. These simplifications introduce a maximum error of 2-3 minutes.
The parallactic angle is the angle between the meridian and the great circle passing through an object and the celestial poles. Aligning a spectrograph slit at the parallactic angle avoids differential refraction along the slit. observability.date calculates the parallactic angle using the following equation, and displays it in the dynamic display on the daily chart.
HA = RA - 15*LAST PA = (360-arctan(sin(HA)/(tan(latitude)*cos(dec)-sin(dec)*cos(HA))))%360
where RA and dec are the celestial coordinates of the object, LAST is the local apparent sidereal time in hours, and HA is the hour angle.
I've compared the numbers from observability.date to a few other almanac services. I randomly chose July 9 2018 and Paranal observatory, looking at NGC 5189. Here is a table comparing results from observability.date (calculated on 16 Jan 2018, internal version ID a38519), with results from the ING's staralt, Heavens Above, and ESO's skycalc.
|observability.date||heavens above||eso skycalc||staralt|
|End of civil twilight||18:31:45||18:32|
|End of nautical twilight||18:59:58||19:01||19:01|
|End of astronomical twilight||19:27:45||19:28||19:28||18:27|
|Start of astronomical twilight||06:05:20||06:06||06:06||06:05|
|Start of nautical twilight||06:33:06||06:33||06:33|
|Start of civil twilight||07:01:17||07:01|
|Length of night (sunset-sunrise)||13:02||13:18||13.0||13:03|
|Length of astronomical night||10:38||10:38||10.6||10:38|
|LST at civil midnight||18:30||18:30|
|Moon altitude at midnight||-58.9°|
|Distance from object||124.5°||123|
|Days past full||12|
|object max alt||48.6||48*|
|parallactic angle at midnight||86||79|
|hours above 30 degrees in dark:||4h18m*|
|* = read from chart|
There is generally fair agreement between the various pages. Heavens Above's discrepant sunset and sunrise times are because it does not correct for altitude, where the other options all do. All other solar phenomena agree to within a minute; actual variation in sunrise and sunset due to varying atmospheric conditions along the line of sight to the horizon is likely to be larger than this.
Lunar phenomena show much more variation. Moon rise and set times have a maximum spread of 14-18 minutes. There are three or four likely causes of this. Firstly, the precision of the lunar calculations; observability.date uses only the first 13 periodic terms in lunar longitude tabulated in Meeus. The details of the calculations for the other programs are not documented, as far as I am aware. Secondly, the lunar position may be geocentric rather than topocentric; lack of available documentation prevents a conclusion from being drawn on that. Thirdly, altitude may not be corrected for; and finally the elevation of the observatory may not be corrected for. This is certainly the case for Heavens Above, which explicitly states that it uses a horizon altitude of -0.8 degrees, which places the refracted upper edge of the Sun or Moon onto the horizon at sea level.
Most of the equations used in observability.date cannot be used indefinitely, as they contain polynomal expressions in time which will diverge far from 2000. In case you should wish to use the site for dates very far into the future or past, the validity of the results is roughly as follows:
Celestial objects: the correction for precession is valid for at least 100 centuries. observability.date covers dates from 0-9999AD (at least with the date support in Chrome) and so the precessed coordinates throughout that time should be accurate.
Local sidereal time: observability.date uses a method from the US Naval Observatory which they say loses precision at 0.1s/century. Thus, by 9999 (the last year for which the HTML5 date input works), the discrepancy should only be 8s.
Moon: Lunar calculations omit smaller periodic terms to keep the calculations fast. This limits the accuracy of lunar timings to a few minutes. Any secular trend should come from the secular variation of the Moon itself, an effect probably negligible within the 0-9999 date range available.