WIP: make de-embedding and calibration work with arbitrary number of ports

This commit is contained in:
Jan Käberich 2022-09-13 21:42:47 +02:00
parent 8301448343
commit 9cf76c9681
30 changed files with 478 additions and 193 deletions

View File

@ -45,6 +45,7 @@ QString Calibration::TypeToString(Calibration::Type type)
switch(type) {
case Type::None: return "None";
case Type::SOLT: return "SOLT";
case Type::ThroughNormalization: return "ThroughNormalization";
case Type::Last: return "Invalid";
}
}
@ -134,6 +135,18 @@ void Calibration::correctMeasurement(VirtualDevice::VNAMeasurement &d)
}
}
void Calibration::correctTraces(std::map<QString, Trace *> traceSet)
{
auto points = Trace::assembleDatapoints(traceSet);
if(points.size()) {
// succeeded in assembling datapoints
for(auto &p : points) {
correctMeasurement(p);
}
Trace::fillFromDatapoints(traceSet, points);
}
}
void Calibration::edit()
{
auto d = new QDialog();
@ -390,8 +403,7 @@ CalibrationMeasurement::Base *Calibration::newMeasurement(CalibrationMeasurement
return m;
}
Calibration::Point Calibration::computeSOLT(double f)
{
Calibration::Point Calibration::createInitializedPoint(double f) {
Point point;
point.frequency = f;
// resize vectors
@ -405,6 +417,12 @@ Calibration::Point Calibration::computeSOLT(double f)
fill(point.L.begin(), point.L.end(), vector<complex<double>>(caltype.usedPorts.size()));
fill(point.T.begin(), point.T.end(), vector<complex<double>>(caltype.usedPorts.size()));
fill(point.I.begin(), point.I.end(), vector<complex<double>>(caltype.usedPorts.size()));
return point;
}
Calibration::Point Calibration::computeSOLT(double f)
{
Point point = createInitializedPoint(f);
// Calculate SOL coefficients
for(unsigned int i=0;i<caltype.usedPorts.size();i++) {
@ -464,6 +482,47 @@ Calibration::Point Calibration::computeSOLT(double f)
return point;
}
Calibration::Point Calibration::computeThroughNormalization(double f)
{
Point point = createInitializedPoint(f);
// Calculate SOL coefficients
for(unsigned int i=0;i<caltype.usedPorts.size();i++) {
// use ideal coefficients
point.D[i] = 0.0;
point.S[i] = 0.0;
point.R[i] = 1.0;
}
// calculate forward match and transmission
for(unsigned int i=0;i<caltype.usedPorts.size();i++) {
for(unsigned int j=0;j<caltype.usedPorts.size();j++) {
if(i == j) {
// this is the exciting port, SOL error box used here
continue;
}
auto p1 = caltype.usedPorts[i];
auto p2 = caltype.usedPorts[j];
// grab measurement and calkit through definitions
auto throughForward = static_cast<CalibrationMeasurement::Through*>(findMeasurement(CalibrationMeasurement::Base::Type::Through, p1, p2));
auto throughReverse = static_cast<CalibrationMeasurement::Through*>(findMeasurement(CalibrationMeasurement::Base::Type::Through, p2, p1));
complex<double> S11, S21;
Sparam Sideal;
if(throughForward) {
S21 = throughForward->getMeasured(f).m21;
Sideal = throughForward->getActual(f);
} else if(throughReverse) {
S21 = throughReverse->getMeasured(f).m12;
Sideal = throughReverse->getActual(f);
swap(Sideal.m12, Sideal.m21);
}
point.L[i][j] = 0.0;
point.T[i][j] = S21 / Sideal.m21;
point.I[i][j] = 0.0;
}
}
return point;
}
Calibration::CalType Calibration::getCaltype() const
{
return caltype;
@ -504,12 +563,151 @@ Calibration::InterpolationType Calibration::getInterpolation(double f_start, dou
std::vector<Trace *> Calibration::getErrorTermTraces()
{
return vector<Trace*>(); // TODO
vector<Trace*> ret;
if(points.size() == 0) {
return ret;
}
for(unsigned int i=0;i<caltype.usedPorts.size();i++) {
auto p = caltype.usedPorts[i];
auto tDir = new Trace("Directivity_Port"+QString::number(p));
tDir->setReflection(true);
tDir->setCalibration();
auto tSM = new Trace("SourceMatch_Port"+QString::number(p));
tSM->setReflection(true);
tSM->setCalibration();
auto tRT = new Trace("ReflectionTracking_Port"+QString::number(p));
tRT->setReflection(false);
tRT->setCalibration();
for(auto p : points) {
Trace::Data td;
td.x = p.frequency;
td.y = p.D[i];
tDir->addData(td, Trace::DataType::Frequency);
td.y = p.S[i];
tSM->addData(td, Trace::DataType::Frequency);
td.y = p.R[i];
tRT->addData(td, Trace::DataType::Frequency);
}
ret.push_back(tDir);
ret.push_back(tSM);
ret.push_back(tRT);
for(unsigned int j=0;j<caltype.usedPorts.size();j++) {
if(i==j) {
continue;
}
auto p2 = caltype.usedPorts[j];
auto tRM = new Trace("ReceiverMatch_"+QString::number(p)+QString::number(p2));
tRM->setReflection(true);
tRM->setCalibration();
auto tTT = new Trace("TransmissionTracking_"+QString::number(p)+QString::number(p2));
tTT->setReflection(false);
tTT->setCalibration();
auto tTI = new Trace("TransmissionIsolation_"+QString::number(p)+QString::number(p2));
tTI->setReflection(false);
tTI->setCalibration();
for(auto p : points) {
Trace::Data td;
td.x = p.frequency;
td.y = p.L[i][j];
tRM->addData(td, Trace::DataType::Frequency);
td.y = p.T[i][j];
tTT->addData(td, Trace::DataType::Frequency);
td.y = p.I[i][j];
tTI->addData(td, Trace::DataType::Frequency);
}
ret.push_back(tRM);
ret.push_back(tTT);
ret.push_back(tTI);
}
}
return ret;
}
std::vector<Trace *> Calibration::getMeasurementTraces()
{
return vector<Trace*>(); // TODO
vector<Trace*> ret;
for(auto m : measurements) {
switch(m->getType()) {
case CalibrationMeasurement::Base::Type::Open:
case CalibrationMeasurement::Base::Type::Short:
case CalibrationMeasurement::Base::Type::Load: {
auto onePort = static_cast<CalibrationMeasurement::OnePort*>(m);
auto t = new Trace(CalibrationMeasurement::Base::TypeToString(onePort->getType())+"_Port"+QString::number(onePort->getPort()));
t->setCalibration();
t->setReflection(true);
for(auto d : onePort->getPoints()) {
Trace::Data td;
td.x = d.frequency;
td.y = d.S;
t->addData(td, Trace::DataType::Frequency);
}
ret.push_back(t);
}
break;
case CalibrationMeasurement::Base::Type::Through: {
auto twoPort = static_cast<CalibrationMeasurement::TwoPort*>(m);
auto ts11 = new Trace(CalibrationMeasurement::Base::TypeToString(twoPort->getType())+"_Port"+QString::number(twoPort->getPort1())+QString::number(twoPort->getPort2())+"_S11");
auto ts12 = new Trace(CalibrationMeasurement::Base::TypeToString(twoPort->getType())+"_Port"+QString::number(twoPort->getPort1())+QString::number(twoPort->getPort2())+"_S12");
auto ts21 = new Trace(CalibrationMeasurement::Base::TypeToString(twoPort->getType())+"_Port"+QString::number(twoPort->getPort1())+QString::number(twoPort->getPort2())+"_S21");
auto ts22 = new Trace(CalibrationMeasurement::Base::TypeToString(twoPort->getType())+"_Port"+QString::number(twoPort->getPort1())+QString::number(twoPort->getPort2())+"_S22");
ts11->setCalibration();
ts11->setReflection(true);
ts12->setCalibration();
ts12->setReflection(false);
ts21->setCalibration();
ts21->setReflection(false);
ts22->setCalibration();
ts22->setReflection(true);
for(auto d : twoPort->getPoints()) {
Trace::Data td;
td.x = d.frequency;
td.y = d.S.m11;
ts11->addData(td, Trace::DataType::Frequency);
td.y = d.S.m12;
ts12->addData(td, Trace::DataType::Frequency);
td.y = d.S.m21;
ts21->addData(td, Trace::DataType::Frequency);
td.y = d.S.m22;
ts22->addData(td, Trace::DataType::Frequency);
}
ret.push_back(ts11);
ret.push_back(ts12);
ret.push_back(ts21);
ret.push_back(ts22);
}
break;
case CalibrationMeasurement::Base::Type::Isolation: {
auto iso = static_cast<CalibrationMeasurement::Isolation*>(m);
int ports = iso->getPoints()[0].S.size();
// Create the traces
vector<vector<Trace*>> traces;
traces.resize(ports);
for(int i=0;i<ports;i++) {
for(int j=0;j<ports;j++) {
auto t = new Trace(CalibrationMeasurement::Base::TypeToString(iso->getType())+"_S"+QString::number(i+1)+QString::number(j+1));
t->setCalibration();
t->setReflection(i==j);
traces[i].push_back(t);
// also add to main return vector
ret.push_back(t);
}
}
// Fill the traces
for(auto p : iso->getPoints()) {
Trace::Data td;
td.x = p.frequency;
for(int i=0;i<p.S.size();i++) {
for(int j=0;j<p.S[i].size();j++) {
td.y = p.S[i][j];
traces[i][j]->addData(td, Trace::DataType::Frequency);
}
}
}
}
break;
}
}
return ret;
}
QString Calibration::getCurrentCalibrationFile()
@ -735,15 +933,15 @@ std::vector<Calibration::Type> Calibration::getTypes()
bool Calibration::canCompute(Calibration::CalType type, double *startFreq, double *stopFreq, int *points)
{
using RequiredMeasurements = struct {
CalibrationMeasurement::Base::Type type;
int port1, port2;
};
vector<RequiredMeasurements> required;
switch(type.type) {
case Type::None:
return true; // Always possible to reset the calibration
case Type::SOLT: {
using RequiredMeasurements = struct {
CalibrationMeasurement::Base::Type type;
int port1, port2;
};
vector<RequiredMeasurements> required;
case Type::SOLT:
// SOL measurements for every port
for(auto p : type.usedPorts) {
required.push_back({.type = CalibrationMeasurement::Base::Type::Short, .port1 = p});
@ -756,6 +954,17 @@ bool Calibration::canCompute(Calibration::CalType type, double *startFreq, doubl
required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j});
}
}
break;
case Type::ThroughNormalization:
// through measurements between all ports
for(int i=1;i<=type.usedPorts.size();i++) {
for(int j=i+1;j<=type.usedPorts.size();j++) {
required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j});
}
}
break;
}
if(required.size() > 0) {
vector<CalibrationMeasurement::Base*> foundMeasurements;
for(auto m : required) {
auto meas = findMeasurement(m.type, m.port1, m.port2);
@ -767,8 +976,6 @@ bool Calibration::canCompute(Calibration::CalType type, double *startFreq, doubl
}
}
return hasFrequencyOverlap(foundMeasurements, startFreq, stopFreq, points);
}
break;
}
return false;
}
@ -816,6 +1023,7 @@ int Calibration::minimumPorts(Calibration::Type type)
{
switch(type) {
case Type::SOLT: return 1;
case Type::ThroughNormalization: return 2;
}
return -1;
}

View File

@ -17,6 +17,7 @@ public:
enum class Type {
None,
SOLT,
ThroughNormalization,
Last,
};
class CalType {
@ -36,6 +37,7 @@ public:
// Applies calculated calibration coefficients to measurement data
void correctMeasurement(VirtualDevice::VNAMeasurement &d);
void correctTraces(std::map<QString, Trace*> traceSet);
// Starts the calibration edit dialog, allowing the user to make/delete measurements
void edit();
@ -123,8 +125,8 @@ private:
public:
double frequency;
std::vector<std::complex<double>> D; // Directivity
std::vector<std::complex<double>> R; // Source Match
std::vector<std::complex<double>> S; // Reflection tracking
std::vector<std::complex<double>> R; // Reflection tracking
std::vector<std::complex<double>> S; // Source Match
std::vector<std::vector<std::complex<double>>> L; // Receiver Match
std::vector<std::vector<std::complex<double>>> T; // Transmission tracking
std::vector<std::vector<std::complex<double>>> I; // Transmission isolation
@ -132,7 +134,9 @@ private:
};
std::vector<Point> points;
Point createInitializedPoint(double f);
Point computeSOLT(double f);
Point computeThroughNormalization(double f);
std::vector<CalibrationMeasurement::Base*> measurements;

View File

@ -328,6 +328,11 @@ void CalibrationMeasurement::OnePort::setPort(int p)
}
}
std::vector<CalibrationMeasurement::OnePort::Point> CalibrationMeasurement::OnePort::getPoints() const
{
return points;
}
double CalibrationMeasurement::TwoPort::minFreq()
{
if(points.size() > 0) {
@ -529,6 +534,11 @@ void CalibrationMeasurement::TwoPort::setReverseStandard(bool reverse)
}
}
std::vector<CalibrationMeasurement::TwoPort::Point> CalibrationMeasurement::TwoPort::getPoints() const
{
return points;
}
int CalibrationMeasurement::TwoPort::getPort1() const
{
return port1;
@ -658,3 +668,8 @@ std::complex<double> CalibrationMeasurement::Isolation::getMeasured(double frequ
return p.S[portRcv][portSrc];
}
}
std::vector<CalibrationMeasurement::Isolation::Point> CalibrationMeasurement::Isolation::getPoints() const
{
return points;
}

