Astro

Hex.pm Hex.pm Hex.pm Hex.pm

Astro is a library of accurate astronomical functions, with a focus on those that support solar, lunar and lunisolar calendars such as the Islamic, Chinese, Hebrew and Persian calendars.

Features

Supported Elixir and OTP versions

Astro requires Elixir 1.17 or later and Erlang/OTP 26 or later.

Installation

Add astro to your list of dependencies in mix.exs:

def deps do
[
{:astro, "~> 2.6"}
]
end

Install a time zone database

A time zone database is required for time zone conversions. Two popular options are tzdata and tz. Configure it in config.exs or runtime.exs as the default time zone database, for example:

# If using tzdata
config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
# If using tz
config :elixir, :time_zone_database, Tz.TimeZoneDatabase

Optionally install tz_world

The rise and set functions return a date time in the time zone of the location. The tz_world library resolves that time zone and, when it is a dependency, those functions use it automatically. Most applications configure it, although it is not required.

tz_world downloads about 50 MB of time zone boundary data, which may not suit an embedded device. Astro.sunrise/3, Astro.sunset/3, Astro.moonrise/3 and Astro.moonset/3 therefore also take a :time_zone_resolver option for a function of your own that resolves the time zone of a location.

If tz_world is a dependency, install its data:

mix deps.get
mix tz_world.update --force

Then start its backend in your application's supervision tree, either directly or through Astro.Supervisor:

defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
# tz_world's recommended backend. Alternatively, list
# Astro.Supervisor, which starts it.
TzWorld.Backend.SpatialIndex
]
options = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, options)
end
end

Quick start

# Sunrise in Sydney on December 4th
iex> {:ok, datetime} = Astro.sunrise({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> datetime
#DateTime<2019-12-04 05:37:08.672884+11:00 AEDT Australia/Sydney>
# Sunset in Sydney on December 4th
iex> {:ok, datetime} = Astro.sunset({151.20666584, -33.8559799094}, ~D[2019-12-04])
iex> datetime
#DateTime<2019-12-04 19:53:20.995687+11:00 AEDT Australia/Sydney>
# Sunset in the town of Alert in Nunavut, Canada
# ...doesn't exist since there is no sunset in summer
iex> Astro.sunset({-62.3481, 82.5018}, ~D[2019-07-01])
{:error, :no_time}
# ...or sunrise in winter
iex> Astro.sunrise({-62.3481, 82.5018}, ~D[2019-12-04])
{:error, :no_time}
# Hours of daylight on December 7th in Sydney
iex> Astro.hours_of_daylight {151.20666584, -33.8559799094}, ~D[2019-12-07]
{:ok, ~T[14:18:44]}
# No sunset in summer at high latitudes
iex> Astro.hours_of_daylight {-62.3481, 82.5018}, ~D[2019-06-07]
{:ok, ~T[23:59:59]}
# No sunrise in winter at high latitudes
iex> Astro.hours_of_daylight {-62.3481, 82.5018}, ~D[2019-12-07]
{:ok, ~T[00:00:00]}
# Calculate solstices for 2019
iex> Astro.solstice 2019, :december
{:ok, ~U[2019-12-22 04:19:19.643304Z]}
iex> Astro.solstice 2019, :june
{:ok, ~U[2019-06-21 15:54:07.713837Z]}
# Calculate equinoxes for 2019
iex> Astro.equinox 2019, :march
{:ok, ~U[2019-03-20 21:58:28.749476Z]}
iex> Astro.equinox 2019, :september
{:ok, ~U[2019-09-23 07:49:52.677810Z]}

Specifying a location

A location can be given as:

Longitude is positive east and negative west, and latitude positive north and negative south, both in degrees. Elevation is in metres.

The JPL ephemeris

Astro computes positions directly from a JPL Development Ephemeris. A compact ephemeris covering 1900 to 2100 is bundled with the package, so no download is required and Astro works as soon as it is installed.

The bundled file is extracted from JPL's DE440s kernel and contains only the Sun, Moon and Earth segments Astro uses. The Chebyshev coefficients are copied verbatim, so results are identical to those computed from the full JPL file for any date it covers.

Dates outside 1900–2100

For dates beyond the bundled range, download the full DE440s kernel, which covers 1849 to 2150:

mix astro.download_ephemeris

The file is placed in Astro's priv directory and takes precedence over the bundled ephemeris automatically. To place it elsewhere, pass --dest and configure the path:

config :astro,
ephemeris: "/path/to/de440s.bsp"

Dates outside the range of the loaded ephemeris return {:error, :not_found}.

Using a different ephemeris

The :ephemeris option accepts any compatible DAF/SPK kernel, such as de440.bsp or de441.bsp for a much wider date range.

To build your own compact ephemeris over a different span of years — trading file size for coverage at roughly 42 KB per year — use:

mix astro.build_ephemeris --from 2000 --to 2050

Coverage cannot exceed that of the source kernel, which is 1849 to 2150 for the default DE440s. Pass --source to subset a wider kernel such as de441.bsp.

Migration from Astro 1.x

The public functions in the Astro module keep the same signatures in Astro 2.x, which should mean a smooth migration in most cases. When upgrading:

Rise and set algorithms

Astro 2 finds rise and set times from the JPL DE440s ephemeris by scanning and bisecting:

For the Moon it is also fully topocentric: the observer's displacement from the Earth's centre is applied to the Moon's position before its altitude is computed, rather than using Meeus's approximation of the parallax in altitude, h0 = 0.7275π − 0.5667°.

A comparison document shows how Astro's rise and set times compare with Skyfield (JPL DE440s), the USNO (DE430) and timeanddate.com.

References

Documentation

The full API documentation is on HexDocs.

Development

The Astro test suite needs tz_world's data in the test environment. Once the other dependencies are installed, run:

MIX_ENV=test mix tz_world.update --force

License

Astro is released under the Apache License 2.0.