Include mode/sweep/acquisition settings in setup files

This commit is contained in:
Jan Käberich 2021-09-03 19:01:22 +02:00
parent cf22f40630
commit 68dc9842e9
13 changed files with 316 additions and 19 deletions

Binary file not shown.

View File

@ -245,6 +245,8 @@ This dock shows the debug output of the \vna{} (the same messages as on the inte
\subsubsection{File Menu} \subsubsection{File Menu}
Changing the default setup of the application can require a lot of configuration, especially if some of the more complicated math or de-embedding options are used. To simplify this process, setup-files can be saved and opened, which perform these steps automatically. A setup file contains all settings and configuration of these elements: Changing the default setup of the application can require a lot of configuration, especially if some of the more complicated math or de-embedding options are used. To simplify this process, setup-files can be saved and opened, which perform these steps automatically. A setup file contains all settings and configuration of these elements:
\begin{itemize} \begin{itemize}
\item Sweep and Acquisition settings (start/stop frequency, IF bandwidth, ...)
\item The currently active mode (VNA, Signalgenerator or Spectrumanalyzer)
\item Traces (Name, Color, Parameters, ...) \item Traces (Name, Color, Parameters, ...)
\item Math operations applied to the traces \item Math operations applied to the traces
\item Markers \item Markers

View File

@ -40,6 +40,19 @@ void Generator::initializeDevice()
updateDevice(); updateDevice();
} }
nlohmann::json Generator::toJSON()
{
return central->toJSON();
}
void Generator::fromJSON(nlohmann::json j)
{
if(j.is_null()) {
return;
}
central->fromJSON(j);
}
void Generator::updateDevice() void Generator::updateDevice()
{ {
if(!window->getDevice() || Mode::getActiveMode() != this) { if(!window->getDevice() || Mode::getActiveMode() != this) {

View File

@ -13,8 +13,8 @@ public:
void initializeDevice() override; void initializeDevice() override;
// Nothing to do for now // Nothing to do for now
virtual nlohmann::json toJSON() override {return nlohmann::json();}; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override {Q_UNUSED(j)}; virtual void fromJSON(nlohmann::json j) override;
private slots: private slots:
void updateDevice(); void updateDevice();

View File

@ -157,6 +157,44 @@ Protocol::GeneratorSettings SignalgeneratorWidget::getDeviceStatus()
return s; return s;
} }
nlohmann::json SignalgeneratorWidget::toJSON()
{
nlohmann::json j;
j["frequency"] = ui->frequency->value();
j["power"] = ui->levelSpin->value();
if(ui->EnablePort1->isChecked()) {
j["port"] = 1;
} else if(ui->EnablePort2->isChecked()) {
j["port"] = 2;
} else {
j["port"] = 0;
}
nlohmann::json sweep;
sweep["span"] = ui->span->value();
sweep["steps"] = ui->steps->value();
sweep["dwell"] = ui->dwell->value();
sweep["enabled"] = ui->EnabledSweep->isChecked();
j["sweep"] = sweep;
return j;
}
void SignalgeneratorWidget::fromJSON(nlohmann::json j)
{
setFrequency(j.value("frequency", ui->frequency->value()));
setLevel(j.value("power", ui->levelSpin->value()));
setPort(j.value("port", 0));
if(j.contains("sweep")) {
auto sweep = j["sweep"];
// extract sweep settings, keeping current values as default
ui->span->setValue(sweep.value("span", ui->span->value()));
ui->steps->setValue(sweep.value("steps", ui->steps->value()));
ui->dwell->setValue(sweep.value("dwell", ui->dwell->value()));
ui->EnabledSweep->setChecked(sweep.value("enabled", false));
} else {
ui->EnabledSweep->setChecked(false);
}
}
void SignalgeneratorWidget::setLevel(double level) void SignalgeneratorWidget::setLevel(double level)
{ {
// TODO constrain to frequency dependent levels // TODO constrain to frequency dependent levels

View File

@ -3,12 +3,13 @@
#include <QWidget> #include <QWidget>
#include "Device/device.h" #include "Device/device.h"
#include "savable.h"
namespace Ui { namespace Ui {
class SignalgeneratorWidget; class SignalgeneratorWidget;
} }
class SignalgeneratorWidget : public QWidget class SignalgeneratorWidget : public QWidget, public Savable
{ {
Q_OBJECT Q_OBJECT
@ -17,6 +18,8 @@ public:
~SignalgeneratorWidget(); ~SignalgeneratorWidget();
Protocol::GeneratorSettings getDeviceStatus(); Protocol::GeneratorSettings getDeviceStatus();
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
signals: signals:
void SettingsChanged(); void SettingsChanged();

View File

@ -181,7 +181,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="SIUnitEdit" name="span"> <widget class="SIUnitEdit" name="span">
<property name="text"> <property name="text">
<string>0MHz</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
@ -195,7 +195,7 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="SIUnitEdit" name="steps"> <widget class="SIUnitEdit" name="steps">
<property name="text"> <property name="text">
<string>100</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
@ -209,7 +209,7 @@
<item row="2" column="1"> <item row="2" column="1">
<widget class="SIUnitEdit" name="dwell"> <widget class="SIUnitEdit" name="dwell">
<property name="text"> <property name="text">
<string>100ms</string> <string/>
</property> </property>
</widget> </widget>
</item> </item>
@ -226,7 +226,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>0MHz</string> <string/>
</property> </property>
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>true</bool>

View File

@ -297,6 +297,37 @@ void SpectrumAnalyzer::initializeDevice()
nlohmann::json SpectrumAnalyzer::toJSON() nlohmann::json SpectrumAnalyzer::toJSON()
{ {
nlohmann::json j; nlohmann::json j;
// save current sweep/acquisition settings
nlohmann::json sweep;
nlohmann::json freq;
freq["start"] = settings.f_start;
freq["stop"] = settings.f_stop;
sweep["frequency"] = freq;
nlohmann::json acq;
acq["RBW"] = settings.RBW;
acq["window"] = WindowToString((Window) settings.WindowType).toStdString();
acq["detector"] = DetectorToString((Detector) settings.Detector).toStdString();
acq["signal ID"] = settings.SignalID ? true : false;
sweep["acquisition"] = acq;
nlohmann::json tracking;
tracking["enabled"] = settings.trackingGenerator ? true : false;
tracking["port"] = settings.trackingGeneratorPort ? 2 : 1;
tracking["offset"] = settings.trackingGeneratorOffset;
tracking["power"] = settings.trackingPower;
sweep["trackingGenerator"] = tracking;
if(normalize.active) {
nlohmann::json norm;
norm["start"] = normalize.f_start;
norm["stop"] = normalize.f_stop;
norm["points"] = normalize.points;
norm["level"] = normalize.Level->value();
norm["port1"] = normalize.port1Correction;
norm["port2"] = normalize.port2Correction;
sweep["normalization"] = norm;
}
j["sweep"] = sweep;
j["traces"] = traceModel.toJSON(); j["traces"] = traceModel.toJSON();
j["tiles"] = central->toJSON(); j["tiles"] = central->toJSON();
j["markers"] = markerModel->toJSON(); j["markers"] = markerModel->toJSON();
@ -305,6 +336,9 @@ nlohmann::json SpectrumAnalyzer::toJSON()
void SpectrumAnalyzer::fromJSON(nlohmann::json j) void SpectrumAnalyzer::fromJSON(nlohmann::json j)
{ {
if(j.is_null()) {
return;
}
if(j.contains("traces")) { if(j.contains("traces")) {
traceModel.fromJSON(j["traces"]); traceModel.fromJSON(j["traces"]);
} }
@ -314,6 +348,63 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
if(j.contains("markers")) { if(j.contains("markers")) {
markerModel->fromJSON(j["markers"]); markerModel->fromJSON(j["markers"]);
} }
if(j.contains("sweep")) {
// restore sweep settings
auto sweep = j["sweep"];
if(sweep.contains("frequency")) {
auto freq = sweep["frequency"];
SetStartFreq(freq.value("start", settings.f_start));
SetStartFreq(freq.value("start", settings.f_start));
}
if(sweep.contains("acquisition")) {
auto acq = sweep["acquisition"];
SetRBW(acq.value("RBW", settings.RBW));
auto w = WindowFromString(QString::fromStdString(acq.value("window", "")));
if(w == Window::Last) {
// invalid, keep current value
w = (Window) settings.WindowType;
}
SetWindow(w);
auto d = DetectorFromString(QString::fromStdString(acq.value("detector", "")));
if(d == Detector::Last) {
// invalid, keep current value
d = (Detector) settings.Detector;
}
SetDetector(d);
SetSignalID(acq.value("signal ID", settings.SignalID ? true : false));
}
if(sweep.contains("trackingGenerator")) {
auto tracking = sweep["trackingGenerator"];
SetTGEnabled(tracking.value("enabled", settings.trackingGenerator ? true : false));
int port = tracking.value("port", 1);
// Function expects 0 for port1, 1 for port2
SetTGPort(port - 1);
SetTGLevel(tracking.value("power", settings.trackingPower));
SetTGOffset(tracking.value("offset", settings.trackingGeneratorOffset));
}
if(sweep.contains("normalization")) {
auto norm = sweep["normalization"];
// restore normalization data
normalize.port1Correction.clear();
for(double p1 : norm["port1"]) {
normalize.port1Correction.push_back(p1);
}
normalize.port2Correction.clear();
for(double p2 : norm["port2"]) {
normalize.port2Correction.push_back(p2);
}
normalize.f_start = norm.value("start", normalize.f_start);
normalize.f_stop = norm.value("stop", normalize.f_stop);
normalize.points = norm.value("points", normalize.points);
normalize.Level->setValue(norm.value("level", normalize.Level->value()));
if((normalize.port1Correction.size() == normalize.points) && (normalize.port1Correction.size() == normalize.points)) {
// got the correct number of points
EnableNormalization(true);
} else {
EnableNormalization(false);
}
}
}
} }
using namespace std; using namespace std;
@ -570,7 +661,7 @@ void SpectrumAnalyzer::SetTGEnabled(bool enabled)
void SpectrumAnalyzer::SetTGPort(int port) void SpectrumAnalyzer::SetTGPort(int port)
{ {
if(port < 01 || port > 1) { if(port < 0 || port > 1) {
return; return;
} }
if(port != settings.trackingGeneratorPort) { if(port != settings.trackingGeneratorPort) {
@ -971,3 +1062,48 @@ void SpectrumAnalyzer::updateGraphColors()
{ {
emit graphColorsChanged(); emit graphColorsChanged();
} }
QString SpectrumAnalyzer::WindowToString(SpectrumAnalyzer::Window w)
{
switch(w) {
case Window::None: return "None";
case Window::Kaiser: return "Kaiser";
case Window::Hann: return "Hann";
case Window::FlatTop: return "FlatTop";
default: return "Unknown";
}
}
SpectrumAnalyzer::Window SpectrumAnalyzer::WindowFromString(QString s)
{
for(int i=0;i<(int)Window::Last;i++) {
if(WindowToString((Window) i) == s) {
return (Window) i;
}
}
// not found
return Window::Last;
}
QString SpectrumAnalyzer::DetectorToString(SpectrumAnalyzer::Detector d)
{
switch(d) {
case Detector::PPeak: return "+Peak";
case Detector::NPeak: return "-Peak";
case Detector::Sample: return "Sample";
case Detector::Normal: return "Normal";
case Detector::Average: return "Average";
default: return "Unknown";
}
}
SpectrumAnalyzer::Detector SpectrumAnalyzer::DetectorFromString(QString s)
{
for(int i=0;i<(int)Detector::Last;i++) {
if(DetectorToString((Detector) i) == s) {
return (Detector) i;
}
}
// not found
return Detector::Last;
}

View File

@ -32,7 +32,8 @@ private:
None = 0, None = 0,
Kaiser = 1, Kaiser = 1,
Hann = 2, Hann = 2,
FlatTop = 3 FlatTop = 3,
Last
}; };
enum class Detector { enum class Detector {
PPeak = 0, PPeak = 0,
@ -40,8 +41,14 @@ private:
Sample = 2, Sample = 2,
Normal = 3, Normal = 3,
Average = 4, Average = 4,
Last
}; };
static QString WindowToString(Window w);
static Window WindowFromString(QString s);
static QString DetectorToString(Detector d);
static Detector DetectorFromString(QString s);
private slots: private slots:
void NewDatapoint(Protocol::SpectrumAnalyzerResult d); void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
void StartImpedanceMatching(); void StartImpedanceMatching();

View File

@ -560,6 +560,8 @@ Calibration::InterpolationType VNA::getCalInterpolation()
{ {
double f_min, f_max; double f_min, f_max;
switch(settings.sweepType) { switch(settings.sweepType) {
case SweepType::Last:
// should never get here, use frequency values just in case
case SweepType::Frequency: case SweepType::Frequency:
f_min = settings.Freq.start; f_min = settings.Freq.start;
f_max = settings.Freq.stop; f_max = settings.Freq.stop;
@ -678,6 +680,23 @@ void VNA::shutdown()
nlohmann::json VNA::toJSON() nlohmann::json VNA::toJSON()
{ {
nlohmann::json j; nlohmann::json j;
// save current sweep/acquisition settings
nlohmann::json sweep;
sweep["type"] = SweepTypeToString(settings.sweepType).toStdString();
nlohmann::json freq;
freq["start"] = settings.Freq.start;
freq["stop"] = settings.Freq.stop;
freq["power"] = settings.Freq.excitation_power;
sweep["frequency"] = freq;
nlohmann::json power;
power["start"] = settings.Power.start;
power["stop"] = settings.Power.stop;
power["frequency"] = settings.Power.frequency;
sweep["power"] = power;
sweep["points"] = settings.npoints;
sweep["IFBW"] = settings.bandwidth;
j["sweep"] = sweep;
j["traces"] = traceModel.toJSON(); j["traces"] = traceModel.toJSON();
j["tiles"] = central->toJSON(); j["tiles"] = central->toJSON();
j["markers"] = markerModel->toJSON(); j["markers"] = markerModel->toJSON();
@ -688,6 +707,9 @@ nlohmann::json VNA::toJSON()
void VNA::fromJSON(nlohmann::json j) void VNA::fromJSON(nlohmann::json j)
{ {
if(j.is_null()) {
return;
}
if(j.contains("traces")) { if(j.contains("traces")) {
traceModel.fromJSON(j["traces"]); traceModel.fromJSON(j["traces"]);
} }
@ -703,6 +725,32 @@ void VNA::fromJSON(nlohmann::json j)
} else { } else {
EnableDeembedding(false); EnableDeembedding(false);
} }
// sweep configuration has to go last sog graphs can catch events from changed sweep
if(j.contains("sweep")) {
auto sweep = j["sweep"];
// restore sweep settings, keep current value as default in case of missing entry
SetPoints(sweep.value("points", settings.npoints));
SetIFBandwidth(sweep.value("IFBW", settings.bandwidth));
if(sweep.contains("frequency")) {
auto freq = sweep["frequency"];
SetStartFreq(freq.value("start", settings.Freq.start));
SetStopFreq(freq.value("stop", settings.Freq.stop));
SetSourceLevel(freq.value("power", settings.Freq.excitation_power));
}
if(sweep.contains("power")) {
auto power = sweep["power"];
SetStartPower(power.value("start", settings.Power.start));
SetStopPower(power.value("stop", settings.Power.stop));
SetPowerSweepFrequency(power.value("frequency", settings.Power.frequency));
}
auto type = SweepTypeFromString(QString::fromStdString(sweep["type"]));
if(type == SweepType::Last) {
// default to frequency sweep
type = SweepType::Frequency;
}
SetSweepType(type);
}
} }
using namespace std; using namespace std;
@ -735,6 +783,7 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
TraceMath::DataType type; TraceMath::DataType type;
switch(settings.sweepType) { switch(settings.sweepType) {
case SweepType::Last:
case SweepType::Frequency: case SweepType::Frequency:
type = TraceMath::DataType::Frequency; type = TraceMath::DataType::Frequency;
break; break;
@ -793,16 +842,21 @@ void VNA::SettingsChanged(std::function<void (Device::TransmissionResult)> cb)
s.cdbm_excitation_stop = settings.Power.stop * 100; s.cdbm_excitation_stop = settings.Power.stop * 100;
} }
if(window->getDevice() && Mode::getActiveMode() == this) { if(window->getDevice() && Mode::getActiveMode() == this) {
window->getDevice()->Configure(s, [=](Device::TransmissionResult res){ if(s.excitePort1 == 0 && s.excitePort2 == 0) {
// device received command, reset traces now // no signal at either port, just set the device to idel
average.reset(s.points); window->getDevice()->SetIdle();
traceModel.clearLiveData(); } else {
UpdateAverageCount(); window->getDevice()->Configure(s, [=](Device::TransmissionResult res){
UpdateCalWidget(); // device received command, reset traces now
if(cb) { average.reset(s.points);
cb(res); traceModel.clearLiveData();
} UpdateAverageCount();
}); UpdateCalWidget();
if(cb) {
cb(res);
}
});
}
} }
emit traceModel.SpanChanged(s.f_start, s.f_stop); emit traceModel.SpanChanged(s.f_start, s.f_stop);
} }
@ -1374,3 +1428,23 @@ void VNA::updateGraphColors()
{ {
emit graphColorsChanged(); emit graphColorsChanged();
} }
QString VNA::SweepTypeToString(VNA::SweepType sw)
{
switch(sw) {
case SweepType::Frequency: return "Frequency";
case SweepType::Power: return "Power";
default: return "Unknown";
}
}
VNA::SweepType VNA::SweepTypeFromString(QString s)
{
for(int i=0;i<(int)SweepType::Last;i++) {
if(SweepTypeToString((SweepType) i) == s) {
return (SweepType) i;
}
}
// not found
return SweepType::Last;
}

View File

@ -32,7 +32,12 @@ public:
enum class SweepType { enum class SweepType {
Frequency = 0, Frequency = 0,
Power = 1, Power = 1,
Last,
}; };
static QString SweepTypeToString(SweepType sw);
static SweepType SweepTypeFromString(QString s);
using Settings = struct { using Settings = struct {
SweepType sweepType; SweepType sweepType;
struct { struct {

View File

@ -911,6 +911,7 @@ void AppWindow::FrequencyCalibrationDialog()
nlohmann::json AppWindow::SaveSetup() nlohmann::json AppWindow::SaveSetup()
{ {
nlohmann::json j; nlohmann::json j;
j["activeMode"] = Mode::getActiveMode()->getName().toStdString();
j["VNA"] = vna->toJSON(); j["VNA"] = vna->toJSON();
j["Generator"] = generator->toJSON(); j["Generator"] = generator->toJSON();
j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON(); j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON();
@ -924,6 +925,16 @@ void AppWindow::LoadSetup(nlohmann::json j)
vna->fromJSON(j["VNA"]); vna->fromJSON(j["VNA"]);
generator->fromJSON(j["Generator"]); generator->fromJSON(j["Generator"]);
spectrumAnalyzer->fromJSON(j["SpectrumAnalyzer"]); spectrumAnalyzer->fromJSON(j["SpectrumAnalyzer"]);
// activate the correct mode
QString modeName = QString::fromStdString(j.value("activeMode", ""));
std::vector<Mode*> modes = {vna, generator, spectrumAnalyzer};
for(auto m : modes) {
if(m->getName() == modeName) {
m->activate();
break;
}
}
} }
Device *AppWindow::getDevice() const Device *AppWindow::getDevice() const

View File

@ -88,6 +88,14 @@ void Mode::activate()
} }
activeMode = this; activeMode = this;
// force activation of correct pushbutton in case the mode switch was done via script/setup load.
// This will trigger a second activation of this mode in the signal of the button, but since it is
// already the active mode, this function will just return -> no recursion
for(auto b : modeButtonGroup->buttons()) {
if(b->text() == name) {
b->click();
}
}
if(window->getDevice()) { if(window->getDevice()) {
initializeDevice(); initializeDevice();