Various small bugfixes

- Improved device communication (callbacks for transmissions working properly now)
- Honor averaging when calibrating
- Ignore delayed points from last sweep during calibration
- Stop the sweep when disconnecting
This commit is contained in:
Jan Käberich 2020-10-29 19:27:04 +01:00
parent aee73f0c87
commit c0e4f41115
9 changed files with 142 additions and 63 deletions

View File

@ -484,7 +484,7 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_21"> <widget class="QLabel" name="label_21">
<property name="text"> <property name="text">
<string>Offset delay [ps]:</string> <string>Delay [ps]:</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -494,7 +494,7 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="label_22"> <widget class="QLabel" name="label_22">
<property name="text"> <property name="text">
<string>Offset loss [GΩ/s]: </string> <string>Loss [GΩ/s]: </string>
</property> </property>
</widget> </widget>
</item> </item>
@ -903,10 +903,10 @@
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="LoadType"/>
<buttongroup name="ShortType"/> <buttongroup name="ShortType"/>
<buttongroup name="LoadType"/>
<buttongroup name="TRL_Rtype"/>
<buttongroup name="OpenType"/> <buttongroup name="OpenType"/>
<buttongroup name="ThroughType"/> <buttongroup name="ThroughType"/>
<buttongroup name="TRL_Rtype"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -35,7 +35,10 @@ USBInBuffer::~USBInBuffer()
// wait for cancellation to complete // wait for cancellation to complete
mutex mtx; mutex mtx;
unique_lock<mutex> lck(mtx); unique_lock<mutex> lck(mtx);
cv.wait(lck); using namespace std::chrono_literals;
if(cv.wait_for(lck, 100ms) == cv_status::timeout) {
qWarning() << "Timed out waiting for mutex acquisition during disconnect";
}
} }
delete buffer; delete buffer;
} }
@ -69,10 +72,13 @@ void USBInBuffer::Callback(libusb_transfer *transfer)
inCallback = false; inCallback = false;
break; break;
case LIBUSB_TRANSFER_ERROR: case LIBUSB_TRANSFER_ERROR:
case LIBUSB_TRANSFER_NO_DEVICE:
case LIBUSB_TRANSFER_OVERFLOW:
case LIBUSB_TRANSFER_STALL:
qCritical() << "LIBUSB_TRANSFER_ERROR"; qCritical() << "LIBUSB_TRANSFER_ERROR";
case LIBUSB_TRANSFER_NO_DEVICE:
qCritical() << "LIBUSB_TRANSFER_NO_DEVICE";
case LIBUSB_TRANSFER_OVERFLOW:
qCritical() << "LIBUSB_TRANSFER_OVERFLOW";
case LIBUSB_TRANSFER_STALL:
qCritical() << "LIBUSB_TRANSFER_STALL";
libusb_free_transfer(transfer); libusb_free_transfer(transfer);
this->transfer = nullptr; this->transfer = nullptr;
emit TransferError(); emit TransferError();
@ -167,12 +173,13 @@ Device::Device(QString serial)
qInfo() << "USB connection established" << flush; qInfo() << "USB connection established" << flush;
m_connected = true; m_connected = true;
m_receiveThread = new std::thread(&Device::USBHandleThread, this); m_receiveThread = new std::thread(&Device::USBHandleThread, this);
dataBuffer = new USBInBuffer(m_handle, EP_Data_In_Addr, 2048); dataBuffer = new USBInBuffer(m_handle, EP_Data_In_Addr, 65536);
logBuffer = new USBInBuffer(m_handle, EP_Log_In_Addr, 2048); logBuffer = new USBInBuffer(m_handle, EP_Log_In_Addr, 65536);
connect(dataBuffer, &USBInBuffer::DataReceived, this, &Device::ReceivedData, Qt::DirectConnection); connect(dataBuffer, &USBInBuffer::DataReceived, this, &Device::ReceivedData, Qt::DirectConnection);
connect(dataBuffer, &USBInBuffer::TransferError, this, &Device::ConnectionLost); connect(dataBuffer, &USBInBuffer::TransferError, this, &Device::ConnectionLost);
connect(logBuffer, &USBInBuffer::DataReceived, this, &Device::ReceivedLog, Qt::DirectConnection); connect(logBuffer, &USBInBuffer::DataReceived, this, &Device::ReceivedLog, Qt::DirectConnection);
connect(&transmissionTimer, &QTimer::timeout, this, &Device::transmissionTimeout); connect(&transmissionTimer, &QTimer::timeout, this, &Device::transmissionTimeout);
connect(this, &Device::receivedAnswer, this, &Device::transmissionFinished, Qt::QueuedConnection);
transmissionTimer.setSingleShot(true); transmissionTimer.setSingleShot(true);
transmissionActive = false; transmissionActive = false;
// got a new connection, request limits // got a new connection, request limits
@ -182,6 +189,7 @@ Device::Device(QString serial)
Device::~Device() Device::~Device()
{ {
if(m_connected) { if(m_connected) {
SetIdle();
delete dataBuffer; delete dataBuffer;
delete logBuffer; delete logBuffer;
m_connected = false; m_connected = false;
@ -197,25 +205,26 @@ Device::~Device()
} }
} }
bool Device::SendPacket(Protocol::PacketInfo packet, std::function<void(TransmissionResult)> cb, unsigned int timeout) bool Device::SendPacket(const Protocol::PacketInfo& packet, std::function<void(TransmissionResult)> cb, unsigned int timeout)
{ {
Transmission t; Transmission t;
t.packet = packet; t.packet = packet;
t.timeout = timeout; t.timeout = timeout;
t.callback = cb; t.callback = cb;
transmissionQueue.enqueue(t); transmissionQueue.enqueue(t);
// qDebug() << "Enqueued packet, queue at " << transmissionQueue.size();
if(!transmissionActive) { if(!transmissionActive) {
startNextTransmission(); startNextTransmission();
} }
return true; return true;
} }
bool Device::Configure(Protocol::SweepSettings settings) bool Device::Configure(Protocol::SweepSettings settings, std::function<void(TransmissionResult)> cb)
{ {
Protocol::PacketInfo p; Protocol::PacketInfo p;
p.type = Protocol::PacketType::SweepSettings; p.type = Protocol::PacketType::SweepSettings;
p.settings = settings; p.settings = settings;
return SendPacket(p); return SendPacket(p, cb);
} }
bool Device::Configure(Protocol::SpectrumAnalyzerSettings settings) bool Device::Configure(Protocol::SpectrumAnalyzerSettings settings)
@ -234,6 +243,14 @@ bool Device::SetManual(Protocol::ManualControl manual)
return SendPacket(p); return SendPacket(p);
} }
bool Device::SetIdle()
{
Protocol::SweepSettings s;
s.excitePort1 = 0;
s.excitePort2 = 0;
return Configure(s);
}
bool Device::SendFirmwareChunk(Protocol::FirmwarePacket &fw) bool Device::SendFirmwareChunk(Protocol::FirmwarePacket &fw)
{ {
Protocol::PacketInfo p; Protocol::PacketInfo p;
@ -402,11 +419,11 @@ void Device::ReceivedData()
break; break;
case Protocol::PacketType::Ack: case Protocol::PacketType::Ack:
emit AckReceived(); emit AckReceived();
// transmissionFinished(TransmissionResult::Ack); emit receivedAnswer(TransmissionResult::Ack);
break; break;
case Protocol::PacketType::Nack: case Protocol::PacketType::Nack:
emit NackReceived(); emit NackReceived();
// transmissionFinished(TransmissionResult::Nack); emit receivedAnswer(TransmissionResult::Nack);
break; break;
case Protocol::PacketType::DeviceLimits: case Protocol::PacketType::DeviceLimits:
limits = packet.limits; limits = packet.limits;
@ -437,12 +454,12 @@ QString Device::serial() const
return m_serial; return m_serial;
} }
void Device::startNextTransmission() bool Device::startNextTransmission()
{ {
if(transmissionQueue.isEmpty() || !m_connected) { if(transmissionQueue.isEmpty() || !m_connected) {
// nothing more to transmit // nothing more to transmit
transmissionActive = false; transmissionActive = false;
return; return false;
} }
transmissionActive = true; transmissionActive = true;
auto t = transmissionQueue.head(); auto t = transmissionQueue.head();
@ -450,29 +467,45 @@ void Device::startNextTransmission()
unsigned int length = Protocol::EncodePacket(t.packet, buffer, sizeof(buffer)); unsigned int length = Protocol::EncodePacket(t.packet, buffer, sizeof(buffer));
if(!length) { if(!length) {
qCritical() << "Failed to encode packet"; qCritical() << "Failed to encode packet";
transmissionFinished(TransmissionResult::InternalError); return false;
} }
int actual_length; int actual_length;
auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0); auto ret = libusb_bulk_transfer(m_handle, EP_Data_Out_Addr, buffer, length, &actual_length, 0);
if(ret < 0) { if(ret < 0) {
qCritical() << "Error sending data: " qCritical() << "Error sending data: "
<< libusb_strerror((libusb_error) ret); << libusb_strerror((libusb_error) ret);
transmissionFinished(TransmissionResult::InternalError); return false;
} }
transmissionTimer.start(t.timeout); transmissionTimer.start(t.timeout);
// qDebug() << "Transmission started, queue at " << transmissionQueue.size();
return true;
} }
void Device::transmissionFinished(TransmissionResult result) void Device::transmissionFinished(TransmissionResult result)
{ {
transmissionTimer.stop();
// remove transmitted packet // remove transmitted packet
if(!transmissionQueue.isEmpty()) { // qDebug() << "Transmission finsished (" << result << "), queue at " << transmissionQueue.size();
if(transmissionQueue.empty()) {
qWarning() << "transmissionFinished with empty transmission queue, stray Ack?";
return;
}
auto t = transmissionQueue.dequeue(); auto t = transmissionQueue.dequeue();
if(t.callback) { if(t.callback) {
t.callback(result); t.callback(result);
} }
startNextTransmission(); transmissionTimer.stop();
} else { bool success = false;
while(!transmissionQueue.isEmpty() && !success) {
success = startNextTransmission();
if(!success) {
// failed to send this packet
auto t = transmissionQueue.dequeue();
if(t.callback) {
t.callback(TransmissionResult::InternalError);
}
}
}
if(transmissionQueue.isEmpty()) {
transmissionActive = false; transmissionActive = false;
} }
} }

