Parial eCal dialog

This commit is contained in:
Jan Käberich 2022-09-10 00:06:54 +02:00
parent 00ef868671
commit cb0e553a17
18 changed files with 1264 additions and 31 deletions

View File

@ -0,0 +1,354 @@
#include "caldevice.h"
#include <thread>
#include <QDebug>
using namespace std;
CalDevice::CalDevice(QString serial) :
usb(new USBDevice(serial))
{
// Check device identification
auto id = usb->Query("*IDN?");
if(!id.startsWith("LibreCAL_")) {
delete usb;
throw std::runtime_error("Invalid response to *IDN?: "+id.toStdString());
}
firmware = usb->Query(":FIRMWARE?");
QString ports = usb->Query(":PORTS?");
bool okay;
numPorts = ports.toInt(&okay);
if(!okay) {
numPorts = 0;
}
}
CalDevice::~CalDevice()
{
delete usb;
}
QString CalDevice::StandardToString(CalDevice::Standard s)
{
switch(s) {
case Standard::Open: return "OPEN";
case Standard::Short: return "SHORT";
case Standard::Load: return "LOAD";
case Standard::Through: return "THROUGH";
case Standard::None: return "NONE";
}
}
CalDevice::Standard CalDevice::StandardFromString(QString s)
{
for(int i=0;i<=(int) Standard::None;i++) {
if(s == StandardToString((Standard) i)) {
return (Standard) i;
}
}
return Standard::None;
}
CalDevice::Standard CalDevice::getStandard(int port)
{
auto query = ":PORT? "+QString::number(port);
auto response = usb->Query(query);
return StandardFromString(response);
}
bool CalDevice::setStandard(int port, CalDevice::Standard s)
{
auto cmd = ":PORT "+QString::number(port)+" "+StandardToString(s);
return usb->Cmd(cmd);
}
std::vector<CalDevice::Standard> CalDevice::availableStandards()
{
return {Standard::None, Standard::Open, Standard::Short, Standard::Load, Standard::Through};
}
double CalDevice::getTemperature()
{
QString tempString = usb->Query(":TEMP?");
bool okay;
double temp = tempString.toDouble(&okay);
if(!okay) {
temp = 0.0;
}
return temp;
}
bool CalDevice::stabilized()
{
auto stable = usb->Query(":TEMPerature:STABLE?");
return stable == "TRUE";
}
double CalDevice::getHeaterPower()
{
QString tempString = usb->Query(":HEATER:POWER?");
bool okay;
double power = tempString.toDouble(&okay);
if(!okay) {
power = 0.0;
}
return power;
}
QString CalDevice::serial()
{
return usb->serial();
}
QString CalDevice::getFirmware() const
{
return firmware;
}
int CalDevice::getNumPorts() const
{
return numPorts;
}
void CalDevice::loadCoefficientSets(QStringList names)
{
coeffSets.clear();
new std::thread(&CalDevice::loadCoefficientSetsThread, this, names);
}
void CalDevice::saveCoefficientSets()
{
if(!hasModifiedCoefficients()) {
// nothing to do, already done
emit updateCoefficientsDone(true);
} else {
new std::thread(&CalDevice::saveCoefficientSetsThread, this);
}
}
void CalDevice::loadCoefficientSetsThread(QStringList names)
{
QStringList coeffList = getCoefficientSetNames();
if(coeffList.empty()) {
// something went wrong
emit updateCoefficientsDone(false);
return;
}
if(names.size() > 0) {
// check if all the requested names are actually available
for(auto n : names) {
if(!coeffList.contains(n)) {
// this coefficient does not exist
emit updateCoefficientsDone(false);
return;
}
}
coeffList = names;
}
// get total number of coefficient points for accurate percentage calculation
unsigned long totalPoints = 0;
for(auto name : coeffList) {
for(int i=1;i<=numPorts;i++) {
totalPoints += usb->Query(":COEFF:NUM? "+name+" P"+QString::number(i)+"_OPEN").toInt();
totalPoints += usb->Query(":COEFF:NUM? "+name+" P"+QString::number(i)+"_SHORT").toInt();
totalPoints += usb->Query(":COEFF:NUM? "+name+" P"+QString::number(i)+"_LOAD").toInt();
for(int j=i+1;j<=numPorts;j++) {
totalPoints += usb->Query(":COEFF:NUM? "+name+" P"+QString::number(i)+QString::number(j)+"_THROUGH").toInt();
}
}
}
unsigned long readPoints = 0;
int lastPercentage = 0;
for(auto name : coeffList) {
// create the coefficient set
CoefficientSet set;
set.name = name;
set.ports = numPorts;
// Read this coefficient set
for(int i=1;i<=numPorts;i++) {
auto createCoefficient = [&](QString setName, QString paramName) -> CoefficientSet::Coefficient* {
int points = usb->Query(":COEFF:NUM? "+setName+" "+paramName).toInt();
CoefficientSet::Coefficient *c = new CoefficientSet::Coefficient();
if(paramName.endsWith("THROUGH")) {
c->t = Touchstone(2);
} else {
c->t = Touchstone(1);
}
for(int i=0;i<points;i++) {
QString pString = usb->Query(":COEFF:GET? "+setName+" "+paramName+" "+QString::number(i));
QStringList values = pString.split(",");
Touchstone::Datapoint p;
p.frequency = values[0].toDouble() * 1e9;
for(int j = 0;j<(values.size()-1)/2;j++) {
double real = values[1+j*2].toDouble();
double imag = values[2+j*2].toDouble();
p.S.push_back(complex<double>(real, imag));
}
if(p.S.size() == 4) {
// S21 and S12 are swapped in the touchstone file order (S21 goes first)
// but Touchstone::AddDatapoint expects S11 S12 S21 S22 order. Swap to match that
swap(p.S[1], p.S[2]);
}
c->t.AddDatapoint(p);
readPoints++;
int newPercentage = readPoints * 100 / totalPoints;
if(newPercentage != lastPercentage) {
lastPercentage = newPercentage;
emit updateCoefficientsPercent(newPercentage);
}
}
c->t.setFilename("LibreCAL/"+paramName);
return c;
};
set.opens.push_back(createCoefficient(name, "P"+QString::number(i)+"_OPEN"));
set.shorts.push_back(createCoefficient(name, "P"+QString::number(i)+"_SHORT"));
set.loads.push_back(createCoefficient(name, "P"+QString::number(i)+"_LOAD"));
for(int j=i+1;j<=numPorts;j++) {
set.throughs.push_back(createCoefficient(name, "P"+QString::number(i)+QString::number(j)+"_THROUGH"));
}
}
coeffSets.push_back(set);
}
emit updateCoefficientsDone(true);
}
void CalDevice::saveCoefficientSetsThread()
{
// figure out how many points need to be transferred
unsigned long totalPoints = 0;
for(auto set : coeffSets) {
for(auto c : set.opens) {
if(c->modified) {
totalPoints += c->t.points();
}
}
for(auto c : set.shorts) {
if(c->modified) {
totalPoints += c->t.points();
}
}
for(auto c : set.loads) {
if(c->modified) {
totalPoints += c->t.points();
}
}
for(auto c : set.throughs) {
if(c->modified) {
totalPoints += c->t.points();
}
}
}
unsigned long transferredPoints = 0;
int lastPercentage = 0;
bool success = true;
for(auto set : coeffSets) {
auto createCoefficient = [&](QString setName, QString paramName, Touchstone &t, bool &modified) -> bool {
if(!modified) {
// no changes, nothing to do
return true;
}
int points = t.points();
if(points > 0) {
// create the file
if(!usb->Cmd(":COEFF:CREATE "+setName+" "+paramName)) {
return false;
}
for(unsigned int i=0;i<points;i++) {
auto point = t.point(i);
if(point.S.size() == 4) {
// S parameters in point are in S11 S12 S21 S22 order but the LibreCAL expects
// S11 S21 S12 S22 (according to the two port touchstone format. Swap here.
swap(point.S[1], point.S[2]);
}
QString cmd = ":COEFF:ADD "+QString::number(point.frequency / 1000000000.0);
for(auto s : point.S) {
cmd += " "+QString::number(s.real())+" "+QString::number(s.imag());
}
if(!usb->Cmd(cmd)) {
return false;
}
transferredPoints++;
int newPercentage = transferredPoints * 100 / totalPoints;
if(newPercentage != lastPercentage) {
lastPercentage = newPercentage;
emit updateCoefficientsPercent(newPercentage);
}
}
if(!usb->Cmd(":COEFF:FIN")) {
return false;
}
} else {
// no points, delete coefficient
if(!usb->Cmd(":COEFF:DEL "+setName+" "+paramName)) {
return false;
}
}
modified = false;
return true;
};
for(int i=1;i<=numPorts;i++) {
success &= createCoefficient(set.name, "P"+QString::number(i)+"_OPEN", set.opens[i-1]->t, set.opens[i-1]->modified);
success &= createCoefficient(set.name, "P"+QString::number(i)+"_SHORT", set.shorts[i-1]->t, set.shorts[i-1]->modified);
success &= createCoefficient(set.name, "P"+QString::number(i)+"_LOAD", set.loads[i-1]->t, set.loads[i-1]->modified);
for(int j=i+1;j<=numPorts;j++) {
success &= createCoefficient(set.name, "P"+QString::number(i)+QString::number(j)+"_THROUGH", set.getThrough(i,j)->t, set.getThrough(i,j)->modified);
}
}
}
emit updateCoefficientsDone(success);
}
std::vector<CalDevice::CoefficientSet> CalDevice::getCoefficientSets() const
{
return coeffSets;
}
QStringList CalDevice::getCoefficientSetNames()
{
QString resp = usb->Query(":COEFF:LIST?");
if(!resp.startsWith("FACTORY")) {
return QStringList();
}
return resp.split(",");
}
bool CalDevice::hasModifiedCoefficients()
{
for(auto set : coeffSets) {
for(auto c : set.opens) {
if(c->modified) {
return true;
}
}
for(auto c : set.shorts) {
if(c->modified) {
return true;
}
}
for(auto c : set.loads) {
if(c->modified) {
return true;
}
}
for(auto c : set.throughs) {
if(c->modified) {
return true;
}
}
}
}
CalDevice::CoefficientSet::Coefficient *CalDevice::CoefficientSet::getThrough(int port1, int port2) const
{
if(port1 > ports || port2 > ports || port1 >= port2) {
return nullptr;
}
int index = port2 - port1 - 1;
while(port1 > 1) {
index += ports - port1 + 1;
port1--;
}
return throughs[index];
}

