1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-04-26 09:23:59 -04:00

Meshtastic demod: removed scheme button and useless decoders

This commit is contained in:
f4exb 2026-03-16 00:11:41 +01:00
parent ab040bd2c7
commit 3624a51d6a
11 changed files with 207 additions and 805 deletions

View File

@ -12,10 +12,7 @@ set(meshtastic_SOURCES
meshtasticdemodbaseband.cpp
meshtasticplugin.cpp
meshtasticdemoddecoder.cpp
meshtasticdemoddecodertty.cpp
meshtasticdemoddecoderascii.cpp
meshtasticdemoddecoderlora.cpp
meshtasticdemoddecoderft.cpp
meshtasticdemodmsg.cpp
meshtasticdemodwebapiadapter.cpp
)
@ -26,10 +23,7 @@ set(meshtastic_HEADERS
meshtasticdemodsink.h
meshtasticdemodbaseband.h
meshtasticdemoddecoder.h
meshtasticdemoddecodertty.h
meshtasticdemoddecoderascii.h
meshtasticdemoddecoderlora.h
meshtasticdemoddecoderft.h
meshtasticdemodmsg.h
meshtasticplugin.h
meshtasticdemodwebapiadapter.h

View File

@ -20,10 +20,7 @@
#include <cmath>
#include "meshtasticdemoddecoder.h"
#include "meshtasticdemoddecodertty.h"
#include "meshtasticdemoddecoderascii.h"
#include "meshtasticdemoddecoderlora.h"
#include "meshtasticdemoddecoderft.h"
#include "meshtasticdemodmsg.h"
MeshtasticDemodDecoder::MeshtasticDemodDecoder() :
@ -66,98 +63,45 @@ void MeshtasticDemodDecoder::setNbSymbolBits(unsigned int spreadFactor, unsigned
m_nbSymbolBits = m_spreadFactor - m_deBits;
}
void MeshtasticDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QString& str)
{
switch(m_codingScheme)
{
case MeshtasticDemodSettings::CodingTTY:
if (m_nbSymbolBits == 5) {
MeshtasticDemodDecoderTTY::decodeSymbols(symbols, str);
}
break;
case MeshtasticDemodSettings::CodingASCII:
if (m_nbSymbolBits == 7) {
MeshtasticDemodDecoderASCII::decodeSymbols(symbols, str);
}
break;
default:
break;
}
}
void MeshtasticDemodDecoder::decodeSymbols(const std::vector<unsigned short>& symbols, QByteArray& bytes)
{
switch(m_codingScheme)
if (m_nbSymbolBits >= 5)
{
case MeshtasticDemodSettings::CodingLoRa:
if (m_nbSymbolBits >= 5)
{
unsigned int headerNbSymbolBits;
unsigned int headerNbSymbolBits;
if (m_hasHeader && (m_spreadFactor > 2U)) {
headerNbSymbolBits = m_spreadFactor - 2U;
} else {
headerNbSymbolBits = m_nbSymbolBits;
}
MeshtasticDemodDecoderLoRa::decodeBytes(
bytes,
symbols,
m_nbSymbolBits,
headerNbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
MeshtasticDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
headerNbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
if (m_hasHeader && (m_spreadFactor > 2U)) {
headerNbSymbolBits = m_spreadFactor - 2U;
} else {
headerNbSymbolBits = m_nbSymbolBits;
}
break;
default:
break;
}
}
void MeshtasticDemodDecoder::decodeSymbols( //!< For FT coding scheme
const std::vector<std::vector<float>>& mags, // vector of symbols magnitudes
int nbSymbolBits, //!< number of bits per symbol
std::string& msg, //!< formatted message
std::string& call1, //!< 1st callsign or shorthand
std::string& call2, //!< 2nd callsign
std::string& loc, //!< locator, report or shorthand
bool& reply //!< true if message is a reply report
)
{
if (m_codingScheme != MeshtasticDemodSettings::CodingFT) {
return;
}
MeshtasticDemodDecoderLoRa::decodeBytes(
bytes,
symbols,
m_nbSymbolBits,
headerNbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
MeshtasticDemodDecoderFT::decodeSymbols(
mags,
nbSymbolBits,
msg,
call1,
call2,
loc,
reply,
m_payloadParityStatus,
m_payloadCRCStatus
);
MeshtasticDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
headerNbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
}
}
bool MeshtasticDemodDecoder::handleMessage(const Message& cmd)
@ -240,249 +184,197 @@ bool MeshtasticDemodDecoder::handleMessage(const Message& cmd)
QDateTime dt = QDateTime::currentDateTime();
QString msgTimestamp = dt.toString(Qt::ISODateWithMs);
if (m_codingScheme == MeshtasticDemodSettings::CodingLoRa)
QByteArray msgBytes;
const std::vector<std::vector<float>>& msgMags = msg.getMagnitudes();
const bool canSoftDecode = !msgMags.empty()
&& (msgMags.size() >= msg.getSymbols().size())
&& (m_spreadFactor >= 5U)
&& (m_loRaBandwidth > 0U);
struct LoRaDecodeState
{
QByteArray msgBytes;
const std::vector<std::vector<float>>& msgMags = msg.getMagnitudes();
const bool canSoftDecode = !msgMags.empty()
&& (msgMags.size() >= msg.getSymbols().size())
&& (m_spreadFactor >= 5U)
&& (m_loRaBandwidth > 0U);
QByteArray bytes;
bool hasCRC;
unsigned int nbParityBits;
unsigned int packetLength;
unsigned int nbSymbols;
unsigned int nbCodewords;
bool earlyEOM;
int headerParityStatus;
bool headerCRCStatus;
int payloadParityStatus;
bool payloadCRCStatus;
};
struct LoRaDecodeState
{
QByteArray bytes;
bool hasCRC;
unsigned int nbParityBits;
unsigned int packetLength;
unsigned int nbSymbols;
unsigned int nbCodewords;
bool earlyEOM;
int headerParityStatus;
bool headerCRCStatus;
int payloadParityStatus;
bool payloadCRCStatus;
};
auto captureLoRaState = [this](const QByteArray& bytes) -> LoRaDecodeState {
LoRaDecodeState s;
s.bytes = bytes;
s.hasCRC = m_hasCRC;
s.nbParityBits = m_nbParityBits;
s.packetLength = m_packetLength;
s.nbSymbols = m_nbSymbols;
s.nbCodewords = m_nbCodewords;
s.earlyEOM = m_earlyEOM;
s.headerParityStatus = m_headerParityStatus;
s.headerCRCStatus = m_headerCRCStatus;
s.payloadParityStatus = m_payloadParityStatus;
s.payloadCRCStatus = m_payloadCRCStatus;
return s;
};
auto captureLoRaState = [this](const QByteArray& bytes) -> LoRaDecodeState {
LoRaDecodeState s;
s.bytes = bytes;
s.hasCRC = m_hasCRC;
s.nbParityBits = m_nbParityBits;
s.packetLength = m_packetLength;
s.nbSymbols = m_nbSymbols;
s.nbCodewords = m_nbCodewords;
s.earlyEOM = m_earlyEOM;
s.headerParityStatus = m_headerParityStatus;
s.headerCRCStatus = m_headerCRCStatus;
s.payloadParityStatus = m_payloadParityStatus;
s.payloadCRCStatus = m_payloadCRCStatus;
return s;
};
auto restoreLoRaState = [this, &msgBytes](const LoRaDecodeState& s) {
msgBytes = s.bytes;
m_hasCRC = s.hasCRC;
m_nbParityBits = s.nbParityBits;
m_packetLength = s.packetLength;
m_nbSymbols = s.nbSymbols;
m_nbCodewords = s.nbCodewords;
m_earlyEOM = s.earlyEOM;
m_headerParityStatus = s.headerParityStatus;
m_headerCRCStatus = s.headerCRCStatus;
m_payloadParityStatus = s.payloadParityStatus;
m_payloadCRCStatus = s.payloadCRCStatus;
};
auto restoreLoRaState = [this, &msgBytes](const LoRaDecodeState& s) {
msgBytes = s.bytes;
m_hasCRC = s.hasCRC;
m_nbParityBits = s.nbParityBits;
m_packetLength = s.packetLength;
m_nbSymbols = s.nbSymbols;
m_nbCodewords = s.nbCodewords;
m_earlyEOM = s.earlyEOM;
m_headerParityStatus = s.headerParityStatus;
m_headerCRCStatus = s.headerCRCStatus;
m_payloadParityStatus = s.payloadParityStatus;
m_payloadCRCStatus = s.payloadCRCStatus;
};
if (canSoftDecode)
{
unsigned int headerNbSymbolBits;
if (m_hasHeader && (m_spreadFactor > 2U)) {
headerNbSymbolBits = m_spreadFactor - 2U;
} else {
headerNbSymbolBits = m_nbSymbolBits;
}
MeshtasticDemodDecoderLoRa::decodeBytesSoft(
msgBytes,
msgMags,
msg.getSymbols(),
m_spreadFactor,
m_loRaBandwidth,
m_nbSymbolBits,
headerNbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
MeshtasticDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
headerNbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
const LoRaDecodeState softState = captureLoRaState(msgBytes);
// Soft path is canonical for gr-lora_sdr, but if this approximation misses CRC
// on noisy captures, retry hard decode once and keep whichever path validates.
if (m_hasCRC && !m_payloadCRCStatus)
{
QByteArray hardBytes;
decodeSymbols(msg.getSymbols(), hardBytes); // hard path updates decoder state
const LoRaDecodeState hardState = captureLoRaState(hardBytes);
if (hardState.payloadCRCStatus) {
restoreLoRaState(hardState);
} else {
restoreLoRaState(softState);
}
}
}
else
{
decodeSymbols(msg.getSymbols(), msgBytes);
}
if (m_hasCRC && !m_payloadCRCStatus && (m_spreadFactor >= 5U))
{
const LoRaDecodeState baseState = captureLoRaState(msgBytes);
const unsigned int headerNbSymbolBits = (m_hasHeader && (m_spreadFactor > 2U))
? (m_spreadFactor - 2U)
: m_nbSymbolBits;
bool recovered = false;
for (int delta : {-1, 1})
{
std::vector<unsigned short> shifted = msg.getSymbols();
for (size_t i = 0; i < shifted.size(); i++)
{
const bool isHeader = m_hasHeader && (i < 8U);
const unsigned int bits = isHeader ? headerNbSymbolBits : m_nbSymbolBits;
const unsigned int mod = 1U << std::max(1U, bits);
const int s = static_cast<int>(shifted[i]);
const int v = (s + delta) % static_cast<int>(mod);
shifted[i] = static_cast<unsigned short>(v < 0 ? (v + static_cast<int>(mod)) : v);
}
QByteArray shiftedBytes;
decodeSymbols(shifted, shiftedBytes); // hard-path decode with adjusted symbol indices
const LoRaDecodeState shiftedState = captureLoRaState(shiftedBytes);
if (shiftedState.payloadCRCStatus)
{
restoreLoRaState(shiftedState);
recovered = true;
break;
}
}
if (!recovered) {
restoreLoRaState(baseState);
}
}
qDebug(
"MeshtasticDemodDecoder::handleMessage: decode symbols=%zu bytes=%lld earlyEOM=%d hCRC=%d pCRC=%d hParity=%d pParity=%d",
msg.getSymbols().size(),
static_cast<long long>(msgBytes.size()),
m_earlyEOM ? 1 : 0,
m_headerCRCStatus ? 1 : 0,
m_payloadCRCStatus ? 1 : 0,
m_headerParityStatus,
m_payloadParityStatus
);
if (m_outputMessageQueue)
{
qDebug(
"MeshtasticDemodDecoder::handleMessage: push report ts=%s bytes=%lld pCRC=%d",
qPrintable(msgTimestamp),
static_cast<long long>(msgBytes.size()),
m_payloadCRCStatus ? 1 : 0
);
MeshtasticDemodMsg::MsgReportDecodeBytes *outputMsg = MeshtasticDemodMsg::MsgReportDecodeBytes::create(msgBytes);
outputMsg->setFrameId(msg.getFrameId());
outputMsg->setSyncWord(msgSyncWord);
outputMsg->setSignalDb(msgSignalDb);
outputMsg->setNoiseDb(msgNoiseDb);
outputMsg->setMsgTimestamp(msgTimestamp);
outputMsg->setPacketSize(getPacketLength());
outputMsg->setNbParityBits(getNbParityBits());
outputMsg->setHasCRC(getHasCRC());
outputMsg->setNbSymbols(getNbSymbols());
outputMsg->setNbCodewords(getNbCodewords());
outputMsg->setEarlyEOM(getEarlyEOM());
outputMsg->setHeaderParityStatus(getHeaderParityStatus());
outputMsg->setHeaderCRCStatus(getHeaderCRCStatus());
outputMsg->setPayloadParityStatus(getPayloadParityStatus());
outputMsg->setPayloadCRCStatus(getPayloadCRCStatus());
outputMsg->setPipelineMetadata(m_pipelineId, m_pipelineName, m_pipelinePreset);
outputMsg->setDechirpedSpectrum(msg.getDechirpedSpectrum());
m_outputMessageQueue->push(outputMsg);
}
}
else if (m_codingScheme == MeshtasticDemodSettings::CodingFT)
if (canSoftDecode)
{
std::string fmsg, call1, call2, loc;
bool reply;
decodeSymbols(
msg.getMagnitudes(),
unsigned int headerNbSymbolBits;
if (m_hasHeader && (m_spreadFactor > 2U)) {
headerNbSymbolBits = m_spreadFactor - 2U;
} else {
headerNbSymbolBits = m_nbSymbolBits;
}
MeshtasticDemodDecoderLoRa::decodeBytesSoft(
msgBytes,
msgMags,
msg.getSymbols(),
m_spreadFactor,
m_loRaBandwidth,
m_nbSymbolBits,
fmsg,
call1,
call2,
loc,
reply
headerNbSymbolBits,
m_hasHeader,
m_hasCRC,
m_nbParityBits,
m_packetLength,
m_earlyEOM,
m_headerParityStatus,
m_headerCRCStatus,
m_payloadParityStatus,
m_payloadCRCStatus
);
if (m_outputMessageQueue)
MeshtasticDemodDecoderLoRa::getCodingMetrics(
m_nbSymbolBits,
headerNbSymbolBits,
m_nbParityBits,
m_packetLength,
m_hasHeader,
m_hasCRC,
m_nbSymbols,
m_nbCodewords
);
const LoRaDecodeState softState = captureLoRaState(msgBytes);
// Soft path is canonical for gr-lora_sdr, but if this approximation misses CRC
// on noisy captures, retry hard decode once and keep whichever path validates.
if (m_hasCRC && !m_payloadCRCStatus)
{
MeshtasticDemodMsg::MsgReportDecodeFT *outputMsg = MeshtasticDemodMsg::MsgReportDecodeFT::create();
outputMsg->setSyncWord(msgSyncWord);
outputMsg->setSignalDb(msgSignalDb);
outputMsg->setNoiseDb(msgNoiseDb);
outputMsg->setMsgTimestamp(msgTimestamp);
outputMsg->setMessage(QString(fmsg.c_str()));
outputMsg->setCall1(QString(call1.c_str()));
outputMsg->setCall2(QString(call2.c_str()));
outputMsg->setLoc(QString(loc.c_str()));
outputMsg->setReply(reply);
outputMsg->setPayloadParityStatus(getPayloadParityStatus());
outputMsg->setPayloadCRCStatus(getPayloadCRCStatus());
outputMsg->setPipelineMetadata(m_pipelineId, m_pipelineName, m_pipelinePreset);
m_outputMessageQueue->push(outputMsg);
QByteArray hardBytes;
decodeSymbols(msg.getSymbols(), hardBytes); // hard path updates decoder state
const LoRaDecodeState hardState = captureLoRaState(hardBytes);
if (hardState.payloadCRCStatus) {
restoreLoRaState(hardState);
} else {
restoreLoRaState(softState);
}
}
}
else
{
QString msgString;
decodeSymbols(msg.getSymbols(), msgString);
decodeSymbols(msg.getSymbols(), msgBytes);
}
if (m_outputMessageQueue)
if (m_hasCRC && !m_payloadCRCStatus && (m_spreadFactor >= 5U))
{
const LoRaDecodeState baseState = captureLoRaState(msgBytes);
const unsigned int headerNbSymbolBits = (m_hasHeader && (m_spreadFactor > 2U))
? (m_spreadFactor - 2U)
: m_nbSymbolBits;
bool recovered = false;
for (int delta : {-1, 1})
{
MeshtasticDemodMsg::MsgReportDecodeString *outputMsg = MeshtasticDemodMsg::MsgReportDecodeString::create(msgString);
outputMsg->setFrameId(msg.getFrameId());
outputMsg->setSyncWord(msgSyncWord);
outputMsg->setSignalDb(msgSignalDb);
outputMsg->setNoiseDb(msgNoiseDb);
outputMsg->setMsgTimestamp(msgTimestamp);
outputMsg->setPipelineMetadata(m_pipelineId, m_pipelineName, m_pipelinePreset);
m_outputMessageQueue->push(outputMsg);
std::vector<unsigned short> shifted = msg.getSymbols();
for (size_t i = 0; i < shifted.size(); i++)
{
const bool isHeader = m_hasHeader && (i < 8U);
const unsigned int bits = isHeader ? headerNbSymbolBits : m_nbSymbolBits;
const unsigned int mod = 1U << std::max(1U, bits);
const int s = static_cast<int>(shifted[i]);
const int v = (s + delta) % static_cast<int>(mod);
shifted[i] = static_cast<unsigned short>(v < 0 ? (v + static_cast<int>(mod)) : v);
}
QByteArray shiftedBytes;
decodeSymbols(shifted, shiftedBytes); // hard-path decode with adjusted symbol indices
const LoRaDecodeState shiftedState = captureLoRaState(shiftedBytes);
if (shiftedState.payloadCRCStatus)
{
restoreLoRaState(shiftedState);
recovered = true;
break;
}
}
if (!recovered) {
restoreLoRaState(baseState);
}
}
qDebug(
"MeshtasticDemodDecoder::handleMessage: decode symbols=%zu bytes=%lld earlyEOM=%d hCRC=%d pCRC=%d hParity=%d pParity=%d",
msg.getSymbols().size(),
static_cast<long long>(msgBytes.size()),
m_earlyEOM ? 1 : 0,
m_headerCRCStatus ? 1 : 0,
m_payloadCRCStatus ? 1 : 0,
m_headerParityStatus,
m_payloadParityStatus
);
if (m_outputMessageQueue)
{
qDebug(
"MeshtasticDemodDecoder::handleMessage: push report ts=%s bytes=%lld pCRC=%d",
qPrintable(msgTimestamp),
static_cast<long long>(msgBytes.size()),
m_payloadCRCStatus ? 1 : 0
);
MeshtasticDemodMsg::MsgReportDecodeBytes *outputMsg = MeshtasticDemodMsg::MsgReportDecodeBytes::create(msgBytes);
outputMsg->setFrameId(msg.getFrameId());
outputMsg->setSyncWord(msgSyncWord);
outputMsg->setSignalDb(msgSignalDb);
outputMsg->setNoiseDb(msgNoiseDb);
outputMsg->setMsgTimestamp(msgTimestamp);
outputMsg->setPacketSize(getPacketLength());
outputMsg->setNbParityBits(getNbParityBits());
outputMsg->setHasCRC(getHasCRC());
outputMsg->setNbSymbols(getNbSymbols());
outputMsg->setNbCodewords(getNbCodewords());
outputMsg->setEarlyEOM(getEarlyEOM());
outputMsg->setHeaderParityStatus(getHeaderParityStatus());
outputMsg->setHeaderCRCStatus(getHeaderCRCStatus());
outputMsg->setPayloadParityStatus(getPayloadParityStatus());
outputMsg->setPayloadCRCStatus(getPayloadCRCStatus());
outputMsg->setPipelineMetadata(m_pipelineId, m_pipelineName, m_pipelinePreset);
outputMsg->setDechirpedSpectrum(msg.getDechirpedSpectrum());
m_outputMessageQueue->push(outputMsg);
}
return true;

View File

@ -53,17 +53,7 @@ public:
private:
bool handleMessage(const Message& cmd);
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)
void decodeSymbols( //!< For FT coding scheme
const std::vector<std::vector<float>>& mags, // vector of symbols magnitudes
int nbSymbolBits, //!< number of bits per symbol
std::string& msg, //!< formatted message
std::string& call1, //!< 1st callsign or shorthand
std::string& call2, //!< 2nd callsign
std::string& loc, //!< locator, report or shorthand
bool& reply //!< true if message is a reply report
);
unsigned int getNbParityBits() const { return m_nbParityBits; }
unsigned int getPacketLength() const { return m_packetLength; }
bool getHasCRC() const { return m_hasCRC; }

View File

@ -1,32 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 "meshtasticdemoddecoderascii.h"
void MeshtasticDemodDecoderASCII::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

@ -1,32 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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_MESHTASTICDEMODDECODERASCII_H
#define INCLUDE_MESHTASTICDEMODDECODERASCII_H
#include <vector>
#include <QString>
class MeshtasticDemodDecoderASCII
{
public:
static void decodeSymbols(const std::vector<unsigned short>& symbols, QString& str);
};
#endif // INCLUDE_MESHTASTICDEMODDECODERASCII_H

View File

@ -1,187 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 "meshtasticdemodsettings.h"
#include "meshtasticdemoddecoderft.h"
#ifndef HAS_FT8
void MeshtasticDemodDecoderFT::decodeSymbols(
const std::vector<std::vector<float>>& mags, // vector of symbols magnitudes
int nbSymbolBits, //!< number of bits per symbol
QString& msg, //!< formatted message
QString& call1, //!< 1st callsign or shorthand
QString& call2, //!< 2nd callsign
QString& loc, //!< locator, report or shorthand
bool& reply //!< true if message is a reply report
)
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: not implemented");
}
#else
#include "ft8.h"
#include "packing.h"
void MeshtasticDemodDecoderFT::decodeSymbols(
const std::vector<std::vector<float>>& mags, // vector of symbols magnitudes
int nbSymbolBits, //!< number of bits per symbol
std::string& msg, //!< formatted message
std::string& call1, //!< 1st callsign or shorthand
std::string& call2, //!< 2nd callsign
std::string& loc, //!< locator, report or shorthand
bool& reply, //!< true if message is a reply report
int& payloadParityStatus,
bool& payloadCRCStatus
)
{
if (mags.size()*nbSymbolBits < 174)
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: insufficient number of symbols for FT payload");
return;
}
FT8::FT8Params params;
int r174[174];
std::string comments;
payloadParityStatus = (int) MeshtasticDemodSettings::ParityOK;
payloadCRCStatus = false;
std::vector<std::vector<float>> magsp = mags;
qDebug("MeshtasticDemodDecoderFT::decodeSymbols: try decode with symbol shift 0");
int res = decodeWithShift(params, magsp, nbSymbolBits, r174, comments);
if (res == 0)
{
std::vector<std::vector<float>> magsn = mags;
int shiftcount = 0;
while ((res == 0) && (shiftcount < 7))
{
qDebug("MeshtasticDemodDecoderFT::decodeSymbols: try decode with symbol shift %d", shiftcount + 1);
res = decodeWithShift(params, magsp, nbSymbolBits, r174, comments, 1);
if (res == 0)
{
qDebug("MeshtasticDemodDecoderFT::decodeSymbols: try decode with symbol shift -%d", shiftcount + 1);
res = decodeWithShift(params, magsn, nbSymbolBits, r174, comments, -1);
}
shiftcount++;
}
}
if (res == 0)
{
if (comments == "LDPC fail")
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: LDPC failed");
payloadParityStatus = (int) MeshtasticDemodSettings::ParityError;
}
else if (comments == "OSD fail")
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: OSD failed");
payloadParityStatus = (int) MeshtasticDemodSettings::ParityError;
}
else if (comments == "CRC fail")
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: CRC failed");
}
else
{
qWarning("MeshtasticDemodDecoderFT::decodeSymbols: decode failed for unknown reason");
payloadParityStatus = (int) MeshtasticDemodSettings::ParityUndefined;
}
return;
}
payloadCRCStatus = true;
FT8::Packing packing;
std::string msgType;
msg = packing.unpack(r174, call1, call2, loc, msgType);
reply = false;
if ((msgType == "0.3") || (msgType == "0.3")) {
reply = r174[56] != 0;
}
if ((msgType == "1") || (msgType == "2")) {
reply = r174[58] != 0;
}
if ((msgType == "3")) {
reply = r174[57] != 0;
}
if ((msgType == "5")) {
reply = r174[34] != 0;
}
}
int MeshtasticDemodDecoderFT::decodeWithShift(
FT8::FT8Params& params,
std::vector<std::vector<float>>& mags,
int nbSymbolBits,
int *r174,
std::string& comments,
int shift
)
{
if (shift > 0)
{
for (unsigned int si = 0; si < mags.size(); si++)
{
for (int bini = (1<<nbSymbolBits) - 1; bini > 0; bini--)
{
float x = mags[si][bini - 1];
mags[si][bini - 1] = mags[si][bini];
mags[si][bini] = x;
}
}
}
if (shift < 0)
{
for (unsigned int si = 0; si < mags.size(); si++)
{
for (int bini = 0; bini < (1<<nbSymbolBits) - 1; bini++)
{
float x = mags[si][bini + 1];
mags[si][bini + 1] = mags[si][bini];
mags[si][bini] = x;
}
}
}
float *lls = new float[mags.size()*nbSymbolBits]; // bits log likelihoods (>0 for 0, <0 for 1)
std::fill(lls, lls+mags.size()*nbSymbolBits, 0.0);
FT8::FT8::soft_decode_mags(params, mags, nbSymbolBits, lls);
deinterleave174(lls);
int ret = FT8::FT8::decode(lls, r174, params, 0, comments);
delete[] lls;
return ret;
}
void MeshtasticDemodDecoderFT::deinterleave174(float ll174[])
{
// 174 = 2*3*29
float t174[174];
std::copy(ll174, ll174+174, t174);
for (int i = 0; i < 174; i++) {
ll174[i] = t174[(i%6)*29 + (i%29)];
}
}
#endif // HAS_FT8

View File

@ -1,64 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2024 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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_MESHTASTICDEMODDECODERFT_H
#define INCLUDE_MESHTASTICDEMODDECODERFT_H
#include <vector>
#include <string>
namespace FT8 {
class FT8Params;
}
class MeshtasticDemodDecoderFT
{
public:
enum ParityStatus
{
ParityUndefined,
ParityError,
ParityCorrected,
ParityOK
};
static void decodeSymbols(
const std::vector<std::vector<float>>& mags, // vector of symbols magnitudes
int nbSymbolBits, //!< number of bits per symbol
std::string& msg, //!< formatted message
std::string& call1, //!< 1st callsign or shorthand
std::string& call2, //!< 2nd callsign
std::string& loc, //!< locator, report or shorthand
bool& reply, //!< true if message is a reply report
int& payloadParityStatus,
bool& payloadCRCStatus
);
private:
static int decodeWithShift(
FT8::FT8Params& params,
std::vector<std::vector<float>>& mags,
int nbSymbolBits,
int *r174,
std::string& comments,
int shift = 0
);
static void deinterleave174(float ll174[]);
};
#endif

View File

@ -1,67 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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 "meshtasticdemoddecodertty.h"
const char MeshtasticDemodDecoderTTY::ttyLetters[32] = {
'_', '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 MeshtasticDemodDecoderTTY::ttyFigures[32] = { // U.S. standard
'_', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', ' ', '.', '/', ';', ' '
};
void MeshtasticDemodDecoderTTY::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
{
signed char asciiChar = -1;
if (ttyState == TTYLetters) {
asciiChar = ttyLetters[(int) ttyChar];
} else if (ttyState == TTYFigures) {
asciiChar = ttyFigures[(int) ttyChar];
}
if (asciiChar >= 0) {
bytes.push_back(asciiChar);
}
}
}
str = QString(bytes.toStdString().c_str());
}

View File

@ -1,44 +0,0 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
// //
// 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_MESHTASTICDEMODDECODERTTY_H
#define INCLUDE_MESHTASTICDEMODDECODERTTY_H
#include <vector>
#include <QString>
class MeshtasticDemodDecoderTTY
{
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_MESHTASTICDEMODDECODERTTY_H

View File

@ -328,18 +328,6 @@ void MeshtasticDemodGUI::on_preambleChirps_valueChanged(int value)
applySettings();
}
void MeshtasticDemodGUI::on_scheme_currentIndexChanged(int index)
{
m_settings.m_codingScheme = (MeshtasticDemodSettings::CodingScheme) index;
if (m_settings.m_codingScheme != MeshtasticDemodSettings::CodingLoRa) {
resetLoRaStatus();
}
updateControlAvailabilityHints();
applySettings();
}
void MeshtasticDemodGUI::on_mute_toggled(bool checked)
{
m_settings.m_decodeActive = !checked;
@ -1495,7 +1483,6 @@ void MeshtasticDemodGUI::applyMeshtasticProfileFromSelection()
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->header->setChecked(m_settings.m_hasHeader);
ui->fecParity->setValue(m_settings.m_nbParityBits);
ui->fecParityText->setText(tr("%1").arg(m_settings.m_nbParityBits));
@ -1827,8 +1814,6 @@ MeshtasticDemodGUI::MeshtasticDemodGUI(PluginAPI* pluginAPI, DeviceUISet *device
ui->preambleChirps->setToolTip(tr("Expected LoRa preamble chirp count. Meshtastic profiles default to 17 (sub-GHz) or 12 (2.4 GHz)."));
ui->preambleChirpsLabel->setToolTip(tr("Expected LoRa preamble length in chirps."));
ui->preambleChirpsText->setToolTip(tr("Current preamble chirp value."));
ui->scheme->setToolTip(tr("Decoder mode. Use LoRa for Meshtastic traffic."));
ui->schemeLabel->setToolTip(tr("Select decoding scheme."));
ui->mute->setToolTip(tr("Disable decoder output."));
ui->clear->setToolTip(tr("Clear decoded message log."));
ui->eomSquelch->setToolTip(tr("End-of-message squelch threshold."));
@ -1996,7 +1981,6 @@ void MeshtasticDemodGUI::displaySettings()
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->udpSend->setChecked(m_settings.m_sendViaUDP);
@ -3483,7 +3467,6 @@ void MeshtasticDemodGUI::makeUIConnections()
QObject::connect(ui->deBits, &QSlider::valueChanged, this, &MeshtasticDemodGUI::on_deBits_valueChanged);
QObject::connect(ui->fftWindow, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MeshtasticDemodGUI::on_fftWindow_currentIndexChanged);
QObject::connect(ui->preambleChirps, &QSlider::valueChanged, this, &MeshtasticDemodGUI::on_preambleChirps_valueChanged);
QObject::connect(ui->scheme, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &MeshtasticDemodGUI::on_scheme_currentIndexChanged);
QObject::connect(ui->mute, &QToolButton::toggled, this, &MeshtasticDemodGUI::on_mute_toggled);
QObject::connect(ui->clear, &QPushButton::clicked, this, &MeshtasticDemodGUI::on_clear_clicked);
QObject::connect(ui->eomSquelch, &QDial::valueChanged, this, &MeshtasticDemodGUI::on_eomSquelch_valueChanged);

View File

@ -578,37 +578,6 @@
</property>
<item>
<layout class="QHBoxLayout" name="schemeLayout">
<item>
<widget class="QLabel" name="schemeLabel">
<property name="text">
<string>Scheme</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="scheme">
<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>
<item>
<property name="text">
<string>FT</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="mute">
<property name="toolTip">