GUI Development for Real-Time Applications

Tools for creating graphical process control

Avram K. Tetewsky

Avram, who holds a BSEE from RPI and an MSEE from MIT, works at Draper Laboratories in Cambridge, MA. He can be reached at atetewsky@draper.com.


At their best, real-time control applications have traditionally sported nothing fancier than pick-a-number, character-based interfaces. Still, any real-time control application which presents an operator interface can benefit from a GUI. A GUI can, for instance, decrease the learning curve for new users and lessen the chance of erroneous input by experienced users. There are PC-based tools for developing GUIs for real-time systems, including both RTWare's ControlCalc Version 1.78 and National Instruments' LabView 3.0. In addition to letting you put a pretty face on real-time control apps, these tools can enable rapid prototyping. In ControlCalc's case, you can even ROM the application. And with today's 32-bit processors, you can have your GUI and get real-time performance, even with one CPU.

While LabView has been examined several times in DDJ (most recently in "A Visual Approach to Data Acquisition" by James F. Farley and Peter D. Varhol, May 1993), you're probably less familiar with ControlCalc. Interestingly, ControlCalc is based on a multipage-spreadsheet paradigm with each page constituting a single thread of a multithreaded program. Of course, this requires a real-time operating system. Consequently, ControlCalc utilizes Microware's (Des Moines, IA) OS-9000 1.3 operating system and Gespac's (Mesa, AZ) G-Windows 2.3 windowing package to provide the underlying multithreaded environment and graphics. ControlCalc currently runs on both 680x0/OS-9 and 386/OS-9000 systems. LabView is available for the Windows, Macintosh, and Sun platforms, none of which are real-time operating systems.

Because of the many DOS/Windows applications for Intel processors, OS-9000 provides for dual-boot DOS/OS-9000, file exchange, and the ability to run DOS and standard Windows programs as OS-9000 processes. G-Windows is the GUI add-on that replaces OS-9000's text interface. The spreadsheet's slash commands provide control of a built-in compiler, debugger, and run-time environment.

In this article, I'll examine ControlCalc's performance and real-time multithreaded programming paradigm, as well as take a quick look at National Instruments' LabView for Windows data-flow driven software. In the process, I'll explore each tool's programming metaphor, addressing issues such as how you specify algorithms, dataflow, state machines, timing, and scheduling using ControlCalc's multithreaded scheduling model and LabView's data-flow paradigm.

Real-Time Control Benchmarks

