1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-08 17:46:03 -05:00
sdrangel/sdrbase/dsp/scopevis.cpp

1142 lines
40 KiB
C++
Raw Normal View History

2017-01-29 13:51:45 -05:00
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 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 //
// (at your option) any later version. //
2017-01-29 13:51:45 -05:00
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
2020-11-04 16:52:15 -05:00
#include <QtGlobal>
2017-01-29 13:51:45 -05:00
#include <QDebug>
#include <QMutexLocker>
2018-08-12 11:22:39 -04:00
#include "scopevis.h"
#include "spectrumvis.h"
2017-01-31 02:26:13 -05:00
#include "dsp/dspcommands.h"
#include "dsp/glscopeinterface.h"
2017-01-29 13:51:45 -05:00
2018-08-12 11:18:58 -04:00
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgConfigureScopeVisNG, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGAddTrigger, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGChangeTrigger, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGRemoveTrigger, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGMoveTrigger, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGFocusOnTrigger, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGAddTrace, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGChangeTrace, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGRemoveTrace, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGMoveTrace, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGFocusOnTrace, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGOneShot, Message)
MESSAGE_CLASS_DEFINITION(ScopeVis::MsgScopeVisNGMemoryTrace, Message)
ScopeVis::ScopeVis() :
m_glScope(nullptr),
m_spectrumVis(nullptr),
m_messageQueueToGUI(nullptr),
m_preTriggerDelay(0),
m_livePreTriggerDelay(0),
m_currentTriggerIndex(0),
m_focusedTriggerIndex(0),
m_triggerState(TriggerUntriggered),
m_focusedTraceIndex(0),
m_nbStreams(1),
m_traceChunkSize(GLScopeSettings::m_traceChunkDefaultSize),
m_traceSize(GLScopeSettings::m_traceChunkDefaultSize),
m_liveTraceSize(GLScopeSettings::m_traceChunkDefaultSize),
m_nbSamples(0),
2017-05-25 14:13:34 -04:00
m_timeBase(1),
m_timeOfsProMill(0),
m_traceStart(true),
m_triggerLocation(0),
m_sampleRate(0),
m_liveSampleRate(0),
m_traceDiscreteMemory(GLScopeSettings::m_nbTraceMemories),
m_freeRun(true),
2017-02-21 19:18:50 -05:00
m_maxTraceDelay(0),
m_triggerOneShot(false),
m_triggerWaitForReset(false),
m_currentTraceMemoryIndex(0)
2017-01-29 13:51:45 -05:00
{
2018-08-12 11:18:58 -04:00
setObjectName("ScopeVis");
m_traceDiscreteMemory.resize(GLScopeSettings::m_traceChunkDefaultSize); // arbitrary
for (int i = 0; i < (int) Projector::nbProjectionTypes; i++) {
m_projectorCache[i] = 0.0;
}
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
2017-01-29 13:51:45 -05:00
}
2018-08-12 11:18:58 -04:00
ScopeVis::~ScopeVis()
2017-01-29 13:51:45 -05:00
{
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
for (std::vector<TriggerCondition*>::iterator it = m_triggerConditions.begin(); it != m_triggerConditions.end(); ++ it) {
delete *it;
}
2017-01-29 13:51:45 -05:00
}
void ScopeVis::setGLScope(GLScopeInterface* glScope)
{
m_glScope = glScope;
m_glScope->setTraces(&m_traces.m_tracesData, &m_traces.m_traces[0]);
}
void ScopeVis::setLiveRate(int sampleRate)
{
m_liveSampleRate = sampleRate;
if (m_currentTraceMemoryIndex == 0) { // update only in live mode
setSampleRate(m_liveSampleRate);
}
}
2018-08-12 11:18:58 -04:00
void ScopeVis::setSampleRate(int sampleRate)
2017-01-29 16:52:38 -05:00
{
qDebug("ScopeVis::setSampleRate: %d S/s", sampleRate);
m_sampleRate = sampleRate;
if (m_glScope) {
m_glScope->setSampleRate(m_sampleRate);
2017-01-29 16:52:38 -05:00
}
}
void ScopeVis::setTraceSize(uint32_t traceSize, bool emitSignal)
{
m_traceSize = traceSize;
m_traces.resize(m_traceSize);
m_traceDiscreteMemory.resize(m_traceSize);
initTraceBuffers();
if (m_glScope) {
m_glScope->setTraceSize(m_traceSize, emitSignal);
}
}
void ScopeVis::setPreTriggerDelay(uint32_t preTriggerDelay, bool emitSignal)
{
m_preTriggerDelay = preTriggerDelay;
if (m_glScope) {
m_glScope->setTriggerPre(m_preTriggerDelay, emitSignal);
}
}
void ScopeVis::configure(
uint32_t nbStreams,
uint32_t traceSize,
uint32_t timeBase,
uint32_t timeOfsProMill,
uint32_t triggerPre,
bool freeRun
)
2017-01-29 13:51:45 -05:00
{
Message* cmd = MsgConfigureScopeVisNG::create(nbStreams, traceSize, timeBase, timeOfsProMill, triggerPre, freeRun);
getInputMessageQueue()->push(cmd);
}
2021-05-28 18:55:04 -04:00
void ScopeVis::addTrace(const GLScopeSettings::TraceData& traceData)
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::addTrace:"
2017-02-20 16:19:50 -05:00
<< " m_amp: " << traceData.m_amp
<< " m_ofs: " << traceData.m_ofs
<< " m_traceDelay: " << traceData.m_traceDelay;
Message* cmd = MsgScopeVisNGAddTrace::create(traceData);
getInputMessageQueue()->push(cmd);
}
2021-05-28 18:55:04 -04:00
void ScopeVis::changeTrace(const GLScopeSettings::TraceData& traceData, uint32_t traceIndex)
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::changeTrace:"
<< " trace: " << traceIndex
<< " m_amp: " << traceData.m_amp
2017-02-16 19:54:55 -05:00
<< " m_ofs: " << traceData.m_ofs
<< " m_traceDelay: " << traceData.m_traceDelay;
Message* cmd = MsgScopeVisNGChangeTrace::create(traceData, traceIndex);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::removeTrace(uint32_t traceIndex)
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::removeTrace:"
2017-02-20 16:19:50 -05:00
<< " trace: " << traceIndex;
Message* cmd = MsgScopeVisNGRemoveTrace::create(traceIndex);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::moveTrace(uint32_t traceIndex, bool upElseDown)
2017-02-26 05:26:23 -05:00
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::moveTrace:"
2017-02-26 05:26:23 -05:00
<< " trace: " << traceIndex
<< " up: " << upElseDown;
Message* cmd = MsgScopeVisNGMoveTrace::create(traceIndex, upElseDown);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::focusOnTrace(uint32_t traceIndex)
{
Message* cmd = MsgScopeVisNGFocusOnTrace::create(traceIndex);
getInputMessageQueue()->push(cmd);
}
void ScopeVis::addTrigger(const GLScopeSettings::TriggerData& triggerData)
{
Message* cmd = MsgScopeVisNGAddTrigger::create(triggerData);
getInputMessageQueue()->push(cmd);
}
void ScopeVis::changeTrigger(const GLScopeSettings::TriggerData& triggerData, uint32_t triggerIndex)
{
Message* cmd = MsgScopeVisNGChangeTrigger::create(triggerData, triggerIndex);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::removeTrigger(uint32_t triggerIndex)
{
Message* cmd = MsgScopeVisNGRemoveTrigger::create(triggerIndex);
getInputMessageQueue()->push(cmd);
2017-01-29 13:51:45 -05:00
}
2018-08-12 11:18:58 -04:00
void ScopeVis::moveTrigger(uint32_t triggerIndex, bool upElseDown)
2017-02-26 05:26:23 -05:00
{
Message* cmd = MsgScopeVisNGMoveTrigger::create(triggerIndex, upElseDown);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::focusOnTrigger(uint32_t triggerIndex)
2017-02-11 04:36:10 -05:00
{
Message* cmd = MsgScopeVisNGFocusOnTrigger::create(triggerIndex);
getInputMessageQueue()->push(cmd);
}
2018-08-12 11:18:58 -04:00
void ScopeVis::setOneShot(bool oneShot)
2017-02-21 19:18:50 -05:00
{
Message* cmd = MsgScopeVisNGOneShot::create(oneShot);
getInputMessageQueue()->push(cmd);
}
2017-01-29 13:51:45 -05:00
2018-08-12 11:18:58 -04:00
void ScopeVis::setMemoryIndex(uint32_t memoryIndex)
{
Message* cmd = MsgScopeVisNGMemoryTrace::create(memoryIndex);
getInputMessageQueue()->push(cmd);
}
void ScopeVis::feed(const std::vector<SampleVector::const_iterator>& vbegin, int nbSamples)
2017-01-29 13:51:45 -05:00
{
if (vbegin.size() == 0) {
return;
}
if (m_currentTraceMemoryIndex > 0) { // in memory mode live trace is suspended
return;
}
if (!m_mutex.tryLock(0)) { // prevent conflicts with configuration process
return;
}
if (m_triggerWaitForReset)
{
m_triggerLocation = 0;
m_mutex.unlock();
return;
}
2017-02-05 20:40:31 -05:00
if (m_freeRun) {
m_triggerLocation = nbSamples;
2017-01-29 13:51:45 -05:00
}
else if (m_triggerState == TriggerTriggered) {
m_triggerLocation = nbSamples;
2017-01-29 13:51:45 -05:00
}
else if (m_triggerState == TriggerUntriggered) {
m_triggerLocation = 0;
2017-01-29 13:51:45 -05:00
}
else {
m_triggerLocation = nbSamples;
2017-01-29 13:51:45 -05:00
}
SampleVector::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);
//while (begin < end)
while (remainder > 0)
{
if (remainder < (int) m_traceSize) // buffer smaller than trace size (end - bagin) < m_traceSize
{
triggerPointToEnd = -1;
processTrace(nvbegin, remainder, triggerPointToEnd); // use all buffer
m_triggerLocation = triggerPointToEnd < 0 ? 0 : triggerPointToEnd; // trim negative values
m_triggerLocation = m_triggerLocation > remainder ? remainder : m_triggerLocation; // trim past begin values
remainder = 0; // effectively breaks out the loop
}
else // trace size fits in buffer
{
triggerPointToEnd = -1;
processTrace(nvbegin, m_traceSize, triggerPointToEnd); // use part of buffer to fit trace size
//m_triggerPoint = begin + m_traceSize - triggerPointToEnd;
m_triggerLocation = remainder + m_traceSize - triggerPointToEnd; // should always refer to end iterator
m_triggerLocation = m_triggerLocation < 0 ? 0 : m_triggerLocation; // trim negative values
m_triggerLocation = m_triggerLocation > remainder ? remainder : m_triggerLocation; // trim past begin values
for (auto begin : nvbegin) {
begin += m_traceSize;
}
remainder -= m_traceSize;
}
}
m_mutex.unlock();
}
2018-08-12 11:18:58 -04:00
void ScopeVis::processMemoryTrace()
{
if ((m_currentTraceMemoryIndex > 0) && (m_currentTraceMemoryIndex < GLScopeSettings::m_nbTraceMemories))
{
int traceMemoryIndex = m_traceDiscreteMemory.currentIndex() - m_currentTraceMemoryIndex; // actual index in memory bank
if (traceMemoryIndex < 0) {
traceMemoryIndex += GLScopeSettings::m_nbTraceMemories;
}
std::vector<SampleVector::const_iterator> mend;
m_traceDiscreteMemory.getEndPointAt(traceMemoryIndex, mend);
std::vector<SampleVector::const_iterator> mbegin(mend.size());
TraceBackDiscreteMemory::moveIt(mend, mbegin, -m_traceSize);
std::vector<SampleVector::const_iterator> mbegin_tb(mbegin.size());
TraceBackDiscreteMemory::moveIt(mbegin, mbegin_tb, -m_maxTraceDelay);
2017-02-24 17:24:47 -05:00
m_nbSamples = m_traceSize + m_maxTraceDelay;
processTraces(mbegin_tb, m_maxTraceDelay, true); // traceback
processTraces(mbegin, m_traceSize, false);
}
}
void ScopeVis::processTrace(const std::vector<SampleVector::const_iterator>& vcbegin, int length, int& triggerPointToEnd)
{
std::vector<SampleVector::const_iterator> vbegin(vcbegin);
int firstRemainder = length;
// memory storage
2017-02-01 12:31:16 -05:00
m_traceDiscreteMemory.writeCurrent(vbegin, length);
2017-02-01 12:31:16 -05:00
// Removed in 4.2.4 may cause trigger bug
// if (m_traceDiscreteMemory.current().absoluteFill() < m_traceSize)
// {
// return; // not enough samples in memory
// }
2017-01-29 13:51:45 -05:00
2017-02-05 20:40:31 -05:00
// trigger process
2017-02-01 12:31:16 -05:00
if ((m_freeRun) || (m_triggerConditions.size() == 0)) // immediate re-trigger
{
if (m_triggerState == TriggerUntriggered)
{
m_traceStart = true; // start trace processing
m_nbSamples = m_traceSize + m_maxTraceDelay;
m_triggerState = TriggerTriggered;
}
}
else if ((m_triggerState == TriggerUntriggered) || (m_triggerState == TriggerDelay)) // look for trigger or past trigger in delay mode
{
TriggerCondition* triggerCondition = m_triggerConditions[m_currentTriggerIndex]; // current trigger condition
int processed = 0;
while (firstRemainder > 0)
{
if (m_triggerState == TriggerDelay) // delayed trigger
{
if (triggerCondition->m_triggerDelayCount > 0) // skip samples during delay period
{
for (auto begin : vbegin) {
begin += triggerCondition->m_triggerDelayCount;
}
processed += triggerCondition->m_triggerDelayCount;
firstRemainder -= triggerCondition->m_triggerDelayCount;
triggerCondition->m_triggerDelayCount = 0;
continue;
}
else // process trigger
{
if (nextTrigger()) // move to next trigger and keep going
{
m_triggerComparator.reset();
m_triggerState = TriggerUntriggered;
for (auto begin : vbegin) {
++begin;
}
++processed;
--firstRemainder;
continue;
}
else // this was the last trigger then start trace
{
m_traceStart = true; // start trace processing
m_nbSamples = m_traceSize + m_maxTraceDelay;
m_triggerComparator.reset();
m_triggerState = TriggerTriggered;
triggerPointToEnd = firstRemainder;
break;
}
}
}
uint32_t triggerStreamIndex = triggerCondition->m_triggerData.m_streamIndex;
const Sample& s = *vbegin[triggerStreamIndex];
if (m_triggerComparator.triggered(s, *triggerCondition)) // matched the current trigger
{
if (triggerCondition->m_triggerData.m_triggerDelay > 0)
{
triggerCondition->m_triggerDelayCount = triggerCondition->m_triggerData.m_triggerDelay; // initialize delayed samples counter
m_triggerState = TriggerDelay;
for (auto begin : vbegin) {
++begin;
}
++processed;
--firstRemainder;
continue;
}
if (nextTrigger()) // move to next trigger and keep going
{
m_triggerComparator.reset();
m_triggerState = TriggerUntriggered;
}
else // this was the last trigger then start trace
{
m_traceStart = true; // start of trace processing
m_nbSamples = m_traceSize + m_maxTraceDelay;
m_triggerComparator.reset();
m_triggerState = TriggerTriggered;
triggerPointToEnd = firstRemainder;
break;
}
}
for (auto begin : vbegin) {
++begin;
}
++processed;
--firstRemainder;
} // look for trigger
} // untriggered or delayed
2017-01-29 13:51:45 -05:00
// trace process
if (m_triggerState == TriggerTriggered)
{
int remainder;
int count = firstRemainder; // number of samples in traceback buffer past the current point
std::vector<SampleVector::const_iterator> mend;
m_traceDiscreteMemory.getCurrent(mend);
std::vector<SampleVector::const_iterator> mbegin(mend.size());
TraceBackDiscreteMemory::moveIt(mend, mbegin, -count);
if (m_traceStart) // start of trace processing
{
// if trace time is 1s or more the display is progressive so we have to clear it first
float traceTime = ((float) m_traceSize) / m_sampleRate;
if (traceTime >= 1.0f) {
initTraceBuffers();
}
// process until begin point
if (m_maxTraceDelay > 0)
{ // trace back
std::vector<SampleVector::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());
TraceBackDiscreteMemory::moveIt(mbegin, tbegin, -m_preTriggerDelay);
processTraces(tbegin, m_preTriggerDelay);
}
// process the rest of the trace
remainder = processTraces(mbegin, count);
m_traceStart = false;
}
else // process the current trace
2017-02-02 02:27:49 -05:00
{
remainder = processTraces(mbegin, count);
2017-02-02 02:27:49 -05:00
}
2017-02-01 12:31:16 -05:00
if (remainder >= 0) // finished
{
TraceBackDiscreteMemory::moveIt(mend, mbegin, -remainder);
m_traceDiscreteMemory.setCurrentEndPoint(mbegin);
m_traceDiscreteMemory.store(m_preTriggerDelay+remainder); // next memory trace.
2017-02-07 01:45:42 -05:00
m_triggerState = TriggerUntriggered;
2017-02-21 19:18:50 -05:00
m_triggerWaitForReset = m_triggerOneShot;
//if (m_glScope) m_glScope->incrementTraceCounter();
2017-01-29 13:51:45 -05:00
2017-02-08 18:04:19 -05:00
// process remainder recursively
if (remainder != 0)
{
int mTriggerPointToEnd = -1;
processTrace(mbegin, remainder, mTriggerPointToEnd);
if (mTriggerPointToEnd >= 0) {
triggerPointToEnd = mTriggerPointToEnd;
}
2018-08-12 11:18:58 -04:00
//qDebug("ScopeVis::processTrace: process remainder recursively (%d %d)", mpoint, mTriggerPoint);
2017-02-08 18:04:19 -05:00
}
}
}
2017-01-29 13:51:45 -05:00
}
2018-08-12 11:18:58 -04:00
bool ScopeVis::nextTrigger()
2017-02-01 12:31:16 -05:00
{
TriggerCondition *triggerCondition = m_triggerConditions[m_currentTriggerIndex]; // current trigger condition
if (triggerCondition->m_triggerData.m_triggerRepeat > 0)
{
if (triggerCondition->m_triggerCounter < triggerCondition->m_triggerData.m_triggerRepeat)
{
triggerCondition->m_triggerCounter++;
return true; // not final keep going
}
else
{
triggerCondition->m_triggerCounter = 0; // reset for next time
}
}
2017-05-25 14:13:34 -04:00
if (m_triggerConditions.size() == 0)
{
m_currentTriggerIndex = 0;
return false; // final
}
else if (m_currentTriggerIndex < m_triggerConditions.size() - 1) // check if next trigger is available
{
m_currentTriggerIndex++;
return true; // not final keep going
}
else
{
// now this is really finished
m_currentTriggerIndex = 0;
return false; // final
}
2017-02-01 12:31:16 -05:00
}
int ScopeVis::processTraces(const std::vector<SampleVector::const_iterator>& vcbegin, int ilength, bool traceBack)
2017-01-29 13:51:45 -05:00
{
std::vector<SampleVector::const_iterator> vbegin(vcbegin);
2017-05-25 14:13:34 -04:00
uint32_t shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
uint32_t length = m_traceSize / m_timeBase;
int remainder = ilength;
2017-01-29 13:51:45 -05:00
if (m_spectrumVis) {
m_spectrumVis->feed(vcbegin[0], vcbegin[0] + ilength, false); // TODO: use spectrum stream index
}
while ((remainder > 0) && (m_nbSamples > 0))
2017-01-29 13:51:45 -05:00
{
std::vector<TraceControl*>::iterator itCtl = m_traces.m_tracesControl.begin();
2021-05-28 18:55:04 -04:00
std::vector<GLScopeSettings::TraceData>::iterator itData = m_traces.m_tracesData.begin();
2017-02-05 20:40:31 -05:00
std::vector<float *>::iterator itTrace = m_traces.m_traces[m_traces.currentBufferIndex()].begin();
for (; itCtl != m_traces.m_tracesControl.end(); ++itCtl, ++itData, ++itTrace)
2017-02-02 02:27:49 -05:00
{
if (traceBack && ((remainder) > itData->m_traceDelay)) { // before start of trace
2017-02-02 02:27:49 -05:00
continue;
}
2017-01-29 13:51:45 -05:00
Projector::ProjectionType projectionType = itData->m_projectionType;
if ((*itCtl)->m_traceCount[m_traces.currentBufferIndex()] < m_traceSize)
2017-02-02 02:27:49 -05:00
{
uint32_t& traceCount = (*itCtl)->m_traceCount[m_traces.currentBufferIndex()]; // reference for code clarity
2017-02-06 18:25:40 -05:00
float v;
uint32_t streamIndex = itData->m_streamIndex;
2017-02-06 18:25:40 -05:00
if (projectionType == Projector::ProjectionMagLin)
{
v = ((*itCtl)->m_projector.run(*vbegin[streamIndex]) - itData->m_ofs)*itData->m_amp - 1.0f;
}
else if (projectionType == Projector::ProjectionMagSq)
{
Real magsq = (*itCtl)->m_projector.run(*vbegin[streamIndex]);
v = (magsq - itData->m_ofs)*itData->m_amp - 1.0f;
if ((traceCount >= shift) && (traceCount < shift+length)) // power display overlay values construction
{
if (traceCount == shift)
{
(*itCtl)->m_maxPow = 0.0f;
(*itCtl)->m_sumPow = 0.0f;
(*itCtl)->m_nbPow = 1;
}
if (magsq > 0.0f)
{
if (magsq > (*itCtl)->m_maxPow)
{
(*itCtl)->m_maxPow = magsq;
}
(*itCtl)->m_sumPow += magsq;
(*itCtl)->m_nbPow++;
}
}
if ((m_nbSamples == 1) && ((*itCtl)->m_nbPow > 0)) // on last sample create power display overlay
{
double avgPow = (*itCtl)->m_sumPow / (*itCtl)->m_nbPow;
itData->m_textOverlay = QString("%1 %2").arg((*itCtl)->m_maxPow, 0, 'e', 2).arg(avgPow, 0, 'e', 2);
(*itCtl)->m_nbPow = 0;
}
}
else if (projectionType == Projector::ProjectionMagDB)
{
Real re = vbegin[streamIndex]->m_real / SDR_RX_SCALEF;
Real im = vbegin[streamIndex]->m_imag / SDR_RX_SCALEF;
double magsq = re*re + im*im;
float pdB = log10f(magsq) * 10.0f;
float p = pdB - (100.0f * itData->m_ofs);
v = ((p/50.0f) + 2.0f)*itData->m_amp - 1.0f;
if ((traceCount >= shift) && (traceCount < shift+length)) // power display overlay values construction
{
if (traceCount == shift)
{
(*itCtl)->m_maxPow = 0.0f;
(*itCtl)->m_sumPow = 0.0f;
(*itCtl)->m_nbPow = 1;
}
if (magsq > 0.0f)
{
if (magsq > (*itCtl)->m_maxPow)
{
(*itCtl)->m_maxPow = magsq;
}
(*itCtl)->m_sumPow += magsq;
(*itCtl)->m_nbPow++;
}
}
if ((m_nbSamples == 1) && ((*itCtl)->m_nbPow > 0)) // on last sample create power display overlay
{
double avgPow = log10f((*itCtl)->m_sumPow / (*itCtl)->m_nbPow)*10.0;
double peakPow = log10f((*itCtl)->m_maxPow)*10.0;
double peakToAvgPow = peakPow - avgPow;
itData->m_textOverlay = QString("%1 %2 %3").arg(peakPow, 0, 'f', 1).arg(avgPow, 0, 'f', 1).arg(peakToAvgPow, 4, 'f', 1, ' ');
(*itCtl)->m_nbPow = 0;
}
}
else
{
v = ((*itCtl)->m_projector.run(*vbegin[streamIndex]) - itData->m_ofs) * itData->m_amp;
2017-02-06 18:25:40 -05:00
}
2017-02-02 02:27:49 -05:00
if(v > 1.0f) {
v = 1.0f;
} else if (v < -1.0f) {
v = -1.0f;
2017-02-02 02:27:49 -05:00
}
(*itTrace)[2*traceCount]
= traceCount - shift; // display x
(*itTrace)[2*traceCount + 1] = v; // display y
traceCount++;
2017-02-02 02:27:49 -05:00
}
}
2017-01-29 13:51:45 -05:00
for (unsigned int i = 0; i < vbegin.size(); i++) {
++vbegin[i];
}
remainder--;
m_nbSamples--;
2017-01-29 13:51:45 -05:00
}
2017-02-02 02:27:49 -05:00
float traceTime = ((float) m_traceSize) / m_sampleRate;
if (m_glScope && (traceTime >= 1.0f)) { // display continuously if trace time is 1 second or more
m_glScope->newTraces(m_traces.m_traces, m_traces.currentBufferIndex(), &m_traces.m_projectionTypes);
}
if (m_glScope && (m_nbSamples == 0)) // finished
2017-02-02 02:27:49 -05:00
{
// display only at trace end if trace time is less than 1 second
if (traceTime < 1.0f)
{
2020-11-04 16:52:15 -05:00
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
2020-11-04 02:59:16 -05:00
if (m_glScope->getProcessingTraceIndex().loadRelaxed() < 0) {
m_glScope->newTraces(m_traces.m_traces, m_traces.currentBufferIndex(), &m_traces.m_projectionTypes);
}
2020-11-04 16:52:15 -05:00
#else
if (m_glScope->getProcessingTraceIndex().load() < 0) {
m_glScope->newTraces(m_traces.m_traces, m_traces.currentBufferIndex(), &m_traces.m_projectionTypes);
}
#endif
}
// switch to next buffer only if it is not being processed by the scope
2020-11-04 16:52:15 -05:00
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
2020-11-04 02:59:16 -05:00
if (m_glScope->getProcessingTraceIndex().loadRelaxed() != (((int) m_traces.currentBufferIndex() + 1) % 2)) {
m_traces.switchBuffer();
}
2020-11-04 16:52:15 -05:00
#else
if (m_glScope->getProcessingTraceIndex().load() != (((int) m_traces.currentBufferIndex() + 1) % 2)) {
m_traces.switchBuffer();
}
#endif
return remainder; // return remainder count
2017-02-02 02:27:49 -05:00
}
else
{
return -1; // mark not finished
}
2017-02-04 22:41:32 -05:00
}
void ScopeVis::handleInputMessages()
2017-01-29 13:51:45 -05:00
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
2017-01-29 13:51:45 -05:00
}
2018-08-12 11:18:58 -04:00
bool ScopeVis::handleMessage(const Message& message)
2017-01-29 13:51:45 -05:00
{
2017-01-31 02:26:13 -05:00
if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (DSPSignalNotification&) message;
setLiveRate(notif.getSampleRate());
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: DSPSignalNotification: m_sampleRate: " << m_sampleRate;
2017-01-31 02:26:13 -05:00
return true;
}
else if (MsgConfigureScopeVisNG::match(message))
{
QMutexLocker configLocker(&m_mutex);
2017-01-31 02:26:13 -05:00
MsgConfigureScopeVisNG& conf = (MsgConfigureScopeVisNG&) message;
uint32_t nbStreams = conf.getNbStreams();
2017-02-04 22:41:32 -05:00
uint32_t traceSize = conf.getTraceSize();
2017-02-25 22:55:15 -05:00
uint32_t timeBase = conf.getTimeBase();
2017-02-04 22:41:32 -05:00
uint32_t timeOfsProMill = conf.getTimeOfsProMill();
2017-02-07 12:50:08 -05:00
uint32_t triggerPre = conf.getTriggerPre();
2017-02-05 20:40:31 -05:00
bool freeRun = conf.getFreeRun();
2017-02-04 22:41:32 -05:00
if (m_traceSize != traceSize) {
setTraceSize(traceSize);
2017-02-04 22:41:32 -05:00
}
if (m_nbStreams != nbStreams)
{
m_traceDiscreteMemory.setNbStreams(nbStreams);
m_nbStreams = nbStreams;
}
2017-02-25 22:55:15 -05:00
if (m_timeBase != timeBase)
{
m_timeBase = timeBase;
if (m_glScope) {
m_glScope->setTimeBase(m_timeBase);
}
}
2017-02-04 22:41:32 -05:00
if (m_timeOfsProMill != timeOfsProMill)
{
m_timeOfsProMill = timeOfsProMill;
if (m_glScope) {
m_glScope->setTimeOfsProMill(m_timeOfsProMill);
}
2017-02-03 02:27:03 -05:00
}
if (m_preTriggerDelay != triggerPre) {
setPreTriggerDelay(triggerPre);
2017-02-07 12:50:08 -05:00
}
if (freeRun != m_freeRun) {
2017-02-05 20:40:31 -05:00
m_freeRun = freeRun;
}
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgConfigureScopeVisNG:"
<< " m_nbStreams: " << m_nbStreams
<< " m_traceSize: " << m_traceSize
<< " m_timeOfsProMill: " << m_timeOfsProMill
<< " m_preTriggerDelay: " << m_preTriggerDelay
<< " m_freeRun: " << m_freeRun;
2017-02-03 02:27:03 -05:00
if ((m_glScope) && (m_currentTraceMemoryIndex > 0)) {
processMemoryTrace();
}
2017-02-03 02:27:03 -05:00
return true;
}
else if (MsgScopeVisNGAddTrigger::match(message))
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGAddTrigger";
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGAddTrigger& conf = (MsgScopeVisNGAddTrigger&) message;
m_triggerConditions.push_back(new TriggerCondition(conf.getTriggerData()));
m_triggerConditions.back()->initProjector();
2017-02-03 02:27:03 -05:00
return true;
}
else if (MsgScopeVisNGChangeTrigger::match(message))
{
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGChangeTrigger& conf = (MsgScopeVisNGChangeTrigger&) message;
2017-05-25 14:13:34 -04:00
uint32_t triggerIndex = conf.getTriggerIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGChangeTrigger: " << triggerIndex;
2017-02-03 02:27:03 -05:00
if (triggerIndex < m_triggerConditions.size())
{
m_triggerConditions[triggerIndex]->setData(conf.getTriggerData());
if (triggerIndex == m_focusedTriggerIndex)
{
computeDisplayTriggerLevels();
if (m_glScope) {
m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex]->m_triggerData);
}
updateGLScopeDisplay();
}
2017-02-03 02:27:03 -05:00
}
return true;
}
else if (MsgScopeVisNGRemoveTrigger::match(message))
{
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGRemoveTrigger& conf = (MsgScopeVisNGRemoveTrigger&) message;
2017-05-25 14:13:34 -04:00
uint32_t triggerIndex = conf.getTriggerIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGRemoveTrigger: " << triggerIndex;
2017-02-03 02:27:03 -05:00
if (triggerIndex < m_triggerConditions.size())
{
TriggerCondition *triggerCondition = m_triggerConditions[triggerIndex];
2017-02-03 02:27:03 -05:00
m_triggerConditions.erase(m_triggerConditions.begin() + triggerIndex);
delete triggerCondition;
2017-02-03 02:27:03 -05:00
}
return true;
}
2017-02-26 13:14:27 -05:00
else if (MsgScopeVisNGMoveTrigger::match(message))
{
QMutexLocker configLocker(&m_mutex);
MsgScopeVisNGMoveTrigger& conf = (MsgScopeVisNGMoveTrigger&) message;
int triggerIndex = conf.getTriggerIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGMoveTrigger: " << triggerIndex;
2017-02-26 13:14:27 -05:00
if (!conf.getMoveUp() && (triggerIndex == 0)) {
return true;
}
int nextTriggerIndex = (triggerIndex + (conf.getMoveUp() ? 1 : -1)) % m_triggerConditions.size();
TriggerCondition *nextTrigger = m_triggerConditions[nextTriggerIndex];
2017-02-26 13:14:27 -05:00
m_triggerConditions[nextTriggerIndex] = m_triggerConditions[triggerIndex];
m_triggerConditions[triggerIndex] = nextTrigger;
computeDisplayTriggerLevels();
if (m_glScope) {
m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex]->m_triggerData);
}
2017-02-26 13:14:27 -05:00
updateGLScopeDisplay();
return true;
}
2017-02-11 04:36:10 -05:00
else if (MsgScopeVisNGFocusOnTrigger::match(message))
{
MsgScopeVisNGFocusOnTrigger& conf = (MsgScopeVisNGFocusOnTrigger&) message;
2017-05-25 14:13:34 -04:00
uint32_t triggerIndex = conf.getTriggerIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGFocusOnTrigger: " << triggerIndex;
2017-02-11 04:36:10 -05:00
if (triggerIndex < m_triggerConditions.size())
{
m_focusedTriggerIndex = triggerIndex;
computeDisplayTriggerLevels();
if (m_glScope) {
m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex]->m_triggerData);
}
updateGLScopeDisplay();
2017-02-11 04:36:10 -05:00
}
return true;
}
2017-02-03 02:27:03 -05:00
else if (MsgScopeVisNGAddTrace::match(message))
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGAddTrace";
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGAddTrace& conf = (MsgScopeVisNGAddTrace&) message;
2017-02-05 20:40:31 -05:00
m_traces.addTrace(conf.getTraceData(), m_traceSize);
initTraceBuffers();
updateMaxTraceDelay();
2017-02-11 04:36:10 -05:00
computeDisplayTriggerLevels();
updateGLScopeDisplay();
2017-02-03 02:27:03 -05:00
return true;
}
else if (MsgScopeVisNGChangeTrace::match(message))
{
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGChangeTrace& conf = (MsgScopeVisNGChangeTrace&) message;
bool doComputeTriggerLevelsOnDisplay = m_traces.isVerticalDisplayChange(conf.getTraceData(), conf.getTraceIndex());
2018-05-21 12:00:10 -04:00
uint32_t traceIndex = conf.getTraceIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGChangeTrace: " << traceIndex;
2018-05-21 12:00:10 -04:00
m_traces.changeTrace(conf.getTraceData(), traceIndex);
2017-02-05 20:40:31 -05:00
updateMaxTraceDelay();
2017-02-11 04:36:10 -05:00
if (doComputeTriggerLevelsOnDisplay) computeDisplayTriggerLevels();
updateGLScopeDisplay();
2017-02-03 02:27:03 -05:00
return true;
2017-01-31 02:26:13 -05:00
}
2017-02-03 02:27:03 -05:00
else if (MsgScopeVisNGRemoveTrace::match(message))
{
QMutexLocker configLocker(&m_mutex);
2017-02-03 02:27:03 -05:00
MsgScopeVisNGRemoveTrace& conf = (MsgScopeVisNGRemoveTrace&) message;
2018-05-21 12:00:10 -04:00
uint32_t traceIndex = conf.getTraceIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGRemoveTrace: " << traceIndex;
2018-05-21 12:00:10 -04:00
m_traces.removeTrace(traceIndex);
2017-02-05 20:40:31 -05:00
updateMaxTraceDelay();
2017-02-11 04:36:10 -05:00
computeDisplayTriggerLevels();
updateGLScopeDisplay();
2017-02-03 02:27:03 -05:00
return true;
}
2017-02-26 05:26:23 -05:00
else if (MsgScopeVisNGMoveTrace::match(message))
{
QMutexLocker configLocker(&m_mutex);
MsgScopeVisNGMoveTrace& conf = (MsgScopeVisNGMoveTrace&) message;
2018-05-21 12:00:10 -04:00
uint32_t traceIndex = conf.getTraceIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGMoveTrace: " << traceIndex;
2018-05-21 12:00:10 -04:00
m_traces.moveTrace(traceIndex, conf.getMoveUp());
2017-02-26 05:26:23 -05:00
//updateMaxTraceDelay();
computeDisplayTriggerLevels();
updateGLScopeDisplay();
return true;
}
else if (MsgScopeVisNGFocusOnTrace::match(message))
{
MsgScopeVisNGFocusOnTrace& conf = (MsgScopeVisNGFocusOnTrace&) message;
2017-05-25 14:13:34 -04:00
uint32_t traceIndex = conf.getTraceIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGFocusOnTrace: " << traceIndex;
if (traceIndex < m_traces.m_tracesData.size())
{
m_focusedTraceIndex = traceIndex;
computeDisplayTriggerLevels();
if (m_glScope) {
m_glScope->setFocusedTraceIndex(m_focusedTraceIndex);
}
updateGLScopeDisplay();
}
return true;
}
2017-02-21 19:18:50 -05:00
else if (MsgScopeVisNGOneShot::match(message))
{
MsgScopeVisNGOneShot& conf = (MsgScopeVisNGOneShot&) message;
bool oneShot = conf.getOneShot();
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGOneShot: oneShot:" << oneShot;
2017-02-21 19:18:50 -05:00
m_triggerOneShot = oneShot;
if (m_triggerWaitForReset && !oneShot) {
m_triggerWaitForReset = false;
}
return true;
2017-02-21 19:18:50 -05:00
}
else if (MsgScopeVisNGMemoryTrace::match(message))
{
MsgScopeVisNGMemoryTrace& conf = (MsgScopeVisNGMemoryTrace&) message;
uint32_t memoryIndex = conf.getMemoryIndex();
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage: MsgScopeVisNGMemoryTrace: " << memoryIndex;
if (memoryIndex != m_currentTraceMemoryIndex)
{
// transition from live mode
if (m_currentTraceMemoryIndex == 0)
{
m_liveTraceSize = m_traceSize;
m_livePreTriggerDelay = m_preTriggerDelay;
}
m_currentTraceMemoryIndex = memoryIndex;
// transition to live mode
if (m_currentTraceMemoryIndex == 0)
{
setLiveRate(m_liveSampleRate); // reset to live rate
setTraceSize(m_liveTraceSize, true); // reset to live trace size
setPreTriggerDelay(m_livePreTriggerDelay, true); // reset to live pre-trigger delay
}
else
{
2017-02-24 17:24:47 -05:00
processMemoryTrace();
}
}
return true;
}
2017-02-03 02:27:03 -05:00
else
{
2018-08-12 11:18:58 -04:00
qDebug() << "ScopeVis::handleMessage" << message.getIdentifier() << " not handled";
2017-02-03 02:27:03 -05:00
return false;
}
2017-01-29 13:51:45 -05:00
}
2018-08-12 11:18:58 -04:00
void ScopeVis::updateMaxTraceDelay()
2017-02-05 20:40:31 -05:00
{
int maxTraceDelay = 0;
2017-02-18 22:46:15 -05:00
bool allocateCache = false;
uint32_t projectorCounts[(int) Projector::nbProjectionTypes];
memset(projectorCounts, 0, ((int) Projector::nbProjectionTypes)*sizeof(uint32_t));
2021-05-28 18:55:04 -04:00
std::vector<GLScopeSettings::TraceData>::iterator itData = m_traces.m_tracesData.begin();
std::vector<TraceControl*>::iterator itCtrl = m_traces.m_tracesControl.begin();
2017-02-05 20:40:31 -05:00
2017-02-18 22:46:15 -05:00
for (; itData != m_traces.m_tracesData.end(); ++itData, ++itCtrl)
2017-02-05 20:40:31 -05:00
{
if (itData->m_traceDelay > maxTraceDelay)
{
maxTraceDelay = itData->m_traceDelay;
}
2017-02-18 22:46:15 -05:00
2018-02-15 12:48:31 -05:00
if (itData->m_projectionType < 0) {
itData->m_projectionType = Projector::ProjectionReal;
2018-02-15 12:48:31 -05:00
}
2017-02-18 22:46:15 -05:00
if (projectorCounts[(int) itData->m_projectionType] > 0)
{
allocateCache = true;
(*itCtrl)->m_projector.setCacheMaster(false);
2017-02-18 22:46:15 -05:00
}
else
{
(*itCtrl)->m_projector.setCacheMaster(true);
2017-02-18 22:46:15 -05:00
}
projectorCounts[(int) itData->m_projectionType]++;
}
itCtrl = m_traces.m_tracesControl.begin();
for (; itCtrl != m_traces.m_tracesControl.end(); ++itCtrl)
{
if (allocateCache) {
(*itCtrl)->m_projector.setCache(m_projectorCache);
2017-02-18 22:46:15 -05:00
} else {
(*itCtrl)->m_projector.setCache(0);
2017-02-18 22:46:15 -05:00
}
2017-02-05 20:40:31 -05:00
}
m_maxTraceDelay = maxTraceDelay;
}
2018-08-12 11:18:58 -04:00
void ScopeVis::initTraceBuffers()
2017-02-05 20:40:31 -05:00
{
int shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
std::vector<float *>::iterator it0 = m_traces.m_traces[0].begin();
std::vector<float *>::iterator it1 = m_traces.m_traces[1].begin();
for (; it0 != m_traces.m_traces[0].end(); ++it0, ++it1)
{
2017-05-25 14:13:34 -04:00
for (unsigned int i = 0; i < m_traceSize; i++)
2017-02-05 20:40:31 -05:00
{
(*it0)[2*i] = (i - shift); // display x
(*it0)[2*i + 1] = 0.0f; // display y
(*it1)[2*i] = (i - shift); // display x
(*it1)[2*i + 1] = 0.0f; // display y
}
}
}
2018-08-12 11:18:58 -04:00
void ScopeVis::computeDisplayTriggerLevels()
{
2021-05-28 18:55:04 -04:00
std::vector<GLScopeSettings::TraceData>::iterator itData = m_traces.m_tracesData.begin();
for (; itData != m_traces.m_tracesData.end(); ++itData)
{
if ((m_focusedTriggerIndex < m_triggerConditions.size()) && (m_triggerConditions[m_focusedTriggerIndex]->m_projector.getProjectionType() == itData->m_projectionType))
{
float level = m_triggerConditions[m_focusedTriggerIndex]->m_triggerData.m_triggerLevel;
float levelPowerLin = level + 1.0f;
float levelPowerdB = (100.0f * (level - 1.0f));
float v;
if ((itData->m_projectionType == Projector::ProjectionMagLin) || (itData->m_projectionType == Projector::ProjectionMagSq))
{
v = (levelPowerLin - itData->m_ofs)*itData->m_amp - 1.0f;
}
else if (itData->m_projectionType == Projector::ProjectionMagDB)
{
float ofsdB = itData->m_ofs * 100.0f;
v = ((levelPowerdB + 100.0f - ofsdB)*itData->m_amp)/50.0f - 1.0f;
}
else
{
v = (level - itData->m_ofs) * itData->m_amp;
}
if(v > 1.0f) {
v = 1.0f;
} else if (v < -1.0f) {
v = -1.0f;
}
itData->m_triggerDisplayLevel = v;
}
else
{
itData->m_triggerDisplayLevel = 2.0f;
}
}
}
2018-08-12 11:18:58 -04:00
void ScopeVis::updateGLScopeDisplay()
{
if (!m_glScope) {
return;
}
if (m_currentTraceMemoryIndex > 0)
{
m_glScope->setConfigChanged();
processMemoryTrace();
}
else
{
m_glScope->updateDisplay();
}
}