Jump to content

Programming Reference:VisualizationContainerDemo Signal Processing

From BCI2000 Wiki

Location

src/contrib/SignalProcessing/VisualizationContainerDemo

Synopsis

The VisualizationContainerDemo signal processing module demonstrates how to maintain a number of visualization displays within a container window. It is similar to Programming Reference:ComplexVisualizationDemo Signal Processing but does not render graphics itself; rather, it maintains a group of visualization windows within a container window, and sends data to these.

Inheritance

The VisualizationContainerDemo signal processing filter derives from GenericFilter.

Function

The VisualizationContainerDemo computes pairwise determination coefficients (squared correlation, r2 values) between its input channels. Determination coefficients are sent to a group of visualizations in the Operator module.

Operator visualizations are contained in a parent window, and arranged in form of a triangular matrix, with each window appearing at the place of its associated correlation matrix element:

Implementation

Each visualization window is implemented as a VisualizationObject that contains a Visualization and a Computation object. Whenever a new block of data arrives, computation is done for the individual window's pair of channels in the main thread. Then, the result is sent to the Operator window inside a GenericSignal object. As both computation effort and the amount of data is limited, and to keep the example code simple, no multithreading is involved in this demo. (For an example that involves multithreading, see Programming Reference:ComplexVisualizationDemo Signal Processing.)

Declaration of internal variables

The code example uses a pointer to an internal private struct to hide implementation details from the outer header file of the filter class (PIMPL idiom).

A VisualizationObject class is declared that contains all members necessary to compute an r2 value, and to send it to a visualization window.

struct VisualizationContainerDemoFilter::Private
{
    GenericVisualization mContainerVis;

    class VisualizationObject;
    std::vector<VisualizationObject *> mVisualizations;

    ~Private()
    {
        destroyVisualizations();
    }
    // Creates visualization objects for pairs of channels.
    void createVisualizations(const SignalProperties &);
    // Destroys all visualization objects.
    void destroyVisualizations();
    // Sets visualizations to their initial state.
    void resetVisualizations();
    // Computes data values, and updates visualization windows.
    void updateVisualizations(const GenericSignal &);

    class VisualizationObject
    {
      public:
        VisualizationObject(const std::string &visID);
        ~VisualizationObject();
        void setTitle(const std::string &);
        void configureSignal(const SignalProperties&);
        void setPosition(int row, int col);
        void reset();
        void update(const GenericSignal &);

        struct Computation
        {
            int inputCh1, inputCh2;
            double result;
            // run() is called whenever a new signal arrives.
            void run(const GenericSignal &);
        } mComputation;

      private:
        GenericVisualization mVis;
        GenericSignal mSignal;
        PhysicalUnit mSampleUnit, mValueUnit;
        std::string mTitle;
        int mRow, mCol;
    };
};

VisualizationObject::update()

This function computes the correlation matrix element, and sends it to the visualization display wrapped into a GenericSignal object.

void
VisualizationContainerDemoFilter::Private::VisualizationObject::update(const GenericSignal& Input)
{
    mComputation.run(Input);
    mSignal(0, 0) = mComputation.result;
    mVis.Send(mSignal);
}

VisualizationObject::reset()

This function resets the visualization window's position and size before sending an empty signal to the Operator (this will enforce creation of the visualization display on the Operator side).

void
VisualizationContainerDemoFilter::Private::VisualizationObject::reset()
{
    // reset position and size
    mVis.Send(CfgID::PlacementVis, "CNTR");
    mVis.Send(CfgID::PlacementRow, mRow + 1); // rows are one-based
    mVis.Send(CfgID::PlacementCol, mCol + 1); // columns are one-based
    mVis.Send(CfgID::Visible, true);
    mVis.Send(CfgID::FixedScale, true);
    mVis.Send(CfgID::MinValue, mValueUnit.RawMin());
    mVis.Send(CfgID::MaxValue, mValueUnit.RawMax() * 1.1);
    mVis.Send(CfgID::ShowStatusBar, false);
    mVis.Send(CfgID::SampleUnit, mSampleUnit.RawToPhysical(1));
    mVis.Send(CfgID::ShowYTicks, false);
    LabelList markers;
    markers.push_back(Label(mValueUnit.PhysicalToRaw("1"), "1|red|-|0.5"));
    markers.push_back(Label(mValueUnit.PhysicalToRaw("0.5"), "0.5|green|-.|0.5"));
    mVis.Send(CfgID::YAxisMarkers, markers);
    mVis.Send(CfgID::WindowTitle, mTitle);
    mVis.Send(CfgID::WindowFrame, true);
    mVis.Send(GenericSignal());
}

Process() function

The Process() function calls updateVisualizations(), which, in turn, will call VisualizationObject::update() for each visualization object contained in mVisualizations.

void VisualizationContainerDemoFilter::Process(
  const GenericSignal& Input,
        GenericSignal& Output)
{
    Output = Input;
    p->updateVisualizations(Input);
}

StartRun() and StopRun()

These reset all visualization objects.

void 
VisualizationContainerDemoFilter::StartRun()
{
    p->resetVisualizations();
}

void
VisualizationContainerDemoFilter::StopRun()
{
    p->resetVisualizations();
}

See also

Programming Reference:GraphDisplay Class, Programming Reference:GenericVisualization Class, Programming Reference:VisualizationDemo Signal Processing, Programming Reference:ComplexVisualizationDemo Signal Processing, Technical Reference:Visualization Properties