More intuitive handling of calibration measurements

- Allow saving of calibration only if a calibration is active (no more
calibration files that "don't do anything" when they are opened)
- Delete old measurements when loading a new calibration file
- Update calibration when a measurement is updated (no need to disable
and enable again)
- Disable calibration when a required measurement is deleted
This commit is contained in:
Jan Käberich 2020-11-10 19:16:16 +01:00
parent 3f66bdda48
commit 0d6e844def
12 changed files with 149 additions and 88 deletions

View File

@ -7,6 +7,7 @@ HEADERS += \
Calibration/json.hpp \ Calibration/json.hpp \
Calibration/measurementmodel.h \ Calibration/measurementmodel.h \
CustomWidgets/colorpickerbutton.h \ CustomWidgets/colorpickerbutton.h \
CustomWidgets/informationbox.h \
CustomWidgets/siunitedit.h \ CustomWidgets/siunitedit.h \
CustomWidgets/tilewidget.h \ CustomWidgets/tilewidget.h \
CustomWidgets/toggleswitch.h \ CustomWidgets/toggleswitch.h \
@ -53,6 +54,7 @@ SOURCES += \
Calibration/calkitdialog.cpp \ Calibration/calkitdialog.cpp \
Calibration/measurementmodel.cpp \ Calibration/measurementmodel.cpp \
CustomWidgets/colorpickerbutton.cpp \ CustomWidgets/colorpickerbutton.cpp \
CustomWidgets/informationbox.cpp \
CustomWidgets/qwtplotpiecewisecurve.cpp \ CustomWidgets/qwtplotpiecewisecurve.cpp \
CustomWidgets/siunitedit.cpp \ CustomWidgets/siunitedit.cpp \
CustomWidgets/tilewidget.cpp \ CustomWidgets/tilewidget.cpp \

View File

@ -24,7 +24,7 @@ Calibration::Calibration()
void Calibration::clearMeasurements() void Calibration::clearMeasurements()
{ {
for(auto m : measurements) { for(auto m : measurements) {
m.second.datapoints.clear(); clearMeasurement(m.first);
} }
} }
@ -42,11 +42,19 @@ void Calibration::addMeasurement(Calibration::Measurement type, Protocol::Datapo
bool Calibration::calculationPossible(Calibration::Type type) bool Calibration::calculationPossible(Calibration::Type type)
{ {
if(type == Type::None) {
// always possible to reset to None
return true;
}
return SanityCheckSamples(Measurements(type, false)); return SanityCheckSamples(Measurements(type, false));
} }
#include <QDebug> #include <QDebug>
bool Calibration::constructErrorTerms(Calibration::Type type) bool Calibration::constructErrorTerms(Calibration::Type type)
{ {
if(type == Type::None) {
resetErrorTerms();
return true;
}
if(!calculationPossible(type)) { if(!calculationPossible(type)) {
return false; return false;
} }
@ -602,6 +610,10 @@ bool Calibration::openFromFile(QString filename)
} }
} }
// reset all data before loading new calibration
clearMeasurements();
resetErrorTerms();
// attempt to load associated calibration kit first (needs to be available when performing calibration) // attempt to load associated calibration kit first (needs to be available when performing calibration)
auto calkit_file = filename; auto calkit_file = filename;
auto dotPos = calkit_file.lastIndexOf('.'); auto dotPos = calkit_file.lastIndexOf('.');
@ -610,7 +622,7 @@ bool Calibration::openFromFile(QString filename)
} }
calkit_file.append(".calkit"); calkit_file.append(".calkit");
try { try {
kit = Calkit::fromFile(calkit_file.toStdString()); kit = Calkit::fromFile(calkit_file);
} catch (runtime_error e) { } catch (runtime_error e) {
QMessageBox::warning(nullptr, "Missing calibration kit", "The calibration kit file associated with the selected calibration could not be parsed. The calibration might not be accurate. (" + QString(e.what()) + ")"); QMessageBox::warning(nullptr, "Missing calibration kit", "The calibration kit file associated with the selected calibration could not be parsed. The calibration might not be accurate. (" + QString(e.what()) + ")");
} }
@ -636,20 +648,17 @@ bool Calibration::saveToFile(QString filename)
return false; return false;
} }
} }
// strip any potential file name extension and set default
auto dotPos = filename.lastIndexOf('.'); if(filename.endsWith(".cal")) {
if(dotPos >= 0) { filename.chop(4);
filename.truncate(dotPos);
} }
auto calibration_file = filename; auto calibration_file = filename + ".cal";
calibration_file.append(".cal");
ofstream file; ofstream file;
file.open(calibration_file.toStdString()); file.open(calibration_file.toStdString());
file << *this; file << *this;
auto calkit_file = filename; auto calkit_file = filename + ".calkit";
calkit_file.append(".calkit"); kit.toFile(calkit_file);
kit.toFile(calkit_file.toStdString());
return true; return true;
} }

