Corrected a mistake in the explanation of the
C function Astronomy_GravSimInit: the `bodyStates`
parameter is NOT barycentric -- it is relative to the
originBody parameter.
Python had improperly formatted documentation for
Time.FromTerrestrialTime parameter `tt`.
The Python markdown generator `pydown` did not
correctly handle links to compound symbols like
`#GravitySimulator.Update`. It also was trying
to link to `StateVector[]` instead of `StateVector`.
Removed unnecessary and unhelpful documentation
for C# internal class constructors. They do not appear
in the generated markdown documentation anyway.
Other minor wording revisions in the documentation.
The Python version of the GravitySimulator class
is now passing all unit tests. This completes
the initial coding. I still need to review
documentation across all the language implementations.
Finished coding the Python version of the gravity simulator.
No unit tests have been written yet.
Cleaned up documentation in the other languages.
Made some functions static that did not need to be members.
Started implementation of the Python gravity simulator.
Updated the `pydown` markdown generator to include
class constructors `__init__` when they contain docstrings.
It makes more sense to report Jupiter's moons with
individually named structure fields rather than an array.
It reduces the overall code and documentation size,
and outside of unit testing, there are few cases
where iterating over an array of moons is more
lucid than using the names of the moons.
This is a breaking change, but hopefully very few
developers are using this function yet.
Fixing the breakage is very simple.
Also added operator overloads for adding and
subtracting StateVector, just like we already had
for Vector.
In anticipation of releasing the Kotlin version,
I set the version numbers for the following to 2.1.0:
Kotlin
Node.js / npm
Python / pip
The intention is to keep matching releases of
Astronomy Engine at a common version number.
The existing lunar libration functions in the
other languages (C, C#, Python, JavaScript) were
calculating the Moon's ecliptic latitude and longitude
in radians, not degrees as intended. They have been fixed.
Implemented the libration function for Kotlin.
For years before 1582 or years after 3668, the Seasons functions
were unable to find many equinoxes and/or solstices.
The problem was that over time, the Earth's axis precesses
enough that the calendar dates of these events drifts outside
the fixed search ranges I had provided for them.
I expanded the search ranges so all season changes can be found
for a much wider range of years, as verified by unit tests:
C/C++: -2000..9999
C#: 1..9999
JavaScript: -2000..9999
Python: 1..9999
Kotlin: 1..9999
Note: C#, Python, and Kotlin currently do not allow
years values below +1. In fact, I discovered we were not
noticing when an invalid year was passed into the Kotlin code.
I updated that code to throw an exception when the year does
not match what was expected. It is disturbing that the
GregorianCalendar class silently ignores invalid years!
Constricted the search tolerance from 1 second to 0.01
seconds for the seasons search, to ensure more consistent
behavior.
Fixed a bug in the Kotlin search() function's
quadratic interpolation that was causing the convergence
to be slower than it should have been.
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`.
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.
Defined consistent __repr__ methods for
Astronomy Engine Python classes.
Each string representation is reversible:
eval(repr(x)) -> x
The main goal is to facilitate interactive
debugging and experimentation for developers
working directly in the Python interpreter.
Fixed documentation mistakes in the following classes:
IlluminationInfo
LunarEclipseInfo
Generate astronomy.py directly in the package directory.
I realized it doesn't make sense to generate it in the
parent directory and then copy it; just generate it where
it will end up anyway.
Updated documentation so people know they can just do
pip install astronomy-engine
to install Astronomy Engine in their Python project.
Removed the GitHub Actions status badge because it is redundant with
the checkmark/X indicator.
Now that private symbols are no longer exported, I had to
fix a couple of places where the unit tests still accessed them.
I realized that the way I structured the pip package
in version 2.0.15 made the private symbols (those whose
names begin with an underscore) to be visible by the importer.
I reworked the package structure so this no longer happens.
This is now fixed in:
https://pypi.org/project/astronomy-engine/2.0.16/
The pip package was broken!
I violated ancient software development wisdom:
"If you haven't tested it, it doesn't work."
It is now working in:
https://pypi.org/project/astronomy-engine/2.0.15/
Version 2.0.15 of Astronomy Engine for Python (pip)
and Node.js (npm) add support for the new SiderealTime
function. This was previously an internal function,
but now it is exposed for outside callers.
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.
The way I was publishing the Python package was annoying.
I found a better way to authenticate myself to pypi.org,
but I needed to test it. This required bumping the version
numbers of the packages. There is no difference between
2.0.12 and 2.0.13.
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.
In languages that support it, using hypot(x,y) is a little
easier to read than sqrt(x*x + y*y). Some documentation
(e.g. the man page for the C function) leads me to believe
hypot might also be better behaved than sqrt in some cases.
The JavaScript Math.hypot() is especially nice because it works
for any number of dimensions, so I can use it in 2D and 3D cases.
C only allows 2D usage, as does Python 3.7. Python 3.8 added
support for any number of dimensions, but I don't want to break
compatibility with Python 3.7 just yet. Therefore, in C and Python,
I am only using hypot for 2D cases.
C# does not appear to have any kind of hypot function,
so no changes were made to the C# code.
Thanks to https://github.com/ebraminio for this suggestion.
Python and npm package version: 2.0.11.
Finished implementing new functions across all
supported languages:
EclipticGeoMoon
Calculate the Moon's ecliptic geocentric position
in angular coordinates. The ecliptic longitude is
measured with respect to the mean equinox of date.
SearchMoonNode
NextMoonNode
A pair of functions to search for consecutive occurrences
of the Moon's center passing through the ecliptic plane.
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.)
Thanks to ebrominio, I was able to create a pip
package for Astronomy Engine. See:
https://pypi.org/project/astronomy-engine/1.0.1/
I will probably need to go back and keep the version
numbers in sync with the matching npm package.
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.
More work getting MacOS build process to work.
Avoid excessive number of floating point digits of
output in the demo tests, so that insignificant
floating point variations don't cause unit test failures.
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.
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.
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.
The Pluto gravity simulator constants now come from
a single source: pluto_gravsim.h. This will allow me
to experiment with the Pluto state table to get a better
compromise between size and accuracy.
Tonight as I was walking outside, I saw a fairly bright
star about half a degree away from the edge of the Moon.
I wondered what it was, so I decided to write a quick
program to find out.
This Python demo program scans the HYG Database
(https://github.com/astronexus/HYG-Database)
to find which bright stars are within a small angular
distance of the Moon, as seen at a given time, latitude, and longitude.
It turns out the star I saw was Nunki (Sigma Sagittarii).
It was handy to do vector subtraction to implement this program,
and it was trivial to do in the Python code's Vector class,
so I went ahead and added that.
There is now a Libration function in all 4 supported languages.
The returned structure contains libration angles in
ecliptic latitude and ecliptic longitude, along with
the Moon's ecliptic position and distance.
Also included is the Moon's apparent angular diameter.
All 4 languages have added a `diam_deg` field to the
structure returned by the Libration function.
It is the apparent angular diameter of the Moon as
seen from the center of the Earth, expressed in degrees.
In JavaScript and Python, throw an exception if provided
an invalid refraction option. Especially in JavaScript,
it was too easy to pass in a value like 'true', which did
not calculate refraction as expected.