VanGo
High-Rate Data Acquisition System
»Home
»Download
»Install
»Brief Intro
System Overview

Filters

Filters all follow a very typical format.

includes MotephoneTypes;
module TYPICAL_FILTER_MODULE {
  provides {
    interface FilterControl as FC_Reset;
    interface FilterControl as FC_Enable;
// additional FilterControl interfaces

    interface SingleFilter as Filter_p;
  }
  uses {
    interface SingleFilter as Filter_u;
    interface Leds;
    interface SampleSet;
  }
}
implementation {
#include "SnackConstants.h"

  bool m_enabled;
// other global variables

  command result_t FC_Reset.setValue(uint16_t val){
    // reset variables
    return SUCCESS;
  }
  command result_t FC_Reset.getValue(uint16_t *val){
    if (val) *val = 0;
    return SUCCESS;
  }

  command result_t FC_Enable.setValue(uint16_t val){
    // enable/disable filter
    return SUCCESS;
  }
  command result_t FC_Enable.getValue(uint16_t *val){
    if (val) { *val = m_enabled; return SUCCESS; }
    return FAIL;
  }

// more FilterControl definitions

  command uint16_t Filter_p.bufferSize(uint16_t headersize){
    uint16_t bsz;
    // allocate room for attributes
    bsz = call Filter_u.bufferSize(headersize + sizeof(attr_t) + sizeof(/*attribute*/));
    // calculate data compression/inflation
    return bsz;
  }

  command void Filter_p.put(sample_set_t *input){
    // process data and push data along
    call Filter_u.put(input);
  }
}

FilterControl Interface

A Filter is controlled through its FilterControl interfaces. The interface itself is composed of two simple function: a set and a get function.

Example: vango/tos/interface/FilterControl.nc
...
interface FilterControl {
  command result_t setValue(uint16_t val);
  command result_t getValue(uint16_t *val);
}

When a filter declares a FilterControl interface (excluding Reset and Enable), it is providing a means by which the user (via the ControlM module) can tweak a filter variable or modify the way the filter processes data. The set function modifies a variable, the get function probes the variable state. For example, the GateM filters data that does not meet a certain threshold requirements; a FC_Threshold FilterControl interface is added to adjust the threshold at which data is cut off.

Example: vango/tos/lib/GateM.nc
...
module GateM {
  provides {
    ...
    interface FilterControl as FC_Threshold;
    ...
  }
  ...
}
implementation {
  ...
  uint16_t m_threshold;
  ...
  command result_t FC_Threshold.setValue(uint16_t val){
    dbg(DBG_USR1, "%s [%d] - val %d\n",__FILE__,__LINE__, val);
    m_threshold = val;
    return SUCCESS;
  }
  command result_t FC_Threshold.getValue(uint16_t *val){
    if (val) { *val = m_threshold; return SUCCESS; }
    return FAIL;
  }
  ...
}

SingleFilter

In addition to controlling the filter, data needs to be pushed though the module; the SingleFilter interface provides that functionality. There are two finctions in the interface: put and bufferSize.

Example: vango/tos/interface/SingleFilter.nc
...
interface SingleFilter {
  command void put(sample_set_t *input);
  command uint16_t bufferSize(uint16_t headersize);
}

Put is used to push data through a filter, bufferSize is used to calculate the amount of data that can be passed through a sample set.

A filter that only provides a SingleFilter interface is a data sink (i.e. PacketizeM). A filter that only uses a SingleFilter interface is a data source (i.e. Sampler). The SingleFilter interface, however, is both provided and used in a typical filter. A filter that both uses and provides a SingleFilter interface is declaring that it can take a SampleSet as input on the uses interface and output a SampleSet on the provides interface. GateM is such a filter; it takes a SampleSet as input, checks the amplitude of the data, marks SampleSets that passes the threshold requirement, and passes the SampleSet to its provides interface.

There can be more that one SingleFilter that is provided or used in any filter. For example, the Sampler uses three different SingleFilter interfaces. The Sampler is capable of using the MSP430's three different DMAs to gather ADC data form three different channels. Since the data generated from the different channels may need to be processed by different filter stacks, each channel has its own SingleFilter interface.

BufferSize

The bufferSize function is used to calculate how large a buffer the Sampler needs to allocate to accommodate all of the Attributes and data requirements of the filters. The bufferSize function is needed in a high-speed sampling framework to adjust the amount of data generated at the Sampler per buffer so as not to overload or underload the filters.

