Merge branch 'master' of github.com:jankae/LibreVNA

This commit is contained in:
Jan Käberich 2023-07-24 10:29:20 +02:00
commit 175cade3da
9 changed files with 114 additions and 14 deletions

View File

@ -212,7 +212,9 @@ Some commands are both events and queries, depending on whether the question mar
\section{Commands}
\subsection{General Commands}
\subsubsection{*IDN}
\query{Returns the identifications string}{*IDN?}{None}{LibreVNA-GUI}
\query{Returns the identifications string}{*IDN?}{None}{LibreVNA,LibreVNA-GUI,dummy\_serial,<software version>}
\subsubsection{*OPC}
\query{Returns a 1 after every previous command has been handled}{*OPC?}{None}{1}
\subsubsection{*LST}
\query{Lists all available commands}{*LST?}{None}{List of commands, separated by newline}
\subsection{Device Commands}
@ -249,6 +251,22 @@ This section contains general device commands, available regardless of the curre
VNA
\end{example}
\subsubsection{DEVice:SETUP:SAVE}
\event{Saves the GUI setup to a file}{DEVice:SETUP:SAVE}{<filename>}
Important points when saving/loading setup files through SCPI commands:
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item If the LibreVNA-GUI (and thus also the SCPI server) is running on a different machine than the SCPI client, the setup files will be saved/loaded from the machine that runs the GUI.
\item If no (or a wrong) file ending is specified, ``.setup'' is automatically added to the filename.
\end{itemize}
\subsubsection{DEVice:SETUP:LOAD}
\query{Loads a setup file}{DEVice:SETUP:LOAD?}{<filename>}{TRUE or FALSE}
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item The filename must include the file ending ``.setup''.
\end{itemize}
\subsubsection{DEVice:REFerence:OUT}
\event{Sets the reference output frequency}{DEVice:REFerence:OUT <freq>}{<freq> in MHz, either 0 (disabled), 10 or 100}
\query{Queries the reference output frequency}{DEVice:REFerence:OUT?}{None}{Output frequency in MHz}
@ -561,7 +579,6 @@ Any number of measurements can be specified (by their number). These measurement
Important points when saving/loading calibration files through SCPI commands:
\begin{itemize}
\item Filenames must be either absolute or relative to the location of the GUI application.
\item SCPI parsing implicitly capitalizes all commands, the file will be saved using only uppercase letters. Similarly, it is not possible to load a file whose filename contains lowercase characters.
\item If the LibreVNA-GUI (and thus also the SCPI server) is running on a different machine than the SCPI client, the calibration files will be saved/loaded from the machine that runs the GUI.
\end{itemize}

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
import time
from libreVNA import libreVNA
# Create the control instance
vna = libreVNA('localhost', 19542)
# Quick connection check (should print "LibreVNA-GUI")
print(vna.query("*IDN?"))
vna.cmd(":VNA:DEEMB:CLEAR")
vna.cmd(":VNA:DEEMB:NEW PORT_EXTENSION")
vna.cmd(":VNA:DEEMB:NEW 2XTHRU")
vna.cmd(":VNA:DEEMB:NEW MATCHING_NETWORK")
vna.cmd(":VNA:DEEMB:NEW IMPEDANCE_RENORMALIZATION")
print("Number of total deembedding options:")
num_options = int(vna.query(":VNA:DEEMB:NUM?"))
print(num_options)
for i in range(1, num_options+1):
option_name = vna.query(":VNA:DEEMB:TYPE? "+str(i))
print("Option "+str(i)+": "+option_name)
# edit port extension
vna.cmd(":VNA:DEEMB:1:PORT 2")
vna.cmd(":VNA:DEEMB:1:DELAY 0.00002")
vna.cmd(":VNA:DEEMB:1:DCLOSS 1")
vna.cmd(":VNA:DEEMB:1:LOSS 3")
vna.cmd(":VNA:DEEMB:1:FREQUENCY 5000000000")
vna.cmd(":VNA:DEEMB:3:PORT 3")
vna.cmd(":VNA:DEEMB:3:CLEAR")
vna.cmd(":VNA:DEEMB:3:ADD FALSE")
vna.cmd(":VNA:DEEMB:3:NEW ParallelC")
vna.cmd(":VNA:DEEMB:3:NEW SeriesR")
vna.cmd(":VNA:DEEMB:3:NEW ParallelL")
vna.cmd(":VNA:DEEMB:3:NEW SeriesL")
vna.cmd(":VNA:DEEMB:3:NEW touchstone_shunt")
vna.cmd(":VNA:DEEMB:3:1:VALUE 0.0001")
vna.cmd(":VNA:DEEMB:3:2:VALUE 0.00002")
vna.cmd(":VNA:DEEMB:3:3:VALUE 0.000003")
vna.cmd(":VNA:DEEMB:3:4:VALUE 0.000004")
vna.cmd(":VNA:DEEMB:3:5:FILE TEST.S2P")
vna.cmd(":VNA:DEEMB:4:IMPedance 75")

View File

