# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2016-06-12 16:57:45 # Node ID 84f934d0e298f3794e05c7b5124b02d693e59110 # Parent 300a8da1b0c9b443330227fe78dec72b4c8abcda # Parent 1b83ea543128f37d4f998687e5cbc4a31394f293 Merge with new-reader diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,11 @@ qt5_wrap_ui(UI_FILES src/commandwidget.ui src/dataformatpanel.ui src/plotcontrolpanel.ui + src/numberformatbox.ui + src/endiannessbox.ui + src/binarystreamreadersettings.ui + src/asciireadersettings.ui + src/framedreadersettings.ui ) if (WIN32) @@ -93,6 +98,16 @@ add_executable(${PROGRAM_NAME} WIN32 src/sneakylineedit.cpp src/channelmanager.cpp src/framebufferseries.cpp + src/numberformatbox.cpp + src/endiannessbox.cpp + src/abstractreader.cpp + src/binarystreamreader.cpp + src/binarystreamreadersettings.cpp + src/asciireader.cpp + src/asciireadersettings.cpp + src/demoreader.cpp + src/framedreader.cpp + src/framedreadersettings.cpp misc/windows_icon.rc ${UI_FILES} ${RES_FILES} diff --git a/misc/pseudo_device.py b/misc/pseudo_device.py --- a/misc/pseudo_device.py +++ b/misc/pseudo_device.py @@ -71,6 +71,34 @@ def uint32_test(port, little): time.sleep(0.05) i = i+1 if i <= maxi else 0 +def frame_test(port, fixed_size=False, hasChecksum=True): + """Sends binary data in framed format.""" + SYNCWORD = [0xAA, 0xBB] + NUMSAMPLES = 10 + SIZE = NUMSAMPLES * 4 # integer + if fixed_size: + HEADER = bytes(SYNCWORD) + else: + HEADER = bytes(SYNCWORD + [SIZE]) + i = 0 + checksum = 0 + bytesent = 0 + while True: + if i > 100: i = 0 + if bytesent == 0: # beginning of a frame? + os.write(port, HEADER) + os.write(port, struct.pack('. +*/ + +#include "abstractreader.h" + +AbstractReader::AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + QObject(parent) +{ + _device = device; + _channelMan = channelMan; + + // initialize sps counter + sampleCount = 0; + samplesPerSecond = 0; + QObject::connect(&spsTimer, &QTimer::timeout, + this, &AbstractReader::spsTimerTimeout); + // TODO: start sps timer when reader is enabled + spsTimer.start(SPS_UPDATE_TIMEOUT * 1000); +} + +void AbstractReader::spsTimerTimeout() +{ + unsigned currentSps = samplesPerSecond; + samplesPerSecond = (sampleCount/numOfChannels())/SPS_UPDATE_TIMEOUT; + if (currentSps != samplesPerSecond) + { + emit samplesPerSecondChanged(samplesPerSecond); + } + sampleCount = 0; +} diff --git a/src/abstractreader.h b/src/abstractreader.h new file mode 100644 --- /dev/null +++ b/src/abstractreader.h @@ -0,0 +1,89 @@ +/* + Copyright © 2016 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 ABSTRACTREADER_H +#define ABSTRACTREADER_H + +#include +#include +#include +#include + +#include "channelmanager.h" + +/** + * All reader classes must inherit this class. + */ +class AbstractReader : public QObject +{ + Q_OBJECT +public: + explicit AbstractReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + + /** + * Returns a widget to be shown in data format panel when reader + * is selected. + */ + virtual QWidget* settingsWidget() = 0; + + /** + * Number of channels being read. + * + * This number may be user selected or automatically determined + * from incoming stream. + */ + virtual unsigned numOfChannels() = 0; + + /// Reader should only read when enabled. Default state should be + /// 'disabled'. + virtual void enable(bool enabled = true) = 0; + +signals: + void numOfChannelsChanged(unsigned); + // TODO: this must be signaled by 'channel man' for better abstraction + void dataAdded(); ///< emitted when data added to channel man. + // TODO: this should be a part of 'channel man' + void samplesPerSecondChanged(unsigned); + +public slots: + /** + * Pauses the reading. + * + * Reader should actually continue reading to keep the + * synchronization but shouldn't commit data. + */ + virtual void pause(bool) = 0; + +protected: + QIODevice* _device; + ChannelManager* _channelMan; + + /// Implementing class should simply increase this count as samples are read + unsigned sampleCount; + +private: + const int SPS_UPDATE_TIMEOUT = 1; // second + unsigned samplesPerSecond; + QTimer spsTimer; + +private slots: + void spsTimerTimeout(); +}; + +#endif // ABSTRACTREADER_H diff --git a/src/asciireader.cpp b/src/asciireader.cpp new file mode 100644 --- /dev/null +++ b/src/asciireader.cpp @@ -0,0 +1,110 @@ +/* + Copyright © 2016 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 + +#include "asciireader.h" + +AsciiReader::AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + sampleCount = 0; + // TODO: sps counter + + _numOfChannels = _settingsWidget.numOfChannels(); + connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged, + this, &AsciiReader::numOfChannelsChanged); + connect(&_settingsWidget, &AsciiReaderSettings::numOfChannelsChanged, + [this](unsigned value){_numOfChannels = value;}); +} + +QWidget* AsciiReader::settingsWidget() +{ + return &_settingsWidget; +} + +unsigned AsciiReader::numOfChannels() +{ + return _numOfChannels; +} + +// TODO: this could be a part of AbstractReader +void AsciiReader::enable(bool enabled) +{ + if (enabled) + { + QObject::connect(_device, &QIODevice::readyRead, + this, &AsciiReader::onDataReady); + } + else + { + QObject::disconnect(_device, 0, this, 0); + } +} + +void AsciiReader::pause(bool enabled) +{ + paused = enabled; +} + +void AsciiReader::onDataReady() +{ + while(_device->canReadLine()) + { + QByteArray line = _device->readLine(); + + // discard data if paused + if (paused) + { + 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) + { + _channelMan->addChannelData(ci, &channelSample, 1); + sampleCount++; + } + else + { + qWarning() << "Data parsing error for channel: " << ci; + } + } + emit dataAdded(); + } +} diff --git a/src/asciireader.h b/src/asciireader.h new file mode 100644 --- /dev/null +++ b/src/asciireader.h @@ -0,0 +1,48 @@ +/* + Copyright © 2016 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 ASCIIREADER_H +#define ASCIIREADER_H + +#include "abstractreader.h" +#include "asciireadersettings.h" + +class AsciiReader : public AbstractReader +{ + Q_OBJECT + +public: + explicit AsciiReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + QWidget* settingsWidget(); + unsigned numOfChannels(); + void enable(bool enabled = true); + +public slots: + void pause(bool); + +private: + AsciiReaderSettings _settingsWidget; + unsigned _numOfChannels; + bool paused; + +private slots: + void onDataReady(); +}; + +#endif // ASCIIREADER_H diff --git a/src/asciireadersettings.cpp b/src/asciireadersettings.cpp new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.cpp @@ -0,0 +1,47 @@ +/* + Copyright © 2016 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 "utils.h" + +#include "asciireadersettings.h" +#include "ui_asciireadersettings.h" + +AsciiReaderSettings::AsciiReaderSettings(QWidget *parent) : + QWidget(parent), + ui(new Ui::AsciiReaderSettings) +{ + ui->setupUi(this); + + // Note: if directly connected we get a runtime warning on incompatible signal arguments + connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit numOfChannelsChanged(value); + }); +} + +AsciiReaderSettings::~AsciiReaderSettings() +{ + delete ui; +} + +unsigned AsciiReaderSettings::numOfChannels() +{ + return ui->spNumOfChannels->value(); +} diff --git a/src/asciireadersettings.h b/src/asciireadersettings.h new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.h @@ -0,0 +1,46 @@ +/* + Copyright © 2016 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 ASCIIREADERSETTINGS_H +#define ASCIIREADERSETTINGS_H + +#include + +namespace Ui { +class AsciiReaderSettings; +} + +class AsciiReaderSettings : public QWidget +{ + Q_OBJECT + +public: + explicit AsciiReaderSettings(QWidget *parent = 0); + ~AsciiReaderSettings(); + + unsigned numOfChannels(); + +signals: + void numOfChannelsChanged(unsigned); + +private: + Ui::AsciiReaderSettings *ui; +}; + +#endif // ASCIIREADERSETTINGS_H diff --git a/src/asciireadersettings.ui b/src/asciireadersettings.ui new file mode 100644 --- /dev/null +++ b/src/asciireadersettings.ui @@ -0,0 +1,92 @@ + + + AsciiReaderSettings + + + + 0 + 0 + 414 + 171 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Number Of Channels: + + + + + + + + 60 + 0 + + + + Select number of channels + + + false + + + 1 + + + 32 + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + diff --git a/src/binarystreamreader.cpp b/src/binarystreamreader.cpp new file mode 100644 --- /dev/null +++ b/src/binarystreamreader.cpp @@ -0,0 +1,190 @@ +/* + Copyright © 2016 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 +#include + +#include "binarystreamreader.h" +#include "floatswap.h" + +BinaryStreamReader::BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + skipByteRequested = false; + sampleCount = 0; + + _numOfChannels = _settingsWidget.numOfChannels(); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged, + this, &BinaryStreamReader::numOfChannelsChanged); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numOfChannelsChanged, + this, &BinaryStreamReader::onNumOfChannelsChanged); + + // initial number format selection + onNumberFormatChanged(_settingsWidget.numberFormat()); + connect(&_settingsWidget, &BinaryStreamReaderSettings::numberFormatChanged, + this, &BinaryStreamReader::onNumberFormatChanged); + + // enable skip byte button + connect(&_settingsWidget, &BinaryStreamReaderSettings::skipByteRequested, + [this]() + { + skipByteRequested = true; + }); +} + +QWidget* BinaryStreamReader::settingsWidget() +{ + return &_settingsWidget; +} + +unsigned BinaryStreamReader::numOfChannels() +{ + return _numOfChannels; +} + +void BinaryStreamReader::enable(bool enabled) +{ + if (enabled) + { + QObject::connect(_device, &QIODevice::readyRead, + this, &BinaryStreamReader::onDataReady); + } + else + { + QObject::disconnect(_device, 0, this, 0); + } +} + +void BinaryStreamReader::pause(bool enabled) +{ + paused = enabled; +} + +void BinaryStreamReader::onNumberFormatChanged(NumberFormat numberFormat) +{ + switch(numberFormat) + { + case NumberFormat_uint8: + sampleSize = 1; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int8: + sampleSize = 1; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_uint16: + sampleSize = 2; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int16: + sampleSize = 2; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_uint32: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_int32: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + case NumberFormat_float: + sampleSize = 4; + readSample = &BinaryStreamReader::readSampleAs; + break; + } +} + +void BinaryStreamReader::onNumOfChannelsChanged(unsigned value) +{ + _numOfChannels = value; +} + +void BinaryStreamReader::onDataReady() +{ + // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...} + int packageSize = sampleSize * _numOfChannels; + int bytesAvailable = _device->bytesAvailable(); + int numOfPackagesToRead = + (bytesAvailable - (bytesAvailable % packageSize)) / packageSize; + + if (paused) + { + // read and discard data + _device->read(numOfPackagesToRead*packageSize); + return; + } + + if (bytesAvailable > 0 && skipByteRequested) + { + _device->read(1); + skipByteRequested = false; + bytesAvailable--; + } + + if (bytesAvailable < packageSize) return; + + double* channelSamples = new double[numOfPackagesToRead*_numOfChannels]; + + for (int i = 0; i < numOfPackagesToRead; i++) + { + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + // channelSamples[ci].replace(i, (this->*readSample)()); + channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)(); + } + } + + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + addChannelData(ci, + channelSamples + ci*numOfPackagesToRead, + numOfPackagesToRead); + } + emit dataAdded(); + + delete channelSamples; +} + + +template double BinaryStreamReader::readSampleAs() +{ + T data; + + _device->read((char*) &data, sizeof(data)); + + if (_settingsWidget.endianness() == LittleEndian) + { + data = qFromLittleEndian(data); + } + else + { + data = qFromBigEndian(data); + } + + return double(data); +} + +void BinaryStreamReader::addChannelData(unsigned int channel, + double* data, unsigned size) +{ + _channelMan->addChannelData(channel, data, size); + sampleCount += size; +} diff --git a/src/binarystreamreader.h b/src/binarystreamreader.h new file mode 100644 --- /dev/null +++ b/src/binarystreamreader.h @@ -0,0 +1,70 @@ +/* + Copyright © 2016 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 BINARYSTREAMREADER_H +#define BINARYSTREAMREADER_H + +#include "abstractreader.h" +#include "binarystreamreadersettings.h" + +/** + * Reads a simple stream of samples in binary form from the + * device. There is no means of synchronization other than a button + * that should be manually triggered by user. + */ +class BinaryStreamReader : public AbstractReader +{ + Q_OBJECT +public: + explicit BinaryStreamReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + QWidget* settingsWidget(); + unsigned numOfChannels(); + void enable(bool enabled = true); + +public slots: + void pause(bool); + +private: + BinaryStreamReaderSettings _settingsWidget; + unsigned _numOfChannels; + unsigned sampleSize; + bool paused; + bool skipByteRequested; + + /// points to the readSampleAs function for currently selected number format + double (BinaryStreamReader::*readSample)(); + + /** + * Reads 1 sample from the device in given format. + * + * @note Device should already have enough bytes present before + * calling this function. + */ + template double readSampleAs(); + + // `data` contains i th channels data + void addChannelData(unsigned int channel, double* data, unsigned size); + +private slots: + void onNumberFormatChanged(NumberFormat numberFormat); + void onNumOfChannelsChanged(unsigned value); + void onDataReady(); +}; + +#endif // BINARYSTREAMREADER_H diff --git a/src/binarystreamreadersettings.cpp b/src/binarystreamreadersettings.cpp new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.cpp @@ -0,0 +1,62 @@ +/* + Copyright © 2016 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 "binarystreamreadersettings.h" +#include "ui_binarystreamreadersettings.h" + +#include "utils.h" + +BinaryStreamReaderSettings::BinaryStreamReaderSettings(QWidget *parent) : + QWidget(parent), + ui(new Ui::BinaryStreamReaderSettings) +{ + ui->setupUi(this); + + // Note: if directly connected we get a runtime warning on incompatible signal arguments + connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit numOfChannelsChanged(value); + }); + + connect(ui->nfBox, SIGNAL(selectionChanged(NumberFormat)), + this, SIGNAL(numberFormatChanged(NumberFormat))); + + connect(ui->pbSkipByte, SIGNAL(clicked()), this, SIGNAL(skipByteRequested())); +} + +BinaryStreamReaderSettings::~BinaryStreamReaderSettings() +{ + delete ui; +} + +unsigned BinaryStreamReaderSettings::numOfChannels() +{ + return ui->spNumOfChannels->value(); +} + +NumberFormat BinaryStreamReaderSettings::numberFormat() +{ + return ui->nfBox->currentSelection(); +} + +Endianness BinaryStreamReaderSettings::endianness() +{ + return ui->endiBox->currentSelection(); +} diff --git a/src/binarystreamreadersettings.h b/src/binarystreamreadersettings.h new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.h @@ -0,0 +1,52 @@ +/* + Copyright © 2016 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 BINARYSTREAMREADERSETTINGS_H +#define BINARYSTREAMREADERSETTINGS_H + +#include +#include "numberformatbox.h" +#include "endiannessbox.h" + +namespace Ui { +class BinaryStreamReaderSettings; +} + +class BinaryStreamReaderSettings : public QWidget +{ + Q_OBJECT + +public: + explicit BinaryStreamReaderSettings(QWidget *parent = 0); + ~BinaryStreamReaderSettings(); + + unsigned numOfChannels(); + NumberFormat numberFormat(); + Endianness endianness(); + +signals: + void numOfChannelsChanged(unsigned); + void numberFormatChanged(NumberFormat); + void skipByteRequested(); + +private: + Ui::BinaryStreamReaderSettings *ui; +}; + +#endif // BINARYSTREAMREADERSETTINGS_H diff --git a/src/binarystreamreadersettings.ui b/src/binarystreamreadersettings.ui new file mode 100644 --- /dev/null +++ b/src/binarystreamreadersettings.ui @@ -0,0 +1,172 @@ + + + BinaryStreamReaderSettings + + + + 0 + 0 + 588 + 212 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Number Of Channels: + + + + + + + + 60 + 0 + + + + Select number of channels + + + false + + + 1 + + + 32 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Skip reading 1 byte to correct the alignment + + + Skip Byte + + + + + + + + + QFormLayout::FieldsStayAtSizeHint + + + 3 + + + + + + 0 + 0 + + + + Number Type: + + + + + + + + 0 + 0 + + + + Endianness: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + NumberFormatBox + QWidget +
numberformatbox.h
+ 1 +
+ + EndiannessBox + QWidget +
endiannessbox.h
+ 1 +
+
+ + +
diff --git a/src/dataformatpanel.cpp b/src/dataformatpanel.cpp --- a/src/dataformatpanel.cpp +++ b/src/dataformatpanel.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2015 Hasan Yavuz Özderya + Copyright © 2016 Hasan Yavuz Özderya This file is part of serialplot. @@ -20,6 +20,7 @@ #include "dataformatpanel.h" #include "ui_dataformatpanel.h" +#include #include #include @@ -30,7 +31,11 @@ DataFormatPanel::DataFormatPanel(QSerial ChannelManager* channelMan, QWidget *parent) : QWidget(parent), - ui(new Ui::DataFormatPanel) + ui(new Ui::DataFormatPanel), + bsReader(port, channelMan, this), + asciiReader(port, channelMan, this), + framedReader(port, channelMan, this), + demoReader(port, channelMan, this) { ui->setupUi(this); @@ -38,41 +43,36 @@ DataFormatPanel::DataFormatPanel(QSerial _channelMan = channelMan; paused = false; - // setup number format buttons - numberFormatButtons.addButton(ui->rbUint8, NumberFormat_uint8); - numberFormatButtons.addButton(ui->rbUint16, NumberFormat_uint16); - numberFormatButtons.addButton(ui->rbUint32, NumberFormat_uint32); - numberFormatButtons.addButton(ui->rbInt8, NumberFormat_int8); - numberFormatButtons.addButton(ui->rbInt16, NumberFormat_int16); - numberFormatButtons.addButton(ui->rbInt32, NumberFormat_int32); - numberFormatButtons.addButton(ui->rbFloat, NumberFormat_float); - numberFormatButtons.addButton(ui->rbASCII, NumberFormat_ASCII); - - QObject::connect( - &numberFormatButtons, SIGNAL(buttonToggled(int, bool)), - this, SLOT(onNumberFormatButtonToggled(int, bool))); - - // init number format - selectNumberFormat((NumberFormat) numberFormatButtons.checkedId()); + // initalize default reader + currentReader = &bsReader; + bsReader.enable(); + ui->rbBinary->setChecked(true); + ui->horizontalLayout->addWidget(bsReader.settingsWidget(), 1); + connect(&bsReader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded())); + connect(&bsReader, SIGNAL(numOfChannelsChanged(unsigned)), + this, SIGNAL(numOfChannelsChanged(unsigned))); + connect(&bsReader, SIGNAL(samplesPerSecondChanged(unsigned)), + this, SIGNAL(samplesPerSecondChanged(unsigned))); - // setup number of channels spinbox - QObject::connect(ui->spNumOfChannels, - SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), - this, &DataFormatPanel::onNumOfChannelsSP); - - _numOfChannels = ui->spNumOfChannels->value(); + // initalize reader selection buttons + connect(ui->rbBinary, &QRadioButton::toggled, [this](bool checked) + { + if (checked) selectReader(&bsReader); + }); - // Init sps (sample per second) counter - sampleCount = 0; - QObject::connect(&spsTimer, &QTimer::timeout, - this, &DataFormatPanel::spsTimerTimeout); - spsTimer.start(SPS_UPDATE_TIMEOUT * 1000); + connect(ui->rbAscii, &QRadioButton::toggled, [this](bool checked) + { + if (checked) selectReader(&asciiReader); + }); - // Init demo mode - demoCount = 0; - demoTimer.setInterval(100); - QObject::connect(&demoTimer, &QTimer::timeout, - this, &DataFormatPanel::demoTimerTimeout); + connect(ui->rbFramed, &QRadioButton::toggled, [this](bool checked) + { + if (checked) selectReader(&framedReader); + }); + + // re-purpose numofchannels settings from actual reader settings to demo reader + connect(this, &DataFormatPanel::numOfChannelsChanged, + &demoReader, &DemoReader::setNumOfChannels); } DataFormatPanel::~DataFormatPanel() @@ -80,246 +80,68 @@ DataFormatPanel::~DataFormatPanel() delete ui; } -void DataFormatPanel::onNumberFormatButtonToggled(int numberFormatId, - bool checked) -{ - if (checked) selectNumberFormat((NumberFormat) numberFormatId); -} - -void DataFormatPanel::selectNumberFormat(NumberFormat numberFormatId) -{ - numberFormat = numberFormatId; - - switch(numberFormat) - { - case NumberFormat_uint8: - sampleSize = 1; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int8: - sampleSize = 1; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_uint16: - sampleSize = 2; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int16: - sampleSize = 2; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_uint32: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_int32: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_float: - sampleSize = 4; - readSample = &DataFormatPanel::readSampleAs; - break; - case NumberFormat_ASCII: - sampleSize = 0; // these two members should not be used - readSample = NULL; // in this mode - break; - } - - if (numberFormat == NumberFormat_ASCII) - { - QObject::disconnect(serialPort, &QSerialPort::readyRead, 0, 0); - QObject::connect(this->serialPort, &QSerialPort::readyRead, - this, &DataFormatPanel::onDataReadyASCII); - } - else - { - QObject::disconnect(serialPort, &QSerialPort::readyRead, 0, 0); - QObject::connect(serialPort, &QSerialPort::readyRead, - this, &DataFormatPanel::onDataReady); - } - - emit skipByteEnabledChanged(skipByteEnabled()); -} - -bool DataFormatPanel::skipByteEnabled() -{ - return numberFormat != NumberFormat_ASCII; -} - unsigned DataFormatPanel::numOfChannels() { - return _numOfChannels; -} - -void DataFormatPanel::onNumOfChannelsSP(int value) -{ - _numOfChannels = value; - emit numOfChannelsChanged(value); -} - -void DataFormatPanel::requestSkipByte() -{ - skipByteRequested = true; + return currentReader->numOfChannels(); } void DataFormatPanel::pause(bool enabled) { paused = enabled; + currentReader->pause(enabled); + demoReader.pause(enabled); } void DataFormatPanel::enableDemo(bool enabled) { if (enabled) { - demoTimer.start(); + demoReader.enable(); + connect(&demoReader, &DemoReader::dataAdded, + this, &DataFormatPanel::dataAdded); + connect(&demoReader, &DemoReader::samplesPerSecondChanged, + this, &DataFormatPanel::samplesPerSecondChanged); } else { - demoTimer.stop(); - } -} - -void DataFormatPanel::spsTimerTimeout() -{ - unsigned currentSps = _samplesPerSecond; - _samplesPerSecond = (sampleCount/_numOfChannels)/SPS_UPDATE_TIMEOUT; - if (currentSps != _samplesPerSecond) - { - emit samplesPerSecondChanged(_samplesPerSecond); - } - sampleCount = 0; -} - - -void DataFormatPanel::demoTimerTimeout() -{ - const double period = 100; - demoCount++; - if (demoCount >= 100) demoCount = 0; - - if (!paused) - { - for (unsigned ci = 0; ci < _numOfChannels; ci++) - { - // 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); - addChannelData(ci, &value, 1); - } - emit dataAdded(); - } -} - -void DataFormatPanel::onDataReady() -{ - // a package is a set of channel data like {CHAN0_SAMPLE, CHAN1_SAMPLE...} - int packageSize = sampleSize * _numOfChannels; - int bytesAvailable = serialPort->bytesAvailable(); - int numOfPackagesToRead = - (bytesAvailable - (bytesAvailable % packageSize)) / packageSize; - - if (paused) - { - // read and discard data - serialPort->read(numOfPackagesToRead*packageSize); - return; - } - - if (bytesAvailable > 0 && skipByteRequested) - { - serialPort->read(1); - skipByteRequested = false; - bytesAvailable--; - } - - if (bytesAvailable < packageSize) return; - - double* channelSamples = new double[numOfPackagesToRead*_numOfChannels]; - - for (int i = 0; i < numOfPackagesToRead; i++) - { - for (unsigned int ci = 0; ci < _numOfChannels; ci++) - { - // channelSamples[ci].replace(i, (this->*readSample)()); - channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)(); - } + demoReader.enable(false); + disconnect(&demoReader, 0, this, 0); } - - for (unsigned int ci = 0; ci < _numOfChannels; ci++) - { - addChannelData(ci, - channelSamples + ci*numOfPackagesToRead, - numOfPackagesToRead); - } - emit dataAdded(); - - delete channelSamples; -} - -void DataFormatPanel::onDataReadyASCII() -{ - while(serialPort->canReadLine()) - { - QByteArray line = serialPort->readLine(); - - // discard data if paused - if (paused) - { - 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, &channelSample, 1); - } - else - { - qWarning() << "Data parsing error for channel: " << ci; - } - } - emit dataAdded(); - } -} - -template double DataFormatPanel::readSampleAs() -{ - T data; - serialPort->read((char*) &data, sizeof(data)); - - if (ui->rbLittleE->isChecked()) - { - data = qFromLittleEndian(data); - } - else - { - data = qFromBigEndian(data); - } - - return double(data); } void DataFormatPanel::addChannelData(unsigned int channel, double* data, unsigned size) { _channelMan->addChannelData(channel, data, size); - sampleCount += size; } + +void DataFormatPanel::selectReader(AbstractReader* reader) +{ + currentReader->enable(false); + reader->enable(); + + // re-connect signals + disconnect(currentReader, 0, this, 0); + connect(reader, SIGNAL(dataAdded()), this, SIGNAL(dataAdded())); + connect(reader, SIGNAL(numOfChannelsChanged(unsigned)), + this, SIGNAL(numOfChannelsChanged(unsigned))); + connect(reader, SIGNAL(samplesPerSecondChanged(unsigned)), + this, SIGNAL(samplesPerSecondChanged(unsigned))); + + // switch the settings widget + ui->horizontalLayout->removeWidget(currentReader->settingsWidget()); + currentReader->settingsWidget()->hide(); + ui->horizontalLayout->addWidget(reader->settingsWidget(), 1); + reader->settingsWidget()->show(); + + // notify if number of channels is different + if (currentReader->numOfChannels() != reader->numOfChannels()) + { + emit numOfChannelsChanged(reader->numOfChannels()); + } + + // pause + reader->pause(paused); + + currentReader = reader; +} diff --git a/src/dataformatpanel.h b/src/dataformatpanel.h --- a/src/dataformatpanel.h +++ b/src/dataformatpanel.h @@ -1,5 +1,5 @@ /* - Copyright © 2015 Hasan Yavuz Özderya + Copyright © 2016 Hasan Yavuz Özderya This file is part of serialplot. @@ -29,6 +29,10 @@ #include "framebuffer.h" #include "channelmanager.h" +#include "binarystreamreader.h" +#include "asciireader.h" +#include "demoreader.h" +#include "framedreader.h" namespace Ui { class DataFormatPanel; @@ -45,74 +49,36 @@ public: ~DataFormatPanel(); unsigned numOfChannels(); - unsigned samplesPerSecond(); - bool skipByteEnabled(void); // true for binary formats public slots: - // during next read operation reader will skip 1 byte, - // requests are not accumulated - void requestSkipByte(); void pause(bool); void enableDemo(bool); // demo shouldn't be enabled when port is open signals: void numOfChannelsChanged(unsigned); void samplesPerSecondChanged(unsigned); - void skipByteEnabledChanged(bool); void dataAdded(); private: - enum NumberFormat - { - NumberFormat_uint8, - NumberFormat_uint16, - NumberFormat_uint32, - NumberFormat_int8, - NumberFormat_int16, - NumberFormat_int32, - NumberFormat_float, - NumberFormat_ASCII - }; - Ui::DataFormatPanel *ui; - QButtonGroup numberFormatButtons; QSerialPort* serialPort; ChannelManager* _channelMan; - unsigned int _numOfChannels; - NumberFormat numberFormat; - unsigned int sampleSize; // number of bytes in the selected number format - bool skipByteRequested; + BinaryStreamReader bsReader; + AsciiReader asciiReader; + FramedReader framedReader; + /// Currently selected reader + AbstractReader* currentReader; + /// Disable current reader and enable a another one + void selectReader(AbstractReader* reader); + bool paused; - const int SPS_UPDATE_TIMEOUT = 1; // second - unsigned _samplesPerSecond; - unsigned int sampleCount; - QTimer spsTimer; - - // demo - QTimer demoTimer; - int demoCount; - - void selectNumberFormat(NumberFormat numberFormatId); - - // points to the readSampleAs function for currently selected number format - double (DataFormatPanel::*readSample)(); - - // note that serialPort should already have enough bytes present - template double readSampleAs(); + DemoReader demoReader; // `data` contains i th channels data void addChannelData(unsigned int channel, double* data, unsigned size); - -private slots: - void onDataReady(); // used with binary number formats - void onDataReadyASCII(); // used with ASCII number format - void onNumberFormatButtonToggled(int numberFormatId, bool checked); - void onNumOfChannelsSP(int value); - void spsTimerTimeout(); - void demoTimerTimeout(); }; #endif // DATAFORMATPANEL_H diff --git a/src/dataformatpanel.ui b/src/dataformatpanel.ui --- a/src/dataformatpanel.ui +++ b/src/dataformatpanel.ui @@ -15,195 +15,38 @@ - - - - - Number Of Channels: + + + + + Data is sent as consecutive samples in binary form. Syncing can be a problem. - - - - - - - 60 - 0 - + + Simple Binary - - false - - - 1 - - - 32 + + true - - - - - - Qt::Vertical - - - - - - - 0 - - - - Number Format: + + + Data is sent in the form of ASCII text as comma seperated values. Easy to implement. - - - - - unsigned 4 bytes integer - - - uint32 - - - - - - - signed 1 byte integer - - - int8 - - - - - - - unsigned 1 byte integer - - - uint8 - - - true - - - - - - - Comma Separated Values - - - ASCII(CSV) - - - - - - - signed 2 bytes integer - - - int16 - - - - - - - signed 4 bytes integer - - - int32 - - - - - - - 4 bytes floating point number - - - float - - - - - - - unsigned 2 bytes integer - - - uint16 - - - - + + ASCII + - - - Qt::Vertical - - - - 20 - 1 - - - - - - - - - - 0 - - - - - - 0 - 0 - + + + Define a custom binary frame. Powerful. - - Byte Order: + + Custom Frame - - - - - least significant byte first - - - Little Endian - - - true - - - - - - - most significant byte first - - - Big Endian - - - - @@ -214,7 +57,7 @@ 20 - 1 + 40 @@ -222,20 +65,11 @@ - + - Qt::Horizontal - - - QSizePolicy::MinimumExpanding + Qt::Vertical - - - 37 - 20 - - - + diff --git a/src/demoreader.cpp b/src/demoreader.cpp new file mode 100644 --- /dev/null +++ b/src/demoreader.cpp @@ -0,0 +1,82 @@ +/* + Copyright © 2016 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 "demoreader.h" + +DemoReader::DemoReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + _numOfChannels = 1; + count = 0; + timer.setInterval(100); + QObject::connect(&timer, &QTimer::timeout, + this, &DemoReader::demoTimerTimeout); +} + +QWidget* DemoReader::settingsWidget() +{ + return NULL; +} + +void DemoReader::enable(bool enabled) +{ + if (enabled) + { + timer.start(); + } + else + { + timer.stop(); + } +} + +unsigned DemoReader::numOfChannels() +{ + return _numOfChannels; +} + +void DemoReader::setNumOfChannels(unsigned value) +{ + _numOfChannels = value; +} + +void DemoReader::pause(bool enabled) +{ + paused = enabled; +} + +void DemoReader::demoTimerTimeout() +{ + const double period = 100; + count++; + if (count >= 100) count = 0; + + if (!paused) + { + for (unsigned ci = 0; ci < _numOfChannels; ci++) + { + // we are calculating the fourier components of square wave + double value = 4*sin(2*M_PI*double((ci+1)*count)/period)/((2*(ci+1))*M_PI); + _channelMan->addChannelData(ci, &value, 1); + sampleCount++; + } + emit dataAdded(); + } +} diff --git a/src/demoreader.h b/src/demoreader.h new file mode 100644 --- /dev/null +++ b/src/demoreader.h @@ -0,0 +1,66 @@ +/* + Copyright © 2016 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 DEMOREADER_H +#define DEMOREADER_H + +#include + +#include "abstractreader.h" + +/** + * This is a special case of reader implementation and should be used + * with care. + * + * There is no settings widget. Number of channels should be set from + * currently selected actual readers settings widget. + * + * This reader should not be enabled when port is open! + */ +class DemoReader : public AbstractReader +{ + Q_OBJECT + +public: + explicit DemoReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + + /// Demo reader is an exception so this function returns NULL + QWidget* settingsWidget(); + + unsigned numOfChannels(); + + void enable(bool enabled = true); + +public slots: + void pause(bool); + + /// Sets the number of channels, this doesn't trigger a `numOfChannelsChanged` signal. + void setNumOfChannels(unsigned value); + +private: + bool paused; + unsigned _numOfChannels; + QTimer timer; + int count; + +private slots: + void demoTimerTimeout(); +}; + +#endif // DEMOREADER_H diff --git a/src/endiannessbox.cpp b/src/endiannessbox.cpp new file mode 100644 --- /dev/null +++ b/src/endiannessbox.cpp @@ -0,0 +1,50 @@ +/* + Copyright © 2016 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 "endiannessbox.h" +#include "ui_endiannessbox.h" + +EndiannessBox::EndiannessBox(QWidget *parent) : + QWidget(parent), + ui(new Ui::EndiannessBox) +{ + ui->setupUi(this); + + connect(ui->rbLittleE, &QRadioButton::toggled, [this](bool checked) + { + emit selectionChanged(currentSelection()); + }); +} + +EndiannessBox::~EndiannessBox() +{ + delete ui; +} + +Endianness EndiannessBox::currentSelection() +{ + if (ui->rbLittleE->isChecked()) + { + return LittleEndian; + } + else + { + return BigEndian; + } +} diff --git a/src/endiannessbox.h b/src/endiannessbox.h new file mode 100644 --- /dev/null +++ b/src/endiannessbox.h @@ -0,0 +1,53 @@ +/* + Copyright © 2016 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 ENDIANNESSBOX_H +#define ENDIANNESSBOX_H + +#include + +namespace Ui { +class EndiannessBox; +} + +enum Endianness +{ + LittleEndian, + BigEndian +}; + +class EndiannessBox : public QWidget +{ + Q_OBJECT + +public: + explicit EndiannessBox(QWidget *parent = 0); + ~EndiannessBox(); + + Endianness currentSelection(); ///< currently selected endianness + +signals: + /// Signaled when endianness selection is changed + void selectionChanged(Endianness endianness); + +private: + Ui::EndiannessBox *ui; +}; + +#endif // ENDIANNESSBOX_H diff --git a/src/endiannessbox.ui b/src/endiannessbox.ui new file mode 100644 --- /dev/null +++ b/src/endiannessbox.ui @@ -0,0 +1,59 @@ + + + EndiannessBox + + + + 0 + 0 + 202 + 22 + + + + EndiannessBox + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + least significant byte first + + + Little Endian + + + true + + + + + + + most significant byte first + + + Big Endian + + + + + + + + diff --git a/src/framedreader.cpp b/src/framedreader.cpp new file mode 100644 --- /dev/null +++ b/src/framedreader.cpp @@ -0,0 +1,345 @@ +/* + Copyright © 2016 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 +#include +#include "floatswap.h" + +#include "framedreader.h" + +FramedReader::FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent) : + AbstractReader(device, channelMan, parent) +{ + paused = false; + + // initial settings + settingsInvalid = 0; + _numOfChannels = _settingsWidget.numOfChannels(); + hasSizeByte = _settingsWidget.frameSize() == 0; + frameSize = _settingsWidget.frameSize(); + syncWord = _settingsWidget.syncWord(); + checksumEnabled = _settingsWidget.isChecksumEnabled(); + onNumberFormatChanged(_settingsWidget.numberFormat()); + debugModeEnabled = _settingsWidget.isDebugModeEnabled(); + checkSettings(); + + // init setting connections + connect(&_settingsWidget, &FramedReaderSettings::numberFormatChanged, + this, &FramedReader::onNumberFormatChanged); + + connect(&_settingsWidget, &FramedReaderSettings::numOfChannelsChanged, + this, &FramedReader::onNumOfChannelsChanged); + + connect(&_settingsWidget, &FramedReaderSettings::syncWordChanged, + this, &FramedReader::onSyncWordChanged); + + connect(&_settingsWidget, &FramedReaderSettings::frameSizeChanged, + this, &FramedReader::onFrameSizeChanged); + + connect(&_settingsWidget, &FramedReaderSettings::checksumChanged, + [this](bool enabled){checksumEnabled = enabled; reset();}); + + connect(&_settingsWidget, &FramedReaderSettings::debugModeChanged, + [this](bool enabled){debugModeEnabled = enabled;}); + + // init reader state + reset(); +} + +void FramedReader::enable(bool enabled) +{ + if (enabled) + { + connect(_device, &QIODevice::readyRead, + this, &FramedReader::onDataReady); + } + else + { + QObject::disconnect(_device, 0, this, 0); + } +} + +QWidget* FramedReader::settingsWidget() +{ + return &_settingsWidget; +} + +unsigned FramedReader::numOfChannels() +{ + return _numOfChannels; +} + +void FramedReader::pause(bool enabled) +{ + paused = enabled; +} + +void FramedReader::onNumberFormatChanged(NumberFormat numberFormat) +{ + switch(numberFormat) + { + case NumberFormat_uint8: + sampleSize = 1; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_int8: + sampleSize = 1; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_uint16: + sampleSize = 2; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_int16: + sampleSize = 2; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_uint32: + sampleSize = 4; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_int32: + sampleSize = 4; + readSample = &FramedReader::readSampleAs; + break; + case NumberFormat_float: + sampleSize = 4; + readSample = &FramedReader::readSampleAs; + break; + } + + checkSettings(); + reset(); +} + +void FramedReader::checkSettings() +{ + // sync word is invalid (empty or missing a nibble at the end) + if (!syncWord.size()) + { + settingsInvalid |= SYNCWORD_INVALID; + } + else // sync word is valid + { + settingsInvalid &= ~SYNCWORD_INVALID; + } + + // check if fixed frame size is multiple of a sample set size + if (!hasSizeByte && frameSize % (_numOfChannels * sampleSize) != 0) + { + settingsInvalid |= FRAMESIZE_INVALID; + } + else + { + settingsInvalid &= ~FRAMESIZE_INVALID; + } + + // show an error message + if (settingsInvalid & SYNCWORD_INVALID) + { + _settingsWidget.showMessage("Sync word is invalid!", true); + } + else if (settingsInvalid & FRAMESIZE_INVALID) + { + QString errorMessage = + QString("Frame size must be multiple of %1 (#channels * sample size)!")\ + .arg(_numOfChannels * sampleSize); + + _settingsWidget.showMessage(errorMessage, true); + } + else + { + _settingsWidget.showMessage("All is well!"); + } +} + +void FramedReader::onNumOfChannelsChanged(unsigned value) +{ + _numOfChannels = value; + checkSettings(); + reset(); + emit numOfChannelsChanged(value); +} + +void FramedReader::onSyncWordChanged(QByteArray word) +{ + syncWord = word; + checkSettings(); + reset(); +} + +void FramedReader::onFrameSizeChanged(unsigned value) +{ + if (value == 0) + { + hasSizeByte = true; + } + else + { + hasSizeByte = false; + frameSize = value; + } + checkSettings(); + reset(); +} + +void FramedReader::onDataReady() +{ + if (settingsInvalid) return; + + // loop until we run out of bytes or more bytes is required + unsigned bytesAvailable; + while ((bytesAvailable = _device->bytesAvailable())) + { + if (!gotSync) // read sync word + { + char c; + _device->getChar(&c); + if (c == syncWord[sync_i]) // correct sync byte? + { + sync_i++; + if (sync_i == (unsigned) syncWord.length()) + { + gotSync = true; + } + } + else + { + if (debugModeEnabled) qCritical() << "Missed " << sync_i+1 << "th sync byte."; + } + } + else if (hasSizeByte && !gotSize) // skipped if fixed frame size + { + frameSize = 0; + _device->getChar((char*) &frameSize); + + if (frameSize == 0) // check size + { + qCritical() << "Frame size is 0!"; + reset(); + } + else if (frameSize % (_numOfChannels * sampleSize) != 0) + { + qCritical() << + QString("Frame size is not multiple of %1 (#channels * sample size)!") \ + .arg(_numOfChannels * sampleSize); + reset(); + } + else + { + if (debugModeEnabled) qDebug() << "Frame size:" << frameSize; + gotSize = true; + } + } + else // read data bytes + { + // have enough data bytes? (+1 for checksum) + if (bytesAvailable < (checksumEnabled ? frameSize+1 : frameSize)) + { + break; + } + else // read data bytes and checksum + { + readFrameDataAndCheck(); + reset(); + } + } + } +} + +void FramedReader::reset() +{ + sync_i = 0; + gotSync = false; + gotSize = false; + if (hasSizeByte) frameSize = 0; + calcChecksum = 0; +} + +// Important: this function assumes device has enough bytes to read a full frames data and checksum +void FramedReader::readFrameDataAndCheck() +{ + // a package is 1 set of samples for all channels + unsigned numOfPackagesToRead = frameSize / (_numOfChannels * sampleSize); + double* channelSamples = new double[numOfPackagesToRead * _numOfChannels]; + + for (unsigned i = 0; i < numOfPackagesToRead; i++) + { + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + channelSamples[ci*numOfPackagesToRead+i] = (this->*readSample)(); + } + } + + // read checksum + unsigned rChecksum = 0; + bool checksumPassed = false; + if (checksumEnabled) + { + _device->read((char*) &rChecksum, 1); + calcChecksum &= 0xFF; + checksumPassed = (calcChecksum == rChecksum); + } + + if (!checksumEnabled || checksumPassed) + { + // commit data + for (unsigned int ci = 0; ci < _numOfChannels; ci++) + { + _channelMan->addChannelData( + ci, + channelSamples + ci*numOfPackagesToRead, + numOfPackagesToRead); + sampleCount += numOfPackagesToRead; + } + emit dataAdded(); + } + else + { + qCritical() << "Checksum failed! Received:" << rChecksum << "Calculated:" << calcChecksum; + } + + delete channelSamples; +} + +template double FramedReader::readSampleAs() +{ + T data; + + _device->read((char*) &data, sizeof(data)); + + if (checksumEnabled) + { + for (unsigned i = 0; i < sizeof(data); i++) + { + calcChecksum += ((unsigned char*) &data)[i]; + } + } + + if (_settingsWidget.endianness() == LittleEndian) + { + data = qFromLittleEndian(data); + } + else + { + data = qFromBigEndian(data); + } + + return double(data); +} diff --git a/src/framedreader.h b/src/framedreader.h new file mode 100644 --- /dev/null +++ b/src/framedreader.h @@ -0,0 +1,92 @@ +/* + Copyright © 2016 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 FRAMEDREADER_H +#define FRAMEDREADER_H + +#include "abstractreader.h" +#include "framedreadersettings.h" + +/** + * Reads data in a customizable framed format. + */ +class FramedReader : public AbstractReader +{ + Q_OBJECT + +public: + explicit FramedReader(QIODevice* device, ChannelManager* channelMan, QObject *parent = 0); + QWidget* settingsWidget(); + unsigned numOfChannels(); + void enable(bool enabled = true); + +public slots: + void pause(bool); + +private: + /// bit wise fields for `settingsValid` member + enum SettingInvalidFlag + { + SYNCWORD_INVALID = 1, + FRAMESIZE_INVALID = 2 + }; + + // settings related members + FramedReaderSettings _settingsWidget; + unsigned _numOfChannels; + unsigned sampleSize; + bool paused; + unsigned settingsInvalid; /// settings are all valid if this is 0, if not no reading is done + QByteArray syncWord; + bool checksumEnabled; + bool hasSizeByte; + unsigned frameSize; + bool debugModeEnabled; + + /// Checks the validity of syncWord and frameSize then shows an + /// error message. Also updates `settingsInvalid`. If settings are + /// valid `settingsInvalid` should be `0`. + void checkSettings(); + + // read state related members + unsigned sync_i; /// sync byte index to be read next + bool gotSync; /// indicates if sync word is captured + bool gotSize; /// indicates if size is captured, ignored if size byte is disabled (fixed size) + unsigned calcChecksum; + + void reset(); /// Resets the reading state. Used in case of error or setting change. + /// points to the readSampleAs function for currently selected number format + double (FramedReader::*readSample)(); + template double readSampleAs(); + /// reads payload portion of the frame, calculates checksum and commits data + /// @note should be called only if there are enough bytes on device + void readFrameDataAndCheck(); + // `data` contains i th channels data + void addChannelData(unsigned int channel, double* data, unsigned size); + +private slots: + void onDataReady(); + + void onNumberFormatChanged(NumberFormat numberFormat); + void onNumOfChannelsChanged(unsigned value); + void onSyncWordChanged(QByteArray); + void onFrameSizeChanged(unsigned); +}; + +#endif // FRAMEDREADER_H diff --git a/src/framedreadersettings.cpp b/src/framedreadersettings.cpp new file mode 100644 --- /dev/null +++ b/src/framedreadersettings.cpp @@ -0,0 +1,146 @@ +/* + Copyright © 2016 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 "utils.h" +#include "framedreadersettings.h" +#include "ui_framedreadersettings.h" + +FramedReaderSettings::FramedReaderSettings(QWidget *parent) : + QWidget(parent), + ui(new Ui::FramedReaderSettings) +{ + ui->setupUi(this); + + ui->leSyncWord->setMode(false); // hex mode + ui->leSyncWord->setText("AA BB"); + + connect(ui->cbChecksum, &QCheckBox::toggled, + [this](bool enabled) + { + emit checksumChanged(enabled); + }); + + connect(ui->cbDebugMode, &QCheckBox::toggled, + this, &FramedReaderSettings::debugModeChanged); + + connect(ui->rbFixedSize, &QRadioButton::toggled, + ui->spSize, &QWidget::setEnabled); + + connect(ui->rbFixedSize, &QRadioButton::toggled, + [this](bool checked) + { + emit frameSizeChanged(frameSize()); + }); + + // Note: if directly connected we get a runtime warning on incompatible signal arguments + connect(ui->spSize, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit frameSizeChanged(value); + }); + + connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), + [this](int value) + { + emit numOfChannelsChanged(value); + }); + + connect(ui->leSyncWord, &QLineEdit::textChanged, + this, &FramedReaderSettings::onSyncWordEdited); + + connect(ui->nfBox, SIGNAL(selectionChanged(NumberFormat)), + this, SIGNAL(numberFormatChanged(NumberFormat))); +} + +FramedReaderSettings::~FramedReaderSettings() +{ + delete ui; +} + +void FramedReaderSettings::showMessage(QString message, bool error) +{ + ui->lMessage->setText(message); + if (error) + { + ui->lMessage->setStyleSheet("color: red;"); + } + else + { + ui->lMessage->setStyleSheet(""); + } +} + +unsigned FramedReaderSettings::numOfChannels() +{ + return ui->spNumOfChannels->value(); +} + +NumberFormat FramedReaderSettings::numberFormat() +{ + return ui->nfBox->currentSelection(); +} + +Endianness FramedReaderSettings::endianness() +{ + return ui->endiBox->currentSelection(); +} + +QByteArray FramedReaderSettings::syncWord() +{ + QString text = ui->leSyncWord->text().remove(' '); + + // check if nibble is missing + if (text.size() % 2 == 1) + { + // TODO: remove this warning + return QByteArray(); + } + else + { + return QByteArray::fromHex(text.toLatin1()); + } +} + +void FramedReaderSettings::onSyncWordEdited() +{ + // TODO: emit with a delay so that error message doesn't flash! + emit syncWordChanged(syncWord()); +} + +unsigned FramedReaderSettings::frameSize() +{ + if (ui->rbFixedSize->isChecked()) + { + return ui->spSize->value(); + } + else + { + return 0; // frame byte is enabled + } +} + +bool FramedReaderSettings::isChecksumEnabled() +{ + return ui->cbChecksum->isChecked(); +} + +bool FramedReaderSettings::isDebugModeEnabled() +{ + return ui->cbDebugMode->isChecked(); +} diff --git a/src/framedreadersettings.h b/src/framedreadersettings.h new file mode 100644 --- /dev/null +++ b/src/framedreadersettings.h @@ -0,0 +1,69 @@ +/* + Copyright © 2016 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 FRAMEDREADERSETTINGS_H +#define FRAMEDREADERSETTINGS_H + +#include +#include + +#include "numberformatbox.h" +#include "endiannessbox.h" + +namespace Ui { +class FramedReaderSettings; +} + +class FramedReaderSettings : public QWidget +{ + Q_OBJECT + +public: + explicit FramedReaderSettings(QWidget *parent = 0); + ~FramedReaderSettings(); + + void showMessage(QString message, bool error = false); + + unsigned numOfChannels(); + NumberFormat numberFormat(); + Endianness endianness(); + QByteArray syncWord(); + unsigned frameSize(); /// If frame bye is enabled `0` is returned + bool isChecksumEnabled(); + bool isDebugModeEnabled(); + +signals: + /// If sync word is invalid (empty or 1 nibble missing at the end) + /// signaled with an empty array + void syncWordChanged(QByteArray); + /// `0` indicates frame size byte is enabled + void frameSizeChanged(unsigned); + void checksumChanged(bool); + void numOfChannelsChanged(unsigned); + void numberFormatChanged(NumberFormat); + void debugModeChanged(bool); + +private: + Ui::FramedReaderSettings *ui; + +private slots: + void onSyncWordEdited(); +}; + +#endif // FRAMEDREADERSETTINGS_H diff --git a/src/framedreadersettings.ui b/src/framedreadersettings.ui new file mode 100644 --- /dev/null +++ b/src/framedreadersettings.ui @@ -0,0 +1,239 @@ + + + FramedReaderSettings + + + + 0 + 0 + 852 + 222 + + + + Form + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFormLayout::FieldsStayAtSizeHint + + + + + Enter the 'Frame Start' bytes in hexadecimal. + + + + + + + Number of Channels + + + # Channels: + + + + + + + Select number of channels + + + 1 + + + 32 + + + + + + + Frame Size: + + + + + + + + + Frame size is always the same + + + Fixed Size: + + + + + + + false + + + <html><head/><body><p>Enter the frame size. It <span style=" font-weight:600;">must</span> be the multiple of (#channels * sample size).</p></body></html> + + + 1 + + + 255 + + + + + + + First byte after the 'frame start' bytes should be the size of the frame. Not counting itself and checksum. + + + First byte of the payload is size + + + true + + + + + + + + + + + + Number Type: + + + + + + + + 0 + 0 + + + + + + + + Byte Order + + + Endianness: + + + + + + + + + + Checksum: + + + + + + + Last byte of the frame is checksum. + + + Enabled + + + + + + + Frame Start: + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + 0 + 0 + + + + All is well. + + + + + + + Enable printing of extra log messages that can be useful for debugging + + + Debug Mode + + + + + + + + + + NumberFormatBox + QWidget +
numberformatbox.h
+ 1 +
+ + EndiannessBox + QWidget +
endiannessbox.h
+ 1 +
+ + CommandEdit + QLineEdit +
commandedit.h
+
+
+ + +
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -105,9 +105,6 @@ MainWindow::MainWindow(QWidget *parent) QObject::connect(&portControl, &PortControl::portToggled, this, &MainWindow::onPortToggled); - QObject::connect(&portControl, &PortControl::skipByteRequested, - &dataFormatPanel, &DataFormatPanel::requestSkipByte); - connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged, this, &MainWindow::onNumOfSamplesChanged); diff --git a/src/numberformatbox.cpp b/src/numberformatbox.cpp new file mode 100644 --- /dev/null +++ b/src/numberformatbox.cpp @@ -0,0 +1,56 @@ +/* + Copyright © 2016 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 "numberformatbox.h" +#include "ui_numberformatbox.h" + +NumberFormatBox::NumberFormatBox(QWidget *parent) : + QWidget(parent), + ui(new Ui::NumberFormatBox) +{ + ui->setupUi(this); + + // setup buttons + buttonGroup.addButton(ui->rbUint8, NumberFormat_uint8); + buttonGroup.addButton(ui->rbUint16, NumberFormat_uint16); + buttonGroup.addButton(ui->rbUint32, NumberFormat_uint32); + buttonGroup.addButton(ui->rbInt8, NumberFormat_int8); + buttonGroup.addButton(ui->rbInt16, NumberFormat_int16); + buttonGroup.addButton(ui->rbInt32, NumberFormat_int32); + buttonGroup.addButton(ui->rbFloat, NumberFormat_float); + + QObject::connect( + &buttonGroup, SIGNAL(buttonToggled(int, bool)), + this, SLOT(onButtonToggled(int, bool))); +} + +NumberFormatBox::~NumberFormatBox() +{ + delete ui; +} + +void NumberFormatBox::onButtonToggled(int numberFormatId, bool checked) +{ + if (checked) emit selectionChanged((NumberFormat) numberFormatId); +} + +NumberFormat NumberFormatBox::currentSelection() +{ + return (NumberFormat) buttonGroup.checkedId(); +} diff --git a/src/numberformatbox.h b/src/numberformatbox.h new file mode 100644 --- /dev/null +++ b/src/numberformatbox.h @@ -0,0 +1,63 @@ +/* + Copyright © 2016 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 NUMBERFORMATBOX_H +#define NUMBERFORMATBOX_H + +#include +#include + +namespace Ui { +class NumberFormatBox; +} + +enum NumberFormat +{ + NumberFormat_uint8, + NumberFormat_uint16, + NumberFormat_uint32, + NumberFormat_int8, + NumberFormat_int16, + NumberFormat_int32, + NumberFormat_float, +}; + +class NumberFormatBox : public QWidget +{ + Q_OBJECT + +public: + explicit NumberFormatBox(QWidget *parent = 0); + ~NumberFormatBox(); + + NumberFormat currentSelection(); ///< returns the currently selected number format + +signals: + /// Signaled when number format selection is changed + void selectionChanged(NumberFormat numberFormat); + +private: + Ui::NumberFormatBox *ui; + QButtonGroup buttonGroup; + +private slots: + void onButtonToggled(int numberFormatId, bool checked); +}; + +#endif // NUMBERFORMATBOX_H diff --git a/src/numberformatbox.ui b/src/numberformatbox.ui new file mode 100644 --- /dev/null +++ b/src/numberformatbox.ui @@ -0,0 +1,109 @@ + + + NumberFormatBox + + + + 0 + 0 + 440 + 22 + + + + NumberFormat + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + unsigned 1 byte integer + + + uint8 + + + true + + + + + + + unsigned 2 bytes integer + + + uint16 + + + + + + + unsigned 4 bytes integer + + + uint32 + + + + + + + signed 1 byte integer + + + int8 + + + + + + + signed 2 bytes integer + + + int16 + + + + + + + signed 4 bytes integer + + + int32 + + + + + + + 4 bytes floating point number + + + float + + + + + + + + diff --git a/src/portcontrol.cpp b/src/portcontrol.cpp --- a/src/portcontrol.cpp +++ b/src/portcontrol.cpp @@ -115,10 +115,6 @@ PortControl::PortControl(QSerialPort* po SELECT::OVERLOAD_OF(&QButtonGroup::buttonClicked), this, &PortControl::selectFlowControl); - // init skip byte button - QObject::connect(ui->pbSkipByte, &QPushButton::clicked, - [this](){emit skipByteRequested();}); - loadPortList(); loadBaudRateList(); ui->cbBaudRate->setCurrentIndex(ui->cbBaudRate->findText("9600")); @@ -272,11 +268,6 @@ void PortControl::selectPort(QString por } } -void PortControl::enableSkipByte(bool enabled) -{ - ui->pbSkipByte->setDisabled(enabled); -} - QToolBar* PortControl::toolBar() { return &portToolBar; diff --git a/src/portcontrol.h b/src/portcontrol.h --- a/src/portcontrol.h +++ b/src/portcontrol.h @@ -64,7 +64,6 @@ public slots: void loadBaudRateList(); void togglePort(); void selectPort(QString portName); - void enableSkipByte(bool enabled = true); void selectBaudRate(QString baudRate); void selectParity(int parity); // parity must be one of QSerialPort::Parity @@ -79,7 +78,6 @@ private slots: void onTbPortListActivated(int index); signals: - void skipByteRequested(); void portToggled(bool open); }; diff --git a/src/portcontrol.ui b/src/portcontrol.ui --- a/src/portcontrol.ui +++ b/src/portcontrol.ui @@ -7,7 +7,7 @@ 0 0 631 - 213 + 232 @@ -285,7 +285,7 @@ - 0 + 85 50 @@ -301,16 +301,6 @@
- - - Skip reading 1 byte to correct the alignment - - - Skip Byte - - - - Qt::Vertical