diff --git a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp index 8c0e7ae..957ac6c 100644 --- a/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/LibreVNA-GUI/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -333,6 +333,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) void SpectrumAnalyzer::deactivate() { + setOperationPending(false); StoreSweepSettings(); Mode::deactivate(); } @@ -503,6 +504,9 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m) } auto m_avg = average.process(m); + if(average.settled()) { + setOperationPending(false); + } if(settings.freqStart == settings.freqStop) { // keep track of first point time @@ -560,6 +564,7 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m) void SpectrumAnalyzer::SettingsChanged() { + setOperationPending(true); configurationTimer.start(100); ResetLiveTraces(); } @@ -703,6 +708,7 @@ void SpectrumAnalyzer::SetAveraging(unsigned int averages) average.setAverages(averages); emit averagingChanged(averages); UpdateAverageCount(); + setOperationPending(!average.settled()); } void SpectrumAnalyzer::SetTGEnabled(bool enabled) @@ -887,6 +893,7 @@ void SpectrumAnalyzer::ConfigureDevice() void SpectrumAnalyzer::ResetLiveTraces() { + setOperationPending(true); average.reset(DeviceDriver::SApoints()); traceModel.clearLiveData(); UpdateAverageCount(); diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp index e46eca0..13006f4 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/vna.cpp @@ -692,6 +692,7 @@ QString VNA::getCalToolTip() void VNA::deactivate() { + setOperationPending(false); StoreSweepSettings(); Mode::deactivate(); } @@ -901,6 +902,9 @@ void VNA::NewDatapoint(DeviceDriver::VNAMeasurement m) } m_avg = average.process(m_avg); + if(average.settled()) { + setOperationPending(false); + } if(calMeasuring) { if(average.currentSweep() == averages) { @@ -979,6 +983,7 @@ void VNA::UpdateAverageCount() void VNA::SettingsChanged(bool resetTraces, int delay) { + setOperationPending(true); configurationTimer.start(delay); changingSettings = true; configurationTimerResetTraces = resetTraces; @@ -1218,6 +1223,7 @@ void VNA::SetAveraging(unsigned int averages) average.setAverages(averages); emit averagingChanged(averages); UpdateAverageCount(); + setOperationPending(!average.settled()); } void VNA::ExcitationRequired() @@ -1317,7 +1323,6 @@ void VNA::SetupSCPI() if(params.size() >= 1) { if(params[0] == "FREQUENCY") { SetSweepType(SweepType::Frequency); - ResetLiveTraces(); return SCPI::getResultName(SCPI::Result::Empty); } else if(params[0] == "POWER") { SetSweepType(SweepType::Power); @@ -1378,7 +1383,6 @@ void VNA::SetupSCPI() scpi_freq->add(new SCPICommand("FULL", [=](QStringList params) -> QString { Q_UNUSED(params) SetFullSpan(); - ResetLiveTraces(); return SCPI::getResultName(SCPI::Result::Empty); }, nullptr)); scpi_freq->add(new SCPICommand("ZERO", [=](QStringList params) -> QString { @@ -1449,7 +1453,7 @@ void VNA::SetupSCPI() return QString::number(average.getLevel()); })); scpi_acq->add(new SCPICommand("FINished", nullptr, [=](QStringList) -> QString { - return average.getLevel() == averages ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); + return average.settled() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); })); scpi_acq->add(new SCPICommand("LIMit", nullptr, [=](QStringList) -> QString { return tiles->allLimitsPassing() ? "PASS" : "FAIL"; @@ -1855,6 +1859,7 @@ void VNA::ResetLiveTraces() traceModel.clearLiveData(); UpdateAverageCount(); UpdateCalWidget(); + setOperationPending(true); } bool VNA::LoadCalibration(QString filename) diff --git a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp index 79301a6..327147c 100644 --- a/Software/PC_Application/LibreVNA-GUI/appwindow.cpp +++ b/Software/PC_Application/LibreVNA-GUI/appwindow.cpp @@ -490,9 +490,6 @@ void AppWindow::SetupSCPI() SetInitialState(); return SCPI::getResultName(SCPI::Result::Empty); }, nullptr)); - scpi.add(new SCPICommand("*OPC", nullptr, [=](QStringList){ - return "1"; - })); auto scpi_dev = new SCPINode("DEVice"); scpi.add(scpi_dev); scpi_dev->add(new SCPICommand("DISConnect", [=](QStringList params) -> QString { diff --git a/Software/PC_Application/LibreVNA-GUI/averaging.cpp b/Software/PC_Application/LibreVNA-GUI/averaging.cpp index 341f72c..79cae68 100644 --- a/Software/PC_Application/LibreVNA-GUI/averaging.cpp +++ b/Software/PC_Application/LibreVNA-GUI/averaging.cpp @@ -84,6 +84,11 @@ unsigned int Averaging::currentSweep() } } +bool Averaging::settled() +{ + return getLevel() == averages; +} + Averaging::Mode Averaging::getMode() const { return mode; diff --git a/Software/PC_Application/LibreVNA-GUI/averaging.h b/Software/PC_Application/LibreVNA-GUI/averaging.h index ab2ad4c..e77d4cf 100644 --- a/Software/PC_Application/LibreVNA-GUI/averaging.h +++ b/Software/PC_Application/LibreVNA-GUI/averaging.h @@ -26,6 +26,8 @@ public: // Returns the number of the currently active sweep. Value is incremented whenever the the first point of the sweep is added // Returned values are in range 0 (when no data has been added yet) to averages unsigned int currentSweep(); + // Returns true if all required averages have been taken + bool settled(); Mode getMode() const; void setMode(const Mode &value); diff --git a/Software/PC_Application/LibreVNA-GUI/scpi.cpp b/Software/PC_Application/LibreVNA-GUI/scpi.cpp index 6b5a89b..15a7e89 100644 --- a/Software/PC_Application/LibreVNA-GUI/scpi.cpp +++ b/Software/PC_Application/LibreVNA-GUI/scpi.cpp @@ -5,6 +5,40 @@ SCPI::SCPI() : SCPINode("") { + WAIexecuting = false; + OPCQueryScheduled = false; + OCAS = false; + ESR = 0x00; + + add(new SCPICommand("*OPC", [=](QStringList){ + // OPC command + if(isOperationPending()) { + OCAS = true; + } + return SCPI::getResultName(SCPI::Result::Empty); + }, [=](QStringList) -> QString { + // OPC query + if(isOperationPending()) { + // operation pending + OPCQueryScheduled = true; + OCAS = true; + return SCPI::getResultName(SCPI::Result::Empty); + } else { + // no operation, can return immediately + OCAS = false; + setFlag(Flag::OPC); + return "1"; + } + })); + + add(new SCPICommand("*WAI", [=](QStringList){ + // WAI command + if(isOperationPending()) { + WAIexecuting = true; + } + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); + add(new SCPICommand("*LST", nullptr, [=](QStringList){ QString list; createCommandList("", list); @@ -95,23 +129,68 @@ QString SCPI::getResultName(SCPI::Result r) void SCPI::input(QString line) { - auto cmds = line.split(";"); - SCPINode *lastNode = this; - for(auto cmd : cmds) { - if(cmd.size() > 0) { - if(cmd[0] == ':' || cmd[0] == '*') { - // reset to root node - lastNode = this; + cmdQueue.append(line); + process(); +} + +void SCPI::process() +{ + while(!WAIexecuting && !cmdQueue.isEmpty()) { + auto cmd = cmdQueue.front(); + cmdQueue.pop_front(); + auto cmds = cmd.split(";"); + SCPINode *lastNode = this; + for(auto cmd : cmds) { + if(cmd.size() > 0) { + if(cmd[0] == ':' || cmd[0] == '*') { + // reset to root node + lastNode = this; + } + if(cmd[0] == ':') { + cmd.remove(0, 1); + } + auto response = lastNode->parse(cmd, lastNode); + emit output(response); } - if(cmd[0] == ':') { - cmd.remove(0, 1); - } - auto response = lastNode->parse(cmd, lastNode); - emit output(response); } } } +void SCPI::someOperationCompleted() +{ + if(!isOperationPending()) { + // all operations are complete + if(OCAS) { + OCAS = false; + setFlag(Flag::OPC); + if(OPCQueryScheduled) { + output("1"); + OPCQueryScheduled = false; + } + } + if(WAIexecuting) { + WAIexecuting = false; + // process any queued commands + process(); + } + } +} + +void SCPI::setFlag(Flag flag) +{ + ESR |= ((int) flag); +} + +void SCPI::clearFlag(Flag flag) +{ + ESR &= ~((int) flag); +} + +bool SCPI::getFlag(Flag flag) +{ + return ESR & (int) flag; +} + SCPINode::~SCPINode() { if(parent) { @@ -235,6 +314,36 @@ bool SCPINode::changeName(QString newname) return true; } +void SCPINode::setOperationPending(bool pending) +{ + if(operationPending != pending) { + operationPending = pending; + if(!operationPending) { + // operation completed, needs to perform check if all operations are complete + auto root = this; + while(root->parent) { + root = root->parent; + } + auto scpi = static_cast(root); + scpi->someOperationCompleted(); + } + } +} + +bool SCPINode::isOperationPending() +{ + if(operationPending) { + return true; + } + for(auto node : subnodes) { + if(node->isOperationPending()) { + return true; + } + } + // no node has any pending operations + return false; +} + bool SCPINode::nameCollision(QString name) { for(auto n : subnodes) { diff --git a/Software/PC_Application/LibreVNA-GUI/scpi.h b/Software/PC_Application/LibreVNA-GUI/scpi.h index 10634cf..5c5bb62 100644 --- a/Software/PC_Application/LibreVNA-GUI/scpi.h +++ b/Software/PC_Application/LibreVNA-GUI/scpi.h @@ -31,7 +31,7 @@ class SCPINode { friend class SCPI; public: SCPINode(QString name) : - name(name), parent(nullptr){} + name(name), parent(nullptr), operationPending(false){} virtual ~SCPINode(); bool add(SCPINode *node); @@ -44,6 +44,11 @@ public: bool changeName(QString newname); +protected: + void setOperationPending(bool pending); + + bool isOperationPending(); + private: QString parse(QString cmd, SCPINode* &lastNode); bool nameCollision(QString name); @@ -52,6 +57,7 @@ private: std::vector subnodes; std::vector commands; SCPINode *parent; + bool operationPending; }; class SCPI : public QObject, public SCPINode @@ -77,10 +83,39 @@ public: static QString getResultName(SCPI::Result r); + // call whenever a subnode completes an operation + void someOperationCompleted(); + public slots: void input(QString line); + void process(); signals: void output(QString line); + +private: + + enum class Flag { + OPC = 0x01, // Operation complete + RQC = 0x02, // device wants to become the controller (of the bus) + QYE = 0x04, // query error + DDE = 0x08, // device-dependent error + EXE = 0x10, // execution error + CME = 0x20, // command error + URQ = 0x40, // user request + PON = 0x80, // power on + }; + + void setFlag(Flag flag); + void clearFlag(Flag flag); + bool getFlag(Flag flag); + + unsigned int ESR; + + bool OCAS; + bool OPCQueryScheduled; + bool WAIexecuting; + + QList cmdQueue; }; #endif // SCPI_H