# `Astro.Lunar.Eclipse`
[🔗](https://github.com/kipcole9/astro/blob/v2.11.0/lib/astro/lunar/eclipse.ex#L1)

Finds lunar eclipses and computes their circumstances.

This module is the implementation behind `Astro.lunar_eclipse_at_or_after/2`
and `Astro.lunar_eclipse_before/2`, and works in moments (see
`t:Astro.Time.moment/0`).

## The Earth's shadow

The Earth casts two shadows away from the Sun. The *umbra* is the cone
in which the Sun is wholly hidden by the Earth; the *penumbra* is the
wider cone in which it is partly hidden. At the Moon's distance both
are circles centred on the *shadow axis*, the point opposite the Sun,
and a lunar eclipse is the Moon passing through them. It can only
happen at a full moon, and only near a node of the Moon's orbit,
because otherwise the Moon passes north or south of both circles.

Seen from the centre of the Earth, the radii of the two circles are

    penumbra  Rp = 1.01 πm + ss + πs
    umbra     Ru = 1.01 πm − ss + πs

where `πm` and `πs` are the Moon's and the Sun's equatorial horizontal
parallaxes, the angles the Earth's equatorial radius subtends at each,
and `ss` is the Sun's angular semi-diameter. The factor `1.01` is
Danjon's enlargement of the shadows for the Earth's atmosphere, a layer
about 75 km deep that stops sunlight as the solid Earth does,
`1 + 1/85 − 1/594`, the last term for the Earth's flattening at 45°.
This is the convention of NASA's eclipse catalogues. The older almanac
convention, Chauvenet's, multiplies the radii by `1.02` instead,

    penumbra  Rp = 1.02 (0.99834 πm + ss + πs)
    umbra     Ru = 1.02 (0.99834 πm − ss + πs)

which makes the umbral magnitude about 0.006 larger and can make an
eclipse at the limit total where Danjon's makes it partial.

## Greatest eclipse, gamma and magnitude

The Moon's angular distance `σ` from the shadow axis is least at
*greatest eclipse*. *Gamma* is that least distance in equatorial
radii of the Earth, positive when the Moon passes north of the axis.
The *penumbral* and *umbral magnitudes* are the fractions of the Moon's
diameter inside each shadow at greatest eclipse,

    penumbral magnitude = (Rp + sm − σ) / 2sm
    umbral magnitude    = (Ru + sm − σ) / 2sm

where `sm` is the Moon's angular semi-diameter. An eclipse is total
when the umbral magnitude is at least 1, partial when it is between 0
and 1, and penumbral when only the penumbral magnitude is positive.

## The contacts

The eclipse begins when the Moon's limb first touches the penumbra
(P1) and ends when it leaves it (P4). A partial or total eclipse has
two more contacts with the umbra, U1 and U4, and a total eclipse two
more, U2 and U3, between which the Moon is wholly inside the umbra.
Each is the instant `σ` equals a shadow's radius plus or minus `sm`,
found by bisection either side of greatest eclipse.

The contacts happen at the same instants wherever the Moon is seen;
`Astro.Lunar.Eclipse.Local` gives the Moon's altitude at each from a
place, and whether the eclipse is seen there.

## Positions

The Sun's and the Moon's apparent geocentric positions and distances
are those of `Astro.sun_position_at/1` and `Astro.moon_position_at/1`:
from the JPL ephemeris where the loaded kernel covers the moment, and
from the analytical series elsewhere.

# `eclipse_type`

```elixir
@type eclipse_type() :: :penumbral | :partial | :total
```

The kind of lunar eclipse: the Moon passes through the penumbra only,
partly into the umbra, or wholly into it.

# `shadow`

```elixir
@type shadow() :: :danjon | :chauvenet
```

The convention for enlarging the Earth's shadows: Danjon's, used by
NASA's catalogues, or Chauvenet's, the older almanac convention.

# `t`

```elixir
@type t() :: %Astro.Lunar.Eclipse{
  gamma: float(),
  greatest: DateTime.t(),
  partial_begins: DateTime.t() | nil,
  partial_ends: DateTime.t() | nil,
  penumbral_begins: DateTime.t(),
  penumbral_ends: DateTime.t(),
  penumbral_magnitude: float(),
  saros: integer(),
  total_begins: DateTime.t() | nil,
  total_ends: DateTime.t() | nil,
  type: eclipse_type(),
  umbral_magnitude: float()
}
```

A lunar eclipse. Every instant is a UTC `t:DateTime.t/0`; the umbral
contacts are `nil` for an eclipse that does not reach them, and the
Saros series is numbered as NASA numbers it (`Astro.Saros`).

# `eclipse_at_or_after`

```elixir
@spec eclipse_at_or_after(Astro.Time.moment(), shadow()) ::
  {:ok, map()} | {:error, :no_eclipse}
```

Returns the circumstances of the first lunar eclipse whose greatest
eclipse is at or after a moment.

### Arguments

* `t` is a `t:Astro.Time.moment/0` within 50,000 years of J2000.

* `shadow` is the enlargement convention, `:danjon` or `:chauvenet`.

### Returns

* `{:ok, circumstances}`, a map with the keys of `t:t/0`, every
  instant a moment and every missing contact `nil`.

* `{:error, :no_eclipse}` if none is found within 13 lunations, which
  does not happen.

### Examples

    iex> {:ok, eclipse} = Astro.Lunar.Eclipse.eclipse_at_or_after(739_600.0, :danjon)
    iex> {eclipse.type, Float.round(eclipse.umbral_magnitude, 3)}
    {:total, 1.178}

# `eclipse_before`

```elixir
@spec eclipse_before(Astro.Time.moment(), shadow()) ::
  {:ok, map()} | {:error, :no_eclipse}
```

Returns the circumstances of the last lunar eclipse whose greatest
eclipse is before a moment.

### Arguments

* `t` is a `t:Astro.Time.moment/0` within 50,000 years of J2000.

* `shadow` is the enlargement convention, `:danjon` or `:chauvenet`.

### Returns

* `{:ok, circumstances}`, a map with the keys of `t:t/0`, every
  instant a moment and every missing contact `nil`.

* `{:error, :no_eclipse}` if none is found within 13 lunations, which
  does not happen.

### Examples

    iex> {:ok, eclipse} = Astro.Lunar.Eclipse.eclipse_before(739_700.0, :danjon)
    iex> eclipse.type
    :total

# `geometry`

```elixir
@spec geometry(Astro.Time.moment(), shadow()) :: %{
  distance: float(),
  penumbra: float(),
  umbra: float(),
  semi_diameter: float(),
  gamma: float()
}
```

Returns the geometry of the Earth's shadow and the Moon at a moment.

### Arguments

* `t` is a `t:Astro.Time.moment/0`.

* `shadow` is the enlargement convention, `:danjon` or `:chauvenet`.

### Returns

* A map with these keys, every angle in degrees:

  * `:distance` — the Moon's angular distance from the shadow axis, `σ`.

  * `:penumbra` and `:umbra` — the radii of the shadows at the Moon,
    `Rp` and `Ru`.

  * `:semi_diameter` — the Moon's angular semi-diameter, `sm`.

  * `:gamma` — `σ` in equatorial radii of the Earth at the Moon's
    distance, positive when the Moon is north of the axis.

### Examples

    iex> geometry = Astro.Lunar.Eclipse.geometry(739_689.2917, :danjon)
    iex> Float.round(geometry.gamma, 3)
    0.349

---

*Consult [api-reference.md](api-reference.md) for complete listing*
