#include "vna.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "unit.h" #include #include "CustomWidgets/toggleswitch.h" #include "Device/manualcontroldialog.h" #include "Traces/tracemodel.h" #include "tracewidgetvna.h" #include "Traces/tracesmithchart.h" #include "Traces/tracexyplot.h" #include "Traces/traceimportdialog.h" #include "CustomWidgets/tilewidget.h" #include "CustomWidgets/siunitedit.h" #include #include "Traces/markerwidget.h" #include "Tools/impedancematchdialog.h" #include "Calibration/calibrationtracedialog.h" #include "ui_main.h" #include "Device/firmwareupdatedialog.h" #include "preferences.h" #include "Generator/signalgenwidget.h" #include #include #include #include #include "CustomWidgets/informationbox.h" #include VNA::VNA(AppWindow *window) : Mode(window, "Vector Network Analyzer"), deembedding(traceModel), central(new TileWidget(traceModel)) { averages = 1; calValid = false; calMeasuring = false; calDialog.reset(); calEdited = false; // Create default traces auto tS11 = new Trace("S11", Qt::yellow); tS11->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::S11); traceModel.addTrace(tS11); auto tS12 = new Trace("S12", Qt::blue); tS12->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::S12); traceModel.addTrace(tS12); auto tS21 = new Trace("S21", Qt::green); tS21->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::S21); traceModel.addTrace(tS21); auto tS22 = new Trace("S22", Qt::red); tS22->fromLivedata(Trace::LivedataType::Overwrite, Trace::LiveParameter::S22); traceModel.addTrace(tS22); auto tracesmith1 = new TraceSmithChart(traceModel); tracesmith1->enableTrace(tS11, true); auto tracesmith2 = new TraceSmithChart(traceModel); tracesmith2->enableTrace(tS22, true); auto traceXY1 = new TraceXYPlot(traceModel); traceXY1->enableTrace(tS12, true); auto traceXY2 = new TraceXYPlot(traceModel); traceXY2->enableTrace(tS21, true); connect(&traceModel, &TraceModel::requiredExcitation, this, &VNA::ExcitationRequired); central->splitVertically(); central->Child1()->splitHorizontally(); central->Child2()->splitHorizontally(); central->Child1()->Child1()->setPlot(tracesmith1); central->Child1()->Child2()->setPlot(traceXY1); central->Child2()->Child1()->setPlot(traceXY2); central->Child2()->Child2()->setPlot(tracesmith2); // Create menu entries and connections auto calMenu = new QMenu("Calibration", window); window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu); actions.insert(calMenu->menuAction()); auto calLoad = calMenu->addAction("Load"); saveCal = calMenu->addAction("Save"); calMenu->addSeparator(); saveCal->setEnabled(false); connect(calLoad, &QAction::triggered, [=](){ cal.openFromFile(); if(cal.getType() == Calibration::Type::None) { DisableCalibration(); } else { ApplyCalibration(cal.getType()); } calEdited = false; }); connect(saveCal, &QAction::triggered, [=](){ if(cal.saveToFile()) { calEdited = false; } }); auto calDisable = calMenu->addAction("Disabled"); calDisable->setCheckable(true); calDisable->setChecked(true); calMenu->addSeparator(); auto calData = calMenu->addAction("Calibration Measurements"); connect(calData, &QAction::triggered, [=](){ StartCalibrationDialog(); }); auto calEditKit = calMenu->addAction("Edit Calibration Kit"); connect(calEditKit, &QAction::triggered, [=](){ cal.getCalibrationKit().edit([=](){ if(calValid) { ApplyCalibration(cal.getType()); } }); }); calMenu->addSeparator(); auto calImportTerms = calMenu->addAction("Import error terms as traces"); calImportTerms->setEnabled(false); connect(calImportTerms, &QAction::triggered, [=](){ auto import = new TraceImportDialog(traceModel, cal.getErrorTermTraces()); import->show(); }); auto calImportMeas = calMenu->addAction("Import measurements as traces"); calImportMeas->setEnabled(false); connect(calImportMeas, &QAction::triggered, [=](){ auto import = new TraceImportDialog(traceModel, cal.getMeasurementTraces()); import->show(); }); // portExtension.setCalkit(&cal.getCalibrationKit()); // Tools menu auto toolsMenu = new QMenu("Tools", window); window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), toolsMenu); actions.insert(toolsMenu->menuAction()); auto impedanceMatching = toolsMenu->addAction("Impedance Matching"); connect(impedanceMatching, &QAction::triggered, this, &VNA::StartImpedanceMatching); auto confDeembed = toolsMenu->addAction("De-embedding"); connect(confDeembed, &QAction::triggered, &deembedding, &Deembedding::configure); defaultCalMenu = new QMenu("Default Calibration", window); assignDefaultCal = defaultCalMenu->addAction("Assign..."); removeDefaultCal = defaultCalMenu->addAction("Remove"); removeDefaultCal->setEnabled(false); defaultCalMenu->setEnabled(false); actions.insert(window->getUi()->menuDevice->addSeparator()); window->getUi()->menuDevice->addMenu(defaultCalMenu); actions.insert(defaultCalMenu->menuAction()); connect(assignDefaultCal, &QAction::triggered, [=](){ if(window->getDevice()) { auto key = "DefaultCalibration"+window->getDevice()->serial(); QSettings settings; auto filename = QFileDialog::getOpenFileName(nullptr, "Load calibration data", settings.value(key).toString(), "Calibration files (*.cal)", nullptr, QFileDialog::DontUseNativeDialog); if(!filename.isEmpty()) { settings.setValue(key, filename); removeDefaultCal->setEnabled(true); } } }); connect(removeDefaultCal, &QAction::triggered, [=](){ QSettings settings; settings.remove("DefaultCalibration"+window->getDevice()->serial()); removeDefaultCal->setEnabled(false); }); // Sweep toolbar auto tb_sweep = new QToolBar("Sweep"); auto eStart = new SIUnitEdit("Hz", " kMG", 6); // calculate width required with expected string length auto width = QFontMetrics(eStart->font()).width("3.00000GHz") + 15; eStart->setFixedWidth(width); eStart->setToolTip("Start frequency"); connect(eStart, &SIUnitEdit::valueChanged, this, &VNA::SetStartFreq); connect(this, &VNA::startFreqChanged, eStart, &SIUnitEdit::setValueQuiet); tb_sweep->addWidget(new QLabel("Start:")); tb_sweep->addWidget(eStart); auto eCenter = new SIUnitEdit("Hz", " kMG", 6); eCenter->setFixedWidth(width); eCenter->setToolTip("Center frequency"); connect(eCenter, &SIUnitEdit::valueChanged, this, &VNA::SetCenterFreq); connect(this, &VNA::centerFreqChanged, eCenter, &SIUnitEdit::setValueQuiet); tb_sweep->addWidget(new QLabel("Center:")); tb_sweep->addWidget(eCenter); auto eStop = new SIUnitEdit("Hz", " kMG", 6); eStop->setFixedWidth(width); eStop->setToolTip("Stop frequency"); connect(eStop, &SIUnitEdit::valueChanged, this, &VNA::SetStopFreq); connect(this, &VNA::stopFreqChanged, eStop, &SIUnitEdit::setValueQuiet); tb_sweep->addWidget(new QLabel("Stop:")); tb_sweep->addWidget(eStop); auto eSpan = new SIUnitEdit("Hz", " kMG", 6); eSpan->setFixedWidth(width); eSpan->setToolTip("Span"); connect(eSpan, &SIUnitEdit::valueChanged, this, &VNA::SetSpan); connect(this, &VNA::spanChanged, eSpan, &SIUnitEdit::setValueQuiet); tb_sweep->addWidget(new QLabel("Span:")); tb_sweep->addWidget(eSpan); auto bFull = new QPushButton(QIcon::fromTheme("zoom-fit-best", QIcon(":/icons/zoom-fit.png")), ""); bFull->setToolTip("Full span"); connect(bFull, &QPushButton::clicked, this, &VNA::SetFullSpan); tb_sweep->addWidget(bFull); auto bZoomIn = new QPushButton(QIcon::fromTheme("zoom-in", QIcon(":/icons/zoom-in.png")), ""); bZoomIn->setToolTip("Zoom in"); connect(bZoomIn, &QPushButton::clicked, this, &VNA::SpanZoomIn); tb_sweep->addWidget(bZoomIn); auto bZoomOut = new QPushButton(QIcon::fromTheme("zoom-out", QIcon(":/icons/zoom-out.png")), ""); bZoomOut->setToolTip("Zoom out"); connect(bZoomOut, &QPushButton::clicked, this, &VNA::SpanZoomOut); tb_sweep->addWidget(bZoomOut); window->addToolBar(tb_sweep); toolbars.insert(tb_sweep); // Acquisition toolbar auto tb_acq = new QToolBar("Acquisition"); auto dbm = new QDoubleSpinBox(); width = QFontMetrics(dbm->font()).width("-30.00dBm") + 20; dbm->setFixedWidth(width); dbm->setRange(-100.0, 100.0); dbm->setSingleStep(0.25); dbm->setSuffix("dbm"); dbm->setToolTip("Stimulus level"); dbm->setKeyboardTracking(false); connect(dbm, qOverload(&QDoubleSpinBox::valueChanged), this, &VNA::SetSourceLevel); connect(this, &VNA::sourceLevelChanged, dbm, &QDoubleSpinBox::setValue); tb_acq->addWidget(new QLabel("Level:")); tb_acq->addWidget(dbm); auto points = new QSpinBox(); points->setFixedWidth(55); points->setRange(1, 9999); points->setSingleStep(100); points->setToolTip("Points/sweep"); points->setKeyboardTracking(false); connect(points, qOverload(&QSpinBox::valueChanged), this, &VNA::SetPoints); connect(this, &VNA::pointsChanged, [=](int p) { points->blockSignals(true); points->setValue(p); points->blockSignals(false); }); tb_acq->addWidget(new QLabel("Points:")); tb_acq->addWidget(points); auto eBandwidth = new SIUnitEdit("Hz", " k", 3); eBandwidth->setFixedWidth(70); eBandwidth->setToolTip("IF bandwidth"); connect(eBandwidth, &SIUnitEdit::valueChanged, this, &VNA::SetIFBandwidth); connect(this, &VNA::IFBandwidthChanged, eBandwidth, &SIUnitEdit::setValueQuiet); tb_acq->addWidget(new QLabel("IF BW:")); tb_acq->addWidget(eBandwidth); tb_acq->addWidget(new QLabel("Averaging:")); lAverages = new QLabel("0/"); tb_acq->addWidget(lAverages); auto sbAverages = new QSpinBox; sbAverages->setRange(1, 99); sbAverages->setFixedWidth(40); connect(sbAverages, qOverload(&QSpinBox::valueChanged), this, &VNA::SetAveraging); connect(this, &VNA::averagingChanged, sbAverages, &QSpinBox::setValue); tb_acq->addWidget(sbAverages); window->addToolBar(tb_acq); toolbars.insert(tb_acq); // Calibration toolbar (and populate calibration menu) auto tb_cal = new QToolBar("Calibration"); calLabel = new QLabel("Calibration:"); UpdateCalWidget(); tb_cal->addWidget(calLabel); auto cbEnableCal = new QCheckBox; tb_cal->addWidget(cbEnableCal); auto cbType = new QComboBox(); auto calMenuGroup = new QActionGroup(this); calMenuGroup->addAction(calDisable); for(auto type : Calibration::Types()) { cbType->addItem(Calibration::TypeToString(type), (int) type); auto menuAction = new QAction(Calibration::TypeToString(type), calMenu); calMenuGroup->addAction(menuAction); connect(menuAction, &QAction::triggered, [=](){ ApplyCalibration(type); }); connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){ if(type == applied) { menuAction->setChecked(true); } }); menuAction->setCheckable(true); calMenu->insertAction(calDisable, menuAction); } auto calToolbarLambda = [=]() { if(cbEnableCal->isChecked()) { // Get requested calibration type from combobox ApplyCalibration((Calibration::Type) cbType->itemData(cbType->currentIndex()).toInt()); } else { DisableCalibration(); } }; // Calibration connections connect(cbEnableCal, &QCheckBox::stateChanged, calToolbarLambda); connect(cbType, qOverload(&QComboBox::currentIndexChanged), calToolbarLambda); connect(this, &VNA::CalibrationDisabled, [=](){ cbType->blockSignals(true); cbEnableCal->blockSignals(true); calDisable->setChecked(true); cbEnableCal->setCheckState(Qt::CheckState::Unchecked); // visually indicate loss of calibration // cal. file unknown at this moment UpdateCalWidget(); cbType->blockSignals(false); cbEnableCal->blockSignals(false); calImportTerms->setEnabled(false); calImportMeas->setEnabled(false); saveCal->setEnabled(false); }); connect(calDisable, &QAction::triggered, this, &VNA::DisableCalibration); connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){ cbType->blockSignals(true); cbEnableCal->blockSignals(true); for(int i=0;icount();i++) { if(cbType->itemData(i).toInt() == (int) applied) { cbType->setCurrentIndex(i); break; } } cbEnableCal->setCheckState(Qt::CheckState::Checked); // restore default look of widget // on hover, show name of active cal. file UpdateCalWidget(); cbType->blockSignals(false); cbEnableCal->blockSignals(false); calImportTerms->setEnabled(true); calImportMeas->setEnabled(true); saveCal->setEnabled(true); }); tb_cal->addWidget(cbType); window->addToolBar(tb_cal); toolbars.insert(tb_cal); // auto tb_portExtension = portExtension.createToolbar(); // window->addToolBar(tb_portExtension); // toolbars.insert(tb_portExtension); markerModel = new TraceMarkerModel(traceModel, this); auto tracesDock = new QDockWidget("Traces"); tracesDock->setWidget(new TraceWidgetVNA(traceModel)); window->addDockWidget(Qt::LeftDockWidgetArea, tracesDock); docks.insert(tracesDock); auto markerWidget = new MarkerWidget(*markerModel); auto markerDock = new QDockWidget("Marker"); markerDock->setWidget(markerWidget); window->addDockWidget(Qt::BottomDockWidgetArea, markerDock); docks.insert(markerDock); // Set initial sweep settings auto pref = Preferences::getInstance(); if(pref.Acquisition.alwaysExciteBothPorts) { settings.excitePort1 = 1; settings.excitePort2 = 1; } else { settings.excitePort1 = traceModel.PortExcitationRequired(1); settings.excitePort2 = traceModel.PortExcitationRequired(2); } if(pref.Startup.RememberSweepSettings) { LoadSweepSettings(); } else { settings.f_start = pref.Startup.DefaultSweep.start; settings.f_stop = pref.Startup.DefaultSweep.stop; ConstrainAndUpdateFrequencies(); SetSourceLevel(pref.Startup.DefaultSweep.excitation); SetIFBandwidth(pref.Startup.DefaultSweep.bandwidth); SetAveraging(pref.Startup.DefaultSweep.averaging); SetPoints(pref.Startup.DefaultSweep.points); } // Set ObjectName for toolbars and docks for(auto d : findChildren()) { d->setObjectName(d->windowTitle()); } for(auto t : findChildren()) { t->setObjectName(t->windowTitle()); } finalize(central); } QString VNA::getCalStyle() { Calibration::InterpolationType interpol = cal.getInterpolation(settings); QString style = ""; switch (interpol) { case Calibration::InterpolationType::Unchanged: case Calibration::InterpolationType::Exact: case Calibration::InterpolationType::Interpolate: style = ""; break; case Calibration::InterpolationType::Extrapolate: style = "background-color: yellow"; break; case Calibration::InterpolationType::NoCalibration: style = "background-color: red"; break; } return style; } QString VNA::getCalToolTip() { Calibration::InterpolationType interpol = cal.getInterpolation(settings); QString txt = ""; switch (interpol) { case Calibration::InterpolationType::Unchanged: case Calibration::InterpolationType::Exact: case Calibration::InterpolationType::Interpolate: case Calibration::InterpolationType::Extrapolate: { QString lo = Unit::ToString(cal.getMinFreq(), "", " kMG", 5); QString hi = Unit::ToString(cal.getMaxFreq(), "", " kMG", 5); if (settings.f_start < cal.getMinFreq() ) { lo = "" + lo + "";} if (settings.f_stop > cal.getMaxFreq() ) { hi = "" + hi + "";} txt = "limits: " + lo + " - " + hi + "
" + "points: " + QString::number(cal.getNumPoints()) + "
" "file: " + cal.getCurrentCalibrationFile(); break; } case Calibration::InterpolationType::NoCalibration: txt = "none"; break; } return txt; } void VNA::deactivate() { StoreSweepSettings(); Mode::deactivate(); } void VNA::initializeDevice() { defaultCalMenu->setEnabled(true); connect(window->getDevice(), &Device::DatapointReceived, this, &VNA::NewDatapoint, Qt::UniqueConnection); // Check if default calibration exists and attempt to load it QSettings s; auto key = "DefaultCalibration"+window->getDevice()->serial(); if (s.contains(key)) { auto filename = s.value(key).toString(); qDebug() << "Attempting to load default calibration file " << filename; if(QFile::exists(filename)) { if(cal.openFromFile(filename)) { ApplyCalibration(cal.getType()); // portExtension.setCalkit(&cal.getCalibrationKit()); qDebug() << "Calibration successful from " << filename; } else { qDebug() << "Calibration not successfull from: " << filename; } } else { qDebug() << "Calibration file not found: " << filename; } removeDefaultCal->setEnabled(true); } else { qDebug() << "No default calibration file set for this device"; removeDefaultCal->setEnabled(false); } // Configure initial state of device SettingsChanged(); } void VNA::deviceDisconnected() { defaultCalMenu->setEnabled(false); } void VNA::shutdown() { if(calEdited && calValid) { auto save = InformationBox::AskQuestion("Save calibration?", "The calibration contains data that has not been saved yet. Do you want to save it before exiting?", false); if(save) { cal.saveToFile(); } } } nlohmann::json VNA::toJSON() { nlohmann::json j; j["traces"] = traceModel.toJSON(); j["tiles"] = central->toJSON(); j["markers"] = markerModel->toJSON(); j["de-embedding"] = deembedding.toJSON(); return j; } void VNA::fromJSON(nlohmann::json j) { if(j.contains("traces")) { traceModel.fromJSON(j["traces"]); } if(j.contains("tiles")) { central->fromJSON(j["tiles"]); } if(j.contains("markers")) { markerModel->fromJSON(j["markers"]); } if(j.contains("de-embedding")) { deembedding.fromJSON(j["de-embedding"]); } } using namespace std; void VNA::NewDatapoint(Protocol::Datapoint d) { d = average.process(d); if(calMeasuring) { if(average.currentSweep() == averages) { // this is the last averaging sweep, use values for calibration if(!calWaitFirst || d.pointNum == 0) { calWaitFirst = false; cal.addMeasurement(calMeasurement, d); if(d.pointNum == settings.points - 1) { calMeasuring = false; qDebug() << "Calibration measurement" << cal.MeasurementToString(calMeasurement) << "complete"; emit CalibrationMeasurementComplete(calMeasurement); } } } int percentage = (((average.currentSweep() - 1) * 100) + (d.pointNum + 1) * 100 / settings.points) / averages; calDialog.setValue(percentage); } if(calValid) { cal.correctMeasurement(d); } deembedding.Deembed(d); traceModel.addVNAData(d, settings); emit dataChanged(); if(d.pointNum == settings.points - 1) { UpdateAverageCount(); markerModel->updateMarkers(); } static unsigned int lastPoint = 0; if(d.pointNum > 0 && d.pointNum != lastPoint + 1) { qWarning() << "Got point" << d.pointNum << "but last received point was" << lastPoint << "("<<(d.pointNum-lastPoint-1)<<"missed points)"; } lastPoint = d.pointNum; } void VNA::UpdateAverageCount() { lAverages->setText(QString::number(average.getLevel()) + "/"); } void VNA::SettingsChanged(std::function cb) { settings.suppressPeaks = Preferences::getInstance().Acquisition.suppressPeaks ? 1 : 0; if(window->getDevice()) { window->getDevice()->Configure(settings, [=](Device::TransmissionResult res){ // device received command, reset traces now average.reset(settings.points); traceModel.clearVNAData(); UpdateAverageCount(); UpdateCalWidget(); if(cb) { cb(res); } }); } emit traceModel.SpanChanged(settings.f_start, settings.f_stop); } void VNA::StartImpedanceMatching() { auto dialog = new ImpedanceMatchDialog(*markerModel); dialog->show(); } void VNA::SetStartFreq(double freq) { settings.f_start = freq; if(settings.f_stop < freq) { settings.f_stop = freq; } ConstrainAndUpdateFrequencies(); } void VNA::SetStopFreq(double freq) { settings.f_stop = freq; if(settings.f_start > freq) { settings.f_start = freq; } ConstrainAndUpdateFrequencies(); } void VNA::SetCenterFreq(double freq) { auto old_span = settings.f_stop - settings.f_start; if (freq - old_span / 2 <= Device::Info().limits_minFreq) { // would shift start frequency below minimum settings.f_start = 0; settings.f_stop = 2 * freq; } else if(freq + old_span / 2 >= Device::Info().limits_maxFreq) { // would shift stop frequency above maximum settings.f_start = 2 * freq - Device::Info().limits_maxFreq; settings.f_stop = Device::Info().limits_maxFreq; } else { settings.f_start = freq - old_span / 2; settings.f_stop = freq + old_span / 2; } ConstrainAndUpdateFrequencies(); } void VNA::SetSpan(double span) { auto old_center = (settings.f_start + settings.f_stop) / 2; if(old_center < Device::Info().limits_minFreq + span / 2) { // would shift start frequency below minimum settings.f_start = Device::Info().limits_minFreq; settings.f_stop = Device::Info().limits_minFreq + span; } else if(old_center > Device::Info().limits_maxFreq - span / 2) { // would shift stop frequency above maximum settings.f_start = Device::Info().limits_maxFreq - span; settings.f_stop = Device::Info().limits_maxFreq; } else { settings.f_start = old_center - span / 2; settings.f_stop = settings.f_start + span; } ConstrainAndUpdateFrequencies(); } void VNA::SetFullSpan() { settings.f_start = Device::Info().limits_minFreq; settings.f_stop = Device::Info().limits_maxFreq; ConstrainAndUpdateFrequencies(); } void VNA::SpanZoomIn() { auto center = (settings.f_start + settings.f_stop) / 2; auto old_span = settings.f_stop - settings.f_start; settings.f_start = center - old_span / 4; settings.f_stop = center + old_span / 4; ConstrainAndUpdateFrequencies(); } void VNA::SpanZoomOut() { auto center = (settings.f_start + settings.f_stop) / 2; auto old_span = settings.f_stop - settings.f_start; if(center > old_span) { settings.f_start = center - old_span; } else { settings.f_start = 0; } settings.f_stop = center + old_span; ConstrainAndUpdateFrequencies(); } void VNA::SetSourceLevel(double level) { if(level > Device::Info().limits_cdbm_max / 100.0) { level = Device::Info().limits_cdbm_max / 100.0; } else if(level < Device::Info().limits_cdbm_min / 100.0) { level = Device::Info().limits_cdbm_min / 100.0; } emit sourceLevelChanged(level); settings.cdbm_excitation = level * 100; SettingsChanged(); } void VNA::SetPoints(unsigned int points) { if(points > Device::Info().limits_maxPoints) { points = Device::Info().limits_maxPoints; } else if (points < 2) { points = 2; } emit pointsChanged(points); settings.points = points; SettingsChanged(); } void VNA::SetIFBandwidth(double bandwidth) { if(bandwidth > Device::Info().limits_maxIFBW) { bandwidth = Device::Info().limits_maxIFBW; } else if(bandwidth < Device::Info().limits_minIFBW) { bandwidth = Device::Info().limits_minIFBW; } settings.if_bandwidth = bandwidth; emit IFBandwidthChanged(bandwidth); SettingsChanged(); } void VNA::SetAveraging(unsigned int averages) { this->averages = averages; average.setAverages(averages); emit averagingChanged(averages); SettingsChanged(); } void VNA::ExcitationRequired(bool port1, bool port2) { if(Preferences::getInstance().Acquisition.alwaysExciteBothPorts) { port1 = true; port2 = true; } // check if settings actually changed if(settings.excitePort1 != port1 || settings.excitePort2 != port2) { settings.excitePort1 = port1; settings.excitePort2 = port2; SettingsChanged(); } } void VNA::DisableCalibration(bool force) { if(calValid || force) { calValid = false; emit CalibrationDisabled(); } } void VNA::ApplyCalibration(Calibration::Type type) { if(cal.calculationPossible(type)) { try { if(cal.constructErrorTerms(type)) { calValid = true; emit CalibrationApplied(type); } else { DisableCalibration(true); } } catch (runtime_error e) { QMessageBox::critical(window, "Calibration failure", e.what()); DisableCalibration(true); } } else { // Not all required traces available InformationBox::ShowMessage("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. The calibration can be enabled after the missing measurements have been acquired."); DisableCalibration(true); StartCalibrationDialog(type); } } void VNA::StartCalibrationMeasurement(Calibration::Measurement m) { auto device = window->getDevice(); if(!device) { return; } // Stop sweep StopSweep(); qDebug() << "Taking" << Calibration::MeasurementToString(m) << "measurement"; calMeasurement = m; // Delete any already captured data of this measurement cal.clearMeasurement(m); calWaitFirst = true; QString text = "Measuring \""; text.append(Calibration::MeasurementToString(m)); text.append("\" parameters."); calDialog.setLabelText(text); calDialog.setCancelButtonText("Abort"); calDialog.setWindowTitle("Taking calibration measurement..."); calDialog.setValue(0); calDialog.setWindowModality(Qt::ApplicationModal); // always show the dialog calDialog.setMinimumDuration(0); connect(&calDialog, &QProgressDialog::canceled, [=]() { // the user aborted the calibration measurement calMeasuring = false; cal.clearMeasurement(calMeasurement); }); // Trigger sweep to start from beginning SettingsChanged([=](Device::TransmissionResult){ // enable calibration measurement only in transmission callback (prevents accidental sampling of data which was still being processed) calMeasuring = true; }); calEdited = true; } void VNA::ConstrainAndUpdateFrequencies() { auto pref = Preferences::getInstance(); double maxFreq; if(pref.Acquisition.harmonicMixing) { maxFreq = Device::Info().limits_maxFreqHarmonic; } else { maxFreq = Device::Info().limits_maxFreq; } if(settings.f_stop > maxFreq) { settings.f_stop = maxFreq; } if(settings.f_start > settings.f_stop) { settings.f_start = settings.f_stop; } if(settings.f_start < Device::Info().limits_minFreq) { settings.f_start = Device::Info().limits_minFreq; } emit startFreqChanged(settings.f_start); emit stopFreqChanged(settings.f_stop); emit spanChanged(settings.f_stop - settings.f_start); emit centerFreqChanged((settings.f_stop + settings.f_start)/2); SettingsChanged(); } void VNA::LoadSweepSettings() { auto pref = Preferences::getInstance(); QSettings s; settings.f_start = s.value("SweepStart", pref.Startup.DefaultSweep.start).toULongLong(); settings.f_stop = s.value("SweepStop", pref.Startup.DefaultSweep.stop).toULongLong(); SetPoints(s.value("SweepPoints", pref.Startup.DefaultSweep.points).toInt()); SetIFBandwidth(s.value("SweepBandwidth", pref.Startup.DefaultSweep.bandwidth).toUInt()); SetAveraging(s.value("SweepAveraging", pref.Startup.DefaultSweep.averaging).toInt()); SetSourceLevel(s.value("SweepLevel", pref.Startup.DefaultSweep.excitation).toDouble()); ConstrainAndUpdateFrequencies(); } void VNA::StoreSweepSettings() { QSettings s; s.setValue("SweepStart", static_cast(settings.f_start)); s.setValue("SweepStop", static_cast(settings.f_stop)); s.setValue("SweepBandwidth", settings.if_bandwidth); s.setValue("SweepPoints", settings.points); s.setValue("SweepAveraging", averages); s.setValue("SweepLevel", (double) settings.cdbm_excitation / 100.0); } void VNA::StopSweep() { if(window->getDevice()) { window->getDevice()->SetIdle(); } } void VNA::StartCalibrationDialog(Calibration::Type type) { auto traceDialog = new CalibrationTraceDialog(&cal, settings, type); connect(traceDialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement); connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration); connect(this, &VNA::CalibrationMeasurementComplete, traceDialog, &CalibrationTraceDialog::measurementComplete); connect(traceDialog, &CalibrationTraceDialog::calibrationInvalidated, [=](){ DisableCalibration(true); InformationBox::ShowMessage("Calibration disabled", "The currently active calibration is no longer supported by the available measurements and was disabled."); }); traceDialog->show(); } void VNA::UpdateCalWidget() { calLabel->setStyleSheet(getCalStyle()); calLabel->setToolTip(getCalToolTip()); }