It turns out that GetSystemTimeAsFileTime only returns
time with millisecond resolution.
In order to get microsecond resolution in Astronomy_CurrentTime(),
I had to switch to GetSystemTimePreciseAsFileTime for Windows.
Example output from unit test:
C Test_AstroTime: PASS - realtime increment = 3.143e-07 seconds after 1 iterations.
This is a follow-up to work provided by:
Eric Wheeler, KJ7LNW <astronomy-git@z.ewheeler.org>
Before now, the C function Astronomy_GetCurrentTime returned
the current time from the system clock, but only with whole
second resolution. Now it supports microsecond resolution on
Linux/Unix, Mac OS, and Windows.
For unsupported platforms, a compiler error will occur
to indicate that microsecond resolution is not available.
However, it is possible to define one of the following two
preprocessor symbols to work around the compiler error:
1. ASTRONOMY_ENGINE_NO_CURRENT_TIME
Excludes the function Astronomy_CurrentTime from the build.
If your project does not need to obtain the current time,
or your hardware platform does not provide current date
and time in the first place, this is likely the better option.
2. ASTRONOMY_ENGINE_WHOLE_SECOND
If your project does need to use the current date and time
for astronomy calculations, and it can tolerate whole
second resolution, this option provides a version of
Astronomy_CurrentTime that uses a call to `time(NULL)`.
Notes:
- Added unit test to confirm at least millisecond resolution.
Because these tests have to run on GitHub Actions cloud platform,
and those systems can be heavily CPU-loaded, I want to be tolerant
of resolution and avoid false failures.
- Added detection of Mac platform.
- Added preprocessor options documented above.
- On Windows, use ULARGE_INTEGER and eliminated one integer division.
- Added comments and developer documentation.
- Converted tabs to spaces in astronomy.c, for consistent code format.
This patch adds support for microsecond time resolution on the UNIX and
WIN32 platforms.
Implementation details:
Previously, the `Astronomy_CurrentTime()` function used `time(NULL)` to
get the current time, thus providing 1-second resolution. This patch
uses `gettimeofday()` for UNIX and `GetSystemTimeAsFileTime()` for WIN32
platforms. If neither are supported, it will fall back to `time()` and
issue a #warning. (Note that windows.h defines ARRAYSIZE, which may or
may not be compatible. Thus, the internal define `ARRAYSIZE` is renamed
to `ASTRO_ARRAYSIZE`.)
The UNIX code was tested in Linux, and in arm-eabi-none under newlib.
The WIN32 code was tested using MinGW/64 under WINE.
Use case:
One-second resolution is enough in most cases. However, there are cases
where higher resolutions are desirable. For example:
We are using the astronomy.c library to control a mechanical rotor to
track celestial objects (planets, stars, and satellites). The rotor
controller uses a PID controller with 100 tick/sec updates, and tracks
the velocity of the azimuth and elevation angles from the previous tick.
With 1-second resolution, the PID controller jerks and oscillates once
per second as it adjusts to the new position. With at least
10-millisecond resolution (100/sec), it can calculate the per-tick
velocity change and track smoothly with far less jitter.
More information about the project:
Source using astronomy.c:
https://github.com/KJ7NLL/space-ham
Lego-controlled az/el:
https://youtu.be/vrlw4QPKMRY
Lego-controlled telescope focus:
https://youtu.be/p-5dOQG95xg
APID+SMC control algorithm:
https://doi.org/10.1016/j.precisioneng.2022.01.006
Signed-off-by: Eric Wheeler, KJ7LNW <astronomy-git@z.ewheeler.org>
Tested-by: Zeke Wheeler, KJ7NLL <kj7nll@gmail.com>
It was possible for certain starting times to have a search
failure in this demo, because it could place more than
one zero-crossing of the function in the same time interval.
So now we iterate over an interval of 10 days at a time
until we find the solution.
This demo shows how to search for the next time
the Moon reaches extreme ecliptic latitude or
extreme declination. In other words, it finds
when the Moon reaches the farthest north or south,
expressed in either ecliptic coordinates or equatorial
coordinates.
Both angles are measured using the Earth's equator of date.
On the Raspberry Pi 4, using latest versions of cppcheck
and pylint, a few more minor fixes were needed for eliminating
warnings.
Also had to soften a tolerance for the Kotlin unit tests.
Slightly different cppcheck dev 2.11 behaviors have added
another warning that I don't care about. I don't want to
have to convert callback pointers to const, then cast them
to const.
However, it did find a couple of useful cases I fixed in
astronomy.c where GravSim parameters could be made const.
The following build error occurred in Mac OS:
Compiling altazsearch.cpp
clang: error: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated [-Werror,-Wdeprecated]
Added options to explicitly perform mixed C/C++ programming.
See the following discussion for context:
https://github.com/cosinekitty/astronomy/discussions/308
Added a demo program that shows how to search for when
a body enters a window defined in terms of an observer's
horizontal frame of reference, given a range of altitudes
and a range of azimuths.
The newer version of cppcheck reported that I was assigning
a value to a variable that was never used before another
assignment occurred. Fixed this to eliminate the warning.
The star database changed again, which causes my hash check
to fail. This time I locked on to the specific commit of the
file, so my build process won't break if it is changed again.
The camera demos all have a bug where I was calculating
the angle of the sunlit side of the Moon incorrectly.
The arguments to atan2 were backwards.
Added test data for Florida, New Zealand, and Canada
that are backed up by photographic evidence and
my first-hand observation.
The star database file hygdata_v3.csv has been updated.
Updated the expected checksum for it.
Reworked the downloader to check for checksum disagreement.
If checksum doesn't match, delete the file and download,
then try the checksum again.
This change will automatically fix obsolete files that have already
been downloaded on contributor's development systems.
Now that we use Python type hints, I discovered that
Python 3.11.2 includes the placeholder type `Any`
in the metadata reflected by `importlib`.
In order to generate consistent documentation with
older versions of Python, I exclude `Any` if it is present.
This change affects only the documentation and has
no effect on the Astronomy Engine package itself.
Because I plan on adding metersAboveGround as a parameter
that defaults to 0.0 in the other languages, and I want
the language implementations to be reasonably consistent,
I moved the metersAboveGround parameter to the end of
the parameter list for the C version of SearchRiseSetEx.
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.
Enhanced the Time class to correctly calculate calendar
dates for the year range -999999 to +999999.
Made unit tests in C, C#, and Kotlin all exercise
the full year range, for February 28 and March 1 in each year,
to make sure we cover before and after each potential leap day.
The C functions for calculating calendar dates used the
type `long` to perform calculations that require 64-bit
integers. However, in some C compilers, `long` is still
32 bits. This caused a failure in Windows for extreme
year values. So I now use the type `int64_t` to explicitly
require a 64-bit integer.
In many of my Windows batch files, I used the following
construct to detect failures:
do_something
if errorlevel 1 (
echo.An error occurred in do_something
exit /b 1
)
I discovered that it is possible for a Windows program
to exit with a negative integer error code.
This causes the above construct to miss the failure
and the batch file blithely continues.
So I have replaced that construct with
do_something || (
echo.An error occurred in do_something
exit /b 1
)
This way, if the command exits with any nonzero error,
we correctly detect it as a failure.
Applying the same recent fixes to C and C# to the Python code.
I'm also changing my philosophy of representing times.
From now on, they will be truncated to the floor millisecond,
not rounded to the nearest millisecond. This means we don't reach
another calendar date until we have had 60 full seconds after
the last minute. Otherwise there is too much nasty logic for
rounding up calendar dates. I will follow suit across all languages.
Fixed problems converting AstroTime to calendar dates and back.
Also expose struct CalendarDateTime to outside callers,
for convenience dealing with Gregorian calendar dates.
With more rigorous testing, I discovered more bugs
in the C functions for converting calendar dates
to times and vice versa.
Astronomy_UtcFromTime():
When the year went before -4714, the value of the variable
`djd` went negative, causing the typecast `(long)djd` to
round toward zero instead of taking the true floor.
Changed this to `(long)floor(djd)`.
Astronomy_MakeTime():
Reworked the logic so that none of the integer divisions
involve negative values over the year range -999999..+999999.