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

Implements three criteria for predicting the visibility of the new
crescent moon: Yallop (1997), Odeh (2004), and Schaefer (1988/2000).

All three functions accept a normalized location and a moment (midnight
UTC for the date of interest) and return `{:ok, visibility}` where
`visibility` is one of `:A`, `:B`, `:C`, `:D`, or `:E`.

## Yallop (1997)

An empirical single-parameter criterion based on the **geocentric**
arc of vision (ARCV) and crescent width (W). The q-value is the
scaled residual between the observed ARCV and a cubic polynomial
fit to Bruin's (1977) visibility curves:

    ARCV' = 11.8371 − 6.3226·W + 0.7319·W² − 0.1018·W³
    q = (ARCV − ARCV') / 10

## Odeh (2004)

An updated empirical criterion using the same polynomial family as
Yallop but fitted to 737 observations (vs Yallop's 295). Uses
**topocentric** ARCV and a different intercept:

    ARCV' = 7.1651 − 6.3226·W + 0.7319·W² − 0.1018·W³
    V = ARCV − ARCV'

Odeh also enforces a Danjon limit of 6.4° (ARCL < 6.4° → not visible).

## Schaefer (1988/2000)

A physics-based model that computes the contrast between the crescent
moon's brightness and the twilight sky brightness, then compares
against the human contrast detection threshold (Blackwell 1946). The
best observation time is found by scanning from sunset to moonset.

The visibility parameter is:

    Rs = log₁₀(contrast / threshold)

where Rs > 0 means the crescent is detectable. This implementation
uses the V-band sky brightness model from Schaefer (1993) with
default atmospheric parameters suitable for a sea-level site with
clean air.

## Visibility categories

| Code | Meaning                           |
|------|-----------------------------------|
| `:A` | Visible to the naked eye          |
| `:B` | Visible with optical aid          |
| `:C` | May need optical aid              |
| `:D` | Not visible with optical aid      |
| `:E` | Not visible                       |

## References

* Yallop, B. D. (1997). *A Method for Predicting the First Sighting
  of the New Crescent Moon*. NAO Technical Note No. 69.

* Odeh, M. Sh. (2004). *New Criterion for Lunar Crescent Visibility*.
  Experimental Astronomy, 18, 39–64.

* Schaefer, B. E. (1993). *Astronomy and the Limits of Vision*.
  Vistas in Astronomy, 36, 311–361.

* Schaefer, B. E. (2000). *New Methods and Techniques for Historical
  Astronomy and Archaeoastronomy*. Archaeoastronomy, XV, 121–136.

# `visibility`

```elixir
@type visibility() :: :A | :B | :C | :D | :E
```

# `first_visible_crescent_on_or_after`
*since 2.10.0* 

```elixir
@spec first_visible_crescent_on_or_after(Geo.PointZ.t(), integer(), atom(), keyword()) ::
  {:ok, integer()} | {:error, atom()}
```

Returns the evening of the next first sighting of a new crescent on
or after an evening.

See `first_visible_crescent_on_or_before/4` for the first sighting.

### Arguments

* `location` is a `t:Geo.PointZ.t/0` struct (already normalized).

* `day` is the evening as an integer number of days since
  `0000-01-01`.

* `method` is `:odeh`, `:yallop` or `:schaefer`.

* `options` is a keyword list of options, as for
  `first_visible_crescent_on_or_before/4`.

### Returns

* `{:ok, day}`, the evening as an integer number of days since
  `0000-01-01`.

* `{:error, :no_visible_crescent}` if a lunation's crescent is not
  seen within 30 evenings of its new moon.

Raises `ArgumentError` for a day more than 50,000 years from J2000.

### Examples

    iex> mecca = %Geo.PointZ{coordinates: {39.8262, 21.4225, 277.0}}
    iex> day = Date.to_gregorian_days(~D[2024-12-20])
    iex> {:ok, evening} = Astro.Lunar.CrescentVisibility.first_visible_crescent_on_or_after(mecca, day, :odeh)
    iex> Date.from_gregorian_days(evening)
    ~D[2025-01-01]

# `first_visible_crescent_on_or_before`
*since 2.10.0* 

```elixir
@spec first_visible_crescent_on_or_before(
  Geo.PointZ.t(),
  integer(),
  atom(),
  keyword()
) ::
  {:ok, integer()} | {:error, atom()}
```

Returns the evening of the last first sighting of a new crescent on
or before an evening.

A lunation's crescent is first seen on the first evening, from the
evening before the day of its new moon, on which `method` classes it
visible. This is the latest such evening on or before `day`: the
evening a lunar month that begins at first sighting began on.

### Arguments

* `location` is a `t:Geo.PointZ.t/0` struct (already normalized).

* `day` is the evening as an integer number of days since
  `0000-01-01`.

* `method` is `:odeh`, `:yallop` or `:schaefer`.

* `options` is a keyword list of options.

### Options

* `:visible` is the list of categories that count as a sighting. The
  default is `[:A, :B, :C]`.

* `:utc_offset` and `:extinction` are as for the criteria.

### Returns

* `{:ok, day}`, the evening as an integer number of days since
  `0000-01-01`.

* `{:error, :no_visible_crescent}` if a lunation's crescent is not
  seen within 30 evenings of its new moon, as at high latitudes.

Raises `ArgumentError` for a day more than 50,000 years from J2000.

### Examples

    iex> mecca = %Geo.PointZ{coordinates: {39.8262, 21.4225, 277.0}}
    iex> day = Date.to_gregorian_days(~D[2024-12-20])
    iex> {:ok, evening} = Astro.Lunar.CrescentVisibility.first_visible_crescent_on_or_before(mecca, day, :odeh)
    iex> Date.from_gregorian_days(evening)
    ~D[2024-12-02]

# `odeh_new_visible_crescent`

```elixir
@spec odeh_new_visible_crescent(Geo.PointZ.t(), Astro.Time.moment(), keyword()) ::
  {:ok, visibility()} | {:error, atom()}
```

Computes the Odeh (2004) visibility criterion for the new crescent moon.

Uses topocentric ARCV, the same polynomial family as Yallop with an
updated intercept (7.1651), and enforces a Danjon limit of 6.4°.

### Arguments

* `location` is a `t:Geo.PointZ.t/0` struct (already normalized).

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

### Returns

* `{:ok, visibility}` where `visibility` is one of `:A`, `:B`, `:C`, `:D`, or `:E`.

* `{:error, :no_sunset}` if no sunset occurs on the given date at
  the given location.

### Examples

    iex> mecca = %Geo.PointZ{coordinates: {39.8579, 21.3891, 0.0}}
    iex> Astro.Lunar.CrescentVisibility.odeh_new_visible_crescent(mecca, Astro.Time.date_time_to_moment(~D[2024-12-02]))
    {:ok, :A}
    iex> Astro.Lunar.CrescentVisibility.odeh_new_visible_crescent(mecca, Astro.Time.date_time_to_moment(~D[2024-11-01]))
    {:ok, :E}

# `schaefer_new_visible_crescent`

```elixir
@spec schaefer_new_visible_crescent(Geo.PointZ.t(), Astro.Time.moment(), keyword()) ::
  {:ok, visibility()} | {:error, atom()}
```

Computes the Schaefer (1988/2000) visibility criterion for the new
crescent moon.

This physics-based model computes the contrast between the crescent
moon's brightness and the twilight sky brightness, comparing against
the human contrast detection threshold. The best observation time is
found by scanning from sunset to moonset.

### Arguments

* `location` is a `t:Geo.PointZ.t/0` struct (already normalized).

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

* `options` is a keyword list of options.

### Options

* `:extinction` is the V-band zenith extinction coefficient. The
  default is `0.172`, a clean sea-level site. Typical values are
  `0.12` on a high mountain, `0.17` at sea level and `0.25` in hazy
  conditions.

### Returns

* `{:ok, visibility}` where `visibility` is one of `:A`, `:B`, `:C`, `:D`, or `:E`.

* `{:error, :no_sunset}` if no sunset occurs on the given date at
  the given location.

### Examples

    iex> mecca = %Geo.PointZ{coordinates: {39.8579, 21.3891, 0.0}}
    iex> moment = Astro.Time.date_time_to_moment(~D[2024-12-02])
    iex> Astro.Lunar.CrescentVisibility.schaefer_new_visible_crescent(mecca, moment, extinction: 0.25)
    {:ok, :A}

# `yallop_new_visible_crescent`

```elixir
@spec yallop_new_visible_crescent(Geo.PointZ.t(), Astro.Time.moment(), keyword()) ::
  {:ok, visibility()} | {:error, atom()}
```

Computes the Yallop (1997) visibility criterion for the new crescent moon.

Uses geocentric ARCV and the polynomial fit to Bruin's visibility curves.

### Arguments

* `location` is a `t:Geo.PointZ.t/0` struct (already normalized).

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

### Returns

* `{:ok, visibility}` where `visibility` is one of `:A`, `:B`, `:C`, `:D`, or `:E`.

* `{:error, :no_sunset}` if no sunset occurs on the given date at
  the given location.

### Examples

    iex> mecca = %Geo.PointZ{coordinates: {39.8579, 21.3891, 0.0}}
    iex> Astro.Lunar.CrescentVisibility.yallop_new_visible_crescent(mecca, Astro.Time.date_time_to_moment(~D[2024-12-02]))
    {:ok, :A}
    iex> Astro.Lunar.CrescentVisibility.yallop_new_visible_crescent(mecca, Astro.Time.date_time_to_moment(~D[2024-11-01]))
    {:ok, :E}

---

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