2022-10-01 23:10:44 +08:00
|
|
|
#include "deembedding.h"
|
|
|
|
|
|
|
|
#include "deembeddingdialog.h"
|
|
|
|
#include "ui_measurementdialog.h"
|
|
|
|
#include "Traces/sparamtraceselector.h"
|
|
|
|
#include "appwindow.h"
|
|
|
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
void Deembedding::configure()
|
|
|
|
{
|
|
|
|
auto d = new DeembeddingDialog(this);
|
|
|
|
if(AppWindow::showGUI()) {
|
|
|
|
d->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::measurementCompleted()
|
|
|
|
{
|
|
|
|
// pass on the measurement result to the option that triggered the measurement
|
|
|
|
if (measuringOption) {
|
|
|
|
measuringOption->measurementCompleted(measurements);
|
|
|
|
measuringOption = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete measurementDialog;
|
|
|
|
measurementDialog = nullptr;
|
|
|
|
measurementUI = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::startMeasurementDialog(DeembeddingOption *option)
|
|
|
|
{
|
|
|
|
measurements.clear();
|
|
|
|
measurementDialog = new QDialog;
|
|
|
|
auto ui = new Ui_DeembeddingMeasurementDialog;
|
|
|
|
measurementUI = ui;
|
|
|
|
ui->setupUi(measurementDialog);
|
|
|
|
connect(measurementDialog, &QDialog::finished, [=](){
|
|
|
|
delete ui;
|
|
|
|
});
|
|
|
|
|
|
|
|
// add the trace selector
|
|
|
|
auto traceChooser = new SparamTraceSelector(tm, option->getAffectedPorts());
|
|
|
|
ui->horizontalLayout_2->insertWidget(0, traceChooser, 1);
|
|
|
|
|
|
|
|
connect(traceChooser, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);
|
|
|
|
|
|
|
|
connect(ui->bMeasure, &QPushButton::clicked, [=](){
|
|
|
|
ui->bMeasure->setEnabled(false);
|
|
|
|
traceChooser->setEnabled(false);
|
|
|
|
ui->buttonBox->setEnabled(false);
|
|
|
|
measuring = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
|
|
|
|
// create datapoints from individual traces
|
|
|
|
measurements.clear();
|
|
|
|
auto points = Trace::assembleDatapoints(traceChooser->getTraces());
|
|
|
|
for(auto p : points) {
|
|
|
|
measurements.push_back(p);
|
|
|
|
}
|
|
|
|
measurementCompleted();
|
|
|
|
});
|
|
|
|
|
|
|
|
if(AppWindow::showGUI()) {
|
|
|
|
measurementDialog->show();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-13 00:39:17 +08:00
|
|
|
void Deembedding::updateSCPINames()
|
|
|
|
{
|
|
|
|
// Need to remove all options from the subnode list first, otherwise
|
|
|
|
// name changes wouldn't work due to temporarily name collisions
|
|
|
|
for(auto &option : options) {
|
|
|
|
remove(option);
|
|
|
|
}
|
|
|
|
unsigned int i=1;
|
|
|
|
for(auto &option : options) {
|
|
|
|
option->changeName(QString::number(i));
|
|
|
|
add(option);
|
2022-12-13 05:42:33 +08:00
|
|
|
i++;
|
2022-12-13 00:39:17 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-01 23:10:44 +08:00
|
|
|
Deembedding::Deembedding(TraceModel &tm)
|
2022-12-13 00:39:17 +08:00
|
|
|
: SCPINode("DEEMBedding"),
|
|
|
|
measuringOption(nullptr),
|
2022-10-24 06:08:10 +08:00
|
|
|
tm(tm),
|
2022-10-01 23:10:44 +08:00
|
|
|
measuring(false),
|
2022-10-24 06:08:10 +08:00
|
|
|
measurementDialog(nullptr),
|
|
|
|
measurementUI(nullptr),
|
2022-10-01 23:10:44 +08:00
|
|
|
sweepPoints(0)
|
|
|
|
{
|
2022-12-13 00:39:17 +08:00
|
|
|
add(new SCPICommand("NUMber", nullptr, [=](QStringList params) -> QString {
|
|
|
|
Q_UNUSED(params);
|
|
|
|
return QString::number(options.size());
|
|
|
|
}));
|
|
|
|
add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString {
|
|
|
|
unsigned long long index;
|
|
|
|
if(!SCPI::paramToULongLong(params, 0, index)) {
|
|
|
|
return SCPI::getResultName(SCPI::Result::Error);
|
|
|
|
}
|
|
|
|
if(index < 1 || index > options.size()) {
|
|
|
|
return SCPI::getResultName(SCPI::Result::Error);
|
|
|
|
}
|
2022-12-13 05:42:33 +08:00
|
|
|
return DeembeddingOption::TypeToString(options[index-1]->getType()).replace(" ", "_");
|
2022-12-13 00:39:17 +08:00
|
|
|
}));
|
|
|
|
add(new SCPICommand("NEW", [=](QStringList params) -> QString {
|
|
|
|
if(params.size() < 1) {
|
|
|
|
return SCPI::getResultName(SCPI::Result::Error);
|
|
|
|
}
|
2022-12-13 05:42:33 +08:00
|
|
|
auto type = DeembeddingOption::TypeFromString(params[0].replace("_", " "));
|
2022-12-13 00:39:17 +08:00
|
|
|
if(type == DeembeddingOption::Type::Last) {
|
|
|
|
return SCPI::getResultName(SCPI::Result::Error);
|
|
|
|
}
|
|
|
|
auto option = DeembeddingOption::create(type);
|
|
|
|
addOption(option);
|
|
|
|
return SCPI::getResultName(SCPI::Result::Empty);
|
|
|
|
}, nullptr));
|
2022-12-13 05:42:33 +08:00
|
|
|
add(new SCPICommand("CLEAR", [=](QStringList params) -> QString {
|
|
|
|
Q_UNUSED(params);
|
|
|
|
clear();
|
|
|
|
return SCPI::getResultName(SCPI::Result::Empty);
|
|
|
|
}, nullptr));
|
2022-10-01 23:10:44 +08:00
|
|
|
}
|
|
|
|
|
2023-01-16 07:25:29 +08:00
|
|
|
void Deembedding::Deembed(DeviceDriver::VNAMeasurement &d)
|
2022-10-01 23:10:44 +08:00
|
|
|
{
|
|
|
|
// figure out the point in one sweep based on the incomig pointNums
|
|
|
|
static unsigned lastPointNum;
|
|
|
|
if (d.pointNum == 0) {
|
|
|
|
sweepPoints = lastPointNum;
|
|
|
|
} else if(d.pointNum > sweepPoints) {
|
|
|
|
sweepPoints = d.pointNum;
|
|
|
|
}
|
|
|
|
lastPointNum = d.pointNum;
|
|
|
|
|
|
|
|
for(auto it = options.begin();it != options.end();it++) {
|
|
|
|
if (measuring && measuringOption == *it) {
|
|
|
|
// this option needs a measurement
|
|
|
|
if (d.pointNum == 0) {
|
|
|
|
if(measurements.size() == 0) {
|
|
|
|
// this is the first point of the measurement
|
|
|
|
measurements.push_back(d);
|
|
|
|
} else {
|
|
|
|
// this is the first point of the next sweep, measurement complete
|
|
|
|
measuring = false;
|
|
|
|
measurementCompleted();
|
|
|
|
}
|
|
|
|
} else if(measurements.size() > 0) {
|
|
|
|
// in the middle of the measurement, add point
|
|
|
|
measurements.push_back(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(measurementUI) {
|
|
|
|
measurementUI->progress->setValue(100 * measurements.size() / sweepPoints);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(*it)->transformDatapoint(d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::Deembed(std::map<QString, Trace *> traceSet)
|
|
|
|
{
|
|
|
|
auto points = Trace::assembleDatapoints(traceSet);
|
|
|
|
if(points.size()) {
|
|
|
|
// succeeded in assembling datapoints
|
|
|
|
for(auto &p : points) {
|
|
|
|
Deembed(p);
|
|
|
|
}
|
2022-12-12 03:37:29 +08:00
|
|
|
Trace::fillFromDatapoints(traceSet, points, true);
|
|
|
|
for(auto t : traceSet) {
|
|
|
|
t.second->setDeembeddingActive(true);
|
|
|
|
}
|
2022-10-01 23:10:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::removeOption(unsigned int index)
|
|
|
|
{
|
|
|
|
if(index < options.size()) {
|
|
|
|
delete options[index];
|
|
|
|
options.erase(options.begin() + index);
|
|
|
|
}
|
2022-12-13 00:39:17 +08:00
|
|
|
updateSCPINames();
|
2022-10-01 23:10:44 +08:00
|
|
|
if(options.size() == 0) {
|
|
|
|
emit allOptionsCleared();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::addOption(DeembeddingOption *option)
|
|
|
|
{
|
|
|
|
options.push_back(option);
|
|
|
|
connect(option, &DeembeddingOption::deleted, [=](DeembeddingOption *o){
|
|
|
|
// find deleted option and remove from list
|
|
|
|
auto pos = find(options.begin(), options.end(), o);
|
|
|
|
if(pos != options.end()) {
|
|
|
|
options.erase(pos);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
connect(option, &DeembeddingOption::triggerMeasurement, [=]() {
|
|
|
|
measuringOption = option;
|
|
|
|
startMeasurementDialog(option);
|
|
|
|
});
|
2022-12-13 00:39:17 +08:00
|
|
|
updateSCPINames();
|
2022-10-01 23:10:44 +08:00
|
|
|
emit optionAdded();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::swapOptions(unsigned int index)
|
|
|
|
{
|
|
|
|
if(index + 1 >= options.size()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::swap(options[index], options[index+1]);
|
2022-12-13 00:39:17 +08:00
|
|
|
updateSCPINames();
|
2022-10-01 23:10:44 +08:00
|
|
|
}
|
|
|
|
|
2022-12-13 05:42:33 +08:00
|
|
|
void Deembedding::clear()
|
|
|
|
{
|
|
|
|
while(options.size() > 0) {
|
|
|
|
removeOption(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 06:27:22 +08:00
|
|
|
std::set<unsigned int> Deembedding::getAffectedPorts()
|
2022-10-01 23:10:44 +08:00
|
|
|
{
|
2022-10-14 06:27:22 +08:00
|
|
|
set<unsigned int> ret;
|
2022-10-01 23:10:44 +08:00
|
|
|
for(auto o : options) {
|
|
|
|
auto affected = o->getAffectedPorts();
|
|
|
|
ret.insert(affected.begin(), affected.end());
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
nlohmann::json Deembedding::toJSON()
|
|
|
|
{
|
|
|
|
nlohmann::json list;
|
|
|
|
for(auto m : options) {
|
|
|
|
nlohmann::json jm;
|
2022-12-13 00:39:17 +08:00
|
|
|
jm["operation"] = DeembeddingOption::TypeToString(m->getType()).toStdString();
|
2022-10-01 23:10:44 +08:00
|
|
|
jm["settings"] = m->toJSON();
|
|
|
|
list.push_back(jm);
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Deembedding::fromJSON(nlohmann::json j)
|
|
|
|
{
|
|
|
|
// clear all options
|
|
|
|
while(options.size() > 0) {
|
|
|
|
removeOption(0);
|
|
|
|
}
|
|
|
|
for(auto jm : j) {
|
|
|
|
QString operation = QString::fromStdString(jm.value("operation", ""));
|
|
|
|
if(operation.isEmpty()) {
|
|
|
|
qWarning() << "Skipping empty de-embedding operation";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// attempt to find the type of operation
|
|
|
|
DeembeddingOption::Type type = DeembeddingOption::Type::Last;
|
|
|
|
for(unsigned int i=0;i<(int) DeembeddingOption::Type::Last;i++) {
|
2022-12-13 00:39:17 +08:00
|
|
|
if(DeembeddingOption::TypeToString((DeembeddingOption::Type) i) == operation) {
|
2022-10-01 23:10:44 +08:00
|
|
|
// found the correct operation
|
|
|
|
type = (DeembeddingOption::Type) i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(type == DeembeddingOption::Type::Last) {
|
|
|
|
// unable to find this operation
|
|
|
|
qWarning() << "Unable to create de-embedding operation:" << operation;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
qDebug() << "Creating math operation of type:" << operation;
|
|
|
|
auto op = DeembeddingOption::create(type);
|
|
|
|
if(jm.contains("settings")) {
|
|
|
|
op->fromJSON(jm["settings"]);
|
|
|
|
}
|
|
|
|
addOption(op);
|
|
|
|
}
|
|
|
|
}
|