1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-04-24 00:14:03 -04:00

LoRa modulator: implement LoRa

This commit is contained in:
f4exb 2020-02-18 10:30:42 +01:00
parent 09a980de70
commit c6f8ac2ca4
18 changed files with 975 additions and 250 deletions

View File

@ -7,6 +7,9 @@ set(modlora_SOURCES
loramodbaseband.cpp
loramodplugin.cpp
loramodencoder.cpp
loramodencodertty.cpp
loramodencoderascii.cpp
loramodencoderlora.cpp
loramodwebapiadapter.cpp
)
@ -17,6 +20,9 @@ set(modlora_HEADERS
loramodbaseband.h
loramodplugin.h
loramodencoder.h
loramodencodertty.h
loramodencoderascii.h
loramodencoderlora.h
loramodwebapiadapter.h
)

View File

@ -157,15 +157,27 @@ void LoRaMod::applySettings(const LoRaModSettings& settings, bool force)
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|| (settings.m_deBits != m_settings.m_deBits) || force) {
m_encoder.setNbSymbolBits(settings.m_spreadFactor - settings.m_deBits);
m_encoder.setNbSymbolBits(settings.m_spreadFactor, settings.m_deBits);
}
if ((settings.m_codingScheme != m_settings.m_codingScheme) || force) {
m_encoder.setCodingScheme(settings.m_codingScheme);
}
if ((settings.m_nbParityBits != m_settings.m_nbParityBits || force)) {
m_encoder.setLoRaParityBits(settings.m_nbParityBits);
}
if ((settings.m_hasCRC != m_settings.m_hasCRC) || force) {
m_encoder.setLoRaHasCRC(settings.m_hasCRC);
}
if ((settings.m_hasHeader != m_settings.m_hasHeader) || force) {
m_encoder.setLoRaHasHeader(settings.m_hasHeader);
}
LoRaModBaseband::MsgConfigureLoRaModPayload *payloadMsg = nullptr;
std::vector<unsigned int> symbols;
std::vector<unsigned short> symbols;
if ((settings.m_messageType == LoRaModSettings::MessageNone)
&& ((settings.m_messageType != m_settings.m_messageType) || force))

View File

@ -60,22 +60,22 @@ public:
MESSAGE_CLASS_DECLARATION
public:
const std::vector<unsigned int>& getPayload() const { return m_payload; }
const std::vector<unsigned short>& getPayload() const { return m_payload; }
static MsgConfigureLoRaModPayload* create() {
return new MsgConfigureLoRaModPayload();
}
static MsgConfigureLoRaModPayload* create(const std::vector<unsigned int>& payload) {
static MsgConfigureLoRaModPayload* create(const std::vector<unsigned short>& payload) {
return new MsgConfigureLoRaModPayload(payload);
}
private:
std::vector<unsigned int> m_payload;
std::vector<unsigned short> m_payload;
MsgConfigureLoRaModPayload() : // This is empty payload notification
Message()
{}
MsgConfigureLoRaModPayload(const std::vector<unsigned int>& payload) :
MsgConfigureLoRaModPayload(const std::vector<unsigned short>& payload) :
Message()
{ m_payload = payload; }
};

View File

