/* -*- C++ -*- This file is part of ViPEC Copyright (C) 1991-2001 Johan Rossouw (jrossouw@alcatel.altech.co.za) This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; //----------------------------------------------------------------- Schematic::Schematic() : name_(""), portCount_(0), maxNodeNr_(0), hasChanged_( FALSE ), isSolved_( FALSE ), yn_(0) { componentList_.setAutoDelete( TRUE ); sdata_.setAutoDelete( TRUE ); ydata_.setAutoDelete( TRUE ); zdata_.setAutoDelete( TRUE ); setSize( mediumSize ); } //----------------------------------------------------------------- Schematic::~Schematic() { componentList_.clear(); sdata_.clear(); ydata_.clear(); zdata_.clear(); if ( yn_ ) { delete yn_; } } //----------------------------------------------------------------- Schematic& Schematic::operator=( const Schematic& schematic) { componentList_.clear(); nodeList_.clear(); lineList_.clear(); portCount_ = 0; maxNodeNr_ = 0; hasChanged_ = FALSE; isSolved_ = FALSE; if ( yn_ ) { delete yn_; yn_ = 0; } Schematic& sch = (Schematic&) schematic; Component* component = 0; for ( component = sch.componentList_.first(); component != 0; component = sch.componentList_.next() ) { Component* c = ComponentFactory::createComponent( component->getName(), component->getCenter() ); addComponent( c ); c->copyData( *component ); } CircuitLine* line = 0; for ( line = sch.lineList_.first(); line != 0; line = sch.lineList_.next() ) { CircuitNode* node1 = findNodeAt( line->startPoint() ); CircuitNode* node2 = findNodeAt( line->endPoint() ); if ( node1 == 0 ) { node1 = new CircuitNode( line->startPoint() ); addNode( node1 ); } if ( node2 == 0 ) { node2 = new CircuitNode( line->endPoint() ); addNode( node2 ); } addLine( new CircuitLine( *node1, *node2 ) ); } return *this; } //----------------------------------------------------------------- void Schematic::setName(const QString& name) { name_ = name; } //----------------------------------------------------------------- const QString& Schematic::getName() const { return name_; } //----------------------------------------------------------------- void Schematic::setSize( SchematicSize size ) { size_ = size; switch ( size_ ) { case smallSize: width_ = 300; height_ = 200; break; case mediumSize: width_ = 510; height_ = 340; break; case largeSize: width_ = 750; height_ = 500; break; } if ( SchematicFrame::instance() ) { SchematicFrame::instance()->schematicSizeChanged( name_ ); } } //----------------------------------------------------------------- Schematic::SchematicSize Schematic::getSize() const { return size_; } //----------------------------------------------------------------- uint Schematic::width() const { return width_; } //----------------------------------------------------------------- uint Schematic::height() const { return height_; } //----------------------------------------------------------------- uint Schematic::getNumberOfNodes() const { return maxNodeNr_; } //----------------------------------------------------------------- uint Schematic::getNumberOfPorts() const { return portCount_; } //----------------------------------------------------------------- void Schematic::addComponent(Component* component) { componentList_.append( component ); component->setSchematic( this ); component->initComponent(); } //----------------------------------------------------------------- void Schematic::removeComponent( Component* component ) { ASSERT( componentList_.remove( component ) ); removeEmptyNodes(); } //----------------------------------------------------------------- void Schematic::addNode( CircuitNode* node ) { hasChanged_ = TRUE; nodeList_.append( node ); node->setCircuit( this ); if ( node->isPortNode() ) { portCount_++; node->setNodeNumber(portCount_); } } //----------------------------------------------------------------- void Schematic::removeNode( CircuitNode* node ) { hasChanged_ = TRUE; bool isPortNode = node->isPortNode(); ASSERT( nodeList_.remove( node ) ); if ( isPortNode ) { portCount_--; renumberPortNodes(); } } //----------------------------------------------------------------- void Schematic::addLine(CircuitLine* line) { hasChanged_ = TRUE; lineList_.append(line); } //----------------------------------------------------------------- void Schematic::removeLine(CircuitLine* line) { hasChanged_ = TRUE; ASSERT( lineList_.remove(line) ); delete line; } //----------------------------------------------------------------- void Schematic::writeToStream( QTextStream& stream ) { Component *component; for ( component=componentList_.first(); component != 0; component=componentList_.next() ) { component->writeToStream( stream ); } CircuitLine* line = 0; for ( line = lineList_.first(); line != 0; line = lineList_.next() ) { stream << "" << endl; stream << "startPoint()).x() << "\" "; stream << "Y=\"" << (line->startPoint()).y() << "\" />" << endl; stream << "endPoint()).x() << "\" "; stream << "Y=\"" << (line->endPoint()).y() << "\" />" << endl; stream << "" << endl; } } //----------------------------------------------------------------- bool Schematic::readFromDOM( QDomElement& element ) { QDomNode node = element.firstChild(); while( !node.isNull() ) { QDomElement childElement = node.toElement(); if( !childElement.isNull() ) { if ( childElement.tagName() == "COMPONENT" ) { QString type = childElement.attribute( "TYPE" ); Component* component = ComponentFactory::createComponent( type, QPoint(0,0) ); ASSERT( component != 0); addComponent( component ); if ( !component->readFromDOM( childElement ) ) { return FALSE; } } else if ( childElement.tagName() == "LINE" ) { bool success = readLineFromDOM( childElement ); if ( !success ) { return FALSE; } } else { ASSERT( "Unknown circuit element in file!" == 0 ); } } node = node.nextSibling(); } return TRUE; } //----------------------------------------------------------------- bool Schematic::readLineFromDOM( QDomElement& element ) { QPoint start, stop; QDomNode node = element.firstChild(); while( !node.isNull() ) { QDomElement childElement = node.toElement(); if( !childElement.isNull() ) { QString xStr = childElement.attribute( "X" ); QString yStr = childElement.attribute( "Y" ); int x = xStr.toInt(); int y = yStr.toInt(); if ( childElement.tagName() == "START" ) { start.setX( x ); start.setY( y ); } else if ( childElement.tagName() == "STOP" ) { stop.setX( x ); stop.setY( y ); } else { return FALSE; } } node = node.nextSibling(); } CircuitNode* startNode = findNodeAt(start); CircuitNode* stopNode = findNodeAt(stop); if ( !startNode ) { startNode = new CircuitNode(start); addNode(startNode); } if ( !stopNode ) { stopNode = new CircuitNode(stop); addNode(stopNode); } CircuitLine* line = new CircuitLine(*startNode, *stopNode); addLine( line ); return TRUE; } //----------------------------------------------------------------- CircuitNode* Schematic::findNodeAt( const QPoint& point ) { CircuitNode* node = 0; for ( node = nodeList_.first(); node != 0; node = nodeList_.next() ) { if ( node->pos() == point ) { return node; } } return 0; } //----------------------------------------------------------------- CircuitLine* Schematic::findLineAt( const QPoint& point, bool orthoganalOnly ) { CircuitLine* line = 0; for (line = lineList_.first(); line != 0; line = lineList_.next()) { int distance = distanceFromLine(point, *line, orthoganalOnly ); if ( (distance>=0) && (distance<5)) { return line; } } return 0; } //----------------------------------------------------------------- Component* Schematic::findComponentAt( const QPoint& point ) { Component* selectedComponent = 0; Component* component = 0; for (component = componentList_.first(); component != 0; component = componentList_.next()) { QRect rect = component->getBoundingRect(); if ( rect.contains(point) ) { selectedComponent = component; } } return selectedComponent; } //----------------------------------------------------------------- Component* Schematic::findTextAt( const QPoint& point ) { Component* selectedComponent = 0; Component* component = 0; for (component = componentList_.first(); component != 0; component = componentList_.next()) { QRect rect = component->getTextRect(); if ( rect.contains(point) ) { selectedComponent = component; } } return selectedComponent; } //----------------------------------------------------------------- int Schematic::distanceFromLine( const QPoint& point, const CircuitLine& line, bool orthoganalOnly ) { int xs = line.startPoint().x(); int ys = line.startPoint().y(); int xe = line.endPoint().x(); int ye = line.endPoint().y(); int x1 = (xs y1) && (point.y() < y2) ) { distance = abs(line.startPoint().x() - point.x()); } } else if ( y1 == y2 ) { if ( (point.x() > x1) && (point.x() < x2) ) { distance = abs(line.startPoint().y() - point.y()); } } else if ( !orthoganalOnly ) { TReal Ma = ((TReal) (ys-ye)) / ((TReal) (xs-xe)); TReal Ca = ys - Ma*xs; if ( fabs(Ma) > 1.0 ) { if ( (point.x() > x1) && (point.x() < x2) ) { int x = (int)( (point.y()-Ca)/Ma ); distance = abs(point.x() - x); } } else { if ( (point.y() > y1) && (point.y() < y2) ) { int y = (int)( Ma*point.x() + Ca ); distance = abs(point.y() - y); } } } return distance; } //----------------------------------------------------------------- void Schematic::draw( QPainter* p ) { p->save(); p->setPen( Qt::blue ); //Draw components Component* component = 0; for (component = componentList_.first(); component != 0; component = componentList_.next()) { component->draw (p ); } //Draw lines CircuitLine* line = 0; for ( line = lineList_.first(); line != 0; line = lineList_.next() ) { line->draw( p ); } //Draw nodes CircuitNode* node = 0; for (node = nodeList_.first(); node != 0; node = nodeList_.next()) { node->draw(p); } p->restore(); } //----------------------------------------------------------------- void Schematic::removeEmptyNodes() { QList emptyNodeList; CircuitNode* node = 0; for (node = nodeList_.first(); node != 0; node = nodeList_.next()) { if ( node->isFree() ) { emptyNodeList.append(node); } } for (node = emptyNodeList.first(); node != 0; node = emptyNodeList.next()) { QString message = QString("Found free node at %1").arg((ulong)node,0,16); Logger::debug(message); delete node; } } //----------------------------------------------------------------- void Schematic::renumberPortNodes() { int count = 0; CircuitNode* node = 0; for ( node = nodeList_.first(); node != 0; node = nodeList_.next() ) { if ( node->isPortNode() ) { count++; node->setNodeNumber( count ); } else if ( node->isGndNode() ) { node->setNodeNumber( 0 ); } else { node->setNodeNumber( -1 ); } } SchematicFrame::instance()->reDraw(); } //----------------------------------------------------------------- void Schematic::numberAllNodes() { renumberPortNodes(); //Number all nets connected to port nodes & ground nodes int startNode = 0; maxNodeNr_ = 0; CircuitNode* node = 0; for ( node = nodeList_.first(); node != 0; node = nodeList_.next() ) { if ( node->getNodeNumber() >= 0 ) { int portNodeNumber = node->getNodeNumber(); if (portNodeNumber > startNode) { startNode = portNodeNumber; } QList portNodes; findAllConnectedNodes(node, portNodes); CircuitNode* n = 0; for ( n = portNodes.first(); n!=0; n = portNodes.next() ) { if ( n != node ) { int nodeNumber = n->getNodeNumber(); if ( (nodeNumber >= 0) && (nodeNumber != portNodeNumber) ) { throw Exception::ShortedPorts(); } else { n->setNodeNumber(portNodeNumber); } } } } } //Now number remaining nodes for ( node = nodeList_.first(); node != 0; node = nodeList_.next() ) { if ( node->getNodeNumber() < 0 ) { startNode++; node->setNodeNumber( startNode ); QList portNodes; findAllConnectedNodes( node, portNodes ); CircuitNode* n = 0; for ( n = portNodes.first(); n!=0; n = portNodes.next() ) { n->setNodeNumber( startNode ); } } } maxNodeNr_ = startNode; if (Setup::instance()->isDebugMode()) { SchematicFrame::instance()->reDraw(); } QString message = QString("Circuit %1 has %2 nodes.").arg(name_).arg(maxNodeNr_); Logger::debug(message); } //----------------------------------------------------------------- void Schematic::findAllConnectedNodes(CircuitNode* node, QList& nodeList) { QList lineList; node->buildLineList(lineList); CircuitLine* line = 0; for (line = lineList.first(); line != 0; line = lineList.next()) { if ( nodeList.find( line->start() ) < 0) { nodeList.append( line->start() ); } if ( nodeList.find( line->stop() ) < 0) { nodeList.append( line->stop() ); } } } //----------------------------------------------------------------- void Schematic::reset() { isSolved_ = FALSE; hasChanged_ = TRUE; } //----------------------------------------------------------------- bool Schematic::isSolved() const { return isSolved_; } //----------------------------------------------------------------- void Schematic::initSweep() { Component* component = 0; for (component=componentList_.first(); component!=0; component=componentList_.next()) { bool changed = component->initSweep(); hasChanged_ = hasChanged_ || changed; } if (hasChanged_) { //Remove old solution if exist Logger::debug("Circuit " + name_ + " has changed - recomputing"); isSolved_ = FALSE; sdata_.clear(); ydata_.clear(); zdata_.clear(); } } //----------------------------------------------------------------- void Schematic::sweep(Vector& frequencies) { if ( isSolved_ ) { return; } zo_.resize( getNumberOfPorts() ); CircuitNode* node = 0; for ( node=nodeList_.first(); node!=0; node=nodeList_.next() ) { if ( node->isPortNode() ) { uint nodeNr = node->getNodeNumber() - 1; zo_(nodeNr, nodeNr) = node->getImpedance(); } } for ( uint i=0; iaddToAdmittanceMatrix( frequencies.at(i) , yn_ ); } Matrix result( getNumberOfPorts() ); //Solve matrix if ( Setup::instance()->solveByInversion() ) { uint noNodes = getNumberOfNodes(); (*yn_) = ~(yn_->subMatrix( 1, 1, noNodes, noNodes )); for (uint r=0; rswapRows( n, n+1 ); yn_->swapColumns( n, n+1 ); } yn_->reduce( getNumberOfPorts() ); } DataPoint* dataPoint = new DataPoint( frequencies.at(i), result ); if ( Setup::instance()->solveByInversion() ) { zdata_.append( dataPoint ); } else { ydata_.append( dataPoint ); } } Logger::debug("Solved circuit " + name_ + " ..."); isSolved_ = TRUE; hasChanged_ = FALSE; } //----------------------------------------------------------------- QList& Schematic::getSData() { if ( sdata_.isEmpty() ) { QList& zdata = getZData(); DataPoint* entry = 0; for ( entry=zdata.first(); entry != 0; entry=zdata.next() ) { Matrix data; Utils::convertZtoS( entry->data(), zo_, data ); DataPoint* sdata = new DataPoint( entry->frequency(), data ); sdata_.append( sdata ); } } return sdata_; } //----------------------------------------------------------------- QList& Schematic::getYData() { if ( ydata_.isEmpty() ) { ASSERT( zdata_.count() > 0 ); DataPoint* entry = 0; for ( entry=zdata_.first(); entry != 0; entry=zdata_.next() ) { Matrix data = entry->data(); ~data; DataPoint* ydata = new DataPoint( entry->frequency(), data ); ydata_.append( ydata ); } } return ydata_; } //----------------------------------------------------------------- QList& Schematic::getZData() { if ( zdata_.isEmpty() ) { ASSERT( ydata_.count() > 0 ); DataPoint* entry = 0; for ( entry=ydata_.first(); entry != 0; entry=ydata_.next() ) { Matrix data = entry->data(); ~data; DataPoint* zdata = new DataPoint( entry->frequency(), data ); zdata_.append( zdata ); } } return zdata_; } //----------------------------------------------------------------- TComplex Schematic::getPortImpedance( uint port ) { ASSERT( port < zo_.rows() ); return zo_( port, port ); } //----------------------------------------------------------------- void Schematic::findComponentsInsideRect( QList& list, QRect& rect ) { Component* c = 0; for ( c = componentList_.first(); c != 0; c = componentList_.next() ) { QRect componentRect = c->getBoundingRect(); if ( rect.contains( componentRect ) ) { if ( list.find( c ) < 0 ) { list.append( c ); c->setSelected( TRUE ); } } } } //----------------------------------------------------------------- void Schematic::findNodesInsideRect( QList& list, QRect& rect ) { CircuitNode* node = 0; for ( node = nodeList_.first(); node != 0; node = nodeList_.next() ) { if ( rect.contains( node->pos() ) ) { if ( list.find( node ) < 0 ) { list.append( node ); node->setSelected( TRUE ); } } } } //----------------------------------------------------------------- void Schematic::findLinesInsideRect( QList& list, QRect& rect ) { CircuitLine* line = 0; for ( line = lineList_.first(); line != 0; line = lineList_.next() ) { if ( rect.contains( line->startPoint() ) && rect.contains( line->endPoint() ) ) { if ( list.find( line ) < 0 ) { list.append( line ); line->setSelected( TRUE ); } } } }