From 015d41624d780e1245f5ae2dcd0324327cc08a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Mestre?= <36825825+SebastianMestre@users.noreply.github.com> Date: Wed, 23 Jan 2019 02:13:40 -0300 Subject: [PATCH] Isobuffer refactor (#64) * make isobuffer.cpp follow the common bracket style * Add a few utility functions to isoBuffer, and reorganize and comment isobuffer.h * refactor a few functions in isoBuffer using the newly added functions * Clean up isoBuffer::readBuffer. Requires particular attention during review * remove unused variable isoBuffer::firstTime * Fix formatting and add a comment to isoBuffer::maybeOutputSampleToFile * move invariant out of loop in isoBuffer::writeBuffer_short * Substantially refactor isoBuffer::readBuffer * change spaces to tabs on isoBuffer.h * spaces to tabs on isobuffer.cpp and isobuffer.h, but properly * Enforce const correctness on some methods of isoBuffer * Add some comments to isobuffer.cpp * Remove duplication in isoBuffer::cap_xnfromxxx functions by extracting common functionality to a free function * Format isobuffer.h and isobuffer.cpp * add inserted count to isoBuffer * further modification to isoBuffer::readBuffer * Correct a comment in isoBuffer::readBuffer * add an anonymous namespace with file-wide constants * remove isoBuffer::openFile, and move initialization isoBuffer to initializer list * add lines between if statements in isoBuffer::insertIntoBuffer * add spaces after // to some comments * extract common functionality from writeBuffer_xxx functions to function template * update header file to reflect previous commits * make isoBuffer::return nullptr in a troublesome case * add space after // to some other comments * Make capSample a member function of isoBuffer, delete a macro in isobuffer.cpp * clean up various comments and declarations in isobuffer.cpp * make isoBuffer::gainBuffer work for any gain value * make enableFileIO use the constant definitions made in a recent commit * Remove type argument from isoBuffer::serialManage * make isoBuffer::readBuffer return a zero-filled buffer instead of null * Remove isoBuffer::glitchInsert * Add a bunch of TODOs to isobuffer.cpp and isobuffer.h * Rename member variables of isoBuffer to have an m_ prefix * Move isoBuffer::capSample comparison functors to anonymous namespace * Make CONSOLE_UPDATE_TIMER_PERIOD on isobuffer.h a constexpr intead of a macro --- Desktop_Interface/i2cdecoder.cpp | 22 +- Desktop_Interface/isobuffer.cpp | 616 ++++++++++++------------- Desktop_Interface/isobuffer.h | 135 ++++-- Desktop_Interface/isodriver.cpp | 16 +- Desktop_Interface/mainwindow.cpp | 44 +- Desktop_Interface/uartstyledecoder.cpp | 34 +- 6 files changed, 429 insertions(+), 438 deletions(-) diff --git a/Desktop_Interface/i2cdecoder.cpp b/Desktop_Interface/i2cdecoder.cpp index 44b74851..be60ed65 100644 --- a/Desktop_Interface/i2cdecoder.cpp +++ b/Desktop_Interface/i2cdecoder.cpp @@ -20,14 +20,14 @@ void i2cDecoder::reset() { qDebug () << "Resetting I2C"; - if (sda->back != scl->back) + if (sda->m_back != scl->m_back) { // Perhaps the data could be saved, but just resetting them seems much safer sda->clearBuffer(); scl->clearBuffer(); } - serialPtr_bit = sda->back * 8; + serialPtr_bit = sda->m_back * 8; { std::lock_guard lock(mutex); @@ -40,20 +40,20 @@ void i2cDecoder::reset() void i2cDecoder::run() { // qDebug() << "i2cDecoder::run()"; - while (serialDistance(sda) > SERIAL_DELAY * sda->sampleRate_bit) + while (serialDistance(sda) > SERIAL_DELAY * sda->m_sampleRate_bit) { updateBitValues(); runStateMachine(); serialPtr_bit ++; - if (serialPtr_bit > (sda->bufferEnd * 8)) - serialPtr_bit -= (sda->bufferEnd * 8); + if (serialPtr_bit > (sda->m_bufferEnd * 8)) + serialPtr_bit -= (sda->m_bufferEnd * 8); } } int i2cDecoder::serialDistance(isoBuffer* buffer) { - int back_bit = buffer->back * 8; - int bufferEnd_bit = buffer->bufferEnd * 8; + int back_bit = buffer->m_back * 8; + int bufferEnd_bit = buffer->m_bufferEnd * 8; if (back_bit >= serialPtr_bit) return back_bit - serialPtr_bit; else @@ -66,8 +66,8 @@ void i2cDecoder::updateBitValues(){ int coord_byte = serialPtr_bit/8; int coord_bit = serialPtr_bit - (8*coord_byte); - unsigned char dataByteSda = sda->buffer[coord_byte]; - unsigned char dataByteScl = scl->buffer[coord_byte]; + unsigned char dataByteSda = sda->m_buffer[coord_byte]; + unsigned char dataByteScl = scl->m_buffer[coord_byte]; unsigned char mask = (0x01 << coord_bit); currentSdaValue = dataByteSda & mask; currentSclValue = dataByteScl & mask; @@ -91,7 +91,7 @@ void i2cDecoder::runStateMachine() state = transmissionState::unknown; qDebug() << "Dumping I2C state and aborting..."; for (int i=31; i>=0; i--) - qDebug("%02x\t%02x", sda->buffer[serialPtr_bit/8 - i] & 0xFF, scl->buffer[serialPtr_bit/8 - i] & 0xFF); + qDebug("%02x\t%02x", sda->m_buffer[serialPtr_bit/8 - i] & 0xFF, scl->m_buffer[serialPtr_bit/8 - i] & 0xFF); throw std::runtime_error("unknown i2c transmission state"); return; } @@ -217,7 +217,7 @@ void i2cDecoder::updateConsole(){ return; console->setPlainText(QString::fromLocal8Bit(serialBuffer->begin(), serialBuffer->size())); - if(sda->serialAutoScroll){ + if(sda->m_serialAutoScroll){ QTextCursor c = console->textCursor(); c.movePosition(QTextCursor::End); console->setTextCursor(c); diff --git a/Desktop_Interface/isobuffer.cpp b/Desktop_Interface/isobuffer.cpp index 3cb1e75b..957e347e 100644 --- a/Desktop_Interface/isobuffer.cpp +++ b/Desktop_Interface/isobuffer.cpp @@ -2,391 +2,343 @@ #include "isodriver.h" #include "uartstyledecoder.h" -isoBuffer::isoBuffer(QWidget *parent, int bufferLen, isoDriver *caller, unsigned char channel_value) : QWidget(parent) -{ - buffer = (short *) calloc(bufferLen*2, sizeof(short)); - bufferEnd = bufferLen-1; - samplesPerSecond = (double) bufferLen/(double)21; - samplesPerSecond = samplesPerSecond/375*VALID_DATA_PER_375; - sampleRate_bit = samplesPerSecond * 8; - virtualParent = caller; - channel = channel_value; +namespace { + static char const * fileHeaderFormat = + "EspoTek Labrador DAQ V1.0 Output File\n" + "Averaging = %d\n" + "Mode = %d\n"; + + constexpr auto kSamplesSeekingCap = 20; + + #ifdef INVERT_MM + constexpr auto fX0Comp = std::greater {}; + constexpr auto fX1X2Comp = std::less {}; + #else + constexpr auto fX0Comp = std::less {}; + constexpr auto fX1X2Comp = std::greater {}; + #endif + + constexpr auto kTopMultimeter = 2048; } -void isoBuffer::openFile(QString newFile) + +isoBuffer::isoBuffer(QWidget* parent, int bufferLen, isoDriver* caller, unsigned char channel_value) + : QWidget(parent) + , m_buffer((short*)calloc(bufferLen*2, sizeof(short))) + , m_bufferEnd(bufferLen-1) + , m_samplesPerSecond(bufferLen/21.0/375*VALID_DATA_PER_375) + , m_sampleRate_bit(bufferLen/21.0/375*VALID_DATA_PER_375*8) + , m_virtualParent(caller) + , m_channel(channel_value) { - if (fptr != NULL){ - fclose(fptr); - } - if (newFile.isEmpty()){ - fptr = NULL; - } - else { - QByteArray temp = newFile.toLatin1(); - char *fileName = temp.data(); - fptr = fopen(fileName, "w"); - if (fptr == NULL) qFatal("Null fptr in isoBuffer::openFile"); - qDebug() << "opening file" << fileName; - qDebug() << "fptr = " << fptr; - } +} + +void isoBuffer::insertIntoBuffer(short item) +{ + m_buffer[m_back] = item; + m_back++; + m_insertedCount++; + + if (m_insertedCount > m_bufferEnd) + { + m_insertedCount = m_bufferEnd+1; + } + + if (m_back > m_bufferEnd) + { + m_back = 0; + } +} + +short isoBuffer::bufferAt(int idx) const +{ + return m_buffer[m_back - idx]; +} + +bool isoBuffer::maybeOutputSampleToFile(double convertedSample) +{ + /* + * This function adds a sample to an accumulator and bumps the sample count. + * After the sample count hits some threshold, the accumulated sample is + * outputted to a file. If this 'saturates' the file, then fileIO is disabled. + */ + m_average_sample_temp += convertedSample; + m_fileIO_sampleCount++; + + // Check to see if we can write a new sample to file + if (m_fileIO_sampleCount == m_fileIO_maxIncrementedSampleValue) + { + char numStr[32]; + sprintf(numStr,"%7.5f, ", m_average_sample_temp/((double)m_fileIO_maxIncrementedSampleValue)); + m_currentFile->write(numStr); + m_currentColumn++; + if (m_currentColumn >= COLUMN_BREAK) + { + m_currentFile->write("\n"); + m_currentColumn = 0; + } + + // Reset the average and sample count for next data point + m_fileIO_sampleCount = 0; + m_average_sample_temp = 0; + + // Check to see if we've reached the max file size. + if (m_fileIO_max_file_size != 0) // value of 0 means "no limit" + { + m_fileIO_numBytesWritten += 9; // 7 chars for the number, 1 for the comma and 1 for the space = 9 bytes per sample. + if (m_fileIO_numBytesWritten >= m_fileIO_max_file_size) + { + m_fileIOEnabled = false; // Just in case signalling fails. + fileIOinternalDisable(); + return false; + } + } + } + return true; +} + +template +void isoBuffer::writeBuffer(T* data, int len, int TOP, Function transform) +{ + for (int i = 0; i < len; ++i) + { + insertIntoBuffer(transform(data[i])); + } + + // Output to CSV + if (m_fileIOEnabled) + { + bool isUsingAC = m_channel == 1 + ? m_virtualParent->AC_CH1 + : m_virtualParent->AC_CH2; + + for (int i = 0; i < len; i++) + { + double convertedSample = sampleConvert(data[i], TOP, isUsingAC); + + bool keepOutputting = maybeOutputSampleToFile(convertedSample); + + if (!keepOutputting) break; + } + } } void isoBuffer::writeBuffer_char(char* data, int len) { - double convertedSample; - for (int i=0; iwrite(numStr); - currentColumn++; - if (currentColumn >= COLUMN_BREAK){ - currentFile->write("\n"); - currentColumn = 0; - } - - //Reset the average and sample count for next data point - fileIO_sampleCount = 0; - average_sample_temp = 0; - - //Check to see if we've reached the max file size. - if(fileIO_max_file_size != 0){ //value of 0 means "no limit" - fileIO_numBytesWritten += 9; //7 chars for the number, 1 for the comma and 1 for the space = 9 bytes per sample. - if(fileIO_numBytesWritten >= fileIO_max_file_size){ - fileIOEnabled = false; //Just in case signalling fails. - fileIOinternalDisable(); - } - } - } - } - } - return; + writeBuffer(data, len, 128, [](char item) -> short {return item;}); } void isoBuffer::writeBuffer_short(short* data, int len) { - //for (int i=(len-1);i>-1;i--){ - for (int i=0; iwrite(numStr); - currentColumn++; - if (currentColumn >= COLUMN_BREAK){ - currentFile->write("\n"); - currentColumn = 0; - } - - //Reset the average and sample count for next data point - fileIO_sampleCount = 0; - average_sample_temp = 0; - - //Check to see if we've reached the max file size. - if(fileIO_max_file_size != 0){ //value of 0 means "no limit" - fileIO_numBytesWritten += 9; //7 chars for the number, 1 for the comma and 1 for the space = 9 bytes per sample. - if(fileIO_numBytesWritten >= fileIO_max_file_size){ - fileIOEnabled = false; //Just in case signalling fails. - fileIOinternalDisable(); - } - } - } - } - } - return; + writeBuffer(data, len, 2048, [](short item) -> short {return item >> 4;}); } - -short *isoBuffer::readBuffer(double sampleWindow, int numSamples, bool singleBit, double delayOffset) +short* isoBuffer::readBuffer(double sampleWindow, int numSamples, bool singleBit, double delayOffset) { - //ignore singleBit for now - double timeBetweenSamples = (double) sampleWindow * (double) samplesPerSecond / (double) numSamples; - double accumulatedDelay = 0; - int delaySamples = (int)((double)delayOffset * (double)samplesPerSecond); + /* Refactor Note: + * + * Refactoring this function took a few passes were i made some assumptions: + * - round() should be replaced by floor() where it was used + * - int(floor(x)) and int(x) are equivalent (since we are always positive) + * - free(NULL) is a no-op. This is mandated by the C standard, and virtually all + * implementations comply. A few known exceptions are: + * - PalmOS + * - 3BSD + * - UNIX 7 + * I do not know of any non-compliant somewhat modern implementations. + * + * The expected behavior is to cycle backwards over the buffer, taking into + * acount only the part of the buffer that has things stored, with a stride + * of timeBetweenSamples steps, and insert the touched elements into readData. + * + * ~Sebastian Mestre + */ + const double timeBetweenSamples = sampleWindow * m_samplesPerSecond / numSamples; + const int delaySamples = delayOffset * m_samplesPerSecond; - int front = back - 1 - delaySamples; - if (front < 0) front = 0; - int idx, subIdx; - if(readData!=NULL) free(readData); - readData = (short *) calloc(numSamples, sizeof(short)); + free(m_readData); - if(singleBit){ - for (int i=0; i (double) front){ - accumulatedDelay -= (double) front; - front = bufferEnd; - } + m_readData = (short*) calloc(numSamples, sizeof(short)); - idx = (int) floor(((double) front - accumulatedDelay)); - subIdx = (int) floor(8*(((double) front - accumulatedDelay) - floor(((double) front - accumulatedDelay)))); + // TODO: replace by return nullptr and add error handling upstream + if(delaySamples+1 > m_insertedCount) + { + return m_readData; + } - //qDebug() << "subIdx = " << subIdx; + double itr = delaySamples + 1; + for (int i = 0; i < numSamples; i++) + { + while (itr > m_insertedCount) + itr -= m_insertedCount; - if (idx < 0){ - accumulatedDelay--; - accumulatedDelay -= (double) front; - front = bufferEnd; - idx = (int) round(((double) front - accumulatedDelay)); - } - readData[i] = buffer[idx] & (1 << subIdx); + m_readData[i] = bufferAt(int(itr)); - accumulatedDelay += timeBetweenSamples; - } - }else{ - for (int i=0; i (double) front){ - accumulatedDelay -= (double) front; - front = bufferEnd; - } + if (singleBit) + { + int subIdx = 8*(-itr-floor(-itr)); + m_readData[i] &= (1 << subIdx); + } - idx = (int) round(((double) front - accumulatedDelay)); - if (idx < 0){ - accumulatedDelay--; - accumulatedDelay -= (double) front; - front = bufferEnd; - idx = (int) round(((double) front - accumulatedDelay)); - } - readData[i] = buffer[idx]; + itr += timeBetweenSamples; + } - accumulatedDelay += timeBetweenSamples; - } - } - - return readData; + return m_readData; } void isoBuffer::clearBuffer() { - for (int i=0; i>= gain_log; + } } - -void isoBuffer::glitchInsert(short type) +void isoBuffer::enableFileIO(QFile* file, int samplesToAverage, qulonglong max_file_size) { + // Open the file + file->open(QIODevice::WriteOnly); + m_currentFile = file; + + // Add the header + char headerLine[256]; + sprintf(headerLine, fileHeaderFormat, samplesToAverage, m_virtualParent->driver->deviceMode); + m_currentFile->write(headerLine); + + // Set up the isoBuffer for DAQ + m_fileIO_maxIncrementedSampleValue = samplesToAverage; + m_fileIO_max_file_size = max_file_size; + m_fileIO_sampleCount = 0; + m_fileIO_numBytesWritten = 0; + m_average_sample_temp = 0; + + // Enable DAQ + m_fileIOEnabled = true; + + qDebug("File IO enabled, averaging %d samples, max file size %lluMB", samplesToAverage, max_file_size/1000000); + qDebug() << max_file_size; + return; } -void isoBuffer::enableFileIO(QFile *file, int samplesToAverage, qulonglong max_file_size){ - - //Open the file - file->open(QIODevice::WriteOnly); - currentFile = file; - - //Add the header - char headerLine[256]; - sprintf(headerLine, "EspoTek Labrador DAQ V1.0 Output File\nAveraging = %d\nMode = %d\n", samplesToAverage, virtualParent->driver->deviceMode); - currentFile->write(headerLine); - - //Set up the isoBuffer for DAQ - fileIO_maxIncrementedSampleValue = samplesToAverage; - fileIO_max_file_size = max_file_size; - fileIO_sampleCount = 0; - fileIO_numBytesWritten = 0; - average_sample_temp = 0; - - //Enable DAQ - fileIOEnabled = true; - - qDebug("File IO enabled, averaging %d samples, max file size %uMB", samplesToAverage, max_file_size/1000000); - qDebug() << max_file_size; - return; +void isoBuffer::disableFileIO() +{ + m_fileIOEnabled = false; + m_currentColumn = 0; + m_currentFile->close(); + return; } -void isoBuffer::disableFileIO(){ - fileIOEnabled = false; - currentColumn = 0; - currentFile->close(); - return; -} +double isoBuffer::sampleConvert(short sample, int TOP, bool AC) const +{ + double scope_gain = (double)(m_virtualParent->driver->scopeGain); -double isoBuffer::sampleConvert(short sample, int TOP, bool AC){ - - double scope_gain = (double)(virtualParent->driver->scopeGain); - double voltageLevel; - - voltageLevel = (sample * (vcc/2)) / (frontendGain*scope_gain*TOP); - if (virtualParent->driver->deviceMode != 7) voltageLevel += voltage_ref; - #ifdef INVERT_MM - if(virtualParent->driver->deviceMode == 7) voltageLevel *= -1; - #endif - - if(AC){ - voltageLevel -= virtualParent->currentVmean; //This is old (1 frame in past) value and might not be good for signals with large variations in DC level (although the cap should filter that anyway)?? - } - return voltageLevel; -} - - - -short isoBuffer::inverseSampleConvert(double voltageLevel, int TOP, bool AC){ - - double scope_gain = (double)(virtualParent->driver->scopeGain); - short sample; - - if(AC){ - voltageLevel += virtualParent->currentVmean; //This is old (1 frame in past) value and might not be good for signals with large variations in DC level (although the cap should filter that anyway)?? - } + double voltageLevel = (sample * (vcc/2)) / (m_frontendGain*scope_gain*TOP); + if (m_virtualParent->driver->deviceMode != 7) voltageLevel += m_voltage_ref; #ifdef INVERT_MM - if(virtualParent->driver->deviceMode == 7) voltageLevel *= -1; -#endif - if (virtualParent->driver->deviceMode != 7) voltageLevel -= voltage_ref; - - //voltageLevel = (sample * (vcc/2)) / (frontendGain*scope_gain*TOP); - sample = (voltageLevel * (frontendGain*scope_gain*TOP))/(vcc/2); - return sample; -} - -#define NUM_SAMPLES_SEEKING_CAP (20) - -#ifdef INVERT_MM - #define X0_COMPARISON_CAP > - #define X1_X2_COMPARISON_CAP < -#else - #define X0_COMPARISON_CAP < - #define X1_X2_COMPARISON_CAP > + if (m_virtualParent->driver->deviceMode == 7) voltageLevel *= -1; #endif -//For capacitance measurement. x0, x1 and x2 are all various time points used to find the RC coefficient. -int isoBuffer::cap_x0fromLast(double seconds, double vbot){ - int samplesInPast = seconds * samplesPerSecond; - if(back < samplesInPast){ - return -1; //too hard, not really important - } - short vbot_s = inverseSampleConvert(vbot, 2048, 0); - qDebug() << "vbot_s (x0) = " << vbot_s; - - int num_found = 0; - for(int i=samplesInPast; i; i--){ - short currentSample = buffer[back - i]; - if(currentSample X0_COMPARISON_CAP vbot_s){ - num_found++; - } else num_found--; - if(num_found < 0){ - num_found = 0; - } - if (num_found > NUM_SAMPLES_SEEKING_CAP){ - return samplesInPast-i; - } - } - return -1; + if (AC) + { + // This is old (1 frame in past) value and might not be good for signals with + // large variations in DC level (although the cap should filter that anyway)?? + voltageLevel -= m_virtualParent->currentVmean; + } + return voltageLevel; } -int isoBuffer::cap_x1fromLast(double seconds, int x0, double vbot){ - int samplesInPast = seconds * samplesPerSecond; - samplesInPast -= x0; - if(back < samplesInPast){ - return -1; //too hard, not really important - } - short vbot_s = inverseSampleConvert(vbot, 2048, 0); - qDebug() << "vbot_s (x1) = " << vbot_s; +short isoBuffer::inverseSampleConvert(double voltageLevel, int TOP, bool AC) const +{ + double scope_gain = m_virtualParent->driver->scopeGain; - int num_found = 0; - for(int i=samplesInPast; i; i--){ - short currentSample = buffer[back - i]; - if(currentSample X1_X2_COMPARISON_CAP vbot_s){ - num_found++; - } else num_found--; - if(num_found < 0){ - num_found = 0; - } - if (num_found > NUM_SAMPLES_SEEKING_CAP){ - return samplesInPast-i + x0; - } + if (AC) + { + // This is old (1 frame in past) value and might not be good for signals with + // large variations in DC level (although the cap should filter that anyway)?? + voltageLevel += m_virtualParent->currentVmean; + } - } - return -1; +#ifdef INVERT_MM + if (m_virtualParent->driver->deviceMode == 7) voltageLevel *= -1; +#endif + if (m_virtualParent->driver->deviceMode != 7) voltageLevel -= m_voltage_ref; + + // voltageLevel = (sample * (vcc/2)) / (frontendGain*scope_gain*TOP); + short sample = (voltageLevel * (m_frontendGain*scope_gain*TOP))/(vcc/2); + return sample; } -int isoBuffer::cap_x2fromLast(double seconds, int x1, double vtop){ - int samplesInPast = seconds * samplesPerSecond; - samplesInPast -= x1; - if(back < samplesInPast){ - return -1; //too hard, not really important - } - short vtop_s = inverseSampleConvert(vtop, 2048, 0); - qDebug() << "vtop_s (x2) = " << vtop_s; +template +int isoBuffer::capSample(int offset, int target, double seconds, double value, Function comp) +{ + int samples = seconds * m_samplesPerSecond; - int num_found = 0; - for(int i=samplesInPast; i; i--){ - short currentSample = buffer[back - i]; - if(currentSample X1_X2_COMPARISON_CAP vtop_s){ - num_found++; - } else num_found--; - if(num_found < 0){ - num_found = 0; - } - if (num_found > NUM_SAMPLES_SEEKING_CAP){ - return samplesInPast-i + x1; - } - } - return -1; + if (m_back < samples + offset) return -1; + + short sample = inverseSampleConvert(value, 2048, 0); + + int found = 0; + for (int i = samples + offset; i--;) + { + short currentSample = bufferAt(i); + if (comp(currentSample, sample)) + found = found + 1; + else + found = std::max(0, found-1); + + if (found > target) + return samples - i; + } + + return -1; } -void isoBuffer::serialManage(double baudRate, int type, UartParity parity){ - //Types: - // 0 - standard UART, no parity - // 1 - standard UART, with parity bit - // 100 - I2C - if(decoder == NULL){ - decoder = new uartStyleDecoder(this); - connect(decoder, SIGNAL(wireDisconnected(int)), virtualParent, SLOT(serialNeedsDisabling(int))); - } - if(stopDecoding){ - decoder->updateTimer->start(CONSOLE_UPDATE_TIMER_PERIOD); - stopDecoding = false; - } - decoder->setParityMode(parity); - decoder->serialDecode(baudRate); +// For capacitance measurement. x0, x1 and x2 are all various time points used to find the RC coefficient. +int isoBuffer::cap_x0fromLast(double seconds, double vbot) +{ + return capSample(0, kSamplesSeekingCap, seconds, vbot, fX0Comp); } +int isoBuffer::cap_x1fromLast(double seconds, int x0, double vbot) +{ + return capSample(-x0, kSamplesSeekingCap, seconds, vbot, fX1X2Comp); +} +int isoBuffer::cap_x2fromLast(double seconds, int x1, double vtop) +{ + return capSample(-x1, kSamplesSeekingCap, seconds, vtop, fX1X2Comp); +} +void isoBuffer::serialManage(double baudRate, UartParity parity) +{ + if (m_decoder == NULL) + { + m_decoder = new uartStyleDecoder(this); + // TODO: Look into using the type safe version of connect. + // NOTE: I believe Qt has a type-safe version of this, without the macros and the + // explicit signature and stuff, i think it uses member-function pointers instead. + connect(m_decoder, SIGNAL(wireDisconnected(int)), m_virtualParent, SLOT(serialNeedsDisabling(int))); + } + if (m_stopDecoding) + { + m_decoder->updateTimer->start(CONSOLE_UPDATE_TIMER_PERIOD); + m_stopDecoding = false; + } + m_decoder->setParityMode(parity); + m_decoder->serialDecode(baudRate); +} diff --git a/Desktop_Interface/isobuffer.h b/Desktop_Interface/isobuffer.h index 3aad3dde..ce606b51 100644 --- a/Desktop_Interface/isobuffer.h +++ b/Desktop_Interface/isobuffer.h @@ -1,6 +1,8 @@ #ifndef ISOBUFFER_H #define ISOBUFFER_H +// TODO: Make object macros constexprs or globals +// TODO: Move headers used only in implementation to isobuffer.cpp #include #include #include @@ -18,62 +20,99 @@ class isoDriver; class uartStyleDecoder; enum class UartParity : uint8_t; -//isoBuffer is a generic class that enables O(1) read times (!!!) on all read/write operations, while maintaining a huge buffer size. -//Imagine it as a circular buffer, but with access functions specifically designed for isochronous data from an Xmega. +// isoBuffer is a generic class that enables O(1) read times (!!!) on all +// read/write operations, while maintaining a huge buffer size. +// Imagine it as a circular buffer, but with access functions specifically +// designed for isochronous data from an Xmega. -#define CONSOLE_UPDATE_TIMER_PERIOD (ISO_PACKETS_PER_CTX * 4) +constexpr auto CONSOLE_UPDATE_TIMER_PERIOD = ISO_PACKETS_PER_CTX * 4; +// TODO: Make private what should be private +// TODO: Add m_ prefix to member variables +// TODO: Change integer types to cstdint types class isoBuffer : public QWidget { - Q_OBJECT + Q_OBJECT public: - isoBuffer(QWidget *parent = 0, int bufferLen = 0, isoDriver *caller = 0, unsigned char channel_value = 0); - //Generic Functions - void openFile(QString newFile); - void writeBuffer_char(char *data, int len); - void writeBuffer_short(short *data, int len); - short *readBuffer(double sampleWindow, int numSamples, bool singleBit, double delayOffset); - void clearBuffer(); - void gainBuffer(int gain_log); - void glitchInsert(short type); - double sampleConvert(short sample, int TOP, bool AC); - short inverseSampleConvert(double voltageLevel, int TOP, bool AC); - int cap_x0fromLast(double seconds, double vbot); - int cap_x1fromLast(double seconds, int x0, double vbot); - int cap_x2fromLast(double seconds, int x1, double vtop); - void serialManage(double baudRate, int type, UartParity parity); - //Generic Vars - QPlainTextEdit *console1, *console2; - bool serialAutoScroll = true; - unsigned char channel = 255; - double voltage_ref = 1.65; - double frontendGain = (R4 / (R3 + R4)); - int samplesPerSecond; - int sampleRate_bit; - int bufferEnd, back = 0; - short *buffer, *readData = NULL; - uartStyleDecoder *decoder = NULL; - bool stopDecoding = false; + // TODO: Add consoles as constructor arguments + isoBuffer(QWidget* parent = 0, int bufferLen = 0, isoDriver* caller = 0, unsigned char channel_value = 0); + // TODO?: Add a destructor + +// Basic buffer operations + short bufferAt(int idx) const; + void insertIntoBuffer(short item); + void clearBuffer(); + void gainBuffer(int gain_log); + +// Advanced buffer operations private: - //Generic Vars - bool firstTime = true; - //File I/O - bool fileIOEnabled = false; - FILE* fptr = NULL; - QFile *currentFile; - int fileIO_maxIncrementedSampleValue; - int fileIO_sampleCount; - qulonglong fileIO_max_file_size; - qulonglong fileIO_numBytesWritten; - //isoDriver *parent; - unsigned int currentColumn = 0; - isoDriver *virtualParent; - double average_sample_temp; + template + void writeBuffer(T* data, int len, int TOP, Function transform); +public: + void writeBuffer_char(char* data, int len); + void writeBuffer_short(short* data, int len); + + // TODO: Change return value to unique_ptr + short* readBuffer(double sampleWindow, int numSamples, bool singleBit, double delayOffset); + +// file I/O + bool maybeOutputSampleToFile(double convertedSample); + double sampleConvert(short sample, int TOP, bool AC) const; + short inverseSampleConvert(double voltageLevel, int TOP, bool AC) const; + +private: + template + int capSample(int offset, int target, double seconds, double value, Function comp); +public: + int cap_x0fromLast(double seconds, double vbot); + int cap_x1fromLast(double seconds, int x0, double vbot); + int cap_x2fromLast(double seconds, int x1, double vtop); + void serialManage(double baudRate, UartParity parity); + +// ---- MEMBER VARIABLES ---- + +// Presentantion? +// NOTE: it seems like these are never initialized but they are used as though they were... + QPlainTextEdit* m_console1; + QPlainTextEdit* m_console2; + unsigned char m_channel = 255; + bool m_serialAutoScroll = true; + +// Conversion And Sampling + double m_voltage_ref = 1.65; + double m_frontendGain = (R4 / (R3 + R4)); + int m_samplesPerSecond; + int m_sampleRate_bit; + +// Internal Storage + int m_back = 0; + int m_insertedCount = 0; + int m_bufferEnd; +// TODO: Change buffer to be a unique_ptr + short* m_buffer; + short* m_readData = NULL; + +// UARTS decoding + uartStyleDecoder* m_decoder = NULL; + // TODO: change this to keepDecoding + bool m_stopDecoding = false; +private: +// File I/O + bool m_fileIOEnabled = false; + FILE* m_fptr = NULL; + QFile* m_currentFile; + int m_fileIO_maxIncrementedSampleValue; + int m_fileIO_sampleCount; + qulonglong m_fileIO_max_file_size; + qulonglong m_fileIO_numBytesWritten; + unsigned int m_currentColumn = 0; + isoDriver* m_virtualParent; + double m_average_sample_temp; signals: - void fileIOinternalDisable(); + void fileIOinternalDisable(); public slots: - void enableFileIO(QFile *file, int samplesToAverage, qulonglong max_file_size); - void disableFileIO(); + void enableFileIO(QFile* file, int samplesToAverage, qulonglong max_file_size); + void disableFileIO(); }; #endif // ISOBUFFER_H diff --git a/Desktop_Interface/isodriver.cpp b/Desktop_Interface/isodriver.cpp index ef3a2fae..4901a020 100644 --- a/Desktop_Interface/isodriver.cpp +++ b/Desktop_Interface/isodriver.cpp @@ -90,10 +90,10 @@ void isoDriver::timerTick(void){ if (deviceMode_prev != 1) clearBuffers(false, true, false); - internalBuffer375_CH2->channel = 1; + internalBuffer375_CH2->m_channel = 1; frameActionGeneric(1,2); if(serialDecodeEnabled_CH1 && serialType == 0){ - internalBuffer375_CH2->serialManage(baudRate_CH1, 0, parity_CH1); + internalBuffer375_CH2->serialManage(baudRate_CH1, parity_CH1); } break; case 2: @@ -110,7 +110,7 @@ void isoDriver::timerTick(void){ frameActionGeneric(2,0); if(serialDecodeEnabled_CH1 && serialType == 0){ - internalBuffer375_CH1->serialManage(baudRate_CH1, 0, parity_CH1); + internalBuffer375_CH1->serialManage(baudRate_CH1, parity_CH1); } break; case 4: @@ -119,13 +119,13 @@ void isoDriver::timerTick(void){ if (deviceMode_prev != 4) clearBuffers(false, true, false); - internalBuffer375_CH2->channel = 2; + internalBuffer375_CH2->m_channel = 2; frameActionGeneric(2,2); if(serialDecodeEnabled_CH1 && serialType == 0){ - internalBuffer375_CH1->serialManage(baudRate_CH1, 0, parity_CH1); + internalBuffer375_CH1->serialManage(baudRate_CH1, parity_CH1); } if(serialDecodeEnabled_CH2 && serialType == 0){ - internalBuffer375_CH2->serialManage(baudRate_CH2, 0, parity_CH2); + internalBuffer375_CH2->serialManage(baudRate_CH2, parity_CH2); } if (serialDecodeEnabled_CH1 && serialType == 1) { @@ -1169,7 +1169,7 @@ void isoDriver::multimeterStats(){ qDebug() << "x2 = " << cap_x2; qDebug() << "dt = " << cap_x2-cap_x1; - double dt = (double)(cap_x2-cap_x1)/internalBuffer375_CH1->samplesPerSecond; + double dt = (double)(cap_x2-cap_x1)/internalBuffer375_CH1->m_samplesPerSecond; double Cm = -dt/(seriesResistance * log((vcc-cap_vtop)/(vcc-cap_vbot))); qDebug() << "Cm = " << Cm; @@ -1555,7 +1555,7 @@ void isoDriver::setSerialType(unsigned char type) { if (twoWire) delete twoWire; - twoWire = new i2c::i2cDecoder(internalBuffer375_CH1, internalBuffer375_CH2, internalBuffer375_CH1->console1); + twoWire = new i2c::i2cDecoder(internalBuffer375_CH1, internalBuffer375_CH2, internalBuffer375_CH1->m_console1); } } diff --git a/Desktop_Interface/mainwindow.cpp b/Desktop_Interface/mainwindow.cpp index 3f34c7b3..91328e4b 100644 --- a/Desktop_Interface/mainwindow.cpp +++ b/Desktop_Interface/mainwindow.cpp @@ -41,11 +41,11 @@ MainWindow::MainWindow(QWidget *parent) : ui->voltageInfoRmsDisplay_CH1->display(6.00); connectDisplaySignals(); - ui->controller_iso->internalBuffer375_CH1->console1 = ui->console1; - ui->controller_iso->internalBuffer375_CH1->console2 = ui->console2; + ui->controller_iso->internalBuffer375_CH1->m_console1 = ui->console1; + ui->controller_iso->internalBuffer375_CH1->m_console2 = ui->console2; - ui->controller_iso->internalBuffer375_CH2->console1 = ui->console1; - ui->controller_iso->internalBuffer375_CH2->console2 = ui->console2; + ui->controller_iso->internalBuffer375_CH2->m_console1 = ui->console1; + ui->controller_iso->internalBuffer375_CH2->m_console2 = ui->console2; initShortcuts(); ui->debugButton1->setVisible(0); @@ -1204,12 +1204,12 @@ void MainWindow::readSettingsFile(){ ui->controller_iso->ch2_ref = 3.3 - calibrate_vref_ch2; ui->controller_iso->frontendGain_CH1 = calibrate_gain_ch1; ui->controller_iso->frontendGain_CH2 = calibrate_gain_ch2; - ui->controller_iso->internalBuffer375_CH1->voltage_ref = 3.3 - calibrate_vref_ch1; - ui->controller_iso->internalBuffer750->voltage_ref = 3.3 - calibrate_vref_ch1; - ui->controller_iso->internalBuffer375_CH2->voltage_ref = 3.3 - calibrate_vref_ch2; - ui->controller_iso->internalBuffer375_CH1->frontendGain = calibrate_gain_ch1; - ui->controller_iso->internalBuffer750->frontendGain = calibrate_gain_ch1; - ui->controller_iso->internalBuffer375_CH2->frontendGain = calibrate_gain_ch2; + ui->controller_iso->internalBuffer375_CH1->m_voltage_ref = 3.3 - calibrate_vref_ch1; + ui->controller_iso->internalBuffer750->m_voltage_ref = 3.3 - calibrate_vref_ch1; + ui->controller_iso->internalBuffer375_CH2->m_voltage_ref = 3.3 - calibrate_vref_ch2; + ui->controller_iso->internalBuffer375_CH1->m_frontendGain = calibrate_gain_ch1; + ui->controller_iso->internalBuffer750->m_frontendGain = calibrate_gain_ch1; + ui->controller_iso->internalBuffer375_CH2->m_frontendGain = calibrate_gain_ch2; if(!dt_AlreadyAskedAboutCalibration && ((calibrate_vref_ch1 == 1.65) || (calibrate_vref_ch2 == 1.65) || (calibrate_gain_ch1 == R4/(R3+R4)) || (calibrate_gain_ch2 == R4/(R3+R4)))){ //Prompt user to calibrate if no calibration data found. @@ -1698,12 +1698,12 @@ void MainWindow::on_actionCalibrate_triggered() ui->controller_iso->ch2_ref = 1.65; ui->controller_iso->frontendGain_CH1 = (R4/(R3+R4)); ui->controller_iso->frontendGain_CH2 = (R4/(R3+R4)); - ui->controller_iso->internalBuffer375_CH1->voltage_ref = 1.65; - ui->controller_iso->internalBuffer750->voltage_ref = 1.65; - ui->controller_iso->internalBuffer375_CH2->voltage_ref = 1.65; - ui->controller_iso->internalBuffer375_CH1->frontendGain = R4/(R3+R4); - ui->controller_iso->internalBuffer750->frontendGain = R4/(R3+R4); - ui->controller_iso->internalBuffer375_CH2->frontendGain = R4/(R3+R4); + ui->controller_iso->internalBuffer375_CH1->m_voltage_ref = 1.65; + ui->controller_iso->internalBuffer750->m_voltage_ref = 1.65; + ui->controller_iso->internalBuffer375_CH2->m_voltage_ref = 1.65; + ui->controller_iso->internalBuffer375_CH1->m_frontendGain = R4/(R3+R4); + ui->controller_iso->internalBuffer750->m_frontendGain = R4/(R3+R4); + ui->controller_iso->internalBuffer375_CH2->m_frontendGain = R4/(R3+R4); settings->setValue("CalibrateVrefCH1", 1.65); settings->setValue("CalibrateVrefCH2", 1.65); @@ -1733,9 +1733,9 @@ void MainWindow::calibrateStage2(){ ui->controller_iso->ch1_ref = 3.3 - vref_CH1; ui->controller_iso->ch2_ref = 3.3 - vref_CH2; - ui->controller_iso->internalBuffer375_CH1->voltage_ref = 3.3 - vref_CH1; - ui->controller_iso->internalBuffer750->voltage_ref = 3.3 - vref_CH1; - ui->controller_iso->internalBuffer375_CH2->voltage_ref = 3.3 - vref_CH2; + ui->controller_iso->internalBuffer375_CH1->m_voltage_ref = 3.3 - vref_CH1; + ui->controller_iso->internalBuffer750->m_voltage_ref = 3.3 - vref_CH1; + ui->controller_iso->internalBuffer375_CH2->m_voltage_ref = 3.3 - vref_CH2; settings->setValue("CalibrateVrefCH1", vref_CH1); settings->setValue("CalibrateVrefCH2", vref_CH2); @@ -1769,9 +1769,9 @@ void MainWindow::calibrateStage3(){ ui->controller_iso->frontendGain_CH2 = (vref_CH2 - vMeasured_CH2)*(ui->controller_iso->frontendGain_CH2)/vref_CH2; qDebug() << "New gain (CH1) = " << ui->controller_iso->frontendGain_CH1; - ui->controller_iso->internalBuffer375_CH1->frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; - ui->controller_iso->internalBuffer750->frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; - ui->controller_iso->internalBuffer375_CH2->frontendGain = (vref_CH2 - vMeasured_CH2)*(ui->controller_iso->frontendGain_CH2)/vref_CH2; + ui->controller_iso->internalBuffer375_CH1->m_frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; + ui->controller_iso->internalBuffer750->m_frontendGain = (vref_CH1 - vMeasured_CH1)*(ui->controller_iso->frontendGain_CH1)/vref_CH1; + ui->controller_iso->internalBuffer375_CH2->m_frontendGain = (vref_CH2 - vMeasured_CH2)*(ui->controller_iso->frontendGain_CH2)/vref_CH2; settings->setValue("CalibrateGainCH1", ui->controller_iso->frontendGain_CH1); settings->setValue("CalibrateGainCH2", ui->controller_iso->frontendGain_CH2); calibrationMessages->setText("Oscilloscope Calibration complete."); diff --git a/Desktop_Interface/uartstyledecoder.cpp b/Desktop_Interface/uartstyledecoder.cpp index fe0be07f..eebb88a6 100644 --- a/Desktop_Interface/uartstyledecoder.cpp +++ b/Desktop_Interface/uartstyledecoder.cpp @@ -7,7 +7,7 @@ uartStyleDecoder::uartStyleDecoder(QObject *parent_in) : QObject(parent_in) parent = (isoBuffer *) parent_in; // Begin decoding SAMPLE_DELAY seconds in the past. - serialPtr_bit = (int)(parent->back * 8 - SERIAL_DELAY * parent->sampleRate_bit + parent->bufferEnd * 8) % (parent->bufferEnd*8); + serialPtr_bit = (int)(parent->m_back * 8 - SERIAL_DELAY * parent->m_sampleRate_bit + parent->m_bufferEnd * 8) % (parent->m_bufferEnd*8); updateTimer = new QTimer(); updateTimer->setTimerType(Qt::PreciseTimer); @@ -16,8 +16,8 @@ uartStyleDecoder::uartStyleDecoder(QObject *parent_in) : QObject(parent_in) serialBuffer = new isoBufferBuffer(SERIAL_BUFFER_LENGTH); - if(parent->channel == 1) console = parent->console1; - else if(parent->channel == 2) console = parent->console2; + if(parent->m_channel == 1) console = parent->m_console1; + else if(parent->m_channel == 2) console = parent->m_console2; else qFatal("Nonexistant console requested in uartStyleDecoder::serialDecode"); } @@ -34,7 +34,7 @@ void uartStyleDecoder::updateConsole(){ //qDebug() << serialBuffer->size(); console->setPlainText(QString::fromLocal8Bit(serialBuffer->begin(), serialBuffer->size())); - if(parent->serialAutoScroll){ + if(parent->m_serialAutoScroll){ //http://stackoverflow.com/questions/21059678/how-can-i-set-auto-scroll-for-a-qtgui-qtextedit-in-pyqt4 DANKON QTextCursor c = console->textCursor(); c.movePosition(QTextCursor::End); @@ -50,7 +50,7 @@ void uartStyleDecoder::serialDecode(double baudRate) /*if(stopDecoding){ return; }*/ - double dist_seconds = (double)serialDistance()/(parent->sampleRate_bit); + double dist_seconds = (double)serialDistance()/(parent->m_sampleRate_bit); double bitPeriod_seconds = 1/baudRate; // Used to check for wire disconnects. You should get at least one "1" for a stop bit. @@ -76,22 +76,22 @@ void uartStyleDecoder::serialDecode(double baudRate) // Update the pointer, accounting for jitter updateSerialPtr(baudRate, uart_bit); // Calculate stopping condition - dist_seconds = (double)serialDistance()/(parent->sampleRate_bit); + dist_seconds = (double)serialDistance()/(parent->m_sampleRate_bit); } //Not a single stop bit, or idle bit, in the whole stream. Wire must be disconnected. if(allZeroes){ qDebug() << "Wire Disconnect detected!"; - wireDisconnected(parent->channel); - parent->stopDecoding = true; + wireDisconnected(parent->m_channel); + parent->m_stopDecoding = true; updateTimer->stop(); } } int uartStyleDecoder::serialDistance() { - int back_bit = parent->back * 8; - int bufferEnd_bit = parent->bufferEnd * 8; + int back_bit = parent->m_back * 8; + int bufferEnd_bit = parent->m_bufferEnd * 8; if(back_bit >= serialPtr_bit){ return back_bit - serialPtr_bit; }else return bufferEnd_bit - serialPtr_bit + back_bit; @@ -103,20 +103,20 @@ void uartStyleDecoder::updateSerialPtr(double baudRate, unsigned char current_bi jitterCompensationNeeded = jitterCompensationProcedure(baudRate, current_bit); } - int distance_between_bits = (parent->sampleRate_bit)/baudRate; + int distance_between_bits = (parent->m_sampleRate_bit)/baudRate; if(uartTransmitting){ serialPtr_bit += distance_between_bits; } else serialPtr_bit += (distance_between_bits - 1); //Less than one baud period so that it will always see that start bit. - if (serialPtr_bit > (parent->bufferEnd * 8)){ - serialPtr_bit -= (parent->bufferEnd * 8); + if (serialPtr_bit > (parent->m_bufferEnd * 8)){ + serialPtr_bit -= (parent->m_bufferEnd * 8); } } unsigned char uartStyleDecoder::getNextUartBit(){ int coord_byte = serialPtr_bit/8; int coord_bit = serialPtr_bit - (8*coord_byte); - unsigned char dataByte = parent->buffer[coord_byte]; + unsigned char dataByte = parent->m_buffer[coord_byte]; unsigned char mask = (0x01 << coord_bit); return ((dataByte & mask) ? 1 : 0); } @@ -155,14 +155,14 @@ bool uartStyleDecoder::jitterCompensationProcedure(double baudRate, unsigned cha //Can't be bothered dealing with the corner case where the serial pointer is at the very start of the buffer. //Just return and try again next time. - int left_coord = serialPtr_bit - (parent->sampleRate_bit)/baudRate; + int left_coord = serialPtr_bit - (parent->m_sampleRate_bit)/baudRate; //qDebug() << "left_coord =" << left_coord; if (left_coord < 0){ return true; //Don't want to read out of bounds!! } //The usual case, when transmitting anyway. - unsigned char left_byte = (parent->buffer[left_coord/8] & 0xff); + unsigned char left_byte = (parent->m_buffer[left_coord/8] & 0xff); //Only run when a zero is detected in the leftmost symbol. if(left_byte != 255){ //Step back, one sample at a time, to the 0->1 transition point @@ -172,7 +172,7 @@ bool uartStyleDecoder::jitterCompensationProcedure(double baudRate, unsigned cha serialPtr_bit--; } //Jump the pointer forward by half a uart bit period, and return "done!". - serialPtr_bit += (parent->sampleRate_bit/baudRate)/2; + serialPtr_bit += (parent->m_sampleRate_bit/baudRate)/2; return false; }