diff --git a/sdrbase/dsp/dsptypes.h b/sdrbase/dsp/dsptypes.h index c1b9728e0..9cea4c049 100644 --- a/sdrbase/dsp/dsptypes.h +++ b/sdrbase/dsp/dsptypes.h @@ -41,6 +41,7 @@ typedef qint16 FixReal; typedef float Real; typedef std::complex Complex; +typedef std::vector ComplexVector; #pragma pack(push, 1) struct Sample diff --git a/sdrbase/dsp/scopevis.cpp b/sdrbase/dsp/scopevis.cpp index ca9775cd0..f507aaeaf 100644 --- a/sdrbase/dsp/scopevis.cpp +++ b/sdrbase/dsp/scopevis.cpp @@ -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& vbegin, int nbSamples) +{ + std::vector vcbegin; + std::vector& 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& vbegin, int nbSamples) { if (vbegin.size() == 0) { return; @@ -447,11 +474,11 @@ void ScopeVis::feed(const std::vector& 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 nvbegin(vbegin); + std::vector nvbegin(vbegin); //while (begin < end) while (remainder > 0) @@ -495,11 +522,11 @@ void ScopeVis::processMemoryTrace() traceMemoryIndex += GLScopeSettings::m_nbTraceMemories; } - std::vector mend; + std::vector mend; m_traceDiscreteMemory.getEndPointAt(traceMemoryIndex, mend); - std::vector mbegin(mend.size()); + std::vector mbegin(mend.size()); TraceBackDiscreteMemory::moveIt(mend, mbegin, -m_traceSize); - std::vector mbegin_tb(mbegin.size()); + std::vector 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& vcbegin, int length, int& triggerPointToEnd) +void ScopeVis::processTrace(const std::vector& vcbegin, int length, int& triggerPointToEnd) { - std::vector vbegin(vcbegin); + std::vector vbegin(vcbegin); int firstRemainder = length; // memory storage @@ -579,7 +606,7 @@ void ScopeVis::processTrace(const std::vector& 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& vcb { int remainder; int count = firstRemainder; // number of samples in traceback buffer past the current point - std::vector mend; + std::vector mend; m_traceDiscreteMemory.getCurrent(mend); - std::vector mbegin(mend.size()); + std::vector 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& vcb if (m_maxTraceDelay > 0) { // trace back - std::vector tbegin(mbegin.size()); + std::vector tbegin(mbegin.size()); TraceBackDiscreteMemory::moveIt(mbegin, tbegin, - m_preTriggerDelay - m_maxTraceDelay); processTraces(tbegin, m_maxTraceDelay, true); } if (m_preTriggerDelay > 0) { // pre-trigger - std::vector tbegin(mbegin.size()); + std::vector 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& vcbegin, int ilength, bool traceBack) +int ScopeVis::processTraces(const std::vector& vcbegin, int ilength, bool traceBack) { - std::vector vbegin(vcbegin); + std::vector 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& 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); diff --git a/sdrbase/dsp/scopevis.h b/sdrbase/dsp/scopevis.h index ff67a47ad..8bdcae718 100644 --- a/sdrbase/dsp/scopevis.h +++ b/sdrbase/dsp/scopevis.h @@ -432,6 +432,7 @@ public: uint32_t getNbTraces() const { return m_traces.size(); } void feed(const std::vector& vbegin, int nbSamples); + void feed(const std::vector& vbegin, int nbSamples); //virtual void start(); //virtual void stop(); bool handleMessage(const Message& message); @@ -510,7 +511,7 @@ private: /** * Complex trace stuff */ - typedef DoubleBufferSimple TraceBuffer; + typedef DoubleBufferSimple 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 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& getBuffers() { + return m_convertBuffers; + } + + private: + unsigned int m_size; + std::vector 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& vit) + void getCurrent(std::vector& 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& vit) + void setCurrentEndPoint(const std::vector& 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& vend) + void getEndPointAt(int index, std::vector& 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& vbegin, int length) + void writeCurrent(const std::vector& 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& vx, std::vector& vy, int amount) + static void moveIt(const std::vector& vx, std::vector& 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& vbegin, int length, int& triggerPointToEnd); + void processTrace(const std::vector& 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& vbegin, int length, bool traceBack = false); + int processTraces(const std::vector& vbegin, int length, bool traceBack = false); /** * Get maximum trace delay diff --git a/sdrbase/dsp/spectrumvis.cpp b/sdrbase/dsp/spectrumvis.cpp index 07cd692b1..093a157c5 100644 --- a/sdrbase/dsp/spectrumvis.cpp +++ b/sdrbase/dsp/spectrumvis.cpp @@ -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::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::iterator it = m_fftBuffer.begin() + m_fftBufferFill; begin < end; ++begin) - { + for (std::vector::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& copy) const diff --git a/sdrbase/dsp/spectrumvis.h b/sdrbase/dsp/spectrumvis.h index 3006d71d0..26194ab81 100644 --- a/sdrbase/dsp/spectrumvis.h +++ b/sdrbase/dsp/spectrumvis.h @@ -151,6 +151,7 @@ public: void getZoomedPSDCopy(std::vector& 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);