diff --git a/src/sun.ts b/src/sun.ts index 8e3c011..1572ec0 100644 --- a/src/sun.ts +++ b/src/sun.ts @@ -12,7 +12,7 @@ import type { EclipticCoordinate, EquatorialCoordinate, GeographicCoordinate } f import { AU_IN_METERS, c } from './constants' -import { convertEclipticToEquatorial, convertEquatorialToHorizontal } from './coordinates' +import { convertEclipticToEquatorial } from './coordinates' import { B, L, R, getEccentricityOfOrbit } from './earth' @@ -22,7 +22,7 @@ import { getLocalHorizon } from './observer' import { getFOrbitalParameter } from './orbit' -import { convertDegreesToRadians as radians } from './utilities' +import { convertRadiansToDegrees as degrees, convertDegreesToRadians as radians } from './utilities' import { calculateB, calculateL, calculateR } from './vsop87' @@ -472,10 +472,23 @@ export const SOLAR_STANDARD_ALTITUDE_OF_RISE_AND_SET = -0.8333 as const /*****************************************************************************************************************/ // The geometric altitude of the centre of the Sun (in degrees), e.g., the altitude the standard -// almanac convention of rise and set is stated against, which is not corrected for refraction: +// almanac convention of rise and set is stated against, which is not corrected for refraction. +// +// N.B. The altitude is resolved from the apparent hour angle of the Sun, e.g., taken against +// the apparent sidereal time, as the right ascension of the Sun is an apparent place that +// carries the nutation in longitude, which the mean sidereal time would leave unbalanced by +// the equation of the equinoxes: const getSolarGeometricAltitude = (datetime: Date, observer: GeographicCoordinate): number => { - return convertEquatorialToHorizontal(datetime, observer, getSolarEquatorialCoordinate(datetime)) - .alt + const { ra, dec } = getSolarEquatorialCoordinate(datetime) + + // Get the apparent hour angle of the Sun (in radians): + const ha = radians(getApparentHourAngle(datetime, observer.longitude, ra)) + + const φ = radians(observer.latitude) + + const δ = radians(dec) + + return degrees(Math.asin(Math.sin(φ) * Math.sin(δ) + Math.cos(φ) * Math.cos(δ) * Math.cos(ha))) } /*****************************************************************************************************************/ diff --git a/tests/conformance/spa.spec.ts b/tests/conformance/spa.spec.ts index 8251f3c..fee4fb3 100644 --- a/tests/conformance/spa.spec.ts +++ b/tests/conformance/spa.spec.ts @@ -64,8 +64,10 @@ const APPARENT_MERIDIAN_TRANSIT_TOLERANCE = 0.1 // The sunrise and sunset of the standard almanac convention, e.g., the crossings of the // geometric altitude of the centre of the Sun through the standard altitude of -0.8333° (in -// seconds). The residual carries the equation of the equinoxes, as the meridian transit does: -const STANDARD_RISE_AND_SET_TOLERANCE = 2 +// seconds). The altitude is resolved from the apparent hour angle, and so the equation of the +// equinoxes is balanced, and the residual is that of the bisection resolution and of the +// apparent place of the Sun against the full series of the SPA: +const STANDARD_RISE_AND_SET_TOLERANCE = 0.1 /*****************************************************************************************************************/ diff --git a/tests/sun.spec.ts b/tests/sun.spec.ts index 7938a0b..b5c7590 100644 --- a/tests/sun.spec.ts +++ b/tests/sun.spec.ts @@ -331,7 +331,7 @@ describe('getSunrise', () => { it('should return the sunrise of the standard almanac convention for the given date', () => { const sunrise = getSunrise(datetime, { latitude: 49.914425, longitude: -6.315165 }) - expect(sunrise?.toISOString()).toBe('2021-05-14T04:40:52.430Z') + expect(sunrise?.toISOString()).toBe('2021-05-14T04:40:53.501Z') }) it('should return an earlier sunrise for an observer at an elevation', () => { @@ -343,7 +343,7 @@ describe('getSunrise', () => { elevation: 2000 }) - expect(sunrise?.toISOString()).toBe('2021-05-14T04:31:18.119Z') + expect(sunrise?.toISOString()).toBe('2021-05-14T04:31:19.190Z') }) it('should return null for an observer in perpetual daylight', () => { @@ -371,7 +371,7 @@ describe('getSunset', () => { it('should return the sunset of the standard almanac convention for the given date', () => { const sunset = getSunset(datetime, { latitude: 49.914425, longitude: -6.315165 }) - expect(sunset?.toISOString()).toBe('2021-05-14T20:03:12.954Z') + expect(sunset?.toISOString()).toBe('2021-05-14T20:03:14.025Z') }) it('should return null for an observer in perpetual daylight', () => {