Real-time systems are defined as systems where the correctness of computations not only depends upon logical correctness, but also upon the time or timeliness at which results are produced. Embedded systems are broadly defined as systems consisting of specialized hardware with internal computerized intelligence in firmware or software. A common and elemental embedded real-time application is that of an electronic clock or chronograph. An embedded electronic clock is de facto real-time since it must update in real time. Depending on how often it updates, this may not pose much of an immediacy or speed challenge, but it still must be real time to within its native precision or granularity of time to be useful.
I recently had to wrestle with the challenge of the automated and accurate recording of the time duration of events. The specific application had to do with the development of an inexpensive yet accurate system for timing athletic competitions or races. This included such activities as track running, long distance running, swimming, and skiing. Some activities involve timing a single participant; others, such as long distance road running (marathons), may involve timing tens of thousands of participants over the span of a few hours. While I don't have first-hand experience in the timing of motor sports racing, I expect the requirements there are similar. The ideas of precision timing are not limited to the fun and perhaps frivolous (to some) application of timing sporting events, but apply to process control and monitoring, data aquisition instrumentation, and profiling of software. Athletes that train hard for years and vie for personal glory or even world records and the resulting million dollar bonuses, demand accurate times and results. When a world record time is established, the timing system better have some redundant backup and test out accurately by the sport's governing body for certification of the world record.
In this article, I examine the use of C and C++ for the embedded real-time application of event timing. In doing so, I present a generic system that supports Standard C/C++ on Windows and Palm handheld platforms. You can use the code to determine the resolution or precision of a computer's system clock. It also provides a hardware-independent method for increasing the resolution and techniques for calibrating the clock and compensating for clock error.
Background
A chronograph is an instrument that records time. The word chronograph comes from the Greek kronos, which means "time," and graph, which means "record." A simple stopwatch is a chronograph. As recently as the 1960s, timing athletic events even at the Olympic level was done by hand using an analog stopwatch. A good hand timer could get time results to repeat within a tenth of a second. As soon as electronic timing came on the scene, the precision of repeatable results increased an order of magnitude to one hundredth of a second. An interesting side effect of electronic timing was that measured time durations for a given sports event were longer. Old records that were established when hand timing was used are footnoted to indicate they were hand timed and not directly comparable to electronic times.
Exploring the differences between hand timing and electronic timing is a convenient way to bring up the concept of latency. An event or action must occur to record the start time and the stop time. The delay between the event and the actual recording of a time is known as the latency of the recording system. If the delay is the same for recording the start time as it is for recording the stop or finish time, the two delays cancel and have no effect on the measured time duration between the start and finish. In the case of the start of a track event such as the 100-meter dash, the latency at the start is the time delay between the firing of the starter's pistol and the start of the clock. The latency at the finish is the time delay between the runner crossing the finish line and the recording of the finish time. For electronic timing, the mechanism for starting and stopping the clock is basically the same. It is an electronic switch triggered either mechanically or photo-optically. This generates an event, typically triggering a hardware interrupt that the computerized clock responds to by recording the time. The latency is small and the same for both recording the start time and stop time and has no effect on the measured time duration. In contrast, for hand timing, the latency for starting the clock is the timer's reaction time to hearing the starter's pistol firing and starting the clock. The latency for stopping the clock is the timer's reaction time in response to seeing the runner cross the finish line. For hand timing, these two latencies or delays are significantly different. Why the difference? It turns out that people have less latency when responding to an event that they could see visually coming to an end. In effect, they could anticipate and plan for the event's end. They then time their response to correspond to the runner crossing the finish line. On the other hand, they could not anticipate the audible signal of the starter's pistol firing. The latency at the start subtracts from the time duration of the race and the latency at the finish adds to the time duration. Since the latency at the start is larger, the result is race times that are faster or under what electronic timing devices yield. This gets us back to why hand timing yields shorter times than electronic timing and the resulting lesson that if the latency of recording the start time and the finish time is the same, the latency of the timing device can be ignored for situations interested in measuring elapsed time durations.
Electronic timing systems consist of two basic parts:
- A sensor that detects the start or finish of a race.
- A computerized clock that responds to the signal generated by the sensor.
The start sensor is typically a photo-sensor that detects the muzzle flash or an electronic switch that is tripped with the start pistol trigger. The end of the event is likewise sensed electronically with a light beam that is interrupted or by a high frame rate video taping the finish line and feeding a digital clock signal onto the video tape, which someone reviews in slow or stop motion and then reads the clock off the screen for each person as they cross the finish line. The most sophisticated timing methods use a combination of the photo beam and video for multilane track events. For running events that have thousands or tens of thousands of participants, such as the New York marathon, the most popular timing method employs the use of Radio Frequency Identification (RFID) tags. These are small chips that are worn on the runners shoe and are inductively energized when they cross over a magnetic field mat. When energized, they broadcast their unique ID number to a receiver that posts an event to a computer clock. The computer clock is typically a PC running some variant of a Microsoft operating system. The computer communicates with the sensor through an input port, which can be serial, parallel, or even the keyboard port. Systems popular for road races that have less than 1000 runners combine hand timing with a computer clock. This method replaces the sensor with a timing person that records finish times by activating a pickle switch connected to an input port on the computer or by keying the keyboard.
The advantages of utilizing a PC as the clock for timing is that PCs are generally inexpensive, easy to work with, and easy to develop applications for. The disadvantages are that a PC's system clock has some limitations and inaccuracies that challenge the notion of using a PC as a precision chronograph. The code I present here addresses some of these problems. Is it possible to time to hundredths or even thousandths of the second that some sporting events are timed to? Yes it is possible, but it takes a bit more effort than what meets the eye.
Quantifying the Clock
The first step in assessing the usability of a particular system for a chronograph is quantifying the performance of the system's clock. This assessment involves determining the precision or resolution of the clock and the overall accuracy. The resolution is the smallest difference in time that the clock can measure. The accuracy is how well the clock keeps time over a long period. Resolution and accuracy are two different and independent parameters and are quantified with different techniques.
To read the system clock, the Standard C library provides the function clock(). It returns the number of clock ticks elapsed since the beginning of the process that clock() is called from. The units of clock ticks are defined by the constant CLOCKS_PER_SEC. The Standard C library header file for clock() is time.h. clock() is available on most hardware/software platforms. Where it is not available, there is usually a platform-specific function that provides similar functionality. In the case of the Palm OS, the most popular development tools are the Metrowerks CodeWarrior C compiler or the GNU-based C compiler and PRC-Tools. These tools for the Palm OS are compliant with the ISO C language standard, but do not support the Standard C Library in its entirety. The Palm OS SDK mimics many of the Standard C functions. The standard function clock() is not supported on the Palm OS, but the Palm OS SDK has a nearly equivalent function TimGetTicks(). All Windows variants provide both clock() and the Windows equivalent GetTickCount(), which returns times in milliseconds.
A way to quantify the performance of the system clock is to make repeated calls to clock() until the clock updates. The return value of the clock() function changes when the clock updates or ticks. The quantum difference between return values of clock() is the native resolution of the system clock. A single clock tick or the resolution of the clock may or may not be equal to 1 CLOCKS_PER_SEC. On a PC, the difference between return values is either 50 or 60. The two different values occur alternating with equal frequency. The average resolution is 55. Given the value of CLOCKS_PER_SEC of 1000, the resolution is 55 milliseconds. This value results from the PC's system clock, which updates 18.3 times a second and is the same across a wide range of versions of Windows on a PC. It may be different for various handheld or pocket PCs utilizing Windows CE or .NET Embedded. The Windows API function GetTickCount() yields a resolution that varies with the version of Windows. It can be as low as 5 for Windows 9x and as high as 15 for Windows NT/2000/XP [1]. On the Palm device, the function TimGetTicks() yields a resolution of 1. The units of TimGetTicks() are specified by the SysTicksPerSecond() function that is a wrapper for the constant sysTicksPerSecond, which is 100 and the Palm equivalent of CLOCKS_PER_SEC.
To determine the accuracy of the system clock, you need to run the clock for a long period of time and compare it to a clock with known accuracy. A good approach is to run the clock for at least a couple of days and compare it to an accurate clock such as a global positioning system (GPS) clock signal or some other official time standard. It helps to run this accuracy or calibration test for a long period of time so that the error or difference becomes a significant, measurable value. To do this test, you start the clock at a known time and stop the clock much later after a known amount of time has elapsed. The difference between the measured elapsed time and the known elapsed time is the error that can later be accommodated for using a correction or calibration process. Remember the concept of latency or response time to starting and stopping the clock and make sure that these two latencies are the same so that they do not skew the measured amount of time elapsed.
An interesting side note about PC clock accuracy is that it can vary significantly between systems and depending upon model types. Some PCs have system clocks that are accurate to within a second in a year. Others must have a clock crystal that was taken from the bottom of the barrel and are in error many seconds per day. My search for a hardware platform for a computer clock for timing sporting events began with specialized timing hardware that I found to be costly and nonstandard in peripheral interfacing. I rejected that approach and naturally was attracted to the benefits of portability and lightweight battery-operated features found in notebook computers. Finding a contemporary notebook computer that had a screen legible in bright sunlight proved difficult. Eventually, I reverted to using decade-old notebook computers with monochrome LCD screens that would not wash out in bright sun. There is a readily available source of these old workhorses through corporate liquidators or even on eBay. For a single-purpose computer clock "embedded-like" application, they work fine and can be had for less then $50.00. The downside of some of these old computers is that their system clocks are notoriously inaccurate and require calibration and compensation to be useful. If you really want to be accurate, it is worth additionally testing the system over the various temperature ranges that the system may encounter if used outdoors. There is also the possibility that the clock may require a different calibration depending on whether it is being run on batteries or on AC power.
Notebook computers may be portable and light, but they are still awkward to use outdoors, standing and without a table to support them. The alternative is the handheld computer such as the Pocket PC and Palm. Lower end models can be found for under $100.00. Their size advantage is attractive and their screens stay readable in bright sunlight even the ones with color displays. Most employ transreflective screen technology that works well in all lighting conditions. The accuracy of the clocks on the handhelds I have tested is within the resolution of the clock over the period of a couple days, resulting in no need for calibration correcting the clock. With wireless networking options and their extended battery life, these new handhelds become ideal for remote data acquisition and then transmitting to a server or base station that processes the data.
Obtaining Finer Resolution Using Call Counting
If your timing requirements demand a finer resolution than the native resolution of the system clock, there are some ways of wringing a bit of extra resolution out the clock() function or its platform equivalents. Possible techniques include reprogramming the system clock to update quicker [2] or programming the real-time clock in a PC to generate an interrupt at a desired frequency [3] and writing the requisite hardware device drivers depending upon the operating system. There is a simpler way one that utilizes the overhead of calling the clock() function as a way to measure to finer resolution than the native clock resolution [4]. This technique is portable among operating systems and hardware. To accomplish this, you first call clock() and get a tick count. To then determine how much time has elapsed since the clock was last updated, you call clock() in a loop, counting the times you call clock() until the tick count changes. You then compare this number of calls to clock() to the total number of calls to clock() that can occur between two tick-count updates and calculate the fraction of a tick that has transpired since the last tick. This technique requires you to know the total number of calls to clock() that can take place between tick counts. This value is easily obtained when you first quantify the system clock before you start performing timing chores.
Listings 1 and 2 present the cClock class that implements this technique for measuring time to a higher resolution than the native resolution. This C++ code is designed for a single-threaded/single-tasking operating system such as the Palm OS. Embedded systems are by definition usually a dedicated, single function piece of hardware that has a CPU or embedded computer in it to add some intelligence. In the case of electronic timing systems or computerized chronographs, the single purpose of the device is recording accurate times. Consequently, a timing application does not have to be as concerned about cooperating with the operating system and being well behaved as far as not hogging the CPU. The timing method here works well for embedded systems, but may exact some performance hits on multithreaded/multitasking systems. The code is set to compile for a standard implementation of C++. To target any Windows version, use #define _WINDOWS; to target the Palm OS, use #define _PALMOS.
All the data members of cClock are declared static. This is fine for single-threaded timing situations that have only one start and many possible finishes. The code assumes there is only one clock for a specific system and that it needs to be quantified only once. Consequently, there is only one set of quantification parameters such as the native clock resolution, the number of calls between clock updates, and the calibration values for the clock. Since there are only static data members, all the function members could also be static. The problem with this is that public static member functions can be called without an instance of the class being instantiated. To force the constructor for cClock to be called before the clock is started or a clock value obtained, I chose to make the member functions StartClock(), GetClock(), and GetCalClock() nonstatic. They must be called through an instance of cClock.
The private member function InitClock() determines the native resolution of the system clock and the number of calls to clock() in between clock updates. The static data members _mNatClockRes and _mNumClockCalls store these values. The constructor for cClock calls InitClock() during the construction of the first cClock object. There are two constructors for cClock. The first takes an optional parameter that specifies the desired units of time to be returned by GetClock(). This parameter also sets the resolution of times returned by GetClock() as one unit. The default value of units is the value CLOCKS_PER_SEC. The second constructor takes the calibration correction values as the first two parameters and the desired units of time as the optional third parameter. Once an object is constructed, you can start the clock using the member function StartClock(). You can then retrieve times since the start using the member function GetClock(). The member function GetCalClock() retrieves a time and corrects it using the calibration values provided during construction. The function CalcCalibration() determines the calibration parameters for a given actual time and measured time duration.
Limitations
The call-counting method makes the assumption that the number of times clock() can be called in between clock ticks is a constant. This is a good assumption on single-threaded, nonpreemptive operating systems. The Palm OS works well, straight DOS works well, 16-bit Windows works pretty well, but 32-bit Windows starting with Windows 95 does not work as well. Preempting the call-counting process causes the number of calls to clock() between clock updates to vary. You could call count over many clock updates and average, but it still will not be as accurate as a single-threaded system. The implementation of InitClock() does average over two clock updates. Fortunately, the Windows-supported function GetTickCount() has a better native resolution than clock(). This may be good enough for some timing purposes without using the call-counting method.
Call counting is not suited for recording multiple finish times that may differ by less than a clock tick (the native resolution). If you are trying to get sub-clock tick resolution and accuracy, you are limited to events that must be at least one clock tick apart. Every time GetClock() is called, the clock ticks at least one time. Take note that the function GetClock() does not use call counting if the specified units result in a resolution that is larger than the native resolution. In the worst case, the overhead is nearly a whole clock tick in duration; best case is a small fraction of a clock tick. Obviously, the average would be one half of a clock tick. If you are recording a succession of times as close as possible together, they will all be about 1 clock tick apart, since each event will wait for a clock tick. If time requests are coming in faster than one per clock tick, they will not be handled in an accurate manner.
Another limitation to be aware of is the possibility of the clock wrapping around to zero or a negative number when timing very long duration events. The return type of clock() is clock_t, a long. This is a signed 32-bit integer and goes negative after about 25 days if CLOCKS_PER_SEC is 1000. For other devices such as the Palm, the value of CLOCKS_PER_SEC is 100 and will wrap later. The tick value on the Palm is also the accumulated time the device has been on since the device was last reset. It is not the time since the current process started. To continue timing at the finest native resolution, the Palm device must remain on. The timing application must override the natural shut down or sleep process the Palm uses to save battery life. Also, depending on the designated resolution of the clock, and because all math in cClock is integer math of type long, an overflow could occur before expected. I left the code simple for illustration and it has no exception trapping in this regard.
C Versus C++
Some embedded platforms do not support C++. In the case of the Palm OS, C is the native development language of choice. There are some C++ tools for the Palm OS, but the size penalty they incur on the compiled code image may preclude their use. At the moment, Palm devices have some fairly restrictive memory constraints. Depending upon the compiler tools and the target platform, certain C++ features such as multiple inheritance, exceptions, and templates can add a lot to the code size and memory requirements of an application. The C++ code here can be considered C++ light or C++ as a better C, as it stays confined to a small subset of C++. The files clock.c and clock.h (available at http://www.cuj.com/code/) are an equivalent C version of the code. The disadvantage of the C version is that the clock must be quantified before any of the timing routines are called. You must call the InitClock() function explicitly before you call any other functions such as StartClock(), GetClock(), and so on. You could hide the InitClock() chores inside the other functions and have them call InitClock() the first time they are called. The only caveat with this is the InitClock() has a lot of overhead (three clock ticks) and it could interfere with the timing process. To minimize the overhead or interference, you could call InitClock() after you get a clock reading and then adjust its value later.
Conclusion
One of the most basic tasks for an embedded real-time system is to determine the time an event occurred or to generate an event at a specific time. Most computers and embedded systems have an internal clock that software can access to determine the time. If a development environment supports the Standard C Library, the function to access the internal system clock is clock(). To get the highest accuracy and resolution out of the system clock, an application needs to first quantify the limitations of the clock and then compensate for inaccuracies and employ techniques such as call counting to yield higher resolution times.
References
[1] Wilson, Matthew. "Win32 Performance Measurement Options," Windows Developer Network, May 2003. (Compares the timing functions available under Win32; excludes clock().)
[2] Allison, Chuck. "Fast Times with the PC Clock," Windows/DOS Developer's Journal, April 1992. (Covers the 8253/8254 counter chip and the 8259A interrupt controller chip.)
[3] Foster, J. "Using the 146818 Real-Time Clock Chip," Windows/DOS Developer's Journal, September 1992. (Covers programming the real-time clock chip and having it generate periodic interrupts.)
[4] Koenig, Andrew and Barbara Moo. "Performance: Myths, Measurements, and Morals," The Journal of Object-Oriented Programming, May 2000. (Uses the overhead of calling the clock function to increase precision.)
William Smith manages development of data acquisition instrumentation for Montana Software. He can be reached at [email protected].