Astro.Solar (Astro v2.11.0)

Copy Markdown View Source

Solar position, orbital mechanics, and equinox/solstice calculations.

Two sources of the Sun's position

The Sun's apparent position comes from one of two sources:

  • The JPL DE440s ephemeris, read by Astro.Ephemeris. It is a numerical integration of the solar system, accurate to well under a milliarcsecond, but it covers a limited span of years (1900–2100 for the file bundled with Astro).

  • Analytical series, computed in this module. They describe the Earth's orbit with a mean motion, the equation of the centre and a sum of periodic perturbations. They are less accurate (about 1″ near the present) but give a position for any date.

The accuracy guide compares the two.

The analytical series work from the Earth's orbital elements: sun_geometric_mean_longitude/1, sun_geometric_mean_anomaly/1, earth_orbit_eccentricity/1 and sun_equation_of_center/1.

For sunrise and sunset calculations see Astro.Solar.SunRiseSet.

Function groups

Position

Analytical series

Orbital elements

Time

Equinoxes and solstices

Solar elevation

  • solar_elevation/1 — zenith angle for geometric, civil, nautical, astronomical twilight or a custom elevation

Time conventions

Functions in this module accept either a moment (fractional days since epoch) or Julian centuries from J2000.0, as noted in each function's documentation. Use Astro.Time.julian_centuries_from_moment/1 to convert between the two.

Summary

Functions

Returns the Sun's apparent geocentric ecliptic position from analytical series.

Returns the Sun's apparent geocentric right ascension, declination and distance from analytical series.

Returns the eccentricity of the Earth's orbit.

Returns the equation of time in minutes.

Returns the UTC datetime of an equinox or solstice.

Returns an approximate moment at or before t when the solar ecliptic longitude just exceeded lambda degrees.

Returns the solar ecliptic longitude in degrees.

Returns the moment (UT) of the first time at or after moment t when the solar ecliptic longitude will be lambda degrees.

Returns the local day on which the Sun's apparent longitude first reaches lambda at or after the start of a local day.

Returns the local day on which the Sun's apparent longitude last reached lambda, on or before a local day.

Returns the moment of solar noon on a day at a longitude.

Returns solar noon as minutes since midnight UTC.

Returns the Sun's equation of the centre in degrees.

Returns the Sun's geometric mean anomaly in degrees.

Returns the Sun's geometric mean longitude in degrees.

Functions

analytical_solar_ecliptic_position(t)

(since 2.10.0)
@spec analytical_solar_ecliptic_position(Astro.Time.moment()) ::
  {float(), float(), float()}

Returns the Sun's apparent geocentric ecliptic position from analytical series.

The longitude is the Sun's true geometric longitude from the 49-term series of Bretagnon and Simon (as given in Calendrical Calculations), corrected for aberration and for the nutation in longitude of Astro.Earth.nutation/1. Over 1900–2100 it agrees with the JPL DE440s ephemeris to about 1″ RMS and 3.3″ at most. The latitude is taken as zero, since the Sun's never exceeds about 1.2″, and the distance is the radius vector of the Keplerian ellipse.

Arguments

  • t is a moment (fractional days since the epoch).

Returns

  • {longitude, latitude, distance} where longitude is in degrees in the range 0 to 360, latitude is 0.0 and distance is in astronomical units.

Accuracy

Against JPL over 1900–2100: longitude 0.75″ RMS and 2.3″ at most, with a mean bias of +0.56″, and distance 4,300 km RMS. Between −2000 and 3000 the series agrees with Meeus's chapter 25 series within 45″; at ±10,000 years they differ by more than a day of the Sun's motion. The accuracy guide gives the details.

Examples

iex> {longitude, latitude, distance} =
...>   Astro.Solar.analytical_solar_ecliptic_position(Astro.Time.j2000())
iex> {Float.round(longitude, 4), latitude, Float.round(distance, 6)}
{280.369, 0.0, 0.983308}

Reference

  • Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, for the series of Bretagnon and Simon.

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

analytical_solar_position(t)

(since 2.10.0)
@spec analytical_solar_position(Astro.Time.moment()) :: {float(), float(), float()}

Returns the Sun's apparent geocentric right ascension, declination and distance from analytical series.

