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

View File

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

View File

@ -52,14 +52,16 @@ public:
Timeout,
InternalError,
};
Q_ENUM(TransmissionResult)
// 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();
bool SendPacket(Protocol::PacketInfo packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 10);
bool Configure(Protocol::SweepSettings settings);
bool SendPacket(const Protocol::PacketInfo& packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 200);
bool Configure(Protocol::SweepSettings settings, std::function<void(TransmissionResult)> cb = nullptr);
bool Configure(Protocol::SpectrumAnalyzerSettings settings);
bool SetManual(Protocol::ManualControl manual);
bool SetIdle();
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type);
QString serial() const;
@ -84,6 +86,9 @@ private slots:
void transmissionTimeout() {
transmissionFinished(TransmissionResult::Timeout);
}
void transmissionFinished(TransmissionResult result);
signals:
void receivedAnswer(TransmissionResult result);
private:
static constexpr int EP_Data_Out_Addr = 0x01;
@ -107,8 +112,7 @@ private:
};
QQueue<Transmission> transmissionQueue;
void startNextTransmission();
void transmissionFinished(TransmissionResult result);
bool startNextTransmission();
QTimer transmissionTimer;
bool transmissionActive;

View File

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

View File

@ -95,11 +95,7 @@ VNA::VNA(AppWindow *window)
calMenu->addSeparator();
auto calData = calMenu->addAction("Calibration Data");
connect(calData, &QAction::triggered, [=](){
auto dialog = new CalibrationTraceDialog(&cal);
connect(dialog, &CalibrationTraceDialog::triggerMeasurement, this, &VNA::StartCalibrationMeasurement);
connect(dialog, &CalibrationTraceDialog::applyCalibration, this, &VNA::ApplyCalibration);
connect(this, &VNA::CalibrationMeasurementComplete, dialog, &CalibrationTraceDialog::measurementComplete);
dialog->show();
StartCalibrationDialog();
});
auto calImport = calMenu->addAction("Import error terms as traces");
@ -402,21 +398,26 @@ using namespace std;
void VNA::NewDatapoint(Protocol::Datapoint d)
{
d = average.process(d);
if(calMeasuring) {
if(!calWaitFirst || d.pointNum == 0) {
calWaitFirst = false;
cal.addMeasurement(calMeasurement, d);
if(d.pointNum == settings.points - 1) {
calMeasuring = false;
emit CalibrationMeasurementComplete(calMeasurement);
if(average.currentSweep() == averages) {
// this is the last averaging sweep, use values for calibration
if(!calWaitFirst || d.pointNum == 0) {
calWaitFirst = false;
cal.addMeasurement(calMeasurement, d);
if(d.pointNum == settings.points - 1) {
calMeasuring = false;
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) {
cal.correctMeasurement(d);
}
d = average.process(d);
traceModel.addVNAData(d);
emit dataChanged();
if(d.pointNum == settings.points - 1) {
@ -430,13 +431,13 @@ void VNA::UpdateAverageCount()
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;
if(window->getDevice()) {
window->getDevice()->Configure(settings);
window->getDevice()->Configure(settings, cb);
}
average.reset();
average.reset(settings.points);
traceModel.clearVNAData();
UpdateAverageCount();
emit traceModel.SpanChanged(settings.f_start, settings.f_stop);
@ -586,7 +587,7 @@ void VNA::DisableCalibration(bool force)
if(calValid || force) {
calValid = false;
emit CalibrationDisabled();
average.reset();
average.reset(settings.points);
}
}
@ -596,7 +597,7 @@ void VNA::ApplyCalibration(Calibration::Type type)
try {
if(cal.constructErrorTerms(type)) {
calValid = true;
average.reset();
average.reset(settings.points);
emit CalibrationApplied(type);
}
} catch (runtime_error e) {
@ -608,27 +609,25 @@ void VNA::ApplyCalibration(Calibration::Type type)
// 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.");
DisableCalibration(true);
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();
StartCalibrationDialog(type);
}
}
void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
{
// Trigger sweep to start from beginning
SettingsChanged();
calMeasurement = m;
auto device = window->getDevice();
if(!device) {
return;
}
// Stop sweep
StopSweep();
calMeasurement = m;
// Delete any already captured data of this measurement
cal.clearMeasurement(m);
calWaitFirst = true;
calMeasuring = true;
QString text = "Measuring \"";
text.append(Calibration::MeasurementToString(m));
text.append("\" parameters.");
calDialog.setRange(0, settings.points);
calDialog.setLabelText(text);
calDialog.setCancelButtonText("Abort");
calDialog.setWindowTitle("Taking calibration measurement...");
@ -641,6 +640,11 @@ void VNA::StartCalibrationMeasurement(Calibration::Measurement m)
calMeasuring = false;
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()
@ -684,3 +688,19 @@ void VNA::StoreSweepSettings()
s.setValue("SweepAveraging", averages);
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 "mode.h"
#include "CustomWidgets/tilewidget.h"
#include "Device/device.h"
#include <functional>
class VNA : public Mode
{
@ -43,10 +45,12 @@ signals:
private:
void UpdateAverageCount();
void SettingsChanged();
void SettingsChanged(std::function<void (Device::TransmissionResult)> cb = nullptr);
void ConstrainAndUpdateFrequencies();
void LoadSweepSettings();
void StoreSweepSettings();
void StopSweep();
void StartCalibrationDialog(Calibration::Type type = Calibration::Type::None);
Protocol::SweepSettings settings;
unsigned int averages;

View File

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

View File

@ -7,15 +7,18 @@ Averaging::Averaging()
averages = 1;
}
void Averaging::reset()
void Averaging::reset(unsigned int points)
{
avg.clear();
for(unsigned int i = 0;i<points;i++) {
avg.push_back(deque<array<complex<double>, 4>>());
}
}
void Averaging::setAverages(unsigned int a)
{
averages = a;
reset();
reset(avg.size());
}
Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
@ -110,3 +113,12 @@ unsigned int Averaging::getLevel()
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:
Averaging();
void reset();
void reset(unsigned int points);
void setAverages(unsigned int a);
Protocol::Datapoint process(Protocol::Datapoint 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();
// 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:
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
int maxPoints;