Additional SCPI commands + configuration of port in preferences

This commit is contained in:
Jan Käberich 2021-04-11 12:33:37 +02:00
parent 7d682f53ae
commit 611b1ef59d
10 changed files with 432 additions and 29 deletions

View File

@ -42,7 +42,7 @@ void Generator::initializeDevice()
void Generator::updateDevice()
{
if(!window->getDevice()) {
if(!window->getDevice() || Mode::getActiveMode() != this) {
// can't update if not connected
return;
}

View File

@ -45,6 +45,7 @@
SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
: Mode(window, "Spectrum Analyzer"),
SCPINode("SA"),
central(new TileWidget(traceModel, window))
{
averages = 1;
@ -144,8 +145,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
cbWindowType->addItem("Flat Top");
cbWindowType->setCurrentIndex(1);
connect(cbWindowType, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
settings.WindowType = index;
SettingsChanged();
SetWindow((Window) index);
});
tb_acq->addWidget(cbWindowType);
@ -158,8 +158,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
cbDetector->addItem("Average");
cbDetector->setCurrentIndex(0);
connect(cbDetector, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
settings.Detector = index;
SettingsChanged();
SetDetector((Detector) index);
});
tb_acq->addWidget(cbDetector);
@ -175,10 +174,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
tb_acq->addWidget(sbAverages);
cbSignalID = new QCheckBox("Signal ID");
connect(cbSignalID, &QCheckBox::toggled, [=](bool enabled) {
settings.SignalID = enabled;
SettingsChanged();
});
connect(cbSignalID, &QCheckBox::toggled, this, &SpectrumAnalyzer::SetSignalID);
tb_acq->addWidget(cbSignalID);
window->addToolBar(tb_acq);
@ -188,18 +184,15 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
auto tb_trackgen = new QToolBar("Tracking Generator");
auto cbTrackGenEnable = new QCheckBox("Tracking Generator");
connect(cbTrackGenEnable, &QCheckBox::toggled, this, &SpectrumAnalyzer::SetTGEnabled);
connect(this, &SpectrumAnalyzer::TGStateChanged, cbTrackGenEnable, &QCheckBox::setChecked);
tb_trackgen->addWidget(cbTrackGenEnable);
auto cbTrackGenPort = new QComboBox();
cbTrackGenPort->addItem("Port 1");
cbTrackGenPort->addItem("Port 2");
cbTrackGenPort->setCurrentIndex(0);
connect(cbTrackGenPort, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index) {
settings.trackingGeneratorPort = index;
if(settings.trackingGenerator) {
SettingsChanged();
}
});
connect(cbTrackGenPort, qOverload<int>(&QComboBox::currentIndexChanged), this, &SpectrumAnalyzer::SetTGPort);
connect(this, &SpectrumAnalyzer::TGPortChanged, cbTrackGenPort, qOverload<int>(&QComboBox::setCurrentIndex));
tb_trackgen->addWidget(cbTrackGenPort);
auto dbm = new QDoubleSpinBox();
@ -254,6 +247,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
window->addDockWidget(Qt::BottomDockWidgetArea, markerDock);
docks.insert(markerDock);
SetupSCPI();
// Set initial TG settings
SetTGLevel(-20.0);
@ -412,7 +406,7 @@ void SpectrumAnalyzer::SettingsChanged()
}
}
if(window->getDevice()) {
if(window->getDevice() && Mode::getActiveMode() == this) {
window->getDevice()->Configure(settings);
}
average.reset(settings.pointNum);
@ -522,6 +516,20 @@ void SpectrumAnalyzer::SetRBW(double bandwidth)
SettingsChanged();
}
void SpectrumAnalyzer::SetWindow(SpectrumAnalyzer::Window w)
{
settings.WindowType = (int) w;
cbWindowType->setCurrentIndex((int) w);
SettingsChanged();
}
void SpectrumAnalyzer::SetDetector(SpectrumAnalyzer::Detector d)
{
settings.Detector = (int) d;
cbDetector->setCurrentIndex((int) d);
SettingsChanged();
}
void SpectrumAnalyzer::SetAveraging(unsigned int averages)
{
this->averages = averages;
@ -530,10 +538,18 @@ void SpectrumAnalyzer::SetAveraging(unsigned int averages)
SettingsChanged();
}
void SpectrumAnalyzer::SetSignalID(bool enabled)
{
settings.SignalID = enabled ? 1 : 0;
cbSignalID->setChecked(enabled);
SettingsChanged();
}
void SpectrumAnalyzer::SetTGEnabled(bool enabled)
{
if(enabled != settings.trackingGenerator) {
settings.trackingGenerator = enabled;
emit TGStateChanged(enabled);
SettingsChanged();
}
normalize.Level->setEnabled(enabled);
@ -545,6 +561,20 @@ void SpectrumAnalyzer::SetTGEnabled(bool enabled)
}
}
void SpectrumAnalyzer::SetTGPort(int port)
{
if(port < 01 || port > 1) {
return;
}
if(port != settings.trackingGeneratorPort) {
settings.trackingGeneratorPort = port;
emit TGPortChanged(port);
if(settings.trackingGenerator) {
SettingsChanged();
}
}
}
void SpectrumAnalyzer::SetTGLevel(double level)
{
if(level > Device::Info().limits_cdbm_max / 100.0) {
@ -617,6 +647,269 @@ void SpectrumAnalyzer::EnableNormalization(bool enabled)
normalize.enable->blockSignals(false);
}
void SpectrumAnalyzer::SetNormalizationLevel(double level)
{
normalize.Level->setValueQuiet(level);
emit NormalizationLevelChanged(level);
}
void SpectrumAnalyzer::SetupSCPI()
{
auto scpi_freq = new SCPINode("FREQuency");
SCPINode::add(scpi_freq);
auto toULong = [](QStringList params) {
bool ok;
if(params.size() != 1) {
return std::numeric_limits<unsigned long>::max();
}
auto newval = params[0].toULong(&ok);
if(!ok) {
return std::numeric_limits<unsigned long>::max();
} else {
return newval;
}
};
scpi_freq->add(new SCPICommand("SPAN", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetSpan(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.f_stop - settings.f_start);
}));
scpi_freq->add(new SCPICommand("START", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetStartFreq(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.f_start);
}));
scpi_freq->add(new SCPICommand("CENTer", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetCenterFreq(newval);
return "";
}
}, [=]() -> QString {
return QString::number((settings.f_start + settings.f_stop)/2);
}));
scpi_freq->add(new SCPICommand("STOP", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetStopFreq(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.f_stop);
}));
scpi_freq->add(new SCPICommand("FULL", [=](QStringList params) -> QString {
SetFullSpan();
return "";
}, nullptr));
auto scpi_acq = new SCPINode("ACQuisition");
SCPINode::add(scpi_acq);
scpi_acq->add(new SCPICommand("RBW", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetRBW(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.RBW);
}));
scpi_acq->add(new SCPICommand("WINDow", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if (params[0] == "NONE") {
SetWindow(Window::None);
} else if(params[0] == "KAISER") {
SetWindow(Window::Kaiser);
} else if(params[0] == "HANN") {
SetWindow(Window::Hann);
} else if(params[0] == "FLATTOP") {
SetWindow(Window::FlatTop);
} else {
return "INVALID MDOE";
}
return "";
}, [=]() -> QString {
switch((Window) settings.WindowType) {
case Window::None: return "NONE";
case Window::Kaiser: return "KAISER";
case Window::Hann: return "HANN";
case Window::FlatTop: return "FLATTOP";
default: return "ERROR";
}
}));
scpi_acq->add(new SCPICommand("WINDow", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if (params[0] == "+PEAK") {
SetDetector(Detector::PPeak);
} else if(params[0] == "-PEAK") {
SetDetector(Detector::NPeak);
} else if(params[0] == "NORMAL") {
SetDetector(Detector::Normal);
} else if(params[0] == "SAMPLE") {
SetDetector(Detector::Sample);
} else if(params[0] == "AVERAGE") {
SetDetector(Detector::Average);
} else {
return "INVALID MDOE";
}
return "";
}, [=]() -> QString {
switch((Detector) settings.Detector) {
case Detector::PPeak: return "+PEAK";
case Detector::NPeak: return "-PEAK";
case Detector::Normal: return "NORMAL";
case Detector::Sample: return "SAMPLE";
case Detector::Average: return "AVERAGE";
default: return "ERROR";
}
}));
scpi_acq->add(new SCPICommand("AVG", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";
} else {
SetAveraging(newval);
return "";
}
}, [=]() -> QString {
return QString::number(averages);
}));
scpi_acq->add(new SCPICommand("SIGid", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if(params[0] == "1" || params[0] == "ON") {
SetSignalID(true);
} else if(params[0] == "0" || params[0] == "OFF") {
SetSignalID(false);
} else {
return "ERROR";
}
return "";
}, [=]() -> QString {
return settings.SignalID ? "1" : "0";
}));
auto scpi_tg = new SCPINode("TRACKing");
SCPINode::add(scpi_tg);
scpi_tg->add(new SCPICommand("ENable", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if(params[0] == "1" || params[0] == "ON") {
SetTGEnabled(true);
} else if(params[0] == "0" || params[0] == "OFF") {
SetTGEnabled(false);
} else {
return "ERROR";
}
return "";
}, [=]() -> QString {
return settings.trackingGenerator ? "1" : "0";
}));
scpi_tg->add(new SCPICommand("Port", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if(params[0] == "1") {
SetTGPort(0);
} else if(params[0] == "2") {
SetTGPort(1);
} else {
return "ERROR";
}
return "";
}, [=]() -> QString {
return settings.trackingGeneratorPort ? "2" : "1";
}));
scpi_tg->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
bool ok;
if(params.size() != 1) {
return "ERROR";
}
auto newval = params[0].toDouble(&ok);
if(!ok) {
return "ERROR";
} else {
SetTGLevel(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.trackingPower / 100.0);
}));
scpi_tg->add(new SCPICommand("OFFset", [=](QStringList params) -> QString {
bool ok;
if(params.size() != 1) {
return "ERROR";
}
auto newval = params[0].toLong(&ok);
if(!ok) {
return "ERROR";
} else {
SetTGOffset(newval);
return "";
}
}, [=]() -> QString {
return QString::number(settings.trackingGeneratorOffset);
}));
auto scpi_norm = new SCPINode("NORMalize");
scpi_tg->add(scpi_norm);
scpi_norm->add(new SCPICommand("ENable", [=](QStringList params) -> QString {
if (params.size() != 1) {
return "ERROR";
}
if(params[0] == "1" || params[0] == "ON") {
EnableNormalization(true);
} else if(params[0] == "0" || params[0] == "OFF") {
EnableNormalization(false);
} else {
return "ERROR";
}
return "";
}, [=]() -> QString {
return normalize.active ? "1" : "0";
}));
scpi_norm->add(new SCPICommand("MEASure", [=](QStringList params) -> QString {
Q_UNUSED(params)
MeasureNormalization();
return "";
}, nullptr));
scpi_norm->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
bool ok;
if(params.size() != 1) {
return "ERROR";
}
auto newval = params[0].toDouble(&ok);
if(!ok) {
return "ERROR";
} else {
SetNormalizationLevel(newval);
return "";
}
}, [=]() -> QString {
return QString::number(normalize.Level->value());
}));
}
void SpectrumAnalyzer::UpdateAverageCount()
{
lAverages->setText(QString::number(average.getLevel()) + "/");

View File

@ -8,8 +8,9 @@
#include "CustomWidgets/tilewidget.h"
#include <QComboBox>
#include <QCheckBox>
#include "scpi.h"
class SpectrumAnalyzer : public Mode
class SpectrumAnalyzer : public Mode, public SCPINode
{
Q_OBJECT
public:
@ -22,6 +23,21 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
private:
enum class Window {
None = 0,
Kaiser = 1,
Hann = 2,
FlatTop = 3
};
enum class Detector {
PPeak = 0,
NPeak = 1,
Sample = 2,
Normal = 3,
Average = 4,
};
private slots:
void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
void StartImpedanceMatching();
@ -35,16 +51,22 @@ private slots:
void SpanZoomOut();
// Acquisition control
void SetRBW(double bandwidth);
void SetWindow(Window w);
void SetDetector(Detector d);
void SetAveraging(unsigned int averages);
void SetSignalID(bool enabled);
// TG control
void SetTGEnabled(bool enabled);
void SetTGPort(int port);
void SetTGLevel(double level);
void SetTGOffset(double offset);
void MeasureNormalization();
void AbortNormalization();
void EnableNormalization(bool enabled);
void SetNormalizationLevel(double level);
private:
void SetupSCPI();
void UpdateAverageCount();
void SettingsChanged();
void ConstrainAndUpdateFrequencies();
@ -86,8 +108,11 @@ signals:
void centerFreqChanged(double freq);
void spanChanged(double span);
void RBWChanged(double RBW);
void TGStateChanged(bool enabled);
void TGPortChanged(int port);
void TGOffsetChanged(double offset);
void TGLevelChanged(double level);
void NormalizationLevelChanged(double level);
void averagingChanged(unsigned int averages);
};

View File

@ -648,7 +648,7 @@ void VNA::UpdateAverageCount()
void VNA::SettingsChanged(std::function<void (Device::TransmissionResult)> cb)
{
settings.suppressPeaks = Preferences::getInstance().Acquisition.suppressPeaks ? 1 : 0;
if(window->getDevice()) {
if(window->getDevice() && Mode::getActiveMode() == this) {
window->getDevice()->Configure(settings, [=](Device::TransmissionResult res){
// device received command, reset traces now
average.reset(settings.points);
@ -916,7 +916,7 @@ void VNA::SetupSCPI()
}, [=]() -> QString {
return QString::number(settings.f_start);
}));
scpi_freq->add(new SCPICommand("CENTER", [=](QStringList params) -> QString {
scpi_freq->add(new SCPICommand("CENTer", [=](QStringList params) -> QString {
auto newval = toULong(params);
if(newval == std::numeric_limits<unsigned long>::max()) {
return "ERROR";

View File

@ -73,7 +73,7 @@ AppWindow::AppWindow(QWidget *parent)
parser.setApplicationDescription("LibreVNA-GUI");
parser.addHelpOption();
parser.addVersionOption();
parser.addOption(QCommandLineOption({"p","port"}, "Specify port to listen for SCPY commands", "port"));
parser.addOption(QCommandLineOption({"p","port"}, "Specify port to listen for SCPI commands", "port"));
parser.addOption(QCommandLineOption({"d","device"}, "Only allow connections to the specified device", "device"));
parser.addOption(QCommandLineOption("no-gui", "Disables the graphical interface"));
@ -87,11 +87,12 @@ AppWindow::AppWindow(QWidget *parent)
auto port = parser.value("port").toUInt(&OK);
if(!OK) {
// set default port
port = 19542;
port = Preferences::getInstance().General.SCPI.port;
}
server = new TCPServer(port);
connect(server, &TCPServer::received, &scpi, &SCPI::input);
connect(&scpi, &SCPI::output, server, &TCPServer::send);
StartTCPServer(port);
Preferences::getInstance().manualTCPport();
} else if(Preferences::getInstance().General.SCPI.enabled) {
StartTCPServer(Preferences::getInstance().General.SCPI.port);
}
scpi.add(new SCPICommand("*IDN", nullptr, [=](){
@ -197,6 +198,7 @@ AppWindow::AppWindow(QWidget *parent)
scpi.add(vna);
scpi.add(generator);
scpi.add(spectrumAnalyzer);
// UI connections
connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList);
@ -239,7 +241,17 @@ AppWindow::AppWindow(QWidget *parent)
connect(ui->actionSource_Calibration, &QAction::triggered, this, &AppWindow::SourceCalibrationDialog);
connect(ui->actionReceiver_Calibration, &QAction::triggered, this, &AppWindow::ReceiverCalibrationDialog);
connect(ui->actionPreferences, &QAction::triggered, [=](){
Preferences::getInstance().edit();
// save previous SCPI settings in case they change
auto p = Preferences::getInstance();
auto SCPIenabled = p.General.SCPI.enabled;
auto SCPIport = p.General.SCPI.port;
p.edit();
if(SCPIenabled != p.General.SCPI.enabled || SCPIport != p.General.SCPI.port) {
StopTCPServer();
if(p.General.SCPI.enabled) {
StartTCPServer(p.General.SCPI.port);
}
}
// settings might have changed, update necessary stuff
// TraceXYPlot::updateGraphColors();
});
@ -281,8 +293,8 @@ AppWindow::AppWindow(QWidget *parent)
AppWindow::~AppWindow()
{
StopTCPServer();
delete ui;
delete server;
}
void AppWindow::closeEvent(QCloseEvent *event)
@ -404,6 +416,18 @@ void AppWindow::CreateToolbars()
tb_reference->setObjectName("Reference Toolbar");
}
void AppWindow::StartTCPServer(int port)
{
server = new TCPServer(port);
connect(server, &TCPServer::received, &scpi, &SCPI::input);
connect(&scpi, &SCPI::output, server, &TCPServer::send);
}
void AppWindow::StopTCPServer()
{
delete server;
}
int AppWindow::UpdateDeviceList()
{
deviceActionGroup->setExclusive(true);

View File

@ -58,6 +58,8 @@ private slots:
private:
void DeviceConnectionLost();
void CreateToolbars();
void StartTCPServer(int port);
void StopTCPServer();
QStackedWidget *central;

View File

@ -76,6 +76,12 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->AcquisitionDFTlimitRBW->setEnabled(enabled);
});
// General page
if(p->TCPoverride) {
ui->GeneralSCPIPort->setEnabled(false);
ui->GeneralSCPIEnabled->setEnabled(false);
}
// Page selection
connect(ui->treeWidget, &QTreeWidget::currentItemChanged, [=](QTreeWidgetItem *current, QTreeWidgetItem *) {
auto name = current->text(0);
@ -122,6 +128,8 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->General.graphColors.background = ui->GeneralGraphBackground->getColor();
p->General.graphColors.axis = ui->GeneralGraphAxis->getColor();
p->General.graphColors.divisions = ui->GeneralGraphDivisions->getColor();
p->General.SCPI.enabled = ui->GeneralSCPIEnabled->isChecked();
p->General.SCPI.port = ui->GeneralSCPIPort->value();
accept();
});
@ -176,6 +184,8 @@ void PreferencesDialog::setInitialGUIState()
ui->GeneralGraphBackground->setColor(p->General.graphColors.background);
ui->GeneralGraphAxis->setColor(p->General.graphColors.axis);
ui->GeneralGraphDivisions->setColor(p->General.graphColors.divisions);
ui->GeneralSCPIEnabled->setChecked(p->General.SCPI.enabled);
ui->GeneralSCPIPort->setValue(p->General.SCPI.port);
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(0);
if (item != nullptr) {

View File

@ -16,6 +16,8 @@ public:
void edit();
void setDefault();
void manualTCPport() { TCPoverride = true; }
struct {
bool ConnectToFirstDevice;
bool RememberSweepSettings;
@ -54,16 +56,23 @@ public:
QColor axis;
QColor divisions;
} graphColors;
struct {
bool enabled;
int port;
} SCPI;
} General;
bool TCPoverride; // in case of manual port specification via command line
private:
Preferences(){};
Preferences() :
TCPoverride(false) {};
static Preferences instance;
using SettingDescription = struct {
QPointerVariant var;
QString name;
QVariant def;
};
const std::array<SettingDescription, 25> descr = {{
const std::array<SettingDescription, 27> descr = {{
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
{&Startup.DefaultSweep.start, "Startup.DefaultSweep.start", 1000000.0},
@ -89,6 +98,8 @@ private:
{&General.graphColors.background, "General.graphColors.background", QColor(Qt::black)},
{&General.graphColors.axis, "General.graphColors.axis", QColor(Qt::white)},
{&General.graphColors.divisions, "General.graphColors.divisions", QColor(Qt::gray)},
{&General.SCPI.enabled, "General.SCPI.enabled", true},
{&General.SCPI.port, "General.SCPI.port", 19542},
}};
};

View File

@ -73,7 +73,7 @@
</size>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4">
@ -587,6 +587,43 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_8">
<property name="title">
<string>SCPI Control</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QCheckBox" name="GeneralSCPIEnabled">
<property name="text">
<string>Enable server</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="GeneralSCPIPort">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">

View File

@ -11,6 +11,7 @@ class TCPServer : public QObject
public:
TCPServer(int port);
public slots:
bool send(QString line);
signals: