-
John Tapsell authoredJohn Tapsell authored
LinechartPlot.cc 25.67 KiB
/*=====================================================================
======================================================================*/
/**
* @file
* @brief Line chart for vehicle data
*
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include "float.h"
#include <QDebug>
#include <QTimer>
#include <qwt_plot.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_grid.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_zoomer.h>
#include <qwt_symbol.h>
#include <LinechartPlot.h>
#include <MG.h>
#include <QPaintEngine>
#include "ChartPlot.h"
#include "QGC.h"
/**
* @brief The default constructor
*
* @param parent The parent widget
* @param interval The maximum interval for which data is stored (default: 30 minutes) in milliseconds
**/
LinechartPlot::LinechartPlot(QWidget *parent, int plotid, quint64 interval):
ChartPlot(parent),
minTime(0),
lastTime(0),
maxTime(100),
maxInterval(MAX_STORAGE_INTERVAL),
plotPosition(0),
timeScaleStep(DEFAULT_SCALE_INTERVAL), // 10 seconds
automaticScrollActive(false),
m_active(false),
m_groundTime(true),
d_data(NULL),
d_curve(NULL)
{
this->plotid = plotid;
this->plotInterval = interval;
maxValue = -DBL_MAX;
minValue = DBL_MAX;
//lastMaxTimeAdded = QTime();
data = QMap<QString, TimeSeriesData*>();
scaleMaps = QMap<QString, QwtScaleMap*>();
yScaleEngine = new QwtLinearScaleEngine();
setAxisScaleEngine(QwtPlot::yLeft, yScaleEngine);
// Set left scale
//setAxisOptions(QwtPlot::yLeft, QwtAutoScale::Logarithmic);
// Set bottom scale
setAxisScaleDraw(QwtPlot::xBottom, new TimeScaleDraw());
setAxisLabelRotation(QwtPlot::xBottom, -25.0);
setAxisLabelAlignment(QwtPlot::xBottom, Qt::AlignLeft | Qt::AlignBottom);
// Add some space on the left and right side of the scale to prevent flickering
QwtScaleWidget* bottomScaleWidget = axisWidget(QwtPlot::xBottom);
const int fontMetricsX = QFontMetrics(bottomScaleWidget->font()).height();
bottomScaleWidget->setMinBorderDist(fontMetricsX * 2, fontMetricsX / 2);
plotLayout()->setAlignCanvasToScales(true);
// Start QTimer for plot update
updateTimer = new QTimer(this);
connect(updateTimer, SIGNAL(timeout()), this, SLOT(paintRealtime()));
//updateTimer->start(DEFAULT_REFRESH_RATE);
connect(&timeoutTimer, SIGNAL(timeout()), this, SLOT(removeTimedOutCurves()));
//timeoutTimer.start(5000);
}
LinechartPlot::~LinechartPlot()
{
// datalock.lock();
// // Delete curves
// QMap<QString, QwtPlotCurve*>::iterator i;
// for(i = curves.begin(); i != curves.end(); ++i) {
// // Remove from curve list
// QwtPlotCurve* curve = curves.take(i.key());
// // Delete the object
// delete curve;
// // Set the pointer null
// curve = NULL;
// }
// // Delete data
// QMap<QString, TimeSeriesData*>::iterator j;
// for(j = data.begin(); j != data.end(); ++j) {
// // Remove from data list
// TimeSeriesData* d = data.take(j.key());
// // Delete the object
// delete d;
// // Set the pointer null
// d = NULL;
// }
// datalock.unlock();
}
void LinechartPlot::showEvent(QShowEvent* event)
{
Q_UNUSED(event);
updateTimer->start(DEFAULT_REFRESH_RATE);
}
void LinechartPlot::hideEvent(QHideEvent* event)
{
Q_UNUSED(event);
updateTimer->stop();
}
int LinechartPlot::getPlotId()
{
return this->plotid;
}
/**
* @param id curve identifier
*/
double LinechartPlot::getCurrentValue(QString id)
{
return data.value(id)->getCurrentValue();
}
/**
* @param id curve identifier
*/
double LinechartPlot::getMean(QString id)
{
return data.value(id)->getMean();
}
/**
* @param id curve identifier
*/
double LinechartPlot::getMedian(QString id)
{
return data.value(id)->getMedian();
}
/**
* @param id curve identifier
*/
double LinechartPlot::getVariance(QString id)
{
return data.value(id)->getVariance();
}
int LinechartPlot::getAverageWindow()
{
return averageWindowSize;
}
/**
* @brief Set the plot refresh rate
* The default refresh rate is defined by LinechartPlot::DEFAULT_REFRESH_RATE.
* @param ms The refresh rate in milliseconds
**/
void LinechartPlot::setRefreshRate(int ms)
{
updateTimer->setInterval(ms);
}
void LinechartPlot::setActive(bool active)
{
m_active = active;
}
void LinechartPlot::removeTimedOutCurves()
{
foreach(QString key, lastUpdate.keys())
{
quint64 time = lastUpdate.value(key);
if (QGC::groundTimeMilliseconds() - time > 10000)
{
// Remove this curve
// Delete curves
QwtPlotCurve* curve = curves.take(key);
// Delete the object
delete curve;
// Set the pointer null
curve = NULL;
// Notify connected components about the removal
emit curveRemoved(key);
// Remove from data list
TimeSeriesData* d = data.take(key);
// Delete the object
delete d;
// Set the pointer null
d = NULL;
emit curveRemoved(key);
}
}
}
/**
* @brief Set the zero (center line) value
* The zero value defines the centerline of the plot.
*
* @param id The id of the curve
* @param zeroValue The zero value
**/
void LinechartPlot::setZeroValue(QString id, double zeroValue)
{
if(data.contains(id)) {
data.value(id)->setZeroValue(zeroValue);
} else {
data.insert(id, new TimeSeriesData(this, id, maxInterval, zeroValue));
}
}
void LinechartPlot::appendData(QString dataname, quint64 ms, double value)
{
/* Lock resource to ensure data integrity */
datalock.lock();
/* Check if dataset identifier already exists */
if(!data.contains(dataname)) {
addCurve(dataname);
enforceGroundTime(m_groundTime);
// qDebug() << "ADDING CURVE WITH" << dataname << ms << value;
// qDebug() << "MINTIME:" << minTime << "MAXTIME:" << maxTime;
// qDebug() << "LASTTIME:" << lastTime;
}
// Add new value
TimeSeriesData* dataset = data.value(dataname);
quint64 time;
// Append data
if (!m_groundTime)
{
// Use timestamp from dataset
time = ms;
}
else
{
time = QGC::groundTimeMilliseconds();
}
dataset->append(time, value);
lastUpdate.insert(dataname, time);
// Scaling values
if(ms < minTime) minTime = ms;
if(ms > maxTime) maxTime = ms;
storageInterval = maxTime - minTime;
if(time > lastTime)
{
//qDebug() << "UPDATED LAST TIME!" << dataname << time << lastTime;
lastTime = time;
}
//
if (value < minValue) minValue = value;
if (value > maxValue) maxValue = value;
valueInterval = maxValue - minValue;
// Assign dataset to curve
QwtPlotCurve* curve = curves.value(dataname);
curve->setRawData(dataset->getPlotX(), dataset->getPlotY(), dataset->getPlotCount());
// qDebug() << "mintime" << minTime << "maxtime" << maxTime << "last max time" << "window position" << getWindowPosition();
datalock.unlock();
}
/**
* @param enforce true to reset the data timestamp with the receive / ground timestamp
*/
void LinechartPlot::enforceGroundTime(bool enforce)
{
m_groundTime = enforce;
if (enforce)
{
lastTime = QGC::groundTimeMilliseconds();
plotPosition = lastTime;
maxTime = lastTime;
}
else
{
lastTime = 0;
plotPosition = 0;
minTime = 0;
maxTime = 100;
}
}
/**
* @return True if the data points are stamped with the packet receive time
*/
bool LinechartPlot::groundTime()
{
return m_groundTime;
}
void LinechartPlot::addCurve(QString id)
{
QColor currentColor = getNextColor();
// Create new curve and set style
QwtPlotCurve* curve = new QwtPlotCurve(id);
// Add curve to list
curves.insert(id, curve);
curve->setStyle(QwtPlotCurve::Lines);
curve->setPaintAttribute(QwtPlotCurve::PaintFiltered);
setCurveColor(id, currentColor);
//curve->setBrush(currentColor); Leads to a filled curve
// curve->setRenderHint(QwtPlotItem::RenderAntialiased);
curve->attach(this);
//@TODO Color differentiation between the curves will be necessary
/* Create symbol for datapoints on curve */
/*
* Symbols have significant performance penalty, better avoid them
*
QwtSymbol sym = QwtSymbol();
sym.setStyle(QwtSymbol::Ellipse);
sym.setPen(currentColor);
sym.setSize(3);
curve->setSymbol(sym);*/
// Create dataset
TimeSeriesData* dataset = new TimeSeriesData(this, id, this->plotInterval, maxInterval);
// Add dataset to list
data.insert(id, dataset);
// Notify connected components about new curve
emit curveAdded(id);
}
/**
* @brief Set the time window for the plot
* The time window defines which data is shown in the plot.
*
* @param end The end of the interval in milliseconds
* */
void LinechartPlot::setWindowPosition(quint64 end)
{
windowLock.lock();
if(end <= this->getMaxTime() && end >= (this->getMinTime() + this->getPlotInterval())) {
plotPosition = end;
setAxisScale(QwtPlot::xBottom, (plotPosition - getPlotInterval()), plotPosition, timeScaleStep);
}
//@TODO Update the rest of the plot and update drawing
windowLock.unlock();
}
/**
* @brief Get the time window position
* The position marks the right edge of the plot window
*
* @return The position of the plot window, in milliseconds
**/
quint64 LinechartPlot::getWindowPosition()
{
return plotPosition;
}
/**
* @brief Set the scaling of the (vertical) y axis
* The mapping of the variable values on the drawing pane can be
* adjusted with this method. The default is that the y axis will the chosen
* to fit all curves in their normal base units. This can however hide all
* details if large differences in the data values exist.
*
* The scaling can be changed to best fit, which fits all curves in a +100 to -100 interval.
* The logarithmic scaling does not fit the variables, but instead applies a log10
* scaling to all variables.
*
* @param scaling LinechartPlot::SCALE_ABSOLUTE for linear scaling, LinechartPlot::SCALE_BEST_FIT for the best fit scaling and LinechartPlot::SCALE_LOGARITHMIC for the logarithmic scaling.
**/
void LinechartPlot::setScaling(int scaling)
{
this->scaling = scaling;
switch (scaling) {
case LinechartPlot::SCALE_ABSOLUTE:
setLinearScaling();
break;
case LinechartPlot::SCALE_LOGARITHMIC:
setLogarithmicScaling();
break;
}
}
/**
* @brief Change the visibility of a curve
*
* @param id The string id of the curve
* @param visible The visibility: True to make it visible
**/
void LinechartPlot::setVisible(QString id, bool visible)
{
if(curves.contains(id)) {
curves.value(id)->setVisible(visible);
if(visible)
{
curves.value(id)->attach(this);
}
else
{
curves.value(id)->detach();
}
}
}
/**
* @brief Hide a curve.
*
* This is a convenience method and maps to setVisible(id, false).
*
* @param id The curve to hide
* @see setVisible() For the implementation
**/
void LinechartPlot::hideCurve(QString id)
{
setVisible(id, false);
}
/**
* @brief Show a curve.
*
* This is a convenience method and maps to setVisible(id, true);
*
* @param id The curve to show
* @see setVisible() For the implementation
**/
void LinechartPlot::showCurve(QString id)
{
setVisible(id, true);
}
//void LinechartPlot::showCurve(QString id, int position)
//{
// //@TODO Implement this position-dependent
// curves.value(id)->show();
//}
/**
* @brief Set the color of a curve and its symbols.
*
* @param id The id-string of the curve
* @param color The newly assigned color
**/
void LinechartPlot::setCurveColor(QString id, QColor color)
{
QwtPlotCurve* curve = curves.value(id);
curve->setPen(QPen(QBrush(color), curveWidth));
QwtSymbol x = curve->symbol();
x.setPen(QPen(QBrush(color), symbolWidth));
curve->setSymbol(x);
}
/**
* @brief Check the visibility of a curve
*
* @param id The id of the curve
* @return The visibility, true if it is visible, false otherwise
**/
bool LinechartPlot::isVisible(QString id)
{
return curves.value(id)->isVisible();
}
/**
* @return The visibility, true if it is visible, false otherwise
**/
bool LinechartPlot::anyCurveVisible()
{
bool visible = false;
foreach (QString key, curves.keys())
{
if (curves.value(key)->isVisible())
{
visible = true;
}
}
return visible;
}
/**
* @brief Allows to block interference of the automatic scrolling with user interaction
* When the plot is updated very fast (at 1 ms for example) with new data, it might
* get impossible for an user to interact. Therefore the automatic scrolling must be
* explicitly activated.
*
* @param active The status of automatic scrolling, true to turn it on
**/
void LinechartPlot::setAutoScroll(bool active)
{
automaticScrollActive = active;
}
/**
* @brief Get a list of all curves (visible and not visible curves)
*
* @return The list of curves
**/
QList<QwtPlotCurve*> LinechartPlot::getCurves()
{
return curves.values();
}
/**
* @brief Get the smallest time value in all datasets
*
* @return The smallest time value
**/
quint64 LinechartPlot::getMinTime()
{
return minTime;
}
/**
* @brief Get the biggest time value in all datasets
*
* @return The biggest time value
**/
quint64 LinechartPlot::getMaxTime()
{
return maxTime;
}
/**
* @brief Get the plot interval
* The plot interval is the time interval which is displayed on the plot
*
* @return The plot inteval in milliseconds
* @see setPlotInterval()
* @see getDataInterval() To get the interval for which data is available
**/
quint64 LinechartPlot::getPlotInterval()
{
return plotInterval;
}
/**
* @brief Set the plot interval
*
* @param interval The time interval to plot, in milliseconds
* @see getPlotInterval()
**/
void LinechartPlot::setPlotInterval(int interval)
{
//Only ever increase the amount of stored data,
// so that we allow the user to change between
// different intervals without constantly losing
// data points
if((unsigned)interval > plotInterval) {
QMap<QString, TimeSeriesData*>::iterator j;
for(j = data.begin(); j != data.end(); ++j)
{
TimeSeriesData* d = data.value(j.key());
d->setInterval(interval);
}
}
plotInterval = interval;
if(plotInterval > 5*60*1000) //If the interval is longer than 4 minutes, change the time scale step to 2 minutes
timeScaleStep = 2*60*1000;
else if(plotInterval >= 4*60*1000) //If the interval is longer than 4 minutes, change the time scale step to 1 minutes
timeScaleStep = 1*60*1000;
else if(plotInterval >= 60*1000) //If the interval is longer than a minute, change the time scale step to 30 seconds
timeScaleStep = 30*1000;
else
timeScaleStep = DEFAULT_SCALE_INTERVAL;
}
/**
* @brief Get the data interval
* The data interval is defined by the time interval for which data
* values are available.
*
* @return The data interval
* @see getPlotInterval() To get the time interval which is currently displayed by the plot
**/
quint64 LinechartPlot::getDataInterval()
{
return storageInterval;
}
/**
* @brief Set logarithmic scaling for the curve
**/
void LinechartPlot::setLogarithmicScaling()
{
yScaleEngine = new QwtLog10ScaleEngine();
setAxisScaleEngine(QwtPlot::yLeft, yScaleEngine);
}
/**
* @brief Set linear scaling for the curve
**/
void LinechartPlot::setLinearScaling()
{
yScaleEngine = new QwtLinearScaleEngine();
setAxisScaleEngine(QwtPlot::yLeft, yScaleEngine);
}
void LinechartPlot::setAverageWindow(int windowSize)
{
this->averageWindowSize = windowSize;
foreach(TimeSeriesData* series, data)
{
series->setAverageWindowSize(windowSize);
}
}
/**
* @brief Paint immediately the plot
* This method is a replacement for replot(). In contrast to replot(), it takes the
* time window size and eventual zoom interaction into account.
**/
void LinechartPlot::paintRealtime()
{
if (m_active) {
#if (QGC_EVENTLOOP_DEBUG)
static quint64 timestamp = 0;
qDebug() << "EVENTLOOP: (" << MG::TIME::getGroundTimeNow() - timestamp << ")" << __FILE__ << __LINE__;
timestamp = MG::TIME::getGroundTimeNow();
#endif
// Update plot window value to new max time if the last time was also the max time
windowLock.lock();
if (automaticScrollActive)
{
// FIXME Check, but commenting this out should have been
// beneficial (does only add complexity)
// if (MG::TIME::getGroundTimeNow() > maxTime && abs(MG::TIME::getGroundTimeNow() - maxTime) < 5000000)
// {
// plotPosition = MG::TIME::getGroundTimeNow();
// }
// else
// {
plotPosition = lastTime;// + lastMaxTimeAdded.msec();
// }
setAxisScale(QwtPlot::xBottom, plotPosition - plotInterval, plotPosition, timeScaleStep);
// FIXME Last fix for scroll zoomer is here
//setAxisScale(QwtPlot::yLeft, minValue + minValue * 0.05, maxValue + maxValue * 0.05f, (maxValue - minValue) / 10.0);
/* Notify about change. Even if the window position was not changed
* itself, the relative position of the window to the interval must
* have changed, as the interval likely increased in length */
emit windowPositionChanged(getWindowPosition());
}
windowLock.unlock();
// Defined both on windows 32- and 64 bit
#if !(defined Q_OS_WIN)
// const bool cacheMode =
// canvas()->testPaintAttribute(QwtPlotCanvas::PaintCached);
const bool oldDirectPaint =
canvas()->testAttribute(Qt::WA_PaintOutsidePaintEvent);
const QPaintEngine *pe = canvas()->paintEngine();
bool directPaint = pe->hasFeature(QPaintEngine::PaintOutsidePaintEvent);
//if ( pe->type() == QPaintEngine::X11 ) {
// Even if not recommended by TrollTech, Qt::WA_PaintOutsidePaintEvent
// works on X11. This has an tremendous effect on the performance..
directPaint = true;
//}
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, directPaint);
#endif
// Only set current view as zoombase if zoomer is not active
// else we could not zoom out any more
if(zoomer->zoomStack().size() < 2) {
zoomer->setZoomBase(true);
} else {
replot();
}
#if !(defined Q_OS_WIN)
canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, oldDirectPaint);
#endif
/*
QMap<QString, QwtPlotCurve*>::iterator i;
for(i = curves.begin(); i != curves.end(); ++i) {
const bool cacheMode = canvas()->testPaintAttribute(QwtPlotCanvas::PaintCached);
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, false);
i.value()->drawItems();
canvas()->setPaintAttribute(QwtPlotCanvas::PaintCached, cacheMode);
}*/
}
}
/**
* @brief Removes all data and curves from the plot
**/
void LinechartPlot::removeAllData()
{
datalock.lock();
// Delete curves
QMap<QString, QwtPlotCurve*>::iterator i;
for(i = curves.begin(); i != curves.end(); ++i)
{
// Remove from curve list
QwtPlotCurve* curve = curves.take(i.key());
// Delete the object
delete curve;
// Set the pointer null
curve = NULL;
// Notify connected components about the removal
emit curveRemoved(i.key());
}
// Delete data
QMap<QString, TimeSeriesData*>::iterator j;
for(j = data.begin(); j != data.end(); ++j)
{
// Remove from data list
TimeSeriesData* d = data.take(j.key());
// Delete the object
delete d;
// Set the pointer null
d = NULL;
}
datalock.unlock();
replot();
}
TimeSeriesData::TimeSeriesData(QwtPlot* plot, QString friendlyName, quint64 plotInterval, quint64 maxInterval, double zeroValue):
minValue(DBL_MAX),
maxValue(DBL_MIN),
zeroValue(0),
count(0),
mean(0.0),
median(0.0),
variance(0.0),
averageWindow(50)
{
this->plot = plot;
this->friendlyName = friendlyName;
this->maxInterval = maxInterval;
this->zeroValue = zeroValue;
this->plotInterval = plotInterval;
/* initialize time */
startTime = QUINT64_MAX;
stopTime = QUINT64_MIN;
plotCount = 0;
}
TimeSeriesData::~TimeSeriesData()
{
}
void TimeSeriesData::setInterval(quint64 ms)
{
plotInterval = ms;
}
void TimeSeriesData::setAverageWindowSize(int windowSize)
{
this->averageWindow = windowSize;
}
/**
* @brief Append a data point to this data set
*
* @param ms The time in milliseconds
* @param value The data value
**/
void TimeSeriesData::append(quint64 ms, double value)
{
dataMutex.lock();
// Pre- allocate new space
// FIXME Check this for validity
if(static_cast<quint64>(size()) < (count + 100)) {
this->ms.resize(size() + 10000);
this->value.resize(size() + 10000);
}
this->ms[count] = ms;
this->value[count] = value;
this->lastValue = value;
this->mean = 0;
//QList<double> medianList = QList<double>();
for (unsigned int i = 0; (i < averageWindow) && (((int)count - (int)i) >= 0); ++i) {
this->mean += this->value[count-i];
//medianList.append(this->value[count-i]);
}
this->mean = mean / static_cast<double>(qMin(averageWindow,static_cast<unsigned int>(count)));
this->variance = 0;
for (unsigned int i = 0; (i < averageWindow) && (((int)count - (int)i) >= 0); ++i) {
this->variance += (this->value[count-i] - mean) * (this->value[count-i] - mean);
}
this->variance = this->variance / static_cast<double>(qMin(averageWindow,static_cast<unsigned int>(count)));
// qSort(medianList);
// if (medianList.count() > 2)
// {
// if (medianList.count() % 2 == 0)
// {
// median = (medianList.at(medianList.count()/2) + medianList.at(medianList.count()/2+1)) / 2.0;
// }
// else
// {
// median = medianList.at(medianList.count()/2+1);
// }
// }
// Update statistical values
if(ms < startTime) startTime = ms;
if(ms > stopTime) stopTime = ms;
interval = stopTime - startTime;
if (interval > plotInterval) {
while (this->ms[count - plotCount] < stopTime - plotInterval) {
plotCount--;
}
}
count++;
plotCount++;
if(minValue > value) minValue = value;
if(maxValue < value) maxValue = value;
// Trim dataset if necessary
if(maxInterval > 0) {
// maxInterval = 0 means infinite
if(interval > maxInterval && !this->ms.isEmpty() && !this->value.isEmpty()) {
// The time at which this time series should be cut
double minTime = stopTime - maxInterval;
// Delete elements from the start of the list as long the time
// value of this elements is before the cut time
while(this->ms.first() < minTime) {
this->ms.pop_front();
this->value.pop_front();
}
}
}
dataMutex.unlock();
}
/**
* @brief Get the id of this data set
*
* @return The id-string
**/
int TimeSeriesData::getID()
{
return id;
}
/**
* @brief Get the minimum value in the data set
*
* @return The minimum value
**/
double TimeSeriesData::getMinValue()
{
return minValue;
}
/**
* @brief Get the maximum value in the data set
*
* @return The maximum value
**/
double TimeSeriesData::getMaxValue()
{
return maxValue;
}
/**
* @return the mean
*/
double TimeSeriesData::getMean()
{
return mean;
}
/**
* @return the median
*/
double TimeSeriesData::getMedian()
{
return median;
}
/**
* @return the variance
*/
double TimeSeriesData::getVariance()
{
return variance;
}
double TimeSeriesData::getCurrentValue()
{
return lastValue;
}
/**
* @brief Get the zero (center) value in the data set
* The zero value is not a statistical value, but instead manually defined
* when creating the data set.
* @return The zero value
**/
double TimeSeriesData::getZeroValue()
{
return zeroValue;
}
/**
* @brief Set the zero (center) value
*
* @param zeroValue The zero value
* @see getZeroValue()
**/
void TimeSeriesData::setZeroValue(double zeroValue)
{
this->zeroValue = zeroValue;
}
/**
* @brief Get the number of points in the dataset
*
* @return The number of points
**/
int TimeSeriesData::getCount() const
{
return count;
}
/**
* @brief Get the number of points in the plot selection
*
* @return The number of points
**/
int TimeSeriesData::getPlotCount() const
{
return plotCount;
}
/**
* @brief Get the data array size
* The data array size is \e NOT equal to the number of items in the data set, as
* array space is pre-allocated. Use getCount() to get the number of data points.
*
* @return The data array size
* @see getCount()
**/
int TimeSeriesData::size() const
{
return ms.size();
}
/**
* @brief Get the X (time) values
*
* @return The x values
**/
const double* TimeSeriesData::getX() const
{
return ms.data();
}
const double* TimeSeriesData::getPlotX() const
{
return ms.data() + (count - plotCount);
}
/**
* @brief Get the Y (data) values
*
* @return The y values
**/
const double* TimeSeriesData::getY() const
{
return value.data();
}
const double* TimeSeriesData::getPlotY() const
{
return value.data() + (count - plotCount);
}