View File

@ -0,0 +1,90 @@
#ifndef CALDEVICE_H
#define CALDEVICE_H
#include "usbdevice.h"
#include "touchstone.h"
#include <QString>
#include <QObject>
class CalDevice : public QObject
{
Q_OBJECT
public:
CalDevice(QString serial);
~CalDevice();
enum class Standard {
Open,
Short,
Load,
Through,
None
};
static QString StandardToString(Standard s);
static Standard StandardFromString(QString s);
Standard getStandard(int port);
bool setStandard(int port, Standard s);
static std::vector<Standard> availableStandards();
double getTemperature();
bool stabilized();
double getHeaterPower();
QString serial();
QString getFirmware() const;
int getNumPorts() const;
class CoefficientSet {
public:
QString name;
int ports;
class Coefficient {
public:
Coefficient() : t(Touchstone(1)), modified(false) {}
Touchstone t;
bool modified;
};
std::vector<Coefficient*> opens;
std::vector<Coefficient*> shorts;
std::vector<Coefficient*> loads;
std::vector<Coefficient*> throughs;
Coefficient *getThrough(int port1, int port2) const;
};
// Extracts the coefficients from the device. This is done with a dedicated thread.
// Do not call any other functions until the update is finished. Process can be
// monitored through the updateCoefficientsPercent and updateCoefficientsDone signals
void loadCoefficientSets(QStringList names = QStringList());
// Writes coefficient sets to the device. This will only write modified files to save
// time. This is done with a dedicated thread.
// Do not call any other functions until the update is finished. Process can be
// monitored through the updateCoefficientsPercent and updateCoefficientsDone signals
void saveCoefficientSets();
std::vector<CoefficientSet> getCoefficientSets() const;
QStringList getCoefficientSetNames();
bool hasModifiedCoefficients();
signals:
void updateCoefficientsPercent(int percent);
// emitted when all coefficients have been received and it is safe to call all functions again
void updateCoefficientsDone(bool success);
private:
void loadCoefficientSetsThread(QStringList names = QStringList());
void saveCoefficientSetsThread();
USBDevice *usb;
QString firmware;
int numPorts;
std::vector<CoefficientSet> coeffSets;
};
#endif // CALDEVICE_H

