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}
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}
\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 Math operations applied to the traces
\item Markers

View File

@ -40,6 +40,19 @@ void Generator::initializeDevice()
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()
{
if(!window->getDevice() || Mode::getActiveMode() != this) {

View File

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

View File

@ -157,6 +157,44 @@ Protocol::GeneratorSettings SignalgeneratorWidget::getDeviceStatus()
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)
{
// TODO constrain to frequency dependent levels

View File

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

View File

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

View File

@ -297,6 +297,37 @@ void SpectrumAnalyzer::initializeDevice()
nlohmann::json SpectrumAnalyzer::toJSON()
{
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["tiles"] = central->toJSON();
j["markers"] = markerModel->toJSON();
@ -305,6 +336,9 @@ nlohmann::json SpectrumAnalyzer::toJSON()
void SpectrumAnalyzer::fromJSON(nlohmann::json j)
{
if(j.is_null()) {
return;
}
if(j.contains("traces")) {
traceModel.fromJSON(j["traces"]);
}
@ -314,6 +348,63 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
if(j.contains("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;
@ -570,7 +661,7 @@ void SpectrumAnalyzer::SetTGEnabled(bool enabled)
void SpectrumAnalyzer::SetTGPort(int port)
{
if(port < 01 || port > 1) {
if(port < 0 || port > 1) {
return;
}
if(port != settings.trackingGeneratorPort) {
@ -971,3 +1062,48 @@ void SpectrumAnalyzer::updateGraphColors()
{
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,
Kaiser = 1,
Hann = 2,
FlatTop = 3
FlatTop = 3,
Last
};
enum class Detector {
PPeak = 0,
@ -40,8 +41,14 @@ private:
Sample = 2,
Normal = 3,
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:
void NewDatapoint(Protocol::SpectrumAnalyzerResult d);
void StartImpedanceMatching();

View File

@ -560,6 +560,8 @@ Calibration::InterpolationType VNA::getCalInterpolation()
{
double f_min, f_max;
switch(settings.sweepType) {
case SweepType::Last:
// should never get here, use frequency values just in case
case SweepType::Frequency:
f_min = settings.Freq.start;
f_max = settings.Freq.stop;
@ -678,6 +680,23 @@ void VNA::shutdown()
nlohmann::json VNA::toJSON()
{
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["tiles"] = central->toJSON();
j["markers"] = markerModel->toJSON();
@ -688,6 +707,9 @@ nlohmann::json VNA::toJSON()
void VNA::fromJSON(nlohmann::json j)
{
if(j.is_null()) {
return;
}
if(j.contains("traces")) {
traceModel.fromJSON(j["traces"]);
}
@ -703,6 +725,32 @@ void VNA::fromJSON(nlohmann::json j)
} else {
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;
@ -735,6 +783,7 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
TraceMath::DataType type;
switch(settings.sweepType) {
case SweepType::Last:
case SweepType::Frequency:
type = TraceMath::DataType::Frequency;
break;
@ -793,16 +842,21 @@ void VNA::SettingsChanged(std::function<void (Device::TransmissionResult)> cb)
s.cdbm_excitation_stop = settings.Power.stop * 100;
}
if(window->getDevice() && Mode::getActiveMode() == this) {
window->getDevice()->Configure(s, [=](Device::TransmissionResult res){
// device received command, reset traces now
average.reset(s.points);
traceModel.clearLiveData();
UpdateAverageCount();
UpdateCalWidget();
if(cb) {
cb(res);
}
});
if(s.excitePort1 == 0 && s.excitePort2 == 0) {
// no signal at either port, just set the device to idel
window->getDevice()->SetIdle();
} else {
window->getDevice()->Configure(s, [=](Device::TransmissionResult res){
// device received command, reset traces now
average.reset(s.points);
traceModel.clearLiveData();
UpdateAverageCount();
UpdateCalWidget();
if(cb) {
cb(res);
}
});
}
}
emit traceModel.SpanChanged(s.f_start, s.f_stop);
}
@ -1374,3 +1428,23 @@ void VNA::updateGraphColors()
{
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 {
Frequency = 0,
Power = 1,
Last,
};
static QString SweepTypeToString(SweepType sw);
static SweepType SweepTypeFromString(QString s);
using Settings = struct {
SweepType sweepType;
struct {

View File

@ -911,6 +911,7 @@ void AppWindow::FrequencyCalibrationDialog()
nlohmann::json AppWindow::SaveSetup()
{
nlohmann::json j;
j["activeMode"] = Mode::getActiveMode()->getName().toStdString();
j["VNA"] = vna->toJSON();
j["Generator"] = generator->toJSON();
j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON();
@ -924,6 +925,16 @@ void AppWindow::LoadSetup(nlohmann::json j)
vna->fromJSON(j["VNA"]);
generator->fromJSON(j["Generator"]);
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

View File

@ -88,6 +88,14 @@ void Mode::activate()
}
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()) {
initializeDevice();