From 68dc9842e981357053151f03b1ca5bc8704e974b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Fri, 3 Sep 2021 19:01:22 +0200 Subject: [PATCH] Include mode/sweep/acquisition settings in setup files --- Documentation/UserManual/manual.pdf | Bin 1730033 -> 1730163 bytes Documentation/UserManual/manual.tex | 2 + .../PC_Application/Generator/generator.cpp | 13 ++ Software/PC_Application/Generator/generator.h | 4 +- .../Generator/signalgenwidget.cpp | 38 +++++ .../Generator/signalgenwidget.h | 5 +- .../Generator/signalgenwidget.ui | 8 +- .../SpectrumAnalyzer/spectrumanalyzer.cpp | 138 +++++++++++++++++- .../SpectrumAnalyzer/spectrumanalyzer.h | 9 +- Software/PC_Application/VNA/vna.cpp | 94 ++++++++++-- Software/PC_Application/VNA/vna.h | 5 + Software/PC_Application/appwindow.cpp | 11 ++ Software/PC_Application/mode.cpp | 8 + 13 files changed, 316 insertions(+), 19 deletions(-) diff --git a/Documentation/UserManual/manual.pdf b/Documentation/UserManual/manual.pdf index c833b451a63d27950bd36cc714112404b18b8eaf..be2e63bef1775d58632f6cf14af589f67cc1e1e8 100644 GIT binary patch delta 5187 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 diff --git a/Documentation/UserManual/manual.tex b/Documentation/UserManual/manual.tex index dbd1e46..eac74fe 100644 --- a/Documentation/UserManual/manual.tex +++ b/Documentation/UserManual/manual.tex @@ -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 diff --git a/Software/PC_Application/Generator/generator.cpp b/Software/PC_Application/Generator/generator.cpp index 04dbc6f..8106618 100644 --- a/Software/PC_Application/Generator/generator.cpp +++ b/Software/PC_Application/Generator/generator.cpp @@ -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) { diff --git a/Software/PC_Application/Generator/generator.h b/Software/PC_Application/Generator/generator.h index 3418a1b..0897bf4 100644 --- a/Software/PC_Application/Generator/generator.h +++ b/Software/PC_Application/Generator/generator.h @@ -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(); diff --git a/Software/PC_Application/Generator/signalgenwidget.cpp b/Software/PC_Application/Generator/signalgenwidget.cpp index 01b0218..4b93192 100644 --- a/Software/PC_Application/Generator/signalgenwidget.cpp +++ b/Software/PC_Application/Generator/signalgenwidget.cpp @@ -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 diff --git a/Software/PC_Application/Generator/signalgenwidget.h b/Software/PC_Application/Generator/signalgenwidget.h index 90b72bf..1ebcec9 100644 --- a/Software/PC_Application/Generator/signalgenwidget.h +++ b/Software/PC_Application/Generator/signalgenwidget.h @@ -3,12 +3,13 @@ #include #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(); diff --git a/Software/PC_Application/Generator/signalgenwidget.ui b/Software/PC_Application/Generator/signalgenwidget.ui index a1bfe94..a8da534 100644 --- a/Software/PC_Application/Generator/signalgenwidget.ui +++ b/Software/PC_Application/Generator/signalgenwidget.ui @@ -181,7 +181,7 @@ - 0MHz + @@ -195,7 +195,7 @@ - 100 + @@ -209,7 +209,7 @@ - 100ms + @@ -226,7 +226,7 @@ false - 0MHz + true diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index 79fe142..7778968 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -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; +} diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h index 48bfff1..e319e3d 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h @@ -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(); diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 8466471..83039fa 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -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 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; +} diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index d3b5bf9..eccba3f 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -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 { diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index 1564697..918093f 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -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 modes = {vna, generator, spectrumAnalyzer}; + for(auto m : modes) { + if(m->getName() == modeName) { + m->activate(); + break; + } + } } Device *AppWindow::getDevice() const diff --git a/Software/PC_Application/mode.cpp b/Software/PC_Application/mode.cpp index 4a7cb6d..12e32f6 100644 --- a/Software/PC_Application/mode.cpp +++ b/Software/PC_Application/mode.cpp @@ -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();