diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd9474312..9b9f5d206 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -131,6 +131,7 @@ set(sdrbase_SOURCES
sdrbase/dsp/spectrumscopengcombovis.cpp
sdrbase/dsp/scopevis.cpp
sdrbase/dsp/scopevisng.cpp
+ sdrbase/dsp/scopevismulti.cpp
sdrbase/dsp/spectrumvis.cpp
sdrbase/dsp/threadedbasebandsamplesink.cpp
sdrbase/dsp/threadedbasebandsamplesource.cpp
@@ -146,7 +147,9 @@ set(sdrbase_SOURCES
sdrbase/gui/glscope.cpp
sdrbase/gui/glscopegui.cpp
sdrbase/gui/glscopeng.cpp
+ sdrbase/gui/glscopemulti.cpp
sdrbase/gui/glscopenggui.cpp
+ sdrbase/gui/glscopemultigui.cpp
sdrbase/gui/glshadersimple.cpp
sdrbase/gui/glshadertextured.cpp
sdrbase/gui/glspectrum.cpp
@@ -248,6 +251,7 @@ set(sdrbase_HEADERS
sdrbase/dsp/spectrumscopengcombovis.h
sdrbase/dsp/scopevis.h
sdrbase/dsp/scopevisng.h
+ sdrbase/dsp/scopevismulti.h
sdrbase/dsp/spectrumvis.h
sdrbase/dsp/threadedbasebandsamplesink.h
sdrbase/dsp/threadedbasebandsamplesource.h
@@ -262,7 +266,9 @@ set(sdrbase_HEADERS
sdrbase/gui/glscope.h
sdrbase/gui/glscopegui.h
sdrbase/gui/glscopeng.h
+ sdrbase/gui/glscopemulti.h
sdrbase/gui/glscopenggui.h
+ sdrbase/gui/glscopemultigui.h
sdrbase/gui/glshadersimple.h
sdrbase/gui/glshadertextured.h
sdrbase/gui/glspectrum.h
@@ -323,6 +329,7 @@ set(sdrbase_FORMS
sdrbase/gui/cwkeyergui.ui
sdrbase/gui/glscopegui.ui
sdrbase/gui/glscopenggui.ui
+ sdrbase/gui/glscopemultigui.ui
sdrbase/gui/glspectrumgui.ui
sdrbase/gui/pluginsdialog.ui
sdrbase/gui/audiodialog.ui
diff --git a/sdrbase/dsp/scopevismulti.cpp b/sdrbase/dsp/scopevismulti.cpp
new file mode 100644
index 000000000..ddc7d1715
--- /dev/null
+++ b/sdrbase/dsp/scopevismulti.cpp
@@ -0,0 +1,848 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+
+#include "scopevismulti.h"
+#include "dsp/dspcommands.h"
+#include "gui/glscopemulti.h"
+
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgConfigureScopeVisNG, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGAddTrigger, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGChangeTrigger, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGRemoveTrigger, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMoveTrigger, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGFocusOnTrigger, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGAddTrace, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGChangeTrace, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGRemoveTrace, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMoveTrace, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGFocusOnTrace, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGOneShot, Message)
+MESSAGE_CLASS_DEFINITION(ScopeVisMulti::MsgScopeVisNGMemoryTrace, Message)
+
+ScopeVisMulti::ScopeVisMulti(GLScopeMulti* glScope) :
+ m_glScope(glScope),
+ m_preTriggerDelay(0),
+ m_currentTriggerIndex(0),
+ m_focusedTriggerIndex(0),
+ m_triggerState(TriggerUntriggered),
+ m_focusedTraceIndex(0),
+ m_traceSize(m_traceChunkSize),
+ m_nbSamples(0),
+ m_traceStart(true),
+ m_postTrigBuffering(false),
+ m_traceFill(0),
+ m_zTraceIndex(-1),
+ m_timeBase(1),
+ m_timeOfsProMill(0),
+ m_sampleRate(0),
+ m_freeRun(true),
+ m_maxTraceDelay(0),
+ m_triggerOneShot(false),
+ m_triggerWaitForReset(false),
+ m_currentTraceMemoryIndex(0),
+ m_nbSources(0)
+{
+ setObjectName("ScopeVisNG");
+ m_glScope->setTraces(&m_traces.m_tracesData, &m_traces.m_traces[0]);
+}
+
+ScopeVisMulti::~ScopeVisMulti()
+{
+}
+
+void ScopeVisMulti::setSampleRate(int sampleRate)
+{
+ if (sampleRate != m_sampleRate)
+ {
+ m_sampleRate = sampleRate;
+ if (m_glScope) m_glScope->setSampleRate(m_sampleRate);
+ }
+}
+
+void ScopeVisMulti::configure(
+ uint32_t nbSources,
+ uint32_t traceSize,
+ uint32_t timeBase,
+ uint32_t timeOfsProMill,
+ uint32_t triggerPre,
+ bool freeRun)
+{
+ Message* cmd = MsgConfigureScopeVisNG::create(nbSources, traceSize, timeBase, timeOfsProMill, triggerPre, freeRun);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::addTrace(const TraceData& traceData)
+{
+ qDebug() << "ScopeVisMulti::addTrace:"
+ << " m_amp: " << traceData.m_amp
+ << " m_ofs: " << traceData.m_ofs
+ << " m_traceDelay: " << traceData.m_traceDelay;
+ Message* cmd = MsgScopeVisNGAddTrace::create(traceData);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::changeTrace(const TraceData& traceData, uint32_t traceIndex)
+{
+ qDebug() << "ScopeVisMulti::changeTrace:"
+ << " trace: " << traceIndex
+ << " m_amp: " << traceData.m_amp
+ << " m_ofs: " << traceData.m_ofs
+ << " m_traceDelay: " << traceData.m_traceDelay;
+ Message* cmd = MsgScopeVisNGChangeTrace::create(traceData, traceIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::removeTrace(uint32_t traceIndex)
+{
+ qDebug() << "ScopeVisMulti::removeTrace:"
+ << " trace: " << traceIndex;
+ Message* cmd = MsgScopeVisNGRemoveTrace::create(traceIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::moveTrace(uint32_t traceIndex, bool upElseDown)
+{
+ qDebug() << "ScopeVisMulti::moveTrace:"
+ << " trace: " << traceIndex
+ << " up: " << upElseDown;
+ Message* cmd = MsgScopeVisNGMoveTrace::create(traceIndex, upElseDown);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::focusOnTrace(uint32_t traceIndex)
+{
+ Message* cmd = MsgScopeVisNGFocusOnTrace::create(traceIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::addTrigger(const TriggerData& triggerData)
+{
+ Message* cmd = MsgScopeVisNGAddTrigger::create(triggerData);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::changeTrigger(const TriggerData& triggerData, uint32_t triggerIndex)
+{
+ Message* cmd = MsgScopeVisNGChangeTrigger::create(triggerData, triggerIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::removeTrigger(uint32_t triggerIndex)
+{
+ Message* cmd = MsgScopeVisNGRemoveTrigger::create(triggerIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::moveTrigger(uint32_t triggerIndex, bool upElseDown)
+{
+ Message* cmd = MsgScopeVisNGMoveTrigger::create(triggerIndex, upElseDown);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::focusOnTrigger(uint32_t triggerIndex)
+{
+ Message* cmd = MsgScopeVisNGFocusOnTrigger::create(triggerIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::setOneShot(bool oneShot)
+{
+ Message* cmd = MsgScopeVisNGOneShot::create(oneShot);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::setMemoryIndex(uint32_t memoryIndex)
+{
+ Message* cmd = MsgScopeVisNGMemoryTrace::create(memoryIndex);
+ m_inputMessageQueue.push(cmd);
+}
+
+void ScopeVisMulti::feed(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, uint32_t sourceIndex)
+{
+ if ((m_triggerWaitForReset) || (m_currentTraceMemoryIndex > 0)) {
+ return;
+ }
+
+ if(!m_mutex.tryLock(2)) { // prevent conflicts with configuration process
+ return;
+ }
+
+ m_traceDiscreteMemory.feed(cbegin, end, sourceIndex); // memory storage
+
+ if (m_traceDiscreteMemory.minFill() >= m_traceSize)
+ {
+ if (m_triggerState == TriggerTriggered)
+ {
+ processSources();
+ }
+ else
+ {
+ lookForTrigger();
+ }
+ }
+}
+
+void ScopeVisMulti::lookForTrigger()
+{
+ int count = 0;
+
+ 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))
+ {
+ TriggerCondition& triggerCondition = m_triggerConditions[m_currentTriggerIndex]; // current trigger condition
+ uint32_t sourceIndex = triggerCondition.m_triggerData.m_inputIndex;
+
+ SampleVector::iterator begin = m_traceDiscreteMemory.getBeginIterator(sourceIndex);
+ SampleVector::iterator end = begin + m_traceSize;
+ SampleVector::const_iterator cbegin = begin;
+
+ while (begin < end)
+ {
+ if (m_triggerState == TriggerDelay)
+ {
+ if (triggerCondition.m_triggerDelayCount > 0) // skip samples during delay period
+ {
+ triggerCondition.m_triggerDelayCount--;
+ ++begin;
+ continue;
+ }
+ else // process trigger
+ {
+ if (nextTrigger()) // move to next trigger and keep going
+ {
+ m_triggerComparator.reset();
+ m_triggerState = TriggerUntriggered;
+ ++begin;
+ 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;
+ break;
+ }
+ }
+ }
+
+ // look for trigger
+ if (m_triggerComparator.triggered(*begin, triggerCondition))
+ {
+ if (triggerCondition.m_triggerData.m_triggerDelay > 0)
+ {
+ triggerCondition.m_triggerDelayCount = triggerCondition.m_triggerData.m_triggerDelay; // initialize delayed samples counter
+ m_triggerState = TriggerDelay;
+ ++begin;
+ 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 trace processing
+ m_nbSamples = m_traceSize + m_maxTraceDelay;
+ m_triggerComparator.reset();
+ m_triggerState = TriggerTriggered;
+ break;
+ }
+ }
+
+ ++begin;
+ }
+
+ count = begin - cbegin;
+ }
+
+ m_traceDiscreteMemory.consume(count);
+}
+
+void ScopeVisMulti::processSources()
+{
+ if (m_glScope->getDataChanged()) // optimization: process trace only if required by glScope
+ {
+ m_triggerState = TriggerUntriggered;
+ return;
+ }
+
+ uint32_t nbSources = m_traceDiscreteMemory.getNbSources();
+
+ for (int is = 0; is < nbSources; is++)
+ {
+ SampleVector::iterator begin = m_traceDiscreteMemory.getBeginIterator(is); // trigger position
+ SampleVector::iterator end = begin + m_traceSize;
+
+ if (m_traceStart)
+ {
+ // trace back
+ if (m_maxTraceDelay > 0)
+ {
+ processTraces(begin - m_preTriggerDelay - m_maxTraceDelay, begin - m_preTriggerDelay, true, is);
+ }
+
+ // pre-trigger
+ if (m_preTriggerDelay > 0)
+ {
+ processTraces(begin - m_preTriggerDelay, begin, false, is);
+ }
+
+ m_traceStart = false;
+ }
+
+ processTraces(begin, end, false, is);
+ m_traceDiscreteMemory.markEnd(is, end);
+ } // sources loop
+
+ m_glScope->newTraces(&m_traces.m_traces[m_traces.currentBufferIndex()]);
+ m_traces.switchBuffer();
+ m_traceDiscreteMemory.store();
+}
+
+void ScopeVisMulti::processMemorySources()
+{
+ if ((m_currentTraceMemoryIndex > 0) && (m_currentTraceMemoryIndex < m_nbTraceMemories))
+ {
+ uint32_t nbSources = m_traceDiscreteMemory.getNbSources();
+
+ for (int is = 0; is < nbSources; is++)
+ {
+ SampleVector::const_iterator mend = m_traceDiscreteMemory.m_traceBackBuffers[is].at(m_currentTraceMemoryIndex).m_endPoint;
+ SampleVector::const_iterator mbegin = mend - m_traceSize;
+ SampleVector::const_iterator mbegin_tb = mbegin - m_maxTraceDelay;
+ processTraces(mbegin_tb, mbegin, true, is); // traceback
+ processTraces(mbegin, mend, false, is);
+ }
+ }
+}
+
+
+bool ScopeVisMulti::nextTrigger()
+{
+ 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
+ }
+ }
+
+ 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
+ }
+}
+
+void ScopeVisMulti::processTraces(const SampleVector::const_iterator& cbegin, const SampleVector::const_iterator& end, bool traceBack, int sourceIndex)
+{
+ SampleVector::const_iterator begin(cbegin);
+ int shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
+ int length = m_traceSize / m_timeBase;
+ int nbSamples = end - begin;
+
+ while (begin < end)
+ {
+ std::vector::iterator itCtl = m_traces.m_tracesControl.begin();
+ std::vector::iterator itData = m_traces.m_tracesData.begin();
+ std::vector::iterator itTrace = m_traces.m_traces[m_traces.currentBufferIndex()].begin();
+
+ for (; itCtl != m_traces.m_tracesControl.end(); ++itCtl, ++itData, ++itTrace)
+ {
+ if (itData->m_inputIndex != sourceIndex) {
+ continue;
+ }
+
+ if (traceBack && ((end - begin) > itData->m_traceDelay)) { // before start of trace
+ continue;
+ }
+
+ ProjectionType projectionType = itData->m_projectionType;
+
+ if (itCtl->m_traceCount[m_traces.currentBufferIndex()] < m_traceSize)
+ {
+ int& traceCount = itCtl->m_traceCount[m_traces.currentBufferIndex()]; // reference for code clarity
+ float v;
+
+ if (projectionType == ProjectionMagLin)
+ {
+ v = (itCtl->m_projector.run(*begin) - itData->m_ofs)*itData->m_amp - 1.0f;
+ }
+ else if (projectionType == ProjectionMagDB)
+ {
+ // there is no processing advantage in direct calculation without projector
+// uint32_t magsq = begin->m_real*begin->m_real + begin->m_imag*begin->m_imag;
+// v = ((log10f(magsq/1073741824.0f)*0.2f - 2.0f*itData->m_ofs) + 2.0f)*itData->m_amp - 1.0f;
+ float pdB = itCtl->m_projector.run(*begin);
+ 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 = -200.0f;
+ itCtl->m_sumPow = 0.0f;
+ itCtl->m_nbPow = 1;
+ }
+
+ if (pdB > -200.0f)
+ {
+ if (pdB > itCtl->m_maxPow)
+ {
+ itCtl->m_maxPow = pdB;
+ }
+
+ itCtl->m_sumPow += pdB;
+ itCtl->m_nbPow++;
+ }
+ }
+
+ if ((nbSamples == 1) && (itCtl->m_nbPow > 0)) // on last sample create power display overlay
+ {
+ double avgPow = itCtl->m_sumPow / itCtl->m_nbPow;
+ double peakToAvgPow = itCtl->m_maxPow - avgPow;
+ itData->m_textOverlay = QString("%1 %2 %3").arg(itCtl->m_maxPow, 0, 'f', 1).arg(avgPow, 0, 'f', 1).arg(peakToAvgPow, 4, 'f', 1, ' ');
+ itCtl->m_nbPow = 0;
+ }
+ }
+ else
+ {
+ v = (itCtl->m_projector.run(*begin) - itData->m_ofs) * itData->m_amp;
+ }
+
+ if(v > 1.0f) {
+ v = 1.0f;
+ } else if (v < -1.0f) {
+ v = -1.0f;
+ }
+
+ (*itTrace)[2*traceCount]
+ = traceCount - shift; // display x
+ (*itTrace)[2*traceCount + 1] = v; // display y
+ traceCount++;
+ }
+ }
+
+ ++begin;
+ nbSamples--;
+ }
+}
+
+bool ScopeVisMulti::handleMessage(const Message& message)
+{
+ qDebug() << "ScopeVisNG::handleMessage" << message.getIdentifier();
+
+ if (DSPSignalNotification::match(message))
+ {
+ DSPSignalNotification& notif = (DSPSignalNotification&) message;
+ setSampleRate(notif.getSampleRate());
+ qDebug() << "ScopeVisNG::handleMessage: DSPSignalNotification: m_sampleRate: " << m_sampleRate;
+ return true;
+ }
+ else if (MsgConfigureScopeVisNG::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgConfigureScopeVisNG& conf = (MsgConfigureScopeVisNG&) message;
+
+ uint32_t nbSources = conf.getNbSources();
+ uint32_t traceSize = conf.getTraceSize();
+ uint32_t timeBase = conf.getTimeBase();
+ uint32_t timeOfsProMill = conf.getTimeOfsProMill();
+ uint32_t triggerPre = conf.getTriggerPre();
+ bool freeRun = conf.getFreeRun();
+
+ if (m_nbSources != nbSources)
+ {
+ if (nbSources == 0) {
+ m_nbSources = 1;
+ } else if (nbSources > m_maxNbTraceSources) {
+ m_nbSources = m_maxNbTraceSources;
+ } else {
+ m_nbSources = nbSources;
+ }
+ }
+
+ if (m_traceSize != traceSize)
+ {
+ m_traceSize = traceSize;
+ m_traces.resize(m_traceSize);
+ m_traceDiscreteMemory.resizeBuffers(m_traceSize);
+ initTraceBuffers();
+
+ if (m_glScope) {
+ m_glScope->setTraceSize(m_traceSize);
+ }
+ }
+
+ if (m_timeBase != timeBase)
+ {
+ m_timeBase = timeBase;
+
+ if (m_glScope) {
+ m_glScope->setTimeBase(m_timeBase);
+ }
+ }
+
+ if (m_timeOfsProMill != timeOfsProMill)
+ {
+ m_timeOfsProMill = timeOfsProMill;
+
+ if (m_glScope) {
+ m_glScope->setTimeOfsProMill(m_timeOfsProMill);
+ }
+ }
+
+ if (m_preTriggerDelay != triggerPre)
+ {
+ m_preTriggerDelay = triggerPre;
+ m_glScope->setTriggerPre(m_preTriggerDelay);
+ }
+
+ if (freeRun != m_freeRun)
+ {
+ m_freeRun = freeRun;
+ }
+
+ qDebug() << "ScopeVisNG::handleMessage: MsgConfigureScopeVisNG:"
+ << " m_traceSize: " << m_traceSize
+ << " m_timeOfsProMill: " << m_timeOfsProMill
+ << " m_preTriggerDelay: " << m_preTriggerDelay
+ << " m_freeRun: " << m_freeRun;
+
+ if ((m_glScope) && (m_currentTraceMemoryIndex > 0)) {
+ processMemorySources();
+ }
+
+ return true;
+ }
+ else if (MsgScopeVisNGAddTrigger::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGAddTrigger& conf = (MsgScopeVisNGAddTrigger&) message;
+ m_triggerConditions.push_back(TriggerCondition(conf.getTriggerData()));
+ m_triggerConditions.back().initProjector();
+ return true;
+ }
+ else if (MsgScopeVisNGChangeTrigger::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGChangeTrigger& conf = (MsgScopeVisNGChangeTrigger&) message;
+ int triggerIndex = conf.getTriggerIndex();
+
+ if (triggerIndex < m_triggerConditions.size())
+ {
+ m_triggerConditions[triggerIndex].setData(conf.getTriggerData());
+
+ if (triggerIndex == m_focusedTriggerIndex)
+ {
+ computeDisplayTriggerLevels();
+ m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData);
+ updateGLScopeDisplay();
+ }
+ }
+
+ return true;
+ }
+ else if (MsgScopeVisNGRemoveTrigger::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGRemoveTrigger& conf = (MsgScopeVisNGRemoveTrigger&) message;
+ int triggerIndex = conf.getTriggerIndex();
+
+ if (triggerIndex < m_triggerConditions.size()) {
+ m_triggerConditions.erase(m_triggerConditions.begin() + triggerIndex);
+ }
+
+ return true;
+ }
+ else if (MsgScopeVisNGMoveTrigger::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGMoveTrigger& conf = (MsgScopeVisNGMoveTrigger&) message;
+ int triggerIndex = conf.getTriggerIndex();
+
+ if (!conf.getMoveUp() && (triggerIndex == 0)) {
+ return true;
+ }
+
+ int nextTriggerIndex = (triggerIndex + (conf.getMoveUp() ? 1 : -1)) % m_triggerConditions.size();
+
+ TriggerCondition nextTrigger = m_triggerConditions[nextTriggerIndex];
+ m_triggerConditions[nextTriggerIndex] = m_triggerConditions[triggerIndex];
+ m_triggerConditions[triggerIndex] = nextTrigger;
+
+ computeDisplayTriggerLevels();
+ m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData);
+ updateGLScopeDisplay();
+
+ return true;
+ }
+ else if (MsgScopeVisNGFocusOnTrigger::match(message))
+ {
+ MsgScopeVisNGFocusOnTrigger& conf = (MsgScopeVisNGFocusOnTrigger&) message;
+ int triggerIndex = conf.getTriggerIndex();
+
+ if (triggerIndex < m_triggerConditions.size())
+ {
+ m_focusedTriggerIndex = triggerIndex;
+ computeDisplayTriggerLevels();
+ m_glScope->setFocusedTriggerData(m_triggerConditions[m_focusedTriggerIndex].m_triggerData);
+ updateGLScopeDisplay();
+ }
+
+ return true;
+ }
+ else if (MsgScopeVisNGAddTrace::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGAddTrace& conf = (MsgScopeVisNGAddTrace&) message;
+ m_traces.addTrace(conf.getTraceData(), m_traceSize);
+ initTraceBuffers();
+ updateMaxTraceDelay();
+ computeDisplayTriggerLevels();
+ updateGLScopeDisplay();
+ return true;
+ }
+ else if (MsgScopeVisNGChangeTrace::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGChangeTrace& conf = (MsgScopeVisNGChangeTrace&) message;
+ bool doComputeTriggerLevelsOnDisplay = m_traces.isVerticalDisplayChange(conf.getTraceData(), conf.getTraceIndex());
+ m_traces.changeTrace(conf.getTraceData(), conf.getTraceIndex());
+ updateMaxTraceDelay();
+ if (doComputeTriggerLevelsOnDisplay) computeDisplayTriggerLevels();
+ updateGLScopeDisplay();
+ return true;
+ }
+ else if (MsgScopeVisNGRemoveTrace::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGRemoveTrace& conf = (MsgScopeVisNGRemoveTrace&) message;
+ m_traces.removeTrace(conf.getTraceIndex());
+ updateMaxTraceDelay();
+ computeDisplayTriggerLevels();
+ updateGLScopeDisplay();
+ return true;
+ }
+ else if (MsgScopeVisNGMoveTrace::match(message))
+ {
+ QMutexLocker configLocker(&m_mutex);
+ MsgScopeVisNGMoveTrace& conf = (MsgScopeVisNGMoveTrace&) message;
+ m_traces.moveTrace(conf.getTraceIndex(), conf.getMoveUp());
+ //updateMaxTraceDelay();
+ computeDisplayTriggerLevels();
+ updateGLScopeDisplay();
+ return true;
+ }
+ else if (MsgScopeVisNGFocusOnTrace::match(message))
+ {
+ MsgScopeVisNGFocusOnTrace& conf = (MsgScopeVisNGFocusOnTrace&) message;
+ int traceIndex = conf.getTraceIndex();
+
+ if (traceIndex < m_traces.m_tracesData.size())
+ {
+ m_focusedTraceIndex = traceIndex;
+ computeDisplayTriggerLevels();
+ m_glScope->setFocusedTraceIndex(m_focusedTraceIndex);
+ updateGLScopeDisplay();
+ }
+
+ return true;
+ }
+ else if (MsgScopeVisNGOneShot::match(message))
+ {
+ MsgScopeVisNGOneShot& conf = (MsgScopeVisNGOneShot&) message;
+ bool oneShot = conf.getOneShot();
+ m_triggerOneShot = oneShot;
+ if (m_triggerWaitForReset && !oneShot) m_triggerWaitForReset = false;
+ return true;
+ }
+ else if (MsgScopeVisNGMemoryTrace::match(message))
+ {
+ MsgScopeVisNGMemoryTrace& conf = (MsgScopeVisNGMemoryTrace&) message;
+ uint32_t memoryIndex = conf.getMemoryIndex();
+
+ if (memoryIndex != m_currentTraceMemoryIndex)
+ {
+ m_currentTraceMemoryIndex = memoryIndex;
+
+ if (m_currentTraceMemoryIndex > 0) {
+ processMemorySources();
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void ScopeVisMulti::updateMaxTraceDelay()
+{
+ int maxTraceDelay = 0;
+ bool allocateCache = false;
+ uint32_t projectorCounts[(int) nbProjectionTypes];
+ memset(projectorCounts, 0, ((int) nbProjectionTypes)*sizeof(uint32_t));
+ std::vector::iterator itData = m_traces.m_tracesData.begin();
+ std::vector::iterator itCtrl = m_traces.m_tracesControl.begin();
+
+ for (; itData != m_traces.m_tracesData.end(); ++itData, ++itCtrl)
+ {
+ if (itData->m_traceDelay > maxTraceDelay)
+ {
+ maxTraceDelay = itData->m_traceDelay;
+ }
+
+ if (projectorCounts[(int) itData->m_projectionType] > 0)
+ {
+ allocateCache = true;
+ itCtrl->m_projector.setCacheMaster(false);
+ }
+ else
+ {
+ itCtrl->m_projector.setCacheMaster(true);
+ }
+
+ 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);
+ } else {
+ itCtrl->m_projector.setCache(0);
+ }
+ }
+
+ m_maxTraceDelay = maxTraceDelay;
+}
+
+void ScopeVisMulti::initTraceBuffers()
+{
+ int shift = (m_timeOfsProMill / 1000.0) * m_traceSize;
+
+ std::vector::iterator it0 = m_traces.m_traces[0].begin();
+ std::vector::iterator it1 = m_traces.m_traces[1].begin();
+
+ for (; it0 != m_traces.m_traces[0].end(); ++it0, ++it1)
+ {
+ for (int i = 0; i < m_traceSize; i++)
+ {
+ (*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
+ }
+ }
+}
+
+void ScopeVisMulti::computeDisplayTriggerLevels()
+{
+ std::vector::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 == ProjectionMagLin)
+ {
+ v = (levelPowerLin - itData->m_ofs)*itData->m_amp - 1.0f;
+ }
+ else if (itData->m_projectionType == 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;
+ }
+ }
+}
+
+void ScopeVisMulti::updateGLScopeDisplay()
+{
+ if (m_currentTraceMemoryIndex > 0) {
+ m_glScope->setConfigChanged();
+ processMemorySources();
+ } else {
+ m_glScope->updateDisplay();
+ }
+}
+
+void ScopeVisMulti::handleInputMessages()
+{
+ Message* message;
+
+ while ((message = m_inputMessageQueue.pop()) != 0)
+ {
+ if (handleMessage(*message))
+ {
+ delete message;
+ }
+ }
+}
diff --git a/sdrbase/dsp/scopevismulti.h b/sdrbase/dsp/scopevismulti.h
new file mode 100644
index 000000000..3dcbc167c
--- /dev/null
+++ b/sdrbase/dsp/scopevismulti.h
@@ -0,0 +1,1162 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2017 F4EXB //
+// written by Edouard Griffiths //
+// //
+// ScopeVis class specialized for multiple sources handling //
+// //
+// This program is free software; you can redistribute it and/or modify //
+// it under the terms of the GNU General Public License as published by //
+// the Free Software Foundation as version 3 of the License, or //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SDRBASE_DSP_SCOPEVISMULTI_H_
+#define SDRBASE_DSP_SCOPEVISMULTI_H_
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include "dsp/dsptypes.h"
+#include "util/export.h"
+#include "util/message.h"
+#include "util/messagequeue.h"
+#include "util/doublebuffer.h"
+
+class GLScopeMulti;
+
+class SDRANGEL_API ScopeVisMulti : public QObject {
+ Q_OBJECT
+public:
+ enum ProjectionType
+ {
+ ProjectionReal = 0, //!< Extract real part
+ ProjectionImag, //!< Extract imaginary part
+ ProjectionMagLin, //!< Calculate linear magnitude or modulus
+ ProjectionMagDB, //!< Calculate logarithmic (dB) of squared magnitude
+ ProjectionPhase, //!< Calculate phase
+ ProjectionDPhase, //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate
+ nbProjectionTypes //!< Gives the number of projections in the enum
+ };
+
+ struct TraceData
+ {
+ ProjectionType m_projectionType; //!< Complex to real projection type
+ uint32_t m_inputIndex; //!< Input or feed index this trace is associated with
+ float m_amp; //!< Amplification factor
+ uint32_t m_ampIndex; //!< Index in list of amplification factors
+ float m_ofs; //!< Offset factor
+ int m_ofsCoarse; //!< Coarse offset slider value
+ int m_ofsFine; //!< Fine offset slider value
+ int m_traceDelay; //!< Trace delay in number of samples
+ int m_traceDelayCoarse; //!< Coarse delay slider value
+ int m_traceDelayFine; //!< Fine delay slider value
+ float m_triggerDisplayLevel; //!< Displayable trigger display level in -1:+1 scale. Off scale if not displayable.
+ QColor m_traceColor; //!< Trace display color
+ float m_traceColorR; //!< Trace display color - red shortcut
+ float m_traceColorG; //!< Trace display color - green shortcut
+ float m_traceColorB; //!< Trace display color - blue shortcut
+ bool m_hasTextOverlay; //!< True if a text overlay has to be displayed
+ QString m_textOverlay; //!< Text overlay to display
+ bool m_viewTrace; //!< Trace visibility
+
+ TraceData() :
+ m_projectionType(ProjectionReal),
+ m_inputIndex(0),
+ m_amp(1.0f),
+ m_ampIndex(0),
+ m_ofs(0.0f),
+ m_ofsCoarse(0),
+ m_ofsFine(0),
+ m_traceDelay(0),
+ m_traceDelayCoarse(0),
+ m_traceDelayFine(0),
+ m_triggerDisplayLevel(2.0), // OVer scale by default (2.0)
+ m_traceColor(255,255,64),
+ m_hasTextOverlay(false),
+ m_viewTrace(true)
+ {
+ setColor(m_traceColor);
+ }
+
+ void setColor(QColor color)
+ {
+ m_traceColor = color;
+ qreal r,g,b,a;
+ m_traceColor.getRgbF(&r, &g, &b, &a);
+ m_traceColorR = r;
+ m_traceColorG = g;
+ m_traceColorB = b;
+ }
+ };
+
+
+ struct TriggerData
+ {
+ ProjectionType m_projectionType; //!< Complex to real projection type
+ uint32_t m_inputIndex; //!< Input or feed index this trigger is associated with
+ Real m_triggerLevel; //!< Level in real units
+ int m_triggerLevelCoarse;
+ int m_triggerLevelFine;
+ bool m_triggerPositiveEdge; //!< Trigger on the positive edge (else negative)
+ bool m_triggerBothEdges; //!< Trigger on both edges (else only one)
+ uint32_t m_triggerDelay; //!< Delay before the trigger is kicked off in number of samples (trigger delay)
+ double m_triggerDelayMult; //!< Trigger delay as a multiplier of trace length
+ int m_triggerDelayCoarse;
+ int m_triggerDelayFine;
+ uint32_t m_triggerRepeat; //!< Number of trigger conditions before the final decisive trigger
+ QColor m_triggerColor; //!< Trigger line display color
+ float m_triggerColorR; //!< Trigger line display color - red shortcut
+ float m_triggerColorG; //!< Trigger line display color - green shortcut
+ float m_triggerColorB; //!< Trigger line display color - blue shortcut
+
+ TriggerData() :
+ m_projectionType(ProjectionReal),
+ m_inputIndex(0),
+ m_triggerLevel(0.0f),
+ m_triggerLevelCoarse(0),
+ m_triggerLevelFine(0),
+ m_triggerPositiveEdge(true),
+ m_triggerBothEdges(false),
+ m_triggerDelay(0),
+ m_triggerDelayMult(0.0),
+ m_triggerDelayCoarse(0),
+ m_triggerDelayFine(0),
+ m_triggerRepeat(0),
+ m_triggerColor(0,255,0)
+ {
+ setColor(m_triggerColor);
+ }
+
+ void setColor(QColor color)
+ {
+ m_triggerColor = color;
+ qreal r,g,b,a;
+ m_triggerColor.getRgbF(&r, &g, &b, &a);
+ m_triggerColorR = r;
+ m_triggerColorG = g;
+ m_triggerColorB = b;
+ }
+ };
+
+ static const uint32_t m_traceChunkSize = 4800;
+ static const uint32_t m_maxNbTriggers = 10;
+ static const uint32_t m_maxNbTraces = 10;
+ static const uint32_t m_nbTraceMemories = 16;
+ static const uint32_t m_maxNbTraceSources = 4;
+
+ ScopeVisMulti(GLScopeMulti* glScope = 0);
+ ~ScopeVisMulti();
+
+ void setSampleRate(int sampleRate);
+ void configure(uint32_t nbSources, uint32_t traceSize, uint32_t timeBase, uint32_t timeOfsProMill, uint32_t triggerPre, bool freeRun);
+ void addTrace(const TraceData& traceData);
+ void changeTrace(const TraceData& traceData, uint32_t traceIndex);
+ void removeTrace(uint32_t traceIndex);
+ void moveTrace(uint32_t traceIndex, bool upElseDown);
+ void focusOnTrace(uint32_t traceIndex);
+ void addTrigger(const TriggerData& triggerData);
+ void changeTrigger(const TriggerData& triggerData, uint32_t triggerIndex);
+ void removeTrigger(uint32_t triggerIndex);
+ void moveTrigger(uint32_t triggerIndex, bool upElseDown);
+ void focusOnTrigger(uint32_t triggerIndex);
+ void setOneShot(bool oneShot);
+ void setMemoryIndex(uint32_t memoryIndex);
+
+ void getTriggerData(TriggerData& triggerData, uint32_t triggerIndex)
+ {
+ if (triggerIndex < m_triggerConditions.size())
+ {
+ triggerData = m_triggerConditions[triggerIndex].m_triggerData;
+ }
+ }
+
+ void getTraceData(TraceData& traceData, uint32_t traceIndex)
+ {
+ if (traceIndex < m_traces.m_tracesData.size())
+ {
+ traceData = m_traces.m_tracesData[traceIndex];
+ }
+ }
+
+ const TriggerData& getTriggerData(uint32_t triggerIndex) const { return m_triggerConditions[triggerIndex].m_triggerData; }
+ const std::vector& getTracesData() const { return m_traces.m_tracesData; }
+ uint32_t getNbTriggers() const { return m_triggerConditions.size(); }
+
+ void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, uint32_t sourceIndex);
+
+protected:
+ MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
+ MessageQueue m_outputMessageQueue; //!< Queue for asynchronous outbound communication
+
+ bool handleMessage(const Message& message);
+
+protected slots:
+ void handleInputMessages();
+
+private:
+ // === messages ===
+ // ---------------------------------------------
+ class MsgConfigureScopeVisNG : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgConfigureScopeVisNG* create(
+ uint32_t nbSources,
+ uint32_t traceSize,
+ uint32_t timeBase,
+ uint32_t timeOfsProMill,
+ uint32_t triggerPre,
+ bool freeRun)
+ {
+ return new MsgConfigureScopeVisNG(nbSources, traceSize, timeBase, timeOfsProMill, triggerPre, freeRun);
+ }
+
+ uint32_t getNbSources() const { return m_nbSources; }
+ uint32_t getTraceSize() const { return m_traceSize; }
+ uint32_t getTimeBase() const { return m_timeBase; }
+ uint32_t getTimeOfsProMill() const { return m_timeOfsProMill; }
+ uint32_t getTriggerPre() const { return m_triggerPre; }
+ bool getFreeRun() const { return m_freeRun; }
+
+ private:
+ uint32_t m_nbSources;
+ uint32_t m_traceSize;
+ uint32_t m_timeBase;
+ uint32_t m_timeOfsProMill;
+ uint32_t m_triggerPre;
+ bool m_freeRun;
+
+ MsgConfigureScopeVisNG(
+ uint32_t nbSources,
+ uint32_t traceSize,
+ uint32_t timeBase,
+ uint32_t timeOfsProMill,
+ uint32_t triggerPre,
+ bool freeRun) :
+ m_nbSources(nbSources),
+ m_traceSize(traceSize),
+ m_timeBase(timeBase),
+ m_timeOfsProMill(timeOfsProMill),
+ m_triggerPre(triggerPre),
+ m_freeRun(freeRun)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGAddTrigger : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGAddTrigger* create(
+ const TriggerData& triggerData)
+ {
+ return new MsgScopeVisNGAddTrigger(triggerData);
+ }
+
+ const TriggerData& getTriggerData() const { return m_triggerData; }
+
+ private:
+ TriggerData m_triggerData;
+
+ MsgScopeVisNGAddTrigger(const TriggerData& triggerData) :
+ m_triggerData(triggerData)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGChangeTrigger : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGChangeTrigger* create(
+ const TriggerData& triggerData, uint32_t triggerIndex)
+ {
+ return new MsgScopeVisNGChangeTrigger(triggerData, triggerIndex);
+ }
+
+ const TriggerData& getTriggerData() const { return m_triggerData; }
+ uint32_t getTriggerIndex() const { return m_triggerIndex; }
+
+ private:
+ TriggerData m_triggerData;
+ uint32_t m_triggerIndex;
+
+ MsgScopeVisNGChangeTrigger(const TriggerData& triggerData, uint32_t triggerIndex) :
+ m_triggerData(triggerData),
+ m_triggerIndex(triggerIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGRemoveTrigger : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGRemoveTrigger* create(
+ uint32_t triggerIndex)
+ {
+ return new MsgScopeVisNGRemoveTrigger(triggerIndex);
+ }
+
+ uint32_t getTriggerIndex() const { return m_triggerIndex; }
+
+ private:
+ uint32_t m_triggerIndex;
+
+ MsgScopeVisNGRemoveTrigger(uint32_t triggerIndex) :
+ m_triggerIndex(triggerIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGMoveTrigger : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGMoveTrigger* create(
+ uint32_t triggerIndex,
+ bool moveUpElseDown)
+ {
+ return new MsgScopeVisNGMoveTrigger(triggerIndex, moveUpElseDown);
+ }
+
+ uint32_t getTriggerIndex() const { return m_triggerIndex; }
+ bool getMoveUp() const { return m_moveUpElseDown; }
+
+ private:
+ uint32_t m_triggerIndex;
+ bool m_moveUpElseDown;
+
+ MsgScopeVisNGMoveTrigger(uint32_t triggerIndex, bool moveUpElseDown) :
+ m_triggerIndex(triggerIndex),
+ m_moveUpElseDown(moveUpElseDown)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGFocusOnTrigger : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGFocusOnTrigger* create(
+ uint32_t triggerIndex)
+ {
+ return new MsgScopeVisNGFocusOnTrigger(triggerIndex);
+ }
+
+ uint32_t getTriggerIndex() const { return m_triggerIndex; }
+
+ private:
+ uint32_t m_triggerIndex;
+
+ MsgScopeVisNGFocusOnTrigger(uint32_t triggerIndex) :
+ m_triggerIndex(triggerIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGAddTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGAddTrace* create(
+ const TraceData& traceData)
+ {
+ return new MsgScopeVisNGAddTrace(traceData);
+ }
+
+ const TraceData& getTraceData() const { return m_traceData; }
+
+ private:
+ TraceData m_traceData;
+
+ MsgScopeVisNGAddTrace(const TraceData& traceData) :
+ m_traceData(traceData)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGChangeTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGChangeTrace* create(
+ const TraceData& traceData, uint32_t traceIndex)
+ {
+ return new MsgScopeVisNGChangeTrace(traceData, traceIndex);
+ }
+
+ const TraceData& getTraceData() const { return m_traceData; }
+ uint32_t getTraceIndex() const { return m_traceIndex; }
+
+ private:
+ TraceData m_traceData;
+ uint32_t m_traceIndex;
+
+ MsgScopeVisNGChangeTrace(TraceData traceData, uint32_t traceIndex) :
+ m_traceData(traceData),
+ m_traceIndex(traceIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGRemoveTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGRemoveTrace* create(
+ uint32_t traceIndex)
+ {
+ return new MsgScopeVisNGRemoveTrace(traceIndex);
+ }
+
+ uint32_t getTraceIndex() const { return m_traceIndex; }
+
+ private:
+ uint32_t m_traceIndex;
+
+ MsgScopeVisNGRemoveTrace(uint32_t traceIndex) :
+ m_traceIndex(traceIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGMoveTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGMoveTrace* create(
+ uint32_t traceIndex,
+ bool moveUpElseDown)
+ {
+ return new MsgScopeVisNGMoveTrace(traceIndex, moveUpElseDown);
+ }
+
+ uint32_t getTraceIndex() const { return m_traceIndex; }
+ bool getMoveUp() const { return m_moveUpElseDown; }
+
+ private:
+ uint32_t m_traceIndex;
+ bool m_moveUpElseDown;
+
+ MsgScopeVisNGMoveTrace(uint32_t traceIndex, bool moveUpElseDown) :
+ m_traceIndex(traceIndex),
+ m_moveUpElseDown(moveUpElseDown)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGFocusOnTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGFocusOnTrace* create(
+ uint32_t traceIndex)
+ {
+ return new MsgScopeVisNGFocusOnTrace(traceIndex);
+ }
+
+ uint32_t getTraceIndex() const { return m_traceIndex; }
+
+ private:
+ uint32_t m_traceIndex;
+
+ MsgScopeVisNGFocusOnTrace(uint32_t traceIndex) :
+ m_traceIndex(traceIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGOneShot : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGOneShot* create(
+ bool oneShot)
+ {
+ return new MsgScopeVisNGOneShot(oneShot);
+ }
+
+ bool getOneShot() const { return m_oneShot; }
+
+ private:
+ bool m_oneShot;
+
+ MsgScopeVisNGOneShot(bool oneShot) :
+ m_oneShot(oneShot)
+ {}
+ };
+
+ // ---------------------------------------------
+ class MsgScopeVisNGMemoryTrace : public Message {
+ MESSAGE_CLASS_DECLARATION
+
+ public:
+ static MsgScopeVisNGMemoryTrace* create(
+ uint32_t memoryIndex)
+ {
+ return new MsgScopeVisNGMemoryTrace(memoryIndex);
+ }
+
+ uint32_t getMemoryIndex() const { return m_memoryIndex; }
+
+ private:
+ uint32_t m_memoryIndex;
+
+ MsgScopeVisNGMemoryTrace(uint32_t memoryIndex) :
+ m_memoryIndex(memoryIndex)
+ {}
+ };
+
+ // ---------------------------------------------
+
+ /**
+ * Projection stuff
+ */
+ class Projector
+ {
+ public:
+ Projector(ProjectionType projectionType) :
+ m_projectionType(projectionType),
+ m_prevArg(0.0f),
+ m_cache(0),
+ m_cacheMaster(true)
+ {}
+
+ ~Projector()
+ {}
+
+ ProjectionType getProjectionType() const { return m_projectionType; }
+ void settProjectionType(ProjectionType projectionType) { m_projectionType = projectionType; }
+ void setCache(Real *cache) { m_cache = cache; }
+ void setCacheMaster(bool cacheMaster) { m_cacheMaster = cacheMaster; }
+
+ Real run(const Sample& s)
+ {
+ Real v;
+
+ if ((m_cache) && !m_cacheMaster) {
+ return m_cache[(int) m_projectionType];
+ }
+ else
+ {
+ switch (m_projectionType)
+ {
+ case ProjectionImag:
+ v = s.m_imag / 32768.0f;
+ break;
+ case ProjectionMagLin:
+ {
+ uint32_t magsq = s.m_real*s.m_real + s.m_imag*s.m_imag;
+ v = std::sqrt(magsq/1073741824.0f);
+ }
+ break;
+ case ProjectionMagDB:
+ {
+ uint32_t magsq = s.m_real*s.m_real + s.m_imag*s.m_imag;
+ v = log10f(magsq/1073741824.0f) * 10.0f;
+ }
+ break;
+ case ProjectionPhase:
+ v = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI;
+ break;
+ case ProjectionDPhase:
+ {
+ Real curArg = std::atan2((float) s.m_imag, (float) s.m_real);
+ Real dPhi = (curArg - m_prevArg) / M_PI;
+ m_prevArg = curArg;
+
+ if (dPhi < -1.0f) {
+ dPhi += 2.0f;
+ } else if (dPhi > 1.0f) {
+ dPhi -= 2.0f;
+ }
+
+ v = dPhi;
+ }
+ break;
+ case ProjectionReal:
+ default:
+ v = s.m_real / 32768.0f;
+ break;
+ }
+
+ if (m_cache) {
+ m_cache[(int) m_projectionType] = v;
+ }
+
+ return v;
+ }
+ }
+
+ private:
+ ProjectionType m_projectionType;
+ Real m_prevArg;
+ Real *m_cache;
+ bool m_cacheMaster;
+ };
+
+ /**
+ * Trigger stuff
+ */
+ enum TriggerState
+ {
+ TriggerUntriggered, //!< Trigger is not kicked off yet (or trigger list is empty)
+ TriggerTriggered, //!< Trigger has been kicked off
+ TriggerDelay, //!< Trigger conditions have been kicked off but it is waiting for delay before final kick off
+ TriggerNewConfig, //!< Special condition when a new configuration has been received
+ };
+
+ struct TriggerCondition
+ {
+ public:
+ Projector m_projector;
+ TriggerData m_triggerData; //!< Trigger data
+ bool m_prevCondition; //!< Condition (above threshold) at previous sample
+ uint32_t m_triggerDelayCount; //!< Counter of samples for delay
+ uint32_t m_triggerCounter; //!< Counter of trigger occurences
+
+ TriggerCondition(const TriggerData& triggerData) :
+ m_projector(ProjectionReal),
+ m_triggerData(triggerData),
+ m_prevCondition(false),
+ m_triggerDelayCount(0),
+ m_triggerCounter(0)
+ {
+ }
+
+ ~TriggerCondition()
+ {
+ }
+
+ void initProjector()
+ {
+ m_projector.settProjectionType(m_triggerData.m_projectionType);
+ }
+
+ void releaseProjector()
+ {
+ }
+
+ void setData(const TriggerData& triggerData)
+ {
+ m_triggerData = triggerData;
+
+ if (m_projector.getProjectionType() != m_triggerData.m_projectionType)
+ {
+ m_projector.settProjectionType(m_triggerData.m_projectionType);
+ }
+
+ m_prevCondition = false;
+ m_triggerDelayCount = 0;
+ m_triggerCounter = 0;
+ }
+
+ void operator=(const TriggerCondition& other)
+ {
+ setData(other.m_triggerData);
+ }
+ };
+
+ /**
+ * Complex trace stuff
+ */
+ typedef DoubleBufferSimple TraceBuffer;
+
+ struct TraceBackBuffer
+ {
+ TraceBuffer m_traceBuffer;
+ SampleVector::iterator m_endPoint;
+
+ TraceBackBuffer()
+ {
+ m_endPoint = m_traceBuffer.getCurrent();
+ }
+
+ void resize(uint32_t size)
+ {
+ m_traceBuffer.resize(size);
+ }
+
+ void reset()
+ {
+ m_traceBuffer.reset();
+ }
+
+ void write(const SampleVector::const_iterator begin, const SampleVector::const_iterator end)
+ {
+ m_traceBuffer.write(begin, end);
+ }
+
+ unsigned int absoluteFill() const {
+ return m_traceBuffer.absoluteFill();
+ }
+
+ SampleVector::iterator getCurrent() { return m_traceBuffer.getCurrent(); }
+ };
+
+ typedef std::vector TBBVector;
+
+ struct TraceBackDiscreteMemory
+ {
+ std::vector m_traceBackBuffers;
+ std::vector m_sourceFill;
+ uint32_t m_nbSources;
+ uint32_t m_memSize;
+ uint32_t m_currentMemIndex;
+ uint32_t m_traceSize;
+ int m_preTrigCount;
+
+ /**
+ * Allocate with maximum number of traces
+ */
+ TraceBackDiscreteMemory() :
+ m_nbSources(1),
+ m_memSize(m_nbTraceMemories),
+ m_currentMemIndex(0),
+ m_traceSize(m_traceChunkSize)
+ {
+ m_traceBackBuffers.resize(m_nbSources);
+ m_sourceFill.resize(m_nbSources);
+ resizeBuffers(m_traceSize);
+ }
+
+ /**
+ * Resize all trace buffers in memory
+ */
+ void resizeBuffers(uint32_t size)
+ {
+ m_traceSize = size;
+ std::vector::iterator itFill = m_sourceFill.begin();
+
+ for (std::vector::iterator itTBB = m_traceBackBuffers.begin(); itTBB != m_traceBackBuffers.end(); ++itTBB)
+ {
+ for (std::vector::iterator it = itTBB->begin(); it != itTBB->end(); ++it)
+ {
+ it->resize(4*m_traceSize);
+ }
+
+ *itFill = 0;
+ ++itFill;
+ }
+ }
+
+ /**
+ * Set the number of sources
+ */
+ void setNbSources(uint32_t nbSources)
+ {
+ if ((nbSources < 1) || (nbSources > m_maxNbTraceSources)) {
+ return;
+ }
+
+ if (nbSources != m_nbSources)
+ {
+ m_traceBackBuffers.resize(nbSources);
+ resizeBuffers(m_traceSize);
+ m_nbSources = nbSources;
+ }
+ }
+
+ /**
+ * Feed current buffer at source index with sample
+ */
+ void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, uint32_t sourceIndex)
+ {
+ m_traceBackBuffers[sourceIndex][m_currentMemIndex].write(begin, end);
+ m_sourceFill[sourceIndex] += end - begin;
+ }
+
+ /**
+ * Return the minimum number of samples in buffer
+ */
+ int minFill() const
+ {
+ return *std::min_element(m_sourceFill.begin(), m_sourceFill.end());
+ }
+
+ /**
+ * Consume a number of samples from the buffers (that is reduce the fill count)
+ */
+ void consume(int nbSamples)
+ {
+ for (std::vector::iterator itFill = m_sourceFill.begin(); itFill != m_sourceFill.end(); ++itFill)
+ {
+ (*itFill) -= nbSamples;
+ }
+ }
+
+ SampleVector::iterator getBeginIterator(int sourceIndex)
+ {
+ return m_traceBackBuffers[sourceIndex][m_currentMemIndex].getCurrent() - m_sourceFill[sourceIndex];
+ }
+
+ uint32_t getNbSources() const
+ {
+ return m_nbSources;
+ }
+
+ void markEnd(int sourceIndex, SampleVector::iterator& end)
+ {
+ m_traceBackBuffers[sourceIndex][m_currentMemIndex].m_endPoint = end;
+ }
+
+ void store()
+ {
+ uint32_t nextMemIndex = m_currentMemIndex < (m_memSize-1) ? m_currentMemIndex+1 : 0;
+
+ for (std::vector::iterator itTBB = m_traceBackBuffers.begin(); itTBB != m_traceBackBuffers.end(); ++itTBB)
+ {
+ (*itTBB)[nextMemIndex].reset();
+ (*itTBB)[nextMemIndex].write(
+ (*itTBB)[m_currentMemIndex].m_endPoint - m_traceSize,
+ (*itTBB)[m_currentMemIndex].m_endPoint);
+ }
+
+ m_currentMemIndex = nextMemIndex;
+ }
+ };
+
+ /**
+ * Displayable trace stuff
+ */
+ struct TraceControl
+ {
+ Projector m_projector; //!< Projector transform from complex trace to real (displayable) trace
+ int m_traceCount[2]; //!< Count of samples processed (double buffered)
+ Real m_maxPow; //!< Maximum power over the current trace for MagDB overlay display
+ Real m_sumPow; //!< Cumulative power over the current trace for MagDB overlay display
+ int m_nbPow; //!< Number of power samples over the current trace for MagDB overlay display
+
+ TraceControl() : m_projector(ProjectionReal)
+ {
+ reset();
+ }
+
+ ~TraceControl()
+ {
+ }
+
+ void initProjector(ProjectionType projectionType)
+ {
+ m_projector.settProjectionType(projectionType);
+ }
+
+ void releaseProjector()
+ {
+ }
+
+ void reset()
+ {
+ m_traceCount[0] = 0;
+ m_traceCount[1] = 0;
+ m_maxPow = 0.0f;
+ m_sumPow = 0.0f;
+ m_nbPow = 0;
+ }
+ };
+
+ struct Traces
+ {
+ std::vector m_tracesControl; //!< Corresponding traces control data
+ std::vector m_tracesData; //!< Corresponding traces data
+ std::vector m_traces[2]; //!< Double buffer of traces processed by glScope
+ int m_traceSize; //!< Current size of a trace in buffer
+ int m_maxTraceSize; //!< Maximum Size of a trace in buffer
+ bool evenOddIndex; //!< Even (true) or odd (false) index
+
+ Traces() : evenOddIndex(true), m_traceSize(0), m_maxTraceSize(0), m_x0(0), m_x1(0)
+ {
+ }
+
+ ~Traces()
+ {
+ if (m_x0) delete[] m_x0;
+ if (m_x1) delete[] m_x1;
+ m_maxTraceSize = 0;
+ }
+
+ bool isVerticalDisplayChange(const TraceData& traceData, uint32_t traceIndex)
+ {
+ return (m_tracesData[traceIndex].m_projectionType != traceData.m_projectionType)
+ || (m_tracesData[traceIndex].m_amp != traceData.m_amp)
+ || (m_tracesData[traceIndex].m_ofs != traceData.m_ofs
+ || (m_tracesData[traceIndex].m_traceColor != traceData.m_traceColor));
+ }
+
+ void addTrace(const TraceData& traceData, int traceSize)
+ {
+ if (m_traces[0].size() < m_maxNbTraces)
+ {
+ m_traces[0].push_back(0);
+ m_traces[1].push_back(0);
+ m_tracesData.push_back(traceData);
+ m_tracesControl.push_back(TraceControl());
+ m_tracesControl.back().initProjector(traceData.m_projectionType);
+
+ resize(traceSize);
+ }
+ }
+
+ void changeTrace(const TraceData& traceData, uint32_t traceIndex)
+ {
+ if (traceIndex < m_tracesControl.size()) {
+ m_tracesControl[traceIndex].releaseProjector();
+ m_tracesControl[traceIndex].initProjector(traceData.m_projectionType);
+ m_tracesData[traceIndex] = traceData;
+ }
+ }
+
+ void removeTrace(uint32_t traceIndex)
+ {
+ if (traceIndex < m_tracesControl.size())
+ {
+ m_traces[0].erase(m_traces[0].begin() + traceIndex);
+ m_traces[1].erase(m_traces[1].begin() + traceIndex);
+ m_tracesControl[traceIndex].releaseProjector();
+ m_tracesControl.erase(m_tracesControl.begin() + traceIndex);
+ m_tracesData.erase(m_tracesData.begin() + traceIndex);
+
+ resize(m_traceSize); // reallocate pointers
+ }
+ }
+
+ void moveTrace(uint32_t traceIndex, bool upElseDown)
+ {
+ if ((!upElseDown) && (traceIndex == 0)) {
+ return;
+ }
+
+ int nextControlIndex = (traceIndex + (upElseDown ? 1 : -1)) % (m_tracesControl.size());
+ int nextDataIndex = (traceIndex + (upElseDown ? 1 : -1)) % (m_tracesData.size()); // should be the same
+
+ m_tracesControl[traceIndex].releaseProjector();
+ m_tracesControl[nextControlIndex].releaseProjector();
+
+ TraceControl nextControl = m_tracesControl[nextControlIndex];
+ m_tracesControl[nextControlIndex] = m_tracesControl[traceIndex];
+ m_tracesControl[traceIndex] = nextControl;
+
+ TraceData nextData = m_tracesData[nextDataIndex];
+ m_tracesData[nextDataIndex] = m_tracesData[traceIndex];
+ m_tracesData[traceIndex] = nextData;
+
+ m_tracesControl[traceIndex].initProjector(m_tracesData[traceIndex].m_projectionType);
+ m_tracesControl[nextControlIndex].initProjector(m_tracesData[nextDataIndex].m_projectionType);
+ }
+
+ void resize(int traceSize)
+ {
+ m_traceSize = traceSize;
+
+ if (m_traceSize > m_maxTraceSize)
+ {
+ delete[] m_x0;
+ delete[] m_x1;
+ m_x0 = new float[2*m_traceSize*m_maxNbTraces];
+ m_x1 = new float[2*m_traceSize*m_maxNbTraces];
+
+ m_maxTraceSize = m_traceSize;
+ }
+
+ std::fill_n(m_x0, 2*m_traceSize*m_traces[0].size(), 0.0f);
+ std::fill_n(m_x1, 2*m_traceSize*m_traces[0].size(), 0.0f);
+
+ for (int i = 0; i < m_traces[0].size(); i++)
+ {
+ (m_traces[0])[i] = &m_x0[2*m_traceSize*i];
+ (m_traces[1])[i] = &m_x1[2*m_traceSize*i];
+ }
+ }
+
+ uint32_t currentBufferIndex() const { return evenOddIndex? 0 : 1; }
+ uint32_t size() const { return m_tracesControl.size(); }
+
+ void switchBuffer()
+ {
+ evenOddIndex = !evenOddIndex;
+
+ for (std::vector::iterator it = m_tracesControl.begin(); it != m_tracesControl.end(); ++it)
+ {
+ it->m_traceCount[currentBufferIndex()] = 0;
+ }
+ }
+
+ private:
+ float *m_x0;
+ float *m_x1;
+ };
+
+ class TriggerComparator
+ {
+ public:
+ TriggerComparator() : m_level(0), m_reset(true)
+ {
+ computeLevels();
+ }
+
+ bool triggered(const Sample& s, TriggerCondition& triggerCondition)
+ {
+ if (triggerCondition.m_triggerData.m_triggerLevel != m_level)
+ {
+ m_level = triggerCondition.m_triggerData.m_triggerLevel;
+ computeLevels();
+ }
+
+ bool condition, trigger;
+
+ if (triggerCondition.m_projector.getProjectionType() == ProjectionMagDB) {
+ condition = triggerCondition.m_projector.run(s) > m_levelPowerDB;
+ } else if (triggerCondition.m_projector.getProjectionType() == ProjectionMagLin) {
+ condition = triggerCondition.m_projector.run(s) > m_levelPowerLin;
+ } else {
+ condition = triggerCondition.m_projector.run(s) > m_level;
+ }
+
+ if (m_reset)
+ {
+ triggerCondition.m_prevCondition = condition;
+ m_reset = false;
+ return false;
+ }
+
+ if (triggerCondition.m_triggerData.m_triggerBothEdges) {
+ trigger = triggerCondition.m_prevCondition ? !condition : condition; // This is a XOR between bools
+ } else if (triggerCondition.m_triggerData.m_triggerPositiveEdge) {
+ trigger = !triggerCondition.m_prevCondition && condition;
+ } else {
+ trigger = triggerCondition.m_prevCondition && !condition;
+ }
+
+// if (trigger) {
+// qDebug("ScopeVisNG::triggered: %s/%s %f/%f",
+// triggerCondition.m_prevCondition ? "T" : "F",
+// condition ? "T" : "F",
+// triggerCondition.m_projector->run(s),
+// triggerCondition.m_triggerData.m_triggerLevel);
+// }
+
+ triggerCondition.m_prevCondition = condition;
+ return trigger;
+ }
+
+ void reset()
+ {
+ m_reset = true;
+ }
+
+ private:
+ void computeLevels()
+ {
+ m_levelPowerLin = m_level + 1.0f;
+ m_levelPowerDB = (100.0f * (m_level - 1.0f));
+ }
+
+ Real m_level;
+ Real m_levelPowerDB;
+ Real m_levelPowerLin;
+ bool m_reset;
+ };
+
+ typedef std::vector TBMemoriesBegins;
+
+ GLScopeMulti* m_glScope;
+ uint32_t m_preTriggerDelay; //!< Pre-trigger delay in number of samples
+ std::vector m_triggerConditions; //!< Chain of triggers
+ int m_currentTriggerIndex; //!< Index of current index in the chain
+ int m_focusedTriggerIndex; //!< Index of the trigger that has focus
+ TriggerState m_triggerState; //!< Current trigger state
+ Traces m_traces; //!< Displayable traces
+ int m_focusedTraceIndex; //!< Index of the trace that has focus
+ int m_traceSize; //!< Size of traces in number of samples
+ int m_nbSamples; //!< Number of samples yet to process in one complex trace
+ int m_timeBase; //!< Trace display time divisor
+ int m_timeOfsProMill; //!< Start trace shift in 1/1000 trace size
+ bool m_traceStart; //!< Trace is at start point
+ bool m_postTrigBuffering; //!< Buffering after trigger match to get enough samples for the display traces
+ int m_traceFill; //!< Count of samples accumulated into trace
+ int m_zTraceIndex; //!< Index of the trace used for Z input (luminance or false colors)
+ SampleVector::const_iterator m_triggerPoint; //!< Trigger start location in the samples vector
+ int m_sampleRate;
+ TraceBackDiscreteMemory m_traceDiscreteMemory; //!< Complex trace memories for triggered states. One trace history per source
+ 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
+ QMutex m_mutex;
+ Real m_projectorCache[(int) nbProjectionTypes];
+ bool m_triggerOneShot; //!< True when one shot mode is active
+ bool m_triggerWaitForReset; //!< In one shot mode suspended until reset by UI
+ uint32_t m_currentTraceMemoryIndex; //!< The current index of trace in memory (0: current)
+ uint32_t m_nbSources; //!< Current number of sample sources
+ TBMemoriesBegins m_tbMemoriesBegins; //!< Current begin iterators
+
+ /**
+ * Moves on to the next trigger if any or increments trigger count if in repeat mode
+ * - If not final it returns true
+ * - If final i.e. signal is actually triggerd it returns false
+ */
+ bool nextTrigger(); //!< Returns true if not final
+
+ /**
+ * Process all input sources when ready. Assumes all memoruy traces are fed with enough samples
+ */
+ void processSources();
+
+ /**
+ * process a trace in memory at current trace index in memory
+ */
+ void processMemorySources();
+
+ /**
+ * Process traces from complex trace memory buffer.
+ * - if finished it returns the number of unprocessed samples left in the buffer
+ * - if not finished it returns -1
+ */
+ void processTraces(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool traceBack, int sourceIndex);
+
+ /**
+ * Get maximum trace delay
+ */
+ void updateMaxTraceDelay();
+
+ /**
+ * Initialize trace buffers
+ */
+ void initTraceBuffers();
+
+ /**
+ * Calculate trigger levels on display
+ * - every time a trigger condition focus changes TBD
+ * - every time the focused trigger condition changes its projection type or level
+ * - every time a trace data changes: projection type, amp, offset
+ * - every time a trace data is added or removed
+ */
+ void computeDisplayTriggerLevels();
+
+ /**
+ * Update glScope display
+ * - Live trace: call glScipe update method
+ * - Trace in memory: call process memory trace
+ */
+ void updateGLScopeDisplay();
+
+ void lookForTrigger();
+};
+
+
+
+
+#endif /* SDRBASE_DSP_SCOPEVISMULTI_H_ */
diff --git a/sdrbase/gui/glscopemulti.cpp b/sdrbase/gui/glscopemulti.cpp
new file mode 100644
index 000000000..ffa81869a
--- /dev/null
+++ b/sdrbase/gui/glscopemulti.cpp
@@ -0,0 +1,1954 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "glscopemulti.h"
+
+GLScopeMulti::GLScopeMulti(QWidget* parent) :
+ QGLWidget(parent),
+ m_tracesData(0),
+ m_traces(0),
+ m_bufferIndex(0),
+ m_displayMode(DisplayX),
+ m_dataChanged(false),
+ m_configChanged(false),
+ m_displayGridIntensity(10),
+ m_displayTraceIntensity(50),
+ m_timeBase(1),
+ m_traceSize(0),
+ m_sampleRate(0),
+ m_triggerPre(0),
+ m_timeOfsProMill(0),
+ m_focusedTraceIndex(0),
+ m_timeOffset(0)
+{
+ setAttribute(Qt::WA_OpaquePaintEvent);
+ connect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
+ m_timer.start(50);
+
+ m_y1Scale.setFont(font());
+ m_y1Scale.setOrientation(Qt::Vertical);
+ m_y2Scale.setFont(font());
+ m_y2Scale.setOrientation(Qt::Vertical);
+ m_x1Scale.setFont(font());
+ m_x1Scale.setOrientation(Qt::Horizontal);
+ m_x2Scale.setFont(font());
+ m_x2Scale.setOrientation(Qt::Horizontal);
+
+ m_channelOverlayFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
+ m_channelOverlayFont.setBold(true);
+ m_channelOverlayFont.setPointSize(font().pointSize()+1);
+
+ //m_traceCounter = 0;
+}
+
+GLScopeMulti::~GLScopeMulti()
+{
+ cleanup();
+}
+
+void GLScopeMulti::setDisplayGridIntensity(int intensity)
+{
+ m_displayGridIntensity = intensity;
+ if (m_displayGridIntensity > 100) {
+ m_displayGridIntensity = 100;
+ } else if (m_displayGridIntensity < 0) {
+ m_displayGridIntensity = 0;
+ }
+ update();
+}
+
+void GLScopeMulti::setDisplayTraceIntensity(int intensity)
+{
+ m_displayTraceIntensity = intensity;
+ if (m_displayTraceIntensity > 100) {
+ m_displayTraceIntensity = 100;
+ } else if (m_displayTraceIntensity < 0) {
+ m_displayTraceIntensity = 0;
+ }
+ update();
+}
+
+void GLScopeMulti::setTraces(std::vector* tracesData, std::vector* traces)
+{
+ m_tracesData = tracesData;
+ m_traces = traces;
+}
+
+void GLScopeMulti::newTraces(std::vector* traces)
+{
+ if (traces->size() > 0)
+ {
+ if(!m_mutex.tryLock(2))
+ return;
+
+ m_traces = traces;
+ m_dataChanged = true;
+
+ m_mutex.unlock();
+ }
+}
+
+void GLScopeMulti::initializeGL()
+{
+ QOpenGLContext *glCurrentContext = QOpenGLContext::currentContext();
+
+ if (glCurrentContext) {
+ if (QOpenGLContext::currentContext()->isValid()) {
+ qDebug() << "GLScopeMulti::initializeGL: context:"
+ << " major: " << (QOpenGLContext::currentContext()->format()).majorVersion()
+ << " minor: " << (QOpenGLContext::currentContext()->format()).minorVersion()
+ << " ES: " << (QOpenGLContext::currentContext()->isOpenGLES() ? "yes" : "no");
+ }
+ else {
+ qDebug() << "GLScopeMulti::initializeGL: current context is invalid";
+ }
+ } else {
+ qCritical() << "GLScopeMulti::initializeGL: no current context";
+ return;
+ }
+
+ QSurface *surface = glCurrentContext->surface();
+
+ if (surface == 0)
+ {
+ qCritical() << "GLScopeMulti::initializeGL: no surface attached";
+ return;
+ }
+ else
+ {
+ if (surface->surfaceType() != QSurface::OpenGLSurface)
+ {
+ qCritical() << "GLScopeMulti::initializeGL: surface is not an OpenGLSurface: " << surface->surfaceType()
+ << " cannot use an OpenGL context";
+ return;
+ }
+ else
+ {
+ qDebug() << "GLScopeMulti::initializeGL: OpenGL surface:"
+ << " class: " << (surface->surfaceClass() == QSurface::Window ? "Window" : "Offscreen");
+ }
+ }
+
+ connect(glCurrentContext, &QOpenGLContext::aboutToBeDestroyed, this, &GLScopeMulti::cleanup); // TODO: when migrating to QOpenGLWidget
+
+ QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
+ glFunctions->initializeOpenGLFunctions();
+
+ //glDisable(GL_DEPTH_TEST);
+ m_glShaderSimple.initializeGL();
+ m_glShaderLeft1Scale.initializeGL();
+ m_glShaderBottom1Scale.initializeGL();
+ m_glShaderLeft2Scale.initializeGL();
+ m_glShaderBottom2Scale.initializeGL();
+ m_glShaderPowerOverlay.initializeGL();
+}
+
+void GLScopeMulti::resizeGL(int width, int height)
+{
+ QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
+ glFunctions->glViewport(0, 0, width, height);
+ m_configChanged = true;
+}
+
+void GLScopeMulti::paintGL()
+{
+ if(!m_mutex.tryLock(2))
+ return;
+
+ if(m_configChanged)
+ applyConfig();
+
+// qDebug("GLScopeMulti::paintGL: m_traceCounter: %d", m_traceCounter);
+// m_traceCounter = 0;
+
+ m_dataChanged = false;
+
+ QOpenGLFunctions *glFunctions = QOpenGLContext::currentContext()->functions();
+ glFunctions->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glFunctions->glClear(GL_COLOR_BUFFER_BIT);
+
+ if ((m_displayMode == DisplayX) || (m_displayMode == DisplayXYV) || (m_displayMode == DisplayXYH)) // display trace #0
+ {
+ // draw rect around
+ {
+ GLfloat q3[] {
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0
+ };
+
+ QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
+ m_glShaderSimple.drawContour(m_glScopeMatrix1, color, q3, 4);
+ }
+
+ // paint grid
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ // Y1 (X trace or trace #0)
+ {
+ tickList = &m_y1Scale.getTickList();
+
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+
+ for (int i= 0; i < tickList->count(); i++)
+ {
+ tick = &(*tickList)[i];
+
+ if (tick->major)
+ {
+ if (tick->textSize > 0)
+ {
+ float y = 1 - (tick->pos / m_y1Scale.getSize());
+ q3[4*effectiveTicks] = 0;
+ q3[4*effectiveTicks+1] = y;
+ q3[4*effectiveTicks+2] = 1;
+ q3[4*effectiveTicks+3] = y;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ float blue = 1.0f;
+ QVector4D color(1.0f, 1.0f, blue, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
+ }
+
+ // X1 (time)
+ {
+ tickList = &m_x1Scale.getTickList();
+
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float x = tick->pos / m_x1Scale.getSize();
+ q3[4*effectiveTicks] = x;
+ q3[4*effectiveTicks+1] = 0;
+ q3[4*effectiveTicks+2] = x;
+ q3[4*effectiveTicks+3] = 1;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
+ }
+
+ // paint left #1 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ m_glShaderLeft1Scale.drawSurface(m_glLeft1ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint bottom #1 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ m_glShaderBottom1Scale.drawSurface(m_glBot1ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint trace #1
+ if (m_traceSize > 0)
+ {
+ const float *trace = (*m_traces)[0];
+ const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[0];
+
+ if (traceData.m_viewTrace)
+ {
+ int start = (m_timeOfsProMill/1000.0) * m_traceSize;
+ int end = std::min(start + m_traceSize/m_timeBase, m_traceSize);
+
+ if(end - start < 2)
+ start--;
+
+ float rectX = m_glScopeRect1.x();
+ float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f;
+ float rectW = m_glScopeRect1.width() * (float)m_timeBase / (float)(m_traceSize - 1);
+ //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp;
+ float rectH = -m_glScopeRect1.height() / 2.0f;
+
+ //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f);
+ QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start);
+
+ // Paint trigger level if any
+ if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f))
+ {
+ GLfloat q3[] {
+ 0, traceData.m_triggerDisplayLevel,
+ 1, traceData.m_triggerDisplayLevel
+ };
+
+ float rectX = m_glScopeRect1.x();
+ float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f;
+ float rectW = m_glScopeRect1.width();
+ float rectH = -m_glScopeRect1.height() / 2.0f;
+
+ QVector4D color(
+ m_focusedTriggerData.m_triggerColorR,
+ m_focusedTriggerData.m_triggerColorG,
+ m_focusedTriggerData.m_triggerColorB,
+ 0.4f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawSegments(mat, color, q3, 2);
+ } // display trigger
+
+ // Paint overlay if any
+ if ((m_focusedTraceIndex == 0) && (traceData.m_hasTextOverlay))
+ {
+ drawChannelOverlay(
+ traceData.m_textOverlay,
+ traceData.m_traceColor,
+ m_channelOverlayPixmap1,
+ m_glScopeRect1);
+ } // display overlay
+ } // displayable trace
+ } // trace length > 0
+ } // Display X
+
+ if ((m_displayMode == DisplayY) || (m_displayMode == DisplayXYV) || (m_displayMode == DisplayXYH)) // display traces #1..n
+ {
+ // draw rect around
+ {
+ GLfloat q3[] {
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0
+ };
+
+ QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
+ m_glShaderSimple.drawContour(m_glScopeMatrix2, color, q3, 4);
+ }
+
+ // paint grid
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ // Y2 (Focused Y trace)
+ {
+ tickList = &m_y2Scale.getTickList();
+
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+
+ for (int i= 0; i < tickList->count(); i++)
+ {
+ tick = &(*tickList)[i];
+
+ if (tick->major)
+ {
+ if (tick->textSize > 0)
+ {
+ float y = 1 - (tick->pos / m_y2Scale.getSize());
+ q3[4*effectiveTicks] = 0;
+ q3[4*effectiveTicks+1] = y;
+ q3[4*effectiveTicks+2] = 1;
+ q3[4*effectiveTicks+3] = y;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ float blue = 1.0f;
+ QVector4D color(1.0f, 1.0f, blue, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks);
+ }
+
+ // X2 (time)
+ {
+ tickList = &m_x2Scale.getTickList();
+
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float x = tick->pos / m_x2Scale.getSize();
+ q3[4*effectiveTicks] = x;
+ q3[4*effectiveTicks+1] = 0;
+ q3[4*effectiveTicks+2] = x;
+ q3[4*effectiveTicks+3] = 1;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks);
+ }
+
+ // paint left #2 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ m_glShaderLeft2Scale.drawSurface(m_glLeft2ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint bottom #2 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ m_glShaderBottom2Scale.drawSurface(m_glBot2ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint traces #1..n
+ if (m_traceSize > 0)
+ {
+ int start = (m_timeOfsProMill/1000.0) * m_traceSize;
+ int end = std::min(start + m_traceSize/m_timeBase, m_traceSize);
+
+ if(end - start < 2)
+ start--;
+
+ for (int i = 1; i < m_traces->size(); i++)
+ {
+ const float *trace = (*m_traces)[i];
+ const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i];
+
+ if (!traceData.m_viewTrace) {
+ continue;
+ }
+
+ float rectX = m_glScopeRect2.x();
+ float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f;
+ float rectW = m_glScopeRect2.width() * (float)m_timeBase / (float)(m_traceSize - 1);
+ //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp;
+ float rectH = -m_glScopeRect2.height() / 2.0f;
+
+ //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f);
+ QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start);
+
+ // Paint trigger level if any
+ if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f))
+ {
+ GLfloat q3[] {
+ 0, traceData.m_triggerDisplayLevel,
+ 1, traceData.m_triggerDisplayLevel
+ };
+
+ float rectX = m_glScopeRect2.x();
+ float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f;
+ float rectW = m_glScopeRect2.width();
+ float rectH = -m_glScopeRect2.height() / 2.0f;
+
+ QVector4D color(
+ m_focusedTriggerData.m_triggerColorR,
+ m_focusedTriggerData.m_triggerColorG,
+ m_focusedTriggerData.m_triggerColorB,
+ 0.4f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawSegments(mat, color, q3, 2);
+ }
+
+ // Paint overlay if any
+ if ((i == m_focusedTraceIndex) && (traceData.m_hasTextOverlay))
+ {
+ drawChannelOverlay(
+ traceData.m_textOverlay,
+ traceData.m_traceColor,
+ m_channelOverlayPixmap2,
+ m_glScopeRect2);
+ }
+
+ } // one trace display
+ } // trace length > 0
+ } // Display Y
+
+ if (m_displayMode == DisplayPol)
+ {
+ // paint left display: mixed XY
+
+ // draw rect around
+ {
+ GLfloat q3[] {
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0
+ };
+
+ QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
+ m_glShaderSimple.drawContour(m_glScopeMatrix1, color, q3, 4);
+
+ }
+
+ // paint grid
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ // Horizontal Y1
+ tickList = &m_y1Scale.getTickList();
+ {
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+
+ for (int i= 0; i < tickList->count(); i++)
+ {
+ tick = &(*tickList)[i];
+
+ if (tick->major)
+ {
+ if (tick->textSize > 0)
+ {
+ float y = 1 - (tick->pos / m_y1Scale.getSize());
+ q3[4*effectiveTicks] = 0;
+ q3[4*effectiveTicks+1] = y;
+ q3[4*effectiveTicks+2] = 1;
+ q3[4*effectiveTicks+3] = y;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 0.25f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
+ }
+
+ // Vertical X1
+ tickList = &m_x1Scale.getTickList();
+ {
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float x = tick->pos / m_x1Scale.getSize();
+ q3[4*effectiveTicks] = x;
+ q3[4*effectiveTicks+1] = 0;
+ q3[4*effectiveTicks+2] = x;
+ q3[4*effectiveTicks+3] = 1;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
+ }
+
+ // paint left #1 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ m_glShaderLeft1Scale.drawSurface(m_glLeft1ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint bottom #1 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ m_glShaderBottom1Scale.drawSurface(m_glBot1ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // Paint secondary grid
+
+ // Horizontal Y2
+ tickList = &m_y2Scale.getTickList();
+ {
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float y = 1 - (tick->pos / m_y2Scale.getSize());
+ q3[4*effectiveTicks] = 0;
+ q3[4*effectiveTicks+1] = y;
+ q3[4*effectiveTicks+2] = 1;
+ q3[4*effectiveTicks+3] = y;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(0.25f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix1, color, q3, 2*effectiveTicks);
+ }
+
+ // Paint secondary scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ m_glShaderLeft2Scale.drawSurface(m_glRight1ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint all traces
+ if (m_traceSize > 0)
+ {
+ int start = (m_timeOfsProMill/1000.0) * m_traceSize;
+ int end = std::min(start + m_traceSize/m_timeBase, m_traceSize);
+
+ if(end - start < 2)
+ start--;
+
+ for (int i = 0; i < m_traces->size(); i++)
+ {
+ const float *trace = (*m_traces)[i];
+ const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i];
+
+ if (!traceData.m_viewTrace) {
+ continue;
+ }
+
+ float rectX = m_glScopeRect1.x();
+ float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f;
+ float rectW = m_glScopeRect1.width() * (float)m_timeBase / (float)(m_traceSize - 1);
+ //float rectH = -(m_glScopeRect1.height() / 2.0f) * traceData.m_amp;
+ float rectH = -m_glScopeRect1.height() / 2.0f;
+
+ //QVector4D color(1.0f, 1.0f, 0.25f, m_displayTraceIntensity / 100.0f);
+ QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawPolyline(mat, color, (GLfloat *) &trace[2*start], end - start);
+
+ // Paint trigger level if any
+ if ((traceData.m_triggerDisplayLevel > -1.0f) && (traceData.m_triggerDisplayLevel < 1.0f))
+ {
+ GLfloat q3[] {
+ 0, traceData.m_triggerDisplayLevel,
+ 1, traceData.m_triggerDisplayLevel
+ };
+
+ float rectX = m_glScopeRect1.x();
+ float rectY = m_glScopeRect1.y() + m_glScopeRect1.height() / 2.0f;
+ float rectW = m_glScopeRect1.width();
+ float rectH = -m_glScopeRect1.height() / 2.0f;
+
+ QVector4D color(
+ m_focusedTriggerData.m_triggerColorR,
+ m_focusedTriggerData.m_triggerColorG,
+ m_focusedTriggerData.m_triggerColorB,
+ 0.4f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawSegments(mat, color, q3, 2);
+ }
+
+ // Paint overlay if any
+ if ((i == m_focusedTraceIndex) && (traceData.m_hasTextOverlay))
+ {
+ drawChannelOverlay(
+ traceData.m_textOverlay,
+ traceData.m_traceColor,
+ m_channelOverlayPixmap1,
+ m_glScopeRect1);
+ }
+ } // all traces display
+ } // trace length > 0
+
+ // paint right display: polar XY
+
+ // draw rect around
+ {
+ GLfloat q3[] {
+ 1, 1,
+ 0, 1,
+ 0, 0,
+ 1, 0
+ };
+
+ QVector4D color(1.0f, 1.0f, 1.0f, 0.5f);
+ m_glShaderSimple.drawContour(m_glScopeMatrix2, color, q3, 4);
+ }
+
+ // paint grid
+
+ // Horizontal Y2
+ tickList = &m_y2Scale.getTickList();
+ {
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float y = 1 - (tick->pos / m_y2Scale.getSize());
+ q3[4*effectiveTicks] = 0;
+ q3[4*effectiveTicks+1] = y;
+ q3[4*effectiveTicks+2] = 1;
+ q3[4*effectiveTicks+3] = y;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks);
+ }
+
+ // Vertical X2
+ tickList = &m_x2Scale.getTickList();
+ {
+ GLfloat q3[4*tickList->count()];
+ int effectiveTicks = 0;
+ for(int i= 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ float x = tick->pos / m_x2Scale.getSize();
+ q3[4*effectiveTicks] = x;
+ q3[4*effectiveTicks+1] = 0;
+ q3[4*effectiveTicks+2] = x;
+ q3[4*effectiveTicks+3] = 1;
+ effectiveTicks++;
+ }
+ }
+ }
+
+ QVector4D color(1.0f, 1.0f, 1.0f, (float) m_displayGridIntensity / 100.0f);
+ m_glShaderSimple.drawSegments(m_glScopeMatrix2, color, q3, 2*effectiveTicks);
+ }
+
+ // paint left #2 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ m_glShaderLeft2Scale.drawSurface(m_glLeft2ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint bottom #2 scale
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ m_glShaderBottom2Scale.drawSurface(m_glBot2ScaleMatrix, tex1, vtx1, 4);
+ }
+
+ // paint polar traces
+
+ if (m_traceSize > 0)
+ {
+ int start = (m_timeOfsProMill/1000.0) * m_traceSize;
+ int end = std::min(start + m_traceSize/m_timeBase, m_traceSize);
+
+ if(end - start < 2)
+ start--;
+
+ GLfloat q3[2*(end - start)];
+ const float *trace0 = (*m_traces)[0];
+ memcpy(q3, &(trace0[2*start+1]), (2*(end - start) - 1)*sizeof(float)); // copy X values
+
+ for (int i = 1; i < m_traces->size(); i++)
+ {
+ const float *trace = (*m_traces)[i];
+ const ScopeVisMulti::TraceData& traceData = (*m_tracesData)[i];
+
+ if (!traceData.m_viewTrace) {
+ continue;
+ }
+
+ for(int i = start; i < end; i++)
+ {
+ float y = trace[2*i+1];
+ q3[2*(i-start)+1] = y;
+ }
+
+ float rectX = m_glScopeRect2.x() + m_glScopeRect2.width() / 2.0f;
+ float rectY = m_glScopeRect2.y() + m_glScopeRect2.height() / 2.0f;
+ float rectW = m_glScopeRect2.width() / 2.0f;
+ float rectH = -(m_glScopeRect2.height() / 2.0f);
+
+ QVector4D color(traceData.m_traceColorR, traceData.m_traceColorG, traceData.m_traceColorB, m_displayTraceIntensity / 100.0f);
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderSimple.drawPolyline(mat, color, q3, end -start);
+ } // XY polar display
+ } // trace length > 0
+ } // XY mixed + polar display
+
+ m_mutex.unlock();
+}
+
+void GLScopeMulti::setSampleRate(int sampleRate)
+{
+ m_sampleRate = sampleRate;
+ m_configChanged = true;
+ update();
+ emit sampleRateChanged(m_sampleRate);
+}
+
+void GLScopeMulti::setTimeBase(int timeBase)
+{
+ m_timeBase = timeBase;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::setTriggerPre(uint32_t triggerPre)
+{
+ m_triggerPre = triggerPre;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::setTimeOfsProMill(int timeOfsProMill)
+{
+ m_timeOfsProMill = timeOfsProMill;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::setFocusedTraceIndex(uint32_t traceIndex)
+{
+ m_focusedTraceIndex = traceIndex;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::setDisplayMode(DisplayMode displayMode)
+{
+ m_displayMode = displayMode;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::setTraceSize(int traceSize)
+{
+ m_traceSize = traceSize;
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::updateDisplay()
+{
+ m_configChanged = true;
+ update();
+}
+
+void GLScopeMulti::applyConfig()
+{
+ m_configChanged = false;
+
+ QFontMetrics fm(font());
+ int M = fm.width("-");
+ //float t_start = ((m_timeOfsProMill / 1000.0) * ((float) m_traceSize / m_sampleRate)) - ((float) m_triggerPre / m_sampleRate);
+ float t_start = (((m_timeOfsProMill / 1000.0f) * (float) m_traceSize) / m_sampleRate) - ((float) m_triggerPre / m_sampleRate);
+ float t_len = ((float) m_traceSize / m_sampleRate) / (float) m_timeBase;
+
+ // scales
+
+ m_x1Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
+
+ if (m_displayMode == DisplayPol)
+ {
+ setYScale(m_x2Scale, 0); // polar scale (X)
+ }
+ else
+ {
+ m_x2Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
+ }
+
+ if (m_traces->size() > 0)
+ {
+ setYScale(m_y1Scale, 0); // This is always the X trace (trace #0)
+ }
+
+ if ((m_traces->size() > 1) && (m_focusedTraceIndex < m_traces->size()))
+ {
+ setYScale(m_y2Scale, m_focusedTraceIndex > 0 ? m_focusedTraceIndex : 1); // if Highlighted trace is #0 (X trace) set it to first Y trace (trace #1)
+ }
+ else
+ {
+ setYScale(m_y2Scale, 0); // Default to the X trace (trace #0) - If there is only one trace it should not get there (Y displays disabled in the UI)
+ }
+
+ // display arrangements
+
+ if ((m_displayMode == DisplayX) || (m_displayMode == DisplayY)) // unique displays
+ {
+ setUniqueDisplays();
+ }
+ else if (m_displayMode == DisplayXYV) // both displays vertically arranged
+ {
+ setVerticalDisplays();
+ }
+ else if (m_displayMode == DisplayXYH) // both displays horizontally arranged
+ {
+ setHorizontalDisplays();
+ }
+ else if (m_displayMode == DisplayPol) // horizontal arrangement: XY stacked on left and polar on right
+ {
+ setPolarDisplays();
+ }
+}
+
+void GLScopeMulti::setUniqueDisplays()
+{
+ QFontMetrics fm(font());
+ int M = fm.width("-");
+ int scopeHeight = height() - m_topMargin - m_botMargin;
+ int scopeWidth = width() - m_leftMargin - m_rightMargin;
+
+ // X display
+
+ m_glScopeRect1 = QRectF(
+ (float) m_leftMargin / (float) width(),
+ (float) m_topMargin / (float) height(),
+ (float) scopeWidth / (float) width(),
+ (float) scopeHeight / (float) height()
+ );
+
+ m_glScopeMatrix1.setToIdentity();
+ m_glScopeMatrix1.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix1.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot1ScaleMatrix.setToIdentity();
+ m_glBot1ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot1ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft1ScaleMatrix.setToIdentity();
+ m_glLeft1ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft1ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ // Y displays
+
+ m_glScopeRect2 = QRectF(
+ (float) m_leftMargin / (float) width(),
+ (float) m_topMargin / (float) height(),
+ (float) scopeWidth / (float) width(),
+ (float) scopeHeight / (float) height()
+ );
+
+ m_glScopeMatrix2.setToIdentity();
+ m_glScopeMatrix2.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix2.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot2ScaleMatrix.setToIdentity();
+ m_glBot2ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot2ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft2ScaleMatrix.setToIdentity();
+ m_glLeft2ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft2ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ { // X horizontal scale (X1)
+ m_x1Scale.setSize(scopeWidth);
+
+ m_bot1ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
+ } // X horizontal scale
+
+ { // Y horizontal scale (X2)
+ m_x2Scale.setSize(scopeWidth);
+
+ m_bot2ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
+ } // Y horizontal scale
+
+ { // X vertical scale (Y1)
+ m_y1Scale.setSize(scopeHeight);
+
+ m_left1ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
+ } // X vertical scale
+
+ { // Y vertical scale (Y2)
+ m_y2Scale.setSize(scopeHeight);
+
+ m_left2ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
+ } // Y vertical scale
+}
+
+void GLScopeMulti::setVerticalDisplays()
+{
+ QFontMetrics fm(font());
+ int M = fm.width("-");
+ int scopeHeight = (height() - m_topMargin) / 2 - m_botMargin;
+ int scopeWidth = width() - m_leftMargin - m_rightMargin;
+
+ // X display
+
+ m_glScopeRect1 = QRectF(
+ (float) m_leftMargin / (float) width(),
+ (float) m_topMargin / (float) height(),
+ (float) scopeWidth / (float) width(),
+ (float) scopeHeight / (float) height()
+ );
+
+ m_glScopeMatrix1.setToIdentity();
+ m_glScopeMatrix1.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix1.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot1ScaleMatrix.setToIdentity();
+ m_glBot1ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot1ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft1ScaleMatrix.setToIdentity();
+ m_glLeft1ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft1ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ // Y display
+
+ m_glScopeRect2 = QRectF(
+ (float) m_leftMargin / (float)width(),
+ (float) (m_botMargin + m_topMargin + scopeHeight) / (float)height(),
+ (float) scopeWidth / (float)width(),
+ (float) scopeHeight / (float)height()
+ );
+
+ m_glScopeMatrix2.setToIdentity();
+ m_glScopeMatrix2.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(m_botMargin + m_topMargin + scopeHeight) / (float) height())
+ );
+ m_glScopeMatrix2.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot2ScaleMatrix.setToIdentity();
+ m_glBot2ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + scopeHeight + m_botMargin + 1) / (float) height())
+ );
+ m_glBot2ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft2ScaleMatrix.setToIdentity();
+ m_glLeft2ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*(m_topMargin + scopeHeight + m_botMargin) / (float) height())
+ );
+ m_glLeft2ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ { // X horizontal scale (X1)
+ m_x1Scale.setSize(scopeWidth);
+
+ m_bot1ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
+ } // X horizontal scale (X1)
+
+ { // Y horizontal scale (X2)
+ m_x2Scale.setSize(scopeWidth);
+ m_bot2ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
+ } // Y horizontal scale (X2)
+
+ { // X vertical scale (Y1)
+ m_y1Scale.setSize(scopeHeight);
+
+ m_left1ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
+ } // X vertical scale (Y1)
+
+ { // Y vertical scale (Y2)
+ m_y2Scale.setSize(scopeHeight);
+
+ m_left2ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
+ } // Y vertical scale (Y2)
+}
+
+void GLScopeMulti::setHorizontalDisplays()
+{
+ QFontMetrics fm(font());
+ int M = fm.width("-");
+ int scopeHeight = height() - m_topMargin - m_botMargin;
+ int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin;
+
+ // X display
+
+ m_glScopeRect1 = QRectF(
+ (float) m_leftMargin / (float) width(),
+ (float) m_topMargin / (float) height(),
+ (float) scopeWidth / (float) width(),
+ (float) scopeHeight / (float) height()
+ );
+ m_glScopeMatrix1.setToIdentity();
+ m_glScopeMatrix1.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix1.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot1ScaleMatrix.setToIdentity();
+ m_glBot1ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot1ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft1ScaleMatrix.setToIdentity();
+ m_glLeft1ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft1ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ // Y display
+
+ m_glScopeRect2 = QRectF(
+ (float)(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float)width(),
+ (float)m_topMargin / (float)height(),
+ (float)((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float)width(),
+ (float)(height() - m_topMargin - m_botMargin) / (float)height()
+ );
+ m_glScopeMatrix2.setToIdentity();
+ m_glScopeMatrix2.translate (
+ -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + ((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2)) / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix2.scale (
+ (float) 2*((width() - m_leftMargin - m_leftMargin - m_rightMargin) / 2) / (float) width(),
+ (float) -2*(height() - m_topMargin - m_botMargin) / (float) height()
+ );
+
+ m_glBot2ScaleMatrix.setToIdentity();
+ m_glBot2ScaleMatrix.translate (
+ -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot2ScaleMatrix.scale (
+ (float) 2*scopeWidth / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft2ScaleMatrix.setToIdentity();
+ m_glLeft2ScaleMatrix.translate (
+ -1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft2ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ { // X horizontal scale (X1)
+ m_x1Scale.setSize(scopeWidth);
+
+ m_bot1ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
+
+ } // X horizontal scale (X1)
+
+ { // Y horizontal scale (X2)
+ m_x2Scale.setSize(scopeWidth);
+ m_bot2ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
+ } // Y horizontal scale (X2)
+
+ { // X vertical scale (Y1)
+ m_y1Scale.setSize(scopeHeight);
+
+ m_left1ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
+ } // X vertical scale (Y1)
+
+ { // Y vertical scale (Y2)
+ m_y2Scale.setSize(scopeHeight);
+
+ m_left2ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
+ } // Y vertical scale (Y2)
+}
+
+void GLScopeMulti::setPolarDisplays()
+{
+ QFontMetrics fm(font());
+ int M = fm.width("-");
+ int scopeHeight = height() - m_topMargin - m_botMargin;
+ int scopeWidth = (width() - m_rightMargin)/2 - m_leftMargin;
+ int scopeDim = std::min(scopeWidth, scopeHeight);
+ scopeWidth += scopeWidth - scopeDim;
+
+ // Mixed XY display (left)
+
+ m_glScopeRect1 = QRectF(
+ (float) m_leftMargin / (float) width(),
+ (float) m_topMargin / (float) height(),
+ (float) (scopeWidth-m_leftMargin) / (float) width(),
+ (float) scopeHeight / (float) height()
+ );
+ m_glScopeMatrix1.setToIdentity();
+ m_glScopeMatrix1.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix1.scale (
+ (float) 2*(scopeWidth-m_leftMargin) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glBot1ScaleMatrix.setToIdentity();
+ m_glBot1ScaleMatrix.translate (
+ -1.0f + ((float) 2*m_leftMargin / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot1ScaleMatrix.scale (
+ (float) 2*(scopeWidth-m_leftMargin) / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft1ScaleMatrix.setToIdentity();
+ m_glLeft1ScaleMatrix.translate (
+ -1.0f,
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft1ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ m_glRight1ScaleMatrix.setToIdentity();
+ m_glRight1ScaleMatrix.translate (
+ -1.0f + ((float) 2*scopeWidth / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glRight1ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ // Polar XY display (right)
+
+ m_glScopeRect2 = QRectF(
+ (float)(m_leftMargin + scopeWidth + m_leftMargin) / (float)width(),
+ (float)m_topMargin / (float)height(),
+ (float) scopeDim / (float)width(),
+ (float)(height() - m_topMargin - m_botMargin) / (float)height()
+ );
+ m_glScopeMatrix2.setToIdentity();
+ m_glScopeMatrix2.translate (
+ -1.0f + ((float) 2*(m_leftMargin + scopeWidth + m_leftMargin) / (float) width()),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glScopeMatrix2.scale (
+ (float) 2*scopeDim / (float) width(),
+ (float) -2*(height() - m_topMargin - m_botMargin) / (float) height()
+ );
+
+ m_glBot2ScaleMatrix.setToIdentity();
+ m_glBot2ScaleMatrix.translate (
+ -1.0f + ((float) 2*(m_leftMargin + m_leftMargin + scopeWidth) / (float) width()),
+ 1.0f - ((float) 2*(scopeHeight + m_topMargin + 1) / (float) height())
+ );
+ m_glBot2ScaleMatrix.scale (
+ (float) 2*scopeDim / (float) width(),
+ (float) -2*(m_botMargin - 1) / (float) height()
+ );
+
+ m_glLeft2ScaleMatrix.setToIdentity();
+ m_glLeft2ScaleMatrix.translate (
+ -1.0f + (float) 2*(m_leftMargin + scopeWidth) / (float) width(),
+ 1.0f - ((float) 2*m_topMargin / (float) height())
+ );
+ m_glLeft2ScaleMatrix.scale (
+ (float) 2*(m_leftMargin-1) / (float) width(),
+ (float) -2*scopeHeight / (float) height()
+ );
+
+ { // Mixed XY horizontal scale (X1)
+ m_x1Scale.setSize(scopeWidth);
+
+ m_bot1ScalePixmap = QPixmap(
+ scopeWidth,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom1Scale.initTexture(m_bot1ScalePixmap.toImage());
+ } // Mixed XY horizontal scale (X1)
+
+ { // Polar XY horizontal scale (X2)
+ m_x2Scale.setSize(scopeDim);
+ m_bot2ScalePixmap = QPixmap(
+ scopeDim,
+ m_botMargin - 1
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_bot2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_bot2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_x2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(tick->textPos, fm.height() - 1), tick->text);
+ }
+ }
+ }
+
+ m_glShaderBottom2Scale.initTexture(m_bot2ScalePixmap.toImage());
+ } // Polar XY horizontal scale (X2)
+
+ { // Mixed XY vertical scale (Y1)
+ m_y1Scale.setSize(scopeHeight);
+
+ m_left1ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left1ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left1ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y1Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft1Scale.initTexture(m_left1ScalePixmap.toImage());
+
+ } // Mixed XY vertical scale (Y1)
+
+ { // Polar XY vertical scale (Y2)
+ m_y2Scale.setSize(scopeHeight);
+
+ m_left2ScalePixmap = QPixmap(
+ m_leftMargin - 1,
+ scopeHeight
+ );
+
+ const ScaleEngine::TickList* tickList;
+ const ScaleEngine::Tick* tick;
+
+ m_left2ScalePixmap.fill(Qt::black);
+ QPainter painter(&m_left2ScalePixmap);
+ painter.setPen(QColor(0xf0, 0xf0, 0xff));
+ painter.setFont(font());
+ tickList = &m_y2Scale.getTickList();
+
+ for(int i = 0; i < tickList->count(); i++) {
+ tick = &(*tickList)[i];
+ if(tick->major) {
+ if(tick->textSize > 0) {
+ painter.drawText(QPointF(m_leftMargin - M - tick->textSize, m_topMargin + scopeHeight - tick->textPos - fm.ascent()/2), tick->text);
+ }
+ }
+ }
+
+ m_glShaderLeft2Scale.initTexture(m_left2ScalePixmap.toImage());
+ } // Polar XY vertical scale (Y2)
+}
+
+void GLScopeMulti::setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex)
+{
+ ScopeVisMulti::TraceData& traceData = (*m_tracesData)[highlightedTraceIndex];
+ float amp_range = 2.0 / traceData.m_amp;
+ float amp_ofs = traceData.m_ofs;
+ float pow_floor = -100.0 + traceData.m_ofs * 100.0;
+ float pow_range = 100.0 / traceData.m_amp;
+
+ switch (traceData.m_projectionType)
+ {
+ case ScopeVisMulti::ProjectionMagDB: // dB scale
+ scale.setRange(Unit::Decibel, pow_floor, pow_floor + pow_range);
+ break;
+ case ScopeVisMulti::ProjectionMagLin:
+ if (amp_range < 2.0) {
+ scale.setRange(Unit::None, amp_ofs * 1000.0, amp_range * 1000.0 + amp_ofs * 1000.0);
+ } else {
+ scale.setRange(Unit::None, amp_ofs, amp_range + amp_ofs);
+ }
+ break;
+ case ScopeVisMulti::ProjectionPhase: // Phase or frequency
+ case ScopeVisMulti::ProjectionDPhase:
+ scale.setRange(Unit::None, -1.0/traceData.m_amp + amp_ofs, 1.0/traceData.m_amp + amp_ofs);
+ break;
+ case ScopeVisMulti::ProjectionReal: // Linear generic
+ case ScopeVisMulti::ProjectionImag:
+ default:
+ if (amp_range < 2.0) {
+ scale.setRange(Unit::None, - amp_range * 500.0 + amp_ofs * 1000.0, amp_range * 500.0 + amp_ofs * 1000.0);
+ } else {
+ scale.setRange(Unit::None, - amp_range * 0.5 + amp_ofs, amp_range * 0.5 + amp_ofs);
+ }
+ break;
+ }
+}
+
+void GLScopeMulti::drawChannelOverlay(
+ const QString& text,
+ const QColor& color,
+ QPixmap& channelOverlayPixmap,
+ const QRectF& glScopeRect)
+{
+ if (text.isEmpty()) {
+ return;
+ }
+
+ QFontMetricsF metrics(m_channelOverlayFont);
+ QRectF rect = metrics.boundingRect(text);
+ channelOverlayPixmap = QPixmap(rect.width() + 4.0f, rect.height());
+ channelOverlayPixmap.fill(Qt::transparent);
+ QPainter painter(&channelOverlayPixmap);
+ painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing, false);
+ painter.fillRect(rect, QColor(0, 0, 0, 0x80));
+ QColor textColor(color);
+ textColor.setAlpha(0xC0);
+ painter.setPen(textColor);
+ painter.setFont(m_channelOverlayFont);
+ painter.drawText(QPointF(0, rect.height() - 2.0f), text);
+ painter.end();
+
+ m_glShaderPowerOverlay.initTexture(channelOverlayPixmap.toImage());
+
+ {
+ GLfloat vtx1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+ GLfloat tex1[] = {
+ 0, 1,
+ 1, 1,
+ 1, 0,
+ 0, 0
+ };
+
+ float shiftX = glScopeRect.width() - ((rect.width() + 4.0f) / width());
+ float rectX = glScopeRect.x() + shiftX;
+ float rectY = glScopeRect.y();
+ float rectW = rect.width() / (float) width();
+ float rectH = rect.height() / (float) height();
+
+ QMatrix4x4 mat;
+ mat.setToIdentity();
+ mat.translate(-1.0f + 2.0f * rectX, 1.0f - 2.0f * rectY);
+ mat.scale(2.0f * rectW, -2.0f * rectH);
+ m_glShaderPowerOverlay.drawSurface(mat, tex1, vtx1, 4);
+ }
+}
+
+void GLScopeMulti::tick()
+{
+ if(m_dataChanged) {
+ update();
+ }
+}
+
+void GLScopeMulti::connectTimer(const QTimer& timer)
+{
+ qDebug() << "GLScopeMulti::connectTimer";
+ disconnect(&m_timer, SIGNAL(timeout()), this, SLOT(tick()));
+ connect(&timer, SIGNAL(timeout()), this, SLOT(tick()));
+ m_timer.stop();
+}
+
+void GLScopeMulti::cleanup()
+{
+ //makeCurrent();
+ m_glShaderSimple.cleanup();
+ m_glShaderBottom1Scale.cleanup();
+ m_glShaderBottom2Scale.cleanup();
+ m_glShaderLeft1Scale.cleanup();
+ m_glShaderPowerOverlay.cleanup();
+ //doneCurrent();
+}
+
diff --git a/sdrbase/gui/glscopemulti.h b/sdrbase/gui/glscopemulti.h
new file mode 100644
index 000000000..577008586
--- /dev/null
+++ b/sdrbase/gui/glscopemulti.h
@@ -0,0 +1,162 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SDRBASE_GUI_GLSCOPEMULTI_H_
+#define SDRBASE_GUI_GLSCOPEMULTI_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "dsp/dsptypes.h"
+#include "dsp/scopevismulti.h"
+#include "gui/scaleengine.h"
+#include "gui/glshadersimple.h"
+#include "gui/glshadertextured.h"
+#include "util/export.h"
+#include "util/bitfieldindex.h"
+
+class QPainter;
+
+class SDRANGEL_API GLScopeMulti: public QGLWidget {
+ Q_OBJECT
+
+public:
+ enum DisplayMode {
+ DisplayXYH,
+ DisplayXYV,
+ DisplayX,
+ DisplayY,
+ DisplayPol
+ };
+
+ GLScopeMulti(QWidget* parent = 0);
+ virtual ~GLScopeMulti();
+
+ void connectTimer(const QTimer& timer);
+
+ void setTraces(std::vector* tracesData, std::vector* traces);
+ void newTraces(std::vector* traces);
+
+ int getSampleRate() const { return m_sampleRate; }
+ int getTraceSize() const { return m_traceSize; }
+
+ void setTriggerPre(uint32_t triggerPre); //!< number of samples
+ void setTimeOfsProMill(int timeOfsProMill);
+ void setSampleRate(int sampleRate);
+ void setTimeBase(int timeBase);
+ void setFocusedTraceIndex(uint32_t traceIndex);
+ void setDisplayMode(DisplayMode displayMode);
+ void setTraceSize(int trceSize);
+ void updateDisplay();
+ void setDisplayGridIntensity(int intensity);
+ void setDisplayTraceIntensity(int intensity);
+ void setFocusedTriggerData(ScopeVisMulti::TriggerData& triggerData) { m_focusedTriggerData = triggerData; }
+ void setConfigChanged() { m_configChanged = true; }
+ //void incrementTraceCounter() { m_traceCounter++; }
+
+ bool getDataChanged() const { return m_dataChanged; }
+ DisplayMode getDisplayMode() const { return m_displayMode; }
+
+signals:
+ void sampleRateChanged(int);
+
+private:
+ std::vector *m_tracesData;
+ std::vector *m_traces;
+ ScopeVisMulti::TriggerData m_focusedTriggerData;
+ //int m_traceCounter;
+ uint32_t m_bufferIndex;
+ DisplayMode m_displayMode;
+ QTimer m_timer;
+ QMutex m_mutex;
+ bool m_dataChanged;
+ bool m_configChanged;
+ int m_sampleRate;
+ int m_timeOfsProMill;
+ uint32_t m_triggerPre;
+ int m_traceSize;
+ int m_timeBase;
+ int m_timeOffset;
+ uint32_t m_focusedTraceIndex;
+
+ // graphics stuff
+ QRectF m_glScopeRect1;
+ QRectF m_glScopeRect2;
+ QMatrix4x4 m_glScopeMatrix1;
+ QMatrix4x4 m_glScopeMatrix2;
+ QMatrix4x4 m_glLeft1ScaleMatrix;
+ QMatrix4x4 m_glRight1ScaleMatrix;
+ QMatrix4x4 m_glLeft2ScaleMatrix;
+ QMatrix4x4 m_glBot1ScaleMatrix;
+ QMatrix4x4 m_glBot2ScaleMatrix;
+
+ QPixmap m_left1ScalePixmap;
+ QPixmap m_left2ScalePixmap;
+ QPixmap m_bot1ScalePixmap;
+ QPixmap m_bot2ScalePixmap;
+ QPixmap m_channelOverlayPixmap1;
+ QPixmap m_channelOverlayPixmap2;
+
+ int m_displayGridIntensity;
+ int m_displayTraceIntensity;
+
+ ScaleEngine m_x1Scale; //!< Display #1 X scale. Time scale
+ ScaleEngine m_x2Scale; //!< Display #2 X scale. Time scale
+ ScaleEngine m_y1Scale; //!< Display #1 Y scale. Always connected to trace #0 (X trace)
+ ScaleEngine m_y2Scale; //!< Display #2 Y scale. Connected to highlighted Y trace (#1..n)
+
+ QFont m_channelOverlayFont;
+
+ GLShaderSimple m_glShaderSimple;
+ GLShaderTextured m_glShaderLeft1Scale;
+ GLShaderTextured m_glShaderBottom1Scale;
+ GLShaderTextured m_glShaderLeft2Scale;
+ GLShaderTextured m_glShaderBottom2Scale;
+ GLShaderTextured m_glShaderPowerOverlay;
+
+ static const int m_topMargin = 5;
+ static const int m_botMargin = 20;
+ static const int m_leftMargin = 35;
+ static const int m_rightMargin = 5;
+
+ void initializeGL();
+ void resizeGL(int width, int height);
+ void paintGL();
+
+ void applyConfig();
+ void setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex);
+ void setUniqueDisplays(); //!< Arrange displays when X and Y are unique on screen
+ void setVerticalDisplays(); //!< Arrange displays when X and Y are stacked vertically
+ void setHorizontalDisplays(); //!< Arrange displays when X and Y are stacked horizontally
+ void setPolarDisplays(); //!< Arrange displays when X and Y are stacked over on the left and polar display is on the right
+
+ void drawChannelOverlay( //!< Draws a text overlay
+ const QString& text,
+ const QColor& color,
+ QPixmap& channelOverlayPixmap,
+ const QRectF& glScopeRect);
+
+protected slots:
+ void cleanup();
+ void tick();
+
+};
+
+#endif /* SDRBASE_GUI_GLSCOPENG_H_ */
diff --git a/sdrbase/gui/glscopemultigui.cpp b/sdrbase/gui/glscopemultigui.cpp
new file mode 100644
index 000000000..02d49e1c1
--- /dev/null
+++ b/sdrbase/gui/glscopemultigui.cpp
@@ -0,0 +1,1543 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "glscopemultigui.h"
+#include "glscopemulti.h"
+#include "ui_glscopemultigui.h"
+#include "util/simpleserializer.h"
+
+const double GLScopeMultiGUI::amps[11] = { 0.2, 0.1, 0.05, 0.02, 0.01, 0.005, 0.002, 0.001, 0.0005, 0.0002, 0.0001 };
+
+GLScopeMultiGUI::GLScopeMultiGUI(QWidget* parent) :
+ QWidget(parent),
+ ui(new Ui::GLScopeMultiGUI),
+ m_messageQueue(0),
+ m_glScope(0),
+ m_scopeVis(0),
+ m_sampleRate(0),
+ m_traceLenMult(1),
+ m_timeBase(1),
+ m_timeOffset(0)
+{
+ qDebug("GLScopeMultiGUI::GLScopeMultiGUI");
+ setEnabled(false);
+ ui->setupUi(this);
+ ui->trigDelayFine->setMaximum(ScopeVisMulti::m_traceChunkSize / 10.0);
+ ui->traceColor->setStyleSheet("QLabel { background-color : rgb(255,255,64); }");
+ m_focusedTraceColor.setRgb(255,255,64);
+ ui->trigColor->setStyleSheet("QLabel { background-color : rgb(0,255,0); }");
+ m_focusedTriggerColor.setRgb(0,255,0);
+ ui->traceText->setText("X");
+ ui->mem->setMaximum(ScopeVisMulti::m_nbTraceMemories - 1);
+}
+
+GLScopeMultiGUI::~GLScopeMultiGUI()
+{
+ delete ui;
+}
+
+void GLScopeMultiGUI::setBuddies(MessageQueue* messageQueue, ScopeVisMulti* scopeVis, GLScopeMulti* glScope)
+{
+ qDebug("GLScopeMultiGUI::setBuddies");
+
+ m_messageQueue = messageQueue;
+ m_scopeVis = scopeVis;
+ m_glScope = glScope;
+
+ // initialize display combo
+ ui->onlyX->setChecked(true);
+ ui->onlyY->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ ui->polar->setChecked(false);
+ ui->onlyY->setEnabled(false);
+ ui->horizontalXY->setEnabled(false);
+ ui->verticalXY->setEnabled(false);
+ ui->polar->setEnabled(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayX);
+
+ // initialize trigger combo
+ ui->trigPos->setChecked(true);
+ ui->trigNeg->setChecked(false);
+ ui->trigBoth->setChecked(false);
+ ui->trigOneShot->setChecked(false);
+ ui->trigOneShot->setEnabled(false);
+ ui->freerun->setChecked(true);
+
+ // Add a trigger
+ ScopeVisMulti::TriggerData triggerData;
+ fillTriggerData(triggerData);
+ m_scopeVis->addTrigger(triggerData);
+
+ // Add a trace
+ ScopeVisMulti::TraceData traceData;
+ fillTraceData(traceData);
+ m_scopeVis->addTrace(traceData);
+
+ setEnabled(true);
+ connect(m_glScope, SIGNAL(sampleRateChanged(int)), this, SLOT(on_scope_sampleRateChanged(int)));
+
+ ui->traceMode->clear();
+ fillProjectionCombo(ui->traceMode);
+
+ ui->trigMode->clear();
+ fillProjectionCombo(ui->trigMode);
+
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ 2*m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+
+ setTraceLenDisplay();
+ setTimeScaleDisplay();
+ setTimeOfsDisplay();
+ setAmpScaleDisplay();
+ setAmpOfsDisplay();
+ setTraceDelayDisplay();
+}
+
+void GLScopeMultiGUI::setSampleRate(int sampleRate)
+{
+ m_sampleRate = sampleRate;
+}
+
+void GLScopeMultiGUI::on_scope_sampleRateChanged(int sampleRate)
+{
+ //m_sampleRate = m_glScope->getSampleRate();
+ m_sampleRate = sampleRate;
+ ui->sampleRateText->setText(tr("%1\nkS/s").arg(m_sampleRate / 1000.0f, 0, 'f', 2));
+ setTraceLenDisplay();
+ setTimeScaleDisplay();
+ setTimeOfsDisplay();
+ setTraceDelayDisplay();
+ setTrigPreDisplay();
+ setTrigDelayDisplay();
+}
+
+void GLScopeMultiGUI::resetToDefaults()
+{
+}
+
+
+QByteArray GLScopeMultiGUI::serialize() const
+{
+ SimpleSerializer s(1);
+
+ // first row
+ s.writeS32(1, (int) m_glScope->getDisplayMode());
+ s.writeS32(2, ui->traceIntensity->value());
+ s.writeS32(3, ui->gridIntensity->value());
+ s.writeS32(4, ui->time->value());
+ s.writeS32(5, ui->timeOfs->value());
+ s.writeS32(6, ui->traceLen->value());
+
+ // second row - by trace
+ const std::vector& tracesData = m_scopeVis->getTracesData();
+ std::vector::const_iterator traceDataIt = tracesData.begin();
+ s.writeU32(10, (uint32_t) tracesData.size());
+ int i = 0;
+
+ for (; traceDataIt != tracesData.end(); ++traceDataIt, i++)
+ {
+ s.writeS32(20 + 16*i, (int) traceDataIt->m_projectionType);
+ s.writeU32(21 + 16*i, traceDataIt->m_ampIndex);
+ s.writeS32(22 + 16*i, traceDataIt->m_ofsCoarse);
+ s.writeS32(23 + 16*i, traceDataIt->m_ofsFine);
+ s.writeS32(24 + 16*i, traceDataIt->m_traceDelayCoarse);
+ s.writeS32(25 + 16*i, traceDataIt->m_traceDelayFine);
+ s.writeFloat(26 + 16*i, traceDataIt->m_traceColorR);
+ s.writeFloat(27 + 16*i, traceDataIt->m_traceColorG);
+ s.writeFloat(28 + 16*i, traceDataIt->m_traceColorB);
+ }
+
+ // third row - by trigger
+ s.writeU32(200, (uint32_t) m_scopeVis->getNbTriggers());
+ s.writeS32(201, ui->trigPre->value());
+
+ for (int i = 0; i < m_scopeVis->getNbTriggers(); i++)
+ {
+ const ScopeVisMulti::TriggerData& triggerData = m_scopeVis->getTriggerData(i);
+ s.writeS32(210 + 16*i, (int) triggerData.m_projectionType);
+ s.writeS32(211 + 16*i, triggerData.m_triggerRepeat);
+ s.writeBool(212 + 16*i, triggerData.m_triggerPositiveEdge);
+ s.writeBool(213 + 16*i, triggerData.m_triggerBothEdges);
+ s.writeS32(214 + 16*i, triggerData.m_triggerLevelCoarse);
+ s.writeS32(215 + 16*i, triggerData.m_triggerLevelFine);
+ s.writeS32(216 + 16*i, triggerData.m_triggerDelayCoarse);
+ s.writeS32(217 + 16*i, triggerData.m_triggerDelayFine);
+ s.writeFloat(218 + 16*i, triggerData.m_triggerColorR);
+ s.writeFloat(219 + 16*i, triggerData.m_triggerColorG);
+ s.writeFloat(220 + 16*i, triggerData.m_triggerColorB);
+ }
+
+ return s.final();
+}
+
+bool GLScopeMultiGUI::deserialize(const QByteArray& data)
+{
+ qDebug("GLScopeMultiGUI::deserialize");
+ SimpleDeserializer d(data);
+
+ if(!d.isValid()) {
+ resetToDefaults();
+ return false;
+ }
+
+ if(d.getVersion() == 1)
+ {
+ TraceUIBlocker traceUIBlocker(ui);
+ TrigUIBlocker trigUIBlocker(ui);
+ int intValue;
+ uint32_t uintValue;
+ bool boolValue;
+
+ ui->onlyX->setEnabled(false);
+ ui->onlyY->setEnabled(false);
+ ui->horizontalXY->setEnabled(false);
+ ui->verticalXY->setEnabled(false);
+ ui->polar->setEnabled(false);
+
+ ui->traceMode->setCurrentIndex(intValue);
+ d.readS32(1, &intValue, (int) GLScopeMulti::DisplayX);
+ m_glScope->setDisplayMode((GLScopeMulti::DisplayMode) intValue);
+
+ ui->onlyX->setChecked(false);
+ ui->onlyY->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ ui->polar->setChecked(false);
+
+ switch (m_glScope->getDisplayMode())
+ {
+ case GLScopeMulti::DisplayY:
+ ui->onlyY->setChecked(true);
+ break;
+ case GLScopeMulti::DisplayXYH:
+ ui->horizontalXY->setChecked(true);
+ break;
+ case GLScopeMulti::DisplayXYV:
+ ui->verticalXY->setChecked(true);
+ break;
+ case GLScopeMulti::DisplayPol:
+ ui->polar->setChecked(true);
+ break;
+ case GLScopeMulti::DisplayX:
+ default:
+ ui->onlyX->setChecked(true);
+ break;
+ }
+
+ d.readS32(2, &intValue, 50);
+ ui->traceIntensity->setValue(intValue);
+ d.readS32(3, &intValue, 10);
+ ui->gridIntensity->setValue(intValue);
+ d.readS32(4, &intValue, 1);
+ ui->time->setValue(intValue);
+ d.readS32(5, &intValue, 0);
+ ui->timeOfs->setValue(intValue);
+ d.readS32(6, &intValue, 1);
+ ui->traceLen->setValue(intValue);
+
+ // trace stuff
+
+ uint32_t nbTracesSaved;
+ d.readU32(10, &nbTracesSaved, 1);
+ const std::vector& tracesData = m_scopeVis->getTracesData();
+ int iTrace = tracesData.size();
+
+ qDebug("GLScopeMultiGUI::deserialize: nbTracesSaved: %u tracesData.size(): %lu", nbTracesSaved, tracesData.size());
+
+ while (iTrace > nbTracesSaved) // remove possible traces in excess
+ {
+ m_scopeVis->removeTrace(iTrace - 1);
+ iTrace--;
+ }
+
+ for (iTrace = 0; iTrace < nbTracesSaved; iTrace++)
+ {
+ ScopeVisMulti::TraceData traceData;
+ float r, g, b;
+
+ d.readS32(20 + 16*iTrace, &intValue, 0);
+ ui->traceMode->setCurrentIndex(intValue);
+ d.readU32(21 + 16*iTrace, &uintValue, 0);
+ ui->amp->setValue(uintValue);
+ d.readS32(22 + 16*iTrace, &intValue, 0);
+ ui->ofsCoarse->setValue(intValue);
+ d.readS32(23 + 16*iTrace, &intValue, 0);
+ ui->ofsFine->setValue(intValue);
+ d.readS32(24 + 16*iTrace, &intValue, 0);
+ ui->traceDelayCoarse->setValue(intValue);
+ d.readS32(25 + 16*iTrace, &intValue, 0);
+ ui->traceDelayFine->setValue(intValue);
+ d.readFloat(26 + 16*iTrace, &r, 1.0f);
+ d.readFloat(27 + 16*iTrace, &g, 1.0f);
+ d.readFloat(28 + 16*iTrace, &b, 1.0f);
+ m_focusedTraceColor.setRgbF(r, g, b);
+
+ fillTraceData(traceData);
+
+ if (iTrace < tracesData.size()) // change existing traces
+ {
+ m_scopeVis->changeTrace(traceData, iTrace);
+ }
+ else // add new traces
+ {
+ m_scopeVis->addTrace(traceData);
+ }
+ }
+
+
+ ui->trace->setMaximum(nbTracesSaved-1);
+ ui->trace->setValue(nbTracesSaved-1);
+ m_glScope->setFocusedTraceIndex(nbTracesSaved-1);
+
+ int r,g,b,a;
+ m_focusedTraceColor.getRgb(&r, &g, &b, &a);
+ ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+
+ setTraceIndexDisplay();
+ setAmpScaleDisplay();
+ setAmpOfsDisplay();
+ setTraceDelayDisplay();
+
+ ui->onlyX->setEnabled(true);
+ ui->onlyY->setEnabled(nbTracesSaved > 1);
+ ui->horizontalXY->setEnabled(nbTracesSaved > 1);
+ ui->verticalXY->setEnabled(nbTracesSaved > 1);
+ ui->polar->setEnabled(nbTracesSaved > 1);
+
+ // trigger stuff
+
+ uint32_t nbTriggersSaved;
+ d.readU32(200, &nbTriggersSaved, 1);
+ uint32_t nbTriggers = m_scopeVis->getNbTriggers();
+ int iTrigger = nbTriggers;
+
+ d.readS32(201, &intValue, 0);
+ ui->trigPre->setValue(intValue);
+
+ qDebug("GLScopeMultiGUI::deserialize: nbTriggersSaved: %u nbTriggers: %u", nbTriggersSaved, nbTriggers);
+
+ while (iTrigger > nbTriggersSaved) // remove possible triggers in excess
+ {
+ m_scopeVis->removeTrigger(iTrigger - 1);
+ iTrigger--;
+ }
+
+ for (iTrigger = 0; iTrigger < nbTriggersSaved; iTrigger++)
+ {
+ ScopeVisMulti::TriggerData triggerData = m_scopeVis->getTriggerData(iTrigger);
+ float r, g, b;
+
+ d.readS32(210 + 16*iTrigger, &intValue, 0);
+ ui->trigMode->setCurrentIndex(intValue);
+ d.readS32(211 + 16*iTrigger, &intValue, 1);
+ ui->trigCount->setValue(intValue);
+ d.readBool(212 + 16*iTrigger, &boolValue, true);
+ ui->trigPos->setChecked(boolValue);
+ d.readBool(213 + 16*iTrigger, &boolValue, false);
+ ui->trigBoth->setChecked(boolValue);
+ d.readS32(214 + 16*iTrigger, &intValue, 1);
+ ui->trigLevelCoarse->setValue(intValue);
+ d.readS32(215 + 16*iTrigger, &intValue, 1);
+ ui->trigLevelFine->setValue(intValue);
+ d.readS32(216 + 16*iTrigger, &intValue, 1);
+ ui->trigDelayCoarse->setValue(intValue);
+ d.readS32(217 + 16*iTrigger, &intValue, 1);
+ ui->trigDelayFine->setValue(intValue);
+ d.readFloat(218 + 16*iTrigger, &r, 1.0f);
+ d.readFloat(219 + 16*iTrigger, &g, 1.0f);
+ d.readFloat(220 + 16*iTrigger, &b, 1.0f);
+ m_focusedTriggerColor.setRgbF(r, g, b);
+
+ fillTriggerData(triggerData);
+
+ if (iTrigger < nbTriggers) // change existing triggers
+ {
+ m_scopeVis->changeTrigger(triggerData, iTrigger);
+ }
+ else // add new trigers
+ {
+ m_scopeVis->addTrigger(triggerData);
+ }
+
+ if (iTrigger == nbTriggersSaved-1)
+ {
+ m_glScope->setFocusedTriggerData(triggerData);
+ }
+ }
+
+ ui->trig->setMaximum(nbTriggersSaved-1);
+ ui->trig->setValue(nbTriggersSaved-1);
+
+ m_focusedTriggerColor.getRgb(&r, &g, &b, &a);
+ ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+
+ setTrigCountDisplay();
+ setTrigDelayDisplay();
+ setTrigIndexDisplay();
+ setTrigLevelDisplay();
+ setTrigPreDisplay();
+
+ return true;
+ }
+ else
+ {
+ resetToDefaults();
+ return false;
+ }
+}
+
+void GLScopeMultiGUI::on_onlyX_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->onlyY->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ ui->polar->setChecked(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayX);
+ }
+}
+
+void GLScopeMultiGUI::on_onlyY_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->onlyX->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ ui->polar->setChecked(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayY);
+ }
+}
+
+void GLScopeMultiGUI::on_horizontalXY_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->onlyX->setChecked(false);
+ ui->onlyY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ ui->polar->setChecked(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayXYH);
+ }
+}
+
+void GLScopeMultiGUI::on_verticalXY_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->onlyX->setChecked(false);
+ ui->onlyY->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->polar->setChecked(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayXYV);
+ }
+}
+
+void GLScopeMultiGUI::on_polar_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->onlyX->setChecked(false);
+ ui->onlyY->setChecked(false);
+ ui->horizontalXY->setChecked(false);
+ ui->verticalXY->setChecked(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayPol);
+ }
+}
+
+void GLScopeMultiGUI::on_traceIntensity_valueChanged(int value)
+{
+ ui->traceIntensity->setToolTip(QString("Trace intensity: %1").arg(value));
+ m_glScope->setDisplayTraceIntensity(value);
+}
+
+void GLScopeMultiGUI::on_gridIntensity_valueChanged(int value)
+{
+ ui->gridIntensity->setToolTip(QString("Grid intensity: %1").arg(value));
+ m_glScope->setDisplayGridIntensity(value);
+}
+
+void GLScopeMultiGUI::on_time_valueChanged(int value)
+{
+ m_timeBase = value;
+ setTimeScaleDisplay();
+ setTraceDelayDisplay();
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+}
+
+void GLScopeMultiGUI::on_timeOfs_valueChanged(int value)
+{
+ if ((value < 0) || (value > 100)) {
+ return;
+ }
+
+ m_timeOffset = value;
+ setTimeOfsDisplay();
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+}
+
+void GLScopeMultiGUI::on_traceLen_valueChanged(int value)
+{
+ if ((value < 1) || (value > 100)) {
+ return;
+ }
+
+ m_traceLenMult = value;
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+ setTraceLenDisplay();
+ setTimeScaleDisplay();
+ setTimeOfsDisplay();
+ setTrigDelayDisplay();
+ setTrigPreDisplay();
+}
+
+void GLScopeMultiGUI::on_trace_valueChanged(int value)
+{
+ ui->traceText->setText(value == 0 ? "X" : QString("Y%1").arg(ui->trace->value()));
+
+ ScopeVisMulti::TraceData traceData;
+ m_scopeVis->getTraceData(traceData, value);
+
+ qDebug() << "GLScopeMultiGUI::on_trace_valueChanged:"
+ << " m_projectionType: " << (int) traceData.m_projectionType
+ << " m_amp" << traceData.m_amp
+ << " m_ofs" << traceData.m_ofs
+ << " m_traceDelay" << traceData.m_traceDelay;
+
+ setTraceUI(traceData);
+
+ m_scopeVis->focusOnTrace(value);
+}
+
+void GLScopeMultiGUI::on_traceAdd_clicked(bool checked)
+{
+ ScopeVisMulti::TraceData traceData;
+ fillTraceData(traceData);
+ addTrace(traceData);
+}
+
+void GLScopeMultiGUI::on_traceDel_clicked(bool checked)
+{
+ if (ui->trace->value() > 0) // not the X trace
+ {
+ ui->trace->setMaximum(ui->trace->maximum() - 1);
+
+ if (ui->trace->value() == 0)
+ {
+ ui->onlyX->setChecked(true);
+ ui->onlyY->setEnabled(false);
+ ui->horizontalXY->setEnabled(false);
+ ui->verticalXY->setEnabled(false);
+ ui->polar->setEnabled(false);
+ m_glScope->setDisplayMode(GLScopeMulti::DisplayX);
+ }
+
+ m_scopeVis->removeTrace(ui->trace->value());
+ changeCurrentTrace();
+ }
+}
+
+void GLScopeMultiGUI::on_traceUp_clicked(bool checked)
+{
+ if (ui->trace->maximum() > 0) // more than one trace
+ {
+ int newTraceIndex = (ui->trace->value() + 1) % (ui->trace->maximum()+1);
+ m_scopeVis->moveTrace(ui->trace->value(), true);
+ ui->trace->setValue(newTraceIndex); // follow trace
+ ScopeVisMulti::TraceData traceData;
+ m_scopeVis->getTraceData(traceData, ui->trace->value());
+ setTraceUI(traceData);
+ m_scopeVis->focusOnTrace(ui->trace->value());
+ }
+}
+
+void GLScopeMultiGUI::on_traceDown_clicked(bool checked)
+{
+ if (ui->trace->value() > 0) // not the X (lowest) trace
+ {
+ int newTraceIndex = (ui->trace->value() - 1) % (ui->trace->maximum()+1);
+ m_scopeVis->moveTrace(ui->trace->value(), false);
+ ui->trace->setValue(newTraceIndex); // follow trace
+ ScopeVisMulti::TraceData traceData;
+ m_scopeVis->getTraceData(traceData, ui->trace->value());
+ setTraceUI(traceData);
+ m_scopeVis->focusOnTrace(ui->trace->value());
+ }
+}
+
+void GLScopeMultiGUI::on_trig_valueChanged(int value)
+{
+ ui->trigText->setText(tr("%1").arg(value));
+
+ ScopeVisMulti::TriggerData triggerData;
+ m_scopeVis->getTriggerData(triggerData, value);
+
+ qDebug() << "GLScopeMultiGUI::on_trig_valueChanged:"
+ << " m_projectionType: " << (int) triggerData.m_projectionType
+ << " m_triggerRepeat" << triggerData.m_triggerRepeat
+ << " m_triggerPositiveEdge" << triggerData.m_triggerPositiveEdge
+ << " m_triggerBothEdges" << triggerData.m_triggerBothEdges
+ << " m_triggerLevel" << triggerData.m_triggerLevel;
+
+ setTriggerUI(triggerData);
+
+ m_scopeVis->focusOnTrigger(value);
+}
+
+void GLScopeMultiGUI::on_trigAdd_clicked(bool checked)
+{
+ ScopeVisMulti::TriggerData triggerData;
+ fillTriggerData(triggerData);
+ addTrigger(triggerData);
+}
+
+void GLScopeMultiGUI::on_trigDel_clicked(bool checked)
+{
+ if (ui->trig->value() > 0)
+ {
+ m_scopeVis->removeTrigger(ui->trig->value());
+ ui->trig->setMaximum(ui->trig->maximum() - 1);
+ }
+}
+
+void GLScopeMultiGUI::on_trigUp_clicked(bool checked)
+{
+ if (ui->trig->maximum() > 0) // more than one trigger
+ {
+ int newTriggerIndex = (ui->trig->value() + 1) % (ui->trig->maximum()+1);
+ m_scopeVis->moveTrigger(ui->trace->value(), true);
+ ui->trig->setValue(newTriggerIndex); // follow trigger
+ ScopeVisMulti::TriggerData triggerData;
+ m_scopeVis->getTriggerData(triggerData, ui->trig->value());
+ setTriggerUI(triggerData);
+ m_scopeVis->focusOnTrigger(ui->trig->value());
+ }
+}
+
+void GLScopeMultiGUI::on_trigDown_clicked(bool checked)
+{
+ if (ui->trig->value() > 0) // not the 0 (lowest) trigger
+ {
+ int newTriggerIndex = (ui->trig->value() - 1) % (ui->trig->maximum()+1);
+ m_scopeVis->moveTrigger(ui->trace->value(), false);
+ ui->trig->setValue(newTriggerIndex); // follow trigger
+ ScopeVisMulti::TriggerData triggerData;
+ m_scopeVis->getTriggerData(triggerData, ui->trig->value());
+ setTriggerUI(triggerData);
+ m_scopeVis->focusOnTrigger(ui->trig->value());
+ }
+}
+
+void GLScopeMultiGUI::on_traceMode_currentIndexChanged(int index)
+{
+ setAmpScaleDisplay();
+ setAmpOfsDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_amp_valueChanged(int value)
+{
+ setAmpScaleDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_ofsCoarse_valueChanged(int value)
+{
+ setAmpOfsDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_ofsFine_valueChanged(int value)
+{
+ setAmpOfsDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_traceDelayCoarse_valueChanged(int value)
+{
+ setTraceDelayDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_traceDelayFine_valueChanged(int value)
+{
+ setTraceDelayDisplay();
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_traceView_toggled(bool checked)
+{
+ changeCurrentTrace();
+}
+
+void GLScopeMultiGUI::on_traceColor_clicked()
+{
+ QColor newColor = QColorDialog::getColor(m_focusedTraceColor);
+
+ if (newColor.isValid()) // user clicked OK and selected a color
+ {
+ m_focusedTraceColor = newColor;
+ int r,g,b,a;
+ m_focusedTraceColor.getRgb(&r, &g, &b, &a);
+ ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+ changeCurrentTrace();
+ }
+}
+
+void GLScopeMultiGUI::on_mem_valueChanged(int value)
+{
+ QString text;
+ text.sprintf("%02d", value);
+ ui->memText->setText(text);
+ disableLiveMode(value > 0); // block trigger UI line if memory is active
+ m_scopeVis->setMemoryIndex(value);
+}
+
+void GLScopeMultiGUI::on_trigMode_currentIndexChanged(int index)
+{
+ setTrigLevelDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigCount_valueChanged(int value)
+{
+ setTrigCountDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigPos_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->trigNeg->setChecked(false);
+ ui->trigBoth->setChecked(false);
+ }
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigNeg_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->trigPos->setChecked(false);
+ ui->trigBoth->setChecked(false);
+ }
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigBoth_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->trigNeg->setChecked(false);
+ ui->trigPos->setChecked(false);
+ }
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigLevelCoarse_valueChanged(int value)
+{
+ setTrigLevelDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigLevelFine_valueChanged(int value)
+{
+ setTrigLevelDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigDelayCoarse_valueChanged(int value)
+{
+ setTrigDelayDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigDelayFine_valueChanged(int value)
+{
+ setTrigDelayDisplay();
+ changeCurrentTrigger();
+}
+
+void GLScopeMultiGUI::on_trigPre_valueChanged(int value)
+{
+ setTrigPreDisplay();
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+}
+
+void GLScopeMultiGUI::on_trigColor_clicked()
+{
+ QColor newColor = QColorDialog::getColor(m_focusedTriggerColor);
+
+ if (newColor.isValid()) // user clicked "OK"
+ {
+ m_focusedTriggerColor = newColor;
+ int r,g,b,a;
+ m_focusedTriggerColor.getRgb(&r, &g, &b, &a);
+ ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+ changeCurrentTrigger();
+ }
+}
+
+void GLScopeMultiGUI::on_trigOneShot_toggled(bool checked)
+{
+ m_scopeVis->setOneShot(checked);
+}
+
+void GLScopeMultiGUI::on_freerun_toggled(bool checked)
+{
+ if (checked)
+ {
+ ui->trigOneShot->setChecked(false);
+ ui->trigOneShot->setEnabled(false);
+ }
+ else
+ {
+ ui->trigOneShot->setEnabled(true);
+ }
+
+ m_scopeVis->configure(
+ ui->sourcePort->maximum() + 1,
+ m_traceLenMult*ScopeVisMulti::m_traceChunkSize,
+ m_timeBase,
+ m_timeOffset*10,
+ (uint32_t) (m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f)),
+ ui->freerun->isChecked());
+}
+
+void GLScopeMultiGUI::setTraceIndexDisplay()
+{
+ ui->traceText->setText(ui->trace->value() == 0 ? "X" : QString("Y%1").arg(ui->trace->value()));
+}
+
+void GLScopeMultiGUI::setTrigCountDisplay()
+{
+ QString text;
+ text.sprintf("%02d", ui->trigCount->value());
+ ui->trigCountText->setText(text);
+}
+
+void GLScopeMultiGUI::setTimeScaleDisplay()
+{
+ m_sampleRate = m_glScope->getSampleRate();
+ unsigned int n_samples = (m_glScope->getTraceSize() * 1.0) / (double) m_timeBase;
+ double t = (m_glScope->getTraceSize() * 1.0 / m_sampleRate) / (double) m_timeBase;
+
+ if (n_samples < 1000) {
+ ui->timeText->setToolTip(tr("%1 S").arg(n_samples));
+ } else if (n_samples < 1000000) {
+ ui->timeText->setToolTip(tr("%1 kS").arg(n_samples/1000.0));
+ } else {
+ ui->timeText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0));
+ }
+
+ if(t < 0.000001)
+ {
+ t = round(t * 100000000000.0) / 100.0;
+ ui->timeText->setText(tr("%1\nns").arg(t));
+ }
+ else if(t < 0.001)
+ {
+ t = round(t * 100000000.0) / 100.0;
+ ui->timeText->setText(tr("%1\nµs").arg(t));
+ }
+ else if(t < 1.0)
+ {
+ t = round(t * 100000.0) / 100.0;
+ ui->timeText->setText(tr("%1\nms").arg(t));
+ }
+ else
+ {
+ t = round(t * 100.0) / 100.0;
+ ui->timeText->setText(tr("%1\ns").arg(t));
+ }
+}
+
+void GLScopeMultiGUI::setTraceLenDisplay()
+{
+ unsigned int n_samples = m_traceLenMult * ScopeVisMulti::m_traceChunkSize;
+
+ if (n_samples < 1000) {
+ ui->traceLenText->setToolTip(tr("%1 S").arg(n_samples));
+ } else if (n_samples < 1000000) {
+ ui->traceLenText->setToolTip(tr("%1 kS").arg(n_samples/1000.0));
+ } else {
+ ui->traceLenText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0));
+ }
+
+ m_sampleRate = m_glScope->getSampleRate();
+ double t = (m_glScope->getTraceSize() * 1.0 / m_sampleRate);
+
+ if(t < 0.000001)
+ ui->traceLenText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2));
+ else if(t < 0.001)
+ ui->traceLenText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2));
+ else if(t < 1.0)
+ ui->traceLenText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2));
+ else
+ ui->traceLenText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2));
+}
+
+void GLScopeMultiGUI::setTimeOfsDisplay()
+{
+ unsigned int n_samples = m_glScope->getTraceSize() * (m_timeOffset/100.0);
+ double dt = m_glScope->getTraceSize() * (m_timeOffset/100.0) / m_sampleRate;
+
+ if (n_samples < 1000) {
+ ui->timeOfsText->setToolTip(tr("%1 S").arg(n_samples));
+ } else if (n_samples < 1000000) {
+ ui->timeOfsText->setToolTip(tr("%1 kS").arg(n_samples/1000.0));
+ } else {
+ ui->timeOfsText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0));
+ }
+
+ if(dt < 0.000001)
+ ui->timeOfsText->setText(tr("%1\nns").arg(dt * 1000000000.0, 0, 'f', 2));
+ else if(dt < 0.001)
+ ui->timeOfsText->setText(tr("%1\nµs").arg(dt * 1000000.0, 0, 'f', 2));
+ else if(dt < 1.0)
+ ui->timeOfsText->setText(tr("%1\nms").arg(dt * 1000.0, 0, 'f', 2));
+ else
+ ui->timeOfsText->setText(tr("%1\ns").arg(dt * 1.0, 0, 'f', 2));
+}
+
+void GLScopeMultiGUI::setAmpScaleDisplay()
+{
+ ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex();
+ double ampValue = amps[ui->amp->value()];
+
+ if (projectionType == ScopeVisMulti::ProjectionMagDB)
+ {
+ double displayValue = ampValue*500.0f;
+
+ if (displayValue < 10.0f) {
+ ui->ampText->setText(tr("%1\ndB").arg(displayValue, 0, 'f', 2));
+ }
+ else {
+ ui->ampText->setText(tr("%1\ndB").arg(displayValue, 0, 'f', 1));
+ }
+ }
+ else
+ {
+ double a = ampValue*10.0f;
+
+ if(a < 0.000001)
+ ui->ampText->setText(tr("%1\nn").arg(a * 1000000000.0));
+ else if(a < 0.001)
+ ui->ampText->setText(tr("%1\nµ").arg(a * 1000000.0));
+ else if(a < 1.0)
+ ui->ampText->setText(tr("%1\nm").arg(a * 1000.0));
+ else
+ ui->ampText->setText(tr("%1").arg(a * 1.0));
+ }
+}
+
+void GLScopeMultiGUI::setAmpOfsDisplay()
+{
+ ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex();
+ double o = (ui->ofsCoarse->value() * 10.0f) + (ui->ofsFine->value() / 20.0f);
+
+ if (projectionType == ScopeVisMulti::ProjectionMagDB)
+ {
+ ui->ofsText->setText(tr("%1\ndB").arg(o/10.0f - 100.0f, 0, 'f', 1));
+ }
+ else
+ {
+ double a;
+
+ if (projectionType == ScopeVisMulti::ProjectionMagLin)
+ {
+ a = o/2000.0f;
+ }
+ else
+ {
+ a = o/1000.0f;
+ }
+
+ if(fabs(a) < 0.000001f)
+ ui->ofsText->setText(tr("%1\nn").arg(a * 1000000000.0));
+ else if(fabs(a) < 0.001f)
+ ui->ofsText->setText(tr("%1\nµ").arg(a * 1000000.0));
+ else if(fabs(a) < 1.0f)
+ ui->ofsText->setText(tr("%1\nm").arg(a * 1000.0));
+ else
+ ui->ofsText->setText(tr("%1").arg(a * 1.0));
+ }
+}
+
+void GLScopeMultiGUI::setTraceDelayDisplay()
+{
+ if (m_sampleRate > 0)
+ {
+ int n_samples = ui->traceDelayCoarse->value()*100 + ui->traceDelayFine->value();
+ double t = ((double) n_samples) / m_sampleRate;
+
+ if (n_samples < 1000) {
+ ui->traceDelayText->setToolTip(tr("%1 S").arg(n_samples));
+ } else if (n_samples < 1000000) {
+ ui->traceDelayText->setToolTip(tr("%1 kS").arg(n_samples/1000.0));
+ } else {
+ ui->traceDelayText->setToolTip(tr("%1 MS").arg(n_samples/1000000.0));
+ }
+
+ if(t < 0.000001)
+ ui->traceDelayText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2));
+ else if(t < 0.001)
+ ui->traceDelayText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2));
+ else if(t < 1.0)
+ ui->traceDelayText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2));
+ else
+ ui->traceDelayText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2));
+ }
+}
+
+void GLScopeMultiGUI::setTrigIndexDisplay()
+{
+ ui->trigText->setText(tr("%1").arg(ui->trig->value()));
+}
+
+void GLScopeMultiGUI::setTrigLevelDisplay()
+{
+ double t = (ui->trigLevelCoarse->value() / 100.0f) + (ui->trigLevelFine->value() / 20000.0f);
+ ScopeVisMulti::ProjectionType projectionType = (ScopeVisMulti::ProjectionType) ui->trigMode->currentIndex();
+
+ ui->trigLevelCoarse->setToolTip(QString("Trigger level coarse: %1 %").arg(ui->trigLevelCoarse->value() / 100.0f));
+ ui->trigLevelFine->setToolTip(QString("Trigger level fine: %1 ppm").arg(ui->trigLevelFine->value() * 50));
+
+ if (projectionType == ScopeVisMulti::ProjectionMagDB) {
+ ui->trigLevelText->setText(tr("%1\ndB").arg(100.0 * (t - 1.0), 0, 'f', 1));
+ }
+ else
+ {
+ double a;
+
+ if (projectionType == ScopeVisMulti::ProjectionMagLin) {
+ a = 1.0 + t;
+ } else {
+ a = t;
+ }
+
+ if(fabs(a) < 0.000001)
+ ui->trigLevelText->setText(tr("%1\nn").arg(a * 1000000000.0f, 0, 'f', 2));
+ else if(fabs(a) < 0.001)
+ ui->trigLevelText->setText(tr("%1\nµ").arg(a * 1000000.0f, 0, 'f', 2));
+ else if(fabs(a) < 1.0)
+ ui->trigLevelText->setText(tr("%1\nm").arg(a * 1000.0f, 0, 'f', 2));
+ else
+ ui->trigLevelText->setText(tr("%1").arg(a * 1.0f, 0, 'f', 2));
+ }
+}
+
+void GLScopeMultiGUI::setTrigDelayDisplay()
+{
+ if (m_sampleRate > 0)
+ {
+ double delayMult = ui->trigDelayCoarse->value() + ui->trigDelayFine->value() / (ScopeVisMulti::m_traceChunkSize / 10.0);
+ unsigned int n_samples_delay = m_traceLenMult * ScopeVisMulti::m_traceChunkSize * delayMult;
+
+ if (n_samples_delay < 1000) {
+ ui->trigDelayText->setToolTip(tr("%1 S").arg(n_samples_delay));
+ } else if (n_samples_delay < 1000000) {
+ ui->trigDelayText->setToolTip(tr("%1 kS").arg(n_samples_delay/1000.0));
+ } else if (n_samples_delay < 1000000000) {
+ ui->trigDelayText->setToolTip(tr("%1 MS").arg(n_samples_delay/1000000.0));
+ } else {
+ ui->trigDelayText->setToolTip(tr("%1 GS").arg(n_samples_delay/1000000000.0));
+ }
+
+ m_sampleRate = m_glScope->getSampleRate();
+ double t = (n_samples_delay * 1.0f / m_sampleRate);
+
+ if(t < 0.000001)
+ ui->trigDelayText->setText(tr("%1\nns").arg(t * 1000000000.0, 0, 'f', 2));
+ else if(t < 0.001)
+ ui->trigDelayText->setText(tr("%1\nµs").arg(t * 1000000.0, 0, 'f', 2));
+ else if(t < 1.0)
+ ui->trigDelayText->setText(tr("%1\nms").arg(t * 1000.0, 0, 'f', 2));
+ else
+ ui->trigDelayText->setText(tr("%1\ns").arg(t * 1.0, 0, 'f', 2));
+ }
+}
+
+void GLScopeMultiGUI::setTrigPreDisplay()
+{
+ if (m_sampleRate > 0)
+ {
+ unsigned int n_samples_delay = m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f);
+ double dt = m_glScope->getTraceSize() * (ui->trigPre->value()/100.0f) / m_sampleRate;
+
+ if (n_samples_delay < 1000) {
+ ui->trigPreText->setToolTip(tr("%1 S").arg(n_samples_delay));
+ } else if (n_samples_delay < 1000000) {
+ ui->trigPreText->setToolTip(tr("%1 kS").arg(n_samples_delay/1000.0));
+ } else if (n_samples_delay < 1000000000) {
+ ui->trigPreText->setToolTip(tr("%1 MS").arg(n_samples_delay/1000000.0));
+ } else {
+ ui->trigPreText->setToolTip(tr("%1 GS").arg(n_samples_delay/1000000000.0));
+ }
+
+ if(dt < 0.000001)
+ ui->trigPreText->setText(tr("%1\nns").arg(dt * 1000000000.0f, 0, 'f', 2));
+ else if(dt < 0.001)
+ ui->trigPreText->setText(tr("%1\nµs").arg(dt * 1000000.0f, 0, 'f', 2));
+ else if(dt < 1.0)
+ ui->trigPreText->setText(tr("%1\nms").arg(dt * 1000.0f, 0, 'f', 2));
+ else
+ ui->trigPreText->setText(tr("%1\ns").arg(dt * 1.0f, 0, 'f', 2));
+ }
+}
+
+void GLScopeMultiGUI::changeCurrentTrace()
+{
+ ScopeVisMulti::TraceData traceData;
+ fillTraceData(traceData);
+ uint32_t currentTraceIndex = ui->trace->value();
+ m_scopeVis->changeTrace(traceData, currentTraceIndex);
+}
+
+void GLScopeMultiGUI::changeCurrentTrigger()
+{
+ ScopeVisMulti::TriggerData triggerData;
+ fillTriggerData(triggerData);
+ uint32_t currentTriggerIndex = ui->trig->value();
+ m_scopeVis->changeTrigger(triggerData, currentTriggerIndex);
+}
+
+void GLScopeMultiGUI::fillProjectionCombo(QComboBox* comboBox)
+{
+ comboBox->addItem("Real", ScopeVisMulti::ProjectionReal);
+ comboBox->addItem("Imag", ScopeVisMulti::ProjectionImag);
+ comboBox->addItem("Mag", ScopeVisMulti::ProjectionMagLin);
+ comboBox->addItem("MagdB", ScopeVisMulti::ProjectionMagDB);
+ comboBox->addItem("Phi", ScopeVisMulti::ProjectionPhase);
+ comboBox->addItem("dPhi", ScopeVisMulti::ProjectionDPhase);
+}
+
+void GLScopeMultiGUI::disableLiveMode(bool disable)
+{
+ ui->traceLen->setEnabled(!disable);
+ ui->trig->setEnabled(!disable);
+ ui->trigAdd->setEnabled(!disable);
+ ui->trigDel->setEnabled(!disable);
+ ui->trigMode->setEnabled(!disable);
+ ui->trigCount->setEnabled(!disable);
+ ui->trigPos->setEnabled(!disable);
+ ui->trigNeg->setEnabled(!disable);
+ ui->trigBoth->setEnabled(!disable);
+ ui->trigLevelCoarse->setEnabled(!disable);
+ ui->trigLevelFine->setEnabled(!disable);
+ ui->trigDelayCoarse->setEnabled(!disable);
+ ui->trigDelayFine->setEnabled(!disable);
+ ui->trigPre->setEnabled(!disable);
+ ui->trigOneShot->setEnabled(!disable);
+ ui->freerun->setEnabled(!disable);
+}
+
+void GLScopeMultiGUI::fillTraceData(ScopeVisMulti::TraceData& traceData)
+{
+ traceData.m_projectionType = (ScopeVisMulti::ProjectionType) ui->traceMode->currentIndex();
+ traceData.m_hasTextOverlay = (traceData.m_projectionType == ScopeVisMulti::ProjectionMagDB);
+ traceData.m_textOverlay.clear();
+ traceData.m_inputIndex = 0;
+ traceData.m_amp = 0.2 / amps[ui->amp->value()];
+ traceData.m_ampIndex = ui->amp->value();
+
+ traceData.m_ofsCoarse = ui->ofsCoarse->value();
+ traceData.m_ofsFine = ui->ofsFine->value();
+
+ if (traceData.m_projectionType == ScopeVisMulti::ProjectionMagLin) {
+ traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 2000.0f;
+ } else {
+ traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 1000.0f;
+ }
+
+ traceData.m_traceDelayCoarse = ui->traceDelayCoarse->value();
+ traceData.m_traceDelayFine = ui->traceDelayFine->value();
+ traceData.m_traceDelay = traceData.m_traceDelayCoarse * 100 + traceData.m_traceDelayFine;
+ traceData.setColor(m_focusedTraceColor);
+ traceData.m_viewTrace = ui->traceView->isChecked();
+}
+
+void GLScopeMultiGUI::fillTriggerData(ScopeVisMulti::TriggerData& triggerData)
+{
+ triggerData.m_projectionType = (ScopeVisMulti::ProjectionType) ui->trigMode->currentIndex();
+ triggerData.m_inputIndex = 0;
+ triggerData.m_triggerLevel = (ui->trigLevelCoarse->value() / 100.0) + (ui->trigLevelFine->value() / 20000.0);
+ triggerData.m_triggerLevelCoarse = ui->trigLevelCoarse->value();
+ triggerData.m_triggerLevelFine = ui->trigLevelFine->value();
+ triggerData.m_triggerPositiveEdge = ui->trigPos->isChecked();
+ triggerData.m_triggerBothEdges = ui->trigBoth->isChecked();
+ triggerData.m_triggerRepeat = ui->trigCount->value();
+ triggerData.m_triggerDelayMult = ui->trigDelayCoarse->value() + ui->trigDelayFine->value() / (ScopeVisMulti::m_traceChunkSize / 10.0);
+ triggerData.m_triggerDelay = (int) (m_traceLenMult * ScopeVisMulti::m_traceChunkSize * triggerData.m_triggerDelayMult);
+ triggerData.m_triggerDelayCoarse = ui->trigDelayCoarse->value();
+ triggerData.m_triggerDelayFine = ui->trigDelayFine->value();
+ triggerData.setColor(m_focusedTriggerColor);
+}
+
+void GLScopeMultiGUI::setTraceUI(const ScopeVisMulti::TraceData& traceData)
+{
+ TraceUIBlocker traceUIBlocker(ui);
+
+ ui->traceMode->setCurrentIndex((int) traceData.m_projectionType);
+ ui->amp->setValue(traceData.m_ampIndex);
+ setAmpScaleDisplay();
+
+ ui->ofsCoarse->setValue(traceData.m_ofsCoarse);
+ ui->ofsFine->setValue(traceData.m_ofsFine);
+ setAmpOfsDisplay();
+
+ ui->traceDelayCoarse->setValue(traceData.m_traceDelayCoarse);
+ ui->traceDelayFine->setValue(traceData.m_traceDelayFine);
+ setTraceDelayDisplay();
+
+ m_focusedTraceColor = traceData.m_traceColor;
+ int r, g, b, a;
+ m_focusedTraceColor.getRgb(&r, &g, &b, &a);
+ ui->traceColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+
+ ui->traceView->setChecked(traceData.m_viewTrace);
+}
+
+void GLScopeMultiGUI::setTriggerUI(const ScopeVisMulti::TriggerData& triggerData)
+{
+ TrigUIBlocker trigUIBlocker(ui);
+
+ ui->trigMode->setCurrentIndex((int) triggerData.m_projectionType);
+ ui->trigCount->setValue(triggerData.m_triggerRepeat);
+ setTrigCountDisplay();
+
+ ui->trigPos->setChecked(false);
+ ui->trigNeg->setChecked(false);
+ ui->trigPos->doToggle(false);
+ ui->trigNeg->doToggle(false);
+
+ if (triggerData.m_triggerBothEdges)
+ {
+ ui->trigBoth->setChecked(true);
+ ui->trigBoth->doToggle(true);
+ }
+ else
+ {
+ ui->trigBoth->setChecked(false);
+ ui->trigBoth->doToggle(false);
+
+ if (triggerData.m_triggerPositiveEdge)
+ {
+ ui->trigPos->setChecked(true);
+ ui->trigPos->doToggle(true);
+ }
+ else
+ {
+ ui->trigNeg->setChecked(true);
+ ui->trigNeg->doToggle(true);
+ }
+ }
+
+ ui->trigLevelCoarse->setValue(triggerData.m_triggerLevelCoarse);
+ ui->trigLevelFine->setValue(triggerData.m_triggerLevelFine);
+ setTrigLevelDisplay();
+
+ ui->trigDelayCoarse->setValue(triggerData.m_triggerDelayCoarse);
+ ui->trigDelayFine->setValue(triggerData.m_triggerDelayFine);
+ setTrigDelayDisplay();
+
+ m_focusedTriggerColor = triggerData.m_triggerColor;
+ int r, g, b, a;
+ m_focusedTriggerColor.getRgb(&r, &g, &b, &a);
+ ui->trigColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b));
+}
+
+void GLScopeMultiGUI::applySettings()
+{
+}
+
+bool GLScopeMultiGUI::handleMessage(Message* message)
+{
+ return false;
+}
+
+GLScopeMultiGUI::TrigUIBlocker::TrigUIBlocker(Ui::GLScopeMultiGUI *ui) :
+ m_ui(ui)
+{
+ m_oldStateTrigMode = ui->trigMode->blockSignals(true);
+ m_oldStateTrigCount = ui->trigCount->blockSignals(true);
+ m_oldStateTrigPos = ui->trigPos->blockSignals(true);
+ m_oldStateTrigNeg = ui->trigNeg->blockSignals(true);
+ m_oldStateTrigBoth = ui->trigBoth->blockSignals(true);
+ m_oldStateTrigLevelCoarse = ui->trigLevelCoarse->blockSignals(true);
+ m_oldStateTrigLevelFine = ui->trigLevelFine->blockSignals(true);
+ m_oldStateTrigDelayCoarse = ui->trigDelayCoarse->blockSignals(true);
+ m_oldStateTrigDelayFine = ui->trigDelayFine->blockSignals(true);
+}
+
+GLScopeMultiGUI::TrigUIBlocker::~TrigUIBlocker()
+{
+ unBlock();
+}
+
+void GLScopeMultiGUI::TrigUIBlocker::unBlock()
+{
+ m_ui->trigMode->blockSignals(m_oldStateTrigMode);
+ m_ui->trigCount->blockSignals(m_oldStateTrigCount);
+ m_ui->trigPos->blockSignals(m_oldStateTrigPos);
+ m_ui->trigNeg->blockSignals(m_oldStateTrigNeg);
+ m_ui->trigBoth->blockSignals(m_oldStateTrigBoth);
+ m_ui->trigLevelCoarse->blockSignals(m_oldStateTrigLevelCoarse);
+ m_ui->trigLevelFine->blockSignals(m_oldStateTrigLevelFine);
+ m_ui->trigDelayCoarse->blockSignals(m_oldStateTrigDelayCoarse);
+ m_ui->trigDelayFine->blockSignals(m_oldStateTrigDelayFine);
+}
+
+GLScopeMultiGUI::TraceUIBlocker::TraceUIBlocker(Ui::GLScopeMultiGUI* ui) :
+ m_ui(ui)
+{
+ m_oldStateTrace = m_ui->trace->blockSignals(true);
+ m_oldStateTraceAdd = m_ui->traceAdd->blockSignals(true);
+ m_oldStateTraceDel = m_ui->traceDel->blockSignals(true);
+ m_oldStateTraceMode = m_ui->traceMode->blockSignals(true);
+ m_oldStateAmp = m_ui->amp->blockSignals(true);
+ m_oldStateOfsCoarse = m_ui->ofsCoarse->blockSignals(true);
+ m_oldStateOfsFine = m_ui->ofsFine->blockSignals(true);
+ m_oldStateTraceDelayCoarse = m_ui->traceDelayCoarse->blockSignals(true);
+ m_oldStateTraceDelayFine = m_ui->traceDelayFine->blockSignals(true);
+ m_oldStateTraceColor = m_ui->traceColor->blockSignals(true);
+}
+
+GLScopeMultiGUI::TraceUIBlocker::~TraceUIBlocker()
+{
+ unBlock();
+}
+
+void GLScopeMultiGUI::TraceUIBlocker::unBlock()
+{
+ m_ui->trace->blockSignals(m_oldStateTrace);
+ m_ui->traceAdd->blockSignals(m_oldStateTraceAdd);
+ m_ui->traceDel->blockSignals(m_oldStateTraceDel);
+ m_ui->traceMode->blockSignals(m_oldStateTraceMode);
+ m_ui->amp->blockSignals(m_oldStateAmp);
+ m_ui->ofsCoarse->blockSignals(m_oldStateOfsCoarse);
+ m_ui->ofsFine->blockSignals(m_oldStateOfsFine);
+ m_ui->traceDelayCoarse->blockSignals(m_oldStateTraceDelayCoarse);
+ m_ui->traceDelayFine->blockSignals(m_oldStateTraceDelayFine);
+ m_ui->traceColor->blockSignals(m_oldStateTraceColor);
+}
+
+GLScopeMultiGUI::MainUIBlocker::MainUIBlocker(Ui::GLScopeMultiGUI* ui) :
+ m_ui(ui)
+{
+ m_oldStateOnlyX = m_ui->onlyX->blockSignals(true);
+ m_oldStateOnlyY = m_ui->onlyY->blockSignals(true);
+ m_oldStateHorizontalXY = m_ui->horizontalXY->blockSignals(true);
+ m_oldStateVerticalXY = m_ui->verticalXY->blockSignals(true);
+ m_oldStatePolar = m_ui->polar->blockSignals(true);
+// m_oldStateTime = m_ui->time->blockSignals(true);
+// m_oldStateTimeOfs = m_ui->timeOfs->blockSignals(true);
+// m_oldStateTraceLen = m_ui->traceLen->blockSignals(true);
+}
+
+GLScopeMultiGUI::MainUIBlocker::~MainUIBlocker()
+{
+ unBlock();
+}
+
+void GLScopeMultiGUI::MainUIBlocker::unBlock()
+{
+ m_ui->onlyX->blockSignals(m_oldStateOnlyX);
+ m_ui->onlyY->blockSignals(m_oldStateOnlyY);
+ m_ui->horizontalXY->blockSignals(m_oldStateHorizontalXY);
+ m_ui->verticalXY->blockSignals(m_oldStateVerticalXY);
+ m_ui->polar->blockSignals(m_oldStatePolar);
+// m_ui->time->blockSignals(m_oldStateTime);
+// m_ui->timeOfs->blockSignals(m_oldStateTimeOfs);
+// m_ui->traceLen->blockSignals(m_oldStateTraceLen);
+}
+
+void GLScopeMultiGUI::setDisplayMode(DisplayMode displayMode)
+{
+ if (ui->trace->maximum() == 0)
+ {
+ ui->onlyX->setChecked(true);
+ }
+ else
+ {
+ switch (displayMode)
+ {
+ case DisplayX:
+ ui->onlyX->setChecked(true);
+ break;
+ case DisplayY:
+ ui->onlyY->setChecked(true);
+ break;
+ case DisplayXYH:
+ ui->horizontalXY->setChecked(true);
+ break;
+ case DisplayXYV:
+ ui->verticalXY->setChecked(true);
+ break;
+ case DisplayPol:
+ ui->polar->setChecked(true);
+ break;
+ default:
+ ui->onlyX->setChecked(true);
+ break;
+ }
+ }
+}
+
+void GLScopeMultiGUI::setTraceIntensity(int value)
+{
+ if ((value < ui->traceIntensity->minimum()) || (value > ui->traceIntensity->maximum())) {
+ return;
+ }
+
+ ui->traceIntensity->setValue(value);
+}
+
+void GLScopeMultiGUI::setGridIntensity(int value)
+{
+ if ((value < ui->gridIntensity->minimum()) || (value > ui->gridIntensity->maximum())) {
+ return;
+ }
+
+ ui->gridIntensity->setValue(value);
+}
+
+void GLScopeMultiGUI::setTimeBase(int step)
+{
+ if ((step < ui->time->minimum()) || (step > ui->time->maximum())) {
+ return;
+ }
+
+ ui->time->setValue(step);
+}
+
+void GLScopeMultiGUI::setTimeOffset(int step)
+{
+ if ((step < ui->timeOfs->minimum()) || (step > ui->timeOfs->maximum())) {
+ return;
+ }
+
+ ui->timeOfs->setValue(step);
+}
+
+void GLScopeMultiGUI::setTraceLength(int step)
+{
+ if ((step < ui->traceLen->minimum()) || (step > ui->traceLen->maximum())) {
+ return;
+ }
+
+ ui->traceLen->setValue(step);
+}
+
+void GLScopeMultiGUI::setPreTrigger(int step)
+{
+ if ((step < ui->trigPre->minimum()) || (step > ui->trigPre->maximum())) {
+ return;
+ }
+
+ ui->trigPre->setValue(step);
+}
+
+void GLScopeMultiGUI::changeTrace(int traceIndex, const ScopeVisMulti::TraceData& traceData)
+{
+ m_scopeVis->changeTrace(traceData, traceIndex);
+}
+
+void GLScopeMultiGUI::addTrace(const ScopeVisMulti::TraceData& traceData)
+{
+ if (ui->trace->maximum() < 3)
+ {
+ if (ui->trace->value() == 0)
+ {
+ ui->onlyY->setEnabled(true);
+ ui->horizontalXY->setEnabled(true);
+ ui->verticalXY->setEnabled(true);
+ ui->polar->setEnabled(true);
+ }
+
+ m_scopeVis->addTrace(traceData);
+ ui->trace->setMaximum(ui->trace->maximum() + 1);
+ }
+}
+
+void GLScopeMultiGUI::focusOnTrace(int traceIndex)
+{
+ on_trace_valueChanged(traceIndex);
+}
+
+void GLScopeMultiGUI::changeTrigger(int triggerIndex, const ScopeVisMulti::TriggerData& triggerData)
+{
+ m_scopeVis->changeTrigger(triggerData, triggerIndex);
+}
+
+void GLScopeMultiGUI::addTrigger(const ScopeVisMulti::TriggerData& triggerData)
+{
+ if (ui->trig->maximum() < 9)
+ {
+ m_scopeVis->addTrigger(triggerData);
+ ui->trig->setMaximum(ui->trig->maximum() + 1);
+ }
+}
+
+void GLScopeMultiGUI::focusOnTrigger(int triggerIndex)
+{
+ on_trig_valueChanged(triggerIndex);
+}
+
+
diff --git a/sdrbase/gui/glscopemultigui.h b/sdrbase/gui/glscopemultigui.h
new file mode 100644
index 000000000..0c04df3dc
--- /dev/null
+++ b/sdrbase/gui/glscopemultigui.h
@@ -0,0 +1,235 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU General Public License V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef SDRBASE_GUI_GLSCOPEMULTIGUI_H_
+#define SDRBASE_GUI_GLSCOPEMULTIGUI_H_
+
+#include
+#include
+
+#include "dsp/dsptypes.h"
+#include "util/export.h"
+#include "util/message.h"
+#include "dsp/scopevismulti.h"
+
+namespace Ui {
+ class GLScopeMultiGUI;
+}
+
+class MessageQueue;
+class GLScopeMulti;
+
+class SDRANGEL_API GLScopeMultiGUI : public QWidget {
+ Q_OBJECT
+
+public:
+ enum DisplayMode {
+ DisplayXYH,
+ DisplayXYV,
+ DisplayX,
+ DisplayY,
+ DisplayPol
+ };
+
+ explicit GLScopeMultiGUI(QWidget* parent = 0);
+ ~GLScopeMultiGUI();
+
+ void setBuddies(MessageQueue* messageQueue, ScopeVisMulti* scopeVis, GLScopeMulti* glScope);
+
+ void setSampleRate(int sampleRate);
+ void resetToDefaults();
+ QByteArray serialize() const;
+ bool deserialize(const QByteArray& data);
+
+ bool handleMessage(Message* message);
+
+ // preconfiguration methods
+ // global (first line):
+ void setDisplayMode(DisplayMode displayMode);
+ void setTraceIntensity(int value);
+ void setGridIntensity(int value);
+ void setTimeBase(int step);
+ void setTimeOffset(int step);
+ void setTraceLength(int step);
+ void setPreTrigger(int step);
+ // trace (second line):
+ void changeTrace(int traceIndex, const ScopeVisMulti::TraceData& traceData);
+ void addTrace(const ScopeVisMulti::TraceData& traceData);
+ void focusOnTrace(int traceIndex);
+ // trigger (third line):
+ void changeTrigger(int triggerIndex, const ScopeVisMulti::TriggerData& triggerData);
+ void addTrigger(const ScopeVisMulti::TriggerData& triggerData);
+ void focusOnTrigger(int triggerIndex);
+
+private:
+ class TrigUIBlocker
+ {
+ public:
+ TrigUIBlocker(Ui::GLScopeMultiGUI *ui);
+ ~TrigUIBlocker();
+
+ void unBlock();
+
+ private:
+ Ui::GLScopeMultiGUI *m_ui;
+ bool m_oldStateTrigMode;
+ bool m_oldStateTrigCount;
+ bool m_oldStateTrigPos;
+ bool m_oldStateTrigNeg;
+ bool m_oldStateTrigBoth;
+ bool m_oldStateTrigLevelCoarse;
+ bool m_oldStateTrigLevelFine;
+ bool m_oldStateTrigDelayCoarse;
+ bool m_oldStateTrigDelayFine;
+ };
+
+ class TraceUIBlocker
+ {
+ public:
+ TraceUIBlocker(Ui::GLScopeMultiGUI *ui);
+ ~TraceUIBlocker();
+
+ void unBlock();
+
+ private:
+ Ui::GLScopeMultiGUI *m_ui;
+ bool m_oldStateTrace;
+ bool m_oldStateTraceAdd;
+ bool m_oldStateTraceDel;
+ bool m_oldStateTraceMode;
+ bool m_oldStateAmp;
+ bool m_oldStateOfsCoarse;
+ bool m_oldStateOfsFine;
+ bool m_oldStateTraceDelayCoarse;
+ bool m_oldStateTraceDelayFine;
+ bool m_oldStateTraceColor;
+ };
+
+ class MainUIBlocker
+ {
+ public:
+ MainUIBlocker(Ui::GLScopeMultiGUI *ui);
+ ~MainUIBlocker();
+
+ void unBlock();
+
+ private:
+ Ui::GLScopeMultiGUI *m_ui;
+ bool m_oldStateOnlyX;
+ bool m_oldStateOnlyY;
+ bool m_oldStateHorizontalXY;
+ bool m_oldStateVerticalXY;
+ bool m_oldStatePolar;
+// bool m_oldStateTime;
+// bool m_oldStateTimeOfs;
+// bool m_oldStateTraceLen;
+ };
+
+ Ui::GLScopeMultiGUI* ui;
+
+ MessageQueue* m_messageQueue;
+ ScopeVisMulti* m_scopeVis;
+ GLScopeMulti* m_glScope;
+
+ int m_sampleRate;
+ int m_timeBase;
+ int m_timeOffset;
+ int m_traceLenMult;
+ QColor m_focusedTraceColor;
+ QColor m_focusedTriggerColor;
+
+ static const double amps[11];
+
+ void applySettings();
+ // First row
+ void setTraceIndexDisplay();
+ void setTimeScaleDisplay();
+ void setTraceLenDisplay();
+ void setTimeOfsDisplay();
+ // Second row
+ void setAmpScaleDisplay();
+ void setAmpOfsDisplay();
+ void setTraceDelayDisplay();
+ // Third row
+ void setTrigIndexDisplay();
+ void setTrigCountDisplay();
+ void setTrigLevelDisplay();
+ void setTrigDelayDisplay();
+ void setTrigPreDisplay();
+
+ void changeCurrentTrace();
+ void changeCurrentTrigger();
+
+ void fillTraceData(ScopeVisMulti::TraceData& traceData);
+ void fillTriggerData(ScopeVisMulti::TriggerData& triggerData);
+ void setTriggerUI(const ScopeVisMulti::TriggerData& triggerData);
+ void setTraceUI(const ScopeVisMulti::TraceData& traceData);
+
+ void fillProjectionCombo(QComboBox* comboBox);
+ void disableLiveMode(bool disable);
+
+private slots:
+ void on_scope_sampleRateChanged(int value);
+ // First row
+ void on_onlyX_toggled(bool checked);
+ void on_onlyY_toggled(bool checked);
+ void on_horizontalXY_toggled(bool checked);
+ void on_verticalXY_toggled(bool checked);
+ void on_polar_toggled(bool checked);
+ void on_traceIntensity_valueChanged(int value);
+ void on_gridIntensity_valueChanged(int value);
+ void on_time_valueChanged(int value);
+ void on_timeOfs_valueChanged(int value);
+ void on_traceLen_valueChanged(int value);
+ // Second row
+ void on_trace_valueChanged(int value);
+ void on_traceAdd_clicked(bool checked);
+ void on_traceDel_clicked(bool checked);
+ void on_traceUp_clicked(bool checked);
+ void on_traceDown_clicked(bool checked);
+ void on_traceMode_currentIndexChanged(int index);
+ void on_amp_valueChanged(int value);
+ void on_ofsCoarse_valueChanged(int value);
+ void on_ofsFine_valueChanged(int value);
+ void on_traceDelayCoarse_valueChanged(int value);
+ void on_traceDelayFine_valueChanged(int value);
+ void on_traceView_toggled(bool checked);
+ void on_traceColor_clicked();
+ void on_mem_valueChanged(int value);
+ // Third row
+ void on_trig_valueChanged(int value);
+ void on_trigAdd_clicked(bool checked);
+ void on_trigDel_clicked(bool checked);
+ void on_trigUp_clicked(bool checked);
+ void on_trigDown_clicked(bool checked);
+ void on_trigMode_currentIndexChanged(int index);
+ void on_trigCount_valueChanged(int value);
+ void on_trigPos_toggled(bool checked);
+ void on_trigNeg_toggled(bool checked);
+ void on_trigBoth_toggled(bool checked);
+ void on_trigLevelCoarse_valueChanged(int value);
+ void on_trigLevelFine_valueChanged(int value);
+ void on_trigDelayCoarse_valueChanged(int value);
+ void on_trigDelayFine_valueChanged(int value);
+ void on_trigPre_valueChanged(int value);
+ void on_trigColor_clicked();
+ void on_trigOneShot_toggled(bool checked);
+ void on_freerun_toggled(bool checked);
+};
+
+
+#endif /* SDRBASE_GUI_GLSCOPEMULTIGUI_H_ */
diff --git a/sdrbase/gui/glscopemultigui.ui b/sdrbase/gui/glscopemultigui.ui
new file mode 100644
index 000000000..253784e37
--- /dev/null
+++ b/sdrbase/gui/glscopemultigui.ui
@@ -0,0 +1,1907 @@
+
+
+ GLScopeMultiGUI
+
+
+
+ 0
+ 0
+ 750
+ 120
+
+
+
+
+ 750
+ 0
+
+
+
+
+ 8
+
+
+
+ Oscilloscope
+
+
+
+ 1
+
+
+ 2
+
+
+ 1
+
+
+ 2
+
+
+ 1
+
+ -
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 24
+ 24
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+ Show only X trace (trace #0)
+
+
+ X
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+ Show only Y traces
+
+
+ Y
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Display X and Y traces side by side
+
+
+ ...
+
+
+
+ :/horizontal_w.png:/horizontal_w.png
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Display X and Y trances on top of each other
+
+
+ ...
+
+
+
+ :/vertical_w.png:/vertical_w.png
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Display XY traces and polar trace
+
+
+ ...
+
+
+
+ :/constellation.png:/constellation.png
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 24
+ 24
+
+
+
+ Trace intensity
+
+
+ 100
+
+
+ 1
+
+
+ 50
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Grid intensity
+
+
+ 100
+
+
+ 1
+
+
+ 10
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Time range
+
+
+ T:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Time range
+
+
+ 1
+
+
+ 100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Time offset
+
+
+ O:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Time offset
+
+
+ 100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Trace length
+
+
+ L:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Trace length
+
+
+ 1
+
+
+ 20
+
+
+ 1
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 52
+ 0
+
+
+
+ Currently displayed trace sample rate (kS/s)
+
+
+ 00000.00
+kS/s
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+
+ 24
+ 0
+
+
+
+ Tra
+
+
+
+ -
+
+
+
+ 15
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Trace index (0 is X trace)
+
+
+ 0
+
+
+ 1
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+ 18
+ 18
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+
+ 10
+
+
+
+ Add a new Y trace
+
+
+ +
+
+
+
+ -
+
+
+
+ 18
+ 18
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+
+ 10
+
+
+
+ Remove current Y trace
+
+
+ -
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 18
+ 18
+
+
+
+ Move trace up in trace list (wraps around)
+
+
+
+
+
+
+ :/arrow_up.png:/arrow_up.png
+
+
+
+ -
+
+
+
+ 18
+ 18
+
+
+
+ Move trace down in trace list
+
+
+
+
+
+
+ :/arrow_down.png:/arrow_down.png
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 65
+ 16777215
+
+
+
+ Trace mode
+
+
-
+
+ Real
+
+
+ -
+
+ Imag
+
+
+ -
+
+ Mag
+
+
+ -
+
+ MagdB
+
+
+ -
+
+ Phi
+
+
+ -
+
+ dPhi
+
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Source port selection
+
+
+ 0
+
+
+ 1
+
+
+
+ -
+
+
+
+ 12
+ 0
+
+
+
+ Source port number
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Vertical range
+
+
+ A:
+
+
+
+ -
+
+
+
+ 36
+ 0
+
+
+
+ Vertical range value
+
+
+ 000
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Vertical range
+
+
+ 10
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Vertical offset
+
+
+ O:
+
+
+
+ -
+
+
+
+ 36
+ 0
+
+
+
+ Vertical offset value
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+
+ 16777215
+ 14
+
+
+
+ Vertical offset coarse
+
+
+ -100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 16777215
+ 14
+
+
+
+ Vertical offset fine
+
+
+ 200
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Trace delay
+
+
+ D:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Trace delay coarse
+
+
+ 48
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Trace delay fine
+
+
+ 100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ View trace toggle
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+ Current trace color (click to change)
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ M:
+
+
+
+ -
+
+
+ Trace memory index (greater is older)
+
+
+ 00
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Select trace memory (0 is live)
+
+
+ 15
+
+
+ 1
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+
+ 24
+ 0
+
+
+
+ Trig
+
+
+
+ -
+
+
+
+ 15
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Sellect triggger in trigger chain
+
+
+ 0
+
+
+ 1
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+
+ 18
+ 18
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+
+ 10
+
+
+
+ Add a new trigger to the trigger chain
+
+
+ +
+
+
+
+ -
+
+
+
+ 18
+ 18
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+
+ 10
+
+
+
+ Remove current trigger from the chain
+
+
+ -
+
+
+
+
+
+ -
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 18
+ 18
+
+
+
+ Move trigger up in trigger chain (wraps around)
+
+
+
+
+
+
+ :/arrow_up.png:/arrow_up.png
+
+
+
+ -
+
+
+
+ 18
+ 18
+
+
+
+ Move trigger down in trigger chain
+
+
+
+
+
+
+ :/arrow_down.png:/arrow_down.png
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 65
+ 16777215
+
+
+
+ Trigger mode
+
+
-
+
+ Real
+
+
+ -
+
+ Imag
+
+
+ -
+
+ Mag
+
+
+ -
+
+ MagdB
+
+
+ -
+
+ Phi
+
+
+ -
+
+ dPhi
+
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ 0
+
+
+ 1
+
+
+
+ -
+
+
+
+ 12
+ 0
+
+
+
+ 0
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Number of trigger condition before trigger is actuated
+
+
+ 100
+
+
+ 1
+
+
+
+ -
+
+
+ 00
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Trigger on rising edge
+
+
+ ...
+
+
+
+ :/slopep_icon.png:/slopep_icon.png
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Trigger on falling edge
+
+
+ ...
+
+
+
+ :/slopen_icon.png:/slopen_icon.png
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+ Trigger on both edges
+
+
+ ...
+
+
+
+ :/slopeb_icon.png:/slopeb_icon.png
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Triger level
+
+
+ L:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 2
+
+
-
+
+
+
+ 16777215
+ 14
+
+
+
+ Trigger level coarse
+
+
+ -100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+
+ 16777215
+ 14
+
+
+
+ Trigger level fine
+
+
+ 200
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Trigger delay
+
+
+ D:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ 2
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Trigger delay coarse (trace length multiplier)
+
+
+ 100
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Trigger delay fine (trace length divider)
+
+
+ 480
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Pre-trigger delay
+
+
+ P:
+
+
+
+ -
+
+
+
+ 40
+ 0
+
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Pre-trigger delay
+
+
+ 1
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+ Current trigger color (click to change)
+
+
+
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+ One shot trigger
+
+
+ 1
+
+
+
+ -
+
+
+
+ 24
+ 24
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 255
+ 255
+ 255
+
+
+
+
+
+
+
+
+ 190
+ 190
+ 190
+
+
+
+
+
+
+
+ Freerun (de-activate triggers)
+
+
+ F
+
+
+
+
+
+
+
+
+
+ ButtonSwitch
+ QToolButton
+
+
+
+ ClickableLabel
+ QLabel
+
+
+
+
+
+
+
+