The ecliptic position of analytical_solar_ecliptic_position/1 is rotated to the equator with the true obliquity of the ecliptic by Astro.Coordinates.ecliptic_to_equatorial/3.

Arguments

  • t is a moment (fractional days since the epoch).

Returns

  • {right_ascension, declination, distance} where right_ascension is in degrees in the range 0 to 360, declination is in degrees and distance is in astronomical units.

Accuracy

Against JPL over 1900–2100: right ascension 0.76″ RMS and declination 0.50″ RMS (2.2″ and 1.3″ at most). The accuracy guide gives the details.

Examples

iex> {right_ascension, declination, distance} =
...>   Astro.Solar.analytical_solar_position(Astro.Time.j2000())
iex> {Float.round(right_ascension, 4), Float.round(declination, 4), Float.round(distance, 6)}
{281.2785, -23.0327, 0.983308}

Reference

  • Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, for the series of Bretagnon and Simon.

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 13 (transformation of coordinates).

earth_orbit_eccentricity(julian_centuries)

@spec earth_orbit_eccentricity(Astro.Time.julian_centuries()) :: float()

Returns the eccentricity of the Earth's orbit.

Arguments

  • julian_centuries is any moment in time expressed as Julian centuries from J2000.0.

Returns

  • the eccentricity as a unitless float. Zero is a circle and values between 0 and 1 are ellipses; the Earth's is about 0.0167 and slowly decreasing (Meeus, Astronomical Algorithms, equation 25.4).

Accuracy

