Changeset - 8bc730cfda45
[Not reviewed]
default
0 4 0
Hasan Yavuz ÖZDERYA - 10 years ago 2015-08-15 13:17:08
hy@ozderya.net
use simple double array instead of QVector for FrameBuffer interface
4 files changed with 17 insertions and 18 deletions:
0 comments (0 inline, 0 general)
framebuffer.cpp
Show inline comments
 
@@ -17,99 +17,99 @@
 
  along with serialplot.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include "framebuffer.h"
 

	
 
FrameBuffer::FrameBuffer(size_t size)
 
{
 
    _size = size;
 
    data = new double[_size]();
 
    headIndex = 0;
 
}
 

	
 
FrameBuffer::~FrameBuffer()
 
{
 
    delete data;
 
}
 

	
 
void FrameBuffer::resize(size_t size)
 
{
 
    int offset = size - _size;
 
    if (offset == 0) return;
 

	
 
    double* newData = new double[size];
 

	
 
    // move data to new array
 
    int fill_start = offset > 0 ? offset : 0;
 

	
 
    for (int i = fill_start; i < int(size); i++)
 
    {
 
        newData[i] = _sample(i - offset);
 
    }
 

	
 
    // fill the beginning of the new data
 
    if (fill_start > 0)
 
    {
 
        for (int i = 0; i < fill_start; i++)
 
        {
 
            newData[i] = 0;
 
        }
 
    }
 

	
 
    // data is ready, clean and re-point
 
    delete data;
 
    data = newData;
 
    headIndex = 0;
 
    _size = size;
 
}
 

	
 
void FrameBuffer::addSamples(QVector<double> samples)
 
void FrameBuffer::addSamples(double* samples, size_t size)
 
{
 
    unsigned shift = samples.size();
 
    unsigned shift = size;
 
    if (shift < _size)
 
    {
 
        unsigned x = _size - headIndex; // distance of `head` to end
 

	
 
        if (shift <= x) // there is enough room at the end of array
 
        {
 
            for (size_t i = 0; i < shift; i++)
 
            {
 
                data[i+headIndex] = samples[i];
 
            }
 

	
 
            if (shift == x) // we used all the room at the end
 
            {
 
                headIndex = 0;
 
            }
 
            else
 
            {
 
                headIndex += shift;
 
            }
 
        }
 
        else // there isn't enough room
 
        {
 
            for (size_t i = 0; i < x; i++) // fill the end part
 
            {
 
                data[i+headIndex] = samples[i];
 
            }
 
            for (size_t i = 0; i < (shift-x); i++) // continue from the beginning
 
            {
 
                data[i] = samples[i+x];
 
            }
 
            headIndex = shift-x;
 
        }
 
    }
 
    else // number of new samples equal or bigger than current size
 
    {
 
        int x = shift - _size;
 
        for (size_t i = 0; i < _size; i++)
 
        {
 
            data[i] = samples[i+x];
 
        }
 
        headIndex = 0;
 
    }
 
}
 

	
 
void FrameBuffer::clear()
 
{
 
    for (size_t i=0; i < _size; i++) data[i] = 0.;
 
}
framebuffer.h
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#ifndef FRAMEBUFFER_H
 
#define FRAMEBUFFER_H
 

	
 
#include <qwt_series_data.h>
 
#include <QPointF>
 
#include <QRectF>
 

	
 
class FrameBuffer : public QwtSeriesData<QPointF>
 
{
 
public:
 
    FrameBuffer(size_t size);
 
    ~FrameBuffer();
 

	
 
    void resize(size_t size);
 
    void addSamples(QVector<double> samples);
 
    void addSamples(double* samples, size_t size);
 
    void clear(); // fill 0
 

	
 
    // QwtSeriesData implementations
 
    size_t size() const;
 
    QPointF sample(size_t i) const;
 
    QRectF boundingRect() const;
 

	
 
private:
 
    size_t _size; // size of `data`
 
    double* data;
 
    size_t headIndex; // indicates the actual `0` index of the ring buffer
 

	
 
    double _sample(size_t i) const;
 
};
 

	
 
#endif // FRAMEBUFFER_H
mainwindow.cpp
Show inline comments
 
@@ -210,229 +210,230 @@ MainWindow::~MainWindow()
 
    delete ui;
 
    ui = NULL; // we check if ui is deleted in messageHandler
 
}
 

	
 
void MainWindow::setupAboutDialog()
 
