Parial eCal dialog
This commit is contained in:
parent
00ef868671
commit
cb0e553a17
354
Software/PC_Application/Calibration/LibreCAL/caldevice.cpp
Normal file
354
Software/PC_Application/Calibration/LibreCAL/caldevice.cpp
Normal 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];
|
||||||
|
}
|
90
Software/PC_Application/Calibration/LibreCAL/caldevice.h
Normal file
90
Software/PC_Application/Calibration/LibreCAL/caldevice.h
Normal 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
|
261
Software/PC_Application/Calibration/LibreCAL/librecaldialog.cpp
Normal file
261
Software/PC_Application/Calibration/LibreCAL/librecaldialog.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
144
Software/PC_Application/Calibration/LibreCAL/librecaldialog.ui
Normal file
144
Software/PC_Application/Calibration/LibreCAL/librecaldialog.ui
Normal 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>
|
253
Software/PC_Application/Calibration/LibreCAL/usbdevice.cpp
Normal file
253
Software/PC_Application/Calibration/LibreCAL/usbdevice.cpp
Normal 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;
|
||||||
|
}
|
34
Software/PC_Application/Calibration/LibreCAL/usbdevice.h
Normal file
34
Software/PC_Application/Calibration/LibreCAL/usbdevice.h
Normal 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
|
@ -3,6 +3,7 @@
|
|||||||
#include "CustomWidgets/informationbox.h"
|
#include "CustomWidgets/informationbox.h"
|
||||||
#include "Util/app_common.h"
|
#include "Util/app_common.h"
|
||||||
#include "unit.h"
|
#include "unit.h"
|
||||||
|
#include "LibreCAL/librecaldialog.h"
|
||||||
|
|
||||||
#include "Eigen/Dense"
|
#include "Eigen/Dense"
|
||||||
|
|
||||||
@ -339,6 +340,11 @@ void Calibration::edit()
|
|||||||
updateCalibrationList();
|
updateCalibrationList();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ui->eCal, &QPushButton::clicked, [=](){
|
||||||
|
auto d = new LibreCALDialog(this);
|
||||||
|
d->show();
|
||||||
|
});
|
||||||
|
|
||||||
QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons);
|
QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons);
|
||||||
|
|
||||||
auto addMenu = new QMenu();
|
auto addMenu = new QMenu();
|
||||||
|
@ -159,6 +159,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="eCal">
|
||||||
|
<property name="text">
|
||||||
|
<string>Electronic
|
||||||
|
Calibration</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
@ -328,6 +328,47 @@ Calkit Calkit::fromFile(QString filename)
|
|||||||
return c;
|
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)
|
void Calkit::edit(std::function<void (void)> updateCal)
|
||||||
{
|
{
|
||||||
auto dialog = new CalkitDialog(*this);
|
auto dialog = new CalkitDialog(*this);
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include "calstandard.h"
|
#include "calstandard.h"
|
||||||
#include "savable.h"
|
#include "savable.h"
|
||||||
|
|
||||||
|
#include "LibreCAL/caldevice.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@ -41,6 +43,7 @@ public:
|
|||||||
|
|
||||||
void toFile(QString filename);
|
void toFile(QString filename);
|
||||||
static Calkit fromFile(QString filename);
|
static Calkit fromFile(QString filename);
|
||||||
|
static Calkit fromLibreCAL(CalDevice *device, CalDevice::CoefficientSet s);
|
||||||
void edit(std::function<void(void)> updateCal = nullptr);
|
void edit(std::function<void(void)> updateCal = nullptr);
|
||||||
|
|
||||||
std::vector<CalStandard::Virtual *> getStandards() const;
|
std::vector<CalStandard::Virtual *> getStandards() const;
|
||||||
|
@ -86,6 +86,16 @@ unsigned long long Virtual::getID()
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Virtual::getName() const
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Virtual::setName(const QString &value)
|
||||||
|
{
|
||||||
|
name = value;
|
||||||
|
}
|
||||||
|
|
||||||
void OnePort::setMeasurement(const Touchstone &ts, int port)
|
void OnePort::setMeasurement(const Touchstone &ts, int port)
|
||||||
{
|
{
|
||||||
if(!touchstone) {
|
if(!touchstone) {
|
||||||
|
@ -43,6 +43,9 @@ public:
|
|||||||
|
|
||||||
unsigned long long getID();
|
unsigned long long getID();
|
||||||
|
|
||||||
|
QString getName() const;
|
||||||
|
void setName(const QString &value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString name;
|
QString name;
|
||||||
double minFreq;
|
double minFreq;
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
HEADERS += \
|
HEADERS += \
|
||||||
../VNA_embedded/Application/Communication/Protocol.hpp \
|
../VNA_embedded/Application/Communication/Protocol.hpp \
|
||||||
|
Calibration/LibreCAL/caldevice.h \
|
||||||
|
Calibration/LibreCAL/librecaldialog.h \
|
||||||
|
Calibration/LibreCAL/usbdevice.h \
|
||||||
Calibration/amplitudecaldialog.h \
|
Calibration/amplitudecaldialog.h \
|
||||||
Calibration/calibration.h \
|
Calibration/calibration.h \
|
||||||
Calibration/calibrationmeasurement.h \
|
Calibration/calibrationmeasurement.h \
|
||||||
@ -139,6 +142,9 @@ HEADERS += \
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
../VNA_embedded/Application/Communication/Protocol.cpp \
|
../VNA_embedded/Application/Communication/Protocol.cpp \
|
||||||
|
Calibration/LibreCAL/caldevice.cpp \
|
||||||
|
Calibration/LibreCAL/librecaldialog.cpp \
|
||||||
|
Calibration/LibreCAL/usbdevice.cpp \
|
||||||
Calibration/amplitudecaldialog.cpp \
|
Calibration/amplitudecaldialog.cpp \
|
||||||
Calibration/calibration.cpp \
|
Calibration/calibration.cpp \
|
||||||
Calibration/calibrationmeasurement.cpp \
|
Calibration/calibrationmeasurement.cpp \
|
||||||
@ -276,6 +282,7 @@ FORMS += \
|
|||||||
Calibration/CalStandardOpenEditDialog.ui \
|
Calibration/CalStandardOpenEditDialog.ui \
|
||||||
Calibration/CalStandardShortEditDialog.ui \
|
Calibration/CalStandardShortEditDialog.ui \
|
||||||
Calibration/CalStandardThroughEditDialog.ui \
|
Calibration/CalStandardThroughEditDialog.ui \
|
||||||
|
Calibration/LibreCAL/librecaldialog.ui \
|
||||||
Calibration/addamplitudepointsdialog.ui \
|
Calibration/addamplitudepointsdialog.ui \
|
||||||
Calibration/amplitudecaldialog.ui \
|
Calibration/amplitudecaldialog.ui \
|
||||||
Calibration/automaticamplitudedialog.ui \
|
Calibration/automaticamplitudedialog.ui \
|
||||||
|
@ -42,6 +42,8 @@ Trace::Trace(QString name, QColor color, QString live)
|
|||||||
mathCalcTimer.setSingleShot(true);
|
mathCalcTimer.setSingleShot(true);
|
||||||
connect(&mathCalcTimer, &QTimer::timeout, this, &Trace::calculateMath);
|
connect(&mathCalcTimer, &QTimer::timeout, this, &Trace::calculateMath);
|
||||||
|
|
||||||
|
fromLivedata(LivedataType::Overwrite, live);
|
||||||
|
|
||||||
self.enabled = false;
|
self.enabled = false;
|
||||||
dataType = DataType::Frequency;
|
dataType = DataType::Frequency;
|
||||||
connect(this, &Trace::typeChanged, [=](){
|
connect(this, &Trace::typeChanged, [=](){
|
||||||
|
@ -5,10 +5,6 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Tools/parameters.h"
|
|
||||||
#include <complex>
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static QApplication *app;
|
static QApplication *app;
|
||||||
static AppWindow *window;
|
static AppWindow *window;
|
||||||
|
|
||||||
@ -32,33 +28,7 @@ int main(int argc, char *argv[]) {
|
|||||||
window->getAppGitHash().left(9));
|
window->getAppGitHash().left(9));
|
||||||
|
|
||||||
Device::RegisterTypes();
|
Device::RegisterTypes();
|
||||||
|
VirtualDevice::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);
|
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
signal(SIGINT, tryExitGracefully);
|
signal(SIGINT, tryExitGracefully);
|
||||||
|
@ -445,3 +445,8 @@ void Touchstone::setReferenceImpedance(double value)
|
|||||||
{
|
{
|
||||||
referenceImpedance = value;
|
referenceImpedance = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Touchstone::setFilename(const QString &value)
|
||||||
|
{
|
||||||
|
filename = value;
|
||||||
|
}
|
||||||
|
@ -47,6 +47,7 @@ public:
|
|||||||
void reduceTo1Port(unsigned int port);
|
void reduceTo1Port(unsigned int port);
|
||||||
unsigned int ports() { return m_ports; }
|
unsigned int ports() { return m_ports; }
|
||||||
QString getFilename() const;
|
QString getFilename() const;
|
||||||
|
void setFilename(const QString &value);
|
||||||
|
|
||||||
virtual nlohmann::json toJSON();
|
virtual nlohmann::json toJSON();
|
||||||
virtual void fromJSON(nlohmann::json j);
|
virtual void fromJSON(nlohmann::json j);
|
||||||
@ -54,6 +55,7 @@ public:
|
|||||||
double getReferenceImpedance() const;
|
double getReferenceImpedance() const;
|
||||||
void setReferenceImpedance(double value);
|
void setReferenceImpedance(double value);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int m_ports;
|
unsigned int m_ports;
|
||||||
double referenceImpedance;
|
double referenceImpedance;
|
||||||
|
Loading…
Reference in New Issue
Block a user