diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ add_executable(${PROGRAM_NAME} WIN32 plot.cpp zoomer.cpp hidabletabwidget.cpp + framebuffer.cpp ${UI_FILES} misc/windows_icon.rc ) diff --git a/framebuffer.cpp b/framebuffer.cpp new file mode 100644 --- /dev/null +++ b/framebuffer.cpp @@ -0,0 +1,98 @@ +/* + Copyright © 2015 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include "framebuffer.h" + +FrameBuffer::FrameBuffer(size_t size) : + data(size, 0.) +{ + +} + +void FrameBuffer::resize(size_t size) +{ + size_t old_size = this->size(); + size_t new_size = size; + + if (new_size < old_size) + { + data.remove(0, old_size - new_size); + } + else if (new_size > old_size) + { + // This is seriously inefficient! + for (size_t i = old_size; i < new_size; i++) + { + data.prepend(0); + } + } +} + +void FrameBuffer::addSamples(QVector samples) +{ + int offset = size() - samples.size(); + + if (offset < 0) + { + // new samples exceed the size of frame buffer + // excess part (from beginning) of the input will be ignored + for (unsigned int i = 0; i < size(); i++) + { + data[i] = samples[i - offset]; + } + } + else if (offset == 0) // input is the same size as the framebuffer + { + data = samples; + } + else // regular case; input is smaller than framebuffer + { + // shift old samples + int shift = samples.size(); + for (int i = 0; i < offset; i++) + { + data[i] = data[i + shift]; + } + // place new samples + for (int i = 0; i < samples.size(); i++) + { + data[offset + i] = samples[i]; + } + } +} + +void FrameBuffer::clear() +{ + data.fill(0); +} + +size_t FrameBuffer::size() const +{ + return (size_t) data.size(); +} + +QPointF FrameBuffer::sample(size_t i) const +{ + return QPointF(i, data[i]); +} + +QRectF FrameBuffer::boundingRect() const +{ + return qwtBoundingRect(*this); +} diff --git a/framebuffer.h b/framebuffer.h new file mode 100644 --- /dev/null +++ b/framebuffer.h @@ -0,0 +1,45 @@ +/* + Copyright © 2015 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef FRAMEBUFFER_H +#define FRAMEBUFFER_H + +#include +#include +#include + +class FrameBuffer : public QwtSeriesData +{ +public: + FrameBuffer(size_t size); + + void resize(size_t size); + void addSamples(QVector samples); + void clear(); // fill 0 + + // QwtSeriesData implementations + size_t size() const; + QPointF sample(size_t i) const; + QRectF boundingRect() const; + +private: + QVector data; +}; + +#endif // FRAMEBUFFER_H diff --git a/main.cpp b/main.cpp --- a/main.cpp +++ b/main.cpp @@ -23,6 +23,12 @@ #include "version.h" +// TEST CODE +#include "framebuffer.h" +// #include +#include +#include + MainWindow* pMainWindow; void messageHandler(QtMsgType type, const QMessageLogContext &context, @@ -45,5 +51,33 @@ int main(int argc, char *argv[]) qDebug() << "Revision" << VERSION_REVISION; w.show(); + + // test framebuffer + FrameBuffer buffer(10); + QVector s(3,15.); + s.append(1.0); + s.append(2.0); + s.append(3.0); + buffer.addSamples(s); + // buffer.addSamples(s); + // buffer.resize(5); + QwtPlot plot; + QwtPlotCurve curve; + curve.setSamples(&buffer); + curve.attach(&plot); + plot.show(); + qDebug() << buffer.sample(0); + qDebug() << buffer.sample(1); + qDebug() << "size:" << buffer.size(); + buffer.addSamples(s); + // curve.setSamples(&buffer); + plot.replot(); + // qDebug() << buffer.sample(5); + // qDebug() << buffer.sample(7); + // qDebug() << buffer.sample(8); + // qDebug() << buffer.sample(9); + // qDebug() << buffer.sample(15); + // qDebug() << buffer.sample(19); + return a.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -137,18 +137,12 @@ MainWindow::MainWindow(QWidget *parent) numOfSamples = ui->spNumOfSamples->value(); numOfChannels = 1; - dataX.resize(numOfSamples); - for (int i = 0; i < dataX.size(); i++) - { - dataX[i] = i; - } - // init channel data and curve list for (unsigned int i = 0; i < numOfChannels; i++) { - channelsData.append(DataArray(numOfSamples, 0.0)); + channelBuffers.append(new FrameBuffer(numOfSamples)); curves.append(new QwtPlotCurve()); - curves[i]->setSamples(dataX, channelsData[i]); + curves[i]->setSamples(channelBuffers[i]); curves[i]->setPen(makeColor(i)); curves[i]->attach(ui->plot); } @@ -205,6 +199,7 @@ MainWindow::~MainWindow() { for (auto curve : curves) { + // also deletes respective FrameBuffer delete curve; } @@ -396,39 +391,8 @@ void MainWindow::skipByte() void MainWindow::addChannelData(unsigned int channel, DataArray data) { - DataArray* channelDataArray = &(channelsData[channel]); - int offset = numOfSamples - data.size(); - - if (offset < 0) - { - for (unsigned int i = 0; i < numOfSamples; i++) - { - (*channelDataArray)[i] = data[i - offset]; - } - } - else if (offset == 0) - { - (*channelDataArray) = data; - } - else - { - // shift old samples - int shift = data.size(); - for (int i = 0; i < offset; i++) - { - (*channelDataArray)[i] = (*channelDataArray)[i + shift]; - } - // place new samples - for (int i = 0; i < data.size(); i++) - { - (*channelDataArray)[offset + i] = data[i]; - } - } - - // update plot - curves[channel]->setSamples(dataX, (*channelDataArray)); + channelBuffers[channel]->addSamples(data); ui->plot->replot(); // TODO: replot after all channel data updated - sampleCount += data.size(); } @@ -436,51 +400,21 @@ void MainWindow::clearPlot() { for (unsigned int ci = 0; ci < numOfChannels; ci++) { - channelsData[ci].fill(0.0); - curves[ci]->setSamples(dataX, channelsData[ci]); + channelBuffers[ci]->clear(); } - - // update plot ui->plot->replot(); } void MainWindow::onNumOfSamplesChanged(int value) { - unsigned int oldNum = this->numOfSamples; numOfSamples = value; - // resize data arrays - if (numOfSamples < oldNum) - { - dataX.resize(numOfSamples); - for (unsigned int ci = 0; ci < numOfChannels; ci++) - { - channelsData[ci].remove(0, oldNum - numOfSamples); - curves[ci]->setSamples(dataX, channelsData[ci]); - } - ui->plot->replot(); - } - else if(numOfSamples > oldNum) + for (unsigned int ci = 0; ci < numOfChannels; ci++) { - // update data arrays - dataX.resize(numOfSamples); - for (unsigned int i = oldNum; i < numOfSamples; i++) - { - dataX[i] = i; - for (unsigned int ci = 0; ci < numOfChannels; ci++) - { - // TODO: opportunity of major optimization here - // let's hope nobody sees this - channelsData[ci].prepend(0); - } - } - // update curves - for (unsigned int ci = 0; ci < numOfChannels; ci++) - { - curves[ci]->setSamples(dataX, channelsData[ci]); - } - ui->plot->replot(); + channelBuffers[ci]->resize(numOfSamples); } + + ui->plot->replot(); } void MainWindow::onNumOfChannelsChanged(int value) @@ -493,9 +427,9 @@ void MainWindow::onNumOfChannelsChanged( // add new channels for (unsigned int i = 0; i < numOfChannels - oldNum; i++) { - channelsData.append(DataArray(numOfSamples, 0.0)); + channelBuffers.append(new FrameBuffer(numOfSamples)); curves.append(new QwtPlotCurve()); - curves.last()->setSamples(dataX, channelsData.last()); + curves.last()->setSamples(channelBuffers.last()); curves.last()->setPen(makeColor(curves.length()-1)); curves.last()->attach(ui->plot); } @@ -505,10 +439,9 @@ void MainWindow::onNumOfChannelsChanged( // remove channels for (unsigned int i = 0; i < oldNum - numOfChannels; i++) { - channelsData.removeLast(); - auto curve = curves.takeLast(); - curve->detach(); - delete curve; + // also deletes owned FrameBuffer + delete curves.takeLast(); + channelBuffers.removeLast(); } } } @@ -732,7 +665,7 @@ void MainWindow::onExportCsv() { for (unsigned int ci = 0; ci < numOfChannels; ci++) { - fileStream << channelsData[ci][i]; + fileStream << channelBuffers[ci]->sample(i).y(); if (ci != numOfChannels-1) fileStream << ","; } fileStream << '\n'; diff --git a/mainwindow.h b/mainwindow.h --- a/mainwindow.h +++ b/mainwindow.h @@ -36,6 +36,7 @@ #include "portcontrol.h" #include "ui_about_dialog.h" +#include "framebuffer.h" namespace Ui { class MainWindow; @@ -79,8 +80,8 @@ private: QList curves; typedef QVector DataArray; - DataArray dataX; // array that simply contains numbers 0..numberOfSamples - QList channelsData; + // Note: FrameBuffer s are owned by their respective QwtPlotCurve s. + QList channelBuffers; // `data` contains i th channels data void addChannelData(unsigned int channel, DataArray data); diff --git a/serialplot.pro b/serialplot.pro --- a/serialplot.pro +++ b/serialplot.pro @@ -39,7 +39,8 @@ SOURCES += main.cpp\ portcontrol.cpp \ plot.cpp \ zoomer.cpp \ - hidabletabwidget.cpp + hidabletabwidget.cpp \ + framebuffer.cpp HEADERS += mainwindow.h \ utils.h \ @@ -48,7 +49,8 @@ HEADERS += mainwindow.h \ floatswap.h \ plot.h \ zoomer.h \ - hidabletabwidget.h + hidabletabwidget.h \ + framebuffer.h FORMS += mainwindow.ui \ about_dialog.ui \