diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.cpp index d3133db..0e00eb0 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.cpp @@ -80,6 +80,7 @@ void Deembedding::updateSCPINames() for(auto &option : options) { option->changeName(QString::number(i)); add(option); + i++; } } @@ -104,13 +105,13 @@ Deembedding::Deembedding(TraceModel &tm) if(index < 1 || index > options.size()) { return SCPI::getResultName(SCPI::Result::Error); } - return DeembeddingOption::TypeToString(options[index]->getType()); + return DeembeddingOption::TypeToString(options[index-1]->getType()).replace(" ", "_"); })); add(new SCPICommand("NEW", [=](QStringList params) -> QString { if(params.size() < 1) { return SCPI::getResultName(SCPI::Result::Error); } - auto type = DeembeddingOption::TypeFromString(params[0]); + auto type = DeembeddingOption::TypeFromString(params[0].replace("_", " ")); if(type == DeembeddingOption::Type::Last) { return SCPI::getResultName(SCPI::Result::Error); } @@ -118,6 +119,11 @@ Deembedding::Deembedding(TraceModel &tm) addOption(option); return SCPI::getResultName(SCPI::Result::Empty); }, nullptr)); + add(new SCPICommand("CLEAR", [=](QStringList params) -> QString { + Q_UNUSED(params); + clear(); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); } void Deembedding::Deembed(VirtualDevice::VNAMeasurement &d) @@ -210,6 +216,13 @@ void Deembedding::swapOptions(unsigned int index) updateSCPINames(); } +void Deembedding::clear() +{ + while(options.size() > 0) { + removeOption(0); + } +} + std::set Deembedding::getAffectedPorts() { set ret; diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.h b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.h index 4d1ab0c..405e6c3 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.h +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembedding.h @@ -28,6 +28,7 @@ public: void removeOption(unsigned int index); void addOption(DeembeddingOption* option); void swapOptions(unsigned int index); + void clear(); std::set getAffectedPorts(); diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembeddingoption.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembeddingoption.cpp index ade07fe..45ecbde 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembeddingoption.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/deembeddingoption.cpp @@ -40,7 +40,7 @@ QString DeembeddingOption::TypeToString(DeembeddingOption::Type type) DeembeddingOption::Type DeembeddingOption::TypeFromString(QString string) { for(unsigned int i=0;i<(int) Type::Last;i++) { - if(TypeToString((Type) i) == string) { + if(TypeToString((Type) i).compare(string, Qt::CaseInsensitive) == 0) { return (Type) i; } } diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.cpp index f996d9a..9fe98ac 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.cpp @@ -27,6 +27,50 @@ MatchingNetwork::MatchingNetwork() dropComponent = nullptr; addNetwork = true; port = 1; + + graph = nullptr; + insertIndicator = nullptr; + + addUnsignedIntParameter("PORT", port); + addBoolParameter("ADD", addNetwork); + add(new SCPICommand("CLEAR", [=](QStringList params) -> QString { + Q_UNUSED(params); + clearNetwork(); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); + add(new SCPICommand("NUMber", nullptr, [=](QStringList params) -> QString { + Q_UNUSED(params); + return QString::number(network.size()); + })); + add(new SCPICommand("NEW", [=](QStringList params) -> QString { + if(params.size() < 1) { + return SCPI::getResultName(SCPI::Result::Error); + } + auto c = MatchingComponent::createFromName(params[0].replace("_", " ")); + if(!c) { + return SCPI::getResultName(SCPI::Result::Error); + } + unsigned long long index = network.size(); + // parse index (unchanged if not provided) + SCPI::paramToULongLong(params, 1, index); + addComponent(index, c); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); + add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString { + unsigned long long index = 0; + if(!SCPI::paramToULongLong(params, 0, index)) { + return SCPI::getResultName(SCPI::Result::Error); + } + if(index < 1 || index > network.size()) { + return SCPI::getResultName(SCPI::Result::Error); + } + return network[index-1]->getName().replace(" ", "_"); + })); +} + +MatchingNetwork::~MatchingNetwork() +{ + clearNetwork(); } std::set MatchingNetwork::getAffectedPorts() @@ -179,7 +223,10 @@ void MatchingNetwork::edit() connect(ui->port, qOverload(&QSpinBox::valueChanged), [=](){ port = ui->port->value(); }); - connect(ui->buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept); + connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){ + graph = nullptr; + dialog->accept(); + }); } nlohmann::json MatchingNetwork::toJSON() @@ -200,7 +247,7 @@ nlohmann::json MatchingNetwork::toJSON() void MatchingNetwork::fromJSON(nlohmann::json j) { - network.clear(); + clearNetwork(); port = j.value("port", 1); if(j.contains("network")) { for(auto jc : j["network"]) { @@ -219,6 +266,15 @@ void MatchingNetwork::fromJSON(nlohmann::json j) matching.clear(); } +void MatchingNetwork::clearNetwork() +{ + while(network.size() > 0) { + auto c = network[0]; + removeComponent(c); + delete c; + } +} + MatchingComponent *MatchingNetwork::componentAtPosition(int pos) { pos -= graph->layout()->itemAt(0)->geometry().width(); @@ -266,14 +322,37 @@ void MatchingNetwork::addComponentAtPosition(int pos, MatchingComponent *c) void MatchingNetwork::addComponent(int index, MatchingComponent *c) { network.insert(network.begin() + index, c); + updateSCPINames(); + if(graph) { + graph->update(); + } matching.clear(); // remove from list when the component deletes itself connect(c, &MatchingComponent::deleted, [=](){ - network.erase(std::remove(network.begin(), network.end(), c), network.end()); - matching.clear(); + removeComponent(c); }); } +void MatchingNetwork::removeComponent(int index) +{ + network.erase(network.begin() + index); + matching.clear(); + updateSCPINames(); + if(graph) { + graph->update(); + } +} + +void MatchingNetwork::removeComponent(MatchingComponent *c) +{ + network.erase(std::remove(network.begin(), network.end(), c), network.end()); + matching.clear(); + updateSCPINames(); + if(graph) { + graph->update(); + } +} + void MatchingNetwork::createDragComponent(MatchingComponent *c) { QDrag *drag = new QDrag(this); @@ -326,7 +405,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(std::remove(network.begin(), network.end(), dragComponent), network.end()); + removeComponent(dragComponent); graph->update(); // network changed, need to recalculate matching @@ -414,7 +493,23 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event) return false; } +void MatchingNetwork::updateSCPINames() +{ + // Need to remove all components from the subnode list first, otherwise + // name changes wouldn't work due to temporarily name collisions + for(auto &c : network) { + remove(c); + } + unsigned int i=1; + for(auto &c : network) { + c->changeName(QString::number(i)); + add(c); + i++; + } +} + MatchingComponent::MatchingComponent(Type type) + : SCPINode("COMPONENT") { this->type = type; eValue = nullptr; @@ -431,7 +526,10 @@ MatchingComponent::MatchingComponent(Type type) eValue = new SIUnitEdit(); eValue->setPrecision(4); eValue->setPrefixes("fpnum k"); - connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged); + connect(eValue, &SIUnitEdit::valueChanged, [=](double newval) { + value = newval; + emit valueChanged(); + }); auto layout = new QVBoxLayout(); layout->addWidget(eValue); setLayout(layout); @@ -443,7 +541,10 @@ MatchingComponent::MatchingComponent(Type type) eValue = new SIUnitEdit(); eValue->setPrecision(4); eValue->setPrefixes("fpnum k"); - connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged); + connect(eValue, &SIUnitEdit::valueChanged, [=](double newval) { + value = newval; + emit valueChanged(); + }); auto layout = new QVBoxLayout(); layout->addWidget(eValue); layout->addStretch(1); @@ -513,6 +614,37 @@ MatchingComponent::MatchingComponent(Type type) default: break; } + switch(type) { + case Type::SeriesR: + case Type::SeriesL: + case Type::SeriesC: + case Type::ParallelR: + case Type::ParallelL: + case Type::ParallelC: + addDoubleParameter("VALue", value, true, true, [=](){ + eValue->setValue(value); + }); + break; + case Type::DefinedThrough: + case Type::DefinedShunt: + add(new SCPICommand("FILE", [=](QStringList params) -> QString { + if(params.size() < 1) { + return SCPI::getResultName(SCPI::Result::Error); + } + try { + *touchstone = Touchstone::fromFile(params[0].toStdString()); + updateTouchstoneLabel(); + emit valueChanged(); + return SCPI::getResultName(SCPI::Result::Empty); + } catch(const std::exception& e) { + // failed to load file + return SCPI::getResultName(SCPI::Result::Error); + } + }, nullptr)); + break; + default: + break; + } } MatchingComponent::~MatchingComponent() @@ -562,6 +694,7 @@ ABCDparam MatchingComponent::parameters(double freq) void MatchingComponent::MatchingComponent::setValue(double v) { + value = v; if(eValue) { eValue->setValue(v); } @@ -570,7 +703,7 @@ void MatchingComponent::MatchingComponent::setValue(double v) MatchingComponent *MatchingComponent::createFromName(QString name) { for(unsigned int i=0;i<(int) Type::Last;i++) { - if(name == typeToName((Type) i)) { + if(typeToName((Type) i).compare(name, Qt::CaseInsensitive) == 0) { return new MatchingComponent((Type) i); } } diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.h b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.h index 6825ebd..336691e 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.h +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/matchingnetwork.h @@ -12,7 +12,7 @@ #include -class MatchingComponent : public QFrame, public Savable +class MatchingComponent : public QFrame, public Savable, public SCPINode { Q_OBJECT public: @@ -56,12 +56,14 @@ private: void focusInEvent(QFocusEvent *event) override; void focusOutEvent(QFocusEvent *event) override; QString oldStylesheet; + double value; }; class MatchingNetwork : public DeembeddingOption { public: MatchingNetwork(); + ~MatchingNetwork(); // DeembeddingOption interface public: @@ -71,6 +73,8 @@ public: Type getType() override {return Type::MatchingNetwork;} nlohmann::json toJSON() override; void fromJSON(nlohmann::json j) override; + + void clearNetwork(); private: static constexpr int imageHeight = 151; static constexpr int componentWidth = 151; @@ -80,10 +84,14 @@ private: unsigned int findInsertPosition(int xcoord); void addComponentAtPosition(int pos, MatchingComponent *c); void addComponent(int index, MatchingComponent *c); + void removeComponent(int index); + void removeComponent(MatchingComponent *c); void createDragComponent(MatchingComponent *c); void updateInsertIndicator(int xcoord); bool eventFilter(QObject *object, QEvent *event) override; + void updateSCPINames(); + std::vector network; unsigned int port; diff --git a/Software/PC_Application/LibreVNA-GUI/scpi.cpp b/Software/PC_Application/LibreVNA-GUI/scpi.cpp index 5a00147..cc78f69 100644 --- a/Software/PC_Application/LibreVNA-GUI/scpi.cpp +++ b/Software/PC_Application/LibreVNA-GUI/scpi.cpp @@ -160,40 +160,65 @@ bool SCPINode::add(SCPICommand *cmd) return true; } -bool SCPINode::addDoubleParameter(QString name, double ¶m, bool gettable, bool settable) +bool SCPINode::addDoubleParameter(QString name, double ¶m, bool gettable, bool settable, std::function setCallback) { - auto cmd = settable ? [&](QStringList params) -> QString { + auto cmd = settable ? [¶m, setCallback](QStringList params) -> QString { if(SCPI::paramToDouble(params, 0, param)) { + if(setCallback) { + setCallback(); + } return SCPI::getResultName(SCPI::Result::Empty); } else { return SCPI::getResultName(SCPI::Result::Error); } } : (std::function) nullptr; - auto query = settable ? [=](QStringList params) -> QString { + auto query = gettable ? [=](QStringList params) -> QString { Q_UNUSED(params) return QString::number(param); } : (std::function) nullptr; return add(new SCPICommand(name, cmd, query)); } -bool SCPINode::addUnsignedIntParameter(QString name, unsigned int ¶m, bool gettable, bool settable) +bool SCPINode::addUnsignedIntParameter(QString name, unsigned int ¶m, bool gettable, bool settable, std::function setCallback) { - auto cmd = settable ? [&](QStringList params) -> QString { + auto cmd = settable ? [¶m, setCallback](QStringList params) -> QString { unsigned long long value; if(SCPI::paramToULongLong(params, 0, value)) { param = value; + if(setCallback) { + setCallback(); + } return SCPI::getResultName(SCPI::Result::Empty); } else { return SCPI::getResultName(SCPI::Result::Error); } } : (std::function) nullptr; - auto query = settable ? [=](QStringList params) -> QString { + auto query = gettable ? [=](QStringList params) -> QString { Q_UNUSED(params) return QString::number(param); } : (std::function) nullptr; return add(new SCPICommand(name, cmd, query)); } +bool SCPINode::addBoolParameter(QString name, bool ¶m, bool gettable, bool settable, std::function setCallback) +{ + auto cmd = settable ? [¶m, setCallback](QStringList params) -> QString { + if(SCPI::paramToBool(params, 0, param)) { + if(setCallback) { + setCallback(); + } + return SCPI::getResultName(SCPI::Result::Empty); + } else { + return SCPI::getResultName(SCPI::Result::Error); + } + } : (std::function) nullptr; + auto query = gettable ? [=](QStringList params) -> QString { + Q_UNUSED(params) + return param ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); + } : (std::function) nullptr; + return add(new SCPICommand(name, cmd, query)); +} + bool SCPINode::changeName(QString newname) { if(newname == name) { @@ -206,6 +231,7 @@ bool SCPINode::changeName(QString newname) } } name = newname; + return true; } bool SCPINode::nameCollision(QString name) diff --git a/Software/PC_Application/LibreVNA-GUI/scpi.h b/Software/PC_Application/LibreVNA-GUI/scpi.h index 85d751e..b81f267 100644 --- a/Software/PC_Application/LibreVNA-GUI/scpi.h +++ b/Software/PC_Application/LibreVNA-GUI/scpi.h @@ -35,8 +35,9 @@ public: bool remove(SCPINode *node); bool add(SCPICommand *cmd); - bool addDoubleParameter(QString name, double ¶m, bool gettable = true, bool settable = true); - bool addUnsignedIntParameter(QString name, unsigned int ¶m, bool gettable = true, bool settable = true); + bool addDoubleParameter(QString name, double ¶m, bool gettable = true, bool settable = true, std::function setCallback = nullptr); + bool addUnsignedIntParameter(QString name, unsigned int ¶m, bool gettable = true, bool settable = true, std::function setCallback = nullptr); + bool addBoolParameter(QString name, bool ¶m, bool gettable = true, bool settable = true, std::function setCallback = nullptr); bool changeName(QString newname);