/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2017-2021, 2023 Edouard Griffiths, F4EXB // // Copyright (C) 2019 Martin Hauke // // Copyright (C) 2020 Kacper Michajłow // // Copyright (C) 2022 Jiří Pinkava // // // // This program is free software; you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation as version 3 of the License, or // // (at your option) any later version. // // // // 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_SCOPEVISNG_H_ #define SDRBASE_DSP_SCOPEVISNG_H_ #include #include #include #include #include #include #include #include #include "dsp/dsptypes.h" #include "dsp/projector.h" #include "dsp/glscopesettings.h" #include "export.h" #include "util/message.h" #include "util/messagequeue.h" #include "util/doublebuffer.h" class GLScopeInterface; class SpectrumVis; class SDRBASE_API ScopeVis : public QObject { Q_OBJECT public: // === messages === // --------------------------------------------- class SDRBASE_API MsgConfigureScopeVis : public Message { MESSAGE_CLASS_DECLARATION public: const GLScopeSettings& getSettings() const { return m_settings; } bool getForce() const { return m_force; } static MsgConfigureScopeVis* create(const GLScopeSettings& settings, bool force) { return new MsgConfigureScopeVis(settings, force); } private: GLScopeSettings m_settings; bool m_force; MsgConfigureScopeVis(const GLScopeSettings& settings, bool force) : Message(), m_settings(settings), m_force(force) { } }; // --------------------------------------------- class SDRBASE_API MsgScopeVisAddTrigger : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisAddTrigger* create( const GLScopeSettings::TriggerData& triggerData) { return new MsgScopeVisAddTrigger(triggerData); } const GLScopeSettings::TriggerData& getTriggerData() const { return m_triggerData; } private: GLScopeSettings::TriggerData m_triggerData; MsgScopeVisAddTrigger(const GLScopeSettings::TriggerData& triggerData) : m_triggerData(triggerData) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisChangeTrigger : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisChangeTrigger* create( const GLScopeSettings::TriggerData& triggerData, uint32_t triggerIndex) { return new MsgScopeVisChangeTrigger(triggerData, triggerIndex); } const GLScopeSettings::TriggerData& getTriggerData() const { return m_triggerData; } uint32_t getTriggerIndex() const { return m_triggerIndex; } private: GLScopeSettings::TriggerData m_triggerData; uint32_t m_triggerIndex; MsgScopeVisChangeTrigger(const GLScopeSettings::TriggerData& triggerData, uint32_t triggerIndex) : m_triggerData(triggerData), m_triggerIndex(triggerIndex) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisRemoveTrigger : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisRemoveTrigger* create( uint32_t triggerIndex) { return new MsgScopeVisRemoveTrigger(triggerIndex); } uint32_t getTriggerIndex() const { return m_triggerIndex; } private: uint32_t m_triggerIndex; MsgScopeVisRemoveTrigger(uint32_t triggerIndex) : m_triggerIndex(triggerIndex) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisMoveTrigger : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisMoveTrigger* create( uint32_t triggerIndex, bool moveUpElseDown) { return new MsgScopeVisMoveTrigger(triggerIndex, moveUpElseDown); } uint32_t getTriggerIndex() const { return m_triggerIndex; } bool getMoveUp() const { return m_moveUpElseDown; } private: uint32_t m_triggerIndex; bool m_moveUpElseDown; MsgScopeVisMoveTrigger(uint32_t triggerIndex, bool moveUpElseDown) : m_triggerIndex(triggerIndex), m_moveUpElseDown(moveUpElseDown) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisFocusOnTrigger : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisFocusOnTrigger* create( uint32_t triggerIndex) { return new MsgScopeVisFocusOnTrigger(triggerIndex); } uint32_t getTriggerIndex() const { return m_triggerIndex; } private: uint32_t m_triggerIndex; MsgScopeVisFocusOnTrigger(uint32_t triggerIndex) : m_triggerIndex(triggerIndex) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisAddTrace : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisAddTrace* create( const GLScopeSettings::TraceData& traceData) { return new MsgScopeVisAddTrace(traceData); } const GLScopeSettings::TraceData& getTraceData() const { return m_traceData; } private: GLScopeSettings::TraceData m_traceData; MsgScopeVisAddTrace(const GLScopeSettings::TraceData& traceData) : m_traceData(traceData) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisChangeTrace : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisChangeTrace* create( const GLScopeSettings::TraceData& traceData, uint32_t traceIndex) { return new MsgScopeVisChangeTrace(traceData, traceIndex); } const GLScopeSettings::TraceData& getTraceData() const { return m_traceData; } uint32_t getTraceIndex() const { return m_traceIndex; } private: GLScopeSettings::TraceData m_traceData; uint32_t m_traceIndex; MsgScopeVisChangeTrace(GLScopeSettings::TraceData traceData, uint32_t traceIndex) : m_traceData(traceData), m_traceIndex(traceIndex) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisRemoveTrace : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisRemoveTrace* create( uint32_t traceIndex) { return new MsgScopeVisRemoveTrace(traceIndex); } uint32_t getTraceIndex() const { return m_traceIndex; } private: uint32_t m_traceIndex; MsgScopeVisRemoveTrace(uint32_t traceIndex) : m_traceIndex(traceIndex) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisMoveTrace : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisMoveTrace* create( uint32_t traceIndex, bool moveUpElseDown) { return new MsgScopeVisMoveTrace(traceIndex, moveUpElseDown); } uint32_t getTraceIndex() const { return m_traceIndex; } bool getMoveUp() const { return m_moveUpElseDown; } private: uint32_t m_traceIndex; bool m_moveUpElseDown; MsgScopeVisMoveTrace(uint32_t traceIndex, bool moveUpElseDown) : m_traceIndex(traceIndex), m_moveUpElseDown(moveUpElseDown) {} }; // --------------------------------------------- class SDRBASE_API MsgScopeVisFocusOnTrace : public Message { MESSAGE_CLASS_DECLARATION public: static MsgScopeVisFocusOnTrace* create( uint32_t traceIndex) { return new MsgScopeVisFocusOnTrace(traceIndex); } uint32_t getTraceIndex() const { return m_traceIndex; } private: uint32_t m_traceIndex; MsgScopeVisFocusOnTrace(uint32_t traceIndex) : m_traceIndex(traceIndex) {} }; // --------------------------------------------- class SDRBASE_API 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 SDRBASE_API 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) {} }; ScopeVis(); virtual ~ScopeVis(); void setGLScope(GLScopeInterface* glScope); void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrumVis = spectrumVis; } void setSSBSpectrum(bool ssbSpectrum) { m_ssbSpectrum = ssbSpectrum; } MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication void setLiveRate(int sampleRate); void setNbStreams(uint32_t nbStreams); void configure( uint32_t traceSize, uint32_t timeBase, uint32_t timeOfsProMill, uint32_t triggerPre, bool freeRun ); void configure( GLScopeSettings::DisplayMode displayMode, uint32_t traceIntensity, uint32_t gridIntensity ); void setOneShot(bool oneShot); void setMemoryIndex(uint32_t memoryIndex); void setTraceChunkSize(uint32_t chunkSize) { m_traceChunkSize = chunkSize; } uint32_t getTraceChunkSize() const { return m_traceChunkSize; } QByteArray serializeMemory() const { SimpleSerializer s(1); s.writeU32(1, m_traceSize); s.writeU32(2, m_preTriggerDelay); s.writeS32(3, m_sampleRate); QByteArray buffer = m_traceDiscreteMemory.serialize(); s.writeBlob(4, buffer); return s.final(); } bool deserializeMemory(const QByteArray& data) { SimpleDeserializer d(data); if(!d.isValid()) { return false; } if (d.getVersion() == 1) { uint32_t traceSize, preTriggerDelay; int sampleRate; QByteArray buf; bool traceDiscreteMemorySuccess; d.readU32(1, &traceSize, GLScopeSettings::m_traceChunkDefaultSize); d.readU32(2, &preTriggerDelay, 0); d.readS32(3, &sampleRate, 0); setSampleRate(sampleRate); setTraceSize(traceSize, true); setPreTriggerDelay(preTriggerDelay, true); d.readBlob(4, &buf); traceDiscreteMemorySuccess = m_traceDiscreteMemory.deserialize(buf); if (traceDiscreteMemorySuccess && (m_glScope) && (m_currentTraceMemoryIndex > 0)) { processMemoryTrace(); } return traceDiscreteMemorySuccess; } else { return false; } } void getTriggerData(GLScopeSettings::TriggerData& triggerData, uint32_t triggerIndex) { if (triggerIndex < m_triggerConditions.size()) { triggerData = m_triggerConditions[triggerIndex]->m_triggerData; } } void getTraceData(GLScopeSettings::TraceData& traceData, uint32_t traceIndex) { if (traceIndex < m_traces.m_tracesData.size()) { traceData = m_traces.m_tracesData[traceIndex]; } } const GLScopeSettings::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(); } uint32_t getNbTraces() const { return m_traces.size(); } void feed(const std::vector& vbegin, int nbSamples); void feed(const std::vector& vbegin, int nbSamples); //virtual void start(); //virtual void stop(); bool handleMessage(const Message& message); int getTriggerLocation() const { return m_triggerLocation; } bool getFreeRun() const { return m_freeRun; } private: /** * 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 }; struct TriggerCondition { public: Projector m_projector; GLScopeSettings::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 occurrences uint32_t m_trues; //!< Count of true conditions for holdoff processing uint32_t m_falses; //!< Count of false conditions for holdoff processing TriggerCondition(const GLScopeSettings::TriggerData& triggerData) : m_projector(Projector::ProjectionReal), m_triggerData(triggerData), m_prevCondition(false), m_triggerDelayCount(0), m_triggerCounter(0), m_trues(0), m_falses(0) { } ~TriggerCondition() { } void initProjector() { m_projector.settProjectionType(m_triggerData.m_projectionType); } void releaseProjector() { } void setData(const GLScopeSettings::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; m_trues = 0; m_falses = 0; } void operator=(const TriggerCondition& other) { setData(other.m_triggerData); } }; /** * Complex trace stuff */ typedef DoubleBufferSimple TraceBuffer; struct TraceBackBuffer { TraceBuffer m_traceBuffer; TraceBackBuffer() { m_endPoint = m_traceBuffer.getCurrent(); } void resize(uint32_t size) { m_traceBuffer.resize(size); } void reset() { m_traceBuffer.reset(); } void write(const ComplexVector::const_iterator begin, int nbSamples) { m_traceBuffer.write(begin, nbSamples); } unsigned int absoluteFill() const { return m_traceBuffer.absoluteFill(); } void current(ComplexVector::iterator& it) { m_traceBuffer.getCurrent(it); } QByteArray serialize() const { SimpleSerializer s(1); QByteArray buffer = m_traceBuffer.serialize(); unsigned int endDelta = m_endPoint - m_traceBuffer.begin(); s.writeU32(1, endDelta); s.writeBlob(2, buffer); return s.final(); } bool deserialize(const QByteArray& data) { SimpleDeserializer d(data); if(!d.isValid()) { return false; } if (d.getVersion() == 1) { unsigned int tmpUInt; QByteArray buf; d.readU32(1, &tmpUInt, 0); d.readBlob(2, &buf); m_traceBuffer.deserialize(buf); m_endPoint = m_traceBuffer.begin() + tmpUInt; return true; } else { return false; } } void setEndPoint(const ComplexVector::const_iterator& endPoint) { m_endPoint = endPoint; } ComplexVector::const_iterator getEndPoint() { return m_endPoint; } void getEndPoint(ComplexVector::const_iterator& it) { it = m_endPoint; } private: ComplexVector::const_iterator m_endPoint; }; typedef std::vector TraceBackBufferStream; struct ConvertBuffers { ConvertBuffers(uint32_t nbStreams = 1) : m_convertBuffers(nbStreams) {} void setNbStreams(uint32_t nbStreams) { m_convertBuffers.resize(nbStreams); resize(m_size); } void resize(unsigned int size) { for (unsigned int s = 0; s < m_convertBuffers.size(); s++) { m_convertBuffers[s].resize(size); } m_size = size; } unsigned int size() const { return m_size; } std::vector& getBuffers() { return m_convertBuffers; } private: unsigned int m_size; std::vector m_convertBuffers; }; struct TraceBackDiscreteMemory { /** * Give memory size in number of traces */ TraceBackDiscreteMemory(uint32_t size, uint32_t nbStreams = 1) : m_traceBackBuffersStreams(nbStreams), m_memSize(size), m_currentMemIndex(0), m_maxMemIndex(0), m_traceSize(0) { for (unsigned int s = 0; s < m_traceBackBuffersStreams.size(); s++) { m_traceBackBuffersStreams[s].resize(m_memSize); } } void setNbStreams(uint32_t nbStreams) { m_traceBackBuffersStreams.resize(nbStreams); for (unsigned int s = 0; s < m_traceBackBuffersStreams.size(); s++) { m_traceBackBuffersStreams[s].resize(m_memSize); } resize(m_traceSize); } /** * Resize all trace buffers in memory */ void resize(uint32_t size) { m_traceSize = size; for (unsigned int s = 0; s < m_traceBackBuffersStreams.size(); s++) { for (std::vector::iterator it = m_traceBackBuffersStreams[s].begin(); it != m_traceBackBuffersStreams[s].end(); ++it) { it->resize(2*m_traceSize); // was multiplied by 4 } } } /** * Move index forward by one position and return reference to the trace at this position * Copy a trace length of samples into the new memory slot * samplesToReport are the number of samples to report on the next trace */ void store(int samplesToReport) { uint32_t nextMemIndex = m_currentMemIndex < (m_memSize-1) ? m_currentMemIndex+1 : 0; for (unsigned int s = 0; s < m_traceBackBuffersStreams.size(); s++) { m_traceBackBuffersStreams[s][nextMemIndex].reset(); m_traceBackBuffersStreams[s][nextMemIndex].write( m_traceBackBuffersStreams[s][m_currentMemIndex].getEndPoint() - samplesToReport, samplesToReport ); } m_currentMemIndex = nextMemIndex; if (m_currentMemIndex > m_maxMemIndex) { m_maxMemIndex = m_currentMemIndex; } } /** * Return current memory index */ uint32_t currentIndex() const { return m_currentMemIndex; } /** * Return max memory index processed */ uint32_t maxIndex() const { return m_maxMemIndex; } /** * Serializer */ QByteArray serialize() const { SimpleSerializer s(1); s.writeU32(1, m_traceBackBuffersStreams.size()); s.writeU32(2, m_memSize); s.writeU32(3, m_currentMemIndex); s.writeU32(4, m_traceSize); for (unsigned int is = 0; is < m_traceBackBuffersStreams.size(); is++) { SimpleSerializer ss(1); for (unsigned int i = 0; i < m_memSize; i++) { QByteArray buffer = m_traceBackBuffersStreams[is][i].serialize(); ss.writeBlob(i, buffer); } s.writeBlob(5+is, ss.final()); } return s.final(); } /** * Deserializer */ bool deserialize(const QByteArray& data) { SimpleDeserializer d(data); if(!d.isValid()) { return false; } if (d.getVersion() == 1) { unsigned int nbStreams; d.readU32(1, &nbStreams, 0); d.readU32(2, &m_memSize, 0); d.readU32(3, &m_currentMemIndex, 0); uint32_t traceSize; d.readU32(4, &traceSize, 0); for (unsigned int is = 0; is < nbStreams; is++) { if (is >= m_traceBackBuffersStreams.size()) { break; } m_traceBackBuffersStreams[is].resize(m_memSize); if (traceSize != m_traceSize) { resize(traceSize); } QByteArray streamData; d.readBlob(5+is, &streamData); SimpleDeserializer ds(streamData); for (unsigned int i = 0; i < m_memSize; i++) { QByteArray buffer; ds.readBlob(i, &buffer); m_traceBackBuffersStreams[is][i].deserialize(buffer); } } return true; } else { return false; } } /** * Get current point at current memory position (first stream) */ void getCurrent(ComplexVector::iterator& it) { current().current(it); } /** * Get current points at current memory position */ void getCurrent(std::vector& vit) { vit.clear(); for (unsigned int is = 0; is < m_traceBackBuffersStreams.size(); is++) { ComplexVector::iterator it; current(is).current(it); vit.push_back(it); } } /** * Set end point at current memory position (first stream) */ void setCurrentEndPoint(const ComplexVector::iterator& it) { current().setEndPoint(it); } /** * Set end points at current memory position */ void setCurrentEndPoint(const std::vector& vit) { for (unsigned int is = 0; is < vit.size(); is++) { if (is >= m_traceBackBuffersStreams.size()) { break; } current(is).setEndPoint(vit[is]); } } /** * Get end point at given memory position (first stream) */ void getEndPointAt(int index, ComplexVector::const_iterator& mend) { at(index).getEndPoint(mend); } /** * Get end points at given memory position */ void getEndPointAt(int index, std::vector& vend) { vend.clear(); for (unsigned int is = 0; is < m_traceBackBuffersStreams.size(); is++) { ComplexVector::const_iterator mend; at(index, is).getEndPoint(mend); vend.push_back(mend); } } /** * Write trace at current memory position (first stream) */ void writeCurrent(const ComplexVector::const_iterator& begin, int length) { current().write(begin, length); } /** * Write traces at current memory position */ void writeCurrent(const std::vector& vbegin, int length) { for (unsigned int i = 0; i < vbegin.size(); i++) { current(i).write(vbegin[i], length); } } /** * Move buffer iterator by a certain amount (first stream) */ static void moveIt(const ComplexVector::const_iterator& x, ComplexVector::const_iterator& y, int amount) { y = x + amount; } /** * Move buffers iterators by a certain amount */ static void moveIt(const std::vector& vx, std::vector& vy, int amount) { for (unsigned int i = 0; i < vx.size(); i++) { if (i >= vy.size()) { break; } vy[i] = vx[i] + amount; } } private: std::vector m_traceBackBuffersStreams; uint32_t m_memSize; uint32_t m_currentMemIndex; uint32_t m_maxMemIndex; uint32_t m_traceSize; TraceBackBuffer& current(uint32_t streamIndex = 0) { //!< Return trace at current memory position return m_traceBackBuffersStreams[streamIndex][m_currentMemIndex]; } TraceBackBuffer& at(int index, uint32_t streamIndex = 0) { //!< Return trace at given memory position return m_traceBackBuffersStreams[streamIndex][index]; } }; /** * Displayable trace stuff */ struct TraceControl { Projector m_projector; //!< Projector transform from complex trace to real (displayable) trace uint32_t m_traceCount[2]; //!< Count of samples processed (double buffered) double m_maxPow; //!< Maximum power over the current trace for MagDB overlay display double 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(Projector::ProjectionReal) { reset(); } ~TraceControl() { } void initProjector(Projector::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 std::vector m_projectionTypes; 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() : m_traceSize(0), m_maxTraceSize(0), evenOddIndex(true), m_x0(0), m_x1(0) { } ~Traces() { for (std::vector::iterator it = m_tracesControl.begin(); it != m_tracesControl.end(); ++it) { delete *it; } if (m_x0) { delete[] m_x0; } if (m_x1) { delete[] m_x1; } m_maxTraceSize = 0; } bool isVerticalDisplayChange(const GLScopeSettings::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 GLScopeSettings::TraceData& traceData, int traceSize) { if (m_traces[0].size() < GLScopeSettings::m_maxNbTraces) { qDebug("ScopeVis::Traces::addTrace"); m_traces[0].push_back(nullptr); m_traces[1].push_back(nullptr); m_tracesData.push_back(traceData); m_projectionTypes.push_back(traceData.m_projectionType); m_tracesControl.push_back(new TraceControl()); TraceControl *traceControl = m_tracesControl.back(); traceControl->initProjector(traceData.m_projectionType); resize(traceSize); } } void changeTrace(const GLScopeSettings::TraceData& traceData, uint32_t traceIndex) { if (traceIndex < m_tracesControl.size()) { TraceControl *traceControl = m_tracesControl[traceIndex]; traceControl->releaseProjector(); traceControl->initProjector(traceData.m_projectionType); m_tracesData[traceIndex] = traceData; m_projectionTypes[traceIndex] = traceData.m_projectionType; } } void removeTrace(uint32_t traceIndex) { if (traceIndex < m_tracesControl.size()) { qDebug("ScopeVis::Traces::removeTrace"); m_traces[0].erase(m_traces[0].begin() + traceIndex); m_traces[1].erase(m_traces[1].begin() + traceIndex); m_projectionTypes.erase(m_projectionTypes.begin() + traceIndex); TraceControl *traceControl = m_tracesControl[traceIndex]; traceControl->releaseProjector(); m_tracesControl.erase(m_tracesControl.begin() + traceIndex); m_tracesData.erase(m_tracesData.begin() + traceIndex); delete traceControl; 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 int nextProjectionTypeIndex = (traceIndex + (upElseDown ? 1 : -1)) % (m_projectionTypes.size()); // should be the same Projector::ProjectionType nextType = m_projectionTypes[traceIndex]; m_projectionTypes[nextProjectionTypeIndex] = m_projectionTypes[traceIndex]; m_projectionTypes[traceIndex] = nextType; TraceControl *traceControl = m_tracesControl[traceIndex]; TraceControl *nextTraceControl = m_tracesControl[nextControlIndex]; traceControl->releaseProjector(); nextTraceControl->releaseProjector(); m_tracesControl[nextControlIndex] = traceControl; m_tracesControl[traceIndex] = nextTraceControl; GLScopeSettings::TraceData nextData = m_tracesData[nextDataIndex]; m_tracesData[nextDataIndex] = m_tracesData[traceIndex]; m_tracesData[traceIndex] = nextData; traceControl = m_tracesControl[traceIndex]; nextTraceControl = m_tracesControl[nextControlIndex]; traceControl->initProjector(m_tracesData[traceIndex].m_projectionType); nextTraceControl->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*GLScopeSettings::m_maxNbTraces]; m_x1 = new float[2*m_traceSize*GLScopeSettings::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 (unsigned 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; } } void resetControls() { for (auto traceControl : m_tracesControl) { traceControl->reset(); } } private: float *m_x0; float *m_x1; }; class TriggerComparator { public: TriggerComparator() : m_level(0), m_reset(true) { computeLevels(); } bool triggered(const Complex& 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() == Projector::ProjectionMagDB) { condition = triggerCondition.m_projector.run(s) > m_levelPowerDB; } else if (triggerCondition.m_projector.getProjectionType() == Projector::ProjectionMagLin) { condition = triggerCondition.m_projector.run(s) > m_levelPowerLin; } else if (triggerCondition.m_projector.getProjectionType() == Projector::ProjectionMagSq) { condition = triggerCondition.m_projector.run(s) > m_levelPowerLin; } else { condition = triggerCondition.m_projector.run(s) > m_level; } if (condition) { if (triggerCondition.m_trues < triggerCondition.m_triggerData.m_triggerHoldoff) { condition = false; triggerCondition.m_trues++; } else { triggerCondition.m_falses = 0; } } else { if (triggerCondition.m_falses < triggerCondition.m_triggerData.m_triggerHoldoff) { condition = true; triggerCondition.m_falses++; } else { triggerCondition.m_trues = 0; } } 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("ScopeVis::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; }; GLScopeInterface* m_glScope; SpectrumVis *m_spectrumVis; bool m_ssbSpectrum; GLScopeSettings m_settings; MessageQueue m_inputMessageQueue; uint32_t m_preTriggerDelay; //!< Pre-trigger delay in number of samples uint32_t m_livePreTriggerDelay; //!< Pre-trigger delay in number of samples in live mode std::vector m_triggerConditions; //!< Chain of triggers uint32_t m_currentTriggerIndex; //!< Index of current index in the chain uint32_t 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 uint32_t m_nbStreams; uint32_t m_traceChunkSize; //!< Trace length unit size in number of samples uint32_t m_traceSize; //!< Size of traces in number of samples uint32_t m_liveTraceSize; //!< Size of traces in number of samples in live mode int m_nbSamples; //!< Number of samples yet to process in one complex trace uint32_t m_timeBase; //!< Trace display time divisor uint32_t m_timeOfsProMill; //!< Start trace shift in 1/1000 trace size bool m_traceStart; //!< Trace is at start point int m_triggerLocation; //!< Trigger location from end point int m_sampleRate; //!< Actual sample rate being used int m_liveSampleRate; //!< Sample rate in live mode TraceBackDiscreteMemory m_traceDiscreteMemory; //!< Complex trace memory ConvertBuffers m_convertBuffers; //!< Sample to Complex conversions bool m_freeRun; //!< True if free running (trigger globally disabled) int m_maxTraceDelay; //!< Maximum trace delay TriggerComparator m_triggerComparator; //!< Compares sample level to trigger level QRecursiveMutex m_mutex; Real m_projectorCache[(int) Projector::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) void applySettings(const GLScopeSettings& settings, bool force = false); void addTrace(const GLScopeSettings::TraceData& traceData); void changeTrace(const GLScopeSettings::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 GLScopeSettings::TriggerData& triggerData); void changeTrigger(const GLScopeSettings::TriggerData& triggerData, uint32_t triggerIndex); void removeTrigger(uint32_t triggerIndex); void moveTrigger(uint32_t triggerIndex, bool upElseDown); void focusOnTrigger(uint32_t triggerIndex); /** * 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 a sample trace which length is at most the trace length (m_traceSize) */ void processTrace(const std::vector& vbegin, int length, int& triggerPointToEnd); /** * process a trace in memory at current trace index in memory */ void processMemoryTrace(); /** * 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 */ int processTraces(const std::vector& vbegin, int length, bool traceBack = false); /** * 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(); /** * Set the actual sample rate */ void setSampleRate(int sampleRate); /** * Set the traces size */ void setTraceSize(uint32_t traceSize, bool emitSignal = false); /** * Set the pre trigger delay */ void setPreTriggerDelay(uint32_t preTriggerDelay, bool emitSignal = false); private slots: void handleInputMessages(); }; #endif /* SDRBASE_DSP_SCOPEVISNG_H_ */