WIP: SCPI commands for de-embedding

This commit is contained in:
Jan Käberich 2022-12-12 17:39:17 +01:00
parent fa8f49086c
commit d674d05083
15 changed files with 227 additions and 48 deletions

View File

@ -493,6 +493,16 @@ $$ S_{11}...S_{1n},S_{21}...S_{2n},...,S_{n1}...S_{nn} $$
\subsubsection{VNA:TRACe:PAUSED}
\query{Queries whether a trace is paused}{VNA:TRACe:PAUSED?}{<trace>, either by name or by index}{TRUE or FALSE}
\subsubsection{VNA:TRACe:DEEMBedding:ACTive}
\event{Enables/disables de-embedding on a trace}{VNA:TRACe:DEEMBedding:ACTive}{<trace>, either by name or by index\\<enable>, either TRUE or FALSE}
If no de-embedding is configured for the selected trace, enabling the de-embedding will fail.
\query{Queries whether de-embedding is active for the selected trace}{VNA:TRACe:DEEMBedding:ACTive?}{<trace>, either by name or by index}{TRUE or FALSE}
\subsubsection{VNA:TRACe:DEEMBedding:AVAILable}
\query{Queries whether de-embedding is available for the selected trace}{VNA:TRACe:DEEMBedding:AVAILable?}{<trace>, either by name or by index}{TRUE or FALSE}
\subsubsection{VNA:TRACe:PARAMeter}
\event{Sets the measurement parameter that is stored in the trace}{VNA:TRACe:PARAMeter}{<trace>, either by name or by index\\<parameter>, options are S11, S12, S21 or S22}
\query{Queries the measurement parameter of a trace}{VNA:TRACe:PARAMeter?}{<trace>, either by name or by index}{S11, S12, S21 or S22}

View File