{
 
    Ui_AboutDialog uiAboutDialog;
 
    uiAboutDialog.setupUi(&aboutDialog);
 

	
 
    QObject::connect(uiAboutDialog.pbAboutQt, &QPushButton::clicked,
 
                     [](){ QApplication::aboutQt();});
 

	
 
    QString aboutText = uiAboutDialog.lbAbout->text();
 
    aboutText.replace("$VERSION_STRING$", VERSION_STRING);
 
    aboutText.replace("$VERSION_REVISION$", VERSION_REVISION);
 
    uiAboutDialog.lbAbout->setText(aboutText);
 
}
 

	
 
void MainWindow::onPortToggled(bool open)
 
{
 
    // make sure demo mode is disabled
 
    if (open && isDemoRunning()) enableDemo(false);
 
    ui->actionDemoMode->setEnabled(!open);
 
}
 

	
 
void MainWindow::onDataReady()
 
{
 
    if (ui->actionPause->isChecked())
 
    {
 
        serialPort.clear(QSerialPort::Input);
 
        return;
 
    }
 

	
 
    // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...}
 
    int packageSize = sampleSize * numOfChannels;
 
    int bytesAvailable = serialPort.bytesAvailable();
 

	
 
    if (bytesAvailable > 0 && skipByteRequested)
 
    {
 
        serialPort.read(1);
 
        skipByteRequested = false;
 
        bytesAvailable--;
 
    }
 

	
 
    if (bytesAvailable < packageSize) return;
 

	
 
    int numOfPackagesToRead =
 
        (bytesAvailable - (bytesAvailable % packageSize)) / packageSize;
 
    QVector<DataArray> channelSamples(numOfChannels);
 
    for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
    {
 
        channelSamples[ci].resize(numOfPackagesToRead);
 
    }
 
    double* channelSamples = new double[numOfPackagesToRead*numOfChannels];
 

	
 
    int i = 0;
 
    while(i < numOfPackagesToRead)
 
    {
 
        for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
        {
 
            channelSamples[ci].replace(i, (this->*readSample)());
 
            // channelSamples[ci].replace(i, (this->*readSample)());
 
            channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)();
 
        }
 
        i++;
 
    }
 

	
 
    for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
    {
 
        addChannelData(ci, channelSamples[ci]);
 
        addChannelData(ci,
 
                       channelSamples + ci*numOfPackagesToRead,
 
                       numOfPackagesToRead);
 
    }
 

	
 
    delete channelSamples;
 
}
 

	
 
void MainWindow::onDataReadyASCII()
 
{
 
    while(serialPort.canReadLine())
 
    {
 
        QByteArray line = serialPort.readLine();
 

	
 
        // discard data if paused
 
        if (ui->actionPause->isChecked())
 
        {
 
            return;
 
        }
 

	
 
        line = line.trimmed();
 
        auto separatedValues = line.split(',');
 

	
 
        int numReadChannels; // effective number of channels to read
 
        if (separatedValues.length() >= int(numOfChannels))
 
        {
 
            numReadChannels = numOfChannels;
 
        }
 
        else // there is missing channel data
 
        {
 
            numReadChannels = separatedValues.length();
 
            qWarning() << "Incoming data is missing data for some channels!";
 
        }
 

	
 
        // parse read line
 
        for (int ci = 0; ci < numReadChannels; ci++)
 
        {
 
            bool ok;
 
            double channelSample = separatedValues[ci].toDouble(&ok);
 
            if (ok)
 
            {
 
                addChannelData(ci, DataArray({channelSample}));
 
                addChannelData(ci, &channelSample, 1);
 
            }
 
            else
 
            {
 
                qWarning() << "Data parsing error for channel: " << ci;
 
            }
 
        }
 
    }
 
}
 

	
 
void MainWindow::onPortError(QSerialPort::SerialPortError error)
 
{
 
    switch(error)
 
    {
 
        case QSerialPort::NoError :
 
            break;
 
        case QSerialPort::ResourceError :
 
            qWarning() << "Port error: resource unavaliable; most likely device removed.";
 
            if (serialPort.isOpen())
 
            {
 
                qWarning() << "Closing port on resource error: " << serialPort.portName();
 
                portControl.togglePort();
 
            }
 
            portControl.loadPortList();
 
            break;
 
        case QSerialPort::DeviceNotFoundError:
 
            qCritical() << "Device doesn't exists: " << serialPort.portName();
 
            break;
 
        case QSerialPort::PermissionError:
 
            qCritical() << "Permission denied. Either you don't have \
 
required privileges or device is already opened by another process.";
 
            break;
 
        case QSerialPort::OpenError:
 
            qWarning() << "Device is already opened!";
 
            break;
 
        case QSerialPort::NotOpenError:
 
            qCritical() << "Device is not open!";
 
            break;
 
        case QSerialPort::ParityError:
 
            qCritical() << "Parity error detected.";
 
            break;
 
        case QSerialPort::FramingError:
 
            qCritical() << "Framing error detected.";
 
            break;
 
        case QSerialPort::BreakConditionError:
 
            qCritical() << "Break condition is detected.";
 
            break;
 
        case QSerialPort::WriteError:
 
            qCritical() << "An error occurred while writing data.";
 
            break;
 
        case QSerialPort::ReadError:
 
            qCritical() << "An error occurred while reading data.";
 
            break;
 
        case QSerialPort::UnsupportedOperationError:
 
            qCritical() << "Operation is not supported.";
 
            break;
 
        case QSerialPort::TimeoutError:
 
            qCritical() << "A timeout error occurred.";
 
            break;
 
        case QSerialPort::UnknownError:
 
            qCritical() << "Unknown error!";
 
            break;
 
        default:
 
            qCritical() << "Unhandled port error: " << error;
 
            break;
 
    }
 
}
 

	
 
