1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-11-10 16:20:24 -05:00

LoRa demod: implement LoRa

This commit is contained in:
f4exb 2020-02-19 09:26:42 +01:00
parent c6f8ac2ca4
commit be2ca2d0fd
21 changed files with 1592 additions and 243 deletions

View File

@ -8,6 +8,9 @@ set(lora_SOURCES
lorademodbaseband.cpp
loraplugin.cpp
lorademoddecoder.cpp
lorademoddecodertty.cpp
lorademoddecoderascii.cpp
lorademoddecoderlora.cpp
lorademodmsg.cpp
lorademodgui.ui
)
@ -19,6 +22,9 @@ set(lora_HEADERS
lorademodsink.h
lorademodbaseband.h
lorademoddecoder.h
lorademoddecodertty.h
lorademoddecoderascii.h
lorademoddecoderlora.h
lorademodmsg.h
loraplugin.h
)

View File

@ -116,6 +116,13 @@ bool LoRaDemod::handleMessage(const Message& cmd)
msgToGUI->setSyncWord(msg.getSyncWord());
msgToGUI->setSignalDb(msg.getSingalDb());
msgToGUI->setNoiseDb(msg.getNoiseDb());
msgToGUI->setPacketSize(m_decoder.getPacketLength());
msgToGUI->setNbParityBits(m_decoder.getNbParityBits());
msgToGUI->setHasCRC(m_decoder.getHasCRC());
msgToGUI->setHeaderParityStatus(m_decoder.getHeaderParityStatus());
msgToGUI->setHeaderCRCStatus(m_decoder.getHeaderCRCStatus());
msgToGUI->setPayloadParityStatus(m_decoder.getPayloadParityStatus());
msgToGUI->setPayloadCRCStatus(m_decoder.getPayloadCRCStatus());
getMessageQueueToGUI()->push(msgToGUI);
}
}
@ -187,19 +194,46 @@ void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spreadFactor: " << settings.m_spreadFactor
<< " m_deBits: " << settings.m_deBits
<< " m_codingScheme: " << settings.m_codingScheme
<< " m_hasHeader: " << settings.m_hasHeader
<< " 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;
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|| (settings.m_deBits != m_settings.m_deBits) || force) {
m_decoder.setNbSymbolBits(settings.m_spreadFactor - settings.m_deBits);
m_decoder.setNbSymbolBits(settings.m_spreadFactor, settings.m_deBits);
}
if ((settings.m_codingScheme != m_settings.m_codingScheme) || force) {
m_decoder.setCodingScheme(settings.m_codingScheme);
}
if ((settings.m_hasHeader != m_settings.m_hasHeader) || force) {
m_decoder.setLoRaHasHeader(settings.m_hasHeader);
}
if ((settings.m_hasCRC != m_settings.m_hasCRC) || force) {
m_decoder.setLoRaHasCRC(settings.m_hasCRC);
}
if ((settings.m_nbParityBits != m_settings.m_nbParityBits) || force) {
m_decoder.setLoRaParityBits(settings.m_nbParityBits);
}
if ((settings.m_packetLength != m_settings.m_packetLength) || 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

@ -65,6 +65,13 @@ public:
unsigned int getSyncWord() const { return m_syncWord; }
float getSingalDb() const { return m_signalDb; }
float getNoiseDb() const { return m_noiseDb; }
unsigned int getPacketSize() const { return m_packetSize; }
unsigned int getNbParityBits() const { return m_nbParityBits; }
bool getHasCRC() const { return m_hasCRC; }
bool getHeaderParityStatus() const { return m_headerParityStatus; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
bool getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
static MsgReportDecodeBytes* create(const QByteArray& bytes) {
return new MsgReportDecodeBytes(bytes);
@ -78,19 +85,54 @@ public:
void setNoiseDb(float db) {
m_noiseDb = db;
}
void setPacketSize(unsigned int packetSize) {
m_packetSize = packetSize;
}
void setNbParityBits(unsigned int nbParityBits) {
m_nbParityBits = nbParityBits;
}
void setHasCRC(bool hasCRC) {
m_hasCRC = hasCRC;
}
void setHeaderParityStatus(bool headerParityStatus) {
m_headerParityStatus = headerParityStatus;
}
void setHeaderCRCStatus(bool headerCRCStatus) {
m_headerCRCStatus = headerCRCStatus;
}
void setPayloadParityStatus(bool payloadParityStatus) {
m_payloadParityStatus = payloadParityStatus;
}
void setPayloadCRCStatus(bool payloadCRCStatus) {
m_payloadCRCStatus = payloadCRCStatus;
}
private:
QByteArray m_bytes;
unsigned int m_syncWord;
float m_signalDb;
float m_noiseDb;
unsigned int m_packetSize;
unsigned int m_nbParityBits;
bool m_hasCRC;
bool m_headerParityStatus;
bool m_headerCRCStatus;
bool m_payloadParityStatus;
bool m_payloadCRCStatus;
MsgReportDecodeBytes(const QByteArray& bytes) :
Message(),
m_bytes(bytes),
m_syncWord(0),
m_signalDb(0.0),
m_noiseDb(0.0)
m_noiseDb(0.0),
m_packetSize(0),
m_nbParityBits(0),
m_hasCRC(false),
m_headerParityStatus(false),
m_headerCRCStatus(false),
m_payloadParityStatus(false),
m_payloadCRCStatus(false)
{ }
};

View File

@ -16,98 +16,75 @@
///////////////////////////////////////////////////////////////////////////////////
#include "lorademoddecoder.h"
const char LoRaDemodDecoder::ttyLetters[32] = {
'\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
'\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
};
const char LoRaDemodDecoder::ttyFigures[32] = { // U.S. standard
'\0', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', ' ', '.', '/', ';', ' '
};
#include "lorademoddecodertty.h"
#include "lorademoddecoderascii.h"
#include "lorademoddecoderlora.h"
LoRaDemodDecoder::LoRaDemodDecoder() :
m_codingScheme(LoRaDemodSettings::CodingTTY),
m_nbSymbolBits(5)
m_nbSymbolBits(5),
m_nbParityBits(1),
m_hasCRC(true),
m_hasHeader(true)
{}
LoRaDemodDecoder::~LoRaDemodDecoder()
{}
void LoRaDemodDecoder::decodeSymbols(const std::vector<unsigned int>& symbols, QString& str)
void LoRaDemodDecoder::setNbSymbolBits(unsigned int spreadFactor, unsigned int deBits)
{
m_spreadFactor = spreadFactor;
if (deBits >= spreadFactor) {
m_deBits = m_spreadFactor - 1;
} else {
m_deBits = deBits;
}
m_nbSymbolBits = m_spreadFactor - m_deBits;
}
void LoRaDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
switch(m_codingScheme)
{
case LoRaDemodSettings::CodingTTY:
decodeSymbolsTTY(symbols, str);
if (m_nbSymbolBits == 5) {
LoRaDemodDecoderTTY::decodeSymbols(symbols, str);
}
break;
case LoRaDemodSettings::CodingASCII:
decodeSymbolsASCII(symbols, str);
if (m_nbSymbolBits == 5) {
LoRaDemodDecoderASCII::decodeSymbols(symbols, str);
}
break;
default:
break;
}
}
void LoRaDemodDecoder::decodeSymbols(const std::vector<unsigned int>& symbols, QByteArray& bytes)
void LoRaDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QByteArray& bytes)
{
}
void LoRaDemodDecoder::decodeSymbolsASCII(const std::vector<unsigned int>& symbols, QString& str)
{
if (m_nbSymbolBits != 7) {
return;
}
std::vector<unsigned int>::const_iterator it = symbols.begin();
QByteArray bytes;
for (; it != symbols.end(); ++it) {
bytes.push_back(*it & 0x7F);
}
str = QString(bytes.toStdString().c_str());
}
void LoRaDemodDecoder::decodeSymbolsTTY(const std::vector<unsigned int>& symbols, QString& str)
{
if (m_nbSymbolBits != 5) {
return;
}
std::vector<unsigned int>::const_iterator it = symbols.begin();
QByteArray bytes;
TTYState ttyState = TTYLetters;
for (; it != symbols.end(); ++it)
switch(m_codingScheme)
{
char ttyChar = *it & 0x1F;
if (ttyChar == lettersTag) {
ttyState = TTYLetters;
} else if (ttyChar == figuresTag) {
ttyState = TTYFigures;
}
else
case LoRaDemodSettings::CodingLoRa:
if (m_nbSymbolBits >= 5)
{
char asciiChar = -1;
if (ttyState == TTYLetters) {
asciiChar = ttyLetters[ttyChar];
} else if (ttyState == TTYFigures) {
asciiChar = ttyFigures[ttyChar];
}
if (asciiChar >= 0) {
bytes.push_back(asciiChar);
}
LoRaDemodDecoderLoRa::decodeBytes(
bytes,
symbols,
m_nbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_errorCheck,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
}
break;
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -28,27 +28,37 @@ public:
~LoRaDemodDecoder();
void setCodingScheme(LoRaDemodSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; }
void setNbSymbolBits(unsigned int symbolBits) { m_nbSymbolBits = symbolBits; }
void decodeSymbols(const std::vector<unsigned int>& symbols, QString& str); //!< For ASCII and TTY
void decodeSymbols(const std::vector<unsigned int>& symbols, QByteArray& bytes); //!< For raw bytes (original LoRa)
void setNbSymbolBits(unsigned int spreadFactor, unsigned int deBits);
void setLoRaParityBits(unsigned int parityBits) { m_nbParityBits = parityBits; }
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; }
bool getHeaderCRCStatus() const { return m_headerCRCStatus; }
bool getPayloadParityStatus() const { return m_payloadParityStatus; }
bool getPayloadCRCStatus() const { return m_payloadCRCStatus; }
private:
enum TTYState
{
TTYLetters,
TTYFigures
};
void decodeSymbolsASCII(const std::vector<unsigned int>& symbols, QString& str);
void decodeSymbolsTTY(const std::vector<unsigned int>& symbols, QString& str);
LoRaDemodSettings::CodingScheme m_codingScheme;
unsigned int m_spreadFactor;
unsigned int m_deBits;
unsigned int m_nbSymbolBits;
static const char ttyLetters[32];
static const char ttyFigures[32];
static const char lettersTag = 0x1f;
static const char figuresTag = 0x1b;
// LoRa attributes
unsigned int m_nbParityBits; //!< 1 to 4 Hamming FEC bits for 4 payload bits
bool m_hasCRC;
bool m_hasHeader;
unsigned int m_packetLength;
bool m_errorCheck;
bool m_headerParityStatus;
bool m_headerCRCStatus;
bool m_payloadParityStatus;
bool m_payloadCRCStatus;
};
#endif // INCLUDE_LORADEMODDECODER_H

View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "lorademoddecoderascii.h"
void LoRaDemodDecoderASCII::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
std::vector<unsigned short>::const_iterator it = symbols.begin();
QByteArray bytes;
for (; it != symbols.end(); ++it) {
bytes.push_back(*it & 0x7F);
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -0,0 +1,30 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODDECODERASCII_H
#define INCLUDE_LORADEMODDECODERASCII_H
#include <vector>
#include <QString>
class LoRaDemodDecoderASCII
{
public:
static void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str);
};
#endif // INCLUDE_LORADEMODDECODERASCII_H

View File

@ -0,0 +1,330 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Inspired by: https://github.com/myriadrf/LoRa-SDR //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "lorademoddecoderlora.h"
void LoRaDemodDecoderLoRa::decodeBytes(
QByteArray& inBytes,
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool errorCheck,
bool& headerParityStatus,
bool& headerCRCStatus,
bool& payloadParityStatus,
bool& payloadCRCStatus
)
{
if (inSymbols.size() < 8) { // need at least a header
return;
}
const unsigned int numSymbols = roundUp(inSymbols.size(), 4 + nbParityBits);
const unsigned int numCodewords = (numSymbols / (4 + nbParityBits))*nbSymbolBits;
std::vector<uint16_t> symbols(numSymbols);
std::copy(inSymbols.begin(), inSymbols.end(), symbols.begin());
//gray encode, when SF > PPM, depad the LSBs with rounding
for (auto &sym : symbols) {
sym = binaryToGray16(sym);
}
std::vector<uint8_t> codewords(numCodewords);
// deinterleave / dewhiten the symbols into codewords
unsigned int sOfs = 0;
unsigned int cOfs = 0;
if (nbParityBits != 4)
{
diagonalDeinterleaveSx(symbols.data(), 8, codewords.data(), nbSymbolBits, 4);
if (hasHeader) {
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, nbSymbolBits - headerCodewords, 0, headerParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), nbSymbolBits, 0, headerParityBits);
}
cOfs += nbSymbolBits;
sOfs += headerSymbols;
if (numSymbols - sOfs > 0)
{
diagonalDeinterleaveSx(symbols.data() + sOfs, numSymbols - sOfs, codewords.data() + cOfs, nbSymbolBits, nbParityBits);
if (hasHeader) {
Sx1272ComputeWhiteningLfsr(codewords.data() + cOfs, numCodewords - cOfs, nbSymbolBits - headerCodewords, nbParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data() + cOfs, numCodewords - cOfs, nbSymbolBits, nbParityBits);
}
}
}
else
{
diagonalDeinterleaveSx(symbols.data(), numSymbols, codewords.data(), nbSymbolBits, nbParityBits);
if (hasHeader) {
Sx1272ComputeWhiteningLfsr(codewords.data() + headerCodewords, numCodewords - headerCodewords, 0, nbParityBits);
} else {
Sx1272ComputeWhiteningLfsr(codewords.data(), numCodewords, 0, nbParityBits);
}
}
bool error = false;
bool bad = false;
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)
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()) {
return;
}
for (; cOfs < nbSymbolBits; cOfs++, dOfs++)
{
if (dOfs % 2 == 1) {
bytes[dOfs/2] |= decodeHamming84sx(codewords[cOfs], error, bad) << 4;
} else {
bytes[dOfs/2] = decodeHamming84sx(codewords[cOfs], error, bad) & 0xf;
}
}
if (dOfs % 2 == 1)
{
if (nbParityBitsInt == 1) {
bytes[dOfs/2] |= checkParity54(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 2) {
bytes[dOfs/2] |= checkParity64(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 3){
bytes[dOfs/2] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
} else if (nbParityBitsInt == 4){
bytes[dOfs/2] |= decodeHamming84sx(codewords[cOfs++], error, bad) << 4;
} else {
bytes[dOfs/2] |= codewords[cOfs++] << 4;
}
dOfs++;
}
dOfs /= 2;
if (error)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: Hamming decode (1) error");
payloadParityStatus = false;
if (errorCheck) {
return;
}
}
else
{
payloadParityStatus = true;
}
//decode each codeword as 2 bytes with correction
if (nbParityBitsInt == 1)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = checkParity54(codewords[cOfs++],error);
bytes[i] |= checkParity54(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBitsInt == 2)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = checkParity64(codewords[cOfs++], error);
bytes[i] |= checkParity64(codewords[cOfs++],error) << 4;
}
}
else if (nbParityBitsInt == 3)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = decodeHamming74sx(codewords[cOfs++], error) & 0xf;
bytes[i] |= decodeHamming74sx(codewords[cOfs++], error) << 4;
}
}
else if (nbParityBitsInt == 4)
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = decodeHamming84sx(codewords[cOfs++], error, bad) & 0xf;
bytes[i] |= decodeHamming84sx(codewords[cOfs++], error, bad) << 4;
}
}
else
{
for (unsigned int i = dOfs; i < dataLength; i++)
{
bytes[i] = codewords[cOfs++] & 0xf;
bytes[i] |= codewords[cOfs++] << 4;
}
}
if (error)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: Hamming decode (2) error");
payloadParityStatus = false;
if (errorCheck) {
return;
}
}
if (hasHeader)
{
dOfs = 3;
dataLength -= 5;
if (hasCRCInt) // always compute crc if present
{
uint16_t crc = sx1272DataChecksum(bytes.data() + dOfs, packetLengthInt - 2);
uint16_t packetCRC = bytes[dOfs + packetLengthInt - 2] | (bytes[dOfs + packetLengthInt - 1] << 8);
if (crc != packetCRC)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: packet CRC error: calc: %x found: %x", crc, packetCRC);
payloadCRCStatus = false;
if (errorCheck) {
return;
}
}
else
{
payloadCRCStatus = true;
}
}
hasCRC = hasCRCInt;
nbParityBits = nbParityBitsInt;
packetLength = packetLengthInt;
}
else
{
dOfs = 0;
if (hasCRCInt)
{
uint16_t crc = sx1272DataChecksum(bytes.data(), packetLengthInt - 2);
uint16_t packetCRC = bytes[packetLengthInt - 2] | (bytes[packetLengthInt - 1] << 8);
if (crc != packetCRC)
{
qDebug("LoRaDemodDecoderLoRa::decodeBytes: packet CRC error: calc: %x found: %x", crc, packetCRC);
payloadCRCStatus = false;
if (errorCheck) {
return;
}
}
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());
}

