1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-01-05 15:58:50 -05:00

LoRa mod: fixed packet length. LoRa demod: enhancements

This commit is contained in:
f4exb 2020-02-21 02:18:36 +01:00
parent 9277813f23
commit 118840db0c
16 changed files with 612 additions and 337 deletions

View File

@ -119,6 +119,9 @@ bool LoRaDemod::handleMessage(const Message& cmd)
msgToGUI->setPacketSize(m_decoder.getPacketLength());
msgToGUI->setNbParityBits(m_decoder.getNbParityBits());
msgToGUI->setHasCRC(m_decoder.getHasCRC());
msgToGUI->setNbSymbols(m_decoder.getNbSymbols());
msgToGUI->setNbCodewords(m_decoder.getNbCodewords());
msgToGUI->setEarlyEOM(m_decoder.getEarlyEOM());
msgToGUI->setHeaderParityStatus(m_decoder.getHeaderParityStatus());
msgToGUI->setHeaderCRCStatus(m_decoder.getHeaderCRCStatus());
msgToGUI->setPayloadParityStatus(m_decoder.getPayloadParityStatus());
@ -200,7 +203,6 @@ void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
<< " m_hasCRC: " << settings.m_hasCRC
<< " m_nbParityBits: " << settings.m_nbParityBits
<< " m_packetLength: " << settings.m_packetLength
<< " m_errorCheck: " << settings.m_errorCheck
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
@ -230,10 +232,6 @@ void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
m_decoder.setLoRaPacketLength(settings.m_packetLength);
}
if ((settings.m_errorCheck != m_settings.m_errorCheck) || force) {
m_decoder.setErrorCheck(settings.m_errorCheck);
}
LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband *msg = LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);

View File