void MainWindow::skipByte()
 
{
 
    skipByteRequested = true;
 
}
 

	
 
void MainWindow::addChannelData(unsigned int channel, DataArray data)
 
void MainWindow::addChannelData(unsigned int channel, double* data, unsigned size)
 
{
 
    channelBuffers[channel]->addSamples(data);
 
    channelBuffers[channel]->addSamples(data, size);
 
    ui->plot->replot(); // TODO: replot after all channel data updated
 
    sampleCount += data.size();
 
    sampleCount += size;
 
}
 

	
 
void MainWindow::clearPlot()
 
{
 
    for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
    {
 
        channelBuffers[ci]->clear();
 
    }
 
    ui->plot->replot();
 
}
 

	
 
void MainWindow::onNumOfSamplesChanged(int value)
 
{
 
    numOfSamples = value;
 

	
 
    for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
    {
 
        channelBuffers[ci]->resize(numOfSamples);
 
    }
 

	
 
    ui->plot->replot();
 
}
 

	
 
void MainWindow::onNumOfChannelsChanged(int value)
 
{
 
    unsigned int oldNum = this->numOfChannels;
 
    this->numOfChannels = value;
 

	
 
    if (numOfChannels > oldNum)
 
    {
 
        // add new channels
 
        for (unsigned int i = 0; i < numOfChannels - oldNum; i++)
 
        {
 
            channelBuffers.append(new FrameBuffer(numOfSamples));
 
            curves.append(new QwtPlotCurve());
 
            curves.last()->setSamples(channelBuffers.last());
 
            curves.last()->setPen(makeColor(curves.length()-1));
 
            curves.last()->attach(ui->plot);
 
        }
 
    }
 
    else if(numOfChannels < oldNum)
 
    {
 
        // remove channels
 
        for (unsigned int i = 0; i < oldNum - numOfChannels; i++)
 
        {
 
            // also deletes owned FrameBuffer
 
            delete curves.takeLast();
 
            channelBuffers.removeLast();
 
@@ -520,101 +521,99 @@ void MainWindow::selectNumberFormat(Numb
 
    }
 
    else
 
    {
 
        QObject::disconnect(&(this->serialPort), &QSerialPort::readyRead, 0, 0);
 
        QObject::connect(&(this->serialPort), &QSerialPort::readyRead,
 
                         this, &MainWindow::onDataReady);
 
        portControl.enableSkipByte(false);
 
    }
 
}
 

	
 
template<typename T> double MainWindow::readSampleAs()
 
{
 
    T data;
 
    this->serialPort.read((char*) &data, sizeof(data));
 

	
 
    if (ui->rbLittleE->isChecked())
 
    {
 
        data = qFromLittleEndian(data);
 
    }
 
    else
 
    {
 
        data = qFromBigEndian(data);
 
    }
 

	
 
    return double(data);
 
}
 

	
 
bool MainWindow::isDemoRunning()
 
{
 
    return ui->actionDemoMode->isChecked();
 
}
 

	
 
void MainWindow::spsTimerTimeout()
 
{
 
    spsLabel.setText(QString::number(sampleCount/SPS_UPDATE_TIMEOUT) + "sps");
 
    sampleCount = 0;
 
}
 

	
 
void MainWindow::demoTimerTimeout()
 
{
 
    const double period = 100;
 
    demoCount++;
 
    if (demoCount > 100) demoCount = 0;
 

	
 
    if (!ui->actionPause->isChecked())
 
    {
 
        for (unsigned int ci = 0; ci < numOfChannels; ci++)
 
        {
 
            DataArray data(1);
 
            // we are calculating the fourier components of square wave
 
            double value = 4*sin(2*M_PI*double((ci+1)*demoCount)/period)/((2*(ci+1))*M_PI);
 
            data.replace(0, value);
 
            addChannelData(ci, data);
 
            addChannelData(ci, &value, 1);
 
        }
 
    }
 
}
 

	
 
void MainWindow::enableDemo(bool enabled)
 
{
 
    if (enabled)
 
    {
 
        if (!serialPort.isOpen())
 
        {
 
            demoTimer.start();
 
            ui->actionDemoMode->setChecked(true);
 
            demoIndicator.show();
 
            ui->plot->replot();
 
        }
 
        else
 
        {
 
            ui->actionDemoMode->setChecked(false);
 
        }
 
    }
 
    else
 
    {
 
        demoTimer.stop();
 
        ui->actionDemoMode->setChecked(false);
 
        demoIndicator.hide();
 
        ui->plot->replot();
 
    }
 
}
 

	
 
/*
 
  Below crude drawing demostrates how color selection occurs for
 
  given channel index
 

	
 
  0°                     <--Hue Value-->                           360°
 
  |* . o . + . o . * . o . + . o . * . o . + . o . * . o . + . o . |
 

	
 
  * -> 0-3
 
  + -> 4-7
 
  o -> 8-15
 
  . -> 16-31
 

	
 
 */
 

	
 
QColor MainWindow::makeColor(unsigned int channelIndex)
 
{
 
    auto i = channelIndex;
 

	
 
    if (i < 4)
mainwindow.h
Show inline comments
 
@@ -39,97 +39,97 @@
 
#include "framebuffer.h"
 

	
 
namespace Ui {
 
class MainWindow;
 
}
 

	
 
class MainWindow : public QMainWindow
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit MainWindow(QWidget *parent = 0);
 
    ~MainWindow();
 

	
 
    void messageHandler(QtMsgType type, const QMessageLogContext &context,
 
                        const QString &msg);
 

	
 
private:
 
    enum NumberFormat
 
    {
 
        NumberFormat_uint8,
 
        NumberFormat_uint16,
 
        NumberFormat_uint32,
 
        NumberFormat_int8,
 
        NumberFormat_int16,
 
        NumberFormat_int32,
 
        NumberFormat_float,
 
        NumberFormat_ASCII
 
    };
 

	
 
    Ui::MainWindow *ui;
 
    QButtonGroup numberFormatButtons;
 

	
 
    QDialog aboutDialog;
 
    void setupAboutDialog();
 

	
 
    QSerialPort serialPort;
 
    PortControl portControl;
 

	
 
    unsigned int numOfSamples;
 
    unsigned int numOfChannels;
 

	
 
    QList<QwtPlotCurve*> curves;
 
    typedef QVector<double> DataArray;
 
    // Note: FrameBuffer s are owned by their respective QwtPlotCurve s.
 
    QList<FrameBuffer*> channelBuffers;
 

	
 
    // `data` contains i th channels data
 
    void addChannelData(unsigned int channel, DataArray data);
 
    void addChannelData(unsigned int channel, double* data, unsigned size);
 

	
 
    NumberFormat numberFormat;
 
    unsigned int sampleSize; // number of bytes in the selected number format
 
    double (MainWindow::*readSample)();
 

	
 
    // note that serialPort should already have enough bytes present
 
    template<typename T> double readSampleAs();
 

	
 
    bool skipByteRequested;
 

	
 
    const int SPS_UPDATE_TIMEOUT = 1;  // second
 
    QLabel spsLabel;
 
    unsigned int sampleCount;
 
    QTimer spsTimer;
 

	
 
    // demo
 
    QTimer demoTimer;
 
    int demoCount;
 
    bool isDemoRunning();
 
    QwtPlotTextLabel demoIndicator;
 

	
 
    QColor makeColor(unsigned int channelIndex);
 

	
 
private slots:
 
    void onPortToggled(bool open);
 
    void onDataReady();      // used with binary number formats
 
    void onDataReadyASCII(); // used with ASCII number format
 
    void onPortError(QSerialPort::SerialPortError error);
 

	
 
    void skipByte();
 

	
 
    void onNumOfSamplesChanged(int value);
 
    void onAutoScaleChecked(bool checked);
 
    void onYScaleChanged();
 

	
 
    void onNumOfChannelsChanged(int value);
 
    void onNumberFormatButtonToggled(int numberFormatId, bool checked);
 
    void selectNumberFormat(NumberFormat numberFormatId);
 

	
 
    void clearPlot();
 

	
 
    void spsTimerTimeout();
 

	
 
    void demoTimerTimeout();
 
    void enableDemo(bool enabled);
 

	
 
    void onExportCsv();
 
};
0 comments (0 inline, 0 general)