/* 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 "dataformatpanel.h" #include "ui_dataformatpanel.h" #include #include "utils.h" #include "floatswap.h" DataFormatPanel::DataFormatPanel(QSerialPort* port, QList* channelBuffers, QWidget *parent) : QWidget(parent), ui(new Ui::DataFormatPanel) { ui->setupUi(this); serialPort = port; _channelBuffers = channelBuffers; 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()); // setup number of channels spinbox QObject::connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), this, &DataFormatPanel::onNumOfChannelsSP); _numOfChannels = ui->spNumOfChannels->value(); // Init sps (sample per second) counter sampleCount = 0; QObject::connect(&spsTimer, &QTimer::timeout, this, &DataFormatPanel::spsTimerTimeout); spsTimer.start(SPS_UPDATE_TIMEOUT * 1000); // Init demo mode demoCount = 0; demoTimer.setInterval(100); QObject::connect(&demoTimer, &QTimer::timeout, this, &DataFormatPanel::demoTimerTimeout); } 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; } void DataFormatPanel::pause(bool enabled) { paused = enabled; } void DataFormatPanel::enableDemo(bool enabled) { if (enabled) { demoTimer.start(); } else { demoTimer.stop(); } } void DataFormatPanel::spsTimerTimeout() { unsigned currentSps = _samplesPerSecond; _samplesPerSecond = sampleCount/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() { // TODO: discard data in the size of packageSize if (paused) { 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; double* channelSamples = new double[numOfPackagesToRead*_numOfChannels]; // TODO: use `for`, it is for this int i = 0; while(i < numOfPackagesToRead) { for (unsigned int ci = 0; ci < _numOfChannels; ci++) { // 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*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) { (*_channelBuffers)[channel]->addSamples(data, size); sampleCount += size; }