@ -67,10 +67,13 @@ public:
float getNoiseDb() const { return m_noiseDb; }
unsigned int getPacketSize() const { return m_packetSize; }
unsigned int getNbParityBits() const { return m_nbParityBits; }
unsigned int getNbSymbols() const { return m_nbSymbols; }
unsigned int getNbCodewords() const { return m_nbCodewords; }
bool getHasCRC() const { return m_hasCRC; }
bool getHeaderParityStatus() const { return m_headerParityStatus; }
bool getEarlyEOM() const { return m_earlyEOM; }
int getHeaderParityStatus() const { return m_headerParityStatus; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
bool getPayloadParityStatus() const { return m_payloadParityStatus; }
int getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
static MsgReportDecodeBytes* create(const QByteArray& bytes) {
@ -91,16 +94,25 @@ public:
void setNbParityBits(unsigned int nbParityBits) {
m_nbParityBits = nbParityBits;
}
void setNbSymbols(unsigned int nbSymbols) {
m_nbSymbols = nbSymbols;
}
void setNbCodewords(unsigned int nbCodewords) {
m_nbCodewords = nbCodewords;
}
void setHasCRC(bool hasCRC) {
m_hasCRC = hasCRC;
}
void setHeaderParityStatus(bool headerParityStatus) {
void setEarlyEOM(bool earlyEOM) {
m_earlyEOM = earlyEOM;
}
void setHeaderParityStatus(int headerParityStatus) {
m_headerParityStatus = headerParityStatus;
}
void setHeaderCRCStatus(bool headerCRCStatus) {
m_headerCRCStatus = headerCRCStatus;
}
void setPayloadParityStatus(bool payloadParityStatus) {
void setPayloadParityStatus(int payloadParityStatus) {
m_payloadParityStatus = payloadParityStatus;
}
void setPayloadCRCStatus(bool payloadCRCStatus) {
@ -114,10 +126,13 @@ public:
float m_noiseDb;
unsigned int m_packetSize;
unsigned int m_nbParityBits;
unsigned int m_nbSymbols;
unsigned int m_nbCodewords;
bool m_hasCRC;
bool m_headerParityStatus;
bool m_earlyEOM;
int m_headerParityStatus;
bool m_headerCRCStatus;
bool m_payloadParityStatus;
int m_payloadParityStatus;
bool m_payloadCRCStatus;
MsgReportDecodeBytes(const QByteArray& bytes) :
@ -128,7 +143,10 @@ public:
m_noiseDb(0.0),
m_packetSize(0),
m_nbParityBits(0),
m_nbSymbols(0),
m_nbCodewords(0),
m_hasCRC(false),
m_earlyEOM(false),
m_headerParityStatus(false),
m_headerCRCStatus(false),
m_payloadParityStatus(false),

View File

@ -78,12 +78,21 @@ void LoRaDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_errorCheck,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
LoRaDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
}
break;
}

View File

@ -33,15 +33,17 @@ public:
void setLoRaHasHeader(bool hasHeader) { m_hasHeader = hasHeader; }
void setLoRaHasCRC(bool hasCRC) { m_hasCRC = hasCRC; }
void setLoRaPacketLength(unsigned int packetLength) { m_packetLength = packetLength; }
void setErrorCheck(bool errorCheck) { m_errorCheck = errorCheck; }
void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str); //!< For ASCII and TTY
void decodeSymbols(const std::vector<unsigned short>& symbols, QByteArray& bytes); //!< For raw bytes (original LoRa)
unsigned int getNbParityBits() const { return m_nbParityBits; }
unsigned int getPacketLength() const { return m_packetLength; }
bool getHasCRC() const { return m_hasCRC; }
bool getHeaderParityStatus() const { return m_headerParityStatus; }
unsigned int getNbSymbols() const { return m_nbSymbols; }
unsigned int getNbCodewords() const { return m_nbCodewords; }
bool getEarlyEOM() const { return m_earlyEOM; }
int getHeaderParityStatus() const { return m_headerParityStatus; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
bool getPayloadParityStatus() const { return m_payloadParityStatus; }
int getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
private:
@ -54,10 +56,12 @@ private:
bool m_hasCRC;
bool m_hasHeader;
unsigned int m_packetLength;
bool m_errorCheck;
bool m_headerParityStatus;
unsigned int m_nbSymbols; //!< Number of encoded symbols: this is only dependent of nbSymbolBits, nbParityBits, packetLength, hasHeader and hasCRC
unsigned int m_nbCodewords; //!< Number of encoded codewords: this is only dependent of nbSymbolBits, nbParityBits, packetLength, hasHeader and hasCRC
bool m_earlyEOM;
int m_headerParityStatus;
bool m_headerCRCStatus;
bool m_payloadParityStatus;
int m_payloadParityStatus;
bool m_payloadCRCStatus;
};

View File

@ -19,6 +19,83 @@
#include "lorademoddecoderlora.h"
void LoRaDemodDecoderLoRa::decodeHeader(
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
int& headerParityStatus,
bool& headerCRCStatus
)
{
// with header (H: header 8-bit codeword P: payload-8 bit codeword):
// nbSymbolBits = 5 |H|H|H|H|H| codewords => 8 symbols (always) : static headerSymbols = 8
// nbSymbolBits = 7 |H|H|H|H|H|P|P|
// without header (P: payload 8-bit codeword):
// nbSymbolBits = 5 |P|P|P|P|P| codewords => 8 symbols (always)
// nbSymbolBits = 7 |P|P|P|P|P|P|P|
// Actual header is always represented with 5 8-bit codewords : static headerCodewords = 5
// These 8-bit codewords are encoded with Hamming(4,8) FEC : static headerParityBits = 4
const unsigned int numSymbols = roundUp(inSymbols.size(), 4 + nbParityBits);
const unsigned int numCodewords = (numSymbols / (4 + nbParityBits))*nbSymbolBits;
std::vector<uint16_t> symbols(headerSymbols);
std::copy(inSymbols.begin(), inSymbols.begin() + headerSymbols, symbols.begin());
//gray encode
for (auto &sym : symbols) {
sym = binaryToGray16(sym);
}
std::vector<uint8_t> codewords(nbSymbolBits);
// Header symbols de-interleave thus headerSymbols (8) symbols into nbSymbolBits (5..12) codewords using header FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), headerSymbols, codewords.data(), nbSymbolBits, headerParityBits);
// whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, nbSymbolBits - headerCodewords, 0, headerParityBits);
bool error = false;
bool bad = false;
uint8_t bytes[3];
// decode actual header inside 8-bit codewords header with 4/8 FEC (5 first codewords)
bytes[0] = decodeHamming84sx(codewords[1], error, bad) & 0xf;
bytes[0] |= decodeHamming84sx(codewords[0], error, bad) << 4; // length
bytes[1] = decodeHamming84sx(codewords[2], error, bad) & 0xf; // coding rate and crc enable
bytes[2] = decodeHamming84sx(codewords[4], error, bad) & 0xf;
bytes[2] |= decodeHamming84sx(codewords[3], error, bad) << 4; // checksum
bytes[2] ^= headerChecksum(bytes);
if (bad)
{
headerParityStatus = (int) ParityError;
}
else
{
if (error) {
headerParityStatus = (int) ParityCorrected;
} else {
headerParityStatus = (int) ParityOK;
}
if (bytes[2] != 0) {
headerCRCStatus = false;
} else {
headerCRCStatus = true;
}
}
hasCRC = (bytes[1] & 1) != 0;
nbParityBits = (bytes[1] >> 1) & 0x7;
packetLength = bytes[0];
}
void LoRaDemodDecoderLoRa::decodeBytes(
QByteArray& inBytes,
const std::vector<unsigned short>& inSymbols,
@ -27,14 +104,44 @@ void LoRaDemodDecoderLoRa::decodeBytes(
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool errorCheck,
bool& headerParityStatus,
bool& earlyEOM,
int& headerParityStatus,
bool& headerCRCStatus,
bool& payloadParityStatus,
int& payloadParityStatus,
bool& payloadCRCStatus
)
{
if (inSymbols.size() < 8) { // need at least a header
// need at least a header (8 symbols of 8 bit codewords) whether an actual header is sent or not
if (inSymbols.size() < headerSymbols)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: need at least %u symbols for header", headerSymbols);
earlyEOM = true;
return;
}
else
{
earlyEOM = false;
}
if (hasHeader)
{
decodeHeader(
inSymbols,
nbSymbolBits,
hasCRC,
nbParityBits,
packetLength,
headerParityStatus,
headerCRCStatus
);
}
qDebug("LoRaDemodDecoderLoRa::decodeBytes: crc: %s nbParityBits: %u packetLength: %u",
hasCRC ? "on": "off", nbParityBits, packetLength);
if (nbParityBits > 4)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: invalid parity bits in header: %u", nbParityBits);
return;
}
@ -54,20 +161,24 @@ void LoRaDemodDecoderLoRa::decodeBytes(
unsigned int sOfs = 0;
unsigned int cOfs = 0;
if (nbParityBits != 4)
{
diagonalDeinterleaveSx(symbols.data(), 8, codewords.data(), nbSymbolBits, 4);
// the first headerSymbols (8 symbols) are coded with 4/8 FEC (thus 8 bit codewords) whether an actual header is present or not
// this corresponds to nbSymbolBits codewords (therefore LoRa imposes nbSymbolBits >= headerCodewords (5 codewords) this is controlled externally)
if (hasHeader) {
if (nbParityBits != 4) // different FEC between header symbols and the rest of the packet
{
// Header symbols de-interleave thus headerSymbols (8) symbols into nbSymbolBits (5..12) codewords using header FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), headerSymbols, codewords.data(), nbSymbolBits, headerParityBits);
if (hasHeader) { // whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, nbSymbolBits - headerCodewords, 0, headerParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), nbSymbolBits, 0, headerParityBits);
}
cOfs += nbSymbolBits;
sOfs += headerSymbols;
cOfs += nbSymbolBits; // nbSymbolBits codewords in header
sOfs += headerSymbols; // headerSymbols symbols in header
if (numSymbols - sOfs > 0)
if (numSymbols - sOfs > 0) // remaining payload symbols after header symbols using their own FEC (4/5..4/7)
{
diagonalDeinterleaveSx(symbols.data() + sOfs, numSymbols - sOfs, codewords.data() + cOfs, nbSymbolBits, nbParityBits);
@ -78,101 +189,48 @@ void LoRaDemodDecoderLoRa::decodeBytes(
}
}
}
else
else // uniform 4/8 FEC for all the packet
{
// De-interleave the whole packet thus numSymbols into nbSymbolBits (5..12) codewords using packet FEC (4/8)
diagonalDeinterleaveSx(symbols.data(), numSymbols, codewords.data(), nbSymbolBits, nbParityBits);
if (hasHeader) {
if (hasHeader) { // whitening does not apply to the header codewords
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, numCodewords - headerCodewords, 0, nbParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), numCodewords, 0, nbParityBits);
}
}
bool error = false;
bool bad = false;
// Now we have nbSymbolBits 8-bit codewords (4/8 FEC) possibly containing the actual header followed by the rest of payload codewords with their own FEC (4/5..4/8)
std::vector<uint8_t> bytes((codewords.size()+1) / 2);
unsigned int dOfs = 0;
cOfs = 0;
unsigned int dataLength = 0;
bool hasCRCInt; // payload has CRC indicator internal (explicit or implicit)
unsigned int nbParityBitsInt; // number of parity bits internal (explicit or implicit)
unsigned int packetLengthInt; // packet length internal (explicit or implicit)
unsigned int dataLength = packetLength + 3 + (hasCRC ? 2 : 0); // include header and CRC
if (hasHeader)
{
bytes[0] = decodeHamming84sx(codewords[1], error, bad) & 0xf;
bytes[0] |= decodeHamming84sx(codewords[0], error, bad) << 4; // length
bytes[1] = decodeHamming84sx(codewords[2], error, bad) & 0xf; // coding rate and crc enable
bytes[2] = decodeHamming84sx(codewords[4], error, bad) & 0xf;
bytes[2] |= decodeHamming84sx(codewords[3], error, bad) << 4; // checksum
bytes[2] ^= headerChecksum(bytes.data());
if (error)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: header Hamming error");
headerParityStatus = false;
if (errorCheck) {
return;
}
}
else
{
headerParityStatus = true;
if (bytes[2] != 0)
{
headerCRCStatus = false;
qDebug("LoRaDemodDecoderLoRa::decodeBytes: header CRC error");
if (errorCheck) {
return;
}
}
else
{
headerCRCStatus = true;
}
}
hasCRCInt = (bytes[1] & 1) != 0;
nbParityBitsInt = (bytes[1] >> 1) & 0x7;
if (nbParityBitsInt > 4) {
return;
}
packetLengthInt = bytes[0];
//dataLength = packetLengthInt + 3 + (hasCRCInt ? 2 : 0); // include header and crc
dataLength = packetLengthInt + 5;
cOfs = headerCodewords;
dOfs = 6;
}
else
{
hasCRCInt = hasCRC;
nbParityBitsInt = nbParityBits;
packetLengthInt = packetLength;
cOfs = 0;
dOfs = 0;
if (hasCRCInt) {
dataLength = packetLengthInt + 2;
} else {
dataLength = packetLengthInt;
}
}
if (dataLength > bytes.size()) {
if (dataLength > bytes.size())
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: not enough data %lu vs %u", bytes.size(), dataLength);
earlyEOM = true;
return;
}
// decode the rest of the payload inside 8-bit codewords header with 4/8 FEC
bool error = false;
bool bad = false;
for (; cOfs < nbSymbolBits; cOfs++, dOfs++)
{
if (dOfs % 2 == 1) {
@ -182,15 +240,15 @@ void LoRaDemodDecoderLoRa::decodeBytes(
}
}
if (dOfs % 2 == 1)
if (dOfs % 2 == 1) // decode the start of the payload codewords with their own FEC when not on an even boundary
{
if (nbParityBitsInt == 1) {
if (nbParityBits == 1) {
bytes[dOfs/2] |= checkParity54(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 2) {
} else if (nbParityBits == 2) {
bytes[dOfs/2] |= checkParity64(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 3){
} else if (nbParityBits == 3){
bytes[dOfs/2] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 4){
} else if (nbParityBits == 4){
bytes[dOfs/2] |= decodeHamming84sx(codewords[cOfs++], error, bad) << 4;
} else {
bytes[dOfs/2] |= codewords[cOfs++] << 4;
@ -201,23 +259,9 @@ void LoRaDemodDecoderLoRa::decodeBytes(
dOfs /= 2;
if (error)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: Hamming decode (1) error");
payloadParityStatus = false;
// decode the rest of the payload codewords with their own FEC
if (errorCheck) {
return;
}
}
else
{
payloadParityStatus = true;
}
//decode each codeword as 2 bytes with correction
if (nbParityBitsInt == 1)
if (nbParityBits == 1)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
@ -225,7 +269,7 @@ void LoRaDemodDecoderLoRa::decodeBytes(
bytes[i] |= checkParity54(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBitsInt == 2)
else if (nbParityBits == 2)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
@ -233,7 +277,7 @@ void LoRaDemodDecoderLoRa::decodeBytes(
bytes[i] |= checkParity64(codewords[cOfs++],error) << 4;
}
}
else if (nbParityBitsInt == 3)
else if (nbParityBits == 3)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
@ -241,7 +285,7 @@ void LoRaDemodDecoderLoRa::decodeBytes(
bytes[i] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBitsInt == 4)
else if (nbParityBits == 4)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
@ -258,73 +302,66 @@ void LoRaDemodDecoderLoRa::decodeBytes(
}
}
if (error)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: Hamming decode (2) error");
payloadParityStatus = false;
if (errorCheck) {
return;
}
if (bad) {
payloadParityStatus = (int) ParityError;
} else if (error) {
payloadParityStatus = (int) ParityCorrected;
} else {
payloadParityStatus = (int) ParityOK;
}
// finalization:
// adjust offsets dpending on header and CRC presence
// compute and verify payload CRC if present
if (hasHeader)
{
dOfs = 3;
dataLength -= 5;
dOfs = 3; // skip header
dataLength -= 3; // remove header
if (hasCRCInt) // always compute crc if present
if (hasCRC) // always compute crc if present skipping the header
{
uint16_t crc = sx1272DataChecksum(bytes.data() + dOfs, packetLengthInt - 2);
uint16_t packetCRC = bytes[dOfs + packetLengthInt - 2] | (bytes[dOfs + packetLengthInt - 1] << 8);
uint16_t crc = sx1272DataChecksum(bytes.data() + dOfs, packetLength);
uint16_t packetCRC = bytes[dOfs + packetLength] | (bytes[dOfs + packetLength + 1] << 8);
if (crc != packetCRC)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: packet CRC error: calc: %x found: %x", crc, packetCRC);
if (crc != packetCRC) {
payloadCRCStatus = false;
if (errorCheck) {
return;
}
}
else
{
} else {
payloadCRCStatus = true;
}
}
hasCRC = hasCRCInt;
nbParityBits = nbParityBitsInt;
packetLength = packetLengthInt;
}
else
{
dOfs = 0;
dOfs = 0; // no header to skip
if (hasCRCInt)
if (hasCRC)
{
uint16_t crc = sx1272DataChecksum(bytes.data(), packetLengthInt - 2);
uint16_t packetCRC = bytes[packetLengthInt - 2] | (bytes[packetLengthInt - 1] << 8);
uint16_t crc = sx1272DataChecksum(bytes.data(), packetLength);
uint16_t packetCRC = bytes[packetLength] | (bytes[packetLength + 1] << 8);
if (crc != packetCRC)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: packet CRC error: calc: %x found: %x", crc, packetCRC);
if (crc != packetCRC) {
payloadCRCStatus = false;
if (errorCheck) {
return;
}
}
else
{
} else {
payloadCRCStatus = true;
}
}
}
qDebug("LoRaDemodDecoderLoRa::decodeBytes: dataLength: %u packetLengthInt: %u", dataLength, packetLengthInt);
inBytes.resize(dataLength);
std::copy(bytes.data() + dOfs, bytes.data() + dOfs + dataLength, inBytes.data());
}
void LoRaDemodDecoderLoRa::getCodingMetrics(
unsigned int nbSymbolBits,
unsigned int nbParityBits,
unsigned int packetLength,
bool hasHeader,
bool hasCRC,
unsigned int& numSymbols,
unsigned int& numCodewords
)
{
numCodewords = roundUp((packetLength + (hasCRC ? 2 : 0))*2 + (hasHeader ? headerCodewords : 0), nbSymbolBits); // uses payload + CRC for encoding size
numSymbols = headerSymbols + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits); // header is always coded with 8 bits and yields exactly 8 symbols (headerSymbols)
}

View File

@ -26,6 +26,14 @@
class LoRaDemodDecoderLoRa
{
public:
enum ParityStatus
{
ParityUndefined,
ParityError,
ParityCorrected,
ParityOK
};
static void decodeBytes(
QByteArray& bytes,
const std::vector<unsigned short>& inSymbols,
@ -34,14 +42,34 @@ public:
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool errorCheck,
bool& headerParityStatus,
bool& earlyEOM,
int& headerParityStatus,
bool& headerCRCStatus,
bool& payloadParityStatus,
int& payloadParityStatus,
bool& payloadCRCStatus
);
static void getCodingMetrics(
unsigned int nbSymbolBits,
unsigned int nbParityBits,
unsigned int packetLength,
bool hasHeader,
bool hasCRC,
unsigned int& numSymbols,
unsigned int& numCodewords
);
private:
static void decodeHeader(
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
int& headerParityStatus,
bool& headerCRCStatus
);
static const unsigned int headerParityBits = 4;
static const unsigned int headerSymbols = 8;
static const unsigned int headerCodewords = 5;
@ -240,6 +268,8 @@ private:
* Decode 7 bits into a 4 bit word with single bit correction.
* Non standard version used in sx1272.
* Set error to true when a parity error was detected
* Non correctable errors are indistinguishable from single or no errors
* therefore no 'bad' variable is proposed
**********************************************************************/
static inline unsigned char decodeHamming74sx(const unsigned char b, bool &error)
{
@ -270,7 +300,7 @@ private:
case 0x0:
case 0x1:
case 0x2:
case 0x4: return b & 0xF;
case 0x4: break;
}
return b & 0xf;

View File

@ -110,39 +110,18 @@ bool LoRaDemodGUI::handleMessage(const Message& message)
}
else if (LoRaDemod::MsgReportDecodeBytes::match(message))
{
const LoRaDemod::MsgReportDecodeBytes& msg = (LoRaDemod::MsgReportDecodeBytes&) message;
QByteArray bytes = msg.getBytes();
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
if (m_settings.m_hasHeader)
{
ui->fecParity->setValue(msg.getNbParityBits());
ui->fecParityText->setText(tr("%1").arg(msg.getNbParityBits()));
ui->crc->setChecked(msg.getHasCRC());
ui->packetLength->setValue(msg.getPacketSize());
ui->packetLengthText->setText(tr("%1").arg(msg.getPacketSize()));
displayBytes(bytes, msg.getPacketSize(), msg.getHasCRC());
}
else
{
displayBytes(bytes, m_settings.m_packetLength, m_settings.m_hasCRC);
}
if (m_settings.m_codingScheme == LoRaDemodSettings::CodingLoRa) {
displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), msg.getPayloadParityStatus(), msg.getPayloadCRCStatus());
showLoRaMessage(message);
}
return true;
}
else if (LoRaDemod::MsgReportDecodeString::match(message))
{
const LoRaDemod::MsgReportDecodeString& msg = (LoRaDemod::MsgReportDecodeString&) message;
addText(msg.getString());
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
if ((m_settings.m_codingScheme == LoRaDemodSettings::CodingASCII)
|| (m_settings.m_codingScheme == LoRaDemodSettings::CodingTTY)) {
showTextMessage(message);
}
return true;
}
@ -246,6 +225,7 @@ void LoRaDemodGUI::on_clear_clicked(bool checked)
{
(void) checked;
ui->messageText->clear();
ui->hexText->clear();
}
void LoRaDemodGUI::on_eomSquelch_valueChanged(int value)
@ -296,12 +276,6 @@ void LoRaDemodGUI::on_crc_stateChanged(int state)
applySettings();
}
void LoRaDemodGUI::on_errorCheck_stateChanged(int state)
{
m_settings.m_errorCheck = (state == Qt::Checked);
applySettings();
}
void LoRaDemodGUI::on_packetLength_valueChanged(int value)
{
m_settings.m_packetLength = value;
@ -426,7 +400,6 @@ void LoRaDemodGUI::displaySettings()
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
ui->messageLength->setValue(m_settings.m_nbSymbolsMax);
ui->header->setChecked(m_settings.m_hasHeader);
ui->errorCheck->setChecked(m_settings.m_errorCheck);
if (!m_settings.m_hasHeader)
{
@ -453,12 +426,14 @@ void LoRaDemodGUI::displaySquelch()
}
}
void LoRaDemodGUI::displayLoRaStatus(bool headerParityStatus, bool headerCRCStatus, bool payloadParityStatus, bool payloadCRCStatus)
void LoRaDemodGUI::displayLoRaStatus(int headerParityStatus, bool headerCRCStatus, int payloadParityStatus, bool payloadCRCStatus)
{
if (m_settings.m_hasHeader && headerParityStatus) {
if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityOK)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (m_settings.m_hasHeader && !headerParityStatus) {
} else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityError)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : red; }");
} else if (m_settings.m_hasHeader && (headerParityStatus == (int) ParityCorrected)) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : blue; }");
} else {
ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
@ -471,10 +446,14 @@ void LoRaDemodGUI::displayLoRaStatus(bool headerParityStatus, bool headerCRCStat
ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (payloadParityStatus) {
if (payloadParityStatus == (int) ParityOK) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : green; }");
} else {
} else if (payloadParityStatus == (int) ParityError) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : red; }");
} else if (payloadParityStatus == (int) ParityCorrected) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : blue; }");
} else {
ui->payloadFECStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (payloadCRCStatus) {
@ -490,6 +469,8 @@ void LoRaDemodGUI::resetLoRaStatus()
ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->payloadFECStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->payloadCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
ui->nbSymbolsText->setText("---");
ui->nbCodewordsText->setText("---");
}
void LoRaDemodGUI::setBandwidths()
@ -509,30 +490,105 @@ void LoRaDemodGUI::setBandwidths()
}
}
void LoRaDemodGUI::addText(const QString& text)
void LoRaDemodGUI::showLoRaMessage(const Message& message)
{
const LoRaDemod::MsgReportDecodeBytes& msg = (LoRaDemod::MsgReportDecodeBytes&) message;
QByteArray bytes = msg.getBytes();
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
unsigned int packetLength;
if (m_settings.m_hasHeader)
{
ui->fecParity->setValue(msg.getNbParityBits());
ui->fecParityText->setText(tr("%1").arg(msg.getNbParityBits()));
ui->crc->setChecked(msg.getHasCRC());
ui->packetLength->setValue(msg.getPacketSize());
ui->packetLengthText->setText(tr("%1").arg(msg.getPacketSize()));
packetLength = msg.getPacketSize();
}
else
{
packetLength = m_settings.m_packetLength;
}
QDateTime dt = QDateTime::currentDateTime();
QString dateStr = dt.toString("HH:mm:ss");
if (msg.getEarlyEOM())
{
QString loRaStatus = tr("%1 %2 S:%3 SN:%4 HF:%5 HC:%6 EOM:too early")
.arg(dateStr)
.arg(msg.getSyncWord(), 2, 16)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1)
.arg(getParityStr(msg.getHeaderParityStatus()))
.arg(msg.getHeaderCRCStatus() ? "ok" : "err");
displayStatus(loRaStatus);
displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), (int) ParityUndefined, true);
ui->payloadCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }"); // reset payload CRC
}
else
{
QString loRaHeader = tr("%1 %2 S:%3 SN:%4 HF:%5 HC:%6 FEC:%7 CRC:%8")
.arg(dateStr)
.arg(msg.getSyncWord(), 2, 16)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1)
.arg(getParityStr(msg.getHeaderParityStatus()))
.arg(msg.getHeaderCRCStatus() ? "ok" : "err")
.arg(getParityStr(msg.getPayloadParityStatus()))
.arg(msg.getPayloadCRCStatus() ? "ok" : "err");
displayBytes(loRaHeader, bytes);
QByteArray bytesCopy(bytes);
bytesCopy.truncate(packetLength);
bytesCopy.replace('\0', " ");
QString str = QString(bytesCopy.toStdString().c_str());
displayText(dateStr, str);
displayLoRaStatus(msg.getHeaderParityStatus(), msg.getHeaderCRCStatus(), msg.getPayloadParityStatus(), msg.getPayloadCRCStatus());
}
ui->nbSymbolsText->setText(tr("%1").arg(msg.getNbSymbols()));
ui->nbCodewordsText->setText(tr("%1").arg(msg.getNbCodewords()));
}
void LoRaDemodGUI::showTextMessage(const Message& message)
{
const LoRaDemod::MsgReportDecodeString& msg = (LoRaDemod::MsgReportDecodeString&) message;
QDateTime dt = QDateTime::currentDateTime();
QString dateStr = dt.toString("HH:mm:ss");
displayText(dateStr, msg.getString());
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
QString status = tr("%1 S:%2 SN:%3")
.arg(dateStr)
.arg(msg.getSingalDb(), 0, 'f', 1)
.arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1);
displayStatus(status);
}
void LoRaDemodGUI::displayText(const QString& header, const QString& text)
{
QTextCursor cursor = ui->messageText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (!ui->messageText->document()->isEmpty()) {
cursor.insertText("\n");
}
cursor.insertText(tr("%1 %2").arg(dateStr).arg(text));
cursor.insertText(tr("%1 %2").arg(header).arg(text));
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
}
void LoRaDemodGUI::displayBytes(const QByteArray& bytes, unsigned int packetLength, bool hasCRC)
void LoRaDemodGUI::displayBytes(const QString& header, const QByteArray& bytes)
{
QByteArray bytesCopy(bytes);
bytesCopy.truncate(packetLength);
bytesCopy.replace('\0', " ");
QString str = QString(bytesCopy.toStdString().c_str());
str.chop(hasCRC ? 2 : 0);
addText(str);
QDateTime dt = QDateTime::currentDateTime();
QString dateStr = dt.toString("=== HH:mm:ss ===");
QTextCursor cursor = ui->hexText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
@ -540,7 +596,7 @@ void LoRaDemodGUI::displayBytes(const QByteArray& bytes, unsigned int packetLeng
cursor.insertText("\n");
}
cursor.insertText(tr("%1\n").arg(dateStr));
cursor.insertText(tr(">%1\n").arg(header));
QByteArray::const_iterator it = bytes.begin();
unsigned int i = 0;
@ -566,6 +622,32 @@ void LoRaDemodGUI::displayBytes(const QByteArray& bytes, unsigned int packetLeng
ui->hexText->verticalScrollBar()->setValue(ui->hexText->verticalScrollBar()->maximum());
}
void LoRaDemodGUI::displayStatus(const QString& status)
{
QTextCursor cursor = ui->hexText->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
if (!ui->hexText->document()->isEmpty()) {
cursor.insertText("\n");
}
cursor.insertText(tr(">%1").arg(status));
ui->hexText->verticalScrollBar()->setValue(ui->hexText->verticalScrollBar()->maximum());
}
QString LoRaDemodGUI::getParityStr(int parityStatus)
{
if (parityStatus == (int) ParityError) {
return "err";
} else if (parityStatus == (int) ParityCorrected) {
return "fix";
} else if (parityStatus == (int) ParityOK) {
return "ok";
} else {
return "n/a";
}
}
void LoRaDemodGUI::tick()
{
if (m_tickCount < 10)

View File

@ -68,7 +68,6 @@ private slots:
void on_header_stateChanged(int state);
void on_fecParity_valueChanged(int value);
void on_crc_stateChanged(int state);
void on_errorCheck_stateChanged(int state);
void on_packetLength_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
void channelMarkerHighlightedByCursor();
@ -76,6 +75,14 @@ private slots:
void tick();
private:
enum ParityStatus // matches LoRa decoder status
{
ParityUndefined,
ParityError,
ParityCorrected,
ParityOK
};
Ui::LoRaDemodGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
@ -97,9 +104,13 @@ private:
void displaySettings();
void displaySquelch();
void setBandwidths();
void addText(const QString& text);
void displayBytes(const QByteArray& bytes, unsigned int packetLength, bool hasCRC);
void displayLoRaStatus(bool headerParityStatus, bool headerCRCStatus, bool payloadParityStatus, bool payloadCRCStatus);
void showLoRaMessage(const Message& message);
void showTextMessage(const Message& message); //!< For TTY and ASCII
void displayText(const QString& header, const QString& text);
void displayBytes(const QString& header, const QByteArray& bytes);
void displayStatus(const QString& status);
void displayLoRaStatus(int headerParityStatus, bool headerCRCStatus, int payloadParityStatus, bool payloadCRCStatus);
QString getParityStr(int parityStatus);
void resetLoRaStatus();
};

View File

@ -6,13 +6,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<width>512</width>
<height>660</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<width>512</width>
<height>660</height>
</size>
</property>
@ -28,9 +28,9 @@
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>5</x>
<x>1</x>
<y>20</y>
<width>490</width>
<width>510</width>
<height>90</height>
</rect>
</property>
@ -74,7 +74,7 @@
<rect>
<x>30</x>
<y>50</y>
<width>361</width>
<width>381</width>
<height>16</height>
</rect>
</property>
@ -102,7 +102,7 @@
<rect>
<x>30</x>
<y>70</y>
<width>80</width>
<width>101</width>
<height>16</height>
</rect>
</property>
@ -131,7 +131,7 @@
<widget class="QLabel" name="SpreadText">
<property name="geometry">
<rect>
<x>120</x>
<x>140</x>
<y>70</y>
<width>20</width>
<height>16</height>
@ -153,7 +153,7 @@
<widget class="QLabel" name="BWText">
<property name="geometry">
<rect>
<x>400</x>
<x>420</x>
<y>50</y>
<width>80</width>
<height>16</height>
@ -175,7 +175,7 @@
<widget class="QLabel" name="deBitsLabel">
<property name="geometry">
<rect>
<x>170</x>
<x>190</x>
<y>70</y>
<width>22</width>
<height>16</height>
@ -188,7 +188,7 @@
<widget class="QLabel" name="deBitsText">
<property name="geometry">
<rect>
<x>290</x>
<x>320</x>
<y>70</y>
<width>15</width>
<height>16</height>
@ -210,9 +210,9 @@
<widget class="QSlider" name="deBits">
<property name="geometry">
<rect>
<x>200</x>
<x>220</x>
<y>70</y>
<width>80</width>
<width>91</width>
<height>16</height>
</rect>
</property>
@ -243,7 +243,7 @@
<rect>
<x>10</x>
<y>10</y>
<width>471</width>
<width>491</width>
<height>26</height>
</rect>
</property>
@ -392,7 +392,7 @@
<widget class="QLabel" name="preambleChirpsText">
<property name="geometry">
<rect>
<x>450</x>
<x>480</x>
<y>70</y>
<width>20</width>
<height>16</height>
@ -414,7 +414,7 @@
<widget class="QLabel" name="preambleChirpsLabel">
<property name="geometry">
<rect>
<x>340</x>
<x>350</x>
<y>70</y>
<width>32</width>
<height>16</height>
@ -427,9 +427,9 @@
<widget class="QSlider" name="preambleChirps">
<property name="geometry">
<rect>
<x>370</x>
<x>380</x>
<y>70</y>
<width>81</width>
<width>91</width>
<height>16</height>
</rect>
</property>
@ -459,9 +459,9 @@
<widget class="QWidget" name="payloadContainer" native="true">
<property name="geometry">
<rect>
<x>5</x>
<x>1</x>
<y>120</y>
<width>490</width>
<width>510</width>
<height>230</height>
</rect>
</property>
@ -505,7 +505,7 @@
<rect>
<x>30</x>
<y>60</y>
<width>455</width>
<width>471</width>
<height>80</height>
</rect>
</property>
@ -712,7 +712,7 @@
<widget class="QLineEdit" name="syncWord">
<property name="geometry">
<rect>
<x>455</x>
<x>480</x>
<y>10</y>
<width>25</width>
<height>20</height>
@ -827,28 +827,12 @@
<string>FEC</string>
</property>
</widget>
<widget class="QCheckBox" name="errorCheck">
<property name="geometry">
<rect>
<x>230</x>
<y>35</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Check to disable display of payloads with errors</string>
</property>
<property name="text">
<string>ERR</string>
</property>
</widget>
<widget class="QPlainTextEdit" name="hexText">
<property name="geometry">
<rect>
<x>30</x>
<y>145</y>
<width>455</width>
<width>471</width>
<height>75</height>
</rect>
</property>
@ -862,7 +846,7 @@
<widget class="QLabel" name="packetLengthLabel">
<property name="geometry">
<rect>
<x>290</x>
<x>240</x>
<y>35</y>
<width>20</width>
<height>16</height>
@ -875,7 +859,7 @@
<widget class="QDial" name="packetLength">
<property name="geometry">
<rect>
<x>310</x>
<x>260</x>
<y>30</y>
<width>24</width>
<height>24</height>
@ -897,7 +881,7 @@
<widget class="QLabel" name="packetLengthText">
<property name="geometry">
<rect>
<x>330</x>
<x>280</x>
<y>35</y>
<width>25</width>
<height>16</height>
@ -913,7 +897,7 @@
<widget class="QLabel" name="syncWordLabel">
<property name="geometry">
<rect>
<x>420</x>
<x>440</x>
<y>10</y>
<width>30</width>
<height>19</height>
@ -926,7 +910,7 @@
<widget class="QLabel" name="headerHammingStatus">
<property name="geometry">
<rect>
<x>370</x>
<x>380</x>
<y>35</y>
<width>20</width>
<height>16</height>
@ -942,7 +926,7 @@
<widget class="QLabel" name="headerCRCStatus">
<property name="geometry">
<rect>
<x>395</x>
<x>410</x>
<y>35</y>
<width>20</width>
<height>16</height>
@ -958,7 +942,7 @@
<widget class="QLabel" name="payloadFECStatus">
<property name="geometry">
<rect>
<x>420</x>
<x>440</x>
<y>35</y>
<width>25</width>
<height>16</height>
@ -974,7 +958,7 @@
<widget class="QLabel" name="payloadCRCStatus">
<property name="geometry">
<rect>
<x>455</x>
<x>475</x>
<y>35</y>
<width>28</width>
<height>16</height>
@ -987,13 +971,67 @@
<string>CRC</string>
</property>
</widget>
<widget class="QLabel" name="nbSymbolsText">
<property name="geometry">
<rect>
<x>310</x>
<y>35</y>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Number of symbols in the payload with header and CRC</string>
</property>
<property name="text">
<string>---</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="nbCodewordsText">
<property name="geometry">
<rect>
<x>340</x>
<y>35</y>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Number of codewords in the payload with header and CRC</string>
</property>
<property name="text">
<string>---</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="fecParityText_2">
<property name="geometry">
<rect>
<x>328</x>
<y>35</y>
<width>12</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>/</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
<rect>
<x>5</x>
<x>1</x>
<y>360</y>
<width>490</width>
<width>510</width>
<height>260</height>
</rect>
</property>

View File

@ -50,7 +50,6 @@ void LoRaDemodSettings::resetToDefaults()
m_nbParityBits = 1;
m_hasCRC = true;
m_hasHeader = true;
m_errorCheck = false;
m_rgbColor = QColor(255, 0, 255).rgb();
m_title = "LoRa Demodulator";
}
@ -80,7 +79,6 @@ QByteArray LoRaDemodSettings::serialize() const
s.writeS32(13, m_nbParityBits);
s.writeBool(14, m_hasCRC);
s.writeBool(15, m_hasHeader);
s.writeBool(16, m_errorCheck);
s.writeS32(17, m_preambleChirps);
return s.final();
@ -126,7 +124,6 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
d.readS32(13, &m_nbParityBits, 1);
d.readBool(14, &m_hasCRC, true);
d.readBool(15, &m_hasHeader, true);
d.readBool(16, &m_errorCheck, false);
d.readS32(17, &m_preambleChirps, 8);
return true;

