Merge branch 'SCPI_improvement' into HIL_actions

This commit is contained in:
Jan Käberich 2024-04-22 13:21:48 +02:00
commit c5d045364c
21 changed files with 813 additions and 110 deletions

View file

@ -546,6 +546,9 @@ bool LibreVNADriver::setExtRef(QString option_in, QString option_out)
case Reference::TypeIn::None:
p.reference.UseExternalRef = 0;
p.reference.AutomaticSwitch = 0;
if(hardwareVersion == 0x01) {
lastStatus.V1.extRefInUse = 0;
}
break;
case Reference::TypeIn::Auto:
p.reference.UseExternalRef = 0;
@ -554,6 +557,9 @@ bool LibreVNADriver::setExtRef(QString option_in, QString option_out)
case Reference::TypeIn::External:
p.reference.UseExternalRef = 1;
p.reference.AutomaticSwitch = 0;
if(hardwareVersion == 0x01) {
lastStatus.V1.extRefInUse = 1;
}
break;
}
switch(refOut) {

View file

@ -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,9 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m)
void SpectrumAnalyzer::SettingsChanged()
{
if(window->getDevice()) {
setOperationPending(true);
}
configurationTimer.start(100);
ResetLiveTraces();
}
@ -703,6 +710,7 @@ void SpectrumAnalyzer::SetAveraging(unsigned int averages)
average.setAverages(averages);
emit averagingChanged(averages);
UpdateAverageCount();
setOperationPending(!average.settled());
}
void SpectrumAnalyzer::SetTGEnabled(bool enabled)
@ -887,6 +895,9 @@ void SpectrumAnalyzer::ConfigureDevice()
void SpectrumAnalyzer::ResetLiveTraces()
{
if(window->getDevice()) {
setOperationPending(true);
}
average.reset(DeviceDriver::SApoints());
traceModel.clearLiveData();
UpdateAverageCount();
@ -952,6 +963,16 @@ void SpectrumAnalyzer::SetupSCPI()
}, nullptr));
auto scpi_acq = new SCPINode("ACQuisition");
SCPINode::add(scpi_acq);
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));
scpi_acq->add(new SCPICommand("RBW", [=](QStringList params) -> QString {
unsigned long long newval;
if(!SCPI::paramToULongLong(params, 0, newval)) {

View file

@ -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,9 @@ void VNA::UpdateAverageCount()
void VNA::SettingsChanged(bool resetTraces, int delay)
{
if(window->getDevice()) {
setOperationPending(true);
}
configurationTimer.start(delay);
changingSettings = true;
configurationTimerResetTraces = resetTraces;
@ -1218,6 +1225,7 @@ void VNA::SetAveraging(unsigned int averages)
average.setAverages(averages);
emit averagingChanged(averages);
UpdateAverageCount();
setOperationPending(!average.settled());
}
void VNA::ExcitationRequired()
@ -1317,7 +1325,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 +1385,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 {
@ -1412,6 +1418,16 @@ void VNA::SetupSCPI()
}));
auto scpi_acq = new SCPINode("ACQuisition");
SCPINode::add(scpi_acq);
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));
scpi_acq->add(new SCPICommand("IFBW", [=](QStringList params) -> QString {
unsigned long long newval;
if(!SCPI::paramToULongLong(params, 0, newval)) {
@ -1449,7 +1465,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";
@ -1465,16 +1481,6 @@ 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 {
@ -1855,6 +1861,9 @@ void VNA::ResetLiveTraces()
traceModel.clearLiveData();
UpdateAverageCount();
UpdateCalWidget();
if(window->getDevice()) {
setOperationPending(true);
}
}
bool VNA::LoadCalibration(QString filename)

View file

@ -143,11 +143,6 @@ AppWindow::AppWindow(QWidget *parent)
central = new QStackedWidget;
setCentralWidget(central);
auto vnaIndex = modeHandler->createMode("Vector Network Analyzer", Mode::Type::VNA);
modeHandler->createMode("Signal Generator", Mode::Type::SG);
modeHandler->createMode("Spectrum Analyzer", Mode::Type::SA);
modeHandler->setCurrentIndex(vnaIndex);
auto setModeStatusbar = [=](const QString &msg) {
lModeInfo.setText(msg);
};
@ -170,10 +165,9 @@ AppWindow::AppWindow(QWidget *parent)
SetupSCPI();
SetInitialState();
auto& pref = Preferences::getInstance();
if(pref.Startup.UseSetupFile) {
LoadSetup(pref.Startup.SetupFile);
}
// List available devices
UpdateDeviceList();
if(pref.Startup.ConnectToFirstDevice && deviceList.size() > 0) {
@ -315,6 +309,23 @@ void AppWindow::closeEvent(QCloseEvent *event)
QMainWindow::closeEvent(event);
}
void AppWindow::SetInitialState()
{
modeHandler->closeModes();
auto& pref = Preferences::getInstance();
if(pref.Startup.UseSetupFile) {
LoadSetup(pref.Startup.SetupFile);
} else {
auto vnaIndex = modeHandler->createMode("Vector Network Analyzer", Mode::Type::VNA);
modeHandler->createMode("Signal Generator", Mode::Type::SG);
modeHandler->createMode("Spectrum Analyzer", Mode::Type::SA);
modeHandler->setCurrentIndex(vnaIndex);
}
ResetReference();
}
bool AppWindow::ConnectToDevice(QString serial, DeviceDriver *driver)
{
if(serial.isEmpty()) {
@ -477,9 +488,10 @@ void AppWindow::SetupSCPI()
scpi.add(new SCPICommand("*IDN", nullptr, [=](QStringList){
return "LibreVNA,LibreVNA-GUI,dummy_serial,"+appVersion;
}));
scpi.add(new SCPICommand("*OPC", nullptr, [=](QStringList){
return "1";
}));
scpi.add(new SCPICommand("*RST", [=](QStringList){
SetInitialState();
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
auto scpi_dev = new SCPINode("DEVice");
scpi.add(scpi_dev);
scpi_dev->add(new SCPICommand("DISConnect", [=](QStringList params) -> QString {
@ -1050,6 +1062,12 @@ int AppWindow::UpdateDeviceList()
return available;
}
void AppWindow::ResetReference()
{
toolbars.reference.type->setCurrentIndex(0);
toolbars.reference.outFreq->setCurrentIndex(0);
}
//void AppWindow::StartManualControl()
//{
// if(!vdevice || vdevice->isCompoundDevice()) {

View file

@ -58,10 +58,12 @@ public slots:
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
void SetInitialState();
bool ConnectToDevice(QString serial = QString(), DeviceDriver *driver = nullptr);
void DisconnectDevice();
int UpdateDeviceList();
// void StartManualControl();
void ResetReference();
void UpdateReferenceToolbar();
void UpdateReference();
void DeviceStatusUpdated();

View file

@ -84,6 +84,11 @@ unsigned int Averaging::currentSweep()
}
}
bool Averaging::settled()
{
return getLevel() == averages;
}
Averaging::Mode Averaging::getMode() const
{
return mode;

View file

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

View file

@ -5,7 +5,70 @@
SCPI::SCPI() :
SCPINode("")
{
lastNode = this;
WAIexecuting = false;
OPCsetBitScheduled = false;
OPCQueryScheduled = false;
OCAS = false;
SESR = 0x00;
ESE = 0xFF;
add(new SCPICommand("*CLS", [=](QStringList) {
SESR = 0x00;
OCAS = false;
OPCQueryScheduled = false;
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("*ESE", [=](QStringList params){
unsigned long long newval;
if(!SCPI::paramToULongLong(params, 0, newval) || newval >= 256) {
return SCPI::getResultName(SCPI::Result::Error);
} else {
ESE = newval;
return SCPI::getResultName(SCPI::Result::Empty);
}
}, [=](QStringList){
return QString::number(ESE);
}));
add(new SCPICommand("*ESR", nullptr, [=](QStringList){
auto ret = QString::number(SESR);
SESR = 0x00;
return ret;
}));
add(new SCPICommand("*OPC", [=](QStringList){
// OPC command
if(isOperationPending()) {
OPCsetBitScheduled = true;
OCAS = true;
} else {
// operation already complete
setFlag(Flag::OPC);
}
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;
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);
@ -48,8 +111,14 @@ bool SCPI::paramToULongLong(QStringList params, int index, unsigned long long &d
if(index >= params.size()) {
return false;
}
bool okay;
dest = params[index].toULongLong(&okay);
double res;
bool okay = paramToDouble(params, index, res);
if(res > std::numeric_limits<unsigned long long>::max() || res < std::numeric_limits<unsigned long long>::min()) {
okay = false;
}
if(okay) {
dest = res;
}
return okay;
}
@ -58,8 +127,14 @@ bool SCPI::paramToLong(QStringList params, int index, long &dest)
if(index >= params.size()) {
return false;
}
bool okay;
dest = params[index].toLong(&okay);
double res;
bool okay = paramToDouble(params, index, res);
if(res > std::numeric_limits<long>::max() || res < std::numeric_limits<long>::min()) {
okay = false;
}
if(okay) {
dest = res;
}
return okay;
}
@ -69,10 +144,10 @@ bool SCPI::paramToBool(QStringList params, int index, bool &dest)
return false;
}
bool okay = false;
if(params[index] == "TRUE") {
if(params[index] == "TRUE" || params[index] == "ON" || params[index] == "1") {
dest = true;
okay = true;
} else if(params[index] == "FALSE") {
} else if(params[index] == "FALSE" || params[index] == "OFF" || params[index] == "0") {
dest = false;
okay = true;
}
@ -87,6 +162,12 @@ QString SCPI::getResultName(SCPI::Result r)
case Result::Error:
default:
return "ERROR";
case Result::CmdError:
return "CMD_ERROR";
case Result::QueryError:
return "QUERY_ERROR";
case Result::ExecError:
return "EXEC_ERROR";
case Result::False:
return "FALSE";
case Result::True:
@ -96,20 +177,83 @@ QString SCPI::getResultName(SCPI::Result r)
void SCPI::input(QString line)
{
auto cmds = line.split(";");
for(auto cmd : cmds) {
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);
if(response == getResultName(Result::Error)) {
setFlag(Flag::CME);
} else if(response == getResultName(Result::QueryError)) {
setFlag(Flag::CME);
} else if(response == getResultName(Result::CmdError)) {
setFlag(Flag::CME);
} else if(response == getResultName(Result::ExecError)) {
setFlag(Flag::EXE);
} else if(response == getResultName(Result::Empty)) {
// do nothing
} else {
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;
if(OPCsetBitScheduled) {
setFlag(Flag::OPC);
OPCsetBitScheduled = false;
}
if(OPCQueryScheduled) {
output("1");
OPCQueryScheduled = false;
}
}
if(WAIexecuting) {
WAIexecuting = false;
// process any queued commands
process();
}
}
}
void SCPI::setFlag(Flag flag)
{
SESR |= ((int) flag);
}
void SCPI::clearFlag(Flag flag)
{
SESR &= ~((int) flag);
}
bool SCPI::getFlag(Flag flag)
{
return SESR & (int) flag;
}
SCPINode::~SCPINode()
{
if(parent) {
@ -233,6 +377,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<SCPI*>(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) {
@ -314,17 +488,25 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
QString SCPICommand::execute(QStringList params)
{
if(fn_cmd == nullptr) {
return SCPI::getResultName(SCPI::Result::Error);
return SCPI::getResultName(SCPI::Result::CmdError);
} else {
return fn_cmd(params);
auto ret = fn_cmd(params);
if(ret == SCPI::getResultName(SCPI::Result::Error)) {
ret = SCPI::getResultName(SCPI::Result::CmdError);
}
return ret;
}
}
QString SCPICommand::query(QStringList params)
{
if(fn_query == nullptr) {
return SCPI::getResultName(SCPI::Result::Error);
return SCPI::getResultName(SCPI::Result::QueryError);
} else {
return fn_query(params);
auto ret = fn_query(params);
if(ret == SCPI::getResultName(SCPI::Result::Error)) {
ret = SCPI::getResultName(SCPI::Result::QueryError);
}
return ret;
}
}

View file

@ -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<SCPINode*> subnodes;
std::vector<SCPICommand*> commands;
SCPINode *parent;
bool operationPending;
};
class SCPI : public QObject, public SCPINode
@ -71,19 +77,50 @@ public:
enum class Result {
Empty,
Error,
CmdError,
QueryError,
ExecError,
False,
True
};
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:
SCPINode *lastNode;
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 SESR;
unsigned int ESE;
bool OCAS;
bool OPCsetBitScheduled;
bool OPCQueryScheduled;
bool WAIexecuting;
QList<QString> cmdQueue;
};
#endif // SCPI_H

View file

@ -12,7 +12,7 @@ TCPServer::TCPServer(int port)
delete socket;
socket = server.nextPendingConnection();
connect(socket, &QTcpSocket::readyRead, [=](){
if(socket->canReadLine()) {
while(socket->canReadLine()) {
auto available = socket->bytesAvailable();
char data[available+1];
socket->readLine(data, sizeof(data));