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 @@
gui/glspectrumgui.h
1 + + ValueDialZ + QWidget +
gui/valuedialz.h
+ 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;