The reason SSB vector errors were larger than other bodies
is because the Sun/Barycenter relationship does not have
position and velocity vectors of a "typical" size.
The distance of a planet from the SSB is fairly constant,
and the speed a planet travels is fairly constant.
Therefore, comparing errors by dividing by the magnitude
of the correct vector usually makes sense for scaling.
But for the barycentric Sun (or the heliocentric SSB),
the magnitude of the vectors can become arbitrarily small,
nearly zero in fact, resulting in surprisingly large ratios.
I compensated for this in all the tests by adding a new rule.
When the error thresholds r_thresh and v_thresh are negative,
it is a flag that indicates they are absolute, not relative.
In other words, r_thresh < 0 indicates that abs(r_thresh) is
the maximum number of astronomical units allowed in position
errors, and v_thresh < 0 specifies maximum AU/day.
This results in more consistent numbers that give confidence
the errors are indeed very small and not worth worrying about.
In the C unit test for barystate, heliostate, and topostate,
I had switched from checking absolute differences to relative
differences. I forgot to do that in the other 3 languages
until now. They are all working consistently in how they
measure calculation errors.
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.
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.
I was able to get the diffcalc test to confirm that
the C and C# algorithms are producing absolutely identical
output values. I just needed to make the C code print its
output in scientific notation with 18 significant figures.
All along, ctest(diff) has been treating the "v" lines as
v x y z
instead of
v tt x y z
and ignoring the z values. Fixed that. It was a relief
it didn't reveal any lurking problems in the z values.
Also, dramatically decreased the disagreement measured between the
C and C# code just by printing out more decimal places in both.
First file: temp/c_check.txt
Second file: dotnet/csharp_test/csharp_check.txt
Tolerance = 5.600000e-17
lnum a_value b_value factor diff name
OK 9269 4.3776373521942023e-05 4.3776373521942017e-05 1.00000 6.776e-21 helio_x
OK 5589 -4.3623354680948618e-05 -4.3623354680948625e-05 1.00000 6.776e-21 helio_y
OK 606 -4.3091817463880202e-05 -4.3091817463880195e-05 1.00000 6.776e-21 helio_z
OK 199595 -2.5752073258047489e-05 -2.5752073258047493e-05 1.00000 3.388e-21 sky_j2000_dec
----------------------------------------------------------------------------------------------------
First file: temp/c_check.txt
Second file: temp/js_check.txt
Tolerance = 1.200000e-12
lnum a_value b_value factor diff name
OK 388830 1.0342029871624860e+01 1.0342029871624890e+01 1.00000 3.020e-14 helio_x
OK 67200 -2.9057622338509663e+00 -2.9057622338509423e+00 1.00000 2.398e-14 helio_y
OK 67200 -4.3874295048838763e-01 -4.3874295048837719e-01 1.00000 1.044e-14 helio_z
OK 282864 4.2562907944628900e+00 4.2562907944628821e+00 0.94021 7.516e-15 sky_j2000_ra
OK 333852 -9.3967798183388087e+00 -9.3967798183388513e+00 1.00000 4.263e-14 sky_j2000_dec
OK 151220 3.0052362065237119e+01 3.0052362065237109e+01 1.00000 1.066e-14 sky_j2000_dist
OK 123271 8.3934732121148897e+01 8.3934732121147533e+01 0.83370 1.137e-12 sky_hor_az
OK 123271 -3.3519630572770836e+01 -3.3519630572771831e+01 1.00000 9.948e-13 sky_hor_alt
----------------------------------------------------------------------------------------------------
First file: temp/c_check.txt
Second file: temp/py_check.txt
Tolerance = 5.100000e-14
lnum a_value b_value factor diff name
OK 22 -2.5294053992874876e-01 -2.5294053992874882e-01 1.00000 5.551e-17 helio_x
OK 29 -3.3385318243368106e-01 -3.3385318243368112e-01 1.00000 5.551e-17 helio_y
OK 22 3.7850398890096215e-01 3.7850398890096221e-01 1.00000 5.551e-17 helio_z
OK 135350 2.3433972116724325e-01 2.3433972116724319e-01 1.00000 5.551e-17 sky_j2000_ra
OK 603 4.5414554731189877e-01 4.5414554731189882e-01 1.00000 5.551e-17 sky_j2000_dec
OK 490 4.2751511640162104e-01 4.2751511640162099e-01 1.00000 5.551e-17 sky_j2000_dist
OK 49066 3.2035956679701377e+02 3.2035956679701371e+02 0.88694 5.042e-14 sky_hor_az
OK 49066 -2.7508172411136329e+01 -2.7508172411136300e+01 1.00000 2.842e-14 sky_hor_alt
----------------------------------------------------------------------------------------------------
First file: temp/js_check.txt
Second file: temp/py_check.txt
Tolerance = 1.200000e-12
lnum a_value b_value factor diff name
OK 388830 1.0342029871624890e+01 1.0342029871624860e+01 1.00000 3.020e-14 helio_x
OK 67200 -2.9057622338509423e+00 -2.9057622338509663e+00 1.00000 2.398e-14 helio_y
OK 67200 -4.3874295048837719e-01 -4.3874295048838757e-01 1.00000 1.038e-14 helio_z
OK 282864 4.2562907944628821e+00 4.2562907944628900e+00 0.94021 7.516e-15 sky_j2000_ra
OK 333852 -9.3967798183388513e+00 -9.3967798183388087e+00 1.00000 4.263e-14 sky_j2000_dec
OK 151220 3.0052362065237109e+01 3.0052362065237119e+01 1.00000 1.066e-14 sky_j2000_dist
OK 123271 8.3934732121147533e+01 8.3934732121148897e+01 0.83370 1.137e-12 sky_hor_az
OK 123271 -3.3519630572771831e+01 -3.3519630572770836e+01 1.00000 9.948e-13 sky_hor_alt
----------------------------------------------------------------------------------------------------
Before making these changes, I had the following discrepancies
between the calculations made by the different programming
language implementations of Astronomy Engine:
C vs C#: 5.55112e-17, worst line number = 6
C vs JS: 2.78533e-12, worst line number = 196936
C vs PY: 1.52767e-12, worst line number = 159834
Now the results are:
Diffing calculations: C vs C#
ctest(Diff): Maximum numeric difference = 5.55112e-17, worst line number = 5
Diffing calculations: C vs JS
ctest(Diff): Maximum numeric difference = 1.02318e-12, worst line number = 133677
Diffing calculations: C vs PY
ctest(Diff): Maximum numeric difference = 5.68434e-14, worst line number = 49066
Diffing calculations: JS vs PY
ctest(Diff): Maximum numeric difference = 1.02318e-12, worst line number = 133677
Here is how I did this:
1. Use new constants HOUR2RAD, RAD2HOUR that directly convert between radians and sidereal hours.
This reduces tiny roundoff errors in the conversions.
2. In VSOP longitude calculations, keep clamping the angular sum to
the range [-2pi, +2pi], to prevent it from accumulating thousands
of radians. This reduces the accumulated error in the final result
before it is fed into trig functions.
The remaining discrepancies are largely because of an "azimuth amplification" effect:
When converting equatorial coordinates to horizontal coordinates, an object near
the zenith (or nadir) has an azimuth that is highly sensitive to the input
equatorial coordinates. A tiny change in right ascension (RA) can cause a much
larger change in azimuth.
I tracked down the RA discrepancy, and it is due to a different behavior
of the atan2 function in C and JavaScript. There are cases where the least
significant decimal digit is off by 1, as if due to a difference of opinion
about rounding policy.
My best thought is to go back and have a more nuanced diffcalc that
applies less strict tests for azimuth values than the other calculated values.
It seems like every other computed quantity is less sensitive, because solar
system bodies tend to stay away from "poles" of other angular coordinate
systems: their ecliptic latitudes and equatorial declinations are usually
reasonably close to zero. Therefore, right ascensions and ecliptic longitudes
are usually insensitive to changes in the cartesian coordinates they
are calculated from.
This is technically a breaking change, but only for clients
that use the cartesian coordinates in an ecliptic coordinate
return type. Before now, the coordinates were just separate
floating-point members ex, ey, ez. Now they are a standard
vector type.
The purpose is to allow seamless interfacing with vector
rotation functions, and to be consistent with the equatorial
coordinate types.
Now that equatorial coordinates include both angles
and cartesian coordinates, there is no need for the
VectorFromEquator function. It has been removed
from all four supported languages.
The expression "VectorFromEquator(equ, time)" can be
replaced with "equ.vec" in any calling code.
Created new rotation matrix functions for the C# version.
IdentityMatrix creates a new instance of the 3x3 identity matrix
1 0 0
0 1 0
0 0 1
Pivot transforms a rotation matrix by pivoting it about
one of its coordinate axes by a specified angle.
Still need to port the C version of the "camera" demo.
When built from a system with a European (or similar) culture setting
where a comma is used as a decimal marker instead of a period,
the C# unit tests and demos would fail.
Now explicitly specify InvariantCulture to resolve these problems.
In all four versions of Astronomy Engine (C, C#, JavaScript, and Python),
starting a search for a full moon near December 19, 2020 would fail.
I added a unit test to all four languages and it failed consistently
across them all.
The root cause: I was too optimistic about how narrow I could make
the window around the approximate moon phase time in the
SearchMoonPhase functions. Finding the exact moon phase time failed
because it was outside this excessively small window around the approximate
time. I increased the window from 1.8 days to 3.0 days.
This should handle all cases with minimal impact on performance.
Now all four of the new unit tests pass.
Ported Pluto integrator to C#.
Along the way, I noticed that I had VSOP87 latitude and longitude
swapped in such a way that they worked, but were labeled wrong.
This confused me quite a bit as I tried to implement functions
to calculate the derivatives of the VSOP87 spherical coordinates.
Fixed this in the code generator and the C and C# template files.
In the JavaScript version, check throughout for valid
finite numeric/boolean values as needed.
This should make debugging a lot easier for everybody.
In the unit tests for all languages, also check for infinite
results, not just NaN.
I discovered that JS Astronomy.NextLocalSolarEclipse() was broken:
It was trying to call a nonexistent function.
Fixed it, and added unit test that would have caught the breakage.
Fixed mistakes in JS documentation for the field names of the
Observer class.
Now that I have switched to TOP2013 for calculating Pluto's
position in the C# code, there is no need for the unit test
to handle errors for out-of-bound time coordinate.
Also corrected code generator to output term coefficients
in scientific notation. In the C code, it was dropping signficant
digits by outputting in fixed point notation.
All the other languages have a lookup table that allows
any specific test to be run by name, or all tests to be run
using "all" as the name. Now the C# unit test does the same.
unit_test_c was not testing local solar eclipses.
C ParseDate() was not scanning seconds. This caused
discrepancies between C and C# results.
It was also failing to verify the Z on the end.