View File

@ -21,7 +21,7 @@ CalibrationTraceDialog::CalibrationTraceDialog(Calibration *cal, Calibration::Ty
ui->tableView->setColumnWidth(1, 350); ui->tableView->setColumnWidth(1, 350);
ui->tableView->setColumnWidth(2, 320); ui->tableView->setColumnWidth(2, 320);
ui->tableView->setColumnWidth(3, 160); ui->tableView->setColumnWidth(3, 160);
UpdateApplyButton(); UpdateCalibrationStatus();
} }
CalibrationTraceDialog::~CalibrationTraceDialog() CalibrationTraceDialog::~CalibrationTraceDialog()
@ -32,11 +32,19 @@ CalibrationTraceDialog::~CalibrationTraceDialog()
void CalibrationTraceDialog::measurementComplete(Calibration::Measurement m) void CalibrationTraceDialog::measurementComplete(Calibration::Measurement m)
{ {
model->measurementUpdated(m); model->measurementUpdated(m);
UpdateApplyButton(); UpdateCalibrationStatus();
} }
void CalibrationTraceDialog::UpdateApplyButton() void CalibrationTraceDialog::UpdateCalibrationStatus()
{ {
if(!cal->calculationPossible(cal->getType())) {
// some trace for the current calibration was deleted
cal->resetErrorTerms();
emit calibrationInvalidated();
} else {
// update error terms as a measurement might have changed
cal->constructErrorTerms(cal->getType());
}
ui->bApply->setEnabled(cal->calculationPossible(requestedType)); ui->bApply->setEnabled(cal->calculationPossible(requestedType));
} }
@ -45,7 +53,7 @@ void CalibrationTraceDialog::on_bDelete_clicked()
auto measurement = measurements[ui->tableView->currentIndex().row()]; auto measurement = measurements[ui->tableView->currentIndex().row()];
cal->clearMeasurement(measurement); cal->clearMeasurement(measurement);
model->measurementUpdated(measurement); model->measurementUpdated(measurement);
UpdateApplyButton(); UpdateCalibrationStatus();
} }
void CalibrationTraceDialog::on_bMeasure_clicked() void CalibrationTraceDialog::on_bMeasure_clicked()
@ -59,17 +67,3 @@ void CalibrationTraceDialog::on_bApply_clicked()
emit applyCalibration(requestedType); emit applyCalibration(requestedType);
accept(); accept();
} }
void CalibrationTraceDialog::on_bOpen_clicked()
{
cal->openFromFile();
UpdateApplyButton();
if(cal->getType() != Calibration::Type::None) {
emit applyCalibration(cal->getType());
}
}
void CalibrationTraceDialog::on_bSave_clicked()
{
cal->saveToFile();
}

View File

@ -22,18 +22,15 @@ public slots:
signals: signals:
void triggerMeasurement(Calibration::Measurement m); void triggerMeasurement(Calibration::Measurement m);
void applyCalibration(Calibration::Type type); void applyCalibration(Calibration::Type type);
void calibrationInvalidated();
private slots: private slots:
void on_bDelete_clicked(); void on_bDelete_clicked();
void on_bMeasure_clicked(); void on_bMeasure_clicked();
void on_bApply_clicked(); void on_bApply_clicked();
void on_bOpen_clicked();
void on_bSave_clicked();
private: private:
void UpdateApplyButton(); void UpdateCalibrationStatus();
Ui::CalibrationTraceDialog *ui; Ui::CalibrationTraceDialog *ui;
Calibration *cal; Calibration *cal;
Calibration::Type requestedType; Calibration::Type requestedType;