The size of a SampleSet is dynamically calculated during run time on the mote. Most of a SampleSet's size comes from the data buffer and the number of attributes added by the Filters. The SampleSet size calculation is initiated by the Sampler via the bufferSize interface function. Each filter is given a say in the expected data buffer and attribute size.

In a provides bufferSize function, a Filter first calculates whether it needs to use attributes and passes the total attribute size to the next filter in the chain in the form of a parameter in a bufferSize function call. In the end, this information lets Packetizer know how large the header will be - attributes are part of the header.

...
  command uint16_t Filter_p.bufferSize(uint16_t headersize){
    uint16_t bsz;
    bsz = call Filter_u.bufferSize(headersize + sizeof(attr_t) + sizeof(/*attribute*/));
    // calculate data compression/inflation
    return bsz;
  }
...

The very last Filter bufferSize call will enter the Packetizer's bufferSize function. The function will use the headersize parameter to allocate room for the packet header and set aside the rest of the packet for the data buffer. The size of this data buffer will be returned by the Packetizer's bufferSize function to be the buffer size of the data attribute in the SampleSet. This number, however, is not set in stone. As each Filter's bufferSize function is given control during the unwinding of the function call chain; the local Filter's bufferSize function is allowed to adjust the size of the data buffer. In the case of a compressor, the buffer limit can be increased resulting in more data collected by the Sampler; because of the compression, the data will still fit into a single packet.

Creating New Filters

New Filters are created by taking the TYPICAL_FILTER_MODULE, adding a FilterControl interface for each variable that is modifiable outside of the Filter, and implementing the SingleFilter put and bufferSize functions. (Enable and reset FilterControl interfaces are not needed, but make sence in most cases).

Adding New Filters to VanGo

