diff --git a/plugins/channelrx/demodlora/lorademod.cpp b/plugins/channelrx/demodlora/lorademod.cpp
index 4952dff64..7fd9e7b05 100644
--- a/plugins/channelrx/demodlora/lorademod.cpp
+++ b/plugins/channelrx/demodlora/lorademod.cpp
@@ -1,7 +1,8 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany //
// written by Christian Daniel //
-// (c) 2015 John Greb
+// (c) 2015 John Greb //
+// (c) 2020 Edouard Griffiths //
// //
// 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 //
@@ -100,9 +101,15 @@ bool LoRaDemod::handleMessage(const Message& cmd)
m_basebandSampleRate = notif.getSampleRate();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
- qDebug() << "LoRaDemod::handleMessage: DSPSignalNotification";
+ qDebug() << "LoRaDemod::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << m_basebandSampleRate;
m_basebandSink->getInputMessageQueue()->push(rep);
+ if (getMessageQueueToGUI())
+ {
+ DSPSignalNotification* repToGUI = new DSPSignalNotification(notif); // make a copy
+ getMessageQueueToGUI()->push(repToGUI);
+ }
+
return true;
}
else
@@ -136,7 +143,7 @@ bool LoRaDemod::deserialize(const QByteArray& data)
void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force)
{
qDebug() << "LoRaDemod::applySettings:"
- << " m_centerFrequency: " << settings.m_centerFrequency
+ << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spreadFactor: " << settings.m_spreadFactor
<< " m_rgbColor: " << settings.m_rgbColor
diff --git a/plugins/channelrx/demodlora/lorademodbaseband.cpp b/plugins/channelrx/demodlora/lorademodbaseband.cpp
index a7003fc81..894a14ea3 100644
--- a/plugins/channelrx/demodlora/lorademodbaseband.cpp
+++ b/plugins/channelrx/demodlora/lorademodbaseband.cpp
@@ -134,11 +134,11 @@ bool LoRaDemodBaseband::handleMessage(const Message& cmd)
void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool force)
{
if ((settings.m_bandwidthIndex != m_settings.m_bandwidthIndex)
- || (settings.m_centerFrequency != m_settings.m_centerFrequency) || force)
+ || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force)
{
m_channelizer->setChannelization(
LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex],
- settings.m_centerFrequency
+ settings.m_inputFrequencyOffset
);
m_sink.applyChannelSettings(
m_channelizer->getChannelSampleRate(),
diff --git a/plugins/channelrx/demodlora/lorademodgui.cpp b/plugins/channelrx/demodlora/lorademodgui.cpp
index 7f0873e1f..b3677a544 100644
--- a/plugins/channelrx/demodlora/lorademodgui.cpp
+++ b/plugins/channelrx/demodlora/lorademodgui.cpp
@@ -1,3 +1,19 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
#include "device/deviceuiset.h"
#include
@@ -5,10 +21,12 @@
#include "ui_lorademodgui.h"
#include "dsp/spectrumvis.h"
+#include "dsp/dspengine.h"
+#include "dsp/dspcommands.h"
#include "gui/glspectrum.h"
+#include "gui/glspectrumgui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
-#include "dsp/dspengine.h"
#include "lorademod.h"
#include "lorademodgui.h"
@@ -58,11 +76,14 @@ QByteArray LoRaDemodGUI::serialize() const
bool LoRaDemodGUI::deserialize(const QByteArray& data)
{
- if(m_settings.deserialize(data)) {
+ if (m_settings.deserialize(data))
+ {
displaySettings();
applySettings(true);
return true;
- } else {
+ }
+ else
+ {
resetToDefaults();
return false;
}
@@ -70,28 +91,72 @@ bool LoRaDemodGUI::deserialize(const QByteArray& data)
bool LoRaDemodGUI::handleMessage(const Message& message)
{
- (void) message;
- return false;
+ if (DSPSignalNotification::match(message))
+ {
+ DSPSignalNotification& notif = (DSPSignalNotification&) message;
+ int basebandSampleRate = notif.getSampleRate();
+ qDebug() << "LoRaDemodGUI::handleMessage: DSPSignalNotification: m_basebandSampleRate: " << basebandSampleRate;
+
+ if (basebandSampleRate != m_basebandSampleRate)
+ {
+ m_basebandSampleRate = basebandSampleRate;
+ setBandwidths();
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
-void LoRaDemodGUI::viewChanged()
+void LoRaDemodGUI::handleInputMessages()
{
+ Message* message;
+
+ while ((message = getInputMessageQueue()->pop()) != 0)
+ {
+ if (handleMessage(*message)) {
+ delete message;
+ }
+ }
+}
+
+void LoRaDemodGUI::channelMarkerChangedByCursor()
+{
+ ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
+ m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
+void LoRaDemodGUI::on_deltaFrequency_changed(qint64 value)
+{
+ m_channelMarker.setCenterFrequency(value);
+ m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
+ applySettings();
+}
+
+void LoRaDemodGUI::channelMarkerHighlightedByCursor()
+{
+ setHighlighted(m_channelMarker.getHighlighted());
+}
+
void LoRaDemodGUI::on_BW_valueChanged(int value)
{
if (value < 0) {
m_settings.m_bandwidthIndex = 0;
- } else if (value < LoRaDemodSettings::nb_bandwidths) {
+ } else if (value < LoRaDemodSettings::nbBandwidths) {
m_settings.m_bandwidthIndex = value;
} else {
- m_settings.m_bandwidthIndex = LoRaDemodSettings::nb_bandwidths - 1;
+ m_settings.m_bandwidthIndex = LoRaDemodSettings::nbBandwidths - 1;
}
int thisBW = LoRaDemodSettings::bandwidths[value];
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
m_channelMarker.setBandwidth(thisBW);
+ ui->glSpectrum->setSampleRate(thisBW);
+ ui->glSpectrum->setCenterFrequency(thisBW/2);
applySettings();
}
@@ -100,13 +165,18 @@ void LoRaDemodGUI::on_Spread_valueChanged(int value)
{
m_settings.m_spreadFactor = value;
ui->SpreadText->setText(tr("%1").arg(value));
- int spectrumRate = 1 << m_settings.m_spreadFactor;
- ui->glSpectrum->setSampleRate(spectrumRate);
- ui->glSpectrum->setCenterFrequency(spectrumRate/2);
+ ui->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
applySettings();
}
+void LoRaDemodGUI::on_deBits_valueChanged(int value)
+{
+ m_settings.m_deBits = value;
+ ui->deBitsText->setText(tr("%1").arg(m_settings.m_deBits));
+ applySettings();
+}
+
void LoRaDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
@@ -119,6 +189,7 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
+ m_basebandSampleRate(250000),
m_doApplySettings(true)
{
ui->setupUi(this);
@@ -128,17 +199,20 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
m_spectrumVis = new SpectrumVis(SDR_RX_SCALEF, ui->glSpectrum);
m_LoRaDemod = (LoRaDemod*) rxChannel; //new LoRaDemod(m_deviceUISet->m_deviceSourceAPI);
m_LoRaDemod->setSpectrumSink(m_spectrumVis);
+ m_LoRaDemod->setMessageQueueToGUI(getInputMessageQueue());
- int spectrumRate = 1 << m_settings.m_spreadFactor;
- ui->glSpectrum->setSampleRate(spectrumRate);
- ui->glSpectrum->setCenterFrequency(spectrumRate/2);
ui->glSpectrum->setDisplayWaterfall(true);
ui->glSpectrum->setDisplayMaxHold(true);
- m_channelMarker.setMovable(false);
+ ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
+ ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
+ ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
+
+ m_channelMarker.setMovable(true);
m_channelMarker.setVisible(true);
- connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(viewChanged()));
+ connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
+ connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
m_deviceUISet->registerRxChannelInstance(LoRaDemod::m_channelIdURI, this);
m_deviceUISet->addChannelMarker(&m_channelMarker);
@@ -149,6 +223,9 @@ LoRaDemodGUI::LoRaDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setSpectrumGUI(ui->spectrumGUI);
+ connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
+
+ setBandwidths();
displaySettings();
applySettings(true);
}
@@ -181,15 +258,41 @@ void LoRaDemodGUI::displaySettings()
int thisBW = LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex];
m_channelMarker.blockSignals(true);
+ m_channelMarker.setTitle(m_settings.m_title);
+ m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setBandwidth(thisBW);
- m_channelMarker.setCenterFrequency(0);
+ m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor);
setTitleColor(m_settings.m_rgbColor);
- m_channelMarker.blockSignals(false);
+
+ ui->glSpectrum->setSampleRate(thisBW);
+ ui->glSpectrum->setCenterFrequency(thisBW/2);
blockApplySettings(true);
+ ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->BWText->setText(QString("%1 Hz").arg(thisBW));
ui->BW->setValue(m_settings.m_bandwidthIndex);
+ ui->Spread->setValue(m_settings.m_spreadFactor);
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->spectrumGUI->setFFTSize(m_settings.m_spreadFactor);
blockApplySettings(false);
}
+
+void LoRaDemodGUI::setBandwidths()
+{
+ int maxBandwidth = m_basebandSampleRate;
+ int maxIndex = 0;
+
+ for (; (maxIndex < LoRaDemodSettings::nbBandwidths) && (LoRaDemodSettings::bandwidths[maxIndex] <= maxBandwidth); maxIndex++)
+ {}
+
+ if (maxIndex != 0)
+ {
+ qDebug("LoRaDemodGUI::setBandwidths: avl: %d max: %d", maxBandwidth, LoRaDemodSettings::bandwidths[maxIndex-1]);
+ ui->BW->setMaximum(maxIndex - 1);
+ int index = ui->BW->value();
+ ui->BWText->setText(QString("%1 Hz").arg(LoRaDemodSettings::bandwidths[index]));
+ }
+}
\ No newline at end of file
diff --git a/plugins/channelrx/demodlora/lorademodgui.h b/plugins/channelrx/demodlora/lorademodgui.h
index f86751d93..2de112fea 100644
--- a/plugins/channelrx/demodlora/lorademodgui.h
+++ b/plugins/channelrx/demodlora/lorademodgui.h
@@ -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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
#ifndef INCLUDE_LoRaDEMODGUI_H
#define INCLUDE_LoRaDEMODGUI_H
@@ -37,10 +54,14 @@ public:
virtual bool handleMessage(const Message& message);
private slots:
- void viewChanged();
+ void channelMarkerChangedByCursor();
+ void on_deltaFrequency_changed(qint64 value);
void on_BW_valueChanged(int value);
void on_Spread_valueChanged(int value);
+ void on_deBits_valueChanged(int value);
void onWidgetRolled(QWidget* widget, bool rollDown);
+ void channelMarkerHighlightedByCursor();
+ void handleInputMessages();
private:
Ui::LoRaDemodGUI* ui;
@@ -48,6 +69,7 @@ private:
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
LoRaDemodSettings m_settings;
+ int m_basebandSampleRate;
bool m_doApplySettings;
LoRaDemod* m_LoRaDemod;
@@ -60,6 +82,7 @@ private:
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
+ void setBandwidths();
};
#endif // INCLUDE_LoRaDEMODGUI_H
diff --git a/plugins/channelrx/demodlora/lorademodgui.ui b/plugins/channelrx/demodlora/lorademodgui.ui
index d871aae03..df24eef7a 100644
--- a/plugins/channelrx/demodlora/lorademodgui.ui
+++ b/plugins/channelrx/demodlora/lorademodgui.ui
@@ -7,13 +7,13 @@
0
0
350
- 550
+ 500
350
- 550
+ 500
@@ -29,124 +29,298 @@
10
- 35
+ 20
331
- 96
+ 112
-
- Settings
+
+
+ 0
+ 112
+
-
-
+
+ RF/demod Settings
+
+
+
+
+ 2
+ 50
+ 22
+ 16
+
+
+
+ BW
+
+
+
+
+
+ 2
+ 70
+ 22
+ 16
+
+
+
+ SF
+
+
+
+
+
+ 30
+ 50
+ 180
+ 16
+
+
+
+ Bandwidth
+
+
+ 0
+
+
+ 10
+
+
+ 1
+
+
+ 5
+
+
+ Qt::Horizontal
+
+
+
+
+
+ 30
+ 70
+ 100
+ 16
+
+
+
+ Spreading factor
+
+
+ 7
+
+
+ 12
+
+
+ 1
+
+
+ 10
+
+
+ 10
+
+
+ Qt::Horizontal
+
+
+
+
+
+ 140
+ 70
+ 30
+ 16
+
+
+
+
+ 30
+ 0
+
+
+
+ 10
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ 240
+ 50
+ 80
+ 16
+
+
+
+
+ 80
+ 0
+
+
+
+ 7813 Hz
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ 190
+ 70
+ 22
+ 16
+
+
+
+ DE
+
+
+
+
+
+ 290
+ 70
+ 30
+ 16
+
+
+
+
+ 30
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ 220
+ 70
+ 60
+ 16
+
+
+
+ Low data rate optimize (DE) bits
+
+
+ 0
+
+
2
-
- 2
+
+ 1
-
- 2
+
+ 0
-
- 2
+
+ 0
-
- 3
+
+ Qt::Horizontal
- -
-
-
- BW
-
-
-
- -
-
-
- SF
-
-
-
- -
-
-
- Bandwidth
-
-
- 0
-
-
- 6
-
-
- 1
-
-
- 0
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Spreading factor
-
-
- 5
-
-
- 12
-
-
- 1
-
-
- 7
-
-
- 7
-
-
- Qt::Horizontal
-
-
-
- -
-
-
-
- 50
- 0
-
-
-
- 5
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
-
- 50
- 0
-
-
-
- 7813 Hz
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
-
+
+
+
+
+ 10
+ 10
+ 311
+ 26
+
+
+
+ -
+
+
+
+ 16
+ 0
+
+
+
+ Df
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 16
+
+
+
+
+ Liberation Mono
+ 12
+
+
+
+ PointingHandCursor
+
+
+ Qt::StrongFocus
+
+
+ Demod shift frequency from center in Hz
+
+
+
+ -
+
+
+ Hz
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
@@ -154,11 +328,11 @@
10
140
331
- 391
+ 341
- Channel Spectrum
+ De-chirped Spectrum
@@ -181,7 +355,7 @@
0
- 350
+ 300
@@ -211,7 +385,15 @@
1
+
+ ValueDialZ
+ QWidget
+
+ 1
+
-
+
+
+
diff --git a/plugins/channelrx/demodlora/lorademodsettings.cpp b/plugins/channelrx/demodlora/lorademodsettings.cpp
index 5368b1cd8..97b1067c4 100644
--- a/plugins/channelrx/demodlora/lorademodsettings.cpp
+++ b/plugins/channelrx/demodlora/lorademodsettings.cpp
@@ -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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
#include "lorademodsettings.h"
#include
@@ -7,11 +24,11 @@
#include "settings/serializable.h"
#include "lorademodsettings.h"
-const int LoRaDemodSettings::bandwidths[] = {7813, 15625, 31250, 62500, 125000, 250000, 50000};
-const int LoRaDemodSettings::nb_bandwidths = 7;
+const int LoRaDemodSettings::bandwidths[] = {7813, 10417, 15625, 20833, 31250, 41667, 62500, 125000, 250000, 500000};
+const int LoRaDemodSettings::nbBandwidths = 10;
LoRaDemodSettings::LoRaDemodSettings() :
- m_centerFrequency(0),
+ m_inputFrequencyOffset(0),
m_channelMarker(0),
m_spectrumGUI(0)
{
@@ -20,8 +37,9 @@ LoRaDemodSettings::LoRaDemodSettings() :
void LoRaDemodSettings::resetToDefaults()
{
- m_bandwidthIndex = 0;
- m_spreadFactor = 0;
+ m_bandwidthIndex = 5;
+ m_spreadFactor = 7;
+ m_deBits = 0;
m_rgbColor = QColor(255, 0, 255).rgb();
m_title = "LoRa Demodulator";
}
@@ -29,7 +47,7 @@ void LoRaDemodSettings::resetToDefaults()
QByteArray LoRaDemodSettings::serialize() const
{
SimpleSerializer s(1);
- s.writeS32(1, m_centerFrequency);
+ s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_bandwidthIndex);
s.writeS32(3, m_spreadFactor);
@@ -42,6 +60,7 @@ QByteArray LoRaDemodSettings::serialize() const
}
s.writeString(6, m_title);
+ s.writeS32(7, m_deBits);
return s.final();
}
@@ -60,7 +79,7 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
{
QByteArray bytetmp;
- d.readS32(1, &m_centerFrequency, 0);
+ d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_bandwidthIndex, 0);
d.readS32(3, &m_spreadFactor, 0);
@@ -75,6 +94,7 @@ bool LoRaDemodSettings::deserialize(const QByteArray& data)
}
d.readString(6, &m_title, "LoRa Demodulator");
+ d.readS32(7, &m_deBits, 0);
return true;
}
diff --git a/plugins/channelrx/demodlora/lorademodsettings.h b/plugins/channelrx/demodlora/lorademodsettings.h
index 8f2f8908c..f864dc3fe 100644
--- a/plugins/channelrx/demodlora/lorademodsettings.h
+++ b/plugins/channelrx/demodlora/lorademodsettings.h
@@ -1,4 +1,21 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
#ifndef PLUGINS_CHANNELRX_DEMODLORA_LORADEMODSETTINGS_H_
#define PLUGINS_CHANNELRX_DEMODLORA_LORADEMODSETTINGS_H_
@@ -11,9 +28,10 @@ class Serializable;
struct LoRaDemodSettings
{
- int m_centerFrequency;
+ int m_inputFrequencyOffset;
int m_bandwidthIndex;
int m_spreadFactor;
+ int m_deBits; //!< Low data rate optmize (DE) bits
uint32_t m_rgbColor;
QString m_title;
@@ -21,7 +39,7 @@ struct LoRaDemodSettings
Serializable *m_spectrumGUI;
static const int bandwidths[];
- static const int nb_bandwidths;
+ static const int nbBandwidths;
LoRaDemodSettings();
void resetToDefaults();
diff --git a/plugins/channelrx/demodlora/lorademodsink.cpp b/plugins/channelrx/demodlora/lorademodsink.cpp
index 003b61324..7b2eb0a12 100644
--- a/plugins/channelrx/demodlora/lorademodsink.cpp
+++ b/plugins/channelrx/demodlora/lorademodsink.cpp
@@ -21,200 +21,83 @@
#include "dsp/dsptypes.h"
#include "dsp/basebandsamplesink.h"
+#include "dsp/fftengine.h"
#include "lorademodsink.h"
-const int LoRaDemodSink::DATA_BITS = 6;
-const int LoRaDemodSink::SAMPLEBITS = LoRaDemodSink::DATA_BITS + 2;
-const int LoRaDemodSink::LORA_SQUELCH = 3;
-
LoRaDemodSink::LoRaDemodSink() :
- m_spectrumSink(nullptr)
+ m_spectrumSink(nullptr),
+ m_spectrumBuffer(nullptr),
+ m_downChirps(nullptr),
+ m_upChirps(nullptr),
+ m_fftBuffer(nullptr)
{
- m_Bandwidth = LoRaDemodSettings::bandwidths[0];
+ m_bandwidth = LoRaDemodSettings::bandwidths[0];
m_channelSampleRate = 96000;
m_channelFrequencyOffset = 0;
m_nco.setFreq(m_channelFrequencyOffset, m_channelSampleRate);
- m_interpolator.create(16, m_channelSampleRate, m_Bandwidth/1.9);
- m_sampleDistanceRemain = (Real) m_channelSampleRate / m_Bandwidth;
+ m_interpolator.create(16, m_channelSampleRate, m_bandwidth / 1.9f);
+ m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_bandwidth;
+ m_sampleDistanceRemain = 0;
+ m_state = LoRaStateReset;
m_chirp = 0;
- m_angle = 0;
- m_bin = 0;
- m_result = 0;
- m_count = 0;
- m_header = 0;
- m_time = 0;
- m_tune = 0;
+ m_chirp0 = 0;
- m_nbSymbols = 1 << m_settings.m_spreadFactor;
- m_sfftLength = m_nbSymbols / 2;
+ m_fft = FFTEngine::create();
+ m_fftSFD = FFTEngine::create();
- m_loraFilter = new sfft(m_sfftLength);
- m_negaFilter = new sfft(m_sfftLength);
- m_mov = new float[4*m_sfftLength];
- m_mag = new float[m_sfftLength];
- m_rev = new float[m_sfftLength];
- m_history = new short[1024];
- m_finetune = new short[16];
+ initSF(m_settings.m_spreadFactor);
}
LoRaDemodSink::~LoRaDemodSink()
{
- delete m_loraFilter;
- delete m_negaFilter;
- delete [] m_mov;
- delete [] m_history;
- delete [] m_finetune;
+ delete m_fft;
+ delete m_fftSFD;
+ delete[] m_downChirps;
+ delete[] m_upChirps;
+ delete[] m_spectrumBuffer;
}
-void LoRaDemodSink::dumpRaw()
+void LoRaDemodSink::initSF(unsigned int sf)
{
- short bin, j, max;
- char text[256];
+ if (m_downChirps) {
+ delete[] m_downChirps;
+ }
+ if (m_upChirps) {
+ delete[] m_upChirps;
+ }
+ if (m_fftBuffer) {
+ delete[] m_fftBuffer;
+ }
+ if (m_spectrumBuffer) {
+ delete[] m_spectrumBuffer;
+ }
- max = m_time / 4 - 3;
+ m_nbSymbols = 1 << sf;
+ m_fftLength = m_nbSymbols;
+ m_fft->configure(m_fftInterpolation*m_fftLength, false);
+ m_fftSFD->configure(m_fftInterpolation*m_fftLength, false);
+ m_state = LoRaStateReset;
+ m_sfdSkip = m_fftLength / 4;
+ m_fftWindow.create(FFTWindow::Function::Kaiser, m_fftLength);
+ m_fftWindow.setKaiserAlpha(M_PI);
+ m_downChirps = new Complex[2*m_nbSymbols]; // Each table is 2 chirps long to allow memcpying from arbitrary offsets.
+ m_upChirps = new Complex[2*m_nbSymbols];
+ m_fftBuffer = new Complex[m_fftInterpolation*m_fftLength];
+ m_spectrumBuffer = new Complex[m_nbSymbols];
- if (max > 140) {
- max = 140; // about 2 symbols to each char
- }
+ float halfAngle = M_PI;
+ float phase = -halfAngle;
+ double accumulator = 0;
- for ( j=0; j < max; j++)
- {
- bin = (m_history[(j + 1) * 4] + m_tune ) % m_sfftLength;
- text[j] = toGray(bin >> 1);
- }
-
- prng6(text, max);
- // First block is always 8 symbols
- interleave6(text, 6);
- interleave6(&text[8], max);
- hamming6(text, 6);
- hamming6(&text[8], max);
-
- for ( j=0; j < max / 2; j++)
- {
- text[j] = (text[j * 2 + 1] << 4) | (0xf & text[j * 2 + 0]);
-
- if ((text[j] < 32 )||( text[j] > 126)) {
- text[j] = 0x5f;
- }
- }
-
- text[3] = text[2];
- text[2] = text[1];
- text[1] = text[0];
- text[j] = 0;
-
- qDebug("LoRaDemodSink::dumpRaw: %s", &text[1]);
-}
-
-short LoRaDemodSink::synch(short bin)
-{
- short i, j;
-
- if (bin < 0)
- {
- if (m_time > 70) {
- dumpRaw();
- }
-
- m_time = 0;
- return -1;
- }
-
- m_history[m_time] = bin;
-
- if (m_time > 12)
- {
- if (bin == m_history[m_time - 6])
- {
- if (bin == m_history[m_time - 12])
- {
- m_tune = m_sfftLength - bin;
- j = 0;
-
- for (i=0; i<12; i++) {
- j += m_finetune[15 & (m_time - i)];
- }
-
- if (j < 0) {
- m_tune += 1;
- }
-
- m_tune %= m_sfftLength;
- m_time = 0;
- return -1;
- }
- }
- }
-
- m_time++;
- m_time &= 1023;
-
- if (m_time & 3) {
- return -1;
- }
-
- return (bin + m_tune) % m_sfftLength;
-}
-
-int LoRaDemodSink::detect(Complex c, Complex a)
-{
- int p, q;
- short i, result, negresult, movpoint;
- float peak, negpeak, tfloat;
-
- m_loraFilter->run(c * a);
- m_negaFilter->run(c * conj(a));
-
- // process spectrum twice in FFTLEN
- if (++m_count & ((1 << DATA_BITS) - 1)) {
- return m_result;
- }
-
- movpoint = 3 & (m_count >> DATA_BITS);
-
- m_loraFilter->fetch(m_mag);
- m_negaFilter->fetch(m_rev);
- peak = negpeak = 0.0f;
- result = negresult = 0;
-
- for (i = 0; i < m_sfftLength; i++)
- {
- if (m_rev[i] > negpeak)
- {
- negpeak = m_rev[i];
- negresult = i;
- }
-
- tfloat = m_mov[i] + m_mov[m_sfftLength + i] +m_mov[2 * m_sfftLength + i]
- + m_mov[3 * m_sfftLength + i] + m_mag[i];
-
- if (tfloat > peak)
- {
- peak = tfloat;
- result = i;
- }
-
- m_mov[movpoint * m_sfftLength + i] = m_mag[i];
- }
-
- p = (result - 1 + m_sfftLength) % m_sfftLength;
- q = (result + 1) % m_sfftLength;
- m_finetune[15 & m_time] = (m_mag[p] > m_mag[q]) ? -1 : 1;
-
- if (peak < negpeak * LORA_SQUELCH) {
- result = -1;
- }
-
- result = synch(result);
-
- if (result >= 0) {
- m_result = result;
- }
-
- return m_result;
+ for (int i = 0; i < 2*m_nbSymbols; i++)
+ {
+ accumulator = fmod(accumulator + phase, 2*M_PI);
+ m_downChirps[i] = Complex(std::conj(std::polar(1.0, accumulator)));
+ m_upChirps[i] = Complex(std::polar(1.0, accumulator));
+ phase += (2*halfAngle) / m_nbSymbols;
+ }
}
void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
@@ -222,8 +105,6 @@ void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const Sample
int newangle;
Complex ci;
- m_sampleBuffer.clear();
-
for (SampleVector::const_iterator it = begin; it < end; ++it)
{
Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF);
@@ -231,31 +112,331 @@ void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const Sample
if (m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci))
{
- m_angle = (m_angle + m_chirp) % m_nbSymbols;
- Complex upRamp(cos(M_PI*2*m_angle/m_nbSymbols), sin(M_PI*2*m_angle/m_nbSymbols));
- Complex dechirpUp = ci * conj(upRamp); // de-chirp the up ramp to get peamble and data
- Complex dechirpDown = ci * upRamp; // de-chirp the down ramp to get sync
- m_sampleBuffer.push_back(Sample(dechirpUp.real() * SDR_RX_SCALEF, dechirpUp.imag() * SDR_RX_SCALEF));
-
- // Bullshit...
- // Complex cangle(cos(M_PI*2*m_angle/m_nbSymbols),-sin(M_PI*2*m_angle/m_nbSymbols));
- // newangle = detect(ci, cangle);
- // m_bin = (m_bin + newangle) % m_sfftLength;
- // Complex nangle(cos(M_PI*2*m_bin/m_sfftLength),sin(M_PI*2*m_bin/m_sfftLength));
- // m_sampleBuffer.push_back(Sample(nangle.real() * 16384, nangle.imag() * 16384));
-
- m_sampleDistanceRemain += (Real) m_channelSampleRate / m_Bandwidth;
- m_chirp++;
-
- if (m_chirp >= m_nbSymbols) {
- m_chirp = 0;
- }
+ processSample(ci);
+ m_sampleDistanceRemain += m_interpolatorDistance;
}
}
+}
- if (m_spectrumSink) {
- m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false);
- }
+void LoRaDemodSink::processSample(const Complex& ci)
+{
+ if (m_state == LoRaStateReset) // start over
+ {
+ reset();
+ m_state = LoRaStateDetectPreamble;
+ }
+ else if (m_state == LoRaStateDetectPreamble) // look for preamble
+ {
+ m_fft->in()[m_fftCounter++] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
+
+ if (m_fftCounter == m_fftLength)
+ {
+ m_fftWindow.apply(m_fft->in());
+ 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;
+
+ unsigned int imax = argmax(
+ m_fft->out(),
+ m_fftInterpolation,
+ m_fftLength,
+ m_magsq,
+ m_spectrumBuffer,
+ m_fftInterpolation
+ ) / m_fftInterpolation;
+
+ // Debug:
+ // if (m_spectrumSink) {
+ // m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
+ // }
+
+ m_argMaxHistory[m_argMaxHistoryCounter++] = imax;
+
+ if (m_argMaxHistoryCounter == m_requiredPreambleChirps)
+ {
+ m_argMaxHistoryCounter = 0;
+ bool preambleFound = true;
+
+ for (int i = 1; i < m_requiredPreambleChirps; i++)
+ {
+ if (m_argMaxHistory[0] != m_argMaxHistory[i])
+ {
+ preambleFound = false;
+ break;
+ }
+ }
+
+ if (preambleFound)
+ {
+ if (m_spectrumSink) {
+ m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
+ }
+
+ qDebug("LoRaDemodSink::processSample: preamble found: %u|%f", m_argMaxHistory[0], m_magsq);
+ m_chirp0 = m_argMaxHistory[0];
+ m_chirp = m_chirp0;
+ m_fftCounter = 0;
+ m_chirpCount = 0;
+ m_state = LoRaStatePreamble;
+ }
+ }
+ }
+ }
+ else if (m_state == LoRaStatePreamble) // preamble found look for SFD start
+ {
+ m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
+ m_fftSFD->in()[m_fftCounter] = ci * m_upChirps[m_chirp]; // de-chiro the down ramp
+ m_fftCounter++;
+
+ if (m_fftCounter == m_fftLength)
+ {
+ std::copy(m_fftSFD->in(), m_fftSFD->in() + m_fftLength, m_fftBuffer); // save for later
+
+ m_fftWindow.apply(m_fft->in());
+ std::fill(m_fft->in()+m_fftLength, m_fft->in()+m_fftInterpolation*m_fftLength, Complex{0.0, 0.0});
+ m_fft->transform();
+
+ m_fftWindow.apply(m_fftSFD->in());
+ std::fill(m_fftSFD->in()+m_fftLength, m_fftSFD->in()+m_fftInterpolation*m_fftLength, Complex{0.0, 0.0});
+ m_fftSFD->transform();
+
+ m_fftCounter = 0;
+ double magsq, magsqSFD;
+
+ unsigned int imaxSFD = argmax(
+ m_fftSFD->out(),
+ m_fftInterpolation,
+ m_fftLength,
+ magsqSFD,
+ nullptr,
+ m_fftInterpolation
+ ) / m_fftInterpolation;
+
+ unsigned int imax = argmax(
+ m_fft->out(),
+ m_fftInterpolation,
+ m_fftLength,
+ magsq,
+ m_spectrumBuffer,
+ m_fftInterpolation
+ ) / m_fftInterpolation;
+
+ m_preambleHistory[m_chirpCount] = imax;
+ m_chirpCount++;
+
+ if (magsq < magsqSFD) // preamble drop
+ {
+ if (m_chirpCount < 3) // too early
+ {
+ m_state = LoRaStateReset;
+ }
+ else
+ {
+ m_syncWord = round(m_preambleHistory[m_chirpCount-2] / 8.0);
+ m_syncWord += 16 * round(m_preambleHistory[m_chirpCount-3] / 8.0);
+ qDebug("LoRaDemodSink::processSample: SFD found: up: %u|%f down: %u|%f sync: %x", imax, magsq, imaxSFD, magsqSFD, m_syncWord);
+ m_sfdSkipCounter = 0;
+ m_fftCounter = m_fftLength - m_sfdSkip;
+ std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare sliding fft
+ m_state = LoRaStateSlideSFD;
+ m_magsq = magsqSFD;
+ }
+ }
+ else if (m_chirpCount > m_maxSFDSearchChirps) // SFD missed start over
+ {
+ m_state = LoRaStateReset;
+ }
+ else
+ {
+ if (m_spectrumSink) {
+ m_spectrumSink->feed(m_spectrumBuffer, m_nbSymbols);
+ }
+
+ qDebug("LoRaDemodSink::processSample: SFD search: up: %u|%f down: %u|%f", imax, magsq, imaxSFD, magsqSFD);
+ m_magsq = magsq;
+ }
+ }
+ }
+ else if (m_state == LoRaStateSkipSFD) // Just skip SFD
+ {
+ m_fftCounter++;
+
+ if (m_fftCounter == m_fftLength)
+ {
+ m_fftCounter = m_fftLength - m_sfdSkip;
+ m_sfdSkipCounter++;
+
+ if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips left
+ {
+ m_chirp = m_chirp0;
+ m_fftCounter = 0;
+ m_chirpCount = 0;
+ int correction = 0;
+ qDebug("LoRaDemodSink::processSample: SFD skipped");
+ m_state = LoRaStateReadPayload; //LoRaStateReadPayload;
+ }
+ }
+ }
+ else if (m_state == LoRaStateSlideSFD) // perform sliding FFTs over the rest of the SFD period
+ {
+ m_fftBuffer[m_fftCounter] = ci * m_upChirps[m_chirp]; // de-chirp the down ramp
+ m_fftCounter++;
+
+ if (m_fftCounter == m_fftLength)
+ {
+ std::copy(m_fftBuffer, m_fftBuffer + m_fftLength, m_fftSFD->in());
+ std::fill(m_fftSFD->in()+m_fftLength, m_fftSFD->in()+m_fftInterpolation*m_fftLength, Complex{0.0, 0.0});
+ m_fftSFD->transform();
+ std::copy(m_fftBuffer+m_sfdSkip, m_fftBuffer+(m_fftLength-m_sfdSkip), m_fftBuffer); // prepare next sliding fft
+ m_fftCounter = m_fftLength - m_sfdSkip;
+ m_sfdSkipCounter++;
+
+ double magsqSFD;
+
+ unsigned int imaxSFD = argmax(
+ m_fftSFD->out(),
+ m_fftInterpolation,
+ m_fftLength,
+ magsqSFD,
+ m_spectrumBuffer,
+ m_fftInterpolation
+ ) / m_fftInterpolation;
+
+ qDebug("LoRaDemodSink::processSample: SFD slide %u %u|%f", m_sfdSkipCounter, imaxSFD, magsqSFD);
+
+ if (m_sfdSkipCounter == m_sfdFourths) // 1.25 SFD chips length
+ {
+ m_chirp = m_chirp0;
+ m_fftCounter = 0;
+ m_chirpCount = 0;
+ int correction = 0;
+ qDebug("LoRaDemodSink::processSample: SFD done");
+ m_state = LoRaStateReadPayload; //LoRaStateReadPayload;
+ }
+ }
+ }
+ else if (m_state == LoRaStateReadPayload)
+ {
+ m_fft->in()[m_fftCounter] = ci * m_downChirps[m_chirp]; // de-chirp the up ramp
+ m_fftCounter++;
+
+ if (m_fftCounter == m_fftLength)
+ {
+ m_fftWindow.apply(m_fft->in());
+ 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 symbol = round(
+ argmax(
+ m_fft->out(),
+ m_fftInterpolation,
+ m_fftLength,
+ magsq,
+ m_spectrumBuffer,
+ m_fftInterpolation
+ ) / ((float) m_fftInterpolation * (1<feed(m_spectrumBuffer, m_nbSymbols);
+ }
+
+ if ((m_chirpCount == 0) || (10.0*magsq > m_magsq))
+ {
+ qDebug("LoRaDemodSink::processSample: symbol %02u: %4u|%11.6f", m_chirpCount, symbol, magsq);
+ m_magsq = magsq;
+ m_chirpCount++;
+
+ if (m_chirpCount > 255)
+ {
+ qDebug("LoRaDemodSink::processSample: message length exceeded");
+ m_state = LoRaStateReset;
+ }
+ }
+ else
+ {
+ qDebug("LoRaDemodSink::processSample: end of message");
+ m_state = LoRaStateReset;
+ }
+ }
+ }
+ else
+ {
+ m_state = LoRaStateReset;
+ }
+
+ m_chirp++;
+
+ if (m_chirp >= m_chirp0 + m_nbSymbols) {
+ m_chirp = m_chirp0;
+ }
+}
+
+void LoRaDemodSink::reset()
+{
+ m_chirp = 0;
+ m_chirp0 = 0;
+ m_fftCounter = 0;
+ m_argMaxHistoryCounter = 0;
+ m_sfdSkipCounter = 0;
+}
+
+unsigned int LoRaDemodSink::argmax(
+ const Complex *fftBins,
+ unsigned int fftMult,
+ unsigned int fftLength,
+ double& magsqMax,
+ Complex *specBuffer,
+ unsigned int specDecim)
+{
+ magsqMax = 0.0;
+ unsigned int imax;
+ double magSum = 0.0;
+
+ for (unsigned int i = 0; i < fftMult*fftLength; i++)
+ {
+ double magsq = std::norm(fftBins[i]);
+
+ if (magsq > magsqMax)
+ {
+ imax = i;
+ magsqMax = magsq;
+ }
+
+ if (specBuffer)
+ {
+ magSum += magsq;
+
+ if (i % specDecim == specDecim - 1)
+ {
+ specBuffer[i/specDecim] = Complex(std::polar(magSum, 0.0));
+ magSum = 0.0;
+ }
+ }
+ }
+
+ return imax;
+}
+
+void LoRaDemodSink::decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation)
+{
+ for (unsigned int i = 0; i < size; i++)
+ {
+ if (i % decimation == 0) {
+ out[i/decimation] = in[i];
+ }
+ }
+}
+
+int LoRaDemodSink::toSigned(int u, int intSize)
+{
+ if (u > intSize/2) {
+ return u - intSize;
+ } else {
+ return u;
+ }
}
void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force)
@@ -271,42 +452,33 @@ void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, i
m_nco.setFreq(-channelFrequencyOffset, channelSampleRate);
}
- if ((channelSampleRate != m_channelSampleRate) || force)
+ if ((channelSampleRate != m_channelSampleRate) ||
+ (bandwidth != m_bandwidth) || force)
{
- qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create";
m_interpolator.create(16, channelSampleRate, bandwidth / 1.9f);
- m_sampleDistanceRemain = (Real) channelSampleRate / bandwidth;
+ m_interpolatorDistance = (Real) channelSampleRate / (Real) bandwidth;
+ m_sampleDistanceRemain = 0;
+ qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create:"
+ << " m_interpolatorDistance: " << m_interpolatorDistance;
}
m_channelSampleRate = channelSampleRate;
- m_Bandwidth = bandwidth;
+ m_bandwidth = bandwidth;
m_channelFrequencyOffset = channelFrequencyOffset;
}
void LoRaDemodSink::applySettings(const LoRaDemodSettings& settings, bool force)
{
qDebug() << "LoRaDemodSink::applySettings:"
- << " m_centerFrequency: " << settings.m_centerFrequency
+ << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_bandwidthIndex: " << settings.m_bandwidthIndex
<< " m_spreadFactor: " << settings.m_spreadFactor
<< " m_rgbColor: " << settings.m_rgbColor
<< " m_title: " << settings.m_title
<< " force: " << force;
- if ((settings.m_spreadFactor != m_settings.m_spreadFactor) || force)
- {
- m_nbSymbols = 1 << settings.m_spreadFactor;
- m_sfftLength = m_nbSymbols / 2;
- delete m_loraFilter;
- delete m_negaFilter;
- delete m_mov;
- delete m_mag;
- delete m_rev;
- m_loraFilter = new sfft(m_sfftLength);
- m_negaFilter = new sfft(m_sfftLength);
- m_mov = new float[4*m_sfftLength];
- m_mag = new float[m_sfftLength];
- m_rev = new float[m_sfftLength];
+ if ((settings.m_spreadFactor != m_settings.m_spreadFactor) || force) {
+ initSF(settings.m_spreadFactor);
}
m_settings = settings;
diff --git a/plugins/channelrx/demodlora/lorademodsink.h b/plugins/channelrx/demodlora/lorademodsink.h
index 10f44a131..1d2a0b27f 100644
--- a/plugins/channelrx/demodlora/lorademodsink.h
+++ b/plugins/channelrx/demodlora/lorademodsink.h
@@ -24,17 +24,19 @@
#include "dsp/nco.h"
#include "dsp/interpolator.h"
#include "util/message.h"
-#include "dsp/fftfilt.h"
+#include "dsp/fftwindow.h"
#include "lorademodsettings.h"
class BasebandSampleSink;
+class FFTEngine;
class LoRaDemodSink : public ChannelSampleSink {
public:
LoRaDemodSink();
~LoRaDemodSink();
+ void initSF(unsigned int sf); //!< Init tables, FFTs, depending on spread factor
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; }
@@ -42,44 +44,69 @@ public:
void applySettings(const LoRaDemodSettings& settings, bool force = false);
private:
+ enum LoRaState
+ {
+ LoRaStateReset, //!< Reset everything to start all over
+ LoRaStateDetectPreamble, //!< Look for preamble
+ LoRaStatePreamble, //!< Preamble is found and look for SFD start
+ LoRaStateSkipSFD, //!< Skip SFD
+ LoRaStateSlideSFD, //!< Sliding FFTs while going through SFD (not the skip option)
+ LoRaStateReadPayload,
+ LoRaStateTest
+ };
+
LoRaDemodSettings m_settings;
- Real m_Bandwidth;
+ LoRaState m_state;
+ int m_bandwidth;
int m_channelSampleRate;
int m_channelFrequencyOffset;
int m_chirp;
- int m_angle;
- int m_bin;
- int m_result;
- int m_count;
- int m_header;
- int m_time;
- short m_tune;
+ int m_chirp0;
- sfft* m_loraFilter;
- sfft* m_negaFilter;
- float* m_mov;
- float* m_mag;
- float* m_rev;
- short* m_history;
- short* m_finetune;
+ static const unsigned int m_requiredPreambleChirps = 3; //!< Number of chirps required to estimate preamble
+ static const unsigned int m_maxSFDSearchChirps = 8; //!< Maximum number of chirps when looking for SFD after preamble detection
+ static const unsigned int m_sfdFourths = 5; //!< Number of SFD chip period fourths to skip until payload
+ static const unsigned int m_fftInterpolation = 1; //!< FFT interpolation factor (usually a power of 2)
+
+ FFTEngine *m_fft;
+ FFTEngine *m_fftSFD;
+ FFTWindow m_fftWindow;
+ Complex *m_downChirps;
+ Complex *m_upChirps;
+ Complex *m_fftBuffer;
+ unsigned int m_fftCounter;
+ unsigned int m_argMaxHistory[m_requiredPreambleChirps];
+ unsigned int m_argMaxHistoryCounter;
+ unsigned int m_preambleHistory[m_maxSFDSearchChirps];
+ unsigned int m_syncWord;
+ double m_magsq;
+ 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
NCO m_nco;
Interpolator m_interpolator;
Real m_sampleDistanceRemain;
+ Real m_interpolatorDistance;
BasebandSampleSink* m_spectrumSink;
- SampleVector m_sampleBuffer;
+ Complex *m_spectrumBuffer;
unsigned int m_nbSymbols;
- unsigned int m_sfftLength;
+ unsigned int m_fftLength;
- static const int DATA_BITS;
- static const int SAMPLEBITS;
- static const int LORA_SQUELCH;
-
- int detect(Complex sample, Complex angle);
- void dumpRaw(void);
- short synch (short bin);
+ void processSample(const Complex& ci);
+ void reset();
+ unsigned int argmax(
+ const Complex *fftBins,
+ unsigned int fftMult,
+ unsigned int fftLength,
+ double& magsqMax,
+ Complex *specBuffer,
+ unsigned int specDecim
+ );
+ void decimateSpectrum(Complex *in, Complex *out, unsigned int size, unsigned int decimation);
+ int toSigned(int u, int intSize);
/*
Interleaving is "easiest" if the same number of bits is used per symbol as for FEC
diff --git a/sdrbase/dsp/basebandsamplesink.h b/sdrbase/dsp/basebandsamplesink.h
index 4af29a591..c736686c6 100644
--- a/sdrbase/dsp/basebandsamplesink.h
+++ b/sdrbase/dsp/basebandsamplesink.h
@@ -63,6 +63,12 @@ public:
void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; }
+ virtual void feed(const Complex *begin, unsigned int length) //!< Special feed directly with complex array
+ {
+ (void) begin;
+ (void) length;
+ }
+
protected:
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
MessageQueue *m_guiMessageQueue; //!< Input message queue to the GUI
diff --git a/sdrbase/dsp/fftwengine.cpp b/sdrbase/dsp/fftwengine.cpp
index 76b189ead..a1b380af3 100644
--- a/sdrbase/dsp/fftwengine.cpp
+++ b/sdrbase/dsp/fftwengine.cpp
@@ -3,7 +3,7 @@
FFTWEngine::FFTWEngine() :
m_plans(),
- m_currentPlan(NULL)
+ m_currentPlan(nullptr)
{
}
@@ -14,8 +14,10 @@ FFTWEngine::~FFTWEngine()
void FFTWEngine::configure(int n, bool inverse)
{
- for(Plans::const_iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
- if(((*it)->n == n) && ((*it)->inverse == inverse)) {
+ for (Plans::const_iterator it = m_plans.begin(); it != m_plans.end(); ++it)
+ {
+ if (((*it)->n == n) && ((*it)->inverse == inverse))
+ {
m_currentPlan = *it;
return;
}
@@ -37,33 +39,40 @@ void FFTWEngine::configure(int n, bool inverse)
void FFTWEngine::transform()
{
- if(m_currentPlan != NULL)
+ if (m_currentPlan) {
fftwf_execute(m_currentPlan->plan);
+ }
}
Complex* FFTWEngine::in()
{
- if(m_currentPlan != NULL)
+ if (m_currentPlan) {
return reinterpret_cast(m_currentPlan->in);
- else return NULL;
+ } else {
+ return nullptr;
+ }
}
Complex* FFTWEngine::out()
{
- if(m_currentPlan != NULL)
+ if (m_currentPlan) {
return reinterpret_cast(m_currentPlan->out);
- else return NULL;
+ } else {
+ return nullptr;
+ }
}
QMutex FFTWEngine::m_globalPlanMutex;
void FFTWEngine::freeAll()
{
- for(Plans::iterator it = m_plans.begin(); it != m_plans.end(); ++it) {
+ for (Plans::iterator it = m_plans.begin(); it != m_plans.end(); ++it)
+ {
fftwf_destroy_plan((*it)->plan);
fftwf_free((*it)->in);
fftwf_free((*it)->out);
delete *it;
}
+
m_plans.clear();
}
diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp
index 97adccd8e..1cf2826e1 100644
--- a/sdrgui/dsp/spectrumvis.cpp
+++ b/sdrgui/dsp/spectrumvis.cpp
@@ -81,6 +81,110 @@ void SpectrumVis::feedTriggered(const SampleVector::const_iterator& triggerPoint
}*/
}
+void SpectrumVis::feed(const Complex *begin, unsigned int length)
+{
+ if (m_glSpectrum == 0) {
+ return;
+ }
+ if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
+ return;
+ }
+
+ Complex c;
+ Real v;
+
+ if (m_avgMode == AvgModeNone)
+ {
+ for (unsigned int i = 0; i < m_fftSize; i++)
+ {
+ if (i < length) {
+ c = begin[i];
+ } else {
+ c = Complex{0,0};
+ }
+
+ v = c.real() * c.real() + c.imag() * c.imag();
+ v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
+ m_powerSpectrum[i] = v;
+ }
+
+ // send new data to visualisation
+ m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
+ }
+ else if (m_avgMode == AvgModeMovingAvg)
+ {
+ for (unsigned int i = 0; i < m_fftSize; i++)
+ {
+ if (i < length) {
+ c = begin[i];
+ } else {
+ c = Complex{0,0};
+ }
+
+ v = c.real() * c.real() + c.imag() * c.imag();
+ v = m_movingAverage.storeAndGetAvg(v, i);
+ v = m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
+ m_powerSpectrum[i] = v;
+ }
+
+ // send new data to visualisation
+ m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
+ m_movingAverage.nextAverage();
+ }
+ else if (m_avgMode == AvgModeFixedAvg)
+ {
+ double avg;
+
+ for (unsigned int i = 0; i < m_fftSize; i++)
+ {
+ if (i < length) {
+ c = begin[i];
+ } else {
+ c = Complex{0,0};
+ }
+
+ v = c.real() * c.real() + c.imag() * c.imag();
+
+ if (m_fixedAverage.storeAndGetAvg(avg, v, i))
+ { // result available
+ avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
+ m_powerSpectrum[i] = avg;
+ }
+ }
+
+ if (m_fixedAverage.nextAverage()) { // result available
+ m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
+ }
+ }
+ else if (m_avgMode == AvgModeMax)
+ {
+ double max;
+
+ for (unsigned int i = 0; i < m_fftSize; i++)
+ {
+ if (i < length) {
+ c = begin[i];
+ } else {
+ c = Complex{0,0};
+ }
+
+ v = c.real() * c.real() + c.imag() * c.imag();
+
+ if (m_max.storeAndGetMax(max, v, i))
+ { // result available
+ max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
+ m_powerSpectrum[i] = max;
+ }
+ }
+
+ if (m_max.nextMax()) { // result available
+ m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
+ }
+ }
+
+ m_mutex.unlock();
+}
+
void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly)
{
// if no visualisation is set, send the samples to /dev/null
diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h
index 668f67ac5..1bbfdd443 100644
--- a/sdrgui/dsp/spectrumvis.h
+++ b/sdrgui/dsp/spectrumvis.h
@@ -90,6 +90,7 @@ public:
void setScalef(MessageQueue* msgQueue, Real scalef);
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
+ virtual void feed(const Complex *begin, unsigned int length); //!< direct FFT feed
void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly);
virtual void start();
virtual void stop();
diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp
index 2f60909d4..3e32d0342 100644
--- a/sdrgui/gui/glspectrumgui.cpp
+++ b/sdrgui/gui/glspectrumgui.cpp
@@ -590,6 +590,11 @@ void GLSpectrumGUI::setAveragingToolitp()
}
}
+void GLSpectrumGUI::setFFTSize(int log2FFTSize)
+{
+ ui->fftSize->setCurrentIndex(log2FFTSize < 7 ? 0 : log2FFTSize > 12 ? 5 : log2FFTSize - 7); // 128 to 4096 in powers of 2
+}
+
bool GLSpectrumGUI::handleMessage(const Message& message)
{
if (GLSpectrum::MsgReportSampleRate::match(message))
diff --git a/sdrgui/gui/glspectrumgui.h b/sdrgui/gui/glspectrumgui.h
index 8d1840004..d93e6e756 100644
--- a/sdrgui/gui/glspectrumgui.h
+++ b/sdrgui/gui/glspectrumgui.h
@@ -30,6 +30,7 @@ public:
~GLSpectrumGUI();
void setBuddies(MessageQueue* messageQueue, SpectrumVis* spectrumVis, GLSpectrum* glSpectrum);
+ void setFFTSize(int log2FFTSize);
void resetToDefaults();
virtual QByteArray serialize() const;