View File

@ -6,12 +6,12 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1066</width> <width>1079</width>
<height>396</height> <height>578</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Calibration Traces</string> <string>Calibration Measurements</string>
</property> </property>
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
@ -65,28 +65,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="bOpen">
<property name="text">
<string>Open</string>
</property>
<property name="icon">
<iconset theme="document-open" resource="../icons.qrc">
<normaloff>:/icons/open.png</normaloff>:/icons/open.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bSave">
<property name="text">
<string>Save</string>
</property>
<property name="icon">
<iconset theme="document-save" resource="../icons.qrc">
<normaloff>:/icons/save.png</normaloff>:/icons/save.png</iconset>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">

View File

@ -26,9 +26,12 @@ Calkit::Calkit()
#include <QDebug> #include <QDebug>
void Calkit::toFile(std::string filename) void Calkit::toFile(QString filename)
{ {
TransformPathsToRelative(QString::fromStdString(filename)); if(!filename.endsWith(".calkit")) {
filename.append(".calkit");
}
TransformPathsToRelative(filename);
json j; json j;
for(auto e : json_descr) { for(auto e : json_descr) {
@ -39,7 +42,6 @@ void Calkit::toFile(std::string filename)
} }
// json library does not now about QVariant, handle used cases // json library does not now about QVariant, handle used cases
auto val = e.var.value(); auto val = e.var.value();
qDebug() << val << endl;
switch(static_cast<QMetaType::Type>(val.type())) { switch(static_cast<QMetaType::Type>(val.type())) {
case QMetaType::Double: *json_entry = val.toDouble(); break; case QMetaType::Double: *json_entry = val.toDouble(); break;
case QMetaType::Int: *json_entry = val.toInt(); break; case QMetaType::Int: *json_entry = val.toInt(); break;
@ -50,11 +52,11 @@ void Calkit::toFile(std::string filename)
} }
} }
ofstream file; ofstream file;
file.open(filename); file.open(filename.toStdString());
file << setw(4) << j << endl; file << setw(4) << j << endl;
file.close(); file.close();
TransformPathsToAbsolute(QString::fromStdString(filename)); TransformPathsToAbsolute(filename);
} }
static QString readLine(ifstream &file) { static QString readLine(ifstream &file) {
@ -63,11 +65,11 @@ static QString readLine(ifstream &file) {
return QString::fromStdString(line).simplified(); return QString::fromStdString(line).simplified();
} }
Calkit Calkit::fromFile(std::string filename) Calkit Calkit::fromFile(QString filename)
{ {
auto c = Calkit(); auto c = Calkit();
ifstream file; ifstream file;
file.open(filename); file.open(filename.toStdString());
if(!file.is_open()) { if(!file.is_open()) {
throw runtime_error("Unable to open file"); throw runtime_error("Unable to open file");
} }
@ -101,9 +103,7 @@ Calkit Calkit::fromFile(std::string filename)
case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break; case QMetaType::Bool: e.var.setValue((*json_entry).get<bool>()); break;
case QMetaType::QString: { case QMetaType::QString: {
auto s = QString::fromStdString((*json_entry).get<string>()); auto s = QString::fromStdString((*json_entry).get<string>());
qDebug() << s;
e.var.setValue(s); e.var.setValue(s);
qDebug() << e.var.value();
} }
break; break;
@ -162,7 +162,7 @@ Calkit Calkit::fromFile(std::string filename)
auto msg = new QMessageBox(); auto msg = new QMessageBox();
msg->setWindowTitle("Loading calkit file"); msg->setWindowTitle("Loading calkit file");
msg->setText("The file \"" + QString::fromStdString(filename) + "\" is stored in a deprecated" msg->setText("The file \"" + filename + "\" is stored in a deprecated"
" calibration kit format. Future versions of this application might not support" " calibration kit format. Future versions of this application might not support"
" it anymore. Please save the calibration kit to update to the new format"); " it anymore. Please save the calibration kit to update to the new format");
msg->setStandardButtons(QMessageBox::Ok); msg->setStandardButtons(QMessageBox::Ok);
@ -170,7 +170,7 @@ Calkit Calkit::fromFile(std::string filename)
} }
file.close(); file.close();
c.TransformPathsToAbsolute(QString::fromStdString(filename)); c.TransformPathsToAbsolute(filename);
// set default values for non-editable items (for now) // set default values for non-editable items (for now)
c.TRL.Through.Z0 = 50.0; c.TRL.Through.Z0 = 50.0;

View File

@ -34,8 +34,8 @@ public:
std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22; std::complex<double> ThroughS11, ThroughS12, ThroughS21, ThroughS22;
}; };
void toFile(std::string filename); void toFile(QString filename);
static Calkit fromFile(std::string filename); static Calkit fromFile(QString filename);
void edit(); void edit();
SOLT toSOLT(double frequency); SOLT toSOLT(double frequency);
TRL toTRL(double frequency); TRL toTRL(double frequency);