View File

@ -0,0 +1,261 @@
#include "librecaldialog.h"
#include "ui_librecaldialog.h"
#include "caldevice.h"
#include "usbdevice.h"
#include "Device/virtualdevice.h"
#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);
createPortAssignmentUI();
connect(this, &LibreCALDialog::portAssignmentChanged, this, &LibreCALDialog::updateCalibrationStartStatus);
connect(ui->cbDevice, &QComboBox::currentTextChanged, [=](QString text) {
if(device) {
delete device;
}
device = new CalDevice(text);
if(device) {
createPortAssignmentUI();
connect(device, &CalDevice::updateCoefficientsPercent, ui->progressCoeff, &QProgressBar::setValue);
connect(device, &CalDevice::updateCoefficientsDone, [=](bool success){
busy = false;
if(success) {
ui->progressCoeff->setValue(100);
ui->lCoefficientStatus->setText("Coefficients loaded.");
coeffSet = device->getCoefficientSets()[0];
} else {
ui->progressCal->setValue(0);
ui->lCoefficientStatus->setText("Failed to load coefficients");
}
updateCalibrationStartStatus();
});
ui->cbCoefficients->clear();
ui->cbCoefficients->addItem("Select...");
for(auto c : device->getCoefficientSetNames()) {
ui->cbCoefficients->addItem(c);
}
ui->cbCoefficients->setEnabled(true);
} 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);
ui->lCoefficientStatus->setText("No coefficients loaded");
coeffSet = CalDevice::CoefficientSet();
updateCalibrationStartStatus();
if(ui->cbCoefficients->currentIndex() > 0) {
if(!device) {
qWarning() << "Coefficients selected without connected device";
return;
}
busy = true;
ui->lCoefficientStatus->setText("Loading coefficients...");
device->loadCoefficientSets({ui->cbCoefficients->currentText()});
}
});
auto deviceList = USBDevice::GetDevices();
for(auto device : deviceList) {
ui->cbDevice->addItem(device);
}
connect(this, &QDialog::finished, [=](){
delete device;
device = nullptr;
});
connect(ui->start, &QPushButton::clicked, this, &LibreCALDialog::startCalibration);
updateCalibrationStartStatus();
updateDeviceStatus();
connect(&updateTimer, &QTimer::timeout, this, &LibreCALDialog::updateDeviceStatus);
updateTimer.start(1000);
}
LibreCALDialog::~LibreCALDialog()
{
delete device;
delete ui;
}
void LibreCALDialog::updateCalibrationStartStatus()
{
bool canStart = true;
QString status = "Ready to start";
if(!device) {
status = "Not connected to a LibreCAL device.";
canStart = false;
}
set<int> usedCalPorts;
if(canStart) {
// Check port mapping for duplicate entries (and at least one used port)
for(auto port : portAssignment) {
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);
}
}
}
if(canStart) {
// at least one port must be used
if(usedCalPorts.size() == 0) {
status = "At least one port must be assigned.";
canStart = false;
}
}
if(canStart) {
// check if coefficients have been loaded
if(coeffSet.opens.size() != device->getNumPorts()) {
status = "Coefficients not loaded";
canStart = false;
}
}
if(canStart) {
double coeffMinFreq = numeric_limits<double>::max();
double coeffMaxFreq = numeric_limits<double>::lowest();
auto checkCoefficient = [&](CalDevice::CoefficientSet::Coefficient *c) -> bool {
if(c->t.points() == 0) {
return false;
} else {
if(c->t.minFreq() < coeffMinFreq) {
coeffMinFreq = c->t.minFreq();
}
if(c->t.maxFreq() > coeffMaxFreq) {
coeffMaxFreq = c->t.maxFreq();
}
return true;
}
};
// check if coefficients for all ports are available
for(auto i : usedCalPorts) {
// Check if OSL coefficients are there
if(!checkCoefficient(coeffSet.opens[i-1])) {
status = "Open coefficient for LibreCAL port "+QString::number(i)+" is missing.";
canStart = false;
break;
}
if(!checkCoefficient(coeffSet.shorts[i-1])) {
status = "Short coefficient for LibreCAL port "+QString::number(i)+" is missing.";
canStart = false;
break;
}
if(!checkCoefficient(coeffSet.loads[i-1])) {
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.";
canStart = false;
break;
}
}
}
}
ui->lCalibrationStatus->setText(status);
ui->start->setEnabled(canStart);
if(canStart) {
ui->lCalibrationStatus->setStyleSheet("QLabel { color : black; }");
} else {
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
}
}
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; }");
}
}
void LibreCALDialog::startCalibration()
{
ui->cbDevice->setEnabled(false);
ui->cbCoefficients->setEnabled(false);
ui->start->setEnabled(false);
ui->lCalibrationStatus->setText("Creating calibration kit from coefficients...");
auto& kit = cal->getKit();
kit = Calkit::fromLibreCAL(device, coeffSet);
}
void LibreCALDialog::createPortAssignmentUI()
{
auto layout = static_cast<QFormLayout*>(ui->assignmentBox->layout());
// Clear any possible previous elements
portAssignment.clear();
while(layout->rowCount() > 1) {
layout->removeRow(1);
}
auto vnaPorts = VirtualDevice::getInfo(VirtualDevice::getConnected()).ports;
portAssignment.resize(vnaPorts, 0);
auto calPorts = 0;
if(device) {
calPorts = device->getNumPorts();
}
QStringList choices = {"Unused"};
for(int i=1;i<=calPorts;i++) {
choices.push_back("Port "+QString::number(i));
}
for(int p = 1;p<=vnaPorts;p++) {
auto label = new QLabel("Port "+QString::number(p)+":");
auto comboBox = new QComboBox();
comboBox->addItems(choices);
connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged), [=](){
portAssignment[p-1] = comboBox->currentIndex();
emit portAssignmentChanged();
});
// try to set the default
comboBox->setCurrentIndex(p);
layout->addRow(label, comboBox);
}
}

