Added run/stop button

This commit is contained in:
Jan Käberich 2022-10-30 00:20:57 +02:00
parent dfd1abeea5
commit 2f5cbc80e9
6 changed files with 275 additions and 134 deletions

View file

@ -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 {

View file

@ -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

View file

@ -293,6 +293,7 @@ void TracePlot::paintEvent(QPaintEvent *event)
p.setWindow(0, 0, w, h);
draw(p);
replotTimer.start(MaxUpdateInterval);
}
void TracePlot::finishContextMenu()

View file

@ -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<Trace*> activeTraces();

View file

@ -50,6 +50,7 @@
#include <QActionGroup>
#include <QErrorMessage>
#include <QDebug>
#include <QStyle>
VNA::VNA(AppWindow *window, QString name)
: Mode(window, name, "VNA"),
@ -217,11 +218,30 @@ VNA::VNA(AppWindow *window, QString name)
std::vector<QAction*> frequencySweepActions;
std::vector<QAction*> 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<void (bool)> 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;i<VirtualDevice::getInfo(window->getDevice()).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;i<VirtualDevice::getInfo(window->getDevice()).ports;i++) {
s.excitedPorts.push_back(i);
}
} else {
for(unsigned int i=0;i<VirtualDevice::getInfo(window->getDevice()).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<double>(segmentStartPoint, 0, settings.npoints - 1, start, stop);
auto seg_stop = Util::Scale<double>(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;i<VirtualDevice::getInfo(window->getDevice()).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<double>(segmentStartPoint, 0, settings.npoints - 1, start, stop);
auto seg_stop = Util::Scale<double>(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<CalibrationMeasurement::Base*> 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();
}

View file

@ -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