View File

@ -1,4 +1,4 @@
#ifndef CALIBRATIONMEASUREMENT_H
#ifndef CALIBRATIONMEASUREMENT_H
#define CALIBRATIONMEASUREMENT_H
#include "calstandard.h"
@ -83,6 +83,13 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
class Point {
public:
double frequency;
std::complex<double> S;
};
std::vector<Point> getPoints() const;
std::complex<double> getMeasured(double frequency);
std::complex<double> getActual(double frequency);
@ -95,11 +102,6 @@ signals:
void portChanged(int p);
protected:
int port;
class Point {
public:
double frequency;
std::complex<double> S;
};
std::vector<Point> points;
};
@ -156,12 +158,20 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
class Point {
public:
double frequency;
Sparam S;
};
std::vector<Point> getPoints() const;
Sparam getMeasured(double frequency);
Sparam getActual(double frequency);
int getPort1() const;
int getPort2() const;
public slots:
void setPort1(int p);
void setPort2(int p);
@ -174,11 +184,6 @@ signals:
protected:
int port1, port2;
bool reverseStandard; // Set to true if standard is defined with ports swapped
class Point {
public:
double frequency;
Sparam S;
};
std::vector<Point> points;
};
@ -211,17 +216,19 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
class Point {
public:
double frequency;
std::vector<std::vector<std::complex<double>>> S;
};
std::vector<Point> getPoints() const;
std::complex<double> getMeasured(double frequency, unsigned int portRcv, unsigned int portSrc);
virtual std::set<CalStandard::Virtual::Type> supportedStandardTypes() override {return {};}
virtual Type getType() override {return Type::Isolation;}
protected:
class Point {
public:
double frequency;
std::vector<std::vector<std::complex<double>>> S;
};
std::vector<Point> points;
};

