2022-10-01 17:10:44 +02:00
|
|
|
#include "librecaldialog.h"
|
|
|
|
|
#include "ui_librecaldialog.h"
|
|
|
|
|
|
|
|
|
|
#include "caldevice.h"
|
|
|
|
|
#include "usbdevice.h"
|
2022-11-13 18:44:19 +01:00
|
|
|
#include "CustomWidgets/informationbox.h"
|
2022-10-01 17:10:44 +02:00
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
|
|
|
|
|
|
#include <QFormLayout>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
LibreCALDialog::LibreCALDialog(Calibration *cal) :
|
|
|
|
|
QDialog(nullptr),
|
|
|
|
|
ui(new Ui::LibreCALDialog),
|
|
|
|
|
cal(cal),
|
|
|
|
|
device(nullptr),
|
|
|
|
|
busy(false)
|
|
|
|
|
{
|
|
|
|
|
ui->setupUi(this);
|
2024-09-24 19:19:43 +02:00
|
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
2022-10-01 17:10:44 +02:00
|
|
|
|
|
|
|
|
createPortAssignmentUI();
|
|
|
|
|
|
|
|
|
|
connect(this, &LibreCALDialog::portAssignmentChanged, this, &LibreCALDialog::updateCalibrationStartStatus);
|
|
|
|
|
|
|
|
|
|
connect(ui->cbDevice, &QComboBox::currentTextChanged, [=](QString text) {
|
|
|
|
|
if(device) {
|
|
|
|
|
delete device;
|
2022-11-13 18:44:19 +01:00
|
|
|
device = nullptr;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
device = new CalDevice(text);
|
|
|
|
|
} catch (exception &e) {
|
|
|
|
|
device = nullptr;
|
2024-01-04 15:55:55 +01:00
|
|
|
InformationBox::ShowError("Failed to connect", e.what(), this);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
|
|
|
|
if(device) {
|
|
|
|
|
createPortAssignmentUI();
|
|
|
|
|
connect(device, &CalDevice::updateCoefficientsPercent, ui->progressCoeff, &QProgressBar::setValue);
|
2024-01-04 15:55:55 +01:00
|
|
|
connect(device, &CalDevice::updateCoefficientsDone, this, [=](bool success){
|
2022-10-01 17:10:44 +02:00
|
|
|
busy = false;
|
2024-09-25 15:30:42 +02:00
|
|
|
updateCalibrationStartStatus();
|
2022-10-01 17:10:44 +02:00
|
|
|
if(success) {
|
|
|
|
|
ui->progressCoeff->setValue(100);
|
2024-09-25 15:30:42 +02:00
|
|
|
ui->lCalibrationStatus->setText("Coefficients loaded.");
|
2022-10-01 17:10:44 +02:00
|
|
|
coeffSet = device->getCoefficientSets()[0];
|
2024-09-25 15:30:42 +02:00
|
|
|
|
|
|
|
|
if(validateCoefficients()) {
|
|
|
|
|
startCalibration();
|
|
|
|
|
} else {
|
|
|
|
|
enableUI();
|
|
|
|
|
}
|
2022-10-01 17:10:44 +02:00
|
|
|
} else {
|
|
|
|
|
ui->progressCal->setValue(0);
|
2024-09-25 15:30:42 +02:00
|
|
|
ui->lCalibrationStatus->setText("Failed to load coefficients");
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-01-04 15:55:55 +01:00
|
|
|
}, Qt::QueuedConnection);
|
2022-10-01 17:10:44 +02:00
|
|
|
|
|
|
|
|
ui->cbCoefficients->clear();
|
|
|
|
|
ui->cbCoefficients->addItem("Select...");
|
|
|
|
|
for(auto c : device->getCoefficientSetNames()) {
|
|
|
|
|
ui->cbCoefficients->addItem(c);
|
|
|
|
|
}
|
|
|
|
|
ui->cbCoefficients->setEnabled(true);
|
2024-09-25 15:30:42 +02:00
|
|
|
// select first available coefficient set
|
|
|
|
|
if(ui->cbCoefficients->count() > 1) {
|
|
|
|
|
ui->cbCoefficients->setCurrentIndex(1);
|
|
|
|
|
}
|
2022-10-01 17:10:44 +02:00
|
|
|
} else {
|
|
|
|
|
ui->cbCoefficients->clear();
|
|
|
|
|
ui->cbCoefficients->setEnabled(false);
|
|
|
|
|
ui->start->setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(ui->cbCoefficients, &QComboBox::currentTextChanged, [=](){
|
|
|
|
|
// no coefficient set selected
|
|
|
|
|
ui->progressCoeff->setValue(0);
|
|
|
|
|
coeffSet = CalDevice::CoefficientSet();
|
2024-09-25 15:30:42 +02:00
|
|
|
ui->lCalibrationStatus->setText("No coefficients loaded");
|
2022-10-01 17:10:44 +02:00
|
|
|
updateCalibrationStartStatus();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
auto deviceList = USBDevice::GetDevices();
|
|
|
|
|
for(auto device : deviceList) {
|
|
|
|
|
ui->cbDevice->addItem(device);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
connect(ui->start, &QPushButton::clicked, this, &LibreCALDialog::determineAutoPorts);
|
|
|
|
|
connect(this, &LibreCALDialog::autoPortComplete, this, &LibreCALDialog::loadCoefficients);
|
2022-10-01 17:10:44 +02:00
|
|
|
|
|
|
|
|
updateCalibrationStartStatus();
|
|
|
|
|
updateDeviceStatus();
|
|
|
|
|
connect(&updateTimer, &QTimer::timeout, this, &LibreCALDialog::updateDeviceStatus);
|
|
|
|
|
updateTimer.start(1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibreCALDialog::~LibreCALDialog()
|
|
|
|
|
{
|
2024-09-24 19:19:43 +02:00
|
|
|
if(device) {
|
|
|
|
|
device->abortCoefficientLoading();
|
|
|
|
|
}
|
2022-10-01 17:10:44 +02:00
|
|
|
delete device;
|
|
|
|
|
delete ui;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
bool LibreCALDialog::validatePortSelection(bool autoAllowed)
|
2022-10-01 17:10:44 +02:00
|
|
|
{
|
|
|
|
|
set<int> usedCalPorts;
|
2024-09-25 15:30:42 +02:00
|
|
|
QString status;
|
|
|
|
|
bool canStart = true;
|
|
|
|
|
// Check port mapping for duplicate entries (and at least one used port)
|
|
|
|
|
for(auto port : portAssignment) {
|
|
|
|
|
if(autoAllowed && port == -1) {
|
|
|
|
|
// auto port, to be determined later
|
|
|
|
|
usedCalPorts.insert(port);
|
|
|
|
|
// skip duplicate selection
|
|
|
|
|
continue;
|
|
|
|
|
} else if(port < 1) {
|
|
|
|
|
// skip unused ports
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(usedCalPorts.count(port)) {
|
|
|
|
|
status = "LibreCAL port "+QString::number(port)+" is assigned to multiple VNA ports.";
|
|
|
|
|
canStart = false;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
usedCalPorts.insert(port);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(canStart) {
|
|
|
|
|
// at least one port must be used
|
|
|
|
|
if(usedCalPorts.size() == 0) {
|
|
|
|
|
status = "At least one port must be assigned.";
|
|
|
|
|
canStart = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
if(!canStart) {
|
|
|
|
|
ui->lCalibrationStatus->setText(status);
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
return canStart;
|
|
|
|
|
}
|
2022-10-01 17:10:44 +02:00
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
bool LibreCALDialog::validateCoefficients()
|
|
|
|
|
{
|
|
|
|
|
bool canStart = true;
|
|
|
|
|
QString status;
|
2022-10-01 17:10:44 +02:00
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
double coeffMinFreq = numeric_limits<double>::max();
|
|
|
|
|
double coeffMaxFreq = numeric_limits<double>::lowest();
|
|
|
|
|
|
|
|
|
|
auto checkCoefficient = [&](CalDevice::CoefficientSet::Coefficient *c) -> bool {
|
2024-09-25 17:58:32 +02:00
|
|
|
if(!c) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
if(c->t.points() == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
if(c->t.minFreq() < coeffMinFreq) {
|
|
|
|
|
coeffMinFreq = c->t.minFreq();
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
if(c->t.maxFreq() > coeffMaxFreq) {
|
|
|
|
|
coeffMaxFreq = c->t.maxFreq();
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
set<int> usedCalPorts;
|
|
|
|
|
// Check port mapping for duplicate entries (and at least one used port)
|
|
|
|
|
for(auto port : portAssignment) {
|
|
|
|
|
if(port > 0) {
|
|
|
|
|
usedCalPorts.insert(port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check if coefficients for all ports are available
|
|
|
|
|
for(auto i : usedCalPorts) {
|
|
|
|
|
// Check if OSL coefficients are there
|
2024-09-25 17:58:32 +02:00
|
|
|
if(!checkCoefficient(coeffSet.getOpen(i))) {
|
2024-09-25 15:30:42 +02:00
|
|
|
status = "Open coefficient for LibreCAL port "+QString::number(i)+" is missing.";
|
|
|
|
|
canStart = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-09-25 17:58:32 +02:00
|
|
|
if(!checkCoefficient(coeffSet.getShort(i))) {
|
2024-09-25 15:30:42 +02:00
|
|
|
status = "Short coefficient for LibreCAL port "+QString::number(i)+" is missing.";
|
|
|
|
|
canStart = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-09-25 17:58:32 +02:00
|
|
|
if(!checkCoefficient(coeffSet.getLoad(i))) {
|
2024-09-25 15:30:42 +02:00
|
|
|
status = "Load coefficient for LibreCAL port "+QString::number(i)+" is missing.";
|
|
|
|
|
canStart = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
for(auto j : usedCalPorts) {
|
|
|
|
|
if(j <= i) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(!checkCoefficient(coeffSet.getThrough(i,j))) {
|
|
|
|
|
status = "Through coefficient for LibreCAL port "+QString::number(i)+" to "+QString::number(j)+" is missing.";
|
2022-10-01 17:10:44 +02:00
|
|
|
canStart = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(!canStart) {
|
|
|
|
|
ui->lCalibrationStatus->setText(status);
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
|
|
|
|
|
}
|
|
|
|
|
return canStart;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::updateCalibrationStartStatus()
|
|
|
|
|
{
|
|
|
|
|
bool canStart = true;
|
|
|
|
|
if(!device) {
|
|
|
|
|
canStart = false;
|
|
|
|
|
ui->lCalibrationStatus->setText("Not connected to a LibreCAL device");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(canStart) {
|
|
|
|
|
if(ui->cbCoefficients->currentIndex() == 0) {
|
|
|
|
|
canStart = false;
|
|
|
|
|
ui->lCalibrationStatus->setText("No coefficient set selected");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
if(canStart) {
|
|
|
|
|
canStart = validatePortSelection(true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 17:10:44 +02:00
|
|
|
ui->start->setEnabled(canStart);
|
|
|
|
|
if(canStart) {
|
2024-09-25 15:30:42 +02:00
|
|
|
ui->lCalibrationStatus->setText("Ready to start");
|
2022-10-01 17:10:44 +02:00
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : black; }");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::updateDeviceStatus()
|
|
|
|
|
{
|
|
|
|
|
if(!device) {
|
|
|
|
|
ui->lDeviceStatus->setText("No LibreCAL connected");
|
|
|
|
|
ui->lDeviceStatus->setStyleSheet("QLabel { color : red; }");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(busy) {
|
|
|
|
|
// can't update while busy reading coefficients
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(device->stabilized()) {
|
|
|
|
|
ui->lDeviceStatus->setText("LibreCAL ready for calibration");
|
|
|
|
|
ui->lDeviceStatus->setStyleSheet("QLabel { color : black; }");
|
|
|
|
|
} else {
|
|
|
|
|
ui->lDeviceStatus->setText("Heating up, please wait with calibration");
|
|
|
|
|
ui->lDeviceStatus->setStyleSheet("QLabel { color : orange; }");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-25 15:30:42 +02:00
|
|
|
void LibreCALDialog::determineAutoPorts()
|
|
|
|
|
{
|
|
|
|
|
ui->progressCal->setValue(0);
|
|
|
|
|
ui->lCalibrationStatus->setText("Autodetecting port connections...");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : green; }");
|
|
|
|
|
|
|
|
|
|
// check if any ports are set to auto
|
|
|
|
|
bool usesAuto = false;
|
|
|
|
|
for(auto port : portAssignment) {
|
|
|
|
|
if(port == -1) {
|
|
|
|
|
usesAuto = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(usesAuto) {
|
|
|
|
|
driver = DeviceDriver::getActiveDriver();
|
|
|
|
|
emit driver->acquireControl();
|
|
|
|
|
// Determine ports by setting all ports to open and then switching them to short one at a time while observing the change in the VNA measurement
|
|
|
|
|
for(unsigned int i=1;i<=device->getNumPorts();i++) {
|
|
|
|
|
device->setStandard(i, CalDevice::Standard::Type::Open);
|
|
|
|
|
}
|
|
|
|
|
autoPortMeasurements.clear();
|
|
|
|
|
startSweep();
|
|
|
|
|
} else {
|
|
|
|
|
// no auto ports
|
|
|
|
|
emit autoPortComplete();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::loadCoefficients()
|
|
|
|
|
{
|
|
|
|
|
// validate ports again, at this point no "auto" selection is allowed anymore
|
|
|
|
|
if(!validatePortSelection(false)) {
|
|
|
|
|
enableUI();
|
|
|
|
|
updateCalibrationStartStatus();
|
|
|
|
|
} else {
|
|
|
|
|
// can continue with loading coefficients
|
|
|
|
|
// they might already be loaded from a previous calibration run, check first
|
2024-09-25 17:58:32 +02:00
|
|
|
if(!validateCoefficients()) {
|
|
|
|
|
// only load required coefficients
|
2024-09-25 15:30:42 +02:00
|
|
|
ui->progressCal->setValue(0);
|
|
|
|
|
ui->lCalibrationStatus->setText("Loading coefficients...");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : green; }");
|
|
|
|
|
|
2024-09-25 17:58:32 +02:00
|
|
|
// determine the used ports of the LibreCAL
|
|
|
|
|
QList<int> usedPorts;
|
|
|
|
|
for(auto port : portAssignment) {
|
|
|
|
|
if(port > 0) {
|
|
|
|
|
usedPorts.append(port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
busy = true;
|
|
|
|
|
device->loadCoefficientSets({ui->cbCoefficients->currentText()}, usedPorts);
|
|
|
|
|
} else {
|
|
|
|
|
// can proceed with calibration directly
|
|
|
|
|
startCalibration();
|
|
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::handleIncomingMeasurement(DeviceDriver::VNAMeasurement m)
|
|
|
|
|
{
|
|
|
|
|
stopSweep();
|
|
|
|
|
autoPortMeasurements.push_back(m);
|
|
|
|
|
auto nextPort = autoPortMeasurements.size();
|
|
|
|
|
// switch LibreCAL to next port
|
|
|
|
|
if(nextPort <= device->getNumPorts()) {
|
|
|
|
|
device->setStandard(nextPort, CalDevice::Standard::Type::Short);
|
|
|
|
|
}
|
|
|
|
|
if(nextPort > 1) {
|
|
|
|
|
device->setStandard(nextPort-1, CalDevice::Standard::Type::Open);
|
|
|
|
|
}
|
|
|
|
|
if(nextPort > device->getNumPorts()) {
|
|
|
|
|
// auto port measurements complete
|
|
|
|
|
|
|
|
|
|
// reset LibreCAL ports
|
|
|
|
|
for(unsigned int i=1;i<=device->getNumPorts();i++) {
|
|
|
|
|
device->setStandard(i, CalDevice::Standard::Type::None);
|
|
|
|
|
}
|
|
|
|
|
// evaluate results
|
|
|
|
|
for(unsigned int p=0;p<portAssignment.size();p++) {
|
|
|
|
|
auto port = portAssignment[p];
|
|
|
|
|
if(port != -1) {
|
|
|
|
|
// not set to auto, ignore
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto param = "S"+QString::number(p+1)+QString::number(p+1);
|
|
|
|
|
std::vector<std::complex<double>> measurements;
|
|
|
|
|
for(auto m : autoPortMeasurements) {
|
|
|
|
|
if(m.measurements.count(param)) {
|
|
|
|
|
measurements.push_back(m.measurements[param]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(measurements.size() != device->getNumPorts()+1) {
|
|
|
|
|
// not all measurements available (maybe the port has no stimulus?), set to unused
|
|
|
|
|
portAssignmentComboBoxes[p]->setCurrentIndex(0);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// got all required measurements, determine which one deviates the most from the baseline
|
|
|
|
|
double maxDeviation = 0.0;
|
|
|
|
|
int maxDevIndex = 0;
|
|
|
|
|
for(unsigned int i=1;i<=device->getNumPorts();i++) {
|
|
|
|
|
auto diff = abs(measurements[i] - measurements[0]);
|
|
|
|
|
if (diff > maxDeviation) {
|
|
|
|
|
maxDeviation = diff;
|
|
|
|
|
maxDevIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
constexpr double minRequiredDeviation = 0.25;
|
|
|
|
|
if(maxDeviation > minRequiredDeviation) {
|
|
|
|
|
portAssignmentComboBoxes[p]->setCurrentIndex(maxDevIndex+1);
|
|
|
|
|
} else {
|
|
|
|
|
// not enough deviation, probably unused
|
|
|
|
|
portAssignmentComboBoxes[p]->setCurrentIndex(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
emit driver->releaseControl();
|
|
|
|
|
emit autoPortComplete();
|
|
|
|
|
} else {
|
|
|
|
|
// trigger the next measurement
|
|
|
|
|
startSweep();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::startSweep()
|
|
|
|
|
{
|
|
|
|
|
// set up a sweep with a single measurement point at the start frequency
|
|
|
|
|
auto info = driver->getInfo();
|
|
|
|
|
DeviceDriver::VNASettings s = {};
|
|
|
|
|
s.dBmStart = info.Limits.VNA.maxdBm;
|
|
|
|
|
s.dBmStop = info.Limits.VNA.maxdBm;
|
|
|
|
|
|
|
|
|
|
auto freq = info.Limits.VNA.minFreq == 0 ? 100000 : info.Limits.VNA.minFreq;
|
|
|
|
|
|
|
|
|
|
s.freqStart = freq;
|
|
|
|
|
s.freqStop = freq;
|
|
|
|
|
s.IFBW = 100;
|
|
|
|
|
s.logSweep = false;
|
|
|
|
|
s.points = 1;
|
|
|
|
|
|
|
|
|
|
for(unsigned int i=1;i<=info.Limits.VNA.ports;i++) {
|
|
|
|
|
s.excitedPorts.push_back(i);
|
|
|
|
|
}
|
|
|
|
|
driver->setVNA(s, [=](bool){
|
|
|
|
|
connect(driver, &DeviceDriver::VNAmeasurementReceived, this, &LibreCALDialog::handleIncomingMeasurement, Qt::DirectConnection);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::stopSweep()
|
|
|
|
|
{
|
|
|
|
|
disconnect(driver, &DeviceDriver::VNAmeasurementReceived, this, &LibreCALDialog::handleIncomingMeasurement);
|
|
|
|
|
driver->setIdle();
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-01 17:10:44 +02:00
|
|
|
void LibreCALDialog::startCalibration()
|
|
|
|
|
{
|
|
|
|
|
disableUI();
|
|
|
|
|
|
|
|
|
|
ui->progressCal->setValue(0);
|
|
|
|
|
ui->lCalibrationStatus->setText("Creating calibration kit from coefficients...");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : black; }");
|
|
|
|
|
auto& kit = cal->getKit();
|
|
|
|
|
kit.clearStandards();
|
|
|
|
|
kit.manufacturer = "LibreCAL ("+coeffSet.name+")";
|
|
|
|
|
kit.serialnumber = device->serial();
|
|
|
|
|
kit.description = "Automatically created from LibreCAL module";
|
2024-09-25 17:58:32 +02:00
|
|
|
|
|
|
|
|
// determine the used ports of the LibreCAL
|
|
|
|
|
QList<int> usedPorts;
|
|
|
|
|
for(auto port : portAssignment) {
|
|
|
|
|
if(port > 0) {
|
|
|
|
|
usedPorts.append(port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
std::sort(usedPorts.begin(), usedPorts.end());
|
|
|
|
|
|
|
|
|
|
std::map<int, CalStandard::Virtual*> openStandards;
|
|
|
|
|
std::map<int, CalStandard::Virtual*> shortStandards;
|
|
|
|
|
std::map<int, CalStandard::Virtual*> loadStandards;
|
|
|
|
|
std::map<int, CalStandard::Virtual*> throughStandards;
|
|
|
|
|
for(unsigned int idx=0;idx<usedPorts.size();idx++) {
|
|
|
|
|
int i=usedPorts[idx];
|
|
|
|
|
if(coeffSet.getOpen(i)->t.points() > 0) {
|
2022-10-01 17:10:44 +02:00
|
|
|
auto o = new CalStandard::Open();
|
|
|
|
|
o->setName("Port "+QString::number(i));
|
2024-09-25 17:58:32 +02:00
|
|
|
o->setMeasurement(coeffSet.getOpen(i)->t);
|
|
|
|
|
openStandards[i] = o;
|
2024-05-10 18:12:37 +02:00
|
|
|
kit.addStandard(o);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 17:58:32 +02:00
|
|
|
if(coeffSet.getShort(i)->t.points() > 0) {
|
2022-10-01 17:10:44 +02:00
|
|
|
auto o = new CalStandard::Short();
|
|
|
|
|
o->setName("Port "+QString::number(i));
|
2024-09-25 17:58:32 +02:00
|
|
|
o->setMeasurement(coeffSet.getShort(i)->t);
|
|
|
|
|
shortStandards[i] = o;
|
2024-05-10 18:12:37 +02:00
|
|
|
kit.addStandard(o);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 17:58:32 +02:00
|
|
|
if(coeffSet.getLoad(i)->t.points() > 0) {
|
2022-10-01 17:10:44 +02:00
|
|
|
auto o = new CalStandard::Load();
|
|
|
|
|
o->setName("Port "+QString::number(i));
|
2024-09-25 17:58:32 +02:00
|
|
|
o->setMeasurement(coeffSet.getLoad(i)->t);
|
|
|
|
|
loadStandards[i] = o;
|
2024-05-10 18:12:37 +02:00
|
|
|
kit.addStandard(o);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
2024-09-25 17:58:32 +02:00
|
|
|
for(unsigned int jdx=idx+1;jdx<usedPorts.size();jdx++) {
|
|
|
|
|
int j=usedPorts[jdx];
|
2022-10-01 17:10:44 +02:00
|
|
|
auto c = coeffSet.getThrough(i,j);
|
|
|
|
|
if(!c) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(c->t.points() > 0) {
|
|
|
|
|
auto o = new CalStandard::Through();
|
|
|
|
|
o->setName("Port "+QString::number(i)+" to "+QString::number(j));
|
|
|
|
|
o->setMeasurement(c->t);
|
2024-09-25 17:58:32 +02:00
|
|
|
throughStandards[coeffSet.portsToThroughIndex(i, j)] = o;
|
2024-05-10 18:12:37 +02:00
|
|
|
kit.addStandard(o);
|
2022-10-01 17:10:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ui->lCalibrationStatus->setText("Creating calibration measurements...");
|
|
|
|
|
cal->reset();
|
2023-01-17 00:25:58 +01:00
|
|
|
auto vnaPorts = DeviceDriver::getInfo(DeviceDriver::getActiveDriver()).Limits.VNA.ports;
|
2022-10-01 17:10:44 +02:00
|
|
|
set<CalibrationMeasurement::Base*> openMeasurements;
|
|
|
|
|
set<CalibrationMeasurement::Base*> shortMeasurements;
|
|
|
|
|
set<CalibrationMeasurement::Base*> loadMeasurements;
|
|
|
|
|
vector<CalibrationMeasurement::TwoPort*> throughMeasurements;
|
2022-10-14 00:27:22 +02:00
|
|
|
for(unsigned int p=0;p<vnaPorts;p++) {
|
2022-10-01 17:10:44 +02:00
|
|
|
if(portAssignment[p] == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Create SOL measurements with correct port of calkit
|
|
|
|
|
auto open = new CalibrationMeasurement::Open(cal);
|
|
|
|
|
open->setPort(p+1);
|
2024-09-25 17:58:32 +02:00
|
|
|
open->setStandard(openStandards[portAssignment[p]]);
|
2022-10-01 17:10:44 +02:00
|
|
|
openMeasurements.insert(open);
|
|
|
|
|
cal->measurements.push_back(open);
|
|
|
|
|
|
|
|
|
|
auto _short = new CalibrationMeasurement::Short(cal);
|
|
|
|
|
_short->setPort(p+1);
|
2024-09-25 17:58:32 +02:00
|
|
|
_short->setStandard(shortStandards[portAssignment[p]]);
|
2022-10-01 17:10:44 +02:00
|
|
|
shortMeasurements.insert(_short);
|
|
|
|
|
cal->measurements.push_back(_short);
|
|
|
|
|
|
|
|
|
|
auto load = new CalibrationMeasurement::Load(cal);
|
|
|
|
|
load->setPort(p+1);
|
2024-09-25 17:58:32 +02:00
|
|
|
load->setStandard(loadStandards[portAssignment[p]]);
|
2022-10-01 17:10:44 +02:00
|
|
|
loadMeasurements.insert(load);
|
|
|
|
|
cal->measurements.push_back(load);
|
2022-10-14 00:27:22 +02:00
|
|
|
for(unsigned int p2=p+1;p2<vnaPorts;p2++) {
|
2022-10-01 17:10:44 +02:00
|
|
|
if(portAssignment[p2] == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
auto through = new CalibrationMeasurement::Through(cal);
|
|
|
|
|
through->setPort1(p+1);
|
|
|
|
|
through->setPort2(p2+1);
|
|
|
|
|
// find correct through standard
|
|
|
|
|
int libreCALp1 = portAssignment[p];
|
|
|
|
|
int libreCALp2 = portAssignment[p2];
|
|
|
|
|
QString forwardName = "Port "+QString::number(libreCALp1)+" to "+QString::number(libreCALp2);
|
|
|
|
|
QString reverseName = "Port "+QString::number(libreCALp2)+" to "+QString::number(libreCALp1);
|
|
|
|
|
for(auto ts : throughStandards) {
|
2024-09-25 17:58:32 +02:00
|
|
|
if(ts.second->getName() == forwardName) {
|
|
|
|
|
through->setStandard(ts.second);
|
2022-10-01 17:10:44 +02:00
|
|
|
through->setReverseStandard(false);
|
2024-09-25 17:58:32 +02:00
|
|
|
} else if(ts.second->getName() == reverseName) {
|
|
|
|
|
through->setStandard(ts.second);
|
2022-10-01 17:10:44 +02:00
|
|
|
through->setReverseStandard(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throughMeasurements.push_back(through);
|
|
|
|
|
cal->measurements.push_back(through);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ui->lCalibrationStatus->setText("Taking calibration measurements...");
|
|
|
|
|
|
|
|
|
|
measurementsTaken = 0;
|
|
|
|
|
|
|
|
|
|
auto setTerminationOnAllUsedPorts = [=](CalDevice::Standard s) {
|
|
|
|
|
for(auto p : portAssignment) {
|
|
|
|
|
if(p > 0) {
|
|
|
|
|
device->setStandard(p, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto startNextCalibrationStep = [=]() {
|
|
|
|
|
// indicate calibration percentage
|
|
|
|
|
auto totalMeasurements = 3 + throughMeasurements.size();
|
|
|
|
|
ui->progressCal->setValue(measurementsTaken * 100 / totalMeasurements);
|
|
|
|
|
switch(measurementsTaken) {
|
|
|
|
|
case 0:
|
2022-11-14 15:40:32 +01:00
|
|
|
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::Open));
|
2022-10-01 17:10:44 +02:00
|
|
|
emit cal->startMeasurements(openMeasurements);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
2022-11-14 15:40:32 +01:00
|
|
|
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::Short));
|
2022-10-01 17:10:44 +02:00
|
|
|
emit cal->startMeasurements(shortMeasurements);
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
2022-11-14 15:40:32 +01:00
|
|
|
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::Load));
|
2022-10-01 17:10:44 +02:00
|
|
|
emit cal->startMeasurements(loadMeasurements);
|
|
|
|
|
break;
|
|
|
|
|
default: {
|
|
|
|
|
// into through measurements now
|
2022-10-14 00:27:22 +02:00
|
|
|
unsigned int throughIndex = measurementsTaken - 3;
|
2022-10-01 17:10:44 +02:00
|
|
|
if(throughIndex >= throughMeasurements.size()) {
|
|
|
|
|
// this was the last measurement
|
|
|
|
|
// Try to apply the calibration
|
|
|
|
|
Calibration::CalType type;
|
|
|
|
|
type.type = Calibration::Type::SOLT;
|
2022-10-14 00:27:22 +02:00
|
|
|
for(unsigned int i=0;i<vnaPorts;i++) {
|
2022-10-01 17:10:44 +02:00
|
|
|
if(portAssignment[i] > 0) {
|
|
|
|
|
// this VNA port was used in the calibration
|
|
|
|
|
type.usedPorts.push_back(i+1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
auto res = cal->compute(type);
|
|
|
|
|
if(res) {
|
|
|
|
|
ui->progressCal->setValue(100);
|
|
|
|
|
ui->lCalibrationStatus->setText("Calibration activated.");
|
|
|
|
|
} else {
|
|
|
|
|
ui->progressCal->setValue(0);
|
|
|
|
|
ui->lCalibrationStatus->setText("Failed to activate calibration.");
|
|
|
|
|
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
|
|
|
|
|
}
|
2022-11-13 18:44:19 +01:00
|
|
|
// sever connection to this function
|
2022-10-01 17:10:44 +02:00
|
|
|
disconnect(cal, &Calibration::measurementsUpdated, this, nullptr);
|
2022-11-14 15:40:32 +01:00
|
|
|
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
|
2022-10-01 17:10:44 +02:00
|
|
|
enableUI();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-11-14 15:40:32 +01:00
|
|
|
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
|
2022-10-01 17:10:44 +02:00
|
|
|
auto m = throughMeasurements[throughIndex];
|
2024-01-04 15:55:55 +01:00
|
|
|
device->setStandard(portAssignment[m->getPort1()-1], CalDevice::Standard(portAssignment[m->getPort2()-1]));
|
2022-10-01 17:10:44 +02:00
|
|
|
emit cal->startMeasurements({m});
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
measurementsTaken++;
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-13 18:21:29 +01:00
|
|
|
disconnect(cal, &Calibration::measurementsUpdated, this, nullptr);
|
2024-09-25 17:58:32 +02:00
|
|
|
connect(cal, &Calibration::measurementsUpdated, this, startNextCalibrationStep, Qt::QueuedConnection);
|
2024-01-13 18:21:29 +01:00
|
|
|
connect(cal, &Calibration::measurementsAborted, this, [=](){
|
|
|
|
|
enableUI();
|
|
|
|
|
});
|
2022-10-01 17:10:44 +02:00
|
|
|
|
|
|
|
|
startNextCalibrationStep();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::disableUI()
|
|
|
|
|
{
|
|
|
|
|
ui->cbDevice->setEnabled(false);
|
|
|
|
|
ui->cbCoefficients->setEnabled(false);
|
|
|
|
|
ui->start->setEnabled(false);
|
|
|
|
|
for(auto cb : portAssignmentComboBoxes) {
|
|
|
|
|
cb->setEnabled(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::enableUI()
|
|
|
|
|
{
|
|
|
|
|
ui->cbDevice->setEnabled(true);
|
|
|
|
|
ui->cbCoefficients->setEnabled(true);
|
|
|
|
|
ui->start->setEnabled(true);
|
|
|
|
|
for(auto cb : portAssignmentComboBoxes) {
|
|
|
|
|
cb->setEnabled(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void LibreCALDialog::createPortAssignmentUI()
|
|
|
|
|
{
|
|
|
|
|
auto layout = static_cast<QFormLayout*>(ui->assignmentBox->layout());
|
|
|
|
|
// Clear any possible previous elements
|
|
|
|
|
portAssignment.clear();
|
|
|
|
|
portAssignmentComboBoxes.clear();
|
|
|
|
|
while(layout->rowCount() > 1) {
|
|
|
|
|
layout->removeRow(1);
|
|
|
|
|
}
|
2023-01-17 00:25:58 +01:00
|
|
|
auto vnaPorts = DeviceDriver::getInfo(DeviceDriver::getActiveDriver()).Limits.VNA.ports;
|
2022-10-01 17:10:44 +02:00
|
|
|
portAssignment.resize(vnaPorts, 0);
|
|
|
|
|
auto calPorts = 0;
|
|
|
|
|
if(device) {
|
|
|
|
|
calPorts = device->getNumPorts();
|
|
|
|
|
}
|
2024-09-25 15:30:42 +02:00
|
|
|
QStringList choices = {"Unused", "Auto"};
|
2022-10-01 17:10:44 +02:00
|
|
|
for(int i=1;i<=calPorts;i++) {
|
|
|
|
|
choices.push_back("Port "+QString::number(i));
|
|
|
|
|
}
|
2022-10-14 00:27:22 +02:00
|
|
|
for(unsigned int p = 1;p<=vnaPorts;p++) {
|
2022-10-01 17:10:44 +02:00
|
|
|
auto label = new QLabel("Port "+QString::number(p)+":");
|
|
|
|
|
auto comboBox = new QComboBox();
|
|
|
|
|
comboBox->addItems(choices);
|
|
|
|
|
connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged), [=](){
|
2024-09-25 15:30:42 +02:00
|
|
|
if(comboBox->currentText().startsWith("Unused")) {
|
|
|
|
|
portAssignment[p-1] = 0;
|
|
|
|
|
} else if(comboBox->currentText().startsWith("Auto")) {
|
|
|
|
|
portAssignment[p-1] = -1;
|
|
|
|
|
} else if(comboBox->currentText().startsWith("Port")) {
|
|
|
|
|
portAssignment[p-1] = comboBox->currentText().back().digitValue();
|
|
|
|
|
}
|
2022-10-01 17:10:44 +02:00
|
|
|
emit portAssignmentChanged();
|
|
|
|
|
});
|
2024-09-25 15:30:42 +02:00
|
|
|
comboBox->setCurrentIndex(1);
|
2022-10-01 17:10:44 +02:00
|
|
|
layout->addRow(label, comboBox);
|
|
|
|
|
portAssignmentComboBoxes.push_back(comboBox);
|
|
|
|
|
}
|
|
|
|
|
}
|