View File

@ -0,0 +1,40 @@
#ifndef LIBRECALDIALOG_H
#define LIBRECALDIALOG_H
#include "Calibration/calibration.h"
#include "caldevice.h"
#include <QDialog>
#include <QTimer>
namespace Ui {
class LibreCALDialog;
}
class LibreCALDialog : public QDialog
{
Q_OBJECT
public:
explicit LibreCALDialog(Calibration *cal);
~LibreCALDialog();
private:
signals:
void portAssignmentChanged();
private slots:
void updateCalibrationStartStatus();
void updateDeviceStatus();
void startCalibration();
private:
void createPortAssignmentUI();
Ui::LibreCALDialog *ui;
Calibration *cal;
CalDevice *device;
CalDevice::CoefficientSet coeffSet;
QTimer updateTimer;
bool busy;
std::vector<int> portAssignment;
};
#endif // LIBRECALDIALOG_H

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LibreCALDialog</class>
<widget class="QDialog" name="LibreCALDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>346</width>
<height>395</height>
</rect>
</property>
<property name="windowTitle">
<string>Electronic Calibration Dialog</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Device Selection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Device:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbDevice"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lDeviceStatus">
<property name="text">
<string>DEVICE_STATUS_PLACEHOLDER</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Coefficients:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbCoefficients"/>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lCoefficientStatus">
<property name="text">
<string>COEFFICIENT_STATUS_PLACEHOLDER</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressCoeff">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="assignmentBox">
<property name="title">
<string>Port Assignments</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>LibreVNA</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>LibreCAL</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Calibration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="start">
<property name="text">
<string>Start</string>
</property>
<property name="icon">
<iconset theme="media-playback-start"/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lCalibrationStatus">
<property name="text">
<string>CALIBRATION_STATUS_PLACEHOLDER</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressCal">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,253 @@
#include "usbdevice.h"
#include "CustomWidgets/informationbox.h"
#include <signal.h>
#include <QDebug>
#include <QString>
#include <QMessageBox>
#include <mutex>
using namespace std;
using USBID = struct {
int VID;
int PID;
};
static constexpr USBID IDs[] = {
{0x0483, 0x4122},
};
USBDevice::USBDevice(QString serial)
{
m_handle = nullptr;
libusb_init(&m_context);
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(m_context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
#endif
SearchDevices([=](libusb_device_handle *handle, QString found_serial) -> bool {
if(serial.isEmpty() || serial == found_serial) {
// accept connection to this device
m_serial = found_serial;
m_handle = handle;
// abort device search
return false;
} else {
// not the requested device, continue search
return true;
}
}, m_context, false);
if(!m_handle) {
QString message = "No device found";
if(!serial.isEmpty()) {
// only show error message if specific device was requested
InformationBox::ShowError("Error opening device", message);
}
libusb_exit(m_context);
throw std::runtime_error(message.toStdString());
return;
}
// Found the correct device, now connect
/* claim the interface */
int ret = libusb_claim_interface(m_handle, 2);
if (ret < 0) {
libusb_close(m_handle);
/* Failed to open */
QString message = "Failed to claim interface: \"";
message.append(libusb_strerror((libusb_error) ret));
message.append("\" Maybe you are already connected to this device?");
qWarning() << message;
InformationBox::ShowError("Error opening device", message);
libusb_exit(m_context);
throw std::runtime_error(message.toStdString());
}
qInfo() << "USB connection established" << flush;
}
USBDevice::~USBDevice()
{
libusb_release_interface(m_handle, 2);
libusb_close(m_handle);
libusb_exit(m_context);
}
bool USBDevice::Cmd(QString cmd)
{
QString rcv;
bool success = send(cmd) && receive(&rcv);
if(success) {
// empty response expected by commad
return rcv == "";
} else {
// failed to send/receive
return false;
}
}
QString USBDevice::Query(QString query)
{
if(send(query)) {
QString rcv;
if(receive(&rcv)) {
return rcv;
}
}
return QString();
}
std::set<QString> USBDevice::GetDevices()
{
std::set<QString> serials;
libusb_context *ctx;
libusb_init(&ctx);
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
#endif
SearchDevices([&serials](libusb_device_handle *, QString serial) -> bool {
serials.insert(serial);
return true;
}, ctx, true);
libusb_exit(ctx);
return serials;
}
void USBDevice::SearchDevices(std::function<bool (libusb_device_handle *, QString)> foundCallback, libusb_context *context, bool ignoreOpenError)
{
libusb_device **devList;
auto ndevices = libusb_get_device_list(context, &devList);
for (ssize_t idx = 0; idx < ndevices; idx++) {
int ret;
libusb_device *device = devList[idx];
libusb_device_descriptor desc = {};
ret = libusb_get_device_descriptor(device, &desc);
if (ret) {
/* some error occured */
qCritical() << "Failed to get device descriptor: "
<< libusb_strerror((libusb_error) ret);
continue;
}
bool correctID = false;
int numIDs = sizeof(IDs)/sizeof(IDs[0]);
for(int i=0;i<numIDs;i++) {
if(desc.idVendor == IDs[i].VID && desc.idProduct == IDs[i].PID) {
correctID = true;
break;
}
}
if(!correctID) {
continue;
}
/* Try to open the device */
libusb_device_handle *handle = nullptr;
ret = libusb_open(device, &handle);
if (ret) {
qDebug() << libusb_strerror((enum libusb_error) ret);
/* Failed to open */
if(!ignoreOpenError) {
QString message = "Found potential device but failed to open usb connection: \"";
message.append(libusb_strerror((libusb_error) ret));
message.append("\" On Linux this is most likely caused by a missing udev rule. "
"On Windows this most likely means that you are already connected to "
"this device (is another instance of the application already runnning?)");
qWarning() << message;
InformationBox::ShowError("Error opening device", message);
}
continue;
}
char c_product[256];
char c_serial[256];
libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
(unsigned char*) c_serial, sizeof(c_serial));
ret = libusb_get_string_descriptor_ascii(handle, desc.iProduct,
(unsigned char*) c_product, sizeof(c_product));
if (ret > 0) {
/* managed to read the product string */
QString product(c_product);
if (product == "LibreCAL") {
// this is a match
if(!foundCallback(handle, QString(c_serial))) {
// abort search
break;
}
}
} else {
qWarning() << "Failed to get product descriptor: "
<< libusb_strerror((libusb_error) ret);
}
libusb_close(handle);
}
libusb_free_device_list(devList, 1);
}
bool USBDevice::send(const QString &s)
{
qDebug() << "Send:"<<s;
unsigned char data[s.size()+2];
memcpy(data, s.toLatin1().data(), s.size());
memcpy(&data[s.size()], "\r\n", 2);
int actual;
auto r = libusb_bulk_transfer(m_handle, LIBUSB_ENDPOINT_OUT | 0x03, data, s.size() + 2, &actual, 0);
if(r == 0 && actual == s.size() + 2) {
return true;
} else {
return false;
}
}
bool USBDevice::receive(QString *s)
{
char data[512];
memset(data, 0, sizeof(data));
int actual;
int rcvCnt = 0;
bool endOfLineFound = false;
int res;
do {
res = libusb_bulk_transfer(m_handle, LIBUSB_ENDPOINT_IN | 0x03, (unsigned char*) &data[rcvCnt], sizeof(data) - rcvCnt, &actual, 2000);
for(int i=rcvCnt;i<rcvCnt+actual;i++) {
if(i == 0) {
continue;
}
if(data[i] == '\n' && data[i-1] == '\r') {
endOfLineFound = true;
data[i-1] = '\0';
break;
}
}
rcvCnt += actual;
} while(res == 0 && !endOfLineFound);
if(res == 0) {
if(s) {
*s = QString(data);
qDebug() << "Receive:"<<*s;
}
return true;
} else {
return false;
}
}
bool USBDevice::flushRX()
{
char data[512];
// libusb_bulk_transfer(m_handle, LIBUSB_ENDPOINT_IN | 0x03, (unsigned char*) data, sizeof(data), &actual, 1);
}
QString USBDevice::serial() const
{
return m_serial;
}

