From 9982fff01c2069a4a7c344220704076ba46ec32f Mon Sep 17 00:00:00 2001 From: Vadim Kuznetsov Date: Mon, 25 Mar 2024 10:21:59 +0300 Subject: [PATCH 1/2] Backport matching tool from Qucs-0.0.20 --- qucs/dialogs/matchdialog.cpp | 2108 +++++++++++++++++++++++++++------- qucs/dialogs/matchdialog.h | 149 ++- qucs/misc.cpp | 4 +- qucs/misc.h | 2 +- 4 files changed, 1801 insertions(+), 462 deletions(-) diff --git a/qucs/dialogs/matchdialog.cpp b/qucs/dialogs/matchdialog.cpp index d7b357e17..59a85ea73 100644 --- a/qucs/dialogs/matchdialog.cpp +++ b/qucs/dialogs/matchdialog.cpp @@ -5,6 +5,16 @@ copyright : (C) 2005 by Michael Margraf email : michael.margraf@alumni.tu-berlin.de copyright : (C) 2012, 2013, 2016 by Qucs Team (see AUTHORS file) + +----------------------------------------------------------------------------- + Update (2017) : New impedance matching techniques: Single matching, + double stub matching, real to real broadband transformers, + cascaded L-sections and lambda/8 + lambda/4 matching + + Andres Martinez-Mera + Claudio Girardi +------------------------------------------------------------------------------ + ***************************************************************************/ /*************************************************************************** @@ -17,69 +27,232 @@ ***************************************************************************/ #ifdef HAVE_CONFIG_H -# include +#include #endif -#include "matchdialog.h" +#include "../../qucs-filter/material_props.h" #include "main.h" -#include "qucs.h" -#include "element.h" -#include "../components/capacitor.h" -#include "../components/inductor.h" -#include "../components/ground.h" +#include "matchdialog.h" #include "misc.h" +#include "qucs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include +#include #include +#include +#include +#include +#include +#include +#include +#include -#include - - -MatchDialog::MatchDialog(QWidget *parent) - : QDialog(parent) -{ +MatchDialog::MatchDialog(QWidget *parent) : QDialog(parent) { setWindowTitle(tr("Create Matching Circuit")); DoubleVal = new QDoubleValidator(this); DoubleVal->setLocale(QLocale::C); - all = new QVBoxLayout(this); - - TwoCheck = new QCheckBox(tr("calculate two-port matching")); - all->addWidget(TwoCheck); + all = new QHBoxLayout(this); +// all->setSizeConstraint(QLayout::SetFixedSize); + + /* The main frame was divided into two vertical layouts. The first one, on the + left side, is much the old matching tool whereas the other layout was + included specifically for microstrip synthesis. + */ + QVBoxLayout *matchFrame = new QVBoxLayout(); // Matching circuit design panel + QVBoxLayout *micro_layout = new QVBoxLayout(); // Substrate properties + all->addLayout(matchFrame); + all->addLayout(micro_layout); + + MethodBox = new QGroupBox(tr("Implementation")); + matchFrame->addWidget(MethodBox); + MethodLayout = new QVBoxLayout(); + + SubstrateBox = new QGroupBox(tr("Microstrip Substrate")); + QGridLayout *hsubs = new QGridLayout(); + + SubstrateBox->setVisible(false); + micro_layout->addWidget(SubstrateBox); + SubstrateBox->setLayout(hsubs); + RelPermLabel = new QLabel(tr("Relative Permitivity")); + RelPermCombo = new QComboBox(); + RelPermCombo->setEditable(true); + const char **p = List_er; + while (*(++p)) + RelPermCombo->addItem(*p); // The dielectric coeff combobox is filled with + // the materials taken from + // "../../qucs-filter/material_props.h" + RelPermCombo->lineEdit()->setText("9.8"); + hsubs->addWidget(RelPermLabel, 0, 0); + hsubs->addWidget(RelPermCombo, 0, 1, 1, 2); + + subsHLabel = new QLabel(tr("Substrate height")); + SubHeightEdit = new QLineEdit("1.0"); + SubsHScale = new QLabel("mm"); + hsubs->addWidget(subsHLabel, 1, 0); + hsubs->addWidget(SubHeightEdit, 1, 1); + hsubs->addWidget(SubsHScale, 1, 2); + + // Thickness + thicknessLabel = new QLabel(tr("Metal thickness")); + thicknessEdit = new QLineEdit("12.5"); + ThicknessScale = new QLabel("um"); + hsubs->addWidget(thicknessLabel, 2, 0); + hsubs->addWidget(thicknessEdit, 2, 1); + hsubs->addWidget(ThicknessScale, 2, 2); + + // Minimum width + minWLabel = new QLabel(tr("Minimum width")); + minWEdit = new QLineEdit("0.4"); + minWScale = new QLabel("mm"); + hsubs->addWidget(minWLabel, 3, 0); + hsubs->addWidget(minWEdit, 3, 1); + hsubs->addWidget(minWScale, 3, 2); + + // Maximum width + maxWLabel = new QLabel(tr("Maximum width")); + maxWEdit = new QLineEdit("5.0"); + maxWScale = new QLabel("mm"); + hsubs->addWidget(maxWLabel, 4, 0); + hsubs->addWidget(maxWEdit, 4, 1); + hsubs->addWidget(maxWScale, 4, 2); + + // tan(delta) + tanDLabel = new QLabel(tr("tanD")); + tanDEdit = new QLineEdit("0.0125"); + hsubs->addWidget(tanDLabel, 5, 0); + hsubs->addWidget(tanDEdit, 5, 1); + + // Resistivity + ResistivityLabel = new QLabel(tr("Resistivity")); + ResistivityEdit = new QLineEdit("2.43902e-08"); + hsubs->addWidget(ResistivityLabel, 6, 0); + hsubs->addWidget(ResistivityEdit, 6, 1); + + QHBoxLayout *h4 = new QHBoxLayout(); + h4->setSpacing(3); + TopoLabel = new QLabel(tr("Method")); + h4->addWidget(TopoLabel); + + // Matching network topology + TopoCombo = new QComboBox(); + TopoCombo->setFixedWidth(220); + TopoCombo->addItem(tr("L-section")); + TopoCombo->addItem(tr("Single stub")); + TopoCombo->addItem(tr("Double stub")); + QString str = tr("Multistage ") + QString(QChar(0xBB, 0x03)) + "/4"; + TopoCombo->addItem(str); + TopoCombo->addItem("Cascaded L-sections"); + str = QString(QChar(0xBB, 0x03)) + "/8 +" + QString(QChar(0xBB, 0x03)) + + "/4 transformer"; + TopoCombo->addItem(str); + + h4->addWidget(TopoCombo); + connect(TopoCombo, SIGNAL(activated(int)), SLOT(slotChangeMode_TopoCombo())); + h4->addStretch(5); + MethodLayout->addLayout(h4); + + // When the stub implementation is selected, it is possible to select either + // the open or the short circuit solution + QHBoxLayout *h5 = new QHBoxLayout(); + OpenRadioButton = new QRadioButton(tr("Open stub"), this); + h5->addWidget(OpenRadioButton); + OpenRadioButton->setChecked(true); + OpenRadioButton->setVisible(false); + + ShortRadioButton = new QRadioButton(tr("Short circuit stub"), this); + h5->addWidget(ShortRadioButton); + ShortRadioButton->setChecked(false); + ShortRadioButton->setVisible(false); + MethodLayout->addLayout(h5); + + // Number of sections of the cascaded lambda/4 implementation + QHBoxLayout *h6 = new QHBoxLayout(); + OrderLabel = new QLabel(tr("Number of sections")); + OrderEdit = new QLineEdit("3"); + h6->addWidget(OrderLabel); + h6->addWidget(OrderEdit); + OrderLabel->setVisible(false); + OrderEdit->setVisible(false); + OrderEdit->setFixedWidth(50); + OrderEdit->setAlignment(Qt::AlignLeft); + h6->setAlignment(Qt::AlignLeft); + + // Weighting: Binomial or Chebyshev + QHBoxLayout *h7 = new QHBoxLayout(); + Weighting_groupBox = new QGroupBox(tr("Weighting")); + QHBoxLayout *h7_box = new QHBoxLayout(); + QVBoxLayout *v7_box = new QVBoxLayout(); + BinRadio = new QRadioButton(tr("Binomial"), this); + ChebyRadio = new QRadioButton(tr("Chebyshev"), this); + BinRadio->setChecked(true); + h7_box->addWidget(BinRadio); + h7_box->addWidget(ChebyRadio); + + // Maximum ripple + QHBoxLayout *h8 = new QHBoxLayout(); + maxRippleLabel = new QLabel(tr("Maximum ripple")); + h8->addWidget(maxRippleLabel); + MaxRippleEdit = new QLineEdit("0.05"); + h8->addWidget(MaxRippleEdit); + MaxRippleEdit->setFixedWidth(50); + MaxRippleEdit->setAlignment(Qt::AlignLeft); + h8->setAlignment(Qt::AlignLeft); + + QGridLayout *OptLayout = new QGridLayout(); + v7_box->addLayout(h7_box); + v7_box->addLayout(h8); + Weighting_groupBox->setLayout(v7_box); + Weighting_groupBox->setVisible(false); + h7->addWidget(Weighting_groupBox); + MethodLayout->addLayout(h7); + MethodLayout->addLayout(h6); + MethodBox->setLayout(MethodLayout); + BalancedCheck = new QCheckBox(tr("Use balanced stubs")); + BalancedCheck->setEnabled(false); + TwoCheck = new QCheckBox(tr("Calculate two-port matching")); TwoCheck->setChecked(true); + AddSPBlock = + new QCheckBox(tr("Add S-Parameter simulation")); // The user can choose + // whether add a S-param + // simulation or not + AddSPBlock->setChecked(false); + MicrostripCheck = new QCheckBox(tr("Synthesize microstrip lines")); + MicrostripCheck->setEnabled(false); + MicrostripCheck->setChecked(false); + + OptLayout->addWidget(BalancedCheck, 0, 0); + OptLayout->addWidget(TwoCheck, 0, 1); + OptLayout->addWidget(AddSPBlock, 1, 0); + OptLayout->addWidget(MicrostripCheck, 1, 1); + matchFrame->addLayout(OptLayout); + connect(TwoCheck, SIGNAL(toggled(bool)), SLOT(slotSetTwoPort(bool))); + connect(MicrostripCheck, SIGNAL(toggled(bool)), + SLOT(slotSetMicrostripCheck())); + connect(ChebyRadio, SIGNAL(toggled(bool)), SLOT(slotChebyCheck())); // ........................................................... QGroupBox *ImpBox = new QGroupBox(tr("Reference Impedance")); - all->addWidget(ImpBox); + matchFrame->addWidget(ImpBox); QHBoxLayout *ImpLayout = new QHBoxLayout(); Port1Label = new QLabel(tr("Port 1")); Ref1Edit = new QLineEdit("50"); - Ref1Edit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + Ref1Edit->setMaximumWidth(75); Ref1Edit->setValidator(DoubleVal); - Ohm1Label = new QLabel(tr("ohms")); - connect(Ref1Edit, SIGNAL(textChanged(const QString&)), - SLOT(slotImpedanceChanged(const QString&))); + Ohm1Label = new QLabel(QString(QChar(0xA9, 0x03))); + connect(Ref1Edit, SIGNAL(textChanged(const QString &)), + SLOT(slotImpedanceChanged(const QString &))); Port2Label = new QLabel(tr("Port 2")); Ref2Edit = new QLineEdit("50"); - Ref2Edit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + Ref2Edit->setMaximumWidth(75); Ref2Edit->setValidator(DoubleVal); - Ohm2Label = new QLabel(tr("ohms")); + Ohm2Label = new QLabel(QString(QChar(0xA9, 0x03))); ImpLayout->addWidget(Port1Label); ImpLayout->addWidget(Ref1Edit); ImpLayout->addWidget(Ohm1Label); ImpLayout->addSpacing(50); - ImpLayout->addStretch(); ImpLayout->addWidget(Port2Label); ImpLayout->addWidget(Ref2Edit); ImpLayout->addWidget(Ohm2Label); @@ -87,9 +260,8 @@ MatchDialog::MatchDialog(QWidget *parent) // ........................................................... QGroupBox *SParBox = new QGroupBox(tr("S Parameter")); - all->addWidget(SParBox); + matchFrame->addWidget(SParBox); QVBoxLayout *SParLayout = new QVBoxLayout(); - SParLayout->setSpacing(10); SParBox->setLayout(SParLayout); QHBoxLayout *h1 = new QHBoxLayout(); @@ -98,8 +270,9 @@ MatchDialog::MatchDialog(QWidget *parent) h1->addWidget(FormatLabel); FormatCombo = new QComboBox(); h1->addWidget(FormatCombo); - FormatCombo->insertItem(0, tr("real/imag")); - FormatCombo->insertItem(1, tr("mag/deg")); + FormatCombo->setFixedWidth(140); + FormatCombo->addItem(tr("Real/Imag")); + FormatCombo->addItem(tr("mag/deg")); connect(FormatCombo, SIGNAL(activated(int)), SLOT(slotChangeMode(int))); h1->addStretch(5); SParLayout->addLayout(h1); @@ -108,73 +281,82 @@ MatchDialog::MatchDialog(QWidget *parent) h3->setSpacing(3); QVBoxLayout *VBox1 = new QVBoxLayout(); h3->addLayout(VBox1); - S11Label = new QLabel(tr("S11")); - S21Label = new QLabel(tr("S21")); - VBox1->addWidget(S11Label); - VBox1->addWidget(S21Label); + S11Label = new QLabel(tr("S11")); + S21Label = new QLabel(tr("S21")); + VBox1->addWidget(S11Label); + VBox1->addWidget(S21Label); QVBoxLayout *VBox2 = new QVBoxLayout(); h3->addLayout(VBox2); - S11magEdit = new QLineEdit("0.5"); - S11magEdit->setValidator(DoubleVal); - S21magEdit = new QLineEdit("0.5"); - S21magEdit->setValidator(DoubleVal); - VBox2->addWidget(S11magEdit); - VBox2->addWidget(S21magEdit); + S11magEdit = new QLineEdit("0.5"); + S11magEdit->setValidator(DoubleVal); + S11magEdit->setMaximumWidth(75); + S21magEdit = new QLineEdit("0.5"); + S21magEdit->setValidator(DoubleVal); + S21magEdit->setMaximumWidth(75); + + VBox2->addWidget(S11magEdit); + VBox2->addWidget(S21magEdit); QVBoxLayout *VBox3 = new QVBoxLayout(); h3->addLayout(VBox3); - S11sLabel = new QLabel("+j"); - S21sLabel = new QLabel("+j"); - VBox3->addWidget(S11sLabel); - VBox3->addWidget(S21sLabel); + S11sLabel = new QLabel("+j"); + S21sLabel = new QLabel("+j"); + VBox3->addWidget(S11sLabel); + VBox3->addWidget(S21sLabel); QVBoxLayout *VBox4 = new QVBoxLayout(); h3->addLayout(VBox4); - S11degEdit = new QLineEdit("0"); - S11degEdit->setValidator(DoubleVal); - S21degEdit = new QLineEdit("0"); - S21degEdit->setValidator(DoubleVal); - VBox4->addWidget(S11degEdit); - VBox4->addWidget(S21degEdit); + S11degEdit = new QLineEdit("0"); + S11degEdit->setValidator(DoubleVal); + S11degEdit->setMaximumWidth(75); + S21degEdit = new QLineEdit("0"); + S21degEdit->setValidator(DoubleVal); + S21degEdit->setMaximumWidth(75); + VBox4->addWidget(S11degEdit); + VBox4->addWidget(S21degEdit); QVBoxLayout *VBox5 = new QVBoxLayout(); h3->addLayout(VBox5); - S11uLabel = new QLabel(" "); - S21uLabel = new QLabel(" "); - VBox5->addWidget(S11uLabel); - VBox5->addWidget(S21uLabel); + S11uLabel = new QLabel(" "); + S21uLabel = new QLabel(" "); + VBox5->addWidget(S11uLabel); + VBox5->addWidget(S21uLabel); h3->addStretch(5); QVBoxLayout *VBox6 = new QVBoxLayout(); h3->addLayout(VBox6); - S12Label = new QLabel(tr("S12")); - S22Label = new QLabel(tr("S22")); - VBox6->addWidget(S12Label); - VBox6->addWidget(S22Label); + S12Label = new QLabel(tr("S12")); + S22Label = new QLabel(tr("S22")); + VBox6->addWidget(S12Label); + VBox6->addWidget(S22Label); QVBoxLayout *VBox7 = new QVBoxLayout(); h3->addLayout(VBox7); - S12magEdit = new QLineEdit("0"); - S12magEdit->setValidator(DoubleVal); - S22magEdit = new QLineEdit("0.5"); - S22magEdit->setValidator(DoubleVal); - VBox7->addWidget(S12magEdit); - VBox7->addWidget(S22magEdit); + S12magEdit = new QLineEdit("0"); + S12magEdit->setValidator(DoubleVal); + S12magEdit->setMaximumWidth(75); + S22magEdit = new QLineEdit("0.5"); + S22magEdit->setValidator(DoubleVal); + S22magEdit->setMaximumWidth(75); + VBox7->addWidget(S12magEdit); + VBox7->addWidget(S22magEdit); QVBoxLayout *VBox8 = new QVBoxLayout(); h3->addLayout(VBox8); - S12sLabel = new QLabel("+j"); - S22sLabel = new QLabel("+j"); - VBox8->addWidget(S12sLabel); - VBox8->addWidget(S22sLabel); + S12sLabel = new QLabel("+j"); + S22sLabel = new QLabel("+j"); + VBox8->addWidget(S12sLabel); + VBox8->addWidget(S22sLabel); QVBoxLayout *VBox9 = new QVBoxLayout(); h3->addLayout(VBox9); - S12degEdit = new QLineEdit("0"); - S12degEdit->setValidator(DoubleVal); - S22degEdit = new QLineEdit("0"); - S22degEdit->setValidator(DoubleVal); - VBox9->addWidget(S12degEdit); - VBox9->addWidget(S22degEdit); + S12degEdit = new QLineEdit("0"); + S12degEdit->setMaximumWidth(75); + S12degEdit->setValidator(DoubleVal); + S22degEdit = new QLineEdit("0"); + S22degEdit->setMaximumWidth(75); + S22degEdit->setValidator(DoubleVal); + VBox9->addWidget(S12degEdit); + VBox9->addWidget(S22degEdit); QVBoxLayout *VBox0 = new QVBoxLayout(); h3->addLayout(VBox0); - S12uLabel = new QLabel(" "); - S22uLabel = new QLabel(" "); - VBox0->addWidget(S12uLabel); - VBox0->addWidget(S22uLabel); + S12uLabel = new QLabel(" "); + S22uLabel = new QLabel(" "); + VBox0->addWidget(S12uLabel); + VBox0->addWidget(S22uLabel); SParLayout->addLayout(h3); // set tab order to a more natural mode @@ -186,28 +368,29 @@ MatchDialog::MatchDialog(QWidget *parent) setTabOrder(S21degEdit, S22magEdit); setTabOrder(S22magEdit, S22degEdit); - connect(S21magEdit, SIGNAL(textChanged(const QString&)), - SLOT(slotImpedanceChanged(const QString&))); - connect(S21degEdit, SIGNAL(textChanged(const QString&)), - SLOT(slotImpedanceChanged(const QString&))); - connect(S11magEdit, SIGNAL(textChanged(const QString&)), - SLOT(slotReflexionChanged(const QString&))); - connect(S11degEdit, SIGNAL(textChanged(const QString&)), - SLOT(slotReflexionChanged(const QString&))); - + connect(S21magEdit, SIGNAL(textChanged(const QString &)), + SLOT(slotImpedanceChanged(const QString &))); + connect(S21degEdit, SIGNAL(textChanged(const QString &)), + SLOT(slotImpedanceChanged(const QString &))); + connect(S11magEdit, SIGNAL(textChanged(const QString &)), + SLOT(slotReflexionChanged(const QString &))); + connect(S11degEdit, SIGNAL(textChanged(const QString &)), + SLOT(slotReflexionChanged(const QString &))); QHBoxLayout *h2 = new QHBoxLayout(); h2->setSpacing(3); FrequencyLabel = new QLabel(tr("Frequency:")); FrequencyEdit = new QLineEdit(); FrequencyEdit->setValidator(DoubleVal); + FrequencyEdit->setMaximumWidth(75); h2->addWidget(FrequencyLabel); h2->addWidget(FrequencyEdit); UnitCombo = new QComboBox(); - UnitCombo->insertItem(0, "Hz"); - UnitCombo->insertItem(1, "kHz"); - UnitCombo->insertItem(2, "MHz"); - UnitCombo->insertItem(3, "GHz"); + UnitCombo->addItem("Hz"); + UnitCombo->addItem("kHz"); + UnitCombo->addItem("MHz"); + UnitCombo->addItem("GHz"); + UnitCombo->setFixedWidth(100); h2->addWidget(UnitCombo); h2->addStretch(5); SParLayout->addLayout(h2); @@ -215,7 +398,7 @@ MatchDialog::MatchDialog(QWidget *parent) // ........................................................... QHBoxLayout *h0 = new QHBoxLayout(); h0->setSpacing(5); - all->addLayout(h0); + matchFrame->addLayout(h0); h0->addStretch(5); QPushButton *buttCreate = new QPushButton(tr("Create")); QPushButton *buttCancel = new QPushButton(tr("Cancel")); @@ -224,61 +407,86 @@ MatchDialog::MatchDialog(QWidget *parent) connect(buttCreate, SIGNAL(clicked()), SLOT(slotButtCreate())); connect(buttCancel, SIGNAL(clicked()), SLOT(reject())); - slotReflexionChanged(""); // calculate impedance - setFrequency(1e9); // set 1GHz - resize(520, 100); + slotReflexionChanged(""); // calculate impedance + setFrequency(1e9); // set 1GHz } -MatchDialog::~MatchDialog() -{ +MatchDialog::~MatchDialog() { delete all; delete DoubleVal; } // ----------------------------------------------------------------------- -void MatchDialog::setFrequency(double Freq_) -{ +void MatchDialog::setFrequency(double Freq_) { int Expo = int(log10(Freq_) / 3.0); - if(Expo < 0) Expo = 0; - else if(Expo > 3) Expo = 3; + if (Expo < 0) + Expo = 0; + else if (Expo > 3) + Expo = 3; UnitCombo->setCurrentIndex(Expo); - Freq_ /= pow(10.0, double(3*Expo)); + Freq_ /= pow(10.0, double(3 * Expo)); FrequencyEdit->setText(QString::number(Freq_)); } +//------------------------------------------------------ +// This function sets the visibility of the microstrip synthesis panel. The +// substrate properties are visible when the microstrip implementation is +// selected. +void MatchDialog::slotSetMicrostripCheck() { + if (MicrostripCheck->isChecked()) { + SubstrateBox->setVisible(true); + resize(650, 100); + } else { + SubstrateBox->setVisible(false); + setMaximumSize(500, 100); + } +} + +//--------------------------------------------------------- +// When the Chebyshev weighting is selected, the window show the MaxRipple +// textbox and label so as to let the user to enter this parameter. It makes no +// sense to show it otherwise since when using the binomial weighting, the +// maximum ripple is given by the number of sections +void MatchDialog::slotChebyCheck() { + if (ChebyRadio->isChecked()) { + MaxRippleEdit->setVisible(true); + maxRippleLabel->setVisible(true); + } else { + MaxRippleEdit->setVisible(false); + maxRippleLabel->setVisible(false); + } +} + // Set visibility of LineEdits and Labels associated with two-port matching -void MatchDialog::set2PortWidgetsVisible(bool visible) -{ - S12magEdit->setVisible(visible); - S22magEdit->setVisible(visible); - S12degEdit->setVisible(visible); - S22degEdit->setVisible(visible); - S12Label->setVisible(visible); - S22Label->setVisible(visible); - S12sLabel->setVisible(visible); - S22sLabel->setVisible(visible); - S12degEdit->setVisible(visible); - S22degEdit->setVisible(visible); - S12uLabel->setVisible(visible); - S22uLabel->setVisible(visible); - Port2Label->setVisible(visible); - Ref2Edit->setVisible(visible); - Ohm2Label->setVisible(visible); +void MatchDialog::set2PortWidgetsVisible(bool visible) { + S12magEdit->setVisible(visible); + S22magEdit->setVisible(visible); + S12degEdit->setVisible(visible); + S22degEdit->setVisible(visible); + S12Label->setVisible(visible); + S22Label->setVisible(visible); + S12sLabel->setVisible(visible); + S22sLabel->setVisible(visible); + S12degEdit->setVisible(visible); + S22degEdit->setVisible(visible); + S12uLabel->setVisible(visible); + S22uLabel->setVisible(visible); + Port2Label->setVisible(visible); + Ref2Edit->setVisible(visible); + Ohm2Label->setVisible(visible); } // ----------------------------------------------------------------------- // Is called when the checkbox for two-port matching changes. -void MatchDialog::slotSetTwoPort(bool on) -{ - if(on) { // two-port matching ? +void MatchDialog::slotSetTwoPort(bool on) { + if (on) { // two-port matching ? S11Label->setText(tr("S11")); S21Label->setText(tr("S21")); // restore the previous S21 values setS21LineEdits(tmpS21mag, tmpS21deg); set2PortWidgetsVisible(true); - } - else { + } else { S11Label->setText(tr("Reflexion Coefficient")); - S21Label->setText(tr("Impedance (ohms)")); + S21Label->setText(tr("Impedance (Ohms)")); set2PortWidgetsVisible(false); // save S21 values, as these will be overwritten with the impedance value tmpS21mag = S21magEdit->text().toDouble(); @@ -286,12 +494,52 @@ void MatchDialog::slotSetTwoPort(bool on) slotReflexionChanged(""); // calculate impedance } } +//------------------------------------------------------------------------ +// This function is called when a new topology is selected. According to the +// index selected, it makes visible (or invisible) certain window components. +void MatchDialog::slotChangeMode_TopoCombo() { + if ((TopoCombo->currentIndex() == 1) || + (TopoCombo->currentIndex() == 2)) // Single/Double stub selected + { + ShortRadioButton->setVisible(true); + OpenRadioButton->setVisible(true); + OpenRadioButton->setChecked(true); + BalancedCheck->setEnabled(true); + } else { + ShortRadioButton->setVisible(false); + OpenRadioButton->setVisible(false); + BalancedCheck->setEnabled(false); + } + if ((TopoCombo->currentIndex() != 0) && + (TopoCombo->currentIndex() != 4)) // Obviously, the microstrip + // implementation panel cannot be used + // when LC is selected + { + MicrostripCheck->setEnabled(true); + } else { + MicrostripCheck->setEnabled(false); + } + + if (TopoCombo->currentIndex() == 3) + Weighting_groupBox->setVisible(true); // Cascaded lambda/4 sections selected + else + Weighting_groupBox->setVisible(false); + + if ((TopoCombo->currentIndex() == 4) || + (TopoCombo->currentIndex() == 3)) // Cascaded LC sections selected + { + OrderLabel->setVisible(true); + OrderEdit->setVisible(true); + } else { + OrderLabel->setVisible(false); + OrderEdit->setVisible(false); + } +} // ----------------------------------------------------------------------- // Is called when the combobox changes between mag/deg and real/imag. -void MatchDialog::slotChangeMode(int Index) -{ - if(Index) { // polar ? +void MatchDialog::slotChangeMode(int Index) { + if (Index) { // polar ? S11sLabel->setText("/"); S12sLabel->setText("/"); S21sLabel->setText("/"); @@ -322,8 +570,7 @@ void MatchDialog::slotChangeMode(int Index) Imag = S22degEdit->text().toDouble(); c2p(Real, Imag); setS22LineEdits(Real, Imag); - } - else { // cartesian + } else { // cartesian S11sLabel->setText("+j"); S12sLabel->setText("+j"); S21sLabel->setText("+j"); @@ -333,24 +580,24 @@ void MatchDialog::slotChangeMode(int Index) S21uLabel->setText(" "); S22uLabel->setText(" "); - double Mag = S11magEdit->text().toDouble(); + double Mag = S11magEdit->text().toDouble(); double Phase = S11degEdit->text().toDouble(); p2c(Mag, Phase); setS11LineEdits(Mag, Phase); - Mag = S12magEdit->text().toDouble(); + Mag = S12magEdit->text().toDouble(); Phase = S12degEdit->text().toDouble(); p2c(Mag, Phase); setS12LineEdits(Mag, Phase); - Mag = S21magEdit->text().toDouble(); + Mag = S21magEdit->text().toDouble(); Phase = S21degEdit->text().toDouble(); p2c(Mag, Phase); setS21LineEdits(Mag, Phase); // convert also temp entries for future use p2c(tmpS21mag, tmpS21deg); - Mag = S22magEdit->text().toDouble(); + Mag = S22magEdit->text().toDouble(); Phase = S22degEdit->text().toDouble(); p2c(Mag, Phase); setS22LineEdits(Mag, Phase); @@ -360,15 +607,15 @@ void MatchDialog::slotChangeMode(int Index) // ----------------------------------------------------------------------- // Is called if the user changed the impedance. -> The reflexion // coefficient is calculated. -void MatchDialog::slotImpedanceChanged(const QString&) -{ - if(TwoCheck->isChecked()) return; +void MatchDialog::slotImpedanceChanged(const QString &) { + if (TwoCheck->isChecked()) + return; - double Z0 = Ref1Edit->text().toDouble(); + double Z0 = Ref1Edit->text().toDouble(); double Real = S21magEdit->text().toDouble(); double Imag = S21degEdit->text().toDouble(); - if(FormatCombo->currentIndex()) { // entries in polar format + if (FormatCombo->currentIndex()) { // entries in polar format p2c(Real, Imag); z2r(Real, Imag, Z0); c2p(Real, Imag); @@ -382,15 +629,15 @@ void MatchDialog::slotImpedanceChanged(const QString&) // ----------------------------------------------------------------------- // Is called if the user changed the reflexion coefficient. -> The impedance // is calculated. -void MatchDialog::slotReflexionChanged(const QString&) -{ - if(TwoCheck->isChecked()) return; +void MatchDialog::slotReflexionChanged(const QString &) { + if (TwoCheck->isChecked()) + return; - double Z0 = Ref1Edit->text().toDouble(); + double Z0 = Ref1Edit->text().toDouble(); double Real = S11magEdit->text().toDouble(); double Imag = S11degEdit->text().toDouble(); - if(FormatCombo->currentIndex()) { // entries in polar format + if (FormatCombo->currentIndex()) { // entries in polar format p2c(Real, Imag); r2z(Real, Imag, Z0); c2p(Real, Imag); @@ -400,8 +647,7 @@ void MatchDialog::slotReflexionChanged(const QString&) setS21LineEdits(Real, Imag); } -void MatchDialog::setS11LineEdits(double Real, double Imag) -{ +void MatchDialog::setS11LineEdits(double Real, double Imag) { S11magEdit->blockSignals(true); // do not call slot for "textChanged" S11magEdit->setText(QString::number(Real)); S11magEdit->blockSignals(false); @@ -410,14 +656,12 @@ void MatchDialog::setS11LineEdits(double Real, double Imag) S11degEdit->blockSignals(false); } -void MatchDialog::setS12LineEdits(double Real, double Imag) -{ +void MatchDialog::setS12LineEdits(double Real, double Imag) { S12magEdit->setText(QString::number(Real)); S12degEdit->setText(QString::number(Imag)); } -void MatchDialog::setS21LineEdits(double Real, double Imag) -{ +void MatchDialog::setS21LineEdits(double Real, double Imag) { S21magEdit->blockSignals(true); // do not call slot for "textChanged" S21magEdit->setText(QString::number(Real)); S21magEdit->blockSignals(false); @@ -426,21 +670,20 @@ void MatchDialog::setS21LineEdits(double Real, double Imag) S21degEdit->blockSignals(false); } -void MatchDialog::setS22LineEdits(double Real, double Imag) -{ +void MatchDialog::setS22LineEdits(double Real, double Imag) { S22magEdit->setText(QString::number(Real)); S22degEdit->setText(QString::number(Imag)); } // ----------------------------------------------------------------------- // Is called if the "Create"-button is pressed. -void MatchDialog::slotButtCreate() -{ - double Z1 = Ref1Edit->text().toDouble(); - double Z2 = Ref2Edit->text().toDouble(); +void MatchDialog::slotButtCreate() { + double Z1 = Ref1Edit->text().toDouble(); // Port 1 impedance + double Z2 = Ref2Edit->text().toDouble(); // Port 2 impedance double Freq = FrequencyEdit->text().toDouble() * - pow(10.0, 3.0*double(UnitCombo->currentIndex())); + pow(10.0, 3.0 * UnitCombo->currentIndex()); + // S matrix double S11real = S11magEdit->text().toDouble(); double S11imag = S11degEdit->text().toDouble(); double S12real = S12magEdit->text().toDouble(); @@ -449,82 +692,106 @@ void MatchDialog::slotButtCreate() double S21imag = S21degEdit->text().toDouble(); double S22real = S22magEdit->text().toDouble(); double S22imag = S22degEdit->text().toDouble(); - if(FormatCombo->currentIndex()) { // are they polar ? + if (FormatCombo->currentIndex()) { // are they polar ? p2c(S11real, S11imag); p2c(S12real, S12imag); p2c(S21real, S21imag); p2c(S22real, S22imag); } - if(TwoCheck->isChecked()) { // two-port matching ? - // determinante of S-parameter matrix - double DetReal = S11real*S22real - S11imag*S22imag - - S12real*S21real + S12imag*S21imag; - double DetImag = S11real*S22imag + S11imag*S22real - - S12real*S21imag - S12imag*S21real; + bool BalancedStubs = BalancedCheck->isChecked(); + bool micro_syn = MicrostripCheck->isChecked(); // Microstrip implementation? + bool SP_block = AddSPBlock->isChecked(); // Add S-parameter block? + bool open_short = + OpenRadioButton + ->isChecked(); // Open stub or short circuit stub configuration + int order = OrderEdit->text().toInt() + + 1; // Order of the multisection lambda/4 matching + bool success = true; + double gamma_MAX = + MaxRippleEdit->text() + .toDouble(); // Maximum ripple (Chebyshev weighting only) + + tSubstrate Substrate; + if (micro_syn) // In case microstrip implementation is selected. This reads + // the substrate properties given by the user + { + Substrate.er = RelPermCombo->currentText().section(" ", 0, 0).toDouble(); + Substrate.height = SubHeightEdit->text().toDouble() / 1e3; + Substrate.thickness = thicknessEdit->text().toDouble() / 1e6; + Substrate.tand = tanDEdit->text().toDouble(); + Substrate.resistivity = ResistivityEdit->text().toDouble(); + Substrate.roughness = 0.0; + Substrate.minWidth = minWEdit->text().toDouble() / 1e3; + Substrate.maxWidth = maxWEdit->text().toDouble() / 1e3; + } - if(!MatchDialog::calc2PortMatch(S11real, S11imag, S22real, S22imag, - DetReal, DetImag, Z1, Z2, Freq)) - return; + if (TwoCheck->isChecked()) { // two-port matching ? + // determinant of S-parameter matrix + double DetReal = S11real * S22real - S11imag * S22imag - S12real * S21real + + S12imag * S21imag; + double DetImag = S11real * S22imag + S11imag * S22real - S12real * S21imag - + S12imag * S21real; + success = + calc2PortMatch(S11real, S11imag, S22real, S22imag, DetReal, DetImag, Z1, + Z2, Freq, micro_syn, SP_block, open_short, Substrate, + order, gamma_MAX, BalancedStubs); + } else { + success = calcMatchingCircuit(S11real, S11imag, Z1, Freq, micro_syn, + SP_block, open_short, Substrate, order, + gamma_MAX, BalancedStubs); } - else - if(!calcMatchingCircuit(S11real, S11imag, Z1, Freq)) - return; - QucsMain->slotEditPaste(true); + QucsMain->slotEditPaste(success); accept(); } - // ----------------------------------------------------------------------- // transform real/imag into mag/deg (cartesian to polar) -void MatchDialog::c2p(double& Real, double& Imag) -{ +void MatchDialog::c2p(double &Real, double &Imag) { double Real_ = Real; - Real = sqrt(Real*Real + Imag*Imag); // magnitude - Imag = 180.0/pi * atan2(Imag, Real_); // phase in degree + Real = sqrt(Real * Real + Imag * Imag); // magnitude + Imag = 180.0 / pi * atan2(Imag, Real_); // phase in degree } // ----------------------------------------------------------------------- // transform mag/deg into real/imag (polar to cartesian) -void MatchDialog::p2c(double& Real, double& Imag) -{ +void MatchDialog::p2c(double &Real, double &Imag) { double Real_ = Real; - Real = Real_ * cos(Imag * pi/180.0); // real part - Imag = Real_ * sin(Imag * pi/180.0); // imaginary part + Real = Real_ * cos(Imag * pi / 180.0); // real part + Imag = Real_ * sin(Imag * pi / 180.0); // imaginary part } // ----------------------------------------------------------------------- // transform reflexion coefficient into impedance -void MatchDialog::r2z(double& Real, double& Imag, double Z0) -{ - double tmp = Z0 / ((1.0-Real)*(1.0-Real) + Imag*Imag); - Real = (1.0 - Real*Real - Imag*Imag) * tmp; +void MatchDialog::r2z(double &Real, double &Imag, double Z0) { + double tmp = Z0 / ((1.0 - Real) * (1.0 - Real) + Imag * Imag); + Real = (1.0 - Real * Real - Imag * Imag) * tmp; Imag *= 2.0 * tmp; } // ----------------------------------------------------------------------- // transform impedance into reflexion coefficient -void MatchDialog::z2r(double& Real, double& Imag, double Z0) -{ - double tmp = (Real+Z0)*(Real+Z0) + Imag*Imag; - Real = (Real*Real + Imag*Imag - Z0*Z0) / tmp; +void MatchDialog::z2r(double &Real, double &Imag, double Z0) { + double tmp = (Real + Z0) * (Real + Z0) + Imag * Imag; + Real = (Real * Real + Imag * Imag - Z0 * Z0) / tmp; Imag *= 2.0 * Z0 / tmp; } // ----------------------------------------------------------------------- -// Calculate matching circuit. Returns string like "sp:1nH:5pF" -QString MatchDialog::calcMatching(double r_real, double r_imag, - double Z0, double Freq) -{ +// This function calculates a LC matching section +QString MatchDialog::calcMatchingLC(double r_real, double r_imag, double Z0, + double Freq) { double Zreal = r_real, Zimag = r_imag; r2z(Zreal, Zimag, Z0); if (Zreal < 0.0) { if (Zreal < -1e-13) { - QMessageBox::critical(0, tr("Error"), - tr("Real part of impedance must be greater zero,\nbut is %1 !").arg(Zreal)); - return QString(""); // matching not possible + QMessageBox::critical( + 0, tr("Error"), + tr("Real part of impedance must be greater zero,\nbut is %1 !") + .arg(Zreal)); + return QString(""); // matching not possible } // In high-Q circuits, Zreal often becomes somewhat about -1e-16 @@ -532,287 +799,1278 @@ QString MatchDialog::calcMatching(double r_real, double r_imag, Zreal = 0.0; } - double X1, X2, Omega = 2.0 * pi * Freq; QString Str; - if(r_real < 0) { + if (r_real < 0) { // ................................................... // first serial than parallel component (possible if Zreal <= Z0) Str = "sp"; X1 = sqrt(Zreal * (Z0 - Zreal)); - if (Zimag < 0.0) X1 *= -1.0; // always use shortest matching path + if (Zimag < 0.0) + X1 *= -1.0; // always use shortest matching path X1 -= Zimag; // parallel component - X2 = (Zimag + X1) / (Zreal*Zreal + (Zimag + X1)*(Zimag + X1)); - } - else { + X2 = (Zimag + X1) / (Zreal * Zreal + (Zimag + X1) * (Zimag + X1)); + } else { Str = "ps"; - X1 = Zreal + Zimag*Zimag / Zreal - Z0; + X1 = Zreal + Zimag * Zimag / Zreal - Z0; // ................................................... // first parallel than serial component (possible if X >= 0.0) X1 = sqrt(Z0 * X1); - if (Zimag > 0.0) X1 *= -1.0; // always use shortest matching path + if (Zimag > 0.0) + X1 *= -1.0; // always use shortest matching path // parallel component - X2 = Zimag / (Zreal*Zreal + Zimag*Zimag) + X1 / (Z0*Z0 + X1*X1); + X2 = Zimag / (Zreal * Zreal + Zimag * Zimag) + X1 / (Z0 * Z0 + X1 * X1); } - + // Circuit topology and values + QString laddercode = "", series_element, shunt_element; // serial component - if (X1 < 0.0) // capacitance ? - Str += ':' + misc::num2str(-1.0 / Omega / X1) + 'F'; - else // inductance - Str += ':' + misc::num2str(X1 / Omega) + 'H'; + if (X1 < 0.0) // capacitance ? + series_element = QString("CS:%1;").arg(-1.0 / Omega / X1); + else // inductance + series_element = QString("LS:%1;").arg(X1 / Omega); // parallel component - if (X2 < 0.0) // inductance ? - Str += ':' + misc::num2str(-1.0 / Omega / X2) + 'H'; - else // capacitance - Str += ':' + misc::num2str(X2 / Omega) + 'F'; - - return Str; + if (X2 < 0.0) // inductance ? + shunt_element = QString("LP:%1;").arg(-1.0 / Omega / X2); + else // capacitance + shunt_element = QString("CP:%1;").arg(X2 / Omega); + + (r_real < 0) ? laddercode = shunt_element + series_element + : laddercode = series_element + shunt_element; + return laddercode; } // ----------------------------------------------------------------------- -bool MatchDialog::calcMatchingCircuit(double r_real, double r_imag, - double Z0, double Freq) -{ - QString Schematic = - "\n" - "\n"; - - - QString Str = calcMatching(r_real, r_imag, Z0, Freq); - if(Str.isEmpty()) return false; - - if(Str.section(':', 0,0) == "sp") { - // ................................................... - // first serial than parallel component - - if(Str.section(':', 1,1).right(1) == "F") - Schematic += "\n"; - - if(Str.section(':', 2,2).right(1) == "F") - Schematic += "\n"; - - Schematic += - "\n" - "\n" - "\n" - "<10 10 30 10 \"\" 0 0 0 \"\">\n" - "<30 10 70 10 \"\" 0 0 0 \"\">\n" - "<30 10 30 50 \"\" 0 0 0 \"\">\n"; - } - else { - // ................................................... - // first parallel than serial component - - if(Str.section(':', 1,1).right(1) == "F") - Schematic += "\n"; - - if(Str.section(':', 2,2).right(1) == "F") - Schematic += "\n"; - - Schematic += - "\n" - "\n" - "\n" - "<130 10 130 40 \"\" 0 0 0 \"\">\n" - "<130 10 150 10 \"\" 0 0 0 \"\">\n" - "<80 10 130 10 \"\" 0 0 0 \"\">\n"; - } - - Schematic += QString( - "\n" - "\n" - "\n" - "\n" - " \n" - " \n" - "\n").arg(Z0); +// This function calls the specific matching network function so as to get the +// desired matching topology Returns true if the synthesis worked as expected or +// false if it wasn't +bool MatchDialog::calcMatchingCircuit(double S11real, double S11imag, double Z0, + double Freq, bool micro_syn, + bool SP_Block, bool open_short, + tSubstrate Substrate, int order, + double gamma_MAX, bool BalancedStubs) { + QString laddercode; + switch (TopoCombo->currentIndex()) { + case 0: // LC + laddercode = calcMatchingLC(S11real, S11imag, Z0, + Freq); // It calculates the LC matching circuit. + break; + case 1: // Single stub + laddercode = + calcSingleStub(S11real, S11imag, Z0, Freq, open_short, BalancedStubs); + break; + case 2: // Double stub + laddercode = + calcDoubleStub(S11real, S11imag, Z0, Freq, open_short, BalancedStubs); + break; + case 3: // Quarter wave cascaded sections + (BinRadio->isChecked()) + ? laddercode = calcBinomialLines(S11real, S11imag, Z0, order, Freq) + : laddercode = + calcChebyLines(S11real, S11imag, Z0, gamma_MAX, order, Freq); + break; + case 4: // Cascaded LC sections + laddercode = + calcMatchingCascadedLCSections(S11real, S11imag, Z0, Freq, order - 1); + break; + case 5: // Lambda/8 + Lambda/4 impedance transformer + // Reference: Inder J. Bahl. "Fundamentals of RF and microwave + // transistor amplifiers". John Wiley and Sons. 2009. Pages 159-160 + laddercode = calcMatchingLambda8Lambda4(S11real, S11imag, Z0, Freq); + break; + } - QApplication::clipboard()->setText(Schematic, QClipboard::Clipboard); - return true; + if (laddercode.isEmpty()) + return false; + + double RL = S11real, XL = S11imag; + QString wirestr = ""; + QString componentstr = ""; + QString paintingstr = ""; + int x_pos = 0; + + r2z(RL, XL, Z0); + + if (SP_Block) { + laddercode.append("S2P:Freq;"); + laddercode.prepend(QString("P1:%1;").arg(Z0)); + laddercode.append(QString("ZL:%1#%2;").arg(RL).arg(XL)); + } else // Add tags instead of a S-param simulation + { + laddercode.prepend("LBL:Port 1;"); + laddercode.append("LBL:Port 2;"); + } + SchematicParser(laddercode, x_pos, Freq, Substrate, micro_syn); + return true; // The schematic was successfully created } // ----------------------------------------------------------------------- // Fundamental calculations for concurrent 2-port matching. -QString MatchDialog::calcBiMatch(double S11real, double S11imag, - double S22real, double S22imag, double DetReal, double DetImag, - double Z0, double Freq) -{ - double B = 1.0 + S11real*S11real + S11imag*S11imag - - S22real*S22real - S22imag*S22imag - - DetReal*DetReal - DetImag*DetImag; - double Creal = S11real - S22real*DetReal - S22imag*DetImag; - double Cimag = S22real*DetImag - S11imag - S22imag*DetReal; - double Cmag = 2.0 * (Creal*Creal + Cimag*Cimag); +QString MatchDialog::calcBiMatch(double S11real, double S11imag, double S22real, + double S22imag, double DetReal, double DetImag, + double Z0, double Freq, bool open_short, + double gamma_MAX, int order, + bool BalancedStubs) { + double B = 1.0 + S11real * S11real + S11imag * S11imag - S22real * S22real - + S22imag * S22imag - DetReal * DetReal - DetImag * DetImag; + double Creal = S11real - S22real * DetReal - S22imag * DetImag; + double Cimag = S22real * DetImag - S11imag - S22imag * DetReal; + double Cmag = 2.0 * (Creal * Creal + Cimag * Cimag); Creal /= Cmag; Cimag /= Cmag; - double Rreal = B*B - 2.0*Cmag; + double Rreal = B * B - 2.0 * Cmag; double Rimag; - if(Rreal < 0.0) { + if (Rreal < 0.0) { Rimag = Cimag * B - Creal * sqrt(-Rreal); Rreal = Creal * B + Cimag * sqrt(-Rreal); - } - else { - Rreal = B - sqrt(Rreal); - Rimag = Cimag * Rreal; + } else { + Rreal = B - sqrt(Rreal); + Rimag = Cimag * Rreal; Rreal *= Creal; } - return calcMatching(Rreal, -Rimag, Z0, Freq); + QString laddercode; + + switch (TopoCombo->currentIndex()) // Matches both the input and the output + // port to external sources (typically, 50 + // Ohms) + { + case 0: // LC + laddercode = calcMatchingLC(Rreal, -Rimag, Z0, Freq); + break; + case 1: // Single stub + laddercode = + calcSingleStub(Rreal, -Rimag, Z0, Freq, open_short, BalancedStubs); + break; + case 2: // Double stub + laddercode = + calcDoubleStub(Rreal, -Rimag, Z0, Freq, open_short, BalancedStubs); + break; + case 3: // Quarter wave cascaded sections + (BinRadio->isChecked()) + ? laddercode = calcBinomialLines(Rreal, -Rimag, Z0, order, Freq) + : laddercode = + calcChebyLines(Rreal, -Rimag, Z0, gamma_MAX, order, Freq); + break; + case 4: // Cascaded LC sections + laddercode = calcMatchingCascadedLCSections(Rreal, -Rimag, Z0, Freq, order); + break; + case 5: // Lambda/8 + Lambda/4 transformer + laddercode = calcMatchingLambda8Lambda4(Rreal, -Rimag, Z0, Freq); + break; + } + + return laddercode; } // ----------------------------------------------------------------------- -bool MatchDialog::calc2PortMatch(double S11real, double S11imag, - double S22real, double S22imag, double DetReal, double DetImag, - double Z1, double Z2, double Freq) -{ - QString Input = calcBiMatch(S11real, S11imag, S22real, S22imag, - DetReal, DetImag, Z1, Freq); - if(Input.isEmpty()) return false; +// This function calculates both the input and the output matching networks. In +// this sense, it calls 'calcBiMatch' function to get the input/output +// impedance. Then it synthesize the network according to the user choice +bool MatchDialog::calc2PortMatch(double S11real, double S11imag, double S22real, + double S22imag, double DetReal, double DetImag, + double Z1, double Z2, double Freq, + bool microsyn, bool SP_Block, bool open_short, + tSubstrate Substrate, int order, + double gamma_MAX, bool BalancedStubs) { + // Input port network + // The result is a string which gives the structure of the matching network + QString InputLadderCode = + calcBiMatch(S11real, S11imag, S22real, S22imag, DetReal, DetImag, Z1, + Freq, open_short, gamma_MAX, order, BalancedStubs); + if (InputLadderCode.isEmpty()) + return false; // Synthesis error + + // Output port network + QString OutputLadderCode = + calcBiMatch(S22real, S22imag, S11real, S11imag, DetReal, DetImag, Z2, + Freq, open_short, gamma_MAX, order, BalancedStubs); + if (OutputLadderCode.isEmpty()) + return false; // Synthesis error + else + OutputLadderCode = flipLadderCode(OutputLadderCode); + + if (SP_Block) { + InputLadderCode.prepend(QString("S2P:%1;").arg( + Freq)); // Add the s-param simulation to the circuit code + InputLadderCode.prepend(QString("P1:%1;").arg(Z1)); // Port 1 + OutputLadderCode.append(QString("P2:%1;").arg(Z2)); // Port 2 + } else { // Use labels instead of the S-param simulation + InputLadderCode.prepend(QString("LBL:Port 1;")); // Port 1 + OutputLadderCode.append(QString("LBL:Port 2;")); // Port 2 + } + QString laddercode = InputLadderCode + QString("DEV:0") + OutputLadderCode; + int x_pos = 0; + SchematicParser(laddercode, x_pos, Freq, Substrate, microsyn); + + return true; +} + +// This function flips the ladder structure so as to correctly process the +// output matching network +QString MatchDialog::flipLadderCode(QString laddercode) { + QStringList strlist = laddercode.split(";"), auxlist; + std::reverse(strlist.begin(), strlist.end()); + + QString flipped_laddercode = ""; + // Build the flipped ladder code + for (int i = 0; i < strlist.length(); i++) { + flipped_laddercode += strlist.at(i) + ";"; + } + return flipped_laddercode; +} + +// FUNCTIONS FOR THE MICROSTRIP LINE SYNTHESIS. JUST COPIED FROM THE QUCS-FILTER +// TOOL +///////////////////////////////////////////////////////////////////////////////////////////////// +#define MAX_ERROR 1e-7 +void calcMicrostrip(tSubstrate *substrate, double width, double freq, + double &er_eff, double &zl) { + double a, b; + double h = substrate->height; + double t = substrate->thickness; + double er = substrate->er; + double Wh = width / h; + t /= h; + + // quasi-static models by Hammerstad + + double w1 = Wh; + if (t > 1e-100) { // width correction due to metal thickness? + a = coth(sqrt(6.517 * Wh)); + b = t / pi * log(1.0 + 10.873127 / t / a / a); + w1 += b; + Wh += 0.5 * b * (1.0 + sech(sqrt(er - 1.0))); + } + + // relative effective permittivity + a = Wh * Wh; + b = a * a; + er_eff = -0.564 * pow((er - 0.9) / (er + 3.0), 0.053); + er_eff *= 1.0 + log((b + a / 2704.0) / (b + 0.432)) / 49.0 + + log(1.0 + a * Wh / 5929.741) / 18.7; + er_eff = (er + 1.0) / 2.0 + (er - 1.0) / 2.0 * pow(1.0 + 10.0 / Wh, er_eff); + + // characteristic impedance + zl = 6.0 + 0.2831853 * exp(-pow(30.666 / Wh, 0.7528)); + zl = Z_FIELD / 2.0 / pi * log(zl / Wh + sqrt(1.0 + 4.0 / Wh / Wh)); + + // characteristic impedance (same again for "w1") + a = 6.0 + 0.2831853 * exp(-pow(30.666 / w1, 0.7528)); + a = Z_FIELD / 2.0 / pi * log(a / w1 + sqrt(1.0 + 4.0 / w1 / w1)); + + a /= zl; + zl /= sqrt(er_eff); + er_eff *= a * a; + + // dispersion models by Kirschning + + freq *= h / 1e6; // normalize frequency into GHz*mm + + // relative effective permittivity + a = 0.0363 * exp(-4.6 * Wh) * (1.0 - exp(-pow(freq / 38.7, 4.97))); + a *= 1.0 + 2.751 * (1.0 - exp(-pow(er / 15.916, 8.0))); + a = pow((0.1844 + a) * freq, 1.5763); + a *= 0.27488 + Wh * (0.6315 + 0.525 / pow(1.0 + 0.0157 * freq, 20.0)) - + 0.065683 * exp(-8.7513 * Wh); + a *= 0.33622 * (1.0 - exp(-0.03442 * er)); + double er_freq = er - (er - er_eff) / (1.0 + a); + + // characteristic impedance + a = -0.03891 * pow(er, 1.4); + b = -0.267 * pow(Wh, 7.0); + double R7 = 1.206 - 0.3144 * exp(a) * (1.0 - exp(b)); + + a = 0.016 + pow(0.0514 * er, 4.524); + b = pow(freq / 28.843, 12.0); + a = 5.086 * a * b / (0.3838 + 0.386 * a) / (1.0 + 1.2992 * b); + b = -22.2 * pow(Wh, 1.92); + a *= exp(b); + b = pow(er - 1.0, 6.0); + double R9 = a * b / (1.0 + 10.0 * b); + + a = 4.766 * exp(-3.228 * pow(Wh, 0.641)); // = R3 + a = 1.0 + 1.275 * (1.0 - exp(-0.004625 * a * pow(er, 1.674) * + pow(freq / 18.365, 2.745))); // = R8 + + b = 0.9408 * pow(er_freq, a) - 0.9603; // = R13 + b /= (0.9408 - R9) * pow(er_eff, a) - 0.9603; + R9 = b; // = R13 / R14 + + a = 0.00044 * pow(er, 2.136) + 0.0184; // = R10 + a *= 0.707 * pow(freq / 12.3, 1.097); // = R15 + a = exp(-0.026 * pow(freq, 1.15656) - a); + b = pow(freq / 19.47, 6.0); + b /= 1.0 + 0.0962 * b; // = R11 + b = 1.0 + 0.0503 * er * er * b * (1.0 - exp(-pow(Wh / 15, 6.0))); // = R16 + R7 *= (1.0 - 1.1241 * a / b / (1.0 + 0.00245 * Wh * Wh)); // = R17 + + zl *= pow(R9, R7); + er_eff = er_freq; +} + +// ------------------------------------------------------------------- +// Calculates the width 'width' and the relative effective permittivity 'er_eff' +// of a microstrip line. It uses an iterative search algorithm because +// synthesis equations doesn't exist. +void MatchDialog::getMicrostrip(double Z0, double freq, tSubstrate *substrate, + double &width, double &er_eff) { + int iteration = 0; // iteration counter + double Z0_current, Z0_result, increment; + + width = 1e-3; // start with 1mm + + do { + // compute line parameters + calcMicrostrip(substrate, width, freq, er_eff, Z0_current); + + if (fabs(Z0 - Z0_current) < MAX_ERROR) + break; // wanted value was found + + increment = width / 100.0; + width += increment; + + // compute line parameters + calcMicrostrip(substrate, width, freq, er_eff, Z0_result); + + // Newton iteration: w(n+1) = w(n) - f(w(n))/f'(w(n)) + // with f(w(n)) = Z0_current - Z0 + // and f'(w(n)) = (Z0_result - Z0_current) / increment + width -= (Z0_current - Z0) / (Z0_result - Z0_current) * increment; + if (width <= 0.0) + width = increment; + + iteration++; + } while (iteration < 150); +} +//////////////////////////////////////////////////////////////////////////////////////////////////// + +//-------------------------------------------------------------------------------- +// Calculates a matching network according to the stub+line method +// Reference: 'Microwave Engineering'. David Pozar. John Wiley and Sons. 4th +// Edition. Pg 234-241 +QString MatchDialog::calcSingleStub(double r_real, double r_imag, double Z0, + double Freq, bool open_short, + bool BalancedStubs) { + double t = 0, t1 = 0, t2 = 0; + double dl, dl1, dl2, B; + double B1, B2, d, lstub, ll; + double lambda = SPEED_OF_LIGHT / (Freq); + double RL = r_real, XL = r_imag; + r2z(RL, XL, Z0); + + // Stub+line method formulas + if (RL == Z0) { + t = -XL / (2 * Z0); + (t < 0) ? dl = (pi + atan(t)) / (2 * pi) : dl = (atan(t)) / (2 * pi); + B = (RL * RL * t - (Z0 - XL * t) * (Z0 * t + XL)) / + (Z0 * (RL * RL + (Z0 * t + XL) * (Z0 * t + XL))); + } else { + t1 = (XL + sqrt(((RL / Z0) * fabs((Z0 - RL) * (Z0 - RL) + XL * XL)))) / + (RL - Z0); + (t1 < 0) ? dl1 = (pi + atan(t1)) / (2 * pi) : dl1 = (atan(t1)) / (2 * pi); + B1 = (RL * RL * t1 - (Z0 - XL * t1) * (Z0 * t1 + XL)) / + (Z0 * (RL * RL + (Z0 * t1 + XL) * (Z0 * t1 + XL))); + + t2 = (XL - sqrt((RL * fabs((Z0 - RL) * (Z0 - RL) + XL * XL)) / (Z0))) / + (RL - Z0); + (t2 < 0) ? dl2 = (pi + atan(t2)) / (2 * pi) : dl2 = (atan(t2)) / (2 * pi); + B2 = (RL * RL * t2 - (Z0 - XL * t2) * (Z0 * t2 + XL)) / + (Z0 * (RL * RL + (Z0 * t2 + XL) * (Z0 * t2 + XL))); + } - QString Output = calcBiMatch(S22real, S22imag, S11real, S11imag, - DetReal, DetImag, Z2, Freq); - if(Output.isEmpty()) return false; + if (t != 0) { + d = dl * lambda; + (open_short) ? ll = -(atan(B * Z0)) / (2 * pi) + : ll = (atan(1. / (B * Z0))) / (2 * pi); + if ((open_short) && (ll < 0)) + ll += 0.5; + if ((!open_short) && (ll > 0.5)) + ll -= 0.5; + lstub = ll * lambda; + } - QString Schematic = - "\n" - "\n"; + if (t1 != 0) { + d = dl1 * lambda; + (open_short) ? ll = -(atan(B1 * Z0)) / (2 * pi) + : ll = (atan(1. / (1. * B1 * Z0))) / (2 * pi); + if ((open_short) && (ll < 0)) + ll += 0.5; + if ((!open_short) && (ll > 0.5)) + ll -= 0.5; + lstub = ll * lambda; + } else { + if (t2 != 0) { + d = dl2 * lambda; + (open_short) ? ll = -(atan(B2 * Z0)) / (2 * pi) + : ll = (atan(1. / (1. * B2 * Z0))) / (2 * pi); + if ((open_short) && (ll < 0)) + ll += 0.5; + if ((!open_short) && (ll > 0.5)) + ll -= 0.5; + lstub = ll * lambda; + } + } - // ................................................... - // create input circuit - if(Input.section(':', 0,0) == "sp") { - // first serial than parallel component + if (BalancedStubs) { // Balanced stub implementation + double K; + (open_short) ? K = 0.5 : K = 2; + lstub = (lambda / (2 * pi)) * atan(K * tan((2 * pi * lstub) / lambda)); + if (lstub < 0) + lstub += 0.5 * lambda; + } - if(Input.section(':', 1,1).right(1) == "F") - Schematic += "\n"; + // String code + QString laddercode; + if ((open_short) && (!BalancedStubs)) + laddercode = QString("OL:%1#%2;TL:%1#%3;") + .arg(Z0) + .arg(lstub) + .arg(d); // Line + Open stub + if ((open_short) && (BalancedStubs)) + laddercode = QString("OU:%1#%2;OL:%1#%2;TL:%1#%3;") + .arg(Z0) + .arg(lstub) + .arg(d); // Open circuit balanced stubs + if ((!open_short) && (!BalancedStubs)) + laddercode = QString("SL:%1#%2;TL:%1#%3;") + .arg(Z0) + .arg(lstub) + .arg(d); // Line + Short circuited stub + if ((!open_short) && (BalancedStubs)) + laddercode = QString("SU:%1#%2;SL:%1#%2;TL:%1#%3;") + .arg(Z0) + .arg(lstub) + .arg(d); // Short circuited balanced stubs + + return laddercode; +} - if(Input.section(':', 2,2).right(1) == "F") - Schematic += "\n"; +//-------------------------------------------------------------------------------- +// Calculates a matching network according to the stub+line+stub method +// Reference: 'Microwave Engineering', David Pozar. John Wiley and Sons. 4th +// Edition. Pg 241-245 +QString MatchDialog::calcDoubleStub(double r_real, double r_imag, double Z0, + double Freq, bool open_short, + bool BalancedStubs) { + double RL = r_real, XL = r_imag; + r2z(RL, XL, Z0); + double Y0 = 1. / Z0; + double GL = (1 / ((RL * RL) + (XL * XL))) * RL; + double BL = -(1 / ((RL * RL) + (XL * XL))) * XL; + double lambda = SPEED_OF_LIGHT / Freq; + double beta = (2 * pi) / lambda; + double d = lambda / 8; + double t = tan(beta * d); + double ll1, ll2; + + // Double stub method formulas + if (GL > Y0 * ((1 + t * t) / (2 * t * t))) // Not every load can be match + // using the double stub technique. + { + QString str = QString( + "It is not possible to match this load using the double stub method"); + QMessageBox::warning(0, QObject::trUtf8("Error"), + QObject::trUtf8(str.toUtf8())); + return QString(""); + } - Schematic += "\n"; + // Stubs susceptance + double B11 = -BL + (Y0 + sqrt((1 + t * t) * GL * Y0 - GL * GL * t * t)) / + (t); // 1st solution + // double B12 = -BL + (Y0 - sqrt((1+t*t)*GL*Y0 - GL*GL*t*t))/(t); // 2nd + // solution + + double B21 = + ((Y0 * sqrt((1 + t * t) * GL * Y0 - GL * GL * t * t)) + GL * Y0) / + (GL * t); // 1st solution + // double B22 = ((-Y0*sqrt((1+t*t)*GL*Y0 - GL*GL*t*t)) + GL*Y0)/(GL*t);// 2nd + // solution + + // Open circuit solution + (open_short) ? ll1 = (atan(B11 * Z0)) / (2 * pi) + : ll1 = -(atan(1. / (1. * B11 * Z0))) / (2 * pi); + (open_short) ? ll2 = (atan(B21 * Z0)) / (2 * pi) + : ll2 = -(atan(1. / (1. * B21 * Z0))) / (2 * pi); + + if (ll1 < 0) + ll1 += 0.5; + if (ll2 < 0) + ll2 += 0.5; + if ((!open_short) && (ll1 > 0.5)) + ll1 -= 0.5; + if ((!open_short) && (ll2 > 0.5)) + ll2 -= 0.5; + + double lstub1 = ll1 * lambda, lstub2 = ll2 * lambda; + + if (BalancedStubs) { // Balanced stub implementation + double K; + (open_short) ? K = 0.5 : K = 2; + lstub1 = (lambda / (2 * pi)) * atan(K * tan((2 * pi * lstub1) / lambda)); + lstub2 = (lambda / (2 * pi)) * atan(K * tan((2 * pi * lstub2) / lambda)); + if (lstub1 < 0) + lstub1 += 0.5 * lambda; + if (lstub2 < 0) + lstub2 += 0.5 * lambda; } - else { - // first parallel than serial component - if(Input.section(':', 1,1).right(1) == "F") - Schematic += "\n"; + QString laddercode; + if ((open_short) && (BalancedStubs)) + laddercode = QString("OU:%1#%2;OL:%1#%2;TL:%1#%3;OU:%1#%4;OL:%1#%4;") + .arg(Z0) + .arg(lstub2) + .arg(d) + .arg(lstub1); + if ((open_short) && (!BalancedStubs)) + laddercode = QString("OL:%1#%2;TL:%1#%3;OL:%1#%4;") + .arg(Z0) + .arg(lstub2) + .arg(d) + .arg(lstub1); + if ((!open_short) && (BalancedStubs)) + laddercode = QString("SU:%1#%2;SL:%1#%2;TL:%1#%3;SU:%1#%4;SL:%1#%4;") + .arg(Z0) + .arg(lstub2) + .arg(d) + .arg(lstub1); + if ((!open_short) && (!BalancedStubs)) + laddercode = QString("SL:%1#%2;TL:%1#%3;SL:%1#%4;") + .arg(Z0) + .arg(lstub2) + .arg(d) + .arg(lstub1); + + return laddercode; +} - if(Input.section(':', 2,2).right(1) == "F") - Schematic += "\n"; +// ----------------------------------------------------------------------------------------------- +// Binomial coefficients using the multiplicative formula (more efficient than +// the recursive one) +int BinomialCoeffs(int n, int k) { + double coeff = 1; + for (int i = 1; i <= k; i++) { + coeff *= (n + (1 - i)) / (1.0 * i); + } + return (int)coeff; +} - Schematic += "\n"; +//----------------------------------------------------------------------------------- +// This function calculates a multistage lambda/4 matching using binomial +// weigthing Reference: 'Microwave Engineering'. David Pozar. John Wiley and +// Sons. 4th Edition. Pg 252-256 +QString MatchDialog::calcBinomialLines(double r_real, double r_imag, double Z0, + int order, double Freq) { + double RL = r_real, XL = r_imag; + r2z(RL, XL, Z0); + if (RL == 0) { + QMessageBox::warning( + 0, QObject::tr("Error"), + QObject::tr("The load has not resistive part. It cannot be matched " + "using the quarter wavelength method")); + return NULL; + } + if (XL != 0) { + QMessageBox::warning(0, QObject::tr("Warning"), + QObject::tr("Reactive loads cannot be matched. Only " + "the real part will be matched")); + } + double l4 = SPEED_OF_LIGHT / (4. * Freq); + double Ci, Zi, Zaux = Z0; + QString laddercode; + for (int i = 1; i < order; i++) { + Ci = BinomialCoeffs(order - 1, i - 1); + Zi = exp(log(Zaux) + (Ci / pow(2, order - 1)) * log(RL / Z0)); + Zaux = Zi; + laddercode += QString("TL:%1#%2;").arg(Zi).arg(l4); } + return laddercode; +} +//----------------------------------------------------------------------------------- +// This function calculates a multistage lambda/4 matching using the Chebyshev +// weigthing. Reference 'Microwave Engineering'. David Pozar. John Wiley and +// Sons. 4th Edition. Pg 256-261 +QString MatchDialog::calcChebyLines(double r_real, double r_imag, double Z0, + double gamma, int order, double Freq) { + int N = order - 1; // Number of sections + if (N > 7) // So far, it is only available Chebyshev weighting up to 7 + // sections. Probably, it makes no sense to use a higher number of + // sections because of the losses + { + QMessageBox::warning( + 0, QObject::tr("Error"), + QObject::tr("Chebyshev weighting for N>7 is not available")); + return QString(""); + } + QString laddercode; + double RL = r_real, XL = r_imag; + r2z(RL, XL, + Z0); // Calculation of the load impedance given the reflection coeffient + double sec_theta_m; // = + // cosh((1/(1.*N))*acosh((1/gamma)*fabs((RL-Z0)/(Z0+RL))) + // ); + // double sec_theta_m = cosh((1/(1.*N))*acosh(fabs(log(RL/Z0)/(2*gamma))) ); + (fabs(log(RL / Z0) / (2 * gamma)) < 1) + ? sec_theta_m = 0 + : sec_theta_m = + cosh((1 / (1. * N)) * acosh(fabs(log(RL / Z0) / (2 * gamma)))); + + double w[N]; + + switch (N) // The weights are calculated by equating the reflection coeffient + // formula to the N-th Chebyshev polinomial + { + case 1: + w[0] = sec_theta_m; + break; + case 2: + w[0] = sec_theta_m * sec_theta_m; + w[1] = 2 * (sec_theta_m * sec_theta_m - 1); + break; + case 3: + w[0] = pow(sec_theta_m, 3); + w[1] = 3 * (pow(sec_theta_m, 3) - sec_theta_m); + w[2] = w[1]; + break; + case 4: + w[0] = pow(sec_theta_m, 4); + w[1] = 4 * sec_theta_m * sec_theta_m * (sec_theta_m * sec_theta_m - 1); + w[2] = 2 * (1 - 4 * sec_theta_m * sec_theta_m + 3 * pow(sec_theta_m, 4)); + w[3] = w[1]; + break; + case 5: + w[0] = pow(sec_theta_m, 5); + w[1] = 5 * (pow(sec_theta_m, 5) - pow(sec_theta_m, 3)); + w[2] = + 10 * pow(sec_theta_m, 5) - 15 * pow(sec_theta_m, 3) + 5 * sec_theta_m; + w[3] = w[2]; + w[4] = w[1]; + break; + case 6: + w[0] = pow(sec_theta_m, 6); + w[1] = 6 * pow(sec_theta_m, 4) * (sec_theta_m * sec_theta_m - 1); + w[2] = 15 * pow(sec_theta_m, 6) - 24 * pow(sec_theta_m, 4) + + 9 * sec_theta_m * sec_theta_m; + w[3] = 2 * (10 * pow(sec_theta_m, 6) - 18 * pow(sec_theta_m, 4) + + 9 * sec_theta_m * sec_theta_m - 1); + w[4] = w[2]; + w[5] = w[1]; + break; + case 7: + w[0] = pow(sec_theta_m, 7); + w[1] = 7 * pow(sec_theta_m, 5) * (sec_theta_m * sec_theta_m - 1); + w[2] = 21 * pow(sec_theta_m, 7) - 35 * pow(sec_theta_m, 5) + + 14 * pow(sec_theta_m, 3); + w[3] = 35 * pow(sec_theta_m, 7) - 70 * pow(sec_theta_m, 5) + + 42 * pow(sec_theta_m, 3) - 7 * sec_theta_m; + w[4] = w[3]; + w[5] = w[2]; + w[6] = w[1]; + break; + } + double l4 = SPEED_OF_LIGHT / (4. * Freq); + double Zaux = Z0, Zi; + for (int i = 0; i < N; i++) { + (RL < Z0) ? Zi = exp(log(Zaux) - gamma * w[i]) + : Zi = exp(log(Zaux) + gamma * w[i]); // When RL RS)//The design equations were tailored for the RS > RL case. In case of RL > RS, the ports impedance are swapped + R1 = RL, R2 = RS; + else + R1 = RS, R2 = RL; + + Raux = R1; + for (int i = 0; i < N - 1; i++) { + R = pow(R1, 1. * (N - (i + 1)) / N) * pow(R2, 1. * (i + 1) / N); + Q = sqrt(Raux / R - 1); + C = Q / (w * Raux); + L = Q * R / w; + s += QString("CP:%1;LS:%2;").arg(C).arg(L); + Raux = R; + } - if(Output.section(':', 1,1).right(1) == "F") - Schematic += "\n"; + Q = sqrt(R / R2 - 1); + C = Q / (w * R); + L = Q * R2 / w; + s += QString("CP:%1;LS:%2;").arg(C).arg(L); + + if (RL > RS) // Flip string + { + QString temp = ""; + QStringList strlist = s.split(";"); + for (int i = strlist.count() - 1; i >= 0; i--) + temp += strlist.at(i) + ";"; + s = temp; + } - if(Output.section(':', 2,2).right(1) == "F") - Schematic += "\n"; + return s; +} - Schematic += "\n"; +//-------------------------------------------------------------------------- +// It calculates a lambda/8 + lambda/4 transformer to match a complex load to a +// real source +QString MatchDialog::calcMatchingLambda8Lambda4(double r_real, double r_imag, + double Z0, double Freq) { + double RL = r_real, XL = r_imag; + double l4 = SPEED_OF_LIGHT / (4. * Freq); + double l8 = .5 * l4; + r2z(RL, XL, Z0); + double Zmm = sqrt(RL * RL + XL * XL); + double Zm = sqrt((Z0 * RL * Zmm) / (Zmm - XL)); + return QString("TL:%1#%2;TL:%3#%4;").arg(Zm).arg(l4).arg(Zmm).arg(l8); +} + +// Given a string code of inductors, capacitors and transmission lines, it +// generates the Qucs network. Notice that the schematic is split into three +// parts: components, wires and paintings, all of them are passed by reference. +void MatchDialog::SchematicParser(QString laddercode, int &x_pos, double Freq, + tSubstrate Substrate, bool microsyn) { + QStringList strlist = laddercode.split(";");//Slipt the string code to get the components + QString component, tag, label; + qDebug() << laddercode; + double value, value2, er, width; + int x_series = 120, x_shunt = 20; // x-axis spacing depending on whether the + // component is placed in a series or shunt + // configuration + + // Clear schematic strings + QString componentstr = "", wirestr = "", paintingstr = ""; + + // The string format is as follows: "XX;XX;...XX;" + // where XX, YY, ZZ define the type of component and its configuration. + // LS: Series inductance + // CS: Series capacitor + // LP: Shunt inductance + // CP: Shunt capacitor + // TL: Series transmission line + // OU: Open stub (facing up) + // OL: Open stub (facing down) + // SU: Short circuited stub (facing up) + // SL: Short circuited stub (facing down) + // P1: Port 1 + // LBL: LBL + // P2: Port 2 + // DEV: Device label + // S2P: S-param simulation block + + for (int i = 0; i < strlist.count(); i++) { + // Each token of the string descriptor has the following format: + // 'tag:;''tag:#;' + // First, extract the tag + component = strlist.at(i); + int index_colon = component.indexOf(":"); + tag = component.mid(0, index_colon); + + // Now we remove the tag and the colon from the string + component.remove(0, index_colon + 1); + // At this point, we have the component parameters. In case of having more + // than one parameter, they are separated by # + int index = component.indexOf("#"); + // Transmission lines are characterised by its impedance and its length + // whereas capacitors and inductors only depend on + // its capacitance and inductance, respectively. So, the code below is aimed + // to handle such difference + if (index != -1) // The component has two values + { + value = component.mid(0, index).toDouble(); + value2 = component.mid(index + 1).toDouble(); + } else { + if (!tag.compare("LBL")) // The value is a string + { + label = component; + } else // The value is a double number. For a clearer representation in + // the schematic, it will be shown with a limited precision + { + value = component.toDouble(); + } + } + + // The following if-else structure is responsible for placing the + // components, wires and paintings in the schematic + if (!tag.compare("P1")) // Port 1 component + { + componentstr += QString("\n") + .arg(misc::num2str(value, 3, "Ohm")) // reference impedance + .arg(x_pos); + componentstr += QString("\n").arg(x_pos); + wirestr += QString("<%1 -60 %1 -120>\n").arg(x_pos); + wirestr += QString("<%1 -120 %2 -120>\n").arg(x_pos).arg(x_pos + 120); + x_pos += 120; + } else if (!tag.compare("LBL")) // Label + { + paintingstr += QString("\n") + .arg(x_pos) + .arg(component); // Add 'Port 1' or 'Port 2' label + x_pos += 50; + } else if (!tag.compare("P2")) // Port 2 component (it need to be different + // from P1 because of the wiring) + { + x_pos += 100; + componentstr += QString("\n") + .arg(misc::num2str(value, 3, "Ohm")) // reference impedance + .arg(x_pos); + componentstr += QString("\n").arg(x_pos); + wirestr += QString("<%1 -60 %1 -120>\n").arg(x_pos); // Vertical wire + wirestr += QString("<%1 -120 %2 -120>\n") + .arg(x_pos - 100) + .arg(x_pos); // Horizontal wire + } else if (!tag.compare("DEV")) // Device painting + { + paintingstr += QString("\n") + .arg(x_pos + 70); // Add 'Device' label + paintingstr += + QString("\n") + .arg(x_pos + 50); // Box surrounding the 'Device' label + x_pos += 200; + } else if (!tag.compare("LS")) // Series inductor + { + QString val = misc::num2str(value, 3, "H"); // Add prefix, unit - 3 significant digits + componentstr += QString("\n") + .arg(x_pos + 60) + .arg(val); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos) + .arg(x_pos + 30); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos + 90) + .arg(x_pos + x_series); + x_pos += x_series; + } else if (!tag.compare("CS")) // Series capacitor + { + QString val = misc::num2str(value, 3, "F"); // Add prefix, unit - 3 significant digits + componentstr += QString("\n") + .arg(x_pos + 60) + .arg(val); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos) + .arg(x_pos + 30); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos + 90) + .arg(x_pos + x_series); + x_pos += x_series; + } else if (!tag.compare("LP")) // Shunt inductor + { + QString val = misc::num2str(value, 3, "H"); // Add prefix, unit - 3 significant digits + componentstr += QString("\n").arg(x_pos); + componentstr += QString("\n") + .arg(x_pos) + .arg(val); + wirestr += QString("<%1 -60 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos - 20) + .arg(x_pos + x_shunt); + x_pos += x_shunt; + } else if (!tag.compare("CP")) // Shunt capacitor + { + QString val = misc::num2str(value, 3, "F"); // Add prefix, unit - 3 significant digits + componentstr += QString("\n").arg(x_pos); + componentstr += QString("\n") + .arg(x_pos) + .arg(val); + wirestr += QString("<%1 -60 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos - 20) + .arg(x_pos + x_shunt); + x_pos += x_shunt; + } else if (!tag.compare("TL")) // Transmission line + { + if (microsyn) // Microstrip implementation + { + er = Substrate.er; + getMicrostrip(value, Freq, &Substrate, width, er); + QString val_width = + misc::num2str(width, 3, "m"); // Add prefix, unit - 3 significant digits + QString val_length = + misc::num2str(value2 / sqrt(er), 3, "m"); // Add prefix, unit - 3 significant digits + componentstr += + QString("\n") + .arg(val_width) + .arg(val_length) + .arg(x_pos + 60); + } else { + // Add a prefix (although rarely needed here) and unit - 3 significant digits + QString val_impedance = misc::num2str(value, 3, "Ohm"); + // Add prefix, unit - 3 significant digits + QString val_length = misc::num2str(value2, 3, "m"); + componentstr += QString("\n") + .arg(val_impedance) + .arg(val_length) + .arg(x_pos + 60); + } + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos) + .arg(x_pos + 30); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos + 90) + .arg(x_pos + x_series); + x_pos += x_series; + } else if (!tag.compare("OU")) // Open stub (upper) + { + if (microsyn) // Microstrip implementation + { + er = Substrate.er; + getMicrostrip(value, Freq, &Substrate, width, er); + // Add prefix, unit - 3 significant digits + QString val_width = misc::num2str(width, 3, "m"); + // Add prefix, unit - 3 significant digits + QString val_length = misc::num2str(value2 / sqrt(er), 3, "m"); + componentstr += + QString("\n") + .arg(val_width) + .arg(val_length) + .arg(x_pos); + } else { + // Add a prefix (although rarely needed here) and unit - 3 significant digits + QString val_impedance = misc::num2str(value, 3, "Ohm"); + // Add prefix, unit - 3 significant digits + QString val_length = misc::num2str(value2, 3, "m"); + componentstr += QString("\n") + .arg(val_impedance) + .arg(val_length) + .arg(x_pos); + } + wirestr += QString("<%1 -150 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos) + .arg(x_pos + x_shunt); + // Here x_pos is not incremented since upper stubs does not overlap any + // other component + } else if (!tag.compare("OL")) // Open stub (lower) + { + if (microsyn) // Microstrip implementation + { + er = Substrate.er; + getMicrostrip(value, Freq, &Substrate, width, er); + // Add suffix mm, cm - 3 significant digits + QString val_width = misc::num2str(width, 3, "m"); + // Add suffix mm, cm - 3 significant digits + QString val_length = misc::num2str(value2 / sqrt(er), 3, "m"); + componentstr += + QString("\n") + .arg(val_width) + .arg(val_length) + .arg(x_pos); + } else { + // Add a prefix (although rarely needed here) and unit - 3 significant digits + QString val_impedance = misc::num2str(value, 3, "Ohm"); + // Add suffix mm, cm - 3 significant digits + QString val_length = misc::num2str(value2, 3, "m"); + componentstr += QString("\n") + .arg(val_impedance) + .arg(val_length) + .arg(x_pos); + } + wirestr += QString("<%1 -90 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos - 20) + .arg(x_pos + x_shunt); + x_pos += x_shunt; + } else if (!tag.compare("SU")) // Short circuited stub (upper) + { + if (microsyn) // Microstrip implementation + { + er = Substrate.er; + getMicrostrip(value, Freq, &Substrate, width, er); + QString val_width = misc::num2str(width, 3, "m"); + QString val_length = misc::num2str(value2 / sqrt(er), 3, "m"); + componentstr += + QString("\n") + .arg(val_width) + .arg(val_length) + .arg(x_pos); + } else { + QString val_impedance = misc::num2str(value, 3, "Ohm"); + QString val_length = misc::num2str(value2, 3, "m"); + componentstr += QString("\n") + .arg(val_impedance) + .arg(val_length) + .arg(x_pos); + } + componentstr += QString("\n").arg(x_pos); + wirestr += QString("<%1 -150 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos) + .arg(x_pos + x_shunt); + // Here x_pos is not incremented since upper stubs does not overlap any + // other component + } else if (!tag.compare("SL")) // Short circuited stub (lower) + { + if (microsyn) // Microstrip implementation + { + er = Substrate.er; + getMicrostrip(value, Freq, &Substrate, width, er); + QString val_width = misc::num2str(width, 3, "m"); + QString val_length = misc::num2str(value2 / sqrt(er), 3, "m"); + componentstr += + QString("\n") + .arg(val_width) + .arg(val_length) + .arg(x_pos); + } else { + QString val_impedance = misc::num2str(value, 3, "Ohm"); + QString val_length = misc::num2str(value2, 3, "m"); + componentstr += QString("\n") + .arg(val_impedance) + .arg(val_length) + .arg(x_pos); + } + componentstr += QString("\n").arg(x_pos); + wirestr += QString("<%1 -90 %1 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos); + wirestr += QString("<%1 -120 %2 -120 " + " 0 0 0 " + ">\n") + .arg(x_pos - 20) + .arg(x_pos + x_shunt); + x_pos += x_shunt; + } else if (!tag.compare("S2P")) // S-param simulation block + { + // Add the frequency range for the S-param simulation + // cover 1 octave below and 1 above, user will adjust if needed... + double freq_start = Freq / 2.0; + double freq_stop = 2.0 *Freq; + QString val_freq_start = misc::num2str(freq_start, 3, "Hz"); + QString val_freq_stop = misc::num2str(freq_stop, 3, "Hz"); + + componentstr += + QString("<.SP SP1 1 0 100 0 67 0 0 \"lin\" 1 \"%1\" 1 \"%2\" 1 " + "\"300\" 1 \"no\" 0 \"1\" 0 \"2\" 0>\n") + .arg((val_freq_start)) + .arg((val_freq_stop)); + + if (laddercode.indexOf("P2") == -1) // One port simulation + componentstr += QString("\n"); + else // Two ports simulation + componentstr += QString("\n"); + } else if (!tag.compare("ZL")) // Complex load + { + double RL = value; + double XL = value2; + x_pos += 100; + if ((RL > 1e-3) && (XL < -1e-3)) // R + C + { + QString val_Res = misc::num2str(RL, 3, "Ohm"); + // Need to use abs() because XL < 0 + QString val_Cap = misc::num2str(1 / (fabs(XL) * 2 * pi * Freq), 3, "F"); + componentstr += + QString( + "\n") + .arg(x_pos) + .arg(val_Res); + componentstr += QString("\n") + .arg(x_pos) + .arg(val_Cap); + paintingstr += + QString("\n") + .arg(x_pos) + .arg(QChar(0x2126)) + .arg(misc::num2str(Freq, 3, "Hz")) + .arg(RL) + .arg(fabs(XL)); + } else if ((RL > 1e-3) && (XL > 1e-3)) // R + L + { + QString val_Res = misc::num2str(RL, 3, "Ohm"); + QString val_Ind = misc::num2str(XL / (2 * pi * Freq), 3, "H"); + componentstr += + QString( + "\n") + .arg(x_pos) + .arg(val_Res); + componentstr += QString("\n") + .arg(x_pos) + .arg(val_Ind); + paintingstr += + QString("\n") + .arg(x_pos) + .arg(QChar(0x2126)) + .arg(misc::num2str(Freq, 3, "Hz")) + .arg(RL) + .arg(XL); + } else if ((RL > 1e-3) && (fabs(XL) < 1e-3)) // R + { + QString val_Res = misc::num2str(RL, 3, "Ohm"); + componentstr += + QString( + "\n") + .arg(x_pos) + .arg(val_Res); + wirestr += QString("<%1 -60 %1 -120>\n").arg(x_pos); // Vertical wire + paintingstr += QString("\n") + .arg(x_pos) + .arg(QChar(0x2126)) + .arg(misc::num2str(Freq, 3, "Hz")) + .arg(RL); + } else if ((RL < 1e-3) && (XL > 1e-3)) // L + { + QString val_Ind = misc::num2str(XL / (2 * pi * Freq), 3, "H"); + componentstr += + QString( + "\n") + .arg(x_pos) + .arg(val_Ind); + wirestr += QString("<%1 -60 %1 -120>\n").arg(x_pos); // Vertical wire + paintingstr += + QString("\n") + .arg(x_pos) + .arg(QChar(0x2126)) + .arg(misc::num2str(Freq, 3, "Hz")) + .arg(XL); + } else if ((RL < 1e-3) && (XL < -1e-3)) // C + { + // Need to use abs() because XL < 0 + QString val_Cap = misc::num2str(1 / (fabs(XL) * 2 * pi * Freq), 3, "F"); + componentstr += + QString( + "\n") + .arg(x_pos) + .arg(val_Cap); + wirestr += QString("<%1 -60 %1 -120>\n").arg(x_pos); // Vertical wire + paintingstr += + QString("\n") + .arg(x_pos) + .arg(QChar(0x2126)) + .arg(misc::num2str(Freq, 3, "Hz")) + .arg(fabs(XL)); + } + wirestr += QString("<%1 -120 %2 -120>\n") + .arg(x_pos - 100) + .arg(x_pos); // Horizontal wire + componentstr += QString("\n").arg(x_pos); + + // Box surrounding the load + paintingstr += + QString("\n") + .arg(x_pos - 30); + } } - else { - // ................................................... - // first parallel than serial component - - if(Output.section(':', 1,1).right(1) == "F") - Schematic += "\n"; - - if(Output.section(':', 2,2).right(1) == "F") - Schematic += "\n"; - - Schematic += "\n"; - } - Schematic += "\n" - "\n"; - - - // ................................................... - // create wires for input circuit - if(Input.section(':', 0,0) == "sp") // first serial than parallel component - Schematic += - "<-140 10 -120 10 \"\" 0 0 0 \"\">\n" - "<-120 10 -80 10 \"\" 0 0 0 \"\">\n" - "<-120 10 -120 50 \"\" 0 0 0 \"\">\n"; - else // first parallel than serial component - Schematic += - "<-60 10 -60 40 \"\" 0 0 0 \"\">\n" - "<-60 10 -40 10 \"\" 0 0 0 \"\">\n" - "<-110 10 -60 10 \"\" 0 0 0 \"\">\n"; - - - // ................................................... - // create wires for output circuit - if(Output.section(':', 0,0) == "sp") // first serial than parallel component - Schematic += - "<140 10 120 10 \"\" 0 0 0 \"\">\n" - "<120 10 80 10 \"\" 0 0 0 \"\">\n" - "<120 10 120 50 \"\" 0 0 0 \"\">\n"; - else // first parallel than serial component - Schematic += - "<60 10 60 40 \"\" 0 0 0 \"\">\n" - "<60 10 40 10 \"\" 0 0 0 \"\">\n" - "<110 10 60 10 \"\" 0 0 0 \"\">\n"; - - Schematic += - "\n" - "\n" - "\n" - "\n" - " \n" - " \n" - " \n" - "\n"; + // Substrate + if (microsyn) + componentstr += + QString("\n") + .arg(Substrate.er) + .arg(Substrate.height * 1e3) + .arg(Substrate.thickness * 1e6) + .arg(Substrate.tand) + .arg(Substrate.resistivity) + .arg(Substrate.roughness); + + // Schematic header + QString Schematic = "\n"; + + // Add components + Schematic += "\n"; + Schematic += componentstr; + Schematic += "\n"; + + // Add wires + Schematic += "\n"; + Schematic += wirestr; + Schematic += "\n"; + + // Add paintings + Schematic += "\n"; + Schematic += paintingstr; + Schematic += "\n"; + + //Copy the schematic into clipboard QApplication::clipboard()->setText(Schematic, QClipboard::Clipboard); - return true; } diff --git a/qucs/dialogs/matchdialog.h b/qucs/dialogs/matchdialog.h index c2b7ec577..d748cd04e 100644 --- a/qucs/dialogs/matchdialog.h +++ b/qucs/dialogs/matchdialog.h @@ -4,6 +4,16 @@ begin : Fri Jul 22 2005 copyright : (C) 2005 by Michael Margraf email : michael.margraf@alumni.tu-berlin.de + +----------------------------------------------------------------------------- + Update (2017) : New impedance matching techniques: Single matching, + double stub matching, real to real broadband transformers, + cascaded L-sections and lambda/8 + lambda/4 matching + + Andres Martinez-Mera + Claudio Girardi +------------------------------------------------------------------------------ + ***************************************************************************/ /*************************************************************************** @@ -18,69 +28,138 @@ #ifndef MATCHDIALOG_H #define MATCHDIALOG_H -#include -#include -#include #include +#include +#include +#include class Element; class QLabel; class QLineEdit; class QComboBox; +class QGroupBox; +class QRadioButton; +class QHBoxLayout; class QVBoxLayout; class QDoubleValidator; +struct tSubstrate { + double er; + double height; + double thickness; + double tand; + double resistivity; + double roughness; + double minWidth, maxWidth; +}; + +static const double Z_FIELD = 376.73031346958504364963; +static const double SPEED_OF_LIGHT = 299792458.0; + +/*! coth function */ +static inline double coth(const double x) { + return (1.0 + 2.0 / (exp(2.0 * (x)) - 1.0)); +} + +/*! sech function */ +static inline double sech(const double x) { + return (2.0 / (exp(x) + exp(-(x)))); +} + +/* sec function*/ +static inline double sec(const double x) { return 1 / cos(x); } + +/* sec function*/ +static inline double asec(const double x) { return acos(1 / x); } class MatchDialog : public QDialog { - Q_OBJECT + Q_OBJECT public: - MatchDialog(QWidget *parent=0); - ~MatchDialog(); - - static void c2p(double&, double&); - static void p2c(double&, double&); - static void r2z(double&, double&, double); - static void z2r(double&, double&, double); - static QString calcMatching(double, double, double, double); - static bool calcMatchingCircuit(double, double, double, double); - static QString calcBiMatch(double, double, double, double, double, double, - double, double); - static bool calc2PortMatch(double, double, double, double, double, double, - double, double, double); + MatchDialog(QWidget *parent = 0); + ~MatchDialog(); + + static void c2p(double &, double &); + static void p2c(double &, double &); + static void r2z(double &, double &, double); + static void z2r(double &, double &, double); + + bool calcMatchingCircuit(double, double, double, double, bool, bool, bool, + tSubstrate, int, double, bool); + enum SchematicCode { + SNG_PORT, + SNG_PORT_SPAR_SIM, + DBL_PORT, + DBL_PORT_SPAR_SIM + }; + //------------------------------------------------------------------------------------------------------- + // These functions calculate the specified matching network and and generate + // the circuit description code + QString calcMatchingLC(double, double, double, double); + QString calcMatchingCascadedLCSections(double, double, double, double, int); + QString calcSingleStub(double, double, double, double, bool, bool); + QString calcDoubleStub(double, double, double, double, bool, bool); + QString calcMatchingLambda8Lambda4(double, double, double, double); + QString calcBinomialLines(double, double, double, int, double); + QString calcChebyLines(double, double, double, double, int, double); + //-------------------------------------------------------------------------------------------------------- + + QString calcBiMatch(double, double, double, double, double, double, double, + double, bool, double, int, bool); + bool calc2PortMatch(double, double, double, double, double, double, double, + double, double, bool, bool, bool, tSubstrate, int, double, + bool); + + void SchematicParser(QString, int &, double, tSubstrate, + bool); // This function convert the circuit description + // code into a Qucs schematic + + void getMicrostrip(double, double, tSubstrate *, double &, double &); void setFrequency(double); - void setTwoPortMatch(bool on) { TwoCheck->setChecked(on); TwoCheck->setEnabled(false); } + void setTwoPortMatch(bool on) { + TwoCheck->setChecked(on); + TwoCheck->setEnabled(false); + } public slots: void slotButtCreate(); - void slotImpedanceChanged(const QString&); - void slotReflexionChanged(const QString&); + void slotImpedanceChanged(const QString &); + void slotReflexionChanged(const QString &); void slotSetTwoPort(bool); void slotChangeMode(int); void setS11LineEdits(double, double); void setS12LineEdits(double, double); void setS21LineEdits(double, double); void setS22LineEdits(double, double); + void slotChangeMode_TopoCombo(); + void slotSetMicrostripCheck(); + void slotChebyCheck(); private: - QVBoxLayout *all; // the mother of all widgets - QDoubleValidator *DoubleVal; - QLabel *Port1Label, *Port2Label, *Ohm1Label, *Ohm2Label, - *FormatLabel, *FrequencyLabel, - *S11Label, *S11sLabel, *S11uLabel, - *S21Label, *S21sLabel, *S21uLabel, - *S12Label, *S12sLabel, *S12uLabel, - *S22Label, *S22sLabel, *S22uLabel; - QComboBox *FormatCombo, *UnitCombo; - - QLineEdit *Ref1Edit, *Ref2Edit, *FrequencyEdit, - *S11magEdit,*S11degEdit, *S21magEdit,*S21degEdit, - *S12magEdit,*S12degEdit, *S22magEdit,*S22degEdit; - - QCheckBox *TwoCheck; + QHBoxLayout *all; // the mother of all widgets + QVBoxLayout *MethodLayout; + QDoubleValidator *DoubleVal; + QLabel *Port1Label, *Port2Label, *Ohm1Label, *Ohm2Label, *FormatLabel, + *FrequencyLabel, *TopoLabel, *OrderLabel, *RelPermLabel, *S11Label, + *S11sLabel, *S11uLabel, *S21Label, *S21sLabel, *S21uLabel, *S12Label, + *S12sLabel, *S12uLabel, *S22Label, *S22sLabel, *S22uLabel, *SubsHScale, + *ThicknessScale, *minWScale, *maxWScale, *ResistivityLabel, + *maxRippleLabel, *thicknessLabel, *subsHLabel, *minWLabel, *maxWLabel, + *tanDLabel, *WeightingLabel; + QComboBox *FormatCombo, *UnitCombo, *TopoCombo, *RelPermCombo; + + QLineEdit *Ref1Edit, *Ref2Edit, *FrequencyEdit, *OrderEdit, *ResistivityEdit, + *MaxRippleEdit, *SubHeightEdit, *thicknessEdit, *minWEdit, *maxWEdit, + *tanDEdit, *S11magEdit, *S11degEdit, *S21magEdit, *S21degEdit, + *S12magEdit, *S12degEdit, *S22magEdit, *S22degEdit; + + QCheckBox *TwoCheck, *MicrostripCheck, *AddSPBlock, *BalancedCheck; + QRadioButton *OpenRadioButton, *ShortRadioButton, *BinRadio, *ChebyRadio; + QGroupBox *SubstrateBox, *Weighting_groupBox, *MethodBox; double tmpS21mag, tmpS21deg; void set2PortWidgetsVisible(bool); + QString flipLadderCode(QString laddercode); }; #endif diff --git a/qucs/misc.cpp b/qucs/misc.cpp index 5d32c187d..9557f5e9d 100644 --- a/qucs/misc.cpp +++ b/qucs/misc.cpp @@ -268,7 +268,7 @@ void misc::str2num(const QString& s_, double& Number, QString& Unit, double& Fac } // ######################################################################### -QString misc::num2str(double Num, int Precision) +QString misc::num2str(double Num, int Precision, QString unit) { char c = 0; double cal = fabs(Num); @@ -303,6 +303,8 @@ QString misc::num2str(double Num, int Precision) if(c) Str += c; + if (unit != "m") Str += unit; + return Str; } diff --git a/qucs/misc.h b/qucs/misc.h index 223de123f..a17ce0bd9 100644 --- a/qucs/misc.h +++ b/qucs/misc.h @@ -50,7 +50,7 @@ namespace misc { QString complexRad (double, double, int Precision=3); QString StringNum (double, char form='g', int Precision=3); void str2num (const QString&, double&, QString&, double&); - QString num2str (double, int Precision = -1); + QString num2str (double, int Precision = -1, QString unit=""); QColor ColorFromString(const QString& color); QString StringNiceNum(double); void convert2Unicode(QString&); From e4f345d985134dca776661f3025a9c132b39009d Mon Sep 17 00:00:00 2001 From: Vadim Kuznetsov Date: Mon, 25 Mar 2024 17:46:30 +0300 Subject: [PATCH 2/2] Fix Qt6 build error --- qucs/dialogs/matchdialog.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qucs/dialogs/matchdialog.cpp b/qucs/dialogs/matchdialog.cpp index 59a85ea73..ff9f15e2f 100644 --- a/qucs/dialogs/matchdialog.cpp +++ b/qucs/dialogs/matchdialog.cpp @@ -1275,10 +1275,8 @@ QString MatchDialog::calcDoubleStub(double r_real, double r_imag, double Z0, if (GL > Y0 * ((1 + t * t) / (2 * t * t))) // Not every load can be match // using the double stub technique. { - QString str = QString( - "It is not possible to match this load using the double stub method"); - QMessageBox::warning(0, QObject::trUtf8("Error"), - QObject::trUtf8(str.toUtf8())); + QMessageBox::warning(0, tr("Error"), + tr("It is not possible to match this load using the double stub method")); return QString(""); }