diff --git a/Readme.md b/Readme.md
index 90d3fa11c..f7600dfa6 100644
--- a/Readme.md
+++ b/Readme.md
@@ -39,6 +39,7 @@ Since version 2 SDRangel can integrate more than one hardware device running con
Since version 3 transmission or signal generation is supported for BladeRF, HackRF (since version 3.1), LimeSDR (since version 3.4) and PlutoSDR (since version 3.7.8) using a sample sink plugin. These plugins are:
- [BladeRF1 output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf1output)
+ - [BladeRF2 output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/bladerf2output)
- [HackRF output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/hackrfoutput)
- [LimeSDR output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/limesdroutput)
- [PlutoSDR output plugin](https://github.com/f4exb/sdrangel/tree/dev/plugins/samplesink/plutosdroutput)
@@ -114,7 +115,7 @@ The plugins used to support BladeRF classic are specific to this version of the
BladeRF micro (v.2)
-From version 4.2.0. Output (Tx) for Linux only.
+From version 4.2.0.
[BladeRF 2 micro](https://www.nuand.com/bladerf-2-0-micro/) is also supported using libbladeRF library that should be installed and configured in the same way as for BladeRF1.
diff --git a/debian/changelog b/debian/changelog
index a977768c6..10534c313 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,7 +1,14 @@
+sdrangel (4.2.2-1) unstable; urgency=medium
+
+ * Spectrum: option to get max over a number of FFTs. Implements issue #207
+
+ -- Edouard Griffiths, F4EXB Sun, 14 Oct 2018 21:14:18 +0200
+
sdrangel (4.2.1-1) unstable; urgency=medium
* FileRecord improvement with robust header and some fixes. Fixes issue #206
* BladeRF2 MO Tx fix so that the two channels are used effectively. Fixes issue #225
+ * NFM demod: set squelch step to 1 dB
-- Edouard Griffiths, F4EXB Wed, 10 Oct 2018 21:14:18 +0200
diff --git a/plugins/samplesink/bladerf2output/readme.md b/plugins/samplesink/bladerf2output/readme.md
index 070ffc554..8719e7163 100644
--- a/plugins/samplesink/bladerf2output/readme.md
+++ b/plugins/samplesink/bladerf2output/readme.md
@@ -2,7 +2,7 @@
Introduction
-This output sample sink plugin sends its samples to a [BladeRF2 device](https://www.nuand.com/bladerf-2). This is available since v4.2.0 in Linux distributions only.
+This output sample sink plugin sends its samples to a [BladeRF2 device](https://www.nuand.com/bladerf-2). This is available since v4.2.0.
Build
diff --git a/sdrbase/util/fixedaverage2d.h b/sdrbase/util/fixedaverage2d.h
index 192dcfe6a..c9f7b0ffa 100644
--- a/sdrbase/util/fixedaverage2d.h
+++ b/sdrbase/util/fixedaverage2d.h
@@ -24,7 +24,7 @@ template
class FixedAverage2D
{
public:
- FixedAverage2D() : m_sum(0), m_sumSize(0), m_width(0), m_size(0), m_avgIndex(0) {}
+ FixedAverage2D() : m_sum(0), m_maxSize(0), m_width(0), m_size(0), m_maxIndex(0) {}
~FixedAverage2D()
{
@@ -35,20 +35,20 @@ public:
void resize(unsigned int width, unsigned int size)
{
- if (width > m_sumSize)
+ if (width > m_maxSize)
{
- m_sumSize = width;
+ m_maxSize = width;
if (m_sum) {
delete[] m_sum;
}
- m_sum = new T[m_sumSize];
+ m_sum = new T[m_maxSize];
}
m_width = width;
m_size = size;
std::fill(m_sum, m_sum+m_width, 0);
- m_avgIndex = 0;
+ m_maxIndex = 0;
}
bool storeAndGetAvg(T& avg, T v, unsigned int index)
@@ -61,7 +61,7 @@ public:
m_sum[index] += v;
- if (m_avgIndex == m_size - 1)
+ if (m_maxIndex == m_size - 1)
{
avg = m_sum[index]/m_size;
return true;
@@ -82,7 +82,7 @@ public:
m_sum[index] += v;
- if (m_avgIndex < m_size - 1)
+ if (m_maxIndex < m_size - 1)
{
sum = m_sum[index];
return true;
@@ -99,25 +99,25 @@ public:
return true;
}
- if (m_avgIndex == m_size - 1)
+ if (m_maxIndex == m_size - 1)
{
- m_avgIndex = 0;
+ m_maxIndex = 0;
std::fill(m_sum, m_sum+m_width, 0);
return true;
}
else
{
- m_avgIndex++;
+ m_maxIndex++;
return false;
}
}
private:
T *m_sum;
- unsigned int m_sumSize;
+ unsigned int m_maxSize;
unsigned int m_width;
unsigned int m_size;
- unsigned int m_avgIndex;
+ unsigned int m_maxIndex;
};
diff --git a/sdrbase/util/max2d.h b/sdrbase/util/max2d.h
new file mode 100644
index 000000000..58be966c9
--- /dev/null
+++ b/sdrbase/util/max2d.h
@@ -0,0 +1,107 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2018 F4EXB //
+// written by 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 //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// 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 SDRBASE_UTIL_MAX2D_H_
+#define SDRBASE_UTIL_MAX2D_H_
+
+#include
+
+template
+class Max2D
+{
+public:
+ Max2D() : m_max(0), m_maxSize(0), m_width(0), m_size(0), m_maxIndex(0) {}
+
+ ~Max2D()
+ {
+ if (m_max) {
+ delete[] m_max;
+ }
+ }
+
+ void resize(unsigned int width, unsigned int size)
+ {
+ if (width > m_maxSize)
+ {
+ m_maxSize = width;
+ if (m_max) {
+ delete[] m_max;
+ }
+ m_max = new T[m_maxSize];
+ }
+
+ m_width = width;
+ m_size = size;
+
+ std::fill(m_max, m_max+m_width, 0);
+ m_maxIndex = 0;
+ }
+
+ bool storeAndGetMax(T& max, T v, unsigned int index)
+ {
+ if (m_size <= 1)
+ {
+ max = v;
+ return true;
+ }
+
+ if (m_maxIndex == 0)
+ {
+ m_max[index] = v;
+ return false;
+ }
+ else if (m_maxIndex == m_size - 1)
+ {
+ m_max[index] = std::max(m_max[index], v);
+ max = m_max[index];
+ return true;
+ }
+ else
+ {
+ m_max[index] = std::max(m_max[index], v);
+ return false;
+ }
+ }
+
+ bool nextMax()
+ {
+ if (m_size <= 1) {
+ return true;
+ }
+
+ if (m_maxIndex == m_size - 1)
+ {
+ m_maxIndex = 0;
+ std::fill(m_max, m_max+m_width, 0);
+ return true;
+ }
+ else
+ {
+ m_maxIndex++;
+ return false;
+ }
+ }
+
+private:
+ T *m_max;
+ unsigned int m_maxSize;
+ unsigned int m_width;
+ unsigned int m_size;
+ unsigned int m_maxIndex;
+};
+
+#endif /* SDRBASE_UTIL_MAX2D_H_ */
diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp
index 7bc7f5426..0389cdc91 100644
--- a/sdrgui/dsp/spectrumvis.cpp
+++ b/sdrgui/dsp/spectrumvis.cpp
@@ -26,7 +26,7 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) :
m_scalef(scalef),
m_glSpectrum(glSpectrum),
m_averageNb(0),
- m_averagingMode(AvgModeNone),
+ m_avgMode(AvgModeNone),
m_linear(false),
m_ofs(0),
m_powFFTDiv(1.0),
@@ -114,7 +114,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
Real v;
std::size_t halfSize = m_fftSize / 2;
- if (m_averagingMode == AvgModeNone)
+ if (m_avgMode == AvgModeNone)
{
if ( positiveOnly )
{
@@ -146,7 +146,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// send new data to visualisation
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
}
- else if (m_averagingMode == AvgModeMoving)
+ else if (m_avgMode == AvgModeMovingAvg)
{
if ( positiveOnly )
{
@@ -182,7 +182,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
m_movingAverage.nextAverage();
}
- else if (m_averagingMode == AvgModeFixed)
+ else if (m_avgMode == AvgModeFixedAvg)
{
double avg;
@@ -195,7 +195,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
- avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
+ avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i * 2] = avg;
m_powerSpectrum[i * 2 + 1] = avg;
}
@@ -210,7 +210,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
{ // result available
- avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
+ avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg;
}
@@ -219,16 +219,61 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{ // result available
- avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
+ avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i + halfSize] = avg;
}
}
}
- if (m_fixedAverage.nextAverage())
- { // result available
- // send new data to visualisation
- m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize);
+ 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;
+
+ if ( positiveOnly )
+ {
+ for (std::size_t i = 0; i < halfSize; i++)
+ {
+ c = fftOut[i];
+ v = c.real() * c.real() + c.imag() * c.imag();
+
+ if (m_max.storeAndGetMax(max, v, i))
+ {
+ max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
+ m_powerSpectrum[i * 2] = max;
+ m_powerSpectrum[i * 2 + 1] = max;
+ }
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < halfSize; i++)
+ {
+ c = fftOut[i + halfSize];
+ v = c.real() * c.real() + c.imag() * c.imag();
+
+ if (m_max.storeAndGetMax(max, v, i+halfSize))
+ { // result available
+ max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
+ m_powerSpectrum[i] = max;
+ }
+
+ c = fftOut[i];
+ 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 + halfSize] = max;
+ }
+ }
+ }
+
+ if (m_max.nextMax()) { // result available
+ m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation
}
}
@@ -269,7 +314,7 @@ bool SpectrumVis::handleMessage(const Message& message)
handleConfigure(conf.getFFTSize(),
conf.getOverlapPercent(),
conf.getAverageNb(),
- conf.getAveragingMode(),
+ conf.getAvgMode(),
conf.getWindow(),
conf.getLinear());
return true;
@@ -283,10 +328,12 @@ bool SpectrumVis::handleMessage(const Message& message)
void SpectrumVis::handleConfigure(int fftSize,
int overlapPercent,
unsigned int averageNb,
- AveragingMode averagingMode,
+ AvgMode averagingMode,
FFTWindow::Function window,
bool linear)
{
+// qDebug("SpectrumVis::handleConfigure, fftSize: %d overlapPercent: %d averageNb: %u averagingMode: %d window: %d linear: %s",
+// fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false");
QMutexLocker mutexLocker(&m_mutex);
if (fftSize > MAX_FFT_SIZE)
@@ -319,8 +366,9 @@ void SpectrumVis::handleConfigure(int fftSize,
m_fftBufferFill = m_overlapSize;
m_movingAverage.resize(fftSize, averageNb);
m_fixedAverage.resize(fftSize, averageNb);
+ m_max.resize(fftSize, averageNb);
m_averageNb = averageNb;
- m_averagingMode = averagingMode;
+ m_avgMode = averagingMode;
m_linear = linear;
m_ofs = 20.0f * log10f(1.0f / m_fftSize);
m_powFFTDiv = m_fftSize*m_fftSize;
diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h
index a86a5c0fd..b01c6459d 100644
--- a/sdrgui/dsp/spectrumvis.h
+++ b/sdrgui/dsp/spectrumvis.h
@@ -9,6 +9,7 @@
#include "util/message.h"
#include "util/movingaverage2d.h"
#include "util/fixedaverage2d.h"
+#include "util/max2d.h"
class GLSpectrum;
class MessageQueue;
@@ -16,11 +17,12 @@ class MessageQueue;
class SDRGUI_API SpectrumVis : public BasebandSampleSink {
public:
- enum AveragingMode
+ enum AvgMode
{
AvgModeNone,
- AvgModeMoving,
- AvgModeFixed
+ AvgModeMovingAvg,
+ AvgModeFixedAvg,
+ AvgModeMax
};
class MsgConfigureSpectrumVis : public Message {
@@ -31,7 +33,7 @@ public:
int fftSize,
int overlapPercent,
unsigned int averageNb,
- int averagingMode,
+ int preProcessMode,
FFTWindow::Function window,
bool linear) :
Message(),
@@ -41,13 +43,13 @@ public:
m_window(window),
m_linear(linear)
{
- m_averagingMode = averagingMode < 0 ? AvgModeNone : averagingMode > 2 ? AvgModeFixed : (SpectrumVis::AveragingMode) averagingMode;
+ m_avgMode = preProcessMode < 0 ? AvgModeNone : preProcessMode > 3 ? AvgModeMax : (SpectrumVis::AvgMode) preProcessMode;
}
int getFFTSize() const { return m_fftSize; }
int getOverlapPercent() const { return m_overlapPercent; }
unsigned int getAverageNb() const { return m_averageNb; }
- SpectrumVis::AveragingMode getAveragingMode() const { return m_averagingMode; }
+ SpectrumVis::AvgMode getAvgMode() const { return m_avgMode; }
FFTWindow::Function getWindow() const { return m_window; }
bool getLinear() const { return m_linear; }
@@ -55,7 +57,7 @@ public:
int m_fftSize;
int m_overlapPercent;
unsigned int m_averageNb;
- SpectrumVis::AveragingMode m_averagingMode;
+ SpectrumVis::AvgMode m_avgMode;
FFTWindow::Function m_window;
bool m_linear;
};
@@ -95,8 +97,9 @@ private:
GLSpectrum* m_glSpectrum;
MovingAverage2D m_movingAverage;
FixedAverage2D m_fixedAverage;
+ Max2D m_max;
unsigned int m_averageNb;
- AveragingMode m_averagingMode;
+ AvgMode m_avgMode;
bool m_linear;
Real m_ofs;
@@ -108,7 +111,7 @@ private:
void handleConfigure(int fftSize,
int overlapPercent,
unsigned int averageNb,
- AveragingMode averagingMode,
+ AvgMode averagingMode,
FFTWindow::Function window,
bool linear);
};
diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp
index 62a200437..d0ba90b1d 100644
--- a/sdrgui/gui/glspectrumgui.cpp
+++ b/sdrgui/gui/glspectrumgui.cpp
@@ -141,7 +141,7 @@ bool GLSpectrumGUI::deserialize(const QByteArray& data)
Real waterfallShare;
d.readReal(18, &waterfallShare, 0.66);
d.readS32(19, &tmp, 0);
- m_averagingMode = tmp < 0 ? AvgModeNone : tmp > 2 ? AvgModeFixed : (AveragingMode) tmp;
+ m_averagingMode = tmp < 0 ? AvgModeNone : tmp > 3 ? AvgModeMax : (AveragingMode) tmp;
d.readS32(20, &tmp, 0);
m_averagingIndex = getAveragingIndex(tmp);
m_averagingNb = getAveragingValue(m_averagingIndex);
@@ -244,7 +244,7 @@ void GLSpectrumGUI::on_fftSize_currentIndexChanged(int index)
void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index)
{
- m_averagingMode = index < 0 ? AvgModeNone : index > 2 ? AvgModeFixed : (AveragingMode) index;
+ m_averagingMode = index < 0 ? AvgModeNone : index > 3 ? AvgModeMax : (AveragingMode) index;
if(m_spectrumVis != 0) {
m_spectrumVis->configure(m_messageQueueToVis,
@@ -258,7 +258,7 @@ void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index)
if (m_glSpectrum != 0)
{
- if (m_averagingMode == AvgModeFixed) {
+ if ((m_averagingMode == AvgModeFixed) || (m_averagingMode == AvgModeMax)) {
m_glSpectrum->setTimingRate(m_averagingNb == 0 ? 1 : m_averagingNb);
} else {
m_glSpectrum->setTimingRate(1);
@@ -283,8 +283,10 @@ void GLSpectrumGUI::on_averaging_currentIndexChanged(int index)
if (m_glSpectrum != 0)
{
- if (m_averagingMode == AvgModeFixed) {
+ if ((m_averagingMode == AvgModeFixed) || (m_averagingMode == AvgModeMax)) {
m_glSpectrum->setTimingRate(m_averagingNb == 0 ? 1 : m_averagingNb);
+ } else {
+ m_glSpectrum->setTimingRate(1);
}
}
diff --git a/sdrgui/gui/glspectrumgui.h b/sdrgui/gui/glspectrumgui.h
index 13d305231..9cf02a20d 100644
--- a/sdrgui/gui/glspectrumgui.h
+++ b/sdrgui/gui/glspectrumgui.h
@@ -22,7 +22,8 @@ public:
{
AvgModeNone,
AvgModeMoving,
- AvgModeFixed
+ AvgModeFixed,
+ AvgModeMax
};
explicit GLSpectrumGUI(QWidget* parent = NULL);
diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui
index 277c560ed..a874c7068 100644
--- a/sdrgui/gui/glspectrumgui.ui
+++ b/sdrgui/gui/glspectrumgui.ui
@@ -565,6 +565,11 @@
Fix
+ -
+
+ Max
+
+
-