View File

@ -119,7 +119,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Open calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog); auto filename = QFileDialog::getOpenFileName(this, "Open calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) { if(filename.length() > 0) {
ownKit = Calkit::fromFile(filename.toStdString()); ownKit = Calkit::fromFile(filename);
updateEntries(); updateEntries();
} }
}); });
@ -127,14 +127,8 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog); auto filename = QFileDialog::getSaveFileName(this, "Save calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, QFileDialog::DontUseNativeDialog);
if(filename.length() > 0) { if(filename.length() > 0) {
// strip any potential file name extension and set default
auto dotPos = filename.lastIndexOf('.');
if(dotPos >= 0) {
filename.truncate(dotPos);
}
filename.append(".calkit");
parseEntries(); parseEntries();
ownKit.toFile(filename.toStdString()); ownKit.toFile(filename);
} }
}); });
} }

View File

@ -0,0 +1,43 @@
#include "informationbox.h"
#include <QCheckBox>
#include <QSettings>
#include <QDebug>
void InformationBox::ShowMessage(QString title, QString message, QWidget *parent)
{
// check if the user still wants to see this message
auto hash = qHash(message);
QSettings s;
if(!s.contains(hashToSettingsKey(hash))) {
auto box = new InformationBox(title, message, hash, parent);
box->exec();
}
}
InformationBox::InformationBox(QString title, QString message, unsigned int hash, QWidget *parent)
: QMessageBox(parent),
hash(hash)
{
setWindowTitle(title);
setText(message);
setAttribute(Qt::WA_DeleteOnClose, true);
setIcon(QMessageBox::Information);
auto cb = new QCheckBox("Do not show this message again");
setCheckBox(cb);
}
InformationBox::~InformationBox()
{
auto cb = checkBox();
if(cb->isChecked()) {
QSettings s;
s.setValue(hashToSettingsKey(hash), true);
}
}
QString InformationBox::hashToSettingsKey(unsigned int hash)
{
return QString("DoNotShowDialog/") + QString::number(hash);
}

View File

@ -0,0 +1,18 @@
#ifndef INFORMATIONBOX_H
#define INFORMATIONBOX_H
#include <QMessageBox>
class InformationBox : public QMessageBox
{
Q_OBJECT
public:
static void ShowMessage(QString title, QString message, QWidget *parent = nullptr);
private:
InformationBox(QString title, QString message, unsigned int hash, QWidget *parent);
~InformationBox();
static QString hashToSettingsKey(unsigned int hash);
unsigned int hash;
};
#endif // INFORMATIONBOX_H

View File

