Scientific software development encompasses two domains. The algorithm-discovery domain is concerned with the development of a solution, typically in MATLAB where double precision is the default data type and exploratory ease trumps compute efficiency. The software-engineering domain is concerned with hosting algorithms on some architecture, whether it's a desktop Pentium, embedded Texas Instruments' digital signal processor (DSP), or Xilinx field-programmable gate array (FPGA); and in some language, perhaps C, C++, or Verilog; all the while paying attention to compute resources, multithreading, real-time I/O, and software extensibility and maintainability.
A few years ago when tasked with developing algorithms for digital communications signal-processing in MATLAB, I found myself fully in the first domain although much of my career has been spent hosting algorithms crafted by others. After reaching a critical mass of code in the form of numerous MATLAB functions and scripts, the natural question arose as to how the solution might “hang-together” in an environment where the flow of the algorithm could change dynamically at different phases of operation as well as conditions in the data. What quickly came to mind was an abstract object-oriented framework of signal-processing modules more naturally expressed in a language like C++. Because my algorithms had not been fully fleshed out and required a lot more research than development, it was premature to start implementing in a low-level language. Although MATLAB provided a richness of functionality, it needed more in terms of answering the architecture question.
Simulink, a sibling product of MATLAB created for simulation and modeling, purports to address some of the issues of block I/O, control, and implementation. Using another product from The MathWorks called Real-Time Workshop, models can be automatically translated into C for a targeted platform. But for a solution that was in its infancy and likely to change over time, Simulink is cumbersome and subtracts greatly from the ease MATLAB traditionally provides, especially for initial research.
At the time, I opted to tame the function-based language of MATLAB into the abstract framework I had imagined. What resulted was a pseudo-object-oriented, pass-by-value, crude-in-the-extreme mock-up, but sufficient for casting signal-processing stages into an archetypical anatomy that could be readily implemented in C++. By and large, the question of how the algorithm hangs together was answered but not in a way that would trivialize implementation.
Recently The MathWorks released an improved object-oriented programming feature with the 2008a version of MATLAB. Concurrent with research, scientific programmers can now structure algorithms into a set of classes complete with abstraction, virtual methods, encapsulation, reference passing, and even an event-listener model that, eerily, mimics the one I developed in my crude prototype. This article describes the new object-oriented programming features of MATLAB and how they can be leveraged for signal processing development.
Object-oriented analysis and design
It's less common that one would opt to host DSP applications in C++ in the first place given the footprint associated with the language and the number-crunching needs of the applications. Traditionally, DSP programs have been written in C, assembly, Verilog, or, heaven forbid, cast as an ASIC. Nevertheless, tools like Texas Instrument's Code Composer and Analog Devices' VisualDSP++ support C++ development. A performance-sensitive version of the language called Embedded C++ constrains the language to key features forbidding more luxurious items such as multiple inheritance, templates, use of standard template library (STL), and real-time type identification (RTTI).
For software DSP applications, an object-oriented description can go a long way toward functional reuse and extensibility. Little analysis is required to see that the items of a signal-processing flow diagram could be readily mapped to a system of classes, as shown in Figure 1 .
Specifically, an abstract class called Module will perform some operation on an abstract class called Data, which may produce more Data for further processing, detect the occurrence of some feature in the data or both. In the case of detection, another abstract class called Event may be devised to notify interested modules of its occurrence. Modules may have virtual functions for its operation on data (Apply method), notification of an event (Notification method), or resetting to its original state (Reset method).
Any number of concrete classes can be derived from Module to create a library of reusable signal-processing building blocks. For example, the OSCModule would perform overlap-and-save convolution; ALCModule would perform automatic level control on its input; and finally the TDModule subclass could detect the presence of a pure tone, as Figure 2 shows.
These modules could operate on Signal objects derived from the Data abstraction and produce other Signal objects, Event objects, or both. When an event of interest occurs, registered modules would be notified through the Notification method. Finally, a container module shown in Figure 3 could chain subordinate modules into larger signal-processing schemes and handle events produced by its children.
Hosting such architecture in prior editions of MATLAB was possible but a kludge in the extreme (I know because I did it). Although there was barely a nod toward OOP, the age-old policy of passing by value made it particularly difficult to model a system of interacting objects. Now with the recent release of 2008a, MATLAB effectively provides the tools to express signal processing and other scientific algorithms in an object-oriented design.
Thankfully, pass-by-reference is provided as part of the new object-oriented programming features of MATLAB 2008a. This “reference behavior” is done through the use of the MATLAB handle class. This referencing, taken for granted in other languages, is probably the most critical of the new features.
A class can be defined using the new keyword classdef definition-block. Within this block one can create one or more properties definition-blocks for member data declarations as well as one or more methods definition-blocks for member functions. All definition-blocks start with one of the aforementioned keywords and terminate with the all-popular end familiar to those that use switch and if-else statements in traditional MATLAB functions. Like traditional MATLAB code, class declarations and definitions are inscribed in M-Files (in other words, text files using the .m extension)
Attributes of classes, methods and properties can be set in a parenthetical list after the keyword. In this way, classes can be given custom attributes. As one would expect, properties can be abstract, constant, public, protected, private and more. Likewise, methods can be declared virtual, public, private, and static–notions common in languages like Java and C++.
One of the most interesting features of the language is a built-in listener-event model–and this is where reference behavior is crucial. Like the other keywords, an events definition block will declare any number of events that can also be given special attributes. Listeners can be registered using the addlistener function which declares callbacks to be executed once the event is dispatched. Conveniently, these callbacks can also be class methods thereby providing the infrastructure for the design pattern of signal-processing objects described earlier. Modules synthesizing events can call the notify function to agnostically dispatch an event to a system of listeners.
A model for signal processing
Equipped with these new language features, casting the proposed signal-processing architecture becomes straightforward. First, we define a set of MATLAB classes that will constitute an abstract layer: Module, Data and Event (Listings 1, 2, and 3 respectively). The first two are derived from the handle class, which gives objects reference behavior. Without this inheritance, such things like an access method to set an object's state would not work: once returning from the method the state of the object would be as it was before the access method was called. Event is derived from the event.EventData class ordinarily used in the event-listener model. This allows us to customize the event object normally passed to listeners. In this case, we want to pass a time value or any other information associated with an event.
For Module, we define virtual functions Apply, Notification, and Reset. By default all functions in object-oriented MATLAB are virtual; in other words, they can be redefined in derived classes. To make a function pure virtual (one only defined in a derived class) the attribute Abstract can be set to true as it is for the Apply method which embodies the operation of the module on given input Data.
Type casting is weak in MATLAB. Even though one defines a virtual function with a certain argument signature, there is no enforcement on what type of objects can be used when actually calling the method. Remember that MATLAB is an interpreted language designed to be flexible and convenient. If the argument type is incorrect, one will know soon enough when the script is executed.
The special sauce of the abstract layer is the Module's RegisterEvent method, which uses the new MATLAB 2008a event-listener model. In this design pattern, Modules can register themselves to receive notification of another Module's events. When the event occurs, the recipient module will have its Notification method called, presumably to change some state of the notified module. With reference behavior, Notification can be called as soon as the event is posted and any changes to the recipient module's state have retention. This was not the case in my old pass-by-value architecture in which event handling was delayed until any object that was in use had to be returned to an unlocked state before being notified.
With the abstract set of classes so defined, any number of concrete classes can be derived. In this example we define a general Vector class from Data followed by a Signal class that has notions of time, frequency, and sample rate (Listing 4 ). Signal will be the coin of the realm by which our derived Modules transact information. Note also that the constructor of the Signal class illustrates how a super class constructor is used to initialize inherited properties.
Finally, we define a number of concrete classes from Module. The ContainerModule (Listing 5 ) contains a set of subordinate modules to coordinate a chain of signal processing such as convolution, automatic line integration followed by tone detection (TDModule Listing 6 ). In the Apply method of the TDModule, a number of calculations are made to estimate frequency, frequency deviation, and power level. Together these are used to evaluate a set of criteria that determines the presence of a tone. When this detection occurs, the module calls notify to inform any listening modules.
Using RegisterEvent in its constructor, the ContainerModule registers itself to be notified when the TDModule posts the detection event. When this event occurs, the ContainerModule's Notification method is automatically invoked upon which it displays a simple message to MATLAB's command line interface.
Comparisons to other object-oriented languages
For those familiar with other object-oriented languages such as C++ and Java, some of the features found in those languages will be noticeably absent:
• Multiple constructors cannot be defined. The only method for defining different constructors is to check the number of arguments in the one and only constructor and invoke different behavior based on that clue. In this way one may initialize default values for arguments not specified in the original call.
• Static properties are handled differently. One can use the persistent keyword to create a static variable in a static function, but there is no concept of a persistent property in the class definition.
• Member methods require incessant dereferencing. The first argument of each member method is a handle to the class instance, usually called “obj”. This might be viewed as the “this” pointer used in C++ and is required for accessing member data in member methods.
• MATLAB is weakly typed and what method to call is not based on argument signature.
Overall, the important new features of object-oriented programming in the MATLAB environment make it much more possible to model a software solution as a system of objects as it may appear in a final implementation.
Scientific programming in MATLAB using the new OOP language features of the 2008a release has a number of secondary side effects:
• The division between scientist and software architect become blurred as the algorithm and final expression in software are nearly identical.
• Object-oriented approaches can be explored without abandoning the rich functional environment of MATLAB.
• For discrete time models (such as DSP algorithms), a frame based “blockset” of objects can be created and maintained as in Simulink.
• Troubleshooting target DSP solutions can be greatly aided with a parallel system of objects in MATLAB equipped with the analysis and visualization features for which MATLAB is renowned.
James Metzger is the branch chief of Digital Signal Processing at SPARTA, Inc., where he gets to apply his love of object-oriented programming to scientific applications (and they pay him for it, too). You may reach him at email@example.com.