View File

@ -0,0 +1,34 @@
#ifndef USBDEVICE_H
#define USBDEVICE_H
#include <libusb-1.0/libusb.h>
#include <QString>
#include <set>
#include<functional>
class USBDevice
{
public:
// connect to a CAL device. If serial is specified only connecting to this device, otherwise to the first one found
USBDevice(QString serial = QString());
~USBDevice();
bool Cmd(QString cmd);
QString Query(QString query);
QString serial() const;
// Returns serial numbers of all connected devices
static std::set<QString> GetDevices();
private:
static void SearchDevices(std::function<bool (libusb_device_handle *, QString)> foundCallback, libusb_context *context, bool ignoreOpenError);
bool send(const QString &s);
bool receive(QString *s);
bool flushRX();
libusb_device_handle *m_handle;
libusb_context *m_context;
QString m_serial;
};
#endif // DEVICE_H

View File

@ -3,6 +3,7 @@
#include "CustomWidgets/informationbox.h"
#include "Util/app_common.h"
#include "unit.h"
#include "LibreCAL/librecaldialog.h"
#include "Eigen/Dense"
@ -339,6 +340,11 @@ void Calibration::edit()
updateCalibrationList();
});
connect(ui->eCal, &QPushButton::clicked, [=](){
auto d = new LibreCALDialog(this);
d->show();
});
QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons);
auto addMenu = new QMenu();

