kopia lustrzana https://gitlab.com/eliggett/wfview
418 wiersze
14 KiB
C++
418 wiersze
14 KiB
C++
#include "commhandler.h"
|
|
#include "logcategories.h"
|
|
|
|
#include <QDebug>
|
|
|
|
// Copyright 2017-2020 Elliott H. Liggett
|
|
|
|
commHandler::commHandler(QObject* parent) : QObject(parent)
|
|
{
|
|
//constructor
|
|
// TODO: The following should become arguments and/or functions
|
|
// Add signal/slot everywhere for comm port setup.
|
|
// Consider how to "re-setup" and how to save the state for next time.
|
|
baudrate = 115200;
|
|
stopbits = 1;
|
|
portName = "/dev/ttyUSB0";
|
|
this->PTTviaRTS = false;
|
|
this->halfDuplex=false;
|
|
init();
|
|
}
|
|
|
|
commHandler::commHandler(QString portName, quint32 baudRate, quint8 wfFormat, QObject* parent) : QObject(parent)
|
|
{
|
|
//constructor
|
|
// grab baud rate and other comm port details
|
|
// if they need to be changed later, please
|
|
// destroy this and create a new one.
|
|
|
|
if (wfFormat == 1) { // Single waterfall packet
|
|
combineWf = true;
|
|
qDebug(logSerial()) << "*********** Combine Waterfall Mode Enabled!";
|
|
}
|
|
|
|
// TODO: The following should become arguments and/or functions
|
|
// Add signal/slot everywhere for comm port setup.
|
|
// Consider how to "re-setup" and how to save the state for next time.
|
|
baudrate = baudRate;
|
|
stopbits = 1;
|
|
this->portName = portName;
|
|
this->PTTviaRTS = false;
|
|
init();
|
|
|
|
}
|
|
|
|
void commHandler::init()
|
|
{
|
|
if (port != Q_NULLPTR) {
|
|
delete port;
|
|
port = Q_NULLPTR;
|
|
isConnected = false;
|
|
}
|
|
|
|
port = new QSerialPort();
|
|
setupComm(); // basic parameters
|
|
openPort();
|
|
// qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
|
|
//port->setReadBufferSize(1024); // manually. 256 never saw any return from the radio. why...
|
|
//qInfo(logSerial()) << "Serial buffer size: " << port->readBufferSize();
|
|
|
|
connect(port, SIGNAL(readyRead()), this, SLOT(receiveDataIn()));
|
|
#if (QT_VERSION < QT_VERSION_CHECK(6,0,0))
|
|
connect(port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));
|
|
#else
|
|
connect(port, SIGNAL(errorOccurred(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));
|
|
#endif
|
|
|
|
connect(this, SIGNAL(sendDataOutToPort(const QByteArray&)),this,SLOT(sendDataOut(const QByteArray &)));
|
|
lastDataReceived = QTime::currentTime();
|
|
}
|
|
|
|
commHandler::~commHandler()
|
|
{
|
|
qInfo(logSerial()) << "Closing serial port: " << port->portName();
|
|
if (isConnected) {
|
|
this->closePort();
|
|
}
|
|
delete port;
|
|
}
|
|
|
|
void commHandler::setHalfDuplex(bool en)
|
|
{
|
|
halfDuplex=en;
|
|
qInfo(logSerial()) << "Setting half duplex comms: " << ((en) ? "Enabled":"Disabled");
|
|
}
|
|
|
|
void commHandler::setupComm()
|
|
{
|
|
serialError = false;
|
|
port->setPortName(portName);
|
|
port->setBaudRate(baudrate);
|
|
port->setStopBits(QSerialPort::OneStop);// OneStop is other option
|
|
}
|
|
|
|
void commHandler::receiveDataFromUserToRig(const QByteArray &data)
|
|
{
|
|
sendDataOut(data);
|
|
}
|
|
|
|
void commHandler::sendDataOut(const QByteArray &writeData)
|
|
{
|
|
if (halfDuplex)
|
|
{
|
|
QMutexLocker locker(&duplexMutex);
|
|
}
|
|
|
|
// Recycle port to attempt reconnection.
|
|
if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) {
|
|
qDebug(logSerial()) << "Serial port error? Attempting reconnect...";
|
|
lastDataReceived = QTime::currentTime();
|
|
QTimer::singleShot(500, this, SLOT(init()));
|
|
return;
|
|
}
|
|
|
|
mutex.lock();
|
|
|
|
qint64 bytesWritten;
|
|
|
|
previousSent = writeData;
|
|
|
|
if(PTTviaRTS)
|
|
{
|
|
// Size: 1 2 3 4 5 6 7 8
|
|
//index: 0 1 2 3 4 5 6 7
|
|
//Query: FE FE TO FROM 0x1C 0x00 0xFD
|
|
//PTT On: FE FE TO FROM 0x1C 0x00 0x01 0xFD
|
|
//PTT Off: FE FE TO FROM 0x1C 0x00 0x00 0xFD
|
|
|
|
if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\xFD")))
|
|
{
|
|
// Query
|
|
//qDebug(logSerial()) << "Looks like PTT Query";
|
|
bool pttOn = this->rtsStatus();
|
|
QByteArray pttreturncmd = QByteArray("\xFE\xFE");
|
|
pttreturncmd.append(writeData.at(3));
|
|
pttreturncmd.append(writeData.at(2));
|
|
pttreturncmd.append(QByteArray("\x1C\x00", 2));
|
|
pttreturncmd.append((char)pttOn);
|
|
pttreturncmd.append("\xFD");
|
|
//qDebug(logSerial()) << "Sending fake PTT query result: " << (bool)pttOn;
|
|
printHex(pttreturncmd, false, true);
|
|
emit haveDataFromPort(pttreturncmd);
|
|
|
|
|
|
mutex.unlock();
|
|
return;
|
|
} else if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\x01\xFD")))
|
|
{
|
|
// PTT ON
|
|
//qDebug(logSerial()) << "Looks like PTT ON";
|
|
setRTS(true);
|
|
mutex.unlock();
|
|
return;
|
|
} else if(writeData.endsWith(QByteArrayLiteral("\x1C\x00\x00\xFD")))
|
|
{
|
|
// PTT OFF
|
|
//qDebug(logSerial()) << "Looks like PTT OFF";
|
|
setRTS(false);
|
|
mutex.unlock();
|
|
return;
|
|
}
|
|
}
|
|
|
|
bytesWritten = port->write(writeData);
|
|
|
|
if(bytesWritten != (qint64)writeData.size())
|
|
{
|
|
qDebug(logSerial()) << "bytesWritten: " << bytesWritten << " length of byte array: " << writeData.length()\
|
|
<< " size of byte array: " << writeData.size()\
|
|
<< " Wrote all bytes? " << (bool) (bytesWritten == (qint64)writeData.size());
|
|
}
|
|
|
|
mutex.unlock();
|
|
}
|
|
|
|
void commHandler::receiveDataIn()
|
|
{
|
|
if (halfDuplex)
|
|
{
|
|
QMutexLocker locker(&duplexMutex);
|
|
}
|
|
// connected to comm port data signal
|
|
|
|
// Here we get a little specific to CIV radios
|
|
// because we know what constitutes a valid "frame" of data.
|
|
|
|
// new code:
|
|
lastDataReceived = QTime::currentTime();
|
|
port->startTransaction();
|
|
inPortData = port->readAll();
|
|
|
|
if (inPortData.startsWith("\xFC\xFC\xFC\xFC\xFC"))
|
|
{
|
|
// Collision detected by remote end, re-send previous command.
|
|
qInfo(logSerial()) << "Collision detected by remote, resending previous command";
|
|
port->commitTransaction();
|
|
emit sendDataOutToPort(previousSent);
|
|
return;
|
|
}
|
|
|
|
if(inPortData.size() == 1)
|
|
{
|
|
// Generally for baud <= 9600
|
|
if (inPortData == "\xFE")
|
|
{
|
|
// This will get hit twice.
|
|
// After the FE FE, we transition into
|
|
// the normal .startsWith FE FE block
|
|
// where the normal rollback code can handle things.
|
|
port->rollbackTransaction();
|
|
rolledBack = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (inPortData.startsWith("\xFE\xFE"))
|
|
{
|
|
if(inPortData.contains("\xFC"))
|
|
{
|
|
//qInfo(logSerial()) << "Transaction contains collision data. Dumping.";
|
|
//printHex(inPortData, false, true);
|
|
port->commitTransaction();
|
|
return;
|
|
}
|
|
if(inPortData.endsWith("\xFD"))
|
|
{
|
|
// good!
|
|
port->commitTransaction();
|
|
|
|
//payloadIn = data.right(data.length() - 4);
|
|
|
|
// Do we need to combine waterfall into single packet?
|
|
int combined = 0;
|
|
if (combineWf) {
|
|
int pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00"));
|
|
int fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
|
|
//printHex(inPortData, false, true);
|
|
while (pos > -1 && fdPos > -1) {
|
|
combined++;
|
|
spectrumDivisionNumber = inPortData[pos + 3] & 0x0f;
|
|
spectrumDivisionNumber += ((inPortData[pos + 3] & 0xf0) >> 4) * 10;
|
|
|
|
if (spectrumDivisionNumber == 1) {
|
|
// This is the first waveform data.
|
|
spectrumDivisionMax = 0;
|
|
spectrumDivisionMax = inPortData[pos + 4] & 0x0f;
|
|
spectrumDivisionMax += ((inPortData[pos + 4] & 0xf0) >> 4) * 10;
|
|
spectrumData.clear();
|
|
spectrumData = inPortData.mid(pos - 4, fdPos+4); // Don't include terminating FD
|
|
spectrumData[8] = spectrumData[7]; // Make max = current;
|
|
//qDebug() << "New Spectrum seq:" << spectrumDivisionNumber << "pos = " << pos << "len" << fdPos;
|
|
|
|
}
|
|
else if (spectrumDivisionNumber > lastSpectrum && spectrumDivisionNumber <= spectrumDivisionMax) {
|
|
spectrumData.insert(spectrumData.length(), inPortData.mid(pos + 5, fdPos-5));
|
|
//qInfo() << "Added spectrum seq:" << spectrumDivisionNumber << "len" << fdPos-5<< "Spec" << spectrumData.length();
|
|
//printHex(inPortData.mid((pos+4),fdPos - (pos+5)), false, true);
|
|
}
|
|
else {
|
|
qDebug() << "Invalid Spectrum Division received" << spectrumDivisionNumber << "last Spectrum" << lastSpectrum;
|
|
}
|
|
|
|
lastSpectrum = spectrumDivisionNumber;
|
|
|
|
if (spectrumDivisionNumber == spectrumDivisionMax) {
|
|
//qDebug() << "Got Spectrum! length=" << spectrumData.length();
|
|
spectrumData.append("\xfd"); // Need to add FD on the end.
|
|
//printHex(spectrumData, false, true);
|
|
emit haveDataFromPort(spectrumData);
|
|
lastSpectrum = 0;
|
|
}
|
|
inPortData = inPortData.remove(pos-4, fdPos+5);
|
|
pos = inPortData.indexOf(QByteArrayLiteral("\x27\x00\x00"));
|
|
fdPos = inPortData.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
|
|
}
|
|
// If we still have data left, let the main function deal with it, any spectrum data has been removed
|
|
if (inPortData.length() == 0)
|
|
{
|
|
return;
|
|
}
|
|
// qDebug() << "Got extra data!";
|
|
//printHex(inPortData, false, true);
|
|
}
|
|
//
|
|
emit haveDataFromPort(inPortData);
|
|
|
|
if(rolledBack)
|
|
{
|
|
// qInfo(logSerial()) << "Rolled back and was successful. Length: " << inPortData.length();
|
|
//printHex(inPortData, false, true);
|
|
rolledBack = false;
|
|
}
|
|
} else {
|
|
// did not receive the entire thing so roll back:
|
|
// qInfo(logSerial()) << "Rolling back transaction. End not detected. Length: " << inPortData.length();
|
|
//printHex(inPortData, false, true);
|
|
port->rollbackTransaction();
|
|
rolledBack = true;
|
|
}
|
|
} else {
|
|
port->commitTransaction(); // do not emit data, do not keep data.
|
|
//qInfo(logSerial()) << "Warning: received data with invalid start. Dropping data.";
|
|
//qInfo(logSerial()) << "THIS SHOULD ONLY HAPPEN ONCE!!";
|
|
// THIS SHOULD ONLY HAPPEN ONCE!
|
|
|
|
// unrecoverable. We did not receive the start and must
|
|
// have missed it earlier because we did not roll back to
|
|
// preserve the beginning.
|
|
|
|
//printHex(inPortData, false, true);
|
|
|
|
}
|
|
}
|
|
|
|
void commHandler::setRTS(bool rtsOn)
|
|
{
|
|
bool success = port->setRequestToSend(rtsOn);
|
|
if(!success)
|
|
{
|
|
qInfo(logSerial()) << "Error, could not set RTS on port " << portName;
|
|
}
|
|
}
|
|
|
|
bool commHandler::rtsStatus()
|
|
{
|
|
return port->isRequestToSend();
|
|
}
|
|
|
|
void commHandler::setUseRTSforPTT(bool PTTviaRTS)
|
|
{
|
|
this->PTTviaRTS = PTTviaRTS;
|
|
}
|
|
|
|
void commHandler::openPort()
|
|
{
|
|
bool success;
|
|
success = port->open(QIODevice::ReadWrite);
|
|
if(success)
|
|
{
|
|
port->setDataTerminalReady(false);
|
|
port->setRequestToSend(false);
|
|
isConnected = true;
|
|
qInfo(logSerial()) << "Opened port: " << portName;
|
|
return;
|
|
} else {
|
|
qInfo(logSerial()) << "Could not open serial port " << portName << " , please check Radio Access under Settings.";
|
|
isConnected = false;
|
|
serialError = true;
|
|
emit havePortError(errorType(true, portName, "Could not open Serial or USB port.\nPlease check Radio Access under Settings."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void commHandler::closePort()
|
|
{
|
|
if(port)
|
|
{
|
|
port->close();
|
|
//delete port;
|
|
}
|
|
isConnected = false;
|
|
}
|
|
|
|
void commHandler::debugThis()
|
|
{
|
|
// Do not use, function is for debug only and subject to change.
|
|
qInfo(logSerial()) << "comm debug called.";
|
|
|
|
inPortData = port->readAll();
|
|
emit haveDataFromPort(inPortData);
|
|
}
|
|
|
|
|
|
void commHandler::printHex(const QByteArray &pdata, bool printVert, bool printHoriz)
|
|
{
|
|
qDebug(logSerial()) << "---- Begin hex dump -----:";
|
|
QString sdata("DATA: ");
|
|
QString index("INDEX: ");
|
|
QStringList strings;
|
|
|
|
for(int i=0; i < pdata.length(); i++)
|
|
{
|
|
strings << QString("[%1]: %2").arg(i,8,10,QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0'));
|
|
sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')) );
|
|
index.append(QString("%1 ").arg(i, 2, 10, QChar('0')));
|
|
}
|
|
|
|
if(printVert)
|
|
{
|
|
for(int i=0; i < strings.length(); i++)
|
|
{
|
|
qDebug(logSerial()) << strings.at(i);
|
|
}
|
|
}
|
|
|
|
if(printHoriz)
|
|
{
|
|
qDebug(logSerial()) << index;
|
|
qDebug(logSerial()) << sdata;
|
|
}
|
|
qDebug(logSerial()) << "----- End hex dump -----";
|
|
}
|
|
|
|
|
|
void commHandler::handleError(QSerialPort::SerialPortError err)
|
|
{
|
|
switch (err) {
|
|
case QSerialPort::NoError:
|
|
break;
|
|
default:
|
|
if (lastDataReceived.msecsTo(QTime::currentTime()) > 2000) {
|
|
qDebug(logSerial()) << "Serial port" << port->portName() << "Error, attempting disconnect/reconnect";
|
|
lastDataReceived = QTime::currentTime();
|
|
QTimer::singleShot(500, this, SLOT(init()));
|
|
}
|
|
break;
|
|
}
|
|
}
|