Uartstyledecoder refactor (#72)

* fix brackets

* make serialBuffer a member of uartStyleDecoder and add m_ prefix, loosen synchcronization on updateConsole

* make uartStyleDecoder::updateTimer a member variable

* write isEvenParity lambda in a more concise way

* add unused attribute on decode_baudot. make isEvenParity take an argument

* add const where possible on uartStyleDecoder methods

* Refactor performParityCheck to immutability

* Move the computation around a bit. Parameterize with parameters instead of relying on class state

* remove parity!=Node check. isParityCorrect alreaddy checks this.

* Various changes regarding uartStyleDecoder.

 - rename parent to m_parent
 - store baud rate as a member variable
 - use bool instead of unsigned char for single bit values
 - remove maybe_unused attribute on decodeBaudot
 - getUartBit(index) -> getNextUartBit()
This commit is contained in:
Sebastián Mestre 2019-03-18 04:14:49 -03:00 committed by Chris Esposito
parent dd793fc4e7
commit 80b4d04843
3 changed files with 142 additions and 129 deletions

View File

@ -338,18 +338,20 @@ void isoBuffer::serialManage(double baudRate, UartParity parity)
{ {
if (m_decoder == NULL) if (m_decoder == NULL)
{ {
m_decoder = new uartStyleDecoder(this); m_decoder = new uartStyleDecoder(baudRate, this);
connect(m_decoder, &uartStyleDecoder::wireDisconnected, connect(m_decoder, &uartStyleDecoder::wireDisconnected,
m_virtualParent, &isoDriver::serialNeedsDisabling); m_virtualParent, &isoDriver::serialNeedsDisabling);
} }
if (!m_isDecoding) if (!m_isDecoding)
{ {
m_decoder->updateTimer->start(CONSOLE_UPDATE_TIMER_PERIOD); m_decoder->m_updateTimer.start(CONSOLE_UPDATE_TIMER_PERIOD);
m_isDecoding = true; m_isDecoding = true;
} }
m_decoder->m_baudRate = baudRate;
m_decoder->setParityMode(parity); m_decoder->setParityMode(parity);
m_decoder->serialDecode(baudRate); m_decoder->serialDecode();
} }
void isoBuffer::setTriggerType(TriggerType newType) void isoBuffer::setTriggerType(TriggerType newType)

View File

@ -2,41 +2,40 @@
#include <QDebug> #include <QDebug>
#include <cassert> #include <cassert>
uartStyleDecoder::uartStyleDecoder(QObject *parent_in) : QObject(parent_in) uartStyleDecoder::uartStyleDecoder(double baudRate, QObject *parent)
: QObject(parent)
, m_parent{static_cast<isoBuffer*>(parent)}
, m_serialBuffer{SERIAL_BUFFER_LENGTH}
, m_baudRate{baudRate}
{ {
parent = (isoBuffer *) parent_in;
// Begin decoding SAMPLE_DELAY seconds in the past. // Begin decoding SAMPLE_DELAY seconds in the past.
serialPtr_bit = (int)(parent->m_back * 8 - SERIAL_DELAY * parent->m_sampleRate_bit + parent->m_bufferLen * 8) % (parent->m_bufferLen*8); serialPtr_bit = (int)(m_parent->m_back * 8 - SERIAL_DELAY * m_parent->m_sampleRate_bit + m_parent->m_bufferLen * 8) % (m_parent->m_bufferLen*8);
updateTimer = new QTimer(); m_updateTimer.setTimerType(Qt::PreciseTimer);
updateTimer->setTimerType(Qt::PreciseTimer); m_updateTimer.start(CONSOLE_UPDATE_TIMER_PERIOD);
updateTimer->start(CONSOLE_UPDATE_TIMER_PERIOD); connect(&m_updateTimer, &QTimer::timeout, this, &uartStyleDecoder::updateConsole);
connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateConsole()));
serialBuffer = new isoBufferBuffer(SERIAL_BUFFER_LENGTH); if (m_parent->m_channel == 1)
console = m_parent->m_console1;
if(parent->m_channel == 1) console = parent->m_console1; else if (m_parent->m_channel == 2)
else if(parent->m_channel == 2) console = parent->m_console2; console = m_parent->m_console2;
else qFatal("Nonexistant console requested in uartStyleDecoder::serialDecode"); else
qFatal("Nonexistant console requested in uartStyleDecoder::serialDecode");
} }
uartStyleDecoder::~uartStyleDecoder() void uartStyleDecoder::updateConsole()
{ {
std::lock_guard<std::mutex> lock(mutex); if (!newUartSymbol)
delete updateTimer; return;
delete serialBuffer;
}
void uartStyleDecoder::updateConsole(){
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
if(!newUartSymbol) return;
//qDebug() << serialBuffer->size();
console->setPlainText(QString::fromLocal8Bit(serialBuffer->begin(), serialBuffer->size())); console->setPlainText(QString::fromLocal8Bit(m_serialBuffer.begin(), m_serialBuffer.size()));
if(parent->m_serialAutoScroll){ if (m_parent->m_serialAutoScroll)
{
//http://stackoverflow.com/questions/21059678/how-can-i-set-auto-scroll-for-a-qtgui-qtextedit-in-pyqt4 DANKON //http://stackoverflow.com/questions/21059678/how-can-i-set-auto-scroll-for-a-qtgui-qtextedit-in-pyqt4 DANKON
QTextCursor c = console->textCursor(); QTextCursor c = console->textCursor();
c.movePosition(QTextCursor::End); c.movePosition(QTextCursor::End);
console->setTextCursor(c); console->setTextCursor(c);
// txtedit.ensureCursorVisible(); // you might need this also // txtedit.ensureCursorVisible(); // you might need this also
@ -45,20 +44,18 @@ void uartStyleDecoder::updateConsole(){
//charPos = 0; //charPos = 0;
} }
void uartStyleDecoder::serialDecode(double baudRate) void uartStyleDecoder::serialDecode()
{ {
/*if(stopDecoding){ double dist_seconds = (double)serialDistance()/(m_parent->m_sampleRate_bit);
return; double bitPeriod_seconds = 1.0 / m_baudRate;
}*/
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. // Used to check for wire disconnects. You should get at least one "1" for a stop bit.
bool allZeroes = true; bool allZeroes = true;
while(dist_seconds > (bitPeriod_seconds + SERIAL_DELAY)){ while(dist_seconds > (bitPeriod_seconds + SERIAL_DELAY))
{
// Read next uart bit // Read next uart bit
unsigned char uart_bit = getNextUartBit(); bool uart_bit = getNextUartBit();
if (uart_bit == 1) if (uart_bit == 1)
allZeroes = false; allZeroes = false;
@ -70,63 +67,68 @@ void uartStyleDecoder::serialDecode(double baudRate)
} }
else else
{ {
uartTransmitting = (uart_bit == 1) ? false : true; // Uart starts transmitting after start bit (logic low). // Uart starts transmitting after start bit (logic low).
uartTransmitting = uart_bit == false;
jitterCompensationNeeded = true; jitterCompensationNeeded = true;
} }
// Update the pointer, accounting for jitter // Update the pointer, accounting for jitter
updateSerialPtr(baudRate, uart_bit); updateSerialPtr(uart_bit);
// Calculate stopping condition // Calculate stopping condition
dist_seconds = (double)serialDistance()/(parent->m_sampleRate_bit); dist_seconds = (double)serialDistance()/(m_parent->m_sampleRate_bit);
} }
//Not a single stop bit, or idle bit, in the whole stream. Wire must be disconnected. //Not a single stop bit, or idle bit, in the whole stream. Wire must be disconnected.
if(allZeroes){ if (allZeroes)
{
qDebug() << "Wire Disconnect detected!"; qDebug() << "Wire Disconnect detected!";
wireDisconnected(parent->m_channel); wireDisconnected(m_parent->m_channel);
parent->m_isDecoding = false; m_parent->m_isDecoding = false;
updateTimer->stop(); m_updateTimer.stop();
} }
} }
int uartStyleDecoder::serialDistance() int uartStyleDecoder::serialDistance() const
{ {
int back_bit = parent->m_back * 8; int back_bit = m_parent->m_back * 8;
int bufferEnd_bit = (parent->m_bufferLen-1) * 8; int bufferEnd_bit = (m_parent->m_bufferLen-1) * 8;
if(back_bit >= serialPtr_bit){ if (back_bit >= serialPtr_bit)
return back_bit - serialPtr_bit; return back_bit - serialPtr_bit;
}else return bufferEnd_bit - serialPtr_bit + back_bit; else
return bufferEnd_bit - serialPtr_bit + back_bit;
} }
void uartStyleDecoder::updateSerialPtr(double baudRate, unsigned char current_bit) void uartStyleDecoder::updateSerialPtr(bool current_bit)
{ {
if(jitterCompensationNeeded && uartTransmitting){ if (jitterCompensationNeeded && uartTransmitting)
jitterCompensationNeeded = jitterCompensationProcedure(baudRate, current_bit); jitterCompensationNeeded = jitterCompensationProcedure(current_bit);
}
int distance_between_bits = (parent->m_sampleRate_bit)/baudRate; int distance_between_bits = (m_parent->m_sampleRate_bit)/ m_baudRate;
if(uartTransmitting){ if (uartTransmitting)
serialPtr_bit += distance_between_bits; 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. 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->m_bufferLen * 8)){ if (serialPtr_bit >= (m_parent->m_bufferLen * 8))
serialPtr_bit -= (parent->m_bufferLen * 8); serialPtr_bit -= (m_parent->m_bufferLen * 8);
}
} }
unsigned char uartStyleDecoder::getNextUartBit(){ bool uartStyleDecoder::getNextUartBit() const
int coord_byte = serialPtr_bit/8; {
int coord_bit = serialPtr_bit - (8*coord_byte); int bitIndex = serialPtr_bit;
unsigned char dataByte = parent->m_buffer[coord_byte];
unsigned char mask = (0x01 << coord_bit); int coord_byte = bitIndex/8;
return ((dataByte & mask) ? 1 : 0); int coord_bit = bitIndex - (8*coord_byte);
uint8_t dataByte = m_parent->m_buffer[coord_byte];
uint8_t mask = (0x01 << coord_bit);
return dataByte & mask;
} }
void uartStyleDecoder::decodeNextUartBit(unsigned char bitValue) void uartStyleDecoder::decodeNextUartBit(bool bitValue)
{ {
if (dataBit_current == parityIndex) if (dataBit_current == parityIndex)
{ {
assert(parity != UartParity::None); parityCheckFailed = not isParityCorrect(dataBit_current);
performParityCheck();
dataBit_current++; dataBit_current++;
} }
else if (dataBit_current < dataBit_max) else if (dataBit_current < dataBit_max)
@ -136,7 +138,16 @@ void uartStyleDecoder::decodeNextUartBit(unsigned char bitValue)
} }
else else
{ {
decodeDatabit(dataBit_max + 1); char decodedDatabit = decodeDatabit(dataBit_max + 1, currentUartSymbol);
if (parityCheckFailed)
{
m_serialBuffer.insert("\n<ERROR: Following character contains parity error>\n");
parityCheckFailed = false;
}
m_serialBuffer.insert(decodedDatabit);
currentUartSymbol = 0; currentUartSymbol = 0;
dataBit_current = 0; dataBit_current = 0;
uartTransmitting = false; uartTransmitting = false;
@ -146,33 +157,34 @@ void uartStyleDecoder::decodeNextUartBit(unsigned char bitValue)
//This function compensates for jitter by, when the current bit is a "1", and the last bit was a zero, setting the pointer //This function compensates for jitter by, when the current bit is a "1", and the last bit was a zero, setting the pointer
//to the sample at the midpoint between this bit and the last. //to the sample at the midpoint between this bit and the last.
bool uartStyleDecoder::jitterCompensationProcedure(double baudRate, unsigned char current_bit){ bool uartStyleDecoder::jitterCompensationProcedure(bool current_bit)
{
//We only run when the current bit is a "1", to prevent slowdown when there are long breaks between transmissions. //We only run when the current bit is a "1", to prevent slowdown when there are long breaks between transmissions.
if(current_bit == 0){ if (current_bit == false)
return true; return true;
}
//Can't be bothered dealing with the corner case where the serial pointer is at the very start of the buffer. //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. //Just return and try again next time.
int left_coord = serialPtr_bit - (parent->m_sampleRate_bit)/baudRate; int left_coord = serialPtr_bit - (m_parent->m_sampleRate_bit)/ m_baudRate;
//qDebug() << "left_coord =" << left_coord; //qDebug() << "left_coord =" << left_coord;
if (left_coord < 0){ if (left_coord < 0)
return true; //Don't want to read out of bounds!! return true; //Don't want to read out of bounds!!
}
//The usual case, when transmitting anyway. //The usual case, when transmitting anyway.
unsigned char left_byte = (parent->m_buffer[left_coord/8] & 0xff); uint8_t left_byte = (m_parent->m_buffer[left_coord/8] & 0xff);
//Only run when a zero is detected in the leftmost symbol. //Only run when a zero is detected in the leftmost symbol.
if(left_byte != 255){ if (left_byte != 0xff)
{
//Step back, one sample at a time, to the 0->1 transition point //Step back, one sample at a time, to the 0->1 transition point
unsigned char temp_bit = 1; bool temp_bit = 1;
while(temp_bit){ while(temp_bit)
{
temp_bit = getNextUartBit(); temp_bit = getNextUartBit();
serialPtr_bit--; serialPtr_bit--;
} }
//Jump the pointer forward by half a uart bit period, and return "done!". //Jump the pointer forward by half a uart bit period, and return "done!".
serialPtr_bit += (parent->m_sampleRate_bit/baudRate)/2; serialPtr_bit += (m_parent->m_sampleRate_bit/ m_baudRate)/2;
return false; return false;
} }
@ -180,27 +192,24 @@ bool uartStyleDecoder::jitterCompensationProcedure(double baudRate, unsigned cha
} }
//Basically scaffolding to add character maps for other modes (5 bit, for example). //Basically scaffolding to add character maps for other modes (5 bit, for example).
void uartStyleDecoder::decodeDatabit(int mode){ char uartStyleDecoder::decodeDatabit(int mode, short symbol) const
char tempchar; {
switch(mode){ switch(mode)
{
case 5: case 5:
tempchar = decode_baudot(currentUartSymbol); return decodeBaudot(symbol);
break; break;
case 8: //8-bit ASCII; case 8: //8-bit ASCII;
tempchar = currentUartSymbol; return symbol;
break; break;
default: default:
qDebug() << "uartStyleDecoder::decodeDatabit is failing..."; qDebug() << "uartStyleDecoder::decodeDatabit is failing...";
return -1; // Garbage
} }
if (parityCheckFailed)
{
serialBuffer->insert("\n<ERROR: Following character contains parity error>\n");
parityCheckFailed = false;
}
serialBuffer->insert(tempchar);
} }
char uartStyleDecoder::decode_baudot(short symbol){ char uartStyleDecoder::decodeBaudot(short symbol) const
{
return 'a'; return 'a';
} }
@ -219,35 +228,21 @@ void uartStyleDecoder::setParityMode(UartParity newParity)
parity = newParity; parity = newParity;
} }
void uartStyleDecoder::performParityCheck() bool uartStyleDecoder::isParityCorrect(uint32_t bitField) const
{ {
auto isEvenParity = [=] () -> bool if (parity == UartParity::None)
{ std::terminate();
uint32_t mask = 0x00000001;
uint8_t parity = 0;
for (int i = 0; i < dataBit_max; i++)
{
const uint8_t currentBit = (dataBit_current & mask) ? 1 : 0;
parity = parity ^ currentBit;
mask = mask << 1;
}
return parity == 0; return parityOf(bitField) == parity;
};
switch(parity)
{
case UartParity::None:
assert(false);
return;
case UartParity::Even:
parityCheckFailed = ! isEvenParity();
case UartParity::Odd:
parityCheckFailed = isEvenParity();
}
return;
} }
UartParity uartStyleDecoder::parityOf(uint32_t bitField) const
{
bool result = false;
for (uint32_t mask = 1 << (dataBit_max-1); mask != 0; mask >>= 1)
result ^= static_cast<bool>(bitField & mask);
return result ? UartParity::Odd : UartParity::Even;
}

View File

@ -19,14 +19,16 @@ class uartStyleDecoder : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit uartStyleDecoder(QObject *parent_in = NULL); explicit uartStyleDecoder(double baudRate, QObject *parent = NULL);
~uartStyleDecoder(); ~uartStyleDecoder() = default;
void serialDecode(double baudRate);
int serialDistance();
QTimer *updateTimer;
private: private:
isoBuffer *parent; isoBuffer *m_parent;
// Indicates the current bit being decoded.
int serialPtr_bit; int serialPtr_bit;
bool uartTransmitting = false; bool uartTransmitting = false;
bool newUartSymbol = false; bool newUartSymbol = false;
uint32_t dataBit_current = 0; uint32_t dataBit_current = 0;
@ -34,17 +36,31 @@ private:
uint32_t dataBit_max = 7; uint32_t dataBit_max = 7;
unsigned short currentUartSymbol = 0; unsigned short currentUartSymbol = 0;
bool jitterCompensationNeeded = true; bool jitterCompensationNeeded = true;
void updateSerialPtr(double baudRate, unsigned char current_bit);
unsigned char getNextUartBit(); void updateSerialPtr(bool current_bit);
void decodeNextUartBit(unsigned char bitValue); bool getNextUartBit() const;
bool jitterCompensationProcedure(double baudRate, unsigned char current_bit); void decodeNextUartBit(bool bitValue);
bool jitterCompensationProcedure(bool current_bit);
QPlainTextEdit *console; QPlainTextEdit *console;
isoBufferBuffer *serialBuffer; isoBufferBuffer m_serialBuffer;
void decodeDatabit(int mode); public:
char decode_baudot(short symbol); double m_baudRate;
QTimer m_updateTimer; // IMPORTANT: must be after m_serialBuffer. construction / destruction order matters
public:
void serialDecode();
int serialDistance() const;
private:
char decodeDatabit(int mode, short symbol) const;
char decodeBaudot(short symbol) const;
std::mutex mutex; std::mutex mutex;
UartParity parity = UartParity::None; UartParity parity = UartParity::None;
void performParityCheck();
bool isParityCorrect(uint32_t bitField) const;
UartParity parityOf(uint32_t bitField) const;
bool parityCheckFailed = false; bool parityCheckFailed = false;
signals: signals:
void wireDisconnected(int); void wireDisconnected(int);