1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-22 08:04:49 -05:00

Scope on complex<float>: implementation

This commit is contained in:
f4exb 2021-06-24 21:09:11 +02:00
parent 04170f2648
commit dc205bc8e2
5 changed files with 473 additions and 357 deletions

View File

@ -41,6 +41,7 @@ typedef qint16 FixReal;
typedef float Real;
typedef std::complex<Real> Complex;
typedef std::vector<Complex> ComplexVector;
#pragma pack(push, 1)
struct Sample

View File

@ -70,6 +70,7 @@ ScopeVis::ScopeVis() :
{
setObjectName("ScopeVis");
m_traceDiscreteMemory.resize(GLScopeSettings::m_traceChunkDefaultSize); // arbitrary
m_convertBuffers.resize(GLScopeSettings::m_traceChunkDefaultSize);
for (int i = 0; i < (int) Projector::nbProjectionTypes; i++) {
m_projectorCache[i] = 0.0;
@ -140,6 +141,7 @@ void ScopeVis::setNbStreams(uint32_t nbStreams)
if (m_nbStreams != nbStreams)
{
m_traceDiscreteMemory.setNbStreams(nbStreams);
m_convertBuffers.setNbStreams(nbStreams);
m_nbStreams = nbStreams;
}
}
@ -414,6 +416,31 @@ void ScopeVis::setMemoryIndex(uint32_t memoryIndex)
}
void ScopeVis::feed(const std::vector<SampleVector::const_iterator>& vbegin, int nbSamples)
{
std::vector<ComplexVector::const_iterator> vcbegin;
std::vector<ComplexVector>& convertBuffers = m_convertBuffers.getBuffers();
if (nbSamples > (int) m_convertBuffers.size()) {
m_convertBuffers.resize(nbSamples);
}
for (unsigned int s = 0; s < vbegin.size(); s++)
{
std::transform(
vbegin[s],
vbegin[s] + nbSamples,
convertBuffers[s].begin(),
[](const Sample& s) -> Complex {
return Complex{s.m_real / SDR_RX_SCALEF, s.m_imag / SDR_RX_SCALEF};
}
);
vcbegin.push_back(convertBuffers[s].begin());
}
feed(vcbegin, nbSamples);
}
void ScopeVis::feed(const std::vector<ComplexVector::const_iterator>& vbegin, int nbSamples)
{
if (vbegin.size() == 0) {
return;
@ -447,11 +474,11 @@ void ScopeVis::feed(const std::vector<SampleVector::const_iterator>& vbegin, int
m_triggerLocation = nbSamples;
}
SampleVector::const_iterator begin(vbegin[0]);
ComplexVector::const_iterator begin(vbegin[0]);
//const SampleVector::const_iterator end = vbegin[0] + nbSamples;
int triggerPointToEnd;
int remainder = nbSamples;
std::vector<SampleVector::const_iterator> nvbegin(vbegin);
std::vector<ComplexVector::const_iterator> nvbegin(vbegin);
//while (begin < end)
while (remainder > 0)
@ -495,11 +522,11 @@ void ScopeVis::processMemoryTrace()
traceMemoryIndex += GLScopeSettings::m_nbTraceMemories;
}
std::vector<SampleVector::const_iterator> mend;
std::vector<ComplexVector::const_iterator> mend;
m_traceDiscreteMemory.getEndPointAt(traceMemoryIndex, mend);
std::vector<SampleVector::const_iterator> mbegin(mend.size());
std::vector<ComplexVector::const_iterator> mbegin(mend.size());
TraceBackDiscreteMemory::moveIt(mend, mbegin, -m_traceSize);
std::vector<SampleVector::const_iterator> mbegin_tb(mbegin.size());
std::vector<ComplexVector::const_iterator> mbegin_tb(mbegin.size());
TraceBackDiscreteMemory::moveIt(mbegin, mbegin_tb, -m_maxTraceDelay);
m_nbSamples = m_traceSize + m_maxTraceDelay;
@ -508,9 +535,9 @@ void ScopeVis::processMemoryTrace()
}
}
void ScopeVis::processTrace(const std::vector<SampleVector::const_iterator>& vcbegin, int length, int& triggerPointToEnd)
void ScopeVis::processTrace(const std::vector<ComplexVector::const_iterator>& vcbegin, int length, int& triggerPointToEnd)
{
std::vector<SampleVector::const_iterator> vbegin(vcbegin);
std::vector<ComplexVector::const_iterator> vbegin(vcbegin);
int firstRemainder = length;
// memory storage
@ -579,7 +606,7 @@ void ScopeVis::processTrace(const std::vector<SampleVector::const_iterator>& vcb
}
uint32_t triggerStreamIndex = triggerCondition->m_triggerData.m_streamIndex;
const Sample& s = *vbegin[triggerStreamIndex];
const Complex& s = *vbegin[triggerStreamIndex];
if (m_triggerComparator.triggered(s, *triggerCondition)) // matched the current trigger
{
@ -625,9 +652,9 @@ void ScopeVis::processTrace(const std::vector<SampleVector::const_iterator>& vcb
{
int remainder;
int count = firstRemainder; // number of samples in traceback buffer past the current point
std::vector<SampleVector::const_iterator> mend;
std::vector<ComplexVector::const_iterator> mend;
m_traceDiscreteMemory.getCurrent(mend);
std::vector<SampleVector::const_iterator> mbegin(mend.size());
std::vector<ComplexVector::const_iterator> mbegin(mend.size());
TraceBackDiscreteMemory::moveIt(mend, mbegin, -count);
if (m_traceStart) // start of trace processing
@ -644,14 +671,14 @@ void ScopeVis::processTrace(const std::vector<SampleVector::const_iterator>& vcb
if (m_maxTraceDelay > 0)
{ // trace back
std::vector<SampleVector::const_iterator> tbegin(mbegin.size());
std::vector<ComplexVector::const_iterator> tbegin(mbegin.size());
TraceBackDiscreteMemory::moveIt(mbegin, tbegin, - m_preTriggerDelay - m_maxTraceDelay);
processTraces(tbegin, m_maxTraceDelay, true);
}
if (m_preTriggerDelay > 0)
{ // pre-trigger
std::vector<SampleVector::const_iterator> tbegin(mbegin.size());
std::vector<ComplexVector::const_iterator> tbegin(mbegin.size());
TraceBackDiscreteMemory::moveIt(mbegin, tbegin, -m_preTriggerDelay);
processTraces(tbegin, m_preTriggerDelay);
}
@ -727,16 +754,13 @@ bool ScopeVis::nextTrigger()
}
}
int ScopeVis::processTraces(const std::vector<SampleVector::const_iterator>& vcbegin, int ilength, bool traceBack)
int ScopeVis::processTraces(const std::vector<ComplexVector::const_iterator>& vcbegin, int ilength, bool traceBack)
{
std::vector<SampleVector::const_iterator> vbegin(vcbegin);
std::vector<ComplexVector::const_iterator> vbegin(vcbegin);
uint32_t shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
uint32_t length = m_traceSize / m_timeBase;
int remainder = ilength;
if (m_spectrumVis) {
m_spectrumVis->feed(vcbegin[0], vcbegin[0] + ilength, false); // TODO: use spectrum stream index
}
m_spectrumVis->feed(vcbegin[0], vcbegin[0] + ilength, false);
while ((remainder > 0) && (m_nbSamples > 0))
{
@ -797,8 +821,8 @@ int ScopeVis::processTraces(const std::vector<SampleVector::const_iterator>& vcb
}
else if (projectionType == Projector::ProjectionMagDB)
{
Real re = vbegin[streamIndex]->m_real / SDR_RX_SCALEF;
Real im = vbegin[streamIndex]->m_imag / SDR_RX_SCALEF;
Real re = vbegin[streamIndex]->real();
Real im = vbegin[streamIndex]->imag();
double magsq = re*re + im*im;
float pdB = log10f(magsq) * 10.0f;
float p = pdB - (100.0f * itData->m_ofs);

View File

@ -432,6 +432,7 @@ public:
uint32_t getNbTraces() const { return m_traces.size(); }
void feed(const std::vector<SampleVector::const_iterator>& vbegin, int nbSamples);
void feed(const std::vector<ComplexVector::const_iterator>& vbegin, int nbSamples);
//virtual void start();
//virtual void stop();
bool handleMessage(const Message& message);
@ -510,7 +511,7 @@ private:
/**
* Complex trace stuff
*/
typedef DoubleBufferSimple<Sample> TraceBuffer;
typedef DoubleBufferSimple<Complex> TraceBuffer;
struct TraceBackBuffer
{
@ -528,7 +529,7 @@ private:
m_traceBuffer.reset();
}
void write(const SampleVector::const_iterator begin, int nbSamples) {
void write(const ComplexVector::const_iterator begin, int nbSamples) {
m_traceBuffer.write(begin, nbSamples);
}
@ -536,7 +537,7 @@ private:
return m_traceBuffer.absoluteFill();
}
void current(SampleVector::iterator& it) {
void current(ComplexVector::iterator& it) {
m_traceBuffer.getCurrent(it);
}
@ -578,24 +579,57 @@ private:
}
}
void setEndPoint(const SampleVector::const_iterator& endPoint) {
void setEndPoint(const ComplexVector::const_iterator& endPoint) {
m_endPoint = endPoint;
}
SampleVector::const_iterator getEndPoint() {
ComplexVector::const_iterator getEndPoint() {
return m_endPoint;
}
void getEndPoint(SampleVector::const_iterator& it) {
void getEndPoint(ComplexVector::const_iterator& it) {
it = m_endPoint;
}
private:
SampleVector::const_iterator m_endPoint;
ComplexVector::const_iterator m_endPoint;
};
typedef std::vector<TraceBackBuffer> TraceBackBufferStream;
struct ConvertBuffers
{
ConvertBuffers(uint32_t nbStreams = 1) :
m_convertBuffers(nbStreams)
{}
void setNbStreams(uint32_t nbStreams)
{
m_convertBuffers.resize(nbStreams);
resize(m_size);
}
void resize(unsigned int size)
{
for (unsigned int s = 0; s < m_convertBuffers.size(); s++) {
m_convertBuffers[s].resize(size);
}
m_size = size;
}
unsigned int size() const {
return m_size;
}
std::vector<ComplexVector>& getBuffers() {
return m_convertBuffers;
}
private:
unsigned int m_size;
std::vector<ComplexVector> m_convertBuffers;
};
struct TraceBackDiscreteMemory
{
/**
@ -747,20 +781,20 @@ private:
/**
* Get current point at current memory position (first stream)
*/
void getCurrent(SampleVector::iterator& it) {
void getCurrent(ComplexVector::iterator& it) {
current().current(it);
}
/**
* Get current points at current memory position
*/
void getCurrent(std::vector<SampleVector::const_iterator>& vit)
void getCurrent(std::vector<ComplexVector::const_iterator>& vit)
{
vit.clear();
for (unsigned int is = 0; is < m_traceBackBuffersStreams.size(); is++)
{
SampleVector::iterator it;
ComplexVector::iterator it;
current(is).current(it);
vit.push_back(it);
}
@ -769,14 +803,14 @@ private:
/**
* Set end point at current memory position (first stream)
*/
void setCurrentEndPoint(const SampleVector::iterator& it) {
void setCurrentEndPoint(const ComplexVector::iterator& it) {
current().setEndPoint(it);
}
/**
* Set end points at current memory position
*/
void setCurrentEndPoint(const std::vector<SampleVector::const_iterator>& vit)
void setCurrentEndPoint(const std::vector<ComplexVector::const_iterator>& vit)
{
for (unsigned int is = 0; is < vit.size(); is++)
{
@ -791,20 +825,20 @@ private:
/**
* Get end point at given memory position (first stream)
*/
void getEndPointAt(int index, SampleVector::const_iterator& mend) {
void getEndPointAt(int index, ComplexVector::const_iterator& mend) {
at(index).getEndPoint(mend);
}
/**
* Get end points at given memory position
*/
void getEndPointAt(int index, std::vector<SampleVector::const_iterator>& vend)
void getEndPointAt(int index, std::vector<ComplexVector::const_iterator>& vend)
{
vend.clear();
for (unsigned int is = 0; is < m_traceBackBuffersStreams.size(); is++)
{
SampleVector::const_iterator mend;
ComplexVector::const_iterator mend;
at(index, is).getEndPoint(mend);
vend.push_back(mend);
}
@ -813,14 +847,14 @@ private:
/**
* Write trace at current memory position (first stream)
*/
void writeCurrent(const SampleVector::const_iterator& begin, int length) {
void writeCurrent(const ComplexVector::const_iterator& begin, int length) {
current().write(begin, length);
}
/**
* Write traces at current memory position
*/
void writeCurrent(const std::vector<SampleVector::const_iterator>& vbegin, int length)
void writeCurrent(const std::vector<ComplexVector::const_iterator>& vbegin, int length)
{
for (unsigned int i = 0; i < vbegin.size(); i++) {
current().write(vbegin[i], length);
@ -830,14 +864,14 @@ private:
/**
* Move buffer iterator by a certain amount (first stream)
*/
static void moveIt(const SampleVector::const_iterator& x, SampleVector::const_iterator& y, int amount) {
static void moveIt(const ComplexVector::const_iterator& x, ComplexVector::const_iterator& y, int amount) {
y = x + amount;
}
/**
* Move buffers iterators by a certain amount
*/
static void moveIt(const std::vector<SampleVector::const_iterator>& vx, std::vector<SampleVector::const_iterator>& vy, int amount)
static void moveIt(const std::vector<ComplexVector::const_iterator>& vx, std::vector<ComplexVector::const_iterator>& vy, int amount)
{
for (unsigned int i = 0; i < vx.size(); i++)
{
@ -1083,7 +1117,7 @@ private:
computeLevels();
}
bool triggered(const Sample& s, TriggerCondition& triggerCondition)
bool triggered(const Complex& s, TriggerCondition& triggerCondition)
{
if (triggerCondition.m_triggerData.m_triggerLevel != m_level)
{
@ -1194,7 +1228,8 @@ private:
int m_triggerLocation; //!< Trigger location from end point
int m_sampleRate; //!< Actual sample rate being used
int m_liveSampleRate; //!< Sample rate in live mode
TraceBackDiscreteMemory m_traceDiscreteMemory; //!< Complex trace memory for triggered states TODO: vectorize when more than on input is allowed
TraceBackDiscreteMemory m_traceDiscreteMemory; //!< Complex trace memory
ConvertBuffers m_convertBuffers; //!< Sample to Complex conversions
bool m_freeRun; //!< True if free running (trigger globally disabled)
int m_maxTraceDelay; //!< Maximum trace delay
TriggerComparator m_triggerComparator; //!< Compares sample level to trigger level
@ -1227,7 +1262,7 @@ private:
/**
* Process a sample trace which length is at most the trace length (m_traceSize)
*/
void processTrace(const std::vector<SampleVector::const_iterator>& vbegin, int length, int& triggerPointToEnd);
void processTrace(const std::vector<ComplexVector::const_iterator>& vbegin, int length, int& triggerPointToEnd);
/**
* process a trace in memory at current trace index in memory
@ -1239,7 +1274,7 @@ private:
* - if finished it returns the number of unprocessed samples left in the buffer
* - if not finished it returns -1
*/
int processTraces(const std::vector<SampleVector::const_iterator>& vbegin, int length, bool traceBack = false);
int processTraces(const std::vector<ComplexVector::const_iterator>& vbegin, int length, bool traceBack = false);
/**
* Get maximum trace delay

View File

@ -317,6 +317,56 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length)
m_mutex.unlock();
}
void SpectrumVis::feed(const ComplexVector::const_iterator& cbegin, const ComplexVector::const_iterator& end, bool positiveOnly)
{
if (!m_running) {
return;
}
// if no visualisation is set, send the samples to /dev/null
if (!m_glSpectrum && !m_wsSpectrum.socketOpened()) {
return;
}
if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
return;
}
ComplexVector::const_iterator begin(cbegin);
while (begin < end)
{
std::size_t todo = end - begin;
std::size_t samplesNeeded = m_refillSize - m_fftBufferFill;
if (todo >= samplesNeeded)
{
// fill up the buffer
std::copy(begin, begin + samplesNeeded, m_fftBuffer.begin() + m_fftBufferFill);
begin += samplesNeeded;
processFFT(positiveOnly);
// advance buffer respecting the fft overlap factor
std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin());
// start over
m_fftBufferFill = m_overlapSize;
m_needMoreSamples = false;
}
else
{
// not enough samples for FFT - just fill in new data and return
std::copy(begin, end, m_fftBuffer.begin() + m_fftBufferFill);
begin = end;
m_fftBufferFill += todo;
m_needMoreSamples = true;
}
}
m_mutex.unlock();
}
void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool positiveOnly)
{
if (!m_running) {
@ -333,10 +383,6 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
}
SampleVector::const_iterator begin(cbegin);
int fftMin = (m_frequencyZoomFactor == 1.0f) ?
0 : (m_frequencyZoomPos - (0.5f / m_frequencyZoomFactor)) * m_settings.m_fftSize;
int fftMax = (m_frequencyZoomFactor == 1.0f) ?
m_settings.m_fftSize : (m_frequencyZoomPos + (0.5f / m_frequencyZoomFactor)) * m_settings.m_fftSize;
while (begin < end)
{
@ -348,316 +394,11 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// fill up the buffer
std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill;
for (std::size_t i = 0; i < samplesNeeded; ++i, ++begin)
{
for (std::size_t i = 0; i < samplesNeeded; ++i, ++begin) {
*it++ = Complex(begin->real() / m_scalef, begin->imag() / m_scalef);
}
// apply fft window (and copy from m_fftBuffer to m_fftIn)
m_window.apply(&m_fftBuffer[0], m_fft->in());
// calculate FFT
m_fft->transform();
// extract power spectrum and reorder buckets
const Complex* fftOut = m_fft->out();
Complex c;
Real v;
std::size_t halfSize = m_settings.m_fftSize / 2;
if (m_settings.m_averagingMode == SpectrumSettings::AvgModeNone)
{
m_specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v;
m_powerSpectrum[i * 2 + 1] = v;
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v;
}
}
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeMoving)
{
m_specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v;
m_powerSpectrum[i * 2 + 1] = v;
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i+halfSize);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v;
}
}
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
m_movingAverage.nextAverage();
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeFixed)
{
double avg;
Real specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i * 2] = avg;
m_powerSpectrum[i * 2 + 1] = avg;
}
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
{
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg;
}
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
m_psd[i + halfSize] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i + halfSize] = avg;
}
}
}
// result available
if (m_fixedAverage.nextAverage())
{
m_specMax = specMax;
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeMax)
{
double max;
Real specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_max.storeAndGetMax(max, v, i))
{
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.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();
// result available
if (m_max.storeAndGetMax(max, v, i+halfSize))
{
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.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();
// result available
if (m_max.storeAndGetMax(max, v, i))
{
m_psd[i + halfSize] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i + halfSize] = max;
}
}
}
// result available
if (m_max.nextMax())
{
m_specMax = specMax;
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
}
processFFT(positiveOnly);
// advance buffer respecting the fft overlap factor
std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin());
@ -669,8 +410,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
else
{
// not enough samples for FFT - just fill in new data and return
for(std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin)
{
for (std::vector<Complex>::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin) {
*it++ = Complex(begin->real() / m_scalef, begin->imag() / m_scalef);
}
@ -679,7 +419,321 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
}
}
m_mutex.unlock();
m_mutex.unlock();
}
void SpectrumVis::processFFT(bool positiveOnly)
{
int fftMin = (m_frequencyZoomFactor == 1.0f) ?
0 : (m_frequencyZoomPos - (0.5f / m_frequencyZoomFactor)) * m_settings.m_fftSize;
int fftMax = (m_frequencyZoomFactor == 1.0f) ?
m_settings.m_fftSize : (m_frequencyZoomPos + (0.5f / m_frequencyZoomFactor)) * m_settings.m_fftSize;
// apply fft window (and copy from m_fftBuffer to m_fftIn)
m_window.apply(&m_fftBuffer[0], m_fft->in());
// calculate FFT
m_fft->transform();
// extract power spectrum and reorder buckets
const Complex* fftOut = m_fft->out();
Complex c;
Real v;
std::size_t halfSize = m_settings.m_fftSize / 2;
if (m_settings.m_averagingMode == SpectrumSettings::AvgModeNone)
{
m_specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v;
m_powerSpectrum[i * 2 + 1] = v;
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v;
}
}
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeMoving)
{
m_specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v;
m_powerSpectrum[i * 2 + 1] = v;
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i+halfSize);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v;
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v;
}
}
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
m_movingAverage.nextAverage();
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeFixed)
{
double avg;
Real specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i * 2] = avg;
m_powerSpectrum[i * 2 + 1] = avg;
}
}
}
else
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
{
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg;
}
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{
m_psd[i + halfSize] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i + halfSize] = avg;
}
}
}
// result available
if (m_fixedAverage.nextAverage())
{
m_specMax = specMax;
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
}
else if (m_settings.m_averagingMode == SpectrumSettings::AvgModeMax)
{
double max;
Real specMax = 0.0f;
if ( positiveOnly )
{
for (std::size_t i = 0; i < halfSize; i++)
{
c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag();
// result available
if (m_max.storeAndGetMax(max, v, i))
{
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.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();
// result available
if (m_max.storeAndGetMax(max, v, i+halfSize))
{
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.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();
// result available
if (m_max.storeAndGetMax(max, v, i))
{
m_psd[i + halfSize] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax;
max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i + halfSize] = max;
}
}
}
// result available
if (m_max.nextMax())
{
m_specMax = specMax;
// send new data to visualisation
if (m_glSpectrum)
{
m_glSpectrum->newSpectrum(
&m_powerSpectrum.data()[fftMin],
fftMax - fftMin,
m_settings.m_fftSize
);
}
// web socket spectrum connections
if (m_wsSpectrum.socketOpened())
{
m_wsSpectrum.newSpectrum(
m_powerSpectrum,
m_settings.m_fftSize,
m_centerFrequency,
m_sampleRate,
m_settings.m_linear,
m_settings.m_ssb,
m_settings.m_usb
);
}
}
}
}
void SpectrumVis::getZoomedPSDCopy(std::vector<Real>& copy) const

View File

@ -151,6 +151,7 @@ public:
void getZoomedPSDCopy(std::vector<Real>& copy) const;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
void feed(const ComplexVector::const_iterator& begin, const ComplexVector::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();
@ -243,6 +244,7 @@ private:
QMutex m_mutex;
void processFFT(bool positiveOnly);
void setRunning(bool running) { m_running = running; }
void applySettings(const SpectrumSettings& settings, bool force = false);
void handleConfigureDSP(uint64_t centerFrequency, int sampleRate);