There were a few more places where C# code called
Search() but did not check for a search failure.
Throw InternalError exceptions if these ever occur,
because these particular searches should always succeed.
Added an InternalError class to explicitly indicate
that an exception occurs due to an internal assertion
failure inside Astronomy Engine. Any InternalError
should be considered a bug in Astronomy Engine, not
a bug in calling code.
Upon reviewing the code for searching moon phases,
I discovered that there was inconsistent behavior
in SearchMoonPhase. It was sometimes returning null,
other times throwing an exception. Because the caller
passes in `limitDays`, it makes sense to simply
return `null` in any case where the search fails.
This is to support callers that intentionally want
to find whether or not a moon phase occurs in a given
small window of time.
Updated internal callers of SearchMoonPhase to throw
an InternalError when they know they should always
find an event.
Internal function FindSeasonChange did not check to
make sure SearchSunLongitude succeeded. There is no
known case where this failure happens, but if it did,
a null AstroTime would have been stored in SeasonsInfo.
It is better to fail early with an explicit InternalError.
Other miscellaneous C# code cleanup.
In the Python code, I found a couple of `raise Error`
that needed to be changed to `raise InternalError`.
Implemented the following related functions in Kotlin:
sunPosition
searchSunLongitude
seasons
C#: fail Astronomy.Seasons with an exception if any of the
equinox/solstice searches fail. If this ever happens, it is
an internal error. It should not be the burden of the caller
to check for nulls! Fixed mistake in documentation for
searchSunLongitude.
Implemented the low-level search function that will be used
to implement all the special-purpose search functions to come.
Added missing documentation to class NodeEventInfo members.
Minor cleanup in the C# function `Astronomy.Search`:
- removed an unused output parameter.
- deleted confusing sentence in documentation.
While working on the Kotlin implementation, I have
found a few documentation mistakes in the other language
implementations. These have been accumulating in the
`kotlin` branch. I migrated these changes back into
the released code for now, because I don't want to wait
until Kotlin is ready.
Implemented Kotlin functions and code generator
for calculating the state vectors of Jupiter's
largest 4 moons.
Added cautionary comments about needing to correct
Jupiter's moons for light travel time.
This is the first pass to get everything needed
for the AstroCheck tests. I tried comparing
C output to Kotlin output, and there are some
serious problems to debug:
$ ./ctest diff 2.8e-16 temp/{c,k}_check.txt
First file: temp/c_check.txt
Second file: temp/k_check.txt
Tolerance = 2.800e-16
lnum a_value b_value factor diff name
FAIL 137746 4.2937184148112564e+01 4.2944101081740065e+01 0.03364 2.327e-04 helio_x
FAIL 373510 1.4197190315274938e+01 1.4193716564905307e+01 0.03364 1.168e-04 helio_y
FAIL 137746 -6.5897675150466091e+00 -6.5929481589493522e+00 0.03364 1.070e-04 helio_z
FAIL 59150 1.8035183339348251e+01 1.8035909197904104e+01 0.01730 1.255e-05 sky_j2000_ra
FAIL 137747 -8.1222057639092533e+00 -8.1250990689970894e+00 0.00556 1.607e-05 sky_j2000_dec
FAIL 137747 4.8436159305823310e+01 4.8441487614058218e+01 0.03481 1.855e-04 sky_j2000_dist
FAIL 322846 8.7596368704201495e+01 2.6760770774700188e+02 0.00278 4.995e-01 sky_hor_az
FAIL 405828 -6.5075824596574279e+01 5.6922941329250996e+01 0.00556 6.778e-01 sky_hor_alt
OK 92717 4.1268347083494783e-03 4.1268347083494774e-03 223.21429 1.936e-16 jm_x
OK 45091 -8.0149190392649894e-03 -8.0149190392649929e-03 79.42812 2.756e-16 jm_y
OK 135377 1.5470777280065808e-03 1.5470777280065804e-03 223.21429 9.680e-17 jm_z
OK 216836 4.5725777238332412e-03 4.5725777238332394e-03 126.58228 2.196e-16 jm_vx
OK 351647 5.1351566793199944e-03 5.1351566793199962e-03 126.58228 2.196e-16 jm_vy
OK 351647 2.5217607180929289e-03 2.5217607180929298e-03 126.58228 1.098e-16 jm_vz
Score = 6.778e-01
ctest(Diff): EXCEEDED ERROR TOLERANCE.
So I'm checking this in as work-in-progress.
Added calculation of heliocentric and barycentric
state vectors for Pluto. This is done using
a gravity simulator that treats Pluto as a negligible
mass that is affected by the major masses of the
Solar System: Sun, Jupiter, Saturn, Uranus, Neptune.
Updated the code generator to write the Kotlin
version of the Pluto state table, a lookup table
of known-correct state vectors of Pluto at long
intervals, derived from the TOP2013 model.
The gravity simulator interpolates state vectors
of Pluto between these known-correct states.
Minor code style cleanup in the Kotlin source.
Fixed a possible thread-safety issue in the C# code.
Implemented the VSOP87 calculation functions for
heliocentric position vectors, heliocentric velocity vectors,
and heliocentric distances.
Implemented Astronomy.helioVector for everything except Pluto and SSB.
Corrected small errors in C# documentation.
Implemented the Montenbruck/Pfleger version of the NAO1954
geocentric Moon model in Kotlin.
In the process, code review helped simplify parts of the C# code.
I may want to go back and see if I can simplify the CalcMoon
code in the other languages too.
Added the rotationAxis, which calculates dynamic orientation
of planet, Sun, and Moon rotation axes. Added the first
unit test that verifies against JPL Horizons data.
Eliminated more redundant time parameters in precession functions.
More cleanup of C# code: I realized the private function
vector2radec was redundant with the public function EquatorFromVector.
Deleted vector2radec.
Added functions for generating rotation matrices,
rotating position vectors, and rotating state vectors,
for the Earth's axial nutation.
Simplified gyration functions in C#:
it turns out the `time` parameters were redundant,
because time is included in the `t` field of the vector
passed in. I reworked the C# code to eliminate those time
parameters.
The Kotlin code follows suit.
Implemented the functions `terra` and `inverseTerra`.
`terra` converts geographic coordinates and time into
a geocentric state vector.
`inverseTerra` converts a position vector and time
into geographic coordinates.
I realized the C# function `inverse_terra` could be
simplified. It does not need to be passed a separate
time, because the sidereal time can be derived
from, or has already been stored inside, the vector's
time field `ovec.t`.
There were a couple of other minor cleanups to the C# code.
Implemented Astronomy.siderealTime() in Kotlin.
Updated all languages' unit tests for sidereal time
to verify exact conformity between them, rather than
to an externally derived value. I wanted to make
sure all languages, including Kotlin, are calculating
the exact same value.
I don't need an external authoritative test for
sidereal time, because it will be indirectly tested
through its involvement in thousands of other calculations
that depend on it. I just need a quick sanity check
before implementing those other things that depend on it.
Ported the following types to the Kotlin code:
GlobalSolarEclipseInfo
EclipseEvent
LocalSolarEclipseInfo
TransitInfo
ShadowInfo
IllumInfo
AxisInfo
NodeEventKind
NodeEventInfo
Made some wording fixes in the documentation for the
other languages.
Converting between radians and degrees.
Clamping angles to a desired range of degrees.
Converting between vector, spherical, horizontal.
Refraction and inverse refraction.
Implemented most of the RotationMatrix functions.
Added unit tests for combining rotation matrices, using a
rotation matrix to rotate a vector, and pivoting a rotation
matrix around its axes.
Replaced AstroVector operator '*' with infix function 'dot',
because it removes ambiguity between vector dot products
and vector cross products.
Later I will add a 'cross' infix function too.
Corrected minor typo in documentation for Python, C, C#, JavaScript.
"trasnform" -> "transform"
Allow floating point values for seconds when initializing
an AstroTime from (year, month, ..., seconds).
AstroTime can now represent date/time to millisecond resolution.
Represent AstroTime strings in ISO 8601 format:
yyyy-mm-ddThh:mm:ss.sssZ
Minor docstring fixes.
Rename target file to 'astronomy.kt'.
There was already an internal function for calculating
Greenwich Apparent Sidereal Time (GAST). By request,
I have exposed this function for outside users.
Added a minimal unit test to verify the function is
callable and returns the correct result for one case.
This function is already exhaustively tested by unit
tests that verify other functions that already called
this function when it was internal, so minimal testing
is sufficient in this case.
Added the following new functions to all 4 languages:
MassProduct: find the GM product for all Solar System bodies.
LagrangePoint: calculate L1..L5 state vectors for a pair of bodies.
LagrangePointFast: calculate L1..L5 state vectors given
state vectors and GM products of a pair of bodies.
There is no function double.IsFinite() in .NET Framework.
Reworked the sanity check in Astronomy.Pivot so the C# code
builds in these older .NET platforms.
Changed the documentation for the GeoMoon and GeoMoonState
functions to make it explicit that they calculate coordinates
oriented with respect to the Earth's J2000 equator (EQJ).
This is because I will soon add ecliptic (ECL) counterparts
for the GeoMoon function, to more directly search for ascending
and descending nodes of the Moon.
See this discussion:
https://github.com/cosinekitty/astronomy/issues/150
For the case of calculating a map, where each pixel
on the map represents a different location on the Earth,
it is more efficient to factor out expensive calculation
of sidereal times, assuming the entire map represents
some phenomenon at a single moment in time.
For example, to determine whether the Moon is visible
at different places on the Earth, the following
functions can be calculated across thousands of
different (lat, lon) geographic coordinates around
the world:
ObserverVector
Rotation_EQD_HOR
Before iterating over the map pixels, a program
can call GeoMoon, then convert EQJ coordinates to EQD.
Then by passing the same time value in a loop to
ObserverVector and Rotation_EQD_HOR, the program
can calculate a vector from the observer to the Moon
in EQD coordinates, then convert EQD to HOR.
The z-coordinate of the horizontal coordinates
determines whether the Moon is above or below the
observer's horizon at that point on the Earth.
This calculation pattern performed redundant
sidereal time calculations for each pixel on the map.
I changed the code for all 4 languages to cache
sidereal time so that it only needs to be calculated
once.
In the C version of Astronomy Engine, this resulted
in a speedup factor of about 2.3 in the above use case.
(See the function MapPerformanceTest in generate/ctest.c.)
The code generator was creating slightly different numeric
values for the Pluto state tables and the Jupiter rotation matrix.
I decreased the output precision by one decimal digit.
This should allow the code generator to produce identical
source code on both Linux and macOS.
The documentation for the Python function `SearchAltitude`
was missing a mention of the `altitude` parameter.
I searched for similar mistakes in Python, C#, and C,
having just completed the same exercise in the JavaScript code.
I also found several places where extraneous newlines
between the parameter documentation caused the Markdown
to be rendered incorrectly.
Now that Microsoft has officially released .NET 6,
I have upgraded the C# version of Astronomy Engine to use it.
No source code changes were needed. I just bumped the
version number in the project files, and targeted .NET 6
in the GitHub Actions continuous integration tests.
Fixed some obsolete wording in generate/README.md.
I'm concerned that a first-time visitor to the Astronomy Engine
repo on GitHub will get lost. I made it more obvious where to
quickly find the source code needed for a given language.
Fixed a few lingering issues in the documentation of
the C# version of the ObserverState function.
This completes the implementation across all 4 languages.
ObserverState calculates the position vector of a point
on the surface of the Earth, and the velocity vector
of that point, both relative to the center of the Earth.
Implemented the C# version of the ObserverState function.
This returns the geocentric position and velocity for
a point on the Earth's surface at a given time.
Now the Python version of Astronomy Engine supports calculating
the Earth/Moon Barycenter (EMB) state vector (position and velocity)
relative to the Earth's center (geocentric) or relative
to the Solar System Barycenter (SSB).
This completes support for this feature across C, C#, JavaScript, and Python.
The BaryState function did not support Pluto before.
Refactored the code so that the internal CalcPluto function
returns both the position and velocity, and its caller
can select from heliocentric or barycentric coordinates.
HelioVector asks for heliocentric coordinates and keeps
only the position vector. BaryState asks for barycentric
coordinates and returns both position and velocity.
I added test data for Pluto generated by JPL Horizons.
It turns out the Pluto system barycenter is the best fit
for TOP2013, presumably because Charon causes Pluto to
wobble quite a bit.
I also generated JPL Horizons test data for the Moon
and the Earth/Moon barycenter, anticipating that I will
support calculating their barycentric state vectors soon.
I had to increase the enforced size limit for minified
JavaScript from 100000 bytes to 120000 bytes.
I guess this is like raising the "debt ceiling".
Fixed a bug in Python unit tests: if "-v" verbose option
was specified, it was printing a summary line for every
single line of input, instead of a single summary after
processing the whole file, as was intended. This is one
of those Python whitespace indentation bugs!
I'm getting much better accuracy sticking with my original
gravity simulator, just with smaller time increments, than
I was with the Runge-Kutta 4 method. The PlutoStateTable
gets a bit larger (51 state vectors instead of 41), but the
accuracy is so much higher.
Removed the Runge-Kutta code because I won't be going back to it.