Author: Andrew Cormier

As of version 1.10, REDHAWK supports easily creating components whose signal processing “business logic” is written in Octave (and open-source implementation that is very similar to MATLAB).  For more information on GNU Octave, check out https://gnu.org/software/octave/.

For those interested in additional functionality or design patterns for MATLAB integration, Geon has extensive expertise in the subject and is here to help (info@geontech.com).

Will Octave run MATLAB Code?

Whether or not MATLAB code will run in Octave is dependent on what MATLAB toolboxes are being used.  Since the support for Octave-equivalent toolboxes is always changing, the easiest way to determine if there are any compatibility issues is to just give it a try.

It is worth noting that Octave lacks some of the runtime optimization that MATLAB has; in our experience there have been some times where Octave has been slower.  Your results will vary depending on exactly what it is that you are doing and what version of Octave you are using; experimentation is really the only way to know if Octave will meet your performance needs.  We typically recommend converting code, as needed, to C/C++ when performance bottlenecks are encountered.

Why use Octave when C++ performs better?

As you may have guessed, you take a big performance hit when using Octave over C++.  In our experience, we have found Octave to run 3-30 times slower than C++ (depending mostly on how much recursion is used).  That being said, there are several reasons why Octave may be the appropriate development tool for your application.

  • Code can be optimized later.  For many applications, developing in Octave is just easier than C++.  REDHAWK’s language agnosticism supports a workflow where you can develop a prototype in a high-level language (Octave) and then replace components, as necessary with lower-level languages (C++).  No one is suggesting that creating components in Octave is the best solution for your end system, but it allows you to get up-and-running quickly.  As an added bonus, you just might find that the performance of your Octave component is sufficient, and you can skip the language-translation step all together.  Components interacting in a REDHAWK domain are oblivious to what base language was used to create them.
  • Not all signal processing developers know C++ and not everyone is a C++, Object-Oriented, REDHAWK programmer.  It is fairly common to find signal processing developers who have at least some, if not years (or event decades) of MATLAB/Octave experience who do not know C++.  Sticking to a high-level language like Octave can allow a signal processing developer to focus on algorithms without taking the months or years it may take to learn a new language.
  • You may already have solved your problem in Octave/MATLAB.  Using the REDHAWK code-generators allows for rapid integration of existing code without necessitating time-consuming code translation.

Octave sounds great, but I really want MATLAB!

While it’s great that Octave is free in the cost and philosophical senses, sometimes you just need MATLAB.  The matrix performance is great; the plotting is gorgeous; and the toolbox support is hard to beat.

Octave was selected for us in REDHAWK in alternative to MATLAB for a few simple reasons:

  • MATLAB licenses are expensive, and some find the license process to be a pain
  • A particular version of Octave can be delivered with REDHAWK in order to guarantee compatibility.  Specifically, one can reasonably expect REDHAWK to play nicely with whatever version of Octave is delivered with CentOS/RHEL 6 (side note: you will need to build the latest Octave from source for REDHAWK compatibility in CentOS/RHEL 5).
  • Since APIs can change from MATLAB version to MATLAB version, the code generators have to be tailored towards a specific version.

Nothing of the above means that new code generators cannot be created to support MATLAB; as a matter of fact, the code-generator code is structured to be expanded for use with MATLAB.  With Geon’s specific expertise in the Octave code generators, we can help in providing this functionality.  Shoot us an email for inquiries (info@geontech.com).

Standard Properties

bufferingEnabled

Setting this to “true” will cause the component to wait for an EOS flag on each incoming data stream before processing any of the data.  This is useful if you have a component that is set up to process a large file of data, as the data producer (i.e., the file-reading component pushing data to your Octave component) will have to break down the large data set into 2 MB “chunks” (a limit set by omniORB).

LOGGING_CONFIG_URI

This property exists for components in all languages, not just Octave components.  If running on a system where standard out is not available, it is highly recommended that you set this in case the Octave interpreter enters an error state (though we all write perfect, bug-free code, Octave’s dynamic nature can easily lead to failures if unexpected data is encountered).

By design, Octave components will keep running if the Octave interpreter throws an error when executing the M file (much like how the Octave command-line will “keep going” if an error is thrown).  The REDHAWK C++ component wrapper will catch any error encountered, and write a log message when this happens.  This log message, in conjunction with the diary, can be tremendously helpful when tracking down unexpected behavior.

