README.timekeeping:  Keeping time in KStars
copyright 2002 by Jason Harris and the KStars team.
This document is licensed under the terms of the GNU Free Documentation License
-------------------------------------------------------------------------------


1. The Basics

Timekeeping is handled by the SimClock class.  SimClock stores the 
simulation time as the Julian Day, in a long double variable 
("julian").  A long double is required to provide sub-second resolution 
in the Julian Day value.  The date can be converted to a calendar date 
(QDateTime object) with the UTC() function.  julian is updated every 
0.1 sec by an internal QTimer, using the SimClock::tick() SLOT, 
connected to the internal QTimer's timeout() SIGNAL.

We make a distinction between "system time" and "simulation time".  
System time is real time, according to the computer's CPU clock.  
Simulation time is the time according to KStars; since the time and 
date are adjustable, system time and simulation time can have an 
arbitrary offset.  Furthermore, SimClock has an adjustable Scale 
parameter that determines how many seconds of simulation time pass for 
each second of system time.  Scale can even be negative, indicating 
that the simulation clock is running backwards.

The simplest way to advance the simulation time would be to add 
(0.1*Scale) seconds to julian every time tick() is called.  However, 
this is not accurate, because there is always some error associated 
with the time it takes to execute tick(), and these errors would 
accumulate during each cycle.  Instead, tick() measures the elapsed 
time since some fixed system-time marker ("sysmark"), and adds 
(elapsed_time*Scale) seconds to "julianmark", a fixed simulation-time 
marker that was the exact simulation time at the moment the system-time 
marker was set.  This is much more accurate, because any errors in 
tick() do not accumulate.  Any time the clock is started, or its 
scale changed, the sysmark and julianmark markers are reset (they are 
also reset if they have not changed in more than 24 hours of real time).

tick() emits the timeAdvanced() signal, which is connected to 
KStarsData::updateTime(), which takes care of updating object 
coordinates and drawing the skymap (see below for details).

Note also that the SimClock class only handles the Julian Day and the 
Universal Time, not the local time.  Time zone corrections and daylight
savings time are handled by KStarsData::updateTime().


2. Manual Mode

The above procedure works well, as long as tick() takes less than 0.1 sec, 
on average (including the time taken by KStarsData::updateTime()).  In 
practice, large values of Scale cause more calls to updateTime() than the 
CPU is able to handle.  This results in some time steps being skipped 
altogether, which makes the simulation seem jerky.

To compensate for this, we implemented a "Manual Mode" for SimClock.  In 
Manual mode, the internal QTimer is stopped, so that tick() is not 
triggered every 0.1 seconds.  Instead, a similar function (manualTick()) 
is called whenever KStarsData::updateTime() has finished.  manualTick()
adds Scale seconds to the simulation time.  So, the Scale parameter has
a slightly different meaning in Manual mode.  The simulation time
no longer runs at strictly Scale seconds per real-time second; rather, 
every update of the simulation occurs exactly Scale simulation-seconds 
after the previous update, no matter how long the update takes.

There are two bool variables in SimClock, ManualMode and ManualActive. 
The first controls whether the clock is using Manual Mode (accessed by 
isManualMode()); the second controls whether the clock is running in 
Manual Mode (recall that the internal timer is halted when in Manual 
Mode).  The function isActive() returns whether the clock is running, 
for both the standard mode and Manual Mode.


3. KStarsData::updateTime()

updateTime() is a SLOT connected to the SimClock's timeAdvanced() 
SIGNAL, which is emitted every tick() or manualTick().

KStarsData keeps its own representation of the universal time as a 
QDateTime object (UTime); the first thing that updateTime() does is to
reset this with clock->UTC().  It then sets the local time QDateTime 
object (LTime) by adding 3600*geo->TZ() seconds to UTime.  It then 
checks if it has reached the next daylight savings time change point, 
and adjusts the Time Zone offset, if necessary.

There is a group of time-dependent numbers such as the obliquity and 
the sun's mean anomaly; these are kept in the KSNumbers class.  The next 
thing updateTime() does is create a KSNumbers object appropriate for the 
current julian day value [we may be able to save some time by keeping a 
persistent KSNumbers object, and not updating it on every call to 
updateTime(), as the values stored there don't change very quickly].

There are several things that don't need to be updated on every call to
updateTime().  To save time, we only update them if a certain amount of 
time has passed since the last update.  For example, the LastNumUpdate 
variable stores the julian day of the last time object coordinates were 
updated for precession/nutation/aberration.  This needs to happen once 
per simulation day, so whenever (CurrentDate-LastNumUpdate) exceeds 1.0,
it signals the update (by setting needNewCoords=true) and resets 
LastNumUpdate to CurrentDate.  Similarly, we use LastPlanetUpdate to 
update planet coordinates 100 times per day.  LastSkyUpdate monitors
the last time the horizontal coordinates were updated (the update 
interval is dependent on the current zoom setting).  

Next, we update the focus position.  If no object is being tracked, and
useAltAz=true, then the focus RA needs to advance at the sidereal rate 
(one second on the sky per sidereal second of time).  If the simulation 
is tracking an object, then the focus is set to the object's coordinates.
(See README.skymap for details on the focus position and animated 
slewing)

Finally, the last thing updateTime() does is to re-draw the sky by calling
SkyMap::update(); see README.skymap for details.