@ -41,6 +41,8 @@
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QApplication> #include <QApplication>
#include <QActionGroup> #include <QActionGroup>
#include <QErrorMessage>
#include "CustomWidgets/informationbox.h"
VNA::VNA(AppWindow *window) VNA::VNA(AppWindow *window)
: Mode(window, "Vector Network Analyzer"), : Mode(window, "Vector Network Analyzer"),
@ -89,11 +91,29 @@ VNA::VNA(AppWindow *window)
auto calMenu = new QMenu("Calibration", window); auto calMenu = new QMenu("Calibration", window);
window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu); window->menuBar()->insertMenu(window->getUi()->menuWindow->menuAction(), calMenu);
actions.insert(calMenu->menuAction()); 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());
}
});
connect(saveCal, &QAction::triggered, [=](){
cal.saveToFile();
});
auto calDisable = calMenu->addAction("Disabled"); auto calDisable = calMenu->addAction("Disabled");
calDisable->setCheckable(true); calDisable->setCheckable(true);
calDisable->setChecked(true); calDisable->setChecked(true);
calMenu->addSeparator(); calMenu->addSeparator();
auto calData = calMenu->addAction("Calibration Data"); auto calData = calMenu->addAction("Calibration Measurements");
connect(calData, &QAction::triggered, [=](){ connect(calData, &QAction::triggered, [=](){
StartCalibrationDialog(); StartCalibrationDialog();
}); });
@ -290,6 +310,7 @@ VNA::VNA(AppWindow *window)
cbType->blockSignals(false); cbType->blockSignals(false);
cbEnableCal->blockSignals(false); cbEnableCal->blockSignals(false);
calImport->setEnabled(false); calImport->setEnabled(false);
saveCal->setEnabled(false);
}); });
connect(calDisable, &QAction::triggered, this, &VNA::DisableCalibration); connect(calDisable, &QAction::triggered, this, &VNA::DisableCalibration);
connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){ connect(this, &VNA::CalibrationApplied, [=](Calibration::Type applied){
@ -305,6 +326,7 @@ VNA::VNA(AppWindow *window)
cbType->blockSignals(false); cbType->blockSignals(false);
cbEnableCal->blockSignals(false); cbEnableCal->blockSignals(false);
calImport->setEnabled(true); calImport->setEnabled(true);
saveCal->setEnabled(true);
}); });
tb_cal->addWidget(cbType); tb_cal->addWidget(cbType);
@ -383,10 +405,11 @@ void VNA::initializeDevice()
auto filename = s.value(key).toString(); auto filename = s.value(key).toString();
qDebug() << "Attempting to load default calibration file \"" << filename << "\""; qDebug() << "Attempting to load default calibration file \"" << filename << "\"";
if(QFile::exists(filename)) { if(QFile::exists(filename)) {
cal.openFromFile(filename); if(cal.openFromFile(filename)) {
ApplyCalibration(cal.getType()); ApplyCalibration(cal.getType());
portExtension.setCalkit(&cal.getCalibrationKit()); portExtension.setCalkit(&cal.getCalibrationKit());
} }
}
removeDefaultCal->setEnabled(true); removeDefaultCal->setEnabled(true);
} else { } else {
qDebug() << "No default calibration file set for this device"; qDebug() << "No default calibration file set for this device";
@ -594,7 +617,6 @@ void VNA::DisableCalibration(bool force)
if(calValid || force) { if(calValid || force) {
calValid = false; calValid = false;
emit CalibrationDisabled(); emit CalibrationDisabled();
average.reset(settings.points);
} }
} }
@ -604,7 +626,6 @@ void VNA::ApplyCalibration(Calibration::Type type)
try { try {
if(cal.constructErrorTerms(type)) { if(cal.constructErrorTerms(type)) {
calValid = true; calValid = true;
average.reset(settings.points);
emit CalibrationApplied(type); emit CalibrationApplied(type);
} }
} catch (runtime_error e) { } catch (runtime_error e) {
@ -613,7 +634,7 @@ void VNA::ApplyCalibration(Calibration::Type type)
} }
} else { } else {
// Not all required traces available // Not all required traces available
QMessageBox::information(window, "Missing calibration traces", "Not all calibration traces for this type of calibration have been measured. The calibration can be enabled after the missing traces have been acquired."); 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.", window);
DisableCalibration(true); DisableCalibration(true);
StartCalibrationDialog(type); StartCalibrationDialog(type);
} }
@ -708,5 +729,9 @@ void VNA::StartCalibrationDialog(Calibration::Type type)
connect(traceDialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement); connect(traceDialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement);
connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration); connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration);
connect(this, &VNA::CalibrationMeasurementComplete, traceDialog, &CalibrationTraceDialog::measurementComplete); 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(); traceDialog->show();
} }

View File

@ -69,6 +69,7 @@ private:
QMenu *defaultCalMenu; QMenu *defaultCalMenu;
QAction *assignDefaultCal, *removeDefaultCal; QAction *assignDefaultCal, *removeDefaultCal;
QAction *saveCal;
PortExtension portExtension; PortExtension portExtension;