From fa481e206262431422cff8f4c74b95853766f23d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Sat, 15 Jan 2022 17:00:57 +0100 Subject: [PATCH] Save/open preferences --- .../PC_Application/Calibration/calkit.cpp | 74 ++------- Software/PC_Application/Calibration/calkit.h | 145 +++++++++--------- Software/PC_Application/preferences.cpp | 140 +++++++++++------ Software/PC_Application/preferences.h | 16 +- Software/PC_Application/preferencesdialog.ui | 2 +- Software/PC_Application/savable.h | 76 +++++++++ 6 files changed, 257 insertions(+), 196 deletions(-) diff --git a/Software/PC_Application/Calibration/calkit.cpp b/Software/PC_Application/Calibration/calkit.cpp index 3fa14b5..c719f59 100644 --- a/Software/PC_Application/Calibration/calkit.cpp +++ b/Software/PC_Application/Calibration/calkit.cpp @@ -25,7 +25,7 @@ Calkit::Calkit() { // set default values - for(auto e : json_descr) { + for(auto e : descr) { e.var.setValue(e.def); } } @@ -40,24 +40,7 @@ void Calkit::toFile(QString filename) TransformPathsToRelative(filename); - json j; - for(auto e : json_descr) { - auto list = e.name.split("/"); - auto *json_entry = &j; - while(list.size() > 0) { - json_entry = &(*json_entry)[list.takeFirst().toStdString()]; - } - // json library does not now about QVariant, handle used cases - auto val = e.var.value(); - switch(static_cast(val.type())) { - case QMetaType::Double: *json_entry = val.toDouble(); break; - case QMetaType::Int: *json_entry = val.toInt(); break; - case QMetaType::Bool: *json_entry = val.toBool(); break; - case QMetaType::QString: *json_entry = val.toString().toStdString(); break; - default: - throw runtime_error("Unimplemented metatype"); - } - } + json j = Savable::createJSON(descr); ofstream file; file.open(filename.toStdString()); file << setw(4) << j << endl; @@ -90,58 +73,19 @@ Calkit Calkit::fromFile(QString filename) throw runtime_error("JSON parsing error: " + string(e.what())); } if(j.contains("SOLT")) { - // older file versions specify Z0 for resistance. If resistance entry is missing, - // set it to Z0 later - bool loadResistanceMissing_m = false; - bool loadResistanceMissing_f = false; + // older file versions specify Z0 for resistance. Set resistance to Nan to detect missing values later + c.SOLT.load_m.resistance = std::numeric_limits::quiet_NaN(); + c.SOLT.load_f.resistance = std::numeric_limits::quiet_NaN(); qDebug() << "JSON format detected"; // calkit file uses json format, parse - for(auto e : c.json_descr) { - auto list = e.name.split("/"); - auto *json_entry = &j; - bool entry_exists = true; - while(list.size() > 0) { - auto key = list.takeFirst().toStdString(); - if((*json_entry).contains(key)) { - json_entry = &(*json_entry)[key]; - } else { - entry_exists = false; - break; - } - } - if(!entry_exists) { - // missing entry in json file, nothing to do (default values already set in constructor) - qWarning() << "Entry" << e.name << "not present in file, assuming default value"; - if(e.name == "SOLT/Load/Param/Resistance") { - loadResistanceMissing_m = true; - } else if(e.name == "SOLT/Load/Param/Resistance_Female") { - loadResistanceMissing_f = true; - } - continue; - } - // json library does not now about QVariant, handle used cases - auto val = e.var.value(); - switch(static_cast(val.type())) { - case QMetaType::Double: e.var.setValue((*json_entry).get()); break; - case QMetaType::Int: e.var.setValue((*json_entry).get()); break; - case QMetaType::Bool: e.var.setValue((*json_entry).get()); break; - case QMetaType::QString: { - auto s = QString::fromStdString((*json_entry).get()); - e.var.setValue(s); - } - - break; - default: - throw runtime_error("Unimplemented metatype"); - } - } - // adjust Z0/resistance in case of older calkit file version - if(loadResistanceMissing_f) { + Savable::parseJSON(j, c.descr); + // adjust Z0/resistance in case of older calkit file version with missing resistance entries + if(isnan(c.SOLT.load_f.resistance)) { c.SOLT.load_f.resistance = c.SOLT.load_f.Z0; c.SOLT.load_f.Z0 = 50.0; } - if(loadResistanceMissing_m) { + if(isnan(c.SOLT.load_m.resistance)) { c.SOLT.load_m.resistance = c.SOLT.load_m.Z0; c.SOLT.load_m.Z0 = 50.0; } diff --git a/Software/PC_Application/Calibration/calkit.h b/Software/PC_Application/Calibration/calkit.h index 8e3fc85..8947310 100644 --- a/Software/PC_Application/Calibration/calkit.h +++ b/Software/PC_Application/Calibration/calkit.h @@ -105,90 +105,85 @@ private: Touchstone *ts_through; bool ts_cached; - using JSONDescription = struct _jsondescr { - QPointerVariant var; - QString name; - QVariant def; - }; - const std::array json_descr = {{ + const std::vector descr = {{ {&manufacturer, "Manufacturer", ""}, {&serialnumber, "Serialnumber", ""}, {&description, "Description", ""}, - {&SOLT.open_m.Z0, "SOLT/Open/Param/Z0", 50.0}, - {&SOLT.open_m.delay, "SOLT/Open/Param/Delay", 0.0}, - {&SOLT.open_m.loss, "SOLT/Open/Param/Loss", 0.0}, - {&SOLT.open_m.C0, "SOLT/Open/Param/C0", 0.0}, - {&SOLT.open_m.C1, "SOLT/Open/Param/C1", 0.0}, - {&SOLT.open_m.C2, "SOLT/Open/Param/C2", 0.0}, - {&SOLT.open_m.C3, "SOLT/Open/Param/C3", 0.0}, - {&SOLT.open_m.useMeasurements, "SOLT/Open/Measurements/Use", false}, - {&SOLT.open_m.file, "SOLT/Open/Measurements/File", ""}, - {&SOLT.open_m.Sparam, "SOLT/Open/Measurements/Port", 0}, - {&SOLT.open_f.Z0, "SOLT/Open/Param/Z0_Female", 50.0}, - {&SOLT.open_f.delay, "SOLT/Open/Param/Delay_Female", 0.0}, - {&SOLT.open_f.loss, "SOLT/Open/Param/Loss_Female", 0.0}, - {&SOLT.open_f.C0, "SOLT/Open/Param/C0_Female", 0.0}, - {&SOLT.open_f.C1, "SOLT/Open/Param/C1_Female", 0.0}, - {&SOLT.open_f.C2, "SOLT/Open/Param/C2_Female", 0.0}, - {&SOLT.open_f.C3, "SOLT/Open/Param/C3_Female", 0.0}, - {&SOLT.open_f.useMeasurements, "SOLT/Open/Measurements/Use_Female", false}, - {&SOLT.open_f.file, "SOLT/Open/Measurements/File_Female", ""}, - {&SOLT.open_f.Sparam, "SOLT/Open/Measurements/Port_Female", 0}, + {&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0}, + {&SOLT.open_m.delay, "SOLT.Open.Param.Delay", 0.0}, + {&SOLT.open_m.loss, "SOLT.Open.Param.Loss", 0.0}, + {&SOLT.open_m.C0, "SOLT.Open.Param.C0", 0.0}, + {&SOLT.open_m.C1, "SOLT.Open.Param.C1", 0.0}, + {&SOLT.open_m.C2, "SOLT.Open.Param.C2", 0.0}, + {&SOLT.open_m.C3, "SOLT.Open.Param.C3", 0.0}, + {&SOLT.open_m.useMeasurements, "SOLT.Open.Measurements.Use", false}, + {&SOLT.open_m.file, "SOLT.Open.Measurements.File", ""}, + {&SOLT.open_m.Sparam, "SOLT.Open.Measurements.Port", 0}, + {&SOLT.open_f.Z0, "SOLT.Open.Param.Z0_Female", 50.0}, + {&SOLT.open_f.delay, "SOLT.Open.Param.Delay_Female", 0.0}, + {&SOLT.open_f.loss, "SOLT.Open.Param.Loss_Female", 0.0}, + {&SOLT.open_f.C0, "SOLT.Open.Param.C0_Female", 0.0}, + {&SOLT.open_f.C1, "SOLT.Open.Param.C1_Female", 0.0}, + {&SOLT.open_f.C2, "SOLT.Open.Param.C2_Female", 0.0}, + {&SOLT.open_f.C3, "SOLT.Open.Param.C3_Female", 0.0}, + {&SOLT.open_f.useMeasurements, "SOLT.Open.Measurements.Use_Female", false}, + {&SOLT.open_f.file, "SOLT.Open.Measurements.File_Female", ""}, + {&SOLT.open_f.Sparam, "SOLT.Open.Measurements.Port_Female", 0}, - {&SOLT.short_m.Z0, "SOLT/Short/Param/Z0", 50.0}, - {&SOLT.short_m.delay, "SOLT/Short/Param/Delay", 0.0}, - {&SOLT.short_m.loss, "SOLT/Short/Param/Loss", 0.0}, - {&SOLT.short_m.L0, "SOLT/Short/Param/L0", 0.0}, - {&SOLT.short_m.L1, "SOLT/Short/Param/L1", 0.0}, - {&SOLT.short_m.L2, "SOLT/Short/Param/L2", 0.0}, - {&SOLT.short_m.L3, "SOLT/Short/Param/L3", 0.0}, - {&SOLT.short_m.useMeasurements, "SOLT/Short/Measurements/Use", false}, - {&SOLT.short_m.file, "SOLT/Short/Measurements/File", ""}, - {&SOLT.short_m.Sparam, "SOLT/Short/Measurements/Port", 0}, - {&SOLT.short_f.Z0, "SOLT/Short/Param/Z0_Female", 50.0}, - {&SOLT.short_f.delay, "SOLT/Short/Param/Delay_Female", 0.0}, - {&SOLT.short_f.loss, "SOLT/Short/Param/Loss_Female", 0.0}, - {&SOLT.short_f.L0, "SOLT/Short/Param/L0_Female", 0.0}, - {&SOLT.short_f.L1, "SOLT/Short/Param/L1_Female", 0.0}, - {&SOLT.short_f.L2, "SOLT/Short/Param/L2_Female", 0.0}, - {&SOLT.short_f.L3, "SOLT/Short/Param/L3_Female", 0.0}, - {&SOLT.short_f.useMeasurements, "SOLT/Short/Measurements/Use_Female", false}, - {&SOLT.short_f.file, "SOLT/Short/Measurements/File_Female", ""}, - {&SOLT.short_f.Sparam, "SOLT/Short/Measurements/Port_Female", 0}, + {&SOLT.short_m.Z0, "SOLT.Short.Param.Z0", 50.0}, + {&SOLT.short_m.delay, "SOLT.Short.Param.Delay", 0.0}, + {&SOLT.short_m.loss, "SOLT.Short.Param.Loss", 0.0}, + {&SOLT.short_m.L0, "SOLT.Short.Param.L0", 0.0}, + {&SOLT.short_m.L1, "SOLT.Short.Param.L1", 0.0}, + {&SOLT.short_m.L2, "SOLT.Short.Param.L2", 0.0}, + {&SOLT.short_m.L3, "SOLT.Short.Param.L3", 0.0}, + {&SOLT.short_m.useMeasurements, "SOLT.Short.Measurements.Use", false}, + {&SOLT.short_m.file, "SOLT.Short.Measurements.File", ""}, + {&SOLT.short_m.Sparam, "SOLT.Short.Measurements.Port", 0}, + {&SOLT.short_f.Z0, "SOLT.Short.Param.Z0_Female", 50.0}, + {&SOLT.short_f.delay, "SOLT.Short.Param.Delay_Female", 0.0}, + {&SOLT.short_f.loss, "SOLT.Short.Param.Loss_Female", 0.0}, + {&SOLT.short_f.L0, "SOLT.Short.Param.L0_Female", 0.0}, + {&SOLT.short_f.L1, "SOLT.Short.Param.L1_Female", 0.0}, + {&SOLT.short_f.L2, "SOLT.Short.Param.L2_Female", 0.0}, + {&SOLT.short_f.L3, "SOLT.Short.Param.L3_Female", 0.0}, + {&SOLT.short_f.useMeasurements, "SOLT.Short.Measurements.Use_Female", false}, + {&SOLT.short_f.file, "SOLT.Short.Measurements.File_Female", ""}, + {&SOLT.short_f.Sparam, "SOLT.Short.Measurements.Port_Female", 0}, - {&SOLT.load_m.resistance, "SOLT/Load/Param/Resistance", 50.0}, - {&SOLT.load_m.Z0, "SOLT/Load/Param/Z0", 50.0}, - {&SOLT.load_m.delay, "SOLT/Load/Param/Delay", 0.0}, - {&SOLT.load_m.Cparallel, "SOLT/Load/Param/C", 0.0}, - {&SOLT.load_m.Lseries, "SOLT/Load/Param/L", 0.0}, - {&SOLT.load_m.useMeasurements, "SOLT/Load/Measurements/Use", false}, - {&SOLT.load_m.file, "SOLT/Load/Measurements/File", ""}, - {&SOLT.load_m.Sparam, "SOLT/Load/Measurements/Port", 0}, - {&SOLT.load_f.resistance, "SOLT/Load/Param/Resistance_Female", 50.0}, - {&SOLT.load_f.Z0, "SOLT/Load/Param/Z0_Female", 50.0}, - {&SOLT.load_f.delay, "SOLT/Load/Param/Delay_Female", 0.0}, - {&SOLT.load_f.Cparallel, "SOLT/Load/Param/C_Female", 0.0}, - {&SOLT.load_f.Lseries, "SOLT/Load/Param/L_Female", 0.0}, - {&SOLT.load_f.useMeasurements, "SOLT/Load/Measurements/Use_Female", false}, - {&SOLT.load_f.file, "SOLT/Load/Measurements/File_Female", ""}, - {&SOLT.load_f.Sparam, "SOLT/Load/Measurements/Port_Female", 0}, + {&SOLT.load_m.resistance, "SOLT.Load.Param.Resistance", 50.0}, + {&SOLT.load_m.Z0, "SOLT.Load.Param.Z0", 50.0}, + {&SOLT.load_m.delay, "SOLT.Load.Param.Delay", 0.0}, + {&SOLT.load_m.Cparallel, "SOLT.Load.Param.C", 0.0}, + {&SOLT.load_m.Lseries, "SOLT.Load.Param.L", 0.0}, + {&SOLT.load_m.useMeasurements, "SOLT.Load.Measurements.Use", false}, + {&SOLT.load_m.file, "SOLT.Load.Measurements.File", ""}, + {&SOLT.load_m.Sparam, "SOLT.Load.Measurements.Port", 0}, + {&SOLT.load_f.resistance, "SOLT.Load.Param.Resistance_Female", 50.0}, + {&SOLT.load_f.Z0, "SOLT.Load.Param.Z0_Female", 50.0}, + {&SOLT.load_f.delay, "SOLT.Load.Param.Delay_Female", 0.0}, + {&SOLT.load_f.Cparallel, "SOLT.Load.Param.C_Female", 0.0}, + {&SOLT.load_f.Lseries, "SOLT.Load.Param.L_Female", 0.0}, + {&SOLT.load_f.useMeasurements, "SOLT.Load.Measurements.Use_Female", false}, + {&SOLT.load_f.file, "SOLT.Load.Measurements.File_Female", ""}, + {&SOLT.load_f.Sparam, "SOLT.Load.Measurements.Port_Female", 0}, - {&SOLT.Through.Z0, "SOLT/Through/Param/Z0", 50.0}, - {&SOLT.Through.delay, "SOLT/Through/Param/Delay", 0.0}, - {&SOLT.Through.loss, "SOLT/Through/Param/Loss", 0.0}, - {&SOLT.Through.useMeasurements, "SOLT/Through/Measurements/Use", false}, - {&SOLT.Through.file, "SOLT/Through/Measurements/File", ""}, - {&SOLT.Through.Sparam1, "SOLT/Through/Measurements/Port1", 0}, - {&SOLT.Through.Sparam2, "SOLT/Through/Measurements/Port2", 1}, + {&SOLT.Through.Z0, "SOLT.Through.Param.Z0", 50.0}, + {&SOLT.Through.delay, "SOLT.Through.Param.Delay", 0.0}, + {&SOLT.Through.loss, "SOLT.Through.Param.Loss", 0.0}, + {&SOLT.Through.useMeasurements, "SOLT.Through.Measurements.Use", false}, + {&SOLT.Through.file, "SOLT.Through.Measurements.File", ""}, + {&SOLT.Through.Sparam1, "SOLT.Through.Measurements.Port1", 0}, + {&SOLT.Through.Sparam2, "SOLT.Through.Measurements.Port2", 1}, - {&SOLT.separate_male_female, "SOLT/SeparateMaleFemale", false}, + {&SOLT.separate_male_female, "SOLT.SeparateMaleFemale", false}, - {&TRL.Through.Z0, "TRL/Through/Z0", 50.0}, - {&TRL.Reflection.isShort, "TRL/Reflect/isShort", false}, - {&TRL.Line.delay, "TRL/Line/Delay", 74.0}, - {&TRL.Line.minFreq, "TRL/Line/minFreq", 751000000.0}, - {&TRL.Line.maxFreq, "TRL/Line/maxFreq", 6000000000.0}, + {&TRL.Through.Z0, "TRL.Through.Z0", 50.0}, + {&TRL.Reflection.isShort, "TRL.Reflect.isShort", false}, + {&TRL.Line.delay, "TRL.Line.Delay", 74.0}, + {&TRL.Line.minFreq, "TRL.Line.minFreq", 751000000.0}, + {&TRL.Line.maxFreq, "TRL.Line.maxFreq", 6000000000.0}, {&startDialogWithSOLT, "StartDialogWithSOLT", true} }}; diff --git a/Software/PC_Application/preferences.cpp b/Software/PC_Application/preferences.cpp index 87241e1..3b806af 100644 --- a/Software/PC_Application/preferences.cpp +++ b/Software/PC_Application/preferences.cpp @@ -6,8 +6,12 @@ #include #include #include -#include #include +#include + +#include +#include +#include using namespace std; @@ -128,55 +132,34 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) : }); connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, [=](){ // apply GUI state to settings - p->Startup.ConnectToFirstDevice = ui->StartupAutoconnect->isChecked(); - p->Startup.RememberSweepSettings = ui->StartupSweepLastUsed->isChecked(); - p->Startup.DefaultSweep.type = ui->StartupSweepType->currentText(); - p->Startup.DefaultSweep.f_start = ui->StartupSweepStart->value(); - p->Startup.DefaultSweep.f_stop = ui->StartupSweepStop->value(); - p->Startup.DefaultSweep.f_excitation = ui->StartupSweepLevel->value(); - p->Startup.DefaultSweep.dbm_start = ui->StartupSweepPowerStart->value(); - p->Startup.DefaultSweep.dbm_stop = ui->StartupSweepPowerStop->value(); - p->Startup.DefaultSweep.dbm_freq = ui->StartupSweepPowerFrequency->value(); - p->Startup.DefaultSweep.bandwidth = ui->StartupSweepBandwidth->value(); - p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value(); - p->Startup.DefaultSweep.averaging = ui->StartupSweepAveraging->value(); - p->Startup.Generator.frequency = ui->StartupGeneratorFrequency->value(); - p->Startup.Generator.level = ui->StartupGeneratorLevel->value(); - p->Startup.SA.start = ui->StartupSAStart->value(); - p->Startup.SA.stop = ui->StartupSAStop->value(); - p->Startup.SA.RBW = ui->StartupSARBW->value(); - p->Startup.SA.window = ui->StartupSAWindow->currentIndex(); - p->Startup.SA.detector = ui->StartupSADetector->currentIndex(); - p->Startup.SA.signalID = ui->StartupSASignalID->isChecked(); - - p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked(); - p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked(); - p->Acquisition.adjustPowerLevel = ui->AcquisitionAdjustPowerLevel->isChecked(); - p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked(); - p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked(); - p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value(); - p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1; - p->Acquisition.IF1 = ui->AcquisitionIF1->value(); - p->Acquisition.ADCprescaler = ui->AcquisitionADCpresc->value(); - p->Acquisition.DFTPhaseInc = ui->AcquisitionADCphaseInc->value(); - - p->Graphs.showUnits = ui->GraphsShowUnit->isChecked(); - p->Graphs.Color.background = ui->GraphsColorBackground->getColor(); - p->Graphs.Color.axis = ui->GraphsColorAxis->getColor(); - p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked(); - p->Graphs.Color.Ticks.Background.background = ui->GraphsColorTicksBackground->getColor(); - p->Graphs.Color.Ticks.divisions = ui->GraphsColorTicksDivisions->getColor(); - p->Graphs.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex(); - p->Graphs.lineWidth = ui->GraphsLineWidth->value(); - - p->Marker.defaultBehavior.showDataOnGraphs = ui->MarkerShowMarkerData->isChecked(); - p->Marker.defaultBehavior.showAllData = ui->MarkerShowAllMarkerData->isChecked(); - p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1; - - p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked(); - p->SCPIServer.port = ui->SCPIServerPort->value(); + updateFromGUI(); accept(); }); + connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ + auto filename = QFileDialog::getSaveFileName(this, "Save preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, QFileDialog::DontUseNativeDialog); + if(filename.length() > 0) { + if(!filename.toLower().endsWith(".vnapref")) { + filename.append(".vnapref"); + } + ofstream file; + file.open(filename.toStdString()); + updateFromGUI(); + file << setw(1) << p->toJSON(); + file.close(); + } + }); + connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ + auto filename = QFileDialog::getOpenFileName(this, "Load preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, QFileDialog::DontUseNativeDialog); + if(filename.length() > 0) { + ifstream file; + file.open(filename.toStdString()); + nlohmann::json j; + file >> j; + file.close(); + p->fromJSON(j); + setInitialGUIState(); + } + }); setInitialGUIState(); updateADCRate(); @@ -259,6 +242,57 @@ void PreferencesDialog::setInitialGUIState() } } +void PreferencesDialog::updateFromGUI() +{ + p->Startup.ConnectToFirstDevice = ui->StartupAutoconnect->isChecked(); + p->Startup.RememberSweepSettings = ui->StartupSweepLastUsed->isChecked(); + p->Startup.DefaultSweep.type = ui->StartupSweepType->currentText(); + p->Startup.DefaultSweep.f_start = ui->StartupSweepStart->value(); + p->Startup.DefaultSweep.f_stop = ui->StartupSweepStop->value(); + p->Startup.DefaultSweep.f_excitation = ui->StartupSweepLevel->value(); + p->Startup.DefaultSweep.dbm_start = ui->StartupSweepPowerStart->value(); + p->Startup.DefaultSweep.dbm_stop = ui->StartupSweepPowerStop->value(); + p->Startup.DefaultSweep.dbm_freq = ui->StartupSweepPowerFrequency->value(); + p->Startup.DefaultSweep.bandwidth = ui->StartupSweepBandwidth->value(); + p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value(); + p->Startup.DefaultSweep.averaging = ui->StartupSweepAveraging->value(); + p->Startup.Generator.frequency = ui->StartupGeneratorFrequency->value(); + p->Startup.Generator.level = ui->StartupGeneratorLevel->value(); + p->Startup.SA.start = ui->StartupSAStart->value(); + p->Startup.SA.stop = ui->StartupSAStop->value(); + p->Startup.SA.RBW = ui->StartupSARBW->value(); + p->Startup.SA.window = ui->StartupSAWindow->currentIndex(); + p->Startup.SA.detector = ui->StartupSADetector->currentIndex(); + p->Startup.SA.signalID = ui->StartupSASignalID->isChecked(); + + p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked(); + p->Acquisition.suppressPeaks = ui->AcquisitionSuppressPeaks->isChecked(); + p->Acquisition.adjustPowerLevel = ui->AcquisitionAdjustPowerLevel->isChecked(); + p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked(); + p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked(); + p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value(); + p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1; + p->Acquisition.IF1 = ui->AcquisitionIF1->value(); + p->Acquisition.ADCprescaler = ui->AcquisitionADCpresc->value(); + p->Acquisition.DFTPhaseInc = ui->AcquisitionADCphaseInc->value(); + + p->Graphs.showUnits = ui->GraphsShowUnit->isChecked(); + p->Graphs.Color.background = ui->GraphsColorBackground->getColor(); + p->Graphs.Color.axis = ui->GraphsColorAxis->getColor(); + p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked(); + p->Graphs.Color.Ticks.Background.background = ui->GraphsColorTicksBackground->getColor(); + p->Graphs.Color.Ticks.divisions = ui->GraphsColorTicksDivisions->getColor(); + p->Graphs.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex(); + p->Graphs.lineWidth = ui->GraphsLineWidth->value(); + + p->Marker.defaultBehavior.showDataOnGraphs = ui->MarkerShowMarkerData->isChecked(); + p->Marker.defaultBehavior.showAllData = ui->MarkerShowAllMarkerData->isChecked(); + p->Marker.interpolatePoints = ui->MarkerInterpolate->currentIndex() == 1; + + p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked(); + p->SCPIServer.port = ui->SCPIServerPort->value(); +} + void Preferences::load() { QSettings settings; @@ -296,3 +330,13 @@ void Preferences::setDefault() d.var.setValue(d.def); } } + +void Preferences::fromJSON(nlohmann::json j) +{ + parseJSON(j, descr); +} + +nlohmann::json Preferences::toJSON() +{ + return createJSON(descr); +} diff --git a/Software/PC_Application/preferences.h b/Software/PC_Application/preferences.h index a5091f4..1e05839 100644 --- a/Software/PC_Application/preferences.h +++ b/Software/PC_Application/preferences.h @@ -2,6 +2,7 @@ #define PREFERENCESDIALOG_H #include "Util/qpointervariant.h" +#include "savable.h" #include #include @@ -16,7 +17,7 @@ enum GraphDomainChangeBehavior { Q_DECLARE_METATYPE(GraphDomainChangeBehavior); -class Preferences { +class Preferences : public Savable { public: static Preferences& getInstance() { return instance; @@ -104,16 +105,16 @@ public: } SCPIServer; bool TCPoverride; // in case of manual port specification via command line + + void fromJSON(nlohmann::json j) override; + nlohmann::json toJSON() override; + private: Preferences() : TCPoverride(false) {}; static Preferences instance; - using SettingDescription = struct { - QPointerVariant var; - QString name; - QVariant def; - }; - const std::array descr = {{ + + const std::vector descr = {{ {&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true}, {&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false}, {&Startup.DefaultSweep.type, "Startup.DefaultSweep.type", "Frequency"}, @@ -176,6 +177,7 @@ public: private: void setInitialGUIState(); + void updateFromGUI(); Ui::PreferencesDialog *ui; Preferences *p; }; diff --git a/Software/PC_Application/preferencesdialog.ui b/Software/PC_Application/preferencesdialog.ui index 1b0fa87..1fea81e 100644 --- a/Software/PC_Application/preferencesdialog.ui +++ b/Software/PC_Application/preferencesdialog.ui @@ -1144,7 +1144,7 @@ Qt::Horizontal - QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults + QDialogButtonBox::Ok|QDialogButtonBox::Open|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save false diff --git a/Software/PC_Application/savable.h b/Software/PC_Application/savable.h index 75845bd..ebb146e 100644 --- a/Software/PC_Application/savable.h +++ b/Software/PC_Application/savable.h @@ -2,11 +2,87 @@ #define SAVABLE_H #include "json.hpp" +#include "Util/qpointervariant.h" + +#include +#include +#include + +#include +#include class Savable { public: virtual nlohmann::json toJSON() = 0; virtual void fromJSON(nlohmann::json j) = 0; + + using SettingDescription = struct { + QPointerVariant var; + QString name; + QVariant def; + }; + static void parseJSON(nlohmann::json j, std::vector descr) { + for(auto e : descr) { + auto list = e.name.split("."); + auto *json_entry = &j; + bool entry_exists = true; + while(list.size() > 0) { + auto key = list.takeFirst().toStdString(); + if((*json_entry).contains(key)) { + json_entry = &(*json_entry)[key]; + } else { + entry_exists = false; + break; + } + } + if(!entry_exists) { + // missing entry in json file, nothing to do (default values already set in constructor) + qWarning() << "Entry" << e.name << "not present in file"; + continue; + } + // json library does not now about QVariant, handle used cases + auto val = e.var.value(); + switch(static_cast(val.type())) { + case QMetaType::Double: e.var.setValue((*json_entry).get()); break; + case QMetaType::Int: e.var.setValue((*json_entry).get()); break; + case QMetaType::Bool: e.var.setValue((*json_entry).get()); break; + case QMetaType::QString: { + auto s = QString::fromStdString((*json_entry).get()); + e.var.setValue(s); + } + break; + case QMetaType::QColor: { + auto s = QString::fromStdString((*json_entry).get()); + e.var.setValue(QColor(s)); + } + break; + default: + throw std::runtime_error("Unimplemented metatype:"+std::string(val.typeName())); + } + } + } + static nlohmann::json createJSON(std::vector descr) { + nlohmann::json j; + for(auto e : descr) { + auto list = e.name.split("."); + auto *json_entry = &j; + while(list.size() > 0) { + json_entry = &(*json_entry)[list.takeFirst().toStdString()]; + } + // json library does not now about QVariant, handle used cases + auto val = e.var.value(); + switch(static_cast(val.type())) { + case QMetaType::Double: *json_entry = val.toDouble(); break; + case QMetaType::Int: *json_entry = val.toInt(); break; + case QMetaType::Bool: *json_entry = val.toBool(); break; + case QMetaType::QString: *json_entry = val.toString().toStdString(); break; + case QMetaType::QColor: *json_entry = val.value().name().toStdString(); break; + default: + throw std::runtime_error("Unimplemented metatype:"+std::string(val.typeName())); + } + } + return j; + } }; #endif // SAVABLE_H