View File

@ -159,6 +159,14 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="eCal">
<property name="text">
<string>Electronic
Calibration</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@ -328,6 +328,47 @@ Calkit Calkit::fromFile(QString filename)
return c;
}
Calkit Calkit::fromLibreCAL(CalDevice *device, CalDevice::CoefficientSet s)
{
Calkit ret;
ret.manufacturer = "LibreCAL ("+s.name+")";
ret.serialnumber = device->serial();
ret.description = "Automatically created from LibreCAL module";
for(int i=1;i<=device->getNumPorts();i++) {
if(s.opens[i-1]->t.points() > 0) {
auto o = new CalStandard::Open();
o->setName("Port "+QString::number(i)+" Open");
o->setMeasurement(s.opens[i-1]->t);
ret.standards.push_back(o);
}
if(s.shorts[i-1]->t.points() > 0) {
auto o = new CalStandard::Short();
o->setName("Port "+QString::number(i)+" Short");
o->setMeasurement(s.shorts[i-1]->t);
ret.standards.push_back(o);
}
if(s.loads[i-1]->t.points() > 0) {
auto o = new CalStandard::Load();
o->setName("Port "+QString::number(i)+" Load");
o->setMeasurement(s.loads[i-1]->t);
ret.standards.push_back(o);
}
for(int j=i+1;j<=device->getNumPorts();j++) {
auto c = s.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)+" Through");
o->setMeasurement(c->t);
ret.standards.push_back(o);
}
}
}
return ret;
}
void Calkit::edit(std::function<void (void)> updateCal)
{
auto dialog = new CalkitDialog(*this);

View File

@ -6,6 +6,8 @@
#include "calstandard.h"
#include "savable.h"
#include "LibreCAL/caldevice.h"
#include <string>
#include <complex>
#include <QDir>
@ -41,6 +43,7 @@ public:
void toFile(QString filename);
static Calkit fromFile(QString filename);
static Calkit fromLibreCAL(CalDevice *device, CalDevice::CoefficientSet s);
void edit(std::function<void(void)> updateCal = nullptr);
std::vector<CalStandard::Virtual *> getStandards() const;

View File

@ -86,6 +86,16 @@ unsigned long long Virtual::getID()
return id;
}
QString Virtual::getName() const
{
return name;
}
void Virtual::setName(const QString &value)
{
name = value;
}
void OnePort::setMeasurement(const Touchstone &ts, int port)
{
if(!touchstone) {

View File

@ -43,6 +43,9 @@ public:
unsigned long long getID();
QString getName() const;
void setName(const QString &value);
protected:
QString name;
double minFreq;

View File

@ -1,5 +1,8 @@
HEADERS += \
../VNA_embedded/Application/Communication/Protocol.hpp \
Calibration/LibreCAL/caldevice.h \
Calibration/LibreCAL/librecaldialog.h \
Calibration/LibreCAL/usbdevice.h \
Calibration/amplitudecaldialog.h \
Calibration/calibration.h \
Calibration/calibrationmeasurement.h \
@ -139,6 +142,9 @@ HEADERS += \
SOURCES += \
../VNA_embedded/Application/Communication/Protocol.cpp \
Calibration/LibreCAL/caldevice.cpp \
Calibration/LibreCAL/librecaldialog.cpp \
Calibration/LibreCAL/usbdevice.cpp \
Calibration/amplitudecaldialog.cpp \
Calibration/calibration.cpp \
Calibration/calibrationmeasurement.cpp \
@ -276,6 +282,7 @@ FORMS += \
Calibration/CalStandardOpenEditDialog.ui \
Calibration/CalStandardShortEditDialog.ui \
Calibration/CalStandardThroughEditDialog.ui \
Calibration/LibreCAL/librecaldialog.ui \
Calibration/addamplitudepointsdialog.ui \
Calibration/amplitudecaldialog.ui \
Calibration/automaticamplitudedialog.ui \

View File

@ -42,6 +42,8 @@ Trace::Trace(QString name, QColor color, QString live)
mathCalcTimer.setSingleShot(true);
connect(&mathCalcTimer, &QTimer::timeout, this, &Trace::calculateMath);
fromLivedata(LivedataType::Overwrite, live);
self.enabled = false;
dataType = DataType::Frequency;
connect(this, &Trace::typeChanged, [=](){

View File

@ -5,10 +5,6 @@
#include <signal.h>
#endif
#include "Tools/parameters.h"
#include <complex>
using namespace std;
static QApplication *app;
static AppWindow *window;
@ -32,33 +28,7 @@ int main(int argc, char *argv[]) {
window->getAppGitHash().left(9));
Device::RegisterTypes();
auto S11 = complex<double>(-0.5, 0.25);
auto S22 = complex<double>(0.5, 0.15);
auto S33 = complex<double>(0.8, -0.25);
auto S12 = complex<double>(0.1, 0);
auto S21 = complex<double>(0.2, 0.3);
auto S13 = complex<double>(0.3, -0.2);
auto S31 = complex<double>(0.4, 0.4);
auto S23 = complex<double>(0.5, 0.2);
auto S32 = complex<double>(0.6, -0.2);
auto p12 = Sparam(S11, S12, S21, S22);
auto p12_only = Sparam(0.0, S12, 1.0, 0.0);
auto p13 = Sparam(S11, S13, S31, S33);
auto p23 = Sparam(S22, S23, S32, S33);
// convert to 75 ohm
auto p12_75 = Sparam(ABCDparam(p12, 50.0), 75.0);
auto p12_only_75 = Sparam(ABCDparam(p12_only, 50.0), 75.0);
auto p13_75 = Sparam(ABCDparam(p12, 50.0), 75.0);
auto Zp23_75 = Sparam(ABCDparam(p12, 50.0), 75.0);
auto p1 = Sparam(S11, 0.0, 0.0, 1.0);
auto p1_75 = Sparam(ABCDparam(p12, 50.0), 75.0);
VirtualDevice::RegisterTypes();
#ifdef Q_OS_UNIX
signal(SIGINT, tryExitGracefully);

View File

@ -445,3 +445,8 @@ void Touchstone::setReferenceImpedance(double value)
{
referenceImpedance = value;
}
void Touchstone::setFilename(const QString &value)
{
filename = value;
}

View File

@ -47,6 +47,7 @@ public:
void reduceTo1Port(unsigned int port);
unsigned int ports() { return m_ports; }
QString getFilename() const;
void setFilename(const QString &value);
virtual nlohmann::json toJSON();
virtual void fromJSON(nlohmann::json j);
@ -54,6 +55,7 @@ public:
double getReferenceImpedance() const;
void setReferenceImpedance(double value);
private:
unsigned int m_ports;
double referenceImpedance;