6. Time Management

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();

6.1. System Clock

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.

6.2. Chrono

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.

6.3. Session Clock

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.

6.3.1. Detailed Technical Description

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.

_images/Fig_Session_Eq_SessionClockTime.png
_images/Fig_Session_CalculateSessionClock_LocalStation.png

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.

6.4. Pacer

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