mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-01-31 04:44:40 +01:00
Improve de-embedding
- Add Z parameters with general conversion from and to S parameters - Implement impedance renormalization over general Z parameters - Fix crash after taking de-embedding measurement - Fix various small bugs with new parameter implementation
This commit is contained in:
parent
77a3fc5039
commit
0205ab494d
|
|
@ -429,7 +429,7 @@ void CalibrationMeasurement::TwoPort::addPoint(const DeviceDriver::VNAMeasuremen
|
|||
{
|
||||
Point p;
|
||||
p.frequency = m.frequency;
|
||||
p.S = m.toSparam(port1, port2);
|
||||
p.S = m.toSparam().reduceTo({port1, port2});
|
||||
points.push_back(p);
|
||||
timestamp = QDateTime::currentDateTimeUtc();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,33 +61,55 @@ unsigned int DeviceDriver::SApoints() {
|
|||
}
|
||||
}
|
||||
|
||||
Sparam DeviceDriver::VNAMeasurement::toSparam(int port1, int port2) const
|
||||
Sparam DeviceDriver::VNAMeasurement::toSparam(int ports) const
|
||||
{
|
||||
Sparam S;
|
||||
S.set(1,1, measurements.at("S"+QString::number(port1)+QString::number(port1)));
|
||||
S.set(1,2, measurements.at("S"+QString::number(port1)+QString::number(port2)));
|
||||
S.set(2,1, measurements.at("S"+QString::number(port2)+QString::number(port1)));
|
||||
S.set(2,2, measurements.at("S"+QString::number(port2)+QString::number(port2)));
|
||||
if(ports == 0) {
|
||||
// determine number of ports by highest available S parameter
|
||||
for(const auto &m : measurements) {
|
||||
if(!m.first.startsWith("S")) {
|
||||
// something else we can not handle
|
||||
continue;
|
||||
}
|
||||
int to = m.first.mid(1,1).toUInt();
|
||||
int from = m.first.mid(2,1).toUInt();
|
||||
if(to > ports) {
|
||||
ports = to;
|
||||
}
|
||||
if(from > ports) {
|
||||
ports = from;
|
||||
}
|
||||
}
|
||||
}
|
||||
// create S paramters
|
||||
auto S = Sparam(ports);
|
||||
// fill data
|
||||
for(const auto &m : measurements) {
|
||||
if(!m.first.startsWith("S")) {
|
||||
// something else we can not handle
|
||||
continue;
|
||||
}
|
||||
int to = m.first.mid(1,1).toUInt();
|
||||
int from = m.first.mid(2,1).toUInt();
|
||||
S.set(to, from, m.second);
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
void DeviceDriver::VNAMeasurement::fromSparam(Sparam S, int port1, int port2)
|
||||
void DeviceDriver::VNAMeasurement::fromSparam(Sparam S, std::vector<unsigned int> portMapping)
|
||||
{
|
||||
QString s11 = "S"+QString::number(port1)+QString::number(port1);
|
||||
QString s12 = "S"+QString::number(port1)+QString::number(port2);
|
||||
QString s21 = "S"+QString::number(port2)+QString::number(port1);
|
||||
QString s22 = "S"+QString::number(port2)+QString::number(port2);
|
||||
if(measurements.count(s11)) {
|
||||
measurements[s11] = S.get(1,1);
|
||||
if(portMapping.size() == 0) {
|
||||
// set up default port mapping
|
||||
for(unsigned int i=1;i<=S.ports();i++) {
|
||||
portMapping.push_back(i);
|
||||
}
|
||||
}
|
||||
if(measurements.count(s12)) {
|
||||
measurements[s12] = S.get(1,2);
|
||||
}
|
||||
if(measurements.count(s21)) {
|
||||
measurements[s21] = S.get(2,1);
|
||||
}
|
||||
if(measurements.count(s22)) {
|
||||
measurements[s22] = S.get(2,2);
|
||||
for(unsigned int i=0;i<portMapping.size();i++) {
|
||||
for(unsigned int j=0;j<portMapping.size();j++) {
|
||||
QString name = "S"+QString::number(i+1)+QString::number(j+1);
|
||||
if(measurements.count(name)) {
|
||||
measurements[name] = S.get(portMapping[i], portMapping[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -296,8 +296,20 @@ public:
|
|||
// Value: complex measurement in real/imag (linear, not in dB)
|
||||
std::map<QString, std::complex<double>> measurements;
|
||||
|
||||
Sparam toSparam(int port1, int port2) const;
|
||||
void fromSparam(Sparam S, int port1, int port2);
|
||||
Sparam toSparam(int ports = 0) const;
|
||||
/* Sets the measurement values in the VNAmeasurement (if existent) to the values from the S parameter matrix.
|
||||
* The portMapping parameter can be used to specify which values to set from which S parameter:
|
||||
* Example: S parameter contains 4 port S parameters, but the VNAmeasurement is 2 port only with this mapping:
|
||||
* VNAMeasurement port | S parameter port
|
||||
* --------------------|-----------------
|
||||
* 1 | 2
|
||||
* 2 | 4
|
||||
* This means that we want S22 (from the 4 port S parameter) stored as S11 (in the VNAMeasurement).
|
||||
* Function call for this example: fromSparam(S, {2,4})
|
||||
*
|
||||
* If no portMapping is specified, the port order (and mapping) from the S paramters are kept.
|
||||
*/
|
||||
void fromSparam(Sparam S, std::vector<unsigned int> portMapping = {});
|
||||
VNAMeasurement interpolateTo(const VNAMeasurement &to, double a);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,18 +24,52 @@ Sparam::Sparam(const ABCDparam &a, Type Z0)
|
|||
{
|
||||
}
|
||||
|
||||
Sparam::Sparam(const Zparam &Z, std::vector<Type> Z0n)
|
||||
{
|
||||
if(Z.ports() != Z0n.size()) {
|
||||
throw std::runtime_error("number of supplied characteristic impedances does not match number of ports");
|
||||
}
|
||||
/* general formula for converting S parameters to Z parameters:
|
||||
* S = (sqrt(y)*Z*sqrt(y)-1)*(sqrt(y)*Z*sqrt(y)+1)^-1
|
||||
* with:
|
||||
* Z = Z parameter matrix
|
||||
* 1 = identity matrix
|
||||
* sqrt(y) = diagonal matrix with the root of characteristic admittances as it non-zero elements
|
||||
*/
|
||||
// create identity matrix
|
||||
auto ident = Eigen::MatrixXcd::Identity(Z.ports(), Z.ports());
|
||||
// create sqrt(y) matrix
|
||||
Eigen::MatrixXcd sqrty = Eigen::MatrixXcd::Zero(Z.ports(), Z.ports());
|
||||
// fill with characteristic admittance
|
||||
for(unsigned int i=0;i<Z.ports();i++) {
|
||||
sqrty(i, i) = 1.0/(sqrt(Z0n[i]));
|
||||
}
|
||||
// apply formula
|
||||
auto yZy = sqrty*Z.data*sqrty;
|
||||
data = (yZy-ident)*(yZy+ident).inverse();
|
||||
}
|
||||
|
||||
Sparam::Sparam(const Zparam &Z, Type Z0)
|
||||
: Sparam(Z, std::vector<Type>(Z.ports(), Z0))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Sparam::swapPorts(unsigned int p1, unsigned int p2)
|
||||
{
|
||||
// swap columns
|
||||
data.col(p1-1).swap(data.col(p2-1));
|
||||
data.row(p1-1).swap(data.row(p2-1));
|
||||
// auto cbuf = data.col(p1-1);
|
||||
// data.col(p1-1) = data.col(p2-1);
|
||||
// data.col(p2-1) = cbuf;
|
||||
// // swap rows
|
||||
// auto rbuf = data.row(p1-1);
|
||||
// data.row(p1-1) = data.row(p2-1);
|
||||
// data.row(p2-1) = rbuf;
|
||||
}
|
||||
|
||||
Sparam Sparam::reduceTo(std::vector<unsigned int> ports) const
|
||||
{
|
||||
auto ret = Sparam(ports.size());
|
||||
for(unsigned int from=0;from<ports.size();from++) {
|
||||
for(unsigned int to=0;to<ports.size();to++) {
|
||||
ret.data(to, from) = get(ports[to], ports[from]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ABCDparam::ABCDparam(const Sparam &s, Type Z01, Type Z02)
|
||||
|
|
@ -68,6 +102,12 @@ ABCDparam::ABCDparam(const Sparam &s, Type Z0)
|
|||
{
|
||||
}
|
||||
|
||||
Parameters::Parameters(Type m11)
|
||||
: Parameters(1)
|
||||
{
|
||||
data(0, 0) = m11;
|
||||
}
|
||||
|
||||
Parameters::Parameters(Type m11, Type m12, Type m21, Type m22)
|
||||
: Parameters(2)
|
||||
{
|
||||
|
|
@ -130,7 +170,7 @@ Yparam::Yparam(const Sparam &s, Type Z01, Type Z02)
|
|||
{
|
||||
// TODO can this be done for any number of ports
|
||||
if(s.ports() != 2) {
|
||||
throw std::runtime_error("Can only create ABCD parameter from 2 port S parameters");
|
||||
throw std::runtime_error("Can only create Y parameter from 2 port S parameters");
|
||||
}
|
||||
data = Eigen::MatrixXcd(2,2);
|
||||
// from https://www.rfcafe.com/references/electrical/s-h-y-z.htm
|
||||
|
|
@ -145,3 +185,33 @@ Yparam::Yparam(const Sparam &s, Type Z0)
|
|||
: Yparam(s, Z0, Z0)
|
||||
{
|
||||
}
|
||||
|
||||
Zparam::Zparam(const Sparam &S, std::vector<Type> Z0n)
|
||||
{
|
||||
if(S.ports() != Z0n.size()) {
|
||||
throw std::runtime_error("number of supplied characteristic impedances does not match number of ports");
|
||||
}
|
||||
/* general formula for converting S parameters to Z parameters:
|
||||
* Z = sqrt(z)*(1+S)*(1-S)^-1*sqrt(z)
|
||||
* with:
|
||||
* S = S parameter matrix
|
||||
* 1 = identity matrix
|
||||
* sqrt(z) = diagonal matrix with the root of characteristic impedances as it non-zero elements
|
||||
*/
|
||||
// create identity matrix
|
||||
auto ident = Eigen::MatrixXcd::Identity(S.ports(), S.ports());
|
||||
// create sqrt(z) matrix
|
||||
Eigen::MatrixXcd sqrtz = Eigen::MatrixXcd::Zero(S.ports(), S.ports());
|
||||
// fill with characteristic impedance
|
||||
for(unsigned int i=0;i<S.ports();i++) {
|
||||
sqrtz(i, i) = sqrt(Z0n[i]);
|
||||
}
|
||||
// apply formula
|
||||
data = sqrtz*(ident+S.data)*(ident-S.data).inverse()*sqrtz;
|
||||
}
|
||||
|
||||
Zparam::Zparam(const Sparam &S, Type Z0)
|
||||
: Zparam(S, std::vector<Type>(S.ports(), Z0))
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Type = std::complex<double>;
|
|||
|
||||
class Parameters : public Savable {
|
||||
public:
|
||||
Parameters(Type m11);
|
||||
Parameters(Type m11, Type m12, Type m21, Type m22);
|
||||
Parameters(int num_ports);
|
||||
Parameters() : Parameters(2){}
|
||||
|
|
@ -29,6 +30,7 @@ public:
|
|||
|
||||
// forward declaration of parameter classes
|
||||
class Sparam;
|
||||
class Zparam;
|
||||
class Tparam;
|
||||
class ABCDparam;
|
||||
|
||||
|
|
@ -38,6 +40,8 @@ public:
|
|||
Sparam(const Tparam &t);
|
||||
Sparam(const ABCDparam &a, Type Z01, Type Z02);
|
||||
Sparam(const ABCDparam &a, Type Z0);
|
||||
Sparam(const Zparam &Z, std::vector<Type> Z0n);
|
||||
Sparam(const Zparam &Z, Type Z0);
|
||||
Sparam operator+(const Sparam &r) const {
|
||||
Sparam p(ports());
|
||||
p.data = data+r.data;
|
||||
|
|
@ -49,6 +53,19 @@ public:
|
|||
return p;
|
||||
}
|
||||
void swapPorts(unsigned int p1, unsigned int p2);
|
||||
// reduces the S parameter matrix to specified ports.
|
||||
// Example: 4 port S parameters as an input but we want the 2 port data from the original ports 1 and 3
|
||||
// Call: S.reduceTo(1, 3)
|
||||
// Result: 2 port S parameters (S11, S12, S21, S22) which are set to the original (S11, S13, S31, S33)
|
||||
Sparam reduceTo(std::vector<unsigned int> ports) const;
|
||||
};
|
||||
|
||||
class Zparam : public Parameters {
|
||||
public:
|
||||
using Parameters::Parameters;
|
||||
Zparam(int num_ports) : Parameters(num_ports){}
|
||||
Zparam(const Sparam &S, std::vector<Type> Z0n);
|
||||
Zparam(const Sparam &S, Type Z0);
|
||||
};
|
||||
|
||||
class ABCDparam : public Parameters {
|
||||
|
|
@ -66,7 +83,7 @@ public:
|
|||
ABCDparam inverse() {
|
||||
ABCDparam i;
|
||||
// by hand, this is faster because the Eigen matrix is using dynamic size
|
||||
Type det = data(0,0)*data(1,1) - data(0,1)*data(2,1);
|
||||
Type det = data(0,0)*data(1,1) - data(0,1)*data(1,0);
|
||||
i.data(0,0) = data(1,1) / det;
|
||||
i.data(0,1) = -data(0,1) / det;
|
||||
i.data(1,0) = -data(1,0) / det;
|
||||
|
|
@ -87,7 +104,7 @@ public:
|
|||
ABCDparam r = *this;
|
||||
r.data(0,0) += s;
|
||||
r.data(1,1) += s;
|
||||
r = r * (1.0/t);
|
||||
r.data = r.data * (1.0/t);
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
|
@ -109,7 +126,7 @@ public:
|
|||
}
|
||||
Tparam inverse() {
|
||||
Tparam i;
|
||||
Type det = data(0,0)*data(1,1) - data(0,1)*data(2,1);
|
||||
Type det = data(0,0)*data(1,1) - data(0,1)*data(1,0);
|
||||
i.data(0,0) = data(1,1) / det;
|
||||
i.data(0,1) = -data(0,1) / det;
|
||||
i.data(1,0) = -data(1,0) / det;
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ void Deembedding::measurementCompleted()
|
|||
measuringOption = nullptr;
|
||||
}
|
||||
|
||||
delete measurementDialog;
|
||||
measurementDialog->close();
|
||||
measurementDialog->deleteLater();
|
||||
measurementDialog = nullptr;
|
||||
measurementUI = nullptr;
|
||||
}
|
||||
|
|
@ -37,7 +38,7 @@ void Deembedding::startMeasurementDialog(DeembeddingOption *option)
|
|||
auto ui = new Ui_DeembeddingMeasurementDialog;
|
||||
measurementUI = ui;
|
||||
ui->setupUi(measurementDialog);
|
||||
connect(measurementDialog, &QDialog::finished, [=](){
|
||||
connect(measurementDialog, &QDialog::finished, this, [=](){
|
||||
if(measuring) {
|
||||
measuring = false;
|
||||
emit finishedMeasurement();
|
||||
|
|
@ -53,7 +54,7 @@ void Deembedding::startMeasurementDialog(DeembeddingOption *option)
|
|||
|
||||
connect(traceChooser, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);
|
||||
|
||||
connect(ui->bMeasure, &QPushButton::clicked, [=](){
|
||||
connect(ui->bMeasure, &QPushButton::clicked, this, [=](){
|
||||
ui->bMeasure->setEnabled(false);
|
||||
traceChooser->setEnabled(false);
|
||||
ui->buttonBox->setEnabled(false);
|
||||
|
|
@ -61,7 +62,7 @@ void Deembedding::startMeasurementDialog(DeembeddingOption *option)
|
|||
emit triggerMeasurement();
|
||||
});
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, [=](){
|
||||
// create datapoints from individual traces
|
||||
measurements.clear();
|
||||
auto points = Trace::assembleDatapoints(traceChooser->getTraces());
|
||||
|
|
|
|||
|
|
@ -37,36 +37,10 @@ std::set<unsigned int> ImpedanceRenormalization::getAffectedPorts()
|
|||
|
||||
void ImpedanceRenormalization::transformDatapoint(DeviceDriver::VNAMeasurement &p)
|
||||
{
|
||||
std::map<QString, std::complex<double>> transformed;
|
||||
int ports = 0;
|
||||
QString name = "S11";
|
||||
while(p.measurements.count(name) > 0) {
|
||||
ports++;
|
||||
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] = 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);
|
||||
auto S22name = "S"+QString::number(j)+QString::number(j);
|
||||
if(!p.measurements.count(S12name) || !p.measurements.count(S21name) || !p.measurements.count(S22name)) {
|
||||
// not all measurements available, skip this
|
||||
continue;
|
||||
}
|
||||
auto S12 = p.measurements[S12name];
|
||||
auto S21 = p.measurements[S21name];
|
||||
auto S22 = p.measurements[S22name];
|
||||
auto S_t = Sparam(ABCDparam(Sparam(S11, S12, S21, S22), p.Z0), impedance);
|
||||
transformed[S12name] = S_t.get(1,2);
|
||||
transformed[S21name] = S_t.get(2,1);
|
||||
}
|
||||
}
|
||||
p.measurements = transformed;
|
||||
auto S = p.toSparam();
|
||||
auto Z = Zparam(S, p.Z0);
|
||||
auto S_renorm = Sparam(Z, impedance);
|
||||
p.fromSparam(S_renorm);
|
||||
p.Z0 = impedance;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ void MatchingNetwork::transformDatapoint(DeviceDriver::VNAMeasurement &p)
|
|||
// handle the measurements
|
||||
for(auto &meas : p.measurements) {
|
||||
QString name = meas.first;
|
||||
if(!name.startsWith("S")) {
|
||||
continue;
|
||||
}
|
||||
unsigned int i = name.mid(1,1).toUInt();
|
||||
unsigned int j = name.mid(2,1).toUInt();
|
||||
if(i == j) {
|
||||
|
|
@ -126,9 +129,9 @@ void MatchingNetwork::transformDatapoint(DeviceDriver::VNAMeasurement &p)
|
|||
} else {
|
||||
// another reflection measurement
|
||||
try {
|
||||
auto S = uncorrected.toSparam(i, port);
|
||||
auto S = uncorrected.toSparam().reduceTo({i, port});
|
||||
auto corrected = Sparam(ABCDparam(S, p.Z0) * m.reverse, p.Z0);
|
||||
p.fromSparam(corrected, i, port);
|
||||
p.fromSparam(corrected, {i, port});
|
||||
} catch (...) {
|
||||
// missing measurements, nothing can be done
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ void TwoThru::transformDatapoint(DeviceDriver::VNAMeasurement &p)
|
|||
{
|
||||
// correct measurement
|
||||
if(points.size() > 0) {
|
||||
Tparam meas(p.toSparam(port1,port2));
|
||||
Tparam meas(p.toSparam().reduceTo({port1, port2}));
|
||||
|
||||
Tparam inv1, inv2;
|
||||
if(p.frequency < points.front().freq) {
|
||||
|
|
@ -59,7 +59,7 @@ void TwoThru::transformDatapoint(DeviceDriver::VNAMeasurement &p)
|
|||
// perform correction
|
||||
Tparam corrected = inv1*meas*inv2;
|
||||
// transform back into S parameters
|
||||
p.fromSparam(Sparam(corrected), port1, port2);
|
||||
p.fromSparam(Sparam(corrected), {port1, port2});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +280,7 @@ std::vector<TwoThru::Point> TwoThru::calculateErrorBoxes(std::vector<DeviceDrive
|
|||
// ignore possible DC point
|
||||
continue;
|
||||
}
|
||||
auto S = m.toSparam(port1, port2);
|
||||
auto S = m.toSparam().reduceTo({port1, port2});
|
||||
S11.push_back(S.get(1,1));
|
||||
S12.push_back(S.get(1,2));
|
||||
S21.push_back(S.get(2,1));
|
||||
|
|
@ -466,13 +466,13 @@ std::vector<TwoThru::Point> TwoThru::calculateErrorBoxes(std::vector<DeviceDrive
|
|||
vector<Sparam> p;
|
||||
vector<double> f;
|
||||
for(auto d : data_2xthru) {
|
||||
p.push_back(d.toSparam(1, 2));
|
||||
p.push_back(d.toSparam().reduceTo({port1, port2}));
|
||||
f.push_back(d.frequency);
|
||||
}
|
||||
auto data_2xthru_Sparam = p;
|
||||
vector<Sparam> data_fix_dut_fix_Sparam;
|
||||
for(auto d : data_fix_dut_fix) {
|
||||
data_fix_dut_fix_Sparam.push_back(d.toSparam(1, 2));
|
||||
data_fix_dut_fix_Sparam.push_back(d.toSparam().reduceTo({port1, port2}));
|
||||
}
|
||||
|
||||
// grabbing S21
|
||||
|
|
|
|||
|
|
@ -61,3 +61,109 @@ void ParameterTests::ABCD2S()
|
|||
QVERIFY(qFuzzyCompare(s.get(2,2).real(), S22.real()));
|
||||
QVERIFY(qFuzzyCompare(s.get(2,2).imag(), S22.imag()));
|
||||
}
|
||||
|
||||
void ParameterTests::S2Z_1P()
|
||||
{
|
||||
using namespace std::complex_literals;
|
||||
|
||||
std::complex<double> S11 = 0.0038 + 0.0248i;
|
||||
auto S = Sparam(S11);
|
||||
|
||||
// test for various characteristic impedances
|
||||
for(auto Z0 = 10.0; Z0 <= 100.0; Z0 += 10.0) {
|
||||
auto Z = Zparam(S, Z0);
|
||||
|
||||
// calculate expected Z values based on two-port formulas
|
||||
auto Z11 = (1.0+S11) / (1.0-S11) * Z0;
|
||||
|
||||
// error due to floating point calculations are too big for qFuzzyCompare(double, double), use qFuzzyCompare(float, float) instead
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,1).real(), (float)Z11.real()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,1).imag(), (float)Z11.imag()));
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterTests::S2Z_2P()
|
||||
{
|
||||
using namespace std::complex_literals;
|
||||
|
||||
std::complex<double> S11 = 0.0038 + 0.0248i;
|
||||
std::complex<double> S12 = 0.9961 - 0.0250i;
|
||||
std::complex<double> S21 = 0.9964 - 0.0254i;
|
||||
std::complex<double> S22 = 0.0037 + 0.0249i;
|
||||
auto S = Sparam(S11, S12, S21, S22);
|
||||
|
||||
// test for various characteristic impedances
|
||||
for(auto Z0 = 10.0; Z0 <= 100.0; Z0 += 10.0) {
|
||||
auto Z = Zparam(S, Z0);
|
||||
|
||||
// calculate expected Z values based on two-port formulas
|
||||
auto deltaS = (1.0-S.get(1,1))*(1.0-S.get(2,2))-S.get(1,2)*S.get(2,1);
|
||||
auto Z11 = ((1.0+S.get(1,1))*(1.0-S.get(2,2))+S.get(1,2)*S.get(2,1))/deltaS*Z0;
|
||||
auto Z12 = 2.0*S.get(1,2)/deltaS*Z0;
|
||||
auto Z21 = 2.0*S.get(2,1)/deltaS*Z0;
|
||||
auto Z22 = ((1.0-S.get(1,1))*(1.0+S.get(2,2))+S.get(1,2)*S.get(2,1))/deltaS*Z0;
|
||||
|
||||
// error due to floating point calculations are too big for qFuzzyCompare(double, double), use qFuzzyCompare(float, float) instead
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,1).real(), (float)Z11.real()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,1).imag(), (float)Z11.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,2).real(), (float)Z12.real()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(1,2).imag(), (float)Z12.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(2,1).real(), (float)Z21.real()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(2,1).imag(), (float)Z21.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(2,2).real(), (float)Z22.real()));
|
||||
QVERIFY(qFuzzyCompare((float)Z.get(2,2).imag(), (float)Z22.imag()));
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterTests::Z2S_1P()
|
||||
{
|
||||
using namespace std::complex_literals;
|
||||
|
||||
std::complex<double> Z11 = 0.0038 + 0.0248i;
|
||||
auto Z = Zparam(Z11);
|
||||
|
||||
// test for various characteristic impedances
|
||||
for(auto Z0 = 10.0; Z0 <= 100.0; Z0 += 10.0) {
|
||||
auto S = Sparam(Z, Z0);
|
||||
|
||||
// calculate expected Z values based on two-port formulas
|
||||
auto S11 = (Z11/Z0-1.0) / (Z11/Z0+1.0);
|
||||
|
||||
// error due to floating point calculations are too big for qFuzzyCompare(double, double), use qFuzzyCompare(float, float) instead
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,1).real(), (float)S11.real()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,1).imag(), (float)S11.imag()));
|
||||
}
|
||||
}
|
||||
|
||||
void ParameterTests::Z2S_2P()
|
||||
{
|
||||
using namespace std::complex_literals;
|
||||
|
||||
std::complex<double> Z11 = 0.0038 + 0.0248i;
|
||||
std::complex<double> Z12 = 0.9961 - 0.0250i;
|
||||
std::complex<double> Z21 = 0.9964 - 0.0254i;
|
||||
std::complex<double> Z22 = 0.0037 + 0.0249i;
|
||||
auto Z = Zparam(Z11, Z12, Z21, Z22);
|
||||
|
||||
// test for various characteristic impedances
|
||||
for(auto Z0 = 10.0; Z0 <= 100.0; Z0 += 10.0) {
|
||||
auto S = Sparam(Z, Z0);
|
||||
|
||||
// calculate expected Z values based on two-port formulas
|
||||
auto delta = (Z.get(1,1)+Z0)*(Z.get(2,2)+Z0)-Z.get(1,2)*Z.get(2,1);
|
||||
auto S11 = ((Z.get(1,1)-Z0)*(Z.get(2,2)+Z0)-Z.get(1,2)*Z.get(2,1))/delta;
|
||||
auto S12 = 2.0*Z0*Z.get(1,2)/delta;
|
||||
auto S21 = 2.0*Z0*Z.get(2,1)/delta;
|
||||
auto S22 = ((Z.get(1,1)+Z0)*(Z.get(2,2)-Z0)-Z.get(1,2)*Z.get(2,1))/delta;
|
||||
|
||||
// error due to floating point calculations are too big for qFuzzyCompare(double, double), use qFuzzyCompare(float, float) instead
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,1).real(), (float)S11.real()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,1).imag(), (float)S11.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,2).real(), (float)S12.real()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(1,2).imag(), (float)S12.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(2,1).real(), (float)S21.real()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(2,1).imag(), (float)S21.imag()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(2,2).real(), (float)S22.real()));
|
||||
QVERIFY(qFuzzyCompare((float)S.get(2,2).imag(), (float)S22.imag()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ public:
|
|||
private slots:
|
||||
void S2ABCD();
|
||||
void ABCD2S();
|
||||
void S2Z_1P();
|
||||
void S2Z_2P();
|
||||
void Z2S_1P();
|
||||
void Z2S_2P();
|
||||
};
|
||||
|
||||
#endif // PARAMETERTESTS_H
|
||||
|
|
|
|||
Loading…
Reference in a new issue