View File

@ -0,0 +1,327 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// Inspired by: https://github.com/myriadrf/LoRa-SDR //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODDECODERLORA_H
#define INCLUDE_LORADEMODDECODERLORA_H
#include <vector>
#include <QByteArray>
class LoRaDemodDecoderLoRa
{
public:
static void decodeBytes(
QByteArray& bytes,
const std::vector<unsigned short>& inSymbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool& hasCRC,
unsigned int& nbParityBits,
unsigned int& packetLength,
bool errorCheck,
bool& headerParityStatus,
bool& headerCRCStatus,
bool& payloadParityStatus,
bool& payloadCRCStatus
);
private:
static const unsigned int headerParityBits = 4;
static const unsigned int headerSymbols = 8;
static const unsigned int headerCodewords = 5;
/***********************************************************************
* Round functions
**********************************************************************/
static inline unsigned roundUp(unsigned num, unsigned factor)
{
return ((num + factor - 1) / factor) * factor;
}
/***********************************************************************
* https://en.wikipedia.org/wiki/Gray_code
**********************************************************************/
/*
* This function converts an unsigned binary
* number to reflected binary Gray code.
*
* The operator >> is shift right. The operator ^ is exclusive or.
*/
static inline unsigned short binaryToGray16(unsigned short num)
{
return num ^ (num >> 1);
}
/***********************************************************************
* Diagonal deinterleaver
**********************************************************************/
static inline void diagonalDeinterleaveSx(
const uint16_t *symbols,
const unsigned int numSymbols,
uint8_t *codewords,
const unsigned int nbSymbolBits,
const unsigned int nbParityBits)
{
for (unsigned int x = 0; x < numSymbols / (4 + nbParityBits); x++)
{
const unsigned int cwOff = x*nbSymbolBits;
const unsigned int symOff = x*(4 + nbParityBits);
for (unsigned int k = 0; k < 4 + nbParityBits; k++)
{
for (unsigned int m = 0; m < nbSymbolBits; m++)
{
const unsigned int i = (m + k) % nbSymbolBits;
const auto bit = (symbols[symOff + k] >> m) & 0x1;
codewords[cwOff + i] |= (bit << k);
}
}
}
}
/***********************************************************************
* Whitening generator reverse engineered from Sx1272 data stream.
* Same as above but using the actual interleaved LFSRs.
**********************************************************************/
static inline void Sx1272ComputeWhiteningLfsr(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const unsigned int nbParityBits)
{
static const uint64_t seed1[2] = {0x6572D100E85C2EFF,0xE85C2EFFFFFFFFFF}; // lfsr start values
static const uint64_t seed2[2] = {0x05121100F8ECFEEF,0xF8ECFEEFEFEFEFEF}; // lfsr start values for single parity mode (1 == nbParityBits)
const uint8_t m = 0xff >> (4 - nbParityBits);
uint64_t r[2] = {(1 == nbParityBits)?seed2[0]:seed1[0],(1 == nbParityBits)?seed2[1]:seed1[1]};
int i,j;
for (i = 0; i < bitOfs;i++)
{
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56); // poly: 0x1D
}
for (j = 0; j < bufferSize; j++,i++)
{
buffer[j] ^= r[i & 1] & m;
r[i & 1] = (r[i & 1] >> 8) | (((r[i & 1] >> 32) ^ (r[i & 1] >> 24) ^ (r[i & 1] >> 16) ^ r[i & 1]) << 56);
}
}
/***********************************************************************
* Decode 8 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
* Set bad to true when the result could not be corrected
**********************************************************************/
static inline unsigned char decodeHamming84sx(const unsigned char b, bool &error, bool &bad)
{
auto b0 = (b >> 0) & 0x1;
auto b1 = (b >> 1) & 0x1;
auto b2 = (b >> 2) & 0x1;
auto b3 = (b >> 3) & 0x1;
auto b4 = (b >> 4) & 0x1;
auto b5 = (b >> 5) & 0x1;
auto b6 = (b >> 6) & 0x1;
auto b7 = (b >> 7) & 0x1;
auto p0 = (b0 ^ b1 ^ b2 ^ b4);
auto p1 = (b1 ^ b2 ^ b3 ^ b5);
auto p2 = (b0 ^ b1 ^ b3 ^ b6);
auto p3 = (b0 ^ b2 ^ b3 ^ b7);
auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2) | (p3 << 3);
if (parity != 0) error = true;
switch (parity & 0xf)
{
case 0xD: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0xB: return (b ^ 4) & 0xf;
case 0xE: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4:
case 0x8: return b & 0xf;
default: bad = true; return b & 0xf;
}
}
/***********************************************************************
* Simple 8-bit checksum routine
**********************************************************************/
static inline uint8_t checksum8(const uint8_t *p, const size_t len)
{
uint8_t acc = 0;
for (size_t i = 0; i < len; i++)
{
acc = (acc >> 1) + ((acc & 0x1) << 7); //rotate
acc += p[i]; //add
}
return acc;
}
static inline uint8_t headerChecksum(const uint8_t *h)
{
auto a0 = (h[0] >> 4) & 0x1;
auto a1 = (h[0] >> 5) & 0x1;
auto a2 = (h[0] >> 6) & 0x1;
auto a3 = (h[0] >> 7) & 0x1;
auto b0 = (h[0] >> 0) & 0x1;
auto b1 = (h[0] >> 1) & 0x1;
auto b2 = (h[0] >> 2) & 0x1;
auto b3 = (h[0] >> 3) & 0x1;
auto c0 = (h[1] >> 0) & 0x1;
auto c1 = (h[1] >> 1) & 0x1;
auto c2 = (h[1] >> 2) & 0x1;
auto c3 = (h[1] >> 3) & 0x1;
uint8_t res;
res = (a0 ^ a1 ^ a2 ^ a3) << 4;
res |= (a3 ^ b1 ^ b2 ^ b3 ^ c0) << 3;
res |= (a2 ^ b0 ^ b3 ^ c1 ^ c3) << 2;
res |= (a1 ^ b0 ^ b2 ^ c0 ^ c1 ^ c2) << 1;
res |= a0 ^ b1 ^ c0 ^ c1 ^ c2 ^ c3;
return res;
}
/***********************************************************************
* Check parity for 5/4 code.
* return true if parity is valid.
**********************************************************************/
static inline unsigned char checkParity54(const unsigned char b, bool &error)
{
auto x = b ^ (b >> 2);
x = x ^ (x >> 1) ^ (b >> 4);
if (x & 1) {
error = true;
}
return b & 0xf;
}
/***********************************************************************
* Check parity for 6/4 code.
* return true if parity is valid.
**********************************************************************/
static inline unsigned char checkParity64(const unsigned char b, bool &error)
{
auto x = b ^ (b >> 1) ^ (b >> 2);
auto y = x ^ b ^ (b >> 3);
x ^= b >> 4;
y ^= b >> 5;
if ((x | y) & 1) {
error = true;
}
return b & 0xf;
}
/***********************************************************************
* 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
**********************************************************************/
static inline unsigned char decodeHamming74sx(const unsigned char b, bool &error)
{
auto b0 = (b >> 0) & 0x1;
auto b1 = (b >> 1) & 0x1;
auto b2 = (b >> 2) & 0x1;
auto b3 = (b >> 3) & 0x1;
auto b4 = (b >> 4) & 0x1;
auto b5 = (b >> 5) & 0x1;
auto b6 = (b >> 6) & 0x1;
auto p0 = (b0 ^ b1 ^ b2 ^ b4);
auto p1 = (b1 ^ b2 ^ b3 ^ b5);
auto p2 = (b0 ^ b1 ^ b3 ^ b6);
auto parity = (p0 << 0) | (p1 << 1) | (p2 << 2);
if (parity != 0) {
error = true;
}
switch (parity)
{
case 0x5: return (b ^ 1) & 0xf;
case 0x7: return (b ^ 2) & 0xf;
case 0x3: return (b ^ 4) & 0xf;
case 0x6: return (b ^ 8) & 0xf;
case 0x0:
case 0x1:
case 0x2:
case 0x4: return b & 0xF;
}
return b & 0xf;
}
/***********************************************************************
* CRC reverse engineered from Sx1272 data stream.
* Modified CCITT crc with masking of the output with an 8bit lfsr
**********************************************************************/
static inline uint16_t crc16sx(uint16_t crc, const uint16_t poly)
{
for (int i = 0; i < 8; i++)
{
if (crc & 0x8000) {
crc = (crc << 1) ^ poly;
} else {
crc <<= 1;
}
}
return crc;
}
static inline uint8_t xsum8(uint8_t t)
{
t ^= t >> 4;
t ^= t >> 2;
t ^= t >> 1;
return (t & 1);
}
static inline uint16_t sx1272DataChecksum(const uint8_t *data, int length)
{
uint16_t res = 0;
uint8_t v = 0xff;
uint16_t crc = 0;
for (int i = 0; i < length; i++)
{
crc = crc16sx(res, 0x1021);
v = xsum8(v & 0xB8) | (v << 1);
res = crc ^ data[i];
}
res ^= v;
v = xsum8(v & 0xB8) | (v << 1);
res ^= v << 8;
return res;
}
};
#endif // INCLUDE_LORADEMODDECODERLORA_H