@ -14,6 +14,7 @@ using USBID = struct {
static constexpr USBID IDs[] = {
{0x0483, 0x564e},
{0x0483, 0x4121},
{0x1209, 0x4121},
};
USBInBuffer::USBInBuffer(libusb_device_handle *handle, unsigned char endpoint, int buffer_size) :

View File

@ -477,7 +477,10 @@ void AppWindow::CreateToolbars()
void AppWindow::SetupSCPI()
{
scpi.add(new SCPICommand("*IDN", nullptr, [=](QStringList){
return "LibreVNA-GUI";
return "LibreVNA,LibreVNA-GUI,dummy_serial,"+appVersion;
}));
scpi.add(new SCPICommand("*OPC", nullptr, [=](QStringList){
return "1";
}));
auto scpi_dev = new SCPINode("DEVice");
scpi.add(scpi_dev);
@ -518,6 +521,27 @@ void AppWindow::SetupSCPI()
ret.chop(1);
return ret;
}));
auto scpi_setup = new SCPINode("SETUP");
scpi_dev->add(scpi_setup);
scpi_setup->add(new SCPICommand("SAVE", [=](QStringList params) -> QString {
if(params.size() != 1) {
// no filename given
return SCPI::getResultName(SCPI::Result::Error);
}
SaveSetup(params[0]);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr, false));
scpi_setup->add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString {
if(params.size() != 1) {
// no filename given
return SCPI::getResultName(SCPI::Result::False);
}
if(!LoadSetup(params[0])) {
// some error when loading the setup file
return SCPI::getResultName(SCPI::Result::False);
}
return SCPI::getResultName(SCPI::Result::True);
}, false));
auto scpi_ref = new SCPINode("REFerence");
scpi_dev->add(scpi_ref);
scpi_ref->add(new SCPICommand("OUT", [=](QStringList params) -> QString {
@ -1182,13 +1206,13 @@ nlohmann::json AppWindow::SaveSetup()
return j;
}
void AppWindow::LoadSetup(QString filename)
bool AppWindow::LoadSetup(QString filename)
{
ifstream file;
file.open(filename.toStdString());
if(!file.is_open()) {
qWarning() << "Unable to open file:" << filename;
return;
return false;
}
nlohmann::json j;
try {
@ -1197,12 +1221,13 @@ void AppWindow::LoadSetup(QString filename)
InformationBox::ShowError("Error", "Failed to parse the setup file (" + QString(e.what()) + ")");
qWarning() << "Parsing of setup file failed: " << e.what();
file.close();
return;
return false;
}
file.close();
LoadSetup(j);
QFileInfo fi(filename);
lSetupName.setText("Setup: "+fi.fileName());
return true;
}
void AppWindow::LoadSetup(nlohmann::json j)

View File

@ -68,7 +68,7 @@ private slots:
void DeviceFlagsUpdated();
void DeviceInfoUpdated();
void SaveSetup(QString filename);
void LoadSetup(QString filename);
bool LoadSetup(QString filename);
private:
nlohmann::json SaveSetup();
void LoadSetup(nlohmann::json j);

View File

@ -105,7 +105,6 @@ void SCPI::input(QString line)
if(cmd[0] == ':') {
cmd.remove(0, 1);
}
cmd = cmd.toUpper();
auto response = lastNode->parse(cmd, lastNode);
emit output(response);
}
@ -274,7 +273,7 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
// have not reached a leaf, find next subnode
auto subnode = cmd.left(splitPos);
for(auto n : subnodes) {
if(SCPI::match(n->name, subnode)) {
if(SCPI::match(n->name, subnode.toUpper())) {
// pass on to next level
return n->parse(cmd.right(cmd.size() - splitPos - 1), lastNode);
}
@ -292,9 +291,14 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
cmd.chop(1);
}
for(auto c : commands) {
if(SCPI::match(c->name(), cmd)) {
if(SCPI::match(c->name(), cmd.toUpper())) {
// save current node in case of non-root for the next command
lastNode = this;
if(c->convertToUppercase()) {
for(auto &p : params) {
p = p.toUpper();
}
}
if(isQuery) {
return c->query(params);
} else {

View File

@ -8,20 +8,23 @@
class SCPICommand {
public:
SCPICommand(QString name, std::function<QString(QStringList)> cmd, std::function<QString(QStringList)> query) :
SCPICommand(QString name, std::function<QString(QStringList)> cmd, std::function<QString(QStringList)> query, bool convertToUppercase = true) :
_name(name),
fn_cmd(cmd),
fn_query(query){}
fn_query(query),
argAlwaysUppercase(convertToUppercase){}
QString execute(QStringList params);
QString query(QStringList params);
QString name() {return _name;}
bool queryable() { return fn_query != nullptr;}
bool executable() { return fn_cmd != nullptr;}
bool convertToUppercase() { return argAlwaysUppercase;}
private:
const QString _name;
std::function<QString(QStringList)> fn_cmd;
std::function<QString(QStringList)> fn_query;
bool argAlwaysUppercase;
};
class SCPINode {

View File

@ -63,10 +63,10 @@
* @{
*/
#define USBD_VID 0x0483
#define USBD_VID 0x1209
#define USBD_PID_FS 0x4121
#define USBD_LANGID_STRING 0x0409
#define USBD_MANUFACTURER_STRING "STMicroelectronics"
#define USBD_MANUFACTURER_STRING "LibreVNA"
#define USBD_PRODUCT_STRING_FS "VNA"
#define USBD_CONFIGURATION_STRING_FS "CustomUSBDevice Config"
#define USBD_INTERFACE_STRING_FS "CustomUSBDevice Interface"