qucs_s/qucs/dialogs/sweepdialog.cpp
ThomasZecha 62cf2d4bdc New Feature: CDL netlist export
Introduced very basic c++ coding style for the class Schematic and it's
parent-class QucsDoc based on https://google.github.io/styleguide/cppguide.html:
-Prefix class attributes with a_ (much more better readability!)
-Class member initialization via constructor member initialization list
-No public class attributes (where possible with small effort)

Signed-off-by: ThomasZecha <zecha@ihp-microelectronics.com>
2024-11-15 12:39:39 +01:00

305 lines
9.4 KiB
C++

/***************************************************************************
sweepdialog.cpp
-----------------
begin : Sat Aug 13 2005
copyright : (C) 2005 by Michael Margraf
email : michael.margraf@alumni.tu-berlin.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "sweepdialog.h"
#include "schematic.h"
#include "qucs.h"
#include <QGridLayout>
#include "main.h"
#include "../diagrams/graph.h"
#include "misc.h"
#include <QLabel>
#include <QLineEdit>
#include <QValidator>
#include <QPushButton>
#include <QDebug>
// SpinBoxes are used to show the calculated bias points at the given set of sweep points
mySpinBox::mySpinBox(int Min, int Max, int Step, double *Val, QWidget *Parent)
: QSpinBox(Parent)
{
setMinimum(Min);
setMaximum(Max);
setSingleStep(Step);
Values = Val;
ValueSize = Max;
//editor()->
// setReadOnly(true);
}
#include <iostream>
using namespace std;
QString mySpinBox::textFromValue(int Val) const
{
if (Values == NULL) return "";
//qDebug() << "Values + Val" << *(Values+Val) << endl;
return QString::number(*(Values+Val));
}
QValidator::State mySpinBox::validate ( QString & text, int & pos ) const
{
if(pos>ValueSize)return QValidator::Invalid;
if(QString::number(*(Values+pos))==text)
return QValidator::Acceptable;
else return QValidator::Invalid;
}
SweepDialog::SweepDialog(Schematic *Doc_,QHash<QString,double> *NodeVals)
: QDialog(Doc_)
{
qDebug() << "SweepDialog::SweepDialog()";
Doc = Doc_;
isSpice = false;
if (QucsSettings.DefaultSimulator != spicecompat::simQucsator) {
isSpice = true;
if (NodeVals) pGraph = setBiasPoints(NodeVals);
return;
}
pGraph = setBiasPoints();
// if simulation has no sweeps, terminate dialog before showing it
if(!pGraph->numAxes()) {
reject();
return;
}
if(pGraph->numAxes() <= 1)
if(pGraph->axis(0)->count <= 1) {
reject();
return;
}
setWindowTitle(tr("Bias Points"));
int i = 0;
// ...........................................................
QGridLayout *all = new QGridLayout(this);//, pGraph->cPointsX.count()+2,2,3,3);
all->setContentsMargins(5,5,5,5);
all->setSpacing(5);
all->setColumnStretch(1,5);
DataX const *pD;
mySpinBox *Box;
for(unsigned ii=0; (pD=pGraph->axis(ii)); ++ii) {
all->addWidget(new QLabel(pD->Var, this), i,0);
//cout<<"count: "<<pD->count-1<<", points: "<<*pD->Points<<endl;
//works only for linear:
/*double Min = pD->Points[0];
double Max = pD->Points[pD->count-1];
double Step = (Max-Min)/(pD->count);
cout<<"Min: "<<Min<<", Max: "<<Max<<", Step: "<<Step<<endl;
Box = new mySpinBox(Min, Max, Step, pD->Points, this);*/
Box = new mySpinBox(0, pD->count-1, 1, pD->Points, this);
Box->setValue(0);
all->addWidget(Box, i++,1);
connect(Box, SIGNAL(valueChanged(int)), SLOT(slotNewValue(int)));
BoxList.append(Box);
}
// ...........................................................
all->setRowStretch(i,5);
QPushButton *ButtClose = new QPushButton(tr("Close"), this);
all->addWidget(ButtClose, i+1,0);
connect(ButtClose, SIGNAL(clicked()), SLOT(accept()));
show();
}
SweepDialog::~SweepDialog()
{
delete pGraph;
while(!ValueList.isEmpty()) {
delete ValueList.takeFirst();
}
}
// ---------------------------------------------------------------
void SweepDialog::slotNewValue(int)
{
DataX const*pD = pGraph->axis(0);
int Factor = 1, Index = 0;
QList<mySpinBox *>::const_iterator it;
for(it = BoxList.constBegin(); it != BoxList.constEnd(); it++) {
Index += (*it)->value() * Factor;
Factor *= pD->count;
}
Index *= 2; // because of complex values
QList<Node *>::iterator node_it;
QList<double *>::const_iterator value_it = ValueList.begin();
for(node_it = NodeList.begin(); node_it != NodeList.end(); node_it++) {
qDebug() << "SweepDialog::slotNewValue:(*node_it)->Name:" << (*node_it)->Name;
(*node_it)->Name = misc::num2str(*((*value_it)+Index));
(*node_it)->Name += ((*node_it)->x1 & 0x10)? "A" : "V";
value_it++;
}
Doc->viewport()->update();
}
// ---------------------------------------------------
Graph* SweepDialog::setBiasPoints(QHash<QString,double> *NodeVals)
{
// When this function is entered, a simulation was performed.
// Thus, the node names are still in "node->Name".
qDebug() << "SweepDialog::setBiasPoints()";
bool hasNoComp;
Graph *pg = new Graph(NULL, ""); // HACK!
QFileInfo Info(Doc->getDocName());
QString DataSet = Info.absolutePath() + QDir::separator() + Doc->getDataSet();
Node *pn;
// Note 1:
// Invalidate it so that "Graph::loadDatFile()" does not check for the previously loaded time.
// This is a current hack as "Graph::loadDatFile()" does not support multi-node data loading
// from the simulation results without refreshing (changing) or invalidating the timestamp.
NodeList.clear();
ValueList.clear();
// create DC voltage for all nodes
for(pn = Doc->a_Nodes->first(); pn != 0; pn = Doc->a_Nodes->next()) {
if(pn->Name.isEmpty()) continue;
pn->x1 = 0;
if(pn->conn_count() < 2) {
pn->Name = ""; // no text at open nodes
continue;
}
else {
hasNoComp = true;
for(auto *pe : *pn)
if(pe->Type == isWire) {
if( ((Wire*)pe)->isHorizontal() ) pn->x1 |= 2;
}
else {
if( ((Component*)pe)->Model == "GND" ) {
hasNoComp = true; // no text at ground symbol
break;
}
if(pn->cx < pe->cx) pn->x1 |= 1; // to the right is no room
hasNoComp = false;
}
if(hasNoComp) { // text only were a component is connected
pn->Name = "";
continue;
}
}
if (!isSpice) {
pg->Var = pn->Name + ".V";
pg->lastLoaded = QDateTime(); // Note 1 at the start of this function
if(pg->loadDatFile(DataSet) == 2) {
pn->Name = misc::num2str(*(pg->cPointsY)) + "V";
NodeList.append(pn); // remember node ...
ValueList.append(pg->cPointsY); // ... and all of its values
pg->cPointsY = 0; // do not delete it next time !
}
else
pn->Name = "0V";
} else {
if (NodeVals->contains(pn->Name.toLower())) {
double volts = NodeVals->value(pn->Name.toLower());
pn->Name = misc::num2str(volts) + "V";
} else pn->Name = "0V";
}
for(auto pe : *pn)
if(pe->Type == isWire) {
if( ((Wire*)pe)->Port1 != pn ) // no text at next node
((Wire*)pe)->Port1->Name = "";
else ((Wire*)pe)->Port2->Name = "";
}
}
// create DC current through each probe
Component *pc;
for(pc = Doc->a_Components->first(); pc != 0; pc = Doc->a_Components->next())
if(pc->Model == "IProbe") {
pn = pc->Ports.first()->Connection;
if(!pn->Name.isEmpty()) // preserve node voltage ?
pn = pc->Ports.at(1)->Connection;
pn->x1 = 0x10; // mark current
if (!isSpice) {
pg->Var = pc->Name + ".I";
pg->lastLoaded = QDateTime(); // Note 1 at the start of this function
if(pg->loadDatFile(DataSet) == 2) {
pn->Name = misc::num2str(*(pg->cPointsY)) + "A";
NodeList.append(pn); // remember node ...
ValueList.append(pg->cPointsY); // ... and all of its values
pg->cPointsY = 0; // do not delete it next time !
}
else
pn->Name = "0A";
} else {
QString src_nam = QStringLiteral("V%1#branch").arg(pc->Name).toLower();
if (NodeVals->contains(src_nam)) {
pn->Name = misc::num2str(NodeVals->value(src_nam))+"A";
} else pn->Name = "0A";
}
for(auto pe : *pn)
if(pe->Type == isWire) {
if( ((Wire*)pe)->isHorizontal() ) pn->x1 |= 2;
}
else {
if(pn->cx < pe->cx) pn->x1 |= 1; // to the right is no room
}
} else if (isSpice) {
if ((pc->Model == "S4Q_V")||(pc->Model == "Vdc")) {
pn = pc->Ports.first()->Connection;
if(!pn->Name.isEmpty()) // preserve node voltage ?
pn = pc->Ports.at(1)->Connection;
pn->x1 = 0x10; // mark current
QString src_nam = QString(pc->Name+"#branch").toLower();
if (NodeVals->contains(src_nam)) {
pn->Name = misc::num2str(NodeVals->value(src_nam))+"A";
} else pn->Name = "0A";
for(auto pe : *pn)
if(pe->Type == isWire) {
if( ((Wire*)pe)->isHorizontal() ) pn->x1 |= 2;
}
else {
if(pn->cx < pe->cx) pn->x1 |= 1; // to the right is no room
}
}
}
Doc->setShowBias(1);
return pg;
}