Adding the newly created Filter to the VanGo framework takes a little more effort. There are five main additions that need to take place:

  • Add an identifier to the filter_param_t enumeration in the MotephoneTypes.nc file for each FilterControl interface. The accepted identifier format is: "FILTER_PARAM_" + name of the Filter + "_" + name of the interface. This identifier will be used to identify the Filter's interfaces in ControlM.

    ...
    FILTER_PARAM_GATE_ENABLE,
    FILTER_PARAM_GATE_RESET,
    FILTER_PARAM_GATE_THRESHOLD,
    FILTER_PARAM_GATE_COUNT,
    FILTER_PARAM_GATE_SUMMARY,
    ...
  • Create a default value for each FilterControl interface to be used to initialize the Filter at boot-up. The defaults are added to the SnackConstants.h file and follow the format: name of Filter + "___" + name of interface.

    ...
    #define GateM____
    #define GateM____reset            (0)
    #define GateM____threshold        (1000)
    #define GateM____count            (10)
    #define GateM____summary          (1)
    #define GateM____enable           (1)
    ...
  • Wire the new Filter's FilterControl interfaces to ControlM using the ControlM's single parameterized FilterControl interface. ControlM can control many different Filters because of this parameterization; this type of parameterization is best described as a named port - ControlM controls Filters through a named port. In addition to wiring the Filter to ControlM, wire any other necessary modules (i.e.: SampleSetM, Leds).

    ...
      ControlM.FilterControl[ FILTER_PARAM_GATE_RESET     ]          -> GateM.FC_Reset;
      ControlM.FilterControl[ FILTER_PARAM_GATE_ENABLE    ]          -> GateM.FC_Enable;
      ControlM.FilterControl[ FILTER_PARAM_GATE_THRESHOLD ]          -> GateM.FC_Threshold;
      ControlM.FilterControl[ FILTER_PARAM_GATE_COUNT     ]          -> GateM.FC_Count;
      ControlM.FilterControl[ FILTER_PARAM_GATE_SUMMARY   ]          -> GateM.FC_Summary;
    ...
  • Instruct ControlM how to initialize the Filter at boot-up by setting the Filter's FilterControl interfaces to their default values as defined in the SnackConstants.h file.

    ...
    void set_defaults(){
      ...
      call FilterControl.setValue[ FILTER_PARAM_GATE_RESET     ]((uint16_t)GateM____reset);
      call FilterControl.setValue[ FILTER_PARAM_GATE_THRESHOLD ]((uint16_t)GateM____threshold);
      call FilterControl.setValue[ FILTER_PARAM_GATE_COUNT     ]((uint16_t)GateM____count);
      call FilterControl.setValue[ FILTER_PARAM_GATE_SUMMARY   ]((uint16_t)GateM____summary);
      call FilterControl.setValue[ FILTER_PARAM_GATE_ENABLE    ]((uint16_t)GateM____enable);
      ...
    }
    ...
  • To actuate the filters variables on a mote from the microserver, instruct ControlSocketM how to interpret the new commands. Commands usually follow the format: name of Filter + "-" + name of command. After creating the names, bind them in the get_command_id function in ControlSocketM.nc to the filter_param_t identifier created above.

    filter_param_t get_command_id(char *str_command){
      ...
      else if (strcmp(str_command,"gate-enable")==0){
        return FILTER_PARAM_GATE_ENABLE;
      }
      else if (strcmp(str_command,"gate-threshold")==0){
        return FILTER_PARAM_GATE_THRESHOLD;
      }
      else if (strcmp(str_command,"gate-count")==0){
        return FILTER_PARAM_GATE_COUNT;
      }
      else if (strcmp(str_command,"gate-summary")==0){
        return FILTER_PARAM_GATE_SUMMARY;
      }
      ...
    }

    After binding the name, tell ControSocketM how to interpret the command's parameters. There are three types of parameters that can be passed to a filter command: bool, numeric, and blob. Bool consists of either 0 for false and anything else for true, or the litterals "true" or "false"; numeric consists of an integer; and blob consists of a comma-separated collection of integers. To instruct ControlSocketM how to interpret each commands parameters, list the identifier in the build_attr_block function under the appropriate case in the switch statement.

    attr_block_t * build_attr_block(char *buf, uint8_t len, addr_t *addr){
      ...
      switch(cmd){
      ...
      // --- BOOL ---
      case FILTER_PARAM_GATE_ENABLE:
      case FILTER_PARAM_GATE_SUMMARY:
      ...
      // --- NUMERIC ---
      ...
      case FILTER_PARAM_GATE_THRESHOLD:
      case FILTER_PARAM_GATE_COUNT:
      ...
      // --- NONE ---
      case FILTER_PARAM_GATE_RESET:
      // --- BLOB ---
      ...
    }

Filters at Run Time

Filters are scheduled to run synchronously as TinyOS tasks from the TinyOS task scheduler. The Sampler starts the filter chain data processing and as long as no Filter post tasks to process data, the data will be pushed through all the Filters uninterrupted.

Filter Variable Actuation

The filter variables can be modified remotely by sending commands over a socket. The VanGo microserver application has a built-in server that listens for incoming connections on the default port 8003. Once a connection is established, simple ASCII command strings will trigger the microserver to actuate mote filter parameters. The commands are defined in the ControlSocketM.nc module and follow a common format:

Example: vango/tos/platform/emstar/ControlSocketM.nc
...
/*
  msg format :
  destination <colon> command-1 <semi-colon> command-2  ...
  command format (whitespace delimited):
  command [<space>|<tab>]+ value
  value format (a value containing more than one field is comma delimited):
  value [,next_value]*

  recognized non-numeric destinations: local, broadcast
  recognized non-numeric values: true, false

  values are 16-bit unsigned

  e.g.:
 'broadcast: fir-set-vector 4,12,1,0,3; gate-enable true\n'
*/
...

Other than using conventional socket programming, commands can be sent to the microserver using tools like telnet or netcat.

Example: Command Session
broadcast: gate-enable false
2: gate-enable true; gate-threshold 150
4: gate-enable true; gate-threshold 300; gate-summary true

ControlSocketM runs an EmTOS socket server on port 8000 used to receive the ASCII commands. The received commands are translated into attributes and then into attribute blocks, packetized, and sent over the radio to the motes. The attributes are composed of the attribute type and the attribute value. The attribute type, in this case, is the command number as defined in the filter_param_t enumeration in the MotephonesTypes.nc file and the attribute value is the value passed with the command.

Note In the current implementation of attributes, there is a limit to the number of bytes attributes can take up in total. The max is defined as the MAX_ATTR_BYTES #define in the MotephonesTypes.nc file.
Example: vango/tos/lib/MotephonesTypes.nc
...
typedef struct attr_s {
  uint8_t type;
  uint8_t length;
  uint8_t value[0];
} __attribute__ ((packed)) attr_t;

struct attr_block_s {
  uint8_t current;
  uint8_t count;
  uint8_t attrs[MAX_ATTR_BYTES];
} __attribute__ ((packed));
...

All received packets are forwarded to ControlM; ControlM reads the attributes in the attribute block and calls the appropriate filter function to change the value of the filter. ControlM calls its FilterControl setValue function with the attribute type as the command identifier and the attribute as the value to the function.

Example: vango/tos/lib/ControlM.nc
...
call FilterControl.setValue[attr->type](*((uint16_t *)(attr->value)));
...