View File

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "lorademoddecodertty.h"
const char LoRaDemodDecoderTTY::ttyLetters[32] = {
'\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
'\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
'O', 'B', 'G', ' ', 'M', 'X', 'V', ' '
};
const char LoRaDemodDecoderTTY::ttyFigures[32] = { // U.S. standard
'\0', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', ' ', '.', '/', ';', ' '
};
void LoRaDemodDecoderTTY::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
std::vector<unsigned short>::const_iterator it = symbols.begin();
QByteArray bytes;
TTYState ttyState = TTYLetters;
for (; it != symbols.end(); ++it)
{
char ttyChar = *it & 0x1F;
if (ttyChar == lettersTag) {
ttyState = TTYLetters;
} else if (ttyChar == figuresTag) {
ttyState = TTYFigures;
}
else
{
char asciiChar = -1;
if (ttyState == TTYLetters) {
asciiChar = ttyLetters[ttyChar];
} else if (ttyState == TTYFigures) {
asciiChar = ttyFigures[ttyChar];
}
if (asciiChar >= 0) {
bytes.push_back(asciiChar);
}
}
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -0,0 +1,42 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LORADEMODDECODERTTY_H
#define INCLUDE_LORADEMODDECODERTTY_H
#include <vector>
#include <QString>
class LoRaDemodDecoderTTY
{
public:
static void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str);
private:
enum TTYState
{
TTYLetters,
TTYFigures
};
static const char ttyLetters[32];
static const char ttyFigures[32];
static const char lettersTag = 0x1f;
static const char figuresTag = 0x1b;
};
#endif // INCLUDE_LORADEMODDECODERTTY_H

