1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-08-01 16:38:06 -04:00

LoRa demod rework (2)

This commit is contained in:
f4exb
2020-02-08 18:21:38 +01:00
parent 6028938108
commit 9e088dc601
15 changed files with 1074 additions and 396 deletions
+10 -3
View File
@@ -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
@@ -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(),
+121 -18
View File
@@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "device/deviceuiset.h"
#include <QDockWidget>
@@ -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]));
}
}
+24 -1
View File
@@ -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_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
+296 -114
View File
@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>350</width>
<height>550</height>
<height>500</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>550</height>
<height>500</height>
</size>
</property>
<property name="font">
@@ -29,124 +29,298 @@
<property name="geometry">
<rect>
<x>10</x>
<y>35</y>
<y>20</y>
<width>331</width>
<height>96</height>
<height>112</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
<property name="minimumSize">
<size>
<width>0</width>
<height>112</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<property name="windowTitle">
<string>RF/demod Settings</string>
</property>
<widget class="QLabel" name="bwLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>50</y>
<width>22</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>BW</string>
</property>
</widget>
<widget class="QLabel" name="spreadLabel">
<property name="geometry">
<rect>
<x>2</x>
<y>70</y>
<width>22</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>SF</string>
</property>
</widget>
<widget class="QSlider" name="BW">
<property name="geometry">
<rect>
<x>30</x>
<y>50</y>
<width>180</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Bandwidth</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>5</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QSlider" name="Spread">
<property name="geometry">
<rect>
<x>30</x>
<y>70</y>
<width>100</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Spreading factor</string>
</property>
<property name="minimum">
<number>7</number>
</property>
<property name="maximum">
<number>12</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="sliderPosition">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="SpreadText">
<property name="geometry">
<rect>
<x>140</x>
<y>70</y>
<width>30</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>10</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="BWText">
<property name="geometry">
<rect>
<x>240</x>
<y>50</y>
<width>80</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>7813 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QLabel" name="deBitsLabel">
<property name="geometry">
<rect>
<x>190</x>
<y>70</y>
<width>22</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>DE</string>
</property>
</widget>
<widget class="QLabel" name="deBitsText">
<property name="geometry">
<rect>
<x>290</x>
<y>70</y>
<width>30</width>
<height>16</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
<widget class="QSlider" name="deBits">
<property name="geometry">
<rect>
<x>220</x>
<y>70</y>
<width>60</width>
<height>16</height>
</rect>
</property>
<property name="toolTip">
<string>Low data rate optimize (DE) bits</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
<property name="pageStep">
<number>1</number>
</property>
<property name="rightMargin">
<number>2</number>
<property name="value">
<number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
<property name="sliderPosition">
<number>0</number>
</property>
<property name="spacing">
<number>3</number>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>BW</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mabel">
<property name="text">
<string>SF</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSlider" name="BW">
<property name="toolTip">
<string>Bandwidth</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>6</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="Spread">
<property name="toolTip">
<string>Spreading factor</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>12</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>7</number>
</property>
<property name="sliderPosition">
<number>7</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="SpreadText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>5</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="BWText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>7813 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>311</width>
<height>26</height>
</rect>
</property>
<layout class="QHBoxLayout" name="deltaFrequencyLayout">
<item>
<widget class="QLabel" name="deltaFrequencyLabel">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Df</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="deltaFrequency" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Demod shift frequency from center in Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deltaUnits">
<property name="text">
<string>Hz </string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="spectrumContainer" native="true">
<property name="geometry">
@@ -154,11 +328,11 @@
<x>10</x>
<y>140</y>
<width>331</width>
<height>391</height>
<height>341</height>
</rect>
</property>
<property name="windowTitle">
<string>Channel Spectrum</string>
<string>De-chirped Spectrum</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@@ -181,7 +355,7 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>350</height>
<height>300</height>
</size>
</property>
</widget>
@@ -211,7 +385,15 @@
<header>gui/glspectrumgui.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>
@@ -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 "lorademodsettings.h"
#include <QColor>
@@ -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;
}
@@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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();
+387 -215
View File
@@ -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<<m_settings.m_deBits))
);
if (m_spectrumSink) {
m_spectrumSink->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;
+52 -25
View File
@@ -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
+6
View File
@@ -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
+18 -9
View File
@@ -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<Complex*>(m_currentPlan->in);
else return NULL;
} else {
return nullptr;
}
}
Complex* FFTWEngine::out()
{
if(m_currentPlan != NULL)
if (m_currentPlan) {
return reinterpret_cast<Complex*>(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();
}
+104
View File
@@ -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
+1
View File
@@ -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();
+5
View File
@@ -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))
+1
View File
@@ -30,6 +30,7 @@ public:
~GLSpectrumGUI();
void setBuddies(MessageQueue* messageQueue, SpectrumVis* spectrumVis, GLSpectrum* glSpectrum);
void setFFTSize(int log2FFTSize);
void resetToDefaults();
virtual QByteArray serialize() const;