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
This commit is contained in:
Sebastián Mestre 2019-01-23 02:13:40 -03:00 committed by Chris Esposito
parent 4499db10bf
commit 015d41624d
6 changed files with 429 additions and 438 deletions

View File

@ -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<std::mutex> 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);

View File

@ -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<int> {};
constexpr auto fX1X2Comp = std::less<int> {};
#else
constexpr auto fX0Comp = std::less<int> {};
constexpr auto fX1X2Comp = std::greater<int> {};
#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<typename T, typename Function>
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; i<len;i++){
//qDebug() << "i = " << i;
buffer[back] = (short) data[i];
if (back == bufferEnd){
back = 0;
firstTime = false;
}
else back++;
//Output to CSV
if(fileIOEnabled){
//Current sample
convertedSample = sampleConvert(data[i], 128, channel==1 ? virtualParent->AC_CH1 : virtualParent->AC_CH2);
//Accumulate
average_sample_temp += convertedSample;
fileIO_sampleCount++;
//Check to see if we can write a new sample to file
if(fileIO_sampleCount == fileIO_maxIncrementedSampleValue){
char numStr[32];
sprintf(numStr,"%7.5f, ", average_sample_temp/((double)fileIO_maxIncrementedSampleValue));
currentFile->write(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; i<len;i++){
//qDebug() << "i = " << i;
buffer[back] = (short) data[i] >> 4; //Because it's a left adjust value!
if (back == bufferEnd){
back = 0;
firstTime = false;
}
else back++;
double convertedSample;
//Output to CSV
if(fileIOEnabled){
//Current sample
convertedSample = sampleConvert((data[i] >> 4), 2048, channel==1 ? virtualParent->AC_CH1 : virtualParent->AC_CH2);
//Accumulate
average_sample_temp += convertedSample;
fileIO_sampleCount++;
//Check to see if we can write a new sample to file
if(fileIO_sampleCount == fileIO_maxIncrementedSampleValue){
char numStr[32];
sprintf(numStr,"%7.5f, ", average_sample_temp/((double)fileIO_maxIncrementedSampleValue));
currentFile->write(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<numSamples;i++){
if (timeBetweenSamples > (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<numSamples;i++){
if (timeBetweenSamples > (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<bufferEnd;i++){
buffer[i] = 0;
}
for (int i = 0; i < m_bufferEnd; i++)
{
m_buffer[i] = 0;
}
back = 0;
firstTime = true;
m_back = 0;
}
void isoBuffer::gainBuffer(int gain_log)
{
qDebug() << "Buffer shifted by" << gain_log;
for (int i=0; i<bufferEnd; i++){
if (gain_log == -1) buffer[i] *= 2;
else buffer[i] /= 2;
}
qDebug() << "Buffer shifted by" << gain_log;
for (int i = 0; i < m_bufferEnd; i++)
{
if (gain_log < 0)
m_buffer[i] <<= -gain_log;
else
m_buffer[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<typename Function>
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);
}

View File

@ -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 <QWidget>
#include <QString>
#include <QByteArray>
@ -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<typename T, typename Function>
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<typename Function>
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

View File

@ -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);
}
}

View File

@ -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.");

View File

@ -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;
}