diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index b66be51..1f497d4 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -315,7 +315,7 @@ nlohmann::json SpectrumAnalyzer::toJSON() tracking["enabled"] = settings.trackingGenerator ? true : false; tracking["port"] = settings.trackingGeneratorPort ? 2 : 1; tracking["offset"] = settings.trackingGeneratorOffset; - tracking["power"] = settings.trackingPower; + tracking["power"] = (double) settings.trackingPower / 100.0; // convert to dBm sweep["trackingGenerator"] = tracking; if(normalize.active) { diff --git a/Software/PC_Application/Traces/Marker/marker.cpp b/Software/PC_Application/Traces/Marker/marker.cpp index 0111c05..1e76a93 100644 --- a/Software/PC_Application/Traces/Marker/marker.cpp +++ b/Software/PC_Application/Traces/Marker/marker.cpp @@ -1560,7 +1560,7 @@ bool Marker::isMovable() // helper traces are never movable by the user return false; } - if(trace()->size() == 0) { + if(!parentTrace || parentTrace->size() == 0) { return false; } switch(type) { diff --git a/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp b/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp index f6592c0..3b08796 100644 --- a/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp +++ b/Software/PC_Application/VNA/Deembedding/matchingnetwork.cpp @@ -1,6 +1,8 @@ #include "matchingnetwork.h" #include "ui_matchingnetworkdialog.h" +#include "unit.h" +#include "CustomWidgets/informationbox.h" #include #include @@ -11,6 +13,7 @@ #include #include #include +#include using namespace std; @@ -83,24 +86,25 @@ void MatchingNetwork::edit() ui->lParallelC->installEventFilter(this); ui->lParallelL->installEventFilter(this); ui->lParallelR->installEventFilter(this); + ui->lDefinedThrough->installEventFilter(this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); layout->addStretch(1); auto p1 = new QWidget(); - p1->setMinimumSize(portWidth, 150); - p1->setMaximumSize(portWidth, 150); + p1->setMinimumSize(portWidth, 151); + p1->setMaximumSize(portWidth, 151); p1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - p1->setStyleSheet("image: url(:/icons/port1.svg);"); + p1->setStyleSheet("image: url(:/icons/port1.png);"); auto DUT = new QWidget(); - DUT->setMinimumSize(DUTWidth, 150); - DUT->setMaximumSize(DUTWidth, 150); + DUT->setMinimumSize(DUTWidth, 151); + DUT->setMaximumSize(DUTWidth, 151); DUT->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - DUT->setStyleSheet("image: url(:/icons/DUT.svg);"); + DUT->setStyleSheet("image: url(:/icons/DUT.png);"); auto p2 = new QWidget(); - p2->setMinimumSize(portWidth, 150); - p2->setMaximumSize(portWidth, 150); + p2->setMinimumSize(portWidth, 151); + p2->setMaximumSize(portWidth, 151); p2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - p2->setStyleSheet("image: url(:/icons/port2.svg);"); + p2->setStyleSheet("image: url(:/icons/port2.png);"); layout->addWidget(p1); for(auto w : p1Network) { layout->addWidget(w); @@ -136,27 +140,31 @@ void MatchingNetwork::edit() nlohmann::json MatchingNetwork::toJSON() { nlohmann::json j; - for(int i=0;i<2;i++) { - auto network = i==0 ? p1Network : p2Network; - nlohmann::json jn; - for(auto c : network) { - nlohmann::json jc; - jc["component"] = c->getName().toStdString(); - jc["params"] = c->toJSON(); - jn.push_back(jc); - } - j.push_back(jn); + nlohmann::json jn1, jn2; + for(auto c : p1Network) { + nlohmann::json jc; + jc["component"] = c->getName().toStdString(); + jc["params"] = c->toJSON(); + jn1.push_back(jc); } + for(auto c : p2Network) { + nlohmann::json jc; + jc["component"] = c->getName().toStdString(); + jc["params"] = c->toJSON(); + jn2.push_back(jc); + } + j["port1"] = jn1; + j["port2"] = jn2; + j["addNetwork"] = addNetwork; return j; } void MatchingNetwork::fromJSON(nlohmann::json j) { - for(int i=0;i<2;i++) { - auto jn = j[i]; - auto &network = i==0 ? p1Network : p2Network; - network.clear(); - for(auto jc : jn) { + p1Network.clear(); + p2Network.clear(); + if(j.contains("port1")) { + for(auto jc : j["port1"]) { if(!jc.contains("component")) { continue; } @@ -165,9 +173,23 @@ void MatchingNetwork::fromJSON(nlohmann::json j) continue; } c->fromJSON(jc["params"]); - network.push_back(c); + p1Network.push_back(c); } } + if(j.contains("port2")) { + for(auto jc : j["port2"]) { + if(!jc.contains("component")) { + continue; + } + auto c = MatchingComponent::createFromName(QString::fromStdString(jc["component"])); + if(!c) { + continue; + } + c->fromJSON(jc["params"]); + p2Network.push_back(c); + } + } + addNetwork = j.value("addNetwork", true); matching.clear(); } @@ -329,8 +351,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event) dropComponent = (MatchingComponent*) dropPtr; dragEvent->acceptProposedAction(); insertIndicator = new QWidget(); - insertIndicator->setMinimumSize(2, 150); - insertIndicator->setMaximumSize(2, 150); + insertIndicator->setMinimumSize(2, imageHeight); + insertIndicator->setMaximumSize(2, imageHeight); insertIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); insertIndicator->setStyleSheet("background-color:red;"); updateInsertIndicator(dragEvent->pos().x()); @@ -368,6 +390,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event) dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelL); } else if(object->objectName() == "lParallelR") { dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelR); + } else if(object->objectName() == "lDefinedThrough") { + dragComponent = new MatchingComponent(MatchingComponent::Type::DefinedThrough); } else { dragComponent = nullptr; } @@ -397,18 +421,21 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event) MatchingComponent::MatchingComponent(Type type) { this->type = type; - setMinimumSize(150, 150); - setMaximumSize(150, 150); + eValue = nullptr; + touchstone = nullptr; + touchstoneLabel = nullptr; + setMinimumSize(151, 151); + setMaximumSize(151, 151); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); - eValue = new SIUnitEdit(); - eValue->setPrecision(4); - eValue->setPrefixes("fpnum k"); - connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged); setFocusPolicy(Qt::FocusPolicy::ClickFocus); switch(type) { case Type::SeriesR: case Type::SeriesL: case Type::SeriesC: { + eValue = new SIUnitEdit(); + eValue->setPrecision(4); + eValue->setPrefixes("fpnum k"); + connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged); auto layout = new QVBoxLayout(); layout->addWidget(eValue); setLayout(layout); @@ -417,13 +444,16 @@ MatchingComponent::MatchingComponent(Type type) case Type::ParallelR: case Type::ParallelL: case Type::ParallelC: { + eValue = new SIUnitEdit(); + eValue->setPrecision(4); + eValue->setPrefixes("fpnum k"); + connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged); auto layout = new QVBoxLayout(); layout->addWidget(eValue); layout->addStretch(1); layout->setContentsMargins(9, 5, 9, 9); setLayout(layout); } - break; default: break; } @@ -431,38 +461,58 @@ MatchingComponent::MatchingComponent(Type type) case Type::SeriesR: eValue->setUnit("Ω"); eValue->setValue(50); - setStyleSheet("image: url(:/icons/seriesR.svg);"); + setStyleSheet("image: url(:/icons/seriesR.png);"); break; case Type::SeriesL: eValue->setUnit("H"); eValue->setValue(1e-9); - setStyleSheet("image: url(:/icons/seriesL.svg);"); + setStyleSheet("image: url(:/icons/seriesL.png);"); break; case Type::SeriesC: eValue->setUnit("F"); eValue->setValue(1e-12); - setStyleSheet("image: url(:/icons/seriesC.svg);"); + setStyleSheet("image: url(:/icons/seriesC.png);"); break; case Type::ParallelR: eValue->setUnit("Ω"); eValue->setValue(50); - setStyleSheet("image: url(:/icons/parallelR.svg);"); + setStyleSheet("image: url(:/icons/parallelR.png);"); break; case Type::ParallelL: eValue->setUnit("H"); eValue->setValue(1e-9); - setStyleSheet("image: url(:/icons/parallelL.svg);"); + setStyleSheet("image: url(:/icons/parallelL.png);"); break; case Type::ParallelC: eValue->setUnit("F"); eValue->setValue(1e-12); - setStyleSheet("image: url(:/icons/parallelC.svg);"); + setStyleSheet("image: url(:/icons/parallelC.png);"); + break; + case Type::DefinedThrough: { + touchstone = new Touchstone(2); + touchstoneLabel = new QLabel(); + touchstoneLabel->setWordWrap(true); + touchstoneLabel->setAlignment(Qt::AlignCenter); + auto layout = new QVBoxLayout(); + layout->addWidget(touchstoneLabel); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + setStyleSheet("image: url(:/icons/definedThrough.png);"); + updateTouchstoneLabel(); + } break; default: break; } } +MatchingComponent::~MatchingComponent() +{ + delete eValue; + delete touchstone; + delete touchstoneLabel; +} + ABCDparam MatchingComponent::parameters(double freq) { switch(type) { @@ -478,6 +528,15 @@ ABCDparam MatchingComponent::parameters(double freq) return ABCDparam(1.0, 0.0, 1.0/complex(0, freq * 2 * M_PI * eValue->value()), 1.0); case Type::ParallelC: return ABCDparam(1.0, 0.0, 1.0/complex(0, -1.0 / (freq * 2 * M_PI * eValue->value())), 1.0); + case Type::DefinedThrough: + if(touchstone->points() == 0 || freq < touchstone->minFreq() || freq > touchstone->maxFreq()) { + // outside of provided frequency range, pass through unchanged + return ABCDparam(1.0, 0.0, 0.0, 1.0); + } else { + auto d = touchstone->interpolate(freq); + auto S = Sparam(d.S[0], d.S[1], d.S[2], d.S[3]); + return ABCDparam(S, 50.0); + } default: return ABCDparam(1.0, 0.0, 0.0, 1.0); } @@ -485,7 +544,9 @@ ABCDparam MatchingComponent::parameters(double freq) void MatchingComponent::MatchingComponent::setValue(double v) { - eValue->setValue(v); + if(eValue) { + eValue->setValue(v); + } } MatchingComponent *MatchingComponent::createFromName(QString name) @@ -507,13 +568,73 @@ QString MatchingComponent::getName() nlohmann::json MatchingComponent::toJSON() { nlohmann::json j; - j["value"] = eValue->value(); + switch(type) { + case Type::SeriesC: + case Type::SeriesR: + case Type::SeriesL: + case Type::ParallelC: + case Type::ParallelR: + case Type::ParallelL: + j["value"] = eValue->value(); + break; + case Type::DefinedThrough: + j["touchstone"] = touchstone->toJSON(); + break; + case Type::Last: + break; + } return j; } void MatchingComponent::fromJSON(nlohmann::json j) { - eValue->setValue(j.value("value", 1e-12)); + switch(type) { + case Type::SeriesC: + case Type::SeriesR: + case Type::SeriesL: + case Type::ParallelC: + case Type::ParallelR: + case Type::ParallelL: + eValue->setValue(j.value("value", 1e-12)); + break; + case Type::DefinedThrough: + touchstone->fromJSON(j["touchstone"]); + updateTouchstoneLabel(); + break; + case Type::Last: + break; + } +} + +void MatchingComponent::mouseDoubleClickEvent(QMouseEvent *e) +{ + Q_UNUSED(e); + if(type == Type::DefinedThrough) { + // select new touchstone file + auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s2p)", nullptr, QFileDialog::DontUseNativeDialog); + if (!filename.isEmpty()) { + try { + *touchstone = Touchstone::fromFile(filename.toStdString()); + } catch(const std::exception& e) { + InformationBox::ShowError("Failed to load file", QString("Attempt to load file ended with error: \"") + e.what()+"\""); + } + updateTouchstoneLabel(); + } + } +} + +void MatchingComponent::updateTouchstoneLabel() +{ + if(!touchstone || !touchstoneLabel) { + return; + } + if(touchstone->points() == 0) { + touchstoneLabel->setText("No data. Double-click to select touchstone file"); + } else { + QString text = QString::number(touchstone->points()) + " points from "+Unit::ToString(touchstone->minFreq(), "Hz", " kMG", 4) + + " to "+Unit::ToString(touchstone->maxFreq(), "Hz", " kMG", 4); + touchstoneLabel->setText(text); + } } QString MatchingComponent::typeToName(MatchingComponent::Type type) @@ -525,6 +646,7 @@ QString MatchingComponent::typeToName(MatchingComponent::Type type) case Type::ParallelR: return "ParallelR"; case Type::ParallelL: return "ParallelL"; case Type::ParallelC: return "ParallelC"; + case Type::DefinedThrough: return "Touchstone Through"; default: return ""; } } diff --git a/Software/PC_Application/VNA/Deembedding/matchingnetwork.h b/Software/PC_Application/VNA/Deembedding/matchingnetwork.h index 7ea983e..afc1fc7 100644 --- a/Software/PC_Application/VNA/Deembedding/matchingnetwork.h +++ b/Software/PC_Application/VNA/Deembedding/matchingnetwork.h @@ -5,8 +5,10 @@ #include "deembeddingoption.h" #include "Tools/parameters.h" #include "savable.h" +#include "touchstone.h" #include +#include #include @@ -21,11 +23,13 @@ public: ParallelR, ParallelL, ParallelC, + DefinedThrough, // Add new matching components here, do not explicitly assign values and keep the Last entry at the last position Last, }; MatchingComponent(Type type); + ~MatchingComponent(); ABCDparam parameters(double freq); void setValue(double v); @@ -40,7 +44,11 @@ signals: void deleted(MatchingComponent* m); protected: SIUnitEdit *eValue; + Touchstone *touchstone; + QLabel *touchstoneLabel; private: + void mouseDoubleClickEvent(QMouseEvent *e) override; + void updateTouchstoneLabel(); static QString typeToName(Type type); Type type; void keyPressEvent(QKeyEvent *event) override; @@ -62,9 +70,10 @@ public: nlohmann::json toJSON() override; void fromJSON(nlohmann::json j) override; private: - static constexpr int componentWidth = 150; - static constexpr int DUTWidth = 150; - static constexpr int portWidth = 75; + static constexpr int imageHeight = 151; + static constexpr int componentWidth = 151; + static constexpr int DUTWidth = 151; + static constexpr int portWidth = 76; MatchingComponent *componentAtPosition(int pos); unsigned int findInsertPosition(int xcoord); void addComponentAtPosition(int pos, MatchingComponent *c); diff --git a/Software/PC_Application/VNA/Deembedding/matchingnetworkdialog.ui b/Software/PC_Application/VNA/Deembedding/matchingnetworkdialog.ui index 2a52e6f..a1c67f3 100644 --- a/Software/PC_Application/VNA/Deembedding/matchingnetworkdialog.ui +++ b/Software/PC_Application/VNA/Deembedding/matchingnetworkdialog.ui @@ -7,7 +7,7 @@ 0 0 772 - 442 + 443 @@ -131,20 +131,8 @@ Matching Components - - - 0 - - - 0 - - - 0 - - - 0 - - + + @@ -159,369 +147,423 @@ - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/seriesC.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + Series C - - 0 + + Qt::AlignCenter - - - - border-image: url(:/icons/seriesC.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Series C - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/seriesL.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + Series L - - 0 + + Qt::AlignCenter - - - - border-image: url(:/icons/seriesL.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Series L - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/seriesR.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + Series R - - 0 + + Qt::AlignCenter - - - - border-image: url(:/icons/seriesR.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Series R - - - Qt::AlignCenter - - - - - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/definedThrough.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + <html><head/><body><p align="center"><br/><br/>Defined<br/>Through</p></body></html> - - 0 + + Qt::AlignHCenter|Qt::AlignTop - - - - border-image: url(:/icons/parallelC.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Parallel C - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/parallelC.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + Parallel C - - 0 + + Qt::AlignHCenter|Qt::AlignTop - - - - - 0 - 0 - - - - border-image: url(:/icons/parallelL.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Parallel L - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - - - - - 0 - 0 - - - - - 80 - 80 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + - - 0 + + border-image: url(:/icons/parallelL.svg); - - 0 + + QFrame::NoFrame - - 0 + + QFrame::Plain - - 0 + + Parallel L - - - - border-image: url(:/icons/parallelR.svg); - - - QFrame::NoFrame - - - QFrame::Plain - - - Parallel R - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - + + Qt::AlignHCenter|Qt::AlignTop + + + + + + + + + + + 0 + 0 + + + + + 80 + 80 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + border-image: url(:/icons/parallelR.svg); + + + QFrame::NoFrame + + + QFrame::Plain + + + Parallel R + + + Qt::AlignHCenter|Qt::AlignTop + + + + + diff --git a/Software/PC_Application/icons.qrc b/Software/PC_Application/icons.qrc index b4b2c2f..148fb55 100644 --- a/Software/PC_Application/icons.qrc +++ b/Software/PC_Application/icons.qrc @@ -50,5 +50,13 @@ icons/down.png icons/up.png icons/chainlink.png + icons/definedThrough.svg + icons/seriesR.png + icons/seriesL.png + icons/seriesC.png + icons/parallelR.png + icons/parallelL.png + icons/parallelC.png + icons/definedThrough.png diff --git a/Software/PC_Application/icons/definedThrough.png b/Software/PC_Application/icons/definedThrough.png new file mode 100644 index 0000000..71b4182 Binary files /dev/null and b/Software/PC_Application/icons/definedThrough.png differ diff --git a/Software/PC_Application/icons/definedThrough.svg b/Software/PC_Application/icons/definedThrough.svg new file mode 100644 index 0000000..c50ab38 --- /dev/null +++ b/Software/PC_Application/icons/definedThrough.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/PC_Application/icons/parallelC.png b/Software/PC_Application/icons/parallelC.png new file mode 100644 index 0000000..9acfd38 Binary files /dev/null and b/Software/PC_Application/icons/parallelC.png differ diff --git a/Software/PC_Application/icons/parallelL.png b/Software/PC_Application/icons/parallelL.png new file mode 100644 index 0000000..5e7b85a Binary files /dev/null and b/Software/PC_Application/icons/parallelL.png differ diff --git a/Software/PC_Application/icons/parallelR.png b/Software/PC_Application/icons/parallelR.png new file mode 100644 index 0000000..78feadc Binary files /dev/null and b/Software/PC_Application/icons/parallelR.png differ diff --git a/Software/PC_Application/icons/port1.svg b/Software/PC_Application/icons/port1.svg index c93f08d..12252e3 100644 --- a/Software/PC_Application/icons/port1.svg +++ b/Software/PC_Application/icons/port1.svg @@ -1,6 +1,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -12,6 +32,17 @@ + + + + + + + + + + + diff --git a/Software/PC_Application/icons/port2.svg b/Software/PC_Application/icons/port2.svg index f98b346..9ead629 100644 --- a/Software/PC_Application/icons/port2.svg +++ b/Software/PC_Application/icons/port2.svg @@ -1,12 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Software/PC_Application/icons/seriesC.png b/Software/PC_Application/icons/seriesC.png new file mode 100644 index 0000000..3e5155a Binary files /dev/null and b/Software/PC_Application/icons/seriesC.png differ diff --git a/Software/PC_Application/icons/seriesL.png b/Software/PC_Application/icons/seriesL.png new file mode 100644 index 0000000..ae373ce Binary files /dev/null and b/Software/PC_Application/icons/seriesL.png differ diff --git a/Software/PC_Application/icons/seriesR.png b/Software/PC_Application/icons/seriesR.png new file mode 100644 index 0000000..f4028ee Binary files /dev/null and b/Software/PC_Application/icons/seriesR.png differ diff --git a/Software/PC_Application/touchstone.cpp b/Software/PC_Application/touchstone.cpp index 555667e..5525dbe 100644 --- a/Software/PC_Application/touchstone.cpp +++ b/Software/PC_Application/touchstone.cpp @@ -384,3 +384,57 @@ QString Touchstone::getFilename() const { return filename; } + +nlohmann::json Touchstone::toJSON() +{ + nlohmann::json j; + j["ports"] = m_ports; + j["filename"] = filename.toStdString(); + if(m_datapoints.size() > 0) { + nlohmann::json json_points; + for(auto d : m_datapoints) { + nlohmann::json point; + point["frequency"] = d.frequency; + nlohmann::json sparams; + for(auto s : d.S) { + nlohmann::json sparam; + sparam["real"] = s.real(); + sparam["imag"] = s.imag(); + sparams.push_back(sparam); + } + point["Sparams"] = sparams; + json_points.push_back(point); + } + j["datapoints"] = json_points; + } + return j; +} + +void Touchstone::fromJSON(nlohmann::json j) +{ + m_datapoints.clear(); + filename = QString::fromStdString(j.value("filename", "")); + m_ports = j.value("ports", 0); + if(!m_ports || !j.contains("datapoints")) { + return; + } + auto json_points = j["datapoints"]; + for(auto point : json_points) { + Datapoint d; + if(!point.contains("frequency") || !point.contains("Sparams")) { + // missing data, abort here + qWarning() << "Touchstone data point does not contain frequency or S parameters"; + break; + } + d.frequency = point["frequency"]; + if(point["Sparams"].size() != m_ports * m_ports) { + // invalid number of Sparams, abort here + qWarning() << "Invalid number of S parameters, got" << point["Sparams"].size() << "expected" << m_ports*m_ports; + break; + } + for(auto Sparam : point["Sparams"]) { + d.S.push_back(complex(Sparam.value("real", 0.0), Sparam.value("imag", 0.0))); + } + m_datapoints.push_back(d); + } +} diff --git a/Software/PC_Application/touchstone.h b/Software/PC_Application/touchstone.h index a73b4ce..fa34fa1 100644 --- a/Software/PC_Application/touchstone.h +++ b/Software/PC_Application/touchstone.h @@ -1,12 +1,14 @@ #ifndef TOUCHSTONE_H #define TOUCHSTONE_H +#include "savable.h" + #include #include #include #include -class Touchstone +class Touchstone : public Savable { public: enum class Scale { @@ -29,6 +31,7 @@ public: }; Touchstone(unsigned int m_ports); + virtual ~Touchstone(){}; void AddDatapoint(Datapoint p); void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary); std::stringstream toString(Scale unit = Scale::GHz, Format format = Format::RealImaginary); @@ -45,6 +48,9 @@ public: unsigned int ports() { return m_ports; } QString getFilename() const; + virtual nlohmann::json toJSON(); + virtual void fromJSON(nlohmann::json j); + private: unsigned int m_ports; std::vector m_datapoints;