From 392cb6dc3f0ee87fe61771a7aa01525aa8c31713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20K=C3=A4berich?= Date: Tue, 24 Sep 2024 19:19:43 +0200 Subject: [PATCH] WIP: improve coefficient readout from LibreCAL --- .../Calibration/LibreCAL/caldevice.cpp | 148 +++++++++++++++++- .../Calibration/LibreCAL/caldevice.h | 10 +- .../Calibration/LibreCAL/librecaldialog.cpp | 9 +- .../Calibration/LibreCAL/usbdevice.cpp | 87 ++++++---- .../Calibration/LibreCAL/usbdevice.h | 7 +- 5 files changed, 220 insertions(+), 41 deletions(-) diff --git a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.cpp b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.cpp index 642b6f3..f90c4ca 100644 --- a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.cpp @@ -1,7 +1,5 @@ #include "caldevice.h" -#include - #include #include using namespace std; @@ -33,6 +31,8 @@ static QString getLocalDateTimeWithUtcOffset() CalDevice::CalDevice(QString serial) : usb(new USBDevice(serial)) { + loadThread = nullptr; + // Check device identification auto id = usb->Query("*IDN?"); if(!id.startsWith("LibreCAL,")) { @@ -169,10 +169,24 @@ QString CalDevice::getDateTimeUTC() } } -void CalDevice::loadCoefficientSets(QStringList names) +void CalDevice::loadCoefficientSets(QStringList names, bool fast) { coeffSets.clear(); - new std::thread(&CalDevice::loadCoefficientSetsThread, this, names); + abortLoading = false; + if(fast) { + loadThread = new std::thread(&CalDevice::loadCoefficientSetsThreadFast, this, names); + } else { + loadThread = new std::thread(&CalDevice::loadCoefficientSetsThreadSlow, this, names); + } +} + +void CalDevice::abortCoefficientLoading() +{ + if(loadThread) { + abortLoading = true; + loadThread->join(); + loadThread = nullptr; + } } void CalDevice::saveCoefficientSets() @@ -185,7 +199,7 @@ void CalDevice::saveCoefficientSets() } } -void CalDevice::loadCoefficientSetsThread(QStringList names) +void CalDevice::loadCoefficientSetsThreadSlow(QStringList names) { QStringList coeffList = getCoefficientSetNames(); if(coeffList.empty()) { @@ -208,6 +222,9 @@ void CalDevice::loadCoefficientSetsThread(QStringList names) unsigned long totalPoints = 0; for(auto name : coeffList) { for(int i=1;i<=numPorts;i++) { + if(abortLoading) { + return; + } 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(); @@ -234,6 +251,9 @@ void CalDevice::loadCoefficientSetsThread(QStringList names) c->t = Touchstone(1); } for(int i=0;iQuery(":COEFF:GET? "+setName+" "+paramName+" "+QString::number(i)); QStringList values = pString.split(","); Touchstone::Datapoint p; @@ -265,6 +285,124 @@ void CalDevice::loadCoefficientSetsThread(QStringList names) for(int j=i+1;j<=numPorts;j++) { set.throughs.push_back(createCoefficient(name, "P"+QString::number(i)+QString::number(j)+"_THROUGH")); } + if(abortLoading) { + return; + } + } + coeffSets.push_back(set); + } + emit updateCoefficientsDone(true); +} + +void CalDevice::loadCoefficientSetsThreadFast(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; + } + + QStringList coeffNames; + for(int i=1;i<=numPorts;i++) { + coeffNames.append("P"+QString::number(i)+"_OPEN"); + coeffNames.append("P"+QString::number(i)+"_SHORT"); + coeffNames.append("P"+QString::number(i)+"_LOAD"); + for(int j=i+1;j<=numPorts;j++) { + coeffNames.append("P"+QString::number(i)+QString::number(j)+"_THROUGH"); + } + } + + int total_coeffs = coeffNames.size() * names.size(); + int read_coeffs = 0; + + for(auto name : coeffList) { + // create the coefficient set + CoefficientSet set; + set.name = name; + set.ports = numPorts; + + auto createCoefficient = [&](QString setName, QString paramName) -> CoefficientSet::Coefficient* { + CoefficientSet::Coefficient *c = new CoefficientSet::Coefficient(); + // ask for the whole set at once + usb->send(":COEFF:GET? "+setName+" "+paramName); + // handle incoming lines + if(paramName.endsWith("THROUGH")) { + c->t = Touchstone(2); + } else { + c->t = Touchstone(1); + } + c->t.setFilename("LibreCAL/"+paramName); + while(true) { + QString line; + if(!usb->receive(&line, 100)) { + // failed to receive something, abort + return c; + } + if(line.startsWith("ERROR")) { + // something went wront + return c; + } + // ignore start, comments and option line + if(line.startsWith("START") || line.startsWith("!") || line.startsWith("#")) { + // ignore + continue; + } + if(line.startsWith("END")) { + // got all data + return c; + } + // parse the values + try { + QStringList values = line.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(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); + } catch (...) { + return c; + } + } + }; + + for(auto coeff : coeffNames) { + auto c = createCoefficient(name, coeff); + if(abortLoading) { + return; + } + if(c) { + if(coeff.endsWith("_OPEN")) { + set.opens.push_back(c); + } else if(coeff.endsWith("_SHORT")) { + set.shorts.push_back(c); + } else if(coeff.endsWith("_LOAD")) { + set.loads.push_back(c); + } else if(coeff.endsWith("_THROUGH")) { + set.throughs.push_back(c); + } + } + read_coeffs++; + emit updateCoefficientsPercent(read_coeffs * 100 / total_coeffs); } coeffSets.push_back(set); } diff --git a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.h b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.h index 587651d..6fd967f 100644 --- a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.h +++ b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/caldevice.h @@ -6,6 +6,7 @@ #include #include +#include class CalDevice : public QObject { @@ -70,7 +71,9 @@ public: // 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()); + void loadCoefficientSets(QStringList names = QStringList(), bool fast=true); + + void abortCoefficientLoading(); // 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 @@ -92,12 +95,15 @@ signals: void disconnected(); private: - void loadCoefficientSetsThread(QStringList names = QStringList()); + void loadCoefficientSetsThreadSlow(QStringList names = QStringList()); + void loadCoefficientSetsThreadFast(QStringList names = QStringList()); void saveCoefficientSetsThread(); USBDevice *usb; QString firmware; int numPorts; + std::thread *loadThread; + bool abortLoading; float firmware_major_minor; diff --git a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.cpp b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.cpp index ebe74b2..b49a125 100644 --- a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.cpp @@ -19,6 +19,7 @@ LibreCALDialog::LibreCALDialog(Calibration *cal) : busy(false) { ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); createPortAssignmentUI(); @@ -87,11 +88,6 @@ LibreCALDialog::LibreCALDialog(Calibration *cal) : ui->cbDevice->addItem(device); } - connect(this, &QDialog::finished, [=](){ - delete device; - device = nullptr; - }); - connect(ui->start, &QPushButton::clicked, this, &LibreCALDialog::startCalibration); updateCalibrationStartStatus(); @@ -102,6 +98,9 @@ LibreCALDialog::LibreCALDialog(Calibration *cal) : LibreCALDialog::~LibreCALDialog() { + if(device) { + device->abortCoefficientLoading(); + } delete device; delete ui; } diff --git a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp index 395eee9..d160879 100644 --- a/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp @@ -21,6 +21,7 @@ static constexpr USBID IDs[] = { USBDevice::USBDevice(QString serial) { + rx_cnt = 0; m_handle = nullptr; libusb_init(&m_context); #if LIBUSB_API_VERSION >= 0x01000106 @@ -200,7 +201,7 @@ void USBDevice::SearchDevices(std::function bool { + for(unsigned int i=1;i GetDevices(); + bool send(const QString &s); + bool receive(QString *s, unsigned int timeout = 2000); signals: void communicationFailure(); private: static void SearchDevices(std::function foundCallback, libusb_context *context, bool ignoreOpenError); - bool send(const QString &s); - bool receive(QString *s); libusb_device_handle *m_handle; libusb_context *m_context; QString m_serial; + + char rx_buf[1024]; + unsigned int rx_cnt; }; #endif // DEVICE_H