Class AstronomicalCalculator
- All Implemented Interfaces:
Cloneable
- Direct Known Subclasses:
MeeusCalculator, NOAACalculator, SPACalculator, SunTimesCalculator
- Author:
- © Eliyahu Hershfeld 2004 - 2026
- TODO:
- Consider methods that would allow atmospheric modeling. This can currently be adjusted by
setting the refraction.
-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate doubleThe commonly used average earth radius of 6371.0088 KM (the IUGG mean radius R1 = (2a + b) / 3 of the WGS-84 ellipsoid).private static final doubleThe zenith of astronomical sunrise and sunset.private doubleThe commonly used average solar refraction in degrees that defaults to 34′, or 0.5666°.private static final double[]The Sun's apparent angular semi-diameter (the "solar radius") in degrees for each day of the year, precomputed from the VSOP87 Earth-Sun distance for reference year 2050.private doubleThe commonly used average solar radius that is about 16′ or 0.2666°.private booleanWhether sunrise and sunset should use the date-based apparent solar radius (getApparentSolarRadius(LocalDate)), which varies slightly through the year as the Earth-Sun distance changes, rather than the fixedsolar radius. -
Constructor Summary
ConstructorsConstructorDescription -
Method Summary
Modifier and TypeMethodDescriptionprotected static doubleacosDegrees(double angle) A helper method to retun the arc cosine in degrees.(package private) doubleadjustZenith(double zenith, double elevation, LocalDate localDate) Adjusts the zenith of astronomical sunrise and sunset to account for solar refraction, solar radius and elevation.protected static doubleasinDegrees(double angle) A helper method to retun the arc sine in degrees.clone()protected static doublecosDegrees(double angle) A helper method to retun the cosine in degrees.booleandoublegetApparentSolarRadius(LocalDate localDate) Returns the Sun's apparent angular semi-diameter (the "solar radius") for the given date, in degrees.abstract StringReturns the name of the algorithm.static AstronomicalCalculatorReturns the default class for calculating sunrise and sunset.doubleA method that returns the earth radius in KM.(package private) doublegetElevationAdjustment(double elevation) Method to return the adjustment to the zenith required to account for the elevation.doubleMethod to get the refraction value to be used when calculating sunrise and sunset.abstract doublegetSolarAzimuth(Instant instant, GeoLocation geoLocation) Return the Solar Azimuth for the horizontal coordinate system at the given location at the given time.abstract doublegetSolarElevation(Instant instant, GeoLocation geoLocation) Return the Solar Elevation for the horizontal coordinate system at the given location at the given time.doubleMethod to get the fixed sun's radius.abstract doublegetTimeAtAzimuth(LocalDate localDate, GeoLocation geoLocation, double azimuth) Returns the time that the azimuth will occur for the date and location passed to this method.abstract doublegetUTCMidnight(LocalDate localDate, GeoLocation geoLocation) Return the Universal Coordinated Time (UTC) of solar midnight (UTC) for the given day at the given location.abstract doublegetUTCNoon(LocalDate localDate, GeoLocation geoLocation) Return the Universal Coordinated Time (UTC) of solar noon (UTC) for the given day at the given location.abstract doublegetUTCSunrise(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation) A method that calculates UTC sunrise as well as any time based on an angle above or below sunrise.abstract doublegetUTCSunset(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation) A method that calculates UTC sunset as well as any time based on an angle above or below sunset.inthashCode()booleanReturns if useApparentSolarRadius is true (the default) or false.voidsetEarthRadius(double earthRadius) A method that allows setting the earth's radius.voidsetRefraction(double refraction) A method to allow overriding the default refraction of the calculator.voidsetSolarRadius(double solarRadius) Sets the Sun's radius as a fixed, date-independent value in degrees, for example16.0 / 60.0for the conventional 16′.voidsetUseApparentSolarRadius(boolean useApparentSolarRadius) Sets if useApparentSolarRadius should be true (the default) or false.protected static doublesinDegrees(double angle) A helper method to retun the sine in degrees.protected static doubletanDegrees(double angle) A helper method to retun the tan / tangent in degrees.
-
Field Details
-
refraction
The commonly used average solar refraction in degrees that defaults to 34′, or 0.5666°. Calendrical Calculations lists a more accurate global average of 34.478885263888294′ or 0.574648087731472°,- See Also:
-
solarRadius
The commonly used average solar radius that is about 16′ or 0.2666°.- See Also:
-
useApparentSolarRadius
Whether sunrise and sunset should use the date-based apparent solar radius (getApparentSolarRadius(LocalDate)), which varies slightly through the year as the Earth-Sun distance changes, rather than the fixedsolar radius. Defaults totrue. Whenfalse, the fixedgetSolarRadius()value (default 16′) is used instead, which is appropriate for matching other implementations that assume a constant solar radius. Note that callingsetSolarRadius(double)automatically sets this tofalse.- See Also:
-
earthRadius
The commonly used average earth radius of 6371.0088 KM (the IUGG mean radius R1 = (2a + b) / 3 of the WGS-84 ellipsoid). At this time, this only affects the elevation adjustment, not the underlying solar position calculations.- See Also:
-
GEOMETRIC_ZENITH
The zenith of astronomical sunrise and sunset. The sun is 90° from the vertical 0°- See Also:
-
SOLAR_RADIUS_BY_DAY_OF_YEAR
The Sun's apparent angular semi-diameter (the "solar radius") in degrees for each day of the year, precomputed from the VSOP87 Earth-Sun distance for reference year 2050. The Sun's apparent size varies through the year as the Earth-Sun distance changes between the perihelion (~Jan 3, about 16.27′ or 0.2710°) and the aphelion (~Jul 5, about 15.73′ or 0.2622°), with a mean of the conventional 16′. The table has 365 entries indexed by day-of-year (1-365); 2050 is a common year, so February 29 reuses the February 28 value (seegetApparentSolarRadius(LocalDate)).Valid epoch. Reference year 2050 was chosen as the midpoint of the years 2000-2100 so that the small drift in the value for a fixed calendar date stays symmetric and minimal across that span. The date of the perihelion advances through the calendar by roughly one day every 57 years, so the apparent radius for a given calendar date is not perfectly constant from year to year. Across 2000-2100 this table is within about 0.4″ of the exact value (at most roughly 50 ms of sunrise / sunset time at latitude 60°, and less elsewhere) - negligible for any practical purpose. Outside 2000-2100 the table is still returned but its accuracy degrades gradually as the perihelion drifts further; for use centuries away from 2050 the table should be regenerated against a closer reference year.
A fixed table is used in preference to evaluating the VSOP87 series at run time because, given the negligible drift above, the lookup is both simpler and faster while remaining far more accurate than any sunrise / sunset calculation requires.
- See Also:
-
-
Constructor Details
-
AstronomicalCalculator
public AstronomicalCalculator()
-
-
Method Details
-
isUseApparentSolarRadius
Returns if useApparentSolarRadius is true (the default) or false.- Returns:
- if useApparentSolarRadius is true or false.
- See Also:
-
setUseApparentSolarRadius
Sets if useApparentSolarRadius should be true (the default) or false.- Parameters:
useApparentSolarRadius- should apparent solar radius be used (default is true).- See Also:
-
getEarthRadius
A method that returns the earth radius in KM. The value currently defaults to the commonly used average earth radius of 6371.0088 KM (the IUGG mean radius R1 = (2a + b) / 3 of the WGS-84 ellipsoid) if not set. At this time, this only affects the elevation adjustment, not the underlying solar position calculations.- Returns:
- the earth radius in KM.
-
setEarthRadius
A method that allows setting the earth's radius.- Parameters:
earthRadius- the earthRadius to set in KM
-
getDefault
Returns the default class for calculating sunrise and sunset. This is currently the more accurateNOAACalculator, but this may change in the future.- Returns:
- AstronomicalCalculator the default class for calculating sunrise and sunset. In the current
implementation the default calculator returned is the more accurate
NOAACalculator.
-
getCalculatorName
Returns the name of the algorithm.- Returns:
- the descriptive name of the algorithm.
-
getUTCSunrise
public abstract double getUTCSunrise(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation) A method that calculates UTC sunrise as well as any time based on an angle above or below sunrise. This abstract method is implemented by the classes that extend this class.- Parameters:
localDate- TheLocalDaterepresenting the date to calculate sunrise for.geoLocation- The location information used for astronomical calculation of solar times.zenith- the azimuth below the vertical zenith of 90°. For sunrise typically thezenithused for the calculation uses geometric zenith of 90° andadjuststhis slightly to account for solar refraction and the sun's radius. Another example would beAstronomicalCalendar.getBeginNauticalTwilight()that passesAstronomicalCalendar.NAUTICAL_ZENITHto this method.adjustForElevation- Should the time be adjusted for elevation- Returns:
- The UTC time of sunrise in 24-hour format. 5:45:00 AM will return 5.75. If an error was encountered in the
calculation (expected behavior for some locations such as near the poles,
Double.NaNwill be returned. - See Also:
-
getUTCSunset
public abstract double getUTCSunset(LocalDate localDate, GeoLocation geoLocation, double zenith, boolean adjustForElevation) A method that calculates UTC sunset as well as any time based on an angle above or below sunset. This abstract method is implemented by the classes that extend this class.- Parameters:
localDate- TheLocalDate> representing the date to calculate sunset for.geoLocation- The location information used for astronomical calculation of solar times.zenith- the azimuth below the vertical zenith of 90°. For sunset typically the zenith used for the calculation uses geometric zenith of 90° andadjuststhis slightly to account for solar refraction and the sun's radius. Another example would beAstronomicalCalendar.getEndNauticalTwilight()that passesAstronomicalCalendar.NAUTICAL_ZENITHto this method.adjustForElevation- Should the time be adjusted for elevation- Returns:
- The UTC time of sunset in 24-hour format. 5:45:00 AM will return 5.75. If an error was encountered in the
calculation (expected behavior for some locations such as near the poles,
Double.NaNwill be returned. - See Also:
-
getUTCNoon
Return the Universal Coordinated Time (UTC) of solar noon (UTC) for the given day at the given location. TheNOAACalculator, the default calculator implementation calculates true solar noon, something that can be calculated even in the Arctic / Antarctic where there may be no sunrise or sunset, while theSunTimesCalculatorapproximates it, calculating the time as halfway between sunrise and sunset, something that can't be calculated in Polar regions where there is no sunrise or sunset for part of the year. See The Definition of Chatzos for details on solar noon / midnight calculations.- Parameters:
localDate- TheLocalDaterepresenting the date to calculate noon for.geoLocation- The location information used for astronomical calculation of solar times.- Returns:
- The UTC time of solar noon in 24-hour format. 1:45:00 PM will return 13.75. If an error was encountered in the
the calculation (expected behavior for some locations such as near the poles,
Double.NaNwill be returned. - See Also:
-
getUTCMidnight
Return the Universal Coordinated Time (UTC) of solar midnight (UTC) for the given day at the given location. TheNOAACalculator, the default calculator implementation calculates true solar midnight, something that can be calculated even in the Arctic / Antarctic where there may be no sunrise or sunset, while theSunTimesCalculatorapproximates it, calculating the time as 12 hours after halfway between sunrise and sunset, something that can't be calculated in Polar regions where there is no sunrise or sunset for part of the year. See The Definition of Chatzos for details on solar noon / midnight calculations.- Parameters:
localDate- TheLocalDaterepresenting the date to calculate midnight for. The calculation will be for midnight at the end of the day passed in.geoLocation- The location information used for astronomical calculation of solar times.- Returns:
- The UTC time of solar midnight in a 24-hour
doubleformat. 1:45:00 AM will return 1.75. If an error was encountered in the calculation (expected behavior for theSunTimesCalculatorat times of the year in Polar regions),Double.NaNwill be returned. - See Also:
-
getTimeAtAzimuth
public abstract double getTimeAtAzimuth(LocalDate localDate, GeoLocation geoLocation, double azimuth) Returns the time that the azimuth will occur for the date and location passed to this method. As an example, to know when the sun will be directly west, pass in an azimuth of 270°, and for directly east, pass in an azimuth of 90°.- Parameters:
localDate- TheLocalDaterepresenting the date to calculate the time of the azimuth for.geoLocation- The location information used for astronomical calculation of solar times.azimuth- the azimuth to run the calculation for- Returns:
- The UTC time that the azimuth will be reached in a 24-hour
doubleformat. 5:45:00 PM will return 17.75. If an error was encountered in the calculation (expected behavior for some dates at latitudes below 23.44°, aDouble.NaNwill be returned.
-
getSolarElevation
Return the Solar Elevation for the horizontal coordinate system at the given location at the given time. Can be negative if the sun is below the horizon. Not corrected for altitude.- Parameters:
instant- Theinstantto calculate the elevation for.geoLocation- The location information used for astronomical calculation of solar times.- Returns:
- solar elevation in degrees. The horizon (calculated in a vacuum using the solar radius as the point)is 90°, civil twilight is 96° etc. This means that sunrise and sunset that do use refraction and are calculated from the upper limb of the sun will return about 0.8333°.
-
getSolarAzimuth
Return the Solar Azimuth for the horizontal coordinate system at the given location at the given time. Not corrected for altitude. True south is 180°.- Parameters:
instant- Theinstantto calculate the azimuth for.geoLocation- The location information used for astronomical calculation of solar times.- Returns:
- the solar azimuth in degrees. Astronomical midday would be 180° in the northern hemisphere and 0° in the southern hemisphere. Depending on the location and time of year, sunrise will have an azimuth of about 90° and sunset about 270°.
-
getElevationAdjustment
Method to return the adjustment to the zenith required to account for the elevation. Since a person at a higher elevation can see farther below the horizon, the calculation for sunrise / sunset is calculated below the horizon used at sea level. This is only used for sunrise and sunset and not times before or after it such asnautical twilightsince those calculations are based on the level of available light at the given dip below the horizon, something that is not affected by elevation, the adjustment should only be made if the zenith == 90°adjustedfor refraction and solar radius. The algorithm used iselevationAdjustment = Math.toDegrees(Math.acos(earthRadiusInMeters / (earthRadiusInMeters + elevationMeters)));
The source of this algorithm is Calendrical Calculations by Edward M. Reingold and Nachum Dershowitz. An alternate algorithm that produces similar (but not completely accurate) result found in Ma'aglay Tzedek by Moishe Kosower and other sources is:elevationAdjustment = 0.0347 * Math.sqrt(elevationMeters);
- Parameters:
elevation- elevation in Meters.- Returns:
- the adjusted zenith
-
adjustZenith
Adjusts the zenith of astronomical sunrise and sunset to account for solar refraction, solar radius and elevation. The value for Sun's zenith and true rise/set Zenith (used in this class and subclasses) is the angle that the center of the Sun makes to a line perpendicular to the Earth's surface. If the Sun were a point and the Earth were without an atmosphere, true sunset and sunrise would correspond to a 90° zenith. Because the Sun is not a point, and because the atmosphere refracts light, this 90° zenith does not, in fact, correspond to true sunset or sunrise, instead the center of the Sun's disk must lie just below the horizon for the upper edge to be obscured. This means that a zenith of just above 90° must be used. The Sun subtends an angle of 16 minutes of arc (this can be changed via thesetSolarRadius(double)method , and atmospheric refraction accounts for 34 minutes or so (this can be changed via thesetRefraction(double)method), giving a total of 50 arcminutes. The total value for ZENITH is 90+(5/6) or 90.8333333° for true sunrise/sunset. Since a person at an elevation can see below the horizon of a person at sea level, this will also adjust the zenith to account for elevation if available. Note that this will only adjust the value if the zenith is exactly 90°. For values below and above this no correction is done. As an example, astronomical twilight is when the sun is 18° below the horizon or108° below the zenith. This is traditionally calculated with none of the above mentioned adjustments. The same goes for various tzais and alos times such as the16.1°dip used inZmanimCalendar.getAlos16Point1Degrees().- Parameters:
zenith- the azimuth below the vertical zenith of 90°. For sunset typically thezenithused for the calculation uses geometric zenith of 90° andadjuststhis slightly to account for solar refraction and the sun's radius. Another example would beAstronomicalCalendar.getEndNauticalTwilight()that passesAstronomicalCalendar.NAUTICAL_ZENITHto this method.elevation- elevation in Meters.localDate- the date to use for the solar radius. SeegetApparentSolarRadius(LocalDate).- Returns:
- The zenith adjusted to include the
sun's radius,refractionandelevationadjustment. This will only be adjusted for sunrise and sunset (if the zenith == 90°) - See Also:
-
getRefraction
Method to get the refraction value to be used when calculating sunrise and sunset. The default value is 34 arcminutes (returned in degrees). The Errata and Notes for Calendrical Calculations: The Millennium Edition by Edward M. Reingold and Nachum Dershowitz lists the actual average refraction value as 34.478885263888294 or approximately 34' 29". The refraction value as well as the solar radius and elevation adjustment are added to the zenith used to calculate sunrise and sunset.- Returns:
- The refraction in degrees.
-
setRefraction
A method to allow overriding the default refraction of the calculator.- Parameters:
refraction- The refraction in degrees.- See Also:
- TODO:
- At some point in the future, an AtmosphericModel or Refraction object that models the atmosphere of different locations might be used for increased accuracy.
-
getSolarRadius
Method to get the fixed sun's radius. The default value is 16 arcminutes. The sun's radius as it appears from earth is almost universally given as 16 arcminutes but in fact it differs by the time of the year. At the perihelion it has an apparent radius of 16.293′ (0.2710°), while at the aphelion it has an apparent radius of 15.755′ (0.2622°). There is little effect for most locations, but at high and low latitudes the difference becomes more apparent. Calculations for the difference at the location of the Royal Observatory, Greenwich show only a 4.494-second difference between the perihelion and aphelion radii, but moving into the arctic circle the difference becomes more noticeable. Tests for Tromso, Norway (latitude 69.67°, longitude 19.05°) show that on May 17, the rise of the midnight sun, a 2 minute and 23 second difference is observed between the perihelion and aphelion radii using the USNO algorithm, but only 1 minute and 6 seconds difference using the NOAA algorithm. Areas farther north show an even greater difference. Note that these are not real-world tests. It simply compared the min and max solar radius at different locations. Real world examples of comparing the actual apparent solar radius would yield less significant differences. Regardless, this is exactly the error that the date-based apparent solar radius eliminates: by default (isUseApparentSolarRadius()istrue) sunrise and sunset usegetApparentSolarRadius(LocalDate)instead, and the fixed value returned here only applies when that setting isfalse(typically to match other implementations that assume a constant 16′ radius; seesetSolarRadius(double), which switches that setting off automatically).- Returns:
- The fixed sun's radius in degrees, used only when
isUseApparentSolarRadius()isfalse. - See Also:
-
setSolarRadius
Sets the Sun's radius as a fixed, date-independent value in degrees, for example16.0 / 60.0for the conventional 16′. This is typically used to match another implementation or reference that assumes a constant solar radius.A fixed radius has no effect while
isUseApparentSolarRadius()istrue(the default), since in that mode the date-basedapparent solar radiusis used instead. So that a value set here actually takes effect, this method also disables that mode (equivalent to callingsetUseApparentSolarRadius(boolean)). To return to the date-based radius afterward, passtruetosetUseApparentSolarRadius(boolean).- Parameters:
solarRadius- the Sun's radius in degrees. Must be a non-negative number.- Throws:
IllegalArgumentException- ifsolarRadiusis < 0 or isDouble.NaN.- See Also:
-
equals
-
hashCode
This implementation hashes the earth radius, refraction, and solar radius properties to maintain the contract with
equals(Object). -
clone
-
tanDegrees
A helper method to retun the tan / tangent in degrees.- Parameters:
angle- the angle- Returns:
- the tangent in degrees
-
sinDegrees
A helper method to retun the sine in degrees.- Parameters:
angle- the angle- Returns:
- the sine in degrees.
-
cosDegrees
A helper method to retun the cosine in degrees.- Parameters:
angle- the angle- Returns:
- the cosine in degrees.
-
acosDegrees
A helper method to retun the arc cosine in degrees.- Parameters:
angle- the angle- Returns:
- the arc cosine in degrees.
-
asinDegrees
A helper method to retun the arc sine in degrees.- Parameters:
angle- the angle- Returns:
- the arc sine in degrees.
-
getApparentSolarRadius
Returns the Sun's apparent angular semi-diameter (the "solar radius") for the given date, in degrees. The Sun's apparent size changes over the year as the Earth-Sun distance varies between the perihelion (~Jan 3, when the Sun is largest) and the aphelion (~Jul 5, when it is smallest). The value is read from a precomputed table (SOLAR_RADIUS_BY_DAY_OF_YEAR) keyed by calendar day; the date's month and day are mapped onto the (common-year) reference year 2050 viawithYear(2050), so a February 29 input is automatically resolved to February 28 and assigned that day's value.This is the value the calculator applies automatically for sunrise and sunset while
isUseApparentSolarRadius()istrue(the default). It is recomputed from the date on every call and is never stored, so it is always correct for the date being calculated. The method is exposed only for inspection and comparison.Do not pass the result to
setSolarRadius(double).setSolarRadius(double)is meant for a fixed, date-independent radius (for example to match another implementation that uses a constant 16′). Storing a single day's apparent radius there would freeze that one day's value onto this instance and apply it incorrectly to every other date subsequently calculated (and would also switch offapparent-radius mode). To use the date-based radius, simply leaveisUseApparentSolarRadius()enabled and let the calculator call this method itself.- Parameters:
localDate- the date for which to return the Sun's apparent semi-diameter. If anullis passed, the default solar radius of 16/60 will be returned.- Returns:
- the Sun's apparent semi-diameter in degrees: about 0.2711° near the perihelion, about 0.2622° near the aphelion, and about 0.2666° - the conventional 16′ - near the equinoxes.
- See Also:
-