From 2f5cbc80e9494728906eee7226c5faa3ed19042a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sun, 30 Oct 2022 00:20:57 +0200 Subject: [PATCH] Added run/stop button --- .../SpectrumAnalyzer/spectrumanalyzer.cpp | 169 +++++++++---- .../SpectrumAnalyzer/spectrumanalyzer.h | 7 + .../LibreVNA-GUI/Traces/traceplot.cpp | 1 + .../LibreVNA-GUI/Traces/traceplot.h | 1 + .../PC_Application/LibreVNA-GUI/VNA/vna.cpp | 224 +++++++++++------- .../PC_Application/LibreVNA-GUI/VNA/vna.h | 7 +- 6 files changed, 275 insertions(+), 134 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp index a67d64f..8e32edf 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -73,6 +73,31 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) // Sweep toolbar auto tb_sweep = new QToolBar("Sweep"); + auto bRun = new QPushButton("Run/Stop"); + bRun->setToolTip("Pause/continue sweep"); + bRun->setCheckable(true); + running = true; + connect(bRun, &QPushButton::toggled, [=](){ + if(bRun->isChecked()) { + Run(); + } else { + Stop(); + } + }); + connect(this, &SpectrumAnalyzer::sweepStopped, [=](){ + bRun->blockSignals(true); + bRun->setChecked(false); + bRun->setIcon(bRun->style()->standardIcon(QStyle::SP_MediaPause)); + bRun->blockSignals(false); + }); + connect(this, &SpectrumAnalyzer::sweepStarted, [=](){ + bRun->blockSignals(true); + bRun->setChecked(true); + bRun->setIcon(bRun->style()->standardIcon(QStyle::SP_MediaPlay)); + bRun->blockSignals(false); + }); + tb_sweep->addWidget(bRun); + auto bSingle = new QPushButton("Single"); bSingle->setToolTip("Single sweep"); bSingle->setCheckable(true); @@ -311,6 +336,11 @@ void SpectrumAnalyzer::initializeDevice() SettingsChanged(); } +void SpectrumAnalyzer::deviceDisconnected() +{ + emit sweepStopped(); +} + nlohmann::json SpectrumAnalyzer::toJSON() { nlohmann::json j; @@ -447,11 +477,7 @@ void SpectrumAnalyzer::NewDatapoint(VirtualDevice::SAMeasurement m) } if(singleSweep && average.getLevel() == averages) { - changingSettings = true; - // single sweep finished - window->getDevice()->setIdle([=](bool){ - changingSettings = false; - }); + Stop(); } if(m.pointNum >= settings.points) { @@ -517,58 +543,77 @@ void SpectrumAnalyzer::NewDatapoint(VirtualDevice::SAMeasurement m) void SpectrumAnalyzer::SettingsChanged() { - changingSettings = true; - if(settings.freqStop - settings.freqStart >= 1000 || settings.freqStop - settings.freqStart <= 0) { - settings.points = 1001; - } else { - settings.points = settings.freqStop - settings.freqStart + 1; - } - - if(settings.trackingGenerator && settings.freqStop >= 25000000) { - // Check point spacing. - // The highband PLL used as the tracking generator is not able to reach every frequency exactly. This - // could lead to sharp drops in the spectrum at certain frequencies. If the span is wide enough with - // respect to the point number, it is ensured that every displayed point has at least one sample with - // a reachable PLL frequency in it. Display a warning message if this is not the case with the current - // settings. - auto pointSpacing = (settings.freqStop - settings.freqStart) / (settings.points - 1); - // The frequency resolution of the PLL is frequency dependent (due to PLL divider). - // This code assumes some knowledge of the actual hardware and probably should be moved - // onto the device at some point - double minSpacing = 25000; - auto stop = settings.freqStop; - while(stop <= 3000000000) { - minSpacing /= 2; - stop *= 2; + if(running) { + changingSettings = true; + if(settings.freqStop - settings.freqStart >= 1000 || settings.freqStop - settings.freqStart <= 0) { + settings.points = 1001; + } else { + settings.points = settings.freqStop - settings.freqStart + 1; } - if(pointSpacing < minSpacing) { - auto requiredMinSpan = minSpacing * (settings.points - 1); - auto message = QString() + "Due to PLL limitations, the tracking generator can not reach every frequency exactly. " - "With your current span, this could result in the signal not being detected at some bands. A minimum" - " span of " + Unit::ToString(requiredMinSpan, "Hz", " kMG") + " is recommended at this stop frequency."; - InformationBox::ShowMessage("Warning", message, "TrackingGeneratorSpanTooSmallWarning"); - } - } - if(normalize.active) { - // check if normalization is still valid - if(normalize.f_start != settings.freqStart || normalize.f_stop != settings.freqStop || normalize.points != settings.points) { - // normalization was taken at different settings, disable - EnableNormalization(false); - InformationBox::ShowMessage("Information", "Normalization was disabled because the span has been changed"); + if(settings.trackingGenerator && settings.freqStop >= 25000000) { + // Check point spacing. + // The highband PLL used as the tracking generator is not able to reach every frequency exactly. This + // could lead to sharp drops in the spectrum at certain frequencies. If the span is wide enough with + // respect to the point number, it is ensured that every displayed point has at least one sample with + // a reachable PLL frequency in it. Display a warning message if this is not the case with the current + // settings. + auto pointSpacing = (settings.freqStop - settings.freqStart) / (settings.points - 1); + // The frequency resolution of the PLL is frequency dependent (due to PLL divider). + // This code assumes some knowledge of the actual hardware and probably should be moved + // onto the device at some point + double minSpacing = 25000; + auto stop = settings.freqStop; + while(stop <= 3000000000) { + minSpacing /= 2; + stop *= 2; + } + if(pointSpacing < minSpacing) { + auto requiredMinSpan = minSpacing * (settings.points - 1); + auto message = QString() + "Due to PLL limitations, the tracking generator can not reach every frequency exactly. " + "With your current span, this could result in the signal not being detected at some bands. A minimum" + " span of " + Unit::ToString(requiredMinSpan, "Hz", " kMG") + " is recommended at this stop frequency."; + InformationBox::ShowMessage("Warning", message, "TrackingGeneratorSpanTooSmallWarning"); + } } - } - if(window->getDevice() && isActive) { - window->getDevice()->setSA(settings, [=](bool){ - // device received command + if(normalize.active) { + // check if normalization is still valid + if(normalize.f_start != settings.freqStart || normalize.f_stop != settings.freqStop || normalize.points != settings.points) { + // normalization was taken at different settings, disable + EnableNormalization(false); + InformationBox::ShowMessage("Information", "Normalization was disabled because the span has been changed"); + } + } + + if(window->getDevice() && isActive) { + window->getDevice()->setSA(settings, [=](bool){ + // device received command + changingSettings = false; + }); + emit sweepStarted(); + } else { + // no device, unable to start sweep + emit sweepStopped(); changingSettings = false; - }); + } + average.reset(settings.points); + UpdateAverageCount(); + traceModel.clearLiveData(); + emit traceModel.SpanChanged(settings.freqStart, settings.freqStop); + } else { + if(window->getDevice()) { + changingSettings = true; + // single sweep finished + window->getDevice()->setIdle([=](bool){ + emit sweepStopped(); + changingSettings = false; + }); + } else { + emit sweepStopped(); + changingSettings = false; + } } - average.reset(settings.points); - UpdateAverageCount(); - traceModel.clearLiveData(); - emit traceModel.SpanChanged(settings.freqStart, settings.freqStop); } void SpectrumAnalyzer::SetStartFreq(double freq) @@ -840,6 +885,18 @@ void SpectrumAnalyzer::SetNormalizationLevel(double level) emit NormalizationLevelChanged(level); } +void SpectrumAnalyzer::Run() +{ + running = true; + SettingsChanged(); +} + +void SpectrumAnalyzer::Stop() +{ + running = false; + SettingsChanged(); +} + void SpectrumAnalyzer::SetupSCPI() { auto scpi_freq = new SCPINode("FREQuency"); @@ -1010,6 +1067,16 @@ void SpectrumAnalyzer::SetupSCPI() }, [=](QStringList) -> QString { return singleSweep ? SCPI::getResultName(SCPI::Result::True): SCPI::getResultName(SCPI::Result::False); })); + scpi_acq->add(new SCPICommand("RUN", [=](QStringList) -> QString { + Run(); + return SCPI::getResultName(SCPI::Result::Empty); + }, [=](QStringList) -> QString { + return running ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); + })); + scpi_acq->add(new SCPICommand("STOP", [=](QStringList) -> QString { + Stop(); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); auto scpi_tg = new SCPINode("TRACKing"); SCPINode::add(scpi_tg); scpi_tg->add(new SCPICommand("ENable", [=](QStringList params) -> QString { diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.h index 5729476..9336689 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.h @@ -21,6 +21,7 @@ public: void deactivate() override; void initializeDevice() override; + void deviceDisconnected() override; virtual Type getType() override { return Type::SA;} @@ -70,6 +71,9 @@ private slots: void ClearNormalization(); void SetNormalizationLevel(double level); + void Run(); + void Stop(); + private: void SetupSCPI(); void UpdateAverageCount(); @@ -84,6 +88,7 @@ private: bool changingSettings; unsigned int averages; bool singleSweep; + bool running; double firstPointTime; // timestamp of the first point in the sweep, only use when zerospan is used TraceModel traceModel; TraceWidget *traceWidget; @@ -127,6 +132,8 @@ signals: void NormalizationLevelChanged(double level); void averagingChanged(unsigned int averages); + void sweepStopped(); + void sweepStarted(); }; #endif // VNA_H diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp index 50890c5..8e2b26e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.cpp @@ -293,6 +293,7 @@ void TracePlot::paintEvent(QPaintEvent *event) p.setWindow(0, 0, w, h); draw(p); + replotTimer.start(MaxUpdateInterval); } void TracePlot::finishContextMenu() diff --git a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.h b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.h index 6ed5df3..8e0665c 100644 --- a/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.h +++ b/Software/PC_Application/LibreVNA-GUI/Traces/traceplot.h @@ -49,6 +49,7 @@ signals: protected: static constexpr int MinUpdateInterval = 100; + static constexpr int MaxUpdateInterval = 2000; // need to be called in derived class constructor void initializeTraceInfo(); std::vector activeTraces(); diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp index bb47cef..3dd4fac 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp @@ -50,6 +50,7 @@ #include #include #include +#include VNA::VNA(AppWindow *window, QString name) : Mode(window, name, "VNA"), @@ -217,11 +218,30 @@ VNA::VNA(AppWindow *window, QString name) std::vector frequencySweepActions; std::vector powerSweepActions; - tb_sweep->addWidget(new QLabel("Sweep type:")); - auto cbSweepType = new QComboBox(); - cbSweepType->addItem("Frequency"); - cbSweepType->addItem("Power"); - tb_sweep->addWidget(cbSweepType); + auto bRun = new QPushButton("Run/Stop"); + bRun->setToolTip("Pause/continue sweep"); + bRun->setCheckable(true); + running = true; + connect(bRun, &QPushButton::toggled, [=](){ + if(bRun->isChecked()) { + Run(); + } else { + Stop(); + } + }); + connect(this, &VNA::sweepStopped, [=](){ + bRun->blockSignals(true); + bRun->setChecked(false); + bRun->setIcon(bRun->style()->standardIcon(QStyle::SP_MediaPause)); + bRun->blockSignals(false); + }); + connect(this, &VNA::sweepStarted, [=](){ + bRun->blockSignals(true); + bRun->setChecked(true); + bRun->setIcon(bRun->style()->standardIcon(QStyle::SP_MediaPlay)); + bRun->blockSignals(false); + }); + tb_sweep->addWidget(bRun); auto bSingle = new QPushButton("Single"); bSingle->setToolTip("Single sweep"); @@ -230,6 +250,12 @@ VNA::VNA(AppWindow *window, QString name) connect(this, &VNA::singleSweepChanged, bSingle, &QPushButton::setChecked); tb_sweep->addWidget(bSingle); + tb_sweep->addWidget(new QLabel("Sweep type:")); + auto cbSweepType = new QComboBox(); + cbSweepType->addItem("Frequency"); + cbSweepType->addItem("Power"); + tb_sweep->addWidget(cbSweepType); + auto eStart = new SIUnitEdit("Hz", " kMG", 6); // calculate width required with expected string length auto width = QFontMetrics(eStart->font()).width("3.00000GHz") + 15; @@ -663,6 +689,7 @@ void VNA::initializeDevice() void VNA::deviceDisconnected() { defaultCalMenu->setEnabled(false); + emit sweepStopped(); } void VNA::shutdown() @@ -770,11 +797,7 @@ void VNA::NewDatapoint(VirtualDevice::VNAMeasurement m) } if(singleSweep && average.getLevel() == averages) { - changingSettings = true; - // single sweep finished - window->getDevice()->setIdle([=](bool){ - changingSettings = false; - }); + Stop(); } auto m_avg = m; @@ -874,73 +897,92 @@ void VNA::UpdateAverageCount() void VNA::SettingsChanged(bool resetTraces, std::function cb) { - if (resetTraces) { - settings.activeSegment = 0; - } - changingSettings = true; - // assemble VNA protocol settings - VirtualDevice::VNASettings s = {}; - s.IFBW = settings.bandwidth; - if(Preferences::getInstance().Acquisition.alwaysExciteAllPorts) { - for(unsigned int i=0;igetDevice()).ports;i++) { - s.excitedPorts.push_back(i); + if(running) { + if (resetTraces) { + settings.activeSegment = 0; + } + changingSettings = true; + // assemble VNA protocol settings + VirtualDevice::VNASettings s = {}; + s.IFBW = settings.bandwidth; + if(Preferences::getInstance().Acquisition.alwaysExciteAllPorts) { + for(unsigned int i=0;igetDevice()).ports;i++) { + s.excitedPorts.push_back(i); + } + } else { + for(unsigned int i=0;igetDevice()).ports;i++) { + if(traceModel.PortExcitationRequired(i)) + s.excitedPorts.push_back(i); + } + } + settings.excitedPorts = s.excitedPorts; + + double start = settings.sweepType == SweepType::Frequency ? settings.Freq.start : settings.Power.start; + double stop = settings.sweepType == SweepType::Frequency ? settings.Freq.stop : settings.Power.stop; + int npoints = settings.npoints; + emit traceModel.SpanChanged(start, stop); + if (settings.segments > 1) { + // more than one segment, adjust start/stop + npoints = ceil((double) settings.npoints / settings.segments); + unsigned int segmentStartPoint = npoints * settings.activeSegment; + unsigned int segmentStopPoint = segmentStartPoint + npoints - 1; + if(segmentStopPoint >= settings.npoints) { + segmentStopPoint = settings.npoints - 1; + npoints = settings.npoints - segmentStartPoint; + } + auto seg_start = Util::Scale(segmentStartPoint, 0, settings.npoints - 1, start, stop); + auto seg_stop = Util::Scale(segmentStopPoint, 0, settings.npoints - 1, start, stop); + start = seg_start; + stop = seg_stop; + } + + if(settings.sweepType == SweepType::Frequency) { + s.freqStart = start; + s.freqStop = stop; + s.points = npoints; + s.dBmStart = settings.Freq.excitation_power; + s.dBmStop = settings.Freq.excitation_power; + s.logSweep = settings.Freq.logSweep; + } else if(settings.sweepType == SweepType::Power) { + s.freqStart = settings.Power.frequency; + s.freqStop = settings.Power.frequency; + s.points = npoints; + s.dBmStart = start; + s.dBmStop = stop; + s.logSweep = false; + } + if(window->getDevice() && isActive) { + window->getDevice()->setVNA(s, [=](bool res){ + // device received command, reset traces now + if (resetTraces) { + average.reset(settings.npoints); + traceModel.clearLiveData(); + UpdateAverageCount(); + UpdateCalWidget(); + } + if(cb) { + cb(res); + } + changingSettings = false; + }); + emit sweepStarted(); + } else { + // no device, unable to start sweep + emit sweepStopped(); + changingSettings = false; } } else { - for(unsigned int i=0;igetDevice()).ports;i++) { - if(traceModel.PortExcitationRequired(i)) - s.excitedPorts.push_back(i); - } - } - settings.excitedPorts = s.excitedPorts; - - double start = settings.sweepType == SweepType::Frequency ? settings.Freq.start : settings.Power.start; - double stop = settings.sweepType == SweepType::Frequency ? settings.Freq.stop : settings.Power.stop; - int npoints = settings.npoints; - emit traceModel.SpanChanged(start, stop); - if (settings.segments > 1) { - // more than one segment, adjust start/stop - npoints = ceil((double) settings.npoints / settings.segments); - unsigned int segmentStartPoint = npoints * settings.activeSegment; - unsigned int segmentStopPoint = segmentStartPoint + npoints - 1; - if(segmentStopPoint >= settings.npoints) { - segmentStopPoint = settings.npoints - 1; - npoints = settings.npoints - segmentStartPoint; - } - auto seg_start = Util::Scale(segmentStartPoint, 0, settings.npoints - 1, start, stop); - auto seg_stop = Util::Scale(segmentStopPoint, 0, settings.npoints - 1, start, stop); - start = seg_start; - stop = seg_stop; - } - - if(settings.sweepType == SweepType::Frequency) { - s.freqStart = start; - s.freqStop = stop; - s.points = npoints; - s.dBmStart = settings.Freq.excitation_power; - s.dBmStop = settings.Freq.excitation_power; - s.logSweep = settings.Freq.logSweep; - } else if(settings.sweepType == SweepType::Power) { - s.freqStart = settings.Power.frequency; - s.freqStop = settings.Power.frequency; - s.points = npoints; - s.dBmStart = start; - s.dBmStop = stop; - s.logSweep = false; - } - if(window->getDevice() && isActive) { - window->getDevice()->setVNA(s, [=](bool res){ - // device received command, reset traces now - if (resetTraces) { - average.reset(settings.npoints); - traceModel.clearLiveData(); - UpdateAverageCount(); - UpdateCalWidget(); - } - if(cb) { - cb(res); - } + if(window->getDevice()) { + changingSettings = true; + // single sweep finished + window->getDevice()->setIdle([=](bool){ + emit sweepStopped(); + changingSettings = false; + }); + } else { + emit sweepStopped(); changingSettings = false; - }); + } } } @@ -1198,7 +1240,8 @@ void VNA::StartCalibrationMeasurements(std::set m return; } // Stop sweep - StopSweep(); + running = false; + SettingsChanged(); calMeasurements = m; // Delete any already captured data of this measurement cal.clearMeasurements(m); @@ -1383,6 +1426,16 @@ void VNA::SetupSCPI() }, [=](QStringList) -> QString { return singleSweep ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); })); + scpi_acq->add(new SCPICommand("RUN", [=](QStringList) -> QString { + Run(); + return SCPI::getResultName(SCPI::Result::Empty); + }, [=](QStringList) -> QString { + return running ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); + })); + scpi_acq->add(new SCPICommand("STOP", [=](QStringList) -> QString { + Stop(); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); auto scpi_stim = new SCPINode("STIMulus"); SCPINode::add(scpi_stim); scpi_stim->add(new SCPICommand("LVL", [=](QStringList params) -> QString { @@ -1482,13 +1535,6 @@ void VNA::StoreSweepSettings() s.setValue("SweepAveraging", averages); } -void VNA::StopSweep() -{ - if(window->getDevice()) { - window->getDevice()->setIdle(); - } -} - void VNA::UpdateCalWidget() { calLabel->setStyleSheet(getCalStyle()); @@ -1646,6 +1692,20 @@ void VNA::SetSingleSweep(bool single) singleSweep = single; emit singleSweepChanged(single); } + if(single) { + SettingsChanged(); + } +} + +void VNA::Run() +{ + running = true; + SettingsChanged(); +} + +void VNA::Stop() +{ + running = false; SettingsChanged(); } diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/vna.h b/Software/PC_Application/LibreVNA-GUI/VNA/vna.h index 45f493f..72ccdd7 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/vna.h +++ b/Software/PC_Application/LibreVNA-GUI/VNA/vna.h @@ -120,7 +120,6 @@ private: void ConstrainAndUpdateFrequencies(); void LoadSweepSettings(); void StoreSweepSettings(); - void StopSweep(); void UpdateCalWidget(); void createDefaultTracesAndGraphs(int ports); @@ -128,6 +127,8 @@ private slots: void EnableDeembedding(bool enable); void UpdateStatusbar(); void SetSingleSweep(bool single); + void Run(); + void Stop(); private: Settings settings; unsigned int averages; @@ -136,6 +137,7 @@ private: MarkerModel *markerModel; Averaging average; bool singleSweep; + bool running; // Calibration Calibration cal; @@ -182,6 +184,9 @@ signals: void startPowerChanged(double level); void stopPowerChanged(double level); void powerSweepFrequencyChanged(double freq); + + void sweepStopped(); + void sweepStarted(); }; #endif // VNA_H