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') / 10Odeh (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.
Summary
Functions
Returns the evening of the next first sighting of a new crescent on or after an evening.
Returns the evening of the last first sighting of a new crescent on or before an evening.
Computes the Odeh (2004) visibility criterion for the new crescent moon.
Computes the Schaefer (1988/2000) visibility criterion for the new crescent moon.
Computes the Yallop (1997) visibility criterion for the new crescent moon.
Types
Functions
@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
locationis aGeo.PointZ.t/0struct (already normalized).dayis the evening as an integer number of days since0000-01-01.methodis:odeh,:yallopor:schaefer.optionsis a keyword list of options, as forfirst_visible_crescent_on_or_before/4.
Returns
{:ok, day}, the evening as an integer number of days since0000-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]
@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
locationis aGeo.PointZ.t/0struct (already normalized).dayis the evening as an integer number of days since0000-01-01.methodis:odeh,:yallopor:schaefer.optionsis a keyword list of options.
Options
:visibleis the list of categories that count as a sighting. The default is[:A, :B, :C].:utc_offsetand:extinctionare as for the criteria.
Returns
{:ok, day}, the evening as an integer number of days since0000-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]
@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
locationis aGeo.PointZ.t/0struct (already normalized).momentis aAstro.Time.moment/0representing the date.
Returns
{:ok, visibility}wherevisibilityis 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}
@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
locationis aGeo.PointZ.t/0struct (already normalized).momentis aAstro.Time.moment/0representing the date.optionsis a keyword list of options.
Options
:extinctionis the V-band zenith extinction coefficient. The default is0.172, a clean sea-level site. Typical values are0.12on a high mountain,0.17at sea level and0.25in hazy conditions.
Returns
{:ok, visibility}wherevisibilityis 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}
@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
locationis aGeo.PointZ.t/0struct (already normalized).momentis aAstro.Time.moment/0representing the date.
Returns
{:ok, visibility}wherevisibilityis 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}