View File

@ -48,7 +48,6 @@ struct LoRaDemodSettings
int m_packetLength; //!< Payload packet length in bytes or characters (LoRa)
bool m_hasCRC; //!< Payload has CRC (LoRa)
bool m_hasHeader; //!< Header present before actual payload (LoRa)
bool m_errorCheck; //!< Error check failure cancels decoding (LoRa)
uint32_t m_rgbColor;
QString m_title;

View File

@ -262,7 +262,7 @@ void LoRaMod::applySettings(const LoRaModSettings& settings, bool force)
if (getMessageQueueToGUI())
{
MsgReportPayloadTime *rpt = MsgReportPayloadTime::create(
(symbols.size()*(1<<settings.m_spreadFactor)*1000) / LoRaModSettings::bandwidths[settings.m_bandwidthIndex]
(symbols.size()*(1<<settings.m_spreadFactor)*1000.0) / LoRaModSettings::bandwidths[settings.m_bandwidthIndex]
);
getMessageQueueToGUI()->push(rpt);
}

View File

@ -70,19 +70,18 @@ public:
MESSAGE_CLASS_DECLARATION
public:
unsigned int getPayloadTimeMs() const { return m_timeMs; }
static MsgReportPayloadTime* create(unsigned int timeMs) {
float getPayloadTimeMs() const { return m_timeMs; }
static MsgReportPayloadTime* create(float timeMs) {
return new MsgReportPayloadTime(timeMs);
}
private:
unsigned int m_timeMs; //!< time in milliseconds
float m_timeMs; //!< time in milliseconds
MsgReportPayloadTime(unsigned int timeMs) :
MsgReportPayloadTime(float timeMs) :
Message(),
m_timeMs(timeMs)
{}
};
//=================================================================