View File

@ -52,14 +52,16 @@ public:
Timeout, Timeout,
InternalError, InternalError,
}; };
Q_ENUM(TransmissionResult)
// connect to a VNA device. If serial is specified only connecting to this device, otherwise to the first one found // connect to a VNA device. If serial is specified only connecting to this device, otherwise to the first one found
Device(QString serial = QString()); Device(QString serial = QString());
~Device(); ~Device();
bool SendPacket(Protocol::PacketInfo packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 10); bool SendPacket(const Protocol::PacketInfo& packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 200);
bool Configure(Protocol::SweepSettings settings); bool Configure(Protocol::SweepSettings settings, std::function<void(TransmissionResult)> cb = nullptr);
bool Configure(Protocol::SpectrumAnalyzerSettings settings); bool Configure(Protocol::SpectrumAnalyzerSettings settings);
bool SetManual(Protocol::ManualControl manual); bool SetManual(Protocol::ManualControl manual);
bool SetIdle();
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw); bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type); bool SendCommandWithoutPayload(Protocol::PacketType type);
QString serial() const; QString serial() const;
@ -84,6 +86,9 @@ private slots:
void transmissionTimeout() { void transmissionTimeout() {
transmissionFinished(TransmissionResult::Timeout); transmissionFinished(TransmissionResult::Timeout);
} }
void transmissionFinished(TransmissionResult result);
signals:
void receivedAnswer(TransmissionResult result);
private: private:
static constexpr int EP_Data_Out_Addr = 0x01; static constexpr int EP_Data_Out_Addr = 0x01;
@ -107,8 +112,7 @@ private:
}; };
QQueue<Transmission> transmissionQueue; QQueue<Transmission> transmissionQueue;
void startNextTransmission(); bool startNextTransmission();
void transmissionFinished(TransmissionResult result);
QTimer transmissionTimer; QTimer transmissionTimer;
bool transmissionActive; bool transmissionActive;