View File

@ -7,13 +7,12 @@ ManualCalibrationDialog::ManualCalibrationDialog(const TraceModel &model, Calibr
ui(new Ui::ManualCalibrationDialog)
{
ui->setupUi(this);
auto traceSelector = new SparamTraceSelector(model, 2);
auto traceSelector = new SparamTraceSelector(model, cal->getCaltype().usedPorts);
ui->verticalLayout->insertWidget(1, traceSelector, 1.0);
ui->buttonBox->setEnabled(false);
connect(traceSelector, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=]() {
auto t = traceSelector->getTraces();
// cal->correctTraces(*t[0], *t[1], *t[2], *t[3]); // TODO
cal->correctTraces(traceSelector->getTraces());
accept();
});
}

View File

@ -5,42 +5,39 @@
using namespace std;
SparamTraceSelector::SparamTraceSelector(const TraceModel &model, unsigned int num_ports, bool empty_allowed, std::set<unsigned int> skip)
SparamTraceSelector::SparamTraceSelector(const TraceModel &model, std::vector<int> used_ports, bool empty_allowed)
: model(model),
num_ports(num_ports),
used_ports(used_ports),
empty_allowed(empty_allowed)
{
// Create comboboxes
auto layout = new QFormLayout;
setLayout(layout);
for(unsigned int i=0;i<num_ports;i++) {
for(unsigned int j=0;j<num_ports;j++) {
auto label = new QLabel("S"+QString::number(i+1)+QString::number(j+1)+":");
auto box = new QComboBox();
connect(box, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
traceSelectionChanged(box);
});
boxes.push_back(box);
layout->addRow(label, box);
if(skip.count(i*num_ports + j)) {
label->setVisible(false);
box->setVisible(false);
}
}
}
createGUI();
setInitialChoices();
}
std::vector<Trace*> SparamTraceSelector::getTraces()
SparamTraceSelector::SparamTraceSelector(const TraceModel &model, std::set<int> used_ports, bool empty_allowed)
: model(model),
empty_allowed(empty_allowed)
{
vector<Trace*> ret;
for(auto b : boxes) {
if(b->currentIndex() == 0) {
ret.push_back(nullptr);
} else {
auto trace = qvariant_cast<Trace*>(b->itemData(b->currentIndex()));
ret.push_back(trace);
// create vector from set
std::copy(used_ports.begin(), used_ports.end(), std::back_inserter(this->used_ports));
createGUI();
setInitialChoices();
}
std::map<QString, Trace*> SparamTraceSelector::getTraces()
{
std::map<QString, Trace*> ret;
for(unsigned int i=0;i<used_ports.size();i++) {
for(unsigned int j=0;j<used_ports.size();j++) {
auto b = boxes[i*used_ports.size()+j];
Trace *t;
if(b->currentIndex() == 0) {
t = nullptr;
} else {
t = qvariant_cast<Trace*>(b->itemData(b->currentIndex()));
}
QString name = "S"+QString::number(used_ports[i])+QString::number(used_ports[j]);
ret[name] = t;
}
}
return ret;
@ -48,7 +45,7 @@ std::vector<Trace*> SparamTraceSelector::getTraces()
void SparamTraceSelector::setInitialChoices()
{
for(unsigned int i=0;i<num_ports*num_ports;i++) {
for(unsigned int i=0;i<used_ports.size()*used_ports.size();i++) {
boxes[i]->blockSignals(true);
boxes[i]->clear();
boxes[i]->addItem("None");
@ -61,7 +58,7 @@ void SparamTraceSelector::setInitialChoices()
// can't select empty traces
continue;
}
bool reflectionRequired = i%(num_ports+1) == 0 ? true : false;
bool reflectionRequired = i%(used_ports.size()+1) == 0 ? true : false;
if(reflectionRequired != t->isReflection()) {
// invalid S parameter
continue;
@ -106,7 +103,7 @@ void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
text.chop(2);
if(text.endsWith("S")) {
// tracename ended in Sxx, probably other traces with matching prefix available
for(unsigned int i=0;i<num_ports*num_ports;i++) {
for(unsigned int i=0;i<used_ports.size()*used_ports.size();i++) {
auto b = boxes[i];
if(b == cb) {
// skip this box
@ -115,7 +112,7 @@ void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
for(int j=0;j<b->count();j++) {
auto candidate = b->itemText(j);
// check if correct parameter
QString expectedSparam = QString::number(i/num_ports+1)+QString::number(i%num_ports+1);
QString expectedSparam = QString::number(i/used_ports.size()+1)+QString::number(i%used_ports.size()+1);
if(!candidate.endsWith(expectedSparam)) {
// wrong S parameter, skip
continue;
@ -170,3 +167,21 @@ void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
emit selectionValid(valid);
}
}
void SparamTraceSelector::createGUI()
{
// Create comboboxes
auto layout = new QFormLayout;
setLayout(layout);
for(unsigned int i=0;i<used_ports.size();i++) {
for(unsigned int j=0;j<used_ports.size();j++) {
auto label = new QLabel("S"+QString::number(used_ports[i])+QString::number(used_ports[j])+":");
auto box = new QComboBox();
connect(box, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
traceSelectionChanged(box);
});
boxes.push_back(box);
layout->addRow(label, box);
}
}
}

View File

@ -3,6 +3,8 @@
#include "tracemodel.h"
#include <vector>
#include <map>
#include <QWidget>
#include <QComboBox>
@ -11,12 +13,13 @@ class SparamTraceSelector : public QWidget
Q_OBJECT
public:
SparamTraceSelector(const TraceModel &model, unsigned int num_ports, bool empty_allowed = false, std::set<unsigned int> skip = {});
SparamTraceSelector(const TraceModel &model, std::vector<int> used_ports, bool empty_allowed = false);
SparamTraceSelector(const TraceModel &model, std::set<int> used_ports, bool empty_allowed = false);
bool isValid();
std::vector<Trace*> getTraces();
unsigned int getPoints() { return points;};
std::map<QString, Trace*> getTraces();
unsigned int getPoints() { return points;}
signals:
void selectionValid(bool valid);
@ -24,12 +27,13 @@ signals:
private:
void setInitialChoices();
void traceSelectionChanged(QComboBox *cb);
void createGUI();
const TraceModel &model;
std::vector<QComboBox*> boxes;
unsigned int num_ports;
bool empty_allowed;
std::vector<int> used_ports;
unsigned int points;
double minFreq, maxFreq;
bool valid;

View File

@ -260,24 +260,24 @@ QString Trace::fillFromCSV(CSV &csv, unsigned int parameter)
return lastTraceName;
}
void Trace::fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<VirtualDevice::VNAMeasurement> &data)
void Trace::fillFromDatapoints(std::map<QString, Trace *> traceSet, const std::vector<VirtualDevice::VNAMeasurement> &data)
{
S11.clear();
S12.clear();
S21.clear();
S22.clear();
// remove all previous points
for(auto m : traceSet) {
m.second->clear();
}
// add new points to traces
for(auto d : data) {
Trace::Data td;
auto S = d.toSparam(1, 2);
td.x = d.frequency;
td.y = S.m11;
S11.addData(td, DataType::Frequency);
td.y = S.m12;
S12.addData(td, DataType::Frequency);
td.y = S.m21;
S21.addData(td, DataType::Frequency);
td.y = S.m22;
S22.addData(td, DataType::Frequency);
for(auto m : d.measurements) {
td.y = m.second;
QString measurement = m.first;
if(traceSet.count(measurement)) {
traceSet[measurement]->addData(td, DataType::Frequency);
}
}
}
}
@ -894,20 +894,16 @@ std::vector<Trace *> Trace::createFromCSV(CSV &csv)
return traces;
}
std::vector<VirtualDevice::VNAMeasurement> Trace::assembleDatapoints(const Trace &S11, const Trace &S12, const Trace &S21, const Trace &S22)
std::vector<VirtualDevice::VNAMeasurement> Trace::assembleDatapoints(std::map<QString, Trace *> traceSet)
{
vector<VirtualDevice::VNAMeasurement> ret;
// Sanity check traces
unsigned int samples = S11.size();
auto impedance = S11.getReferenceImpedance();
vector<const Trace*> traces;
traces.push_back(&S11);
traces.push_back(&S12);
traces.push_back(&S21);
traces.push_back(&S22);
unsigned int samples = traceSet.begin()->second->size();
auto impedance = traceSet.begin()->second->getReferenceImpedance();
vector<double> freqs;
for(const auto t : traces) {
for(auto m : traceSet) {
const Trace *t = m.second;
if(t->size() != samples) {
qWarning() << "Selected traces do not have the same size";
return ret;
@ -939,10 +935,11 @@ std::vector<VirtualDevice::VNAMeasurement> Trace::assembleDatapoints(const Trace
// Checks passed, assemble datapoints
for(unsigned int i=0;i<samples;i++) {
VirtualDevice::VNAMeasurement d;
d.measurements["S11"] = S11.sample(i).y;
d.measurements["S12"] = S12.sample(i).y;
d.measurements["S21"] = S21.sample(i).y;
d.measurements["S22"] = S22.sample(i).y;
for(auto m : traceSet) {
QString measurement = m.first;
const Trace *t = m.second;
d.measurements[measurement] = t->sample(i).y;
}
d.pointNum = i;
d.frequency = freqs[i];
ret.push_back(d);

View File

@ -49,7 +49,7 @@ public:
void setVelocityFactor(double v);
void fillFromTouchstone(Touchstone &t, unsigned int parameter);
QString fillFromCSV(CSV &csv, unsigned int parameter); // returns the suggested trace name (not yet set in member data)
static void fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<VirtualDevice::VNAMeasurement> &data);
static void fillFromDatapoints(std::map<QString, Trace*> traceSet, const std::vector<VirtualDevice::VNAMeasurement> &data);
void fromLivedata(LivedataType type, QString param);
void fromMath();
QString name() { return _name; }
@ -137,7 +137,7 @@ public:
// Assembles datapoints as received from the VNA from four S parameter traces. Requires that all traces are in the frequency domain,
// have the same number of samples and their samples must be at the same frequencies across all traces
static std::vector<VirtualDevice::VNAMeasurement> assembleDatapoints(const Trace &S11, const Trace &S12, const Trace &S21, const Trace &S22);
static std::vector<VirtualDevice::VNAMeasurement> assembleDatapoints(std::map<QString, Trace *> traceSet);
static LivedataType TypeFromString(QString s);
static QString TypeToString(LivedataType t);

View File

@ -232,7 +232,7 @@ void TracePolarChart::draw(QPainter &p) {
}
if(dropPending) {
// TODO adjust coords due to shifted restore
// adjust coords due to shifted restore
p.setOpacity(0.5);
p.setBrush(Qt::white);
p.setPen(Qt::white);

View File

@ -328,7 +328,7 @@ void TraceSmithChart::draw(QPainter &p) {
}
}
if(dropPending) {
// TODO adjust coords due to shifted restore
// adjust coords due to shifted restore
p.setOpacity(0.5);
p.setBrush(Qt::white);
p.setPen(Qt::white);

View File

@ -30,7 +30,7 @@ void Deembedding::measurementCompleted()
measurementUI = nullptr;
}
void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
void Deembedding::startMeasurementDialog(DeembeddingOption *option)
{
measurements.clear();
measurementDialog = new QDialog;
@ -42,20 +42,7 @@ void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
});
// add the trace selector
set<unsigned int> skip;
if(!S11) {
skip.insert(0);
}
if(!S12) {
skip.insert(1);
}
if(!S21) {
skip.insert(2);
}
if(!S22) {
skip.insert(3);
}
auto traceChooser = new SparamTraceSelector(tm, 2, false, skip);
auto traceChooser = new SparamTraceSelector(tm, option->getAffectedPorts());
ui->horizontalLayout_2->insertWidget(0, traceChooser, 1);
connect(traceChooser, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);
@ -70,33 +57,8 @@ void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
// create datapoints from individual traces
measurements.clear();
auto t = traceChooser->getTraces();
auto S11 = t[0];
auto S12 = t[1];
auto S21 = t[2];
auto S22 = t[3];
for(unsigned int i=0;i<traceChooser->getPoints();i++) {
VirtualDevice::VNAMeasurement p;
p.pointNum = i;
p.Z0 = 0;
p.dBm = 0;
Sparam S;
if(S11) {
S.m11 = S11->sample(i).y;
p.frequency = S11->sample(i).x;
}
if(S12) {
S.m12 = S12->sample(i).y;
p.frequency = S11->sample(i).x;
}
if(S21) {
S.m21 = S21->sample(i).y;
p.frequency = S11->sample(i).x;
}
if(S22) {
S.m22 = S22->sample(i).y;
p.frequency = S11->sample(i).x;
}
auto points = Trace::assembleDatapoints(traceChooser->getTraces());
for(auto p : points) {
measurements.push_back(p);
}
measurementCompleted();
@ -151,15 +113,15 @@ void Deembedding::Deembed(VirtualDevice::VNAMeasurement &d)
}
}
void Deembedding::Deembed(Trace &S11, Trace &S12, Trace &S21, Trace &S22)
void Deembedding::Deembed(std::map<QString, Trace *> traceSet)
{
auto points = Trace::assembleDatapoints(S11, S12, S21, S22);
auto points = Trace::assembleDatapoints(traceSet);
if(points.size()) {
// succeeded in assembling datapoints
for(auto &p : points) {
Deembed(p);
}
Trace::fillFromDatapoints(S11, S12, S21, S22, points);
Trace::fillFromDatapoints(traceSet, points);
}
}
@ -184,9 +146,9 @@ void Deembedding::addOption(DeembeddingOption *option)
options.erase(pos);
}
});
connect(option, &DeembeddingOption::triggerMeasurement, [=](bool S11, bool S12, bool S21, bool S22) {
connect(option, &DeembeddingOption::triggerMeasurement, [=]() {
measuringOption = option;
startMeasurementDialog(S11, S12, S21, S22);
startMeasurementDialog(option);
});
emit optionAdded();
}
@ -199,6 +161,16 @@ void Deembedding::swapOptions(unsigned int index)
std::swap(options[index], options[index+1]);
}
std::set<int> Deembedding::getAffectedPorts()
{
set<int> ret;
for(auto o : options) {
auto affected = o->getAffectedPorts();
ret.insert(affected.begin(), affected.end());
}
return ret;
}
nlohmann::json Deembedding::toJSON()
{
nlohmann::json list;

View File

@ -6,6 +6,8 @@
#include "Traces/tracemodel.h"
#include <vector>
#include <map>
#include <QObject>
#include <QDialog>
#include <QComboBox>
@ -20,11 +22,14 @@ public:
~Deembedding(){}
void Deembed(VirtualDevice::VNAMeasurement &d);
void Deembed(Trace &S11, Trace &S12, Trace &S21, Trace &S22);
void Deembed(std::map<QString, Trace*> traceSet);
void removeOption(unsigned int index);
void addOption(DeembeddingOption* option);
void swapOptions(unsigned int index);
std::set<int> getAffectedPorts();
std::vector<DeembeddingOption*>& getOptions() {return options;}
nlohmann::json toJSON() override;
void fromJSON(nlohmann::json j) override;
@ -36,7 +41,7 @@ signals:
void allOptionsCleared();
private:
void measurementCompleted();
void startMeasurementDialog(bool S11, bool S12, bool S21, bool S22);
void startMeasurementDialog(DeembeddingOption *option);
std::vector<DeembeddingOption*> options;
DeembeddingOption *measuringOption;
TraceModel &tm;

View File

@ -23,6 +23,7 @@ public:
static DeembeddingOption *create(Type type);
static QString getName(Type type);
virtual std::set<int> getAffectedPorts() = 0;
virtual void transformDatapoint(VirtualDevice::VNAMeasurement &p) = 0;
virtual void edit(){}
virtual Type getType() = 0;
@ -33,7 +34,7 @@ signals:
// Deembedding option may selfdestruct if not applicable with current settings. It should emit this signal before deleting itself
void deleted(DeembeddingOption *option);
void triggerMeasurement(bool S11 = true, bool S12 = true, bool S21 = true, bool S22 = true);
void triggerMeasurement();
};
#endif // DEEMBEDDING_H

View File

@ -15,6 +15,15 @@ ImpedanceRenormalization::ImpedanceRenormalization()
}
std::set<int> ImpedanceRenormalization::getAffectedPorts()
{
set<int> ret;
for(int i=1;i<=VirtualDevice::getInfo(VirtualDevice::getConnected()).ports;i++) {
ret.insert(i);
}
return ret;
}
void ImpedanceRenormalization::transformDatapoint(VirtualDevice::VNAMeasurement &p)
{
std::map<QString, std::complex<double>> transformed;

View File

@ -13,6 +13,7 @@ class ImpedanceRenormalization : public DeembeddingOption
public:
ImpedanceRenormalization();
std::set<int> getAffectedPorts() override;
void transformDatapoint(VirtualDevice::VNAMeasurement &p) override;
Type getType() override { return Type::ImpedanceRenormalization;}
nlohmann::json toJSON() override;

View File

@ -7,13 +7,12 @@ ManualDeembeddingDialog::ManualDeembeddingDialog(const TraceModel &model, Deembe
ui(new Ui::ManualDeembeddingDialog)
{
ui->setupUi(this);
auto traceSelector = new SparamTraceSelector(model, 2);
auto traceSelector = new SparamTraceSelector(model, deemb->getAffectedPorts());
ui->verticalLayout->insertWidget(1, traceSelector, 1.0);
ui->buttonBox->setEnabled(false);
connect(traceSelector, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=]() {
auto t = traceSelector->getTraces();
deemb->Deembed(*t[0], *t[1], *t[2], *t[3]);
deemb->Deembed(traceSelector->getTraces());
accept();
});
}

View File

@ -27,6 +27,11 @@ MatchingNetwork::MatchingNetwork()
port = 1;
}
std::set<int> MatchingNetwork::getAffectedPorts()
{
return {port};
}
void MatchingNetwork::transformDatapoint(VirtualDevice::VNAMeasurement &p)
{
auto S = p.toSparam(1, 2);
@ -106,12 +111,12 @@ void MatchingNetwork::edit()
p1->setMinimumSize(portWidth, 151);
p1->setMaximumSize(portWidth, 151);
p1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
p1->setStyleSheet("image: url(:/icons/port1.png);");
p1->setStyleSheet("image: url(:/icons/port.png);");
auto DUT = new QWidget();
DUT->setMinimumSize(DUTWidth, 151);
DUT->setMaximumSize(DUTWidth, 151);
DUT->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
DUT->setStyleSheet("image: url(:/icons/DUT.png);");
DUT->setStyleSheet("image: url(:/icons/DUT_onePort.png);");
layout->addWidget(p1);
for(auto w : network) {

View File

@ -64,6 +64,7 @@ public:
// DeembeddingOption interface
public:
std::set<int> getAffectedPorts() override;
void transformDatapoint(VirtualDevice::VNAMeasurement &p) override;
void edit() override;
Type getType() override {return Type::MatchingNetwork;}

View File

@ -24,6 +24,11 @@ PortExtension::PortExtension()
kit = nullptr;
}
std::set<int> PortExtension::getAffectedPorts()
{
return {port};
}
void PortExtension::transformDatapoint(VirtualDevice::VNAMeasurement &d)
{
auto phase = -2 * M_PI * ext.delay * d.frequency;
@ -72,6 +77,8 @@ void PortExtension::edit()
ui->DCloss->setValue(ext.DCloss);
ui->Loss->setValue(ext.loss);
ui->Frequency->setValue(ext.frequency);
ui->port->setValue(port);
ui->port->setMaximum(VirtualDevice::getInfo(VirtualDevice::getConnected()).ports);
if(!kit) {
ui->calkit->setEnabled(false);
}
@ -97,6 +104,9 @@ void PortExtension::edit()
ui->Time->setValueQuiet(ui->Distance->value() / (newval * c));
updateValuesFromUI();
});
connect(ui->port, qOverload<int>(&QSpinBox::valueChanged), [=](){
port = ui->port->value();
});
connect(ui->DCloss, &SIUnitEdit::valueChanged, updateValuesFromUI);
connect(ui->Loss, &SIUnitEdit::valueChanged, updateValuesFromUI);
connect(ui->Frequency, &SIUnitEdit::valueChanged, updateValuesFromUI);

View File

@ -18,6 +18,7 @@ class PortExtension : public DeembeddingOption
Q_OBJECT
public:
PortExtension();
std::set<int> getAffectedPorts() override;
void transformDatapoint(VirtualDevice::VNAMeasurement& d) override;
void setCalkit(Calkit *kit);
Type getType() override {return Type::PortExtension;}

View File

@ -17,6 +17,11 @@ TwoThru::TwoThru()
port2 = 2;
}
std::set<int> TwoThru::getAffectedPorts()
{
return {port1, port2};
}
void TwoThru::transformDatapoint(VirtualDevice::VNAMeasurement &p)
{
// correct measurement

View File

@ -16,6 +16,7 @@ class TwoThru : public DeembeddingOption
public:
TwoThru();
std::set<int> getAffectedPorts() override;
virtual void transformDatapoint(VirtualDevice::VNAMeasurement& p) override;
virtual void edit() override;
virtual Type getType() override {return DeembeddingOption::Type::TwoThru;}

View File

@ -20,7 +20,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>You imported a two-port touchstone file, do you want to apply the currently active calibration or de-embed the data?</string>
<string>You imported a touchstone file, do you want to apply the currently active calibration or de-embed the data?</string>
</property>
<property name="wordWrap">
<bool>true</bool>

View File

@ -55,6 +55,7 @@ void TraceWidgetVNA::importDialog()
if (!filename.isEmpty()) {
try {
std::vector<Trace*> traces;
int touchstonePorts = 0;
QString prefix = QString();
if(filename.endsWith(".csv")) {
auto csv = CSV::fromFile(filename);
@ -63,6 +64,7 @@ void TraceWidgetVNA::importDialog()
// must be a touchstone file
auto t = Touchstone::fromFile(filename.toStdString());
traces = Trace::createFromTouchstone(t);
touchstonePorts = t.ports();
}
// contruct prefix from filename
prefix = filename;
@ -78,44 +80,51 @@ void TraceWidgetVNA::importDialog()
if(AppWindow::showGUI()) {
i->show();
}
if(filename.endsWith(".s2p")) {
// potential candidate to process via calibration/de-embedding
connect(i, &TraceImportDialog::importFinsished, [=](const std::vector<Trace*> &traces) {
if(traces.size() == 4) {
// all traces imported, can calculate calibration/de-embedding
bool calAvailable = cal.getNumPoints() > 0;
bool deembedAvailable = deembed.getOptions().size() > 0;
if(calAvailable || deembedAvailable) {
// check if user wants to apply either one to the imported traces
auto dialog = new QDialog();
auto ui = new Ui::s2pImportOptions;
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
ui->applyCal->setEnabled(calAvailable);
ui->deembed->setEnabled(deembedAvailable);
bool applyCal = false;
bool applyDeembed = false;
connect(ui->applyCal, &QCheckBox::toggled, [&](bool checked) {
applyCal = checked;
});
connect(ui->deembed, &QCheckBox::toggled, [&](bool checked) {
applyDeembed = checked;
});
if(AppWindow::showGUI()) {
dialog->exec();
}
if(applyCal) {
// cal.correctTraces(*traces[0], *traces[1], *traces[2], *traces[3]); // TODO
}
if(applyDeembed) {
deembed.Deembed(*traces[0], *traces[1], *traces[2], *traces[3]);
// potential candidate to process via calibration/de-embedding
connect(i, &TraceImportDialog::importFinsished, [=](const std::vector<Trace*> &traces) {
if(traces.size() == touchstonePorts*touchstonePorts) {
// all traces imported, can calculate calibration/de-embedding
bool calAvailable = cal.getNumPoints() > 0;
bool deembedAvailable = deembed.getOptions().size() > 0;
if(calAvailable || deembedAvailable) {
// check if user wants to apply either one to the imported traces
auto dialog = new QDialog();
auto ui = new Ui::s2pImportOptions;
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
ui->applyCal->setEnabled(calAvailable);
ui->deembed->setEnabled(deembedAvailable);
bool applyCal = false;
bool applyDeembed = false;
connect(ui->applyCal, &QCheckBox::toggled, [&](bool checked) {
applyCal = checked;
});
connect(ui->deembed, &QCheckBox::toggled, [&](bool checked) {
applyDeembed = checked;
});
if(AppWindow::showGUI()) {
dialog->exec();
}
// assemble trace set
std::map<QString, Trace*> set;
for(int i=1;i<=touchstonePorts;i++) {
for(int j=1;j<=touchstonePorts;j++) {
QString name = "S"+QString::number(i)+QString::number(j);
int index = (i-1)*touchstonePorts+(j-1);
set[name] = traces[index];
}
}
if(applyCal) {
cal.correctTraces(set);
}
if(applyDeembed) {
deembed.Deembed(set);
}
}
});
}
}
});
} catch(const std::exception& e) {
InformationBox::ShowError("Failed to import file", QString("Attempt to import file ended with error: \"") + e.what()+"\"");
}

View File

@ -573,8 +573,23 @@ void AppWindow::SetupSCPI()
if(!vdevice) {
return QString("0/0/0");
} else if(vdevice->isCompoundDevice()) {
// TODO
return QString();
// show highest temperature of all devices
int maxTempSource = 0;
int maxTempLO = 0;
int maxTempMCU = 0;
for(auto dev : vdevice->getDevices()) {
auto status = dev->StatusV1();
if(status.temp_source > maxTempSource) {
maxTempSource = status.temp_source;
}
if(status.temp_LO1 > maxTempLO) {
maxTempLO = status.temp_LO1;
}
if(status.temp_MCU > maxTempMCU) {
maxTempMCU = status.temp_MCU;
}
}
return QString::number(maxTempSource)+"/"+QString::number(maxTempLO)+"/"+QString::number(maxTempMCU);
} else {
auto dev = vdevice->getDevice();
return QString::number(dev->StatusV1().temp_source)+"/"+QString::number(dev->StatusV1().temp_LO1)+"/"+QString::number(dev->StatusV1().temp_MCU);

View File

@ -67,5 +67,7 @@
<file>icons/compound_V1_Ref_Middle.png</file>
<file>icons/compound_V1_Ref_Right.png</file>
<file>icons/compound_V1_USB.png</file>
<file>icons/DUT_onePort.png</file>
<file>icons/port.png</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB