mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-11-14 20:23:29 -05:00
LoRa demodulator: implement basic messaging
This commit is contained in:
parent
d4ede8457b
commit
733edb2cb2
@ -8,6 +8,7 @@ set(lora_SOURCES
|
||||
lorademodbaseband.cpp
|
||||
loraplugin.cpp
|
||||
lorademoddecoder.cpp
|
||||
lorademodmsg.cpp
|
||||
lorademodgui.ui
|
||||
)
|
||||
|
||||
@ -18,6 +19,7 @@ set(lora_HEADERS
|
||||
lorademodsink.h
|
||||
lorademodbaseband.h
|
||||
lorademoddecoder.h
|
||||
lorademodmsg.h
|
||||
loraplugin.h
|
||||
)
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// written by Christian Daniel //
|
||||
// (c) 2015 John Greb //
|
||||
// (c) 2020 Edouard Griffiths //
|
||||
// (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 //
|
||||
@ -27,21 +27,26 @@
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
|
||||
#include "lorademodmsg.h"
|
||||
#include "lorademod.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureLoRaDemod, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgReportDecodeBytes, Message)
|
||||
MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgReportDecodeString, Message)
|
||||
|
||||
const QString LoRaDemod::m_channelIdURI = "sdrangel.channel.lorademod";
|
||||
const QString LoRaDemod::m_channelId = "LoRaDemod";
|
||||
|
||||
LoRaDemod::LoRaDemod(DeviceAPI* deviceAPI) :
|
||||
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
|
||||
m_deviceAPI(deviceAPI)
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_basebandSampleRate(0)
|
||||
{
|
||||
setObjectName(m_channelId);
|
||||
|
||||
m_thread = new QThread(this);
|
||||
m_basebandSink = new LoRaDemodBaseband();
|
||||
m_basebandSink->setDecoderMessageQueue(getInputMessageQueue()); // Decoder held on the main thread
|
||||
m_basebandSink->moveToThread(m_thread);
|
||||
|
||||
applySettings(m_settings, true);
|
||||
@ -95,6 +100,42 @@ bool LoRaDemod::handleMessage(const Message& cmd)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (LoRaDemodMsg::MsgDecodeSymbols::match(cmd))
|
||||
{
|
||||
qDebug() << "LoRaDemod::handleMessage: MsgDecodeSymbols";
|
||||
LoRaDemodMsg::MsgDecodeSymbols& msg = (LoRaDemodMsg::MsgDecodeSymbols&) cmd;
|
||||
|
||||
if (m_settings.m_codingScheme == LoRaDemodSettings::CodingLoRa)
|
||||
{
|
||||
QByteArray payload;
|
||||
m_decoder.decodeSymbols(msg.getSymbols(), payload);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportDecodeBytes *msgToGUI = MsgReportDecodeBytes::create(payload);
|
||||
msgToGUI->setSyncWord(msg.getSyncWord());
|
||||
msgToGUI->setSignalDb(msg.getSingalDb());
|
||||
msgToGUI->setNoiseDb(msg.getNoiseDb());
|
||||
getMessageQueueToGUI()->push(msgToGUI);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QString payload;
|
||||
m_decoder.decodeSymbols(msg.getSymbols(), payload);
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportDecodeString *msgToGUI = MsgReportDecodeString::create(payload);
|
||||
msgToGUI->setSyncWord(msg.getSyncWord());
|
||||
msgToGUI->setSignalDb(msg.getSingalDb());
|
||||
msgToGUI->setNoiseDb(msg.getNoiseDb());
|
||||
getMessageQueueToGUI()->push(msgToGUI);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
@ -150,8 +191,22 @@ void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
|
||||
<< " m_title: " << settings.m_title
|
||||
<< " force: " << force;
|
||||
|
||||
if ((settings.m_spreadFactor != m_settings.m_spreadFactor)
|
||||
|| (settings.m_deBits != m_settings.m_deBits) || force) {
|
||||
m_decoder.setNbSymbolBits(settings.m_spreadFactor - settings.m_deBits);
|
||||
}
|
||||
|
||||
if ((settings.m_codingScheme != m_settings.m_codingScheme) || force) {
|
||||
m_decoder.setCodingScheme(settings.m_codingScheme);
|
||||
}
|
||||
|
||||
LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband *msg = LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband::create(settings, force);
|
||||
m_basebandSink->getInputMessageQueue()->push(msg);
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
bool LoRaDemod::getDemodActive() const
|
||||
{
|
||||
return m_basebandSink->getDemodActive();
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
|
||||
// (C) 2015 John Greb //
|
||||
// (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 //
|
||||
@ -26,6 +27,7 @@
|
||||
#include "util/message.h"
|
||||
|
||||
#include "lorademodbaseband.h"
|
||||
#include "lorademoddecoder.h"
|
||||
|
||||
class DeviceAPI;
|
||||
class QThread;
|
||||
@ -55,6 +57,81 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportDecodeBytes : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QByteArray& getBytes() const { return m_bytes; }
|
||||
unsigned int getSyncWord() const { return m_syncWord; }
|
||||
float getSingalDb() const { return m_signalDb; }
|
||||
float getNoiseDb() const { return m_noiseDb; }
|
||||
|
||||
static MsgReportDecodeBytes* create(const QByteArray& bytes) {
|
||||
return new MsgReportDecodeBytes(bytes);
|
||||
}
|
||||
void setSyncWord(unsigned int syncWord) {
|
||||
m_syncWord = syncWord;
|
||||
}
|
||||
void setSignalDb(float db) {
|
||||
m_signalDb = db;
|
||||
}
|
||||
void setNoiseDb(float db) {
|
||||
m_noiseDb = db;
|
||||
}
|
||||
|
||||
private:
|
||||
QByteArray m_bytes;
|
||||
unsigned int m_syncWord;
|
||||
float m_signalDb;
|
||||
float m_noiseDb;
|
||||
|
||||
MsgReportDecodeBytes(const QByteArray& bytes) :
|
||||
Message(),
|
||||
m_bytes(bytes),
|
||||
m_syncWord(0),
|
||||
m_signalDb(0.0),
|
||||
m_noiseDb(0.0)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportDecodeString : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QString& getString() const { return m_str; }
|
||||
unsigned int getSyncWord() const { return m_syncWord; }
|
||||
float getSingalDb() const { return m_signalDb; }
|
||||
float getNoiseDb() const { return m_noiseDb; }
|
||||
|
||||
static MsgReportDecodeString* create(const QString& str)
|
||||
{
|
||||
return new MsgReportDecodeString(str);
|
||||
}
|
||||
void setSyncWord(unsigned int syncWord) {
|
||||
m_syncWord = syncWord;
|
||||
}
|
||||
void setSignalDb(float db) {
|
||||
m_signalDb = db;
|
||||
}
|
||||
void setNoiseDb(float db) {
|
||||
m_noiseDb = db;
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_str;
|
||||
unsigned int m_syncWord;
|
||||
float m_signalDb;
|
||||
float m_noiseDb;
|
||||
|
||||
MsgReportDecodeString(const QString& str) :
|
||||
Message(),
|
||||
m_str(str),
|
||||
m_syncWord(0),
|
||||
m_signalDb(0.0),
|
||||
m_noiseDb(0.0)
|
||||
{ }
|
||||
};
|
||||
|
||||
LoRaDemod(DeviceAPI* deviceAPI);
|
||||
virtual ~LoRaDemod();
|
||||
virtual void destroy() { delete this; }
|
||||
@ -82,6 +159,8 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool getDemodActive() const;
|
||||
|
||||
static const QString m_channelIdURI;
|
||||
static const QString m_channelId;
|
||||
|
||||
@ -89,8 +168,9 @@ private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QThread *m_thread;
|
||||
LoRaDemodBaseband* m_basebandSink;
|
||||
LoRaDemodDecoder m_decoder;
|
||||
LoRaDemodSettings m_settings;
|
||||
int m_basebandSampleRate;
|
||||
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
|
||||
|
||||
void applySettings(const LoRaDemodSettings& settings, bool force = false);
|
||||
};
|
||||
|
||||
@ -62,7 +62,9 @@ public:
|
||||
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
|
||||
int getChannelSampleRate() const;
|
||||
bool getDemodActive() const { return m_sink.getDemodActive(); }
|
||||
void setBasebandSampleRate(int sampleRate);
|
||||
void setDecoderMessageQueue(MessageQueue *messageQueue) { m_sink.setDecoderMessageQueue(messageQueue); }
|
||||
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_sink.setSpectrumSink(spectrumSink); }
|
||||
|
||||
private:
|
||||
|
||||
@ -99,7 +99,7 @@ void LoRaDemodDecoder::decodeSymbolsTTY(const std::vector<unsigned int>& symbols
|
||||
|
||||
if (ttyState == TTYLetters) {
|
||||
asciiChar = ttyLetters[ttyChar];
|
||||
} else if (ttyState == TTYLetters) {
|
||||
} else if (ttyState == TTYFigures) {
|
||||
asciiChar = ttyFigures[ttyChar];
|
||||
}
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "device/deviceuiset.h"
|
||||
#include <QDockWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QScrollBar>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ui_lorademodgui.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
@ -27,6 +27,7 @@
|
||||
#include "gui/glspectrumgui.h"
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "lorademod.h"
|
||||
#include "lorademodgui.h"
|
||||
@ -105,6 +106,23 @@ bool LoRaDemodGUI::handleMessage(const Message& message)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (LoRaDemod::MsgReportDecodeBytes::match(message))
|
||||
{
|
||||
const LoRaDemod::MsgReportDecodeBytes& msg = (LoRaDemod::MsgReportDecodeBytes&) message;
|
||||
QByteArray bytes = msg.getBytes();
|
||||
ui->hexText->setText(bytes.toHex());
|
||||
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
|
||||
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
|
||||
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
|
||||
}
|
||||
else if (LoRaDemod::MsgReportDecodeString::match(message))
|
||||
{
|
||||
const LoRaDemod::MsgReportDecodeString& msg = (LoRaDemod::MsgReportDecodeString&) message;
|
||||
addText(msg.getString());
|
||||
ui->syncWord->setText((tr("%1").arg(msg.getSyncWord(), 2, 16)));
|
||||
ui->sText->setText(tr("%1").arg(msg.getSingalDb(), 0, 'f', 1));
|
||||
ui->snrText->setText(tr("%1").arg(msg.getSingalDb() - msg.getNoiseDb(), 0, 'f', 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -177,6 +195,38 @@ void LoRaDemodGUI::on_deBits_valueChanged(int value)
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::on_scheme_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_codingScheme = (LoRaDemodSettings::CodingScheme) index;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::on_mute_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_decodeActive = !checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::on_clear_clicked(bool checked)
|
||||
{
|
||||
(void) checked;
|
||||
ui->messageText->clear();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::on_eomSquelch_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_eomSquelchTenths = value;
|
||||
displaySquelch();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::on_messageLength_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_nbSymbolsMax = value;
|
||||
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
@ -190,7 +240,8 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_channelMarker(this),
|
||||
m_basebandSampleRate(250000),
|
||||
m_doApplySettings(true)
|
||||
m_doApplySettings(true),
|
||||
m_tickCount(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
@ -201,13 +252,19 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
|
||||
m_LoRaDemod->setSpectrumSink(m_spectrumVis);
|
||||
m_LoRaDemod->setMessageQueueToGUI(getInputMessageQueue());
|
||||
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
|
||||
|
||||
ui->glSpectrum->setDisplayWaterfall(true);
|
||||
ui->glSpectrum->setDisplayMaxHold(true);
|
||||
|
||||
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
|
||||
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
|
||||
|
||||
ui->messageText->setReadOnly(true);
|
||||
ui->syncWord->setReadOnly(true);
|
||||
ui->hexText->setReadOnly(true);
|
||||
|
||||
m_channelMarker.setMovable(true);
|
||||
m_channelMarker.setVisible(true);
|
||||
|
||||
@ -276,10 +333,25 @@ void LoRaDemodGUI::displaySettings()
|
||||
ui->SpreadText->setText(tr("%1").arg(m_settings.m_spreadFactor));
|
||||
ui->deBits->setValue(m_settings.m_deBits);
|
||||
ui->deBitsText->setText(tr("%1").arg(m_settings.m_deBits));
|
||||
ui->scheme->setCurrentIndex((int) m_settings.m_codingScheme);
|
||||
ui->messageLengthText->setText(tr("%1").arg(m_settings.m_nbSymbolsMax));
|
||||
ui->messageLength->setValue(m_settings.m_nbSymbolsMax);
|
||||
ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
|
||||
displaySquelch();
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::displaySquelch()
|
||||
{
|
||||
ui->eomSquelch->setValue(m_settings.m_eomSquelchTenths);
|
||||
|
||||
if (m_settings.m_eomSquelchTenths == ui->eomSquelch->maximum()) {
|
||||
ui->eomSquelchText->setText("---");
|
||||
} else {
|
||||
ui->eomSquelchText->setText(tr("%1").arg(m_settings.m_eomSquelchTenths / 10.0, 0, 'f', 1));
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::setBandwidths()
|
||||
{
|
||||
int maxBandwidth = m_basebandSampleRate/LoRaDemodSettings::oversampling;
|
||||
@ -296,3 +368,35 @@ void LoRaDemodGUI::setBandwidths()
|
||||
ui->BWText->setText(QString("%1 Hz").arg(LoRaDemodSettings::bandwidths[index]));
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaDemodGUI::addText(const QString& text)
|
||||
{
|
||||
QDateTime dt = QDateTime::currentDateTime();
|
||||
QString dateStr = dt.toString("HH:mm:ss");
|
||||
QTextCursor cursor = ui->messageText->textCursor();
|
||||
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
|
||||
if (!ui->messageText->document()->isEmpty()) {
|
||||
cursor.insertText("\n");
|
||||
}
|
||||
cursor.insertText(tr("%1 %2").arg(dateStr).arg(text));
|
||||
ui->messageText->verticalScrollBar()->setValue(ui->messageText->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
|
||||
void LoRaDemodGUI::tick()
|
||||
{
|
||||
if (m_tickCount < 10)
|
||||
{
|
||||
m_tickCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tickCount = 0;
|
||||
|
||||
if (m_LoRaDemod->getDemodActive()) {
|
||||
ui->mute->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else {
|
||||
ui->mute->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,9 +59,15 @@ private slots:
|
||||
void on_BW_valueChanged(int value);
|
||||
void on_Spread_valueChanged(int value);
|
||||
void on_deBits_valueChanged(int value);
|
||||
void on_scheme_currentIndexChanged(int index);
|
||||
void on_mute_toggled(bool checked);
|
||||
void on_clear_clicked(bool checked);
|
||||
void on_eomSquelch_valueChanged(int value);
|
||||
void on_messageLength_valueChanged(int value);
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void channelMarkerHighlightedByCursor();
|
||||
void handleInputMessages();
|
||||
void tick();
|
||||
|
||||
private:
|
||||
Ui::LoRaDemodGUI* ui;
|
||||
@ -75,6 +81,7 @@ private:
|
||||
LoRaDemod* m_LoRaDemod;
|
||||
SpectrumVis* m_spectrumVis;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
unsigned int m_tickCount;
|
||||
|
||||
explicit LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~LoRaDemodGUI();
|
||||
@ -82,7 +89,9 @@ private:
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
void displaySquelch();
|
||||
void setBandwidths();
|
||||
void addText(const QString& text);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_LoRaDEMODGUI_H
|
||||
|
||||
@ -6,14 +6,14 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>350</width>
|
||||
<height>500</height>
|
||||
<width>410</width>
|
||||
<height>620</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>500</height>
|
||||
<width>380</width>
|
||||
<height>620</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -30,14 +30,14 @@
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>20</y>
|
||||
<width>331</width>
|
||||
<height>112</height>
|
||||
<width>390</width>
|
||||
<height>102</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>112</height>
|
||||
<height>102</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -74,7 +74,7 @@
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>50</y>
|
||||
<width>180</width>
|
||||
<width>251</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -102,7 +102,7 @@
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>70</y>
|
||||
<width>100</width>
|
||||
<width>131</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -131,7 +131,7 @@
|
||||
<widget class="QLabel" name="SpreadText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>140</x>
|
||||
<x>170</x>
|
||||
<y>70</y>
|
||||
<width>30</width>
|
||||
<height>16</height>
|
||||
@ -153,7 +153,7 @@
|
||||
<widget class="QLabel" name="BWText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>240</x>
|
||||
<x>300</x>
|
||||
<y>50</y>
|
||||
<width>80</width>
|
||||
<height>16</height>
|
||||
@ -175,7 +175,7 @@
|
||||
<widget class="QLabel" name="deBitsLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>190</x>
|
||||
<x>220</x>
|
||||
<y>70</y>
|
||||
<width>22</width>
|
||||
<height>16</height>
|
||||
@ -188,7 +188,7 @@
|
||||
<widget class="QLabel" name="deBitsText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>290</x>
|
||||
<x>350</x>
|
||||
<y>70</y>
|
||||
<width>30</width>
|
||||
<height>16</height>
|
||||
@ -210,14 +210,14 @@
|
||||
<widget class="QSlider" name="deBits">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>220</x>
|
||||
<x>250</x>
|
||||
<y>70</y>
|
||||
<width>60</width>
|
||||
<width>91</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Low data rate optimize (DE) bits</string>
|
||||
<string>Distance Enhancement bits i.e. log2 of number of FFT bins per effective sample</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
@ -243,7 +243,7 @@
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>311</width>
|
||||
<width>371</width>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -319,18 +319,378 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sLabel">
|
||||
<property name="text">
|
||||
<string>S</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>De-chirped signal level</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-50.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="snrLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SNR</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="snrText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>25</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>De-chirped SNR level</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sUnits">
|
||||
<property name="text">
|
||||
<string>dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="payloadContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>130</y>
|
||||
<width>390</width>
|
||||
<height>160</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>160</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Payload</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>2</x>
|
||||
<y>40</y>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Msg</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="hexText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>120</y>
|
||||
<width>351</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="hexLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>2</x>
|
||||
<y>120</y>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Hex</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPlainTextEdit" name="messageText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>30</x>
|
||||
<y>40</y>
|
||||
<width>351</width>
|
||||
<height>75</height>
|
||||
</rect>
|
||||
</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="QPushButton" name="clear">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>4</x>
|
||||
<y>60</y>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Clear text</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/sweep.png</normaloff>:/sweep.png</iconset>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QToolButton" name="mute">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>150</x>
|
||||
<y>8</y>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Run/Stop decoder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/stop.png</normaloff>
|
||||
<normalon>:/play.png</normalon>:/stop.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="eomSquelchLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>180</x>
|
||||
<y>10</y>
|
||||
<width>35</width>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>EOM</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QDial" name="eomSquelch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>210</x>
|
||||
<y>8</y>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>End Of Message squelch factor</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>40</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>121</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>60</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="eomSquelchText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>230</x>
|
||||
<y>10</y>
|
||||
<width>28</width>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>10.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="messageLengthLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>272</x>
|
||||
<y>10</y>
|
||||
<width>20</width>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ML</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QDial" name="messageLength">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>290</x>
|
||||
<y>8</y>
|
||||
<width>22</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Message (payload) length in number of symbols</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="messageLengthText">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>310</x>
|
||||
<y>10</y>
|
||||
<width>25</width>
|
||||
<height>19</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>255</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="syncWord">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>350</x>
|
||||
<y>9</y>
|
||||
<width>25</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sync word (1 byte hex)</string>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string>HH</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>00</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="spectrumContainer" native="true">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>140</y>
|
||||
<width>331</width>
|
||||
<height>341</height>
|
||||
<y>300</y>
|
||||
<width>390</width>
|
||||
<height>310</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>373</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>De-chirped Spectrum</string>
|
||||
</property>
|
||||
|
||||
20
plugins/channelrx/demodlora/lorademodmsg.cpp
Normal file
20
plugins/channelrx/demodlora/lorademodmsg.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 "lorademodmsg.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(LoRaDemodMsg::MsgDecodeSymbols, Message)
|
||||
79
plugins/channelrx/demodlora/lorademodmsg.h
Normal file
79
plugins/channelrx/demodlora/lorademodmsg.h
Normal file
@ -0,0 +1,79 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_LORADEMODMSG_H
|
||||
#define INCLUDE_LORADEMODMSG_H
|
||||
|
||||
#include <QObject>
|
||||
#include "util/message.h"
|
||||
|
||||
namespace LoRaDemodMsg
|
||||
{
|
||||
class MsgDecodeSymbols : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const std::vector<unsigned int>& getSymbols() const { return m_symbols; }
|
||||
unsigned int getSyncWord() const { return m_syncWord; }
|
||||
float getSingalDb() const { return m_signalDb; }
|
||||
float getNoiseDb() const { return m_noiseDb; }
|
||||
|
||||
void pushBackSymbol(unsigned int symbol) {
|
||||
m_symbols.push_back(symbol);
|
||||
}
|
||||
void popSymbol() {
|
||||
m_symbols.pop_back();
|
||||
}
|
||||
void setSyncWord(unsigned char syncWord) {
|
||||
m_syncWord = syncWord;
|
||||
}
|
||||
void setSignalDb(float db) {
|
||||
m_signalDb = db;
|
||||
}
|
||||
void setNoiseDb(float db) {
|
||||
m_noiseDb = db;
|
||||
}
|
||||
|
||||
static MsgDecodeSymbols* create() {
|
||||
return new MsgDecodeSymbols();
|
||||
}
|
||||
static MsgDecodeSymbols* create(const std::vector<unsigned int> symbols) {
|
||||
return new MsgDecodeSymbols(symbols);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<unsigned int> m_symbols;
|
||||
unsigned int m_syncWord;
|
||||
float m_signalDb;
|
||||
float m_noiseDb;
|
||||
|
||||
MsgDecodeSymbols() : //!< create an empty message
|
||||
Message(),
|
||||
m_syncWord(0),
|
||||
m_signalDb(0.0),
|
||||
m_noiseDb(0.0)
|
||||
{}
|
||||
MsgDecodeSymbols(const std::vector<unsigned int> symbols) : //!< create a message with symbols copy
|
||||
Message(),
|
||||
m_syncWord(0),
|
||||
m_signalDb(0.0),
|
||||
m_noiseDb(0.0)
|
||||
{ m_symbols = symbols; }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // INCLUDE_LORADEMODMSG_H
|
||||
@ -41,6 +41,10 @@ void LoRaDemodSettings::resetToDefaults()
|
||||
m_bandwidthIndex = 5;
|
||||
m_spreadFactor = 7;
|
||||
m_deBits = 0;
|
||||
m_codingScheme = CodingLoRa;
|
||||
m_decodeActive = true;
|
||||
m_eomSquelchTenths = 60;
|
||||
m_nbSymbolsMax = 255;
|
||||
m_rgbColor = QColor(255, 0, 255).rgb();
|
||||
m_title = "LoRa Demodulator";
|
||||
}
|
||||
@ -62,6 +66,10 @@ QByteArray LoRaDemodSettings::serialize() const
|
||||
|
||||
s.writeString(6, m_title);
|
||||
s.writeS32(7, m_deBits);
|
||||
s.writeS32(8, m_codingScheme);
|
||||
s.writeBool(9, m_decodeActive);
|
||||
s.writeS32(10, m_eomSquelchTenths);
|
||||
s.writeS32(11, m_nbSymbolsMax);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@ -79,6 +87,7 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
|
||||
if(d.getVersion() == 1)
|
||||
{
|
||||
QByteArray bytetmp;
|
||||
int tmp;
|
||||
|
||||
d.readS32(1, &m_inputFrequencyOffset, 0);
|
||||
d.readS32(2, &m_bandwidthIndex, 0);
|
||||
@ -96,6 +105,11 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
|
||||
|
||||
d.readString(6, &m_title, "LoRa Demodulator");
|
||||
d.readS32(7, &m_deBits, 0);
|
||||
d.readS32(8, &tmp);
|
||||
m_codingScheme = (CodingScheme) tmp;
|
||||
d.readBool(9, &m_decodeActive, true);
|
||||
d.readS32(10, &m_eomSquelchTenths, 60);
|
||||
d.readS32(11, &m_nbSymbolsMax, 255);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ struct LoRaDemodSettings
|
||||
{
|
||||
enum CodingScheme
|
||||
{
|
||||
CodingTTY, //!< plain TTY (5 bits)
|
||||
CodingLoRa, //!< Standard LoRa
|
||||
CodingASCII, //!< plain ASCII (7 bits)
|
||||
CodingLoRa //!< Standard LoRa
|
||||
CodingTTY //!< plain TTY (5 bits)
|
||||
};
|
||||
|
||||
int m_inputFrequencyOffset;
|
||||
@ -40,6 +40,9 @@ struct LoRaDemodSettings
|
||||
int m_spreadFactor;
|
||||
int m_deBits; //!< Low data rate optmize (DE) bits
|
||||
CodingScheme m_codingScheme;
|
||||
bool m_decodeActive;
|
||||
int m_eomSquelchTenths; //!< Squelch factor to trigger end of message (/10)
|
||||
int m_nbSymbolsMax; //!< Maximum number of symbols in a payload
|
||||
uint32_t m_rgbColor;
|
||||
QString m_title;
|
||||
|
||||
|
||||
@ -22,10 +22,14 @@
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "dsp/basebandsamplesink.h"
|
||||
#include "dsp/fftengine.h"
|
||||
#include "util/db.h"
|
||||
|
||||
#include "lorademodmsg.h"
|
||||
#include "lorademodsink.h"
|
||||
|
||||
LoRaDemodSink::LoRaDemodSink() :
|
||||
m_decodeMsg(nullptr),
|
||||
m_decoderMsgQueue(nullptr),
|
||||
m_spectrumSink(nullptr),
|
||||
m_spectrumBuffer(nullptr),
|
||||
m_downChirps(nullptr),
|
||||
@ -33,6 +37,7 @@ LoRaDemodSink::LoRaDemodSink() :
|
||||
m_fftBuffer(nullptr),
|
||||
m_spectrumLine(nullptr)
|
||||
{
|
||||
m_demodActive = false;
|
||||
m_bandwidth = LoRaDemodSettings::bandwidths[0];
|
||||
m_channelSampleRate = 96000;
|
||||
m_channelFrequencyOffset = 0;
|
||||
@ -134,6 +139,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
{
|
||||
if (m_state == LoRaStateReset) // start over
|
||||
{
|
||||
m_demodActive = false;
|
||||
reset();
|
||||
m_state = LoRaStateDetectPreamble;
|
||||
}
|
||||
@ -147,21 +153,22 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_fftInterpolation*m_fftLength, Complex{0.0, 0.0});
|
||||
m_fft->transform();
|
||||
m_fftCounter = 0;
|
||||
double magsq;
|
||||
|
||||
unsigned int imax = argmax(
|
||||
m_fft->out(),
|
||||
m_fftInterpolation,
|
||||
m_fftLength,
|
||||
m_magsq,
|
||||
magsq,
|
||||
m_spectrumBuffer,
|
||||
m_fftInterpolation
|
||||
) / m_fftInterpolation;
|
||||
|
||||
// Debug:
|
||||
// if (m_spectrumSink) {
|
||||
// m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
// }
|
||||
if (m_magsqQueue.size() > m_requiredPreambleChirps + 1) {
|
||||
m_magsqQueue.pop();
|
||||
}
|
||||
|
||||
m_magsqQueue.push(magsq);
|
||||
m_argMaxHistory[m_argMaxHistoryCounter++] = imax;
|
||||
|
||||
if (m_argMaxHistoryCounter == m_requiredPreambleChirps)
|
||||
@ -178,19 +185,23 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
}
|
||||
}
|
||||
|
||||
if ((preambleFound) && (m_magsq > 1e-9))
|
||||
if ((preambleFound) && (magsq > 1e-9))
|
||||
{
|
||||
if (m_spectrumSink) {
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
}
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], m_magsq);
|
||||
qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], magsq);
|
||||
m_chirp = m_argMaxHistory[0];
|
||||
m_fftCounter = m_chirp;
|
||||
m_chirp0 = 0;
|
||||
m_chirpCount = 0;
|
||||
m_state = LoRaStatePreambleResyc;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_magsqOffAvg(m_magsqQueue.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,6 +216,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
}
|
||||
|
||||
m_fftCounter = 0;
|
||||
m_demodActive = true;
|
||||
m_state = LoRaStatePreamble;
|
||||
}
|
||||
}
|
||||
@ -281,7 +293,6 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
m_fftCounter = m_fftLength - m_sfdSkip + zadj;
|
||||
m_chirp += zadj;
|
||||
//std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft
|
||||
m_magsq = magsqSFD;
|
||||
m_state = LoRaStateSkipSFD; //LoRaStateSlideSFD;
|
||||
}
|
||||
}
|
||||
@ -296,7 +307,7 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
}
|
||||
|
||||
qDebug("LoRaDemodSink::processSample: SFD search: up: %4u|%11.6f - down: %4u|%11.6f", imax, magsq, imaxSFD, magsqSFD);
|
||||
m_magsq = magsq;
|
||||
m_magsqOnAvg(magsq);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,12 +322,15 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
|
||||
if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips left
|
||||
{
|
||||
qDebug("LoRaDemodSink::processSample: SFD skipped");
|
||||
m_chirp = m_chirp0;
|
||||
m_fftCounter = 0;
|
||||
m_chirpCount = 0;
|
||||
int correction = 0;
|
||||
qDebug("LoRaDemodSink::processSample: SFD skipped");
|
||||
m_state = LoRaStateReadPayload; //LoRaStateReadPayload;
|
||||
m_magsqMax = 0.0;
|
||||
m_decodeMsg = LoRaDemodMsg::MsgDecodeSymbols::create();
|
||||
m_decodeMsg->setSyncWord(m_syncWord);
|
||||
m_state = LoRaStateReadPayload;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -353,11 +367,14 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
|
||||
if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips length
|
||||
{
|
||||
qDebug("LoRaDemodSink::processSample: SFD done");
|
||||
m_chirp = m_chirp0;
|
||||
m_fftCounter = 0;
|
||||
m_chirpCount = 0;
|
||||
int correction = 0;
|
||||
qDebug("LoRaDemodSink::processSample: SFD done");
|
||||
m_magsqMax = 0.0;
|
||||
m_decodeMsg = LoRaDemodMsg::MsgDecodeSymbols::create();
|
||||
m_decodeMsg->setSyncWord(m_syncWord);
|
||||
m_state = LoRaStateReadPayload; //LoRaStateReadPayload;
|
||||
}
|
||||
}
|
||||
@ -390,22 +407,47 @@ void LoRaDemodSink::processSample(const Complex& ci)
|
||||
m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
|
||||
}
|
||||
|
||||
if ((m_chirpCount == 0) || (10.0*magsq > m_magsq))
|
||||
if (magsq > m_magsqMax) {
|
||||
m_magsqMax = magsq;
|
||||
}
|
||||
|
||||
m_decodeMsg->pushBackSymbol(symbol);
|
||||
|
||||
if ((m_chirpCount == 0)
|
||||
|| (m_settings.m_eomSquelchTenths == 121) // max - disable squelch
|
||||
|| ((m_settings.m_eomSquelchTenths*magsq)/10.0 > m_magsqMax))
|
||||
{
|
||||
qDebug("LoRaDemodSink::processSample: symbol %02u: %4u|%11.6f", m_chirpCount, symbol, magsq);
|
||||
m_magsq = magsq;
|
||||
m_magsqOnAvg(magsq);
|
||||
m_chirpCount++;
|
||||
|
||||
if (m_chirpCount > 255)
|
||||
if (m_chirpCount > m_settings.m_nbSymbolsMax)
|
||||
{
|
||||
qDebug("LoRaDemodSink::processSample: message length exceeded");
|
||||
m_state = LoRaStateReset;
|
||||
m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
|
||||
m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
|
||||
|
||||
if (m_decoderMsgQueue && m_settings.m_decodeActive) {
|
||||
m_decoderMsgQueue->push(m_decodeMsg);
|
||||
} else {
|
||||
delete m_decodeMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("LoRaDemodSink::processSample: end of message");
|
||||
m_state = LoRaStateReset;
|
||||
m_decodeMsg->popSymbol(); // last symbol is garbage
|
||||
m_decodeMsg->setSignalDb(CalcDb::dbPower(m_magsqOnAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
|
||||
m_decodeMsg->setNoiseDb(CalcDb::dbPower(m_magsqOffAvg.asDouble() / (1<<m_settings.m_spreadFactor)));
|
||||
|
||||
if (m_decoderMsgQueue && m_settings.m_decodeActive) {
|
||||
m_decoderMsgQueue->push(m_decodeMsg);
|
||||
} else {
|
||||
delete m_decodeMsg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,17 +19,23 @@
|
||||
#define INCLUDE_LORADEMODSINK_H
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "dsp/channelsamplesink.h"
|
||||
#include "dsp/nco.h"
|
||||
#include "dsp/interpolator.h"
|
||||
#include "util/message.h"
|
||||
#include "dsp/fftwindow.h"
|
||||
#include "util/message.h"
|
||||
#include "util/movingaverage.h"
|
||||
|
||||
#include "lorademodsettings.h"
|
||||
|
||||
class BasebandSampleSink;
|
||||
class FFTEngine;
|
||||
namespace LoRaDemodMsg {
|
||||
class MsgDecodeSymbols;
|
||||
}
|
||||
class MessageQueue;
|
||||
|
||||
class LoRaDemodSink : public ChannelSampleSink {
|
||||
public:
|
||||
@ -38,6 +44,8 @@ public:
|
||||
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
|
||||
|
||||
bool getDemodActive() const { return m_demodActive; }
|
||||
void setDecoderMessageQueue(MessageQueue *messageQueue) { m_decoderMsgQueue = messageQueue; }
|
||||
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
|
||||
void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false);
|
||||
void applySettings(const LoRaDemodSettings& settings, bool force = false);
|
||||
@ -57,6 +65,9 @@ private:
|
||||
|
||||
LoRaDemodSettings m_settings;
|
||||
LoRaState m_state;
|
||||
bool m_demodActive;
|
||||
LoRaDemodMsg::MsgDecodeSymbols *m_decodeMsg;
|
||||
MessageQueue *m_decoderMsgQueue;
|
||||
int m_bandwidth;
|
||||
int m_channelSampleRate;
|
||||
int m_channelFrequencyOffset;
|
||||
@ -80,7 +91,10 @@ private:
|
||||
unsigned int m_argMaxHistoryCounter;
|
||||
unsigned int m_preambleHistory[m_maxSFDSearchChirps];
|
||||
unsigned int m_syncWord;
|
||||
double m_magsq;
|
||||
double m_magsqMax;
|
||||
MovingAverageUtil<double, double, 10> m_magsqOnAvg;
|
||||
MovingAverageUtil<double, double, 10> m_magsqOffAvg;
|
||||
std::queue<double> m_magsqQueue;
|
||||
unsigned int m_chirpCount; //!< Generic chirp counter
|
||||
unsigned int m_sfdSkip; //!< Number of samples in a SFD skip or slide (1/4) period
|
||||
unsigned int m_sfdSkipCounter; //!< Counter of skip or slide periods
|
||||
|
||||
@ -1,3 +1,20 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019-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 <QtPlugin>
|
||||
#include "plugin/pluginapi.h"
|
||||
|
||||
@ -17,7 +34,7 @@ const PluginDescriptor LoRaPlugin::m_pluginDescriptor = {
|
||||
|
||||
LoRaPlugin::LoRaPlugin(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_pluginAPI(0)
|
||||
m_pluginAPI(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,20 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2019-2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program is distributed in the hope that it will be useful, //
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
|
||||
// GNU General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_LoRaPLUGIN_H
|
||||
#define INCLUDE_LoRaPLUGIN_H
|
||||
|
||||
@ -13,7 +30,7 @@ class LoRaPlugin : public QObject, PluginInterface {
|
||||
Q_PLUGIN_METADATA(IID "sdrangel.channel.lorademod")
|
||||
|
||||
public:
|
||||
explicit LoRaPlugin(QObject* parent = NULL);
|
||||
explicit LoRaPlugin(QObject* parent = nullptr);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user