diff --git a/plugins/channelrx/demodlora/CMakeLists.txt b/plugins/channelrx/demodlora/CMakeLists.txt index 66e487f71..6d9ad7a37 100644 --- a/plugins/channelrx/demodlora/CMakeLists.txt +++ b/plugins/channelrx/demodlora/CMakeLists.txt @@ -7,6 +7,7 @@ set(lora_SOURCES lorademodsink.cpp lorademodbaseband.cpp loraplugin.cpp + lorademoddecoder.cpp lorademodgui.ui ) @@ -16,6 +17,7 @@ set(lora_HEADERS lorademodsettings.h lorademodsink.h lorademodbaseband.h + lorademoddecoder.h loraplugin.h ) diff --git a/plugins/channelrx/demodlora/lorademoddecoder.cpp b/plugins/channelrx/demodlora/lorademoddecoder.cpp new file mode 100644 index 000000000..160469be0 --- /dev/null +++ b/plugins/channelrx/demodlora/lorademoddecoder.cpp @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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', '?', '&', ' ', '.', '/', ';', ' ' +}; + + +LoRaDemodDecoder::LoRaDemodDecoder() : + m_codingScheme(LoRaDemodSettings::CodingTTY), + m_nbSymbolBits(5) +{} + +LoRaDemodDecoder::~LoRaDemodDecoder() +{} + +void LoRaDemodDecoder::decodeSymbols(const std::vector& symbols, QString& str) +{ + switch(m_codingScheme) + { + case LoRaDemodSettings::CodingTTY: + decodeSymbolsTTY(symbols, str); + break; + case LoRaDemodSettings::CodingASCII: + decodeSymbolsASCII(symbols, str); + break; + } +} + +void LoRaDemodDecoder::decodeSymbols(const std::vector& symbols, QByteArray& bytes) +{ + +} + +void LoRaDemodDecoder::decodeSymbolsASCII(const std::vector& symbols, QString& str) +{ + if (m_nbSymbolBits != 7) { + return; + } + + std::vector::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& symbols, QString& str) +{ + if (m_nbSymbolBits != 5) { + return; + } + + std::vector::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 == TTYLetters) { + asciiChar = ttyFigures[ttyChar]; + } + + if (asciiChar >= 0) { + bytes.push_back(asciiChar); + } + } + } + + str = QString(bytes.toStdString().c_str()); +} diff --git a/plugins/channelrx/demodlora/lorademoddecoder.h b/plugins/channelrx/demodlora/lorademoddecoder.h new file mode 100644 index 000000000..4a86c6c07 --- /dev/null +++ b/plugins/channelrx/demodlora/lorademoddecoder.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_LORADEMODDECODER_H +#define INCLUDE_LORADEMODDECODER_H + +#include +#include "lorademodsettings.h" + +class LoRaDemodDecoder +{ +public: + LoRaDemodDecoder(); + ~LoRaDemodDecoder(); + + void setCodingScheme(LoRaDemodSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; } + void setNbSymbolBits(unsigned int symbolBits) { m_nbSymbolBits = symbolBits; } + void decodeSymbols(const std::vector& symbols, QString& str); //!< For ASCII and TTY + void decodeSymbols(const std::vector& symbols, QByteArray& bytes); //!< For raw bytes (original LoRa) + +private: + enum TTYState + { + TTYLetters, + TTYFigures + }; + + void decodeSymbolsASCII(const std::vector& symbols, QString& str); + void decodeSymbolsTTY(const std::vector& symbols, QString& str); + + LoRaDemodSettings::CodingScheme m_codingScheme; + unsigned int m_nbSymbolBits; + + static const char ttyLetters[32]; + static const char ttyFigures[32]; + static const char lettersTag = 0x1f; + static const char figuresTag = 0x1b; +}; + +#endif // INCLUDE_LORADEMODDECODER_H diff --git a/plugins/channelrx/demodlora/lorademodsettings.h b/plugins/channelrx/demodlora/lorademodsettings.h index 475c36114..639235a13 100644 --- a/plugins/channelrx/demodlora/lorademodsettings.h +++ b/plugins/channelrx/demodlora/lorademodsettings.h @@ -28,10 +28,18 @@ class Serializable; struct LoRaDemodSettings { + enum CodingScheme + { + CodingTTY, //!< plain TTY (5 bits) + CodingASCII, //!< plain ASCII (7 bits) + CodingLoRa //!< Standard LoRa + }; + int m_inputFrequencyOffset; int m_bandwidthIndex; int m_spreadFactor; int m_deBits; //!< Low data rate optmize (DE) bits + CodingScheme m_codingScheme; uint32_t m_rgbColor; QString m_title; diff --git a/plugins/channeltx/modlora/CMakeLists.txt b/plugins/channeltx/modlora/CMakeLists.txt index 9936be9a0..f3225c707 100644 --- a/plugins/channeltx/modlora/CMakeLists.txt +++ b/plugins/channeltx/modlora/CMakeLists.txt @@ -6,6 +6,7 @@ set(modlora_SOURCES loramodsource.cpp loramodbaseband.cpp loramodplugin.cpp + loramodencoder.cpp loramodwebapiadapter.cpp ) @@ -15,6 +16,7 @@ set(modlora_HEADERS loramodsource.h loramodbaseband.h loramodplugin.h + loramodencoder.h loramodwebapiadapter.h ) diff --git a/plugins/channeltx/modlora/loramodencoder.cpp b/plugins/channeltx/modlora/loramodencoder.cpp new file mode 100644 index 000000000..85f37faa2 --- /dev/null +++ b/plugins/channeltx/modlora/loramodencoder.cpp @@ -0,0 +1,174 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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 + }; + +LoRaModEncoder::LoRaModEncoder() : + m_codingScheme(LoRaModSettings::CodingTTY), + m_nbSymbolBits(5) +{} + +LoRaModEncoder::~LoRaModEncoder() +{} + +void LoRaModEncoder::encodeString(const QString& str, std::vector& symbols) +{ + switch (m_codingScheme) + { + case LoRaModSettings::CodingTTY: + encodeStringTTY(str, symbols); + break; + case LoRaModSettings::CodingASCII: + encodeStringASCII(str, symbols); + break; + default: + break; + } +} + +void LoRaModEncoder::encodeStringASCII(const QString& str, std::vector& symbols) +{ + if (m_nbSymbolBits != 7) { + return; + } + + 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& 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); + } + } + } +} diff --git a/plugins/channeltx/modlora/loramodencoder.h b/plugins/channeltx/modlora/loramodencoder.h new file mode 100644 index 000000000..8cad06a54 --- /dev/null +++ b/plugins/channeltx/modlora/loramodencoder.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINS_CHANNELTX_MODLORA_LORAMODENCODER_H_ +#define PLUGINS_CHANNELTX_MODLORA_LORAMODENCODER_H_ + +#include +#include "loramodsettings.h" + +class LoRaModEncoder +{ +public: + LoRaModEncoder(); + ~LoRaModEncoder(); + + void setCodingScheme(LoRaModSettings::CodingScheme codingScheme) { m_codingScheme = codingScheme; } + void setNbSymbolBits(unsigned int symbolBits) { m_nbSymbolBits = symbolBits; } + void encodeString(const QString& str, std::vector& symbols); + +private: + enum TTYState + { + TTYLetters, + TTYFigures + }; + + void encodeStringASCII(const QString& str, std::vector& symbols); + void encodeStringTTY(const QString& str, std::vector& symbols); + + LoRaModSettings::CodingScheme m_codingScheme; + unsigned int m_nbSymbolBits; + + static const char asciiToTTYLetters[128]; + static const char asciiToTTYFigures[128]; + static const char ttyLetters = 0x1f; + static const char ttyFigures = 0x1b; +}; + +#endif // PLUGINS_CHANNELTX_MODLORA_LORAMODENCODER_H_ + diff --git a/plugins/channeltx/modlora/loramodsettings.cpp b/plugins/channeltx/modlora/loramodsettings.cpp index 324da1eef..03bafd0ea 100644 --- a/plugins/channeltx/modlora/loramodsettings.cpp +++ b/plugins/channeltx/modlora/loramodsettings.cpp @@ -42,7 +42,7 @@ void LoRaModSettings::resetToDefaults() m_deBits = 0; m_preambleChirps = 8; m_quietMillis = 1000; - m_message = "LoRa beacon"; + m_message = "Hello LoRa"; m_syncWord = 0x34; m_channelMute = false; m_rgbColor = QColor(255, 0, 255).rgb(); @@ -101,7 +101,7 @@ bool LoRaModSettings::deserialize(const QByteArray& data) d.readS32(1, &m_inputFrequencyOffset, 0); d.readS32(2, &m_bandwidthIndex, 0); d.readS32(3, &m_spreadFactor, 0); - d.readString(4, &m_message, "LoRa beacon"); + d.readString(4, &m_message, "Hello LoRa"); if (m_channelMarker) { diff --git a/plugins/channeltx/modlora/loramodsettings.h b/plugins/channeltx/modlora/loramodsettings.h index a92461880..ed70983e1 100644 --- a/plugins/channeltx/modlora/loramodsettings.h +++ b/plugins/channeltx/modlora/loramodsettings.h @@ -27,6 +27,13 @@ class Serializable; struct LoRaModSettings { + enum CodingScheme + { + CodingTTY, //!< plain TTY (5 bits) + CodingASCII, //!< plain ASCII (7 bits) + CodingLoRa //!< Standard LoRa + }; + int m_inputFrequencyOffset; int m_bandwidthIndex; int m_spreadFactor; @@ -35,7 +42,12 @@ struct LoRaModSettings int m_quietMillis; //!< Number of milliseconds to pause between transmissions unsigned char m_syncWord; bool m_channelMute; - QString m_message; + CodingScheme m_codingScheme; + QString m_message; //!< Freeflow message + QString m_myCall; //!< QSO mode: my callsign + QString m_urCall; //!< QSO mode: your callsign + QString m_myLoc; //!< QSO mode: my locator + QString m_myRpt; //!< QSO mode: my report uint32_t m_rgbColor; QString m_title; int m_streamIndex;