DSP Blocks

DSP blocks are comparatively easy to build. They are usually single functions, and they only require working with NMPML Data instances, rather than whole documents or GUI APIs. The Data class has one of the most complex APIs of any nmpml class, but this i still simple relative to the whole Mien Object model. In addition, you won't need to understand very much of the Data API in order to write useful extensions.

An important point to note is that DSP blocks are inherently special-purpose. The Data class is complex mostly because it is highly polymorphic. Many different types of data can be stored in a Data instance, and the class supports complex nest arrangements of data and metadata of different types. DSP blocks are intended to work in particular tool chains.

Not every block needs to work in every circumstance. It is quite normal for blocks to require that the data they are provided is of a particular type (for example is a time series with at least 2 and not more than 20 channels), or has some set of properties (for example, is mean zero). It's important to document your block, and explain what the requirements are for using it, but it's not required to write extensive type-detection, sanity checking, or exception handling into you block. If a block fails, Mien will display the exception and fail to do the function, but won't abort running GUIs. If possible, your block should execute any statements that are likely to fail before modifying the Data instance. If you stick to that convention, a user who misuses one of your blocks should suffer no ill effects, and can go read your documentation, make needed adjustments to the data, and try again.

Entry Point Arguments

The basic structure of a DSP block is a function that expects to receive one or more arguments. The first argument is a Data instance that will be processed by the function. The remaining arguments can contain most simple python datatypes and containers. In principal, you can choose any argument names, data types, and default values (or not) that you want. Your blocks will be much easier to use from inside Mien's GUIs, however, if you adhere to some conventions regarding the arguments.

When a Mien GUI uses you block, it will automatically generate a GUI for the user to enter the block arguments in. By default this gui provides text entry windows for each argument, and attempts to eval the resulting text to arrive at a Python data type. This is easy to use for float, in, and string arguments, but seriously difficult if your argument is, for example, a dictionary mapping tuples of ints onto lists of functions!

Some argument types are common, and are either complex (like the "select" tuple described below) or easier to specify with a GUI than with text (like the position of a visual feature in time-series data). Mien deals with these arguments by providing a set of argument names that get special treatment when the GUIs are generated. If you can use argument names that match these patterns, you can get customized GUIs for your blocks without writing any GUI code yourself.

If you have arguments that take a finite set of possible values, you can also specify the list of choices using special syntax in the function documentation string.

If all else fails, provide a reasonable template for your argument as a default value for that argument when you write your function. This way, users will at least see what sort of code they need to write in your argument GUIs.

If you really need a very complex custom GUI for a particular block, and you can't work around it using these shortcuts, you may also need to write a DV extension to provide a custom GUI for accessing your block.

Automatic construction of GUIs for DSP functions is mediated by the mien.dsp.widgets module (particularly the function getArgChoice defined in that module).

Documentation strings and Switches

All DSP block functions should have a documentation string (this means that the first line of the function after the "def" statement should be a triple quoted string that describes how to use the block). Adding this string will allow GUI users to browse the documentation for your block from within the Mien GUIs. In addition the doc string is used to implement arguments with a list of possible values (aka switches). Since the doc string is triple quoted, you can include new lines and arbitrary white space inside it. If you start a line in your doc string with the string "SWITCHVALUES" (optionally preceded by white space) you can set up this behavior. Here is an example:

def printDataLength(ds, whichDim='length'): """Prints info about the size of the data. whichDim tells the function whether to print the length (number of samples) or the width(number of channels) SWITCHVALUES(whichDim)=['length', 'width', 'both'] """ s=ds.shape() if whichDim in ['length', 'both']: print "data set contains %i samples" % (s[0],) if whichDim in ['width', 'both']: print "data set contains %i channels" % (s[1],)

The SWITCHVALUES statement needs a particular syntax, indicated above (even though it's embedded in a comment, it's interpreted by a python-like machine parser). The syntax is as follows:

SWITCHVALUES(argName)=PythonList

where argName is the name of one of your block arguments (other than the first one), and PythonList is some legal python list constant containing the allowed values for your argument.

Automatic choice of default values

When mien calls DSP functions from Dataviewer, the default values of some arguments are over-ridden by values determined by the current state of the Dataviewer GUI. This is done for any argument name that starts with one of the following strings:

Specialized GUI Browsers

The argument GUIs for certain arguments will provide a "browse" button that launches a GUI tool for selecting argument values of a particular type. As with automatic defaults, these are determined by matching the beginning of the argument name against a pattern. The following patterns are supported:

Writing the actual functions

Now that you know how to set up your functions, all that's left is to make them do useful behaviors. IIRC Brian Kernighan said that "once you can write 'hello world' the rest of programming is just details." Various folks have also mentioned, however, that "the Devil is in the details."

DSP blocks will make very heavy use of numpy functionality. For general documentation on python and numpy, see this section of the howto.

DSP blocks can ignore a large part of the MIEN API, since they don't usually have to interact with any GUIs, and the only NMPML element they usually interact with is the Data Element (aka an instance of MIEN class "Data"). That's the good news. The bad news is that the Data class has one of the most complex APIs of any single MIEN class, so much so that it has its own page.

For a quick start, you can use only a few functions and methods from the data API to get numpy arrays from your input data set, modify them (usnig numpy functions), and write them back into the data set. See the most common functions section of the data api page.

Remember that your DSP "functions" don't actually return any values. Their entire effect is to modify the Data element in place. This may include changing a data narray or adding subdata elements. Sometimes, your function will generate a simple Python data type (e.g. a float scalar, string, integer, etc.). In this case you may want to store the result as an attribute of the Data element. You can do this using code like "ds.setAttrib('MyResult', returnValue)".

 

Last edit: 05/29/09

Index