@ -16,174 +16,79 @@
///////////////////////////////////////////////////////////////////////////////////
#include "loramodencoder.h"
const char LoRaModEncoder::asciiToTTYLetters[128] = {
// '\x00' '\x01' '\x02' '\x03' '\x04' '\x05' '\x06' '\x07'
0x00, -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x08' '\t' '\n' '\x0b' '\x0c' '\r' '\x0e' '\x0f'
-1 , -1 , 0x02, -1 , -1 , 0x08, -1 , -1 ,
// '\x10' '\x11' '\x12' '\x13' '\x14' '\x15' '\x16' '\x17'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x18' '\x19' '\x1a' '\x1b' '\x1c' '\x1d' '\x1e' '\x1f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// ' ' '!' '"' '#' '$' '%' '&' "'"
0x04, -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '(' ')' '*' '+' ',' '-' '.' '/'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '0' '1' '2' '3' '4' '5' '6' '7'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '8' '9' ':' ';' '<' '=' '>' '?'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G'
-1 , 0x03, 0x19, 0x0e, 0x09, 0x01, 0x0d, 0x1a,
// 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O'
0x14, 0x06, 0x0b, 0x0f, 0x12, 0x1c, 0x0c, 0x18,
// 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
0x16, 0x17, 0x0a, 0x05, 0x10, 0x07, 0x1e, 0x13,
// 'X' 'Y' 'Z' '[' '\\' ']' '^' '_'
0x1d, 0x15, 0x11, -1 , -1 , -1 , -1 , -1 ,
// '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g'
-1 , 0x03, 0x19, 0x0e, 0x09, 0x01, 0x0d, 0x1a,
// 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
0x14, 0x06, 0x0b, 0x0f, 0x12, 0x1c, 0x0c, 0x18,
// 'p' 'q' 'r' 's' 't' 'u' 'v' 'w'
0x16, 0x17, 0x0a, 0x05, 0x10, 0x07, 0x1e, 0x13,
// 'x' 'y' 'z' '{' '|' '}' '~' '\x7f'
0x1d, 0x15, 0x11, -1 , -1 , -1 , -1 , -1
};
const char LoRaModEncoder::asciiToTTYFigures[128] = {
// '\x00' '\x01' '\x02' '\x03' '\x04' '\x05' '\x06' '\x07'
0x00, -1 , -1 , -1 , -1 , -1 , -1 , 0x05,
// '\x08' '\t' '\n' '\x0b' '\x0c' '\r' '\x0e' '\x0f'
-1 , -1 , 0x02, -1 , -1 , 0x08, -1 , -1 ,
// '\x10' '\x11' '\x12' '\x13' '\x14' '\x15' '\x16' '\x17'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x18' '\x19' '\x1a' '\x1b' '\x1c' '\x1d' '\x1e' '\x1f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// ' ' '!' '"' '#' '$' '%' '&' "'"
0x04, 0x0d, 0x11, 0x14, 0x09, -1 , 0x1a, -1 ,
// '(' ')' '*' '+' ',' '-' '.' '/'
0x0f, 0x12, -1 , -1 , 0x0c, 0x03, 0x1c, 0x1d,
// '0' '1' '2' '3' '4' '5' '6' '7'
0x16, 0x17, 0x13, 0x01, 0x0a, 0x10, 0x15, 0x07,
// '8' '9' ':' ';' '<' '=' '>' '?'
0x06, 0x18, 0x0e, 0x1e, -1 , -1 , -1 , 0x19,
// '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'X' 'Y' 'Z' '[' '\\' ']' '^' '_'
-1 , -1 , -1 , -1 , 0x0b, -1 , -1 , -1 ,
// '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'p' 'q' 'r' 's' 't' 'u' 'v' 'w'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'x' 'y' 'z' '{' '|' '}' '~' '\x7f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1
};
#include "loramodencodertty.h"
#include "loramodencoderascii.h"
#include "loramodencoderlora.h"
LoRaModEncoder::LoRaModEncoder() :
m_codingScheme(LoRaModSettings::CodingTTY),
m_nbSymbolBits(5)
m_nbSymbolBits(5),
m_nbParityBits(1),
m_hasCRC(true),
m_hasHeader(true)
{}
LoRaModEncoder::~LoRaModEncoder()
{}
void LoRaModEncoder::encodeString(const QString& str, std::vector<unsigned int>& symbols)
void LoRaModEncoder::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 LoRaModEncoder::encodeString(const QString& str, std::vector<unsigned short>& symbols)
{
switch (m_codingScheme)
{
case LoRaModSettings::CodingTTY:
encodeStringTTY(str, symbols);
if (m_nbSymbolBits == 5) {
LoRaModEncoderTTY::encodeString(str, symbols);
}
break;
case LoRaModSettings::CodingASCII:
encodeStringASCII(str, symbols);
if (m_nbSymbolBits == 7) {
LoRaModEncoderASCII::encodeString(str, symbols);
}
break;
case LoRaModSettings::CodingLoRa:
// TODO
if (m_nbSymbolBits >= 5)
{
QByteArray bytes = str.toUtf8();
encodeBytesLoRa(bytes, symbols);
}
break;
default:
break;
}
}
void LoRaModEncoder::encodeBytes(const QByteArray& bytes, std::vector<unsigned int>& symbols)
void LoRaModEncoder::encodeBytes(const QByteArray& bytes, std::vector<unsigned short>& symbols)
{
switch (m_codingScheme)
{
case LoRaModSettings::CodingLoRa:
// TODO
encodeBytesLoRa(bytes, symbols);
break;
default:
break;
};
}
void LoRaModEncoder::encodeStringASCII(const QString& str, std::vector<unsigned int>& symbols)
void LoRaModEncoder::encodeBytesLoRa(const QByteArray& bytes, std::vector<unsigned short>& symbols)
{
if (m_nbSymbolBits != 7) {
return;
QByteArray payload(bytes);
if (m_hasCRC) {
LoRaModEncoderLoRa::addChecksum(payload);
}
QByteArray asciiStr = str.toUtf8();
QByteArray::const_iterator it = asciiStr.begin();
for (; it != asciiStr.end(); ++it) {
symbols.push_back(*it & 0x7F);
}
}
void LoRaModEncoder::encodeStringTTY(const QString& str, std::vector<unsigned int>& symbols)
{
if (m_nbSymbolBits != 5) {
return;
}
TTYState ttyState = TTYLetters;
QByteArray asciiStr = str.toUtf8();
QByteArray::const_iterator it = asciiStr.begin();
for (; it != asciiStr.end(); ++it)
{
char asciiChar = *it & 0x7F;
int ttyLetter = asciiToTTYLetters[asciiChar];
int ttyFigure = asciiToTTYFigures[asciiChar];
if (ttyLetter < 0)
{
if (ttyFigure >= 0)
{
if (ttyState != TTYFigures)
{
symbols.push_back(ttyFigures);
ttyState = TTYFigures;
}
symbols.push_back(ttyFigure);
} // else skip
}
else
{
if (ttyFigure >= 0)
{
symbols.push_back(ttyFigure); // same TTY character no state change
}
else
{
if (ttyState != TTYLetters)
{
symbols.push_back(ttyLetters);
ttyState = TTYLetters;
}
symbols.push_back(ttyLetter);
}
}
}
}
LoRaModEncoderLoRa::encodeBytes(payload, symbols, m_nbSymbolBits, m_hasHeader, m_hasCRC, m_nbParityBits);
}

View File

