NetZ provides four time measurements: the system clock, Chrono
, the session clock, and Pacer
. The system clock and Chrono
are used to measure the time of events on a single station, whereas the session clock is used to synchronize and compare events on different stations. The session clock is synchronized across all connected stations. Pacer
is used to pace events.
The SystemClock
and SessionClock
objects can be used to assign a value to a Time
object. For example, to set the value of the variable tNow
to the number of milliseconds elapsed since the commencement of the session, use the following code.
Code 6.1 Getting the Time Elapsed Since the Start of a Session
Time tNow=SessionClock::GetTime();
The system clock gets the time elapsed, in milliseconds, since the NetZ process started. The time provided by this clock is set to zero milliseconds when the process begins on an individual station. The time effectively never wraps, as it is calculated and stored using 64 bits. The time provided by SystemClock
is not synchronized across the connected stations that belong to the session. For synchronized times, see Section 6.3. Typically, the system clock is used to measure the time it takes to perform a set of operations on the local station, such as is performed in the Chrono
and Pacer
classes.
The accuracy of the value of any Time
object is subject to the capabilities of the time provider implemented on NetZ's platforms. NetZ provides a default time provider, but you can also register your own using SystemClock::RegisterTimeProvider
.
The Chrono
class measures time on the local station, and functions just like a stopwatch. It enables you to precisely measure the elapsed time, in milliseconds, between specific events on the local station. After an instance of the Chrono
class is created, it operates in the same manner as a stopwatch using the Chrono::Start
, Chrono::Stop
, Chrono::Pause, Chrono::Resume
, and Chrono::Reset
member functions. The elapsed time can also be checked at any time using the Chrono::Check
function.
The session clock is synchronized for the entire session. Use it to synchronize or compare events on different stations. Get the time from the session clock with SessionClock::GetTime
. When you create a session using Session::CreateSession
, the session clock is automatically set to zero on the session host. Only the session host can adjust the time using SessionClock::SetTime
.
The session clock on each station is synchronized with the session host upon joining the session and periodically synchronizes with the session host throughout the session. Note that a poorly synchronized session clock affects several NetZ processes, including dead reckoning.
The accuracy of the synchronization is determined by the latency and jitter of the network. Latency is the time lag before a message reaches its destination, while jitter is the variation in latency. The synchronization algorithm assumes that the latency between two stations is symmetrical. Latency can become asymmetrical due to network-related factors such as congested relays and due to delays in system operations such as loading files. The asymmetrical latency between two stations causes discrepancies in the session clock on the local station.
When any station joins, the Session::JoinSession
function automatically synchronizes the session clock between the session host and the joining station.
To calculate the session clock on a local station, the local station sends a message to the session host and the session host returns the value of the session clock on the session host, as illustrated in Figure 6.2. Because of latency, the local station receives the message from the session host, which contains the value for the session clock, some time later than the value for the session clock. This value cannot be directly used to update the value of the session clock on the local station. The value for the session clock received on the local station from the session host is adjusted according to the round trip time of the pulse by using the following equation. tS
and tL
represent the session and system clock times, respectively.
Figure 6.2 Calculation of the Session Clock on the Local Station
The synchronization algorithm assumes that the latency between two stations is symmetrical. That is, it takes the same amount of time for a message to be sent from a particular station to the session host as it does for the session host to send a message to the station. The non-symmetry of latency between two stations causes an error in the session clock on the local station. Non-symmetrical latency can be caused by one station performing additional tasks, such as loading files (especially upon a join), which delays synchronization pulses and leads to poorly synchronized session clocks.
After the session clock time on the session host has been estimated, it is compared to the time of the system clock on the local station. The difference between these two values is called the offset. This offset is then added to the time given by the system clock to determine the new synchronized time for the session clock. In this example only one pulse was used to synchronize the session clock on the local station. When more pulses are used in the calculation, the averages of the session clock time on the session host and the moving median of the last 15 time samples on the local station are employed to obtain a more accurate synchronization.
As mentioned previously, a typical cause of poorly synchronized session clocks is non-symmetric latency caused by one station performing additional tasks, such as loading files. If this occurs when a station joins a session, initial session clock synchronization is likely to be poor.
The Pacer
class provides an easy method to ensure that a particular event occurs no faster than a specified frequency. If an event occurs within the specified frequency, the pacer sleeps for some time before the next event is allowed to proceed. For example, if the frequency is set to 2 Hz, the Pacer::Pace
function sleeps between the iterations of the paced event to ensure that consecutive events occur with a minimum time difference of 0.5 seconds. If the delay between two consecutive calls to Pace
is 0.2 seconds, Pace
sleeps for 0.3 seconds so that the next event occurs 0.5 seconds after the previous event. The time between two consecutive calls to Pace
is called the work interval. The work interval in this case is 0.2 seconds.
The maximum frequency, in Hertz, at which the pacer operates can be defined by the Pacer::SetFrequency
function.
Code 6.2 Pacer::SetFrequency
Syntax
Pacer::SetFrequency(qDouble dFrequency)
The maximum frequency can also be set when the class is instantiated.
Code 6.3 Specifying Frequency When the Pacer Class Is Instantiated
Pacer::Pacer(qDouble dFrequency=0)
When the frequency is set to zero, the call to the Pacer::Pace
function is not delayed, that is, the event is not paced.
A Pace
member function can be interrupted by a call to the Pacer::Interrupt
function . This call is used to exit from the Pacer::Pace
function while the pacer is sleeping. It is called from a separate thread to cause the thread that called the Pacer::Pace
function to exit from the member function.
A call to the Pacer::GetStats
function returns the statistics associated with the Pacer
class. The statistics are calculated since the creation of the pacer or since the previous call to the Pacer::ResetStats
function, and include the total, minimum, and maximum processing times, the number of times the Pacer::Pace
function was called, and the total elapsed time.
CONFIDENTIAL