1072 lines
22 KiB
C++
1072 lines
22 KiB
C++
/*
|
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "MatlabPlot.h"
|
|
#ifdef MATLAB
|
|
#include "engine.h"
|
|
#endif
|
|
#include "event_wrapper.h"
|
|
#include "thread_wrapper.h"
|
|
#include "critical_section_wrapper.h"
|
|
#include "tick_util.h"
|
|
|
|
#include <sstream>
|
|
#include <algorithm>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
using namespace webrtc;
|
|
|
|
#ifdef MATLAB
|
|
MatlabEngine eng;
|
|
|
|
MatlabLine::MatlabLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/)
|
|
:
|
|
_xArray(NULL),
|
|
_yArray(NULL),
|
|
_maxLen(maxLen),
|
|
_plotAttribute(),
|
|
_name()
|
|
{
|
|
if (_maxLen > 0)
|
|
{
|
|
_xArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL);
|
|
_yArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL);
|
|
}
|
|
|
|
if (plotAttrib)
|
|
{
|
|
_plotAttribute = plotAttrib;
|
|
}
|
|
|
|
if (name)
|
|
{
|
|
_name = name;
|
|
}
|
|
}
|
|
|
|
MatlabLine::~MatlabLine()
|
|
{
|
|
if (_xArray != NULL)
|
|
{
|
|
mxDestroyArray(_xArray);
|
|
}
|
|
if (_yArray != NULL)
|
|
{
|
|
mxDestroyArray(_yArray);
|
|
}
|
|
}
|
|
|
|
void MatlabLine::Append(double x, double y)
|
|
{
|
|
if (_maxLen > 0 && _xData.size() > static_cast<WebRtc_UWord32>(_maxLen))
|
|
{
|
|
_xData.resize(_maxLen);
|
|
_yData.resize(_maxLen);
|
|
}
|
|
|
|
_xData.push_front(x);
|
|
_yData.push_front(y);
|
|
}
|
|
|
|
|
|
// append y-data with running integer index as x-data
|
|
void MatlabLine::Append(double y)
|
|
{
|
|
if (_xData.empty())
|
|
{
|
|
// first element is index 0
|
|
Append(0, y);
|
|
}
|
|
else
|
|
{
|
|
// take last x-value and increment
|
|
double temp = _xData.back(); // last x-value
|
|
Append(temp + 1, y);
|
|
}
|
|
}
|
|
|
|
|
|
void MatlabLine::SetMaxLen(int maxLen)
|
|
{
|
|
if (maxLen <= 0)
|
|
{
|
|
// means no maxLen
|
|
_maxLen = -1;
|
|
}
|
|
else
|
|
{
|
|
_maxLen = maxLen;
|
|
|
|
if (_xArray != NULL)
|
|
{
|
|
mxDestroyArray(_xArray);
|
|
mxDestroyArray(_yArray);
|
|
}
|
|
_xArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL);
|
|
_yArray = mxCreateDoubleMatrix(1, _maxLen, mxREAL);
|
|
|
|
maxLen = ((unsigned int)maxLen <= _xData.size()) ? maxLen : (int)_xData.size();
|
|
_xData.resize(maxLen);
|
|
_yData.resize(maxLen);
|
|
|
|
//// reserve the right amount of memory
|
|
//_xData.reserve(_maxLen);
|
|
//_yData.reserve(_maxLen);
|
|
}
|
|
}
|
|
|
|
void MatlabLine::SetAttribute(char *plotAttrib)
|
|
{
|
|
_plotAttribute = plotAttrib;
|
|
}
|
|
|
|
void MatlabLine::SetName(char *name)
|
|
{
|
|
_name = name;
|
|
}
|
|
|
|
void MatlabLine::GetPlotData(mxArray** xData, mxArray** yData)
|
|
{
|
|
// Make sure we have enough Matlab allocated memory.
|
|
// Assuming both arrays (x and y) are of the same size.
|
|
if (_xData.empty())
|
|
{
|
|
return; // No data
|
|
}
|
|
unsigned int size = 0;
|
|
if (_xArray != NULL)
|
|
{
|
|
size = (unsigned int)mxGetNumberOfElements(_xArray);
|
|
}
|
|
if (size < _xData.size())
|
|
{
|
|
if (_xArray != NULL)
|
|
{
|
|
mxDestroyArray(_xArray);
|
|
mxDestroyArray(_yArray);
|
|
}
|
|
_xArray = mxCreateDoubleMatrix(1, _xData.size(), mxREAL);
|
|
_yArray = mxCreateDoubleMatrix(1, _yData.size(), mxREAL);
|
|
}
|
|
|
|
if (!_xData.empty())
|
|
{
|
|
double* x = mxGetPr(_xArray);
|
|
|
|
std::list<double>::iterator it = _xData.begin();
|
|
|
|
for (int i = 0; it != _xData.end(); it++, i++)
|
|
{
|
|
x[i] = *it;
|
|
}
|
|
}
|
|
|
|
if (!_yData.empty())
|
|
{
|
|
double* y = mxGetPr(_yArray);
|
|
|
|
std::list<double>::iterator it = _yData.begin();
|
|
|
|
for (int i = 0; it != _yData.end(); it++, i++)
|
|
{
|
|
y[i] = *it;
|
|
}
|
|
}
|
|
*xData = _xArray;
|
|
*yData = _yArray;
|
|
}
|
|
|
|
std::string MatlabLine::GetXName()
|
|
{
|
|
std::ostringstream xString;
|
|
xString << "x_" << _name;
|
|
return xString.str();
|
|
}
|
|
|
|
std::string MatlabLine::GetYName()
|
|
{
|
|
std::ostringstream yString;
|
|
yString << "y_" << _name;
|
|
return yString.str();
|
|
}
|
|
|
|
std::string MatlabLine::GetPlotString()
|
|
{
|
|
|
|
std::ostringstream s;
|
|
|
|
if (_xData.size() == 0)
|
|
{
|
|
s << "[0 1], [0 1]"; // To get an empty plot
|
|
}
|
|
else
|
|
{
|
|
s << GetXName() << "(1:" << _xData.size() << "),";
|
|
s << GetYName() << "(1:" << _yData.size() << ")";
|
|
}
|
|
|
|
s << ", '";
|
|
s << _plotAttribute;
|
|
s << "'";
|
|
|
|
return s.str();
|
|
}
|
|
|
|
std::string MatlabLine::GetRefreshString()
|
|
{
|
|
std::ostringstream s;
|
|
|
|
if (_xData.size() > 0)
|
|
{
|
|
s << "set(h,'xdata',"<< GetXName() <<"(1:" << _xData.size() << "),'ydata',"<< GetYName() << "(1:" << _yData.size() << "));";
|
|
}
|
|
else
|
|
{
|
|
s << "set(h,'xdata',[NaN],'ydata',[NaN]);";
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
std::string MatlabLine::GetLegendString()
|
|
{
|
|
return ("'" + _name + "'");
|
|
}
|
|
|
|
bool MatlabLine::hasLegend()
|
|
{
|
|
return (!_name.empty());
|
|
}
|
|
|
|
|
|
// remove data points, but keep attributes
|
|
void MatlabLine::Reset()
|
|
{
|
|
_xData.clear();
|
|
_yData.clear();
|
|
}
|
|
|
|
|
|
void MatlabLine::UpdateTrendLine(MatlabLine * sourceData, double slope, double offset)
|
|
{
|
|
Reset(); // reset data, not attributes and name
|
|
|
|
double thexMin = sourceData->xMin();
|
|
double thexMax = sourceData->xMax();
|
|
Append(thexMin, thexMin * slope + offset);
|
|
Append(thexMax, thexMax * slope + offset);
|
|
}
|
|
|
|
double MatlabLine::xMin()
|
|
{
|
|
if (!_xData.empty())
|
|
{
|
|
std::list<double>::iterator theStart = _xData.begin();
|
|
std::list<double>::iterator theEnd = _xData.end();
|
|
return(*min_element(theStart, theEnd));
|
|
}
|
|
return (0.0);
|
|
}
|
|
|
|
double MatlabLine::xMax()
|
|
{
|
|
if (!_xData.empty())
|
|
{
|
|
std::list<double>::iterator theStart = _xData.begin();
|
|
std::list<double>::iterator theEnd = _xData.end();
|
|
return(*max_element(theStart, theEnd));
|
|
}
|
|
return (0.0);
|
|
}
|
|
|
|
double MatlabLine::yMin()
|
|
{
|
|
if (!_yData.empty())
|
|
{
|
|
std::list<double>::iterator theStart = _yData.begin();
|
|
std::list<double>::iterator theEnd = _yData.end();
|
|
return(*min_element(theStart, theEnd));
|
|
}
|
|
return (0.0);
|
|
}
|
|
|
|
double MatlabLine::yMax()
|
|
{
|
|
if (!_yData.empty())
|
|
{
|
|
std::list<double>::iterator theStart = _yData.begin();
|
|
std::list<double>::iterator theEnd = _yData.end();
|
|
return(*max_element(theStart, theEnd));
|
|
}
|
|
return (0.0);
|
|
}
|
|
|
|
|
|
|
|
MatlabTimeLine::MatlabTimeLine(int horizonSeconds /*= -1*/, const char *plotAttrib /*= NULL*/,
|
|
const char *name /*= NULL*/,
|
|
WebRtc_Word64 refTimeMs /* = -1*/)
|
|
:
|
|
_timeHorizon(horizonSeconds),
|
|
MatlabLine(-1, plotAttrib, name) // infinite number of elements
|
|
{
|
|
if (refTimeMs < 0)
|
|
_refTimeMs = TickTime::MillisecondTimestamp();
|
|
else
|
|
_refTimeMs = refTimeMs;
|
|
}
|
|
|
|
void MatlabTimeLine::Append(double y)
|
|
{
|
|
MatlabLine::Append(static_cast<double>(TickTime::MillisecondTimestamp() - _refTimeMs) / 1000.0, y);
|
|
|
|
PurgeOldData();
|
|
}
|
|
|
|
|
|
void MatlabTimeLine::PurgeOldData()
|
|
{
|
|
if (_timeHorizon > 0)
|
|
{
|
|
// remove old data
|
|
double historyLimit = static_cast<double>(TickTime::MillisecondTimestamp() - _refTimeMs) / 1000.0
|
|
- _timeHorizon; // remove data points older than this
|
|
|
|
std::list<double>::reverse_iterator ritx = _xData.rbegin();
|
|
WebRtc_UWord32 removeCount = 0;
|
|
while (ritx != _xData.rend())
|
|
{
|
|
if (*ritx >= historyLimit)
|
|
{
|
|
break;
|
|
}
|
|
ritx++;
|
|
removeCount++;
|
|
}
|
|
if (removeCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// remove the range [begin, it).
|
|
//if (removeCount > 10)
|
|
//{
|
|
// printf("Removing %lu elements\n", removeCount);
|
|
//}
|
|
_xData.resize(_xData.size() - removeCount);
|
|
_yData.resize(_yData.size() - removeCount);
|
|
}
|
|
}
|
|
|
|
|
|
WebRtc_Word64 MatlabTimeLine::GetRefTime()
|
|
{
|
|
return(_refTimeMs);
|
|
}
|
|
|
|
|
|
|
|
|
|
MatlabPlot::MatlabPlot()
|
|
:
|
|
_figHandle(-1),
|
|
_smartAxis(false),
|
|
_critSect(CriticalSectionWrapper::CreateCriticalSection()),
|
|
_timeToPlot(false),
|
|
_plotting(false),
|
|
_enabled(true),
|
|
_firstPlot(true),
|
|
_legendEnabled(true),
|
|
_donePlottingEvent(EventWrapper::Create())
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
_xlim[0] = 0;
|
|
_xlim[1] = 0;
|
|
_ylim[0] = 0;
|
|
_ylim[1] = 0;
|
|
|
|
#ifdef PLOT_TESTING
|
|
_plotStartTime = -1;
|
|
_plotDelay = 0;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
MatlabPlot::~MatlabPlot()
|
|
{
|
|
_critSect->Enter();
|
|
|
|
// delete all line objects
|
|
while (!_line.empty())
|
|
{
|
|
delete *(_line.end() - 1);
|
|
_line.pop_back();
|
|
}
|
|
|
|
delete _critSect;
|
|
delete _donePlottingEvent;
|
|
}
|
|
|
|
|
|
int MatlabPlot::AddLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
MatlabLine *newLine = new MatlabLine(maxLen, plotAttrib, name);
|
|
_line.push_back(newLine);
|
|
|
|
return (static_cast<int>(_line.size() - 1)); // index of newly inserted line
|
|
}
|
|
|
|
|
|
int MatlabPlot::AddTimeLine(int maxLen /*= -1*/, const char *plotAttrib /*= NULL*/, const char *name /*= NULL*/,
|
|
WebRtc_Word64 refTimeMs /*= -1*/)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
MatlabTimeLine *newLine = new MatlabTimeLine(maxLen, plotAttrib, name, refTimeMs);
|
|
_line.push_back(newLine);
|
|
|
|
return (static_cast<int>(_line.size() - 1)); // index of newly inserted line
|
|
}
|
|
|
|
|
|
int MatlabPlot::GetLineIx(const char *name)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// search the list for a matching line name
|
|
std::vector<MatlabLine*>::iterator it = _line.begin();
|
|
bool matchFound = false;
|
|
int lineIx = 0;
|
|
|
|
for (; it != _line.end(); it++, lineIx++)
|
|
{
|
|
if ((*it)->_name == name)
|
|
{
|
|
matchFound = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (matchFound)
|
|
{
|
|
return (lineIx);
|
|
}
|
|
else
|
|
{
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
void MatlabPlot::Append(int lineIndex, double x, double y)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// sanity for index
|
|
if (lineIndex < 0 || lineIndex >= static_cast<int>(_line.size()))
|
|
{
|
|
throw "Line index out of range";
|
|
exit(1);
|
|
}
|
|
|
|
return (_line[lineIndex]->Append(x, y));
|
|
}
|
|
|
|
|
|
void MatlabPlot::Append(int lineIndex, double y)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// sanity for index
|
|
if (lineIndex < 0 || lineIndex >= static_cast<int>(_line.size()))
|
|
{
|
|
throw "Line index out of range";
|
|
exit(1);
|
|
}
|
|
|
|
return (_line[lineIndex]->Append(y));
|
|
}
|
|
|
|
|
|
int MatlabPlot::Append(const char *name, double x, double y)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// search the list for a matching line name
|
|
int lineIx = GetLineIx(name);
|
|
|
|
if (lineIx < 0) //(!matchFound)
|
|
{
|
|
// no match; append new line
|
|
lineIx = AddLine(-1, NULL, name);
|
|
}
|
|
|
|
// append data to line
|
|
Append(lineIx, x, y);
|
|
return (lineIx);
|
|
}
|
|
|
|
int MatlabPlot::Append(const char *name, double y)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// search the list for a matching line name
|
|
int lineIx = GetLineIx(name);
|
|
|
|
if (lineIx < 0) //(!matchFound)
|
|
{
|
|
// no match; append new line
|
|
lineIx = AddLine(-1, NULL, name);
|
|
}
|
|
|
|
// append data to line
|
|
Append(lineIx, y);
|
|
return (lineIx);
|
|
}
|
|
|
|
int MatlabPlot::Length(char *name)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int ix = GetLineIx(name);
|
|
if (ix >= 0)
|
|
{
|
|
return (static_cast<int>(_line[ix]->_xData.size()));
|
|
}
|
|
else
|
|
{
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
|
|
void MatlabPlot::SetPlotAttribute(char *name, char *plotAttrib)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (!_enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int lineIx = GetLineIx(name);
|
|
|
|
if (lineIx >= 0)
|
|
{
|
|
_line[lineIx]->SetAttribute(plotAttrib);
|
|
}
|
|
}
|
|
|
|
// Must be called under critical section _critSect
|
|
void MatlabPlot::UpdateData(Engine* ep)
|
|
{
|
|
if (!_enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (std::vector<MatlabLine*>::iterator it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
mxArray* xData = NULL;
|
|
mxArray* yData = NULL;
|
|
(*it)->GetPlotData(&xData, &yData);
|
|
if (xData != NULL)
|
|
{
|
|
std::string xName = (*it)->GetXName();
|
|
std::string yName = (*it)->GetYName();
|
|
_critSect->Leave();
|
|
#ifdef MATLAB6
|
|
mxSetName(xData, xName.c_str());
|
|
mxSetName(yData, yName.c_str());
|
|
engPutArray(ep, xData);
|
|
engPutArray(ep, yData);
|
|
#else
|
|
int ret = engPutVariable(ep, xName.c_str(), xData);
|
|
assert(ret == 0);
|
|
ret = engPutVariable(ep, yName.c_str(), yData);
|
|
assert(ret == 0);
|
|
#endif
|
|
_critSect->Enter();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool MatlabPlot::GetPlotCmd(std::ostringstream & cmd, Engine* ep)
|
|
{
|
|
_critSect->Enter();
|
|
|
|
if (!DataAvailable())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (_firstPlot)
|
|
{
|
|
GetPlotCmd(cmd);
|
|
_firstPlot = false;
|
|
}
|
|
else
|
|
{
|
|
GetRefreshCmd(cmd);
|
|
}
|
|
|
|
UpdateData(ep);
|
|
|
|
_critSect->Leave();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Call inside critsect
|
|
void MatlabPlot::GetPlotCmd(std::ostringstream & cmd)
|
|
{
|
|
// we have something to plot
|
|
// empty the stream
|
|
cmd.str(""); // (this seems to be the only way)
|
|
|
|
cmd << "figure; h" << _figHandle << "= plot(";
|
|
|
|
// first line
|
|
std::vector<MatlabLine*>::iterator it = _line.begin();
|
|
cmd << (*it)->GetPlotString();
|
|
|
|
it++;
|
|
|
|
// remaining lines
|
|
for (; it != _line.end(); it++)
|
|
{
|
|
cmd << ", ";
|
|
cmd << (*it)->GetPlotString();
|
|
}
|
|
|
|
cmd << "); ";
|
|
|
|
if (_legendEnabled)
|
|
{
|
|
GetLegendCmd(cmd);
|
|
}
|
|
|
|
if (_smartAxis)
|
|
{
|
|
double xMin = _xlim[0];
|
|
double xMax = _xlim[1];
|
|
double yMax = _ylim[1];
|
|
for (std::vector<MatlabLine*>::iterator it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
xMax = std::max(xMax, (*it)->xMax());
|
|
xMin = std::min(xMin, (*it)->xMin());
|
|
|
|
yMax = std::max(yMax, (*it)->yMax());
|
|
yMax = std::max(yMax, fabs((*it)->yMin()));
|
|
}
|
|
_xlim[0] = xMin;
|
|
_xlim[1] = xMax;
|
|
_ylim[0] = -yMax;
|
|
_ylim[1] = yMax;
|
|
|
|
cmd << "axis([" << _xlim[0] << ", " << _xlim[1] << ", " << _ylim[0] << ", " << _ylim[1] << "]);";
|
|
}
|
|
|
|
int i=1;
|
|
for (it = _line.begin(); it != _line.end(); i++, it++)
|
|
{
|
|
cmd << "set(h" << _figHandle << "(" << i << "), 'Tag', " << (*it)->GetLegendString() << ");";
|
|
}
|
|
}
|
|
|
|
// Call inside critsect
|
|
void MatlabPlot::GetRefreshCmd(std::ostringstream & cmd)
|
|
{
|
|
cmd.str(""); // (this seems to be the only way)
|
|
std::vector<MatlabLine*>::iterator it = _line.begin();
|
|
for (it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
cmd << "h = findobj(0, 'Tag', " << (*it)->GetLegendString() << ");";
|
|
cmd << (*it)->GetRefreshString();
|
|
}
|
|
//if (_legendEnabled)
|
|
//{
|
|
// GetLegendCmd(cmd);
|
|
//}
|
|
}
|
|
|
|
void MatlabPlot::GetLegendCmd(std::ostringstream & cmd)
|
|
{
|
|
std::vector<MatlabLine*>::iterator it = _line.begin();
|
|
bool anyLegend = false;
|
|
for (; it != _line.end(); it++)
|
|
{
|
|
anyLegend = anyLegend || (*it)->hasLegend();
|
|
}
|
|
if (anyLegend)
|
|
{
|
|
// create the legend
|
|
|
|
cmd << "legend(h" << _figHandle << ",{";
|
|
|
|
|
|
// iterate lines
|
|
int i = 0;
|
|
for (std::vector<MatlabLine*>::iterator it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
if (i > 0)
|
|
{
|
|
cmd << ", ";
|
|
}
|
|
cmd << (*it)->GetLegendString();
|
|
i++;
|
|
}
|
|
|
|
cmd << "}, 2); "; // place legend in upper-left corner
|
|
}
|
|
}
|
|
|
|
// Call inside critsect
|
|
bool MatlabPlot::DataAvailable()
|
|
{
|
|
if (!_enabled)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (std::vector<MatlabLine*>::iterator it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
(*it)->PurgeOldData();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MatlabPlot::Plot()
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
_timeToPlot = true;
|
|
|
|
#ifdef PLOT_TESTING
|
|
_plotStartTime = TickTime::MillisecondTimestamp();
|
|
#endif
|
|
}
|
|
|
|
|
|
void MatlabPlot::Reset()
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
_enabled = true;
|
|
|
|
for (std::vector<MatlabLine*>::iterator it = _line.begin(); it != _line.end(); it++)
|
|
{
|
|
(*it)->Reset();
|
|
}
|
|
|
|
}
|
|
|
|
void MatlabPlot::SetFigHandle(int handle)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (handle > 0)
|
|
_figHandle = handle;
|
|
}
|
|
|
|
bool
|
|
MatlabPlot::TimeToPlot()
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
return _enabled && _timeToPlot;
|
|
}
|
|
|
|
void
|
|
MatlabPlot::Plotting()
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
_plotting = true;
|
|
}
|
|
|
|
void
|
|
MatlabPlot::DonePlotting()
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
_timeToPlot = false;
|
|
_plotting = false;
|
|
_donePlottingEvent->Set();
|
|
}
|
|
|
|
void
|
|
MatlabPlot::DisablePlot()
|
|
{
|
|
_critSect->Enter();
|
|
while (_plotting)
|
|
{
|
|
_critSect->Leave();
|
|
_donePlottingEvent->Wait(WEBRTC_EVENT_INFINITE);
|
|
_critSect->Enter();
|
|
}
|
|
_enabled = false;
|
|
}
|
|
|
|
int MatlabPlot::MakeTrend(const char *sourceName, const char *trendName, double slope, double offset, const char *plotAttrib)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
int sourceIx;
|
|
int trendIx;
|
|
|
|
sourceIx = GetLineIx(sourceName);
|
|
if (sourceIx < 0)
|
|
{
|
|
// could not find source
|
|
return (-1);
|
|
}
|
|
|
|
trendIx = GetLineIx(trendName);
|
|
if (trendIx < 0)
|
|
{
|
|
// no trend found; add new line
|
|
trendIx = AddLine(2 /*maxLen*/, plotAttrib, trendName);
|
|
}
|
|
|
|
_line[trendIx]->UpdateTrendLine(_line[sourceIx], slope, offset);
|
|
|
|
return (trendIx);
|
|
|
|
}
|
|
|
|
|
|
MatlabEngine::MatlabEngine()
|
|
:
|
|
_critSect(CriticalSectionWrapper::CreateCriticalSection()),
|
|
_eventPtr(NULL),
|
|
_plotThread(NULL),
|
|
_running(false),
|
|
_numPlots(0)
|
|
{
|
|
_eventPtr = EventWrapper::Create();
|
|
|
|
_plotThread = ThreadWrapper::CreateThread(MatlabEngine::PlotThread, this, kLowPriority, "MatlabPlot");
|
|
|
|
if (_plotThread == NULL)
|
|
{
|
|
throw "Unable to start MatlabEngine thread";
|
|
exit(1);
|
|
}
|
|
|
|
_running = true;
|
|
|
|
unsigned int tid;
|
|
_plotThread->Start(tid);
|
|
|
|
}
|
|
|
|
MatlabEngine::~MatlabEngine()
|
|
{
|
|
_critSect->Enter();
|
|
|
|
if (_plotThread)
|
|
{
|
|
_plotThread->SetNotAlive();
|
|
_running = false;
|
|
_eventPtr->Set();
|
|
|
|
while (!_plotThread->Stop())
|
|
{
|
|
;
|
|
}
|
|
|
|
delete _plotThread;
|
|
}
|
|
|
|
_plots.clear();
|
|
|
|
_plotThread = NULL;
|
|
|
|
delete _eventPtr;
|
|
_eventPtr = NULL;
|
|
|
|
_critSect->Leave();
|
|
delete _critSect;
|
|
|
|
}
|
|
|
|
MatlabPlot * MatlabEngine::NewPlot(MatlabPlot *newPlot)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
//MatlabPlot *newPlot = new MatlabPlot();
|
|
|
|
if (newPlot)
|
|
{
|
|
newPlot->SetFigHandle(++_numPlots); // first plot is number 1
|
|
_plots.push_back(newPlot);
|
|
}
|
|
|
|
return (newPlot);
|
|
|
|
}
|
|
|
|
|
|
void MatlabEngine::DeletePlot(MatlabPlot *plot)
|
|
{
|
|
CriticalSectionScoped cs(*_critSect);
|
|
|
|
if (plot == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::vector<MatlabPlot *>::iterator it;
|
|
for (it = _plots.begin(); it < _plots.end(); it++)
|
|
{
|
|
if (plot == *it)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert (plot == *it);
|
|
|
|
(*it)->DisablePlot();
|
|
|
|
_plots.erase(it);
|
|
--_numPlots;
|
|
|
|
delete plot;
|
|
}
|
|
|
|
|
|
bool MatlabEngine::PlotThread(void *obj)
|
|
{
|
|
if (!obj)
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
MatlabEngine *eng = (MatlabEngine *) obj;
|
|
|
|
Engine *ep = engOpen(NULL);
|
|
if (!ep)
|
|
{
|
|
throw "Cannot open Matlab engine";
|
|
return (false);
|
|
}
|
|
|
|
engSetVisible(ep, true);
|
|
engEvalString(ep, "close all;");
|
|
|
|
while (eng->_running)
|
|
{
|
|
eng->_critSect->Enter();
|
|
|
|
// iterate through all plots
|
|
for (unsigned int ix = 0; ix < eng->_plots.size(); ix++)
|
|
{
|
|
MatlabPlot *plot = eng->_plots[ix];
|
|
if (plot->TimeToPlot())
|
|
{
|
|
plot->Plotting();
|
|
eng->_critSect->Leave();
|
|
std::ostringstream cmd;
|
|
|
|
if (engEvalString(ep, cmd.str().c_str()))
|
|
{
|
|
// engine dead
|
|
return (false);
|
|
}
|
|
|
|
// empty the stream
|
|
cmd.str(""); // (this seems to be the only way)
|
|
if (plot->GetPlotCmd(cmd, ep))
|
|
{
|
|
// things to plot, we have already accessed what we need in the plot
|
|
plot->DonePlotting();
|
|
|
|
WebRtc_Word64 start = TickTime::MillisecondTimestamp();
|
|
// plot it
|
|
int ret = engEvalString(ep, cmd.str().c_str());
|
|
printf("time=%I64i\n", TickTime::MillisecondTimestamp() - start);
|
|
if (ret)
|
|
{
|
|
// engine dead
|
|
return (false);
|
|
}
|
|
|
|
#ifdef PLOT_TESTING
|
|
if(plot->_plotStartTime >= 0)
|
|
{
|
|
plot->_plotDelay = TickTime::MillisecondTimestamp() - plot->_plotStartTime;
|
|
plot->_plotStartTime = -1;
|
|
}
|
|
#endif
|
|
}
|
|
eng->_critSect->Enter();
|
|
}
|
|
}
|
|
|
|
eng->_critSect->Leave();
|
|
// wait a while
|
|
eng->_eventPtr->Wait(66); // 33 ms
|
|
}
|
|
|
|
if (ep)
|
|
{
|
|
engClose(ep);
|
|
ep = NULL;
|
|
}
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
#endif // MATLAB
|