Changeset - f7670384fcb0
[Not reviewed]
value-tracker
0 14 0
Hasan Yavuz Ă–ZDERYA - 7 years ago 2018-12-12 15:05:26
hy@ozderya.net
added linear index buffer to support scaled x axis

this is also an important step towards "x data" support
14 files changed with 147 insertions and 75 deletions:
0 comments (0 inline, 0 general)
CMakeLists.txt
Show inline comments
 
@@ -126,6 +126,7 @@ add_executable(${PROGRAM_NAME} WIN32
 
  src/ringbuffer.cpp
 
  src/ringbuffer.cpp
 
  src/indexbuffer.cpp
 
  src/linindexbuffer.cpp
 
  src/readonlybuffer.cpp
 
  src/framebufferseries.cpp
 
  src/numberformatbox.cpp
src/framebufferseries.cpp
Show inline comments
 
@@ -20,75 +20,64 @@
 
#include <math.h>
 
#include "framebufferseries.h"
 

	
 
FrameBufferSeries::FrameBufferSeries(const FrameBuffer* buffer)
 
FrameBufferSeries::FrameBufferSeries(const XFrameBuffer* x, const FrameBuffer* y)
 
{
 
    xAsIndex = true;
 
    _xmin = 0;
 
    _xmax = 1;
 
    _buffer = buffer;
 
    _x = x;
 
    _y = y;
 

	
 
    int_index_start = 0;
 
    int_index_end = _buffer->size();
 
    int_index_end = _y->size();
 
}
 

	
 
void FrameBufferSeries::setXAxis(bool asIndex, double xmin, double xmax)
 
void FrameBufferSeries::setX(const XFrameBuffer* x)
 
{
 
    xAsIndex = asIndex;
 
    _xmin = xmin;
 
    _xmax = xmax;
 
    _x = x;
 
}
 

	
 
size_t FrameBufferSeries::size() const
 
{
 
    return int_index_end - int_index_start;
 
    return int_index_end - int_index_start + 1;
 
}
 

	
 
QPointF FrameBufferSeries::sample(size_t i) const
 
{
 
    i += int_index_start;
 
    if (xAsIndex)
 
    {
 
        return QPointF(i, _buffer->sample(i));
 
    }
 
    else
 
    {
 
        return QPointF(i * (_xmax - _xmin) / _buffer->size() + _xmin, _buffer->sample(i));
 
    }
 
    return QPointF(_x->sample(i), _y->sample(i));
 
}
 

	
 
QRectF FrameBufferSeries::boundingRect() const
 
{
 
    QRectF rect;
 
    auto yLim = _buffer->limits();
 
    auto yLim = _y->limits();
 
    auto xLim = _x->limits();
 
    rect.setBottom(yLim.start);
 
    rect.setTop(yLim.end);
 
    if (xAsIndex)
 
    {
 
        rect.setLeft(0);
 
        rect.setRight(size());
 
    }
 
    else
 
    {
 
        rect.setLeft(_xmin);
 
        rect.setRight(_xmax);
 
    }
 
    rect.setLeft(xLim.start);
 
    rect.setRight(xLim.end);
 

	
 
    return rect.normalized();
 
}
 

	
 
void FrameBufferSeries::setRectOfInterest(const QRectF& rect)
 
{
 
    if (xAsIndex)
 
    int_index_start = _x->findIndex(rect.left());
 
    int_index_end = _x->findIndex(rect.right());
 

	
 
    if (int_index_start == XFrameBuffer::OUT_OF_RANGE)
 
    {
 
        int_index_start = floor(rect.left())-1;
 
        int_index_end = ceil(rect.right())+1;
 
        int_index_start = 0;
 
    }
 
    else
 
    else if (int_index_start > 0)
 
    {
 
        double xsize = _xmax - _xmin;
 
        size_t bsize = _buffer->size();
 
        int_index_start =  floor(bsize * (rect.left()-_xmin) / xsize)-1;
 
        int_index_end = ceil(bsize * (rect.right()-_xmin) / xsize)+1;
 
        int_index_start -= 1;
 
    }
 

	
 
    int_index_start = std::max(int_index_start, 0);
 
    int_index_end = std::min((int) _buffer->size(), int_index_end);
 
    if (int_index_end == XFrameBuffer::OUT_OF_RANGE)
 
    {
 
        int_index_end = _x->size()-1;
 
    }
 
    else if (int_index_end < (int)_x->size()-1)
 
    {
 
        int_index_end += 1;
 
    }
 
}
src/framebufferseries.h
Show inline comments
 
@@ -35,10 +35,9 @@
 
class FrameBufferSeries : public QwtSeriesData<QPointF>
 
{
 
public:
 
    FrameBufferSeries(const FrameBuffer* buffer);
 
    FrameBufferSeries(const XFrameBuffer* x, const FrameBuffer* y);
 

	
 
    /// Behavior of X axis
 
    void setXAxis(bool asIndex, double xmin, double xmax);
 
    void setX(const XFrameBuffer* x);
 

	
 
    // QwtSeriesData implementations
 
    size_t size() const;
 
@@ -47,10 +46,8 @@ public:
 
    void setRectOfInterest(const QRectF& rect);
 

	
 
private:
 
    const FrameBuffer* _buffer;
 
    bool xAsIndex;
 
    double _xmin;
 
    double _xmax;
 
    const XFrameBuffer* _x;
 
    const FrameBuffer* _y;
 

	
 
    int int_index_start; ///< starting index of "rectangle of interest"
 
    int int_index_end;   ///< ending index of "rectangle of interest"
src/linindexbuffer.cpp
Show inline comments
 
 /*
 
  Copyright © 2017 Hasan Yavuz Özderya
 
  Copyright © 2018 Hasan Yavuz Özderya
 

	
 
  This file is part of serialplot.
 

	
 
@@ -18,12 +18,14 @@
 
*/
 

	
 
#include <QtGlobal>
 
#include <algorithm>
 

	
 
#include "linindexbuffer.h"
 

	
 
LinIndexBuffer::LinIndexBuffer(unsigned n, Range lim)
 
{
 
    Q_ASSERT(n > 0);
 
    // Note that calculation of _step would cause divide by 0
 
    Q_ASSERT(n > 1);
 

	
 
    _size = n;
 
    setLimits(lim);
 
@@ -50,6 +52,18 @@ void LinIndexBuffer::resize(unsigned n)
 
    setLimits(_limits);         // called to update `_step`
 
}
 

	
 
int LinIndexBuffer::findIndex(double value) const
 
{
 
    if (value < _limits.start || value > _limits.end)
 
    {
 
        return OUT_OF_RANGE;
 
    }
 

	
 
    int r = (value - _limits.start) / _step;
 
    // Note: we are limiting return value because of floating point in-accuracies
 
    return std::min<int>(std::max<int>(r, 0), (_size-1));
 
}
 

	
 
void LinIndexBuffer::setLimits(Range lim)
 
{
 
    _limits = lim;
src/linindexbuffer.h
Show inline comments
 
@@ -26,17 +26,19 @@
 
/// intermediate values are calculated linearly.
 
///
 
/// @note This buffer isn't for storing data.
 
class LinIndexBuffer : public ResizableBuffer
 
class LinIndexBuffer : public XFrameBuffer
 
{
 
public:
 
    LinIndexBuffer(unsigned n, Range lim);
 
    LinIndexBuffer(unsigned n, double min, double max) :
 
        LinIndexBuffer(n, {min, max}) {};
 

	
 
    unsigned size() const;
 
    double sample(unsigned i) const;
 
    Range limits() const;
 
    void resize(unsigned n);
 
    unsigned size() const override;
 
    double sample(unsigned i) const override;
 
    Range limits() const override;
 
    void resize(unsigned n) override;
 
    int findIndex(double value) const override;
 

	
 
    /// Sets minimum and maximum sample values of the buffer.
 
    void setLimits(Range lim);
 

	
src/mainwindow.cpp
Show inline comments
 
@@ -169,6 +169,9 @@ MainWindow::MainWindow(QWidget *parent) 
 
            plotMan, &PlotManager::setYAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::xScaleChanged,
 
            &stream, &Stream::setXAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::xScaleChanged,
 
            plotMan, &PlotManager::setXAxis);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::plotWidthChanged,
 
@@ -215,6 +218,9 @@ MainWindow::MainWindow(QWidget *parent) 
 
    plotControlPanel.setChannelInfoModel(stream.infoModel());
 

	
 
    // init scales
 
    stream.setXAxis(plotControlPanel.xAxisAsIndex(),
 
                    plotControlPanel.xMin(), plotControlPanel.xMax());
 

	
 
    plotMan->setYAxis(plotControlPanel.autoScale(),
 
                      plotControlPanel.yMin(), plotControlPanel.yMax());
 
    plotMan->setXAxis(plotControlPanel.xAxisAsIndex(),
src/plotmanager.cpp
Show inline comments
 
@@ -19,7 +19,6 @@
 

	
 
#include <algorithm>
 
#include <QMetaEnum>
 
#include <QtDebug>
 
#include "qwt_symbol.h"
 

	
 
#include "plot.h"
 
@@ -53,7 +52,7 @@ PlotManager::PlotManager(QWidget* plotAr
 
    // add initial curves if any?
 
    for (unsigned int i = 0; i < stream->numChannels(); i++)
 
    {
 
        addCurve(stream->channel(i)->name(), stream->channel(i)->yData());
 
        addCurve(stream->channel(i)->name(), stream->channel(i)->xData(), stream->channel(i)->yData());
 
    }
 

	
 
}
 
@@ -71,7 +70,7 @@ PlotManager::PlotManager(QWidget* plotAr
 

	
 
    for (unsigned ci = 0; ci < snapshot->numChannels(); ci++)
 
    {
 
        addCurve(snapshot->channelName(ci), snapshot->yData[ci]);
 
        addCurve(snapshot->channelName(ci), snapshot->xData[ci], snapshot->yData[ci]);
 
    }
 

	
 
    connect(infoModel, &QAbstractItemModel::dataChanged,
 
@@ -149,7 +148,7 @@ void PlotManager::onNumChannelsChanged(u
 
        // add new channels
 
        for (unsigned int i = oldNum; i < numOfChannels; i++)
 
        {
 
            addCurve(_stream->channel(i)->name(), _stream->channel(i)->yData());
 
            addCurve(_stream->channel(i)->name(), _stream->channel(i)->xData(), _stream->channel(i)->yData());
 
        }
 
    }
 
    else if(numOfChannels < oldNum)
 
@@ -333,11 +332,10 @@ Plot* PlotManager::addPlotWidget()
 
    return plot;
 
}
 

	
 
void PlotManager::addCurve(QString title, const FrameBuffer* buffer)
 
void PlotManager::addCurve(QString title, const XFrameBuffer* xBuf, const FrameBuffer* yBuf)
 
{
 
    auto curve = new QwtPlotCurve(title);
 
    auto series = new FrameBufferSeries(buffer);
 
    series->setXAxis(_xAxisAsIndex, _xMin, _xMax);
 
    auto series = new FrameBufferSeries(xBuf, yBuf);
 
    curve->setSamples(series);
 
    _addCurve(curve);
 
}
 
@@ -482,10 +480,13 @@ void PlotManager::setXAxis(bool asIndex,
 
    _xAxisAsIndex = asIndex;
 
    _xMin = xMin;
 
    _xMax = xMax;
 

	
 
    int ci = 0;
 
    for (auto curve : curves)
 
    {
 
        FrameBufferSeries* series = static_cast<FrameBufferSeries*>(curve->data());
 
        series->setXAxis(asIndex, xMin, xMax);
 
        series->setX(_stream->channel(ci)->xData());
 
        ci++;
 
    }
 
    for (auto plot : plotWidgets)
 
    {
src/plotmanager.h
Show inline comments
 
@@ -49,7 +49,7 @@ public:
 
    ~PlotManager();
 
    /// Add a new curve with title and buffer. A color is
 
    /// automatically chosen for curve.
 
    void addCurve(QString title, const FrameBuffer* buffer);
 
    void addCurve(QString title, const XFrameBuffer* xBuf, const FrameBuffer* yBuf);
 
    /// Removes curves from the end
 
    void removeCurves(unsigned number);
 
    /// Returns current number of curves known by plot manager
src/snapshot.h
Show inline comments
 
@@ -28,6 +28,7 @@
 

	
 
#include "channelinfomodel.h"
 
#include "readonlybuffer.h"
 
#include "indexbuffer.h"
 

	
 
class SnapshotView;
 
class MainWindow;
 
@@ -40,7 +41,8 @@ public:
 
    Snapshot(MainWindow* parent, QString name, ChannelInfoModel infoModel, bool saved = false);
 
    ~Snapshot();
 

	
 
    // TODO: yData of snapshot shouldn't be public, preferable should be handled in constructor
 
    // TODO: yData and xData of snapshot shouldn't be public, preferable should be handled in constructor
 
    QVector<IndexBuffer*> xData;
 
    QVector<ReadOnlyBuffer*> yData;
 
    QAction* showAction();
 
    QAction* deleteAction();
src/snapshotmanager.cpp
Show inline comments
 
@@ -71,6 +71,7 @@ Snapshot* SnapshotManager::makeSnapshot(
 

	
 
    for (unsigned ci = 0; ci < _stream->numChannels(); ci++)
 
    {
 
        snapshot->xData.append(new IndexBuffer(_stream->numSamples()));
 
        snapshot->yData.append(new ReadOnlyBuffer(_stream->channel(ci)->yData()));
 
    }
 

	
 
@@ -194,6 +195,7 @@ void SnapshotManager::loadSnapshotFromFi
 

	
 
    for (unsigned ci = 0; ci < numOfChannels; ci++)
 
    {
 
        snapshot->xData.append(new IndexBuffer(data[ci].size()));
 
        snapshot->yData.append(new ReadOnlyBuffer(data[ci].data(), data[ci].size()));
 
    }
 

	
src/stream.cpp
Show inline comments
 
@@ -20,6 +20,7 @@
 
#include "stream.h"
 
#include "ringbuffer.h"
 
#include "indexbuffer.h"
 
#include "linindexbuffer.h"
 

	
 
Stream::Stream(unsigned nc, bool x, unsigned ns) :
 
    _infoModel(nc)
 
@@ -27,6 +28,10 @@ Stream::Stream(unsigned nc, bool x, unsi
 
    _numSamples = ns;
 
    _paused = false;
 

	
 
    xAsIndex = true;
 
    xMin = 0;
 
    xMax = 1;
 

	
 
    // create xdata buffer
 
    _hasx = x;
 
    if (x)
 
@@ -36,7 +41,7 @@ Stream::Stream(unsigned nc, bool x, unsi
 
    }
 
    else
 
    {
 
        xData = new IndexBuffer(ns);
 
        xData = makeXBuffer();
 
    }
 

	
 
    // create channels
 
@@ -124,7 +129,7 @@ void Stream::setNumChannels(unsigned nc,
 
        }
 
        else
 
        {
 
            xData = new IndexBuffer(_numSamples);
 
            xData = makeXBuffer();
 
        }
 

	
 
        for (auto c : channels)
 
@@ -144,6 +149,18 @@ void Stream::setNumChannels(unsigned nc,
 
    Sink::setNumChannels(nc, x);
 
}
 

	
 
XFrameBuffer* Stream::makeXBuffer() const
 
{
 
    if (xAsIndex)
 
    {
 
        return new IndexBuffer(_numSamples);
 
    }
 
    else
 
    {
 
        return new LinIndexBuffer(_numSamples, xMin, xMax);
 
    }
 
}
 

	
 
const SamplePack* Stream::applyGainOffset(const SamplePack& pack) const
 
{
 
    Q_ASSERT(infoModel()->gainOrOffsetEn());
 
@@ -241,6 +258,24 @@ void Stream::setNumSamples(unsigned valu
 
    }
 
}
 

	
 
void Stream::setXAxis(bool asIndex, double min, double max)
 
{
 
    xAsIndex = asIndex;
 
    xMin = min;
 
    xMax = max;
 

	
 
    // Note that x axis scaling is ignored when X is provided from source as data
 
    // TODO: assert (UI options for x axis should be disabled)
 
    if (!hasX())
 
    {
 
        xData = makeXBuffer();
 
        for (auto c : channels)
 
        {
 
            c->setX(xData);
 
        }
 
    }
 
}
 

	
 
void Stream::saveSettings(QSettings* settings) const
 
{
 
    _infoModel.saveSettings(settings);
src/stream.h
Show inline comments
 
@@ -48,7 +48,7 @@ public:
 
     * @param x has X data input
 
     * @param ns number of samples
 
     */
 
    Stream(unsigned nc = 0, bool x = false, unsigned ns = 0);
 
    Stream(unsigned nc = 1, bool x = false, unsigned ns = 2);
 
    ~Stream();
 

	
 
    bool hasX() const;
 
@@ -78,10 +78,13 @@ signals:
 
    void dataAdded(); ///< emitted when data added to channel man.
 

	
 
public slots:
 
    // TODO: these won't be public
 
    // void setNumChannels(unsigned number);
 
    /// Change number of samples (buffer size)
 
    void setNumSamples(unsigned value);
 

	
 
    /// Change X axis style
 
    /// @note Ignored when X is provided by source (hasX == true)
 
    void setXAxis(bool asIndex, double min, double max);
 

	
 
    /// When paused data feed is ignored
 
    void pause(bool paused);
 

	
 
@@ -98,6 +101,9 @@ private:
 

	
 
    ChannelInfoModel _infoModel;
 

	
 
    bool xAsIndex;
 
    double xMin, xMax;
 

	
 
    /**
 
     * Applies gain and offset to given pack.
 
     *
 
@@ -110,6 +116,9 @@ private:
 
     * @return modified data
 
     */
 
    const SamplePack* applyGainOffset(const SamplePack& pack) const;
 

	
 
    /// Returns a new virtual X buffer for settings
 
    XFrameBuffer* makeXBuffer() const;
 
};
 

	
 

	
tests/test.cpp
Show inline comments
 
@@ -221,6 +221,14 @@ TEST_CASE("LinIndexBuffer", "[memory, bu
 

	
 
    REQUIRE(buf.sample(0) == -5.0);
 
    REQUIRE(buf.sample(19) == 5.0);
 

	
 
    buf.resize(10);
 
    buf.setLimits({0., 3.});
 
    REQUIRE(buf.findIndex(0.01) == 0);
 
    REQUIRE(buf.findIndex(1.51) == 4);
 
    REQUIRE(buf.findIndex(2.99) == 8);
 
    REQUIRE(buf.findIndex(3.01) == XFrameBuffer::OUT_OF_RANGE);
 
    REQUIRE(buf.findIndex(-0.01) == XFrameBuffer::OUT_OF_RANGE);
 
}
 

	
 
TEST_CASE("RingBuffer sizing", "[memory, buffer]")
tests/test_stream.cpp
Show inline comments
 
@@ -27,17 +27,17 @@ TEST_CASE("construction of stream with d
 
    // default values are an empty stream with no channels
 
    Stream s;
 

	
 
    REQUIRE(s.numChannels() == 0);
 
    REQUIRE(s.numChannels() == 1);
 
    REQUIRE(!s.hasX());
 
    REQUIRE(s.numSamples() == 0);
 
    REQUIRE(s.numSamples() == 2);
 
}
 

	
 
TEST_CASE("construction of stream with parameters", "[memory, stream]")
 
{
 
    Stream s(4, true, 100);
 
    Stream s(4, false, 100);
 

	
 
    REQUIRE(s.numChannels() == 4);
 
    REQUIRE(s.hasX());
 
    REQUIRE(!s.hasX());
 
    REQUIRE(s.numSamples() == 100);
 

	
 
    for (unsigned i = 0; i < 4; i++)
 
@@ -64,6 +64,8 @@ TEST_CASE("changing stream number of cha
 
        REQUIRE(c->index() == i);
 
    }
 

	
 
// TODO: enable test when `Stream` supports X channel
 
#if 0
 
    // increase nc value, add X
 
    so._setNumChannels(5, true);
 

	
 
@@ -76,6 +78,7 @@ TEST_CASE("changing stream number of cha
 
        REQUIRE(c != NULL);
 
        REQUIRE(c->index() == i);
 
    }
 
#endif
 

	
 
    // reduce nc value, remove X
 
    so._setNumChannels(1, false);
 
@@ -127,9 +130,11 @@ TEST_CASE("adding data to a stream with 
 
    }
 
}
 

	
 
// TODO: enable test when `Stream` supports X channel
 
#if 0
 
TEST_CASE("adding data to a stream with X", "[memory, stream, data, sink]")
 
{
 
    Stream s(3, true, 10);
 
    Stream s(3, false, 10);
 

	
 
    // prepare data
 
    SamplePack pack(5, 3, true);
 
@@ -147,7 +152,7 @@ TEST_CASE("adding data to a stream with 
 
    }
 

	
 
    TestSource so(3, true);
 
    so.connectSink(&s);
 
    REQUIRE_THROWS(so.connectSink(&s));
 

	
 
    // test
 
    so._feed(pack);
 
@@ -178,6 +183,7 @@ TEST_CASE("adding data to a stream with 
 
        REQUIRE(x->sample(i) == (i-5)+10);
 
    }
 
}
 
#endif
 

	
 
TEST_CASE("paused stream shouldn't store data", "[memory, stream, pause]")
 
{
0 comments (0 inline, 0 general)