This is a mean element, not a position. Combined as in Meeus, Astronomical Algorithms, chapter 25 (the Sun's true longitude is the mean longitude plus the equation of the centre, then corrected for aberration and nutation), the elements give the Sun's apparent longitude to 0.003° RMS and 0.009° at most against JPL over 1900–2100, the 0.01° Meeus states for the method.

Examples

iex> Astro.Solar.earth_orbit_eccentricity(0.0)
0.016708634

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

  • Orbital eccentricity, Wikipedia.

equation_of_time(julian_centuries)

@spec equation_of_time(float()) :: float()

Returns the equation of time in minutes.

Arguments

  • julian_centuries is any moment in time expressed as Julian centuries from J2000.0.

Returns

  • the discrepancy between apparent solar time and mean solar time in minutes as a float. A positive value means the sundial is ahead of the clock.

Notes

The equation of time describes the discrepancy between apparent solar time (which directly tracks the Sun's diurnal motion) and mean solar time (which tracks a theoretical mean Sun with uniform motion).

During a year the equation of time ranges from about +16 min 33 s (around 3 November) to −14 min 6 s (around 11 February), with zeros near 15 April, 13 June, 1 September, and 25 December.

The two principal causes are the obliquity of the ecliptic (~23.44°) and the eccentricity of the Earth's orbit (~0.0167).

It is calculated as in Meeus, Astronomical Algorithms, chapter 28: the Sun's mean longitude, less the constant of aberration (0.0057183°), less the Sun's apparent right ascension, plus the nutation in right ascension (Δψ cos ε), converted to minutes at four minutes of time a degree.

Accuracy

Solar noon taken as 12:00 local mean time less this equation of time agrees with the meridian transit to 0.2 s over 1850–2149. Far from the present the mean Sun, in dynamical time, and the Earth's rotation drift apart by ΔT, and the agreement falls to about 3 s at the year 1000, which is why solar_noon/2 solves for the transit instead.

Examples

iex> Astro.Solar.equation_of_time(0.0) |> Float.round(4)
-3.2815

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 28 (equation of time).

  • Equation of time, Wikipedia.

equinox_and_solstice(year, event)

@spec equinox_and_solstice(integer(), :march | :june | :september | :december) ::
  {:ok, DateTime.t()} | {:error, :year_out_of_range}

Returns the UTC datetime of an equinox or solstice.

An equinox or solstice is the moment the Sun's apparent ecliptic longitude reaches a multiple of 90°: 0° at the March equinox, 90° at the June solstice, 180° at the September equinox and 270° at the December solstice. It is found by searching for that longitude with solar_ecliptic_longitude_after/2, starting 20 days before the 1st of the month the event falls in, so it uses the same positions as the rest of this module: the JPL ephemeris where it covers the year, and the analytical series for any other year.

The month is a date in dynamical time, in which the Sun's motion is uniform, so the search starts ΔT earlier in Universal Time. ΔT is about a minute today but grows to days thousands of years from the present, and far enough back the event falls in the previous year in UTC.

Arguments

  • year is an integer Gregorian year within 50,000 years of 2000.

  • event is one of :march, :june, :september, or :december identifying the equinox or solstice.

Returns

  • {:ok, datetime} where datetime is a DateTime.t() in UTC.

  • {:error, :year_out_of_range} if year is more than 50,000 years from 2000.

Accuracy

As for Astro.equinox/3: within a second of Meeus's full VSOP87 time for the June 1962 solstice inside the ephemeris, and 18 s RMS from JPL with the series outside it.

Examples

iex> {:ok, dt} = Astro.Solar.equinox_and_solstice(2024, :march)
iex> {dt.year, dt.month, dt.day}
{2024, 3, 20}

iex> {:ok, dt} = Astro.Solar.equinox_and_solstice(-1000, :june)
iex> {dt.year, dt.month}
{-1000, 6}

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 27 (equinoxes and solstices).

estimate_prior_solar_ecliptic_longitude(lambda, t)

Returns an approximate moment at or before t when the solar ecliptic longitude just exceeded lambda degrees.

Arguments

  • lambda is the target solar ecliptic longitude in degrees.

  • t is any moment in time expressed as a moment (fractional days since the epoch).

Returns

  • the approximate moment (fractional days since the epoch) at or before t when the solar ecliptic longitude last exceeded lambda degrees.

Examples

# Estimate when the Sun last passed 90° before the June solstice
iex> moment = Astro.Time.date_time_to_moment(~D[2024-06-21])
iex> result = Astro.Solar.estimate_prior_solar_ecliptic_longitude(90, moment)
iex> {:ok, dt} = Astro.Time.date_time_from_moment(result)
iex> dt.year
2024
iex> dt.month
6

Reference

  • Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, estimate-prior-solar-longitude.

solar_ecliptic_longitude(t)

Returns the solar ecliptic longitude in degrees.

Arguments

  • t is any moment in time expressed as a moment (fractional days since the epoch).

Returns

  • the solar ecliptic longitude in degrees as a float, in the range 0..360.

Notes

The solar ecliptic longitude is the position of the Sun on the celestial sphere along the ecliptic. It is also an effective measure of the position of the Earth in its orbit around the Sun, taken as 0° at the moment of the vernal equinox.

Since it is based on how far the Earth has moved in its orbit since the equinox, it is a measure of what time of the tropical year (the year of seasons) has elapsed, without the inaccuracies of a calendar date which is perturbed by leap years and calendar imperfections.

The longitude comes from the JPL ephemeris where the loaded kernel covers t, and from analytical_solar_ecliptic_position/1, accurate to about 1″ near the present, everywhere else. Calendars that count months by the Sun's longitude, such as the Chinese and Japanese lunisolar calendars, find their solar terms for dates of any era this way.

Accuracy

From JPL inside the loaded ephemeris; elsewhere from analytical_solar_ecliptic_position/1, within 0.75″ RMS and about 2″ of JPL over 1900–2100. The accuracy guide gives the details.

Examples

iex> moment = Astro.Time.date_time_to_moment(~D[2024-03-20])
iex> Astro.Solar.solar_ecliptic_longitude(moment)
359.87140902027863

iex> moment = Astro.Time.date_time_to_moment(~D[1000-06-01])
iex> Astro.Solar.solar_ecliptic_longitude(moment) |> Float.round(4)
69.5898

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

solar_ecliptic_longitude_after(lambda, t)

@spec solar_ecliptic_longitude_after(number(), Astro.Time.time()) :: Astro.Time.time()

Returns the moment (UT) of the first time at or after moment t when the solar ecliptic longitude will be lambda degrees.

Arguments

  • lambda is the target solar ecliptic longitude in degrees.

  • t is any moment in time expressed as a moment (fractional days since the epoch).

Returns

  • the moment (fractional days since the epoch) when the solar ecliptic longitude reaches lambda degrees.

Accuracy

The search ends within a millisecond of the crossing, so the moment is as accurate as the longitude: the Sun moves about 2.5″ a minute, so 1″ of longitude is about 24 s.

Examples

# Find the June solstice (longitude 90°) in 2024
iex> moment = Astro.Time.date_time_to_moment(~D[2024-05-22])
iex> result = Astro.Solar.solar_ecliptic_longitude_after(90, moment)
iex> {:ok, dt} = Astro.Time.date_time_from_moment(result)
iex> dt.year
2024
iex> dt.month
6

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 27 (equinoxes and solstices).

solar_longitude_day_on_or_after(lambda, day, offset)

(since 2.10.0)
@spec solar_longitude_day_on_or_after(
  number(),
  integer(),
  number() | (Astro.Time.moment() -> number())
) ::
  integer()

Returns the local day on which the Sun's apparent longitude first reaches lambda at or after the start of a local day.

Calendars that follow the Sun, such as the Chinese solar terms, need only the day on which its longitude reaches a value, in the time they keep. This is that day, at the offset in force when the longitude is reached: the same day as solar_ecliptic_longitude_after/2 from local midnight, read at the local date, found faster.

The Sun's longitude always advances, at between 0.95° and 1.03° a day, so one longitude evaluated near the crossing places it within a narrow window. The search narrows the window until it falls on one local day, and only a crossing near local midnight is found to its moment.

Arguments

  • lambda is the apparent ecliptic longitude in degrees.

  • day is a local day as an integer number of days since 0000-01-01.

  • offset is the local time's offset from UTC in days, as a number, or a 1-arity function that returns the offset for a moment.

Returns

  • the local day as an integer number of days since 0000-01-01.

Accuracy

The same day as the moment search; see solar_ecliptic_longitude_after/2.

Examples

iex> day = Date.to_gregorian_days(~D[2024-01-01])
iex> Astro.Solar.solar_longitude_day_on_or_after(315, day, 8 / 24) |> Date.from_gregorian_days()
~D[2024-02-04]

Reference

  • Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, solar-longitude-after.

solar_longitude_day_on_or_before(lambda, day, offset)

(since 2.10.0)
@spec solar_longitude_day_on_or_before(
  number(),
  integer(),
  number() | (Astro.Time.moment() -> number())
) :: integer()

Returns the local day on which the Sun's apparent longitude last reached lambda, on or before a local day.

This is the day, at the offset in force when the longitude is reached, of the last time it reached lambda before the end of day. With lambda 270 it is the day of the December solstice on or before day, which the Chinese calendar counts its years from. See solar_longitude_day_on_or_after/3 for how it is found.

Arguments

  • lambda is the apparent ecliptic longitude in degrees.

  • day is a local day as an integer number of days since 0000-01-01.

  • offset is the local time's offset from UTC in days, as a number, or a 1-arity function that returns the offset for a moment.

Returns

  • the local day as an integer number of days since 0000-01-01.

Accuracy

The day of the crossing found to within a millisecond, as solar_ecliptic_longitude_after/2 finds it.

Examples

iex> day = Date.to_gregorian_days(~D[2025-03-01])
iex> Astro.Solar.solar_longitude_day_on_or_before(270, day, 8 / 24) |> Date.from_gregorian_days()
~D[2024-12-21]

Reference

  • Edward M. Reingold and Nachum Dershowitz, Calendrical Calculations, estimate-prior-solar-longitude and the winter solstice of the Chinese calendar.

solar_noon(t, longitude)

(since 2.10.0)

Returns the moment of solar noon on a day at a longitude.

Solar noon is the moment the Sun crosses a location's meridian, when its local hour angle (the local apparent sidereal time less the Sun's apparent right ascension) is zero. The search starts at 12:00 local mean time, longitude / 360 of a day before 12:00 UT, which is within about 16 minutes of the answer (the equation of time, equation_of_time/1). Each step then moves the moment by the hour angle divided by the 360° a day at which it grows, until the step is under a millisecond; three steps are usually enough.

Arguments

  • t is the moment of 0h UT on the day of solar noon.

  • longitude is the longitude in degrees of the location from which solar noon is to be measured. West is negative.

Returns

  • the moment of solar noon.

Accuracy

The hour angle is solved to a millisecond, so solar noon is as accurate as the Sun's right ascension and the sidereal time; see Astro.solar_noon/2.

Examples

iex> midnight = Astro.Time.date_time_to_moment(~D[2019-12-06])
iex> noon = Astro.Solar.solar_noon(midnight, 151.20666584)
iex> {:ok, date_time} = Astro.Time.date_time_from_moment(noon)
iex> DateTime.truncate(date_time, :second)
~U[2019-12-06 01:45:52Z]

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 15 (rising, transit and setting).

solar_noon_utc(julian_centuries, longitude)

@spec solar_noon_utc(float(), Astro.longitude()) :: float()

Returns solar noon as minutes since midnight UTC.

This is solar_noon/2 measured in minutes from the start of the day.

Arguments

Returns

  • solar noon as a float number of minutes since midnight UTC.

Accuracy

As for solar_noon/2.

Examples

iex> julian_day = Astro.Time.julian_day_from_date(~D[2000-01-01])
iex> julian_centuries = Astro.Time.julian_centuries_from_julian_day(julian_day)
iex> Astro.Solar.solar_noon_utc(julian_centuries, 151.2093) |> Float.round(2)
118.25

sun_equation_of_center(julian_centuries)

@spec sun_equation_of_center(Astro.Time.julian_centuries()) :: float()

Returns the Sun's equation of the centre in degrees.

Arguments

  • julian_centuries is any moment in time expressed as Julian centuries from J2000.0.

Returns

  • the equation of the centre in degrees as a float.

Notes

The equation of the centre is the difference between the true anomaly, where the Earth actually is on its ellipse, and the mean anomaly, where it would be at a constant speed. Adding it to the mean longitude gives the true longitude. This is the series of Meeus, Astronomical Algorithms, chapter 25, in the mean anomaly.

Accuracy

This is a mean element, not a position. Combined as in Meeus, Astronomical Algorithms, chapter 25 (the Sun's true longitude is the mean longitude plus the equation of the centre, then corrected for aberration and nutation), the elements give the Sun's apparent longitude to 0.003° RMS and 0.009° at most against JPL over 1900–2100, the 0.01° Meeus states for the method.

Examples

iex> Astro.Solar.sun_equation_of_center(0.0)
-0.08430148943719645

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

  • Equation of the center, Wikipedia.

sun_geometric_mean_anomaly(julian_centuries)

@spec sun_geometric_mean_anomaly(Astro.Time.julian_centuries()) :: float()

Returns the Sun's geometric mean anomaly in degrees.

Arguments

  • julian_centuries is any moment in time expressed as Julian centuries from J2000.0.

Returns

  • the mean anomaly in degrees as a float, in the range 0..360.

Notes

The mean anomaly is the angle the Earth would have moved through since perihelion if it travelled its orbit at a constant speed (Meeus, Astronomical Algorithms, equation 25.3). It grows uniformly with time, which makes it the natural argument for the equation of the centre.

Accuracy

This is a mean element, not a position. Combined as in Meeus, Astronomical Algorithms, chapter 25 (the Sun's true longitude is the mean longitude plus the equation of the centre, then corrected for aberration and nutation), the elements give the Sun's apparent longitude to 0.003° RMS and 0.009° at most against JPL over 1900–2100, the 0.01° Meeus states for the method.

Examples

iex> Astro.Solar.sun_geometric_mean_anomaly(0.0)
357.52911

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

  • Mean anomaly, Wikipedia.

sun_geometric_mean_longitude(julian_centuries)

@spec sun_geometric_mean_longitude(float()) :: float()

Returns the Sun's geometric mean longitude in degrees.

Arguments

  • julian_centuries is any moment in time expressed as Julian centuries from J2000.0.

Returns

  • the mean solar longitude in degrees as a float, in the range 0..360.

Notes

Mean longitude is a convenient uniform measure of how far around its orbit a body has progressed since passing the reference direction. While mean longitude assumes constant speed, true longitude accounts for the body's actual speed which varies around its elliptical orbit. The difference between the two is the equation of the centre.

Accuracy

This is a mean element, not a position. Combined as in Meeus, Astronomical Algorithms, chapter 25 (the Sun's true longitude is the mean longitude plus the equation of the centre, then corrected for aberration and nutation), the elements give the Sun's apparent longitude to 0.003° RMS and 0.009° at most against JPL over 1900–2100, the 0.01° Meeus states for the method.

Examples

iex> Astro.Solar.sun_geometric_mean_longitude(0.0)
280.46646

Reference

  • Jean Meeus, Astronomical Algorithms, 2nd edition (1998), chapter 25 (solar coordinates).

  • Mean longitude, Wikipedia.