@ -28,27 +28,26 @@ public:
~LoRaModEncoder();
void setCodingScheme(LoRaModSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; }
void setNbSymbolBits(unsigned int symbolBits) { m_nbSymbolBits = symbolBits; }
void encodeString(const QString& str, std::vector<unsigned int>& symbols);
void encodeBytes(const QByteArray& bytes, std::vector<unsigned int>& symbols);
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 encodeString(const QString& str, std::vector<unsigned short>& symbols);
void encodeBytes(const QByteArray& bytes, std::vector<unsigned short>& symbols);
private:
enum TTYState
{
TTYLetters,
TTYFigures
};
void encodeStringASCII(const QString& str, std::vector<unsigned int>& symbols);
void encodeStringTTY(const QString& str, std::vector<unsigned int>& symbols);
// LoRa functions
void encodeBytesLoRa(const QByteArray& bytes, std::vector<unsigned short>& symbols);
// General attributes
LoRaModSettings::CodingScheme m_codingScheme;
unsigned int m_spreadFactor;
unsigned int m_deBits;
unsigned int m_nbSymbolBits;
static const char asciiToTTYLetters[128];
static const char asciiToTTYFigures[128];
static const char ttyLetters = 0x1f;
static const char ttyFigures = 0x1b;
// LoRa attributes
unsigned int m_nbParityBits; //!< 1 to 4 Hamming FEC bits for 4 payload bits
bool m_hasCRC;
bool m_hasHeader;
};
#endif // PLUGINS_CHANNELTX_MODLORA_LORAMODENCODER_H_

View File

@ -0,0 +1,28 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "loramodencoderascii.h"
void LoRaModEncoderASCII::encodeString(const QString& str, std::vector<unsigned short>& symbols)
{
QByteArray asciiStr = str.toUtf8();
QByteArray::const_iterator it = asciiStr.begin();
for (; it != asciiStr.end(); ++it) {
symbols.push_back(*it & 0x7F);
}
}

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 PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERASCII_H_
#define PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERASCII_H_
#include <vector>
#include <QString>
class LoRaModEncoderASCII
{
public:
static void encodeString(const QString& str, std::vector<unsigned short>& symbols);
};
#endif // PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERASCII_H_

View File