View File

@ -254,7 +254,7 @@ void SpectrumAnalyzer::SettingsChanged()
if(window->getDevice()) { if(window->getDevice()) {
window->getDevice()->Configure(settings); window->getDevice()->Configure(settings);
} }
average.reset(); average.reset(settings.pointNum);
UpdateAverageCount(); UpdateAverageCount();
traceModel.clearVNAData(); traceModel.clearVNAData();
emit traceModel.SpanChanged(settings.f_start, settings.f_stop); emit traceModel.SpanChanged(settings.f_start, settings.f_stop);

View File

@ -95,11 +95,7 @@ VNA::VNA(AppWindow *window)
calMenu->addSeparator(); calMenu->addSeparator();
auto calData = calMenu->addAction("Calibration Data"); auto calData = calMenu->addAction("Calibration Data");
connect(calData, &QAction::triggered, [=](){ connect(calData, &QAction::triggered, [=](){
auto dialog = new CalibrationTraceDialog(&cal); StartCalibrationDialog();
connect(dialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement);
connect(dialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration);
connect(this, &VNA::CalibrationMeasurementComplete, dialog, &CalibrationTraceDialog::measurementComplete);
dialog->show();
}); });
auto calImport = calMenu->addAction("Import error terms as traces"); auto calImport = calMenu->addAction("Import error terms as traces");
@ -402,7 +398,11 @@ using namespace std;
void VNA::NewDatapoint(Protocol::Datapoint d) void VNA::NewDatapoint(Protocol::Datapoint d)
{ {
d = average.process(d);
if(calMeasuring) { if(calMeasuring) {
if(average.currentSweep() == averages) {
// this is the last averaging sweep, use values for calibration
if(!calWaitFirst || d.pointNum == 0) { if(!calWaitFirst || d.pointNum == 0) {
calWaitFirst = false; calWaitFirst = false;
cal.addMeasurement(calMeasurement, d); cal.addMeasurement(calMeasurement, d);
@ -410,13 +410,14 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
calMeasuring = false; calMeasuring = false;
emit CalibrationMeasurementComplete(calMeasurement); emit CalibrationMeasurementComplete(calMeasurement);
} }
calDialog.setValue(d.pointNum + 1);
} }
} }
int percentage = (((average.currentSweep() - 1) * 100) + (d.pointNum + 1) * 100 / settings.points) / averages;
calDialog.setValue(percentage);
}
if(calValid) { if(calValid) {
cal.correctMeasurement(d); cal.correctMeasurement(d);
} }
d = average.process(d);
traceModel.addVNAData(d); traceModel.addVNAData(d);
emit dataChanged(); emit dataChanged();
if(d.pointNum == settings.points - 1) { if(d.pointNum == settings.points - 1) {
@ -430,13 +431,13 @@ void VNA::UpdateAverageCount()
lAverages->setText(QString::number(average.getLevel()) + "/"); lAverages->setText(QString::number(average.getLevel()) + "/");
} }
void VNA::SettingsChanged() void VNA::SettingsChanged(std::function<void (Device::TransmissionResult)> cb)
{ {
settings.suppressPeaks = Preferences::getInstance().Acquisition.suppressPeaks ? 1 : 0; settings.suppressPeaks = Preferences::getInstance().Acquisition.suppressPeaks ? 1 : 0;
if(window->getDevice()) { if(window->getDevice()) {
window->getDevice()->Configure(settings); window->getDevice()->Configure(settings, cb);
} }
average.reset(); average.reset(settings.points);
traceModel.clearVNAData(); traceModel.clearVNAData();
UpdateAverageCount(); UpdateAverageCount();
emit traceModel.SpanChanged(settings.f_start, settings.f_stop); emit traceModel.SpanChanged(settings.f_start, settings.f_stop);
@ -586,7 +587,7 @@ void VNA::DisableCalibration(bool force)
if(calValid || force) { if(calValid || force) {
calValid = false; calValid = false;
emit CalibrationDisabled(); emit CalibrationDisabled();
average.reset(); average.reset(settings.points);
} }
} }
@ -596,7 +597,7 @@ void VNA::ApplyCalibration(Calibration::Type type)
try { try {
if(cal.constructErrorTerms(type)) { if(cal.constructErrorTerms(type)) {
calValid = true; calValid = true;
average.reset(); average.reset(settings.points);
emit CalibrationApplied(type); emit CalibrationApplied(type);
} }
} catch (runtime_error e) { } catch (runtime_error e) {
@ -608,27 +609,25 @@ void VNA::ApplyCalibration(Calibration::Type type)
// TODO start tracedata dialog with required traces // TODO start tracedata dialog with required traces
QMessageBox::information(this, "Missing calibration traces", "Not all calibration traces for this type of calibration have been measured. The calibration can be enabled after the missing traces have been acquired."); QMessageBox::information(this, "Missing calibration traces", "Not all calibration traces for this type of calibration have been measured. The calibration can be enabled after the missing traces have been acquired.");
DisableCalibration(true); DisableCalibration(true);
auto traceDialog = new CalibrationTraceDialog(&cal, type); StartCalibrationDialog(type);
connect(traceDialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement);
connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration);
connect(this, &VNA::CalibrationMeasurementComplete, traceDialog, &CalibrationTraceDialog::measurementComplete);
traceDialog->show();
} }
} }
void VNA::StartCalibrationMeasurement(Calibration::Measurement m) void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
{ {
// Trigger sweep to start from beginning auto device = window->getDevice();
SettingsChanged(); if(!device) {
return;
}
// Stop sweep
StopSweep();
calMeasurement = m; calMeasurement = m;
// Delete any already captured data of this measurement // Delete any already captured data of this measurement
cal.clearMeasurement(m); cal.clearMeasurement(m);
calWaitFirst = true; calWaitFirst = true;
calMeasuring = true;
QString text = "Measuring \""; QString text = "Measuring \"";
text.append(Calibration::MeasurementToString(m)); text.append(Calibration::MeasurementToString(m));
text.append("\" parameters."); text.append("\" parameters.");
calDialog.setRange(0, settings.points);
calDialog.setLabelText(text); calDialog.setLabelText(text);
calDialog.setCancelButtonText("Abort"); calDialog.setCancelButtonText("Abort");
calDialog.setWindowTitle("Taking calibration measurement..."); calDialog.setWindowTitle("Taking calibration measurement...");
@ -641,6 +640,11 @@ void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
calMeasuring = false; calMeasuring = false;
cal.clearMeasurement(calMeasurement); cal.clearMeasurement(calMeasurement);
}); });
// Trigger sweep to start from beginning
SettingsChanged([=](Device::TransmissionResult){
// enable calibration measurement only in transmission callback (prevents accidental sampling of data which was still being processed)
calMeasuring = true;
});
} }
void VNA::ConstrainAndUpdateFrequencies() void VNA::ConstrainAndUpdateFrequencies()
@ -684,3 +688,19 @@ void VNA::StoreSweepSettings()
s.setValue("SweepAveraging", averages); s.setValue("SweepAveraging", averages);
s.setValue("SweepLevel", (double) settings.cdbm_excitation / 100.0); s.setValue("SweepLevel", (double) settings.cdbm_excitation / 100.0);
} }
void VNA::StopSweep()
{
if(window->getDevice()) {
window->getDevice()->SetIdle();
}
}
void VNA::StartCalibrationDialog(Calibration::Type type)
{
auto traceDialog = new CalibrationTraceDialog(&cal, type);
connect(traceDialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement);
connect(traceDialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration);
connect(this, &VNA::CalibrationMeasurementComplete, traceDialog, &CalibrationTraceDialog::measurementComplete);
traceDialog->show();
}