View File

@ -77,6 +77,8 @@ QByteArray LoRaDemodGUI::serialize() const
bool LoRaDemodGUI::deserialize(const QByteArray& data)
{
resetLoRaStatus();
if (m_settings.deserialize(data))
{
displaySettings();
@ -110,10 +112,29 @@ bool LoRaDemodGUI::handleMessage(const Message& message)
{
const LoRaDemod::MsgReportDecodeBytes& msg = (LoRaDemod::MsgReportDecodeBytes&) message;
QByteArray bytes = msg.getBytes();
ui->hexText->setText(bytes.toHex());
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());
}
return true;
}
else if (LoRaDemod::MsgReportDecodeString::match(message))
{
@ -122,6 +143,8 @@ bool LoRaDemodGUI::handleMessage(const Message& message)
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));
return true;
}
else
{
@ -195,9 +218,21 @@ void LoRaDemodGUI::on_deBits_valueChanged(int value)
applySettings();
}
void LoRaDemodGUI::on_preambleChirps_valueChanged(int value)
{
m_settings.m_preambleChirps = value;
ui->preambleChirpsText->setText(tr("%1").arg(m_settings.m_preambleChirps));
applySettings();
}
void LoRaDemodGUI::on_scheme_currentIndexChanged(int index)
{
m_settings.m_codingScheme = (LoRaDemodSettings::CodingScheme) index;
if (m_settings.m_codingScheme != LoRaDemodSettings::CodingLoRa) {
resetLoRaStatus();
}
applySettings();
}
@ -227,6 +262,53 @@ void LoRaDemodGUI::on_messageLength_valueChanged(int value)
applySettings();
}
void LoRaDemodGUI::on_header_stateChanged(int state)
{
m_settings.m_hasHeader = (state == Qt::Checked);
if (!m_settings.m_hasHeader) // put back values from settings
{
ui->fecParity->blockSignals(true);
ui->crc->blockSignals(true);
ui->fecParity->setValue(m_settings.m_nbParityBits);
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
ui->crc->setChecked(m_settings.m_hasCRC);
ui->fecParity->blockSignals(false);
ui->crc->blockSignals(false);
}
ui->fecParity->setEnabled(state != Qt::Checked);
ui->crc->setEnabled(state != Qt::Checked);
applySettings();
}
void LoRaDemodGUI::on_fecParity_valueChanged(int value)
{
m_settings.m_nbParityBits = value;
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
applySettings();
}
void LoRaDemodGUI::on_crc_stateChanged(int state)
{
m_settings.m_hasCRC = (state == Qt::Checked);
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;
ui->packetLengthText->setText(tr("%1").arg(m_settings.m_packetLength));
applySettings();
}
void LoRaDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
@ -284,6 +366,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
setBandwidths();
displaySettings();
resetLoRaStatus();
applySettings(true);
}
@ -325,6 +408,10 @@ void LoRaDemodGUI::displaySettings()
ui->glSpectrum->setSampleRate(thisBW);
ui->glSpectrum->setCenterFrequency(thisBW/2);
ui->fecParity->setEnabled(!m_settings.m_hasHeader);
ui->crc->setEnabled(!m_settings.m_hasHeader);
ui->packetLength->setEnabled(!m_settings.m_hasHeader);
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
@ -333,11 +420,25 @@ void LoRaDemodGUI::displaySettings()
ui->SpreadText->setText(tr("%1").arg(m_settings.m_spreadFactor));
ui->deBits->setValue(m_settings.m_deBits);
ui->deBitsText->setText(tr("%1").arg(m_settings.m_deBits));
ui->preambleChirps->setValue(m_settings.m_preambleChirps);
ui->preambleChirpsText->setText(tr("%1").arg(m_settings.m_preambleChirps));
ui->scheme->setCurrentIndex((int) m_settings.m_codingScheme);
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
ui->messageLength->setValue(m_settings.m_nbSymbolsMax);
ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
ui->header->setChecked(m_settings.m_hasHeader);
ui->errorCheck->setChecked(m_settings.m_errorCheck);
if (!m_settings.m_hasHeader)
{
ui->fecParity->setValue(m_settings.m_nbParityBits);
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
ui->crc->setChecked(m_settings.m_hasCRC);
ui->packetLength->setValue(m_settings.m_packetLength);
ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
}
displaySquelch();
blockApplySettings(false);
}
@ -352,6 +453,45 @@ void LoRaDemodGUI::displaySquelch()
}
}
void LoRaDemodGUI::displayLoRaStatus(bool headerParityStatus, bool headerCRCStatus, bool payloadParityStatus, bool payloadCRCStatus)
{
if (m_settings.m_hasHeader && headerParityStatus) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (m_settings.m_hasHeader && !headerParityStatus) {
ui->headerHammingStatus->setStyleSheet("QLabel { background-color : red; }");
} else {
ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (m_settings.m_hasHeader && headerCRCStatus) {
ui->headerCRCStatus->setStyleSheet("QLabel { background-color : green; }");
} else if (m_settings.m_hasHeader && !headerCRCStatus) {
ui->headerCRCStatus->setStyleSheet("QLabel { background-color : red; }");
} else {
ui->headerCRCStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
if (payloadParityStatus) {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->payloadFECStatus->setStyleSheet("QLabel { background-color : red; }");
}
if (payloadCRCStatus) {
ui->payloadCRCStatus->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->payloadCRCStatus->setStyleSheet("QLabel { background-color : red; }");
}
}
void LoRaDemodGUI::resetLoRaStatus()
{
ui->headerHammingStatus->setStyleSheet("QLabel { background:rgb(79,79,79); }");
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); }");
}
void LoRaDemodGUI::setBandwidths()
{
int maxBandwidth = m_basebandSampleRate/LoRaDemodSettings::oversampling;
@ -382,6 +522,49 @@ void LoRaDemodGUI::addText(const QString& text)
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
}
void LoRaDemodGUI::displayBytes(const QByteArray& bytes, unsigned int packetLength, bool hasCRC)
{
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);
if (!ui->hexText->document()->isEmpty()) {
cursor.insertText("\n");
}
cursor.insertText(tr("%1\n").arg(dateStr));
QByteArray::const_iterator it = bytes.begin();
unsigned int i = 0;
for (;it != bytes.end(); ++it, i++)
{
unsigned char b = *it;
if (i%16 == 0) {
cursor.insertText(tr("%1|").arg(i, 3, 10, QChar('0')));
}
cursor.insertText(tr("%1").arg(b, 2, 16));
if (i%16 == 15) {
cursor.insertText("\n");
} else if (i%4 == 3) {
cursor.insertText("|");
} else {
cursor.insertText(" ");
}
}
ui->hexText->verticalScrollBar()->setValue(ui->hexText->verticalScrollBar()->maximum());
}
void LoRaDemodGUI::tick()
{
@ -400,3 +583,4 @@ void LoRaDemodGUI::tick()
}
}
}