Example 1 is the pseudocode for a control benchmark (benchmark #1) that contains a high-speed control-loop, data-logger, background-editing session, and a GUI, all running on a single CPU. Code implementations and executables for ControlCalc- and LabView-specific versions of the benchmarks are available electronically, as are programmer's notes; see "Availability," page 3.

The acquisition, logging, and analysis portion of the benchmark can use buffers to compensate for transient loads such as the GUI (due to a windowing system/

editor temporarily usurping CPU time). While buffering allows you to maintain an overall average throughput rate, control problems usually require absolute performance. In these cases, the control inputs and outputs must occur on time; in some cases they may even need to be synchronized with external events (control of certain laser servos or digital-phase tracking loops in GPS, for example).

Quantitative measurements are made possible by allowing the operator to vary the display and control rates and disk/display blocking factors. (With benchmark #1 the external background job is the running of a simple ASCII editor.) Finally, there is an operator control that toggles between simulated and real hardware. This is invaluable for testing when hardware hasn't been built yet. All you have to do is specify a control algorithm.

To keep the closed-loop control part of the benchmark easy to understand, I used a running mean and standard-deviation calculation. As with proportional integral derivative (PID) control algorithms, state memory is required. Unlike with PID algorithms, however, you don't have to be a control specialist or have hardware to understand and verify correct operation of this benchmark.

Basically, as each new point is taken in, the effect of the old point leaving the FIFO is removed; then the new, unscaled running-mean and mean-squared values are calculated. Scaling is based on the size parameter. A new mean and standard deviation is sent out at each sample point, which lets you test without actually closing a loop. Inserting a test sine wave on a bias and picking averaging times that are integral numbers of cycles should yield the familiar [sigma]=A * [sqrt]2 / 2 and m=bias results. Using the Watcom 9.0D compiler on a 66-MHz 486DX2 PC, this algorithm took 17 msec, or equivalently a sustained rate of 57 kHz.

I built a second benchmark (benchmark #2) to measure how well each tool translates the algorithm into compiled code. Because real-time scheduling isn't needed for benchmark #2, it is fair to compare ControlCalc and LabView in this one area. The code consists of two nested loops. The inner loop has some algebra, trigonometry, and two accumulations. Accumulators require state memory between iterations; because state memory is important for control, DSP, and many other scientific calculations, I felt it was an important part of this test for each tool. Example 2 is the pseudocode for benchmark #2.

ControlCalc and Graphical

Real-Time Paradigms

To completely describe a problem, real-time software requires information about data flow, control flow, and scheduling/timing. No single pictorial or abstract programming convention ties all of these diverse requirements together. It isn't that people haven't tried timing diagrams, state-machines, and Buhr diagrams, but it took Rich Clarke, ControlCalc's creator, to realize that users could quickly create real-time applications without constraining the solution with unproven graphical models.

Like LabView, ControlCalc's graphical real-time spreadsheet approach has two parts: a GUI builder and an automated code-generation tool. G-Window's Wedit program builds the front panels, storing them in separate files. For the programming paradigm, ControlCalc uses a spreadsheet to generate the executable code. At the top left of Figure 1 (which illustrates the complete system), the G-Windows Wedit program allows for the design of front panels with special-purpose GUI widgets (gauges and the like). Wedit uses standard drag-and-drop editing techniques. After naming all widgets within a window, each newly created window is stored in a file. The /GC graphics-configure option presents a dialog box listing each widget, its attribute variables, and call-back functions, allowing you to then select the spreadsheet cells you wish to attach them to. Because this architecture is open, you can add your own widgets (as long as compound attributes are not used).

Next, you create all of the separate threads of execution. Each thread is one "scan" page of a multipage spreadsheet (see the middle of Figure 1). By using the /SC scan-configuration menu option, each page can be scheduled as either a cyclic thread with specified fixed priority, an interrupt-service routine, an initialization page, a clean-up page, or a page that can be signaled by the user (see the right side of Figure 1).

To use this scheduling model, you must know when each scan page can be triggered and what happens when it is. When a page is executed, it is scanned from top left to bottom right. If cells contain formulas, they are executed; if they contain text, they're skipped; and if they contain macros, they're usually skipped unless attached to a user-activated input widget or keyboard macro.

Scan-page triggering is determined by both configuration scheduling settings and explicit programming calls. In terms of the /SC scan configurations, the initialization and cleanup scans run at the obvious times. Cyclic scans can run at periods between 2 and 65,536 clock ticks. The default tic size is 100 msec, but on 486 PCs, there is enough throughput to support 0.5-msec tics. (This essentially requires regeneration of the OS-9000 system.) Thus, 1-kHz cyclic scheduling is possible depending on the processor-clock speed. For faster triggering rates, the interrupt configuration must be used, and I will attempt to measure the maximum rate for using the control-loop benchmark. The remaining /SC scan configuration, Signaled, is used to program explicit interthread communication protocols such as "protect a critical region."

Signaled scan pages execute a wait just before they reach the first cell. When they finally receive a signal or wakeup, they execute until hitting a sleep or wait. Scan pages will yield to other scan pages during lengthy I/O operations. When they hit the end of the scan, they go back to the top and wait. Given these low-level building blocks, you can create most of the typical real-time communication protocols such as the "share critical resource" in Figure 2.

ControlCalc allows several user-written ControlCalc programs to simultaneously run and communicate with each other using signal numbers between 500 and 900. The rules are that there are 127 pages, and up to 999 signal numbers. Signal numbers 1--127 are synonymous with pages 1--127. Signal numbers 490--499 are reserved for future use. Signal numbers greater than 500 can be linked with OS-9000 events to talk with other applications, including other ControlCalc programs.

Although there are 65,536 priority levels in OS-9000, G-Windows runs at priority 50000. Thus, you effectively have about 10,000 priority levels, more than enough for most projects to ensure that the GUI is placed at a lower priority than the computations. But real-time GUI performance is actually carried out by the built-in WINNER process (see Figure 1), which does all of the interfacing between the scan pages and graphics windows, hiding the details from the user. Input is always read at 5 Hz and is not missed.

Figure 3 shows the front panel for the ControlCalc/OS-9000 benchmark #1 when it is running, Figure 4(a), the configurations of the six scan pages, and Figure 4(b), the I/O page-definition file. Figure 5 is the graphics configuration page, while Figure 6 shows formula and value modes for the interrupt handler, respectively. Because OS-9000 is a real-time OS, you can spawn a separate process to allow applications such as the umacs editor to run without degrading the higher-priority parts of your ControlCalc program. Watcom C performed the control-loop portion of benchmark #1 at 57-kHz rates; ControlCalc did that part alone at 47-kHz rates.

Table 1 provides the ControlCalc results for benchmark #1. Actual performance numbers depend on whether you have an optimized graphics display driver and whether the A/D board can have the sample ready when the interrupt occurs. For example, National Instruments' AT-MIO-16X board can have a 16-bit sample ready when the interrupt occurs, while the Keithly Metrabyte DAS 8/PGA board cannot; it only has a 30-[mu/micro]sec settling time. Thus, the DAS 8 board is limited to 33-kHz rates, while the NI board is good for 100-kHz sampling. In terms of computing hardware, the local-bus video board on my 66-MHz/486 wasn't supported by G-Windows. However, RTWare had a 33-MHz/486 with a TSENG Labs ET4000 video board that G-Windows does support. By measuring performance with and without various graphics features enabled, it was relatively easy to predict peak performance by extrapolation using the formula in Example 3. Those extrapolations are denoted with an asterisk (*) in Table 1.

For example, if the interrupt handler runs at 5 kHz, and you can save 30 msecs by using the NI board instead of the DAS8, you have saved 5 kHz*30 [mu/micro]sec, or 15 percent loading. At 10 kHz, that's a 30-percent savings in load. Most real-time system programmers like to see the CPU running at not more than 50-percent capacity. This allows you to add future enhancements. Table 1 shows the ControlCalc results for the benchmark described in Example 1.

Even when ControlCalc is saturating, you can view spreadsheet pages and toggle the display modes. Simple displays, gauges, readouts, buttons, and the like can be running along with a 10-kHz control loop. But displays with intense graphic operations eat up all available CPU loading. If you are doing intense graphics displays on a single CPU system, 5 kHz seems more reasonable.

In terms of compilation, in benchmark #2 ControlCalc 1.77 took 18.4 [mu/micro]secs per loop iteration compared with Watcom C's 12.48 [mu/micro]sec.

The ControlCalc spreadsheet is similar to VisiCalc in both features and use. For example, the classic AA1 type-naming conventions are used. Of course, an additional page number is placed in front of each--1AA1 refers to the first cell on page 1. However, there are some key differences. First, the spreadsheet is strongly typed, and the following types are supported: doubles, integers, text, variable text, and in some cases, history cells (FIFO buffers). Second, ControlCalc applications are compiled. Although ControlCalc automatically compiles before running, during development you must sometimes issue the /TC toggle compile. While this gives good performance, there are certain side effects: Until the spreadsheet is compiled, some cells do not always evaluate and display meaningful results. You must also use the SETVAL functions in the initialization pages to force some initial conditions each time you start and stop a run. This is especially true of cells that modify/increment themselves. The most important difference is that the ControlCalc spreadsheet supports history cells. Because history cells can act like either FIFOs or stacks, it is possible to insert buffering into almost any operation on an as-needed basis.

You can give symbolic constants meaningful names such as SIGNAL(MY_KEY) vs. SIGNAL(400). Unfortunately, the /SC menu commands accept only a number, not a named constant. Thus, you cannot give pages names such as the_reader_page or the_writer_page, nor can you place comments in any of the configuration menus. Although printing the /SC configuration lets you see how the program is organized in time, you have no clue about data flow. This is similar to the Gosub facility in early Basics, which had no local variables. Also, loops and logic statements require Gotos, although you can label the targets.

Unless you are very organized, ControlCalc is best suited for small- and medium-scale jobs. Thus, you could use ControlCalc to control the engines of an aircraft, but I wouldn't use it to control an entire aircraft until a full set of development tools is available. LabView has similar problems when tackling large problems. Despite its ability to have a hierarchy of diagrams, it has trouble with timing and state variables. At a minimum, both ControlCalc and LabView need some type of code management, a data dictionary, and organized data-flow tools before undertaking extremely large projects.

LabView's Modified

Data-Flow Paradigm

Figures 7 and 8 show LabView's front panel and wiring diagram for benchmark #1, respectively. Wiring diagrams use G-Language, National Instruments' "graphical" language. Because the data flow is used to determine execution flow, the data-flow model must be specially modified for state equations. The LabView approach is to append shift registers to a While loop (the small triangles in Figure 8). In fact, to create the equivalent of C external variables (static memory), you must use a subroutine with an uninitialized shift register, a switch for reading and writing these variables, and a switch to do a first-time initialization (or use LabView 3.0's globals). Figure 9 is the data-flow equivalent of x(next_time)=x(current_time)+1.0.

LabView tries to mitigate the effects of the non-real-time host operating systems (Macintosh 7.x, MS-DOS 6.x/Windows 3.1, and Solaris 2.0) by building hardware buffers into all their acquisition boards. This essentially allows one extra layer of fast buffering so that, on average, LabView can keep up its throughput whenever the non-real-time OSs preempt the CPU for uncontrolled amounts of time.

Wrapping it Up

In terms of widget assortment and aesthetics, LabView outshines ControlCalc/G-Windows. In particular, LabView graphs and charts all have on-the-fly palette controls, and all charts/graphs support multiple curves with legends. You can add online help for each widget, making your front panels self-documenting (although you take a temporary performance hit in real time when this facility is used). LabView input controls and output indicators can be placed in 1- or 2-D arrays and in clusters. G-Windows minimally supports 1-D arrays of widgets, while 2-D arrays are manually wrapped 1-D arrays. ControlCalc does not support compound (clusters of) widgets, but its WINNER process guarantees good performance. Although the widget selection is limited and not as good as LabView's, you can add your own. ControlCalc also supports password protection and touchscreens. Finally, the polygon bar graph can be used to create chemical tank and other interesting displays.

It is potentially easier to add C code in ControlCalc because you get source code to portions of the software. This lets you debug routines while running ControlCalc from within the OS-9000 debugger. With LabView, you get a debug print window and memory-check routines, but that's about it. Consequently I developed a mini-emulator. Thus, I inserted a dummy mainline into my LabView code, created the LabView input/output arguments, and emulated a few key calls so that I could test my routines without LabView. Without the emulator, you usually crash LabView under MS-DOS or the Macintosh OS if you make a mistake. Neither program is currently compatible with version-control software, but for ControlCalc, it is relatively straightforward to output ASCII versions of the spreadsheet-template files.

Although LabView is primarily for acquisition, analysis, and display output, National Instruments does provide a PID control-application library. So performance-wise, there is some merit in measuring maximum control rates while simultaneously running low-rate graphic displays. No matter what I did to minimize graphics calls on a 66-MHz 486 PC, I could not get more than 30-Hz performance for my control loop. However, LabView clearly can get continuous signal-processing rates of 20 kHz using an AT-MIO-16X board, including displays. After the LabView people reviewed my benchmarks, this apparent paradox was resolved.

The overhead between the 32-bit LabView model, 16-bit Windows, and hardware-interrupt drivers limits you to 300 scans/second (transfers/second) on a 66-MHz 486 PC. (The maximum scan rate on a 33-MHz 68030 Macintosh FX is about 2 kHz, according to the author of the LabView PID package. Ellipsis Inc., of Boston, MA, has found that if you write your C code with operating-system-dependent callbacks and embed the control algorithm in the A/D and D/A code, you can get synchronized kHz performance with low update-rate graphics for symmetric-channel processing on a Macintosh.)

If you are only sending one point (BLOCK_SIZE=1), that's also the control-algorithm limit. For DSP work (such as FFTs), you can process blocks of data at a time. Thus, the effective sample rate is really limited to MAX_SCAN_RATE*BLOCK_SIZE. As long as you have extra memory and your algorithm can crunch BLOCK_SIZE pieces of data within one MAX_SCAN_RATE period, you can keep up with the incoming data. Until LabView is hosted on a real-time OS, it is best to use this tool for algorithms that can be block processed. You can squeeze some additional performance by block processing your graphics. With control benchmark #1, FIFO-driven displays were five times faster than the non-FIFO versions.

With benchmark #2, ControlCalc 1.77 took 18.48 [mu/micro]secs per iteration to compile; Watcom C took 12.48 [mu/micro]secs, and LabView (using globals) took 82.4. By using shift registers on the outside of the loops, I got LabView down to 24.9 [mu/micro]secs, at the expense of cluttering the diagram.

The Future

Both ControlCalc and LabView have enormous potential for real-time developers. RTWare is preparing to release ASCII file exports amendable to code-management systems, improved syntax for loops and logic statements, and better data-dictionary tools. And because it's internally parsed into parallel multithreaded loops, LabView will certainly utilize these features, too, when Windows NT and Solaris show true real-time performance.

Example 1: Pseudocode for benchmark #1.

during setup, size = sampling_rate * requested_averaging_time;
check that size <= max FIFO size (avoid dynamic allocation for now, a bit faster).
new_start = (start + 1) % size;
raw_mean --= data_buffer[start];  raw_rms2 --= data2_buffer[start];
data_buffer[start] = new_data; data2_buffer[start] = new_data*new_data;
raw_mean += data_buffer[start]; raw_rms2 += data2_buffer[start];
start=new_start; the_mean = raw_mean*scale; the_rms2 = raw_rms2*scale;
the_std2 = the_rms2 -- the_mean*the_mean;
(test if negative) the_std = sqrt(the_std2) else 0, inc total_err;

Example 2: Pseudocode for benchmark #2 (n1=n2=2001 for all results).

do ii=0,(n1--1),
    do jj=0,(n2--1)
      x=ii;y=jj;xpy=x+y;xty=x*y;
      x1 = sin(xpy);x2=sqrt(xty);x3=(x+1)/(y+1);
      x2a=--x2 if xty > 0; x1a=x1 if jj%100==0;
      x4=x1a+x2a;
      accumulators:  x5+=1.0;x6+=x1a;
   end jj;
end ii;

Figure 1: ControlCalc overview.

Figure 2: Using signals to share/protect a resource from simultaneous reads/writes. Using the Name utility, define the name MY_KEY as a value of 400 so that SIGNAL(400) can be written as SIGNAL (MY_KEY). The ControlCalc command is /NU MY_KEY V 400.

Given the following Scan Page Configurations (/SC) of:

     Page     Configuration     Comments
     1        Initialization    Put one copy of MY_KEY into play
     2        Signaled          The Reader Page
     3        Signaled          The Writer Page
     4        Cyclic            Will signal page 2 when wanting to read
     5        Interrupt         Will signal page 3 when wanting to write


Shared memory that must be protected from having two pages read/write at the same time


Init Page
SEND(MY_KEY)


Page 2
 ... some code
 ... grab the shared
 ...   resource
WAIT (MY_KEY)
  ... got it, now read it
  ... now release it.
SEND(MY_KEY)
... rest of page 2.


Page 3
 ... some code
 ... grab the shared
 ...   resource
WAIT (MY_KEY)
  ... got it, now write it
  ... now release it.
SEND(MY_KEY)
... rest of page 3.


Figure 3: Front panel of the ControlCalc running benchmark #1.

Figure 4: (a) Scan configuration settings for the ControlCalc benchmark #1; (b) I/O page settings for ControlCalc benchmark #1.

Table 1: ControlCalc performance for benchmark #1.

Platform           All graphics hidden          Graphics at 50 points per minute
                   and turned off               (Two strip charts and one x,y plot)
-----------------------------------------------------------------------------------
33-MHz, VGA,       @0.02 kHz, load=10%          @0.02 kHz, load=61%
  DAS8 64K         @1 kHz, load=28%             @1 kHz, load=70%
  Cache            @2.5 kHz, load=53%           @2.5 kHz, load=89%
                   @5 kHz, load=96%             @5 kHz, saturate--but runs

33-MHz Tseng       @0.02  kHz, load=10%         @0.02  kHz, load=48%
  DAS8             @1 kHz, load=27%             @1 kHz, load=65%
                   @2.5 kHz, load=55%           @2.5 kHz, load=89%
                   @5 kHz, load=96%             @5 kHz, saturate--but runs

33-MHz VGA 
  AT-MIO-16X       *@0.02 kHz,load=10%          *@0.02 kHz, load=61%
  *save rate_kHz   *@1 kHz, load=25%            *@1 kHz, load=67%
  *30msec/10%      *@2.5 kHz, load=48%          *@2.5 kHz, load=82%
  from case 1      *@5 kHz, load=81%            *@5 kHz, saturate

33-MHz Tseng 
  AT-MIO-16X       *@0.02  kHz, load=10%        *@0.02  kHz, load=48%
  *save rate_kHz   *@1 kHz, load=24%            *@1 kHz, load=62%
  *30msec/10%      *@2.5 kHz, load=48%          *@2.5 kHz, load=82%
  from case 2      *@5 kHz, load=81%            *@5 kHz, saturate--but runs

66-MHz, VGA,       @0.02  kHz, load=2%          @0.02 kHz, load=42%
  DAS8 256K        @1 kHz, load=13%             @1 kHz, load=54%
  Cache            @2.5 kHz, load=27%           @2.5 kHz, load=82%
                   @5 kHz, load=50%             @5 kHz, load=92%
                   @8 kHz, load=76%             @8 kHz, load=99%
                   @10 kHz, load=92%            Saturate but runs

66-MHz Tseng       No change from above         Could not reliably extrapolate
  DAS8

66-MHz VGA        *@0.02  kHz, load=2%          *@0.02 kHz, load=42%
  AT-MIO-16X      *@1 kHz, load=10%             *@1 kHz, load=51%
  *save rate_kHz  *@2.5 kHz, load=20%           *@2.5 kHz, load=75%
  *30msec/10%     *@5 kHz, load=35%             *@5 kHz, load=77%
  from case 5     *@8 kHz, load=52%             *Saturate but runs
                  *@10 kHz, load=62%            *Saturate but runs

66-MHz Tseng     No change from above          Could not reliably extrapolate
  AT-MIO-16X

* Extrapolated using the formula in Example 3.

Example 3: Formula used for performance extrapolation.

cyclic_tasks
     S freq_cyclic_taski*Comp_Timei+Background_Load<1
     i=1

Figure 5: Graphics configuration page for ControlCalc benchmark #1.

Figure 6: Interrupt-handler scan page using formula-display mode for ControlCalc benchmark #1.

Figure 7: LabView front panel for benchmark #1.

Figure 8: LabView data-flow wiring diagram for benchmark #1.

Figure 9: Two ways of implementing state memory with LabView's G-Language dataflow notation; x(n+1)=x(n)+1.0.

For More Information

LabView
National Instruments
6504 Bridge Point Parkway, MS 53-02
Austin, TX 78730-5039
800-433-3488

ControlCalc
RTWare Inc.
714 Ninth Street, Suite 206
Durham, NC 27705
919-286-3114


Copyright © 1994, Dr. Dobb's Journal