@ -0,0 +1,147 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "loramodencoderlora.h"
void LoRaModEncoderLoRa::addChecksum(QByteArray& bytes)
{
uint16_t crc = sx1272DataChecksum(reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size());
bytes.append(crc & 0xff);
bytes.append((crc >> 8) & 0xff);
}
void LoRaModEncoderLoRa::encodeBytes(
const QByteArray& bytes,
std::vector<unsigned short>& symbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool hasCRC,
unsigned int nbParityBits
)
{
if (nbSymbolBits < 5) {
return;
}
unsigned int payloadSize = bytes.size();
const unsigned int numCodewords = roundUp(bytes.size()*2 + (hasHeader ? 5 : 0), nbSymbolBits);
unsigned int cOfs = 0;
unsigned int dOfs = 0;
std::vector<uint8_t> codewords(numCodewords);
if (hasHeader)
{
std::vector<uint8_t> hdr(3);
hdr[0] = payloadSize % 256;
hdr[1] = (hasCRC ? 1 : 0) | (nbParityBits << 1);
hdr[2] = headerChecksum(hdr.data());
// Nibble decomposition and parity bit(s) addition. LSNibble first.
codewords[cOfs++] = encodeHamming84sx(hdr[0] >> 4);
codewords[cOfs++] = encodeHamming84sx(hdr[0] & 0xf); // length
codewords[cOfs++] = encodeHamming84sx(hdr[1] & 0xf); // crc / fec info
codewords[cOfs++] = encodeHamming84sx(hdr[2] >> 4); // checksum
codewords[cOfs++] = encodeHamming84sx(hdr[2] & 0xf);
}
unsigned int headerSize = cOfs;
// 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);
// encode and whiten the rest of the payload with 4 + nbParityBits bits codewords
if (numCodewords > nbSymbolBits)
{
unsigned int cOfs2 = cOfs;
encodeFec(codewords, nbParityBits, cOfs, dOfs, reinterpret_cast<const uint8_t*>(bytes.data()), numCodewords - nbSymbolBits);
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
// interleave the codewords into symbols
symbols.clear();
symbols.resize(numSymbols);
diagonalInterleaveSx(codewords.data(), nbSymbolBits, symbols.data(), nbSymbolBits, 4);
if (numCodewords > nbSymbolBits) {
diagonalInterleaveSx(codewords.data() + nbSymbolBits, numCodewords - nbSymbolBits, symbols.data() + 8, nbSymbolBits, nbParityBits);
}
// gray decode
for (auto &sym : symbols) {
sym = grayToBinary16(sym);
}
}
void LoRaModEncoderLoRa::encodeFec(
std::vector<uint8_t> &codewords,
unsigned int nbParityBits,
unsigned int& cOfs,
unsigned int& dOfs,
const uint8_t *bytes,
const unsigned int codewordCount
)
{
for (unsigned int i = 0; i < codewordCount; i++, dOfs++)
{
if (nbParityBits == 1)
{
if (dOfs % 2 == 1) {
codewords[cOfs++] = encodeParity54(bytes[dOfs/2] >> 4);
} else {
codewords[cOfs++] = encodeParity54(bytes[dOfs/2] & 0xf);
}
}
else if (nbParityBits == 2)
{
if (dOfs % 2 == 1) {
codewords[cOfs++] = encodeParity64(bytes[dOfs/2] >> 4);
} else {
codewords[cOfs++] = encodeParity64(bytes[dOfs/2] & 0xf);
}
}
else if (nbParityBits == 3)
{
if (dOfs % 2 == 1) {
codewords[cOfs++] = encodeHamming74sx(bytes[dOfs/2] >> 4);
} else {
codewords[cOfs++] = encodeHamming74sx(bytes[dOfs/2] & 0xf);
}
}
else if (nbParityBits == 4)
{
if (dOfs % 2 == 1) {
codewords[cOfs++] = encodeHamming84sx(bytes[dOfs/2] >> 4);
} else {
codewords[cOfs++] = encodeHamming84sx(bytes[dOfs/2] & 0xf);
}
}
else
{
if (dOfs % 2 == 1) {
codewords[cOfs++] = bytes[dOfs/2] >> 4;
} else {
codewords[cOfs++] = bytes[dOfs/2] & 0xf;
}
}
}
}

View File

@ -0,0 +1,270 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERLORA_H_
#define PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERLORA_H_
#include <vector>
#include <QByteArray>
class LoRaModEncoderLoRa
{
public:
static void addChecksum(QByteArray& bytes);
static void encodeBytes(
const QByteArray& bytes,
std::vector<unsigned short>& symbols,
unsigned int nbSymbolBits,
bool hasHeader,
bool hasCRC,
unsigned int nbParityBits
);
private:
static void encodeFec(
std::vector<uint8_t> &codewords,
unsigned int nbParityBits,
unsigned int& cOfs,
unsigned int& dOfs,
const uint8_t *bytes,
const unsigned int codewordCount
);
/***********************************************************************
* Round functions
**********************************************************************/
static inline unsigned roundUp(unsigned num, unsigned factor)
{
return ((num + factor - 1) / factor) * factor;
}
/***********************************************************************
* Encode a 4 bit word into a 8 bits with parity
* Non standard version used in sx1272.
* https://en.wikipedia.org/wiki/Hamming_code
**********************************************************************/
static inline unsigned char encodeHamming84sx(const unsigned char x)
{
auto d0 = (x >> 0) & 0x1;
auto d1 = (x >> 1) & 0x1;
auto d2 = (x >> 2) & 0x1;
auto d3 = (x >> 3) & 0x1;
unsigned char b = x & 0xf;
b |= (d0 ^ d1 ^ d2) << 4;
b |= (d1 ^ d2 ^ d3) << 5;
b |= (d0 ^ d1 ^ d3) << 6;
b |= (d0 ^ d2 ^ d3) << 7;
return b;
}
/***********************************************************************
* Encode a 4 bit word into a 7 bits with parity.
* Non standard version used in sx1272.
**********************************************************************/
static inline unsigned char encodeHamming74sx(const unsigned char x)
{
auto d0 = (x >> 0) & 0x1;
auto d1 = (x >> 1) & 0x1;
auto d2 = (x >> 2) & 0x1;
auto d3 = (x >> 3) & 0x1;
unsigned char b = x & 0xf;
b |= (d0 ^ d1 ^ d2) << 4;
b |= (d1 ^ d2 ^ d3) << 5;
b |= (d0 ^ d1 ^ d3) << 6;
return b;
}
/***********************************************************************
* Encode a 4 bit word into a 6 bits with parity.
**********************************************************************/
static inline unsigned char encodeParity64(const unsigned char b)
{
auto x = b ^ (b >> 1) ^ (b >> 2);
auto y = x ^ b ^ (b >> 3);
return ((x & 1) << 4) | ((y & 1) << 5) | (b & 0xf);
}
/***********************************************************************
* Encode a 4 bit word into a 5 bits with parity.
**********************************************************************/
static inline unsigned char encodeParity54(const unsigned char b)
{
auto x = b ^ (b >> 2);
x = x ^ (x >> 1);
return (b & 0xf) | ((x << 4) & 0x10);
}
/***********************************************************************
* 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;
}
/***********************************************************************
* Specific checksum for header
**********************************************************************/
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;
}
/***********************************************************************
* Whitening generator reverse engineered from Sx1272 data stream.
* Each bit of a codeword is combined with the output from a different position in the whitening sequence.
**********************************************************************/
static inline void Sx1272ComputeWhitening(uint8_t *buffer, uint16_t bufferSize, const int bitOfs, const int nbParityBits)
{
static const int ofs0[8] = {6,4,2,0,-112,-114,-302,-34 }; // offset into sequence for each bit
static const int ofs1[5] = {6,4,2,0,-360 }; // different offsets used for single parity mode (1 == nbParityBits)
static const int whiten_len = 510; // length of whitening sequence
static const uint64_t whiten_seq[8] = { // whitening sequence
0x0102291EA751AAFFL,0xD24B050A8D643A17L,0x5B279B671120B8F4L,0x032B37B9F6FB55A2L,
0x994E0F87E95E2D16L,0x7CBCFC7631984C26L,0x281C8E4F0DAEF7F9L,0x1741886EB7733B15L
};
const int *ofs = (1 == nbParityBits) ? ofs1 : ofs0;
int i, j;
for (j = 0; j < bufferSize; j++)
{
uint8_t x = 0;
for (i = 0; i < 4 + nbParityBits; i++)
{
int t = (ofs[i] + j + bitOfs + whiten_len) % whiten_len;
if (whiten_seq[t >> 6] & ((uint64_t)1 << (t & 0x3F))) {
x |= 1 << i;
}
}
buffer[j] ^= x;
}
}
/***********************************************************************
* Diagonal interleaver + deinterleaver
**********************************************************************/
static inline void diagonalInterleaveSx(
const uint8_t *codewords,
const size_t numCodewords,
uint16_t *symbols,
const size_t nbSymbolBits,
const size_t nbParityBits
)
{
for (size_t x = 0; x < numCodewords / nbSymbolBits; x++)
{
const size_t cwOff = x*nbSymbolBits;
const size_t symOff = x*(4 + nbParityBits);
for (size_t k = 0; k < 4 + nbParityBits; k++)
{
for (size_t m = 0; m < nbSymbolBits; m++)
{
const size_t i = (m + k + nbSymbolBits) % nbSymbolBits;
const auto bit = (codewords[cwOff + i] >> k) & 0x1;
symbols[symOff + k] |= (bit << m);
}
}
}
}
/***********************************************************************
* https://en.wikipedia.org/wiki/Gray_code
**********************************************************************/
/*
* A more efficient version, for Gray codes of 16 or fewer bits.
*/
static inline unsigned short grayToBinary16(unsigned short num)
{
num = num ^ (num >> 8);
num = num ^ (num >> 4);
num = num ^ (num >> 2);
num = num ^ (num >> 1);
return num;
}
};
#endif // PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERLORA_H_