View File

@ -59,11 +59,17 @@ private slots:
void on_BW_valueChanged(int value);
void on_Spread_valueChanged(int value);
void on_deBits_valueChanged(int value);
void on_preambleChirps_valueChanged(int value);
void on_scheme_currentIndexChanged(int index);
void on_mute_toggled(bool checked);
void on_clear_clicked(bool checked);
void on_eomSquelch_valueChanged(int value);
void on_messageLength_valueChanged(int value);
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();
void handleInputMessages();
@ -92,6 +98,9 @@ private:
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 resetLoRaStatus();
};
#endif // INCLUDE_LoRaDEMODGUI_H

View File

@ -6,14 +6,14 @@
<rect>
<x>0</x>
<y>0</y>
<width>410</width>
<height>620</height>
<width>500</width>
<height>660</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>380</width>
<height>620</height>
<width>500</width>
<height>660</height>
</size>
</property>
<property name="font">
@ -28,16 +28,16 @@
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<x>5</x>
<y>20</y>
<width>390</width>
<height>102</height>
<width>490</width>
<height>90</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>102</height>
<height>90</height>
</size>
</property>
<property name="windowTitle">
@ -74,7 +74,7 @@
<rect>
<x>30</x>
<y>50</y>
<width>251</width>
<width>361</width>
<height>16</height>
</rect>
</property>
@ -102,7 +102,7 @@
<rect>
<x>30</x>
<y>70</y>
<width>131</width>
<width>80</width>
<height>16</height>
</rect>
</property>
@ -131,15 +131,15 @@
<widget class="QLabel" name="SpreadText">
<property name="geometry">
<rect>
<x>170</x>
<x>120</x>
<y>70</y>
<width>30</width>
<width>20</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>20</width>
<height>0</height>
</size>
</property>
@ -153,7 +153,7 @@
<widget class="QLabel" name="BWText">
<property name="geometry">
<rect>
<x>300</x>
<x>400</x>
<y>50</y>
<width>80</width>
<height>16</height>
@ -175,7 +175,7 @@
<widget class="QLabel" name="deBitsLabel">
<property name="geometry">
<rect>
<x>220</x>
<x>170</x>
<y>70</y>
<width>22</width>
<height>16</height>
@ -188,15 +188,15 @@
<widget class="QLabel" name="deBitsText">
<property name="geometry">
<rect>
<x>350</x>
<x>290</x>
<y>70</y>
<width>30</width>
<width>15</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>15</width>
<height>0</height>
</size>
</property>
@ -210,9 +210,9 @@
<widget class="QSlider" name="deBits">
<property name="geometry">
<rect>
<x>250</x>
<x>200</x>
<y>70</y>
<width>91</width>
<width>80</width>
<height>16</height>
</rect>
</property>
@ -243,7 +243,7 @@
<rect>
<x>10</x>
<y>10</y>
<width>371</width>
<width>471</width>
<height>26</height>
</rect>
</property>
@ -389,20 +389,86 @@
</item>
</layout>
</widget>
<widget class="QLabel" name="preambleChirpsText">
<property name="geometry">
<rect>
<x>450</x>
<y>70</y>
<width>20</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="preambleChirpsLabel">
<property name="geometry">
<rect>
<x>340</x>
<y>70</y>
<width>32</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Pre</string>
</property>
</widget>
<widget class="QSlider" name="preambleChirps">
<property name="geometry">
<rect>
<x>370</x>
<y>70</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Expected number of preamble chirps</string>
</property>
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>20</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>8</number>
</property>
<property name="sliderPosition">
<number>8</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</widget>
<widget class="QWidget" name="payloadContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>390</width>
<height>160</height>
<x>5</x>
<y>120</y>
<width>490</width>
<height>230</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>160</height>
<height>230</height>
</size>
</property>
<property name="windowTitle">
@ -412,7 +478,7 @@
<property name="geometry">
<rect>
<x>2</x>
<y>40</y>
<y>60</y>
<width>32</width>
<height>16</height>
</rect>
@ -421,21 +487,11 @@
<string>Msg</string>
</property>
</widget>
<widget class="QLineEdit" name="hexText">
<property name="geometry">
<rect>
<x>30</x>
<y>120</y>
<width>351</width>
<height>20</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="hexLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>120</y>
<y>145</y>
<width>32</width>
<height>16</height>
</rect>
@ -448,9 +504,9 @@
<property name="geometry">
<rect>
<x>30</x>
<y>40</y>
<width>351</width>
<height>75</height>
<y>60</y>
<width>455</width>
<height>80</height>
</rect>
</property>
</widget>
@ -496,7 +552,7 @@
<property name="geometry">
<rect>
<x>4</x>
<y>60</y>
<y>80</y>
<width>20</width>
<height>20</height>
</rect>
@ -548,7 +604,7 @@
<widget class="QLabel" name="eomSquelchLabel">
<property name="geometry">
<rect>
<x>180</x>
<x>190</x>
<y>10</y>
<width>35</width>
<height>19</height>
@ -561,7 +617,7 @@
<widget class="QDial" name="eomSquelch">
<property name="geometry">
<rect>
<x>210</x>
<x>220</x>
<y>8</y>
<width>22</width>
<height>22</height>
@ -586,7 +642,7 @@
<widget class="QLabel" name="eomSquelchText">
<property name="geometry">
<rect>
<x>230</x>
<x>240</x>
<y>10</y>
<width>28</width>
<height>19</height>
@ -602,7 +658,7 @@
<widget class="QLabel" name="messageLengthLabel">
<property name="geometry">
<rect>
<x>272</x>
<x>290</x>
<y>10</y>
<width>20</width>
<height>19</height>
@ -615,7 +671,7 @@
<widget class="QDial" name="messageLength">
<property name="geometry">
<rect>
<x>290</x>
<x>310</x>
<y>8</y>
<width>22</width>
<height>22</height>
@ -625,7 +681,7 @@
<string>Message (payload) length in number of symbols</string>
</property>
<property name="minimum">
<number>20</number>
<number>8</number>
</property>
<property name="maximum">
<number>255</number>
@ -634,13 +690,13 @@
<number>1</number>
</property>
<property name="value">
<number>255</number>
<number>127</number>
</property>
</widget>
<widget class="QLabel" name="messageLengthText">
<property name="geometry">
<rect>
<x>310</x>
<x>330</x>
<y>10</y>
<width>25</width>
<height>19</height>
@ -656,8 +712,8 @@
<widget class="QLineEdit" name="syncWord">
<property name="geometry">
<rect>
<x>350</x>
<y>9</y>
<x>455</x>
<y>10</y>
<width>25</width>
<height>20</height>
</rect>
@ -675,14 +731,270 @@
<string>00</string>
</property>
</widget>
<widget class="QLabel" name="loraLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>35</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>LoRa</string>
</property>
</widget>
<widget class="QDial" name="fecParity">
<property name="geometry">
<rect>
<x>140</x>
<y>30</y>
<width>24</width>
<height>24</height>
</rect>
</property>
<property name="toolTip">
<string>Number of FEC parity bits (0 to 4) for Hamming code</string>
</property>
<property name="maximum">
<number>4</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
<widget class="QCheckBox" name="header">
<property name="geometry">
<rect>
<x>60</x>
<y>35</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Expect header (explicit) - disables manual FEC and CRC</string>
</property>
<property name="text">
<string>HDR</string>
</property>
</widget>
<widget class="QLabel" name="fecParityText">
<property name="geometry">
<rect>
<x>160</x>
<y>35</y>
<width>12</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QCheckBox" name="crc">
<property name="geometry">
<rect>
<x>180</x>
<y>35</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>CRC appended to payload</string>
</property>
<property name="text">
<string>CRC</string>
</property>
</widget>
<widget class="QLabel" name="fecParityLabel">
<property name="geometry">
<rect>
<x>115</x>
<y>35</y>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="text">
<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>
<height>75</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>9</pointsize>
</font>
</property>
</widget>
<widget class="QLabel" name="packetLengthLabel">
<property name="geometry">
<rect>
<x>290</x>
<y>35</y>
<width>20</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Pkt</string>
</property>
</widget>
<widget class="QDial" name="packetLength">
<property name="geometry">
<rect>
<x>310</x>
<y>30</y>
<width>24</width>
<height>24</height>
</rect>
</property>
<property name="toolTip">
<string>Payload packet length in number of bytes or characters</string>
</property>
<property name="maximum">
<number>225</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
<widget class="QLabel" name="packetLengthText">
<property name="geometry">
<rect>
<x>330</x>
<y>35</y>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>255</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="syncWordLabel">
<property name="geometry">
<rect>
<x>420</x>
<y>10</y>
<width>30</width>
<height>19</height>
</rect>
</property>
<property name="text">
<string>Sync</string>
</property>
</widget>
<widget class="QLabel" name="headerHammingStatus">
<property name="geometry">
<rect>
<x>370</x>
<y>35</y>
<width>20</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Header FEC parity status</string>
</property>
<property name="text">
<string>HF</string>
</property>
</widget>
<widget class="QLabel" name="headerCRCStatus">
<property name="geometry">
<rect>
<x>395</x>
<y>35</y>
<width>20</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Header CRC status</string>
</property>
<property name="text">
<string>HC</string>
</property>
</widget>
<widget class="QLabel" name="payloadFECStatus">
<property name="geometry">
<rect>
<x>420</x>
<y>35</y>
<width>25</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Payload FEC parity status</string>
</property>
<property name="text">
<string>FEC</string>
</property>
</widget>
<widget class="QLabel" name="payloadCRCStatus">
<property name="geometry">
<rect>
<x>455</x>
<y>35</y>
<width>28</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Payload CRC status</string>
</property>
<property name="text">
<string>CRC</string>
</property>
</widget>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>300</y>
<width>390</width>
<height>310</height>
<x>5</x>
<y>360</y>
<width>490</width>
<height>260</height>
</rect>
</property>
<property name="minimumSize">
@ -715,7 +1027,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
<height>230</height>
</size>
</property>
</widget>

