I decided it made more sense to report the Sun's altitude
at each solar eclipse event than reporting sunrise and sunset.
Sunrise and sunset are ambiguous because it's not clear which pair
should be reported. It's also harder to interpret than knowing
whether the Sun is above/below the horizon at each interesting time.
This motivated me to create a new type astro_eclipse_event_t that
holds the (time, altitude) pair for each event.
Defined data structure astro_local_solar_eclipse_t.
Created stubs for functions to find local solar eclipses.
Renamed lunar eclipse 'center' to 'peak' to be consistent.
In all 4 supported languages, use consistent constant names for
Earth and Moon radii.
Use Moon's equatorial radius for rise/set timing.
Use Moon's mean radius for calculating Moon's umbra radius for
detecting solar eclipses.
Also use Moon's mean radius for determining whether the Earth's shadow
touches the Moon, for finding lunar eclipses.
Use the Moon's polar radius for distinguishing between total
and annular eclipses, with a 14 meter bias (instead of 1420 meters!)
to match Espenak data.
Use consistent unit test error threshold of 0.57 minutes for rise/set.
Updated demo test data for slight changes to rise/set prediction times.
Updated doxygen options to issue an error on any warnings.
Fixed the incorrect function name link that doxygen was warning me about.
Refactored the shadow calculator so that the abstract logic is centralized
in a new function CalcShadow. Use that function to calculate the umbra
radius at the peak observation site. Theoretically, any positive value
indicates a total eclipse, but I had to fudge a little to get my calculations
to match the test data.
Documented C versions of SearchGlobalSolarEclipse and NextGlobalSolarEclipse.
Removed ECLIPSE_HYBRID enumeration value. Not going to use it.
Reworded structure documentation to indicate that the eclipse
kind refers to the peak observer only.
Use sidereal time to calculate the longitude of the point
on the Earth's surface where the Moon's shadow ray strikes it.
In the unit test, ignore glancing blows, but if the shadow
ray passes within 6100 km of the Earth's center, verify that
the total angular error is within about a quarter degree.
When there is a total or annular eclipse at the peak time and location,
I am calculating the geographic latitude of that peak within
1.006 degrees. I am disappointed by how sloppy that is, so I
will have to double-check all the math, especially related
to correcting for the Earth's oblateness.
There is no need to use absolute value, and it makes the logic
easier for me to understand if I express each of the inequalities
in terms of addition rather than subtraction.
Constrain the search for partial eclipse semiduration to
within what we already found for the penumbral eclipse.
Same for total/partial. This is a very small improvement because
narrowing the search window does not improve quadratic interpolation
very much. But it is an extremely cheap and safe optimization.
It turns out that searching plus or minus 0.03 days around the
full moon is ample for finding minimum shadow distance.
This reduces CalcMoon() call count from 127155 to 112827.
Performance ratio with original algorithm = 5.13.
When the full moon's ecliptic latitude is larger than 1.8 degrees,
even a penumbral eclipse is not possible. Thus there is no need
to search for the minimum shadow distance in that case.
This decreased unit test CalcMoon() count to 127155.
Improvement ratio over original algorithm = 4.55.
Greatly reduced the number of CalcMoon() calls needed to find
the time of the minimum shadow distance, when searching for a lunar eclipse.
Use Astronomy_Search() instead of dumb search.
Added undocumented global variable for counting how manyh times CalcMoon()
is called.
The call count went from 578569 down to 207186 (ratio = 2.79).
Execution time likewise decreased from 2.9 seconds to 1.1.
I'm in the process of replacing how Astronomy Engine calculates
Delta T. Instead of a series of line segments based on canned data,
I'm switching over to use the Espenak/Meeus piecewise polynomials.
Also allowing the user to change the Delta T function to match
an external reference. I will use this in the unit tests that
reference JPL Horizons data, so that I can greatly tighten the
test tolerances.
I had to increase certain error tolerances in the unit tests.
Reworked the unit tests to make more sense by waiting until
each language step is done to check against each other.
That way I can run a single language step independently.
Using some trial and error, I found that using 85 km instead of 65.4 km
for the thickness of the Earth's atmosphere results in better overall
fit with the test data.
Increase type safety by making the enumerated type Body
derive from Enum rather than IntEnum, as recommended by
https://www.python.org/dev/peps/pep-0435/
Fixed places where I was treating Body values as integers.
Now when a Time object is evaluated and represented in
the Python interpreter, it results in a string of the form:
astronomy.Time(ut)
where ut is the numeric representation of the ut field.
This mimics the exact way such a Time value could be constructed.
That is, eval(repr(t)) results in a time value equal to t.
It turns out I was off by nearly 18 hours in the B1875 epoch.
This has a tiny effect on the orientation of the Earth's axis.
Instead of: ut = 1875-01-01T12:00:00.000Z
the correct epoch is: ut = 1874-12-31T18:12.21.950Z
See the comments in the Constellation functions in
each of the source files for more info.