View File

@ -39,8 +39,7 @@ void LoRaModEncoderLoRa::encodeBytes(
return;
}
unsigned int payloadSize = bytes.size();
const unsigned int numCodewords = roundUp(bytes.size()*2 + (hasHeader ? headerCodewords : 0), nbSymbolBits);
const unsigned int numCodewords = roundUp(bytes.size()*2 + (hasHeader ? headerCodewords : 0), nbSymbolBits); // uses payload + CRC for encoding size
unsigned int cOfs = 0;
unsigned int dOfs = 0;
@ -49,6 +48,7 @@ void LoRaModEncoderLoRa::encodeBytes(
if (hasHeader)
{
std::vector<uint8_t> hdr(3);
unsigned int payloadSize = bytes.size() - (hasCRC ? 2 : 0); // actual payload size is without CRC
hdr[0] = payloadSize % 256;
hdr[1] = (hasCRC ? 1 : 0) | (nbParityBits << 1);
hdr[2] = headerChecksum(hdr.data());
@ -75,7 +75,8 @@ void LoRaModEncoderLoRa::encodeBytes(
Sx1272ComputeWhitening(codewords.data() + cOfs2, numCodewords - nbSymbolBits, nbSymbolBits - headerSize, nbParityBits);
}
const unsigned int numSymbols = headerSymbols + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits); // header is always coded with 8 bits
// header is always coded with 8 bits and yields exactly 8 symbols (headerSymbols)
const unsigned int numSymbols = headerSymbols + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits);
// interleave the codewords into symbols
symbols.clear();

View File

@ -108,10 +108,11 @@ bool LoRaModGUI::handleMessage(const Message& message)
else if (LoRaMod::MsgReportPayloadTime::match(message))
{
const LoRaMod::MsgReportPayloadTime& rpt = (LoRaMod::MsgReportPayloadTime&) message;
unsigned int fourthsMs = ((1<<m_settings.m_spreadFactor)*250) / LoRaModSettings::bandwidths[m_settings.m_bandwidthIndex];
unsigned int controlMs = (4*(m_settings.m_preambleChirps)+8+9)*fourthsMs; // preamble + sync word + SFD
ui->msgTimeText->setText(tr("%1 ms").arg(rpt.getPayloadTimeMs()));
ui->msgTotalTimeText->setText(tr("%1 ms").arg(rpt.getPayloadTimeMs()+controlMs));
float fourthsMs = ((1<<m_settings.m_spreadFactor) * 250.0) / LoRaModSettings::bandwidths[m_settings.m_bandwidthIndex];
float controlMs = (4*m_settings.m_preambleChirps + 8 + 9) * fourthsMs; // preamble + sync word + SFD
ui->timePayloadText->setText(tr("%1 ms").arg(QString::number(rpt.getPayloadTimeMs(), 'f', 0)));
ui->timeTotalText->setText(tr("%1 ms").arg(QString::number(rpt.getPayloadTimeMs() + controlMs, 'f', 0)));
ui->timeSymbolText->setText(tr("%1 ms").arg(QString::number(4.0*fourthsMs, 'f', 1)));
return true;
}
else if (DSPSignalNotification::match(message))

View File

@ -6,13 +6,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>376</height>
<width>392</width>
<height>373</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>380</width>
<width>392</width>
<height>180</height>
</size>
</property>
@ -28,9 +28,9 @@
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<x>1</x>
<y>20</y>
<width>370</width>
<width>390</width>
<height>125</height>
</rect>
</property>
@ -74,7 +74,7 @@
<rect>
<x>40</x>
<y>50</y>
<width>221</width>
<width>251</width>
<height>16</height>
</rect>
</property>
@ -102,7 +102,7 @@
<rect>
<x>40</x>
<y>70</y>
<width>61</width>
<width>81</width>
<height>16</height>
</rect>
</property>
@ -131,7 +131,7 @@
<widget class="QLabel" name="spreadText">
<property name="geometry">
<rect>
<x>110</x>
<x>130</x>
<y>70</y>
<width>30</width>
<height>16</height>
@ -153,7 +153,7 @@
<widget class="QLabel" name="bwText">
<property name="geometry">
<rect>
<x>280</x>
<x>300</x>
<y>50</y>
<width>80</width>
<height>16</height>
@ -175,7 +175,7 @@
<widget class="QLabel" name="deBitsLabel">
<property name="geometry">
<rect>
<x>160</x>
<x>180</x>
<y>70</y>
<width>32</width>
<height>16</height>
@ -188,7 +188,7 @@
<widget class="QLabel" name="deBitsText">
<property name="geometry">
<rect>
<x>250</x>
<x>280</x>
<y>70</y>
<width>10</width>
<height>16</height>
@ -210,9 +210,9 @@
<widget class="QSlider" name="deBits">
<property name="geometry">
<rect>
<x>200</x>
<x>210</x>
<y>70</y>
<width>41</width>
<width>61</width>
<height>16</height>
</rect>
</property>
@ -243,7 +243,7 @@
<rect>
<x>10</x>
<y>10</y>
<width>351</width>
<width>371</width>
<height>26</height>
</rect>
</property>
@ -496,7 +496,7 @@
<widget class="QLabel" name="syncLabel">
<property name="geometry">
<rect>
<x>280</x>
<x>310</x>
<y>70</y>
<width>32</width>
<height>16</height>
@ -509,7 +509,7 @@
<widget class="QLineEdit" name="syncWord">
<property name="geometry">
<rect>
<x>320</x>
<x>350</x>
<y>68</y>
<width>30</width>
<height>20</height>
@ -532,9 +532,9 @@
<widget class="QWidget" name="messageContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<x>1</x>
<y>150</y>
<width>370</width>
<width>390</width>
<height>221</height>
</rect>
</property>
@ -576,7 +576,7 @@
<widget class="QLabel" name="urCallLabel">
<property name="geometry">
<rect>
<x>200</x>
<x>210</x>
<y>40</y>
<width>50</width>
<height>16</height>
@ -591,7 +591,7 @@
<rect>
<x>90</x>
<y>40</y>
<width>90</width>
<width>110</width>
<height>16</height>
</rect>
</property>
@ -611,9 +611,9 @@
<widget class="QLineEdit" name="urCall">
<property name="geometry">
<rect>
<x>260</x>
<x>269</x>
<y>40</y>
<width>90</width>
<width>110</width>
<height>16</height>
</rect>
</property>
@ -648,7 +648,7 @@
<rect>
<x>90</x>
<y>60</y>
<width>90</width>
<width>110</width>
<height>16</height>
</rect>
</property>
@ -668,7 +668,7 @@
<widget class="QLabel" name="reportLabel">
<property name="geometry">
<rect>
<x>200</x>
<x>210</x>
<y>60</y>
<width>50</width>
<height>16</height>
@ -681,9 +681,9 @@
<widget class="QLineEdit" name="report">
<property name="geometry">
<rect>
<x>260</x>
<x>270</x>
<y>60</y>
<width>90</width>
<width>110</width>
<height>16</height>
</rect>
</property>
@ -703,7 +703,7 @@
<widget class="QPushButton" name="generateMessages">
<property name="geometry">
<rect>
<x>310</x>
<x>340</x>
<y>80</y>
<width>40</width>
<height>20</height>
@ -807,7 +807,7 @@
<rect>
<x>40</x>
<y>110</y>
<width>311</width>
<width>341</width>
<height>60</height>
</rect>
</property>
@ -830,7 +830,7 @@
<rect>
<x>40</x>
<y>175</y>
<width>311</width>
<width>341</width>
<height>20</height>
</rect>
</property>
@ -881,7 +881,7 @@
<widget class="QLabel" name="repeatLabel">
<property name="geometry">
<rect>
<x>200</x>
<x>210</x>
<y>82</y>
<width>51</width>
<height>16</height>
@ -894,7 +894,7 @@
<widget class="QDial" name="repeatMessage">
<property name="geometry">
<rect>
<x>250</x>
<x>260</x>
<y>78</y>
<width>24</width>
<height>24</height>
@ -916,7 +916,7 @@
<widget class="QLabel" name="repeatText">
<property name="geometry">
<rect>
<x>280</x>
<x>290</x>
<y>82</y>
<width>22</width>
<height>16</height>
@ -929,7 +929,7 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="msgTimeLabel">
<widget class="QLabel" name="timesLabel">
<property name="geometry">
<rect>
<x>2</x>
@ -942,12 +942,12 @@
<string>Time</string>
</property>
</widget>
<widget class="QLabel" name="msgTimeText">
<widget class="QLabel" name="timePayloadText">
<property name="geometry">
<rect>
<x>40</x>
<x>200</x>
<y>200</y>
<width>65</width>
<width>60</width>
<height>16</height>
</rect>
</property>
@ -961,28 +961,28 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="msgTotalTimeLabel">
<widget class="QLabel" name="timeTotalLabel">
<property name="geometry">
<rect>
<x>119</x>
<x>290</x>
<y>200</y>
<width>35</width>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Total</string>
<string>Ttot</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="msgTotalTimeText">
<widget class="QLabel" name="timeTotalText">
<property name="geometry">
<rect>
<x>150</x>
<x>320</x>
<y>200</y>
<width>65</width>
<width>60</width>
<height>16</height>
</rect>
</property>
@ -1037,7 +1037,7 @@
<widget class="QCheckBox" name="header">
<property name="geometry">
<rect>
<x>300</x>
<x>290</x>
<y>10</y>
<width>50</width>
<height>16</height>
@ -1053,7 +1053,7 @@
<widget class="QCheckBox" name="crc">
<property name="geometry">
<rect>
<x>240</x>
<x>230</x>
<y>10</y>
<width>50</width>
<height>16</height>
@ -1069,7 +1069,7 @@
<widget class="QLabel" name="fecParityLabel">
<property name="geometry">
<rect>
<x>160</x>
<x>150</x>
<y>10</y>
<width>32</width>
<height>16</height>
@ -1082,7 +1082,7 @@
<widget class="QDial" name="fecParity">
<property name="geometry">
<rect>
<x>190</x>
<x>180</x>
<y>5</y>
<width>24</width>
<height>24</height>
@ -1104,7 +1104,7 @@
<widget class="QLabel" name="fecParityText">
<property name="geometry">
<rect>
<x>220</x>
<x>205</x>
<y>10</y>
<width>12</width>
<height>16</height>
@ -1117,6 +1117,57 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="timePayloadLabel">
<property name="geometry">
<rect>
<x>160</x>
<y>200</y>
<width>35</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Tpay</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="timeSymbolText">
<property name="geometry">
<rect>
<x>70</x>
<y>200</y>
<width>70</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Payload time in milliseconds</string>
</property>
<property name="text">
<string>0000.0 ms</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="timeSymbolLabel">
<property name="geometry">
<rect>
<x>40</x>
<y>200</y>
<width>30</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Tsym</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
</widget>
<customwidgets>