View File

@ -0,0 +1,133 @@
///////////////////////////////////////////////////////////////////////////////////
// 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 "loramodencodertty.h"
const char LoRaModEncoderTTY::asciiToTTYLetters[128] = {
// '\x00' '\x01' '\x02' '\x03' '\x04' '\x05' '\x06' '\x07'
0x00, -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x08' '\t' '\n' '\x0b' '\x0c' '\r' '\x0e' '\x0f'
-1 , -1 , 0x02, -1 , -1 , 0x08, -1 , -1 ,
// '\x10' '\x11' '\x12' '\x13' '\x14' '\x15' '\x16' '\x17'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x18' '\x19' '\x1a' '\x1b' '\x1c' '\x1d' '\x1e' '\x1f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// ' ' '!' '"' '#' '$' '%' '&' "'"
0x04, -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '(' ')' '*' '+' ',' '-' '.' '/'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '0' '1' '2' '3' '4' '5' '6' '7'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '8' '9' ':' ';' '<' '=' '>' '?'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G'
-1 , 0x03, 0x19, 0x0e, 0x09, 0x01, 0x0d, 0x1a,
// 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O'
0x14, 0x06, 0x0b, 0x0f, 0x12, 0x1c, 0x0c, 0x18,
// 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
0x16, 0x17, 0x0a, 0x05, 0x10, 0x07, 0x1e, 0x13,
// 'X' 'Y' 'Z' '[' '\\' ']' '^' '_'
0x1d, 0x15, 0x11, -1 , -1 , -1 , -1 , -1 ,
// '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g'
-1 , 0x03, 0x19, 0x0e, 0x09, 0x01, 0x0d, 0x1a,
// 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
0x14, 0x06, 0x0b, 0x0f, 0x12, 0x1c, 0x0c, 0x18,
// 'p' 'q' 'r' 's' 't' 'u' 'v' 'w'
0x16, 0x17, 0x0a, 0x05, 0x10, 0x07, 0x1e, 0x13,
// 'x' 'y' 'z' '{' '|' '}' '~' '\x7f'
0x1d, 0x15, 0x11, -1 , -1 , -1 , -1 , -1
};
const char LoRaModEncoderTTY::asciiToTTYFigures[128] = {
// '\x00' '\x01' '\x02' '\x03' '\x04' '\x05' '\x06' '\x07'
0x00, -1 , -1 , -1 , -1 , -1 , -1 , 0x05,
// '\x08' '\t' '\n' '\x0b' '\x0c' '\r' '\x0e' '\x0f'
-1 , -1 , 0x02, -1 , -1 , 0x08, -1 , -1 ,
// '\x10' '\x11' '\x12' '\x13' '\x14' '\x15' '\x16' '\x17'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// '\x18' '\x19' '\x1a' '\x1b' '\x1c' '\x1d' '\x1e' '\x1f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// ' ' '!' '"' '#' '$' '%' '&' "'"
0x04, 0x0d, 0x11, 0x14, 0x09, -1 , 0x1a, -1 ,
// '(' ')' '*' '+' ',' '-' '.' '/'
0x0f, 0x12, -1 , -1 , 0x0c, 0x03, 0x1c, 0x1d,
// '0' '1' '2' '3' '4' '5' '6' '7'
0x16, 0x17, 0x13, 0x01, 0x0a, 0x10, 0x15, 0x07,
// '8' '9' ':' ';' '<' '=' '>' '?'
0x06, 0x18, 0x0e, 0x1e, -1 , -1 , -1 , 0x19,
// '@' 'A' 'B' 'C' 'D' 'E' 'F' 'G'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'X' 'Y' 'Z' '[' '\\' ']' '^' '_'
-1 , -1 , -1 , -1 , 0x0b, -1 , -1 , -1 ,
// '`' 'a' 'b' 'c' 'd' 'e' 'f' 'g'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'p' 'q' 'r' 's' 't' 'u' 'v' 'w'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
// 'x' 'y' 'z' '{' '|' '}' '~' '\x7f'
-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1
};
void LoRaModEncoderTTY::encodeString(const QString& str, std::vector<unsigned short>& symbols)
{
TTYState ttyState = TTYLetters;
QByteArray asciiStr = str.toUtf8();
QByteArray::const_iterator it = asciiStr.begin();
for (; it != asciiStr.end(); ++it)
{
char asciiChar = *it & 0x7F;
int ttyLetter = asciiToTTYLetters[asciiChar];
int ttyFigure = asciiToTTYFigures[asciiChar];
if (ttyLetter < 0)
{
if (ttyFigure >= 0)
{
if (ttyState != TTYFigures)
{
symbols.push_back(ttyFigures);
ttyState = TTYFigures;
}
symbols.push_back(ttyFigure);
} // else skip
}
else
{
if (ttyFigure >= 0)
{
symbols.push_back(ttyFigure); // same TTY character no state change
}
else
{
if (ttyState != TTYLetters)
{
symbols.push_back(ttyLetters);
ttyState = TTYLetters;
}
symbols.push_back(ttyLetter);
}
}
}
}

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 PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERTTY_H_
#define PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERTTY_H_
#include <vector>
#include <QString>
class LoRaModEncoderTTY
{
public:
static void encodeString(const QString& str, std::vector<unsigned short>& symbols);
private:
enum TTYState
{
TTYLetters,
TTYFigures
};
static const char asciiToTTYLetters[128];
static const char asciiToTTYFigures[128];
static const char ttyLetters = 0x1f;
static const char ttyFigures = 0x1b;
};
#endif // PLUGINS_CHANNELTX_MODLORA_LORAMODENCODERTTY_H_

