diff --git a/Readme.md b/Readme.md index ae0f70c38..3b05ec01c 100644 --- a/Readme.md +++ b/Readme.md @@ -116,13 +116,13 @@ Done since the fork - Enhanced channel analyzer: enhanced scope and spectrum displays as mentioned above, make the spectrum display synchronous to scope (hence triggerable a la E4406A). - Sort channel plugins by delta frequency and type before saving to preset - Implemented scope pre-trigger delay + - Implemented variable scope memory depth + - Implemented trigger delay ===== To Do ===== - - Variable scope memory depth - - Trigger delay - Enhance presets management (Edit, Move, Import/Export from/to human readable format like JSON) - Level calibration - Enhance WFM (stereo, RDS?) diff --git a/include-gpl/dsp/scopevis.h b/include-gpl/dsp/scopevis.h index b5cfd652e..04804b0e9 100644 --- a/include-gpl/dsp/scopevis.h +++ b/include-gpl/dsp/scopevis.h @@ -24,7 +24,13 @@ public: ScopeVis(GLScope* glScope = NULL); - void configure(MessageQueue* msgQueue, TriggerChannel triggerChannel, Real triggerLevel, bool triggerPositiveEdge, uint triggerPre, uint traceSize); + void configure(MessageQueue* msgQueue, + TriggerChannel triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + uint triggerPre, + uint triggerDelay, + uint traceSize); void setOneShot(bool oneShot); void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); @@ -46,11 +52,17 @@ private: Real getTriggerLevel() const { return m_triggerLevel; } Real getTriggerPositiveEdge() const { return m_triggerPositiveEdge; } uint getTriggerPre() const { return m_triggerPre; } + uint getTriggerDelay() const { return m_triggerDelay; } uint getTraceSize() const { return m_traceSize; } - static MsgConfigureScopeVis* create(int triggerChannel, Real triggerLevel, bool triggerPositiveEdge, uint triggerPre, uint traceSize) + static MsgConfigureScopeVis* create(int triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + uint triggerPre, + uint triggerDelay, + uint traceSize) { - return new MsgConfigureScopeVis(triggerChannel, triggerLevel, triggerPositiveEdge, triggerPre, traceSize); + return new MsgConfigureScopeVis(triggerChannel, triggerLevel, triggerPositiveEdge, triggerPre, triggerDelay, traceSize); } private: @@ -58,22 +70,44 @@ private: Real m_triggerLevel; bool m_triggerPositiveEdge; uint m_triggerPre; + uint m_triggerDelay; uint m_traceSize; - MsgConfigureScopeVis(int triggerChannel, Real triggerLevel, bool triggerPositiveEdge, uint triggerPre, uint traceSize) : + MsgConfigureScopeVis(int triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + uint triggerPre, + uint triggerDelay, + uint traceSize) : Message(), m_triggerChannel(triggerChannel), m_triggerLevel(triggerLevel), m_triggerPositiveEdge(triggerPositiveEdge), m_triggerPre(triggerPre), + m_triggerDelay(triggerDelay), m_traceSize(traceSize) { } }; + /** + * TriggerState: + * + * send a Trigger condition +--------------------+ + * dummy trace - Immediate m_triggerOneShot | | + * Config -------------> Untriggered ----------------------------------> Triggered ----------------> WaitForReset | + * ^ ^ | ^ | | ^ | + * | | | - Delayed Delay expired | | | | setOneShot(true)| + * | | +---------------------> Delay ----------------+ | | +-----------------+ + * | | !m_triggerOneShot | | + * | +--------------------------------------------------+ setOneShot(false) | + * +-------------------------------------------------------------------------------+ + */ enum TriggerState { - Untriggered, - Triggered, - WaitForReset + Untriggered, //!< Search for trigger + Config, //!< New configuration has just been received + Triggered, //!< Trigger was kicked off + WaitForReset, //!< Wait for release from GUI + Delay //!< Trigger delay engaged }; GLScope* m_glScope; @@ -85,9 +119,11 @@ private: TriggerChannel m_triggerChannel; Real m_triggerLevel; bool m_triggerPositiveEdge; - uint m_triggerPre; //!< Pre-trigger delay in number of samples + uint m_triggerPre; //!< Pre-trigger delay in number of samples bool m_triggerOneShot; bool m_armed; + uint m_triggerDelay; //!< Trigger delay in number of trace sizes + uint m_triggerDelayCount; //!< trace sizes delay counter int m_sampleRate; SampleVector::const_iterator m_triggerPoint; diff --git a/include-gpl/gui/glscopegui.h b/include-gpl/gui/glscopegui.h index 5d5afbc4a..8ba9b0aef 100644 --- a/include-gpl/gui/glscopegui.h +++ b/include-gpl/gui/glscopegui.h @@ -52,6 +52,7 @@ private: qint32 m_triggerLevel; // percent bool m_triggerPositiveEdge; qint32 m_triggerPre; + qint32 m_triggerDelay; qint32 m_traceLenMult; static const qreal amps[11]; @@ -65,6 +66,7 @@ private: void setAmpOfsDisplay(); void setTrigLevelDisplay(); void setTrigPreDisplay(); + void setTrigDelayDisplay(); private slots: void on_amp_valueChanged(int value); @@ -78,6 +80,7 @@ private slots: void on_gridIntensity_valueChanged(int index); void on_traceIntensity_valueChanged(int index); void on_trigPre_valueChanged(int value); + void on_trigDelay_valueChanged(int value); void on_horizView_clicked(); void on_vertView_clicked(); diff --git a/sdrbase/dsp/scopevis.cpp b/sdrbase/dsp/scopevis.cpp index a08f5f7c8..3a8447cf1 100644 --- a/sdrbase/dsp/scopevis.cpp +++ b/sdrbase/dsp/scopevis.cpp @@ -20,6 +20,8 @@ ScopeVis::ScopeVis(GLScope* glScope) : m_triggerLevel(0.0), m_triggerPositiveEdge(true), m_triggerPre(0), + m_triggerDelay(0), + m_triggerDelayCount(0), m_triggerOneShot(false), m_armed(false), m_sampleRate(0) @@ -29,9 +31,15 @@ ScopeVis::ScopeVis(GLScope* glScope) : m_traceback.resize(20*m_traceChunkSize); } -void ScopeVis::configure(MessageQueue* msgQueue, TriggerChannel triggerChannel, Real triggerLevel, bool triggerPositiveEdge, uint triggerPre, uint traceSize) +void ScopeVis::configure(MessageQueue* msgQueue, + TriggerChannel triggerChannel, + Real triggerLevel, + bool triggerPositiveEdge, + uint triggerPre, + uint triggerDelay, + uint traceSize) { - Message* cmd = MsgConfigureScopeVis::create(triggerChannel, triggerLevel, triggerPositiveEdge, triggerPre, traceSize); + Message* cmd = MsgConfigureScopeVis::create(triggerChannel, triggerLevel, triggerPositiveEdge, triggerPre, triggerDelay, traceSize); cmd->submit(msgQueue, this); } @@ -77,6 +85,30 @@ void ScopeVis::feed(SampleVector::const_iterator begin, SampleVector::const_iter { break; } + if(m_triggerState == Config) + { + m_glScope->newTrace(m_trace, m_sampleRate); // send a dummy trace + m_triggerState = Untriggered; + } + if(m_triggerState == Delay) + { + int count = end - begin; + if (count > (int)(m_trace.size() - m_fill)) + { + count = m_trace.size() - m_fill; + } + begin += count; + m_fill += count; + if(m_fill >= m_trace.size()) + { + m_fill = 0; + m_triggerDelayCount--; + if (m_triggerDelayCount == 0) + { + m_triggerState = Triggered; + } + } + } if(m_triggerState == Untriggered) { while(begin < end) @@ -88,14 +120,23 @@ void ScopeVis::feed(SampleVector::const_iterator begin, SampleVector::const_iter { if (m_armed) { - m_triggerState = Triggered; m_armed = false; - m_triggerPoint = begin; - // fill beginning of m_trace with delayed samples from the trace memory FIFO. Increment m_fill accordingly. - if (m_triggerPre) { // do this process only if there is a pre-trigger delay - std::copy(m_traceback.end() - m_triggerPre - 1, m_traceback.end() - 1, m_trace.begin()); - m_fill = m_triggerPre; // Increment m_fill accordingly (from 0). - } + if (m_triggerDelay > 0) + { + m_triggerDelayCount = m_triggerDelay; + m_fill = 0; + m_triggerState = Delay; + } + else + { + m_triggerState = Triggered; + m_triggerPoint = begin; + // fill beginning of m_trace with delayed samples from the trace memory FIFO. Increment m_fill accordingly. + if (m_triggerPre) { // do this process only if there is a pre-trigger delay + std::copy(m_traceback.end() - m_triggerPre - 1, m_traceback.end() - 1, m_trace.begin()); + m_fill = m_triggerPre; // Increment m_fill accordingly (from 0). + } + } break; } } @@ -154,7 +195,7 @@ bool ScopeVis::handleMessageKeep(Message* message) } else if(MsgConfigureScopeVis::match(message)) { MsgConfigureScopeVis* conf = (MsgConfigureScopeVis*)message; m_tracebackCount = 0; - m_triggerState = Untriggered; + m_triggerState = Config; m_triggerChannel = (TriggerChannel) conf->getTriggerChannel(); m_triggerLevel = conf->getTriggerLevel(); m_triggerPositiveEdge = conf->getTriggerPositiveEdge(); @@ -162,6 +203,7 @@ bool ScopeVis::handleMessageKeep(Message* message) if (m_triggerPre >= m_traceback.size()) { m_triggerPre = m_traceback.size() - 1; // top sample in FIFO is always the triggering one (pre-trigger delay = 0) } + m_triggerDelay = conf->getTriggerDelay(); uint newSize = conf->getTraceSize(); if (newSize != m_trace.size()) { m_trace.resize(newSize); @@ -174,6 +216,7 @@ bool ScopeVis::handleMessageKeep(Message* message) << " m_triggerLevel: " << m_triggerLevel << " m_triggerPositiveEdge: " << (m_triggerPositiveEdge ? "edge+" : "edge-") << " m_preTrigger: " << m_triggerPre + << " m_triggerDelay: " << m_triggerDelay << " m_traceSize: " << m_trace.size() << std::endl; return true; /* diff --git a/sdrbase/gui/glscopegui.cpp b/sdrbase/gui/glscopegui.cpp index e787cd676..7423d0243 100644 --- a/sdrbase/gui/glscopegui.cpp +++ b/sdrbase/gui/glscopegui.cpp @@ -29,6 +29,7 @@ GLScopeGUI::GLScopeGUI(QWidget* parent) : m_triggerLevel(0.0), m_triggerPositiveEdge(true), m_triggerPre(0), + m_triggerDelay(0), m_traceLenMult(20) { ui->setupUi(this); @@ -62,6 +63,12 @@ void GLScopeGUI::resetToDefaults() m_timeOffset = 0; m_amplification = 0; m_displayGridIntensity = 5; + m_triggerChannel = ScopeVis::TriggerFreeRun; + m_triggerLevel = 0.0; + m_triggerPositiveEdge = true; + m_triggerPre = 0; + m_triggerDelay = 0; + m_traceLenMult = 20; applySettings(); } @@ -83,6 +90,7 @@ QByteArray GLScopeGUI::serialize() const s.writeS32(12, m_displayTraceIntensity); s.writeS32(13, m_triggerPre); s.writeS32(14, m_traceLenMult); + s.writeS32(15, m_triggerDelay); return s.final(); } @@ -122,6 +130,9 @@ bool GLScopeGUI::deserialize(const QByteArray& data) d.readS32(14, &m_traceLenMult, 20); ui->traceLen->setValue(m_traceLenMult); setTraceLenDisplay(); + d.readS32(15, &m_triggerDelay, 0); + ui->trigDelay->setValue(m_triggerDelay); + setTrigDelayDisplay(); applySettings(); applyTriggerSettings(); return true; @@ -194,6 +205,7 @@ void GLScopeGUI::applyTriggerSettings() triggerLevel, m_triggerPositiveEdge, preTriggerSamples, + m_triggerDelay, m_traceLenMult * ScopeVis::m_traceChunkSize); } @@ -250,6 +262,7 @@ void GLScopeGUI::on_scope_traceSizeChanged(int) setTraceLenDisplay(); setTimeOfsDisplay(); setTrigPreDisplay(); + setTrigDelayDisplay(); applySettings(); applyTriggerSettings(); } @@ -261,6 +274,7 @@ void GLScopeGUI::on_scope_sampleRateChanged(int) setTraceLenDisplay(); setTimeOfsDisplay(); setTrigPreDisplay(); + setTrigDelayDisplay(); applySettings(); applyTriggerSettings(); } @@ -307,6 +321,32 @@ void GLScopeGUI::setTraceLenDisplay() else ui->traceLenText->setText(tr("%1\ns").arg(t * 1.0)); } +void GLScopeGUI::setTrigDelayDisplay() +{ + uint n_samples_delay = m_traceLenMult * ScopeVis::m_traceChunkSize * m_triggerDelay; + + if (n_samples_delay < 1000) { + ui->trigDelayText->setToolTip(tr("%1S").arg(n_samples_delay)); + } else if (n_samples_delay < 1000000) { + ui->trigDelayText->setToolTip(tr("%1kS").arg(n_samples_delay/1000.0)); + } else if (n_samples_delay < 1000000000) { + ui->trigDelayText->setToolTip(tr("%1MS").arg(n_samples_delay/1000000.0)); + } else { + ui->trigDelayText->setToolTip(tr("%1GS").arg(n_samples_delay/1000000000.0)); + } + + m_sampleRate = m_glScope->getSampleRate(); + qreal t = (n_samples_delay * 1.0 / m_sampleRate); + + if(t < 0.000001) + ui->trigDelayText->setText(tr("%1\nns").arg(t * 1000000000.0)); + else if(t < 0.001) + ui->trigDelayText->setText(tr("%1\nµs").arg(t * 1000000.0)); + else if(t < 1.0) + ui->trigDelayText->setText(tr("%1\nms").arg(t * 1000.0)); + else ui->trigDelayText->setText(tr("%1\ns").arg(t * 1.0)); +} + void GLScopeGUI::setTimeOfsDisplay() { qreal dt = m_glScope->getTraceSize() * (m_timeOffset/100.0) / m_sampleRate; @@ -364,7 +404,7 @@ void GLScopeGUI::on_timeOfs_valueChanged(int value) void GLScopeGUI::on_trigPre_valueChanged(int value) { - if ((value < 0) || (value > 99)) { + if ((value < 0) || (value > 100)) { return; } m_triggerPre = value; @@ -372,6 +412,16 @@ void GLScopeGUI::on_trigPre_valueChanged(int value) applyTriggerSettings(); } +void GLScopeGUI::on_trigDelay_valueChanged(int value) +{ + if ((value < 0) || (value > 100)) { + return; + } + m_triggerDelay = value; + setTrigDelayDisplay(); + applyTriggerSettings(); +} + void GLScopeGUI::on_dataMode_currentIndexChanged(int index) { m_displayData = index; diff --git a/sdrbase/gui/glscopegui.ui b/sdrbase/gui/glscopegui.ui index 5cce46ec3..9e5e88e55 100644 --- a/sdrbase/gui/glscopegui.ui +++ b/sdrbase/gui/glscopegui.ui @@ -752,6 +752,33 @@ + + + + Dly + + + + + + + 100 + + + 1 + + + Qt::Horizontal + + + + + + + 0 + + +