diaryEnabled

Setting this to “true” will turn on the Octave diary, which will write standard out/error to a file.  This is very useful for diagnosing problems with an M file, but be mindful of the fact that nothing is cleaning up the diary file for you.  Also note that this can be turned on/off at runtime.

Creating Components with Very Little REDHAWK/C++ Background

The REDHAWK Octave integration patter was designed specifically to facilitate creation of REDHAWK components with very little REDHAWK/C++ background.  The following is a set of examples that walks through basic approach of creating REDHAWK components using Octave, without having to get your hands dirty with C++, Object Oriented, autotools setup, or any of the gory details of REDHAWK.

To get started, consider the following example M function that has no inputs, no outputs, and does nothing.  Define this function in a file named doNothing.m

To generate the REDHAWK source code for this Octave component, run the following command:

If this step does not work, check to make sure REDHAWK 1.10 (or greater) is properly installed.

The next baby-step is to try to generate the code as well as install the component onto the system:

If this step does not work, make sure the octave-devel RPM is installed or that Octave has been installed from source.  Octave version 3.4 or greater is required for the component to compile.

The next step is to create a component with some actual functionality.  The M function below defines a function that will take an input vector, add a constant value to it, and output the result.  Define this function in a file named addConst.m.

Output arguments (“myOutput” in this example) and input arguments without default values (“myInput” in this example) will be given corresponding output and input ports (always type “double”) in the REDHAWK component.  Input arguments with default values (“myConstant” in this example) will be treated as properties.  All properties with numeric default values will be type complex double; all properties with default values defined within quotation marks will be type string; all properties with vector default values will be type vector double.

Avoid using any Octave or C++ keywords in the function arguments.  For example, using “input” instead of “myInput” would cause problems, as “input” is a built-in function in Octave.  Similarly, using  “const” instead of “myConstant” would cause problems as “const” has a special meaning to the C++ compiler.

Now we will install the component, and verify its functionality in the REDHAWK Python sandbox:

This pushes a data vector ([0,1,2,3]) to the addConstant component and prints the results to the terminal.  We expect the output to be the same as the input, as we are adding the default value of 0.  If running these test commands in a script, be sure to put a sleep statement in between the push() and getData() calls in order to give the component some time to complete its processing.

In the same Python session, modify the myConstant property and rerun the test:

The output vector should be [0.5, 1.5, 2.5, 3.5].

Next, we will add a call to the doNothing function in order to demonstrate how to make a component that has multiple M files.

The create command needs to change slightly to include the second M file and to indicate which function will be the primary entry point for the component:

SRI and Sample Rate

Signal Related Information (SRI) is the term used for metadata that is sent with REDHAWK’s BulkIO data packets.  The SRI contains some standard variables (x-delta will be of particular interest) as well as a generic name/value pair list that can be populated fairly arbitrarily.  In REDHAWK 1.10, you will have to modify C++ code in order to map the user-defined name/value pairs (see the preProcess and postProcess functions), but sample rate (1/xdelta) can be accessed more seamlessly.

Sample rate is somewhat of a beautiful-and-unique snowflake in that it has special, first-class treatment with Octave components.  Yes, using special keywords can be bad for a lot of reasons.  That being said, sample rate has its own special, reserved keyword due to its ubiquitous use in signal processing.

To access the sample rate in an Octave M function, simply create an input variable called __sampleRate (note the double underscore: some fonts can make this difficult to see).  In REDHAWK 1.10, this will be ignored as an output variable.  If you need to change the sample rate for a component that is doing resampling, you will need to create a sample rate property (named something other than __sampleRate) and map it to the input and output data structures in the preProcess and/or postProcess methods in C++.

How to Update the M File(s) for an Existing Component