View File

@ -108,7 +108,10 @@ bool LoRaModGUI::handleMessage(const Message& message)
else if (LoRaMod::MsgReportPayloadTime::match(message))
{
const LoRaMod::MsgReportPayloadTime& rpt = (LoRaMod::MsgReportPayloadTime&) message;
ui->msgTimeText->setText(tr("%1").arg(rpt.getPayloadTimeMs()));
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));
return true;
}
else if (DSPSignalNotification::match(message))
@ -225,9 +228,31 @@ void LoRaModGUI::on_syncWord_editingFinished()
void LoRaModGUI::on_scheme_currentIndexChanged(int index)
{
m_settings.m_codingScheme = (LoRaModSettings::CodingScheme) index;
ui->fecParity->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
ui->crc->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
ui->header->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
applySettings();
}
void LoRaModGUI::on_fecParity_valueChanged(int value)
{
m_settings.m_nbParityBits = value;
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
applySettings();
}
void LoRaModGUI::on_crc_stateChanged(int state)
{
m_settings.m_hasCRC = (state == Qt::Checked);
applySettings();
}
void LoRaModGUI::on_header_stateChanged(int state)
{
m_settings.m_hasHeader = (state == Qt::Checked);
applySettings();
}
void LoRaModGUI::on_myCall_editingFinished()
{
m_settings.m_myCall = ui->myCall->text();
@ -460,6 +485,10 @@ void LoRaModGUI::displaySettings()
displayStreamIndex();
displayCurrentPayloadMessage();
ui->fecParity->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
ui->crc->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
ui->header->setEnabled(m_settings.m_codingScheme == LoRaModSettings::CodingLoRa);
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->bwText->setText(QString("%1 Hz").arg(thisBW));
@ -475,6 +504,10 @@ void LoRaModGUI::displaySettings()
ui->syncWord->setText((tr("%1").arg(m_settings.m_syncWord, 2, 16)));
ui->channelMute->setChecked(m_settings.m_channelMute);
ui->scheme->setCurrentIndex((int) m_settings.m_codingScheme);
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->header->setChecked(m_settings.m_hasHeader);
ui->myCall->setText(m_settings.m_myCall);
ui->urCall->setText(m_settings.m_urCall);
ui->myLocator->setText(m_settings.m_myLoc);

View File

@ -95,6 +95,9 @@ private slots:
void on_syncWord_editingFinished();
void on_channelMute_toggled(bool checked);
void on_scheme_currentIndexChanged(int index);
void on_fecParity_valueChanged(int value);
void on_crc_stateChanged(int state);
void on_header_stateChanged(int state);
void on_myCall_editingFinished();
void on_urCall_editingFinished();
void on_myLocator_editingFinished();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>380</width>
<height>411</height>
<height>376</height>
</rect>
</property>
<property name="minimumSize">
@ -31,17 +31,17 @@
<x>10</x>
<y>20</y>
<width>370</width>
<height>150</height>
<height>125</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>150</height>
<height>125</height>
</size>
</property>
<property name="windowTitle">
<string>RF/demod Settings</string>
<string>RF/mod/coder Settings</string>
</property>
<widget class="QLabel" name="bwLabel">
<property name="geometry">
@ -102,7 +102,7 @@
<rect>
<x>40</x>
<y>70</y>
<width>81</width>
<width>61</width>
<height>16</height>
</rect>
</property>
@ -131,7 +131,7 @@
<widget class="QLabel" name="spreadText">
<property name="geometry">
<rect>
<x>130</x>
<x>110</x>
<y>70</y>
<width>30</width>
<height>16</height>
@ -175,7 +175,7 @@
<widget class="QLabel" name="deBitsLabel">
<property name="geometry">
<rect>
<x>180</x>
<x>160</x>
<y>70</y>
<width>32</width>
<height>16</height>
@ -188,15 +188,15 @@
<widget class="QLabel" name="deBitsText">
<property name="geometry">
<rect>
<x>320</x>
<x>250</x>
<y>70</y>
<width>30</width>
<width>10</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<width>10</width>
<height>0</height>
</size>
</property>
@ -210,9 +210,9 @@
<widget class="QSlider" name="deBits">
<property name="geometry">
<rect>
<x>220</x>
<x>200</x>
<y>70</y>
<width>81</width>
<width>41</width>
<height>16</height>
</rect>
</property>
@ -496,8 +496,8 @@
<widget class="QLabel" name="syncLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>114</y>
<x>280</x>
<y>70</y>
<width>32</width>
<height>16</height>
</rect>
@ -509,8 +509,8 @@
<widget class="QLineEdit" name="syncWord">
<property name="geometry">
<rect>
<x>40</x>
<y>112</y>
<x>320</x>
<y>68</y>
<width>30</width>
<height>20</height>
</rect>
@ -528,58 +528,20 @@
<string>00</string>
</property>
</widget>
<widget class="QLabel" name="schemeLabel">
<property name="geometry">
<rect>
<x>90</x>
<y>114</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Scheme</string>
</property>
</widget>
<widget class="QComboBox" name="scheme">
<property name="geometry">
<rect>
<x>140</x>
<y>112</y>
<width>86</width>
<height>20</height>
</rect>
</property>
<item>
<property name="text">
<string>LoRa</string>
</property>
</item>
<item>
<property name="text">
<string>ASCII</string>
</property>
</item>
<item>
<property name="text">
<string>TTY</string>
</property>
</item>
</widget>
</widget>
<widget class="QWidget" name="messageContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>180</y>
<y>150</y>
<width>370</width>
<height>210</height>
<height>221</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>210</height>
<height>220</height>
</size>
</property>
<property name="windowTitle">
@ -589,7 +551,7 @@
<property name="geometry">
<rect>
<x>2</x>
<y>10</y>
<y>40</y>
<width>32</width>
<height>16</height>
</rect>
@ -602,7 +564,7 @@
<property name="geometry">
<rect>
<x>40</x>
<y>10</y>
<y>40</y>
<width>40</width>
<height>16</height>
</rect>
@ -615,7 +577,7 @@
<property name="geometry">
<rect>
<x>200</x>
<y>10</y>
<y>40</y>
<width>50</width>
<height>16</height>
</rect>
@ -628,7 +590,7 @@
<property name="geometry">
<rect>
<x>90</x>
<y>10</y>
<y>40</y>
<width>90</width>
<height>16</height>
</rect>
@ -650,7 +612,7 @@
<property name="geometry">
<rect>
<x>260</x>
<y>10</y>
<y>40</y>
<width>90</width>
<height>16</height>
</rect>
@ -672,7 +634,7 @@
<property name="geometry">
<rect>
<x>40</x>
<y>30</y>
<y>60</y>
<width>40</width>
<height>16</height>
</rect>
@ -685,7 +647,7 @@
<property name="geometry">
<rect>
<x>90</x>
<y>30</y>
<y>60</y>
<width>90</width>
<height>16</height>
</rect>
@ -707,7 +669,7 @@
<property name="geometry">
<rect>
<x>200</x>
<y>30</y>
<y>60</y>
<width>50</width>
<height>16</height>
</rect>
@ -720,7 +682,7 @@
<property name="geometry">
<rect>
<x>260</x>
<y>30</y>
<y>60</y>
<width>90</width>
<height>16</height>
</rect>
@ -742,7 +704,7 @@
<property name="geometry">
<rect>
<x>310</x>
<y>58</y>
<y>80</y>
<width>40</width>
<height>20</height>
</rect>
@ -758,7 +720,7 @@
<property name="geometry">
<rect>
<x>40</x>
<y>58</y>
<y>80</y>
<width>90</width>
<height>20</height>
</rect>
@ -831,7 +793,7 @@
<property name="geometry">
<rect>
<x>2</x>
<y>60</y>
<y>110</y>
<width>32</width>
<height>16</height>
</rect>
@ -844,7 +806,7 @@
<property name="geometry">
<rect>
<x>40</x>
<y>90</y>
<y>110</y>
<width>311</width>
<height>60</height>
</rect>
@ -854,7 +816,7 @@
<property name="geometry">
<rect>
<x>2</x>
<y>160</y>
<y>175</y>
<width>32</width>
<height>16</height>
</rect>
@ -867,7 +829,7 @@
<property name="geometry">
<rect>
<x>40</x>
<y>160</y>
<y>175</y>
<width>311</width>
<height>20</height>
</rect>
@ -877,7 +839,7 @@
<property name="geometry">
<rect>
<x>140</x>
<y>58</y>
<y>80</y>
<width>20</width>
<height>20</height>
</rect>
@ -897,7 +859,7 @@
<property name="geometry">
<rect>
<x>170</x>
<y>58</y>
<y>80</y>
<width>20</width>
<height>20</height>
</rect>
@ -920,7 +882,7 @@
<property name="geometry">
<rect>
<x>200</x>
<y>60</y>
<y>82</y>
<width>51</width>
<height>16</height>
</rect>
@ -933,7 +895,7 @@
<property name="geometry">
<rect>
<x>250</x>
<y>56</y>
<y>78</y>
<width>24</width>
<height>24</height>
</rect>
@ -955,7 +917,7 @@
<property name="geometry">
<rect>
<x>280</x>
<y>60</y>
<y>82</y>
<width>22</width>
<height>16</height>
</rect>
@ -971,7 +933,7 @@
<property name="geometry">
<rect>
<x>2</x>
<y>190</y>
<y>200</y>
<width>40</width>
<height>16</height>
</rect>
@ -984,8 +946,8 @@
<property name="geometry">
<rect>
<x>40</x>
<y>190</y>
<width>50</width>
<y>200</y>
<width>65</width>
<height>16</height>
</rect>
</property>
@ -993,23 +955,166 @@
<string>Payload time in milliseconds</string>
</property>
<property name="text">
<string>00000</string>
<string>00000 ms</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="msgTimeUnits">
<widget class="QLabel" name="msgTotalTimeLabel">
<property name="geometry">
<rect>
<x>95</x>
<y>190</y>
<width>20</width>
<x>119</x>
<y>200</y>
<width>35</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>ms</string>
<string>Total</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="msgTotalTimeText">
<property name="geometry">
<rect>
<x>150</x>
<y>200</y>
<width>65</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Total transmission time in milliseconds</string>
</property>
<property name="text">
<string>00000 ms</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="schemeLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>10</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Scheme</string>
</property>
</widget>
<widget class="QComboBox" name="scheme">
<property name="geometry">
<rect>
<x>60</x>
<y>8</y>
<width>86</width>
<height>20</height>
</rect>
</property>
<item>
<property name="text">
<string>LoRa</string>
</property>
</item>
<item>
<property name="text">
<string>ASCII</string>
</property>
</item>
<item>
<property name="text">
<string>TTY</string>
</property>
</item>
</widget>
<widget class="QCheckBox" name="header">
<property name="geometry">
<rect>
<x>300</x>
<y>10</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Header (explicit)</string>
</property>
<property name="text">
<string>HDR</string>
</property>
</widget>
<widget class="QCheckBox" name="crc">
<property name="geometry">
<rect>
<x>240</x>
<y>10</y>
<width>50</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Append CRC to payload</string>
</property>
<property name="text">
<string>CRC</string>
</property>
</widget>
<widget class="QLabel" name="fecParityLabel">
<property name="geometry">
<rect>
<x>160</x>
<y>10</y>
<width>32</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>FEC</string>
</property>
</widget>
<widget class="QDial" name="fecParity">
<property name="geometry">
<rect>
<x>190</x>
<y>5</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="QLabel" name="fecParityText">
<property name="geometry">
<rect>
<x>220</x>
<y>10</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>

View File

@ -43,6 +43,9 @@ void LoRaModSettings::resetToDefaults()
m_preambleChirps = 8;
m_quietMillis = 1000;
m_codingScheme = CodingLoRa;
m_nbParityBits = 1;
m_hasCRC = true;
m_hasHeader = true;
m_textMessage = "Hello LoRa";
m_myCall = "MYCALL";
m_urCall = "URCALL";
@ -129,6 +132,9 @@ QByteArray LoRaModSettings::serialize() const
s.writeString(28, m_textMessage);
s.writeBlob(29, m_bytesMessage);
s.writeS32(30, (int) m_messageType);
s.writeS32(31, m_nbParityBits);
s.writeBool(32, m_hasCRC);
s.writeBool(33, m_hasHeader);
s.writeString(40, m_myCall);
s.writeString(41, m_urCall);
s.writeString(42, m_myLoc);
@ -199,6 +205,9 @@ bool LoRaModSettings::deserialize(const QByteArray& data)
d.readBlob(29, &m_bytesMessage);
d.readS32(30, &tmp, 0);
m_messageType = (MessageType) tmp;
d.readS32(31, &m_nbParityBits, 1);
d.readBool(32, &m_hasCRC, true);
d.readBool(33, &m_hasHeader, true);
d.readString(40, &m_myCall, "MYCALL");
d.readString(41, &m_urCall, "URCALL");
d.readString(42, &m_myLoc, "AA00AA");

View File

@ -56,6 +56,9 @@ struct LoRaModSettings
int m_deBits; //!< Low data rate optmize (DE) bits
int m_preambleChirps; //!< Number of preamble chirps
int m_quietMillis; //!< Number of milliseconds to pause between transmissions
int m_nbParityBits; //!< Hamming parity bits (LoRa)
bool m_hasCRC; //!< Payload has CRC (LoRa)
bool m_hasHeader; //!< Header present before actual payload (LoRa)
unsigned char m_syncWord;
bool m_channelMute;
CodingScheme m_codingScheme;

View File

@ -332,7 +332,7 @@ void LoRaModSource::modulateSample()
}
}
unsigned int LoRaModSource::encodeSymbol(unsigned int symbol)
unsigned short LoRaModSource::encodeSymbol(unsigned short symbol)
{
if (m_settings.m_deBits == 0) {
return symbol;
@ -421,7 +421,7 @@ void LoRaModSource::applyChannelSettings(int channelSampleRate, int bandwidth, i
reset();
}
void LoRaModSource::setSymbols(const std::vector<unsigned int>& symbols)
void LoRaModSource::setSymbols(const std::vector<unsigned short>& symbols)
{
m_symbols = symbols;
qDebug("LoRaModSource::setSymbols: m_symbols: %lu", m_symbols.size());

View File

@ -47,7 +47,7 @@ public:
}
void applySettings(const LoRaModSettings& settings, bool force = false);
void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false);
void setSymbols(const std::vector<unsigned int>& symbols);
void setSymbols(const std::vector<unsigned short>& symbols);
bool getActive() const { return m_active; }
private:
@ -76,7 +76,7 @@ private:
Complex *m_downChirps;
Complex *m_upChirps;
double *m_phaseIncrements;
std::vector<unsigned int> m_symbols;
std::vector<unsigned short> m_symbols;
unsigned int m_fftLength; //!< chirp length in samples
unsigned int m_chirp; //!< actual chirp index in chirps table
unsigned int m_chirp0; //!< half index of chirp start in chirps table
@ -116,7 +116,7 @@ private:
void processOneSample(Complex& ci);
void calculateLevel(Real& sample);
void modulateSample();
unsigned int encodeSymbol(unsigned int symbol); //!< Encodes symbol with possible DE bits spacing
unsigned short encodeSymbol(unsigned short symbol); //!< Encodes symbol with possible DE bits spacing
};
#endif // INCLUDE_LORAMODSOURCE_H