diff --git a/Software/PC_Application/LibreVNA-GUI/Util/util.cpp b/Software/PC_Application/LibreVNA-GUI/Util/util.cpp index 8dce2ea..2c0fc6e 100644 --- a/Software/PC_Application/LibreVNA-GUI/Util/util.cpp +++ b/Software/PC_Application/LibreVNA-GUI/Util/util.cpp @@ -62,7 +62,19 @@ double Util::distanceToLine(QPointF point, QPointF l1, QPointF l2, QPointF *clos } std::complex Util::SparamToImpedance(std::complex d, std::complex Z0) { - return Z0 * (1.0 + d) / (1.0 - d); + if (d != 1.0) { + return Z0 * (1.0 + d) / (1.0 - d); + } else { + return std::complex(std::numeric_limits::infinity(), 0.0); + } +} + +std::complex Util::ImpedanceToSparam(std::complex Z, std::complex Z0) { + if(std::isinf(Z.real())) { + return 1.0; + } else { + return (Z-Z0)/(Z+Z0); + } } double Util::dBmTodBuV(double dBm) diff --git a/Software/PC_Application/LibreVNA-GUI/Util/util.h b/Software/PC_Application/LibreVNA-GUI/Util/util.h index 8363bd8..de83dc9 100644 --- a/Software/PC_Application/LibreVNA-GUI/Util/util.h +++ b/Software/PC_Application/LibreVNA-GUI/Util/util.h @@ -52,6 +52,7 @@ namespace Util { return SparamToVSWR(abs(d)); } std::complex SparamToImpedance(std::complex d, std::complex Z0 = 50.0); + std::complex ImpedanceToSparam(std::complex Z, std::complex Z0 = 50.0); // all these conversions assume series connection of real and imag part static inline double SparamToResistance(std::complex d, std::complex Z0 = 50.0) { return SparamToImpedance(d, Z0).real(); diff --git a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/impedancerenormalization.cpp b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/impedancerenormalization.cpp index e4564d6..154cc7a 100644 --- a/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/impedancerenormalization.cpp +++ b/Software/PC_Application/LibreVNA-GUI/VNA/Deembedding/impedancerenormalization.cpp @@ -3,6 +3,7 @@ #include "ui_impedancenormalizationdialog.h" #include "Tools/parameters.h" #include "appwindow.h" +#include "Util/util.h" #include @@ -44,9 +45,11 @@ void ImpedanceRenormalization::transformDatapoint(DeviceDriver::VNAMeasurement & name = "S"+QString::number(ports+1)+QString::number(ports+1); } for(auto i=1;i<=ports;i++) { + // handle reflection parameters auto S11name = "S"+QString::number(i)+QString::number(i); auto S11 = p.measurements[S11name]; - transformed[S11name] = Sparam(ABCDparam(Sparam(S11, 0.1, 0.1, 1.0), p.Z0), impedance).m11; + transformed[S11name] = Util::ImpedanceToSparam(Util::SparamToImpedance(S11, p.Z0), impedance); + // handle transmission parameters for(auto j=i+1;j<=ports;j++) { auto S12name = "S"+QString::number(i)+QString::number(j); auto S21name = "S"+QString::number(j)+QString::number(i); diff --git a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro index 88008ec..e17b655 100644 --- a/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro +++ b/Software/PC_Application/LibreVNA-Test/LibreVNA-Test.pro @@ -152,6 +152,7 @@ SOURCES += \ ../LibreVNA-GUI/touchstone.cpp \ ../LibreVNA-GUI/unit.cpp \ ffttests.cpp \ + impedancerenormalizationtests.cpp \ main.cpp \ parametertests.cpp \ portextensiontests.cpp \ @@ -346,6 +347,7 @@ HEADERS += \ ../LibreVNA-GUI/touchstone.h \ ../LibreVNA-GUI/unit.h \ ffttests.h \ + impedancerenormalizationtests.h \ parametertests.h \ portextensiontests.h \ utiltests.h diff --git a/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.cpp b/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.cpp new file mode 100644 index 0000000..3a6d050 --- /dev/null +++ b/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.cpp @@ -0,0 +1,42 @@ +#include "impedancerenormalizationtests.h" + +ImpedanceRenormalizationTests::ImpedanceRenormalizationTests() +{ + renorm = new ImpedanceRenormalization(); + + // set it up to normalize to 75 Ohm + nlohmann::json j; + j["impedance"] = 75; + renorm->fromJSON(j); +} + +ImpedanceRenormalizationTests::~ImpedanceRenormalizationTests() +{ + delete renorm; +} + +void ImpedanceRenormalizationTests::OnePortTests() +{ + // Create dummy measurements + DeviceDriver::VNAMeasurement measShort; + measShort.pointNum = 0; + measShort.Z0 = 50.0; + measShort.measurements["S11"] = -1.0; + + auto measLoad = measShort; + measLoad.measurements["S11"] = 0.0; + + auto measOpen = measShort; + measOpen.measurements["S11"] = 1.0; + + // perform renormalization + renorm->transformDatapoint(measShort); + renorm->transformDatapoint(measLoad); + renorm->transformDatapoint(measOpen); + + QVERIFY(measShort.measurements["S11"] == -1.0); + // a Ohm load renormalized to 75 Ohm impedance has a reflection coefficient of -0.2 + QVERIFY(measLoad.measurements["S11"] == -0.2); + QVERIFY(measOpen.measurements["S11"] == 1.0); +} + diff --git a/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.h b/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.h new file mode 100644 index 0000000..82db941 --- /dev/null +++ b/Software/PC_Application/LibreVNA-Test/impedancerenormalizationtests.h @@ -0,0 +1,21 @@ +#ifndef IMPEDANCERENORMALIZATIONTESTS_H +#define IMPEDANCERENORMALIZATIONTESTS_H + +#include + +#include "impedancerenormalization.h" + +class ImpedanceRenormalizationTests : public QObject +{ + Q_OBJECT +public: + ImpedanceRenormalizationTests(); + ~ImpedanceRenormalizationTests(); +private slots: + void OnePortTests(); + +private: + ImpedanceRenormalization *renorm; +}; + +#endif // IMPEDANCERENORMALIZATIONTESTS_H diff --git a/Software/PC_Application/LibreVNA-Test/main.cpp b/Software/PC_Application/LibreVNA-Test/main.cpp index c3dafaa..7456941 100644 --- a/Software/PC_Application/LibreVNA-Test/main.cpp +++ b/Software/PC_Application/LibreVNA-Test/main.cpp @@ -2,6 +2,7 @@ #include "portextensiontests.h" #include "parametertests.h" #include "ffttests.h" +#include "impedancerenormalizationtests.h" #include @@ -14,6 +15,7 @@ int main(int argc, char *argv[]) status |= QTest::qExec(new PortExtensionTests, argc, argv); status |= QTest::qExec(new ParameterTests, argc, argv); status |= QTest::qExec(new fftTests, argc, argv); + status |= QTest::qExec(new ImpedanceRenormalizationTests, argc, argv); return status; } diff --git a/Software/PC_Application/LibreVNA-Test/utiltests.cpp b/Software/PC_Application/LibreVNA-Test/utiltests.cpp index c8badd3..4febd99 100644 --- a/Software/PC_Application/LibreVNA-Test/utiltests.cpp +++ b/Software/PC_Application/LibreVNA-Test/utiltests.cpp @@ -78,3 +78,31 @@ void UtilTests::FirmwareComparison() QVERIFY(Util::firmwareEqualOrHigher("2.2.2", "2.3") == false); QVERIFY(Util::firmwareEqualOrHigher("2.2", "2.3.1") == false); } + +void UtilTests::ImpedanceSparameterCalculation() +{ + // Check impedance to S parameter conversion and back for different values + auto Z = std::complex(50.0); + auto S = Util::ImpedanceToSparam(Z); + QVERIFY(S == 0.0); + QVERIFY(Util::SparamToImpedance(S) == Z); + + Z = std::complex(0.0); + S = Util::ImpedanceToSparam(Z); + QVERIFY(S == -1.0); + QVERIFY(Util::SparamToImpedance(S) == Z); + + Z = std::complex(100.0); + S = Util::ImpedanceToSparam(Z); + QVERIFY(qFuzzyCompare(S.real(), 1.0/3)); + QVERIFY(S.imag() == 0.0); + QVERIFY(qFuzzyCompare(Util::SparamToImpedance(S).real(), Z.real())); + QVERIFY(qFuzzyCompare(Util::SparamToImpedance(S).imag(), Z.imag())); + + // Edge case: convert S parameter to impedance at 1.0 (which will give a +inf impedance) + S = 1.0; + Z = Util::SparamToImpedance(S); + // convert back, we must land back at 1.0 + auto S_from_Z = Util::ImpedanceToSparam(Z); + QVERIFY(S_from_Z == S); +} diff --git a/Software/PC_Application/LibreVNA-Test/utiltests.h b/Software/PC_Application/LibreVNA-Test/utiltests.h index 4ac676a..9075be5 100644 --- a/Software/PC_Application/LibreVNA-Test/utiltests.h +++ b/Software/PC_Application/LibreVNA-Test/utiltests.h @@ -14,6 +14,7 @@ private slots: void IdealArcApproximation(); void NoisyCircleApproximation(); void FirmwareComparison(); + void ImpedanceSparameterCalculation(); }; #endif // UTILTESTS_H