Once the component is created, it is fairly common for a developer to need to go back and modify the original algorithm.  There are 3 places where this can be done:

  • In the original M file (the file you had before REDHAWK was introduced).  This is often the ideal place to modify the algorithm, as all other copies of this file will in turn be synchronized.  Modifying this copy of the M file will require regeneration of the component, which can potentially take a few minutes.  This regeneration is required if any of the arguments to/from the function have changed (e.g., the order of the inputs changed or an output was added).
  • The M file in the generated component source directory (<yourComponentName/cpp>).  This is often the best place to make changes as it takes little time to deploy your changes and will keep you source code and deployed code synchronized.  Run a “make install” after changes are made.
  • The M file in the SDRROOT ($SDRROOT/dom/components/<yourComponentName>/cpp).  No additional steps need to be taken once modifying the M file: REDHAWK will automatically grab this version of the M file when a new component instance is created.  This can be a good place to make quick, experimental changes, but it is easy to end up with unsynchronized source/installed code.

In all three cases, the component must be redeployed for changes to take effect.

Greater Detail for the REDHAWK/C++ Savvy Developer

The generated component.cpp file is designed to allow for some more advanced functionality.  You can get your hands a little dirty, but still don’t need to work with BULKIO, CORBA, or the component serviceFunction.  There are primarily 3 methods of concern, all of which reside in the component.cpp file: the constructor (which shares the name of your component), preProcess, and postProcess.

The Service Function

Though hand-editing of the component service function is not necessary for Octave components (and not recommended), it is still somewhat important to understand what the service function is.  The service function is called in a loop whenever the component is in a started state.  In simple cases, each call to the service function represents a single call to Octave with whatever data has been sent in on the component ports.

If buffering is enabled (i.e., the bufferingEnabled property is set to “true”), the service function will:

  1. wait for data on each port (blocking once for each port)
  2. add data to a set of buffers once it arrives
  3. assuming the required EOS flags have not yet been received, return “NORMAL”, indicating that the service function should be called again by the component’s main routine

Once all of the buffers are full (i.e., and EOS flag has been received on all ports), the service function will format the data to be sent to Octave.

If buffering is disabled (i.e., the bufferingEnabled property is set to “false”) the service function will wait for at least one packet to arrive on each port before formatting the data to be sent to Octave.

The Service Function Return Value

There are 3 values that can be returned from the service function: NOOP, NORMAL, and FINISH.

  • NORMAL: this will cause the service function to be called again immediately.
  • NOOP: this will cause the service function to be called again after a small delay.  Typically this is used to indicate that port data buffer does not yet have data.
  • FINISH: this will cause the component to go into a stopped state, which will keep the service function from being called again until “start” is called again on the component.  This is useful for file-reader components that read an input file only once.

The SRI Port

By default, Octave components will forward SRI from one of the input ports to all of the output ports.  For components with multiple input ports, which input port is used for SRI is selected essentially at random by the code generators.  If the randomly-selected input port is not what you wanted, set the class variable _sriPort to the name of the desired input port at the component constructor.

The _base Files

If at all possible, do not do not edit these files: you may be entering a world of pain and regret.  This goes double for the service function.  Using the Octave C API is not rocket science, but there are many ways to get yourself into trouble when making changes.  Furthermore, anything you change is subject to being incompatible with future versions of REDHAWK.  It is always safer to override methods in the component.cpp file.

If you think you have found a bug in a generated _base  file, be a hero and report it to the REDHAWK project.

Getting source code

Access to the REDHAWK code generator source code is handy in case you want to either preview an alpha version before it is released or add your own customizations.

REDHAWK source code can be found on both GitHub and Sourceforge.  We recommend going through GitHub to acquire source code.  There are two projects related to code generators: the framework-codegen project is the what you want: eclipse-gov.redhawk.codgen is for the old code generators (for REDHAWK <= 1.8, which does not support Octave).  Here is how to clone the repository

Once checked out, use git branch to make sure you are looking at the latest version (i.e., develop-1.10).

Installing from Source

To install from source, we recommend first doing an RPM install to get the Jinja dependency installed correctly.  Then, simply use the setup.py script to install:

Depending on how you set up your file permissions in $OSSIEHOME, you may need to install using sudo.  If installing using sudo, depending on how you environment is configured, it is possible to inadvertently install in the wrong location (i.e., not in $OSSIEHOME).  You can get around this by either changing the ownership of $OSSIEHOME and installing without sudo or by setting the OSSIEHOME variable by hand in the setup.py file before installing.

Caution: Getting Rid of Old Versions

There is no programatic mechanism for deleting old versions.  If you think a new source install is being overshadowed by an old version, this may help:

This will assure that only the latest version of the code generators get installed.