@ -208,7 +208,7 @@ void TraceWidget::SetupSCPI()
add(new SCPICommand("DATA", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
QString ret;
if(t->size() > 0) {
@ -233,11 +233,11 @@ void TraceWidget::SetupSCPI()
add(new SCPICommand("AT", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
double x;
if(!SCPI::paramToDouble(params, 1, x)) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
} else {
auto d = t->interpolatedSample(x);
if(std::isnan(d.x)) {
@ -250,21 +250,21 @@ void TraceWidget::SetupSCPI()
add(new SCPICommand("TOUCHSTONE", nullptr, [=](QStringList params) -> QString {
if(params.size() < 1) {
// no traces given
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
// check number of paramaters, must be a square number
int numTraces = params.size();
int ports = round(sqrt(numTraces));
if(ports * ports != numTraces) {
// invalid number of traces
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
Trace* traces[numTraces];
for(int i=0;i<numTraces;i++) {
traces[i] = findTraceFromName(params[i]);
if(!traces[i]) {
// couldn't find that trace
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
}
// check if trace selection is valid
@ -277,15 +277,15 @@ void TraceWidget::SetupSCPI()
auto t = traces[j+i*ports];
if(t->getDataType() != Trace::DataType::Frequency) {
// invalid domain
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
if(t->isReflection() != need_reflection) {
// invalid measurement at this position
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
if((t->size() != npoints) || (t->minX() != f_start) || (t->maxX() != f_stop)) {
// frequency points are not identical
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
}
}
@ -307,21 +307,21 @@ void TraceWidget::SetupSCPI()
add(new SCPICommand("MAXFrequency", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
return QString::number(t->maxX(), 'f', 0);
}));
add(new SCPICommand("MINFrequency", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
return QString::number(t->minX(), 'f', 0);
}));
add(new SCPICommand("MAXAmplitude", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
auto d = t->interpolatedSample(t->findExtremum(true));
return QString::number(d.x, 'f', 0)+","+createStringFromData(t, d);
@ -329,90 +329,125 @@ void TraceWidget::SetupSCPI()
add(new SCPICommand("MINAmplitude", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
auto d = t->interpolatedSample(t->findExtremum(false));
return QString::number(d.x, 'f', 0)+","+createStringFromData(t, d);
}));
add(new SCPICommand("NEW", [=](QStringList params) -> QString {
if(params.size() != 1) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
createCount++;
auto t = new Trace(params[0], Qt::darkYellow, defaultParameter());
t->setColor(QColor::fromHsl((createCount * 50) % 360, 250, 128));
model.addTrace(t);
return "";
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("RENAME", [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
if(params.size() != 2) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
t->setName(params[1]);
return "";
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("PAUSE", [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
t->pause();
return "";
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("RESUME", [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) { return "ERROR";
if(!t) {
return SCPI::getResultName(SCPI::Result::Error);
}
t->resume();
return "";
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("PAUSED", nullptr, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
return t->isPaused() ? "TRUE" : "FALSE";
return t->isPaused() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
auto deembed = new SCPINode("DEEMBedding");
deembed->add(new SCPICommand("ACTive", [=](QStringList params) -> QString {
Trace* t = findTrace(params);
if(!t) {
return SCPI::getResultName(SCPI::Result::Error);
}
bool activate = false;
if(!SCPI::paramToBool(params, 1, activate)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(activate) {
if(!t->deembeddingAvailable()) {
return SCPI::getResultName(SCPI::Result::Error);
}
t->setDeembeddingActive(true);
} else {
t->setDeembeddingActive(false);
}
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList params) -> QString {
Trace* t = findTrace(params);
if(!t) {
return SCPI::getResultName(SCPI::Result::Error);
}
return t->isDeembeddingActive() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
deembed->add(new SCPICommand("AVAILable", nullptr, [=](QStringList params) -> QString {
Trace* t = findTrace(params);
if(!t) {
return SCPI::getResultName(SCPI::Result::Error);
}
return t->deembeddingAvailable() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
add(deembed);
add(new SCPICommand("PARAMeter", [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t || params.size() < 2) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
auto newparam = params[1];
if((Trace::isVNAParameter(t->liveParameter()) && Trace::isVNAParameter(newparam))
|| (Trace::isSAParameter(t->liveParameter()) && Trace::isSAParameter(newparam))) {
t->fromLivedata(t->liveType(), newparam);
return "";
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
}, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
return t->liveParameter();
}));
add(new SCPICommand("TYPE", [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t || params.size() < 2) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
auto newtype = Trace::TypeFromString(params[1]);
if(newtype != Trace::LivedataType::Invalid) {
t->fromLivedata(newtype, t->liveParameter());
return "";
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
}, [=](QStringList params) -> QString {
auto t = findTrace(params);
if(!t) {
return "ERROR";
return SCPI::getResultName(SCPI::Result::Error);
}
return Trace::TypeToString(t->liveType());
}));

View File

@ -69,15 +69,55 @@ void Deembedding::startMeasurementDialog(DeembeddingOption *option)
}
}
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);
}
}
Deembedding::Deembedding(TraceModel &tm)
: measuringOption(nullptr),
: SCPINode("DEEMBedding"),
measuringOption(nullptr),
tm(tm),
measuring(false),
measurementDialog(nullptr),
measurementUI(nullptr),
sweepPoints(0)
{
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);
}
return DeembeddingOption::TypeToString(options[index]->getType());
}));
add(new SCPICommand("NEW", [=](QStringList params) -> QString {
if(params.size() < 1) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto type = DeembeddingOption::TypeFromString(params[0]);
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));
}
void Deembedding::Deembed(VirtualDevice::VNAMeasurement &d)
@ -137,6 +177,7 @@ void Deembedding::removeOption(unsigned int index)
delete options[index];
options.erase(options.begin() + index);
}
updateSCPINames();
if(options.size() == 0) {
emit allOptionsCleared();
}
@ -156,6 +197,7 @@ void Deembedding::addOption(DeembeddingOption *option)
measuringOption = option;
startMeasurementDialog(option);
});
updateSCPINames();
emit optionAdded();
}
@ -165,6 +207,7 @@ void Deembedding::swapOptions(unsigned int index)
return;
}
std::swap(options[index], options[index+1]);
updateSCPINames();
}
std::set<unsigned int> Deembedding::getAffectedPorts()
@ -182,7 +225,7 @@ nlohmann::json Deembedding::toJSON()
nlohmann::json list;
for(auto m : options) {
nlohmann::json jm;
jm["operation"] = DeembeddingOption::getName(m->getType()).toStdString();
jm["operation"] = DeembeddingOption::TypeToString(m->getType()).toStdString();
jm["settings"] = m->toJSON();
list.push_back(jm);
}
@ -204,7 +247,7 @@ void Deembedding::fromJSON(nlohmann::json j)
// attempt to find the type of operation
DeembeddingOption::Type type = DeembeddingOption::Type::Last;
for(unsigned int i=0;i<(int) DeembeddingOption::Type::Last;i++) {
if(DeembeddingOption::getName((DeembeddingOption::Type) i) == operation) {
if(DeembeddingOption::TypeToString((DeembeddingOption::Type) i) == operation) {
// found the correct operation
type = (DeembeddingOption::Type) i;
break;

View File

@ -4,6 +4,7 @@
#include "deembeddingoption.h"
#include "savable.h"
#include "Traces/tracemodel.h"
#include "scpi.h"
#include <vector>
#include <map>
@ -14,7 +15,7 @@
class Ui_DeembeddingMeasurementDialog;
class Deembedding : public QObject, public Savable
class Deembedding : public QObject, public Savable, public SCPINode
{
Q_OBJECT
public:
@ -42,6 +43,7 @@ signals:
private:
void measurementCompleted();
void startMeasurementDialog(DeembeddingOption *option);
void updateSCPINames();
std::vector<DeembeddingOption*> options;
DeembeddingOption *measuringOption;
TraceModel &tm;

View File

@ -15,7 +15,7 @@ DeembeddingDialog::DeembeddingDialog(Deembedding *d, QWidget *parent) :
auto addMenu = new QMenu();
for(unsigned int i=0;i<(unsigned int)DeembeddingOption::Type::Last;i++) {
auto type = (DeembeddingOption::Type) i;
auto action = new QAction(DeembeddingOption::getName(type));
auto action = new QAction(DeembeddingOption::TypeToString(type));
connect(action, &QAction::triggered, [=](){
auto option = DeembeddingOption::create(type);
model.addOption(option);
@ -86,7 +86,7 @@ QVariant OptionModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && role == Qt::DisplayRole) {
auto type = d->getOptions()[index.row()]->getType();
return DeembeddingOption::getName(type);
return DeembeddingOption::TypeToString(type);
} else {
return QVariant();
}

View File

@ -21,7 +21,7 @@ DeembeddingOption *DeembeddingOption::create(DeembeddingOption::Type type)
}
}
QString DeembeddingOption::getName(DeembeddingOption::Type type)
QString DeembeddingOption::TypeToString(DeembeddingOption::Type type)
{
switch(type) {
case Type::PortExtension:
@ -36,3 +36,13 @@ QString DeembeddingOption::getName(DeembeddingOption::Type type)
return "";
}
}
DeembeddingOption::Type DeembeddingOption::TypeFromString(QString string)
{
for(unsigned int i=0;i<(int) Type::Last;i++) {
if(TypeToString((Type) i) == string) {
return (Type) i;
}
}
return Type::Last;
}

View File

@ -4,10 +4,11 @@
#include "savable.h"
#include "Device/device.h"
#include "Traces/tracemodel.h"
#include "scpi.h"
#include <QWidget>
class DeembeddingOption : public QObject, public Savable
class DeembeddingOption : public QObject, public Savable, public SCPINode
{
Q_OBJECT
public:
@ -21,7 +22,8 @@ public:
};
static DeembeddingOption *create(Type type);
static QString getName(Type type);
static QString TypeToString(Type type);
static Type TypeFromString(QString string);
virtual std::set<unsigned int> getAffectedPorts() = 0;
virtual void transformDatapoint(VirtualDevice::VNAMeasurement &p) = 0;
@ -35,6 +37,10 @@ signals:
void deleted(DeembeddingOption *option);
void triggerMeasurement();
protected:
DeembeddingOption(QString SCPIname)
: SCPINode(SCPIname){}
};
#endif // DEEMBEDDING_H

View File

@ -9,10 +9,20 @@
using namespace std;
ImpedanceRenormalization::ImpedanceRenormalization()
: DeembeddingOption(),
: DeembeddingOption("ZNORMalization"),
impedance(50.0)
{
add(new SCPICommand("IMPedance", [=](QStringList params) -> QString {
double new_value;
if(!SCPI::paramToDouble(params, 0, new_value)) {
return SCPI::getResultName(SCPI::Result::Error);
}
impedance = new_value;
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList params) -> QString {
Q_UNUSED(params);
return QString::number(impedance);
}));
}
std::set<unsigned int> ImpedanceRenormalization::getAffectedPorts()

View File

@ -20,6 +20,7 @@
using namespace std;
MatchingNetwork::MatchingNetwork()
: DeembeddingOption("MATCHing")
{
dropPending = false;
dragComponent = nullptr;
@ -268,7 +269,7 @@ void MatchingNetwork::addComponent(int index, MatchingComponent *c)
matching.clear();
// remove from list when the component deletes itself
connect(c, &MatchingComponent::deleted, [=](){
network.erase(remove(network.begin(), network.end(), c), network.end());
network.erase(std::remove(network.begin(), network.end(), c), network.end());
matching.clear();
});
}
@ -325,7 +326,7 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
// remove and hide component while it is being dragged
graph->layout()->removeWidget(dragComponent);
dragComponent->hide();
network.erase(remove(network.begin(), network.end(), dragComponent), network.end());
network.erase(std::remove(network.begin(), network.end(), dragComponent), network.end());
graph->update();
// network changed, need to recalculate matching

View File

@ -11,7 +11,7 @@
using namespace std;
PortExtension::PortExtension()
: DeembeddingOption()
: DeembeddingOption("PORTEXTension")
{
ext.frequency = 0;
ext.loss = 0;
@ -25,6 +25,12 @@ PortExtension::PortExtension()
kit = nullptr;
ui = nullptr;
addUnsignedIntParameter("PORT", port);
addDoubleParameter("DELAY", ext.delay);
addDoubleParameter("DCLOSS", ext.DCloss);
addDoubleParameter("LOSS", ext.loss);
addDoubleParameter("FREQuency", ext.frequency);
}
std::set<unsigned int> PortExtension::getAffectedPorts()

View File

@ -11,6 +11,7 @@
using namespace std;
TwoThru::TwoThru()
: DeembeddingOption("TWOTHRU")
{
Z0 = 50.0;
port1 = 1;

View File

@ -1411,6 +1411,8 @@ void VNA::SetupSCPI()
cal.add(new SCPICommand("BUSy", nullptr, [=](QStringList) -> QString {
return CalibrationMeasurementActive() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
SCPINode::add(&deembedding);
}
void VNA::ConstrainAndUpdateFrequencies()

View File

@ -160,6 +160,54 @@ bool SCPINode::add(SCPICommand *cmd)
return true;
}
bool SCPINode::addDoubleParameter(QString name, double &param, bool gettable, bool settable)
{
auto cmd = settable ? [&](QStringList params) -> QString {
if(SCPI::paramToDouble(params, 0, param)) {
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return SCPI::getResultName(SCPI::Result::Error);
}
} : (std::function<QString(QStringList)>) nullptr;
auto query = settable ? [=](QStringList params) -> QString {
Q_UNUSED(params)
return QString::number(param);
} : (std::function<QString(QStringList)>) nullptr;
return add(new SCPICommand(name, cmd, query));
}
bool SCPINode::addUnsignedIntParameter(QString name, unsigned int &param, bool gettable, bool settable)
{
auto cmd = settable ? [&](QStringList params) -> QString {
unsigned long long value;
if(SCPI::paramToULongLong(params, 0, value)) {
param = value;
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return SCPI::getResultName(SCPI::Result::Error);
}
} : (std::function<QString(QStringList)>) nullptr;
auto query = settable ? [=](QStringList params) -> QString {
Q_UNUSED(params)
return QString::number(param);
} : (std::function<QString(QStringList)>) nullptr;
return add(new SCPICommand(name, cmd, query));
}
bool SCPINode::changeName(QString newname)
{
if(newname == name) {
return true;
}
if(parent) {
if(parent->nameCollision(newname)) {
// new name would result in a collision
return false;
}
}
name = newname;
}
bool SCPINode::nameCollision(QString name)
{
for(auto n : subnodes) {

View File

@ -35,11 +35,16 @@ public:
bool remove(SCPINode *node);
bool add(SCPICommand *cmd);
bool addDoubleParameter(QString name, double &param, bool gettable = true, bool settable = true);
bool addUnsignedIntParameter(QString name, unsigned int &param, bool gettable = true, bool settable = true);
bool changeName(QString newname);
private:
QString parse(QString cmd, SCPINode* &lastNode);
bool nameCollision(QString name);
void createCommandList(QString prefix, QString &list);
const QString name;
QString name;
std::vector<SCPINode*> subnodes;
std::vector<SCPICommand*> commands;
SCPINode *parent;