View File

@ -27,12 +27,12 @@ namespace LoRaDemodMsg
MESSAGE_CLASS_DECLARATION
public:
const std::vector<unsigned int>& getSymbols() const { return m_symbols; }
const std::vector<unsigned short>& getSymbols() const { return m_symbols; }
unsigned int getSyncWord() const { return m_syncWord; }
float getSingalDb() const { return m_signalDb; }
float getNoiseDb() const { return m_noiseDb; }
void pushBackSymbol(unsigned int symbol) {
void pushBackSymbol(unsigned short symbol) {
m_symbols.push_back(symbol);
}
void popSymbol() {
@ -51,12 +51,12 @@ namespace LoRaDemodMsg
static MsgDecodeSymbols* create() {
return new MsgDecodeSymbols();
}
static MsgDecodeSymbols* create(const std::vector<unsigned int> symbols) {
static MsgDecodeSymbols* create(const std::vector<unsigned short> symbols) {
return new MsgDecodeSymbols(symbols);
}
private:
std::vector<unsigned int> m_symbols;
std::vector<unsigned short> m_symbols;
unsigned int m_syncWord;
float m_signalDb;
float m_noiseDb;
@ -67,7 +67,7 @@ namespace LoRaDemodMsg
m_signalDb(0.0),
m_noiseDb(0.0)
{}
MsgDecodeSymbols(const std::vector<unsigned int> symbols) : //!< create a message with symbols copy
MsgDecodeSymbols(const std::vector<unsigned short> symbols) : //!< create a message with symbols copy
Message(),
m_syncWord(0),
m_signalDb(0.0),

View File

@ -45,6 +45,12 @@ void LoRaDemodSettings::resetToDefaults()
m_decodeActive = true;
m_eomSquelchTenths = 60;
m_nbSymbolsMax = 255;
m_preambleChirps = 8;
m_packetLength = 32;
m_nbParityBits = 1;
m_hasCRC = true;
m_hasHeader = true;
m_errorCheck = false;
m_rgbColor = QColor(255, 0, 255).rgb();
m_title = "LoRa Demodulator";
}
@ -70,6 +76,12 @@ QByteArray LoRaDemodSettings::serialize() const
s.writeBool(9, m_decodeActive);
s.writeS32(10, m_eomSquelchTenths);
s.writeS32(11, m_nbSymbolsMax);
s.writeS32(12, m_packetLength);
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();
}
@ -110,6 +122,12 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
d.readBool(9, &m_decodeActive, true);
d.readS32(10, &m_eomSquelchTenths, 60);
d.readS32(11, &m_nbSymbolsMax, 255);
d.readS32(12, &m_packetLength, 32);
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

@ -43,6 +43,12 @@ struct LoRaDemodSettings
bool m_decodeActive;
int m_eomSquelchTenths; //!< Squelch factor to trigger end of message (/10)
int m_nbSymbolsMax; //!< Maximum number of symbols in a payload
int m_preambleChirps; //!< Number of expected preamble chirps
int m_nbParityBits; //!< Hamming parity bits (LoRa)
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

@ -159,7 +159,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
m_fftInterpolation
) / m_fftInterpolation;
if (m_magsqQueue.size() > m_requiredPreambleChirps + 1) {
if (m_magsqQueue.size() > m_settings.m_preambleChirps) {
m_magsqQueue.pop();
}
@ -260,6 +260,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
if (m_chirpCount < 3) // too early
{
m_state = LoRaStateReset;
qDebug("LoRaDemodSink::processSample: SFD search: signal drop is too early");
}
else
{
@ -288,8 +289,9 @@ void LoRaDemodSink::processSample(const Complex& ci)
m_state = LoRaStateSkipSFD; //LoRaStateSlideSFD;
}
}
else if (m_chirpCount > m_maxSFDSearchChirps) // SFD missed start over
else if (m_chirpCount > (m_settings.m_preambleChirps - m_requiredPreambleChirps + 2)) // SFD missed start over
{
qDebug("LoRaDemodSink::processSample: SFD search: number of possible chirps exceeded");
m_state = LoRaStateReset;
}
else
@ -339,7 +341,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
m_fftCounter = 0;
double magsq;
unsigned int symbol = evalSymbol(
unsigned short symbol = evalSymbol(
argmax(
m_fft->out(),
m_fftInterpolation,

View File

@ -123,87 +123,6 @@ private:
void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
int toSigned(int u, int intSize);
unsigned int evalSymbol(unsigned int rawSymbol);
/*
Interleaving is "easiest" if the same number of bits is used per symbol as for FEC
Chosen mode "spreading 8, low rate" has 6 bits per symbol, so use 4:6 FEC
More spreading needs higher frequency resolution and longer time on air, increasing drift errors.
Want higher bandwidth when using more spreading, which needs more CPU and a better FFT.
Six bit Hamming can only correct long runs of drift errors when not using interleaving. Interleaving defeats the point of using Gray code and puts multiple bit errors into single FEC blocks. Hardware decoding uses RSSI to detect the symbols most likely to be damaged, so that individual bits can be repaired after de-interleaving.
Using Implicit Mode: explicit starts with a 4:8 block and seems to have a different whitening sequence.
*/
// Six bits per symbol, six chars per block
inline void interleave6(char* inout, int size)
{
int i, j;
char in[6 * 2];
short s;
for (j = 0; j < size; j+=6) {
for (i = 0; i < 6; i++)
in[i] = in[i + 6] = inout[i + j];
// top bits are swapped
for (i = 0; i < 6; i++) {
s = (32 & in[2 + i]) | (16 & in[1 + i]) | (8 & in[3 + i])
| (4 & in[4 + i]) | (2 & in[5 + i]) | (1 & in[6 + i]);
// bits are also rotated
s = (s << 3) | (s >> 3);
s &= 63;
s = (s >> i) | (s << (6 - i));
inout[i + j] = s & 63;
}
}
}
inline short toGray(short num)
{
return (num >> 1) ^ num;
}
// Ignore the FEC bits, just extract the data bits
inline void hamming6(char* c, int size)
{
int i;
for (i = 0; i < size; i++) {
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<0) | ((c[i] & 4)>>0) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<2) | ((c[i] & 2)<<2) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] &32)>>2) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] &16)>>4);
i++;
c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>2) | ((c[i] & 8)>>2);
}
c[i] = 0;
}
// data whitening (6 bit)
inline void prng6(char* inout, int size)
{
const char otp[] = {
//explicit mode
"cOGGg7CM2=b5a?<`i;T2of5jDAB=2DoQ9ko?h_RLQR4@Z\\`9jY\\PX89lHX8h_R]c_^@OB<0`W08ik?Mg>dQZf3kn5Je5R=R4h[<Ph90HHh9j;h:mS^?f:lQ:GG;nU:b?WFU20Lf4@A?`hYJMnW\\QZ\\AMIZ<h:jQk[PP<`6[Z"
#if 0
// implicit mode (offset 2 symbols)
"5^ZSm0=cOGMgUB=bNcb<@a^T;_f=6DEB]2ImPIKg:j]RlYT4YZ<`9hZ\\PPb;@8X8i]Zmc_6B52\\8oUPHIcBOc>dY?d9[n5Lg]b]R8hR<0`T008h9c9QJm[c?a:lQEGa;nU=b_WfUV2?V4@c=8h9B9njlQZDC@9Z<Q8\\iiX\\Rb6k:iY"
#endif
};
int i, maxchars;
maxchars = sizeof( otp );
if (size < maxchars)
maxchars = size;
for (i = 0; i < maxchars; i++)
inout[i] ^= (otp[i] - 48);
}
};
#endif // INCLUDE_LORADEMODSINK_H

View File

@ -40,7 +40,7 @@ void LoRaModEncoderLoRa::encodeBytes(
}
unsigned int payloadSize = bytes.size();
const unsigned int numCodewords = roundUp(bytes.size()*2 + (hasHeader ? 5 : 0), nbSymbolBits);
const unsigned int numCodewords = roundUp(bytes.size()*2 + (hasHeader ? headerCodewords : 0), nbSymbolBits);
unsigned int cOfs = 0;
unsigned int dOfs = 0;
@ -65,7 +65,7 @@ void LoRaModEncoderLoRa::encodeBytes(
// fill nbSymbolBits codewords with 8 bit codewords using payload data (ecode and whiten)
encodeFec(codewords, 4, cOfs, dOfs, reinterpret_cast<const uint8_t*>(bytes.data()), nbSymbolBits - headerSize);
Sx1272ComputeWhitening(codewords.data() + headerSize, nbSymbolBits - headerSize, 0, 4);
Sx1272ComputeWhitening(codewords.data() + headerSize, nbSymbolBits - headerSize, 0, headerParityBits);
// encode and whiten the rest of the payload with 4 + nbParityBits bits codewords
if (numCodewords > nbSymbolBits)
@ -75,15 +75,15 @@ void LoRaModEncoderLoRa::encodeBytes(
Sx1272ComputeWhitening(codewords.data() + cOfs2, numCodewords - nbSymbolBits, nbSymbolBits - headerSize, nbParityBits);
}
const unsigned int numSymbols = 8 + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits); // header is always coded with 8 bits
const unsigned int numSymbols = headerSymbols + (numCodewords / nbSymbolBits - 1) * (4 + nbParityBits); // header is always coded with 8 bits
// interleave the codewords into symbols
symbols.clear();
symbols.resize(numSymbols);
diagonalInterleaveSx(codewords.data(), nbSymbolBits, symbols.data(), nbSymbolBits, 4);
diagonalInterleaveSx(codewords.data(), nbSymbolBits, symbols.data(), nbSymbolBits, headerParityBits);
if (numCodewords > nbSymbolBits) {
diagonalInterleaveSx(codewords.data() + nbSymbolBits, numCodewords - nbSymbolBits, symbols.data() + 8, nbSymbolBits, nbParityBits);
diagonalInterleaveSx(codewords.data() + nbSymbolBits, numCodewords - nbSymbolBits, symbols.data() + headerSymbols, nbSymbolBits, nbParityBits);
}
// gray decode

View File

@ -46,6 +46,10 @@ private:
const unsigned int codewordCount
);
static const unsigned int headerParityBits = 4;
static const unsigned int headerSymbols = 8;
static const unsigned int headerCodewords = 5;
/***********************************************************************
* Round functions
**********************************************************************/