View File

@ -6,6 +6,8 @@
#include "appwindow.h" #include "appwindow.h"
#include "mode.h" #include "mode.h"
#include "CustomWidgets/tilewidget.h" #include "CustomWidgets/tilewidget.h"
#include "Device/device.h"
#include <functional>
class VNA : public Mode class VNA : public Mode
{ {
@ -43,10 +45,12 @@ signals:
private: private:
void UpdateAverageCount(); void UpdateAverageCount();
void SettingsChanged(); void SettingsChanged(std::function<void (Device::TransmissionResult)> cb = nullptr);
void ConstrainAndUpdateFrequencies(); void ConstrainAndUpdateFrequencies();
void LoadSweepSettings(); void LoadSweepSettings();
void StoreSweepSettings(); void StoreSweepSettings();
void StopSweep();
void StartCalibrationDialog(Calibration::Type type = Calibration::Type::None);
Protocol::SweepSettings settings; Protocol::SweepSettings settings;
unsigned int averages; unsigned int averages;

View File

@ -139,6 +139,7 @@ AppWindow::AppWindow(QWidget *parent)
void AppWindow::closeEvent(QCloseEvent *event) void AppWindow::closeEvent(QCloseEvent *event)
{ {
delete device;
QSettings settings; QSettings settings;
settings.setValue("geometry", saveGeometry()); settings.setValue("geometry", saveGeometry());
// deactivate currently used mode (stores mode state in settings) // deactivate currently used mode (stores mode state in settings)

View File

@ -7,15 +7,18 @@ Averaging::Averaging()
averages = 1; averages = 1;
} }
void Averaging::reset() void Averaging::reset(unsigned int points)
{ {
avg.clear(); avg.clear();
for(unsigned int i = 0;i<points;i++) {
avg.push_back(deque<array<complex<double>, 4>>());
}
} }
void Averaging::setAverages(unsigned int a) void Averaging::setAverages(unsigned int a)
{ {
averages = a; averages = a;
reset(); reset(avg.size());
} }
Protocol::Datapoint Averaging::process(Protocol::Datapoint d) Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
@ -110,3 +113,12 @@ unsigned int Averaging::getLevel()
return 0; return 0;
} }
} }
unsigned int Averaging::currentSweep()
{
if(avg.size() > 0) {
return avg.front().size();
} else {
return 0;
}
}

View File

@ -10,11 +10,16 @@ class Averaging
{ {
public: public:
Averaging(); Averaging();
void reset(); void reset(unsigned int points);
void setAverages(unsigned int a); void setAverages(unsigned int a);
Protocol::Datapoint process(Protocol::Datapoint d); Protocol::Datapoint process(Protocol::Datapoint d);
Protocol::SpectrumAnalyzerResult process(Protocol::SpectrumAnalyzerResult d); Protocol::SpectrumAnalyzerResult process(Protocol::SpectrumAnalyzerResult d);
// Returns the number of averaged sweeps. Value is incremented whenever the last point of the sweep is added.
// Returned values are in range 0 to averages
unsigned int getLevel(); unsigned int getLevel();
// Returns the number of the currently active sweep. Value is incremented whenever the the first point of the sweep is added
// Returned values are in range 0 (when no data has been added yet) to averages
unsigned int currentSweep();
private: private:
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg; std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
int maxPoints; int maxPoints;