mirror of
https://github.com/cosinekitty/astronomy.git
synced 2026-05-19 14:27:52 -04:00
C: lunar eclipse obscuration
Starting to add support for calculating the intensity of lunar eclipses and solar eclipses in terms of "obscuration". This commit adds calculation of obscuration for lunar eclipses in C/C++. The structure returned by SearchLunarEclipse and NextLunarEclipse now includes an `obscuration` field whose value is in the range [0, 1], indicating what fraction of the Moon's apparent disc is covered by the Earth's umbra at the eclipse's peak.
This commit is contained in:
@@ -195,6 +195,7 @@ static int RefractionTest(void);
|
||||
static int ConstellationTest(void);
|
||||
static int LunarEclipseIssue78(void);
|
||||
static int LunarEclipseTest(void);
|
||||
static int LunarFractionTest(void);
|
||||
static int GlobalSolarEclipseTest(void);
|
||||
static int GravitySimulatorTest(void);
|
||||
static int PlotDeltaT(const char *outFileName);
|
||||
@@ -256,6 +257,7 @@ static unit_test_t UnitTests[] =
|
||||
{"local_solar_eclipse", LocalSolarEclipseTest},
|
||||
{"lunar_eclipse", LunarEclipseTest},
|
||||
{"lunar_eclipse_78", LunarEclipseIssue78},
|
||||
{"lunar_fraction", LunarFractionTest},
|
||||
{"magnitude", MagnitudeTest},
|
||||
#if PERFORMANCE_TESTS
|
||||
{"map", MapPerformanceTest},
|
||||
@@ -3543,7 +3545,8 @@ static int LunarEclipseTest(void)
|
||||
double diff_minutes, max_diff_minutes = 0.0, sum_diff_minutes = 0.0;
|
||||
int diff_count = 0;
|
||||
double diff_days;
|
||||
int valid = 0;
|
||||
int sd_valid = 0;
|
||||
int frac_valid = 0;
|
||||
int skip_count = 0;
|
||||
const double diff_limit = 2.0;
|
||||
extern int _CalcMoonCount; /* incremented by Astronomy Engine every time expensive CalcMoon() is called */
|
||||
@@ -3566,6 +3569,12 @@ static int LunarEclipseTest(void)
|
||||
{
|
||||
++lnum;
|
||||
|
||||
/* Make sure numeric data are finite numbers. */
|
||||
V(eclipse.obscuration);
|
||||
V(eclipse.sd_partial);
|
||||
V(eclipse.sd_penum);
|
||||
V(eclipse.sd_total);
|
||||
|
||||
/* scan test data */
|
||||
|
||||
/* 2021-05-26T11:19Z 94 9 */
|
||||
@@ -3577,32 +3586,42 @@ static int LunarEclipseTest(void)
|
||||
if (2 != sscanf(line+18, "%lf %lf", &partial_minutes, &total_minutes))
|
||||
FAIL("C LunarEclipseTest(%s line %d): invalid data format.\n", filename, lnum);
|
||||
|
||||
/* verify that the calculated eclipse semi-durations are consistent with the kind */
|
||||
/* Verify that the calculated eclipse semi-durations are consistent with the kind. */
|
||||
/* Verify that fractional coverage values also make sense for the kind. */
|
||||
|
||||
switch (eclipse.kind)
|
||||
{
|
||||
case ECLIPSE_PENUMBRAL:
|
||||
valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial == 0.0) && (eclipse.sd_total == 0.0);
|
||||
sd_valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial == 0.0) && (eclipse.sd_total == 0.0);
|
||||
frac_valid = (eclipse.obscuration == 0.0);
|
||||
break;
|
||||
|
||||
case ECLIPSE_PARTIAL:
|
||||
valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial > 0.0) && (eclipse.sd_total == 0.0);
|
||||
sd_valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial > 0.0) && (eclipse.sd_total == 0.0);
|
||||
frac_valid = (eclipse.obscuration > 0.0) && (eclipse.obscuration < 1.0);
|
||||
break;
|
||||
|
||||
case ECLIPSE_TOTAL:
|
||||
valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial > 0.0) && (eclipse.sd_total > 0.0);
|
||||
sd_valid = (eclipse.sd_penum > 0.0) && (eclipse.sd_partial > 0.0) && (eclipse.sd_total > 0.0);
|
||||
frac_valid = (eclipse.obscuration == 1.0);
|
||||
break;
|
||||
|
||||
default:
|
||||
FAIL("C LunarEclipseTest(%s line %d): invalid eclipse kind %d.\n", filename, lnum, eclipse.kind);
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
if (!sd_valid)
|
||||
{
|
||||
FAIL("C LunarEclipseTest(%s line %d): invalid semiduration(s) for kind %d: penum=%lf, partial=%lf, total=%lf.\n",
|
||||
filename, lnum, eclipse.kind, eclipse.sd_penum, eclipse.sd_partial, eclipse.sd_total);
|
||||
}
|
||||
|
||||
if (!frac_valid)
|
||||
{
|
||||
FAIL("C LunarEclipseTest(%s line %d): invalid obscuration = %0.16lf for kind %d\n",
|
||||
filename, lnum, eclipse.obscuration, eclipse.kind);
|
||||
}
|
||||
|
||||
/* check eclipse peak time */
|
||||
|
||||
diff_days = eclipse.peak.ut - peak_time.ut;
|
||||
@@ -3686,6 +3705,65 @@ fail:
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
static int LunarFractionCase(int year, int month, int day, double obscuration)
|
||||
{
|
||||
int error;
|
||||
astro_time_t time;
|
||||
astro_lunar_eclipse_t eclipse;
|
||||
double dt, diff;
|
||||
|
||||
/* Search for the first lunar eclipse to occur after the given date. */
|
||||
/* It should always happen within 24 hours of the given date. */
|
||||
time = Astronomy_MakeTime(year, month, day, 0, 0, 0.0);
|
||||
eclipse = Astronomy_SearchLunarEclipse(time);
|
||||
CHECK_STATUS(eclipse);
|
||||
|
||||
if (eclipse.kind != ECLIPSE_PARTIAL)
|
||||
FAIL("C LunarFractionCase(%04d-%02d-%02d): expected partial eclipse, but found %d.\n", year, month, day, eclipse.kind);
|
||||
|
||||
dt = V(eclipse.peak.ut - time.ut);
|
||||
if (dt < 0.0 || dt > 1.0)
|
||||
FAIL("C LunarFractionCase(%04d-%02d-%02d): eclipse occurs %0.4lf days after predicted date.\n", year, month, day, dt);
|
||||
|
||||
diff = V(eclipse.obscuration - obscuration);
|
||||
if (ABS(diff) > 0.00901)
|
||||
FAIL("C LunarFractionCase(%04d-%02d-%02d) FAIL: obscuration error = %0.8lf, expected = %0.3lf, actual = %0.8lf\n", year, month, day, diff, obscuration, eclipse.obscuration);
|
||||
DEBUG("C LunarFractionCase(%04d-%02d-%02d) fraction error = %0.8lf\n", year, month, day, diff);
|
||||
|
||||
fail:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int LunarFractionTest(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Verify calculation of the fraction of the Moon's disc covered by the Earth's umbra during a partial eclipse. */
|
||||
/* Data for this is more tedious to gather, because Espenak data does not contain it. */
|
||||
/* We already verify fraction=0.0 for penumbral eclipses and fraction=1.0 for total eclipses in LunarEclipseTest. */
|
||||
|
||||
CHECK(LunarFractionCase(2010, 6, 26, 0.506)); /* https://www.timeanddate.com/eclipse/lunar/2010-june-26 */
|
||||
CHECK(LunarFractionCase(2012, 6, 4, 0.304)); /* https://www.timeanddate.com/eclipse/lunar/2012-june-4 */
|
||||
CHECK(LunarFractionCase(2013, 4, 25, 0.003)); /* https://www.timeanddate.com/eclipse/lunar/2013-april-25 */
|
||||
CHECK(LunarFractionCase(2017, 8, 7, 0.169)); /* https://www.timeanddate.com/eclipse/lunar/2017-august-7 */
|
||||
CHECK(LunarFractionCase(2019, 7, 16, 0.654)); /* https://www.timeanddate.com/eclipse/lunar/2019-july-16 */
|
||||
CHECK(LunarFractionCase(2021, 11, 19, 0.991)); /* https://www.timeanddate.com/eclipse/lunar/2021-november-19 */
|
||||
CHECK(LunarFractionCase(2023, 10, 28, 0.060)); /* https://www.timeanddate.com/eclipse/lunar/2023-october-28 */
|
||||
CHECK(LunarFractionCase(2024, 9, 18, 0.035)); /* https://www.timeanddate.com/eclipse/lunar/2024-september-18 */
|
||||
CHECK(LunarFractionCase(2026, 8, 28, 0.962)); /* https://www.timeanddate.com/eclipse/lunar/2026-august-28 */
|
||||
CHECK(LunarFractionCase(2028, 1, 12, 0.024)); /* https://www.timeanddate.com/eclipse/lunar/2028-january-12 */
|
||||
CHECK(LunarFractionCase(2028, 7, 6, 0.325)); /* https://www.timeanddate.com/eclipse/lunar/2028-july-6 */
|
||||
CHECK(LunarFractionCase(2030, 6, 15, 0.464)); /* https://www.timeanddate.com/eclipse/lunar/2030-june-15 */
|
||||
|
||||
printf("C LunarFractionTest: PASS\n");
|
||||
|
||||
fail:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------------------------------*/
|
||||
|
||||
static int GlobalSolarEclipseTest(void)
|
||||
{
|
||||
const int expected_count = 1180;
|
||||
|
||||
@@ -217,7 +217,7 @@ static const double REFRACTION_NEAR_HORIZON = 34.0 / 60.0; /* degrees of refra
|
||||
#define SUN_RADIUS_AU (SUN_RADIUS_KM / KM_PER_AU)
|
||||
|
||||
#define EARTH_MEAN_RADIUS_KM 6371.0 /* mean radius of the Earth's geoid, without atmosphere */
|
||||
#define EARTH_ATMOSPHERE_KM 88.0 /* effective atmosphere thickness for lunar eclipses */
|
||||
#define EARTH_ATMOSPHERE_KM 88.0 /* effective atmosphere thickness for lunar eclipses. see: https://eclipse.gsfc.nasa.gov/LEcat5/shadow.html */
|
||||
#define EARTH_ECLIPSE_RADIUS_KM (EARTH_MEAN_RADIUS_KM + EARTH_ATMOSPHERE_KM)
|
||||
#define EARTH_EQUATORIAL_RADIUS_AU (EARTH_EQUATORIAL_RADIUS_KM / KM_PER_AU)
|
||||
|
||||
@@ -8915,7 +8915,7 @@ static astro_lunar_eclipse_t LunarEclipseError(astro_status_t status)
|
||||
eclipse.status = status;
|
||||
eclipse.kind = ECLIPSE_NONE;
|
||||
eclipse.peak = TimeError();
|
||||
eclipse.sd_penum = eclipse.sd_partial = eclipse.sd_total = NAN;
|
||||
eclipse.obscuration = eclipse.sd_penum = eclipse.sd_partial = eclipse.sd_total = NAN;
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
@@ -8943,6 +8943,57 @@ shadow_context_t;
|
||||
/** @endcond */
|
||||
|
||||
|
||||
static double Obscuration( /* returns area of intersection of the two discs, divided by area of first disc. */
|
||||
double a, /* radius of first disc */
|
||||
double b, /* radius of second disc */
|
||||
double c) /* distance between the centers of the discs */
|
||||
{
|
||||
double x; /* Horizontal location of intersection point on both circumferences */
|
||||
double y; /* Positive vertical location of intersection point on both circumferences */
|
||||
double radicand, lens1, lens2, obs;
|
||||
|
||||
if (a <= 0.0 || b <= 0.0)
|
||||
return 0.0; /* invalid radius */
|
||||
|
||||
if (c < 0.0)
|
||||
return 0.0; /* invalid distance between centers */
|
||||
|
||||
if (c >= a + b)
|
||||
return 0.0; /* the discs are too far apart to have any overlapping area */
|
||||
|
||||
if (c == 0.0)
|
||||
{
|
||||
/* The discs have a common center. Therefore, one disc is inside the other. */
|
||||
return (a <= b) ? 1.0 : (b*b / a*a);
|
||||
}
|
||||
|
||||
x = (a*a - b*b + c*c) / (2 * c);
|
||||
|
||||
radicand = a*a - x*x;
|
||||
if (radicand <= 0.0)
|
||||
{
|
||||
/* The circumferences do not intersect, or are tangent. */
|
||||
/* We already ruled out the case of non-overlapping discs. */
|
||||
/* Therefore, one disc is inside the other. */
|
||||
return (a <= b) ? 1.0 : (b*b / a*a);
|
||||
}
|
||||
|
||||
/* The discs overlap fractionally in a pair of lens-shaped areas. */
|
||||
|
||||
y = sqrt(radicand);
|
||||
|
||||
/* Return the overlapping fractional area. */
|
||||
/* There are two lens-shaped areas, one to the left of x, the other to the right of x. */
|
||||
/* Each part is calculated by subtracting a triangular area from a sector's area. */
|
||||
lens1 = a*a*acos(x/a) - x*y;
|
||||
lens2 = b*b*acos((c-x)/b) - (c-x)*y;
|
||||
|
||||
/* Find the fractional area with respect to the first disc. */
|
||||
obs = (lens1 + lens2) / (PI*a*a);
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
||||
static shadow_t ShadowError(astro_status_t status)
|
||||
{
|
||||
shadow_t shadow;
|
||||
@@ -9272,6 +9323,7 @@ astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime)
|
||||
/* This is at least a penumbral eclipse. We will return a result. */
|
||||
eclipse.status = ASTRO_SUCCESS;
|
||||
eclipse.kind = ECLIPSE_PENUMBRAL;
|
||||
eclipse.obscuration = 0.0;
|
||||
eclipse.peak = shadow.time;
|
||||
eclipse.sd_total = 0.0;
|
||||
eclipse.sd_partial = 0.0;
|
||||
@@ -9291,10 +9343,16 @@ astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime)
|
||||
{
|
||||
/* This is a total eclipse. */
|
||||
eclipse.kind = ECLIPSE_TOTAL;
|
||||
eclipse.obscuration = 1.0;
|
||||
eclipse.sd_total = ShadowSemiDurationMinutes(shadow.time, shadow.k - MOON_MEAN_RADIUS_KM, eclipse.sd_partial);
|
||||
if (eclipse.sd_total <= 0.0)
|
||||
return LunarEclipseError(ASTRO_SEARCH_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For lunar eclipses, we calculate the fraction of the Moon's disc covered by the Earth's umbra. */
|
||||
eclipse.obscuration = Obscuration(MOON_MEAN_RADIUS_KM, shadow.k, shadow.r);
|
||||
}
|
||||
}
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
@@ -4163,6 +4163,8 @@ When a lunar eclipse is found, it is classified as penumbral, partial, or total.
|
||||
|
||||
The `kind` field thus holds `ECLIPSE_PENUMBRAL`, `ECLIPSE_PARTIAL`, or `ECLIPSE_TOTAL`, depending on the kind of lunar eclipse found.
|
||||
|
||||
The `obscuration` field holds a value in the range [0, 1] that indicates what fraction of the Moon's apparent disc area is covered by the Earth's umbra at the eclipse's peak. This indicates how dark the peak eclipse appears. For penumbral eclipses, the obscuration is 0, because the Moon does not pass through the Earth's umbra. For partial eclipses, the obscuration is somewhere between 0 and 1. For total lunar eclipses, the obscuration is 1.
|
||||
|
||||
Field `peak` holds the date and time of the center of the eclipse, when it is at its peak.
|
||||
|
||||
Fields `sd_penum`, `sd_partial`, and `sd_total` hold the semi-duration of each phase of the eclipse, which is half of the amount of time the eclipse spends in each phase (expressed in minutes), or 0 if the eclipse never reaches that phase. By converting from minutes to days, and subtracting/adding with `center`, the caller may determine the date and time of the beginning/end of each eclipse phase.
|
||||
@@ -4171,6 +4173,7 @@ Fields `sd_penum`, `sd_partial`, and `sd_total` hold the semi-duration of each p
|
||||
| ---- | ------ | ----------- |
|
||||
| [`astro_status_t`](#astro_status_t) | `status` | `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. |
|
||||
| [`astro_eclipse_kind_t`](#astro_eclipse_kind_t) | `kind` | The type of lunar eclipse found. |
|
||||
| `double` | `obscuration` | The peak fraction of the Moon's apparent disc that is covered by the Earth's umbra. |
|
||||
| [`astro_time_t`](#astro_time_t) | `peak` | The time of the eclipse at its peak. |
|
||||
| `double` | `sd_penum` | The semi-duration of the penumbral phase in minutes. |
|
||||
| `double` | `sd_partial` | The semi-duration of the partial phase in minutes, or 0.0 if none. |
|
||||
|
||||
@@ -223,7 +223,7 @@ static const double REFRACTION_NEAR_HORIZON = 34.0 / 60.0; /* degrees of refra
|
||||
#define SUN_RADIUS_AU (SUN_RADIUS_KM / KM_PER_AU)
|
||||
|
||||
#define EARTH_MEAN_RADIUS_KM 6371.0 /* mean radius of the Earth's geoid, without atmosphere */
|
||||
#define EARTH_ATMOSPHERE_KM 88.0 /* effective atmosphere thickness for lunar eclipses */
|
||||
#define EARTH_ATMOSPHERE_KM 88.0 /* effective atmosphere thickness for lunar eclipses. see: https://eclipse.gsfc.nasa.gov/LEcat5/shadow.html */
|
||||
#define EARTH_ECLIPSE_RADIUS_KM (EARTH_MEAN_RADIUS_KM + EARTH_ATMOSPHERE_KM)
|
||||
#define EARTH_EQUATORIAL_RADIUS_AU (EARTH_EQUATORIAL_RADIUS_KM / KM_PER_AU)
|
||||
|
||||
@@ -10609,7 +10609,7 @@ static astro_lunar_eclipse_t LunarEclipseError(astro_status_t status)
|
||||
eclipse.status = status;
|
||||
eclipse.kind = ECLIPSE_NONE;
|
||||
eclipse.peak = TimeError();
|
||||
eclipse.sd_penum = eclipse.sd_partial = eclipse.sd_total = NAN;
|
||||
eclipse.obscuration = eclipse.sd_penum = eclipse.sd_partial = eclipse.sd_total = NAN;
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
@@ -10637,6 +10637,57 @@ shadow_context_t;
|
||||
/** @endcond */
|
||||
|
||||
|
||||
static double Obscuration( /* returns area of intersection of the two discs, divided by area of first disc. */
|
||||
double a, /* radius of first disc */
|
||||
double b, /* radius of second disc */
|
||||
double c) /* distance between the centers of the discs */
|
||||
{
|
||||
double x; /* Horizontal location of intersection point on both circumferences */
|
||||
double y; /* Positive vertical location of intersection point on both circumferences */
|
||||
double radicand, lens1, lens2, obs;
|
||||
|
||||
if (a <= 0.0 || b <= 0.0)
|
||||
return 0.0; /* invalid radius */
|
||||
|
||||
if (c < 0.0)
|
||||
return 0.0; /* invalid distance between centers */
|
||||
|
||||
if (c >= a + b)
|
||||
return 0.0; /* the discs are too far apart to have any overlapping area */
|
||||
|
||||
if (c == 0.0)
|
||||
{
|
||||
/* The discs have a common center. Therefore, one disc is inside the other. */
|
||||
return (a <= b) ? 1.0 : (b*b / a*a);
|
||||
}
|
||||
|
||||
x = (a*a - b*b + c*c) / (2 * c);
|
||||
|
||||
radicand = a*a - x*x;
|
||||
if (radicand <= 0.0)
|
||||
{
|
||||
/* The circumferences do not intersect, or are tangent. */
|
||||
/* We already ruled out the case of non-overlapping discs. */
|
||||
/* Therefore, one disc is inside the other. */
|
||||
return (a <= b) ? 1.0 : (b*b / a*a);
|
||||
}
|
||||
|
||||
/* The discs overlap fractionally in a pair of lens-shaped areas. */
|
||||
|
||||
y = sqrt(radicand);
|
||||
|
||||
/* Return the overlapping fractional area. */
|
||||
/* There are two lens-shaped areas, one to the left of x, the other to the right of x. */
|
||||
/* Each part is calculated by subtracting a triangular area from a sector's area. */
|
||||
lens1 = a*a*acos(x/a) - x*y;
|
||||
lens2 = b*b*acos((c-x)/b) - (c-x)*y;
|
||||
|
||||
/* Find the fractional area with respect to the first disc. */
|
||||
obs = (lens1 + lens2) / (PI*a*a);
|
||||
return obs;
|
||||
}
|
||||
|
||||
|
||||
static shadow_t ShadowError(astro_status_t status)
|
||||
{
|
||||
shadow_t shadow;
|
||||
@@ -10966,6 +11017,7 @@ astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime)
|
||||
/* This is at least a penumbral eclipse. We will return a result. */
|
||||
eclipse.status = ASTRO_SUCCESS;
|
||||
eclipse.kind = ECLIPSE_PENUMBRAL;
|
||||
eclipse.obscuration = 0.0;
|
||||
eclipse.peak = shadow.time;
|
||||
eclipse.sd_total = 0.0;
|
||||
eclipse.sd_partial = 0.0;
|
||||
@@ -10985,10 +11037,16 @@ astro_lunar_eclipse_t Astronomy_SearchLunarEclipse(astro_time_t startTime)
|
||||
{
|
||||
/* This is a total eclipse. */
|
||||
eclipse.kind = ECLIPSE_TOTAL;
|
||||
eclipse.obscuration = 1.0;
|
||||
eclipse.sd_total = ShadowSemiDurationMinutes(shadow.time, shadow.k - MOON_MEAN_RADIUS_KM, eclipse.sd_partial);
|
||||
if (eclipse.sd_total <= 0.0)
|
||||
return LunarEclipseError(ASTRO_SEARCH_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For lunar eclipses, we calculate the fraction of the Moon's disc covered by the Earth's umbra. */
|
||||
eclipse.obscuration = Obscuration(MOON_MEAN_RADIUS_KM, shadow.k, shadow.r);
|
||||
}
|
||||
}
|
||||
return eclipse;
|
||||
}
|
||||
|
||||
@@ -746,6 +746,12 @@ astro_eclipse_kind_t;
|
||||
* The `kind` field thus holds `ECLIPSE_PENUMBRAL`, `ECLIPSE_PARTIAL`, or `ECLIPSE_TOTAL`,
|
||||
* depending on the kind of lunar eclipse found.
|
||||
*
|
||||
* The `obscuration` field holds a value in the range [0, 1] that indicates what fraction
|
||||
* of the Moon's apparent disc area is covered by the Earth's umbra at the eclipse's peak.
|
||||
* This indicates how dark the peak eclipse appears. For penumbral eclipses, the obscuration
|
||||
* is 0, because the Moon does not pass through the Earth's umbra. For partial eclipses,
|
||||
* the obscuration is somewhere between 0 and 1. For total lunar eclipses, the obscuration is 1.
|
||||
*
|
||||
* Field `peak` holds the date and time of the center of the eclipse, when it is at its peak.
|
||||
*
|
||||
* Fields `sd_penum`, `sd_partial`, and `sd_total` hold the semi-duration of each phase
|
||||
@@ -758,6 +764,7 @@ typedef struct
|
||||
{
|
||||
astro_status_t status; /**< `ASTRO_SUCCESS` if this struct is valid; otherwise an error code. */
|
||||
astro_eclipse_kind_t kind; /**< The type of lunar eclipse found. */
|
||||
double obscuration; /**< The peak fraction of the Moon's apparent disc that is covered by the Earth's umbra. */
|
||||
astro_time_t peak; /**< The time of the eclipse at its peak. */
|
||||
double sd_penum; /**< The semi-duration of the penumbral phase in minutes. */
|
||||
double sd_partial; /**< The semi-duration of the partial phase in minutes, or 0.0 if none. */
|
||||
|
||||
Reference in New Issue
Block a user