I realized I had to rework the RiseSetEx function so that
it accepts a height above ground level, rather than a generic
altitude angle correction, because atmospheric effects are
important both for the horizon dip angle and for the amount
of refraction at ground level.
The atmosphere calculations were interesting enough that
I made them public as a new function Astronomy_Atmosphere.
This returns the idealized temperature, pressure, and relative
density of air at the given elevation above/below sea level.
This is the first step toward calculating body rise/set times
for an observer that is significantly above the ground.
It figures out the angular correction of the horizon
using both parallax and refractive correction of a light
ray from the horizon to the observer's eye.
The rise/set search based on hour angles is complicated,
and doesn't handle oddities that happen close to the poles.
I'm starting to rework rise/set as a more brute force solution
that iterates through finite time steps.
I also added a series of Moon data for the arctic circle,
which includes some of the more painful special cases.
For example:
Moon 130 80 2034-05-16T13:21Z s
Moon 130 80 2034-05-16T13:51Z r
Here the Moon sets, then rises 30 minutes later.
So now I'm trying to figure out how to discover
arbitrarily brief intervals like this.
I want the time increments to scale intelligently
so that we don't waste time during long periods
of inactivity (body above or below the horizon continuously),
but without missing examples like the one above.
Added sunrise/sunset test data for locations
at or near the poles. This causes rise/set tests to fail.
I need to fix these cases!
Observations:
1. Sunrise/sunset at the poles has nothing to do with
the Earth's rotation. It is based only on the Sun's
declination. Therefore, it makes no sense to look
at hour angles there.
2. At the poles, there is no such thing as a meridian.
For example, at the north pole, no matter which way
you turn, you are facing south!
So the very notion of hour angles is meaningless.
3. At the poles, there is a single sunrise and a single sunset
per calendar year. The sun rises near the spring solstice
for that hemisphere, and sets near the autumn solstice.
4. Near the poles, sunrise/sunset behavior gets more complicated.
It is possible for there to be more than one sunset or sunrise
per 24-hour period, because of changes of Sun declination.
When parsing the rise/set data, the Linux
`sort` utility had weird behavior. Fixed it
by making parse_riseset.py do its own sorting
in an order that makes sense, before writing
to riseset.txt.
Refactored SearchRiseSet to create a new function
InternalSearchAltitude. SearchRiseSet calls InternalSearchAltitude,
and the new function SearchAltitude also cals InternalSearchAltitude.
This causes the code to be only a tiny big larger.
This is the beginning of adding support for calculating
civil, nautical, and astronomical twilight (dawn/dusk).
Just added the stubbed unit test without the call in place
for the new function that will be added: SearchAltitude.
Latitudes within the arctic/antarctic circles cause too much
hassle because of horizon-grazing that makes it hard for
two different programs to agree on when (or even whether)
a rise or set took place.
The refraction formula went nuts near altitude angle -5.11 degrees.
We were taking the tangent of a value that zoomed toward infinity
near that value, causing essentially random numbers without any
upper bound to their size. Just like JPL Horizons, truncate any
angle more than 1 degree below the horizon, only I have a linear
taper down to 0 refraction as the altitude angle approaches -90.
I did not want any chance of creating an altitude less than -90.
Removed unit tests for the Sun at latitude -80 degrees.
It is too easy for my code to behave differently from another
calculator, because tiny changes in atmospheric modeling can
cause disagreement about whether there even is a sunset/sunrise.
This is because for observers so close to the pole, the Sun
sometimes barely dips below the horizon and then comes back
up for less than an hour.