diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 01:49:02 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 01:49:02 +0000 |
commit | 5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 (patch) | |
tree | bad482b7afa4cdf47422d60a5dd2c61c7e333b09 /src/electronics | |
download | ktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.tar.gz ktechlab-5de3dd4762ca33a0f92e79ffa4fe2ff67069d531.zip |
Added KDE3 version of ktechlab
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/ktechlab@1095338 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/electronics')
161 files changed, 22558 insertions, 0 deletions
diff --git a/src/electronics/Makefile.am b/src/electronics/Makefile.am new file mode 100644 index 0000000..c15e689 --- /dev/null +++ b/src/electronics/Makefile.am @@ -0,0 +1,18 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics \ + -I$(top_srcdir)/src/electronics/components -I$(top_srcdir)/src/electronics/simulation \ + -I$(top_srcdir)/src/flowparts -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages \ + -I$(top_srcdir)/src/mechanics -I$(top_srcdir)/src/micro $(glib_cflags) $(all_includes) + +METASOURCES = AUTO + +SUBDIRS = simulation components + +noinst_LTLIBRARIES = libelectronics.la + +libelectronics_la_SOURCES = component.cpp subcircuits.cpp gpsimprocessor.cpp \ + switch.cpp pin.cpp wire.cpp ecnode.cpp port.cpp + +libelectronics_la_LIBADD = \ + $(top_builddir)/src/electronics/simulation/libelements.la $(top_builddir)/src/electronics/components/libcomponents.la + +noinst_HEADERS = gpsimprocessor.h switch.h pin.h wire.h ecnode.h port.h diff --git a/src/electronics/component.cpp b/src/electronics/component.cpp new file mode 100644 index 0000000..f9f3c12 --- /dev/null +++ b/src/electronics/component.cpp @@ -0,0 +1,1017 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "circuitdocument.h" +#include "component.h" +#include "src/core/ktlconfig.h" +#include "ecnode.h" +#include "itemdocumentdata.h" +#include "node.h" +#include "pin.h" +#include "simulator.h" + +#include "bjt.h" +#include "capacitance.h" +#include "cccs.h" +#include "ccvs.h" +#include "currentsignal.h" +#include "currentsource.h" +#include "diode.h" +#include "inductance.h" +#include "logic.h" +#include "opamp.h" +#include "resistance.h" +#include "switch.h" +#include "vccs.h" +#include "vcvs.h" +#include "voltagepoint.h" +#include "voltagesignal.h" +#include "voltagesource.h" + +#include <cmath> +#include <kdebug.h> +#include <qbitarray.h> +#include <qpainter.h> +#include <qwidget.h> +#include <qwmatrix.h> + +const int dipWidth = 112; +const int pairSep = 32; + +// Degrees per radian +static const double DPR = 57.29577951308232087665461840231273527024; + +Component::Component( ICNDocument *icnDocument, bool newItem, const QString &id ) + : CNItem( icnDocument, newItem, id ), + m_angleDegrees(0), + b_flipped(false) +{ + m_pCircuitDocument = dynamic_cast<CircuitDocument*>(icnDocument); + + for ( int i=0; i<4; ++i ) + { + m_pPNode[i] = 0l; + m_pNNode[i] = 0l; + } + + // Get configuration options + slotUpdateConfiguration(); + + // And finally register this :-) + icnDocument->registerItem(this); +} + +Component::~Component() +{ + removeElements(); + Simulator::self()->detachComponent(this); +} + +void Component::removeItem( ) +{ + if (b_deleted) + return; + Simulator::self()->detachComponent(this); + CNItem::removeItem(); +} + +void Component::removeElements( bool setPinsInterIndependent ) +{ + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + Element * e = (*it).e; + if (e) + { + emit elementDestroyed(e); + e->componentDeleted(); + } + } + m_elementMapList.clear(); + + const SwitchList::iterator swEnd = m_switchList.end(); + for ( SwitchList::iterator it = m_switchList.begin(); it != swEnd; ++it ) + { + Switch * sw = *it; + if ( !sw ) + continue; + + emit switchDestroyed( sw ); + delete sw; + } + m_switchList.clear(); + + if ( setPinsInterIndependent ) + setAllPinsInterIndependent(); +} + + +void Component::removeElement( Element * element, bool setPinsInterIndependent ) +{ + if (!element) + return; + + emit elementDestroyed(element); + element->componentDeleted(); + + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ) + { + ElementMapList::iterator next = it; + ++next; + + if ( (*it).e == element ) + m_elementMapList.remove(it); + + it = next; + } + + if ( setPinsInterIndependent ) + rebuildPinInterDepedence(); +} + + +void Component::removeSwitch( Switch * sw ) +{ + if ( !sw ) + return; + + emit switchDestroyed( sw ); + delete sw; + m_switchList.remove(sw); + m_pCircuitDocument->requestAssignCircuits(); +} + + +void Component::setNodalCurrents() +{ + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + ElementMap m = (*it); + for ( int i=0; i<4; i++ ) + { + if ( m.n[i] ) { + m.n[i]->mergeCurrent( m.e->m_cnodeI[i] ); + } + } + } +} + + +void Component::initPainter( QPainter &p ) +{ + CNItem::initPainter(p); + + if ( !b_flipped && (m_angleDegrees%360 == 0) ) + return; + + p.save(); + + p.translate( int(x()), int(y()) ); + if (b_flipped) + p.scale( -1, 1 ); + + p.rotate(m_angleDegrees); + p.translate( -int(x()), -int(y()) ); +} + + +void Component::deinitPainter( QPainter &p ) +{ + if ( !b_flipped && (m_angleDegrees%360 == 0) ) + return; + + p.restore(); +} + + +void Component::setAngleDegrees( int degrees ) +{ + updateConnectorPoints(false); + m_angleDegrees = degrees; + itemPointsChanged(); + updateAttachedPositioning(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +void Component::setFlipped( bool flipped ) +{ + updateConnectorPoints(false); + b_flipped = flipped; + itemPointsChanged(); + updateAttachedPositioning(); + p_icnDocument->requestRerouteInvalidatedConnectors(); +} + + +void Component::itemPointsChanged() +{ + QPointArray transformedPoints = transMatrix( m_angleDegrees, b_flipped, 0, 0, false ).map(m_itemPoints); +// transformedPoints.translate( int(x()), int(y()) ); + setPoints(transformedPoints); +} + + +void Component::restoreFromItemData( const ItemData &itemData ) +{ + CNItem::restoreFromItemData(itemData); + + setAngleDegrees( int(itemData.angleDegrees) ); + setFlipped(itemData.flipped); +} + + +ItemData Component::itemData() const +{ + ItemData itemData = CNItem::itemData(); + itemData.angleDegrees = m_angleDegrees; + itemData.flipped = b_flipped; + return itemData; +} + + +QWMatrix Component::transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse ) +{ + QWMatrix m; + m.translate( x, y ); + if (inverse) + { + m.rotate(-angleDegrees); + if (flipped) + m.scale( -1, 1 ); + } + else + { + if (flipped) + m.scale( -1, 1 ); + m.rotate(angleDegrees); + } + m.translate( -x, -y ); + m.setTransformationMode( QWMatrix::Areas ); + return m; +} + + +void Component::finishedCreation() +{ + CNItem::finishedCreation(); + updateAttachedPositioning(); +} + + +void Component::updateAttachedPositioning() +{ + if (b_deleted || !m_bDoneCreation) + return; + + //BEGIN Transform the nodes + const NodeMap::iterator end = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != end; ++it ) + { + if ( !it.data().node ) + kdError() << k_funcinfo << "Node in nodemap is null" << endl; + else + { + int nx = int((std::cos(m_angleDegrees/DPR) * it.data().x) - (std::sin(m_angleDegrees/DPR) * it.data().y)); + int ny = int((std::sin(m_angleDegrees/DPR) * it.data().x) + (std::cos(m_angleDegrees/DPR) * it.data().y)); + + if (b_flipped) + nx = -nx; + +#define round_8(x) (((x) > 0) ? int(((x)+4)/8)*8 : int(((x)-4)/8)*8) + nx = round_8(nx); + ny = round_8(ny); +#undef round_8 + + int newDir = (((m_angleDegrees + it.data().orientation)%360)+360)%360; + if (b_flipped) + newDir = (((180-newDir)%360)+360)%360; + + it.data().node->move( nx+x(), ny+y() ); + it.data().node->setOrientation( (Node::node_dir)newDir ); + } + } + //END Transform the nodes + + + //BEGIN Transform the GuiParts + QWMatrix m; + + if (b_flipped) + m.scale( -1, 1 ); + m.rotate(m_angleDegrees); + m.setTransformationMode( QWMatrix::Areas ); + + const TextMap::iterator textMapEnd = m_textMap.end(); + for ( TextMap::iterator it = m_textMap.begin(); it != textMapEnd; ++it ) + { + QRect newPos = m.mapRect( it.data()->recommendedRect() ); + it.data()->move( newPos.x() + x(), newPos.y() + y() ); + it.data()->setGuiPartSize( newPos.width(), newPos.height() ); + it.data()->setAngleDegrees(m_angleDegrees); + } + const WidgetMap::iterator widgetMapEnd = m_widgetMap.end(); + for ( WidgetMap::iterator it = m_widgetMap.begin(); it != widgetMapEnd; ++it ) + { + QRect newPos = m.mapRect( it.data()->recommendedRect() ); + it.data()->move( newPos.x() + x(), newPos.y() + y() ); + it.data()->setGuiPartSize( newPos.width(), newPos.height() ); + it.data()->setAngleDegrees(m_angleDegrees); + } + //END Transform the GuiParts +} + + +void Component::drawPortShape( QPainter & p ) +{ + int h = height(); + int w = width() - 1; + int _x = int( x() + offsetX() ); + int _y = int( y() + offsetY() ); + + double roundSize = 8; + double slantIndent = 8; + + const double pi = 3.1415926536; + const double DPR = 180./pi; + double inner = std::atan(h/slantIndent); // Angle for slight corner + double outer = pi-inner; // Angle for sharp corner + + int inner16 = int(16*inner*DPR); + int outer16 = int(16*outer*DPR); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawPolygon( areaPoints() ); + p.restore(); + + initPainter( p ); + + // Left line + p.drawLine( int(_x), int(_y+roundSize/2), int(_x), int(_y+h-roundSize/2) ); + + // Right line + p.drawLine( int(_x+w), int(_y-slantIndent+h-roundSize/2), int(_x+w), int(_y+slantIndent+roundSize/2) ); + + // Bottom line + p.drawLine( int(_x+(1-std::cos(outer))*(roundSize/2)), int(_y+h+(std::sin(outer)-1)*(roundSize/2)), + int(_x+w+(std::cos(inner)-1)*(roundSize/2)), int(_y+h-slantIndent+(std::sin(inner)-1)*(roundSize/2)) ); + + // Top line + p.drawLine( int(_x+w+(std::cos(outer)-1)*(roundSize/2)), int(_y+slantIndent+(1-std::sin(inner))*(roundSize/2)), + int(_x+(1-std::cos(inner))*(roundSize/2)), int(_y+(1-std::sin(outer))*(roundSize/2)) ); + + + // Top left + p.drawArc( int(_x), int(_y), int(roundSize), int(roundSize), 90*16, outer16 ); + + // Bottom left + p.drawArc( int(_x), int(_y+h-roundSize), int(roundSize), int(roundSize), 180*16, outer16 ); + + // Top right + p.drawArc( int(_x+w-roundSize), int(_y+slantIndent), int(roundSize), int(roundSize), 0, inner16 ); + + // Bottom right + p.drawArc( int(_x+w-roundSize), int(_y-slantIndent+h-roundSize), int(roundSize), int(roundSize), 270*16, inner16 ); + + deinitPainter( p ); +} + + +void Component::initDIP( const QStringList & pins ) +{ + const int numPins = pins.size(); + const int numSide = numPins/2 + numPins%2; + + // Pins along left + for ( int i=0; i<numSide; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const int nodeX = -8+offsetX(); + const int nodeY = (i+1)*16+offsetY(); + ECNode *node = ecNodeWithID(pins[i]); + if (node) + { + m_nodeMap[pins[i]].x = nodeX; + m_nodeMap[pins[i]].y = nodeY; + m_nodeMap[pins[i]].orientation = (Node::node_dir)0; + } + else + createPin( nodeX, nodeY, 0, pins[i] ); + } + } + // Pins along right + for ( int i=numSide; i<numPins; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const int nodeX = width()+8+offsetX(); + const int nodeY = (2*numSide-i)*16+offsetY(); + ECNode *node = ecNodeWithID(pins[i]); + if (node) + { + m_nodeMap[pins[i]].x = nodeX; + m_nodeMap[pins[i]].y = nodeY; + m_nodeMap[pins[i]].orientation = (Node::node_dir)180; + } + else + createPin( nodeX, nodeY, 180, pins[i] ); + } + } + + updateAttachedPositioning(); +} + +void Component::initDIPSymbol( const QStringList & pins, int _width ) +{ + const int numPins = pins.size(); + const int numSide = numPins/2 + numPins%2; + + setSize( -(_width-(_width%16))/2, -(numSide+1)*8, _width, (numSide+1)*16, true ); + + QWidget tmpWidget; + QPainter p(&tmpWidget); + + p.setFont(m_font); + + // Pins along left + for ( int i=0; i<numSide; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const QString text = *pins.at(i); + + const int _top = (i+1)*16-8 + offsetY(); + const int _width = width()/2 - 6; + const int _left = 6 + offsetX(); + const int _height = 16; + + QRect br = p.boundingRect( QRect( _left, _top, _width, _height ), Qt::AlignLeft, text ); + addDisplayText( text, br, text ); + } + } + // Pins along right + for ( int i=numSide; i<numPins; i++ ) + { + if ( !pins[i].isEmpty() ) + { + const QString text = *pins.at(i); + + const int _top = (2*numSide-i)*16 - 8 + offsetY(); + const int _width = width()/2 - 6; + const int _left = (width()/2) + offsetX(); + const int _height = 16; + + QRect br = p.boundingRect( QRect( _left, _top, _width, _height ), Qt::AlignRight, text ); + addDisplayText( text, br, text ); + } + } + + updateAttachedPositioning(); +} + + +// QString createNode( double _x, double _y, int shape, int orientation, const QString &name, int type, bool isInput = true ); + +void Component::init1PinLeft( int h1 ) +{ + if ( h1 == -1 ) h1 = offsetY()+height()/2; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); +} + +void Component::init2PinLeft( int h1, int h2 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); +} + +void Component::init3PinLeft( int h1, int h2, int h3 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()/2; + if ( h3 == -1 ) h3 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); + m_pNNode[2] = createPin( offsetX()-8, h3, 0, "n3" ); +} + +void Component::init4PinLeft( int h1, int h2, int h3, int h4 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+24; + if ( h3 == -1 ) h3 = offsetY()+height()-24; + if ( h4 == -1 ) h4 = offsetY()+height()-8; + + m_pNNode[0] = createPin( offsetX()-8, h1, 0, "n1" ); + m_pNNode[1] = createPin( offsetX()-8, h2, 0, "n2" ); + m_pNNode[2] = createPin( offsetX()-8, h3, 0, "n3" ); + m_pNNode[3] = createPin( offsetX()-8, h4, 0, "n4" ); +} + +void Component::init1PinRight( int h1 ) +{ + if ( h1 == -1 ) h1 = offsetY()+height()/2; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); +} + + +void Component::init2PinRight( int h1, int h2 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); +} + +void Component::init3PinRight( int h1, int h2, int h3 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+height()/2; + if ( h3 == -1 ) h3 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); + m_pPNode[2] = createPin( offsetX()+width()+8, h3, 180, "p3" ); +} + +void Component::init4PinRight( int h1, int h2, int h3, int h4 ) +{ + if ( h1 == -1 ) h1 = offsetY()+8; + if ( h2 == -1 ) h2 = offsetY()+24; + if ( h3 == -1 ) h3 = offsetY()+height()-24; + if ( h4 == -1 ) h4 = offsetY()+height()-8; + + m_pPNode[0] = createPin( offsetX()+width()+8, h1, 180, "p1" ); + m_pPNode[1] = createPin( offsetX()+width()+8, h2, 180, "p2" ); + m_pPNode[2] = createPin( offsetX()+width()+8, h3, 180, "p3" ); + m_pPNode[3] = createPin( offsetX()+width()+8, h4, 180, "p4" ); +} + + +ECNode* Component::ecNodeWithID( const QString &ecNodeId ) +{ + return dynamic_cast<ECNode*>( p_icnDocument->nodeWithID( nodeId(ecNodeId) ) ); +} + + +void Component::slotUpdateConfiguration() +{ + const LogicConfig logicConfig = LogicIn::getConfig(); + + const ElementMapList::iterator end = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + if ( LogicIn * logicIn = dynamic_cast<LogicIn*>((*it).e) ) + logicIn->setLogic(logicConfig); + } +} + + + +BJT * Component::createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN ) +{ return createBJT( c->pin(), b->pin(), e->pin(), isNPN ); } +Capacitance * Component::createCapacitance( ECNode *n0, ECNode *n1, double capacitance ) +{ return createCapacitance( n0->pin(), n1->pin(), capacitance ); } +CCCS * Component::createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createCCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +CCVS * Component::createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createCCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +CurrentSignal * Component::createCurrentSignal( ECNode *n0, ECNode *n1, double current ) +{ return createCurrentSignal( n0->pin(), n1->pin(), current ); } +CurrentSource * Component::createCurrentSource( ECNode *n0, ECNode *n1, double current ) +{ return createCurrentSource( n0->pin(), n1->pin(), current ); } +Diode * Component::createDiode( ECNode *n0, ECNode *n1 ) +{ return createDiode( n0->pin(), n1->pin() ); } +Inductance * Component::createInductance( ECNode *n0, ECNode *n1, double inductance ) +{ return createInductance( n0->pin(), n1->pin(), inductance ); } +LogicIn * Component::createLogicIn( ECNode *node ) +{ return createLogicIn( node->pin() ); } +LogicOut * Component::createLogicOut( ECNode *node, bool isHigh ) +{ return createLogicOut( node->pin(), isHigh ); } +OpAmp * Component::createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ) +{ return createOpAmp( nonInverting->pin(), out->pin(), inverting->pin() ); } +Resistance * Component::createResistance( ECNode *n0, ECNode *n1, double resistance ) +{ return createResistance( n0->pin(), n1->pin(), resistance ); } +Switch * Component::createSwitch( ECNode *n0, ECNode *n1, bool open ) +{ return createSwitch( n0->pin(), n1->pin(), open ); } +VCCS * Component::createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createVCCS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +VCVS * Component::createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ) +{ return createVCVS( n0->pin(), n1->pin(), n2->pin(), n3->pin(), gain ); } +VoltagePoint * Component::createVoltagePoint( ECNode *n0, double voltage ) +{ return createVoltagePoint( n0->pin(), voltage ); } +VoltageSignal * Component::createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ) +{ return createVoltageSignal( n0->pin(), n1->pin(), voltage ); } +VoltageSource * Component::createVoltageSource( ECNode *n0, ECNode *n1, double voltage ) +{ return createVoltageSource( n0->pin(), n1->pin(), voltage ); } + +BJT* Component::createBJT( Pin *cN, Pin *bN, Pin *eN, bool isNPN ) +{ + BJT *e = new BJT(isNPN); + + QValueList<Pin*> pins; + pins << bN << cN << eN; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Capacitance* Component::createCapacitance( Pin *n0, Pin *n1, double capacitance ) +{ + Capacitance *e = new Capacitance( capacitance, 1./LINEAR_UPDATE_RATE ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CCCS* Component::createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + CCCS *e = new CCCS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CCVS* Component::createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + CCVS *e = new CCVS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterCircuitDependent( it, pins ); + + pins.clear(); + pins << n0 << n1; + setInterGroundDependent( it, pins ); + + pins.clear(); + pins << n2 << n3; + setInterGroundDependent( it, pins ); + + return e; +} + +CurrentSignal* Component::createCurrentSignal( Pin *n0, Pin *n1, double current ) +{ + CurrentSignal *e = new CurrentSignal( 1./LINEAR_UPDATE_RATE, current ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +CurrentSource* Component::createCurrentSource( Pin *n0, Pin *n1, double current ) +{ + CurrentSource *e = new CurrentSource(current); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Diode* Component::createDiode( Pin *n0, Pin *n1 ) +{ + Diode *e = new Diode(); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Inductance* Component::createInductance( Pin *n0, Pin *n1, double inductance ) +{ + Inductance *e = new Inductance( inductance, 1./LINEAR_UPDATE_RATE ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +LogicIn *Component::createLogicIn( Pin *node ) +{ + LogicIn *e = new LogicIn(LogicIn::getConfig()); + + QValueList<Pin*> pins; + pins << node; + + ElementMapList::iterator it = handleElement( e, pins ); + return e; +} + +LogicOut *Component::createLogicOut( Pin *node, bool isHigh ) +{ + LogicOut *e = new LogicOut( LogicIn::getConfig(), isHigh); + + QValueList<Pin*> pins; + pins << node; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +OpAmp * Component::createOpAmp( Pin * nonInverting, Pin * inverting, Pin * out ) +{ + OpAmp * e = new OpAmp(); + + QValueList<Pin*> pins; + pins << nonInverting << inverting << out; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Resistance* Component::createResistance( Pin *n0, Pin *n1, double resistance ) +{ + Resistance *e = new Resistance(resistance); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +Switch* Component::createSwitch( Pin *n0, Pin *n1, bool open ) +{ + // Note that a Switch is not really an element (although in many cases it + // behaves very much like one). + + Switch * e = new Switch( this, n0, n1, open ? Switch::Open : Switch::Closed ); + m_switchList.append(e); + n0->addSwitch( e ); + n1->addSwitch( e ); + emit switchCreated( e ); + return e; +} + +VCCS* Component::createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + VCCS *e = new VCCS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VCVS* Component::createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ) +{ + VCVS *e = new VCVS(gain); + + QValueList<Pin*> pins; + pins << n0 << n1 << n2 << n3; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterCircuitDependent( it, pins ); + + pins.clear(); + pins << n0 << n1; + setInterGroundDependent( it, pins ); + + pins.clear(); + pins << n2 << n3; + setInterGroundDependent( it, pins ); + return e; +} + +VoltagePoint* Component::createVoltagePoint( Pin *n0, double voltage ) +{ + VoltagePoint *e = new VoltagePoint(voltage); + + QValueList<Pin*> pins; + pins << n0; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VoltageSignal* Component::createVoltageSignal( Pin *n0, Pin *n1, double voltage ) +{ + VoltageSignal *e = new VoltageSignal( 1./LINEAR_UPDATE_RATE, voltage ); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + +VoltageSource* Component::createVoltageSource( Pin *n0, Pin *n1, double voltage ) +{ + VoltageSource *e = new VoltageSource(voltage); + + QValueList<Pin*> pins; + pins << n0 << n1; + + ElementMapList::iterator it = handleElement( e, pins ); + setInterDependent( it, pins ); + return e; +} + + +ElementMapList::iterator Component::handleElement( Element *e, const QValueList<Pin*> & pins ) +{ + if (!e) + return m_elementMapList.end(); + + ElementMap em; + em.e = e; + int at = 0; + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it = pins.begin(); it != end; ++it ) + { + (*it)->addElement(e); + em.n[at++] = *it; + } + + ElementMapList::iterator it = m_elementMapList.append(em); + + emit elementCreated(e); + return it; +} + + +void Component::setInterDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + setInterCircuitDependent( it, pins ); + setInterGroundDependent( it, pins ); +} + + +void Component::setInterCircuitDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) + { + for ( QValueList<Pin*>::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) + { + (*it1)->addCircuitDependentPin( *it2 ); + } + } + + (*it).interCircuitDependent.append( pins ); +} + + +void Component::setInterGroundDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ) +{ + QValueList<Pin*>::ConstIterator end = pins.end(); + for ( QValueList<Pin*>::ConstIterator it1 = pins.begin(); it1 != end; ++it1 ) + { + for ( QValueList<Pin*>::ConstIterator it2 = pins.begin(); it2 != end; ++it2 ) + { + (*it1)->addGroundDependentPin( *it2 ); + } + } + + (*it).interGroundDependent.append( pins ); +} + + +void Component::rebuildPinInterDepedence() +{ + setAllPinsInterIndependent(); + + // Rebuild dependencies + ElementMapList::iterator emlEnd = m_elementMapList.end(); + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != emlEnd; ++it ) + { + // Many copies of the pin lists as these will be affected when we call setInter*Dependent + PinListList list = (*it).interCircuitDependent; + + PinListList::iterator depEnd = list.end(); + for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) + setInterCircuitDependent( it, *depIt ); + + list = (*it).interGroundDependent; + + depEnd = list.end(); + for ( PinListList::iterator depIt = list.begin(); depIt != depEnd; ++depIt ) + setInterGroundDependent( it, *depIt ); + } +} + + +void Component::setAllPinsInterIndependent() +{ + NodeMap::iterator nmEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nmEnd; ++it ) + { + PinVector pins = (static_cast<ECNode*>(it.data().node))->pins(); + PinVector::iterator pinsEnd = pins.end(); + for ( PinVector::iterator pinsIt = pins.begin(); pinsIt != pinsEnd; ++pinsIt ) + { + if ( *pinsIt ) + (*pinsIt)->removeDependentPins(); + } + } +} + + +void Component::initElements( const uint stage ) +{ + /// @todo this function is ugly and messy and needs tidying up + + const ElementMapList::iterator end = m_elementMapList.end(); + + if ( stage == 1 ) + { + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + (*it).e->add_initial_dc(); + } + return; + } + + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + ElementMap m = (*it); + + if ( m.n[3] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId(), m.n[3]->eqId() ); + } + else if ( m.n[2] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId(), m.n[2]->eqId() ); + } + else if ( m.n[1] ) { + m.e->setCNodes( m.n[0]->eqId(), m.n[1]->eqId() ); + } + else if ( m.n[0] ) { + m.e->setCNodes( m.n[0]->eqId() ); + } + } + + for ( ElementMapList::iterator it = m_elementMapList.begin(); it != end; ++it ) + { + (*it).e->add_map(); + } +} + + +ECNode * Component::createPin( double x, double y, int orientation, const QString & name ) +{ + return dynamic_cast<ECNode*>( createNode( x, y, orientation, name, Node::ec_pin ) ); +} + + +//BEGIN class ElementMap +ElementMap::ElementMap() +{ + e = 0; + for ( int i = 0; i < 4; ++i ) + n[i] = 0; +} +//END class ElementMap + + +#include "component.moc" + + diff --git a/src/electronics/component.h b/src/electronics/component.h new file mode 100644 index 0000000..d45d835 --- /dev/null +++ b/src/electronics/component.h @@ -0,0 +1,362 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef COMPONENT_H +#define COMPONENT_H + +#include "cnitem.h" + +#include <qvaluelist.h> + +class ICNDocument; +class CircuitDocument; +class ECNode; +class ECSubcircuit; +class Element; +class Node; +class Pin; + +class BJT; +class Capacitance; +class CCCS; +class CCVS; +class CurrentSignal; +class CurrentSource; +class Diode; +class Inductance; +class LogicIn; +class LogicOut; +class OpAmp; +class Resistance; +class Switch; +class Transformer; +class VCCS; +class VCVS; +class VoltagePoint; +class VoltageSignal; +class VoltageSource; + +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<Element*> ElementList; +typedef QValueList<Switch*> SwitchList; + +typedef QValueList< QValueList<Pin*> > PinListList; + +/** +Contains vital information about the elements in the component. +*/ +class ElementMap +{ + public: + ElementMap(); + + Element * e; // The element + Pin * n[4]; // The Pins associated with the CNodes in the element + + /// @see Component::setInterCircuitDependent + PinListList interCircuitDependent; + + /// @see Component::setInterGroundDependent + PinListList interGroundDependent; +}; + +typedef QValueList<ElementMap> ElementMapList; + +/** +@short Bass class for all electrical components +@author David Saxton +*/ +class Component : public CNItem +{ + Q_OBJECT + public: + Component( ICNDocument *icnDocument, bool newItem, const QString &id ); + virtual ~Component(); + + ECNode* createPin( double _x, double _y, int orientation, const QString &name ); + /** + * Reinherit this function to disallow rotation of the component. + */ + virtual bool canRotate() const { return true; } + /** + * Angle of orientation + */ + int angleDegrees() const { return m_angleDegrees; } + /** + * Sets the angle (in degrees) + */ + void setAngleDegrees( int degrees ); + /** + * Whether or not the item is flipped + */ + bool flipped() const { return b_flipped; } + /** + * Sets whether or not the item is flipped + */ + void setFlipped( bool flipped ); + /** + * After calculating nodal voltages, each component will be + * called to tell its nodes what the current flowing *into* + * the component is. + */ + void setNodalCurrents(); + /** + * @return pointer to the CircuitDocument that we're in. + */ + CircuitDocument * circuitDocument() const { return m_pCircuitDocument; } + void initElements( const uint stage ); + virtual void finishedCreation(); + /** + * If reinherit (and use) the stepNonLogic function, then you must also + * reinherit this function so that it returns true. Else your component + * will not get called. + */ + virtual bool doesStepNonLogic() const { return false; } + virtual void stepNonLogic() {}; + /** + * Returns the translation matrix used for painting et al + * @param orientation The orientation to use + * @param x x co-ordinate of the center of the object to be mapped + * @param y y co-ordinate of the center of the object to be mapped + * @param inverse If false, maps the unrotated item to a rotated one, else mapped->unmapped + */ + static QWMatrix transMatrix( int angleDegrees, bool flipped, int x, int y, bool inverse = false ); + /** + * @return Information about the component in an ItemData struct. + */ + virtual ItemData itemData() const; + /** + * Restores the state of the component from the ItemData struct. + */ + virtual void restoreFromItemData( const ItemData &itemData ); + + BJT * createBJT( Pin *c, Pin *b, Pin *e, bool isNPN = true ); + BJT * createBJT( ECNode *c, ECNode *b, ECNode *e, bool isNPN = true ); + + Capacitance * createCapacitance( Pin *n0, Pin *n1, double capacitance ); + Capacitance * createCapacitance( ECNode *n0, ECNode *n1, double capacitance ); + + CCCS * createCCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + CCCS * createCCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + CCVS * createCCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + CCVS * createCCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + CurrentSignal * createCurrentSignal( Pin *n0, Pin *n1, double current ); + CurrentSignal * createCurrentSignal( ECNode *n0, ECNode *n1, double current ); + + CurrentSource * createCurrentSource( Pin *n0, Pin *n1, double current ); + CurrentSource * createCurrentSource( ECNode *n0, ECNode *n1, double current ); + + Diode * createDiode( Pin *n0, Pin *n1 ); + Diode * createDiode( ECNode *n0, ECNode *n1 ); + + Inductance * createInductance( Pin *n0, Pin *n1, double inductance ); + Inductance * createInductance( ECNode *n0, ECNode *n1, double inductance ); + + LogicIn * createLogicIn( Pin *node ); + LogicIn * createLogicIn( ECNode *node ); + + LogicOut * createLogicOut( Pin *node, bool isHigh ); + LogicOut * createLogicOut( ECNode *node, bool isHigh ); + + OpAmp * createOpAmp( Pin * nonInverting, Pin * out, Pin * inverting ); + OpAmp * createOpAmp( ECNode * nonInverting, ECNode * out, ECNode * inverting ); + + Resistance * createResistance( Pin *n0, Pin *n1, double resistance ); + Resistance * createResistance( ECNode *n0, ECNode *n1, double resistance ); + + Switch * createSwitch( Pin *n0, Pin *n1, bool open ); + Switch * createSwitch( ECNode *n0, ECNode *n1, bool open ); + + VCCS * createVCCS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + VCCS * createVCCS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + VCVS * createVCVS( Pin *n0, Pin *n1, Pin *n2, Pin *n3, double gain ); + VCVS * createVCVS( ECNode *n0, ECNode *n1, ECNode *n2, ECNode *n3, double gain ); + + VoltagePoint * createVoltagePoint( Pin *n0, double voltage ); + VoltagePoint * createVoltagePoint( ECNode *n0, double voltage ); + + VoltageSignal * createVoltageSignal( Pin *n0, Pin *n1, double voltage ); + VoltageSignal * createVoltageSignal( ECNode *n0, ECNode *n1, double voltage ); + + VoltageSource * createVoltageSource( Pin *n0, Pin *n1, double voltage ); + VoltageSource * createVoltageSource( ECNode *n0, ECNode *n1, double voltage ); + + + ECNode* ecNodeWithID( const QString &ecNodeId ); + + /** + * Safely delete an element - in this case, calls element->componentDeleted, + * and removes it from the element list. + * @param setPinsInterIndependent whether to call + * setPinsInterIndependent. The call is time-consuming, and unnecessary + * if the pins from which the element was originally attached will be/ + * were removed, or they will become interdependent again. + */ + void removeElement( Element * element, bool setPinsInterIndependent ); + /** + * Safely remove a switch. + */ + void removeSwitch( Switch * sw ); + /** + * Removes all elements and switches. + * @param setPinsInterIndependent whether to bother calling + * setPinsInterIndependent. This is false when calling from the + * destructor, or when the dependency information is the same. + */ + void removeElements( bool setPinsInterIndependent = false ); + /** + * @return the list of switches that this component uses. + */ + SwitchList switchList() const { return m_switchList; } + + signals: + /** + * Emitted when an element is created. + */ + void elementCreated( Element * element ); + /** + * Emitted when an element is destroyed. + */ + void elementDestroyed( Element * element ); + /** + * Emitted when a switch. is created + */ + void switchCreated( Switch * sw ); + /** + * Emitted when a switch is destroyed. + */ + void switchDestroyed( Switch * sw ); + + public slots: + virtual void slotUpdateConfiguration(); + virtual void removeItem(); + + protected: + /** + * Convenience functionality provided for components in a port shape + * (such as ParallelPortComponent and SerialPortComponent). + */ + void drawPortShape( QPainter & p ); + virtual void itemPointsChanged(); + virtual void updateAttachedPositioning(); + virtual void initPainter( QPainter &p ); + /** + * Untranforms the painter from the matrix. This *must* be called after doing + * initPainter( QPainter &p ); + */ + virtual void deinitPainter( QPainter &p ); + /** + * This creates a set of nodes with their internal IDs set to those in QStringList pins. + * The pins are in a DIP arrangement, and are spaced width() apart. + */ + void initDIP( const QStringList & pins ); + /** + * Creates the DIP symbol: + * @li constructs rectangular shape + * @li puts on text labels in appropriate positions from QStringList pins + */ + void initDIPSymbol( const QStringList & pins, int width ); + /** + * Create 1 pin on the left of the component, placed half way down if h1 is + * -1 - else at the position of h1. + */ + void init1PinLeft( int h1 = -1 ); + /** + * Create 2 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init2PinLeft( int h1 = -1, int h2 = -1 ); + /** + * Create 3 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init3PinLeft( int h1 = -1, int h2 = -1, int h3 = -1 ); + /** + * Create 4 pins on the left of the component, either spread out, or at the + * given heights. + */ + void init4PinLeft( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); + /** + * Create 1 pin on the right of the component, placed half way down if h1 is + * -1 - else at the position of h1. + */ + void init1PinRight( int h1 = -1 ); + /** + * Create 2 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init2PinRight( int h1 = -1, int h2 = -1 ); + /** + * Create 3 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init3PinRight( int h1 = -1, int h2 = -1, int h3 = -1 ); + /** + * Create 4 pins on the right of the component, either spread out, or at the + * given heights. + */ + void init4PinRight( int h1 = -1, int h2 = -1, int h3 = -1, int h4 = -1 ); + /** + * When we remove an element, we have to rebuild the list of inter-dependent + * nodes. (when adding elements, we just call setInterDependent). + */ + void rebuildPinInterDepedence(); + + // Pointers to commonly used nodes + ECNode * m_pPNode[4]; + ECNode * m_pNNode[4]; + + QGuardedPtr<CircuitDocument> m_pCircuitDocument; + int m_angleDegrees; + bool b_flipped; + + private: + /** + * Convenience function for calling both setInterCircuitDependent and + * setInterGroundDependent. + * @param it Which pins are inter-dependent needs to be recorded in case + * this information is later needed in rebuildPinInterDependence. + */ + void setInterDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * Sets all pins independent of each other. + */ + void setAllPinsInterIndependent(); + /** + * The given pins will affect the simulation of each other. Therefore, they + * will need to be simulated in the same circuit. + */ + void setInterCircuitDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * If any of the given pins are ground, then that will affect whether + * any of the other pins can be ground. + */ + void setInterGroundDependent( ElementMapList::iterator it, const QValueList<Pin*> & pins ); + /** + * List of ElementMaps; which contain information on the pins associated + * with the element as well as the dependence between the pins for that + * element. + * @see ElementMap + */ + ElementMapList m_elementMapList; + /** + * The switches used by the component. + */ + SwitchList m_switchList; + /** + * @return an iterator to the element in m_elementMapList + */ + ElementMapList::iterator handleElement( Element *e, const QValueList<Pin*> & pins ); +}; + +#endif diff --git a/src/electronics/components/Makefile.am b/src/electronics/components/Makefile.am new file mode 100644 index 0000000..c388cf6 --- /dev/null +++ b/src/electronics/components/Makefile.am @@ -0,0 +1,33 @@ +INCLUDES = -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/electronics -I$(top_srcdir)/src/electronics/components \ + -I$(top_srcdir)/src/electronics/simulation -I$(top_srcdir)/src/gui -I$(top_srcdir)/src/languages \ + -I$(top_srcdir)/src/micro $(glib_cflags) $(all_includes) + +METASOURCES = AUTO +noinst_HEADERS = ecresistor.h ecled.h ecdiode.h ecsevensegment.h eckeypad.h \ + eccapacitor.h ec555.h eccurrentsource.h ecfixedvoltage.h ecbcdto7segment.h \ + ecsignallamp.h ecclockinput.h ecpotentiometer.h ecopamp.h ecvoltagesource.h \ + eccurrentsignal.h ecvoltagesignal.h ecground.h multiinputgate.h fulladder.h multiplexer.h \ + demultiplexer.h externalconnection.h ecsubcircuit.h meter.h probe.h resistordip.h \ + addac.h dependentsource.h flipflop.h toggleswitch.h pushswitch.h ram.h \ + discretelogic.h piccomponent.h piccomponentpin.h binarycounter.h bidirled.h \ + matrixdisplay.h bussplitter.h matrixdisplaydriver.h magnitudecomparator.h \ + serialportcomponent.h parallelportcomponent.h inductor.h ecbjt.h rotoswitch.h + +noinst_LTLIBRARIES = libcomponents.la +libcomponents_la_SOURCES = ecresistor.cpp ecled.cpp ecdiode.cpp \ + ecsevensegment.cpp eckeypad.cpp eccapacitor.cpp ec555.cpp eccurrentsource.cpp \ + ecfixedvoltage.cpp ecbcdto7segment.cpp ecsignallamp.cpp ecclockinput.cpp \ + ecpotentiometer.cpp ecopamp.cpp ecvoltagesource.cpp eccurrentsignal.cpp ecvoltagesignal.cpp \ + ecground.cpp multiinputgate.cpp fulladder.cpp multiplexer.cpp demultiplexer.cpp \ + externalconnection.cpp ecsubcircuit.cpp meter.cpp probe.cpp resistordip.cpp addac.cpp \ + dependentsource.cpp flipflop.cpp toggleswitch.cpp pushswitch.cpp ram.cpp discretelogic.cpp \ + piccomponent.cpp piccomponentpin.cpp binarycounter.cpp bidirled.cpp matrixdisplay.cpp \ + bussplitter.cpp matrixdisplaydriver.cpp magnitudecomparator.cpp serialportcomponent.cpp \ + parallelportcomponent.cpp inductor.cpp ecbjt.cpp rotoswitch.cpp + +libcomponents_la_PCH = AUTO + + +libcomponents_la_LIBADD =\ + $(top_builddir)/src/electronics/simulation/libelements.la diff --git a/src/electronics/components/addac.cpp b/src/electronics/components/addac.cpp new file mode 100644 index 0000000..a58e2d8 --- /dev/null +++ b/src/electronics/components/addac.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "addac.h" +#include "ecnode.h" +#include "logic.h" +#include "libraryitem.h" +#include "pin.h" +#include "voltagepoint.h" + +#include <cmath> +#include <kiconloader.h> +#include <klocale.h> + + +Item* ADC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ADC( (ICNDocument*)itemDocument, newItem, id ); +} + + +Item* DAC::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new DAC( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ADC::libraryItem() +{ + return new LibraryItem( + "ec/adc", + i18n("Analog-Digital"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + ADC::construct + ); +} + + +LibraryItem* DAC::libraryItem() +{ + return new LibraryItem( + "ec/dac", + i18n("Digital-Analog"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + DAC::construct + ); +} + + +//BEGIN class ADDAC +ADDAC::ADDAC( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + m_numBits = 0; + m_range = 0; + + createProperty( "numBits", Variant::Type::Int ); + property("numBits")->setCaption( i18n("Number Bits") ); + property("numBits")->setMinValue(2); + property("numBits")->setMaxValue(max_ADDAC_bits); + property("numBits")->setValue(2); + + createProperty( "range", Variant::Type::Double ); + property("range")->setCaption( i18n("Input Range") ); + property("range")->setUnit("V"); + property("range")->setMinValue(-1e12); + property("range")->setMaxValue(1e12); + property("range")->setValue(5); +} + +ADDAC::~ADDAC() +{ +} + + +void ADDAC::dataChanged() +{ + m_range = dataDouble("range"); + initPins(); +} + +//END class ADDAC + + + + +//BEGIN class ADC +ADC::ADC( ICNDocument *icnDocument, bool newItem, const char *id ) + : ADDAC( icnDocument, newItem, (id) ? id : "adc" ) +{ + m_name = i18n("ADC"); + m_desc = i18n("Converts an analog signal into a digital output."); + + for ( int i=0; i<max_ADDAC_bits; ++i ) + m_logic[i] = 0l; + + m_realNode = 0l; +} + +ADC::~ADC() +{ +} + + +void ADC::stepNonLogic() +{ + double floatBitValue = m_realNode->pin()->voltage() * (std::pow( 2, double(m_numBits) )-1.) / m_range; + double roundBitValue = std::floor( floatBitValue+0.5 ); + + if ( roundBitValue < 0 ) + { + for ( int i = 0; i<m_numBits; ++i ) + m_logic[i]->setHigh(false); + return; + } + + uint roundedBitValue = uint(roundBitValue); + for ( int i = 0; i<m_numBits; ++i ) + m_logic[i]->setHigh( roundedBitValue & ( 1 << i ) ); +} + + +void ADC::initPins() +{ + int numBits = dataInt("numBits"); + + if ( numBits < 2 ) + numBits = 2; + else if ( numBits > max_ADDAC_bits ) + numBits = max_ADDAC_bits; + + if ( numBits == m_numBits ) + return; + + QStringList pins; + + int inPos = (numBits-1+(numBits%2))/2; + for ( int i=0; i<inPos; ++i ) + pins += ""; + + pins += "In"; + + for ( int i=inPos+1; i<numBits; ++i ) + pins += ""; + + for ( int i=numBits-1; i>=0; --i ) + pins += QString::number(i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if (!m_realNode) + m_realNode = ecNodeWithID("In"); + + + if ( numBits > m_numBits ) + { + for ( int i=m_numBits; i<numBits; ++i ) + { + ECNode *node = ecNodeWithID( QString::number(i) ); + m_logic[i] = createLogicOut( node, false ); + } + } + else + { + for ( int i=numBits; i<m_numBits; ++i ) + { + QString id = QString::number(i); + removeDisplayText(id); + removeElement( m_logic[i], false ); + removeNode(id); + m_logic[i] = 0l; + } + } + + m_numBits = numBits; +} +//END class ADC + + + + +//BEGIN class DAC +DAC::DAC( ICNDocument *icnDocument, bool newItem, const char *id ) + : ADDAC( icnDocument, newItem, (id) ? id : "dac" ) +{ + m_name = i18n("DAC"); + m_desc = i18n("Converts a digital input to an analog output signal."); + + for ( int i=0; i<max_ADDAC_bits; ++i ) + m_logic[i] = 0l; + + m_voltagePoint = 0l; +} + + +DAC::~DAC() +{ +} + + +void DAC::stepNonLogic() +{ + uint value = 0; + for ( int i=0; i<m_numBits; ++i ) + value |= ( m_logic[i]->isHigh() ? 1 : 0 ) << i; + +// double valueAsDouble = double(value); +// double powChange = std::pow( double(m_numBits), 2 )-1.; +// m_voltagePoint->setVoltage( m_range * valueAsDouble / powChange ); + m_voltagePoint->setVoltage( m_range * double(value) / (std::pow( 2, double(m_numBits) )-1.) ); +} + + +void DAC::initPins() +{ + int numBits = dataInt("numBits"); + + if ( numBits < 2 ) + numBits = 2; + else if ( numBits > max_ADDAC_bits ) + numBits = max_ADDAC_bits; + + if ( numBits == m_numBits ) + return; + + QStringList pins; + + for ( int i=0; i<numBits; ++i ) + pins += QString::number(i); + + int inPos = (numBits+1+(numBits%2))/2; + for ( int i=numBits-1; i>=inPos; --i ) + pins += ""; + + pins += "Out"; + + for ( int i=inPos-2; i>=0; --i ) + pins += ""; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if (!m_voltagePoint) + m_voltagePoint = createVoltagePoint( ecNodeWithID("Out"), 0. ); + + if ( numBits > m_numBits ) + { + for ( int i=m_numBits; i<numBits; ++i ) + { + ECNode *node = ecNodeWithID( QString::number(i) ); + m_logic[i] = createLogicIn(node); + } + } + else + { + for ( int i=numBits; i<m_numBits; ++i ) + { + QString id = QString::number(i); + removeDisplayText(id); + removeElement( m_logic[i], false ); + removeNode(id); + m_logic[i] = 0l; + } + } + + m_numBits = numBits; +} +//END class DAC + diff --git a/src/electronics/components/addac.h b/src/electronics/components/addac.h new file mode 100644 index 0000000..3d69e88 --- /dev/null +++ b/src/electronics/components/addac.h @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ADDAC_H +#define ADDAC_H + +#include "component.h" + +const int max_ADDAC_bits = 32; + +/** +@author David Saxton +*/ +class ADDAC : public Component +{ +public: + public: + ADDAC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ADDAC(); + virtual bool canFlip() const { return true; } + + protected: + void dataChanged(); + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins() = 0; + + int m_numBits; + double m_range; +}; + + +/** +@author David Saxton + */ +class ADC : public ADDAC +{ + public: + ADC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ADC(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins(); + + LogicOut *m_logic[max_ADDAC_bits]; + ECNode *m_realNode; +}; + + +/** +@author David Saxton + */ +class DAC : public ADDAC +{ + public: + DAC( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~DAC(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * Add / remove pins according to the number of outputs the user has requested + */ + virtual void initPins(); + + LogicIn *m_logic[max_ADDAC_bits]; + VoltagePoint *m_voltagePoint; +}; + + +#endif diff --git a/src/electronics/components/bidirled.cpp b/src/electronics/components/bidirled.cpp new file mode 100644 index 0000000..92e242d --- /dev/null +++ b/src/electronics/components/bidirled.cpp @@ -0,0 +1,157 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "bidirled.h" +#include "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* BiDirLED::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BiDirLED( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* BiDirLED::libraryItem() +{ + return new LibraryItem ( + "ec/bidir_led", + i18n("Bidirectional LED"), + i18n("Outputs"), + "bidirled.png", + LibraryItem::lit_component, + BiDirLED::construct + ); +} + +BiDirLED::BiDirLED( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "bidir_led" ) +{ + m_name = i18n("Bidirectional LED"); + m_desc = i18n("Bidrectional Light Emitting Diode"); + m_bDynamicContent = true; + + setSize( -8, -16, 16, 32 ); + init1PinLeft(); + init1PinRight(); + setSize( -8, -24, 24, 40 ); + + m_pDiode[0] = createDiode( m_pNNode[0], m_pPNode[0] ); + m_pDiode[1] = createDiode( m_pPNode[0], m_pNNode[0] ); + + avg_brightness[0] = avg_brightness[1] = 255; + lastUpdatePeriod = 0.; + r[0]=r[1]=g[0]=g[1]=b[0]=b[1]=0; + last_brightness[0] = last_brightness[1] = 255; + + createProperty( "0-color1", Variant::Type::Color ); + property("0-color1")->setCaption( i18n("Color 1") ); + property("0-color1")->setColorScheme( ColorCombo::LED ); + + createProperty( "0-color2", Variant::Type::Color ); + property("0-color2")->setCaption( i18n("Colour 2") ); + property("0-color2")->setColorScheme( ColorCombo::LED ); +} + +BiDirLED::~BiDirLED() +{ +} + +void BiDirLED::dataChanged() +{ + QString colors[] = { "0-color1", "0-color2" }; + for ( unsigned i = 0; i < 2; i++ ) + { + QColor color = dataColor(colors[i]); + r[i] = color.red(); + g[i] = color.green(); + b[i] = color.blue(); + r[i] /= 0x100; + g[i] /= 0x100; + b[i] /= 0x100; + } +} + +void BiDirLED::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + lastUpdatePeriod += interval; + + for ( unsigned i = 0; i < 2; i++ ) + avg_brightness[i] += ECLed::brightness(m_pDiode[i]->current())*interval; +} + +void BiDirLED::drawShape( QPainter &p ) +{ + initPainter(p); + + for ( unsigned i = 0; i < 2; i++ ) + { + uint _b; + if ( lastUpdatePeriod == 0. ) + _b = last_brightness[i]; + + else + { + _b = uint(avg_brightness[i]/lastUpdatePeriod); + last_brightness[i] = _b; + } + avg_brightness[i] = 0.; + + p.setBrush( QColor( uint(255-(255-_b)*(1-r[i])), uint(255-(255-_b)*(1-g[i])), uint(255-(255-_b)*(1-b[i])) ) ); + + + QPointArray pa(3); + if ( i == 0 ) + { + pa[0] = QPoint( 8, -8 ); + pa[1] = QPoint( -8, -16 ); + pa[2] = QPoint( -8, 0 ); + } + else + { + pa[0] = QPoint( -8, 8 ); + pa[1] = QPoint( 8, 0 ); + pa[2] = QPoint( 8, 16 ); + } + + pa.translate( int(x()), int(y()) ); + p.drawPolygon(pa); + p.drawPolyline(pa); + } + lastUpdatePeriod = 0.; + + // Draw the arrows indicating it's a LED + int _x = (int)x()-2; + int _y = (int)y()-21; + + p.drawLine( _x+9, _y+3, _x+12, _y ); // Tail of left arrow + p.drawLine( _x+12, _y, _x+10, _y ); // Left edge of left arrow tip + p.drawLine( _x+12, _y, _x+12, _y+2 ); // Right edge of left arrow tip + + p.drawLine( _x+12, _y+6, _x+15, _y+3 ); // Tail of right arrow + p.drawLine( _x+15, _y+3, _x+13, _y+3 ); // Left edge of right arrow tip + p.drawLine( _x+15, _y+3, _x+15, _y+5 ); // Right edge of right arrow tip + + p.drawLine( _x+10, _y, _x+15, _y+5 ); // Diagonal line that forms base of both arrow tips + + _x = int(x()); + _y = int(y()); + p.drawLine( _x+8, _y-16, _x+8, _y ); + p.drawLine( _x-8, _y, _x-8, _y+16 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/bidirled.h b/src/electronics/components/bidirled.h new file mode 100644 index 0000000..e80f4e0 --- /dev/null +++ b/src/electronics/components/bidirled.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef BIDIRLED_H +#define BIDIRLED_H + +#include <component.h> + +/** +@author David Saxton +*/ +class BiDirLED : public Component +{ + public: + BiDirLED( ICNDocument * icnDocument, bool newItem, const char *id = 0L ); + ~BiDirLED(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void dataChanged(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + private: + virtual void drawShape( QPainter &p ); + + double r[2]; + double g[2]; + double b[2]; + + double avg_brightness[2]; + uint last_brightness[2]; + double lastUpdatePeriod; + Diode *m_pDiode[2]; +}; + +#endif diff --git a/src/electronics/components/binarycounter.cpp b/src/electronics/components/binarycounter.cpp new file mode 100644 index 0000000..f278104 --- /dev/null +++ b/src/electronics/components/binarycounter.cpp @@ -0,0 +1,186 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "binarycounter.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +Item* BinaryCounter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BinaryCounter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* BinaryCounter::libraryItem() +{ + QStringList ids; + ids << "ec/binary_counter" << "ec/4_bit_binary_counter"; + return new LibraryItem( + ids, + i18n("Binary Counter"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + BinaryCounter::construct + ); +} + +BinaryCounter::BinaryCounter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "binary_counter" ) +{ + m_name = i18n("Binary Counter"); + m_desc = i18n("Holds an internal count, which changes when the clock input <i>></i> pin is pulsed.<br><br>" + "Normal operation: <i>en</i> (Enable) and <i>u/d</i> (Up/Down) are held high, <i>r</i> (Reset) is low."); + + enLogic = inLogic = rLogic = udLogic = 0L; + + b_reset = false; + b_triggerHigh = true; + b_oldIn = false; + m_value = 0; + b_en = false; + b_ud = false; + m_numBits = 0; + m_maxValue = false; + m_bDoneLogicIn = false; + + createProperty( "trig", Variant::Type::Select ); + property("trig")->setCaption( i18n("Trigger Edge") ); + property("trig")->setAllowed( QStringList::split( ',', "Rising,Falling" ) ); + property("trig")->setValue("Rising"); + + createProperty( "bitcount", Variant::Type::Int ); + property("bitcount")->setCaption( i18n("Bit Count") ); + property("bitcount")->setMinValue(1); + property("bitcount")->setMaxValue(26); + property("bitcount")->setValue(4); +} + + +BinaryCounter::~BinaryCounter() +{ +} + + +void BinaryCounter::dataChanged() +{ + initPins( dataInt("bitcount") ); + + b_triggerHigh = dataString("trig") == "Rising"; + setDisplayText( ">", b_triggerHigh ? "^>" : "_>" ); +} + + +void BinaryCounter::initPins( unsigned numBits ) +{ + if ( m_numBits == numBits ) + return; + + QStringList pins; + pins << "en" << ">" << "u/d" << "r"; + + for ( int i = 0; i < QABS(4-int(numBits)); i++ ) + pins << ""; + + for ( int i = numBits-1; i >= 0; i-- ) + pins << QChar('A'+i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + if ( m_numBits < numBits ) + { + for ( unsigned i = m_numBits; i < numBits; i++ ) + m_pLogicOut[i] = createLogicOut( ecNodeWithID( QChar('A'+i) ), false ); + } + else + { + for ( unsigned i = numBits; i < m_numBits; i++ ) + { + QString id = QChar('A'+i); + removeElement( m_pLogicOut[i], false ); + removeDisplayText(id); + removeNode(id); + } + } + + m_numBits = numBits; + m_maxValue = (1<<m_numBits)-1; + + if (!m_bDoneLogicIn) + { + enLogic = createLogicIn( ecNodeWithID("en") ); + inLogic = createLogicIn( ecNodeWithID(">") ); + rLogic = createLogicIn( ecNodeWithID("r") ); + udLogic = createLogicIn( ecNodeWithID("u/d") ); + + enLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::enStateChanged) ); + inLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::inStateChanged) ); + rLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::rStateChanged) ); + udLogic->setCallback( this, (CallbackPtr)(&BinaryCounter::udStateChanged) ); + + m_bDoneLogicIn = true; + } + + outputValue(); +} + + +void BinaryCounter::inStateChanged( bool state ) +{ + if ( (state != b_oldIn) && b_en && !b_reset && state == b_triggerHigh ) + { + m_value += (b_ud) ? 1 : -1; + + if ( m_value < 0 ) + m_value = m_maxValue; + + else if ( m_value > m_maxValue ) + m_value = 0; + + outputValue(); + } + + b_oldIn = state; +} + + +void BinaryCounter::rStateChanged( bool state ) +{ + b_reset = state; + if (b_reset) + { + m_value = 0; + outputValue(); + } +} + + +void BinaryCounter::enStateChanged( bool state ) +{ + b_en = state; +} + + +void BinaryCounter::udStateChanged( bool state ) +{ + b_ud = state; +} + + +void BinaryCounter::outputValue() +{ + for ( unsigned i = 0; i < m_numBits; i++ ) + m_pLogicOut[i]->setHigh( m_value & (1 << i) ); +} + diff --git a/src/electronics/components/binarycounter.h b/src/electronics/components/binarycounter.h new file mode 100644 index 0000000..b15762d --- /dev/null +++ b/src/electronics/components/binarycounter.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef EC4BITCOUNTER_H +#define EC4BITCOUNTER_H + +#include "component.h" +#include "logic.h" + +/** +Simple logic counter. 4 Inputs, 4 Outputs. + +Outputs (A-D) represent the stored value (0-15). +The inputs are: +@li en - Enable incrementing of value +@li in - Input (trigger high) +@li r - Reset stored value to 0 +@li ud - Up/Down increment + +@short 4 Bit Binary Counter +@author David Saxton +*/ +class BinaryCounter : public CallbackClass, public Component +{ +public: + BinaryCounter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~BinaryCounter(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool state ); // Input + void rStateChanged( bool state ); // Reset + void enStateChanged( bool state ); // Enable + void udStateChanged( bool state ); // Up/Down + void outputValue(); + void dataChanged(); + void initPins( unsigned numBits ); + + LogicIn *enLogic, *inLogic, *rLogic, *udLogic; + LogicOut * m_pLogicOut[26]; + + unsigned m_numBits; + bool b_triggerHigh; + bool b_en; // Enable + bool b_ud; // Up/Down + bool b_oldIn; + bool b_reset; + long m_value; + long m_maxValue; + bool m_bDoneLogicIn; +}; + +#endif diff --git a/src/electronics/components/bussplitter.cpp b/src/electronics/components/bussplitter.cpp new file mode 100644 index 0000000..62fbb39 --- /dev/null +++ b/src/electronics/components/bussplitter.cpp @@ -0,0 +1,133 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "bussplitter.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "wire.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* BusSplitter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new BusSplitter( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* BusSplitter::libraryItem() +{ + return new LibraryItem( + "ec/bus", + i18n("Bus"), + i18n("Connections"), + "bus.png", + LibraryItem::lit_component, + BusSplitter::construct ); +} + + +const unsigned MAX_BUS_SIZE = 10000; + + +BusSplitter::BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "Bus" ) +{ + m_name = i18n("Bus Splitter"); + m_desc = i18n("Merges several connections into one."); + + m_busSize = 0; + init1PinLeft(); + m_pInNode = m_pNNode[0]; + + createProperty( "size", Variant::Type::Int ); + property("size")->setCaption( i18n("Size") ); + property("size")->setMinValue(1); + property("size")->setMaxValue(MAX_BUS_SIZE); + property("size")->setValue(8); +} + + +BusSplitter::~BusSplitter() +{ +} + + +void BusSplitter::dataChanged() +{ + unsigned busSize = dataInt("size"); + + if ( busSize < 1 ) + busSize = 1; + + else if ( busSize > MAX_BUS_SIZE ) + busSize = MAX_BUS_SIZE; + + if ( busSize == m_busSize ) + return; + + m_pInNode->setNumPins(busSize); + + if ( busSize > m_busSize ) + { + m_pWires.resize(busSize); + for ( unsigned i = m_busSize; i < unsigned(busSize); i++ ) + { + Pin * pin = createPin( 16, 0, 180, outNodeID(i) )->pin(); + m_pWires[i] = new Wire( m_pInNode->pin(i), pin ); + } + } + else + { + for ( unsigned i = busSize; i < unsigned(m_busSize); i++ ) + { + removeNode( outNodeID(i) ); + delete m_pWires[i]; + } + m_pWires.resize(busSize); + } + m_busSize = busSize; + + // Position pins + setSize( 0, -int(m_busSize+1)*8, 8, int(m_busSize+1)*16, true ); + for ( int i = 0; i < int(m_busSize); i++ ) + m_nodeMap[ outNodeID(i) ].y = 16*i - int(m_busSize+1)*8 + 24; + m_nodeMap["n1"].y = -int(m_busSize+1)*8 + 8; + + updateAttachedPositioning(); +} + + +QString BusSplitter::outNodeID( unsigned node ) const +{ + return QString("out_%1").arg(QString::number(node)); +} + + +void BusSplitter::drawShape( QPainter &p ) +{ + initPainter(p); + +// QPen pen(p.pen()); +// pen.setWidth(); +// p.setPen(pen); + + int _x = int(x()); + int _y = int(y()); + + QRect r = m_sizeRect; + r.moveBy( _x, _y ); + p.drawRect(r); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/bussplitter.h b/src/electronics/components/bussplitter.h new file mode 100644 index 0000000..ccd1994 --- /dev/null +++ b/src/electronics/components/bussplitter.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef BUSSPLITTER_H +#define BUSSPLITTER_H + +#include <component.h> + +#include <qguardedptr.h> +#include <qvaluevector.h> + +class Wire; + +/** +@author David Saxton +*/ +class BusSplitter : public Component +{ + public: + BusSplitter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~BusSplitter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + + protected: + QString outNodeID( unsigned node ) const; + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + unsigned m_busSize; + QValueVector<QGuardedPtr<Wire> > m_pWires; + ECNode * m_pInNode; +}; + +#endif diff --git a/src/electronics/components/demultiplexer.cpp b/src/electronics/components/demultiplexer.cpp new file mode 100644 index 0000000..8a01808 --- /dev/null +++ b/src/electronics/components/demultiplexer.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "demultiplexer.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <cmath> + +Item* Demultiplexer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Demultiplexer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Demultiplexer::libraryItem() +{ + return new LibraryItem( + "ec/demultiplexer", + i18n("Demultiplexer"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + Demultiplexer::construct + ); +} + +Demultiplexer::Demultiplexer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "demultiplexer" ) +{ + m_name = i18n("Demultiplexer"); + m_desc = i18n("Seperates the input data stream into components. The value of the input is passed to the \"X\" output selected by the binary number given by the \"A\" inputs."); + + m_input = 0l; + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(8); + property("addressSize")->setValue(1); + + // For backwards compatibility + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setMinValue(-1); + property("numInput")->setValue(-1); + property("numInput")->setHidden(true); +} + +Demultiplexer::~Demultiplexer() +{ +} + + +void Demultiplexer::dataChanged() +{ + if ( hasProperty("numInput") && dataInt("numInput") != -1 ) + { + int addressSize = int( std::ceil( std::log( (double)dataInt("numInput") ) / std::log(2.0) ) ); + property("numInput")->setValue(-1); + + if ( addressSize < 1 ) + addressSize = 1; + else if ( addressSize > 8 ) + addressSize = 8; + + // This function will get called again when we set the value of numInput + property("addressSize")->setValue(addressSize); + return; + } + + if ( hasProperty("numInput") ) + { + m_variantData["numInput"]->deleteLater(); + m_variantData.remove("numInput"); + } + + initPins( unsigned(dataInt("addressSize")) ); +} + + +void Demultiplexer::inStateChanged( bool /*state*/ ) +{ + unsigned long long pos = 0; + for ( unsigned i = 0; i < m_aLogic.size(); ++i ) + { + if ( m_aLogic[i]->isHigh() ) + pos += 1 << i; + } + for ( unsigned i = 0; i < m_xLogic.size(); ++i ) + m_xLogic[i]->setHigh( (pos == i) && m_input->isHigh() ); +} + + +void Demultiplexer::initPins( unsigned newAddressSize ) +{ + unsigned oldAddressSize = m_aLogic.size(); + unsigned long long oldXLogicCount = m_xLogic.size(); + unsigned long long newXLogicCount = 1 << newAddressSize; + + if ( newXLogicCount == oldXLogicCount ) + return; + + QStringList pins; + + for ( unsigned i=0; i<newAddressSize; ++i ) + pins += "A"+QString::number(i); + for ( unsigned i=newAddressSize; i<(newXLogicCount+(newXLogicCount%2))/2; ++i ) + pins += ""; + pins += "X"; + for ( unsigned i=(newXLogicCount+(newXLogicCount%2))/2+1; i<newXLogicCount; ++i ) + pins += ""; + for ( int i=newXLogicCount-1; i>=0; --i ) + pins += "X"+QString::number(i); + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + ECNode *node; + + if (!m_input) + { + node = ecNodeWithID("X"); + m_input = createLogicIn(node); + m_input->setCallback( this, (CallbackPtr)(&Demultiplexer::inStateChanged) ); + } + + if ( newXLogicCount > oldXLogicCount ) + { + m_xLogic.resize(newXLogicCount); + for ( unsigned i = oldXLogicCount; i < newXLogicCount; ++i ) + { + node = ecNodeWithID("X"+QString::number(i)); + m_xLogic.insert( i, createLogicOut(node,false) ); + } + + m_aLogic.resize(newAddressSize); + for ( unsigned i = oldAddressSize; i < newAddressSize; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&Demultiplexer::inStateChanged) ); + } + } + else + { + for ( unsigned i = newXLogicCount; i < oldXLogicCount; ++i ) + { + QString id = "X"+QString::number(i); + removeDisplayText(id); + removeElement( m_xLogic[i], false ); + removeNode(id); + } + m_xLogic.resize(newXLogicCount); + + for ( unsigned i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newAddressSize); + } +} + diff --git a/src/electronics/components/demultiplexer.h b/src/electronics/components/demultiplexer.h new file mode 100644 index 0000000..a2b52dd --- /dev/null +++ b/src/electronics/components/demultiplexer.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef DEMULTIPLEXER_H +#define DEMULTIPLEXER_H + +#include "component.h" +#include "logic.h" + +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class Demultiplexer : public CallbackClass, public Component +{ +public: + Demultiplexer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Demultiplexer(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins( unsigned addressSize ); + + void inStateChanged( bool newState ); + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicOut> m_xLogic; + LogicIn * m_input; +}; + +#endif diff --git a/src/electronics/components/dependentsource.cpp b/src/electronics/components/dependentsource.cpp new file mode 100644 index 0000000..764cc53 --- /dev/null +++ b/src/electronics/components/dependentsource.cpp @@ -0,0 +1,314 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "dependentsource.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include "cccs.h" +#include "ccvs.h" +#include "vccs.h" +#include "vcvs.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class DependentSource +DependentSource::DependentSource( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + setSize( -16, -16, 32, 32 ); + + init2PinLeft(); + init2PinRight(); + + createProperty( "gain", Variant::Type::Double ); + property("gain")->setCaption( i18n("Gain") ); + property("gain")->setValue(1.0); + + addDisplayText( "gain", QRect( -16, -32, 32, 16 ), "" ); +} + + +DependentSource::~DependentSource() +{ +} + + +void DependentSource::drawOutline( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + // Top rectangle + p.drawRect( _x, _y+19, width(), 11 ); + + p.save(); + bool canSetCol = (p.pen().color() != Qt::color0) && (p.pen().color() != Qt::color1); + + // Bottom lines + if (canSetCol) + p.setPen( m_pNNode[1]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x, _y+40, _x+8, _y+40 ); // Left inny + + if (canSetCol) + p.setPen( m_pPNode[1]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x+width(), _y+40, _x+24, _y+40 ); // Right inny + + p.restore(); + + // Bottom diamond + QPointArray pa4(4); + pa4[0] = QPoint( _x+6, _y+40 ); + pa4[1] = QPoint( _x+16, _y+32 ); + pa4[2] = QPoint( _x+26, _y+40 ); + pa4[3] = QPoint( _x+16, _y+48 ); + p.drawPolygon(pa4); +} + + +void DependentSource::drawTopArrow( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + if ( p.pen().color() == m_selectedCol ) + p.setPen(Qt::black); + + if ( p.brush().color() == m_brushCol ) + p.setBrush(Qt::black); + + p.drawLine( _x+8, _y+24, _x+24, _y+24 ); + + QPointArray pa3(3); + pa3[0] = QPoint( _x+24, _y+24 ); + pa3[1] = QPoint( _x+19, _y+21 ); + pa3[2] = QPoint( _x+19, _y+27 ); + p.drawPolygon(pa3); +} + + +void DependentSource::drawBottomArrow( QPainter & p ) +{ + const int _x = (int)x()-16; + const int _y = (int)y()-32; + + if ( p.pen().color() == m_selectedCol ) + p.setPen(Qt::black); + + if ( p.brush().color() == m_brushCol ) + p.setBrush(Qt::black); + + p.drawLine( _x+11, _y+40, _x+21, _y+40 ); + + QPointArray pa3(3); + pa3[0] = QPoint( _x+21, _y+40 ); + pa3[1] = QPoint( _x+16, _y+37 ); + pa3[2] = QPoint( _x+16, _y+43 ); + p.drawPolygon(pa3); +} +//END class DependentSource + + +//BEGIN class ECCCCS +Item* ECCCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCCCS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCCCS::libraryItem() +{ + return new LibraryItem( + QString("ec/cccs"), + i18n("CCCS"), + i18n("Sources"), + "cccs.png", + LibraryItem::lit_component, + ECCCCS::construct ); +} + +ECCCCS::ECCCCS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "cccs" ) +{ + m_name = i18n("Current Controlled Currrent Source"); + m_cccs = createCCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECCCCS::~ECCCCS() +{ +} + +void ECCCCS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_cccs->setGain(gain); +} + +void ECCCCS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawTopArrow(p); + drawBottomArrow(p); + deinitPainter(p); +} +//END class ECCCCS + + +//BEGIN class ECCCVS +Item* ECCCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCCVS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCCVS::libraryItem() +{ + return new LibraryItem( + QString("ec/ccvs"), + i18n("CCVS"), + i18n("Sources"), + "ccvs.png", + LibraryItem::lit_component, + ECCCVS::construct ); +} + +ECCCVS::ECCCVS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "ccvs" ) +{ + m_name = i18n("Current Controlled Voltage Source"); + m_ccvs = createCCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECCCVS::~ECCCVS() +{ +} + +void ECCCVS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_ccvs->setGain(gain); +} + +void ECCCVS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawTopArrow(p); + deinitPainter(p); +} +//END class ECCCVS + + +//BEGIN class ECVCCS +Item* ECVCCS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVCCS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVCCS::libraryItem() +{ + return new LibraryItem( + QString("ec/vccs"), + i18n("VCCS"), + i18n("Sources"), + "vccs.png", + LibraryItem::lit_component, + ECVCCS::construct ); +} + +ECVCCS::ECVCCS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "vccs" ) +{ + m_name = i18n("Voltage Controlled Current Source"); + m_vccs = createVCCS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECVCCS::~ECVCCS() +{ +} + +void ECVCCS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_vccs->setGain(gain); +} + +void ECVCCS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + drawBottomArrow(p); + deinitPainter(p); +} +//END class ECVCCS + + +//BEGIN class ECVCVS +Item* ECVCVS::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVCVS( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVCVS::libraryItem() +{ + return new LibraryItem( + QString("ec/vcvs"), + i18n("VCVS"), + i18n("Sources"), + "vcvs.png", + LibraryItem::lit_component, + ECVCVS::construct ); +} + +ECVCVS::ECVCVS( ICNDocument *icnDocument, bool newItem, const char *id ) + : DependentSource( icnDocument, newItem, id ? id : "vcvs" ) +{ + m_name = i18n("Voltage Controlled Voltage Source"); + m_vcvs = createVCVS( m_pNNode[0], m_pPNode[0], m_pNNode[1], m_pPNode[1], 1. ); + m_pNNode[1]->pin()->setGroundType( Pin::gt_medium ); +} + +ECVCVS::~ECVCVS() +{ +} + +void ECVCVS::dataChanged() +{ + double gain = dataDouble("gain"); + + QString display = QString::number( gain / getMultiplier(gain), 'g', 3 ) + getNumberMag(gain) + QChar(' '); + setDisplayText( "gain", display ); + + m_vcvs->setGain(gain); +} + +void ECVCVS::drawShape( QPainter &p ) +{ + initPainter(p); + drawOutline(p); + deinitPainter(p); +} +//END class ECVCVS diff --git a/src/electronics/components/dependentsource.h b/src/electronics/components/dependentsource.h new file mode 100644 index 0000000..2bccbb7 --- /dev/null +++ b/src/electronics/components/dependentsource.h @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef DEPENDENTSOURCE_H +#define DEPENDENTSOURCE_H + +#include "component.h" + +/** +@author David Saxton +*/ +class DependentSource : public Component +{ + public: + DependentSource( ICNDocument *icnDocument, bool newItem, const char *id ); + ~DependentSource(); + virtual bool canFlip() const { return true; } + + protected: + void drawOutline( QPainter & p ); + void drawTopArrow( QPainter & p ); + void drawBottomArrow( QPainter & p ); +}; + +/** +@short Current Controlled Current Source +@author David Saxton +*/ +class ECCCCS : public DependentSource +{ + public: + ECCCCS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCCCS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + CCCS *m_cccs; +}; + +/** +@short Current Controlled Voltage Source +@author David Saxton +*/ +class ECCCVS : public DependentSource +{ + public: + ECCCVS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCCVS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + CCVS *m_ccvs; +}; + +/** +@short Voltage Controlled Current Source +@author David Saxton +*/ +class ECVCCS : public DependentSource +{ + public: + ECVCCS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVCCS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + VCCS *m_vccs; +}; + +/** +@short Voltage Controlled Voltage Source +@author David Saxton +*/ +class ECVCVS : public DependentSource +{ + public: + ECVCVS( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVCVS(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + VCVS *m_vcvs; +}; + +#endif diff --git a/src/electronics/components/discretelogic.cpp b/src/electronics/components/discretelogic.cpp new file mode 100644 index 0000000..e2e284e --- /dev/null +++ b/src/electronics/components/discretelogic.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "discretelogic.h" +#include "ecnode.h" +#include "logic.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class Inverter +Item* Inverter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Inverter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Inverter::libraryItem() +{ + QStringList ids; + ids << "ec/inverter" << "ec/not"; + return new LibraryItem( + ids, + i18n("Inverter"), + i18n("Logic"), + "not.png", + LibraryItem::lit_component, + Inverter::construct ); +} + +Inverter::Inverter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "not" ) +{ + m_name = i18n("Inverter"); + m_desc = i18n("The output is the logical inverse of the logic-input state."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pIn = createLogicIn(m_pNNode[0]); + m_pOut = createLogicOut( m_pPNode[0], true ); + + m_pIn->setCallback( this, (CallbackPtr)(&Inverter::inStateChanged) ); + inStateChanged(false); +} + + +Inverter::~Inverter() +{ +} + + +void Inverter::inStateChanged( bool newState ) +{ + (static_cast<LogicOut*>(m_pOut))->setHigh( !newState ); +} + + +void Inverter::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPointArray pa(3); + pa[0] = QPoint( _x, _y ); + pa[1] = QPoint( _x+width()-6, _y+(height()/2) ); + pa[2] = QPoint( _x, _y+height() ); + p.drawPolygon(pa); + p.drawPolyline(pa); + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 7, 7 ); + deinitPainter(p); +} +//END class Inverter + + +//BEGIN class Buffer +Item* Buffer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Buffer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Buffer::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/buffer"), + i18n("Buffer"), + i18n("Logic"), + "buffer.png", + LibraryItem::lit_component, + Buffer::construct ); +} + +Buffer::Buffer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "buffer" ) +{ + m_name = i18n("Buffer"); + m_desc = i18n("Cleans the logic input, with the output high or low depending on input trigger levels."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pIn = createLogicIn(m_pNNode[0]); + m_pOut = createLogicOut( m_pPNode[0], true ); + + m_pIn->setCallback( this, (CallbackPtr)(&Buffer::inStateChanged) ); + inStateChanged(false); +} + + +Buffer::~Buffer() +{ +} + + +void Buffer::inStateChanged( bool newState ) +{ + m_pOut->setHigh(newState); +} + + +void Buffer::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPointArray pa(3); + pa[0] = QPoint( _x, _y ); + pa[1] = QPoint( _x+width(), _y+(height()/2) ); + pa[2] = QPoint( _x, _y+height() ); + p.drawPolygon(pa); + p.drawPolyline(pa); + deinitPainter(p); +} +//END class Buffer + + +//BEGIN class ECLogicInput +Item* ECLogicInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLogicInput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLogicInput::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/logic_input"), + i18n("Logic Input"), + i18n("Logic"), + "logic_input.png", + LibraryItem::lit_component, + ECLogicInput::construct ); +} + +ECLogicInput::ECLogicInput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "logic_input" ) +{ + m_name = i18n("Logic Input"); + m_desc = i18n("Provides a user-adjustable logic state.<br><br>" + "Click to pulse high, or drag the mouse off to keep the output high."); + setSize( -8, -8, 16, 16 ); + + b_state = false; + addButton( "button", QRect( -24, -8, 16, 16 ), "", true ); + + createProperty( "useToggle", Variant::Type::Bool ); + property("useToggle")->setCaption( i18n("Use Toggle") ); + property("useToggle")->setValue(true); + + init1PinRight(); + + m_pOut = createLogicOut( m_pPNode[0], false ); +} + + +ECLogicInput::~ECLogicInput() +{ +} + + +void ECLogicInput::dataChanged() +{ + button("button")->setToggle( dataBool("useToggle") ); +} + + +void ECLogicInput::drawShape( QPainter &p ) +{ + initPainter(p); + if (b_state) + p.setBrush( QColor( 255, 166, 0 ) ); + else + p.setBrush( Qt::white ); + p.drawEllipse( (int)x()-4, (int)y()-6, 12, 12 ); + deinitPainter(p); +} + + +void ECLogicInput::buttonStateChanged( const QString &, bool state ) +{ + b_state = state; + m_pOut->setHigh(b_state); +} +//END class ECLogicInput + + +//BEGIN class ECLogicOutput +Item* ECLogicOutput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLogicOutput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLogicOutput::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/logic_output"), + i18n("Logic Output"), + i18n("Logic"), + "logic_output.png", + LibraryItem::lit_component, + ECLogicOutput::construct ); +} + +ECLogicOutput::ECLogicOutput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "logic_output" ) +{ + m_name = i18n("Logic Output"); + m_desc = i18n("Shows the logic-state of the input."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + m_pIn = createLogicIn(m_pNNode[0]); + + m_pSimulator = Simulator::self(); + + m_lastDrawState = 0.0; + m_lastSwitchTime = m_lastDrawTime = m_pSimulator->time(); + m_highTime = 0; + m_bLastState = false; + m_bDynamicContent = true; + + m_pIn->setCallback( this, (CallbackPtr)(&ECLogicOutput::inStateChanged) ); +} + +ECLogicOutput::~ECLogicOutput() +{ +} + +void ECLogicOutput::inStateChanged( bool newState ) +{ + if ( m_bLastState == newState ) + return; + + unsigned long long newTime = m_pSimulator->time(); + unsigned long long dt = newTime - m_lastSwitchTime; + + m_lastSwitchTime = newTime; + + m_bLastState = newState; + if (!newState) + { + // Gone from high to low + m_highTime += dt; + } +} + + +void ECLogicOutput::drawShape( QPainter &p ) +{ + unsigned long long newTime = m_pSimulator->time(); + unsigned long long runTime = newTime - m_lastDrawTime; + m_lastDrawTime = newTime; + + if (m_bLastState) + { + // Logic in is currently high + m_highTime += newTime - m_lastSwitchTime; + } + + double state; + + if ( runTime == 0 ) + state = m_lastDrawState; + + else + state = m_lastDrawState = double(m_highTime)/double(runTime); + + initPainter(p); + p.setBrush( QColor( 255, uint(255-state*(255-166)), uint((1-state)*255) ) ); + p.drawEllipse( int(x()-8), int(y()-8), width(), height() ); + deinitPainter(p); + + m_lastSwitchTime = newTime; + m_highTime = 0; +} +//END class ECLogicOutput + diff --git a/src/electronics/components/discretelogic.h b/src/electronics/components/discretelogic.h new file mode 100644 index 0000000..82775bf --- /dev/null +++ b/src/electronics/components/discretelogic.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef DISCRETELOGIC_H +#define DISCRETELOGIC_H + +#include "component.h" +#include "logic.h" + +class Simulator; + +/** +@short Boolean NOT +@author David Saxton +*/ +class Inverter : public CallbackClass, public Component +{ + public: + Inverter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Inverter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + LogicIn * m_pIn; + LogicOut * m_pOut; +}; + +/** +@short Buffer +@author David Saxton +*/ +class Buffer : public CallbackClass, public Component +{ +public: + Buffer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Buffer(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + LogicIn * m_pIn; + LogicOut * m_pOut; +}; + +/** +@short Boolean logic input +@author David Saxton +*/ +class ECLogicInput : public Component +{ +public: + ECLogicInput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLogicInput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + +private: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + LogicOut * m_pOut; + bool b_state; +}; + +/** +@short Boolean logic output +@author David Saxton +*/ +class ECLogicOutput : public CallbackClass, public Component +{ + public: + ECLogicOutput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLogicOutput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); + + unsigned long long m_lastDrawTime; + unsigned long long m_lastSwitchTime; + unsigned long long m_highTime; + bool m_bLastState; + + double m_lastDrawState; + LogicIn * m_pIn; + Simulator * m_pSimulator; +}; + +#endif diff --git a/src/electronics/components/ec555.cpp b/src/electronics/components/ec555.cpp new file mode 100644 index 0000000..eccf2bd --- /dev/null +++ b/src/electronics/components/ec555.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ec555.h" + +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item* EC555::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new EC555( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* EC555::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/555"), + i18n("555"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + EC555::construct + ); +} + +EC555::EC555( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "555" ) +{ + m_name = i18n("555"); + m_desc = i18n("Common timer IC"); +// m_pins = QStringList::split( ',', "Gnd,Trg,Out,Res,CV,Th,Dis,Vcc" ); +// m_pins = QStringList::split( ',', "Dis,Th,Trg,Gnd,CV,Out,Res,Vcc" ); + + old_com1 = false; + old_com2 = false; + old_q = false; + + setSize( -32, -32, 64, 64 ); + + + // Pins down left + + // Pin 7 + discharge = createPin( -40, -16, 0, "Dis" )->pin(); + addDisplayText( "dis", QRect( -32, -24, 24, 16 ), "Dis" ); + + // Pin 6 + threshold = createPin( -40, 0, 0, "Th" )->pin(); + addDisplayText( "th", QRect( -32, -8, 24, 16 ), "Th" ); + + // Pin 2 + trigger = createPin( -40, 16, 0, "Trg" )->pin(); + addDisplayText( "trg", QRect( -32, 8, 24, 16 ), "Trg" ); + + + + // Top two + + // Pin 8 + vcc = createPin( -16, -40, 90, "Vcc" )->pin(); + addDisplayText( "vcc", QRect( -24, -32, 16, 8 ), "+" ); + + // Pin 4 + reset = createPin( 16, -40, 90, "Res" )->pin(); + addDisplayText( "res", QRect( 8, -28, 16, 16 ), "Res" ); + + + + // Bottom two + + // Pin 1 + ground = createPin( -16, 40, 270, "Gnd" )->pin(); + addDisplayText( "gnd", QRect( -24, 20, 16, 8 ), "-" ); + + // Pin 5 + control = createPin( 16, 40, 270, "CV" )->pin(); + addDisplayText( "cv", QRect( 8, 12, 16, 16 ), "CV" ); + + + + // Output on right + + // Pin 3 + output = createPin( 40, 0, 180, "Out" )->pin(); + addDisplayText( "out", QRect( 8, -8, 16, 16 ), "Out" ); + + + + m_r1 = createResistance( vcc, control, 5e3 ); + m_r23 = createResistance( control, ground, 1e4 ); + m_po_sink = createResistance( output, ground, 0. ); + m_po_source = createResistance( output, vcc, 0. ); + m_po_source->setConductance(0.); + m_r_discharge = createResistance( discharge, ground, 0. ); +} + + +EC555::~EC555() +{ +} + +void EC555::stepNonLogic() +{ + double v_threshold = threshold->voltage(); + double v_control = control->voltage(); + double v_ground = ground->voltage(); + double v_trigger = trigger->voltage(); + double v_reset = reset->voltage(); + double v_vcc = vcc->voltage(); + + double v_r = (v_control+v_ground)/2; + + bool com1 = ( v_threshold == v_control ) ? old_com1 : ( v_threshold < v_control ); + bool com2 = ( v_r == v_trigger ) ? old_com2 : ( v_r > v_trigger ); + bool reset = ( v_reset >= (v_control-v_ground)/2 + v_ground ); + old_com1 = com1; + old_com2 = com2; + + bool r = ( !reset || !com1 ); + bool s = com2; + + bool q = old_q; + if ( v_vcc - v_ground >= 2.5 ) + { + if ( s && !r ) + { + q = true; + } + else if ( r && !s ) + { + q = false; + } + } + else + { + q = false; + } + old_q = q; + + + m_r_discharge->setConductance(0.); + + if (q) + { + m_po_source->setResistance(10.); + m_po_sink->setConductance(0.); + } + else + { + m_po_source->setConductance(0.); + m_po_sink->setResistance(10.); + if ( v_ground+0.7 <= v_vcc ) + { + m_r_discharge->setResistance(10.); + } + } +} + + diff --git a/src/electronics/components/ec555.h b/src/electronics/components/ec555.h new file mode 100644 index 0000000..edeb2a5 --- /dev/null +++ b/src/electronics/components/ec555.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef EC555_H +#define EC555_H + +#include "component.h" + +#include <qstringlist.h> + +/** +@short 555 IC +@author David Saxton +*/ +class EC555 : public Component +{ +public: + EC555( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~EC555(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + Pin * ground; + Pin * trigger; + Pin * output; + Pin * reset; + Pin * control; + Pin * threshold; + Pin * discharge; + Pin * vcc; + + Resistance *m_r1; + Resistance *m_r23; + Resistance *m_po_sink; + Resistance *m_po_source; + Resistance *m_r_discharge; + + bool old_com1; + bool old_com2; + bool old_q; +}; + +#endif diff --git a/src/electronics/components/ecbcdto7segment.cpp b/src/electronics/components/ecbcdto7segment.cpp new file mode 100644 index 0000000..fb210ea --- /dev/null +++ b/src/electronics/components/ecbcdto7segment.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecbcdto7segment.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +// Values for a,b,c,d,e,f,g of common-anode 7 segment display +bool numbers[16][7] = + { { 1, 1, 1, 1, 1, 1, 0 }, // 0 + { 0, 1, 1, 0, 0, 0, 0 }, // 1 + { 1, 1, 0, 1, 1, 0, 1 }, // 2 + { 1, 1, 1, 1, 0, 0, 1 }, // 3 + { 0, 1, 1, 0 ,0, 1, 1 }, // 4 + { 1, 0, 1, 1, 0, 1, 1 }, // 5 + { 1, 0, 1, 1, 1, 1, 1 }, // 6 + { 1, 1, 1, 0, 0, 0, 0 }, // 7 + { 1, 1, 1, 1, 1, 1, 1 }, // 8 + { 1, 1, 1, 0, 0, 1, 1 }, // 9 + { 1, 1, 1, 0, 1, 1, 1 }, // A + { 0, 0, 1, 1, 1, 1, 1 }, // b + { 1, 0, 0, 1, 1, 1, 0 }, // C + { 0, 1, 1, 1, 1, 0, 1 }, // d + { 1, 0, 0, 1, 1, 1, 1 }, // E + { 1, 0, 0, 0, 1, 1, 1 } }; // F + +Item* ECBCDTo7Segment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECBCDTo7Segment( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECBCDTo7Segment::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/bcd_to_seven_segment"), + i18n("BCD to 7 Segment"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + ECBCDTo7Segment::construct + ); +} + +ECBCDTo7Segment::ECBCDTo7Segment( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "bcd_to_seven_segment" ) +{ + m_name = i18n("BCD to Seven Segment"); + m_desc = i18n("Converts a binary-coded-input to a form displayable by a seven segment display.<br><br>" + "Normal operation: <i>lt</i> (Lamp Test) and the <i>rb</i> (Ripple Blanking) are held high, <i>en</i> (Enable) is held low."); + + ALogic = BLogic = CLogic = DLogic = 0L; + ltLogic = rbLogic = enLogic = 0L; + + for ( int i=0; i<7; i++ ) + { + outLogic[i] = 0L; + oldOut[i] = false; + } + + QStringList pins = QStringList::split( ',', "A,B,C,D,,lt,rb,en,d,e,f,g,,a,b,c", true ); + initDIPSymbol( pins, 48 ); + initDIP(pins); + + ALogic = createLogicIn( ecNodeWithID("A") ); + BLogic = createLogicIn( ecNodeWithID("B") ); + CLogic = createLogicIn( ecNodeWithID("C") ); + DLogic = createLogicIn( ecNodeWithID("D") ); + ltLogic = createLogicIn( ecNodeWithID("lt") ); + rbLogic = createLogicIn( ecNodeWithID("rb") ); + enLogic = createLogicIn( ecNodeWithID("en") ); + + ALogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + BLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + CLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + DLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + ltLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + rbLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + enLogic->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + + for ( uint i=0; i<7; ++i ) + { + outLogic[i] = createLogicOut( ecNodeWithID( QChar('a'+i) ), false ); + outLogic[i]->setCallback( this, (CallbackPtr)(&ECBCDTo7Segment::inStateChanged) ); + } + inStateChanged(false); +} + +ECBCDTo7Segment::~ECBCDTo7Segment() +{ +} + +void ECBCDTo7Segment::inStateChanged(bool) +{ + bool A = ALogic->isHigh(); + bool B = BLogic->isHigh(); + bool C = CLogic->isHigh(); + bool D = DLogic->isHigh(); + bool lt = ltLogic->isHigh(); // Lamp test + bool rb = rbLogic->isHigh(); // Ripple Blank + bool en = enLogic->isHigh(); // Enable (store) + + int n = A + 2*B + 4*C + 8*D; +// if ( n > 9 ) n = 0; + + bool out[7]; + + if (lt) // Lamp test + { + if (rb) // Ripple blanking + { + if (en) // Enable (store) + { + for ( int i=0; i<7; i++ ) + { + out[i] = oldOut[i]; + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = numbers[n][i]; + oldOut[i] = out[i]; + } + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = false; + } + } + } + else + { + for ( int i=0; i<7; i++ ) + { + out[i] = true; + } + } + + for ( int i=0; i<7; i++ ) + { + outLogic[i]->setHigh( out[i] ); + } +} diff --git a/src/electronics/components/ecbcdto7segment.h b/src/electronics/components/ecbcdto7segment.h new file mode 100644 index 0000000..03d4c2b --- /dev/null +++ b/src/electronics/components/ecbcdto7segment.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECBCDTO7SEGMENT_H +#define ECBCDTO7SEGMENT_H + +#include "component.h" +#include "logic.h" + +/** +@short Converts a BCD input to 7-Segment Output +@author David Saxton +*/ +class ECBCDTo7Segment : public CallbackClass, public Component +{ +public: + ECBCDTo7Segment( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECBCDTo7Segment(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + LogicIn *ALogic, *BLogic, *CLogic, *DLogic; + LogicIn *ltLogic, *rbLogic, *enLogic; + LogicOut *outLogic[7]; + + // Old values + bool oldOut[7]; +}; + +#endif diff --git a/src/electronics/components/ecbjt.cpp b/src/electronics/components/ecbjt.cpp new file mode 100644 index 0000000..b4b59f2 --- /dev/null +++ b/src/electronics/components/ecbjt.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "bjt.h" +#include "ecbjt.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item * ECBJT::constructNPN( ItemDocument * itemDocument, bool newItem, const char * id ) +{ + return new ECBJT( true, (ICNDocument*)itemDocument, newItem, id ); +} + + +Item * ECBJT::constructPNP( ItemDocument * itemDocument, bool newItem, const char * id ) +{ + return new ECBJT( false, (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ECBJT::libraryItemNPN() +{ + return new LibraryItem( + "ec/npnbjt", + i18n("NPN"), + i18n("Discrete"), + "npn.png", + LibraryItem::lit_component, + ECBJT::constructNPN ); +} + + +LibraryItem* ECBJT::libraryItemPNP() +{ + return new LibraryItem( + "ec/pnpbjt", + i18n("PNP"), + i18n("Discrete"), + "pnp.png", + LibraryItem::lit_component, + ECBJT::constructPNP ); +} + + +ECBJT::ECBJT( bool isNPN, ICNDocument * icnDocument, bool newItem, const char * id ) + : Component( icnDocument, newItem, id ? id : (isNPN ? "npnbjt" : "pnpbjt") ) +{ + m_bIsNPN = isNPN; + if ( m_bIsNPN ) + m_name = i18n("NPN Transistor"); + else + m_name = i18n("PNP Transistor"); + + setSize( -8, -8, 16, 16 ); + m_pBJT = createBJT( createPin( 8, -16, 90, "c" ), createPin( -16, 0, 0, "b" ), createPin( 8, 16, 270, "e" ), m_bIsNPN ); + + BJTSettings s; // will be created with the default settings + + Variant * v = createProperty( "I_S", Variant::Type::Double ); + v->setCaption("Saturation Current"); + v->setUnit("A"); + v->setMinValue(1e-20); + v->setMaxValue(1e-0); + v->setValue( s.I_S ); + v->setAdvanced(true); + + v = createProperty( "N_F", Variant::Type::Double ); + v->setCaption( i18n("Forward Coefficient") ); + v->setMinValue(1e0); + v->setMaxValue(1e1); + v->setValue( s.N_F ); + v->setAdvanced(true); + + v = createProperty( "N_R", Variant::Type::Double ); + v->setCaption( i18n("Reverse Coefficient") ); + v->setMinValue(1e0); + v->setMaxValue(1e1); + v->setValue( s.N_R ); + v->setAdvanced(true); + + v = createProperty( "B_F", Variant::Type::Double ); + v->setCaption( i18n("Forward Beta") ); + v->setMinValue(1e-1); + v->setMaxValue(1e3); + v->setValue( s.B_F ); + v->setAdvanced(true); + + v = createProperty( "B_R", Variant::Type::Double ); + v->setCaption( i18n("Reverse Beta") ); + v->setMinValue(1e-1); + v->setMaxValue(1e3); + v->setValue( s.B_R ); + v->setAdvanced(true); +} + +ECBJT::~ECBJT() +{ +} + + +void ECBJT::dataChanged() +{ + BJTSettings s; + s.I_S = dataDouble( "I_S" ); + s.N_F = dataDouble( "N_F" ); + s.N_R = dataDouble( "N_R" ); + s.B_F = dataDouble( "B_F" ); + s.B_R = dataDouble( "B_R" ); + + m_pBJT->setBJTSettings( s ); +} + + +void ECBJT::drawShape( QPainter &p ) +{ + const int _x = int(x()); + const int _y = int(y()); + + initPainter(p); + + p.drawLine( _x-8, _y-8, _x-8, _y+8 ); + p.drawLine( _x+8, _y-8, _x-8, _y ); + p.drawLine( _x+8, _y+8, _x-8, _y ); + + QPointArray pa(3); + if ( m_bIsNPN ) + { + pa[0] = QPoint( _x+6, _y+7 ); + pa[1] = QPoint( _x+2, _y+8 ); + pa[2] = QPoint( _x+6, _y+3 ); + } + else + { + pa[0] = QPoint( _x-7, _y+1 ); + pa[1] = QPoint( _x-4, _y+5 ); + pa[2] = QPoint( _x-2, _y ); + } + p.setBrush( p.pen().color() ); + p.drawPolygon(pa); + + deinitPainter(p); +} diff --git a/src/electronics/components/ecbjt.h b/src/electronics/components/ecbjt.h new file mode 100644 index 0000000..17a63e9 --- /dev/null +++ b/src/electronics/components/ecbjt.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECBJT_H +#define ECBJT_H + +#include "component.h" + +class BJT; + +/** +@short Simulates a BJT +@author David Saxton +*/ +class ECBJT : public Component +{ + public: + ECBJT( bool isNPN, ICNDocument *icnDocument, bool newItem, const char * id = 0L ); + ~ECBJT(); + virtual bool canFlip() const { return true; } + + static Item * constructNPN( ItemDocument * itemDocument, bool newItem, const char * id ); + static Item * constructPNP( ItemDocument * itemDocument, bool newItem, const char * id ); + static LibraryItem * libraryItemNPN(); + static LibraryItem * libraryItemPNP(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + bool m_bIsNPN; + BJT * m_pBJT; +}; + +#endif diff --git a/src/electronics/components/eccapacitor.cpp b/src/electronics/components/eccapacitor.cpp new file mode 100644 index 0000000..76a09b0 --- /dev/null +++ b/src/electronics/components/eccapacitor.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "capacitance.h" +#include "eccapacitor.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCapacitor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCapacitor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCapacitor::libraryItem() +{ + return new LibraryItem( + "ec/capacitor", + i18n("Capacitor"), + i18n("Discrete"), + "capacitor.png", + LibraryItem::lit_component, + ECCapacitor::construct + ); +} + +ECCapacitor::ECCapacitor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "capacitor" ) +{ + m_name = i18n("Capacitor"); + m_desc = i18n("Stores electrical charge.<br><br>" + "The voltage across the capacitor and capacitance are related by <i>Charge = Capacitance x Voltage</i>."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_capacitance = createCapacitance( m_pNNode[0], m_pPNode[0], 0.001 ); + + createProperty( "Capacitance", Variant::Type::Double ); + property("Capacitance")->setCaption( i18n("Capacitance") ); + property("Capacitance")->setUnit("F"); + property("Capacitance")->setMinValue(1e-12); + property("Capacitance")->setMaxValue(1e12); + property("Capacitance")->setValue(1e-3); + + addDisplayText( "capacitance", QRect( -8, -24, 16, 16 ), "", false ); +} + +ECCapacitor::~ECCapacitor() +{ +} + +void ECCapacitor::dataChanged() +{ + double capacitance = dataDouble("Capacitance"); + + QString display = QString::number( capacitance / getMultiplier(capacitance), 'g', 3 ) + getNumberMag(capacitance) + "F"; + setDisplayText( "capacitance", display ); + + m_capacitance->setCapacitance(capacitance); +} + +void ECCapacitor::drawShape( QPainter &p ) +{ + initPainter(p); + + int _y = (int)y()-8; + int _x = (int)x()-8; + + QPen pen; + pen.setWidth(1); + pen.setColor( p.pen().color() ); + p.setPen(pen); + p.drawRect( _x, _y, 5, 16 ); + p.drawRect( _x+11, _y, 5, 16 ); + + + deinitPainter(p); +// p.drawPolyline( areaPoints() ); +} + + diff --git a/src/electronics/components/eccapacitor.h b/src/electronics/components/eccapacitor.h new file mode 100644 index 0000000..4dd0ee6 --- /dev/null +++ b/src/electronics/components/eccapacitor.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECCAPACITOR_H +#define ECCAPACITOR_H + +#include "component.h" + +class Capacitance; +class ECNode; + +/** +@short Capacitor +Simple capacitor +@author David Saxton +*/ +class ECCapacitor : public Component +{ +public: + ECCapacitor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCapacitor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + + Capacitance * m_capacitance; +}; + +#endif diff --git a/src/electronics/components/ecclockinput.cpp b/src/electronics/components/ecclockinput.cpp new file mode 100644 index 0000000..7f1ec9c --- /dev/null +++ b/src/electronics/components/ecclockinput.cpp @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecclockinput.h" + +#include "logic.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> + +static inline uint roundDouble( const double x ) +{ + return uint(std::floor(x+0.5)); +} + +Item* ECClockInput::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECClockInput( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECClockInput::libraryItem() +{ + return new LibraryItem( + QString("ec/clock_input"), + i18n("Clock Input"), + i18n("Logic"), + "clockinput.png", + LibraryItem::lit_component, + ECClockInput::construct ); +} + +ECClockInput::ECClockInput( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "clock_input" ) +{ + m_name = i18n("Clock Input"); + m_desc = i18n("A square-wave generator, outputing logical high/low at repeating time intervals."); + setSize( -16, -8, 32, 16 ); + + m_lastSetTime = 0; + m_time = 0; + m_high_time = 0; + m_low_time = 0; + m_period = 0; + m_bSetStepCallbacks = true; + m_pSimulator = Simulator::self(); + + for ( unsigned i = 0; i < 1000; i++ ) + { + ComponentCallback * ccb = new ComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepCallback) ); + m_pComponentCallback[i] = new LinkedList<ComponentCallback>(ccb); + } + + init1PinRight(); + m_pOut = createLogicOut( m_pPNode[0], false ); + + createProperty( "low-time", Variant::Type::Double ); + property("low-time")->setUnit("S"); + property("low-time")->setCaption( i18n("Low Time") ); + property("low-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); + property("low-time")->setValue(0.5); + + createProperty( "high-time", Variant::Type::Double ); + property("high-time")->setUnit("S"); + property("high-time")->setCaption( i18n("High Time") ); + property("high-time")->setMinValue(1.0/LOGIC_UPDATE_RATE); + property("high-time")->setValue(0.5); + + addDisplayText( "freq", QRect( -16, -24, 32, 14 ), "", false ); +} + + +ECClockInput::~ECClockInput() +{ + for ( unsigned i = 0; i < 1000; i++ ) + { + delete m_pComponentCallback[i]->data(); + delete m_pComponentCallback[i]; + } +} + + +void ECClockInput::dataChanged() +{ + m_high_time = roundDouble(dataDouble("high-time")*LOGIC_UPDATE_RATE); + m_low_time = roundDouble(dataDouble("low-time")*LOGIC_UPDATE_RATE); + m_period = m_low_time+m_high_time; + + const double frequency = 1./(dataDouble("high-time")+dataDouble("low-time")); + QString display = QString::number( frequency / getMultiplier(frequency), 'g', 3 ) + getNumberMag(frequency) + "Hz"; + setDisplayText( "freq", display ); + + bool setStepCallbacks = m_period > 100; + if ( setStepCallbacks != m_bSetStepCallbacks ) + { + m_bSetStepCallbacks = setStepCallbacks; + if (setStepCallbacks) + m_pSimulator->detachComponentCallbacks(this); + else + m_pSimulator->attachComponentCallback( this, (VoidCallbackPtr)(&ECClockInput::stepLogic) ); + } + + m_bLastStepCallbackOut = false; + m_lastSetTime = m_pSimulator->time(); +} + + +void ECClockInput::stepLogic() +{ + m_pOut->setHigh( m_time>m_low_time ); + + if ( ++m_time > m_period ) { + m_time -= int(m_time/m_period)*m_period; + } +} + + +void ECClockInput::stepCallback() +{ + m_pOut->setHigh(m_bLastStepCallbackOut); + m_bLastStepCallbackOut = !m_bLastStepCallbackOut; +} + + +void ECClockInput::stepNonLogic() +{ + if (!m_bSetStepCallbacks) + return; + + bool addingHigh = !m_bLastStepCallbackOut; + + //TODO 100 number shouldn't be hard-coded + long long lowerTime = m_pSimulator->time(); + long long upperTime = lowerTime + 100; + + long long upTo = m_lastSetTime; + + while ( upTo + (addingHigh?m_high_time:m_low_time) < upperTime ) + { + upTo += addingHigh ? m_high_time : m_low_time; + addingHigh = !addingHigh; + + long long at = upTo-lowerTime; + if ( at >= 0 && at < 100 ) + m_pSimulator->addStepCallback( at, m_pComponentCallback[at] ); + } + + m_lastSetTime = upTo; +} + + +void ECClockInput::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-10; + int _y = (int)y()-8; + + p.drawRect( _x-6, _y, 32, 16 ); + + p.drawLine( _x, _y+8, _x, _y+4 ); + p.drawLine( _x, _y+4, _x+4, _y+4 ); + p.drawLine( _x+4, _y+4, _x+4, _y+12 ); + p.drawLine( _x+4, _y+12, _x+8, _y+12 ); + p.drawLine( _x+8, _y+12, _x+8, _y+4 ); + p.drawLine( _x+8, _y+4, _x+12, _y+4 ); + p.drawLine( _x+12, _y+4, _x+12, _y+12 ); + p.drawLine( _x+12, _y+12, _x+16, _y+12 ); + p.drawLine( _x+16, _y+12, _x+16, _y+4 ); + p.drawLine( _x+16, _y+4, _x+20, _y+4 ); + p.drawLine( _x+20, _y+4, _x+20, _y+8 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/ecclockinput.h b/src/electronics/components/ecclockinput.h new file mode 100644 index 0000000..c7f7d14 --- /dev/null +++ b/src/electronics/components/ecclockinput.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECCLOCKINPUT_H +#define ECCLOCKINPUT_H + +#include "component.h" + +class ComponentCallback; +class Simulator; + +template <typename T> +class LinkedList; + +/** +@short Boolean clock input +@author David Saxton +*/ +class ECClockInput : public Component +{ +public: + ECClockInput( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECClockInput(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + void stepCallback(); + void stepLogic(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + uint m_time; + uint m_high_time; + uint m_low_time; + uint m_period; + long long m_lastSetTime; + LogicOut * m_pOut; + bool m_bSetStepCallbacks; + bool m_bLastStepCallbackOut; + Simulator * m_pSimulator; + LinkedList<ComponentCallback> * m_pComponentCallback[1000]; +}; + +#endif diff --git a/src/electronics/components/eccurrentsignal.cpp b/src/electronics/components/eccurrentsignal.cpp new file mode 100644 index 0000000..e6dc351 --- /dev/null +++ b/src/electronics/components/eccurrentsignal.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "currentsignal.h" +#include "eccurrentsignal.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCurrentSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCurrentSignal( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCurrentSignal::libraryItem() +{ + return new LibraryItem( + QString("ec/ac_current"), + i18n("Current Signal"), + i18n("Sources"), + "currentsignal.png", + LibraryItem::lit_component, + ECCurrentSignal::construct ); +} + +ECCurrentSignal::ECCurrentSignal( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "current_signal" ) +{ + m_name = i18n("Current Signal"); + m_desc = i18n("Provides a variety of current signals"); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); + m_currentSignal = createCurrentSignal( m_pNNode[0], m_pPNode[0], 0. ); + m_currentSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, 50. ); + + createProperty( "1-frequency", Variant::Type::Double ); + property("1-frequency")->setCaption( i18n("Frequency") ); + property("1-frequency")->setUnit("Hz"); + property("1-frequency")->setMinValue(1e-9); + property("1-frequency")->setMaxValue(1e3); + property("1-frequency")->setValue(50.0); + + createProperty( "1-current", Variant::Type::Double ); + property("1-current")->setCaption( i18n("Current Range") ); + property("1-current")->setUnit("A"); + property("1-current")->setMinValue(-1e12); + property("1-current")->setMaxValue(1e12); + property("1-current")->setValue(0.02); + + addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); + addDisplayText( "current", QRect( -16, -24, 32, 16 ), "" ); +} + + +ECCurrentSignal::~ECCurrentSignal() +{ +} + +void ECCurrentSignal::dataChanged() +{ + const double current = dataDouble("1-current"); + const double frequency = dataDouble("1-frequency"); + + QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; + setDisplayText( "current", display ); + + m_currentSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, frequency ); + m_currentSignal->setCurrent(current); +} + +void ECCurrentSignal::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); + deinitPainter(p); +} + diff --git a/src/electronics/components/eccurrentsignal.h b/src/electronics/components/eccurrentsignal.h new file mode 100644 index 0000000..88dd276 --- /dev/null +++ b/src/electronics/components/eccurrentsignal.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECCURRENTSIGNAL_H +#define ECCURRENTSIGNAL_H + +#include "component.h" + +/** +@short Provides a current signal (sinusoidal, square, etc) +@author David Saxton +*/ +class ECCurrentSignal : public Component +{ +public: + ECCurrentSignal( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCurrentSignal(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + CurrentSignal *m_currentSignal; +}; + +#endif diff --git a/src/electronics/components/eccurrentsource.cpp b/src/electronics/components/eccurrentsource.cpp new file mode 100644 index 0000000..76ecf3e --- /dev/null +++ b/src/electronics/components/eccurrentsource.cpp @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "currentsource.h" +#include "eccurrentsource.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECCurrentSource::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCurrentSource( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCurrentSource::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/current_source"), + i18n("Current Source"), + i18n("Sources"), + "current_source.png", + LibraryItem::lit_component, + ECCurrentSource::construct ); +} + +ECCurrentSource::ECCurrentSource( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "current_source" ) +{ + m_name = i18n("Current Source"); + m_desc = i18n("Provides a fixed current source."); + setSize( -16, -8, 24, 24 ); + + init1PinLeft(8); + init1PinRight(8); + m_pNNode[0]->pin()->setGroundType( Pin::gt_low ); + + m_currentSource = createCurrentSource( m_pNNode[0], m_pPNode[0], 0. ); + + createProperty( "current", Variant::Type::Double ); + property("current")->setCaption( i18n("Current") ); + property("current")->setUnit("A"); + property("current")->setMinValue(-1e12); + property("current")->setMaxValue(1e12); + property("current")->setValue(0.02); + + addDisplayText("current", QRect( -16, -16, 24, 0 ), "" ); +} + + +ECCurrentSource::~ECCurrentSource() +{ +} + +void ECCurrentSource::dataChanged() +{ + double current = dataDouble("current"); + m_currentSource->setCurrent(current); + + QString display = QString::number( current / getMultiplier(current), 'g', 3 ) + getNumberMag(current) + "A"; + setDisplayText("current", display ); +} + +void ECCurrentSource::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-24; + + // Top arrow indicating current direction + p.drawLine( _x+width(), _y+19, _x, _y+19 ); + p.drawLine( _x+width(), _y+19, _x+width()-3, _y+16 ); + p.drawLine( _x+width(), _y+19, _x+width()-3, _y+22 ); + + // Double circules + p.drawEllipse( _x, _y+24, 16, 16 ); + p.drawEllipse( _x+8, _y+24, 16, 16 ); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/eccurrentsource.h b/src/electronics/components/eccurrentsource.h new file mode 100644 index 0000000..6332eba --- /dev/null +++ b/src/electronics/components/eccurrentsource.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECCURRENTSOURCE_H +#define ECCURRENTSOURCE_H + +#include "component.h" + +/** +@short Fixed current source +@author David Saxton +*/ +class ECCurrentSource : public Component +{ +public: + ECCurrentSource( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCurrentSource(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + CurrentSource *m_currentSource; +}; + +#endif diff --git a/src/electronics/components/ecdiode.cpp b/src/electronics/components/ecdiode.cpp new file mode 100644 index 0000000..1d63bce --- /dev/null +++ b/src/electronics/components/ecdiode.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "diode.h" +#include "ecdiode.h" +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECDiode::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDiode( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDiode::libraryItem() +{ + return new LibraryItem( + "ec/diode", + i18n("Diode"), + i18n("Discrete"), + "diode.png", + LibraryItem::lit_component, + ECDiode::construct ); +} + +ECDiode::ECDiode( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "diode" ) +{ + m_name = i18n("Diode"); + m_desc = i18n("Allows current to flow in the direction indicated by the arrow when a certain voltage difference has been reached."); + + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_diode = createDiode( m_pNNode[0], m_pPNode[0] ); + + DiodeSettings ds; // it will have the default properties that we use + + createProperty( "I_S", Variant::Type::Double ); + property("I_S")->setCaption("Saturation Current"); + property("I_S")->setUnit("A"); + property("I_S")->setMinValue(1e-20); + property("I_S")->setMaxValue(1e-0); + property("I_S")->setValue( ds.I_S ); + property("I_S")->setAdvanced(true); + + createProperty( "N", Variant::Type::Double ); + property("N")->setCaption( i18n("Emission Coefficient") ); + property("N")->setMinValue(1e0); + property("N")->setMaxValue(1e1); + property("N")->setValue( ds.N ); + property("N")->setAdvanced(true); + + createProperty( "V_B", Variant::Type::Double ); + property("V_B")->setCaption( i18n("Breakdown Voltage") ); + property("V_B")->setUnit("V"); + property("V_B")->setMinAbsValue(1e-5); + property("V_B")->setMaxValue(1e10); + property("V_B")->setValue( ds.V_B ); + property("V_B")->setAdvanced(true); + +// createProperty( "R", Variant::Type::Double ); +// property("R")->setCaption( i18n("Series Resistance") ); +// property("R")->setUnit( QChar(0x3a9) ); +// property("R")->setMinValue(1e-5); +// property("R")->setMaxValue(1e0); +// property("R")->setValue( ds.R ); +// property("R")->setAdvanced(true); +} + + +ECDiode::~ECDiode() +{ +} + + +void ECDiode::dataChanged() +{ + DiodeSettings ds; + + ds.I_S = dataDouble("I_S"); + ds.V_B = dataDouble("V_B"); + ds.N = dataDouble("N"); +// ds.R = dataDouble("R"); + + m_diode->setDiodeSettings( ds ); +} + + +void ECDiode::drawShape( QPainter & p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + QPointArray pa(3); + pa[0] = QPoint( 8, 0 ); + pa[1] = QPoint( -8, -8 ); + pa[2] = QPoint( -8, 8 ); + pa.translate( _x, _y ); + p.drawPolygon(pa); + p.drawPolyline(pa); + + p.drawLine( _x+8, _y-8, _x+8, _y+8 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/ecdiode.h b/src/electronics/components/ecdiode.h new file mode 100644 index 0000000..fa8d034 --- /dev/null +++ b/src/electronics/components/ecdiode.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECDIODE_H +#define ECDIODE_H + +#include "component.h" + +/** +@short Simple diode +@author David Saxton +*/ +class ECDiode : public Component +{ +public: + ECDiode( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDiode(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void drawShape( QPainter & p ); + void dataChanged(); + Diode *m_diode; +}; + +#endif diff --git a/src/electronics/components/ecfixedvoltage.cpp b/src/electronics/components/ecfixedvoltage.cpp new file mode 100644 index 0000000..06eb707 --- /dev/null +++ b/src/electronics/components/ecfixedvoltage.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecfixedvoltage.h" + +#include "ecnode.h" +#include "voltagepoint.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECFixedVoltage::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECFixedVoltage( (ICNDocument*)itemDocument, newItem, id ); +} +LibraryItem* ECFixedVoltage::libraryItem() +{ + return new LibraryItem( + "ec/fixed_voltage", + i18n("Fixed Voltage"), + i18n("Sources"), + "voltage.png", + LibraryItem::lit_component, + ECFixedVoltage::construct ); +} + +ECFixedVoltage::ECFixedVoltage( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "fixed_voltage" ) +{ + m_name = i18n("Fixed Voltage"); + m_desc = i18n("Provides a fixed voltage point to connect components to."); + setSize( -8, -8, 16, 16 ); + + init1PinRight(); + m_voltagePoint = createVoltagePoint( m_pPNode[0], 5.0 ); + + addDisplayText( "voltage", QRect( -24, -20, width()+32, 12 ), "" ); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setUnit("V"); + property("voltage")->setCaption( i18n("Voltage") ); + property("voltage")->setMinValue(-1e15); + property("voltage")->setMaxValue(1e15); + property("voltage")->setValue(5.0); +} + +ECFixedVoltage::~ECFixedVoltage() +{ +} + +void ECFixedVoltage::dataChanged() +{ + const double voltage = dataDouble("voltage"); + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); + m_voltagePoint->setVoltage(voltage); +} + +void ECFixedVoltage::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = int(x()); + int _y = int(y()); + p.drawEllipse( _x-4, _y-4, 8, 8 ); + p.setPen( m_pPNode[0]->isSelected() ? m_selectedCol : Qt::black ); + p.drawLine( _x+4, _y, _x+8, _y ); + deinitPainter(p); +} + diff --git a/src/electronics/components/ecfixedvoltage.h b/src/electronics/components/ecfixedvoltage.h new file mode 100644 index 0000000..ba20358 --- /dev/null +++ b/src/electronics/components/ecfixedvoltage.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECFIXEDVOLTAGE_H +#define ECFIXEDVOLTAGE_H + +#include "component.h" + +/** +@short Fixed voltage source +@author David Saxton +*/ +class ECFixedVoltage : public Component +{ +public: + ECFixedVoltage( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECFixedVoltage(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); + void dataChanged(); + VoltagePoint *m_voltagePoint; +}; + +#endif diff --git a/src/electronics/components/ecground.cpp b/src/electronics/components/ecground.cpp new file mode 100644 index 0000000..efbfc41 --- /dev/null +++ b/src/electronics/components/ecground.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecground.h" + +#include "ecnode.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECGround::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECGround( (ICNDocument*)itemDocument, newItem, id ); +} +LibraryItem* ECGround::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/ground"), + i18n("Ground (0V)"), + i18n("Sources"), + "ground.png", + LibraryItem::lit_component, + ECGround::construct ); +} + +ECGround::ECGround( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ground" ) +{ + m_name = i18n("Ground"); + m_desc = i18n("Ground (0V) point"); + setSize( -8, -8, 16, 16 ); + init1PinRight(); + m_pPNode[0]->pin()->setGroundType( Pin::gt_always ); + setAngleDegrees(270); +} + +ECGround::~ECGround() +{ +} + +void ECGround::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + QPen pen; + pen.setWidth(2); + pen.setColor( p.pen().color() ); + p.setPen(pen); + p.drawLine( _x+15, _y, _x+15, _y+16 ); + p.drawLine( _x+10, _y+3, _x+10, _y+13 ); + p.drawLine( _x+5, _y+6, _x+5, _y+10 ); + deinitPainter(p); +} + + + diff --git a/src/electronics/components/ecground.h b/src/electronics/components/ecground.h new file mode 100644 index 0000000..0cf97bc --- /dev/null +++ b/src/electronics/components/ecground.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECGROUND_H +#define ECGROUND_H + +#include "component.h" + +/** +@short Fixed voltage source +@author David Saxton +*/ +class ECGround : public Component +{ +public: + ECGround( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECGround(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/electronics/components/eckeypad.cpp b/src/electronics/components/eckeypad.cpp new file mode 100644 index 0000000..89d2659 --- /dev/null +++ b/src/electronics/components/eckeypad.cpp @@ -0,0 +1,199 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "eckeypad.h" +#include "libraryitem.h" +#include "switch.h" + +#include "ecnode.h" +#include <klocale.h> + +Item* ECKeyPad::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECKeyPad( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECKeyPad::libraryItem() +{ + return new LibraryItem( + QString("ec/keypad"), + i18n("Keypad"), + i18n("Switches"), + "keypad.png", + LibraryItem::lit_component, + ECKeyPad::construct ); +} + +const QString text[4][9] = + { { "1","2","3","A","E","I","M","Q","U" }, + { "4","5","6","B","F","J","N","R","V" }, + { "7","8","9","C","G","K","O","S","W" }, + { "*","0","#","D","H","L","P","T","X" } }; + +ECKeyPad::ECKeyPad( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "keypad" ) +{ + m_name = i18n("Keypad"); + m_desc = i18n("Provides a numeric array of Push-to-Make switches, with 4 rows and a configurable number of columns."); + + createProperty( "useToggles", Variant::Type::Bool ); + property("useToggles")->setCaption( i18n("Use Toggles") ); + property("useToggles")->setValue(false); + + createProperty( "numCols", Variant::Type::Int ); + property("numCols")->setCaption( i18n("Columns") ); + property("numCols")->setMinValue(3); + property("numCols")->setMaxValue(9); + property("numCols")->setValue(3); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + for ( int i = 0; i < 4; i++ ) + createPin( 0, -32+i*24, 0, QString("row_%1").arg(QString::number(i)) ); + + m_numCols = 0; +} + + +ECKeyPad::~ECKeyPad() +{ +} + + +QString ECKeyPad::buttonID( int row, int col ) const +{ + return QString("b_%1_%2").arg(QString::number(row)).arg(QString::number(col)); +} + + +int ECKeyPad::sideLength( unsigned numButtons ) const +{ + return 8 + 24*numButtons; +} + + +void ECKeyPad::dataChanged() +{ + initPins( dataInt("numCols") ); + + bool useToggle = dataBool("useToggles"); + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = 0; j < m_numCols; j++ ) + { + button( buttonID(i,j) )->setToggle(useToggle); + m_switch[i][j]->setBounce( bounce, bouncePeriod_ms ); + } + } +} + + +void ECKeyPad::initPins( unsigned numCols ) +{ + if ( numCols < 3 ) + numCols = 3; + else if ( numCols > 9 ) + numCols = 9; + + if ( numCols == m_numCols ) + return; + + int w = sideLength(numCols); + int h = sideLength(4); + setSize( -int(w/16)*8, -int(h/16)*8, w, h, true ); + + if ( numCols > m_numCols ) + { + // Adding columns + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = m_numCols; j < numCols; j++ ) + addButton( buttonID(i,j), QRect( 0, 0, 20, 20 ), text[i][j] ); + } + + ECNode * cols[numCols]; + + for ( unsigned j = m_numCols; j < numCols; j++ ) + cols[j] = createPin( 0, 64, 270, "col_" + QString::number(j) ); + + for ( unsigned i = 0; i < 4; i++ ) + { + ECNode * row = ecNodeWithID("row_"+QString::number(i)); + for ( unsigned j = m_numCols; j < numCols; j++ ) + m_switch[i][j] = createSwitch( cols[j], row, true ); + } + } + else + { + // Remove columns + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = numCols; j < m_numCols; j++ ) + removeWidget( buttonID(i,j) ); + } + + for ( unsigned j = numCols; j < m_numCols; j++ ) + removeNode( "col_" + QString::number(j) ); + + for ( unsigned i = 0; i < 4; i++ ) + { + for ( unsigned j = m_numCols; j < numCols; j++ ) + removeSwitch( m_switch[i][j] ); + } + } + + //BEGIN Update Positions + m_numCols = numCols; + + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < int(m_numCols); j++ ) + { + widgetWithID( buttonID(i,j) )->setOriginalRect( + QRect( offsetX() + 6 + 24*j, offsetY() + 6 + 24*i, 20, 20 ) ); + } + } + + for ( int i = 0; i < 4; i++ ) + m_nodeMap["row_" + QString::number(i)].x = width()+offsetX(); + + for ( int j = 0; j < int(m_numCols); j++ ) + m_nodeMap["col_" + QString::number(j)].x = 24*j+offsetX()+16; + + updateAttachedPositioning(); + //END Update Positions +} + + +void ECKeyPad::buttonStateChanged( const QString &id, bool state ) +{ + if ( !id.startsWith("b_") ) + return; + + QStringList tags = QStringList::split( '_', id ); + const int i = tags[1].toInt(); + const int j = tags[2].toInt(); + m_switch[i][j]->setState( state ? Switch::Closed : Switch::Open ); +} diff --git a/src/electronics/components/eckeypad.h b/src/electronics/components/eckeypad.h new file mode 100644 index 0000000..18a497c --- /dev/null +++ b/src/electronics/components/eckeypad.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003,2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECKEYPAD_H +#define ECKEYPAD_H + +#include "component.h" + +/** +@short 4x3 PTM Keypad +@author David Saxton +*/ +class ECKeyPad : public Component +{ + public: + ECKeyPad( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECKeyPad(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual bool canFlip() const { return true; } + + protected: + virtual void dataChanged(); + void initPins( unsigned numCols); + QString buttonID( int row, int col ) const; + int sideLength( unsigned numButtons ) const; + + Switch *m_switch[4][11]; + unsigned m_numCols; +}; + +#endif diff --git a/src/electronics/components/ecled.cpp b/src/electronics/components/ecled.cpp new file mode 100644 index 0000000..c865de6 --- /dev/null +++ b/src/electronics/components/ecled.cpp @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECLed::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECLed( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECLed::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/led"), + i18n("LED"), + i18n("Outputs"), + "led.png", + LibraryItem::lit_component, + ECLed::construct + ); +} + +ECLed::ECLed( ICNDocument *icnDocument, bool newItem, const char *id ) + : ECDiode( icnDocument, newItem, (id) ? id : "led" ) +{ + m_bDynamicContent = true; + m_name = i18n("LED"); + m_desc = i18n("Light Emitting Diode"); + setSize( -8, -16, 24, 24, true ); + avg_brightness = 255; + lastUpdatePeriod = 0.; + r=g=b=0; + last_brightness = 255; + + createProperty( "0-color", Variant::Type::Color ); + property("0-color")->setCaption( i18n("Color") ); + property("0-color")->setColorScheme( ColorCombo::LED ); +} + +ECLed::~ECLed() +{ +} + +void ECLed::dataChanged() +{ + QColor color = dataColor("0-color"); + r = color.red(); + g = color.green(); + b = color.blue(); + r /= 0x100; + g /= 0x100; + b /= 0x100; +} + +void ECLed::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + avg_brightness += brightness(m_diode->current())*interval; + lastUpdatePeriod += interval; +} + +void ECLed::drawShape( QPainter &p ) +{ + int _x = int(x()); + int _y = int(y()); + + initPainter(p); + + //BEGIN draw "Diode" part + uint _b; + if ( lastUpdatePeriod == 0. ) + _b = last_brightness; + else + { + _b = (uint)(avg_brightness/lastUpdatePeriod); + last_brightness = _b; + } + avg_brightness = 0.; + lastUpdatePeriod = 0.; + + p.setBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ); + + QPointArray pa(3); + pa[0] = QPoint( 8, 0 ); + pa[1] = QPoint( -8, -8 ); + pa[2] = QPoint( -8, 8 ); + pa.translate( _x, _y ); + p.drawPolygon(pa); + p.drawPolyline(pa); + + p.drawLine( _x+8, _y-8, _x+8, _y+8 ); + //END draw "Diode" part + + + + //BEGIN draw "Arrows" part + p.drawLine( _x+7, _y-10, _x+10, _y-13 ); // Tail of left arrow + p.drawLine( _x+10, _y-13, _x+8, _y-13 ); // Left edge of left arrow tip + p.drawLine( _x+10, _y-13, _x+10, _y-11 ); // Right edge of left arrow tip + + p.drawLine( _x+10, _y-7, _x+13, _y-10 ); // Tail of right arrow + p.drawLine( _x+13, _y-10, _x+11, _y-10 ); // Left edge of right arrow tip + p.drawLine( _x+13, _y-10, _x+13, _y-8 ); // Right edge of right arrow tip + + p.drawLine( _x+8, _y-13, _x+13, _y-8 ); // Diagonal line that forms base of both arrow tips + //END draw "Arrows" part1 + + + deinitPainter(p); +} + + +uint ECLed::brightness( double i ) +{ + if ( i > 0.018 ) return 0; + if ( i < 0.002 ) return 255; + return (uint)(255*(1-((i-0.002)/0.016))); +} + diff --git a/src/electronics/components/ecled.h b/src/electronics/components/ecled.h new file mode 100644 index 0000000..ce24598 --- /dev/null +++ b/src/electronics/components/ecled.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECLED_H +#define ECLED_H + +#include "component.h" +#include "ecdiode.h" + +/** +@short Simulates a LED +@author David Saxton +*/ +class ECLed : public ECDiode +{ +public: + ECLed( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECLed(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + /** + * Returns the brightness for the given current, from 255 (off) -> 0 (on) + */ + static uint brightness( double i ); + + virtual void dataChanged(); + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + + double r, g, b; + + double avg_brightness; + uint last_brightness; + double lastUpdatePeriod; +}; + +#endif diff --git a/src/electronics/components/ecopamp.cpp b/src/electronics/components/ecopamp.cpp new file mode 100644 index 0000000..dbb7457 --- /dev/null +++ b/src/electronics/components/ecopamp.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecopamp.h" + +#include "ecnode.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECOpAmp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECOpAmp( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* ECOpAmp::libraryItem() +{ + return new LibraryItem( + "ec/opamp", + i18n("Op Amp"), + i18n("Integrated Circuits"), + "opamp.png", + LibraryItem::lit_component, + ECOpAmp::construct ); +} + + +ECOpAmp::ECOpAmp( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "opamp" ) +{ + m_name = i18n("Operational Amplifier"); + m_desc = i18n("Ideal amplifier"); + + QPointArray pa(3); + pa[0] = QPoint( -16, -16 ); + pa[1] = QPoint( 16, 0 ); + pa[2] = QPoint( -16, 16 ); + setItemPoints( pa, true ); + + init2PinLeft( -8, 8 ); + init1PinRight(); + createOpAmp( m_pNNode[0], m_pPNode[0], m_pNNode[1] ); +} + + +ECOpAmp::~ECOpAmp() +{ +} + + +void ECOpAmp::drawShape( QPainter & p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + QPointArray pa(3); + pa[0] = QPoint( _x-16, _y-16 ); + pa[1] = QPoint( _x+16, _y ); + pa[2] = QPoint( _x-16, _y+16 ); + + p.drawPolygon(pa); + p.drawPolyline(pa); + + // Plus symbol + p.drawLine( _x-9, _y-8, _x-9, _y-2 ); + p.drawLine( _x-12, _y-5, _x-6, _y-5 ); + + // Minus symbol + p.drawLine( _x-11, _y+6, _x-7, _y+6 ); + + deinitPainter(p); +} + + diff --git a/src/electronics/components/ecopamp.h b/src/electronics/components/ecopamp.h new file mode 100644 index 0000000..6b6b147 --- /dev/null +++ b/src/electronics/components/ecopamp.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECOPAMP_H +#define ECOPAMP_H + +#include "component.h" + +/** +@short Operational Amplifier +@author David Saxton +*/ +class ECOpAmp : public Component +{ + public: + ECOpAmp( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECOpAmp(); + + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void drawShape( QPainter & p ); +}; + +#endif diff --git a/src/electronics/components/ecpotentiometer.cpp b/src/electronics/components/ecpotentiometer.cpp new file mode 100644 index 0000000..db0fedd --- /dev/null +++ b/src/electronics/components/ecpotentiometer.cpp @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "ecnode.h" +#include "ecpotentiometer.h" +#include "libraryitem.h" +#include "resistance.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstyle.h> + +Item* ECPotentiometer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPotentiometer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPotentiometer::libraryItem() +{ + return new LibraryItem( + "ec/potentiometer", + i18n("Potentiometer"), + i18n("Discrete"), + "potentiometer.png", + LibraryItem::lit_component, + ECPotentiometer::construct ); +} + +ECPotentiometer::ECPotentiometer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "potentiometer" ) +{ + m_name = i18n("Potentiometer"); + m_desc =i18n("Consists of a resistor connected to the end pins, with a central pin connected at an adjustable point along the resistor"); + setSize( -16, -16, 40, 32 ); + + m_p1 = createPin( 32, 0, 180, "p1" ); + + m_sliderProp = 0.0; + m_resistance = 5000.; + m_r1 = createResistance( createPin( -8, -24, 90, "n1" ), m_p1, 1. ); + m_r2 = createResistance( createPin( -8, 24, 270, "n2" ), m_p1, 1. ); + + Slider * s = addSlider( "slider", 0, 100, 5, 50, Qt::Vertical, QRect( 0, -16, 16, 32 ) ); + m_pSlider = static_cast<QSlider*>(s->widget()); + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setMinValue(1e-6); + property("resistance")->setValue(1e5); + + addDisplayText( "res", QRect( -56, -8, 40, 16 ), "" ); +} + +ECPotentiometer::~ECPotentiometer() +{ +} + +void ECPotentiometer::dataChanged() +{ + m_resistance = dataDouble("resistance"); + + QString display = QString::number( m_resistance / getMultiplier(m_resistance), 'g', 3 ) + getNumberMag(m_resistance) + QChar(0x3a9); + setDisplayText( "res", display ); + + sliderValueChanged( "slider", slider("slider")->value() ); +} + +void ECPotentiometer::sliderValueChanged( const QString &id, int newValue ) +{ + if ( id != "slider" ) + return; + + m_sliderProp = (newValue-50.0)/100.0; + + m_r1->setResistance( m_resistance*(double)newValue/100. ); + m_r2->setResistance( m_resistance*(double)(100.-newValue)/100. ); +} + +void ECPotentiometer::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = int(x()); + int _y = int(y()); + + p.drawRect( _x-14, _y-16, 12, 32 ); + + QPointArray pa(3); + pa[0] = QPoint( 0, 0 ); + pa[1] = QPoint( 4, -3 ); + pa[2] = QPoint( 4, 3 ); + + int space = m_pSlider->style().pixelMetric( QStyle::PM_SliderSpaceAvailable, m_pSlider ); + int base_y = _y + (( angleDegrees() == 0 || angleDegrees() == 270 ) ? 1 : -1) * int( space * m_sliderProp ); + + pa.translate( _x+16, base_y ); + + QColor c = m_p1->isSelected() ? m_selectedCol : black; + + p.setPen(c); + p.setBrush(c); + p.drawPolygon(pa); + + p.drawLine( _x+20, base_y, _x+24, base_y ); + p.drawLine( _x+24, base_y, _x+24, _y ); + + deinitPainter(p); +} + + + diff --git a/src/electronics/components/ecpotentiometer.h b/src/electronics/components/ecpotentiometer.h new file mode 100644 index 0000000..e7ca83b --- /dev/null +++ b/src/electronics/components/ecpotentiometer.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECPOTENTIOMETER_H +#define ECPOTENTIOMETER_H + +#include "component.h" + +class QSlider; + +/** +@short Potentiometer +@author David Saxton +*/ +class ECPotentiometer : public Component +{ +public: + ECPotentiometer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPotentiometer(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void sliderValueChanged( const QString &id, int newValue ); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + + ECNode * m_p1; + Resistance *m_r1, *m_r2; + double m_resistance; + double m_sliderProp; + QSlider * m_pSlider; +}; +#endif diff --git a/src/electronics/components/ecresistor.cpp b/src/electronics/components/ecresistor.cpp new file mode 100644 index 0000000..e4ee7a3 --- /dev/null +++ b/src/electronics/components/ecresistor.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecresistor.h" + +#include "libraryitem.h" +#include "resistance.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECResistor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECResistor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECResistor::libraryItem() +{ + return new LibraryItem( + "ec/resistor", + i18n("Resistor"), + i18n("Discrete"), + "resistor.png", + LibraryItem::lit_component, + ECResistor::construct ); +} + +ECResistor::ECResistor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "resistor" ) +{ + m_name = i18n("Resistor"); + m_desc = i18n("Limits the flow of current, obeying Ohms Law"); + setSize( -16, -8, 32, 16 ); + + init1PinLeft(); + init1PinRight(); + m_resistance = createResistance( m_pPNode[0], m_pNNode[0], 1. ); + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setValue(1e4); + property("resistance")->setMinValue(1e-6); + + addDisplayText( "res", QRect( -16, -22, 32, 12 ), "", false ); +} + +ECResistor::~ECResistor() +{ +} + +void ECResistor::dataChanged() +{ + double resistance = dataDouble("resistance"); + + QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); + setDisplayText( "res", display ); + + m_resistance->setResistance(resistance); +} + +void ECResistor::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawRect( (int)x()-16, (int)y()-6, width(), 12 ); + deinitPainter(p); +} + + diff --git a/src/electronics/components/ecresistor.h b/src/electronics/components/ecresistor.h new file mode 100644 index 0000000..17e799e --- /dev/null +++ b/src/electronics/components/ecresistor.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECRESISTOR_H +#define ECRESISTOR_H + +#include "component.h" + +/** +@short Simple resistor +@author David Saxton +*/ +class ECResistor : public Component +{ + public: + ECResistor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECResistor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + Resistance * m_resistance; +}; + +#endif diff --git a/src/electronics/components/ecsevensegment.cpp b/src/electronics/components/ecsevensegment.cpp new file mode 100644 index 0000000..ec35776 --- /dev/null +++ b/src/electronics/components/ecsevensegment.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "ecsevensegment.h" +#include "libraryitem.h" +#include "simulator.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +Item* ECSevenSegment::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSevenSegment( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSevenSegment::libraryItem() +{ + return new LibraryItem( + "ec/seven_segment", + i18n("Seven Segment"), + i18n("Outputs"), + "seven_segment.png", + LibraryItem::lit_component, + ECSevenSegment::construct ); +} + +ECSevenSegment::ECSevenSegment( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "seven_segment" ) +{ + m_name = i18n("Seven Segment LED"); + m_desc = i18n("A seven segment display with a decimal point. This can be configured to either have a common cathode or a common anode."); + m_bDynamicContent = true; + + QStringList pins = QStringList::split( ',', "g,f,e,d,"+QString(QChar(0xB7))+",c,b,a" ); + + createProperty( "0-color", Variant::Type::Color ); + property("0-color")->setCaption( i18n("Color") ); + property("0-color")->setColorScheme( ColorCombo::LED ); + + createProperty( "diode-polarity", Variant::Type::Select ); + property("diode-polarity")->setCaption( i18n("Configuration") ); + property("diode-polarity")->setAllowed( QStringList::split(',',"Common Cathode,Common Anode") ); + property("diode-polarity")->setValue("Common Cathode"); + + for ( int i=0; i<8; i++ ) + { + m_diodes[i] = 0L; + m_nodes[i] = 0L; + avg_brightness[i] = 0.; + last_brightness[i] = 255; + } + m_nNode = 0L; + + lastUpdatePeriod = 0.; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + m_nNode = createPin( width()/2+offsetX(), height()+8+offsetY(), 270, "-v" ); + + for ( int i=0; i<7; i++ ) + m_nodes[i] = ecNodeWithID( QChar('a'+i) ); + + m_nodes[7] = ecNodeWithID(QChar(0xB7)); + + m_bCommonCathode = false; // Force update +} + + +ECSevenSegment::~ECSevenSegment() +{ +} + + +void ECSevenSegment::dataChanged() +{ + QColor color = dataColor("0-color"); + r = color.red(); + g = color.green(); + b = color.blue(); + r /= 0x100; + g /= 0x100; + b /= 0x100; + + bool commonCathode = dataString("diode-polarity") == "Common Cathode"; + if ( commonCathode != m_bCommonCathode ) + { + m_bCommonCathode = commonCathode; + for ( int i=0; i<7; i++ ) + { + removeElement( m_diodes[i], false ); + if (commonCathode) + m_diodes[i] = createDiode( m_nodes[i], m_nNode ); + else + m_diodes[i] = createDiode( m_nNode, m_nodes[i] ); + } + + removeElement( m_diodes[7], false ); + if (commonCathode) + m_diodes[7] = createDiode( m_nodes[7], m_nNode ); + else + m_diodes[7] = createDiode( m_nNode, m_nodes[7] ); + } + + update(); +} + + +void ECSevenSegment::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + + for ( int i=0; i<8; i++ ) { + avg_brightness[i] += ECLed::brightness( m_diodes[i]->current() )*interval; + } + + lastUpdatePeriod += interval; +} + +void ECSevenSegment::drawShape( QPainter &p ) +{ + CNItem::drawShape(p); + + initPainter(p); + + const int _width = 20; + const int _height = 32; + + const int x1 = (int)x()+offsetX() + (width()-_width)/2 - 1; + const int x2 = x1 + _width; + const int y1 = (int)y()+offsetY() + (height()-_height)/2; + const int y2 = y1 + _height/2; + const int y3 = y1 + _height; + const int ds = 2; // "Slope" + +// QPen pen; +// pen.setWidth(2); +// pen.setCapStyle(Qt::RoundCap); +// p.setPen(pen); + + if ( lastUpdatePeriod != 0. ) + { + for ( uint i=0; i<8; ++i ) + { + last_brightness[i] = (uint)(avg_brightness[i]/lastUpdatePeriod); + } + } + + double _b; + + // Top + _b = last_brightness[0]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+3+ds, y1+0, x2-3+ds, y1+0 ); + + // Top right + _b = last_brightness[1]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2+0+ds, y1+3, x2+0, y2-3 ); + + // Bottom right + _b = last_brightness[2]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2+0, y2+3, x2+0-ds, y3-3 ); + + // Bottom + _b = last_brightness[3]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x2-3-ds, y3+0, x1+3-ds, y3+0 ); + + // Bottom left + _b = last_brightness[4]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+0-ds, y3-3, x1+0, y2+3 ); + + // Top left + _b = last_brightness[5]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+0, y2-3, x1+0+ds, y1+3 ); + + // Middle + _b = last_brightness[6]; + p.setPen( QPen( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ), 2 ) ); + p.drawLine( x1+3, y2+0, x2-3, y2+0 ); + + // Decimal point + _b = last_brightness[7]; + p.setBrush( QBrush( QColor( uint(255-(255-_b)*(1-r)), uint(255-(255-_b)*(1-g)), uint(255-(255-_b)*(1-b)) ) ) ); + p.setPen( Qt::NoPen ); + p.drawPie( x2+3, y3-2, 3, 3, 0, 16*360 ); + + lastUpdatePeriod = 0.; + for ( uint i=0; i<8; ++i ) { + avg_brightness[i] = 0.; + } + + deinitPainter(p); +} diff --git a/src/electronics/components/ecsevensegment.h b/src/electronics/components/ecsevensegment.h new file mode 100644 index 0000000..7041c9a --- /dev/null +++ b/src/electronics/components/ecsevensegment.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECSEVENSEGMENT_H +#define ECSEVENSEGMENT_H + +#include "component.h" + +class Diode; +class ECNode; + +/** +@short Seven segment display component +@author David Saxton +*/ +class ECSevenSegment : public Component +{ +public: + ECSevenSegment( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSevenSegment(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + + bool m_bCommonCathode; + double lastUpdatePeriod; + double avg_brightness[8]; + uint last_brightness[8]; + Diode *m_diodes[8]; + ECNode *m_nodes[8]; + ECNode *m_nNode; + double r, g, b; +}; + +#endif diff --git a/src/electronics/components/ecsignallamp.cpp b/src/electronics/components/ecsignallamp.cpp new file mode 100644 index 0000000..c7034f7 --- /dev/null +++ b/src/electronics/components/ecsignallamp.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecnode.h" +#include "ecsignallamp.h" +#include "element.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ECSignalLamp::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSignalLamp( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSignalLamp::libraryItem() +{ + return new LibraryItem( + QString("ec/signal_lamp"), + i18n("Signal Lamp"), + i18n("Outputs"), + "signal_lamp.png", + LibraryItem::lit_component, + ECSignalLamp::construct ); +} + +ECSignalLamp::ECSignalLamp( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "signal_lamp" ) +{ + m_name = i18n("Signal Lamp"); + m_desc = i18n("A simple filament signal lamp, with a 100 ohms series resistance."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + createResistance( m_pPNode[0], m_pNNode[0], 100. ); + + advanceSinceUpdate = 0; + avgPower = 0.; + m_bDynamicContent = true; +} + +ECSignalLamp::~ECSignalLamp() +{ +} + +void ECSignalLamp::stepNonLogic() +{ + const double voltage = m_pPNode[0]->pin()->voltage()-m_pNNode[0]->pin()->voltage(); + avgPower = QABS(avgPower*advanceSinceUpdate + (voltage*voltage/100))/++advanceSinceUpdate; +} + +void ECSignalLamp::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x()); + int _y = int(y()); + + // Calculate the brightness as a linear function of power, bounded below by + // 25 milliWatts and above by 500 milliWatts. + int brightness = (avgPower<0.025) ? 255 : ((avgPower>0.5) ? 0 : (int)(255*(1-((avgPower-0.025)/0.475)))); + advanceSinceUpdate = 0; + + p.setBrush( QColor( 255, 255, brightness ) ); + p.drawEllipse( _x-8, _y-8, 16, 16 ); + + // 2*sqrt(2) = 2.828427125... + int pos = 8 - int(16/2.828); + + p.drawLine( _x-8+pos, _y-8+pos, _x+8-pos, _y+8-pos ); + p.drawLine( _x+8-pos, _y-8+pos, _x-8+pos, _y+8-pos ); + + deinitPainter(p); +} diff --git a/src/electronics/components/ecsignallamp.h b/src/electronics/components/ecsignallamp.h new file mode 100644 index 0000000..e56d724 --- /dev/null +++ b/src/electronics/components/ecsignallamp.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECSIGNALLAMP_H +#define ECSIGNALLAMP_H + +#include "component.h" + +class Resistance; + +/** +@short Signal Lamp - glows when current flows +@author David Saxton +*/ +class ECSignalLamp : public Component +{ +public: + ECSignalLamp( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSignalLamp(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + +private: + void drawShape( QPainter &p ); + double avgPower; + uint advanceSinceUpdate; +}; + +#endif diff --git a/src/electronics/components/ecsubcircuit.cpp b/src/electronics/components/ecsubcircuit.cpp new file mode 100644 index 0000000..a69e720 --- /dev/null +++ b/src/electronics/components/ecsubcircuit.cpp @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "circuitdocument.h" +#include "ecsubcircuit.h" +#include "node.h" +#include "libraryitem.h" +#include "subcircuits.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qfile.h> + +Item* ECSubcircuit::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSubcircuit( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSubcircuit::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/subcircuit"), + QString::null, + QString::null, + QString::null, + LibraryItem::lit_subcircuit, + ECSubcircuit::construct ); +} + +ECSubcircuit::ECSubcircuit( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "subcircuit" ) +{ + m_name = i18n("Subcircuit"); + + createProperty( "id", Variant::Type::Int ); + property("id")->setMinValue(1); + property("id")->setMaxValue(1<<15); + property("id")->setValue(1); + property("id")->setHidden(true); +} + + +ECSubcircuit::~ECSubcircuit() +{ +} + + +void ECSubcircuit::removeItem() +{ + emit subcircuitDeleted(); + Component::removeItem(); +} + + +void ECSubcircuit::setNumExtCon( unsigned numExtCon ) +{ + m_conNames.resize(numExtCon); + + // Remove old pins + const NodeMap::iterator nodeMapEnd = m_nodeMap.end(); + for ( NodeMap::iterator it = m_nodeMap.begin(); it != nodeMapEnd; ++it ) + { + p_icnDocument->appendDeleteList( p_icnDocument->nodeWithID(it.data().id) ); + } + p_icnDocument->flushDeleteList(); + m_nodeMap.clear(); + + QStringList pins; + for ( unsigned i=0; i<numExtCon; ++i ) + { + pins += QString::number(i); + } + + initDIPSymbol( pins, 80 ); + + // We don't want the text that the dip symbol gave us as we initialize it later... + for ( unsigned i = 0; i < numExtCon; ++i ) + removeDisplayText( QString::number(i) ); + + initDIP(pins); +} + + +void ECSubcircuit::dataChanged() +{ + int subcircuitId = dataInt("id"); + if ( subcircuitId == -1 ) { + return; + } + emit subcircuitDeleted(); + Subcircuits::initECSubcircuit( subcircuitId, this ); +} + + +void ECSubcircuit::setExtConName( unsigned numId, const QString & name ) +{ + if ( numId > m_conNames.size() ) + return; + + m_conNames[numId] = name; +} + + +void ECSubcircuit::doneSCInit() +{ + QStringList pins; + for ( unsigned i = 0; i < m_conNames.size(); ++i ) + pins << m_conNames[i]; + initDIPSymbol( pins, 80 ); +} + + +void ECSubcircuit::drawShape( QPainter &p ) +{ + Component::drawShape(p); +} + + +#include "ecsubcircuit.moc" + + + diff --git a/src/electronics/components/ecsubcircuit.h b/src/electronics/components/ecsubcircuit.h new file mode 100644 index 0000000..eaf21ec --- /dev/null +++ b/src/electronics/components/ecsubcircuit.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECSUBCIRCUIT_H +#define ECSUBCIRCUIT_H + +#include <component.h> + +#include <qvaluevector.h> + +/** +"Container" component for subcircuits +@author David Saxton +*/ +class ECSubcircuit : public Component +{ +Q_OBJECT +public: + ECSubcircuit( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSubcircuit(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + /** + * Create numExtCon nodes, deleting any old ones + */ + void setNumExtCon( unsigned numExtCon ); + /** + * Give the connecting node at position numId the given name + */ + void setExtConName( unsigned numId, const QString & name ); + /** + * Called from SubcircuitData once the subcircuit has been fully attached + */ + void doneSCInit(); + +public slots: + virtual void removeItem(); + +signals: + /** + * Emitted when the current subcircuit is deleted + */ + void subcircuitDeleted(); + +protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + QValueVector<QString> m_conNames; +}; + +#endif diff --git a/src/electronics/components/ecvoltagesignal.cpp b/src/electronics/components/ecvoltagesignal.cpp new file mode 100644 index 0000000..c338f36 --- /dev/null +++ b/src/electronics/components/ecvoltagesignal.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecnode.h" +#include "ecvoltagesignal.h" +#include "libraryitem.h" +#include "pin.h" +#include "simulator.h" +#include "voltagesignal.h" + +#include <klocale.h> +#include <qpainter.h> + +const double conductance = 1e5; // Internal resistance + +Item* ECVoltageSignal::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVoltageSignal( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVoltageSignal::libraryItem() +{ + return new LibraryItem( + QString("ec/voltage_signal"), + i18n("Voltage Signal"), + i18n("Sources"), + "voltagesignal.png", + LibraryItem::lit_component, + ECVoltageSignal::construct ); +} + +ECVoltageSignal::ECVoltageSignal( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "voltage_signal" ) +{ + m_name = i18n("Voltage Signal"); + m_desc = i18n("Provides a variety of voltage signals."); + setSize( -8, -8, 16, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); + m_voltageSignal = createVoltageSignal( m_pNNode[0], m_pPNode[0], 0. ); + m_voltageSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, 50. ); + + createProperty( "frequency", Variant::Type::Double ); + property("frequency")->setCaption( i18n("Frequency") ); + property("frequency")->setUnit("Hz"); + property("frequency")->setMinValue(1e-9); + property("frequency")->setMaxValue(1e3); + property("frequency")->setValue(50.0); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setCaption( i18n("Voltage Range") ); + property("voltage")->setUnit("V"); + property("voltage")->setMinValue(-1e12); + property("voltage")->setMaxValue(1e12); + property("voltage")->setValue(5.0); + + addDisplayText( "~", QRect( -8, -8, 16, 16 ), "~" ); + addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); +} + + +ECVoltageSignal::~ECVoltageSignal() +{ +} + +void ECVoltageSignal::dataChanged() +{ + const double voltage = dataDouble("voltage"); + const double frequency = dataDouble("frequency"); + + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); + + m_voltageSignal->setStep( 1./LINEAR_UPDATE_RATE, ElementSignal::st_sinusoidal, frequency ); + m_voltageSignal->setVoltage(voltage); +} + + +void ECVoltageSignal::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-8, (int)y()-8, width(), height() ); + deinitPainter(p); +} + diff --git a/src/electronics/components/ecvoltagesignal.h b/src/electronics/components/ecvoltagesignal.h new file mode 100644 index 0000000..7102132 --- /dev/null +++ b/src/electronics/components/ecvoltagesignal.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECVOLTAGESIGNAL_H +#define ECVOLTAGESIGNAL_H + +#include "component.h" + +/** +@short Provides an alternating voltage source +@author David Saxton +*/ +class ECVoltageSignal : public Component +{ +public: + ECVoltageSignal( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVoltageSignal(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void dataChanged(); + + VoltageSignal *m_voltageSignal; +}; + +#endif diff --git a/src/electronics/components/ecvoltagesource.cpp b/src/electronics/components/ecvoltagesource.cpp new file mode 100644 index 0000000..4b8c543 --- /dev/null +++ b/src/electronics/components/ecvoltagesource.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecvoltagesource.h" + +#include "ecnode.h" +#include "voltagesource.h" +#include "libraryitem.h" +#include "pin.h" + +#include <klocale.h> +#include <qpainter.h> + +const double conductance = 1e5; // Internal resistance + +Item* ECCell::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECCell( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECCell::libraryItem() +{ + QStringList ids; + ids << "ec/battery" << "ec/cell"; + return new LibraryItem( + ids, + i18n("Battery"), + i18n("Sources"), + "cell.png", + LibraryItem::lit_component, + ECCell::construct ); +} + +ECCell::ECCell( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "cell" ) +{ + m_name = i18n("Battery"); + m_desc = i18n("Provides a potential-difference."); + setSize( -8, -8, 16, 16 ); + voltage = 0; + + init1PinLeft(); + init1PinRight(); + + m_pNNode[0]->pin()->setGroundType( Pin::gt_medium ); + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], voltage ); + + createProperty( "voltage", Variant::Type::Double ); + property("voltage")->setUnit("V"); + property("voltage")->setCaption( i18n("Voltage") ); + property("voltage")->setMinValue(-1e12); + property("voltage")->setMaxValue(1e12); + property("voltage")->setValue(5.0); + + addDisplayText( "voltage", QRect( -16, -24, 32, 16 ), "" ); +} + +ECCell::~ECCell() +{ +} + +void ECCell::dataChanged() +{ + voltage = dataDouble("voltage"); + m_voltageSource->setVoltage(voltage); + + QString display = QString::number( voltage / getMultiplier(voltage), 'g', 3 ) + getNumberMag(voltage) + "V"; + setDisplayText( "voltage", display ); +} + +void ECCell::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-8; + int _y = (int)y()-24; + + p.drawLine( _x, _y+20, _x, _y+28 ); + p.drawLine( _x+5, _y+16, _x+5, _y+32 ); + p.drawLine( _x+10, _y+20, _x+10, _y+28 ); + p.drawLine( _x+15, _y+16, _x+15, _y+32 ); + + deinitPainter(p); +// p.drawPolyline( areaPoints() ); +} + diff --git a/src/electronics/components/ecvoltagesource.h b/src/electronics/components/ecvoltagesource.h new file mode 100644 index 0000000..4ba87ef --- /dev/null +++ b/src/electronics/components/ecvoltagesource.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2003 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECCELL_H +#define ECCELL_H + +#include "component.h" + +/** +@short Electrical cell +Simple electrical cell that simulates a PD and internal resistance +@author David Saxton +*/ +class ECCell : public Component +{ +public: + ECCell( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECCell(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); + VoltageSource *m_voltageSource; + double voltage; +}; + +#endif diff --git a/src/electronics/components/externalconnection.cpp b/src/electronics/components/externalconnection.cpp new file mode 100644 index 0000000..596727a --- /dev/null +++ b/src/electronics/components/externalconnection.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "externalconnection.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* ExternalConnection::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ExternalConnection( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ExternalConnection::libraryItem() +{ + return new LibraryItem( + "ec/external_connection", + i18n("External Connection"), + i18n("Connections"), + "external_connection.png", + LibraryItem::lit_component, + ExternalConnection::construct ); +} + +ExternalConnection::ExternalConnection( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "external_connection" ) +{ + m_name = i18n("External Connection"); + m_desc = i18n("Point to connect the circuit to an external entity - e.g. a mechanical component or as part of a subcircuit."); + setSize( -8, -8, 16, 16 ); + + createProperty( "name", Variant::Type::Combo ); + property("name")->setCaption( i18n("Name") ); + property("name")->setValue("ExtCon"); + + init1PinLeft(); + + addDisplayText( "name", QRect( -24, 8, 3*width(), 16 ), "ExtCon" ); +} + +ExternalConnection::~ExternalConnection() +{ +} + + +void ExternalConnection::dataChanged() +{ + QString name = dataString("name"); + + QRect r( -width(), 16, 3*width(), 16 ); + setDisplayText( "name", name ); +} + + +void ExternalConnection::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()-8; + int _y = (int)y()-8; + p.drawEllipse( _x, _y, width(), height() ); + + p.drawLine( _x+3, _y+6, _x+12, _y+6 ); + p.drawLine( _x+8, _y+3, _x+12, _y+6 ); + + p.drawLine( _x+3, _y+9, _x+12, _y+9 ); + p.drawLine( _x+3, _y+9, _x+8, _y+12 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/externalconnection.h b/src/electronics/components/externalconnection.h new file mode 100644 index 0000000..9b733fc --- /dev/null +++ b/src/electronics/components/externalconnection.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef EXTERNALCONNECTION_H +#define EXTERNALCONNECTION_H + +#include <component.h> + +/** +For connecting to something "outside" - e.g. a mechanical component, or as part +of a circuit part +@author David Saxton +*/ +class ExternalConnection : public Component +{ +public: + ExternalConnection( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ExternalConnection(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void dataChanged(); + virtual void drawShape( QPainter &p ); +}; + +#endif diff --git a/src/electronics/components/flipflop.cpp b/src/electronics/components/flipflop.cpp new file mode 100644 index 0000000..5c55baf --- /dev/null +++ b/src/electronics/components/flipflop.cpp @@ -0,0 +1,347 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "flipflop.h" +#include "icndocument.h" +#include "logic.h" +#include "libraryitem.h" +#include "node.h" +#include "simulator.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class ECDFlipFlop +Item* ECDFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDFlipFlop::libraryItem() +{ + return new LibraryItem( + "ec/d_flipflop", + i18n("D Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECDFlipFlop::construct ); +} + +ECDFlipFlop::ECDFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "d_flipflop" ) +{ + m_name = i18n("D-Type Flip-Flop"); + m_desc = i18n("The output state is set from the input state when the clock is pulsed."); + + setSize( -32, -24, 64, 48 ); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_prevD[0] = m_prevD[1] = false; + m_whichPrevD = 0; + m_prevDSimTime = 0; + m_pSimulator = Simulator::self(); + + m_bPrevClock = false; + m_pD = createLogicIn( m_pNNode[0] ); + m_pClock = createLogicIn( m_pNNode[1] ); + m_pQ = createLogicOut( m_pPNode[0], false ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + setp = createLogicIn( createPin( 0, -32, 90, "set" ) ); + rstp = createLogicIn( createPin( 0, 32, 270, "rst" ) ); + + addDisplayText( "D", QRect( -32, -16, 20, 16 ), "D" ); + addDisplayText( ">", QRect( -32, 0, 20, 16 ), ">" ); + addDisplayText( "Q", QRect( 12, -16, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 12, 0, 20, 16 ), "Q'" ); + addDisplayText( "Set", QRect( -16, -20, 32, 16 ), "Set" ); + addDisplayText( "Rst", QRect( -16, 4, 32, 16 ), "Rst" ); + + m_pD->setCallback( this, (CallbackPtr)(&ECDFlipFlop::inputChanged) ); + m_pClock->setCallback( this, (CallbackPtr)(&ECDFlipFlop::clockChanged) ); + setp->setCallback( this, (CallbackPtr)(&ECDFlipFlop::asyncChanged) ); + rstp->setCallback( this, (CallbackPtr)(&ECDFlipFlop::asyncChanged) ); + + inStateChanged(false); +} + +ECDFlipFlop::~ECDFlipFlop() +{ +} + +void ECDFlipFlop::asyncChanged(bool) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + if(set || rst) + { + m_pQ->setHigh(set); + m_pQBar->setHigh(rst); + } +} + +void ECDFlipFlop::inputChanged( bool newState ) +{ + unsigned long long simTime = m_pSimulator->time(); + if ( (simTime == m_prevDSimTime) && (newState == m_prevD[m_whichPrevD]) ) + return; + + m_prevDSimTime = simTime; + m_whichPrevD = 1-m_whichPrevD; + m_prevD[m_whichPrevD] = newState; +} + +void ECDFlipFlop::clockChanged( bool newState ) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + bool fallingEdge = m_bPrevClock && !newState; + m_bPrevClock = newState; + + if( set || rst ) return; + + if (fallingEdge) + { + unsigned long long simTime = m_pSimulator->time(); + bool d = ( simTime == m_prevDSimTime ) ? m_prevD[1-m_whichPrevD] : m_prevD[m_whichPrevD]; + + m_pQ->setHigh(d); + m_pQBar->setHigh(!d); + } +} + +void ECDFlipFlop::inStateChanged(bool) +{ + // Only called when the flipflop is created. + m_pQ->setHigh(false); + m_pQBar->setHigh(true); +} +//END class ECDFlipFlop + + +//BEGIN class ECJKFlipFlop +Item* ECJKFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECJKFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECJKFlipFlop::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/jk_flipflop"), + i18n("JK Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECJKFlipFlop::construct ); +} + +ECJKFlipFlop::ECJKFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "jk_flipflop" ) +{ + m_name = i18n("JK-Type Flip-Flop"); + m_desc = i18n("The output state is set according to J and K when the clock is pulsed."); + + setSize( -32, -32, 64, 64 ); + + init3PinLeft( -16, 0, 16 ); + init2PinRight( -16, 16 ); + + m_pJ = createLogicIn( m_pNNode[0] ); + m_pClock = createLogicIn( m_pNNode[1] ); + m_pK = createLogicIn( m_pNNode[2] ); + + m_pQ = createLogicOut( m_pPNode[0], false ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + setp = createLogicIn( createPin( 0, -40, 90, "set" ) ); + rstp = createLogicIn( createPin( 0, 40, 270, "rst" ) ); + + addDisplayText( "J", QRect( -32, -24, 20, 16 ), "J" ); + addDisplayText( ">", QRect( -32, -8, 20, 16 ), ">" ); + addDisplayText( "K", QRect( -32, 8, 20, 16 ), "K" ); + addDisplayText( "Q", QRect( 12, -24, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 12, 8, 20, 16 ), "Q'" ); + addDisplayText( "Set", QRect( -16, -28, 32, 16 ), "Set" ); + addDisplayText( "Rst", QRect( -16, 12, 32, 16 ), "Rst" ); + + m_pClock->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::clockChanged) ); + setp->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::asyncChanged) ); + rstp->setCallback( this, (CallbackPtr)(&ECJKFlipFlop::asyncChanged) ); + + inStateChanged(false); +} + +ECJKFlipFlop::~ECJKFlipFlop() +{ +} + +void ECJKFlipFlop::clockChanged(bool newvalue) +{ + bool j = m_pJ->isHigh(); + bool k = m_pK->isHigh(); + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + if( set || rst ) return; + +// a JK flip-flop change state when clock do 1->0 + if (!newvalue && (j || k)) { + if ( j && k ) { + m_pQ->setHigh(!prev_state); + m_pQBar->setHigh(prev_state); + prev_state = !prev_state; + } else { + // (J=1 && K=0) || (J=0 && K=1) + m_pQ->setHigh(j); + m_pQBar->setHigh(k); + prev_state = j; + } + } +} + +void ECJKFlipFlop::asyncChanged(bool) +{ + bool set = setp->isHigh(); + bool rst = rstp->isHigh(); + + if (set || rst) { + m_pQ->setHigh(set); + m_pQBar->setHigh(rst); + prev_state = set; + } +} + +void ECJKFlipFlop::inStateChanged(bool) +{ + m_pQBar->setHigh(true); + m_pQ->setHigh(false); + prev_state = false; +} +//END class ECJKFlipFlop + + +//BEGIN class ECSRFlipFlop +Item* ECSRFlipFlop::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSRFlipFlop( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSRFlipFlop::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/sr_flipflop"), + i18n("SR Flip-Flop"), + i18n("Integrated Circuits"), + "ic3.png", + LibraryItem::lit_component, + ECSRFlipFlop::construct ); +} + +ECSRFlipFlop::ECSRFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "sr_flipflop" ) +{ + m_name = i18n("SR Flip-Flop"); + m_desc = i18n("The output is made high by holding <i>set</i> high, and low by holding <i>reset</i> high."); + + setSize( -24, -24, 48, 48 ); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_pS = createLogicIn( m_pNNode[0] ); + m_pR = createLogicIn( m_pNNode[1] ); + m_pQ = createLogicOut( m_pPNode[0], true ); + m_pQBar = createLogicOut( m_pPNode[1], false ); + + old_q1 = true; + old_q2 = false; + m_pQ->setHigh(old_q1); + m_pQBar->setHigh(old_q2); + + addDisplayText( "S", QRect( -24, -16, 20, 16 ), "S" ); + addDisplayText( "R", QRect( -24, 0, 20, 16 ), "R" ); + addDisplayText( "Q", QRect( 4, -16, 20, 16 ), "Q" ); + addDisplayText( "Q'", QRect( 4, 0, 20, 16 ), "Q'" ); + + m_pS->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pR->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pQ->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); + m_pQBar->setCallback( this, (CallbackPtr)(&ECSRFlipFlop::inStateChanged) ); +} + +ECSRFlipFlop::~ECSRFlipFlop() +{ +} + +void ECSRFlipFlop::inStateChanged(bool) +{ + // Q = v_q1, Q-bar = v_q2 + bool new_q1 = false; + bool new_q2 = false; + + bool s = m_pS->isHigh(); + bool r = m_pR->isHigh(); + bool q1 = m_pQ->isHigh(); + bool q2 = m_pQBar->isHigh(); + + // Easy ones to do :-) + if (!q1) new_q2 = true; + if (!q2) new_q1 = true; + + if ( q1 && q2 ) + { + if ( s && !r ) + { + new_q1 = true; + new_q2 = false; + } + else if ( !s && r ) + { + new_q1 = false; + new_q2 = true; + } + else if ( s && r ) + { + new_q1 = old_q1; + new_q2 = old_q2; + } + else if ( !s && !r ) + { + new_q1 = false; + new_q2 = false; + } + } + else if ( q1 && !q2 ) + { + // Note: We only need to set the value of v_q2 + if ( r && !s ) new_q2 = true; + else new_q2 = false; + } + else if ( !q1 && q2 ) + { + // Note: We only need to set the value of v_q1 + if ( s && !r ) new_q1 = true; + else new_q1 = false; + } + + old_q1 = new_q1; + old_q2 = new_q2; + + m_pQ->setHigh(new_q1); + m_pQBar->setHigh(new_q2); +} +//END class ECSRFlipFlop diff --git a/src/electronics/components/flipflop.h b/src/electronics/components/flipflop.h new file mode 100644 index 0000000..7b9d5b7 --- /dev/null +++ b/src/electronics/components/flipflop.h @@ -0,0 +1,107 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef FLIPFLOP_H +#define FLIPFLOP_H + +#include "component.h" +#include "logic.h" + +class Simulator; + +/** +@short Boolean D-Type Flip-Flop +@author David Saxton +*/ +class ECDFlipFlop : public CallbackClass, public Component +{ +public: + ECDFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inputChanged( bool newState ); + void inStateChanged( bool newState ); + void asyncChanged(bool newState ); + void clockChanged(bool newState ); + + LogicIn *m_pD; + LogicIn *m_pClock; + LogicOut *m_pQ; + LogicOut *m_pQBar; + LogicIn *setp; + LogicIn *rstp; + bool m_bPrevClock; + + bool m_prevD[2]; + unsigned m_whichPrevD:1; + unsigned long long m_prevDSimTime; + Simulator * m_pSimulator; +}; + + +/** +@short Boolean JK-Type Flip-Flop +@author Couriousous +*/ +class ECJKFlipFlop : public CallbackClass, public Component +{ +public: + ECJKFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECJKFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + void asyncChanged(bool newState ); + void clockChanged(bool newState ); + bool prev_state; + LogicIn *m_pJ; + LogicIn *m_pClock; + LogicIn *m_pK; + LogicIn *setp; + LogicIn *rstp; + LogicOut *m_pQ; + LogicOut *m_pQBar; +}; + + +/** +@short Boolean Set-Reset Flip-Flop +@author David Saxton +*/ +class ECSRFlipFlop : public CallbackClass, public Component +{ +public: + ECSRFlipFlop( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSRFlipFlop(); + virtual bool canFlip() const { return true; } + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + LogicIn * m_pS; + LogicIn * m_pR; + LogicOut * m_pQ; + LogicOut * m_pQBar; + bool old_q1; + bool old_q2; +}; + +#endif diff --git a/src/electronics/components/fulladder.cpp b/src/electronics/components/fulladder.cpp new file mode 100644 index 0000000..ad5e40c --- /dev/null +++ b/src/electronics/components/fulladder.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "fulladder.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + + +Item* FullAdder::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new FullAdder( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* FullAdder::libraryItem() +{ + return new LibraryItem( + QString("ec/adder"), + i18n("Adder"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + FullAdder::construct + ); +} + +FullAdder::FullAdder( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "adder" ) +{ + m_name = i18n("Adder"); +// m_desc = i18n("Insert missing adder help here."); + + ALogic = BLogic = inLogic = 0l; + outLogic = SLogic = 0l; + + QStringList pins = QStringList::split( ',', "A,B,>,,S,C", true ); + initDIPSymbol( pins, 48 ); + initDIP(pins); + + ECNode *node; + + node = ecNodeWithID("S"); + SLogic = createLogicOut( node, false ); + + node = ecNodeWithID("C"); + outLogic = createLogicOut( node, false ); + + node = ecNodeWithID("A"); + ALogic = createLogicIn(node); + + node = ecNodeWithID("B"); + BLogic = createLogicIn(node); + + node = ecNodeWithID(">"); + inLogic = createLogicIn(node); + + + ALogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); + BLogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); + inLogic->setCallback( this, (CallbackPtr)(&FullAdder::inStateChanged) ); +} + +FullAdder::~FullAdder() +{ +} + + +void FullAdder::inStateChanged( bool /*state*/ ) +{ + const bool A = ALogic->isHigh(); + const bool B = BLogic->isHigh(); + const bool in = inLogic->isHigh(); + + const bool out = (!A && B && in) || (A && !B && in) || (A && B); + const bool S = (!A && !B && in) || (!A && B && !in) || (A && !B && !in) || (A && B && in); + + SLogic->setHigh(S); + outLogic->setHigh(out); +} + + diff --git a/src/electronics/components/fulladder.h b/src/electronics/components/fulladder.h new file mode 100644 index 0000000..d4277e5 --- /dev/null +++ b/src/electronics/components/fulladder.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECFullAdder_H +#define ECFullAdder_H + +#include "component.h" +#include "logic.h" + +/** +@author David Saxton +*/ +class FullAdder : public CallbackClass, public Component +{ +public: + FullAdder( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FullAdder(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + virtual bool canFlip() const { return true; } + +protected: + void inStateChanged( bool newState ); + + LogicIn *ALogic, *BLogic, *inLogic; + LogicOut *outLogic, *SLogic; +}; + +#endif diff --git a/src/electronics/components/inductor.cpp b/src/electronics/components/inductor.cpp new file mode 100644 index 0000000..9e30b34 --- /dev/null +++ b/src/electronics/components/inductor.cpp @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "inductance.h" +#include "inductor.h" +#include "libraryitem.h" + +#include <klocale.h> +#include <qpainter.h> + +Item* Inductor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Inductor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Inductor::libraryItem() +{ + return new LibraryItem( + "ec/inductor", + i18n("Inductor"), + i18n("Discrete"), + "inductor.png", + LibraryItem::lit_component, + Inductor::construct + ); +} + +Inductor::Inductor( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "inductor" ) +{ + m_name = i18n("Inductor"); +// m_desc = i18n("Stores electrical charge.<br><br>" +// "The voltage across the inductor and inductance are related by <i>Charge = Inductance x Voltage</i>."); + setSize( -16, -8, 32, 16 ); + + init1PinLeft(); + init1PinRight(); + + m_pInductance = createInductance( m_pNNode[0], m_pPNode[0], 0.001 ); + + createProperty( "Inductance", Variant::Type::Double ); + property("Inductance")->setCaption( i18n("Inductance") ); + property("Inductance")->setUnit("H"); + property("Inductance")->setMinValue(1e-12); + property("Inductance")->setMaxValue(1e12); + property("Inductance")->setValue(1e-3); + + addDisplayText( "inductance", QRect( -8, -24, 16, 16 ), "", false ); +} + +Inductor::~Inductor() +{ +} + +void Inductor::dataChanged() +{ + double inductance = dataDouble("Inductance"); + + QString display = QString::number( inductance / getMultiplier(inductance), 'g', 3 ) + getNumberMag(inductance) + "H"; + setDisplayText( "inductance", display ); + + m_pInductance->setInductance(inductance); +} + +void Inductor::drawShape( QPainter &p ) +{ + initPainter(p); + int _y = int(y()); + int _x = int(x()); + + p.drawArc( _x-16, _y-5, 11, 11, 0, 180*16 ); + p.drawArc( _x-5, _y-5, 11, 11, 0, 180*16 ); + p.drawArc( _x+6, _y-5, 11, 11, 0, 180*16 ); + + deinitPainter(p); +} + diff --git a/src/electronics/components/inductor.h b/src/electronics/components/inductor.h new file mode 100644 index 0000000..abb4eec --- /dev/null +++ b/src/electronics/components/inductor.h @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef INDUCTOR_H +#define INDUCTOR_H + +#include <component.h> + +/** +@author David Saxton +*/ +class Inductor : public Component +{ + public: + Inductor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Inductor(); + + static Item * construct( ItemDocument * itemDocument, bool newItem, const char * id ); + static LibraryItem * libraryItem(); + + private: + void dataChanged(); + virtual void drawShape( QPainter & p ); + + Inductance * m_pInductance; +}; + +#endif diff --git a/src/electronics/components/magnitudecomparator.cpp b/src/electronics/components/magnitudecomparator.cpp new file mode 100644 index 0000000..2659122 --- /dev/null +++ b/src/electronics/components/magnitudecomparator.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright (C) 2005 by Fredy Yanardi * + * * + * 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 "libraryitem.h" +#include "logic.h" +#include "magnitudecomparator.h" +#include "variant.h" + +#include <cmath> +#include <klocale.h> + +Item* MagnitudeComparator::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MagnitudeComparator( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* MagnitudeComparator::libraryItem() +{ + return new LibraryItem( + QString("ec/magnitudecomparator"), + i18n("Magnitude Comparator"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + MagnitudeComparator::construct + ); +} + +MagnitudeComparator::MagnitudeComparator( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "magnitudecomparator" ) +{ + m_name = i18n("Magnitude Comparator"); +// m_desc = i18n("Compares to two binary number and generates input to indicate which binary number has greater magnitude. It has 3 cascading inputs: I0 for I<SUB>A > B</SUB>, I1 for I<SUB>A < B</SUB>, and I2 for I<SUB>A = B</SUB> and 3 outputs: O0 for O<SUB>A > B</SUB>, O1 for O<SUB>A < B</SUB>, and O2 for O<SUB>A = B</SUB>"); + m_desc = i18n("Compares two binary numbers and generates output to indicate which binary number has the greater magnitude. It has 3 cascading inputs:" + "<ul><li>I: A > B</li>" + "<li>I: A < B</li>" + "<li>I: A = B</li></ul>" + "and 3 outputs:" + "<ul><li>O: A > B</li>" + "<li>O: A < B</li>" + "<li>O: A = B</li></ul>"); + + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setCaption( i18n("Number Inputs") ); + property("numInput")->setMinValue(1); + property("numInput")->setMaxValue(8); + property("numInput")->setValue(4); + + m_oldABLogicCount = 0; + cascadingInputs = 3; + outputs = 3; + + firstTime = true; +} + +MagnitudeComparator::~MagnitudeComparator() +{ +} + + +void MagnitudeComparator::dataChanged() +{ + initPins(); +} + + +void MagnitudeComparator::inStateChanged() +{ + int i; + + for ( i = 0; i < 3; i++ ) + m_output[i]->setHigh(false); + +// for ( i = dataInt("numInput")-1; i >= 0; i-- ) { + for ( i = m_oldABLogicCount-1; i >= 0; i-- ) { + if (m_aLogic[i]->isHigh() && !m_bLogic[i]->isHigh()) + { + m_output[0]->setHigh(true); + return; + } + else if ( !m_aLogic[i]->isHigh() && m_bLogic[i]->isHigh() ) { + m_output[1]->setHigh(true); + return; + } + } + + if ( m_cLogic[2]->isHigh() ) + m_output[2]->setHigh(true); + else if ( m_cLogic[0]->isHigh() ) + if ( !m_cLogic[1]->isHigh() ) + m_output[0]->setHigh(true); + else + ; + else if ( m_cLogic[1]->isHigh() ) + m_output[1]->setHigh(true); + else { + m_output[0]->setHigh(true); + m_output[1]->setHigh(true); + } +} + + +void MagnitudeComparator::initPins() +{ + const double numInputs = dataInt("numInput"); + int newABLogicCount = (int)numInputs; + + if ( newABLogicCount == m_oldABLogicCount ) + return; + + QStringList leftPins; + int space = 3 - newABLogicCount; + for ( int i = 0; i < space; i++ ) + leftPins << ""; + for ( int i = 0; i < newABLogicCount; i++ ) + leftPins << QString("A%1").arg( QString::number(i) ); + for ( int i = 0; i < newABLogicCount; i++ ) + leftPins << QString("B%1").arg( QString::number(i) ); + for ( int i = 0; i < space; i++ ) + leftPins << ""; + + QStringList rightPins; + space = -space; + for ( int i = 0; i < space; i++ ) + rightPins << ""; + QString inNames[] = { "I: A>B", "I: A<B", "I: A=B" }; + rightPins << inNames[2] << inNames[1] << inNames[0]; + QString outNames[] = { "O: A>B", "O: A<B", "O: A=B" }; + rightPins << outNames[2] << outNames[1] << outNames[0]; + for ( int i = 0; i < space; i++ ) + rightPins << ""; + + QStringList pins = leftPins + rightPins; + + initDIPSymbol( pins, 88 ); + initDIP(pins); + + ECNode *node; + + if (firstTime) { + m_cLogic.resize(3); + for ( int i = 0; i < cascadingInputs; i++ ) + { + node = ecNodeWithID( inNames[i] ); + m_cLogic.insert( i, createLogicIn(node) ); + m_cLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged)); + } + + m_output.resize(3); + for ( int i = 0; i < outputs; i++ ) + { + node = ecNodeWithID( outNames[i] ); + m_output.insert( i, createLogicOut(node,false) ); + } + firstTime = false; + } + + if ( newABLogicCount > m_oldABLogicCount ) + { + m_aLogic.resize(newABLogicCount); + for ( int i=m_oldABLogicCount; i<newABLogicCount; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged) ); + } + + m_bLogic.resize(newABLogicCount); + for ( int i=m_oldABLogicCount; i<newABLogicCount; ++i ) + { + node = ecNodeWithID("B"+QString::number(i)); + m_bLogic.insert( i, createLogicIn(node) ); + m_bLogic[i]->setCallback( this, (CallbackPtr)(&MagnitudeComparator::inStateChanged) ); + } + } + else + { + for ( int i=newABLogicCount; i<m_oldABLogicCount; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newABLogicCount); + for ( int i=newABLogicCount; i<m_oldABLogicCount; ++i ) + { + QString id = "B"+QString::number(i); + removeDisplayText(id); + removeElement( m_bLogic[i], false ); + removeNode(id); + } + m_bLogic.resize(newABLogicCount); + } + + m_oldABLogicCount = newABLogicCount; + inStateChanged(); +} + + diff --git a/src/electronics/components/magnitudecomparator.h b/src/electronics/components/magnitudecomparator.h new file mode 100644 index 0000000..57dc748 --- /dev/null +++ b/src/electronics/components/magnitudecomparator.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2005 by Fredy Yanardi * + * * + * 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. * + ***************************************************************************/ + +#ifndef MAGNITUDECOMPARATOR_H +#define MAGNITUDECOMPARATOR_H + +#include "component.h" +#include "logic.h" + +#include <qbitarray.h> +#include <qptrvector.h> + +/** +@author Fredy Yanardi + */ +class MagnitudeComparator : public CallbackClass, public Component +{ + public: + MagnitudeComparator( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MagnitudeComparator(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + protected: + void initPins(); + virtual void dataChanged(); + void inStateChanged(); + + int m_oldABLogicCount; + int cascadingInputs; + int outputs; + bool firstTime; + + QBitArray m_data; + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicIn> m_bLogic; + QPtrVector<LogicIn> m_cLogic; + QPtrVector<LogicOut> m_output; +}; + +#endif diff --git a/src/electronics/components/matrixdisplay.cpp b/src/electronics/components/matrixdisplay.cpp new file mode 100644 index 0000000..dd40b6a --- /dev/null +++ b/src/electronics/components/matrixdisplay.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "colorcombo.h" +#include "diode.h" +#include "ecled.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "matrixdisplay.h" +#include "simulator.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +Item* MatrixDisplay::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MatrixDisplay( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* MatrixDisplay::libraryItem() +{ + return new LibraryItem( + "ec/matrix_display", + i18n("Matrix Display"), + i18n("Outputs"), + "matrixdisplay.png", + LibraryItem::lit_component, + MatrixDisplay::construct ); +} + + +MatrixDisplay::MatrixDisplay( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "matrix_display" ) +{ + m_name = i18n("Matrix Display"); + m_desc = i18n("A matrix display of LEDs with a configurable number of columns and rows."); + m_bDynamicContent = true; + + //BEGIN Reset members + for ( unsigned i = 0; i < max_md_height; i++ ) + m_pRowNodes[i] = 0l; + for ( unsigned i = 0; i < max_md_width; i++ ) + m_pColNodes[i] = 0l; + + m_lastUpdatePeriod = 0.0; + m_r = m_g = m_b = 0.0; + m_bRowCathode = true; + m_numRows = 0; + m_numCols = 0; + //END Reset members + + createProperty( "0-rows", Variant::Type::Int ); + property("0-rows")->setCaption( i18n("Rows") ); + property("0-rows")->setMinValue(1); + property("0-rows")->setMaxValue(max_md_height); + property("0-rows")->setValue(7); + + createProperty( "1-cols", Variant::Type::Int ); + property("1-cols")->setCaption( i18n("Columns") ); + property("1-cols")->setMinValue(1); + property("1-cols")->setMaxValue(max_md_width); + property("1-cols")->setValue(5); + + createProperty( "color", Variant::Type::Color ); + property("color")->setCaption( i18n("Color") ); + property("color")->setColorScheme( ColorCombo::LED ); + + createProperty( "diode-configuration", Variant::Type::Select ); + property("diode-configuration")->setCaption( i18n("Configuration") ); + property("diode-configuration")->setAllowed( QStringList::split(',',"Row Cathode,Column Cathode") ); + property("diode-configuration")->setValue("Row Cathode"); + property("diode-configuration")->setAdvanced(true); +} + + +MatrixDisplay::~MatrixDisplay() +{ +} + + +void MatrixDisplay::dataChanged() +{ + QColor color = dataColor("color"); + m_r = double(color.red()) / double(0x100); + m_g = double(color.green()) / double(0x100); + m_b = double(color.blue()) / double(0x100); + + int numRows = dataInt("0-rows"); + int numCols = dataInt("1-cols"); + + bool ledsChanged = (numRows != int(m_numRows)) || (numCols != int(m_numCols)); + + if (ledsChanged) + initPins( numRows, numCols ); + + bool rowCathode = dataString("diode-configuration") == "Row Cathode"; + if ( (rowCathode != m_bRowCathode) || ledsChanged) + { + m_bRowCathode = rowCathode; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + { + removeElement( m_pDiodes[i][j], false ); + if (rowCathode) + m_pDiodes[i][j] = createDiode( m_pColNodes[i], m_pRowNodes[j] ); + else + m_pDiodes[i][j] = createDiode( m_pRowNodes[j], m_pColNodes[i] ); + } + } + } +} + + +void MatrixDisplay::initPins( unsigned numRows, unsigned numCols ) +{ + if ( (numRows == m_numRows) && (numCols == m_numCols) ) + return; + + if ( numRows > max_md_height ) + numRows = max_md_height; + + if ( numCols > max_md_width ) + numCols = max_md_width; + + m_lastUpdatePeriod = 0.0; + + //BEGIN Remove diodes + // All the diodes are going to be readded from dataChanged (where this + // function is called from), so easiest just to delete the diodes now and + // resize. + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + removeElement( m_pDiodes[i][j], false ); + } + + m_avgBrightness.resize(numCols); + m_lastBrightness.resize(numCols); + m_pDiodes.resize(numCols); + + for ( unsigned i = 0; i < numCols; i++ ) + { + m_avgBrightness[i].resize(numRows); + m_lastBrightness[i].resize(numRows); + m_pDiodes[i].resize(numRows); + + for ( unsigned j = 0; j < numRows; j++ ) + { + m_avgBrightness[i][j] = 0.0; + m_lastBrightness[i][j] = 255; + m_pDiodes[i][j] = 0l; + } + + } + //END Remove diodes + + + //BEGIN Create or destroy pins + if ( numCols >= m_numCols ) + { + for ( unsigned i = m_numCols; i < numCols; i++ ) + m_pColNodes[i] = createPin( 0, 0, 270, colPinID(i) ); + } + else + { + for ( unsigned i = numCols; i < m_numCols; i++ ) + { + removeNode( colPinID(i) ); + m_pColNodes[i] = 0l; + } + } + m_numCols = numCols; + + if ( numRows >= m_numRows ) + { + for ( unsigned i = m_numRows; i < numRows; i++ ) + m_pRowNodes[i] = createPin( 0, 0, 0, rowPinID(i) ); + } + else + { + for ( unsigned i = numRows; i < m_numRows; i++ ) + { + removeNode( rowPinID(i) ); + m_pRowNodes[i] = 0l; + } + } + m_numRows = numRows; + //END Create or destroy pins + + + //BEGIN Position pins et al + setSize( -int(numCols+1)*8, -int(numRows+1)*8, int(numCols+1)*16, int(numRows+1)*16, true ); + + for ( int i = 0; i < int(m_numCols); i++ ) + { + m_nodeMap[colPinID(i)].x = offsetX() + 16 + 16*i; + m_nodeMap[colPinID(i)].y = offsetY() + height() + 8; + } + + for ( int i = 0; i < int(m_numRows); i++ ) + { + m_nodeMap[rowPinID(i)].x = offsetX() - 8; + m_nodeMap[rowPinID(i)].y = offsetY() + 16 + 16*i; + } + + updateAttachedPositioning(); + //END Position pins et al +} + + +QString MatrixDisplay::colPinID( int col ) const +{ + return QString("col_%1").arg(QString::number(col)); +} +QString MatrixDisplay::rowPinID( int row ) const +{ + return QString("row_%1").arg(QString::number(row)); +} + + +void MatrixDisplay::stepNonLogic() +{ + double interval = 1./LINEAR_UPDATE_RATE; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + m_avgBrightness[i][j] += ECLed::brightness( m_pDiodes[i][j]->current() )*interval; + } + + m_lastUpdatePeriod += interval; +} + + +void MatrixDisplay::drawShape( QPainter &p ) +{ + if ( isSelected() ) + p.setPen(m_selectedCol); + p.drawRect( boundingRect() ); + + initPainter(p); + + const int _x = int(x()+offsetX()); + const int _y = int(y()+offsetY()); + + // To avoid flicker, require at least a 10 ms sample before changing + // the brightness + double minUpdatePeriod = 0.0099; + + for ( int i = 0; i < int(m_numCols); i++ ) + { + for ( int j = 0; j < int(m_numRows); j++ ) + { + if ( m_lastUpdatePeriod > minUpdatePeriod ) + m_lastBrightness[i][j] = unsigned(m_avgBrightness[i][j]/m_lastUpdatePeriod); + + double _b = m_lastBrightness[i][j]; + + QColor brush = QColor( uint(255-(255-_b)*(1-m_r)), uint(255-(255-_b)*(1-m_g)), uint(255-(255-_b)*(1-m_b)) ); + p.setBrush(brush); + p.setPen( Qt::NoPen ); + p.drawEllipse( _x+10+i*16, _y+10+j*16, 12, 12 ); + } + } + + if ( m_lastUpdatePeriod > minUpdatePeriod ) + { + m_lastUpdatePeriod = 0.0; + + for ( unsigned i = 0; i < m_numCols; i++ ) + { + for ( unsigned j = 0; j < m_numRows; j++ ) + m_avgBrightness[i][j] = 0.0; + } + } + + deinitPainter(p); +} diff --git a/src/electronics/components/matrixdisplay.h b/src/electronics/components/matrixdisplay.h new file mode 100644 index 0000000..4851817 --- /dev/null +++ b/src/electronics/components/matrixdisplay.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef MATRIXDISPLAY_H +#define MATRIXDISPLAY_H + +#include <component.h> +#include <qvaluevector.h> + +const unsigned max_md_width = 100; +const unsigned max_md_height = 20; + +/** +@author David Saxton +*/ +class MatrixDisplay : public Component +{ + public: + MatrixDisplay( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MatrixDisplay(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + virtual void drawShape( QPainter &p ); + virtual void dataChanged(); + void initPins( unsigned numRows, unsigned numCols ); + QString colPinID( int col ) const; + QString rowPinID( int row ) const; + + + QValueVector< QValueVector<double> > m_avgBrightness; + QValueVector< QValueVector<unsigned> > m_lastBrightness; + QValueVector< QValueVector<Diode*> > m_pDiodes; + + ECNode * m_pRowNodes[max_md_height]; + ECNode * m_pColNodes[max_md_width]; + + double m_lastUpdatePeriod; + + double m_r, m_g, m_b; + bool m_bRowCathode; + + unsigned m_numRows; + unsigned m_numCols; +}; + +#endif diff --git a/src/electronics/components/matrixdisplaydriver.cpp b/src/electronics/components/matrixdisplaydriver.cpp new file mode 100644 index 0000000..da00bb7 --- /dev/null +++ b/src/electronics/components/matrixdisplaydriver.cpp @@ -0,0 +1,380 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "libraryitem.h" +#include "logic.h" +#include "matrixdisplaydriver.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qstring.h> + +#include <assert.h> + +// Thank you Scott Dattalo! +// http://www.dattalo.com/gnupic/lcdfont.inc + +static char characterMap[256][5] = { +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //0 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //1 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //2 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //3 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //4 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //5 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //6 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //7 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //8 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //9 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //10 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //11 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //12 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //13 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //14 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //15 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //16 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //17 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //18 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //19 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //20 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //21 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //22 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //23 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //24 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //25 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //26 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //27 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //28 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //29 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //30 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //31 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //32 +{ 0x00, 0x00, 0x7d, 0x00, 0x00 }, //33 +{ 0x00, 0x70, 0x00, 0x70, 0x00 }, //34 +{ 0x14, 0x7f, 0x14, 0x7f, 0x14 }, //35 +{ 0x12, 0x2a, 0x7f, 0x2a, 0x24 }, //36 +{ 0x62, 0x64, 0x08, 0x13, 0x23 }, //37 +{ 0x36, 0x49, 0x55, 0x22, 0x05 }, //38 +{ 0x00, 0x50, 0x60, 0x00, 0x00 }, //39 +{ 0x00, 0x1c, 0x22, 0x41, 0x00 }, //40 +{ 0x00, 0x41, 0x22, 0x1c, 0x00 }, //41 +{ 0x14, 0x08, 0x3e, 0x08, 0x14 }, //42 +{ 0x08, 0x08, 0x3e, 0x08, 0x08 }, //43 +{ 0x00, 0x05, 0x06, 0x00, 0x00 }, //44 +{ 0x08, 0x08, 0x08, 0x08, 0x08 }, //45 +{ 0x00, 0x03, 0x03, 0x00, 0x00 }, //46 +{ 0x02, 0x04, 0x08, 0x10, 0x20 }, //47 +{ 0x3e, 0x45, 0x49, 0x51, 0x3e }, //48 +{ 0x00, 0x21, 0x7f, 0x01, 0x00 }, //49 +{ 0x21, 0x43, 0x45, 0x49, 0x31 }, //50 +{ 0x42, 0x41, 0x51, 0x69, 0x46 }, //51 +{ 0x0c, 0x14, 0x24, 0x7f, 0x04 }, //52 +{ 0x72, 0x51, 0x51, 0x51, 0x4e }, //53 +{ 0x1e, 0x29, 0x49, 0x49, 0x06 }, //54 +{ 0x40, 0x47, 0x48, 0x50, 0x60 }, //55 +{ 0x36, 0x49, 0x49, 0x49, 0x36 }, //56 +{ 0x30, 0x49, 0x49, 0x4a, 0x3c }, //57 +{ 0x00, 0x36, 0x36, 0x00, 0x00 }, //58 +{ 0x00, 0x35, 0x36, 0x00, 0x00 }, //59 +{ 0x08, 0x14, 0x22, 0x41, 0x00 }, //60 +{ 0x14, 0x14, 0x14, 0x14, 0x14 }, //61 +{ 0x41, 0x22, 0x14, 0x08, 0x00 }, //62 +{ 0x20, 0x40, 0x45, 0x48, 0x30 }, //63 +{ 0x26, 0x49, 0x4f, 0x41, 0x3e }, //64 +{ 0x3f, 0x44, 0x44, 0x44, 0x3f }, //65 +{ 0x7f, 0x49, 0x49, 0x49, 0x36 }, //66 +{ 0x3e, 0x41, 0x41, 0x41, 0x22 }, //67 +{ 0x7f, 0x41, 0x41, 0x41, 0x3e }, //68 +{ 0x7f, 0x49, 0x49, 0x49, 0x41 }, //69 +{ 0x7f, 0x48, 0x48, 0x48, 0x40 }, //70 +{ 0x3e, 0x41, 0x49, 0x49, 0x2f }, //71 +{ 0x7f, 0x08, 0x08, 0x08, 0x7f }, //72 +{ 0x00, 0x41, 0x7f, 0x41, 0x00 }, //73 +{ 0x02, 0x01, 0x41, 0x7e, 0x40 }, //74 +{ 0x7f, 0x08, 0x14, 0x22, 0x41 }, //75 +{ 0x7f, 0x01, 0x01, 0x01, 0x01 }, //76 +{ 0x7f, 0x40, 0x20, 0x40, 0x7f }, //77 +{ 0x7f, 0x10, 0x08, 0x04, 0x7f }, //78 +{ 0x3e, 0x41, 0x41, 0x41, 0x3e }, //79 +{ 0x7f, 0x48, 0x48, 0x48, 0x30 }, //80 +{ 0x3e, 0x41, 0x45, 0x42, 0x3d }, //81 +{ 0x7f, 0x48, 0x4c, 0x4a, 0x31 }, //82 +{ 0x31, 0x49, 0x49, 0x49, 0x46 }, //83 +{ 0x40, 0x40, 0x7f, 0x40, 0x40 }, //84 +{ 0x7e, 0x01, 0x01, 0x01, 0x7e }, //85 +{ 0x7c, 0x02, 0x01, 0x02, 0x7c }, //86 +{ 0x7e, 0x01, 0x0e, 0x01, 0x7e }, //87 +{ 0x63, 0x14, 0x08, 0x14, 0x63 }, //88 +{ 0x70, 0x08, 0x07, 0x08, 0x70 }, //89 +{ 0x43, 0x45, 0x49, 0x51, 0x61 }, //90 +{ 0x00, 0x7f, 0x41, 0x41, 0x00 }, //91 +{ 0x54, 0x34, 0x1f, 0x34, 0x54 }, //92 +{ 0x00, 0x41, 0x41, 0x7f, 0x00 }, //93 +{ 0x10, 0x20, 0x40, 0x20, 0x10 }, //94 +{ 0x01, 0x01, 0x01, 0x01, 0x01 }, //95 +{ 0x00, 0x40, 0x20, 0x10, 0x00 }, //96 +{ 0x02, 0x15, 0x15, 0x15, 0x0f }, //97 +{ 0x7f, 0x09, 0x11, 0x11, 0x0e }, //98 +{ 0x0e, 0x11, 0x11, 0x11, 0x02 }, //99 +{ 0x0e, 0x11, 0x11, 0x09, 0x7f }, //100 +{ 0x0e, 0x15, 0x15, 0x15, 0x0c }, //101 +{ 0x08, 0x3f, 0x48, 0x40, 0x20 }, //102 +{ 0x30, 0x49, 0x49, 0x49, 0x7e }, //103 +{ 0x7f, 0x08, 0x10, 0x10, 0x0f }, //104 +{ 0x00, 0x11, 0x5f, 0x01, 0x00 }, //105 +{ 0x02, 0x01, 0x21, 0x7e, 0x00 }, //106 +{ 0x7f, 0x04, 0x0a, 0x11, 0x00 }, //107 +{ 0x00, 0x41, 0x7f, 0x01, 0x00 }, //108 +{ 0x1f, 0x10, 0x0c, 0x10, 0x0f }, //109 +{ 0x1f, 0x08, 0x10, 0x10, 0x0f }, //110 +{ 0x0e, 0x11, 0x11, 0x11, 0x0e }, //111 +{ 0x1f, 0x14, 0x14, 0x14, 0x08 }, //112 +{ 0x08, 0x14, 0x14, 0x0c, 0x1f }, //113 +{ 0x1f, 0x08, 0x10, 0x10, 0x08 }, //114 +{ 0x09, 0x15, 0x15, 0x15, 0x12 }, //115 +{ 0x20, 0x7e, 0x21, 0x01, 0x02 }, //116 +{ 0x1e, 0x01, 0x01, 0x02, 0x1f }, //117 +{ 0x1c, 0x02, 0x01, 0x02, 0x1c }, //118 +{ 0x1e, 0x01, 0x06, 0x01, 0x1e }, //119 +{ 0x11, 0x0a, 0x04, 0x0a, 0x11 }, //120 +{ 0x18, 0x05, 0x05, 0x05, 0x1e }, //121 +{ 0x11, 0x13, 0x15, 0x19, 0x11 }, //122 +{ 0x00, 0x08, 0x36, 0x41, 0x00 }, //123 +{ 0x00, 0x00, 0x7f, 0x00, 0x00 }, //124 +{ 0x00, 0x41, 0x36, 0x08, 0x00 }, //125 +{ 0x08, 0x08, 0x2a, 0x1c, 0x08 }, //126 +{ 0x08, 0x1c, 0x2a, 0x08, 0x08 }, //127 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //128 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //129 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //130 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //131 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //132 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //133 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //134 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //135 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //136 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //137 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //138 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //139 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //140 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //141 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //142 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //143 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //144 +{ 0x07, 0x05, 0x07, 0x00, 0x00 }, //145 +{ 0x00, 0x00, 0x78, 0x40, 0x40 }, //146 +{ 0x01, 0x01, 0x0f, 0x00, 0x00 }, //147 +{ 0x04, 0x02, 0x01, 0x00, 0x00 }, //148 +{ 0x00, 0x0c, 0x0c, 0x00, 0x00 }, //149 +{ 0x28, 0x28, 0x29, 0x2a, 0x3c }, //150 +{ 0x10, 0x11, 0x16, 0x14, 0x18 }, //151 +{ 0x02, 0x04, 0x0f, 0x10, 0x00 }, //152 +{ 0x0c, 0x08, 0x19, 0x09, 0x0e }, //153 +{ 0x09, 0x09, 0x0f, 0x09, 0x09 }, //154 +{ 0x09, 0x0a, 0x0c, 0x1f, 0x08 }, //155 +{ 0x08, 0x1f, 0x08, 0x0a, 0x0c }, //156 +{ 0x01, 0x09, 0x09, 0x0f, 0x01 }, //157 +{ 0x15, 0x15, 0x15, 0x1f, 0x00 }, //158 +{ 0x0c, 0x00, 0x0d, 0x01, 0x0e }, //159 +{ 0x04, 0x04, 0x04, 0x04, 0x04 }, //160 +{ 0x40, 0x41, 0x5e, 0x48, 0x70 }, //161 +{ 0x04, 0x08, 0x1f, 0x20, 0x40 }, //162 +{ 0x38, 0x20, 0x61, 0x22, 0x3c }, //163 +{ 0x11, 0x11, 0x1f, 0x11, 0x11 }, //164 +{ 0x22, 0x24, 0x28, 0x7f, 0x20 }, //165 +{ 0x21, 0x7e, 0x20, 0x21, 0x3e }, //166 +{ 0x28, 0x28, 0x7f, 0x28, 0x28 }, //167 +{ 0x08, 0x31, 0x21, 0x22, 0x3c }, //168 +{ 0x10, 0x60, 0x21, 0x3e, 0x20 }, //169 +{ 0x21, 0x21, 0x21, 0x21, 0x3f }, //170 +{ 0x20, 0x79, 0x22, 0x7c, 0x20 }, //171 +{ 0x29, 0x29, 0x01, 0x02, 0x1c }, //172 +{ 0x21, 0x22, 0x24, 0x2a, 0x31 }, //173 +{ 0x20, 0x7e, 0x21, 0x29, 0x31 }, //174 +{ 0x30, 0x09, 0x01, 0x02, 0x3c }, //175 +{ 0x08, 0x31, 0x29, 0x26, 0x3c }, //176 +{ 0x28, 0x29, 0x3e, 0x48, 0x08 }, //177 +{ 0x30, 0x00, 0x31, 0x02, 0x3c }, //178 +{ 0x10, 0x51, 0x5e, 0x50, 0x10 }, //179 +{ 0x00, 0x7f, 0x08, 0x04, 0x00 }, //180 +{ 0x11, 0x12, 0x7c, 0x10, 0x10 }, //181 +{ 0x01, 0x21, 0x21, 0x21, 0x01 }, //182 +{ 0x21, 0x2a, 0x24, 0x2a, 0x30 }, //183 +{ 0x22, 0x24, 0x6f, 0x34, 0x22 }, //184 +{ 0x00, 0x01, 0x02, 0x7c, 0x00 }, //185 +{ 0x0f, 0x00, 0x20, 0x10, 0x0f }, //186 +{ 0x7e, 0x11, 0x11, 0x11, 0x11 }, //187 +{ 0x20, 0x21, 0x21, 0x22, 0x3c }, //188 +{ 0x10, 0x20, 0x10, 0x08, 0x06 }, //189 +{ 0x26, 0x20, 0x7f, 0x20, 0x26 }, //190 +{ 0x20, 0x24, 0x22, 0x25, 0x38 }, //191 +{ 0x00, 0x2a, 0x2a, 0x2a, 0x01 }, //192 +{ 0x0e, 0x12, 0x22, 0x02, 0x07 }, //193 +{ 0x01, 0x0a, 0x04, 0x0a, 0x30 }, //194 +{ 0x28, 0x3e, 0x29, 0x29, 0x29 }, //195 +{ 0x10, 0x7f, 0x10, 0x14, 0x18 }, //196 +{ 0x01, 0x21, 0x21, 0x3f, 0x01 }, //197 +{ 0x29, 0x29, 0x29, 0x29, 0x3f }, //198 +{ 0x10, 0x50, 0x51, 0x52, 0x1c }, //199 +{ 0x78, 0x01, 0x02, 0x7c, 0x00 }, //200 +{ 0x1f, 0x00, 0x3f, 0x01, 0x06 }, //201 +{ 0x3f, 0x01, 0x02, 0x04, 0x08 }, //202 +{ 0x3f, 0x21, 0x21, 0x21, 0x3f }, //203 +{ 0x38, 0x20, 0x21, 0x22, 0x3c }, //204 +{ 0x21, 0x21, 0x01, 0x02, 0x0c }, //205 +{ 0x20, 0x10, 0x40, 0x20, 0x00 }, //206 +{ 0x70, 0x50, 0x70, 0x00, 0x00 }, //207 +{ 0x0e, 0x11, 0x09, 0x06, 0x19 }, //208 +{ 0x02, 0x55, 0x15, 0x55, 0x0f }, //209 +{ 0x1f, 0x2a, 0x2a, 0x2a, 0x14 }, //210 +{ 0x0a, 0x15, 0x15, 0x11, 0x02 }, //211 +{ 0x3f, 0x02, 0x02, 0x04, 0x3e }, //212 +{ 0x0e, 0x11, 0x19, 0x15, 0x12 }, //213 +{ 0x0f, 0x12, 0x22, 0x22, 0x1c }, //214 +{ 0x1c, 0x22, 0x22, 0x22, 0x3f }, //215 +{ 0x02, 0x01, 0x1e, 0x10, 0x10 }, //216 +{ 0x20, 0x20, 0x00, 0x70, 0x00 }, //217 +{ 0x00, 0x00, 0x10, 0x5f, 0x00 }, //218 +{ 0x28, 0x10, 0x28, 0x00, 0x00 }, //219 +{ 0x18, 0x24, 0x7e, 0x24, 0x08 }, //220 +{ 0x14, 0x7f, 0x15, 0x01, 0x01 }, //221 +{ 0x1f, 0x48, 0x50, 0x50, 0x0f }, //222 +{ 0x0e, 0x51, 0x11, 0x51, 0x0e }, //223 +{ 0x3f, 0x12, 0x22, 0x22, 0x1c }, //224 +{ 0x1c, 0x22, 0x22, 0x12, 0x3f }, //225 +{ 0x3c, 0x52, 0x52, 0x52, 0x3c }, //226 +{ 0x03, 0x05, 0x02, 0x05, 0x06 }, //227 +{ 0x1a, 0x26, 0x20, 0x26, 0x1a }, //228 +{ 0x1e, 0x41, 0x01, 0x42, 0x1f }, //229 +{ 0x63, 0x55, 0x49, 0x41, 0x41 }, //230 +{ 0x22, 0x3c, 0x20, 0x3e, 0x22 }, //231 +{ 0x51, 0x4a, 0x44, 0x4a, 0x51 }, //232 +{ 0x3c, 0x02, 0x02, 0x02, 0x3f }, //233 +{ 0x28, 0x28, 0x3e, 0x28, 0x48 }, //234 +{ 0x22, 0x3c, 0x28, 0x28, 0x2e }, //235 +{ 0x3e, 0x28, 0x38, 0x28, 0x3e }, //236 +{ 0x04, 0x04, 0x15, 0x04, 0x04 }, //237 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //238 +{ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f }, //239 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //240 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //241 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //242 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //243 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //244 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //245 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //246 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //247 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //248 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //249 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //250 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //251 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //252 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //253 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //254 +{ 0x00, 0x00, 0x00, 0x00, 0x00 }, //255 + }; + + +inline static bool displayBit( unsigned value, unsigned row, unsigned column ) +{ + assert( value < 256 ); + assert( row < 7 ); + assert( column < 5 ); + return characterMap[value][column] & (1 << row); +}; + + +Item* MatrixDisplayDriver::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new MatrixDisplayDriver( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem * MatrixDisplayDriver::libraryItem() +{ + return new LibraryItem( + "ec/matrix_display_driver", + i18n("Matrix Display Driver"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + MatrixDisplayDriver::construct ); +} + + +MatrixDisplayDriver::MatrixDisplayDriver( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "Matrix Display Driver" ) +{ + m_name = i18n("Matrix Display Driver"); + + m_prevCol = 0; + m_nextCol = 0; + m_scanCount = 2; + + createProperty( "diode-configuration", Variant::Type::Select ); + property("diode-configuration")->setCaption( i18n("Configuration") ); + property("diode-configuration")->setAllowed( QStringList::split(',',"Row Cathode,Column Cathode") ); + property("diode-configuration")->setValue("Row Cathode"); + property("diode-configuration")->setAdvanced(true); + + QStringList pins = QStringList::split( ',', "D0,D1,D2,D3,D4,D5,D6,D7,,,,,,C4,C3,C2,C1,C0,,R0,R1,R2,R3,R4,R5,R6", true ); + initDIPSymbol( pins, 64 ); + initDIP(pins); + + m_pValueLogic.resize( 8, 0l ); + for ( unsigned i = 0; i < 8; ++i ) + m_pValueLogic[i] = createLogicIn( ecNodeWithID("D"+QString::number(i)) ); + + m_pRowLogic.resize( 7, 0l ); + for ( unsigned i = 0; i < 7; ++i ) + { + m_pRowLogic[i] = createLogicOut( ecNodeWithID("R"+QString::number(i)), false ); + m_pRowLogic[i]->setOutputLowConductance( 1.0 ); + m_pRowLogic[i]->setOutputHighVoltage(5.0); + } + + m_pColLogic.resize( 5, 0l ); + for ( unsigned i = 0; i < 5; ++i ) + { + m_pColLogic[i] = createLogicOut( ecNodeWithID("C"+QString::number(i)), false ); + m_pColLogic[i]->setOutputHighVoltage(5.0); + } +} + + +MatrixDisplayDriver::~MatrixDisplayDriver() +{ +} + + +void MatrixDisplayDriver::stepNonLogic() +{ + if ( ++m_scanCount < 5 ) + return; + m_scanCount = 0; + + m_pColLogic[m_prevCol]->setHigh(false); + m_pColLogic[m_nextCol]->setHigh(true); + + unsigned value = 0; + for ( unsigned i = 0; i < 8; ++i ) + value |= ( m_pValueLogic[i]->isHigh() ) ? (1 << i) : 0; + + for ( unsigned row = 0; row < 7; row++ ) + { + m_pRowLogic[row]->setHigh( !displayBit( value, row, m_nextCol) ); + } + + m_prevCol = m_nextCol; + + m_nextCol++; + if ( m_nextCol >= 5 ) + m_nextCol = 0; +} + diff --git a/src/electronics/components/matrixdisplaydriver.h b/src/electronics/components/matrixdisplaydriver.h new file mode 100644 index 0000000..e6b01cc --- /dev/null +++ b/src/electronics/components/matrixdisplaydriver.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef MATRIXDISPLAYDRIVER_H +#define MATRIXDISPLAYDRIVER_H + +#include "matrixdisplay.h" + +/** +@author David Saxton + */ +class MatrixDisplayDriver : public Component +{ + public: + MatrixDisplayDriver( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~MatrixDisplayDriver(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual bool canFlip() const { return true; } + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + QValueVector<LogicIn*> m_pValueLogic; + QValueVector<LogicOut*> m_pRowLogic; + QValueVector<LogicOut*> m_pColLogic; + + unsigned m_prevCol; + unsigned m_nextCol; + unsigned m_scanCount; +}; + +#endif diff --git a/src/electronics/components/meter.cpp b/src/electronics/components/meter.cpp new file mode 100644 index 0000000..4437794 --- /dev/null +++ b/src/electronics/components/meter.cpp @@ -0,0 +1,265 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "ecnode.h" +#include "element.h" +#include "libraryitem.h" +#include "meter.h" +#include "variant.h" +#include "voltagesource.h" +#include "pin.h" +#include "simulator.h" + +#include <cmath> +#include <klocale.h> +#include <qpainter.h> + + +//BEGIN class Meter +Meter::Meter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + m_bDynamicContent = true; + b_timerStarted = false; + m_timeSinceUpdate = 0.; + m_old_value = 0.; + m_avgValue = 0.; + b_firstRun = true; + setSize( -16, -16, 32, 32 ); + + p_displayText = addDisplayText( "meter", QRect( -16, 16, 32, 16 ), displayText() ); + + createProperty( "0-minValue", Variant::Type::Double ); + property("0-minValue")->setCaption( i18n("Minimum Value") ); + property("0-minValue")->setMinValue(1e-12); + property("0-minValue")->setMaxValue(1e12); + property("0-minValue")->setValue(1e-3); + + createProperty( "1-maxValue", Variant::Type::Double ); + property("1-maxValue")->setCaption( i18n("Maximum Value") ); + property("1-maxValue")->setMinValue(1e-12); + property("1-maxValue")->setMaxValue(1e12); + property("1-maxValue")->setValue(1e3); +} + + +Meter::~Meter() +{ +} + + +void Meter::dataChanged() +{ + m_minValue = dataDouble("0-minValue"); + m_maxValue = dataDouble("1-maxValue"); + setChanged(); +} + +void Meter::stepNonLogic() +{ + if (b_firstRun) + { + p_displayText->setText(displayText()); + updateAttachedPositioning(); + setChanged(); + property("0-minValue")->setUnit(m_unit); + property("1-maxValue")->setUnit(m_unit); + b_firstRun = false; + } + + const double v = meterValue(); + if ( !b_timerStarted && std::abs(((v-m_old_value)/m_old_value)) > 1e-6 ) { + b_timerStarted = true; + } + + if (b_timerStarted) + { + m_timeSinceUpdate += 1./LINEAR_UPDATE_RATE; + m_avgValue += v/LINEAR_UPDATE_RATE; +// setChanged(); + if ( m_timeSinceUpdate > 0.05 ) + { + if ( p_displayText->setText(displayText()) ); + updateAttachedPositioning(); + } + } +} + +void Meter::drawShape( QPainter &p ) +{ + initPainter(p); + p.drawEllipse( (int)x()-16, (int)y()-16, width(), width() ); + p.setPen(QPen(Qt::black,2)); + p.setBrush(Qt::black); + + // The proportion between 0.1mV and 10KV, on a logarithmic scale + double prop; + const double abs_value = std::abs(m_old_value); + if ( abs_value <= m_minValue ) + prop = 0.; + else if ( abs_value >= m_maxValue ) + prop = 1.; + else + prop = std::log10( abs_value/m_minValue ) / std::log10( m_maxValue/m_minValue ); + if ( m_old_value>0 ) + prop *= -1; + double sin_prop = 10*std::sin(prop*1.571); // 1.571 = pi/2 + double cos_prop = 10*std::cos(prop*1.571); // 1.571 = pi/2 + + int cx = int(x()-16+(width()/2)); + int cy = int(y()-16+(height()/2)); + p.drawLine( int(cx-sin_prop), int(cy-cos_prop), int(cx+sin_prop), int(cy+cos_prop) ); + + QPointArray pa(3); + pa[0] = QPoint( int(cx-sin_prop), int(cy-cos_prop) ); // Arrow head + pa[1] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(-0.3+prop))), int(cy-cos_prop + 8*std::cos(1.571*(-0.3+prop))) ); + pa[2] = QPoint( int(cx-sin_prop + 8*std::sin(1.571*(0.3+prop))), int(cy-cos_prop + 8*std::cos(1.571*(0.3+prop))) ); + p.drawPolygon(pa); + + deinitPainter(p); +} + + +QString Meter::displayText() +{ + double value = m_avgValue/m_timeSinceUpdate; + if ( !std::isfinite(value) ) value = m_old_value; + if ( std::abs((value)) < 1e-9 ) value = 0.; + m_old_value = value; + m_avgValue = 0.; + m_timeSinceUpdate = 0.; + b_timerStarted = false; + return QString::number( value/CNItem::getMultiplier(value), 'g', 3 ) + CNItem::getNumberMag(value) + m_unit; +} +//END class Meter + + +//BEGIN class FrequencyMeter +Item* FrequencyMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new FrequencyMeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* FrequencyMeter::libraryItem() +{ + return new LibraryItem( + QString("ec/frequencymeter"), + i18n("Frequency Meter (TODO)"), + i18n("Outputs"), + "frequencymeter.png", + LibraryItem::lit_component, + FrequencyMeter::construct ); +} + +FrequencyMeter::FrequencyMeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "frequencymeter" ) +{ + m_name = i18n("Frequency Meter"); + m_desc = i18n("Place this at the point where frequency is to be measured."); + m_unit = "Hz"; + + m_probeNode = createPin( 0, -24, 90, "n1" ); +} + +FrequencyMeter::~FrequencyMeter() +{ +} + +double FrequencyMeter::meterValue() +{ + return 0; +} +//END class FrequencyMeter + + +//BEGIN class ECAmmeter +Item* ECAmmeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECAmmeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECAmmeter::libraryItem() +{ + QStringList ids; + ids << "ec/ammeter" << "ec/ammmeter"; + return new LibraryItem( + ids, + i18n("Ammeter"), + i18n("Outputs"), + "ammeter.png", + LibraryItem::lit_component, + ECAmmeter::construct + ); +} + +ECAmmeter::ECAmmeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "ammeter" ) +{ + m_name = i18n("Ammeter"); + m_desc = i18n("Place this in series in the circuit to measure the current flowing."); + setSize( -16, -16, 32, 32 ); + m_unit = "A"; + + init1PinLeft(0); + init1PinRight(0); + + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); +} + +ECAmmeter::~ECAmmeter() +{ +} + +double ECAmmeter::meterValue() +{ + return -m_voltageSource->cbranchCurrent(0); +} +//END class ECAmmeter + + +//BEGIN class ECVoltmeter +Item* ECVoltMeter::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECVoltMeter( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECVoltMeter::libraryItem() +{ + return new LibraryItem( + QString("ec/voltmeter"), + i18n("Voltmeter"), + i18n("Outputs"), + "voltmeter.png", + LibraryItem::lit_component, + ECVoltMeter::construct ); +} + +ECVoltMeter::ECVoltMeter( ICNDocument *icnDocument, bool newItem, const char *id ) + : Meter( icnDocument, newItem, (id) ? id : "voltmeter" ) +{ + m_name = i18n("Voltmeter"); + m_desc = i18n("Place this in parallel in the circuit to meaure the voltage between two points."); + m_unit = "V"; + + init1PinLeft(0); + init1PinRight(0); +} + +ECVoltMeter::~ECVoltMeter() +{ +} + +double ECVoltMeter::meterValue() +{ + return m_pNNode[0]->pin()->voltage() - m_pPNode[0]->pin()->voltage(); +} +//END class ECVoltMeter + diff --git a/src/electronics/components/meter.h b/src/electronics/components/meter.h new file mode 100644 index 0000000..fa52e95 --- /dev/null +++ b/src/electronics/components/meter.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef METER_H +#define METER_H + +#include <component.h> + +/** +@author David Saxton +*/ +class Meter : public Component +{ +public: + Meter( ICNDocument *icnDocument, bool newItem, const char *id ); + ~Meter(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + virtual void drawShape( QPainter &p ); + +protected: + QString displayText(); + virtual void dataChanged(); + /** + * Return the value / current, or whatever the meter is measuring + */ + virtual double meterValue() = 0; + + bool b_firstRun; // If true, then update the text dispalyed + bool b_timerStarted; // The timer to change the text is started on change + double m_timeSinceUpdate; + double m_avgValue; + double m_old_value; + double m_minValue; + double m_maxValue; + Text *p_displayText; + QString m_unit; +}; + +/** +@short Measures the frequency at a point in the circuit +@author David Saxton +*/ +class FrequencyMeter : public Meter +{ +public: + FrequencyMeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FrequencyMeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); + ECNode *m_probeNode; +}; + +/** +@short Simple resistor +@author David Saxton +*/ +class ECAmmeter : public Meter +{ +public: + ECAmmeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECAmmeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); + +private: + VoltageSource *m_voltageSource; +}; + +/** +@short Displays voltage across terminals +@author David Saxton +*/ +class ECVoltMeter : public Meter +{ +public: + ECVoltMeter( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECVoltMeter(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual double meterValue(); +}; + +#endif diff --git a/src/electronics/components/multiinputgate.cpp b/src/electronics/components/multiinputgate.cpp new file mode 100644 index 0000000..7453845 --- /dev/null +++ b/src/electronics/components/multiinputgate.cpp @@ -0,0 +1,530 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecnode.h" +#include "icndocument.h" +#include "libraryitem.h" +#include "multiinputgate.h" +#include "logic.h" + +#include <cmath> +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class MultiInputGate +MultiInputGate::MultiInputGate( ICNDocument *icnDocument, bool newItem, const char *id, int baseWidth ) + : Component( icnDocument, newItem, id ) +{ + b_doneInit = false; + m_numInputs = 0; + if ( baseWidth == -1 ) { + baseWidth = 32; + } + m_baseWidth = baseWidth; + + for ( int i=0; i<maxGateInput; ++i ) + { + inLogic[i] = 0l; + inNode[i] = 0l; + } + + updateInputs(2); + + init1PinRight(16); + m_pOut = createLogicOut( m_pPNode[0], false ); + + + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setCaption( i18n("Number Inputs") ); + property("numInput")->setMinValue(2); + property("numInput")->setMaxValue(maxGateInput); + property("numInput")->setValue(2); + + b_doneInit = true; +} + + +MultiInputGate::~MultiInputGate() +{ +} + + +void MultiInputGate::dataChanged() +{ + updateInputs( QMIN( maxGateInput, dataInt("numInput") ) ); +} + + +void MultiInputGate::updateInputs( int newNum ) +{ + if ( newNum == m_numInputs ) { + return; + } + + if ( newNum < 2 ) { + newNum = 2; + } + else if ( newNum > maxGateInput ) { + newNum = maxGateInput; + } + + const int newWidth = m_baseWidth; + + setSize( -newWidth/2, -8*newNum, newWidth, 16*newNum, true ); + + const bool added = ( newNum > m_numInputs ); + if (added) + { + for ( int i = m_numInputs; i<newNum; ++i ) + { + ECNode *node = createPin( 0, 0, 0, "in"+QString::number(i) ); + inNode[i] = node; + inLogic[i] = createLogicIn(node); + inLogic[i]->setCallback( this, (CallbackPtr)(&MultiInputGate::inStateChanged) ); + } + } + else + { + for ( int i=newNum; i<m_numInputs; ++i ) + { + removeNode("in"+QString::number(i)); + removeElement( inLogic[i], false ); + inNode[i] = 0l; + inLogic[i] = 0l; + } + } + + m_numInputs = newNum; + + // We can't call a pure-virtual function if we haven't finished our constructor yet... + if (b_doneInit) + inStateChanged(!added); + + updateAttachedPositioning(); +} + + +void MultiInputGate::updateAttachedPositioning() +{ + // Check that our ndoes have been created before we attempt to use them + if ( !m_nodeMap.contains("p1") || !m_nodeMap.contains("in"+QString::number(m_numInputs-1)) ) + return; + + int _x = offsetX()+8; + int _y = offsetY()+8; + + m_nodeMap["p1"].x = m_baseWidth/2 + 8; + m_nodeMap["p1"].y = 0; + + for ( int i=0; i< m_numInputs; ++i ) + { + m_nodeMap["in"+QString::number(i)].x = _x - 16; + m_nodeMap["in"+QString::number(i)].y = _y + 16*i; + } + + if (b_doneInit) + Component::updateAttachedPositioning(); +} +//END class MultiInputGate + + +//BEGIN class ECXNor +Item* ECXnor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECXnor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECXnor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/xnor"), + i18n("XNOR gate"), + i18n("Logic"), + "xnor.png", + LibraryItem::lit_component, + ECXnor::construct ); +} + +ECXnor::ECXnor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "xnor", 48 ) +{ + m_name = i18n("XNOR gate"); + m_desc = i18n("Exclusive NOR gate. Output is low when exactly one input is high."); + + inStateChanged(false); +} + +ECXnor::~ECXnor() +{ +} + +void ECXnor::inStateChanged(bool) +{ + int highCount = 0; + for ( int i=0; i<m_numInputs; ++i ) + { + if ( inLogic[i]->isHigh() ) + highCount++; + } + + m_pOut->setHigh( highCount != 1 ); +} + +void ECXnor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+22, _y, 2*width()-28, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+22, _y, 2*width()-28, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); + + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECXnor + + +//BEGIN class ECXor +Item* ECXor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECXor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECXor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/xor"), + i18n("XOR gate"), + i18n("Logic"), + "xor.png", + LibraryItem::lit_component, + ECXor::construct ); +} + +ECXor::ECXor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "xor", 48 ) +{ + m_name = i18n("XOR gate"); + m_desc = i18n("Exclusive OR gate. Output is high when exactly one input is high."); + + inStateChanged(false); +} + +ECXor::~ECXor() +{ +} + +void ECXor::inStateChanged(bool) +{ + int highCount = 0; + for ( int i=0; i<m_numInputs; ++i ) + { + if ( inLogic[i]->isHigh() ) + highCount++; + } + + m_pOut->setHigh( highCount == 1 ); +} + +void ECXor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+16, _y, 2*width()-16, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+16, _y, 2*width()-16, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + p.drawArc( _x, _y, 16, height(), -16*90, 16*180 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECXor + + +//BEGIN class EXOr +Item* ECOr::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECOr( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECOr::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/or"), + i18n("OR gate"), + i18n("Logic"), + "or.png", + LibraryItem::lit_component, + ECOr::construct ); +} + +ECOr::ECOr( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "or", 48 ) +{ + m_name = i18n("OR gate"); + m_desc = i18n("The output is high when at least one of the inputs is high; or low when all of the inputs are off"); + + inStateChanged(false); +} + +ECOr::~ECOr() +{ +} + +void ECOr::inStateChanged(bool) +{ + bool allLow = true; + for ( int i=0; i<m_numInputs && allLow; ++i ) + { + if ( inLogic[i]->isHigh() ) + allLow = false; + } + + m_pOut->setHigh(!allLow); +} + +void ECOr::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); +// p.setBrush( Qt::red ); + p.drawChord( _x-width(), _y, 2*width(), height(), -16*81, 16*162 ); +// p.drawPie( _x-width()+16, _y, 2*width()-16, height(), -16*100, 16*200 ); + p.restore(); + + p.drawArc( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECOr + + +//BEGIN class ECNor +Item* ECNor::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECNor( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECNor::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/nor"), + i18n("NOR gate"), + i18n("Logic"), + "nor.png", + LibraryItem::lit_component, + ECNor::construct ); +} + +ECNor::ECNor( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, (id) ? id : "nor", 48 ) +{ + m_name = i18n("NOR Gate"); + m_desc = i18n("The output is high when all inputs are low."); + + inStateChanged(false); +} + +ECNor::~ECNor() +{ +} + +void ECNor::inStateChanged(bool) +{ + bool allLow = true; + for ( int i=0; i<m_numInputs && allLow; ++i ) + { + if ( inLogic[i]->isHigh() ) + allLow = false; + } + + m_pOut->setHigh(allLow); +} + +void ECNor::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + + p.save(); + p.setPen( Qt::NoPen ); + p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*81, 16*162 ); + p.restore(); + + p.drawArc( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); + p.drawArc( _x-8, _y, 16, height(), -16*90, 16*180 ); + + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + + const int n = m_numInputs; + for ( int i=0; i<n; ++i ) + { + p.setPen( inNode[i]->isSelected() ? m_selectedCol : Qt::black ); + int pin_x = (int)std::sqrt((double)(64*n*n - (8*n-8-16*i)*(8*n-8-16*i)))/n; + p.drawLine( _x, _y+16*i+8, _x+pin_x, _y+16*i+8 ); + } + + deinitPainter(p); +} +//END class ECNor + + +//BEGIN class ECNand +Item* ECNand::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECNand( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECNand::libraryItem() +{ + return new LibraryItem( + QString::QString("ec/nand"), + i18n("NAND gate"), + i18n("Logic"), + "nand.png", + LibraryItem::lit_component, + ECNand::construct ); +} + +ECNand::ECNand( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, id ? id : "nand" ) +{ + m_name = i18n("NAND Gate"); + m_desc = i18n("The output is low only when all of the inputs are high."); + + inStateChanged(false); +} + +ECNand::~ECNand() +{ +} + +void ECNand::inStateChanged(bool) +{ + bool allHigh = true; + for ( int i=0; i<m_numInputs && allHigh; ++i ) + { + if ( !inLogic[i]->isHigh() ) + allHigh = false; + } + + m_pOut->setHigh(!allHigh); +} + +void ECNand::drawShape( QPainter &p ) +{ + initPainter(p); + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + p.drawChord( _x-width()+6, _y, 2*width()-12, height(), -16*90, 16*180 ); + p.drawEllipse( _x+width()-6, _y+(height()/2)-3, 6, 6 ); + deinitPainter(p); +} +//END class ECNand + + +//BEGIN class ECAnd +Item* ECAnd::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECAnd( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECAnd::libraryItem() +{ + QStringList idList; + idList << "ec/and" << "ec/and_2"; + return new LibraryItem( + idList, + i18n("AND gate"), + i18n("Logic"), + "and.png", + LibraryItem::lit_component, + ECAnd::construct ); +} + +ECAnd::ECAnd( ICNDocument *icnDocument, bool newItem, const char *id ) + : MultiInputGate( icnDocument, newItem, id ? id : "and" ) +{ + m_name = i18n("AND Gate"); + m_desc = i18n("The output is high if and only if all of the inputs are high."); + + inStateChanged(false); +} + +ECAnd::~ECAnd() +{ +} + +void ECAnd::inStateChanged(bool) +{ + bool allHigh = true; + for ( int i=0; i<m_numInputs && allHigh; ++i ) + { + if ( !inLogic[i]->isHigh() ) + allHigh = false; + } + + m_pOut->setHigh(allHigh); +} + +void ECAnd::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()+offsetX(); + int _y = (int)y()+offsetY(); + p.drawChord( _x-width(), _y, 2*width(), height(), -16*90, 16*180 ); + + deinitPainter(p); +} +//END class ECAnd diff --git a/src/electronics/components/multiinputgate.h b/src/electronics/components/multiinputgate.h new file mode 100644 index 0000000..1981217 --- /dev/null +++ b/src/electronics/components/multiinputgate.h @@ -0,0 +1,160 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef MULTIINPUTGATE_H +#define MULTIINPUTGATE_H + +#include "component.h" +#include "logic.h" + +const int maxGateInput = 256; + +/** +@author David Saxton +*/ +class MultiInputGate : public CallbackClass, public Component +{ +public: + MultiInputGate( ICNDocument *icnDocument, bool newItem, const char *id, int baseWidth = -1 ); + ~MultiInputGate(); + +protected: + virtual void inStateChanged( bool newState ) = 0; + void dataChanged(); + void updateInputs( int newNum ); + + int m_numInputs; + int m_baseWidth; + + LogicIn *inLogic[maxGateInput]; + ECNode *inNode[maxGateInput]; + + LogicOut * m_pOut; + + virtual void updateAttachedPositioning(); + +private: + bool b_doneInit; +}; + + +/** +@short Boolean XNOR +@author David Saxton +*/ +class ECXnor : public MultiInputGate +{ +public: + ECXnor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECXnor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + +/** +@short Boolean XOR +@author David Saxton +*/ +class ECXor : public MultiInputGate +{ +public: + ECXor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECXor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + +/** +@short Boolean OR +@author David Saxton +*/ +class ECOr : public MultiInputGate +{ +public: + ECOr( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECOr(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean NOR +@author David Saxton +*/ +class ECNor : public MultiInputGate +{ +public: + ECNor( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECNor(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean NAND +@author David Saxton +*/ +class ECNand : public MultiInputGate +{ +public: + ECNand( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECNand(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + +/** +@short Boolean AND +@author David Saxton +*/ +class ECAnd : public MultiInputGate +{ +public: + ECAnd( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECAnd(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +private: + void inStateChanged( bool newState ); + virtual void drawShape( QPainter &p ); +}; + + + +#endif diff --git a/src/electronics/components/multiplexer.cpp b/src/electronics/components/multiplexer.cpp new file mode 100644 index 0000000..e421fee --- /dev/null +++ b/src/electronics/components/multiplexer.cpp @@ -0,0 +1,176 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "multiplexer.h" + +#include "logic.h" +#include "libraryitem.h" + +#include <kiconloader.h> +#include <klocale.h> + +#include <cmath> + +Item* Multiplexer::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new Multiplexer( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* Multiplexer::libraryItem() +{ + return new LibraryItem( + QString("ec/multiplexer"), + i18n("Multiplexer"), + i18n("Integrated Circuits"), + "ic1.png", + LibraryItem::lit_component, + Multiplexer::construct + ); +} + +Multiplexer::Multiplexer( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "multiplexer" ) +{ + m_name = i18n("Multiplexer"); + m_desc = i18n("Combines the input data stream into one single stream. The value of the input selected by the \"A\" inputs is passed to the output."); + + m_output = 0l; + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(8); + property("addressSize")->setValue(1); + + // For backwards compatibility + createProperty( "numInput", Variant::Type::Int ); + property("numInput")->setMinValue(-1); + property("numInput")->setValue(-1); + property("numInput")->setHidden(true); +} + +Multiplexer::~Multiplexer() +{ +} + + +void Multiplexer::dataChanged() +{ + if ( hasProperty("numInput") && dataInt("numInput") != -1 ) + { + int addressSize = int( std::ceil( std::log( (double)dataInt("numInput") ) / std::log(2.0) ) ); + property("numInput")->setValue(-1); + + if ( addressSize < 1 ) + addressSize = 1; + else if ( addressSize > 8 ) + addressSize = 8; + + // This function will get called again when we set the value of numInput + property("addressSize")->setValue(addressSize); + return; + } + + if ( hasProperty("numInput") ) + { + m_variantData["numInput"]->deleteLater(); + m_variantData.remove("numInput"); + } + + initPins( unsigned(dataInt("addressSize")) ); +} + + +void Multiplexer::inStateChanged( bool /*state*/ ) +{ + unsigned long long pos = 0; + for ( unsigned i = 0; i < m_aLogic.size(); ++i ) + { + if ( m_aLogic[i]->isHigh() ) + pos += 1 << i; + } + m_output->setHigh( m_xLogic[pos]->isHigh() ); +} + + +void Multiplexer::initPins( unsigned newAddressSize ) +{ + unsigned oldAddressSize = m_aLogic.size(); + unsigned long long oldXLogicCount = m_xLogic.size(); + unsigned long long newXLogicCount = 1 << newAddressSize; + + if ( newXLogicCount == oldXLogicCount ) + return; + + QStringList pins; + + const int length = newAddressSize + newXLogicCount; + + for ( unsigned i=0; i<newAddressSize; ++i ) + pins += "A"+QString::number(i); + for ( unsigned i=0; i<newXLogicCount; ++i ) + pins += "X"+QString::number(i); + for ( int i=0; i<(length-(length%2))/2; ++i ) + pins += ""; + pins += "X"; + for ( int i=0; i<((length+(length%2))/2)-1; ++i ) + pins += ""; + + initDIPSymbol( pins, 64 ); + initDIP(pins); + + ECNode *node; + + if (!m_output) + { + node = ecNodeWithID("X"); + m_output = createLogicOut( node, false ); + } + + if ( newXLogicCount > oldXLogicCount ) + { + m_xLogic.resize(newXLogicCount); + for ( unsigned i=oldXLogicCount; i<newXLogicCount; ++i ) + { + node = ecNodeWithID("X"+QString::number(i)); + m_xLogic.insert( i, createLogicIn(node) ); + m_xLogic[i]->setCallback( this, (CallbackPtr)(&Multiplexer::inStateChanged) ); + } + + m_aLogic.resize(newAddressSize); + for ( unsigned i=oldAddressSize; i<newAddressSize; ++i ) + { + node = ecNodeWithID("A"+QString::number(i)); + m_aLogic.insert( i, createLogicIn(node) ); + m_aLogic[i]->setCallback( this, (CallbackPtr)(&Multiplexer::inStateChanged) ); + } + } + else + { + for ( unsigned i = newXLogicCount; i < oldXLogicCount; ++i ) + { + QString id = "X"+QString::number(i); + removeDisplayText(id); + removeElement( m_xLogic[i], false ); + removeNode(id); + } + m_xLogic.resize(newXLogicCount); + + for ( unsigned i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = "A"+QString::number(i); + removeDisplayText(id); + removeElement( m_aLogic[i], false ); + removeNode(id); + } + m_aLogic.resize(newAddressSize); + } +} + diff --git a/src/electronics/components/multiplexer.h b/src/electronics/components/multiplexer.h new file mode 100644 index 0000000..42bcfc1 --- /dev/null +++ b/src/electronics/components/multiplexer.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef MULTIPLEXER_H +#define MULTIPLEXER_H + +#include "component.h" +#include "logic.h" + +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class Multiplexer : public CallbackClass, public Component +{ +public: + Multiplexer( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Multiplexer(); + + virtual bool canFlip() const { return true; } + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins( unsigned addressSize ); + + void inStateChanged( bool newState ); + + QPtrVector<LogicIn> m_aLogic; + QPtrVector<LogicIn> m_xLogic; + LogicOut * m_output; +}; + +#endif diff --git a/src/electronics/components/parallelportcomponent.cpp b/src/electronics/components/parallelportcomponent.cpp new file mode 100644 index 0000000..9bc17b4 --- /dev/null +++ b/src/electronics/components/parallelportcomponent.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "port.h" +#include "parallelportcomponent.h" + +#include "ecnode.h" +#include "itemdocument.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> +#include <termios.h> + +Item* ParallelPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ParallelPortComponent( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ParallelPortComponent::libraryItem() +{ + return new LibraryItem( + "ec/parallel_port", + i18n("Parallel Port"), + i18n("Connections"), + "ic1.png", + LibraryItem::lit_component, + ParallelPortComponent::construct + ); +} + +ParallelPortComponent::ParallelPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "parallel_port" ) +{ + m_name = i18n("Parallel Port"); + m_desc = i18n("The pins are divided into 3 registers.<br><br>" + "<b>Data Pins</b><br><br>" + "The data pins can be configured as either all input or all output. They are:" + "<ul>" + "<li><b>D<i>[0..7]</i></b></li>" + "</ul><br>" + "<b>Status Pins</b><br><br>" + "The status pins are read-only. They area:" + "<ul>" + "<li><b>ERR</b> - Error</li>" + "<li><b>ON</b> - Online</li>" + "<li><b>PE</b> - Paper End</li>" + "<li><b>ACK</b> - Acknowledge</li>" + "<li><b>BUSY</b> - Busy</li>" + "</ul><br>" + "<b>Control Pins</b>" + "<ul>" + "<li><b>STR</b> - Strobe</li>" + "<li><b>AUT</b> - Auto Feed</li>" + "<li><b>INIT</b> - Init</li>" + "<li><b>SEL</b> - Select</li>" + "</ul><br>" + "The remaining pins are all ground." + ); + + QPointArray pa( 4 ); + pa[0] = QPoint( -32, -112 ); + pa[1] = QPoint( 32, -104 ); + pa[2] = QPoint( 32, 104 ); + pa[3] = QPoint( -32, 112 ); + setItemPoints( pa ); + + m_pParallelPort = new ParallelPort(); + + for ( unsigned i = 0; i < 24; ++i ) + m_pLogic[i] = 0; + + ECNode * pin = 0; + + //BEGIN Data register + for ( int i = 0; i < 8; ++i ) + { + QString id = QString("D%1").arg(i); + QString name = id; + + pin = createPin( -40, -80 + 16*i, 0, id ); + addDisplayText( id, QRect( -28, -88 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + + m_pLogic[i] = createLogicOut( pin, false ); + m_pLogic[i]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::dataCallback) ); + } + //END Data register + + + //BEGIN Status register + QString statusNames[] = { "ERR", "ON", "PE", "ACK", "BUSY" }; + + // The statusIDs are referenced in the save file and must not change + QString statusIDs[] = { "ERROR", "ONLINE", "PE", "ACK", "BUSY" }; + + // Bits 0...2 in the Status register are not used + for ( int i = 3; i < 8; ++i ) + { + QString id = statusIDs[i-3]; + QString name = statusNames[i-3]; + + // Bit 3 (pin 15) doesn't not follow the same positioning pattern as + // the other pins in the Status register. + if ( i == 3 ) + { + pin = createPin( 40, -72, 180, id ); + addDisplayText( id, QRect( 0, -80, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + else + { + pin = createPin( -40, -16 + 16*i, 0, id ); + addDisplayText( id, QRect( -28, -24 + 16*i, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + } + + m_pLogic[i+8] = createLogicOut( pin, false ); + } + //END Status register + + + //BEGIN Control register + QString controlNames[] = { "STR", "AUT", "INIT", "SEL" }; + + // The controlIDs are referenced in the save file and must not change + QString controlIDs[] = { "STROBE", "AUTO", "INIT", "SELECT" }; + + // Bits 4..7 are not used (well; bit 5 is, but not as a pin) + for ( int i = 0; i < 4; ++i ) + { + QString id = controlIDs[i]; + QString name = controlNames[i]; + + if ( i == 0 ) + { + pin = createPin( -40, -96, 0, id ); + addDisplayText( id, QRect( -28, -104, 28, 16 ), name, true, Qt::AlignLeft | Qt::AlignVCenter ); + } + else if ( i == 1 ) + { + pin = createPin( 40, -88, 180, id ); + addDisplayText( id, QRect( 0, -96, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + else + { + pin = createPin( 40, -88 + i*16, 180, id ); + addDisplayText( id, QRect( 0, -96 + i*16, 28, 16 ), name, true, Qt::AlignRight | Qt::AlignVCenter ); + } + + m_pLogic[i+16] = createLogicOut( pin, false ); + m_pLogic[i+16]->setCallback( this, (CallbackPtr)(&ParallelPortComponent::controlCallback) ); + } + //END Control register + + +#if 0 + // And make the rest of the pins ground + for ( int i = 0; i < 8; ++i ) + { + pin = createPin( 40, -24 + i*16, 180, QString("GND%1").arg( i ) ); + pin->pin()->setGroundType( Pin::gt_always ); + } +#endif + + Variant * v = createProperty( "port", Variant::Type::Combo ); + v->setAllowed( ParallelPort::ports( Port::ExistsAndRW ) ); + v->setCaption( i18n("Port") ); +} + + +ParallelPortComponent::~ParallelPortComponent() +{ + delete m_pParallelPort; +} + + +void ParallelPortComponent::dataChanged() +{ + initPort( dataString("port") ); +} + + +void ParallelPortComponent::initPort( const QString & port ) +{ + if ( port.isEmpty() ) + { + m_pParallelPort->closePort(); + return; + } + + if ( ! m_pParallelPort->openPort( port ) ) + { + p_itemDocument->canvas()->setMessage( i18n("Could not open port %1").arg( port ) ); + return; + } +} + + +void ParallelPortComponent::dataCallback( bool ) +{ + uchar value = 0; + for ( unsigned i = 0; i < 8; ++ i ) + value |= m_pLogic[ i + 0 ]->isHigh() ? 0 : (1 << i); + + m_pParallelPort->writeToData( value ); +} + + +void ParallelPortComponent::controlCallback( bool ) +{ + uchar value = 0; + for ( unsigned i = 0; i < 4; ++ i ) + value |= m_pLogic[ i + 16 ]->isHigh() ? 0 : (1 << i); + + m_pParallelPort->writeToControl( value ); +} + + +void ParallelPortComponent::stepNonLogic() +{ + uchar status = m_pParallelPort->readFromRegister( ParallelPort::Status ); + // Bits 0...2 in the Status register are not used + for ( int i = 3; i < 8; ++i ) + m_pLogic[i + 8]->setHigh( status | (1 << i) ); +} + + +void ParallelPortComponent::drawShape( QPainter & p ) +{ + drawPortShape( p ); +} diff --git a/src/electronics/components/parallelportcomponent.h b/src/electronics/components/parallelportcomponent.h new file mode 100644 index 0000000..89ead80 --- /dev/null +++ b/src/electronics/components/parallelportcomponent.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PARALLELPORTCOMPONENT_H +#define PARALLELPORTCOMPONENT_H + +#include "logic.h" +#include "component.h" + +class ParallelPort; + +/** +@author David Saxton + */ +class ParallelPortComponent : public CallbackClass, public Component +{ + public: + ParallelPortComponent( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ParallelPortComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + void initPort( const QString & port ); + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + void dataCallback( bool ); + void controlCallback( bool ); + + /// Registers: { Data[0...7], Status[0...5], 0[6...7], Control[0...4], 0[5...7] } + LogicOut * m_pLogic[24]; + + ParallelPort * m_pParallelPort; +}; + +#endif diff --git a/src/electronics/components/piccomponent.cpp b/src/electronics/components/piccomponent.cpp new file mode 100644 index 0000000..47320bb --- /dev/null +++ b/src/electronics/components/piccomponent.cpp @@ -0,0 +1,437 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "config.h" +#ifndef NO_GPSIM + +#include "canvasitemparts.h" +#include "circuitdocument.h" +#include "docmanager.h" +#include "gpsimprocessor.h" +#include "libraryitem.h" +#include "logic.h" +#include "ktechlab.h" +#include "micropackage.h" +#include "picinfo.h" +#include "microlibrary.h" +#include "piccomponent.h" +#include "piccomponentpin.h" +#include "projectmanager.h" + +#include <kdebug.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <qguardedptr.h> +#include <qstringlist.h> + +#include "gpsim/ioports.h" +#include "gpsim/pic-processor.h" + +const QString _def_PICComponent_fileName = i18n("<Enter location of PIC Program>"); + + +Item* PICComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new PICComponent( (ICNDocument*)itemDocument, newItem, id ); +} + + +LibraryItem* PICComponent::libraryItem() +{ + QStringList IDs; + IDs << "ec/pic" << "ec/picitem" << "ec/picitem_18pin"; + + return new LibraryItem( + IDs, + "PIC", + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + PICComponent::construct ); +} + +PICComponent::PICComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "pic" ) +{ + m_name = i18n("PIC Micro"); + m_desc = i18n("The PIC component allows the simulation of a PIC program.<br><br>" + "The loadable PIC program must be one of the following formats:" + "<ul><li>Assembly (.asm)</li>" + "<li>FlowCode (.flowcode)</li>" + "<li>Symbol file (.cod)</li>" + "<li>Microbe (.microbe, .basic)</li>" + "<li>C source (.c)</li></ul>" + "Doubleclick on the PIC component to open up the program source file.<br><br>" + "If the program source file is of type assembly, then the the opened text file will automatically be linked to the simulation. " + "You can control the program from the text document using the debug controls.<br><br>" + "Explanation of buttons:" + "<ul><li>Play - Run the PIC program from the point at which it was paused, or from the start otherwise.</li>" + "<li>Pause - Pause the simulation at the current execution point.</li>" + "<li>Stop - Reset all parts of the simulation.</li>" + "<li>Reload - Reread the PIC program from disk and restart gpsim.</li></ul>"); + + m_bCreatedInitialPackage = false; + m_bLoadingProgram = false; + m_pGpsim = 0L; + + addButton( "run", QRect(), KGlobal::iconLoader()->loadIcon( "player_play", KIcon::Small ) ); + addButton( "pause", QRect(), KGlobal::iconLoader()->loadIcon( "player_pause", KIcon::Small ) ); + addButton( "reset", QRect(), KGlobal::iconLoader()->loadIcon( "stop", KIcon::Small ) ); + addButton( "reload", QRect(), KGlobal::iconLoader()->loadIcon( "reload", KIcon::Small ) ); + + if ( icnDocument->ktechlab() ) + connect( icnDocument->ktechlab(), SIGNAL(recentFileAdded(const KURL &)), this, SLOT(slotUpdateFileList()) ); + + connect( ProjectManager::self(), SIGNAL(projectOpened()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(projectClosed()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(projectCreated()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(subprojectCreated()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(filesAdded()), this, SLOT(slotUpdateFileList()) ); + connect( ProjectManager::self(), SIGNAL(filesRemoved()), this, SLOT(slotUpdateFileList()) ); + + createProperty( "program", Variant::Type::FileName ); + property("program")->setCaption( i18n("Program") ); + property("program")->setFilter("*.flowcode *.asm *.cod *.basic|All Supported Files\n*.flowcode|FlowCode (*.flowcode)\n*.cod|Symbol File (*.cod)\n*.asm|Assembly Code (*.asm)\n*.basic *.microbe|Microbe (*.basic, *.microbe)\n*.c|C (*.c)*|All Files"); + + // Used for restoring the pins on file loading before we have had a change + // to compile the PIC program + createProperty( "lastPackage", Variant::Type::String ); + property("lastPackage")->setHidden( true ); + +// //HACK This is to enable loading with pre-0.3 files (which didn't set a "lastPackage" +// // property). This will allow a P16F84 PIC to be initialized (which agrees with pre-0.3 +// // behaviour), but it will also load it if + + // This to allow loading of the PIC component from pre-0.3 files (which didn't set a + // "lastPackage" property). + if ( !newItem ) + property("lastPackage")->setValue("P16F84"); + + slotUpdateFileList(); + slotUpdateBtns(); + + initPackage( 0 ); +} + + +PICComponent::~PICComponent() +{ + deletePICComponentPins(); + delete m_pGpsim; +} + + +void PICComponent::dataChanged() +{ + initPIC(false); +} + + +void PICComponent::initPIC( bool forceReload ) +{ + if ( !m_bCreatedInitialPackage ) + { + // We are still being created, so other connectors will be expecting us to + // have grown pins soonish. + MicroInfo * microInfo = MicroLibrary::self()->microInfoWithID( dataString("lastPackage") ); + if ( microInfo ) + initPackage( microInfo ); + } + + QString newProgram = KURL( dataString("program") ).path(); + bool newFile = (m_picFile != newProgram); + if ( !newFile && !forceReload ) + return; + + delete m_pGpsim; + m_pGpsim = 0L; + + switch ( GpsimProcessor::isValidProgramFile(newProgram) ) + { + case GpsimProcessor::DoesntExist: + if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) + break; + KMessageBox::sorry( 0l, i18n("The file \"%1\" does not exist.").arg( newProgram ) ); + m_picFile = QString::null; + break; + + case GpsimProcessor::IncorrectType: + if ( newProgram == _def_PICComponent_fileName && !newProgram.isEmpty() ) + break; + KMessageBox::sorry( 0L, i18n("\"%1\" is not a valid PIC program.\nThe file must exist, and the extension should be \".cod\", \".asm\", \".flowcode\", \".basic\", \".microbe\" or \".c\".\n\".hex\" is allowed, provided that there is a corresponding \".cod\" file.").arg(newProgram) ); + m_picFile = QString::null; + break; + + case GpsimProcessor::Valid: + m_picFile = newProgram; + m_symbolFile = createSymbolFile(); + break; + } + + slotUpdateBtns(); +} + + +void PICComponent::deletePICComponentPins() +{ + const PICComponentPinMap::iterator picComponentMapEnd = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != picComponentMapEnd; ++it ) + delete it.data(); + m_picComponentPinMap.clear(); +} + + +void PICComponent::initPackage( MicroInfo * microInfo ) +{ + MicroPackage * microPackage = microInfo ? microInfo->package() : 0l; + + if ( microPackage ) + { + m_bCreatedInitialPackage = true; + + //BEGIN Get pin IDs + QStringList allPinIDs = microPackage->pinIDs(); + QStringList ioPinIDs = microPackage->pinIDs( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + + // Now, we make the unwanted pin ids blank, so a pin is not created for them + const QStringList::iterator allPinIDsEnd = allPinIDs.end(); + for ( QStringList::iterator it = allPinIDs.begin(); it != allPinIDsEnd; ++it ) + { + if ( !ioPinIDs.contains(*it) ) + *it = ""; + } + //END Get pin IDs + + + //BEGIN Remove old stuff + // Remove old text + TextMap textMapCopy = m_textMap; + const TextMap::iterator textMapEnd = textMapCopy.end(); + for ( TextMap::iterator it = textMapCopy.begin(); it != textMapEnd; ++it ) + removeDisplayText(it.key()); + + // Remove the old pins + deletePICComponentPins(); + + // Remove old nodes + NodeMap nodeMapCopy = m_nodeMap; + const NodeMap::iterator nodeMapEnd = nodeMapCopy.end(); + for ( NodeMap::iterator it = nodeMapCopy.begin(); it != nodeMapEnd; ++it ) + { + if ( !ioPinIDs.contains(it.key()) ) + removeNode( it.key() ); + } + + removeElements(); + //END Remove old stuff + + + + //BEGIN Create new stuff + initDIPSymbol( allPinIDs, 80 ); + initDIP(allPinIDs); + + PicPinMap picPinMap = microPackage->pins( PicPin::type_bidir | PicPin::type_input | PicPin::type_open ); + const PicPinMap::iterator picPinMapEnd = picPinMap.end(); + for ( PicPinMap::iterator it = picPinMap.begin(); it != picPinMapEnd; ++it ) + m_picComponentPinMap[it.key()] = new PICComponentPin( this, it.data() ); + //END Create new stuff + + + removeDisplayText( "no_file" ); + addDisplayText( "picid", QRect(offsetX(), offsetY()-16, width(), 16), microInfo->id() ); + } + else + { + setSize( -48, -72, 96, 144 ); + removeDisplayText( "picid" ); + addDisplayText( "no_file", sizeRect(), i18n("(No\nprogram\nloaded)") ); + } + + + //BEGIN Update button positions + int leftpos = (width()-88)/2+offsetX(); + button("run")->setOriginalRect( QRect( leftpos, height()+4+offsetY(), 20, 20 ) ); + button("pause")->setOriginalRect( QRect( leftpos+23, height()+4+offsetY(), 20, 20 ) ); + button("reset")->setOriginalRect( QRect( leftpos+46, height()+4+offsetY(), 20, 20 ) ); + button("reload")->setOriginalRect( QRect( leftpos+69, height()+4+offsetY(), 20, 20 ) ); + updateAttachedPositioning(); + //END Update button positions +} + + +void PICComponent::attachPICComponentPins() +{ + if ( !m_pGpsim || !m_pGpsim->picProcessor() ) + return; + + pic_processor * picProcessor = m_pGpsim->picProcessor(); + + const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) + it.data()->attach( picProcessor->get_pin( it.key() ) ); +} + + +void PICComponent::slotUpdateFileList() +{ + QStringList preFileList; + if ( p_icnDocument && p_icnDocument->ktechlab() ) + preFileList += p_icnDocument->ktechlab()->recentFiles(); + + QStringList fileList; + + if ( ProjectInfo * info = ProjectManager::self()->currentProject() ) + { + const KURL::List urls = info->childOutputURLs( ProjectItem::AllTypes, ProjectItem::ProgramOutput ); + KURL::List::const_iterator urlsEnd = urls.end(); + for ( KURL::List::const_iterator it = urls.begin(); it != urlsEnd; ++it ) + fileList << (*it).path(); + } + + const QStringList::iterator end = preFileList.end(); + for ( QStringList::iterator it = preFileList.begin(); it != end; ++it ) + { + QString file = KURL(*it).path(); + if ( (file.endsWith(".flowcode") || file.endsWith(".asm") || file.endsWith(".cod") || file.endsWith(".basic") || file.endsWith(".microbe") ) && !fileList.contains(file) ) { + fileList.append(file); + } + } + + QString fileName = dataString("program"); + + property("program")->setAllowed(fileList); + property("program")->setValue( fileName.isEmpty() ? _def_PICComponent_fileName : fileName ); +} + + +void PICComponent::buttonStateChanged( const QString &id, bool state ) +{ + if (!state) + return; + + if ( id == "reload" ) + { + programReload(); + return; + } + + if (!m_pGpsim) + return; + + if ( id == "run" ) + m_pGpsim->setRunning(true); + + else if ( id == "pause" ) + m_pGpsim->setRunning(false); + + else if ( id == "reset" ) + { + m_pGpsim->reset(); + + // Set all pin outputs to low + const PICComponentPinMap::iterator end = m_picComponentPinMap.end(); + for ( PICComponentPinMap::iterator it = m_picComponentPinMap.begin(); it != end; ++it ) + it.data()->resetOutput(); + } + + slotUpdateBtns(); +} + + +bool PICComponent::mouseDoubleClickEvent ( const EventInfo &eventInfo ) +{ + Q_UNUSED(eventInfo); + if ( m_picFile.isEmpty() || (m_picFile == _def_PICComponent_fileName) ) + return false; + + (void) DocManager::self()->openURL(m_picFile); + + return true; +} + + +QString PICComponent::createSymbolFile() +{ + m_bLoadingProgram = true; + slotUpdateBtns(); + + return GpsimProcessor::generateSymbolFile( dataString("program"), this, SLOT(slotCODCreationSucceeded()), SLOT(slotCODCreationFailed()) ); +} + + +void PICComponent::slotCODCreationSucceeded() +{ + m_bLoadingProgram = false; + + delete m_pGpsim; + m_pGpsim = new GpsimProcessor(m_symbolFile); + + if ( m_pGpsim->codLoadStatus() == GpsimProcessor::CodSuccess ) + { + MicroInfo * microInfo = m_pGpsim->microInfo(); + property("lastPackage")->setValue( microInfo->id() ); + initPackage( microInfo ); + + connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(slotUpdateBtns()) ); + attachPICComponentPins(); + } + + else + { + m_pGpsim->displayCodLoadStatus(); + delete m_pGpsim; + m_pGpsim = 0l; + } + + slotUpdateBtns(); +} + + +void PICComponent::slotCODCreationFailed() +{ + m_bLoadingProgram = false; + slotUpdateBtns(); +} + + +void PICComponent::programReload() +{ + delete m_pGpsim; + m_pGpsim = 0L; + + initPIC(true); + + slotUpdateBtns(); +} + + +void PICComponent::slotUpdateBtns() +{ + // We can get called by the destruction of gpsim after our canvas has been set to NULL + if (!canvas()) + return; + + button("run")->setEnabled( m_pGpsim && !m_pGpsim->isRunning() ); + button("pause")->setEnabled( m_pGpsim && m_pGpsim->isRunning() ); + button("reset")->setEnabled( m_pGpsim ); + button("reload")->setEnabled( !m_bLoadingProgram && (dataString("program") != _def_PICComponent_fileName) ); + + canvas()->setChanged( button("run")->boundingRect() ); + canvas()->setChanged( button("pause")->boundingRect() ); + canvas()->setChanged( button("reset")->boundingRect() ); + canvas()->setChanged( button("reload")->boundingRect() ); +} + + +#include "piccomponent.moc" + +#endif diff --git a/src/electronics/components/piccomponent.h b/src/electronics/components/piccomponent.h new file mode 100644 index 0000000..165fbf6 --- /dev/null +++ b/src/electronics/components/piccomponent.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PICCOMPONENT_H +#define PICCOMPONENT_H + +#include "config.h" +#ifndef NO_GPSIM + +#include "component.h" + +#include <qguardedptr.h> +#include <qmap.h> + +class Document; +class ECNode; +class GpsimProcessor; +class IOPIN; +class KTechlab; +class MicroInfo; +class MicroPackage; +class PIC_IOPORT; +class PICComponent; +class PICComponentPin; +class PicPin; +class TextDocument; + +typedef QMap< int, PICComponentPin * > PICComponentPinMap; + +/** +@short Electronic PIC device +@author David Saxton +*/ +class PICComponent : public Component +{ + Q_OBJECT + public: + PICComponent( ICNDocument * icnDocument, bool newItem, const char *id = 0L ); + ~PICComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual bool mouseDoubleClickEvent( const EventInfo &eventInfo ); + + void programReload(); + /** + * Sets up the pins, text, etc for the given PIC type. If info is null, + * then a generic rectangle is displayed (used when no file has been + * loaded yet). + */ + void initPackage( MicroInfo * info ); + + public slots: + void slotUpdateFileList(); + void slotUpdateBtns(); + + protected slots: + void slotCODCreationSucceeded(); + void slotCODCreationFailed(); + + protected: + /** + * Attaches all PICComponentPins to the current instance of gpsim. + */ + void attachPICComponentPins(); + void deletePICComponentPins(); + /** + * Attempts to compile the program to a symbol file, and connects the assembly + * finish signal to loadGpsim + */ + QString createSymbolFile(); + virtual void dataChanged(); + /** + * Initializes the PIC from the options the user has selected. + */ + void initPIC( bool forceReload ); + + QGuardedPtr<GpsimProcessor> m_pGpsim; + QString m_picFile; ///< The input program that the user selected + QString m_symbolFile; ///< The symbol file that was generated from m_picFile + bool m_bLoadingProgram; ///< True between createSymbolFile being called and the file being created + PICComponentPinMap m_picComponentPinMap; + bool m_bCreatedInitialPackage; ///< Set true once the initial package is loaded; until then, will load a package from the lastPackage data +}; + +#endif +#endif diff --git a/src/electronics/components/piccomponentpin.cpp b/src/electronics/components/piccomponentpin.cpp new file mode 100644 index 0000000..47ef6cb --- /dev/null +++ b/src/electronics/components/piccomponentpin.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "config.h" +#ifndef NO_GPSIM + +#include "micropackage.h" +#include "piccomponent.h" +#include "piccomponentpin.h" + +#include <kdebug.h> + +PICComponentPin::PICComponentPin( PICComponent * picComponent, PicPin picPin ) + : m_id( picPin.pinID ) +{ + m_gOutHigh = 0.0; + m_gOutLow = 0.0; + m_picPin = picPin; + m_pPICComponent = picComponent; + m_pLogicOut = 0l; + m_pLogicIn = 0l; + m_pIOPIN = 0l; + m_pStimulusNode = 0l; + Zth = 0.0; + Vth = 0.0; + + switch ( picPin.type ) + { + case PicPin::type_input: + { + m_pLogicIn = picComponent->createLogicIn( picComponent->ecNodeWithID(picPin.pinID) ); + break; + } + case PicPin::type_bidir: + { + m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); + m_gOutHigh = 0.004; + m_gOutLow = 0.004; + break; + } + case PicPin::type_open: + { + m_pLogicOut = picComponent->createLogicOut( picComponent->ecNodeWithID(picPin.pinID), false ); + m_pLogicOut->setOutputHighVoltage(0.0); + m_pLogicOut->setOutputHighConductance(0.0); + m_gOutHigh = 0.0; + m_gOutLow = 0.004; + break; + } + case PicPin::type_vss: + case PicPin::type_vdd: + case PicPin::type_mclr: + case PicPin::type_osc: + default: + break; + } + + if (m_pLogicIn) + m_pLogicIn->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); + if (m_pLogicOut) + m_pLogicOut->setCallback( this, (CallbackPtr)(&PICComponentPin::logicCallback) ); +} + + +PICComponentPin::~PICComponentPin() +{ + delete m_pStimulusNode; +} + + +void PICComponentPin::attach( IOPIN * iopin ) +{ + if (!iopin) + { + kdWarning() << k_funcinfo << " iopin is NULL" << endl; + return; + } + + if (m_pStimulusNode) + { + kdWarning() << k_funcinfo << " Already have a node stimulus" << endl; + return; + } + + if (m_pIOPIN) + { + kdWarning() << k_funcinfo << " Already have an iopin" << endl; + return; + } + + m_pIOPIN = iopin; + m_pStimulusNode = new Stimulus_Node(m_id.ascii()); + m_pStimulusNode->attach_stimulus(iopin); + m_pStimulusNode->attach_stimulus(this); + + + // We need to tell the iopin whether or not we are high + if (m_pLogicOut) + logicCallback( m_pLogicOut->isHigh() ); + else if (m_pLogicIn) + logicCallback( m_pLogicIn->isHigh() ); +} + + +double PICComponentPin::get_Vth( ) +{ + if (!m_pIOPIN) + return 0.0; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + return Vth; + else + return m_pIOPIN->get_Vth(); +} + + +void PICComponentPin::set_nodeVoltage( double v ) +{ + Q_UNUSED(v); + + if ( !m_pLogicOut || !m_pIOPIN ) + return; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + { + m_pLogicOut->setOutputHighConductance(0.0); + m_pLogicOut->setOutputLowConductance(0.0); + return; + } + + m_pLogicOut->setHigh( m_pIOPIN->getDrivingState() ); + m_pLogicOut->setOutputHighConductance(m_gOutHigh); + m_pLogicOut->setOutputLowConductance(m_gOutLow); +} + + +void PICComponentPin::logicCallback( bool state ) +{ + if (!m_pIOPIN) + return; + + Vth = state ? 5e10 : 0; + bDrivingState = state; + + if ( m_pIOPIN->get_direction() == IOPIN::DIR_INPUT ) + { + Zth = 1e5; + + m_pIOPIN->setDrivenState(state); + if (m_pStimulusNode) + m_pStimulusNode->update(); + } + else + Zth = 0; +} + + +void PICComponentPin::resetOutput() +{ + if ( m_pLogicOut ) + m_pLogicOut->setHigh( false ); +} + +#endif + diff --git a/src/electronics/components/piccomponentpin.h b/src/electronics/components/piccomponentpin.h new file mode 100644 index 0000000..0fc433d --- /dev/null +++ b/src/electronics/components/piccomponentpin.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PICCOMPONENTPIN_H +#define PICCOMPONENTPIN_H + +#include "config.h" +#ifndef NO_GPSIM + +#include "logic.h" +#include "gpsim/stimuli.h" + +#include <qstring.h> + +/** +@short Controls a pin on the PIC component +@author David Saxton + */ +class PICComponentPin : public CallbackClass, public stimulus +{ + public: + PICComponentPin( PICComponent * picComponent, PicPin picPin ); + ~PICComponentPin(); + /** + * Attach this to gpsim + */ + void attach( IOPIN * iopin ); + /** + * Called when the IOPIN this class is associated with changes state. + * Updates the associated LogicOut / LogicIn / etc according to what + * type of pin this is. + */ + virtual void set_nodeVoltage( double v ); + /** + * Called from our logic pin when the logic changes state. + */ + void logicCallback( bool state ); + /** + * Sets the output (if has one) to low. Called when the user stops the + * PIC. + */ + void resetOutput(); + + virtual double get_Vth(); + + protected: + // Conductance of pin in different configurations + double m_gOutHigh; + double m_gOutLow; + + PicPin m_picPin; + IOPIN * m_pIOPIN; + LogicOut * m_pLogicOut; + LogicIn * m_pLogicIn; + PICComponent * m_pPICComponent; + Stimulus_Node * m_pStimulusNode; + const QString m_id; +}; + +#endif +#endif diff --git a/src/electronics/components/probe.cpp b/src/electronics/components/probe.cpp new file mode 100644 index 0000000..db6725e --- /dev/null +++ b/src/electronics/components/probe.cpp @@ -0,0 +1,291 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "ecnode.h" +#include "libraryitem.h" +#include "logic.h" +#include "pin.h" +#include "probe.h" //HACK: This has to be included before the oscilloscope headers +#include "oscilloscope.h" +#include "oscilloscopedata.h" +#include "simulator.h" +#include "voltagesource.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class Probe +Probe::Probe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ) +{ + p_probeData = 0l; + setSize( -16, -8, 32, 16 ); + + createProperty( "color", Variant::Type::Color ); + property("color")->setCaption( i18n("Color") ); + property("color")->setValue( Qt::black ); +} + + +Probe::~ Probe() +{ + delete p_probeData; +} + + +void Probe::dataChanged() +{ + m_color = dataColor("color"); + if (p_probeData) + p_probeData->setColor(m_color); + setChanged(); +} +//END class Probe + + + +//BEGIN class FloatingProbe +FloatingProbe::FloatingProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Probe( icnDocument, newItem, id ) +{ + p_probeData = m_pFloatingProbeData = static_cast<FloatingProbeData*>(registerProbe(this)); + property("color")->setValue( p_probeData->color() ); + + createProperty( "scaling", Variant::Type::Select ); + property("scaling")->setCaption( i18n("Scaling") ); + property("scaling")->setAllowed( QStringList::split( ',', "Linear,Logarithmic") ); + property("scaling")->setValue("Linear"); + property("scaling")->setAdvanced( true ); + + createProperty( "upper_abs_value", Variant::Type::Double ); + property("upper_abs_value")->setCaption( i18n("Upper Absolute Value") ); + property("upper_abs_value")->setValue(10.0); + property("upper_abs_value")->setMinValue(0.0); + property("upper_abs_value")->setUnit("V"); + property("upper_abs_value")->setAdvanced(true); + + createProperty( "lower_abs_value", Variant::Type::Double ); + property("lower_abs_value")->setCaption( i18n("Lower Absolute Value") ); + property("lower_abs_value")->setValue(0.1); + property("lower_abs_value")->setMinValue(0.0); + property("lower_abs_value")->setUnit("V"); + property("lower_abs_value")->setAdvanced(true); +} + + +FloatingProbe::~FloatingProbe() +{ +} + + +void FloatingProbe::dataChanged() +{ + Probe::dataChanged(); + + if ( dataString("scaling") == "Linear" ) + m_pFloatingProbeData->setScaling( FloatingProbeData::Linear ); + else + m_pFloatingProbeData->setScaling( FloatingProbeData::Logarithmic ); + + m_pFloatingProbeData->setUpperAbsValue( dataDouble("upper_abs_value") ); + m_pFloatingProbeData->setLowerAbsValue( dataDouble("lower_abs_value") ); +} + + +void FloatingProbe::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x())-16; + int _y = int(y())-8; + + p.drawRect( _x, _y, 32, 16 ); + + QPointArray bezier(4); + + bezier[0] = QPoint( _x+4, _y+10 ); + bezier[1] = QPoint( _x+12, _y-6 ); + bezier[2] = QPoint( _x+20, _y+24 ); + bezier[3] = QPoint( _x+28, _y+4 ); + + p.setPen( QPen( m_color, 2 ) ); + p.drawCubicBezier(bezier); + + deinitPainter(p); +} +//END class FloatingProbe + + + +//BEGIN class VoltageProbe +Item* VoltageProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new VoltageProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* VoltageProbe::libraryItem() +{ + return new LibraryItem( + "ec/voltageprobe", + i18n("Voltage Probe"), + i18n("Outputs"), + "floatingprobe.png", + LibraryItem::lit_component, + VoltageProbe::construct ); +} + +VoltageProbe::VoltageProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : FloatingProbe( icnDocument, newItem, id ? id : "voltageprobe" ) +{ + m_name = i18n("Voltage Probe"); + m_desc = i18n("Displays the voltage at the probe point on the oscilloscope."); + + init1PinLeft(); + init1PinRight(); + m_pPin1 = m_pNNode[0]->pin(); + m_pPin2 = m_pPNode[0]->pin(); +} + + +VoltageProbe::~VoltageProbe() +{ +} + + +void VoltageProbe::stepNonLogic() +{ + m_pFloatingProbeData->addDataPoint( m_pPin1->voltage() - m_pPin2->voltage() ); +} +//END class VoltageProbe + + + +//BEGIN class CurrentProbe +Item* CurrentProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new CurrentProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* CurrentProbe::libraryItem() +{ + return new LibraryItem( + "ec/currentprobe", + i18n("Current Probe"), + i18n("Outputs"), + "floatingprobe.png", + LibraryItem::lit_component, + CurrentProbe::construct ); +} + +CurrentProbe::CurrentProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : FloatingProbe( icnDocument, newItem, id ? id : "currentprobe" ) +{ + m_name = i18n("Current Probe"); + m_desc = i18n("Displays the current at the probe point on the oscilloscope."); + + + init1PinLeft(0); + init1PinRight(0); + + m_voltageSource = createVoltageSource( m_pNNode[0], m_pPNode[0], 0. ); +} + + +CurrentProbe::~CurrentProbe() +{ +} + + +void CurrentProbe::stepNonLogic() +{ + m_pFloatingProbeData->addDataPoint( -m_voltageSource->cbranchCurrent(0) ); +} +//END class CurrentProbe + + + + +//BEGIN class LogicProbe +Item* LogicProbe::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new LogicProbe( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* LogicProbe::libraryItem() +{ + QStringList ids; + ids << "ec/probe" << "ec/logicprobe"; + return new LibraryItem( + ids, + i18n("Logic Probe"), + i18n("Outputs"), + "logicprobe.png", + LibraryItem::lit_component, + LogicProbe::construct ); +} + +LogicProbe::LogicProbe( ICNDocument *icnDocument, bool newItem, const char *id ) + : Probe( icnDocument, newItem, id ? id : "probe" ) +{ + m_name = i18n("Logic Probe"); + m_desc = i18n("Connect this probe the the point in the circuit to measure the logic value. The output will be displayed in the Oscilloscope view."); + + init1PinRight(); + m_pIn = createLogicIn( m_pPNode[0] ); + + p_probeData = p_logicProbeData = static_cast<LogicProbeData*>(registerProbe(this)); + property("color")->setValue( p_probeData->color() ); + + m_pSimulator = Simulator::self(); + m_pIn->setCallback( this, (CallbackPtr)(&LogicProbe::logicCallback) ); + logicCallback(false); +} + + +LogicProbe::~LogicProbe() +{ +} + + +void LogicProbe::logicCallback( bool value ) +{ + p_logicProbeData->addDataPoint( LogicDataPoint( value, m_pSimulator->time() ) ); +} + + +void LogicProbe::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = int(x())-16; + int _y = int(y())-8; + + p.drawRect( _x, _y, 32, 16 ); + + p.setPen( QPen( m_color, 2 ) ); + + p.drawLine( _x+4, _y+11, _x+6, _y+11 ); + p.drawLine( _x+6, _y+11, _x+6, _y+4 ); + p.drawLine( _x+6, _y+4, _x+10, _y+4 ); + p.drawLine( _x+10, _y+4, _x+10, _y+11 ); + p.drawLine( _x+10, _y+11, _x+16, _y+11 ); + p.drawLine( _x+16, _y+11, _x+16, _y+4 ); + p.drawLine( _x+16, _y+4, _x+23, _y+4 ); + p.drawLine( _x+23, _y+4, _x+23, _y+11 ); + p.drawLine( _x+23, _y+11, _x+28, _y+11 ); +// p.drawLine( _x+23, _y+11, _x+26, _y+11 ); +// p.drawLine( _x+26, _y+11, _x+26, _y+4 ); +// p.drawLine( _x+26, _y+4, _x+28, _y+4 ); + + deinitPainter(p); +} +//END class LogicProbe + + diff --git a/src/electronics/components/probe.h b/src/electronics/components/probe.h new file mode 100644 index 0000000..9beb62e --- /dev/null +++ b/src/electronics/components/probe.h @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PROBE_H +#define PROBE_H + +#include <component.h> + +class LogicProbeData; +class ProbeData; +class FloatingProbeData; + +/** +@author David Saxton +*/ +class Probe : public Component +{ + public: + Probe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~Probe(); + + protected: + virtual void dataChanged(); + + ProbeData * p_probeData; // As obtained via registering with the oscilloscope + QColor m_color; +}; + +/** +@author David Saxton + */ +class FloatingProbe : public Probe +{ + public: + FloatingProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~FloatingProbe(); + + virtual bool doesStepNonLogic() const { return true; } + + protected: + virtual void dataChanged(); + virtual void drawShape( QPainter &p ); + + FloatingProbeData * m_pFloatingProbeData; +}; + +/** +@author David Saxton + */ +class VoltageProbe : public FloatingProbe +{ + public: + VoltageProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~VoltageProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + + protected: + Pin * m_pPin1; + Pin * m_pPin2; +}; + +/** +@author David Saxton + */ +class CurrentProbe : public FloatingProbe +{ + public: + CurrentProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~CurrentProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void stepNonLogic(); + + protected: + VoltageSource *m_voltageSource; +}; + +/** +@author David Saxton + */ +class LogicProbe : public CallbackClass, public Probe +{ + public: + LogicProbe( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~LogicProbe(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + void logicCallback( bool value ); + + protected: + virtual void drawShape( QPainter &p ); + + LogicProbeData * p_logicProbeData; + LogicIn * m_pIn; + Simulator * m_pSimulator; +}; + +#endif diff --git a/src/electronics/components/pushswitch.cpp b/src/electronics/components/pushswitch.cpp new file mode 100644 index 0000000..b7b38b7 --- /dev/null +++ b/src/electronics/components/pushswitch.cpp @@ -0,0 +1,204 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "canvasitemparts.h" +#include "libraryitem.h" +#include "pushswitch.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> +#include <qpixmap.h> +#include <qpoint.h> +#include <qpointarray.h> + +//BEGIN class ECPTBSwitch +Item* ECPTBSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPTBSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPTBSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/ptb_switch"), + i18n("Push-to-Break"), + i18n("Switches"), + "ptb.png", + LibraryItem::lit_component, + ECPTBSwitch::construct ); +} + +ECPTBSwitch::ECPTBSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ptb_switch" ) +{ + m_name = i18n("Push to Break"); + setSize( -16, -16, 32, 24 ); + + addButton( "button", QRect( -16, 8, 32, 20 ), "" ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption( i18n("Bounce") ); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption( i18n("Bounce Period") ); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft(0); + init1PinRight(0); + + m_switch = createSwitch( m_pPNode[0], m_pNNode[0], false ); + pressed = false; +} + +ECPTBSwitch::~ECPTBSwitch() +{ +} + + +void ECPTBSwitch::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECPTBSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + const int _height = height()-8; + + int dy = pressed ? 6 : 4; + + p.drawLine( _x+width()/4, _y+dy, _x+(3*width())/4, _y+dy ); // Top horizontal line + p.drawLine( _x, _y+(_height/2)-radius+dy, _x+width(), _y+(_height/2)-radius+dy ); // Bottom horizontal line + p.drawLine( _x+width()/2, _y+dy, _x+width()/2, _y+(_height/2)-radius+dy ); // Vertical line + + p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle + p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle + + deinitPainter(p); +} + +void ECPTBSwitch::buttonStateChanged( const QString &id, bool state ) +{ + if ( id != "button" ) + return; + m_switch->setState( state ? Switch::Open : Switch::Closed ); + pressed = state; +} +//END class ECPTBSwitch + + +//BEGIN class ECPTMSwitch +Item* ECPTMSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECPTMSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECPTMSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/ptm_switch"), + i18n("Push-to-Make"), + i18n("Switches"), + "ptm.png", + LibraryItem::lit_component, + ECPTMSwitch::construct ); +} + +ECPTMSwitch::ECPTMSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "ptm_switch" ) +{ + m_name = i18n("Push to Make"); + setSize( -16, -16, 32, 24 ); + + addButton( "button", QRect( -16, 8, 32, 20 ), "" ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft(0); + init1PinRight(0); + + m_switch = createSwitch( m_pPNode[0], m_pNNode[0], true ); + pressed = false; +} + + +ECPTMSwitch::~ECPTMSwitch() +{ +} + + +void ECPTMSwitch::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECPTMSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + const int _height = height()-8; + + int dy = pressed ? 1 : 3; + + p.drawLine( _x+width()/4, _y-dy, _x+(3*width())/4, _y-dy ); // Top horizontal line + p.drawLine( _x, _y+(_height/2)-radius-dy, _x+width(), _y+(_height/2)-radius-dy ); // Bottom horizontal line + p.drawLine( _x+width()/2, _y-dy, _x+width()/2, _y+(_height/2)-radius-dy ); // Vertical line + + p.drawEllipse( _x, _y+(_height/2)-radius, 2*radius, 2*radius ); // Left circle + p.drawEllipse( _x+width()-2*radius+1, _y+(_height/2)-radius, 2*radius, 2*radius ); // Right circle + + deinitPainter(p); +} + +void ECPTMSwitch::buttonStateChanged( const QString &id, bool state ) +{ + if ( id != "button" ) return; + m_switch->setState( state ? Switch::Closed : Switch::Open ); + pressed = state; +} +//END class ECPTMSwitch + diff --git a/src/electronics/components/pushswitch.h b/src/electronics/components/pushswitch.h new file mode 100644 index 0000000..c22ea93 --- /dev/null +++ b/src/electronics/components/pushswitch.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PUSHSWITCH_H +#define PUSHSWITCH_H + +#include "component.h" + +/** +@short Push-to-Break switch component +@author David Saxton +*/ +class ECPTBSwitch : public Component +{ +public: + ECPTBSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPTBSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + + +/** +@short Push-to-make switch +@author David Saxton +*/ +class ECPTMSwitch : public Component +{ +public: + ECPTMSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECPTMSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + + +#endif diff --git a/src/electronics/components/ram.cpp b/src/electronics/components/ram.cpp new file mode 100644 index 0000000..add745a --- /dev/null +++ b/src/electronics/components/ram.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "libraryitem.h" +#include "logic.h" +#include "ram.h" +#include "variant.h" + +#include <cmath> +#include <klocale.h> + +Item* RAM::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new RAM( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* RAM::libraryItem() +{ + return new LibraryItem( + QString("ec/ram"), + i18n("RAM"), + i18n("Integrated Circuits"), + "ic2.png", + LibraryItem::lit_component, + RAM::construct + ); +} + +RAM::RAM( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "ram" ) +{ + m_name = i18n("RAM"); + m_desc = i18n("This RAM stores data as a collection of words; each of which contains <i>word size</i> bits of data.<br><br>To read data, set the CS (<i>chip select</i>) and the OE (<i>output enable</i>) pins high, and select the word using the address pins <i>A*</i>. The word is outputted on the data-out pins: <i>DO*</i>.<br><br>To write data, set the CS (<i>chip select</i>) and the WE (<i>write enable</i>) pins high, and select the address to write to with the <i>A*</i> pins. Write to the selected word using the data-in pins: <i>DI*</i>.<br><br>The <i>Address Size</i> is the number of bits that determine an address; so the total number of words stored will be 2^<sup><i>Address Size</i></sup>."); + + m_data = 0l; + m_pCS = 0l; + m_pOE = 0l; + m_pWE = 0l; + m_wordSize = 0; + m_addressSize = 0; + + createProperty( "wordSize", Variant::Type::Int ); + property("wordSize")->setCaption( i18n("Word Size") ); + property("wordSize")->setMinValue(1); + property("wordSize")->setMaxValue(256); + property("wordSize")->setValue(2); + + createProperty( "addressSize", Variant::Type::Int ); + property("addressSize")->setCaption( i18n("Address Size") ); + property("addressSize")->setMinValue(1); + property("addressSize")->setMaxValue(32); + property("addressSize")->setValue(4); + + m_data = createProperty( "data", Variant::Type::Raw )->value().asBitArray(); +} + +RAM::~RAM() +{ +} + + +void RAM::dataChanged() +{ + m_wordSize = dataInt("wordSize"); + m_addressSize = dataInt("addressSize"); + + int newSize = int( m_wordSize * std::pow( 2., m_addressSize ) ); + m_data.resize(newSize); + + initPins(); +} + + +void RAM::inStateChanged( bool newState ) +{ + Q_UNUSED(newState); + + bool cs = m_pCS->isHigh(); + bool oe = m_pOE->isHigh(); + bool we = m_pWE->isHigh(); + + if ( !cs || !oe ) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_dataOut[i]->setHigh(false); + } + + if ( !cs || (!oe && !we) ) + return; + + unsigned address = 0; + for ( int i = 0; i < m_addressSize; ++i ) + address += (m_address[i]->isHigh() ? 1 : 0) << i; + + if (we) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_data[ m_wordSize * address + i ] = m_dataIn[i]->isHigh(); + } + + if (oe) + { + for ( int i = 0; i < m_wordSize; ++i ) + m_dataOut[i]->setHigh( m_data[ m_wordSize * address + i ] ); + } +} + + +void RAM::initPins() +{ + int oldWordSize = m_dataIn.size(); + int oldAddressSize = m_address.size(); + + int newWordSize = dataInt("wordSize"); + int newAddressSize = dataInt("addressSize"); + + if ( newAddressSize == oldAddressSize && + newWordSize == oldWordSize ) + return; + + QStringList leftPins; // Pins on left of IC + leftPins << "CS" << "OE" << "WE"; + for ( int i = 0; i < newAddressSize; ++i ) + leftPins << QString("A%1").arg( QString::number(i) ); + + QStringList rightPins; // Pins on right of IC + for ( unsigned i = newWordSize; i > 0; --i ) + rightPins << QString("DI%1").arg( QString::number(i-1) ); + for ( unsigned i = newWordSize; i > 0; --i ) + rightPins << QString("DO%1").arg( QString::number(i-1) ); + + // Make pin lists of consistent sizes + for ( unsigned i = leftPins.size(); i < rightPins.size(); ++i ) + leftPins.append(""); + for ( unsigned i = rightPins.size(); i < leftPins.size(); ++i ) + rightPins.prepend(""); + + QStringList pins = leftPins + rightPins; + + initDIPSymbol( pins, 72 ); + initDIP(pins); + + ECNode *node; + + if (!m_pCS) + { + node = ecNodeWithID("CS"); + m_pCS = createLogicIn(node); + m_pCS->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if (!m_pOE) + { + node = ecNodeWithID("OE"); + m_pOE = createLogicIn(node); + m_pOE->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if (!m_pWE) + { + node = ecNodeWithID("WE"); + m_pWE = createLogicIn(node); + m_pWE->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + + if ( newWordSize > oldWordSize ) + { + m_dataIn.resize(newWordSize); + m_dataOut.resize(newWordSize); + + for ( int i = oldWordSize; i < newWordSize; ++i ) + { + node = ecNodeWithID( QString("DI%1").arg( QString::number(i) ) ); + m_dataIn.insert( i, createLogicIn(node) ); + m_dataIn[i]->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + + node = ecNodeWithID( QString("DO%1").arg( QString::number(i) ) ); + m_dataOut.insert( i, createLogicOut(node, false) ); + } + } + else if ( newWordSize < oldWordSize ) + { + for ( int i = newWordSize; i < oldWordSize; ++i ) + { + QString id = QString("DO%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_dataIn[i], false ); + removeNode(id); + + id = QString("DI%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_dataOut[i], false ); + removeNode(id); + } + + m_dataIn.resize(newWordSize); + m_dataOut.resize(newWordSize); + } + + if ( newAddressSize > oldAddressSize ) + { + m_address.resize(newAddressSize); + + for ( int i = oldAddressSize; i < newAddressSize; ++i ) + { + node = ecNodeWithID( QString("A%1").arg( QString::number(i) ) ); + m_address.insert( i, createLogicIn(node) ); + m_address[i]->setCallback( this, (CallbackPtr)(&RAM::inStateChanged) ); + } + } + else if ( newAddressSize < oldAddressSize ) + { + for ( int i = newAddressSize; i < oldAddressSize; ++i ) + { + QString id = QString("A%1").arg( QString::number(i) ); + removeDisplayText(id); + removeElement( m_address[i], false ); + removeNode(id); + } + + m_address.resize(newAddressSize); + } +} + + diff --git a/src/electronics/components/ram.h b/src/electronics/components/ram.h new file mode 100644 index 0000000..7b78ee9 --- /dev/null +++ b/src/electronics/components/ram.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef RAM_H +#define RAM_H + +#include "component.h" +#include "logic.h" + +#include <qbitarray.h> +#include <qptrvector.h> + +/** +@author David Saxton +*/ +class RAM : public CallbackClass, public Component +{ + public: + RAM( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~RAM(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + protected: + void initPins(); + virtual void dataChanged(); + void inStateChanged( bool newState ); + + QBitArray m_data; + LogicIn * m_pCS; // Chip select + LogicIn * m_pOE; // Output enable + LogicIn * m_pWE; // Write enable + + int m_wordSize; + int m_addressSize; + + QPtrVector<LogicIn> m_address; + QPtrVector<LogicIn> m_dataIn; + QPtrVector<LogicOut> m_dataOut; +}; + +#endif diff --git a/src/electronics/components/resistordip.cpp b/src/electronics/components/resistordip.cpp new file mode 100644 index 0000000..d2c5bdb --- /dev/null +++ b/src/electronics/components/resistordip.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "libraryitem.h" +#include "node.h" +#include "resistance.h" +#include "resistordip.h" + +#include <kiconloader.h> +#include <klocale.h> +#include <qpainter.h> + +Item* ResistorDIP::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ResistorDIP( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ResistorDIP::libraryItem() +{ + return new LibraryItem( + "ec/resistordip", + i18n("Resistor DIP"), + i18n("Discrete"), + "resistordip.png", + LibraryItem::lit_component, + ResistorDIP::construct + ); +} + +ResistorDIP::ResistorDIP( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "multiplexer" ) +{ + m_name = i18n("Resistor DIP"); + m_desc = i18n("Set of resistors with identical values in a Dual Inline Package."); + + m_resistorCount = 0; + for ( int i=0; i<maxCount; ++i ) + m_resistance[i] = 0l; + + createProperty( "resistance", Variant::Type::Double ); + property("resistance")->setCaption( i18n("Resistance") ); + property("resistance")->setUnit( QChar(0x3a9) ); + property("resistance")->setValue(1e4); + property("resistance")->setMinValue(1e-6); + + createProperty( "count", Variant::Type::Int ); + property("count")->setCaption( i18n("Count") ); + property("count")->setMinValue(2); + property("count")->setMaxValue(maxCount); + property("count")->setValue(8); +} + +ResistorDIP::~ResistorDIP() +{ +} + + +void ResistorDIP::dataChanged() +{ + initPins(); + const double resistance = dataDouble("resistance"); + for ( int i=0; i<m_resistorCount; ++i ) + m_resistance[i]->setResistance(resistance); + + const QString display = QString::number( resistance / getMultiplier(resistance), 'g', 3 ) + getNumberMag(resistance) + QChar(0x3a9); + addDisplayText( "res", QRect( offsetX(), offsetY()-16, 32, 12 ), display ); +} + + +void ResistorDIP::initPins() +{ + const int count = dataInt("count"); + const double resistance = dataDouble("resistance"); + + if ( count == m_resistorCount ) + return; + + if ( count < m_resistorCount ) + { + for ( int i=count; i<m_resistorCount; ++i ) + { + removeElement( m_resistance[i], false ); + m_resistance[i] = 0l; + removeNode( "n"+QString::number(i) ); + removeNode( "p"+QString::number(i) ); + } + } + else + { + for ( int i=m_resistorCount; i<count; ++i ) + { + const QString nid = "n"+QString::number(i); + const QString pid = "p"+QString::number(i); + m_resistance[i] = createResistance( createPin( -24, 0, 0, nid ), createPin( 24, 0, 180, pid ), resistance ); + } + } + m_resistorCount = count; + + setSize( -16, -count*8, 32, count*16, true ); + updateDIPNodePositions(); +} + + +void ResistorDIP::updateDIPNodePositions() +{ + for ( int i=0; i<m_resistorCount; ++i ) + { + m_nodeMap["n"+QString::number(i)].y = offsetY() + 8 + 16*i; + m_nodeMap["p"+QString::number(i)].y = offsetY() + 8 + 16*i; + } + updateAttachedPositioning(); +} + + +void ResistorDIP::drawShape( QPainter &p ) +{ + int _x = int(x()+offsetX()); + int _y = int(y()+offsetY()); + + initPainter(p); + for ( int i=0; i<m_resistorCount; ++i ) + p.drawRect( _x, _y+16*i+2, 32, 12 ); + deinitPainter(p); +} + diff --git a/src/electronics/components/resistordip.h b/src/electronics/components/resistordip.h new file mode 100644 index 0000000..ce1d03a --- /dev/null +++ b/src/electronics/components/resistordip.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef RESISTORDIP_H +#define RESISTORDIP_H + +#include "component.h" + +const int maxCount = 256; + +/** +@author David Saxton +*/ +class ResistorDIP : public Component +{ +public: + ResistorDIP( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ResistorDIP(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + +protected: + virtual void drawShape( QPainter &p ); + void updateDIPNodePositions(); + virtual void dataChanged(); + /** + * Add / remove pins according to the number of inputs the user has requested + */ + void initPins(); + + int m_resistorCount; + Resistance* m_resistance[maxCount]; +}; + +#endif diff --git a/src/electronics/components/rotoswitch.cpp b/src/electronics/components/rotoswitch.cpp new file mode 100644 index 0000000..872714c --- /dev/null +++ b/src/electronics/components/rotoswitch.cpp @@ -0,0 +1,317 @@ +/*************************************************************************** + * Copyright (C) 2005 by John Myers * + * electronerd@electronerdia.net * + * * + * 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 "rotoswitch.h" + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> +#include <cmath> +#include <assert.h> + +#include <kdebug.h> + +//BEGIN class ECRotoSwitch +Item* ECRotoSwitch::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECRotoSwitch( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECRotoSwitch::libraryItem() +{ + return new LibraryItem( + QString("ec/roto_switch"), + i18n("Rotary"), + i18n("Switches"), + "rotary.png", + LibraryItem::lit_component, + ECRotoSwitch::construct ); +} + + +ECRotoSwitch::ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id ) +: Component( icnDocument, newItem, id ? id : "roto_switch" ), +m_numPositions(0) +{ +// m_name = i18n("Rotary Switch(WIP)"); + m_name = i18n("Rotary Switch"); + m_desc = i18n("Rotary Switch"); ///< \todo better description for rotoswitch + QPointArray pa; + pa.makeArc( -_pinInnerRadius, -_pinInnerRadius, 2*_pinInnerRadius, 2*_pinInnerRadius , 0, 16*360 ); + setItemPoints( pa ); + //setSize( -64, -64, 128, 128 ); + + //half the side length of the buttons + int buttonRadius = 10; + addButton( "go_left", QRect( -_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), "<", false ); + addButton( "go_right", QRect(_pinOuterRadius/3-buttonRadius, _pinOuterRadius-3*buttonRadius, 2*buttonRadius, 2*buttonRadius ), ">", false ); + + /*Variant * v = createProperty( "button_map", Variant::Type::String ); + v->setCaption( i18n("Button Map") ); + v->setAdvanced(true); + const QString defButtonMap("SSSSSSSSSSSM"); + v->setValue(defButtonMap); + */ + Variant * v = createProperty( "num_positions", Variant::Type::Int ); + v->setCaption( i18n("Number of Positions") ); + v->setAdvanced(false); + v->setValue(6); + v->setMinValue(3); + m_inNode = createPin(0,height()/2,270,"in"); + + v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + + v = createProperty( "cur_position", Variant::Type::Int ); + v->setHidden( true ); + v->setValue( 0 ); + + //v = createProperty( "left_momentary", Variant::Type::Bool ); + //v->setCaption(i18n("Left Momentary" ) ); + //v->setValue(false); +} + + +ECRotoSwitch::~ECRotoSwitch() +{ +} + + +void ECRotoSwitch::dataChanged() +{ + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_curPosition = dataInt( "cur_position" ); + setUpSwitches(); + if(m_positions[m_curPosition].posSwitch->state() != Switch::Closed) + { + m_positions[m_curPosition].posSwitch->setState(Switch::Closed); + } + for(int i = 0; i < m_numPositions; i++) + { + m_positions[i].posSwitch->setBounce( bounce, bouncePeriod_ms ); + } +} + +inline int roundTo10(int a){return ((a/10)+(a%10<5?0:1))*10;} +void ECRotoSwitch::drawShape( QPainter &p ) +{ + initPainter(p); + + + int cx = static_cast<int>(x()); + int cy = static_cast<int>(y()); + + const int rotorRadius = 5; + + + //draw the rotor + p.drawEllipse(cx - rotorRadius, cy-rotorRadius, 2*rotorRadius, 2*rotorRadius); + //and its connection + p.drawLine(cx, cy+rotorRadius, cx, cy+_pinInnerRadius); + + //draw the output positions + double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1); + //kdDebug() << "drawShape: " << bigRadius << " " << angleBetweenPositions << endl; + + /// \internal \brief Round to the nearest multiple of 8 +#define round_8(a) (((a) > 0) ? int(((a)+4)/8)*8 : int(((a)-4)/8)*8) + for(int i = 0; i < m_numPositions ; i++) + { + double angle = (7*M_PI/6) - (i * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + + p.drawEllipse(cx+contactX-_contactRadius, cy-contactY-_contactRadius, 2*_contactRadius, 2*_contactRadius); + int pinX, pinY; + switch(m_positions[i].pinAngle) + { + case 180: + pinX = _pinInnerRadius; + pinY = round_8(contactY); + break; + case 90: + pinX = round_8(contactX); + pinY = _pinInnerRadius; + break; + case 0: + pinX = -_pinInnerRadius; + pinY = round_8(contactY); + break; + default: + assert(!"Bad pin angle"); + } + p.drawLine(cx+contactX, cy-contactY, cx+pinX, cy-pinY); + //kdDebug() << contactX <<", "<< contactY <<endl; + } +#undef round_8 + //draw the connection to the selected position + double angle = (7*M_PI/6) - (m_curPosition * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + int rotorX = static_cast<int>(rotorRadius * cos(angle)); + int rotorY = static_cast<int>(rotorRadius * sin(angle)); + p.drawLine(cx+rotorX, cy-rotorY, cx+contactX, cy-contactY); + + + deinitPainter(p); +} + +void ECRotoSwitch::buttonStateChanged( const QString & id, bool state ) +{ + SwitchPosition& curSP = m_positions[m_curPosition]; + int nextPos = m_curPosition; + if(m_numPositions < 2) + { + return; + } + if(!state) //release + { + if(!curSP.isMomentary) + { + return; + } + + if(m_curPosition == 0) + { + nextPos = m_curPosition + 1; + } + else if(m_curPosition == m_numPositions - 1) + { + nextPos = m_curPosition - 1; + } + + } + else //press + { + if(id == "go_left" && m_curPosition > 0) + { + nextPos = m_curPosition - 1; + } + else if(id == "go_right" && m_curPosition < m_numPositions - 1) + { + nextPos = m_curPosition + 1; + } + + } + if(nextPos != m_curPosition) + { + SwitchPosition& nextSP = m_positions[nextPos]; + + curSP.posSwitch->setState(Switch::Open); + nextSP.posSwitch->setState(Switch::Closed); + + m_curPosition = nextPos; + + property( "cur_position" )->setValue( m_curPosition ); + } +} + + +/*! + Set up the switches according to the button_map + * + * + */ +void ECRotoSwitch::setUpSwitches() +{ + if( dataInt("num_positions") == m_numPositions ) + { + // number of positions didn't change, so we don't have to do anything. + return; + } + //this uses the _old_ value of m_numPositions! + for(int i=0; i<m_numPositions; i++) + { + SwitchPosition& sp = m_positions[i]; + QString pinName = QString("pin_%1").arg(i); + removeNode(pinName); + removeSwitch(sp.posSwitch); + } + + m_numPositions = dataInt("num_positions"); + if(m_curPosition >= m_numPositions ) + { + setActivePosition( m_numPositions - 1 ); + } + m_positions.clear();///\todo readjust old pins + m_positions.reserve(m_numPositions); + double angleBetweenPositions = (4*M_PI/3)/(m_numPositions - 1); + //kdDebug() << "setUpSwitches: " << bigRadius << " " << angleBetweenPositions << endl; + for( int i = 0; i < m_numPositions; i++) + { + double angle = (7*M_PI/6) - (i * angleBetweenPositions); + int contactX = static_cast<int>(_contactRingRadius * cos(angle)); + int contactY = static_cast<int>(_contactRingRadius * sin(angle)); + + SwitchPosition sp; + if(angle > 3*M_PI/4) + { + sp.pinAngle = 0; + contactX = -_pinOuterRadius; + } + else if(angle > M_PI/4) + { + sp.pinAngle = 90; + contactY=_pinOuterRadius; + } + else + { + sp.pinAngle = 180; + contactX = _pinOuterRadius; + } + // kdDebug() << contactX <<", "<< contactY <<endl; + + + sp.node = createPin(contactX,-contactY,sp.pinAngle,QString("pin_%1").arg(i)); + sp.posSwitch = createSwitch(m_inNode, sp.node, true); + sp.isMomentary = false;//(map[i] == 'M'); + m_positions.push_back(sp); + } + updateAttachedPositioning(); + + // redraw ourself + setChanged(); +} + +/*! + * Set the current position to \c newPosition updating the state of the switch. + * \c m_curPosition must reference a valid position to switch away from + * + * \param newPosition the position to switch to + */ +void ECRotoSwitch::setActivePosition(int newPosition) +{ + SwitchPosition& curSP = m_positions[m_curPosition]; + SwitchPosition& nextSP = m_positions[newPosition]; + + curSP.posSwitch->setState(Switch::Open); + nextSP.posSwitch->setState(Switch::Closed); + + m_curPosition = newPosition; + + property( "cur_position" )->setValue( m_curPosition ); + +} +//END class ECRotoSwitch diff --git a/src/electronics/components/rotoswitch.h b/src/electronics/components/rotoswitch.h new file mode 100644 index 0000000..909c0ea --- /dev/null +++ b/src/electronics/components/rotoswitch.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2005 by John Myers * + * electronerd@electronerdia.net * + * * + * 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. * + ***************************************************************************/ + +#ifndef ROTOSWITCH_H +#define ROTOSWITCH_H + +#include "component.h" +#include <qvaluevector.h> + +struct SwitchPosition +{ + ECNode* node; + Switch* posSwitch; + bool isMomentary; + int pinAngle; +}; + +/** + * A rotary switch + * \author John Myers + */ +class ECRotoSwitch : public Component +{ +public: + ECRotoSwitch( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECRotoSwitch(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + +private: + virtual void drawShape( QPainter &p ); + + int m_numPositions; + int m_curPosition; + + ///Half the total width of the component including pins + static const int _pinOuterRadius = 64; + ///The width of the pins + static const int _pinWidth = 8; + ///the radius of the circle centered at the origin and tangent to the inner edge of the rows of pins + static const int _pinInnerRadius = _pinOuterRadius - _pinWidth; + ///gap between pins and contact circles + static const int _wireGap = 7; + ///The radius of the largest circle tangent to the pin circles + static const int _contactOuterRadius = _pinInnerRadius - _wireGap; + ///The radius of the circles used to show positions + static const int _contactRadius = 2; + ///The radius of the ring of positions + static const int _contactRingRadius = _contactOuterRadius - _contactRadius; + + QValueVector<SwitchPosition> m_positions; + ECNode* m_inNode; + +protected: + void setUpSwitches(); +protected: + void setActivePosition(int newPosition); +}; +#endif //ROTOSWITCH_H diff --git a/src/electronics/components/serialportcomponent.cpp b/src/electronics/components/serialportcomponent.cpp new file mode 100644 index 0000000..c8e6a4e --- /dev/null +++ b/src/electronics/components/serialportcomponent.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "port.h" +#include "serialportcomponent.h" + +#include "ecnode.h" +#include "itemdocument.h" +#include "libraryitem.h" +#include "pin.h" +#include "resistance.h" + +#include <kdebug.h> +#include <klocale.h> +#include <qpainter.h> + +#include <cmath> +#include <termios.h> + +Item* SerialPortComponent::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new SerialPortComponent( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* SerialPortComponent::libraryItem() +{ + return new LibraryItem( + "ec/serial_port", + i18n("Serial Port"), + i18n("Connections"), + "ic1.png", + LibraryItem::lit_component, + SerialPortComponent::construct + ); +} + +SerialPortComponent::SerialPortComponent( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "serial_port" ) +{ + m_name = i18n("Serial Port"); + m_desc = i18n("Interface to a serial port. The pins are:<br>" + "<ul>" + "<li><b>CD</b> - Carrier Detect (control; output)</li>" + "<li><b>RD</b> - Received Data (data; output)</li>" + "<li><b>TD</b> - Transmitted Data (data; input)</li>" + "<li><b>DTR</b> - Data Terminal Ready (control; input)</li>" + "<li><b>GND</b> - Signal Ground (ground)</li>" + "<li><b>DSR</b> - Data Set Ready (control; input)</li>" + "<li><b>RTS</b> - Request to Send (control; input)</li>" + "<li><b>CTS</b> - Clear to Send (control; output)</li>" + "<li><b>RI</b> - Ring Indicator (control; output)</li>" + "</ul>"); + + QPointArray pa( 4 ); + pa[0] = QPoint( -32, -48 ); + pa[1] = QPoint( 32, -40 ); + pa[2] = QPoint( 32, 40 ); + pa[3] = QPoint( -32, 48 ); + + setItemPoints( pa ); + + m_pSerialPort = new SerialPort(); + + ECNode * pin = 0; + + // Works + pin = createPin( -40, 32, 0, "CD" ); + addDisplayText( "CD", QRect( -28, 24, 28, 16 ), "CD", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pCD = createLogicOut( pin, false ); + + // Doesn't work +// pin = createPin( -40, 16, 0, "RD" ); + addDisplayText( "RD", QRect( -28, 8, 28, 16 ), "RD", true, Qt::AlignLeft | Qt::AlignVCenter ); +// m_pRD = createLogicOut( pin, false ); + + // Works + pin = createPin( -40, 0, 0, "TD" ); + addDisplayText( "TD", QRect( -28, -8, 28, 16 ), "TD", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pTD = createLogicIn( pin); + m_pTD->setCallback( this, (CallbackPtr)(&SerialPortComponent::tdCallback) ); + + // Works + pin = createPin( -40, -16, 0, "DTR" ); + addDisplayText( "DTR", QRect( -28, -24, 28, 16 ), "DTR", true, Qt::AlignLeft | Qt::AlignVCenter ); + m_pDTR = createLogicIn( pin ); + m_pDTR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dtrCallback) ); + + // N/A + pin = createPin( -40, -32, 0, "GND" ); + addDisplayText( "GND", QRect( -28, -40, 28, 16 ), "GND", true, Qt::AlignLeft | Qt::AlignVCenter ); + pin->pin()->setGroundType( Pin::gt_always ); + + // Doesn't work +// pin = createPin( 40, 24, 180, "DSR" ); + addDisplayText( "DSR", QRect( 0, 16, 28, 16 ), "DSR", true, Qt::AlignRight | Qt::AlignVCenter ); +// m_pDSR = createLogicIn( pin ); +// m_pDSR->setCallback( this, (CallbackPtr)(&SerialPortComponent::dsrCallback) ); + + // Doesn't work +// pin = createPin( 40, 8, 180, "RTS" ); + addDisplayText( "RTS", QRect( 0, 0, 28, 16 ), "RTS", true, Qt::AlignRight | Qt::AlignVCenter ); +// m_pRTS = createLogicIn( pin ); +// m_pRTS->setCallback( this, (CallbackPtr)(&SerialPortComponent::rtsCallback) ); + + // Works + pin = createPin( 40, -8, 180, "CTS" ); + addDisplayText( "CTS", QRect( 0, -16, 28, 16 ), "CTS", true, Qt::AlignRight | Qt::AlignVCenter ); + m_pCTS = createLogicOut( pin, false ); + + // Works + pin = createPin( 40, -24, 180, "RI" ); + addDisplayText( "RI", QRect( 0, -32, 28, 16 ), "RI", true, Qt::AlignRight | Qt::AlignVCenter ); + m_pRI = createLogicOut( pin, false ); + + Variant * v = createProperty( "port", Variant::Type::Combo ); + v->setAllowed( SerialPort::ports( Port::ExistsAndRW ) ); + v->setCaption( i18n("Port") ); + +// v = createProperty( "baudRate", Variant::Type::Select ); +// v->setAllowed( QStringList::split( ",", "B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400" ) ); +// v->setCaption( i18n("Baud rate") ); +// v->setValue("B9600"); +} + + +SerialPortComponent::~SerialPortComponent() +{ + delete m_pSerialPort; +} + + +void SerialPortComponent::dataChanged() +{ +#if 0 + QString baudString = dataString("baudRate"); + unsigned baudRate = 0; + + if ( baudString == "B0" ) + baudRate = B0; + else if ( baudString == "B50" ) + baudRate = B50; + else if ( baudString == "B75" ) + baudRate = B75; + else if ( baudString == "B110" ) + baudRate = B110; + else if ( baudString == "B134" ) + baudRate = B134; + else if ( baudString == "B150" ) + baudRate = B150; + else if ( baudString == "B200" ) + baudRate = B200; + else if ( baudString == "B300" ) + baudRate = B300; + else if ( baudString == "B600" ) + baudRate = B600; + else if ( baudString == "B1200" ) + baudRate = B1200; + else if ( baudString == "B1800" ) + baudRate = B1800; + else if ( baudString == "B2400" ) + baudRate = B2400; + else if ( baudString == "B4800" ) + baudRate = B4800; + else if ( baudString == "B9600" ) + baudRate = B9600; + else if ( baudString == "B19200" ) + baudRate = B19200; + else if ( baudString == "B38400" ) + baudRate = B38400; + else + { + kdError() << k_funcinfo << "Unknown baud rate = \""<<baudString<<"\""<<endl; + return; + } + + initPort( dataString("port"), baudRate ); +#endif + + initPort( dataString("port"), B200 ); +} + + +void SerialPortComponent::initPort( const QString & port, unsigned baudRate ) +{ + if ( port.isEmpty() ) + { + m_pSerialPort->closePort(); + return; + } + + if ( ! m_pSerialPort->openPort( port, baudRate ) ) + { + p_itemDocument->canvas()->setMessage( i18n("Could not open port %1").arg( port ) ); + return; + } +} + + +void SerialPortComponent::stepNonLogic() +{ + m_pCD->setHigh( m_pSerialPort->pinState( SerialPort::CD ) ); +// m_pRD->setHigh( m_pSerialPort->pinState( SerialPort::RD ) ); + m_pCTS->setHigh( m_pSerialPort->pinState( SerialPort::CTS ) ); + m_pRI->setHigh( m_pSerialPort->pinState( SerialPort::RI ) ); +} + + +void SerialPortComponent::tdCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::TD, isHigh ); +} + + +void SerialPortComponent::dtrCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::DTR, isHigh ); +} + + +void SerialPortComponent::dsrCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::DSR, isHigh ); +} + + +void SerialPortComponent::rtsCallback( bool isHigh ) +{ + m_pSerialPort->setPinState( SerialPort::RTS, isHigh ); +} + + +void SerialPortComponent::drawShape( QPainter & p ) +{ + drawPortShape( p ); +} diff --git a/src/electronics/components/serialportcomponent.h b/src/electronics/components/serialportcomponent.h new file mode 100644 index 0000000..4f9bc9a --- /dev/null +++ b/src/electronics/components/serialportcomponent.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef SERIALPORTCOMPONENT_H +#define SERIALPORTCOMPONENT_H + +#include "logic.h" +#include "component.h" + +class SerialPort; + +/** +@author David Saxton +*/ +class SerialPortComponent : public CallbackClass, public Component +{ + public: + SerialPortComponent( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~SerialPortComponent(); + + virtual bool canFlip() const { return true; } + static Item * construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem * libraryItem(); + + virtual void stepNonLogic(); + virtual bool doesStepNonLogic() const { return true; } + + protected: + /** + * @param baudRate as defined in <bits/termios.h> + */ + void initPort( const QString & port, unsigned baudRate ); + virtual void dataChanged(); + virtual void drawShape( QPainter & p ); + + void tdCallback( bool isHigh ); + void dtrCallback( bool isHigh ); + void dsrCallback( bool isHigh ); + void rtsCallback( bool isHigh ); + + LogicIn * m_pTD; + LogicIn * m_pDTR; +// LogicIn * m_pDSR; +// LogicIn * m_pRTS; + + LogicOut * m_pCD; +// LogicOut * m_pRD; + LogicOut * m_pCTS; + LogicOut * m_pRI; + + SerialPort * m_pSerialPort; +}; + +#endif diff --git a/src/electronics/components/toggleswitch.cpp b/src/electronics/components/toggleswitch.cpp new file mode 100644 index 0000000..4efe9ab --- /dev/null +++ b/src/electronics/components/toggleswitch.cpp @@ -0,0 +1,407 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "toggleswitch.h" + +#include "canvasitemparts.h" +#include "ecnode.h" +#include "libraryitem.h" +#include "switch.h" + +#include <klocale.h> +#include <qpainter.h> + +//BEGIN class ECDPDT +Item* ECDPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDPDT( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDPDT::libraryItem() +{ + return new LibraryItem( + QString("ec/dpdt_toggle"), + i18n("DPDT"), + i18n("Switches"), + "dpdt.png", + LibraryItem::lit_component, + ECDPDT::construct ); +} + + +ECDPDT::ECDPDT( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, id ? id : "dpdt_toggle" ) +{ + m_name = i18n("DPDT Toggle"); + m_desc = i18n("Double-Pole Double-Throw switch."); + setSize( -16, -32, 32, 64 ); + + addButton( "button", QRect( -16, 32, 32, 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init4PinRight( -24, -8, 8, 24 ); + + init2PinLeft( -16, 16 ); + + m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); + m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); + m_switch3 = createSwitch( m_pNNode[1], m_pPNode[2], false ); + m_switch4 = createSwitch( m_pNNode[1], m_pPNode[3], true ); + pressed = false; +} + + +ECDPDT::~ECDPDT() +{ +} + + +void ECDPDT::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); + m_switch3->setBounce( bounce, bouncePeriod_ms ); + m_switch4->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECDPDT::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-32; + const int radius = 2; + + p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); + p.drawEllipse( _x, _y+47, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+23, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+39, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+55, 2*radius, 2*radius ); + + const int dy = pressed ? 6 : -6; + + p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+16+dy ); + p.drawLine( _x+2*radius, _y+48, _x+width()-2*radius+2, _y+48+dy ); + + deinitPainter(p); +} + +void ECDPDT::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch1->setState( state ? Switch::Open : Switch::Closed ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); + m_switch3->setState( state ? Switch::Open : Switch::Closed ); + m_switch4->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECDPDT + + +//BEGIN class ECDPST +Item* ECDPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECDPST( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECDPST::libraryItem() +{ + return new LibraryItem( + QString("ec/dpst_toggle"), + i18n("DPST"), + i18n("Switches"), + "dpst.png", + LibraryItem::lit_component, + ECDPST::construct ); +} + +ECDPST::ECDPST( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "dpst_toggle" ) +{ + m_name = i18n("DPST Toggle"); + m_desc = i18n("Double-Pole Single-Throw switch."); + setSize( -16, -16, 32, 32 ); + + addButton( "button", QRect( -16, 16, 32, 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init2PinLeft( -8, 8 ); + init2PinRight( -8, 8 ); + + m_switch1 = createSwitch( m_pPNode[0], m_pNNode[0], true ); + m_switch2 = createSwitch( m_pPNode[1], m_pNNode[1], true ); + pressed = false; +} + + +ECDPST::~ECDPST() +{ +} + + +void ECDPST::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECDPST::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-16; + const int radius = 2; + + p.drawEllipse( _x, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x, _y+22, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); + + const int dy = pressed ? 6 : 0; + + p.drawLine( _x+2*radius,_y+7,_x+width()-2*radius,_y+1+dy ); + p.drawLine( _x+2*radius,_y+24,_x+width()-2*radius,_y+18+dy ); + + deinitPainter(p); +} + +void ECDPST::buttonStateChanged( const QString &, bool state ) +{ + m_switch1->setState( state ? Switch::Closed : Switch::Open ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); + pressed = state; +} +//END class ECDPST + + +//BEGIN class ECSPDT +Item* ECSPDT::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSPDT( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSPDT::libraryItem() +{ + return new LibraryItem( + QString("ec/spdt_toggle"), + i18n("SPDT"), + i18n("Switches"), + "spdt.png", + LibraryItem::lit_component, + ECSPDT::construct ); +} + + +ECSPDT::ECSPDT( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "spdt_toggle" ) +{ + m_name = i18n("SPDT Toggle"); + m_desc = i18n("Single-Pole Double-Throw switch."); + setSize( -16, -16, 32, 32 ); + + addButton( "button", QRect( -16, 16, width(), 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + init1PinLeft( 0 ); + init2PinRight( -8, 8 ); + + m_switch1 = createSwitch( m_pNNode[0], m_pPNode[0], false ); + m_switch2 = createSwitch( m_pNNode[0], m_pPNode[1], true ); + + pressed = false; +} + + +ECSPDT::~ECSPDT() +{ +} + + +void ECSPDT::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + + m_switch1->setBounce( bounce, bouncePeriod_ms ); + m_switch2->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECSPDT::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-16; + const int radius = 2; + + p.drawEllipse( _x, _y+15, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+6, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+22, 2*radius, 2*radius ); + + const int dy = pressed ? 21 : 10; + p.drawLine( _x+2*radius, _y+16, _x+width()-2*radius+2, _y+dy ); + + deinitPainter(p); +} + +void ECSPDT::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch1->setState( state ? Switch::Open : Switch::Closed ); + m_switch2->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECSPDT + + +//BEGIN class ECSPST +Item* ECSPST::construct( ItemDocument *itemDocument, bool newItem, const char *id ) +{ + return new ECSPST( (ICNDocument*)itemDocument, newItem, id ); +} + +LibraryItem* ECSPST::libraryItem() +{ + return new LibraryItem( + QString("ec/spst_toggle"), + i18n("SPST"), + i18n("Switches"), + "spst.png", + LibraryItem::lit_component, + ECSPST::construct + ); +} + + +ECSPST::ECSPST( ICNDocument *icnDocument, bool newItem, const char *id ) + : Component( icnDocument, newItem, (id) ? id : "spst_toggle" ) +{ + m_name = i18n("SPST Toggle"); + m_desc = i18n("Single-Pole Single-Throw switch."); + setSize( -16, -8, 32, 16 ); + pressed = false; + + addButton( "button", QRect( -16, 8, width(), 20 ), "", true ); + + createProperty( "button_text", Variant::Type::String ); + property("button_text")->setCaption( i18n("Button Text") ); + + Variant * v = createProperty( "bounce", Variant::Type::Bool ); + v->setCaption("Bounce"); + v->setAdvanced(true); + v->setValue(false); + + v = createProperty( "bounce_period", Variant::Type::Double ); + v->setCaption("Bounce Period"); + v->setAdvanced(true); + v->setUnit("s"); + v->setValue(5e-3); + + button("button")->setState(pressed); + + init1PinLeft(); + init1PinRight(); + + m_switch = createSwitch( m_pNNode[0], m_pPNode[0], !pressed ); +} + + +ECSPST::~ECSPST() +{ +} + + +void ECSPST::dataChanged() +{ + button("button")->setText( dataString("button_text") ); + + bool bounce = dataBool("bounce"); + int bouncePeriod_ms = int(dataDouble("bounce_period")*1e3); + m_switch->setBounce( bounce, bouncePeriod_ms ); +} + + +void ECSPST::drawShape( QPainter &p ) +{ + initPainter(p); + + int _x = (int)x()-16; + int _y = (int)y()-8; + const int radius = 2; + + p.drawEllipse( _x, _y+7, 2*radius, 2*radius ); + p.drawEllipse( _x+width()-2*radius+1, _y+7, 2*radius, 2*radius ); + const int dy = pressed ? 0 : -6; + p.drawLine( _x+2*radius, _y+8, _x+width()-2*radius, _y+8+dy ); + + deinitPainter(p); +} + +void ECSPST::buttonStateChanged( const QString &, bool state ) +{ + pressed = state; + m_switch->setState( state ? Switch::Closed : Switch::Open ); +} +//END class ECSPST + diff --git a/src/electronics/components/toggleswitch.h b/src/electronics/components/toggleswitch.h new file mode 100644 index 0000000..80fd064 --- /dev/null +++ b/src/electronics/components/toggleswitch.h @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef TOGGLESWITCH_H +#define TOGGLESWITCH_H + +#include "component.h" + +/** +@short Double Pole Double Throw +@author David Saxton +*/ +class ECDPDT : public Component +{ +public: + ECDPDT( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDPDT(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + Switch *m_switch3; + Switch *m_switch4; + bool pressed; +}; + + +/** +@short Double Pole Single Throw +@author David Saxton +*/ +class ECDPST : public Component +{ +public: + ECDPST( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECDPST(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + bool pressed; +}; + + +/** +@short Single Pole Double Throw +@author David Saxton +*/ +class ECSPDT : public Component +{ +public: + ECSPDT( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSPDT(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch1; + Switch *m_switch2; + bool pressed; +}; + + +/** +@short Single-Pole Single-Throw Switch +@author David Saxton +*/ +class ECSPST : public Component +{ +public: + ECSPST( ICNDocument *icnDocument, bool newItem, const char *id = 0L ); + ~ECSPST(); + + static Item* construct( ItemDocument *itemDocument, bool newItem, const char *id ); + static LibraryItem *libraryItem(); + + virtual void buttonStateChanged( const QString &id, bool state ); + virtual void dataChanged(); + virtual bool canFlip() const { return true; } + +private: + virtual void drawShape( QPainter &p ); + Switch *m_switch; + bool pressed; +}; + +#endif diff --git a/src/electronics/ecnode.cpp b/src/electronics/ecnode.cpp new file mode 100644 index 0000000..0fb2801 --- /dev/null +++ b/src/electronics/ecnode.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "circuitdocument.h" +#include "src/core/ktlconfig.h" +#include "component.h" +#include "connector.h" +#include "ecnode.h" +#include "pin.h" + +#include <kdebug.h> +#include <qpainter.h> + +#include <cmath> + +// The voltage at the middle of the voltage indicator +const double vMidPoint = 5.; + +// The maximum length of the voltage indiactor +const int vLength = 8; + +// The current at the middle of the current indicator +const double iMidPoint = 0.03; + +// The maximum thicnkess of the current indicator +const int iLength = 6; + +inline double calcVProp( const double v ) +{ + return 1 - vMidPoint/(vMidPoint+std::abs(v)); +} + +inline double calcIProp( const double i ) +{ + return 1 - iMidPoint/(iMidPoint+std::abs(i)); +} + +inline int calcThickness( const double prop ) +{ + return (int)((iLength-2)*prop+2); +} + +inline int calcLength( const double prop, const double v ) +{ + return (v>0) ? -int(vLength*prop) : int(vLength*prop); +} + +ECNode::ECNode( ICNDocument *icnDocument, Node::node_type _type, node_dir dir, const QPoint &pos, QString *_id ) + : Node( icnDocument, _type, dir, pos, _id ) +{ + m_prevV = 0; + m_prevI = 0; + m_pinPoint = 0l; + m_bShowVoltageBars = KTLConfig::showVoltageBars(); + + icnDocument->registerItem(this); + + if ( type() == ec_pin ) + { + m_pinPoint = new QCanvasRectangle( 0, 0, 3, 3, canvas() ); + m_pinPoint->setBrush(Qt::black); + m_pinPoint->setPen(Qt::black); + } + + m_pins.resize(1); + m_pins[0] = new Pin(this); +} + + +ECNode::~ECNode() +{ + if (m_pinPoint) + m_pinPoint->setCanvas(0l); + delete m_pinPoint; + m_pinPoint = 0l; + + for ( unsigned i = 0; i < m_pins.size(); i++ ) + delete m_pins[i]; + m_pins.resize(0); +} + + +void ECNode::setNumPins( unsigned num ) +{ + unsigned oldNum = m_pins.size(); + + if ( num == oldNum ) + return; + + if ( num > oldNum ) + { + m_pins.resize(num); + for ( unsigned i = oldNum; i < num; i++ ) + m_pins[i] = new Pin(this); + } + else + { + for ( unsigned i = num; i < oldNum; i++ ) + delete m_pins[i]; + m_pins.resize(num); + } + + emit numPinsChanged(num); +} + + +void ECNode::setNodeChanged() +{ + if ( !canvas() || numPins() != 1 ) + return; + + Pin * pin = m_pins[0]; + + double v = pin->voltage(); + double i = pin->current(); + + if ( v != m_prevV || i != m_prevI ) + { + QRect r = boundingRect(); + r.setCoords( r.left()+(r.width()/2)-1, r.top()+(r.height()/2)-1, r.right()-(r.width()/2)+1, r.bottom()-(r.height()/2)+1 ); + canvas()->setChanged(r); + m_prevV = v; + m_prevI = i; + } +} + + +void ECNode::setParentItem( CNItem * parentItem ) +{ + Node::setParentItem(parentItem); + + if ( Component * component = dynamic_cast<Component*>(parentItem) ) + { + connect( component, SIGNAL(elementDestroyed(Element* )), this, SLOT(removeElement(Element* )) ); + connect( component, SIGNAL(switchDestroyed( Switch* )), this, SLOT(removeSwitch( Switch* )) ); + } +} + + +void ECNode::removeElement( Element * e ) +{ + for ( unsigned i = 0; i < m_pins.size(); i++ ) + m_pins[i]->removeElement(e); +} + + +void ECNode::removeSwitch( Switch * sw ) +{ + for ( unsigned i = 0; i < m_pins.size(); i++ ) + m_pins[i]->removeSwitch( sw ); +} + + +void ECNode::drawShape( QPainter &p ) +{ + const int _x = int(x()); + const int _y = int(y()); + + if ( type() == ec_junction ) + { +// p.drawRect( _x-2, _y-1, 5, 3 ); +// p.drawRect( _x-1, _y-2, 3, 5 ); + p.drawRect( _x-1, _y-1, 3, 3 ); + return; + } + + if (m_pinPoint) + { + bool drawDivPoint; + QPoint divPoint = findConnectorDivergePoint(&drawDivPoint); + m_pinPoint->setVisible(drawDivPoint); + m_pinPoint->move( divPoint.x()-1, divPoint.y()-1 ); + } + + // Now to draw on our current/voltage bar indicators + + if ( numPins() == 1 ) + { + double v = pin()->voltage(); + double vProp = calcVProp(v); + int length = calcLength( vProp, v ); + + if ( m_bShowVoltageBars && length != 0 ) + { + // we can assume that v != 0 as length != 0 + + QPen oldPen = p.pen(); + + double i = pin()->current(); + double iProp = calcIProp(i); + int thickness = calcThickness(iProp); + + if ( v > 0 ) + p.setPen( QPen( QColor( 255, 166, 0 ), thickness ) ); + + else + p.setPen( QPen( QColor( 0, 136, 255 ), thickness ) ); + + // The node line (drawn at the end of this function) will overdraw + // some of the voltage bar, so we need to adapt the length + if ( v > 0 && (m_dir == Node::dir_up || m_dir == Node::dir_down) ) + length--; + else if ( v < 0 && (m_dir == Node::dir_left || m_dir == Node::dir_right) ) + length++; + + if ( m_dir == Node::dir_right ) + p.drawLine( _x+3, _y, _x+3, _y+length ); + + else if ( m_dir == Node::dir_down ) + p.drawLine( _x, _y+3, _x-length, _y+3 ); + + else if ( m_dir == Node::dir_left ) + p.drawLine( _x-3, _y, _x-3, _y+length ); + + else if ( m_dir == Node::dir_up ) + p.drawLine( _x, _y-3, _x-length, _y-3 ); + + p.setPen(oldPen); + } + } + + QPen pen( p.pen() ); + pen.setWidth( (numPins() > 1) ? 2 : 1 ); + p.setPen(pen); + + if ( m_dir == Node::dir_right ) p.drawLine( _x, _y, _x+8, _y ); + else if ( m_dir == Node::dir_down ) p.drawLine( _x, _y, _x, _y+8 ); + else if ( m_dir == Node::dir_left ) p.drawLine( _x, _y, _x-8, _y ); + else if ( m_dir == Node::dir_up ) p.drawLine( _x, _y, _x, _y-8 ); +} + +#include "ecnode.moc" diff --git a/src/electronics/ecnode.h b/src/electronics/ecnode.h new file mode 100644 index 0000000..6f8f36e --- /dev/null +++ b/src/electronics/ecnode.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ECNODE_H +#define ECNODE_H + +#include "node.h" + +#include <qvaluevector.h> + +class ECNode; +class Element; +class Pin; +class Switch; +class QTimer; + +typedef QValueList<ECNode*> ECNodeList; +typedef QValueList<Element*> ElementList; +typedef QValueVector<Pin*> PinVector; + +/** +@short Electrical node with voltage / current / etc properties +@author David Saxton +*/ +class ECNode : public Node +{ + Q_OBJECT + public: + ECNode( ICNDocument *icnDocument, Node::node_type type, node_dir dir, const QPoint &pos, QString *id = 0L ); + ~ECNode(); + + virtual void setParentItem( CNItem *parentItem ); + virtual void drawShape( QPainter &p ); + /** + * Set the number of pins "contained" in this node. + */ + void setNumPins( unsigned num ); + /** + * @return the number of pins in this node. + * @see setNumPins + */ + unsigned numPins() const { return m_pins.size(); } + PinVector pins() const { return m_pins; } + Pin * pin( unsigned num = 0 ) const { return (num < m_pins.size()) ? m_pins[num] : 0l; } + bool showVoltageBars() const { return m_bShowVoltageBars; } + void setShowVoltageBars( bool show ) { m_bShowVoltageBars = show; } + void setNodeChanged(); + + signals: + void numPinsChanged( unsigned newNum ); + + protected slots: + void removeElement( Element * e ); + void removeSwitch( Switch * sw ); + + protected: + bool m_bShowVoltageBars; + double m_prevV; + double m_prevI; + QCanvasRectangle * m_pinPoint; + PinVector m_pins; +}; + +#endif + + + diff --git a/src/electronics/gpsimprocessor.cpp b/src/electronics/gpsimprocessor.cpp new file mode 100644 index 0000000..1a3b862 --- /dev/null +++ b/src/electronics/gpsimprocessor.cpp @@ -0,0 +1,880 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "config.h" +#ifndef NO_GPSIM + +#include "asmparser.h" +#include "debugmanager.h" +#include "flowcodedocument.h" +#include "gpsimprocessor.h" +#include "language.h" +#include "languagemanager.h" +#include "microlibrary.h" +#include "processchain.h" +#include "simulator.h" + +#include <assert.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtimer.h> + +#include "gpsim/cod.h" +#include "gpsim/interface.h" +#include "gpsim/gpsim_classes.h" +#include "gpsim/pic-processor.h" +#include "gpsim/registers.h" +#include "gpsim/14bit-registers.h" +#include "gpsim/symbol.h" +#include "gpsim/sim_context.h" + +bool bDoneGpsimInit = false; +bool bUseGUI = true; +// extern "C" void initialize_gpsim(); +// void initialize_gpsim(void); +extern void initialize_commands(); +void initialize_ConsoleUI(); +extern void initialize_readline(); +extern void gui_main(void); +extern void cli_main(); +void gpsim_version() {}; +void quit_gui() {}; + + +//BEGIN class GpsimProcessor +/** +Work around a bug in gpsim: the directory in a filename is recorded twice, e.g. +"/home/david/afile.asm" is recorded as "/home/david//home/david/afile.asm". This +function will remove the duplicated directory path (by searching for a "//"). +*/ +QString sanitizeGpsimFile( QString file ) +{ + int pos = file.find("//"); + if ( pos != -1 ) + { + file.remove( 0, pos + 1 ); + } + return file; +} + + +GpsimProcessor::GpsimProcessor( QString symbolFile, QObject *parent ) + : QObject(parent), + m_symbolFile(symbolFile) +{ + if (!bDoneGpsimInit) + { +#ifndef GPSIM_0_21_4 + initialize_ConsoleUI(); +#endif + initialize_gpsim_core(); + initialization_is_complete(); + + bDoneGpsimInit = true; + } + + m_bCanExecuteNextCycle = true; + m_bIsRunning = false; + m_pPicProcessor = 0l; + m_codLoadStatus = CodUnknown; + m_pRegisterMemory = 0l; + m_debugMode = GpsimDebugger::AsmDebugger; + m_pDebugger[0] = m_pDebugger[1] = 0l; + + Processor * tempProcessor = 0l; + const char * fileName = symbolFile.ascii(); + +#ifdef GPSIM_0_21_4 + switch ( (cod_errors)load_symbol_file( &tempProcessor, fileName ) ) + { + case COD_SUCCESS: + m_codLoadStatus = CodSuccess; + break; + case COD_FILE_NOT_FOUND: + m_codLoadStatus = CodFileNotFound; + break; + case COD_UNRECOGNIZED_PROCESSOR: + m_codLoadStatus = CodUnrecognizedProcessor; + break; + case COD_FILE_NAME_TOO_LONG: + m_codLoadStatus = CodFileNameTooLong; + break; + case COD_LST_NOT_FOUND: + m_codLoadStatus = CodLstNotFound; + break; + case COD_BAD_FILE: + m_codLoadStatus = CodBadFile; + break; + default: + m_codLoadStatus = CodUnknown; + } +#else // GPSIM_0_21_11+ + FILE * pFile = fopen( fileName, "r" ); + if ( !pFile ) + m_codLoadStatus = CodFileUnreadable; + else + m_codLoadStatus = ( ProgramFileTypeList::GetList().LoadProgramFile( & tempProcessor, fileName, pFile ) ) ? CodSuccess : CodFailure; +#endif + + m_pPicProcessor = dynamic_cast<pic_processor*>(tempProcessor); + + if ( codLoadStatus() == CodSuccess ) + { + m_pRegisterMemory = new RegisterSet( m_pPicProcessor ); + m_pDebugger[0] = new GpsimDebugger( GpsimDebugger::AsmDebugger, this ); + m_pDebugger[1] = new GpsimDebugger( GpsimDebugger::HLLDebugger, this ); + Simulator::self()->attachGpsimProcessor(this); + DebugManager::self()->registerGpsim(this); + } +} + + +GpsimProcessor::~GpsimProcessor() +{ + Simulator::self()->detachGpsimProcessor(this); + delete m_pRegisterMemory; + + if ( m_pDebugger[0] ) + m_pDebugger[0]->deleteLater(); + if ( m_pDebugger[1] ) + m_pDebugger[1]->deleteLater(); +} + + +void GpsimProcessor::displayCodLoadStatus( ) +{ + switch (m_codLoadStatus) + { + case CodSuccess: + break; + case CodFileNotFound: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" was not found.").arg(m_symbolFile), i18n("File Not Found") ); + break; + case CodUnrecognizedProcessor: + KMessageBox::sorry( 0l, i18n("The processor for cod file \"%1\" is unrecognized.").arg(m_symbolFile), i18n("Unrecognized Processor") ); + break; + case CodFileNameTooLong: + KMessageBox::sorry( 0l, i18n("The file name \"%1\" is too long.").arg(m_symbolFile), i18n("Filename Too Long") ); + break; + case CodLstNotFound: + KMessageBox::sorry( 0l, i18n("The lst file associated with the cod file \"%1\" was not found.").arg(m_symbolFile), i18n("LST File Not Found") ); + break; + case CodBadFile: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" is bad.").arg(m_symbolFile), i18n("Bad File") ); + break; + case CodFileUnreadable: + KMessageBox::sorry( 0l, i18n("The cod file \"%1\" could not be read from.").arg(m_symbolFile), i18n("Unreadable File") ); + break; + case CodFailure: + case CodUnknown: + KMessageBox::sorry( 0l, i18n("An error occured with the cod file \"%1\".").arg(m_symbolFile), i18n("Error") ); + break; + } +} + + +unsigned GpsimProcessor::programMemorySize() const +{ + return m_pPicProcessor->program_memory_size(); +} + + +QStringList GpsimProcessor::sourceFileList() +{ + QStringList files; + + // Work around nasty bug in gpsim 0.21.4 where nsrc_files value might be used uninitiazed + int max = m_pPicProcessor->files.nsrc_files(); +#ifdef GPSIM_0_21_4 + if ( max > 10 ) + max = 10; +#endif + + for ( int i = 0; i < max; ++i ) + { + if ( !m_pPicProcessor->files[i] ) + continue; + + files << sanitizeGpsimFile( m_pPicProcessor->files[i]->name().c_str() ); + } + + return files; +} + + +void GpsimProcessor::emitLineReached() +{ + m_pDebugger[0]->emitLineReached(); + m_pDebugger[1]->emitLineReached(); +} + + +void GpsimProcessor::setRunning( bool run ) +{ + if ( m_bIsRunning == run ) + return; + + m_bIsRunning = run; + emit runningStatusChanged(run); +} + + +void GpsimProcessor::executeNext() +{ + if ( !m_bIsRunning ) + return; + + if ( !m_bCanExecuteNextCycle ) + { + m_bCanExecuteNextCycle = true; + return; + } + + unsigned long long beforeExecuteCount = get_cycles().get(); + + m_pPicProcessor->step_one(false); // Don't know what the false is for; gpsim ignores its value anyway + + // Some instructions take more than one cycle to execute, so ignore next cycle if this was the case + if ( (get_cycles().get() - beforeExecuteCount) > 1 ) + m_bCanExecuteNextCycle = false; + + currentDebugger()->checkForBreak(); + + // Let's also update the values of RegisterInfo every 50 milliseconds + if ( (beforeExecuteCount % 20000) == 0 ) + registerMemory()->update(); +} + + +void GpsimProcessor::reset() +{ + bool wasRunning = isRunning(); + m_pPicProcessor->reset(SIM_RESET); + setRunning(false); + if (!wasRunning) + { + // If we weren't running before, then the next signal won't have been emitted + emitLineReached(); + } +} + + +MicroInfo * GpsimProcessor::microInfo( ) const +{ + if ( !m_pPicProcessor ) + return 0l; + + return MicroLibrary::self()->microInfoWithID( m_pPicProcessor->name().c_str() ); +} + + +int GpsimProcessor::operandRegister( unsigned address ) +{ + instruction * ins = m_pPicProcessor->program_memory[ address ]; + if ( Register_op * reg = dynamic_cast<Register_op*>(ins) ) + return reg->register_address; + return -1; +} + + +int GpsimProcessor::operandLiteral( unsigned address ) +{ + instruction * ins = m_pPicProcessor->program_memory[ address ]; + if ( Literal_op * lit = dynamic_cast<Literal_op*>(ins) ) + return lit->L; + return -1; +} + + +GpsimProcessor::ProgramFileValidity GpsimProcessor::isValidProgramFile( const QString & programFile ) +{ + if ( !KStandardDirs::exists(programFile) ) + return DoesntExist; + + QString extension = programFile.right( programFile.length() - programFile.findRev('.') - 1 ).lower(); + + if ( extension == "flowcode" || + extension == "asm" || + extension == "cod" || + extension == "basic" || extension == "microbe" || + extension == "c" ) + return Valid; + + if ( extension == "hex" && QFile::exists( QString(programFile).replace(".hex",".cod") ) ) + return Valid; + + return IncorrectType; +} + + +QString GpsimProcessor::generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember ) +{ + if ( !isValidProgramFile(fileName) ) + return QString::null; + + QString extension = fileName.right( fileName.length() - fileName.findRev('.') - 1 ).lower(); + + if ( extension == "cod" ) + { + QTimer::singleShot( 0, receiver, successMember ); + return fileName; + } + if ( extension == "hex" ) + { + QTimer::singleShot( 0, receiver, successMember ); + // We've already checked for the existance of the ".cod" file in GpsimProcessor::isValidProgramFile + return QString(fileName).replace(".hex",".cod"); + } + + else if ( extension == "basic" || extension == "microbe" ) + { + compileMicrobe( fileName, receiver, successMember, failMember ); + return QString(fileName).replace( "."+extension, ".cod" ); + } + else if ( extension == "flowcode" ) + { + const QString hexFile = KTempFile( QString::null, ".hex" ).name(); + + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( hexFile ); + o.setInputFiles( fileName ); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::FlowCode_Program ); + + ProcessChain * pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(hexFile).replace( ".hex", ".cod" ); + } + else if ( extension == "asm" ) + { + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(fileName).replace(".asm",".hex")); + o.setInputFiles(fileName); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType(fileName), ProcessOptions::ProcessPath::Program ) ); + + ProcessChain *pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(fileName).replace(".asm",".cod"); + } + else if ( extension == "c" ) + { + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(fileName).replace(".c",".hex")); + o.setInputFiles(fileName); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::C_Program ); + + ProcessChain *pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } + + return QString(fileName).replace(".c",".cod"); + } + + if ( failMember ) + QTimer::singleShot( 0, receiver, failMember ); + return QString::null; +} + + +void GpsimProcessor::compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember ) +{ + ProcessOptions o; + o.b_addToProject = false; + o.setTargetFile( QString(filename).replace(".microbe",".hex") ); + o.setInputFiles(filename); + o.setMethod( ProcessOptions::Method::Forget ); + o.setProcessPath( ProcessOptions::ProcessPath::Microbe_Program ); + ProcessChain * pc = LanguageManager::self()->compile(o); + if (receiver) + { + if (successMember) + connect( pc, SIGNAL(successful()), receiver, successMember ); + if (failMember) + connect( pc, SIGNAL(failed()), receiver, failMember ); + } +} +//END class GpsimProcessor + + + +//BEGIN class GpsimDebugger +GpsimDebugger::GpsimDebugger( Type type, GpsimProcessor * gpsim ) + : QObject() +{ + m_pGpsim = gpsim; + m_type = type; + m_pBreakFromOldLine = 0l; + m_addressToLineMap = 0l; + m_stackLevelLowerBreak = -1; + m_addressSize = 0; + + connect( m_pGpsim, SIGNAL(runningStatusChanged(bool )), this, SLOT(gpsimRunningStatusChanged(bool )) ); + + if ( type == HLLDebugger ) + { + const QStringList sourceFileList = m_pGpsim->sourceFileList(); + QStringList::const_iterator sflEnd = sourceFileList.end(); + for ( QStringList::const_iterator it = sourceFileList.begin(); it != sflEnd; ++it ) + { + AsmParser p(*it); + p.parse(this); + } + } + + initAddressToLineMap(); +} + + +GpsimDebugger::~GpsimDebugger() +{ + QValueList<DebugLine*> debugLinesToDelete; + + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || dl->markedAsDeleted() ) + continue; + + dl->markAsDeleted(); + debugLinesToDelete += dl; + } + + const QValueList<DebugLine*>::iterator end = debugLinesToDelete.end(); + for ( QValueList<DebugLine*>::iterator it = debugLinesToDelete.begin(); it != end; ++it ) + delete *it; + + delete [] m_addressToLineMap; +} + + +void GpsimDebugger::gpsimRunningStatusChanged( bool isRunning ) +{ + if (!isRunning) + { + m_stackLevelLowerBreak = -1; + m_pBreakFromOldLine = 0l; + emitLineReached(); + } +} + + +void GpsimDebugger::associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ) +{ + if ( assemblyLine < 0 || sourceLine < 0 ) + { + kdWarning() << k_funcinfo << "Invalid lines: assemblyLine="<<assemblyLine<<" sourceLine="<<sourceLine<<endl; + return; + } + + SourceLine hllSource = SourceLine( sourceFile, sourceLine ); + SourceLine asmSource = SourceLine( assemblyFile, assemblyLine ); + + if ( m_sourceLineMap.contains(asmSource) ) + { + kdWarning() << k_funcinfo << "Already have an association for assembly (\""<<assemblyFile<<"\","<<assemblyLine<<")"<<endl; + return; + } + + m_sourceLineMap[asmSource] = hllSource; +} + + +void GpsimDebugger::initAddressToLineMap() +{ + m_addressSize = m_pGpsim->programMemorySize(); + + delete [] m_addressToLineMap; + m_addressToLineMap = new DebugLine*[m_addressSize]; + memset( m_addressToLineMap, 0, m_addressSize * sizeof(DebugLine*) ); + + if ( m_type == AsmDebugger ) + { + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + int line = m_pGpsim->picProcessor()->pma->get_src_line(i) - 1; + int fileID = m_pGpsim->picProcessor()->pma->get_file_id(i); + FileContext * fileContext = m_pGpsim->picProcessor()->files[fileID]; + + if (fileContext) + m_addressToLineMap[i] = new DebugLine( sanitizeGpsimFile( fileContext->name().c_str() ), line ); + } + } + else + { + SourceLineMap::const_iterator slmEnd = m_sourceLineMap.end(); + for ( SourceLineMap::const_iterator it = m_sourceLineMap.begin(); it != slmEnd; ++it ) + { + SourceLineMap::const_iterator next = it; + ++next; + + int asmToLine = ((next == slmEnd) || (next.key().fileName() != it.key().fileName())) ? -1 : next.key().line() - 1; + + QString asmFile = it.key().fileName(); + int asmFromLine = it.key().line(); + SourceLine sourceLine = it.data(); + + + std::string stdAsmFile( asmFile.ascii() ); + int fileID = m_pGpsim->picProcessor()->files.Find( stdAsmFile ); + if ( fileID == -1 ) + { + kdWarning() << k_funcinfo << "Could not find FileContext (asmFile=\""<<asmFile<<"\")"<<endl; + continue; + } + + if ( asmToLine == -1 ) + asmToLine = m_pGpsim->picProcessor()->files[fileID]->max_line() - 2; + + if ( (asmFromLine < 0) || (asmToLine < asmFromLine) ) + { + kdWarning() << k_funcinfo << "Invalid lines: asmFromLine="<<asmFromLine<<" asmToLine="<<asmToLine<<endl; + continue; + } + + DebugLine * debugLine = new DebugLine( sourceLine.fileName(), sourceLine.line() ); + bool used = false; + + for ( int i = asmFromLine; i <= asmToLine; ++i ) + { +#ifdef GPSIM_0_21_4 + int address = m_pGpsim->picProcessor()->pma->find_address_from_line( fileID, i+1 ); +#else // GPSIM_0_21_11 + int address = m_pGpsim->picProcessor()->pma->find_address_from_line( m_pGpsim->picProcessor()->files[fileID], i+1 ); +#endif + if ( address != -1 ) + { + used = true; + m_addressToLineMap[address] = debugLine; + } + } + + if (!used) + delete debugLine; + } + } +} + + +void GpsimDebugger::setBreakpoints( const QString & path, const IntList & lines ) +{ + for ( unsigned i = 0; i < m_addressSize; i++ ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || dl->fileName() != path ) + continue; + + dl->setBreakpoint( lines.contains( dl->line() ) ); + } +} + + +void GpsimDebugger::setBreakpoint( const QString & path, int line, bool isBreakpoint ) +{ + for ( unsigned i = 0; i < m_addressSize; i++ ) + { + if ( !m_addressToLineMap[i] ) + continue; + + if ( (m_addressToLineMap[i]->fileName() == path) && + ( line == m_addressToLineMap[i]->line() ) ) + m_addressToLineMap[i]->setBreakpoint(isBreakpoint); + } +} + + +DebugLine * GpsimDebugger::currentDebugLine() +{ + return m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ]; +} + + +SourceLine GpsimDebugger::currentLine() +{ + DebugLine * dl = currentDebugLine(); + return dl ? *dl : SourceLine(); +} + + +void GpsimDebugger::emitLineReached() +{ + SourceLine currentAt = currentLine(); + + if ( currentAt == m_previousAtLineEmit ) + return; + + m_previousAtLineEmit = currentAt; + m_pGpsim->registerMemory()->update(); + emit lineReached(currentAt); +} + + +void GpsimDebugger::checkForBreak() +{ + DebugLine * currentLine = m_addressToLineMap[ m_pGpsim->picProcessor()->pc->get_value() ]; + int currentStackLevel = int( m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask ); + + bool ontoNextLine = m_pBreakFromOldLine != currentLine; + bool lineBreakpoint = currentLine ? currentLine->isBreakpoint() : false; + bool stackBreakpoint = m_stackLevelLowerBreak >= currentStackLevel; + + if ( ontoNextLine && (lineBreakpoint || stackBreakpoint) ) + m_pGpsim->setRunning(false); +} + + +int GpsimDebugger::programAddress( const QString & path, int line ) +{ + for ( unsigned i = 0; i < m_addressSize; ++i ) + { + DebugLine * dl = m_addressToLineMap[i]; + if ( !dl || (dl->line() != line) || (dl->fileName() != path) ) + continue; + + return i; + } + + return -1; +} + + +void GpsimDebugger::stepInto() +{ + // I'm not aware of the stack being able to increase in size by more than + // one at a time, so "1" should suffice here...but to be on the safe side, + // make it a nice large number + stackStep( 1 << 16 ); +} +void GpsimDebugger::stepOver() +{ + stackStep(0); +} +void GpsimDebugger::stepOut() +{ + stackStep(-1); +} +void GpsimDebugger::stackStep( int dl ) +{ + if ( m_pGpsim->isRunning() ) + return; + + int initialStack = (m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask) + dl; + DebugLine * initialLine = currentDebugLine(); + + if ( initialStack < 0 ) + initialStack = 0; + + // Reset any previous stackStep, and step + m_pBreakFromOldLine = 0l; + m_stackLevelLowerBreak = -1; + m_pGpsim->picProcessor()->step_one(false); + + int currentStack = m_pGpsim->picProcessor()->stack->pointer & m_pGpsim->picProcessor()->stack->stack_mask; + DebugLine * currentLine = currentDebugLine(); + + if ( (initialStack >= currentStack) && (initialLine != currentLine) ) + emitLineReached(); + + else + { + // Looks like we stepped into something or haven't gone onto the next + // instruction, wait until we step back out.... + m_stackLevelLowerBreak = initialStack; + m_pBreakFromOldLine = initialLine; + m_pGpsim->setRunning(true); + } +} +//END class Debugger + + + +//BEGIN class RegisterSet +RegisterSet::RegisterSet( pic_processor * picProcessor ) +{ + unsigned numRegisters = picProcessor->rma.get_size(); + m_registers.resize( numRegisters, 0l ); + for ( unsigned i = 0; i < numRegisters; ++i ) + { + RegisterInfo * info = new RegisterInfo( & picProcessor->rma[i] ); + m_registers[i] = info; + m_nameToRegisterMap[ info->name() ] = info; + } + + RegisterInfo * info = new RegisterInfo( picProcessor->W ); + m_registers.append( info ); + m_nameToRegisterMap[ info->name() ] = info; +} + + +RegisterSet::~RegisterSet() +{ + for ( unsigned i = 0; i < m_registers.size(); ++i ) + delete m_registers[i]; +} + + +RegisterInfo * RegisterSet::fromAddress( unsigned address ) +{ + return (address < m_registers.size()) ? m_registers[address] : 0l; +} + + +RegisterInfo * RegisterSet::fromName( const QString & name ) +{ + // First try the name as case sensitive, then as case insensitive. + if ( m_nameToRegisterMap.contains( name ) ) + return m_nameToRegisterMap[ name ]; + + QString nameLower = name.lower(); + + RegisterInfoMap::iterator end = m_nameToRegisterMap.end(); + for ( RegisterInfoMap::iterator it = m_nameToRegisterMap.begin(); it != end; ++ it ) + { + if ( it.key().lower() == nameLower ) + return it.data(); + } + + return 0l; +} + + +void RegisterSet::update() +{ + for ( unsigned i = 0; i < m_registers.size(); ++i ) + m_registers[i]->update(); +} +//END class RegisterSet + + + +//BEGIN class RegisterInfo +RegisterInfo::RegisterInfo( Register * reg ) +{ + assert(reg); + m_pRegister = reg; + m_type = Invalid; + m_prevEmitValue = 0; + + switch ( m_pRegister->isa() ) + { + case Register::GENERIC_REGISTER: + m_type = Generic; + break; + case Register::FILE_REGISTER: + m_type = File; + break; + case Register::SFR_REGISTER: + m_type = SFR; + break; + case Register::BP_REGISTER: + m_type = Breakpoint; + break; + case Register::INVALID_REGISTER: + m_type = Invalid; + break; + } + + m_name = m_pRegister->baseName(); +} + + +unsigned RegisterInfo::value() const +{ + return m_pRegister->value.data; +} + + +void RegisterInfo::update() +{ + unsigned newValue = value(); + if ( newValue != m_prevEmitValue ) + { + m_prevEmitValue = newValue; + emit valueChanged(newValue); + } +} + + +QString RegisterInfo::toString( RegisterType type ) +{ + switch ( type ) + { + case Generic: + return i18n("Generic"); + + case File: + return i18n("File"); + + case SFR: + return i18n("SFR"); + + case Breakpoint: + return i18n("Breakpoint"); + + case Invalid: + return i18n("Invalid"); + } + + return i18n("Unknown"); +} +//END class RegisterInfo + + + +//BEGIN class DebugLine +DebugLine::DebugLine( const QString & fileName, int line ) + : SourceLine( fileName, line ) +{ + m_bIsBreakpoint = false; + m_bMarkedAsDeleted = false; +} + + +DebugLine::DebugLine() + : SourceLine() +{ + m_bIsBreakpoint = false; + m_bMarkedAsDeleted = false; +} +//END class DebugLine + + +#include "gpsimprocessor.moc" + +#endif diff --git a/src/electronics/gpsimprocessor.h b/src/electronics/gpsimprocessor.h new file mode 100644 index 0000000..0650fd9 --- /dev/null +++ b/src/electronics/gpsimprocessor.h @@ -0,0 +1,393 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "config.h" +#ifndef NO_GPSIM + +#ifndef GPSIMPROCESSOR_H +#define GPSIMPROCESSOR_H + +#include "sourceline.h" + +#include <qmap.h> +#include <qvaluevector.h> +#include <qobject.h> +#include <qvaluelist.h> + +class DebugLine; +class GpsimProcessor; +class MicroInfo; +class pic_processor; // from gpsim +class Register; +class RegisterMemoryAccess; + +typedef QMap<SourceLine, SourceLine> SourceLineMap; +typedef QMap<int, QString> QStringMap; +typedef QValueList<int> IntList; + + +class DebugLine : public SourceLine +{ + public: + DebugLine(); + DebugLine( const QString & fileName, int line ); + /** + * Whether or not to break when we reach this line. + */ + bool isBreakpoint() const { return m_bIsBreakpoint; } + /** + * Set whether or not to break when we reach this line. + */ + void setBreakpoint( bool breakpoint ) { m_bIsBreakpoint = breakpoint; } + /** + * Used for efficiency purposes by GpsimProcessor. Sets a flag. + */ + void markAsDeleted() { m_bMarkedAsDeleted = true; } + /** + * Used for efficiency purposes by GpsimProcessor. + */ + bool markedAsDeleted() const { return m_bMarkedAsDeleted; } + + protected: + bool m_bIsBreakpoint; + bool m_bMarkedAsDeleted; + + private: + DebugLine( const DebugLine & dl ); + DebugLine & operator = ( const DebugLine & dl ); +}; + + +/** +@short Stores info from gpsim register, used to hide gpsim interface +@author David Saxton +*/ +class RegisterInfo : public QObject +{ + Q_OBJECT + public: + RegisterInfo( Register * reg ); + + enum RegisterType + { + Invalid, + Generic, + File, + SFR, + Breakpoint + }; + + RegisterType type() const { return m_type; } + QString name() const { return m_name; } + unsigned value() const; + static QString toString( RegisterType type ); + + /** + * Checks to see if the value has changed; if so, emit new value. + */ + void update(); + + signals: + void valueChanged( unsigned newValue ); + + protected: + QString m_name; + RegisterType m_type; + Register * m_pRegister; + unsigned m_prevEmitValue; +}; + + +/** +@short Stores information about a set of registers, used to hide gpsim interface. +@author David Saxton +*/ +class RegisterSet +{ + public: + RegisterSet( pic_processor * picProcessor ); + ~RegisterSet(); + + /** + * Calls update for each RegisterInfo in this set. + */ + void update(); + /** + * Returns the number of registers. + */ + unsigned size() const { return m_registers.size(); } + + RegisterInfo * fromAddress( unsigned address ); + RegisterInfo * fromName( const QString & name ); + + protected: + typedef QMap< QString, RegisterInfo * > RegisterInfoMap; + RegisterInfoMap m_nameToRegisterMap; + QValueVector< RegisterInfo * > m_registers; +}; + + +/** +@author David Saxton +*/ +class GpsimDebugger : public QObject +{ + friend class GpsimProcessor; + Q_OBJECT + + public: + enum Type + { + AsmDebugger = 0, + HLLDebugger = 1 + }; + + GpsimDebugger( Type type, GpsimProcessor * gpsim ); + ~GpsimDebugger(); + + GpsimProcessor * gpsim() const { return m_pGpsim; } + + /** + * When an assembly file was generated by a high level language compiler + * like SDCC, it will insert markers like ";#CSRC" that show which line + * of source-code generated the given set of assembly instructions. This + * matches up the assembly file lines with the associated source file + * lines. + */ + void associateLine( const QString & sourceFile, int sourceLine, const QString & assemblyFile, int assemblyLine ); + /** + * Check to see if we've hit a breakpoint or similar; if so, this + * function will stop the execution of the PIC program. + */ + void checkForBreak(); + /** + * Sets the breakpoints used for the given file to exactly those that + * are contained in this list. Breakpoints for other files are not + * affected. + * @param path the location of the file (which gpsim must recognise). + */ + void setBreakpoints( const QString & path, const IntList & lines ); + /** + * Sets / removes the breakpoint at the given line + */ + void setBreakpoint( const QString & path, int line, bool isBreakpoint ); + /** + * Returns the current source line that gpsim is at. By default, this + * will be the corresponding assembly line. That can be overwritten + * using mapAddressBlockToLine. + */ + SourceLine currentLine(); + /** + * Returns a pointer to the debug info for the current line. + */ + DebugLine * currentDebugLine(); + /** + * @return the program address for the given line (or -1 if no such + * line). + */ + int programAddress( const QString & path, int line ); + /** + * Step into the next program line. + */ + void stepInto(); + /** + * Step over the next program instruction. If we are currently running, + * this function will do nothing. Otherwise, it will record the current + * stack level, step, and if the new stack level is <= the initial level + * then return - otherwise, this processor will set a breakpoint for + * stack levels <= initial, and go to running mode. + */ + void stepOver(); + /** + * Similar to stepOver, except we break when the stack level becomes < + * the initial stack level (instead of <= initial). + */ + void stepOut(); + + signals: + /** + * Emitted when a line is reached. By default, this is the line of the + * input assembly file; however, the line associated with an address in + * the PIC memory can be changed with mapAddressBlockToLine. + */ + void lineReached( const SourceLine & sourceLine ); + + protected slots: + void gpsimRunningStatusChanged( bool isRunning ); + + protected: + void initAddressToLineMap(); + void stackStep( int dl ); + void emitLineReached(); + + int m_stackLevelLowerBreak; // Set by step-over, for when the stack level decreases to the one given + SourceLine m_previousAtLineEmit; // Used for working out whether we should emit a new line reached signal + DebugLine ** m_addressToLineMap; + DebugLine * m_pBreakFromOldLine; + GpsimProcessor * m_pGpsim; + Type m_type; + unsigned m_addressSize; + SourceLineMap m_sourceLineMap; // assembly <--> High level language +}; + + +/** +@author David Saxton +*/ +class GpsimProcessor : public QObject +{ + friend class GpsimDebugger; + Q_OBJECT + + public: + /** + * Create a new gpsim processor. After calling this constructor, you + * should always call codLoadStatus() to ensure that the cod file was + * loaded successfully. + */ + GpsimProcessor( QString symbolFile, QObject *parent = 0 ); + ~GpsimProcessor(); + + void setDebugMode( GpsimDebugger::Type mode ) { m_debugMode = mode; } + GpsimDebugger * currentDebugger() const { return m_pDebugger[m_debugMode]; } + + enum CodLoadStatus + { + CodSuccess, + CodFileNotFound, + CodUnrecognizedProcessor, + CodFileNameTooLong, + CodLstNotFound, + CodBadFile, + CodFileUnreadable, + CodFailure, + CodUnknown // Should never be this, but just in case load_symbol_file returns something funny + }; + + enum InstructionType + { + LiteralOp, + BitOp, + RegisterOp, + UnknownOp, + }; + + /** + * @return status of opening the COD file + * @see displayCodLoadStatus + */ + CodLoadStatus codLoadStatus() const { return m_codLoadStatus; } + /** + * Popups a messagebox to the user according to the CodLoadStatus. Will + * only popup a messagebox if the CodLoadStatus wasn't CodSuccess. + */ + void displayCodLoadStatus(); + /** + * Returns a list of source files for the currently running program. + */ + QStringList sourceFileList(); + /** + * Set whether or not to run gpsim. (i.e. whether or not the step + * function should do anything when called with force=false). + */ + void setRunning( bool run ); + /** + * Returns true if running (currently simulating), else gpsim is paused. + */ + bool isRunning() const { return m_bIsRunning; } + /** + * Execute the next program instruction. If we are not in a running + * mode, then this function will do nothing. + */ + void executeNext(); + /** + * Reset all parts of the simulation. Gpsim will not run until + * setRunning(true) is called. Breakpoints are not affected. + */ + void reset(); + /** + * Returns the microinfo describing this processor. + */ + MicroInfo * microInfo() const; + + pic_processor * picProcessor() const { return m_pPicProcessor; } + unsigned programMemorySize() const; + RegisterSet * registerMemory() const { return m_pRegisterMemory; } + /** + * @return the instruction type at the given address. + */ + InstructionType instructionType( unsigned address ); + /** + * @return the address of the operand's register at address if the + * instruction at address is a register operation, and -1 otherwise. + */ + int operandRegister( unsigned address ); + /** + * @return the literal if the instruction at address is a literal + * operation, and -1 otherwise. + */ + int operandLiteral( unsigned address ); + + //BEGIN Convenience functions for PIC files + enum ProgramFileValidity { DoesntExist, IncorrectType, Valid }; + /** + * @return information on the validity of the given program file (either + * DoesntExist, IncorrectType, or Valid). + * @see static QString generateSymbolFile + */ + static ProgramFileValidity isValidProgramFile( const QString & programFile ); + /** + * Converts the file at programFile to a Symbol file for emulation, + * and returns that symbol file's path + * @param programFile The full url to the file + * @param assembled The slot to connect the assembled signal to + * @see static bool isValidProgramFile( const QString &programFile ) + */ + static QString generateSymbolFile( const QString &fileName, QObject *receiver, const char *successMember, const char * failMember = 0l ); + /** + *Compile microbe to output to the given filename + */ + static void compileMicrobe( const QString &filename, QObject *receiver, const char * successMember, const char * failMember = 0l ); + //END convenience functions for PIC files + + signals: + /** + * Emitted when the running status of gpsim changes. + */ + void runningStatusChanged( bool isRunning ); + + protected: + /** + * Calls emitLineReached for each debugger. + */ + void emitLineReached(); + + pic_processor * m_pPicProcessor; + CodLoadStatus m_codLoadStatus; + const QString m_symbolFile; + RegisterSet * m_pRegisterMemory; + GpsimDebugger::Type m_debugMode; + GpsimDebugger * m_pDebugger[2]; // Asm, HLL + + /** + * We are called effectively for each cycle of the cycle of the + * processor. This value is used as some instructions (e.g. goto) take + * two cycles to execute, and so we must ignore one cycle to ensure + * realtime simulation. + */ + bool m_bCanExecuteNextCycle; + + private: + bool m_bIsRunning; +}; + +#endif + +#endif // !NO_GPSIM diff --git a/src/electronics/pin.cpp b/src/electronics/pin.cpp new file mode 100644 index 0000000..56848fc --- /dev/null +++ b/src/electronics/pin.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "pin.h" + +#include <assert.h> +#include <kdebug.h> + +Pin::Pin( ECNode * parent ) +{ + assert(parent); + m_pECNode = parent; + m_voltage = 0.; + m_current = 0.; + m_eqId = -2; + m_bCurrentIsKnown = false; + m_groundType = Pin::gt_never; +} + + +Pin::~Pin() +{ + WireList::iterator end = m_inputWireList.end(); + for ( WireList::iterator it = m_inputWireList.begin(); it != end; ++it ) + delete *it; + + end = m_outputWireList.end(); + for ( WireList::iterator it = m_outputWireList.begin(); it != end; ++it ) + delete *it; +} + + +PinList Pin::localConnectedPins( ) const +{ +// kdDebug() << k_funcinfo << "Input wires: "<<m_inputWireList.size()<<" Output wires: " << m_outputWireList.size() << " Switch connected: " << m_switchConnectedPins.size() << endl; + + PinList pins; + + WireList::const_iterator end = m_inputWireList.end(); + for ( WireList::const_iterator it = m_inputWireList.begin(); it != end; ++it ) + { + if (*it) + pins << (*it)->startPin(); + } + + end = m_outputWireList.end(); + for ( WireList::const_iterator it = m_outputWireList.begin(); it != end; ++it ) + { + if (*it) + pins << (*it)->endPin(); + } + + pins += m_switchConnectedPins; + + return pins; +} + + +void Pin::setSwitchConnected( Pin * pin, bool isConnected ) +{ + if (!pin) + return; + + if (isConnected) + { + if ( !m_switchConnectedPins.contains(pin) ) + m_switchConnectedPins.append(pin); + } + else + m_switchConnectedPins.remove(pin); +} + + +void Pin::addCircuitDependentPin( Pin * pin ) +{ + if ( pin && !m_circuitDependentPins.contains(pin) ) + m_circuitDependentPins.append(pin); +} + + +void Pin::addGroundDependentPin( Pin * pin ) +{ + if ( pin && !m_groundDependentPins.contains(pin) ) + m_groundDependentPins.append(pin); +} + + +void Pin::removeDependentPins() +{ + m_circuitDependentPins.clear(); + m_groundDependentPins.clear(); +} + + +void Pin::addElement( Element * e ) +{ + if ( !e || m_elementList.contains(e) ) + return; + m_elementList.append(e); +} + + +void Pin::removeElement( Element * e ) +{ + m_elementList.remove(e); +} + + +void Pin::addSwitch( Switch * sw ) +{ + if ( !sw || m_switchList.contains( sw ) ) + return; + m_switchList << sw; +} + + +void Pin::removeSwitch( Switch * sw ) +{ + m_switchList.remove( sw ); +} + + +void Pin::addInputWire( Wire * wire ) +{ + if ( wire && !m_inputWireList.contains(wire) ) + m_inputWireList << wire; +} + + +void Pin::addOutputWire( Wire * wire ) +{ + if ( wire && !m_outputWireList.contains(wire) ) + m_outputWireList << wire; +} + + +bool Pin::calculateCurrentFromWires() +{ + m_inputWireList.remove( (Wire*)0l ); + m_outputWireList.remove( (Wire*)0l ); + + const WireList inputs = m_inputWireList; + const WireList outputs = m_outputWireList; + + m_current = 0.0; + + WireList::const_iterator end = inputs.end(); + for ( WireList::const_iterator it = inputs.begin(); it != end; ++it ) + { + if ( !(*it)->currentIsKnown() ) + return false; + + m_current -= (*it)->current(); + } + + end = outputs.end(); + for ( WireList::const_iterator it = outputs.begin(); it != end; ++it ) + { + if ( !(*it)->currentIsKnown() ) + return false; + + m_current += (*it)->current(); + } + + m_bCurrentIsKnown = true; + return true; +} + diff --git a/src/electronics/pin.h b/src/electronics/pin.h new file mode 100644 index 0000000..1e10663 --- /dev/null +++ b/src/electronics/pin.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PIN_H +#define PIN_H + +#include "wire.h" + +#include <qguardedptr.h> +#include <qobject.h> +#include <qvaluelist.h> + +class ECNode; +class Element; +class Pin; +class Switch; +class Wire; + +typedef QValueList<Element*> ElementList; +typedef QValueList<QGuardedPtr<Pin> > PinList; +typedef QValueList<Switch*> SwitchList; +typedef QValueList<QGuardedPtr<Wire> > WireList; + + +/** +@author David Saxton +*/ +class Pin : public QObject +{ + public: + /** + * Priorities for ground pin. gt_always will (as expected) always assign + * the given pin as ground, gt_never will never do. If no gt_always pins + * exist, then the pin with the highest priority will be set as ground - + * if there is at least one pin that is not of ground type gt_never. These + * are only predefined recommended values, so if you choose not to use one + * of these, please respect the priorities with respect to the examples, and + * always specify a priority between 0 and 20. + * @see groundLevel + */ + enum GroundType + { + gt_always = 0, // ground + gt_high = 5, // voltage points + gt_medium = 10, // voltage sources + gt_low = 15, // current sources + gt_never = 20 // everything else + }; + Pin( ECNode * parent ); + ~Pin(); + + ECNode * parentECNode() const { return m_pECNode; } + /** + * This function returns the pins that are directly connected to this pins: + * either at the ends of connected wires, or via switches. + */ + PinList localConnectedPins() const; + /** + * Adds/removes the given pin to the list of ones that this pin is/isn't + * connected to via a switch. + */ + void setSwitchConnected( Pin * pin, bool isConnected ); + /** + * After calculating the nodal voltages in the circuit, this function should + * be called to tell the pin what its voltage is. + */ + void setVoltage( double v ) { m_voltage = v; } + /** + * Returns the voltage as set by setVoltage. + */ + double voltage() const { return m_voltage; } + /** + * After calculating nodal voltages, each component will be called to tell + * its pins what the current flowing *into* the component is. This sets it + * to zero in preparation to merging the current. + */ + void resetCurrent() { m_current = 0.0; } + /** + * Adds the given current to that already flowing into the pin. + * @see setCurrent + */ + void mergeCurrent( double i ) { m_current += i; } + /** + * Returns the current as set by mergeCurrent. + */ + double current() const { return m_current; } + /** + * In many cases (such as if this pin is a ground pin), the current + * flowing into the pin has not been calculated, and so the value + * returned by current() cannot be trusted. + */ + void setCurrentKnown( bool isKnown ) { m_bCurrentIsKnown = isKnown; } + /** + * Tell thie Pin that none of the currents from the switches have yet + * been merged. + */ + void setSwitchCurrentsUnknown() { m_switchList.remove( 0l ); m_unknownSwitchCurrents = m_switchList; } + /** + * This returns the value given by setCurrentKnown AND'd with whether + * we know the current from each switch attached to this pin. + * @see setCurrentKnown + */ + bool currentIsKnown() const { return m_bCurrentIsKnown && m_unknownSwitchCurrents.isEmpty(); } + /** + * Tells the Pin that the current from the given switch has been merged. + */ + void setSwitchCurrentKnown( Switch * sw ) { m_unknownSwitchCurrents.remove( sw ); } + /** + * Tries to calculate the Pin current from the input / output wires. + * @return whether was successful. + */ + bool calculateCurrentFromWires(); + /** + * Sets the "ground type" - i.e. the priority that this pin has for being + * ground over other pins in the circuit. Lower gt = higher priority. It's + * recommended to use Pin::GroundType. + */ + void setGroundType( int gt ) { m_groundType = gt; } + /** + * Returns the priority for ground. + */ + int groundType() const { return m_groundType; } + /** + * Adds a dependent pin - one whose voltages will (or might) affect the + * voltage of this pin. This is set by Component. + */ + void addCircuitDependentPin( Pin * pin ); + /** + * Adds a dependent pin - one whose voltages will (or might) affect the + * voltage of this pin. This is set by Component. + */ + void addGroundDependentPin( Pin * pin ); + /** + * Removes all Circuit and Ground dependent pins. + */ + void removeDependentPins(); + /** + * Returns the ids of the pins whose voltages will affect this pin. + * @see void setDependentPins( QStringList ids ) + */ + PinList circuitDependentPins() const { return m_circuitDependentPins; } + /** + * Returns the ids of the pins whose voltages will affect this pin. + * @see void setDependentPins( QStringList ids ) + */ + PinList groundDependentPins() const { return m_groundDependentPins; } + /** + * Use this function to set the pin identifier for equations, + * which should be done every time new pins are registered. + */ + void setEqId( int id ) { m_eqId = id; } + /** + * The equation identifier. + * @see setEqId + */ + int eqId() const { return m_eqId; } + /** + * Returns a list of elements that will affect this pin (e.g. if this + * pin is part of a resistor, then that list will contain a pointer to a + * Resistance element) + */ + ElementList elements() const { return m_elementList; } + /** + * Adds an element to the list of those that will affect this pin. + */ + void addElement( Element *e ); + /** + * Removes an element from the list of those that will affect this pin. + */ + void removeElement( Element *e ); + /** + * Adds an switch to the list of those that will affect this pin. + */ + void addSwitch( Switch *e ); + /** + * Removes an switch from the list of those that will affect this pin. + */ + void removeSwitch( Switch *e ); + + void addInputWire( Wire * wire ); + void addOutputWire( Wire * wire ); + void removeWire( Wire * wire ); + WireList inputWireList() const { return m_inputWireList; } + WireList outputWireList() const { return m_outputWireList; } + int numWires() const { return m_inputWireList.size() + m_outputWireList.size(); } + + protected: + double m_voltage; + double m_current; + int m_eqId; + bool m_bCurrentIsKnown; + PinList m_circuitDependentPins; + PinList m_groundDependentPins; + ElementList m_elementList; + SwitchList m_switchList; + int m_groundType; + PinList m_switchConnectedPins; + WireList m_inputWireList; + WireList m_outputWireList; + ECNode * m_pECNode; + SwitchList m_unknownSwitchCurrents; +}; + +#endif diff --git a/src/electronics/port.cpp b/src/electronics/port.cpp new file mode 100644 index 0000000..541195b --- /dev/null +++ b/src/electronics/port.cpp @@ -0,0 +1,514 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "port.h" + +#include <kdebug.h> + +#include <errno.h> +#include <fcntl.h> +#include <linux/ppdev.h> +#include <sys/ioctl.h> +#include <unistd.h> + +//BEGIN class Port +Port::Port() +{ +} + + +Port::~Port() +{ +} + + +QStringList Port::ports( unsigned probeResult ) +{ + return SerialPort::ports(probeResult) + ParallelPort::ports(probeResult); +} +//END class Port + + + +//BEGIN class SerialPort +SerialPort::SerialPort() +{ + m_file = -1; +} + + +SerialPort::~SerialPort() +{ + closePort(); +} + + +void SerialPort::setPinState( Pin pin, bool state ) +{ + if ( m_file == -1 ) + return; + + int flags = -1; + + switch ( pin ) + { + case TD: + ioctl( m_file, state ? TIOCSBRK : TIOCCBRK, 0 ); + return; + + case DTR: + flags = TIOCM_DTR; + break; + + case DSR: + flags = TIOCM_DSR; + break; + + case RTS: + flags = TIOCM_RTS; + break; + + case CD: + case RD: + case GND: + case CTS: + case RI: + break; + }; + + if ( flags == -1 ) + { + kdError() << k_funcinfo << "Bad pin " << pin << endl; + return; + } + + if ( ioctl( m_file, state ? TIOCMBIS : TIOCMBIC, & flags ) == -1 ) + kdError() << k_funcinfo << "Could not set pin " << pin << " errno = " << errno << endl; +} + + +bool SerialPort::pinState( Pin pin ) +{ + if ( m_file == -1 ) + return false; + + int mask = 0; + + switch ( pin ) + { + case CD: + mask = TIOCM_CD; + break; + + case RD: + mask = TIOCM_SR; + break; + + case CTS: + mask = TIOCM_CTS; + break; + + case RI: + mask = TIOCM_RI; + break; + + case TD: + case DTR: + case GND: + case DSR: + case RTS: + break; + } + + if ( mask == 0 ) + { + kdError() << k_funcinfo << "Bad pin " << pin << endl; + return false; + } + + int bits = 0; + if ( ioctl( m_file, TIOCMGET, & bits ) == -1 ) + { + kdError() << k_funcinfo << "Could not read pin" << pin << " errno = " << errno << endl; + return false; + } + + return bits & mask; +} + + +Port::ProbeResult SerialPort::probe( const QString & port ) +{ + int file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDONLY ); + if ( file == -1 ) + return Port::DoesntExist; + + close(file); + + file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); + if ( file == -1 ) + return Port::ExistsButNotRW; + close(file); + + return Port::ExistsAndRW; +} + + +bool SerialPort::openPort( const QString & port, speed_t baudRate ) +{ + closePort(); + + m_file = open( port.ascii(), O_NOCTTY | O_NONBLOCK | O_RDWR ); + if ( m_file == -1 ) + { + kdError() << k_funcinfo << "Could not open port " << port << endl; + return false; + } + + termios state; + tcgetattr( m_file, & state ); + + // Save the previous state for restoration in close. + m_previousState = state; + + state.c_iflag = IGNBRK | IGNPAR; + state.c_oflag = 0; + state.c_cflag = baudRate | CS8 | CREAD | CLOCAL; + state.c_lflag = 0; + tcsetattr( m_file, TCSANOW, & state ); + + return true; +} + + +void SerialPort::closePort() +{ + if ( m_file == -1 ) + return; + + ioctl( m_file, TIOCCBRK, 0 ); + usleep(1); + tcsetattr( m_file, TCSANOW, & m_previousState ); + close( m_file ); + m_file = -1; +} + + +QStringList SerialPort::ports( unsigned probeResult ) +{ + QStringList list; + + for ( int i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/ttyS%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/tts/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/ttyUSB%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/usb/tts/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + return list; +} +//END class SerialPort + + + +//BEGIN class ParallelPort +const int IRQ_MODE_BIT = 1 << 20; // Controls if pin 10 (Ack) causes interrupts +const int INPUT_MODE_BIT = 1 << 21; // Controls if the data pins are input or output +const int ALWAYS_INPUT_PINS = ParallelPort::STATUS_PINS; + +const int IOCTL_REG_READ[3] = { + PPRDATA, + PPRSTATUS, + PPRCONTROL, +}; + +const int IOCTL_REG_WRITE[3] = { + PPWDATA, + 0, + PPWCONTROL, +}; + +const int INVERT_MASK[3] = { + 0x0, + 0x80, // 10000000 + 0x0b, // 00001011 +}; + +ParallelPort::ParallelPort() +{ + reset(); +} + + +ParallelPort::~ParallelPort() +{ +} + + +void ParallelPort::reset() +{ + m_file = -1; + m_reg[Data] = 0; + m_reg[Status] = 0; + m_reg[Control] = 0; + m_outputPins = INPUT_MODE_BIT | IRQ_MODE_BIT; + m_inputPins = ALWAYS_INPUT_PINS | INPUT_MODE_BIT | IRQ_MODE_BIT; +} + + +//BEGIN Pin-oriented operations +void ParallelPort::setPinState( int pins, bool state ) +{ + // only allow writing to output pins + pins &= m_outputPins; + + if ( pins & DATA_PINS ) + setDataState( (pins & DATA_PINS) >> 0, state ); + + if ( pins & CONTROL_PINS ) + setControlState( (pins & CONTROL_PINS) >> 16, state ); +} + + +int ParallelPort::pinState( int pins ) +{ + int value = 0; + + // only allow reading from input pins + pins &= m_inputPins; + + if ( pins & DATA_PINS ) + value |= ((readFromRegister( Data ) & ((pins & DATA_PINS) >> 0)) << 0); + + if ( pins & STATUS_PINS ) + value |= ((readFromRegister( Status ) & ((pins & STATUS_PINS) >> 8)) << 8); + + if ( pins & CONTROL_PINS ) + value |= ((readFromRegister( Control ) & ((pins & CONTROL_PINS) >> 16)) << 16); + + return value; +} + + +void ParallelPort::setDataState( uchar pins, bool state ) +{ + uchar value = readFromRegister( Data ); + + if ( state ) + value |= pins; + else + value &= ~pins; + + writeToData( value ); +} + + +void ParallelPort::setControlState( uchar pins, bool state ) +{ + uchar value = readFromRegister( Control ); + + if ( state ) + value |= pins; + else + value &= ~pins; + + writeToControl( value ); +} +//END Pin-oriented operations + + + +//BEGIN Register-oriented operations +uchar ParallelPort::readFromRegister( Register reg ) +{ + if ( m_file == -1 ) + return 0; + +// uchar value = inb( m_lpBase + reg ) ^ INVERT_MASK[reg]; + uchar value = 0; + if ( ioctl( m_file, IOCTL_REG_READ[reg], &value ) ) + kdError() << k_funcinfo << "errno=" << errno << endl; + else + m_reg[reg] = value; + return value; +} + + +void ParallelPort::writeToRegister( Register reg, uchar value ) +{ + if ( m_file == -1 ) + return; + +// outb( value ^ INVERT_MASK[reg], m_lpBase + reg ); + if ( ioctl( m_file, IOCTL_REG_WRITE[reg], & value ) ) + kdError() << k_funcinfo << "errno=" << errno << endl; + else + m_reg[reg] = value; +} + + +void ParallelPort::writeToData( uchar value ) +{ + writeToRegister( Data, value ); +} + + +void ParallelPort::writeToControl( uchar value ) +{ + // Set all inputs to ones + value |= ((m_inputPins & CONTROL_PINS) >> 16); + + writeToRegister( Control, value ); +} +//END Register-oriented operations + + +//BEGIN Changing pin directions +void ParallelPort::setDataDirection( Direction dir ) +{ + if ( dir == Input ) + { + m_inputPins |= DATA_PINS; + m_outputPins &= ~DATA_PINS; + } + else + { + m_inputPins &= DATA_PINS; + m_outputPins |= ~DATA_PINS; + } + + setPinState( INPUT_MODE_BIT, dir == Input ); +} + + +void ParallelPort::setControlDirection( int pins, Direction dir ) +{ + pins &= CONTROL_PINS; + + if ( dir == Input ) + { + m_inputPins |= pins; + m_outputPins &= ~pins; + } + else + { + m_inputPins &= pins; + m_outputPins |= ~pins; + } + + setControlState( 0, true ); +} +//END Changing pin directions + + +Port::ProbeResult ParallelPort::probe( const QString & port ) +{ + int file = open( port.ascii(), O_RDWR ); + if ( file == -1 ) + return Port::DoesntExist; + + if ( ioctl( file, PPCLAIM ) != 0 ) + { + close(file); + return Port::ExistsButNotRW; + } + + ioctl( file, PPRELEASE ); + close(file); + return Port::ExistsAndRW; +} + + +QStringList ParallelPort::ports( unsigned probeResult ) +{ + QStringList list; + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/parport%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + for ( unsigned i = 0; i < 8; ++i ) + { + QString dev = QString("/dev/parports/%1").arg(i); + if ( probe(dev) & probeResult ) + list << dev; + } + + return list; +} + + +bool ParallelPort::openPort( const QString & port ) +{ + if ( m_file != -1 ) + { + kdWarning() << k_funcinfo << "Port already open" << endl; + return false; + } + + m_file = open( port.ascii(), O_RDWR ); + + if ( m_file == -1 ) + { + kdError() << k_funcinfo << "Could not open port \"" << port << "\": errno="<<errno<<endl; + return false; + } + + if ( ioctl( m_file, PPCLAIM ) ) + { + kdError() << k_funcinfo << "Port " << port << " must be RW" << endl; + close( m_file ); + m_file = -1; + return false; + } + + return true; +} + + +void ParallelPort::closePort() +{ + if ( m_file == -1 ) + return; + + int res = ioctl( m_file, PPRELEASE ); + close( m_file ); + + if ( res ) + kdError() << k_funcinfo << "res="<<res<<endl; + + m_file = -1; +} +//END class ParallelPort + diff --git a/src/electronics/port.h b/src/electronics/port.h new file mode 100644 index 0000000..53182bc --- /dev/null +++ b/src/electronics/port.h @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef PORT_H +#define PORT_H + +#include <qstringlist.h> + +#include <termios.h> + +/** +@author David Saxton + */ +class Port +{ + public: + enum ProbeResult + { + ExistsAndRW = 1 << 0, + ExistsButNotRW = 1 << 1, + DoesntExist = 1 << 2 + }; + + Port(); + virtual ~Port(); + + /** + * Returns a list of ports, whose probe status AND's with probeResult. + * This function just returns the combination of the lists for + * SerialPort::ports and ParallelPort::ports. + */ + static QStringList ports( unsigned probeResult ); +}; + + +/** +Abstraction for a serial port, allowing control over individual pins. + +@author David Saxton + */ +class SerialPort : public Port +{ + public: + enum Pin + { + CD = 1, // Carrier detect + RD = 2, // Recieved data + TD = 3, // Transmitted data + DTR = 4, // Data terminal ready + GND = 5, // Signal ground + DSR = 6, // Data set ready + RTS = 7, // Request to send + CTS = 8, // Clear to send + RI = 9, // Ring indicator + }; + + SerialPort(); + ~SerialPort(); + + /** + * Writes state (high or low) to the given pin. + */ + void setPinState( Pin pin, bool state ); + bool pinState( Pin pin ); + + static ProbeResult probe( const QString & port ); + /** + * @see Port::ports + */ + static QStringList ports( unsigned probeResult ); + /** + * Opens the given port. + * @return if the port could be opened. + * @param baudRate The baud rate as defined in bits/termios.h + */ + bool openPort( const QString & port, speed_t baudRate ); + /** + * Closes any currently open port. + */ + void closePort(); + + protected: + /// Read in on port open; restored on port close + termios m_previousState; + + /// File descriptor for the port. + int m_file; +}; + + +/** +Abstraction for a parallel port, allowing control over individual pins. +Based loosely on code in the parapin project; see http://parapin.sf.net + +@author David Saxton +*/ +class ParallelPort : public Port +{ + public: + enum Pin + { + // Data Register + // Offset: Base + 0 + // Readable / writable + PIN02 = 1 << 0, // Data 0 + PIN03 = 1 << 1, // Data 1 + PIN04 = 1 << 2, // Data 2 + PIN05 = 1 << 3, // Data 3 + PIN06 = 1 << 4, // Data 4 + PIN07 = 1 << 5, // Data 5 + PIN08 = 1 << 6, // Data 6 + PIN09 = 1 << 7, // Data 7 + DATA_PINS = PIN02 | PIN03 | PIN04 | PIN05 | PIN06 + | PIN07 | PIN08 | PIN09, + + // Status Register + // Offset: Base + 1 + // Read only + PIN15 = 1 << 11, // Error + PIN13 = 1 << 12, // Online + PIN12 = 1 << 13, // Paper End + PIN10 = 1 << 14, // Ack + PIN11 = 1 << 15, // Busy + STATUS_PINS = PIN15 | PIN13 | PIN12 | PIN10 | PIN11, + + // Control Register + // Offset: Base + 2 + // Readable / writable + PIN01 = 1 << 16, // Strobe + PIN14 = 1 << 17, // Auto Feed + PIN16 = 1 << 18, // Init + PIN17 = 1 << 19, // Select + CONTROL_PINS = PIN01 | PIN14 | PIN16 | PIN17, + + + // Pins 18 to 25 are ground + }; + + enum Register + { + Data = 0, + Status = 1, + Control = 2, + }; + + /** + * For setting the direction of the Data register or the Control pins. + */ + enum Direction + { + Input = 0, + Output = 1, + }; + + ParallelPort(); + ~ParallelPort(); + + /** + * Opens the given port. + * @return if the port could be opened. + */ + bool openPort( const QString & port ); + /** + * Closes any currently open port. + */ + void closePort(); + + //BEGIN Pin-oriented operations + /** + * @param pins A list of ParallelPort::Pin OR'd together. + */ + void setPinState( int pins, bool state ); + /** + * @return the pin states for the given list of pins. + */ + int pinState( int pins ); + /** + * Sets the given pins to the given state in the Data register. + */ + void setDataState( uchar pins, bool state ); + /** + * Sets the given pins to the given state in the Control register. + */ + void setControlState( uchar pins, bool state ); + //END Pin-oriented operations + + + //BEGIN Register-oriented operations + /** + * Reads and stores the value in the given reigsters, and returns it. + */ + uchar readFromRegister( Register reg ); + /** + * Write the given value to the Data register. + */ + void writeToData( uchar value ); + /** + * Write the given value to the Control register (any input pins will be + * set to one). + */ + void writeToControl( uchar value ); + //END Register-oriented operations + + + //BEGIN Changing pin directions + /** + * Sets the (input / ouput) direction of the data pins. + */ + void setDataDirection( Direction dir ); + /** + * Sets the given pins to the given direction. + */ + void setControlDirection( int pins, Direction dir ); + //END Changing pin directions + + static ProbeResult probe( const QString & port ); + /** + * @see Port::ports + */ + static QStringList ports( unsigned probeResult ); + + protected: + /** + * Writes the value to the given register. + */ + void writeToRegister( Register reg, uchar value ); + void reset(); + + uchar m_reg[3]; + + /// Mask of the pins that are currently set as input + int m_inputPins; + + /// Mask of the pins that are currently set as output + int m_outputPins; + + /// File descriptor for the port. + int m_file; +}; + +#endif diff --git a/src/electronics/simulation/Makefile.am b/src/electronics/simulation/Makefile.am new file mode 100644 index 0000000..c45c6a0 --- /dev/null +++ b/src/electronics/simulation/Makefile.am @@ -0,0 +1,11 @@ +INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/electronics $(all_includes) +METASOURCES = AUTO +noinst_LTLIBRARIES = libelements.la +libelements_la_SOURCES = cccs.cpp ccvs.cpp circuit.cpp currentsource.cpp \ + diode.cpp element.cpp elementset.cpp logic.cpp matrix.cpp vccs.cpp vcvs.cpp \ + voltagesource.cpp capacitance.cpp resistance.cpp currentsignal.cpp voltagepoint.cpp \ + voltagesignal.cpp elementsignal.cpp nonlinear.cpp reactive.cpp vec.cpp bjt.cpp opamp.cpp \ + inductance.cpp +noinst_HEADERS = cccs.h ccvs.h circuit.h currentsource.h diode.h element.h \ + elementset.h logic.h matrix.h vccs.h vcvs.h voltagesource.h capacitance.h \ + resistance.h elementsignal.h nonlinear.h reactive.h vec.h bjt.h opamp.h inductance.h diff --git a/src/electronics/simulation/bjt.cpp b/src/electronics/simulation/bjt.cpp new file mode 100644 index 0000000..8daab40 --- /dev/null +++ b/src/electronics/simulation/bjt.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "bjt.h" +#include "diode.h" +#include "elementset.h" +#include "matrix.h" + +#include <cmath> +using namespace std; + + +//BEGIN class BJTSettings +BJTSettings::BJTSettings() +{ + I_S = 1e-16; + N_F = 1.0; + N_R = 1.0; + B_F = 100.0; + B_R = 1.0; +} +//END class BJTSettings + + + +//BEGIN class BJTState +BJTState::BJTState() +{ + reset(); +} + + +void BJTState::reset() +{ + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + A[i][j] = 0.0; + + I[i] = 0.0; + } +} + + +BJTState BJTState::operator-( const BJTState & s ) const +{ + BJTState newState( *this ); + + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0; j < 3; ++j ) + newState.A[i][j] -= s.A[i][j]; + + newState.I[i] -= s.I[i]; + } + + return newState; +} +//END class BJTState + + + +//BEGIN class BJT +BJT::BJT( const bool isNPN ) + : NonLinear() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_pol = isNPN ? 1 : -1; + m_numCNodes = 3; +} + + +BJT::~BJT() +{ +} + + +void BJT::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[2]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround && !p_cnode[2]->isGround ) + { + p_A->setUse( p_cnode[2]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[2]->n(), Map::et_unstable, false ); + } +} + + +void BJT::add_initial_dc() +{ + V_BE_prev = 0.0; + V_BC_prev = 0.0; + m_os.reset(); + update_dc(); +} + + +void BJT::updateCurrents() +{ + if (!b_status) + return; + + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + m_cnodeI[1] = I_BC - I_T; + m_cnodeI[2] = I_BE + I_T; + m_cnodeI[0] = -(m_cnodeI[1] + m_cnodeI[2]); +} + + +void BJT::update_dc() +{ + if (!b_status) + return; + + calc_eq(); + + BJTState diff = m_ns - m_os; + for ( unsigned i = 0; i < 3; ++i ) + { + for ( unsigned j = 0 ; j < 3; ++j ) + A_g( i, j ) += diff.A[i][j]; + + b_i( i ) += diff.I[i]; + } + + m_os = m_ns; +} + + +void BJT::calc_eq() +{ + double V_B = p_cnode[0]->v; + double V_C = p_cnode[1]->v; + double V_E = p_cnode[2]->v; + + double V_BE = (V_B - V_E) * m_pol; + double V_BC = (V_B - V_C) * m_pol; + + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + + // adjust voltage to help convergence + double V_BEcrit = diodeCriticalVoltage( I_S, N_F * V_T ); + double V_BCcrit = diodeCriticalVoltage( I_S, N_R * V_T ); + V_BE_prev = V_BE = diodeVoltage( V_BE, V_BE_prev, V_T * N_F, V_BEcrit ); + V_BC_prev = V_BC = diodeVoltage( V_BC, V_BC_prev, V_T * N_R, V_BCcrit ); + + double I_BE, I_BC, I_T, g_BE, g_BC, g_IF, g_IR; + calcIg( V_BE, V_BC, & I_BE, & I_BC, & I_T, & g_BE, & g_BC, & g_IF, & g_IR ); + + double I_eq_B = I_BE - V_BE * g_BE; + double I_eq_C = I_BC - V_BC * g_BC; + double I_eq_E = I_T - V_BE * g_IF + V_BC * g_IR; + + m_ns.A[0][0] = g_BC + g_BE; + m_ns.A[0][1] = -g_BC; + m_ns.A[0][2] = -g_BE; + + m_ns.A[1][0] = -g_BC + (g_IF - g_IR); + m_ns.A[1][1] = g_IR + g_BC; + m_ns.A[1][2] = -g_IF; + + m_ns.A[2][0] = -g_BE - (g_IF - g_IR); + m_ns.A[2][1] = -g_IR; + m_ns.A[2][2] = g_BE + g_IF; + + m_ns.I[0] = (-I_eq_B - I_eq_C) * m_pol; + m_ns.I[1] = (+I_eq_C - I_eq_E) * m_pol; + m_ns.I[2] = (+I_eq_B + I_eq_E) * m_pol; +} + + +void BJT::calcIg( double V_BE, double V_BC, double * I_BE, double * I_BC, double * I_T, double * g_BE, double * g_BC, double * g_IF, double * g_IR ) +{ + double I_S = m_bjtSettings.I_S; + double N_F = m_bjtSettings.N_F; + double N_R = m_bjtSettings.N_R; + double B_F = m_bjtSettings.B_F; + double B_R = m_bjtSettings.B_R; + + // base-emitter diodes + double g_tiny = (V_BE < (-10 * V_T * N_F)) ? I_S : 0; + + double I_F; + diodeJunction( V_BE, I_S, V_T * N_F, & I_F, g_IF ); + + double I_BEI = I_F / B_F; + double g_BEI = *g_IF / B_F; + double I_BEN = g_tiny * V_BE; + double g_BEN = g_tiny; + *I_BE = I_BEI + I_BEN; + *g_BE = g_BEI + g_BEN; + + // base-collector diodes + g_tiny = (V_BC < (-10 * V_T * N_R)) ? I_S : 0; + + double I_R; + diodeJunction( V_BC, I_S, V_T * N_R, & I_R, g_IR ); + + double I_BCI = I_R / B_R; + double g_BCI = *g_IR / B_R; + double I_BCN = g_tiny * V_BC; + double g_BCN = g_tiny; + *I_BC = I_BCI + I_BCN; + *g_BC = g_BCI + g_BCN; + + *I_T = I_F - I_R; +} + + +void BJT::setBJTSettings( const BJTSettings & settings ) +{ + m_bjtSettings = settings; + + if (p_eSet) + p_eSet->setCacheInvalidated(); +} +//END class BJT + + diff --git a/src/electronics/simulation/bjt.h b/src/electronics/simulation/bjt.h new file mode 100644 index 0000000..52022aa --- /dev/null +++ b/src/electronics/simulation/bjt.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef BJT_H +#define BJT_H + +#include "matrix.h" +#include "nonlinear.h" + +class BJTState +{ + public: + BJTState(); + void reset(); + + BJTState operator-( const BJTState & s ) const; + + double A[3][3]; + double I[3]; +}; + + +class BJTSettings +{ + public: + BJTSettings(); + + double I_S; ///< saturation current + double N_F; ///< forward emission coefficient + double N_R; ///< reverse emission coefficient + double B_F; ///< forward beta + double B_R; ///< reverse beta +}; + + +/** +@author David Saxton +*/ +class BJT : public NonLinear +{ + public: + BJT( bool isNPN ); + virtual ~BJT(); + + virtual Type type() const { return Element_BJT; } + virtual void update_dc(); + virtual void add_initial_dc(); + virtual void add_map(); + BJTSettings settings() const { return m_bjtSettings; } + void setBJTSettings( const BJTSettings & settings ); + + protected: + virtual void updateCurrents(); + /** + * Calculates the new BJTState from the voltages on the nodes. + */ + void calc_eq(); + + void calcIg( double V_BE, double V_BC, double * I_BE, double * I_BC, double * I_T, double * g_BE, double * g_BC, double * g_IF, double * g_IR ); + + BJTState m_os; + BJTState m_ns; + int m_pol; + double V_BE_prev, V_BC_prev; + BJTSettings m_bjtSettings; +}; + +#endif diff --git a/src/electronics/simulation/capacitance.cpp b/src/electronics/simulation/capacitance.cpp new file mode 100644 index 0000000..9087c7f --- /dev/null +++ b/src/electronics/simulation/capacitance.cpp @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "capacitance.h" +#include "matrix.h" + +Capacitance::Capacitance( const double capacitance, const double delta ) + : Reactive(delta) +{ + m_cap = capacitance; + g_eq_old = i_eq_old = 0.; + m_numCNodes = 2; + setMethod( Capacitance::m_euler ); +} + +Capacitance::~Capacitance() +{ +} + +void Capacitance::setCapacitance( const double c ) +{ + m_cap = c; +} + +void Capacitance::add_initial_dc() +{ + // We don't need to do anything here, as time_step() will do that for us, + // apart from to make sure our old values are 0 + g_eq_old = i_eq_old = 0.; +} + +void Capacitance::updateCurrents() +{ + if (!b_status) return; + const double r_i = (p_cnode[0]->v-p_cnode[1]->v)*g_eq_old; + m_cnodeI[0] = -i_eq_old-r_i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + +void Capacitance::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } +} + + +void Capacitance::time_step() +{ + if (!b_status) return; + + double v = p_cnode[0]->v-p_cnode[1]->v; + double i_eq_new = 0.0, g_eq_new = 0.0; + + if ( m_method == Capacitance::m_euler ) + { + g_eq_new = m_cap/m_delta; + i_eq_new = -v*g_eq_new; + } + else if ( m_method == Capacitance::m_trap ) { + // TODO Implement + test trapezoidal method + g_eq_new = 2.*m_cap/m_delta; + } + + if ( g_eq_old != g_eq_new ) + { + A_g( 0, 0 ) += g_eq_new-g_eq_old; + A_g( 1, 1 ) += g_eq_new-g_eq_old; + A_g( 0, 1 ) -= g_eq_new-g_eq_old; + A_g( 1, 0 ) -= g_eq_new-g_eq_old; + } + + if ( i_eq_new != i_eq_old ) + { + b_i( 0 ) -= i_eq_new-i_eq_old; + b_i( 1 ) += i_eq_new-i_eq_old; + } + + g_eq_old = g_eq_new; + i_eq_old = i_eq_new; +} + +bool Capacitance::updateStatus() +{ + b_status = Reactive::updateStatus(); + if ( m_method == Capacitance::m_none ) b_status = false; + return b_status; +} + +void Capacitance::setMethod( Method m ) +{ + m_method = m; + updateStatus(); +} + + diff --git a/src/electronics/simulation/capacitance.h b/src/electronics/simulation/capacitance.h new file mode 100644 index 0000000..ccc083d --- /dev/null +++ b/src/electronics/simulation/capacitance.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CAPACITANCE_H +#define CAPACITANCE_H + +#include "reactive.h" + +/** +@author David Saxton +@short Capacitance +*/ +class Capacitance : public Reactive +{ +public: + enum Method + { + m_none, // None + m_euler, // Backward Euler + m_trap // Trapezoidal (currently unimplemented) + }; + Capacitance( const double capacitance, const double delta ); + virtual ~Capacitance(); + + virtual Type type() const { return Element_Capacitance; } + /** + * Set the stepping use for numerical integration of capacitance, + * and the interval between successive updates + */ + void setMethod( Method m ); + virtual void time_step(); + virtual void add_initial_dc(); + void setCapacitance( const double c ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual bool updateStatus(); + +private: + double m_cap; // Capacitance + Method m_method; // Method of integration + + double g_eq_old; + double i_eq_old; +}; + +#endif diff --git a/src/electronics/simulation/cccs.cpp b/src/electronics/simulation/cccs.cpp new file mode 100644 index 0000000..9725735 --- /dev/null +++ b/src/electronics/simulation/cccs.cpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "cccs.h" + +CCCS::CCCS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 2; + m_numCNodes = 4; +} + +CCCS::~CCCS() +{ +} + +void CCCS::setGain( const double g ) +{ + if ( m_g == g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = g; + add_initial_dc(); +} + + +void CCCS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + } + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[0]->n(), Map::et_stable, true ); + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[1]->n(), Map::et_constant, true ); +} + +void CCCS::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = 1; + A_c( 0, 0 ) = 1; + A_b( 1, 0 ) = -1; + A_c( 0, 1 ) = -1; + A_b( 2, 1 ) = 1; + A_b( 3, 1 ) = -1; + A_d( 1, 0 ) = -m_g; + A_d( 1, 1 ) = 1; +} + +void CCCS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; + m_cnodeI[3] = p_cbranch[1]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + diff --git a/src/electronics/simulation/cccs.h b/src/electronics/simulation/cccs.h new file mode 100644 index 0000000..ba86e9e --- /dev/null +++ b/src/electronics/simulation/cccs.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CCCS_H +#define CCCS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the current control. +CNodes n2 and n3 are used for the current output. +Branches b0 and b1 are for control and output +@short Current Controlled Current Source +@author David Saxton +*/ +class CCCS : public Element +{ +public: + CCCS( const double gain ); + virtual ~CCCS(); + + virtual Type type() const { return Element_CCCS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/ccvs.cpp b/src/electronics/simulation/ccvs.cpp new file mode 100644 index 0000000..fc12bf5 --- /dev/null +++ b/src/electronics/simulation/ccvs.cpp @@ -0,0 +1,91 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "ccvs.h" + +CCVS::CCVS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 2; + m_numCNodes = 4; +} + +CCVS::~CCVS() +{ +} + +void CCVS::setGain( const double g ) +{ + if ( m_g == g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = g; + add_initial_dc(); +} + + +void CCVS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[1]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[1]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[1]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } + p_A->setUse_d( p_cbranch[1]->n(), p_cbranch[0]->n(), Map::et_stable, true ); +} + + +void CCVS::add_initial_dc() +{ + if (!b_status) return; + + A_b( 0, 0 ) = 1; + A_c( 0, 0 ) = 1; + A_b( 1, 0 ) = -1; + A_c( 0, 1 ) = -1; + A_b( 2, 1 ) = 1; + A_c( 1, 2 ) = 1; + A_b( 3, 1 ) = -1; + A_c( 1, 3 ) = -1; + A_d( 1, 0 ) = -m_g; +} + +void CCVS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; + m_cnodeI[3] = p_cbranch[0]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + diff --git a/src/electronics/simulation/ccvs.h b/src/electronics/simulation/ccvs.h new file mode 100644 index 0000000..bcb1ac0 --- /dev/null +++ b/src/electronics/simulation/ccvs.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CCVS_H +#define CCVS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the current control. +CNodes n2 and n3 are used for the voltage output. +Branches b0 and b1 are for control and output +@short Current Controlled Voltage Source +@author David Saxton +*/ +class CCVS : public Element +{ +public: + CCVS( const double gain ); + virtual ~CCVS(); + + virtual Type type() const { return Element_CCVS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/circuit.cpp b/src/electronics/simulation/circuit.cpp new file mode 100644 index 0000000..c152756 --- /dev/null +++ b/src/electronics/simulation/circuit.cpp @@ -0,0 +1,550 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 <vector> +#include "circuit.h" +#include "circuitdocument.h" +#include "element.h" +#include "elementset.h" +#include "logic.h" +#include "matrix.h" +#include "nonlinear.h" +#include "pin.h" +#include "reactive.h" +#include "wire.h" + + +#include <cmath> +#include <map> + +typedef std::multimap<int, PinList> PinListMap; + +//BEGIN class Circuit +Circuit::Circuit() +{ + m_bCanAddChanged = true; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + m_logicOutCount = 0; + m_bCanCache = false; + m_pLogicOut = 0l; + m_elementSet = new ElementSet( this, 0, 0 ); + m_cnodeCount = m_branchCount = -1; + m_prepNLCount = 0; + m_pLogicCacheBase = new LogicCacheNode; +} + +Circuit::~Circuit() +{ + delete m_elementSet; + delete m_pLogicCacheBase; + delete[] m_pLogicOut; +} + + +void Circuit::addPin( Pin *node ) +{ + if ( m_pinList.contains(node) ) return; + m_pinList.append(node); +} + +void Circuit::addElement( Element *element ) +{ + if ( m_elementList.contains(element) ) return; + m_elementList.append(element); +} + +bool Circuit::contains( Pin *node ) +{ + return m_pinList.contains(node); +} + + +// static function +int Circuit::identifyGround( PinList nodeList, int *highest ) +{ + // What this function does: + // We are given a list of pins. First, we divide them into groups of pins + // that are directly connected to each other (e.g. through wires or + // switches). Then, each group of connected pins is looked at to find the + // pin with the highest "ground priority", and this is taken to be + // the priority of the group. The highest ground priority from all the + // groups is recorded. If the highest ground priority found is the maximum, + // then all the pins in groups with this priority are marked as ground + // (their eq-id is set to -1). Otherwise, the first group of pins with the + // highest ground priority found is marked as ground, and all others are + // marked as non ground (their eq-id is set to 0). + + int temp_highest; + if (!highest) + highest = &temp_highest; + + // Now to give all the Pins ids + PinListMap eqs; + while ( !nodeList.isEmpty() ) + { + PinList associated; + PinList nodes; + Pin *node = *nodeList.begin(); + recursivePinAdd( node, &nodeList, &associated, &nodes ); + if ( nodes.size() > 0 ) + { + eqs.insert( std::make_pair( associated.size(), nodes ) ); + } + } + + + // Now, we want to look through the associated Pins, + // to find the ones with the highest "Ground Priority". Anything with a lower + // priority than Pin::gt_never will not be considered + *highest = Pin::gt_never; // The highest priority found so far + int numGround = 0; // The number of node groups found with that priority + const PinListMap::iterator eqsEnd = eqs.end(); + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + int highPri = Pin::gt_never; // The highest priority found in these group of nodes + const PinList::iterator send = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + if ( (*sit)->groundType() < highPri ) + highPri = (*sit)->groundType(); + } + + if ( highPri == *highest ) + numGround++; + + else if ( highPri < *highest ) + { + numGround = 1; + *highest = highPri; + } + } + + if ( *highest == Pin::gt_never ) + { + (*highest)--; + numGround=0; + } + // If there are no Always Ground nodes, then we only want to set one of the nodes as ground + else if ( *highest > Pin::gt_always ) + numGround = 1; + + + // Now, we can give the nodes their cnode ids, or tell them they are ground + bool foundGround = false; // This is only used when we don't have a Always ground node + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + bool ground = false; + const PinList::iterator send = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + ground |= (*sit)->groundType() <= (*highest); + } + if ( ground && (!foundGround || *highest == Pin::gt_always ) ) + { + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + (*sit)->setEqId(-1); + } + foundGround = true; + } + else + { + for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) + { + (*sit)->setEqId(0); + } + } + } + + return numGround; +} + + +void Circuit::init() +{ + m_branchCount = 0; + + const ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + m_branchCount += (*it)->numCBranches(); + } + + // Now to give all the Pins ids + int groundCount = 0; + PinListMap eqs; + PinList unassignedNodes = m_pinList; + while ( !unassignedNodes.isEmpty() ) + { + PinList associated; + PinList nodes; + Pin *node = *unassignedNodes.begin(); + if ( recursivePinAdd( node, &unassignedNodes, &associated, &nodes ) ) { + groundCount++; + } + if ( nodes.size() > 0 ) { + eqs.insert( std::make_pair( associated.size(), nodes ) ); + } + } + + m_cnodeCount = eqs.size() - groundCount; + + delete m_pLogicCacheBase; + m_pLogicCacheBase = 0l; + + delete m_elementSet; + m_elementSet = new ElementSet( this, m_cnodeCount, m_branchCount ); + + // Now, we can give the nodes their cnode ids, or tell them they are ground + Vector *x = m_elementSet->x(); + int i=0; + const PinListMap::iterator eqsEnd = eqs.end(); + for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) + { + bool foundGround = false; + + const PinList::iterator sEnd = it->second.end(); + for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) + foundGround |= (*sit)->eqId() == -1; + + if ( foundGround ) + continue; + + bool foundEnergyStoragePin = false; + + for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) + { + (*sit)->setEqId(i); + + bool energyStorage = false; + const ElementList elements = (*sit)->elements(); + ElementList::const_iterator elementsEnd = elements.end(); + for ( ElementList::const_iterator it = elements.begin(); it != elementsEnd; ++it ) + { + if ( !*it ) + continue; + + if ( ((*it)->type() == Element::Element_Capacitance) + || ((*it)->type() == Element::Element_Inductance) ) + { + energyStorage = true; + break; + } + } + + // A pin attached to an energy storage pin overrides one that doesn't. + // If the two pins have equal status with in this regard, we pick the + // one with the highest absolute voltage on it. + + if ( foundEnergyStoragePin && !energyStorage ) + continue; + + double v = (*sit)->voltage(); + + if ( energyStorage && !foundEnergyStoragePin ) + { + foundEnergyStoragePin = true; + (*x)[i] = v; + continue; + } + + if ( std::abs(v) > std::abs( (*x)[i] ) ) + (*x)[i] = v; + } + i++; + } + + + // And add the elements to the elementSet + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + // We don't want the element to prematurely try to do anything, + // as it doesn't know its actual cnode ids yet + (*it)->setCNodes(); + (*it)->setCBranches(); + m_elementSet->addElement(*it); + } + // And give the branch ids to the elements + i=0; + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + switch ( (*it)->numCBranches() ) + { + case 0: + break; + case 1: + (*it)->setCBranches( i ); + i += 1; + break; + case 2: + (*it)->setCBranches( i, i+1 ); + i += 2; + break; + case 3: + (*it)->setCBranches( i, i+1, i+2 ); + i += 3; + break; + default: + // What the?! + break; + } + } +} + + +void Circuit::initCache() +{ + m_elementSet->updateInfo(); + + m_bCanCache = true; + m_logicOutCount = 0; + + delete[] m_pLogicOut; + m_pLogicOut = 0l; + + delete m_pLogicCacheBase; + m_pLogicCacheBase = 0l; + + const ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) + { + switch ( (*it)->type() ) + { + case Element::Element_BJT: + case Element::Element_CCCS: + case Element::Element_CCVS: + case Element::Element_CurrentSource: + case Element::Element_Diode: + case Element::Element_LogicIn: + case Element::Element_OpAmp: + case Element::Element_Resistance: + case Element::Element_VCCS: + case Element::Element_VCVS: + case Element::Element_VoltagePoint: + case Element::Element_VoltageSource: + { + break; + } + + case Element::Element_LogicOut: + { + m_logicOutCount++; + break; + } + + case Element::Element_CurrentSignal: + case Element::Element_VoltageSignal: + case Element::Element_Capacitance: + case Element::Element_Inductance: + { + m_bCanCache = false; + break; + } + } + } + + if ( !m_bCanCache ) + return; + + m_pLogicOut = new LogicOut*[m_logicOutCount]; + unsigned i = 0; + for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) + { + if ( (*it)->type() == Element::Element_LogicOut ) + m_pLogicOut[i++] = static_cast<LogicOut*>(*it); + } + + m_pLogicCacheBase = new LogicCacheNode; +} + + +void Circuit::setCacheInvalidated() +{ + if (m_pLogicCacheBase) + { + delete m_pLogicCacheBase->high; + m_pLogicCacheBase->high = 0l; + + delete m_pLogicCacheBase->low; + m_pLogicCacheBase->low = 0l; + + delete m_pLogicCacheBase->data; + m_pLogicCacheBase->data = 0l; + } +} + + +void Circuit::cacheAndUpdate() +{ + LogicCacheNode * node = m_pLogicCacheBase; + for ( unsigned i = 0; i < m_logicOutCount; i++ ) + { + if ( m_pLogicOut[i]->outputState() ) + { + if (!node->high) + node->high = new LogicCacheNode; + + node = node->high; + } + else + { + if (!node->low) + node->low = new LogicCacheNode; + + node = node->low; + } + } + + if ( node->data ) + { + (*m_elementSet->x()) = *node->data; + m_elementSet->updateInfo(); + return; + } + + if ( m_elementSet->containsNonLinear() ) + m_elementSet->doNonLinear( 150, 1e-10, 1e-13 ); + else + m_elementSet->doLinear(true); + + node->data = new Vector( m_elementSet->x()->size() ); + *node->data = *m_elementSet->x(); +} + + +void Circuit::createMatrixMap() +{ + m_elementSet->createMatrixMap(); +} + + +bool Circuit::recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ) +{ + if ( !unassignedNodes->contains(node) ) + return false; + unassignedNodes->remove(node); + + bool foundGround = node->eqId() == -1; + + const PinList circuitDependentPins = node->circuitDependentPins(); + const PinList::const_iterator dEnd = circuitDependentPins.end(); + for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) + { + if ( !associated->contains(*it) ) + associated->append(*it); + } + + nodes->append(node); + + const PinList localConnectedPins = node->localConnectedPins(); + const PinList::const_iterator end = localConnectedPins.end(); + for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) + foundGround |= recursivePinAdd( *it, unassignedNodes, associated, nodes ); + + return foundGround; +} + + +void Circuit::doNonLogic() +{ + if ( !m_elementSet || m_cnodeCount+m_branchCount <= 0 ) + return; + + if (m_bCanCache) + { + if ( !m_elementSet->b()->isChanged() && !m_elementSet->matrix()->isChanged() ) + return; + cacheAndUpdate(); + updateNodalVoltages(); + m_elementSet->b()->setUnchanged(); + return; + } + + stepReactive(); + if ( m_elementSet->containsNonLinear() ) + { + m_elementSet->doNonLinear( 10, 1e-9, 1e-12 ); + updateNodalVoltages(); + } + else + { + if ( m_elementSet->doLinear(true) ) + updateNodalVoltages(); + } +} + + +void Circuit::stepReactive() +{ + ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + Element * const e = *it; + if ( e && e->isReactive() ) + (static_cast<Reactive*>(e))->time_step(); + } +} + + +void Circuit::updateNodalVoltages() +{ + CNode **_cnodes = m_elementSet->cnodes(); + + const PinList::iterator endIt = m_pinList.end(); + for ( PinList::iterator it = m_pinList.begin(); it != endIt; ++it ) + { + Pin * const node = *it; + int i = node->eqId(); + if ( i == -1 ) + node->setVoltage(0.); + else + { + const double v = _cnodes[i]->v; + node->setVoltage( std::isfinite(v)?v:0. ); + } + } +} + + +void Circuit::updateCurrents() +{ + ElementList::iterator listEnd = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) + { + (*it)->updateCurrents(); + } +} + +void Circuit::displayEquations() +{ + m_elementSet->displayEquations(); +} +//END class Circuit + + + +//BEGIN class LogicCacheNode +LogicCacheNode::LogicCacheNode() +{ + low = 0l; + high = 0l; + data = 0l; +} + + +LogicCacheNode::~LogicCacheNode() +{ + delete low; + delete high; + delete data; +} +//END class LogicCacheNode + + diff --git a/src/electronics/simulation/circuit.h b/src/electronics/simulation/circuit.h new file mode 100644 index 0000000..2455edc --- /dev/null +++ b/src/electronics/simulation/circuit.h @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CIRCUIT_H +#define CIRCUIT_H + +#include <qguardedptr.h> +#include "qstringlist.h" +#include "qvaluelist.h" + +#include "elementset.h" + +class CircuitDocument; +class Wire; +class Pin; +class Element; +class LogicOut; + +typedef QValueList<QGuardedPtr<Pin> > PinList; +typedef QValueList<Element*> ElementList; + + +class LogicCacheNode +{ + public: + LogicCacheNode(); + ~LogicCacheNode(); + + LogicCacheNode * high; + LogicCacheNode * low; + Vector * data; +}; + + +/** +Usage of this class (usually invoked from CircuitDocument): +(1) Add Wires, Pins and Elements to the class as appropriate +(2) Call init to initialize the simulation +(3) Control the simulation with step() + +This class can be considered a bridge between the gui-tainted CircuitDocument - specific +to this implementation, and the pure untainted ElementSet. Please keep it that way. + +@short Simulates a collection of components +@author David Saxton +*/ +class Circuit +{ + public: + Circuit(); + ~Circuit(); + + void addPin( Pin *node ); + void addElement( Element *element ); + + bool contains( Pin *node ); + bool containsNonLinear() const { return m_elementSet->containsNonLinear(); } + + void init(); + /** + * Called after everything else has been setup - before doNonLogic or + * doLogic are called for the first time. Preps the circuit. + */ + void initCache(); + /** + * Marks all cached results as invalidated and removes them. + */ + void setCacheInvalidated(); + /** + * Solves for non-logic elements + */ + void doNonLogic(); + /** + * Solves for logic elements (i.e just does fbSub) + */ + void doLogic() { m_elementSet->doLinear(false); } + + void displayEquations(); + void updateCurrents(); + + void createMatrixMap(); + /** + * This will identify the ground node and non-ground nodes in the given set. + * Ground will be given the eqId -1, non-ground of 0. + * @param highest The highest ground type of the groundnodes found. If no + ground nodes were found, this will be (gt_never-1). + * @returns the number of ground nodes. If all nodes are at or below the + * gt_never threshold, then this will be zero. + */ + static int identifyGround( PinList nodeList, int *highest = 0l ); + + void setNextChanged( Circuit * circuit, unsigned char chain ) { m_pNextChanged[chain] = circuit; } + Circuit * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } + void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } + bool canAddChanged() const { return m_bCanAddChanged; } + + protected: + void cacheAndUpdate(); + /** + * Update the nodal voltages from those calculated in ElementSet + */ + void updateNodalVoltages(); + /** + * Step the reactive elements. + */ + void stepReactive(); + /** + * Returns true if any of the nodes are ground + */ + static bool recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ); + + int m_cnodeCount; + int m_branchCount; + int m_prepNLCount; // Count until next m_elementSet->prepareNonLinear() is called + + PinList m_pinList; + ElementList m_elementList; + ElementSet *m_elementSet; + + //Stuff for caching + bool m_bCanCache; + LogicCacheNode * m_pLogicCacheBase; + unsigned m_logicOutCount; + LogicOut ** m_pLogicOut; + + bool m_bCanAddChanged; + Circuit * m_pNextChanged[2]; +}; + +#endif diff --git a/src/electronics/simulation/currentsignal.cpp b/src/electronics/simulation/currentsignal.cpp new file mode 100644 index 0000000..5e5388d --- /dev/null +++ b/src/electronics/simulation/currentsignal.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "currentsignal.h" +#include "element.h" +#include "matrix.h" + +CurrentSignal::CurrentSignal( const double delta, const double current ) + : Reactive::Reactive(delta) +{ + m_current = current; + m_oldCurrent = m_newCurrent = 0.; + m_numCNodes = 2; +} + +CurrentSignal::~CurrentSignal() +{ +} + +void CurrentSignal::setCurrent( const double i ) +{ + if ( m_oldCurrent != m_newCurrent ) add_initial_dc(); + m_newCurrent *= i/m_current; // Instead of calling step again, we can just "adjust" what the current should be + m_current = i; + add_initial_dc(); +} + + +void CurrentSignal::add_map() +{ + // We don't need a map for current signal :-) +} + + +void CurrentSignal::add_initial_dc() +{ + if ( !b_status ) + return; + + if ( m_newCurrent == m_oldCurrent ) + return; + + b_i( 0 ) -= m_newCurrent-m_oldCurrent; + b_i( 1 ) += m_newCurrent-m_oldCurrent; + + m_oldCurrent = m_newCurrent; +} + +void CurrentSignal::updateCurrents() +{ + m_cnodeI[1] = m_newCurrent; + m_cnodeI[0] = -m_newCurrent; +} + +void CurrentSignal::time_step() +{ + add_initial_dc(); // Make sure our old and new are synced + m_newCurrent = m_current*advance(); + add_initial_dc(); +} diff --git a/src/electronics/simulation/currentsignal.h b/src/electronics/simulation/currentsignal.h new file mode 100644 index 0000000..b217b79 --- /dev/null +++ b/src/electronics/simulation/currentsignal.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CURRENTSIGNAL_H +#define CURRENTSIGNAL_H + +#include "reactive.h" +#include "elementsignal.h" + +/** +@short CurrentSignal +@author David saxton +*/ +class CurrentSignal : public Reactive, public ElementSignal +{ +public: + CurrentSignal( const double delta, const double current ); + virtual ~CurrentSignal(); + + virtual Element::Type type() const { return Element_CurrentSignal; } + void setCurrent( const double current ); + double current() { return m_current; } + virtual void time_step(); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_current; // Current + double m_oldCurrent; // Old calculated current + double m_newCurrent; // New calculated current +}; + +#endif diff --git a/src/electronics/simulation/currentsource.cpp b/src/electronics/simulation/currentsource.cpp new file mode 100644 index 0000000..675b0b7 --- /dev/null +++ b/src/electronics/simulation/currentsource.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "currentsource.h" +#include "elementset.h" +#include "matrix.h" + +CurrentSource::CurrentSource( const double current ) + : Element::Element() +{ + m_i = current; + m_numCNodes = 2; +} + + +CurrentSource::~CurrentSource() +{ +} + + +void CurrentSource::setCurrent( const double i ) +{ + if ( i == m_i ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove the old current + m_i = -m_i; + add_initial_dc(); + + m_i = i; + add_initial_dc(); +} + + +void CurrentSource::add_map() +{ + // We don't need a map for current source :-) +} + + +void CurrentSource::add_initial_dc() +{ + if (!b_status) + return; + + b_i( 0 ) -= m_i; + b_i( 1 ) += m_i; +} + + +void CurrentSource::updateCurrents() +{ + m_cnodeI[0] = -m_i; + m_cnodeI[1] = m_i; +} diff --git a/src/electronics/simulation/currentsource.h b/src/electronics/simulation/currentsource.h new file mode 100644 index 0000000..9b72e0d --- /dev/null +++ b/src/electronics/simulation/currentsource.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef CURRENTSOURCE_H +#define CURRENTSOURCE_H + +#include "element.h" + +/** +cnode n0 has current flowing otu of it, cnode n1 has current flowing into it +@author David Saxton +@short Current Source +*/ +class CurrentSource : public Element +{ +public: + CurrentSource( const double current ); + virtual ~CurrentSource(); + + virtual Type type() const { return Element_CurrentSource; } + void setCurrent( const double i ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_i; // Current +}; + +#endif diff --git a/src/electronics/simulation/diode.cpp b/src/electronics/simulation/diode.cpp new file mode 100644 index 0000000..e13d478 --- /dev/null +++ b/src/electronics/simulation/diode.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 <vector> +#include "diode.h" +#include "elementset.h" +#include "matrix.h" + +#include <cmath> + + +//BEGIN class Diode Settings +DiodeSettings::DiodeSettings() +{ + reset(); +} + + +void DiodeSettings::reset() +{ + I_S = 1e-15; + N = 1.0; + V_B = 4.7; +// R = 0.001; +} +//END class Diode Settings + + + +//BEGIN class Diode +Diode::Diode() + : NonLinear() +{ + m_numCNodes = 2; + g_new = g_old = I_new = I_old = V_prev = 0.0; +} + + +Diode::~Diode() +{ +} + + +void Diode::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_unstable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_unstable, false ); + } +} + + +void Diode::add_initial_dc() +{ + g_new = g_old = I_new = I_old = V_prev = 0.0; + update_dc(); +} + + +double Diode::current() const +{ + if (!b_status) + return 0.0; + + double I; + calcIg( p_cnode[0]->v - p_cnode[1]->v, & I, 0 ); + + return I; +} + + +void Diode::updateCurrents() +{ + if (!b_status) + return; + + m_cnodeI[1] = current(); + m_cnodeI[0] = -m_cnodeI[1]; +} + + +void Diode::update_dc() +{ + if (!b_status) + return; + + calc_eq(); + + A_g( 0, 0 ) += g_new - g_old; + A_g( 1, 1 ) += g_new - g_old; + A_g( 0, 1 ) -= g_new - g_old; + A_g( 1, 0 ) -= g_new - g_old; + + b_i( 0 ) -= I_new - I_old; + b_i( 1 ) += I_new - I_old; + + g_old = g_new; + I_old = I_new; +} + + + +#ifndef MIN +# define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif + + + +void Diode::calc_eq() +{ + double I_S = m_diodeSettings.I_S; + double N = m_diodeSettings.N; + double V_B = m_diodeSettings.V_B; +// double R = m_diodeSettings.R; + + double v = p_cnode[0]->v - p_cnode[1]->v; + + // adjust voltage to help convergence + double V_crit = diodeCriticalVoltage( I_S, N * V_T ); + if (V_B != 0 && v < MIN (0, -V_B + 10 * N * V_T)) + { + double V = -(v + V_B); + V = diodeVoltage( V, -(V_prev + V_B), V_T * N, V_crit ); + v = -(V + V_B); + } + else + v = diodeVoltage( v, V_prev, V_T * N, V_crit ); + + V_prev = v; + + double I_D; + calcIg( v, & I_D, & g_new ); + + I_new = I_D - (v * g_new); +} + + +void Diode::calcIg( double V, double * I_D, double * g ) const +{ + double I_S = m_diodeSettings.I_S; + double N = m_diodeSettings.N; + double V_B = m_diodeSettings.V_B; +// double R = m_diodeSettings.R; + + double gtiny = (V < - 10 * V_T * N && V_B != 0) ? I_S : 0; + + if ( V >= (-3 * N * V_T) ) + { + if ( g ) + *g = diodeConductance( V, I_S, V_T * N ) + gtiny; + *I_D = diodeCurrent( V, I_S, V_T * N ) + (gtiny * V); + } + else if ( V_B == 0 || V >= -V_B ) + { + double a = (3 * N * V_T) / (V * M_E); + a = a * a * a; + *I_D = (-I_S * (1 + a)) + (gtiny * V); + if ( g ) + *g = ((I_S * 3 * a) / V) + gtiny; + } + else + { + double a = exp( -(V_B + V) / N / V_T ); + *I_D = (-I_S * a) + (gtiny * V); + if ( g ) + *g = I_S * a / V_T / N + gtiny; + } +} + + +void Diode::setDiodeSettings( const DiodeSettings & settings ) +{ + m_diodeSettings = settings; + if (p_eSet) + p_eSet->setCacheInvalidated(); +} +//END class Diode + diff --git a/src/electronics/simulation/diode.h b/src/electronics/simulation/diode.h new file mode 100644 index 0000000..0b13946 --- /dev/null +++ b/src/electronics/simulation/diode.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef DIODE_H +#define DIODE_H + +#include "nonlinear.h" + +class DiodeSettings +{ + public: + DiodeSettings(); + void reset(); + + double I_S; ///< Diode saturation current + double N; ///< Emission coefficient + double V_B; ///< Reverse breakdown +// double R; ///< Series resistance +}; + + +/** +This simulates a diode. The simulated diode characteristics are: + +@li I_s: Diode saturation current +@li V_T: Thermal voltage = kT/4 = 25mV at 20 C +@li n: Emission coefficient, typically between 1 and 2 +@li V_RB: Reverse breakdown (large negative voltage) +@li G_RB: Reverse breakdown conductance +@li R_D: Base resistance of diode + +@short Simulates the electrical property of diode-ness +@author David Saxton +*/ +class Diode : public NonLinear +{ + public: + Diode(); + virtual ~Diode(); + + virtual void update_dc(); + virtual void add_initial_dc(); + virtual void add_map(); + virtual Element::Type type() const { return Element_Diode; } + DiodeSettings settings() const { return m_diodeSettings; } + void setDiodeSettings( const DiodeSettings & settings ); + /** + * Returns the current flowing through the diode + */ + double current() const; + + protected: + virtual void updateCurrents(); + void calc_eq(); + + void calcIg( double V, double * I, double * g ) const; + + double g_new, g_old; + double I_new, I_old; + double V_prev; + + DiodeSettings m_diodeSettings; +}; + +#endif + diff --git a/src/electronics/simulation/element.cpp b/src/electronics/simulation/element.cpp new file mode 100644 index 0000000..2411897 --- /dev/null +++ b/src/electronics/simulation/element.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "element.h" +#include "elementset.h" + +#include <assert.h> + +#include <kdebug.h> + +Element::Element() +{ + b_status = false; + p_A = 0l; + p_eSet = 0l; + p_b = 0l; + b_componentDeleted = false; + b_eSetDeleted = true; + + for ( int i=0; i<8; i++ ) + p_cnode[i] = 0l; + + resetCurrents(); + + for ( int i=0; i<4; i++ ) + p_cbranch[i] = 0l; + + m_numCBranches = 0; + m_numCNodes = 0; +} + +Element::~ Element() +{ +} + +void Element::resetCurrents() +{ + for ( int i=0; i<8; i++ ) + m_cnodeI[i] = 0.0; +} + +void Element::setElementSet( ElementSet *c ) +{ + assert(!b_componentDeleted); + assert(!p_eSet); + if (!c) return elementSetDeleted(); + b_eSetDeleted = false; + p_eSet = c; + p_A = p_eSet->matrix(); + p_b = p_eSet->b(); + updateStatus(); +} + +void Element::componentDeleted() +{ +// assert(!b_componentDeleted); + if (b_componentDeleted) + { + // Something strange happened here.... + } + if (b_eSetDeleted) return delete this; + b_componentDeleted = true; + b_status = false; +// kdDebug() << "Element::componentDeleted(): Setting b_status to false, this="<<this<<endl; + + p_eSet = 0l; + p_A = 0l; + p_b = 0l; + setCNodes(); + setCBranches(); +} + +void Element::elementSetDeleted() +{ +// assert(!b_eSetDeleted); + if (b_eSetDeleted) + { + // Something strange happened here.... + } + if (b_componentDeleted) return delete this; + b_eSetDeleted = true; + b_status = false; +// kdDebug() << "Element::elementSetDeleted(): Setting b_status to false, this="<<this<<endl; + + p_eSet = 0l; + p_A = 0l; + p_b = 0l; + setCNodes(); + setCBranches(); +} + + +void Element::setCNodes( const int n0, const int n1, const int n2, const int n3 ) +{ + if ( !p_eSet ) + { +// cerr << "Element::setCNodes: can't set nodes without circuit!"<<endl; + for ( int i=0; i<8; i++ ) + p_cnode[i] = 0l; + return; + } + + // MAX_CNODES-1 should match the last array index below. + assert( MAX_CNODES == 4 ); + p_cnode[0] = (n0>-1)?p_eSet->cnodes()[n0]:(n0==-1?p_eSet->ground():0l); + p_cnode[1] = (n1>-1)?p_eSet->cnodes()[n1]:(n1==-1?p_eSet->ground():0l); + p_cnode[2] = (n2>-1)?p_eSet->cnodes()[n2]:(n2==-1?p_eSet->ground():0l); + p_cnode[3] = (n3>-1)?p_eSet->cnodes()[n3]:(n3==-1?p_eSet->ground():0l); + updateStatus(); +} + +void Element::setCBranches( const int b0, const int b1, const int b2, const int b3 ) +{ + if ( !p_eSet ) + { +// cerr << "Element::setCBranches: can't set branches without circuit!"<<endl; + for ( int i=0; i<4; i++ ) p_cbranch[i] = 0l; + return; + } + p_cbranch[0] = (b0>-1)?p_eSet->cbranches()[b0]:0l; + p_cbranch[1] = (b1>-1)?p_eSet->cbranches()[b1]:0l; + p_cbranch[2] = (b2>-1)?p_eSet->cbranches()[b2]:0l; + p_cbranch[3] = (b3>-1)?p_eSet->cbranches()[b3]:0l; + updateStatus(); +} + +bool Element::updateStatus() +{ + // First, set status to false if all nodes in use are ground + b_status = false; + for ( int i=0; i<m_numCNodes; i++ ) + { + b_status |= p_cnode[i]?!p_cnode[i]->isGround:false; + } + + // Set status to false if any of the nodes are not set + for ( int i=0; i<m_numCNodes; i++ ) + { + if (!p_cnode[i]) b_status = false; + } + + // Finally, set status to false if not all the required branches are set + for ( int i=0; i<m_numCBranches; i++ ) + { + if (!p_cbranch[i]) b_status = false; + } + + // Finally, check for various pointers + if ( !p_eSet || !p_A || !p_b ) b_status = false; + + if (!b_status) + { + resetCurrents(); + } + // And return the status :-) +// kdDebug() << "Element::updateStatus(): Setting b_status to "<<(b_status?"true":"false")<<" this="<<this<<endl; + return b_status; +} + +double Element::cbranchCurrent( const int branch ) +{ + if ( !b_status || branch<0 || branch>=m_numCBranches ) return 0.; + return (*p_cbranch)[branch].i; +} + +double Element::cnodeVoltage( const int node ) +{ + if ( !b_status || node<0 || node>=m_numCNodes ) return 0.; + return (*p_cnode)[node].v; +} + + +CNode::CNode() +{ + m_n = 0; + v = 0.0; + isGround = false; +} + +CBranch::CBranch() +{ + m_n = 0; + i = 0.0; +} + + diff --git a/src/electronics/simulation/element.h b/src/electronics/simulation/element.h new file mode 100644 index 0000000..e05de46 --- /dev/null +++ b/src/electronics/simulation/element.h @@ -0,0 +1,255 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ELEMENT_H +#define ELEMENT_H + +#include "elementset.h" +#include "matrix.h" + +class ElementSet; +class Vector; +typedef unsigned int uint; + +const double T = 300.; // Temperature in Kelvin +const double K = 1.38e-23; // Boltzmann's constant +const double q = 1.602e-19; // Charge on an electron +const double V_T = K*T/q; // Thermal voltage +const double gmin = 1e-12; // Minimum parallel conductance used in dc domain + +class CNode +{ +public: + CNode(); + void set_n( const uint n ) { m_n=n; } + uint n() { return m_n; } + double v; // Voltage on node. This is set from the last calculated voltage. + bool isGround; // True for ground nodes. Obviously, you should ignore n and v if this is true +private: + uint m_n; // CNode number +}; + +class CBranch +{ +public: + CBranch(); + void set_n( const uint n ) { m_n=n; } + uint n() { return m_n; } + double i; // Current flowing through branch. This is set from the last calculated current. +private: + uint m_n; // CBranch number +}; + +const int MAX_CNODES = 4; + +// Default node number that represents no node (remember that +// Ground node is -1, and the rest are numbered from 0 to n-1 +const int noCNode = -2; +// Likewise for branch (although there is no "ground" branch; +// it is merely -2 for likeness with noCNode) +const int noBranch = -2; + +/** +@short Represents a circuit element (such as resistance) +@author David Saxton +*/ +class Element +{ +public: + enum Type + { + Element_BJT, + Element_Capacitance, + Element_CCCS, + Element_CCVS, + Element_CurrentSignal, + Element_CurrentSource, + Element_Diode, + Element_Inductance, + Element_LogicIn, + Element_LogicOut, + Element_OpAmp, + Element_Resistance, + Element_VCCS, + Element_VCVS, + Element_VoltagePoint, + Element_VoltageSignal, + Element_VoltageSource + }; + + Element(); + virtual ~Element(); + /** + * This must be called when the circuit is changed. The function will get + * all the required pointers from ElementSet + */ + virtual void setElementSet( ElementSet *c ); + /** + * Returns a pointer to the current element set + */ + ElementSet *elementSet() { return p_eSet; } + /** + * Tells the element which nodes to use. Remember that -1 is ground. You + * should refer to the individual elements for which nodes are used for what. + */ + void setCNodes( const int n0 = noCNode, const int n1 = noCNode, const int n2 = noCNode, const int n3 = noCNode ); + /** + * Tells the element it's branch numbers (if it should have one). Not + * all elements use this. + */ + void setCBranches( const int b0 = noBranch, const int b1 = noBranch, const int b2 = noBranch, const int b3 = noBranch ); + /** + * Returns a pointer to the given CNode + */ + CNode *cnode( const uint num ) { return p_cnode[num]; } + /** + * Returns a pointer to the given CNode + */ + CBranch *cbranch( const uint num ) { return p_cbranch[num]; } + /** + * Returns the number of branches used by the element + */ + int numCBranches() { return m_numCBranches; } + /** + * Returns the number of circuit nodes used by the element + */ + int numCNodes() { return m_numCNodes; } + /** + * Call this function to tell the element to calculate the + * current flowing *into* it's cnodes *from* the element. You + * can get the currents with m_cnodeI. Child class must implement this function. + */ + virtual void updateCurrents() = 0; + /** + * Returns true for reactive elements that need stepping for numerical-integration + * (such as capacitors) + */ + virtual bool isReactive() { return false; } + /** + * Returns true for NonLinear elements that need iteration to converge to a solution + * as the matrix A is a function of x. + */ + virtual bool isNonLinear() { return false; } + /** + * Returns the type of element + */ + virtual Type type() const = 0; + /** + * Call this function to tell the element to add its map to the matrix in use + */ + virtual void add_map() {}; + /** + * Does the required MNA stuff. This should be called from ElementSet when necessary. + */ + virtual void add_initial_dc() = 0; + /** + * This is called from the Component destructor. When elementSetDeleted has + * also been called, this class will delete itself. + */ + void componentDeleted(); + void elementSetDeleted(); + + double m_cnodeI[8]; ///< Current flowing into the cnodes from the element + double cbranchCurrent( const int branch ); + double cnodeVoltage( const int node ); + +protected: + /** + * Resets all calculated currents in the nodes to 0 + */ + void resetCurrents(); + + inline double & A_g( uint i, uint j ); + inline double & A_b( uint i, uint j ); + inline double & A_c( uint i, uint j ); + inline double & A_d( uint i, uint j ); + + inline double & b_i( uint i ); + inline double & b_v( uint i ); + + ElementSet *p_eSet; + Matrix *p_A; + Vector *p_b; + CNode *p_cnode[MAX_CNODES]; + CBranch *p_cbranch[4]; + + /** + * True when the element can do add_initial_dc(), i.e. when it has + * pointers to the circuit, and at least one of its nodes is not ground. + */ + bool b_status; + /** + * Update the status, returning b_status + */ + virtual bool updateStatus(); + /** + * Set by child class - the number of branches that the element uses + * Typically, this is 0, but could be 1 (e.g. independent voltage source) + * or 2 (e.g. cccs) + */ + int m_numCBranches; + /** + * Set by child class - the number of circuit nodes that the element uses + */ + int m_numCNodes; + +private: + bool b_componentDeleted; + bool b_eSetDeleted; + double m_temp; +}; + + +double & Element::A_g( uint i, uint j ) +{ + if ( p_cnode[i]->isGround || p_cnode[j]->isGround ) + return m_temp; + return p_A->g( p_cnode[i]->n(), p_cnode[j]->n() ); +} + + +double & Element::A_b( uint i, uint j ) +{ + if ( p_cnode[i]->isGround ) + return m_temp; + return p_A->b( p_cnode[i]->n(), p_cbranch[j]->n() ); +} + + +double & Element::A_c( uint i, uint j ) +{ + if ( p_cnode[j]->isGround ) + return m_temp; + return p_A->c( p_cbranch[i]->n(), p_cnode[j]->n() ); +} + + +double & Element::A_d( uint i, uint j ) +{ + return p_A->d( p_cbranch[i]->n(), p_cbranch[j]->n() ); +} + + + +double & Element::b_i( uint i ) +{ + if ( p_cnode[i]->isGround ) + return m_temp; + + return (*p_b)[ p_cnode[i]->n() ]; +} + + +double & Element::b_v( uint i ) +{ + return (*p_b)[ p_eSet->cnodeCount() + p_cbranch[i]->n() ]; +} + +#endif diff --git a/src/electronics/simulation/elementset.cpp b/src/electronics/simulation/elementset.cpp new file mode 100644 index 0000000..25057c2 --- /dev/null +++ b/src/electronics/simulation/elementset.cpp @@ -0,0 +1,253 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "bjt.h" +#include "circuit.h" +#include "elementset.h" +#include "element.h" +#include "logic.h" +#include "matrix.h" +#include "nonlinear.h" +#include "vec.h" + +#include <kdebug.h> + +#include <cmath> +#include <iostream> +#include <assert.h> + +ElementSet::ElementSet( Circuit * circuit, const int n, const int m ) +{ + m_pCircuit = circuit; + m_cn = n; + m_cb = m; + p_logicIn = 0l; + p_A = new Matrix( m_cn, m_cb ); + p_b = new Vector(m_cn+m_cb); + p_x = new Vector(m_cn+m_cb); + p_x_prev = new Vector(m_cn+m_cb); + m_cbranches = new CBranch*[m_cb]; + m_cnodes = new CNode*[m_cn]; + for ( uint i=0; i<m_cn; i++ ) + { + m_cnodes[i] = new CNode(); + m_cnodes[i]->set_n(i); + } + for ( uint i=0; i<m_cb; i++ ) + { + m_cbranches[i] = new CBranch(); + m_cbranches[i]->set_n(i); + } + m_ground = new CNode(); + m_ground->isGround = true; + b_containsNonLinear = false; +} + +ElementSet::~ElementSet() +{ + const ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + // Note: By calling setElementSet(0l), we might have deleted it (the Element will commit + // suicide when both the the ElementSet and Component to which it belongs have deleted + // themselves). So be very careful it you plan to do anything with the (*it) pointer + if (*it) (*it)->elementSetDeleted(); + } + for ( uint i=0; i<m_cn; i++ ) + { + delete m_cnodes[i]; + } + for ( uint i=0; i<m_cb; i++ ) + { + delete m_cbranches[i]; + } + delete[] m_cbranches; + delete[] m_cnodes; + delete[] p_logicIn; + delete m_ground; + delete p_A; + delete p_b; + delete p_x; + delete p_x_prev; +} + + +void ElementSet::setCacheInvalidated() +{ + m_pCircuit->setCacheInvalidated(); +} + + +void ElementSet::addElement( Element *e ) +{ + if ( !e || m_elementList.contains(e) ) return; + e->setElementSet(this); + m_elementList.append(e); + if ( e->isNonLinear() ) + { + b_containsNonLinear = true; + m_cnonLinearList.append( static_cast<NonLinear*>(e) ); + } +} + + +void ElementSet::createMatrixMap() +{ + p_A->createMap(); + + + // And do our logic as well... + + m_clogic = 0; + ElementList::iterator end = m_elementList.end(); + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + if ( dynamic_cast<LogicIn*>(*it) ) + m_clogic++; + } + + p_logicIn = new LogicIn*[m_clogic]; + int i=0; + for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it ) + { + if ( LogicIn * in = dynamic_cast<LogicIn*>(*it) ) + p_logicIn[i++] = in; + } +} + + +void ElementSet::doNonLinear( int maxIterations, double maxErrorV, double maxErrorI ) +{ +// p_x_prev->reset(); + + // And now tell the cnodes and cbranches about their new voltages & currents + updateInfo(); + + const NonLinearList::iterator end = m_cnonLinearList.end(); + + int k = 0; + do + { + // Tell the nonlinear elements to update its J, A and b from the newly calculated x + for ( NonLinearList::iterator it = m_cnonLinearList.begin(); it != end; ++it ) + (*it)->update_dc(); + + *p_x = *p_b; + p_A->performLU(); + p_A->fbSub(p_x); + updateInfo(); + + // Now, check for convergence + bool converged = true; + for ( unsigned i = 0; i < m_cn; ++i ) + { + double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); + if ( diff > maxErrorI ) + { + converged = false; + break; + } + } + if ( converged ) + { + for ( unsigned i = m_cn; i < m_cn+m_cb; ++i ) + { + double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] ); + if ( diff > maxErrorV ) + { + converged = false; + break; + } + } + } + + *p_x_prev = *p_x; + + if ( converged ) + break; + } + while ( ++k < maxIterations ); +} + + +bool ElementSet::doLinear( bool performLU ) +{ + if ( b_containsNonLinear || (!p_b->isChanged() && ((performLU && !p_A->isChanged()) || !performLU)) ) + return false; + + if (performLU) + p_A->performLU(); + + *p_x = *p_b; + p_A->fbSub(p_x); + updateInfo(); + p_b->setUnchanged(); + + return true; +} + + +void ElementSet::updateInfo() +{ + for ( uint i=0; i<m_cn; i++ ) + { + const double v = (*p_x)[i]; + if (std::isfinite(v)) { + m_cnodes[i]->v = v; + } else { + (*p_x)[i] = 0.; + m_cnodes[i]->v = 0.; + } + } + for ( uint i=0; i<m_cb; i++ ) + { + // NOTE: I've used lowercase and uppercase "I" here, so be careful! + const double I = (*p_x)[i+m_cn]; + if (std::isfinite(I)) { + m_cbranches[i]->i = I; + } else { + (*p_x)[i+m_cn] = 0.; + m_cbranches[i]->i = 0.; + } + } + + // Tell logic to check themselves + for ( uint i=0; i<m_clogic; ++i ) + { + p_logicIn[i]->check(); + } +} + + +void ElementSet::displayEquations() +{ + std::cout.setf(std::ios_base::fixed); + std::cout.precision(5); + std::cout.setf(std::ios_base::showpoint); + std::cout << "A x = b :"<<std::endl; + for ( uint i=0; i<m_cn+m_cb; i++ ) + { + std::cout << "( "; + for ( uint j=0; j<m_cn+m_cb; j++ ) + { + const double value = p_A->g(i,j); +// if ( value > 0 ) cout <<"+"; +// else if ( value == 0 ) cout <<" "; + std::cout.width(10); + std::cout << value<<" "; + } + std::cout << ") ( "<<(*p_x)[i]<<" ) = ( "; + std::cout<<(*p_b)[i]<<" )"<<std::endl; + } + std::cout << "A_LU:"<<std::endl; + p_A->displayLU(); +} + + diff --git a/src/electronics/simulation/elementset.h b/src/electronics/simulation/elementset.h new file mode 100644 index 0000000..c7ef7ca --- /dev/null +++ b/src/electronics/simulation/elementset.h @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ELEMENTSET_H +#define ELEMENTSET_H + +#include <vector> + +#include <qvaluelist.h> + +class CBranch; +class Circuit; +class CNode; +class Element; +class ElementSet; +class LogicIn; +class Matrix; +class NonLinear; +class Vector; + +typedef QValueList<Element*> ElementList; +typedef QValueList<NonLinear*> NonLinearList; + +/** +Steps in simulation of a set of elements: +(1) Create this class with given number of nodes "n" and voltage sources "m" +(2) Add the various elements with addElement. +(3) Call performDC() +(4) Get the nodal voltages and voltage currents with x() +(5) Repeat steps 3 and 4 if necessary for further transient analysis. + +This class shouldn't be confused with the Circuit class, but considered a helper class to Circuit. +Circuit will handle the simulation of a set of components over time. This just finds the DC-operating +point of the circuit for a given set of elements. + +@short Handles a set of circuit elements +@author David Saxton +*/ +class ElementSet +{ +public: + /** + * Create a new circuit, with "n" nodes and "m" voltage sources. + * After creating the circuit, you must call setGround to specify + * the ground nodes, before adding any elements. + */ + ElementSet( Circuit * circuit, const int n, const int m ); + /** + * Destructor. Note that only the matrix and supporting data is deleted. + * i.e. Any elements added to the circuit will not be deleted. + */ + ~ElementSet(); + Circuit * circuit() const { return m_pCircuit; } + void addElement( Element *e ); + void setCacheInvalidated(); + /** + * Returns the matrix in use. This is created once on the creation of the ElementSet + * class, and deleted in the destructor, so the pointer returned will never change. + */ + Matrix *matrix() const { return p_A; } + /** + * Returns the vector for b (i.e. the independent currents & voltages) + */ + Vector *b() const { return p_b; } + /** + * Returns the vector for x (i.e. the currents & voltages at the branches and nodes) + */ + Vector *x() const { return p_x; } + /** + * @return if we have any nonlinear elements (e.g. diodes, tranaistors). + */ + bool containsNonLinear() const { return b_containsNonLinear; } + /** + * Solves for nonlinear elements, or just does linear if it doesn't contain + * any nonlinear. + */ + void doNonLinear( int maxIterations, double maxErrorV = 1e-9, double maxErrorI = 1e-12 ); + /** + * Solves for linear and logic elements. + * @returns true if anything changed + */ + bool doLinear( bool performLU ); + CBranch **cbranches() const { return m_cbranches; } + CNode **cnodes() const { return m_cnodes; } + CNode *ground() const { return m_ground; } + /** + * Returns the number of nodes in the circuit (excluding ground 'nodes') + */ + int cnodeCount() const { return m_cn; } + /** + * Returns the number of voltage sources in the circuit + */ + int cbranchCount() const { return m_cb; } + + void createMatrixMap(); + /** + * Displays the matrix equations Ax=b and J(dx)=-r + */ + void displayEquations(); + /** + * Update the nodal voltages and branch currents from the x vector + */ + void updateInfo(); + +private: + Matrix *p_A; + Vector *p_x; + Vector *p_x_prev; + Vector *p_b; + uint m_cn; + uint m_cb; + ElementList m_elementList; + NonLinearList m_cnonLinearList; + CBranch **m_cbranches; // Pointer to an array of cbranches + CNode **m_cnodes; // Pointer to an array of cnodes + CNode *m_ground; + uint m_clogic; + LogicIn **p_logicIn; + bool b_containsNonLinear; + Circuit * m_pCircuit; +}; + +#endif + diff --git a/src/electronics/simulation/elementsignal.cpp b/src/electronics/simulation/elementsignal.cpp new file mode 100644 index 0000000..31d7d78 --- /dev/null +++ b/src/electronics/simulation/elementsignal.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementsignal.h" +#include <cmath> + +ElementSignal::ElementSignal() +{ + m_type = ElementSignal::st_sinusoidal; + m_time = 0.; + m_frequency = 0.; +} + +ElementSignal::~ElementSignal() +{ +} + +void ElementSignal::setStep( double delta, Type type, double frequency ) +{ + m_type = type; + m_delta = delta; + m_frequency = frequency; + m_omega = 6.283185307179586*m_frequency; + m_time = 1./(4.*m_frequency); +} + +double ElementSignal::advance() +{ + m_time += m_delta; + if ( m_time >= 1./m_frequency ) m_time -= 1./m_frequency; + + switch (m_type) + { + case ElementSignal::st_sawtooth: + { + // TODO Sawtooth signal + return 0.; + } + case ElementSignal::st_square: + { + return (sin(m_time*m_omega)>=0)?1:-1; + } + case ElementSignal::st_triangular: + { + // TODO Triangular signal + return 0.; + } + case ElementSignal::st_sinusoidal: + default: + { + return sin(m_time*m_omega); + } + } +} + + + diff --git a/src/electronics/simulation/elementsignal.h b/src/electronics/simulation/elementsignal.h new file mode 100644 index 0000000..c26bea1 --- /dev/null +++ b/src/electronics/simulation/elementsignal.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef ELEMENTSIGNAL_H +#define ELEMENTSIGNAL_H + +/** +@short Provides different signals +@author David Saxton +*/ +class ElementSignal +{ +public: + enum Type + { + st_sinusoidal, + st_square, + st_sawtooth, + st_triangular + }; + ElementSignal(); + ~ElementSignal(); + + void setStep( double delta, Type type, double frequency ); + /** + * Advances the timer, returns amplitude (between -1 and 1) + */ + double advance(); + +protected: + Type m_type; + double m_time; + double m_frequency; + double m_delta; + double m_omega; // Used for sinusoidal signal +}; + +#endif diff --git a/src/electronics/simulation/inductance.cpp b/src/electronics/simulation/inductance.cpp new file mode 100644 index 0000000..22c5f9e --- /dev/null +++ b/src/electronics/simulation/inductance.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "inductance.h" +#include "matrix.h" + +Inductance::Inductance( double inductance, double delta ) + : Reactive(delta) +{ + m_inductance = inductance; + r_eq_old = v_eq_old = 0.0; + m_numCNodes = 2; + m_numCBranches = 1; + setMethod( Inductance::m_euler ); +} + + +Inductance::~Inductance() +{ +} + + +void Inductance::setInductance( double i ) +{ + m_inductance = i; +} + + +void Inductance::add_initial_dc() +{ + A_c( 0, 0 ) = 1; + A_b( 0, 0 ) = 1; + A_c( 0, 1 ) = -1; + A_b( 1, 0 ) = -1; + + // The adding of r_eg and v_eq will be done for us by time_step. + // So for now, just reset the constants used. + r_eq_old = v_eq_old = 0.0; +} + + +void Inductance::updateCurrents() +{ + if (!b_status) + return; + m_cnodeI[0] = p_cbranch[0]->i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + +void Inductance::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } + + p_A->setUse_d( p_cbranch[0]->n(), p_cbranch[0]->n(), Map::et_unstable, false ); +} + + +void Inductance::time_step() +{ + if (!b_status) return; + + double i = p_cbranch[0]->i; + double v_eq_new = 0.0, r_eq_new = 0.0; + + if ( m_method == Inductance::m_euler ) + { + r_eq_new = m_inductance/m_delta; + v_eq_new = -i*r_eq_new; + } + else if ( m_method == Inductance::m_trap ) { + // TODO Implement + test trapezoidal method + r_eq_new = 2.0*m_inductance/m_delta; + } + + if ( r_eq_old != r_eq_new ) + { + A_d( 0, 0 ) -= r_eq_new - r_eq_old; + } + + if ( v_eq_new != v_eq_old ) + { + b_v( 0 ) += v_eq_new - v_eq_old; + } + + r_eq_old = r_eq_new; + v_eq_old = v_eq_new; +} + + +bool Inductance::updateStatus() +{ + b_status = Reactive::updateStatus(); + if ( m_method == Inductance::m_none ) + b_status = false; + return b_status; +} + + +void Inductance::setMethod( Method m ) +{ + m_method = m; + updateStatus(); +} + diff --git a/src/electronics/simulation/inductance.h b/src/electronics/simulation/inductance.h new file mode 100644 index 0000000..46ccb09 --- /dev/null +++ b/src/electronics/simulation/inductance.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef INDUCTANCE_H +#define INDUCTANCE_H + +#include "reactive.h" + +/** + +@author David Saxton +*/ +class Inductance : public Reactive +{ + public: + enum Method + { + m_none, // None + m_euler, // Backward Euler + m_trap // Trapezoidal (currently unimplemented) + }; + Inductance( double capacitance, double delta ); + virtual ~Inductance(); + + virtual Type type() const { return Element_Inductance; } + /** + * Set the stepping use for numerical integration of inductance, and the + * interval between successive updates. + */ + void setMethod( Method m ); + virtual void time_step(); + virtual void add_initial_dc(); + void setInductance( double i ); + virtual void add_map(); + + protected: + virtual void updateCurrents(); + virtual bool updateStatus(); + + private: + double m_inductance; // Inductance + Method m_method; // Method of integration + + double r_eq_old; + double v_eq_old; +}; + +#endif diff --git a/src/electronics/simulation/logic.cpp b/src/electronics/simulation/logic.cpp new file mode 100644 index 0000000..031dd2e --- /dev/null +++ b/src/electronics/simulation/logic.cpp @@ -0,0 +1,319 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 <vector> +#include "circuit.h" +#include "elementset.h" +#include "logic.h" +#include "matrix.h" +#include "simulator.h" +#include "src/core/ktlconfig.h" + +LogicIn::LogicIn( LogicConfig config ) + : Element::Element() +{ + m_config = config; + m_pCallbackFunction = 0l; + m_numCNodes = 1; + m_bLastState = false; + m_pNextLogic = 0l; + setLogic(getConfig()); +} + +LogicIn::~LogicIn() +{ + Simulator::self()->removeLogicInReferences(this); +} + + +void LogicIn::setCallback( CallbackClass * object, CallbackPtr func ) +{ + m_pCallbackFunction = func; + m_pCallbackObject = object; +} + + +void LogicIn::check() +{ + if (!b_status) + return; + + bool newState; + if (m_bLastState) + { + // Was high, will still be high unless voltage is less than falling trigger + newState = p_cnode[0]->v > m_config.fallingTrigger; + } + else + { + // Was low, will still be low unless voltage is more than rising trigger + newState = p_cnode[0]->v > m_config.risingTrigger; + } + + if ( m_pCallbackFunction && (newState != m_bLastState) ) + { + m_bLastState = newState; + (m_pCallbackObject->*m_pCallbackFunction)(newState); + } + m_bLastState = newState; +} + + +void LogicIn::setLogic( LogicConfig config ) +{ + m_config = config; + check(); +} + + +void LogicIn::setElementSet( ElementSet *c ) +{ + if (c) + m_pNextLogic = 0l; + else + m_cnodeI[0] = 0.; + + Element::setElementSet(c); +} + + +void LogicIn::add_initial_dc() +{ +} + + +void LogicIn::updateCurrents() +{ +} + + +LogicConfig LogicIn::getConfig() +{ + LogicConfig c; + c.risingTrigger = KTLConfig::logicRisingTrigger(); + c.fallingTrigger = KTLConfig::logicFallingTrigger(); + c.output = KTLConfig::logicOutputHigh(); + c.highImpedance = KTLConfig::logicOutputHighImpedance(); + c.lowImpedance = KTLConfig::logicOutputLowImpedance(); + return c; +} + + +LogicOut::LogicOut( LogicConfig config, bool _high ) + : LogicIn(config) +{ + m_bCanAddChanged = true; + m_bOutputHighConductanceConst = false; + m_bOutputLowConductanceConst = false; + m_bOutputHighVoltageConst = false; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + m_pSimulator = 0l; + m_bUseLogicChain = false; + b_state = false; + m_numCNodes = 1; + m_vHigh = m_gHigh = m_gLow = 0.0; + m_old_g_out = m_g_out = 0.0; + m_old_v_out = m_v_out = 0.0; + setHigh(_high); + + // Although we already call this function in LogicIn's constructor, our + // virtual function will not have got called, so we have to call it again. + setLogic(getConfig()); +} + +LogicOut::~LogicOut() +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + // Note that although this function will get called in the destructor of + // LogicIn, we must call it here as well as it needs to be called before + // removeLogicOutReferences(this) is called. + m_pSimulator->removeLogicInReferences(this); + + m_pSimulator->removeLogicOutReferences(this); +} + + +void LogicOut::setUseLogicChain( bool use ) +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + m_bUseLogicChain = use; + if (use) + setElementSet(0l); +} + + +void LogicOut::setElementSet( ElementSet *c ) +{ + if (!m_pSimulator) + m_pSimulator = Simulator::self(); + + if (c) + { + m_bUseLogicChain = false; + m_pNextChanged[0] = m_pNextChanged[1] = 0l; + } + + // NOTE Make sure that the next two lines are the same as those in setHigh and setLogic + m_g_out = b_state ? m_gHigh : m_gLow; + m_v_out = b_state ? m_vHigh : 0.0; + + LogicIn::setElementSet(c); +} + + +void LogicOut::setOutputHighConductance( double g ) +{ + m_bOutputHighConductanceConst = true; + if ( g == m_gHigh ) + return; + m_gHigh = g; + configChanged(); +} + + +void LogicOut::setOutputLowConductance( double g ) +{ + m_bOutputLowConductanceConst = true; + if ( g == m_gLow ) + return; + m_gLow = g; + configChanged(); +} + + +void LogicOut::setOutputHighVoltage( double v ) +{ + m_bOutputHighVoltageConst = true; + if ( v == m_vHigh ) + return; + m_vHigh = v; + configChanged(); +} + + +void LogicOut::setLogic( LogicConfig config ) +{ + m_config = config; + + if (!m_bOutputHighConductanceConst) + m_gHigh = 1.0/config.highImpedance; + + if (!m_bOutputLowConductanceConst) + m_gLow = (config.lowImpedance == 0.0) ? 0.0 : 1.0/config.lowImpedance; + + if (!m_bOutputHighVoltageConst) + m_vHigh = config.output; + + configChanged(); +} + + +void LogicOut::configChanged() +{ + if (m_bUseLogicChain) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Re-add the DC stuff using the new values + + m_old_g_out = m_g_out; + m_old_v_out = m_v_out; + + // NOTE Make sure that the next two lines are the same as those in setElementSet and setHigh + m_g_out = b_state ? m_gHigh : m_gLow; + m_v_out = b_state ? m_vHigh : 0.0; + + add_initial_dc(); + + m_old_g_out = 0.; + m_old_v_out = 0.; + + check(); +} + + +void LogicOut::add_map() +{ + if (!b_status) return; + + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_variable, false ); +} + + +void LogicOut::add_initial_dc() +{ + if (!b_status) + return; + + A_g( 0, 0 ) += m_g_out-m_old_g_out; + b_i( 0 ) += m_g_out*m_v_out-m_old_g_out*m_old_v_out; +} + +void LogicOut::updateCurrents() +{ + if (m_bUseLogicChain) + { + m_cnodeI[0] = 0.; + return; + } + if (!b_status) { + return; + } + m_cnodeI[0] = (p_cnode[0]->v-m_v_out)*m_g_out; +} + +void LogicOut::setHigh( bool high ) +{ + if ( high == b_state ) + return; + + if (m_bUseLogicChain) + { + b_state = high; + + for ( LogicIn * logic = this; logic; logic = logic->nextLogic() ) + logic->setLastState(high); + + if (m_bCanAddChanged) + { + m_pSimulator->addChangedLogic(this); + m_bCanAddChanged = false; + } + + return; + } + + m_old_g_out = m_g_out; + m_old_v_out = m_v_out; + + // NOTE Make sure that the next two lines are the same as those in setElementSet and setLogic + m_g_out = high ? m_gHigh : m_gLow; + m_v_out = high ? m_vHigh : 0.0; + + add_initial_dc(); + + m_old_g_out = 0.; + m_old_v_out = 0.; + + b_state = high; + + if ( p_eSet && p_eSet->circuit()->canAddChanged() ) + { + m_pSimulator->addChangedCircuit( p_eSet->circuit() ); + p_eSet->circuit()->setCanAddChanged(false); + } +} + diff --git a/src/electronics/simulation/logic.h b/src/electronics/simulation/logic.h new file mode 100644 index 0000000..be8374f --- /dev/null +++ b/src/electronics/simulation/logic.h @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "element.h" + +#include <qguardedptr.h> +#include <qvaluelist.h> + +class Component; +class Pin; +class Simulator; + +typedef QValueList<QGuardedPtr<Pin> > PinList; + +typedef struct +{ + float risingTrigger; // Trigger on rising edge + float fallingTrigger; // Trigger on falling edge + float output; // Output voltage + float highImpedance; // Output impedance when high + float lowImpedance; // Output impedance when low +} LogicConfig; + +class CallbackClass {}; +typedef void(CallbackClass::*CallbackPtr)( bool isHigh ); + +/** +Use this class for Logic Inputs - this will have infinite impedance. +Use isHigh() will return whether the voltage level at the pin +is high than the predetermined voltage threshold, and setHigh() will make the +output high/low, also according to the predetermined logic type / voltages. + +@short Boolean Logic input +*/ +class LogicIn : public Element +{ + public: + LogicIn( LogicConfig config ); + virtual ~LogicIn(); + + virtual Type type() const { return Element_LogicIn; } + virtual void setElementSet( ElementSet *c ); + + /** + * Set logic values from the LogicConfig. + */ + virtual void setLogic( LogicConfig config ); + /** + * Check if the input state has changed, to see if we need to callback. + */ + void check(); + /** + * Returns whether the pin is 'high', as defined for the logic type + * Note: this is defined as the voltage on the pin, as opposed to what the + * state was set to (the two are not necessarily the same). + */ + bool isHigh() const { return m_bLastState; } + /** + * When the logic state on this LogicIn changes, the function passed in this + * function will be called. At most one Callback can be added per LogicIn. + */ + void setCallback( CallbackClass * object, CallbackPtr func ); + /** + * Reads the LogicConfig values in from KTLConfig, and returns them in a + * nice object form. + */ + static LogicConfig getConfig(); + /** + * If this belongs to a logic chain, then this will be called from the chain. + */ + void setLastState( bool state ) { m_bLastState = state; } + /** + * Returns a pointer to the next LogicIn in the chain. + */ + LogicIn * nextLogic() const { return m_pNextLogic; } + /** + * Sets the next LogicIn in the chain. + */ + void setNextLogic( LogicIn * next ) { m_pNextLogic = next; } + /** + * Calls the callback function, if there is one. + */ + void callCallback() + { + if (m_pCallbackFunction) + (m_pCallbackObject->*m_pCallbackFunction)(m_bLastState); + } + + protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + + CallbackPtr m_pCallbackFunction; + CallbackClass * m_pCallbackObject; + bool m_bLastState; + LogicIn * m_pNextLogic; + LogicConfig m_config; +}; + + +/** +@short Logic output/input +*/ +class LogicOut : public LogicIn +{ + public: + LogicOut( LogicConfig config, bool _high ); + virtual ~LogicOut(); + + virtual void setLogic( LogicConfig config ); + virtual void setElementSet( ElementSet *c ); + virtual void add_map(); + virtual Type type() const { return Element_LogicOut; } + + /** + * Call this function to override the logic-high output impedance as set by + * the user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputHighConductance( double g ); + /** + * Call this function to override the logic-low output impedance as set by + * the user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputLowConductance( double g ); + /** + * Call this function to override the logic out voltage as set by the + * user. Once set, the impedance will not be changed by the user + * updating the config; only by subsequent calls to this function. + */ + void setOutputHighVoltage( double v ); + /** + * Returns the voltage that this will output when high. + */ + double outputHighVoltage() const { return m_vHigh; } + /** + * Sets the pin to be high/low + */ + void setHigh( bool high ); + /** + * @returns the state that this is outputting (regardless of voltage level on logic) + */ + bool outputState() const { return b_state; } + /** + * Set whether or not this LogicOut is the head of a LogicChain (controls + * itself and a bunch of LogicIns). + */ + void setUseLogicChain( bool use ); + /** + * When a LogicOut configured as the start of a LogicChain changes start, it + * appends a pointer to itself to the list of change LogicOut, starting from + * the Simulator. This functions enables appending the next changed LogicOut + * to this one. + */ + void setNextChanged( LogicOut * logicOut, unsigned char chain ) { m_pNextChanged[chain] = logicOut; } + /** + * To avoid a pointer to this LogicOut being added twice in one + * iteration due to the state changing twice, this LogicOut sets an + * added flag to true after adding it to the list of changed. The flag + * must be reset to false with this function (done by Simulator). + */ + void setCanAddChanged( bool canAdd ) { m_bCanAddChanged = canAdd; } + /** + * Returns the next LogicOut that has changed, when configured as the start + * of a LogicChain. + * @see setNextChanged + */ + LogicOut * nextChanged( unsigned char chain ) const { return m_pNextChanged[chain]; } + PinList pinList; + PinList::iterator pinListBegin; + PinList::iterator pinListEnd; + + protected: + void configChanged(); + virtual void updateCurrents(); + virtual void add_initial_dc(); + + // Pre-initalized levels from config + double m_gHigh; + double m_gLow; + double m_vHigh; + + // Whether to use the user-defined logic values + bool m_bOutputHighConductanceConst; + bool m_bOutputLowConductanceConst; + bool m_bOutputHighVoltageConst; + + double m_g_out; + double m_v_out; + double m_old_g_out; + double m_old_v_out; + bool b_state; + bool m_bCanAddChanged; + LogicOut * m_pNextChanged[2]; + Simulator * m_pSimulator; + bool m_bUseLogicChain; +}; + +#endif + diff --git a/src/electronics/simulation/matrix.cpp b/src/electronics/simulation/matrix.cpp new file mode 100644 index 0000000..fb1248f --- /dev/null +++ b/src/electronics/simulation/matrix.cpp @@ -0,0 +1,546 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "matrix.h" + +#include <kdebug.h> + +#include <assert.h> + +#include <cmath> +#include <iostream> +#include <vector> + +/// Minimum value before an entry is deemed "zero" +const double epsilon = 1e-50; + +Matrix::Matrix( uint n, uint m ) +{ + m_n = n; + m_m = m; + m_size = m_n+m_m; + + m_mat = new matrix(m_size); + m_lu = new matrix(m_size); + m_y = new double[m_size]; + m_inMap = new int[m_size]; +// m_outMap = new int[m_size]; + m_map = new Map(m_size); + zero(); +} + + +Matrix::~Matrix() +{ + delete m_map; + delete m_mat; + delete m_lu; + delete [] m_y; + delete [] m_inMap; +// delete [] m_outMap; +} + + +void Matrix::zero() +{ + for ( uint i=0; i<m_size; i++ ) + { + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] = 0.; + (*m_lu)[i][j] = 0.; + } + m_inMap[i] = i; +// m_outMap[i] = i; + } + + max_k = 0; +} + + +void Matrix::setUse( const uint i, const uint j, Map::e_type type, bool big ) +{ + m_map->setUse( i, j, type, big ); +} + + +void Matrix::createMap() +{ + int newMap[m_size]; + m_map->createMap(newMap); + for ( uint i=0; i<m_size; i++ ) + { + const int nu = newMap[i]; + if ( nu != m_inMap[i] ) + { + int old = -1; + for ( uint j=0; j<m_size && old == -1; j++ ) + { + if ( m_inMap[j] == nu ) { + old = j; + } + } + assert( old != -1 ); + swapRows( old, i ); + } + } +} + + +void Matrix::swapRows( const uint a, const uint b ) +{ + if ( a == b ) return; + m_mat->swapRows( a, b ); + + const int old = m_inMap[a]; + m_inMap[a] = m_inMap[b]; + m_inMap[b] = old; + + max_k = 0; +} + + +/*void Matrix::genOutMap() +{ + for ( uint i=0; i<m_size; i++ ) + { + m_outMap[ m_inMap[i] ] = i; + } +}*/ + + +void Matrix::operator=( Matrix *const m ) +{ + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] = m->m(i,j); + } + } + + max_k = 0; +} + +void Matrix::operator+=( Matrix *const m ) +{ + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*m_mat)[i][j] += m->m(i,j); + } + } + + max_k = 0; +} + +void Matrix::performLU() +{ +// max_k = 0; + uint n = m_size; + if ( n == 0 ) return; + + // Copy the affected segment to LU + for ( uint i=max_k; i<n; i++ ) + { + for ( uint j=max_k; j<n; j++ ) + { + (*m_lu)[i][j] = (*m_mat)[i][j]; + } + } + + // LU decompose the matrix, and store result back in matrix + for ( uint k=0; k<n-1; k++ ) + { + double * const lu_K_K = &(*m_lu)[k][k]; + if ( std::abs(*lu_K_K) < 1e-10 ) + { + if ( *lu_K_K < 0. ) *lu_K_K = -1e-10; + else *lu_K_K = 1e-10; + } + for ( uint i=std::max(k,max_k)+1; i<n; i++ ) + { + (*m_lu)[i][k] /= *lu_K_K; + } + for ( uint i=std::max(k,max_k)+1; i<n; i++ ) + { + const double lu_I_K = (*m_lu)[i][k]; + if ( std::abs(lu_I_K) > 1e-12 ) + { + for ( uint j=std::max(k,max_k)+1; j<n; j++ ) + { + (*m_lu)[i][j] -= lu_I_K*(*m_lu)[k][j]; + } + } + } + } + + max_k = n; +} + +void Matrix::fbSub( Vector* b ) +{ + if ( m_size == 0 ) return; + + for ( uint i=0; i<m_size; i++ ) + { + m_y[m_inMap[i]] = (*b)[i]; + } + + // Forward substitution + for ( uint i=1; i<m_size; i++ ) + { + double sum = 0; + for ( uint j=0; j<i; j++ ) + { + sum += (*m_lu)[i][j]*m_y[j]; + } + m_y[i] -= sum; + } + + // Back substitution + m_y[m_size-1] /= (*m_lu)[m_size-1][m_size-1]; + for ( int i=m_size-2; i>=0; i-- ) + { + double sum = 0; + for ( uint j=i+1; j<m_size; j++ ) + { + sum += (*m_lu)[i][j]*m_y[j]; + } + m_y[i] -= sum; + m_y[i] /= (*m_lu)[i][i]; + } + + for ( uint i=0; i<m_size; i++ ) + (*b)[i] = m_y[i]; +} + + +void Matrix::multiply( Vector *rhs, Vector *result ) +{ + if ( !rhs || !result ) return; + result->reset(); + for ( uint _i=0; _i<m_size; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<m_size; j++ ) + { + (*result)[_i] += (*m_mat)[i][j] * (*rhs)[j]; + } + } +} + + +void Matrix::displayMatrix() +{ + uint n = m_size; + for ( uint _i=0; _i<n; _i++ ) + { + uint i = m_inMap[_i]; + for ( uint j=0; j<n; j++ ) + { + if ( j > 0 && (*m_mat)[i][j] >= 0 ) kdDebug() << "+"; + kdDebug() << (*m_mat)[i][j] << "("<<j<<")"; + } + kdDebug() << endl; + } +} + +void Matrix::displayLU() +{ + uint n = m_size; + for ( uint _i=0; _i<n; _i++ ) + { + uint i = m_inMap[_i]; +// uint i = _i; + for ( uint j=0; j<n; j++ ) + { + if ( j > 0 && (*m_lu)[i][j] >= 0 ) std::cout << "+"; + std::cout << (*m_lu)[i][j] << "("<<j<<")"; + } + std::cout << std::endl; + } + std::cout << "m_inMap: "; + for ( uint i=0; i<n; i++ ) + { + std::cout << i<<"->"<<m_inMap[i]<<" "; + } + std::cout << std::endl; + /*cout << "m_outMap: "; + for ( uint i=0; i<n; i++ ) + { + cout << i<<"->"<<m_outMap[i]<<" "; + } + cout << endl;*/ +} + + +Map::Map( const uint size ) +{ + m_size = size; + m_map = new ETMap( m_size ); + reset(); +} + + +Map::~Map() +{ + delete m_map; +} + + +void Map::reset() +{ + for ( uint i=0; i<m_size; i++ ) + { + for ( uint j=0; j<m_size; j++ ) + { + (*m_map)[i][j] = 0; + } + } +} + + +void Map::setUse( const uint i, const uint j, Map::e_type type, bool big ) +{ + if ( type == Map::et_none ) { + (*m_map)[i][j] = Map::et_none; + } else { + (*m_map)[i][j] = type | (big)?Map::et_big:0; + } +} + + +void Map::createMap( int *map ) +{ + assert(map); + + // In this function, the passes through that we make want to be done from + // top left to bottom right, to minimise fill-in + + // available[i] is true if an external-row can be mapped to internal-row "i" + // map[i] gives the internal-row for external-row i + bool available[m_size]; + for ( uint i=0; i<m_size; i++ ) + { + available[i] = true; + map[i] = -1; + } + + // This loop looks through columns and rows to find any swaps that are necessary + // (e.g. only one matrix-element in that row/column), and if no necessary swaps + // were found, then it will swap two rows according to criteria given below + bool badMap = false; + bool changed; + do + { + changed = false; + + // Pass through columns + int E,N; + uint highest = 0; + for ( uint j=0; j<m_size; j++ ) + { + if ( map[j] == -1 ) // If we haven't mapped this column yet + { + int count = 0; // Number of "spare" elements + int element; // Last element that is "spare", only applicable if count=1 + for ( uint i=0; i<m_size; i++ ) + { + if ( available[i] && (*m_map)[i][j] ) + { + count++; + element = i; + } + } + if ( count == 0 ) { + badMap = true; + } + else if ( count == 1 ) + { + const uint newType = (*m_map)[element][j]; + if ( typeCmp( newType, highest) ) + { + E=element; + N=j; + highest=newType; + } + } + } + } + // Pass through rows + for ( uint i=0; i<m_size; i++ ) + { + if ( map[i] == -1 ) // If we haven't mapped this row yet + { + int count = 0; // Number of "spare" elements + int element; // Last element that is "spare", only applicable if count=1 + for ( uint j=0; j<m_size; j++ ) + { + if ( available[j] && (*m_map)[i][j] ) + { + count++; + element = j; + } + } + if ( count == 0 ) { + badMap = true; + } + else if ( count == 1 ) + { + const uint newType = (*m_map)[i][element]; + if ( typeCmp( newType, highest) ) + { + E=element; + N=i; + highest=newType; + } + } + } + } + if (highest) + { + available[E] = false; + map[N] = E; + changed = true; + } + if (!changed) + { + int next = -1; // next is the row to mapped to (interally) + uint j=0; + + /// TODO We want to change this search to one that finds a swap, taking into acocunt the priorities given below + while ( next == -1 && j<m_size ) + { + if ( available[j] ) next=j; + j++; + } + uint i=0; + while ( i<m_size && map[i] != -1 ) i++; + if ( next != -1 && i < m_size ) + { + available[next] = false; + map[i] = next; + changed = true; + } + } + } while (changed); + + if (badMap) + { +// cerr << "Map::createMap: unable to create decent mapping; do not trust the matrix, Neo!"<<endl; + } + + for ( int i = 0; i < int(m_size); ++i ) + { + assert( map[i] >= 0 && map[i] < int(m_size) ); + } + + // Ignore this, for now: + + // Now, we want to order the matrix, with the following priorities: + // (1) How often values change + // (2) How few values there are + // (3) How large the values are + // For each value in the column, +} + + +bool Map::typeCmp( const uint t1, const uint t2 ) +{ + if (!t2) return true; + if (!t1) return false; + + int t1_score = 1; + if ( t1 | Map::et_constant ) t1_score += 64; + else if ( t1 | Map::et_stable ) t1_score += 16; + else if ( t1 | Map::et_variable ) t1_score += 4; + + int t2_score = 1; + if ( t2 | Map::et_constant ) t2_score += 64; + else if ( t2 | Map::et_stable ) t2_score += 16; + else if ( t2 | Map::et_variable ) t2_score += 4; + + if ( t1 | Map::et_big ) t1_score *= 2; + if ( t2 | Map::et_big ) t2_score *= 2; + + return ( t1_score >= t2_score ); +} + + +Matrix22::Matrix22() +{ + reset(); +} + +bool Matrix22::solve() +{ + const double old_x1 = m_x1; + const double old_x2 = m_x2; + + const bool e11 = std::abs((m_a11))<epsilon; + const bool e12 = std::abs((m_a12))<epsilon; + const bool e21 = std::abs((m_a21))<epsilon; + const bool e22 = std::abs((m_a22))<epsilon; + + if (e11) + { + if ( e12||e21 ) + return false; + m_x2 = m_b1/m_a12; + m_x1 = (m_b2-(m_a22*m_x2))/m_a21; + } + else if (e12) + { + if ( e11||e22 ) + return false; + m_x1 = m_b1/m_a11; + m_x2 = (m_b2-(m_a21*m_x1))/m_a22; + } + else if (e21) + { + if ( e11||e22 ) + return false; + m_x2 = m_b2/m_a22; + m_x1 = (m_b1-(m_a12*m_x2))/m_a11; + } + else if (e22) + { + if ( e12||e21 ) + return false; + m_x1 = m_b2/m_a21; + m_x2 = (m_b1-(m_a11*m_x1))/m_a12; + } + else + { + m_x2 = (m_b2-(m_a21*m_b1/m_a11))/(m_a22-(m_a21*m_a12/m_a11)); + m_x1 = (m_b1-(m_a12*m_x2))/m_a11; + } + if ( !std::isfinite(m_x1) || !std::isfinite(m_x2) ) + { + m_x1 = old_x1; + m_x2 = old_x2; + return false; + } + return true; +} + +void Matrix22::reset() +{ + m_a11=m_a12=m_a21=m_a22=0.; + m_b1=m_b2=0.; + m_x1=m_x2=0.; +} + + + diff --git a/src/electronics/simulation/matrix.h b/src/electronics/simulation/matrix.h new file mode 100644 index 0000000..4c3e518 --- /dev/null +++ b/src/electronics/simulation/matrix.h @@ -0,0 +1,248 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef MATRIX_H +#define MATRIX_H + +#include "vec.h" + +#include <vector> + +class Map; + +typedef std::vector<std::vector<uint> > ETMap; + +/** +@short Handles row-wise permutation of matrixes +*/ +class Map +{ +public: + enum e_type + { + et_none = 0x0, // Default value, none + et_constant = 0x1, // Never changes value in lifetime of matrix + et_stable = 0x2, // Value changes occasionally, e.g. user changing resistance value + et_variable = 0x3, // Rate of changing is unknown, probably average - e.g. logic + et_unstable = 0x4, // Value changes practically every call of performLU + et_big = 0x8 // Ignore this :-) It is used to OR with one of the others, but it should only ever be accessed by the class + }; + Map( const uint size ); + ~Map(); + + void setUse( const uint i, const uint j, Map::e_type type, bool big ); + /** + * Generates an optimal permutation, returned in the given array + */ + void createMap( int *map ); + /** + * Resets the info to a blank pattern + */ + void reset(); + +protected: + /** + * Compares two "types", returning true if t1 >= t2, else false + */ + bool typeCmp( const uint t1, const uint t2 ); + +private: + ETMap *m_map; + uint m_size; +}; + +/** +This class performs matrix storage, lu decomposition, forward and backward +substitution, and a few other useful operations. Steps in using class: +(1) Create an instance of this class with the correct size +(2) Define the matrix pattern as neccessary: + (1) Call zero (unnecessary after initial ceration) to reset the pattern + & matrix + (2) Call setUse to set the use of each element in the matrix + (3) Call createMap to generate the row-wise permutation mapping for use + in partial pivoting +(3) Add the values to the matrix +(4) Call performLU, and get the results with fbSub +(5) Repeat 2, 3, 4 or 5 as necessary. +@todo We need to allow createMap to work while the matrix has already been initalised +@short Matrix manipulation class tailored for circuit equations +@author David Saxton +*/ +class Matrix +{ +public: + /** + * Creates a size x size square matrix m, with all values zero, + * and a right side vector x of size m+n + */ + Matrix( uint n, uint m ); + ~Matrix(); + /** + * Sets all elements to zero + */ + void zero(); + /** + * Sets the type of (matrix-) element at the position i, j. + * @param type Describes how often the value changes + * @param big Set this true if the value is likely to be >= 1, else false + */ + void setUse( const uint i, const uint j, Map::e_type type, bool big ); + void setUse_b( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i, j+m_n, type, big ); + } + void setUse_c( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i+m_n, j, type, big ); + } + void setUse_d( const uint i, const uint j, Map::e_type type, bool big ) + { + setUse( i+m_n, j+m_n, type, big ); + } + /** + * Generates the row-wise permutation mapping from the values set by setUse + */ + void createMap(); + /** + * Returns true if the matrix is changed since last calling performLU() + * - i.e. if we do need to call performLU again. + */ + inline bool isChanged() const { return max_k < m_size; } + /** + * Performs LU decomposition. Going along the rows, + * the value of the decomposed LU matrix depends only on + * the previous values. + */ + void performLU(); + /** + * Applies the right side vector (x) to the decomposed matrix, + * with the solution returned in x. + */ + void fbSub( Vector* x ); + /** + * Prints the matrix to stdout + */ + void displayMatrix(); + /** + * Prints the LU-decomposed matrix to stdout + */ + void displayLU(); + /** + * Sets the element matrix at row i, col j to value x + */ + double& g( uint i, uint j ) + { + i = m_inMap[i]; + if ( i<max_k ) max_k=i; + if ( j<max_k ) max_k=j; + + // I think I need the next line... + if ( max_k>0 ) max_k--; + + return (*m_mat)[i][j]; + } + double& b( uint i, uint j ) { return g( i, j+m_n ); } + double& c( uint i, uint j ) { return g( i+m_n, j ); } + double& d( uint i, uint j ) { return g( i+m_n, j+m_n ); } + const double g( uint i, uint j ) const { return (*m_mat)[m_inMap[i]][j]; } + const double b( uint i, uint j ) const { return g( i, j+m_n ); } + const double c( uint i, uint j ) const { return g( i+m_n, j ); } + const double d( uint i, uint j ) const { return g( i+m_n, j+m_n ); } + /** + * Returns the value of matrix at row i, col j. + */ + const double m( uint i, uint j ) const + { + return (*m_mat)[m_inMap[i]][j]; + } + /** + * Multiplies this matrix by the Vector rhs, and places the result + * in the vector pointed to by result. Will fail if wrong size. + */ + void multiply( Vector *rhs, Vector *result ); + /** + * Sets the values of this matrix to that of the given matrix + */ + void operator=( Matrix *const m ); + /** + * Adds the values of the given matrix to this matrix + */ + void operator+=( Matrix *const m ); + +private: + /** + * Swaps around the rows in the (a) the matrix; and (b) the mappings + */ + void swapRows( const uint a, const uint b ); + +// // Generates m_outMap from m_inMap +// void genOutMap(); + + uint m_n; + uint m_m; + uint m_size; + uint max_k; + + int *m_inMap; // Rowwise permutation mapping from external reference to internal storage +// int *m_outMap; // Opposite of m_inMap + + matrix *m_mat; + matrix *m_lu; + double *m_y; // Avoids recreating it lots of times + Map *m_map; +}; + + +/** +This class provides a very simple, lightweight, 2x2 matrix solver. +It's fast and reliable. Set the values for the entries of A and b: + +A x = b + +call solve() (which returns true if successful - i.e. exactly one solution to the +matrix), and get the values of x with the appropriate functions. + +@short 2x2 Matrix +@author David Saxton +*/ +class Matrix22 +{ +public: + Matrix22(); + + double &a11() { return m_a11; } + double &a12() { return m_a12; } + double &a21() { return m_a21; } + double &a22() { return m_a22; } + + double &b1() { return m_b1; } + double &b2() { return m_b2; } + + /** + * Solve the matrix. Returns true if successful (i.e. non-singular), else + * false. Get the solution with x1() and x2(). + */ + bool solve(); + /** + * Resets all entries to zero + */ + void reset(); + + double x1() const { return m_x1; } + double x2() const { return m_x2; } + +private: + double m_a11, m_a12, m_a21, m_a22; + double m_b1, m_b2; + double m_x1, m_x2; +}; + + +#endif diff --git a/src/electronics/simulation/nonlinear.cpp b/src/electronics/simulation/nonlinear.cpp new file mode 100644 index 0000000..c975f16 --- /dev/null +++ b/src/electronics/simulation/nonlinear.cpp @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "matrix.h" +#include "nonlinear.h" + +#include <cmath> +using namespace std; + +const double KTL_MAX_DOUBLE = 1.7976931348623157e+308; ///< 7fefffff ffffffff +const int KTL_MAX_EXPONENT = int( log( KTL_MAX_DOUBLE ) ); + + +NonLinear::NonLinear() + : Element() +{ +} + + +#ifndef MIN +# define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif + + +// The function computes the exponential pn-junction current. +double NonLinear::diodeCurrent( double v, double I_S, double Vte ) const +{ + return I_S * (exp( MIN( v / Vte, KTL_MAX_EXPONENT ) ) - 1); +} + + + +double NonLinear::diodeConductance( double v, double I_S, double Vte ) const +{ + return I_S * exp( MIN( v / Vte, KTL_MAX_EXPONENT ) ) / Vte; +} + + + +double NonLinear::diodeVoltage( double V, double V_prev, double V_T, double Vcrit ) const +{ + if ( V > Vcrit && fabs( V - V_prev ) > 2 * V_T ) + { + if ( V_prev > 0 ) + { + double arg = (V - V_prev) / V_T; + if (arg > 0) + V = V_prev + V_T * (2 + log( arg - 2 )); + else + V = V_prev - V_T * (2 + log( 2 - arg )); + } + else + V = (V_prev < 0) ? (V_T * log (V / V_T)) : Vcrit; + } + else + { + if ( V < 0 ) + { + double arg = (V_prev > 0) ? (-1 - V_prev) : (2 * V_prev - 1); + if (V < arg) + V = arg; + } + } + return V; +} + + +double NonLinear::diodeCriticalVoltage( double I_S, double V_Te ) const +{ + return V_Te * log( V_Te / M_SQRT2 / I_S ); +} + + +void NonLinear::diodeJunction( double V, double I_S, double V_Te, double * I, double * g ) const +{ + if (V < -3 * V_Te) + { + double a = 3 * V_Te / (V * M_E); + a = a * a * a; + *I = -I_S * (1 + a); + *g = +I_S * 3 * a / V; + } + else + { + double e = exp( MIN( V / V_Te, KTL_MAX_EXPONENT ) ); + *I = I_S * (e - 1); + *g = I_S * e / V_Te; + } +} + diff --git a/src/electronics/simulation/nonlinear.h b/src/electronics/simulation/nonlinear.h new file mode 100644 index 0000000..422f3a4 --- /dev/null +++ b/src/electronics/simulation/nonlinear.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2003-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef NONLINEAR_H +#define NONLINEAR_H + +#include "element.h" + +/** +@short Represents a non-linear circuit element (such as a diode) +@author David Saxton +*/ +class NonLinear : public Element +{ + public: + NonLinear(); + + virtual bool isNonLinear() { return true; } + /** + * Newton-Raphson iteration: Update equation system. + */ + virtual void update_dc() = 0; + + protected: + double diodeCurrent( double v, double I_S, double Vte ) const; + /** + * Conductance of the diode - the derivative of Schockley's + * approximation. + */ + double diodeConductance( double v, double I_S, double Vte ) const; + /** + * Limits the diode voltage to prevent divergence in the nonlinear + * iterations. + */ + double diodeVoltage( double v, double V_prev, double Vt, double V_crit ) const; + void diodeJunction( double v, double I_S, double Vte, double * I, double * g ) const; + + double diodeCriticalVoltage( double I_S, double Vte ) const; +}; + +#endif diff --git a/src/electronics/simulation/opamp.cpp b/src/electronics/simulation/opamp.cpp new file mode 100644 index 0000000..45fbf02 --- /dev/null +++ b/src/electronics/simulation/opamp.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "opamp.h" + +OpAmp::OpAmp() + : Element::Element() +{ + m_numCBranches = 1; + m_numCNodes = 3; +} + + +OpAmp::~OpAmp() +{ +} + + +void OpAmp::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + // Non-inverting input + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[2]->isGround ) + { + // Inverting input + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + // Output + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + } +} + + +void OpAmp::add_initial_dc() +{ + if (!b_status) + return; + + // Non-inverting input + A_c( 0, 0 ) = 1; + + // Inverting input + A_c( 0, 2 ) = -1; + + // Output + A_b( 1, 0 ) = 1; +} + + +void OpAmp::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = m_cnodeI[2] = 0.0; + m_cnodeI[1] = p_cbranch[0]->i; +} + + + diff --git a/src/electronics/simulation/opamp.h b/src/electronics/simulation/opamp.h new file mode 100644 index 0000000..22fe843 --- /dev/null +++ b/src/electronics/simulation/opamp.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef OPAMP_H +#define OPAMP_H + +#include <element.h> + +/** +node 0: non-inverting input +node 1: output +node 2: inverting input +@author David Saxton +*/ +class OpAmp : public Element +{ + public: + OpAmp(); + virtual ~OpAmp(); + + virtual Type type() const { return Element_OpAmp; } + virtual void add_map(); + + protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); +}; + +#endif diff --git a/src/electronics/simulation/reactive.cpp b/src/electronics/simulation/reactive.cpp new file mode 100644 index 0000000..83fcfd4 --- /dev/null +++ b/src/electronics/simulation/reactive.cpp @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "reactive.h" + +Reactive::Reactive( const double delta ) + : Element() +{ + m_delta = delta; +} + + +Reactive::~Reactive() +{ +} + + +void Reactive::setDelta( double delta ) +{ + m_delta = delta; + updateStatus(); +} + + +bool Reactive::updateStatus() +{ + return Element::updateStatus(); +} diff --git a/src/electronics/simulation/reactive.h b/src/electronics/simulation/reactive.h new file mode 100644 index 0000000..1142b34 --- /dev/null +++ b/src/electronics/simulation/reactive.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef REACTIVE_H +#define REACTIVE_H + +#include "element.h" + +/** +@short Represents a reactive element (such as a capacitor) +@author David Saxton +*/ +class Reactive : public Element +{ +public: + Reactive( const double delta ); + virtual ~Reactive(); + + virtual bool isReactive() { return true; } + /** + * Call this function to set the time period (in seconds) + */ + void setDelta( double delta ); + /** + * Called on every time step for the element to update itself + */ + virtual void time_step() = 0; + +protected: + virtual bool updateStatus(); + + double m_delta; // Delta time interval +}; + +#endif diff --git a/src/electronics/simulation/resistance.cpp b/src/electronics/simulation/resistance.cpp new file mode 100644 index 0000000..9c2d25d --- /dev/null +++ b/src/electronics/simulation/resistance.cpp @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "resistance.h" + +// #include <kdebug.h> + +Resistance::Resistance( const double resistance ) + : Element::Element() +{ + m_g = resistance < 1e-9 ? 1e9 : 1./resistance; + m_numCNodes = 2; +// kdDebug() << k_funcinfo << endl; +} + +Resistance::~Resistance() +{ +// kdDebug() << k_funcinfo << endl; +} + +void Resistance::setConductance( const double g ) +{ + if ( g == m_g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove old resistance + m_g = -m_g; + add_initial_dc(); + + m_g = g; + add_initial_dc(); +} + + +void Resistance::setResistance( const double r ) +{ + setConductance( r < 1e-9 ? 1e9 : 1./r ); +} + + +void Resistance::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) { + p_A->setUse( p_cnode[1]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + + if ( !p_cnode[0]->isGround && !p_cnode[1]->isGround ) + { + p_A->setUse( p_cnode[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + p_A->setUse( p_cnode[1]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } +} + + +void Resistance::add_initial_dc() +{ + if (!b_status) return; + + A_g( 0, 0 ) += m_g; + A_g( 1, 1 ) += m_g; + A_g( 0, 1 ) -= m_g; + A_g( 1, 0 ) -= m_g; +} + + +void Resistance::updateCurrents() +{ + if (!b_status) return; + const double v=p_cnode[0]->v-p_cnode[1]->v; + m_cnodeI[1] = v*m_g; + m_cnodeI[0] = -m_cnodeI[1]; +} + + + + diff --git a/src/electronics/simulation/resistance.h b/src/electronics/simulation/resistance.h new file mode 100644 index 0000000..7e5a62e --- /dev/null +++ b/src/electronics/simulation/resistance.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef RESISTANCE_H +#define RESISTANCE_H + +#include "element.h" + +/** +@short Resistance +@author David saxton +*/ +class Resistance : public Element +{ +public: + Resistance( const double resistance ); + virtual ~Resistance(); + + virtual Type type() const { return Element_Resistance; } + + void setConductance( const double g ); + void setResistance( const double r ); + + double resistance() { return 1/m_g; } + double conductance() { return m_g; } + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vccs.cpp b/src/electronics/simulation/vccs.cpp new file mode 100644 index 0000000..7ff1bc1 --- /dev/null +++ b/src/electronics/simulation/vccs.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "vccs.h" + +VCCS::VCCS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 1; + m_numCNodes = 4; +} + + +VCCS::~VCCS() +{ +} + + +void VCCS::setGain( const double g ) +{ + if ( g == m_g ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + // Remove old values + m_g = -m_g; + add_initial_dc(); + + // Add new values + m_g = g; + add_initial_dc(); +} + + +void VCCS::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } +} + + +void VCCS::add_initial_dc() +{ + if (!b_status) + return; + + A_g( 2, 0 ) += m_g; + A_g( 3, 0 ) -= m_g; + A_g( 2, 1 ) -= m_g; + A_g( 3, 1 ) += m_g; +} + + +void VCCS::updateCurrents() +{ + if (!b_status) + return; + + m_cnodeI[0] = m_cnodeI[1] = 0.; + m_cnodeI[3] = (p_cnode[0]->v-p_cnode[1]->v)*m_g; + m_cnodeI[2] = -m_cnodeI[3]; +} + + diff --git a/src/electronics/simulation/vccs.h b/src/electronics/simulation/vccs.h new file mode 100644 index 0000000..26f1101 --- /dev/null +++ b/src/electronics/simulation/vccs.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VCCS_H +#define VCCS_H + +#include "element.h" + +/** +CNodes n0 and n1 are used for the voltage control. +CNodes n2 and n3 are used for the current output. +@short Voltage Controlled Current Source +@author David Saxton +*/ +class VCCS : public Element +{ +public: + VCCS( const double gain ); + virtual ~VCCS(); + + virtual Type type() const { return Element_VCCS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vcvs.cpp b/src/electronics/simulation/vcvs.cpp new file mode 100644 index 0000000..68604df --- /dev/null +++ b/src/electronics/simulation/vcvs.cpp @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "elementset.h" +#include "matrix.h" +#include "vcvs.h" + +VCVS::VCVS( const double gain ) + : Element::Element() +{ + m_g = gain; + m_numCBranches = 1; + m_numCNodes = 4; +} + + +VCVS::~VCVS() +{ +} + + +void VCVS::setGain( const double g ) +{ + if ( g == m_g ) + return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_g = -m_g; + add_initial_dc(); + + m_g = g; + add_initial_dc(); +} + + +void VCVS::add_map() +{ + if (!b_status) + return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_stable, false ); + } + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_stable, false ); + } + if ( !p_cnode[2]->isGround ) + { + p_A->setUse_b( p_cnode[2]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[2]->n(), Map::et_constant, true ); + } + if ( !p_cnode[3]->isGround ) + { + p_A->setUse_b( p_cnode[3]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[3]->n(), Map::et_constant, true ); + } +} + + +void VCVS::add_initial_dc() +{ + if (!b_status) + return; + + A_c( 0, 0 ) -= m_g; + A_c( 0, 1 ) += m_g; + A_b( 2, 0 ) = 1; + A_c( 0, 2 ) = 1; + A_b( 3, 0 ) = -1; + A_c( 0, 3 ) = -1; +} + + +void VCVS::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = m_cnodeI[1] = 0.; + m_cnodeI[3] = p_cbranch[0]->i; + m_cnodeI[2] = -m_cnodeI[3]; +} + + diff --git a/src/electronics/simulation/vcvs.h b/src/electronics/simulation/vcvs.h new file mode 100644 index 0000000..862dea9 --- /dev/null +++ b/src/electronics/simulation/vcvs.h @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VCVS_H +#define VCVS_H + +#include "element.h" + +/** +Voltage source between nodes c2 and c3 +Controlling voltage between nodes c0 and c1 +@short Voltage Controlled Voltage Source +@author David Saxton +*/ +class VCVS : public Element +{ +public: + VCVS( const double gain ); + virtual ~VCVS(); + + virtual Type type() const { return Element_VCVS; } + void setGain( const double g ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_g; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/vec.cpp b/src/electronics/simulation/vec.cpp new file mode 100644 index 0000000..d45f505 --- /dev/null +++ b/src/electronics/simulation/vec.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "vec.h" + +#include <assert.h> +#include <cmath> +#include <string.h> +using namespace std; + +Vector::Vector( const int size ) +{ + m_size = size; + m_vec = new double[m_size]; + reset(); + b_changed = true; +} + + +Vector::~Vector() +{ + // Hmm...this looks like it's the correct format, although valgrind complains + // about improper memory use. Interesting. "delete m_vec" definitely leaks + // memory, so this seems like the lesser of two evils. I miss C memory allocation + // somtimes, with a nice simple free :p + delete [] m_vec; +} + + +void Vector::reset() +{ + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] = 0.; + } + b_changed = true; +} + + +void Vector::limitTo( double scaleMax, Vector * limitVector ) +{ + assert( limitVector ); + assert( limitVector->size() == size() ); + + for ( int i = 0; i < m_size; ++i ) + { + double limitAbs = std::abs( limitVector->m_vec[i] ); + if ( limitAbs < 1e-6 ) + limitAbs = 1e-6; + + double thisAbs = std::abs( m_vec[i] ); + if ( thisAbs < 1e-6 ) + thisAbs = 1e-6; + + if ( (thisAbs / limitAbs) > scaleMax ) + m_vec[i] /= (thisAbs / limitAbs); + + else if ( (limitAbs / thisAbs) > scaleMax ) + m_vec[i] /= (limitAbs / thisAbs); + } + b_changed = true; +} + + +void Vector::operator += ( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] += (*rhs)[i]; + } + b_changed = true; +} + + +void Vector::operator -= ( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] -= (*rhs)[i]; + } + b_changed = true; +} + + +void Vector::operator *=( double s ) +{ + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] *= s; + } + b_changed = true; +} + + +void Vector::operator = ( Vector& v ) +{ + assert( size() == v.size() ); + memcpy( m_vec, v.m_vec, m_size * sizeof( double ) ); + b_changed = true; +} + + +void Vector::negative( Vector *rhs ) +{ + if (!rhs) return; + for ( int i=0; i<m_size; i++ ) + { + m_vec[i] = -(*rhs)[i]; + } + b_changed = true; +} + + +double Vector::abs() const +{ + double s=0; + for ( int i=0; i<m_size; i++ ) + { + s += m_vec[i]*m_vec[i]; + } + return sqrt(s); +} + + + +// matrix stuff... + +matrix::matrix( const uint size ) +{ + m_size = size; + m_mat = new Vector*[m_size]; + for ( uint i=0; i<m_size; ++i ) + { + m_mat[i] = new Vector(m_size); + } +} + +matrix::~matrix() +{ + for ( uint i=0; i<m_size; ++i ) + { + delete m_mat[i]; + } + delete [] m_mat; +} + + +void matrix::swapRows( const uint a, const uint b ) +{ + if ( a == b ) return; + Vector *v = m_mat[a]; + m_mat[a] = m_mat[b]; + m_mat[b] = v; +} + + + diff --git a/src/electronics/simulation/vec.h b/src/electronics/simulation/vec.h new file mode 100644 index 0000000..7192bb3 --- /dev/null +++ b/src/electronics/simulation/vec.h @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VEC_H +#define VEC_H + +typedef unsigned uint; + +/** +@short Vector of doubles, faster than STL vector +@author David Saxton +*/ +class Vector +{ +public: + Vector( const int size ); + ~Vector(); + + double & operator[]( const int i ) { b_changed=true; return m_vec[i]; } + const double operator[]( const int i ) const { return m_vec[i]; } + int size() const { return m_size; } + /** + * Resets all values to 0 + */ + void reset(); + /** + * Limits the absolute value of each component of this vector to scaleMax + * times bigger or smaller than the components of limitVector. + */ + void limitTo( double scaleMax, Vector * limitVector ); + /** + * Adds the Vector rhs to this + */ + void operator+=( Vector *rhs ); + /** + * Subtracts the Vector rhs from this + */ + void operator-=( Vector *rhs ); + /** + * Multiplies this Vector by the given scaler constant + */ + void operator*=( double s ); + /** + * Sets this vector equal to the given vector + */ + void operator=( Vector& v ); + /** + * Copies the negative values of the given vector to this vector. + * (i.e. sets this = -rhs ) + */ + void negative( Vector *rhs ); + /** + * Returns the absolute value of this vector, defined as the squareroot + * of the sum of the square of each element of the vector + */ + double abs() const; + /** + * Returns true if the vector has changed since setUnchanged was last called + * Note that this will return true if the vector has just been read, due to + * limitations with the [] operator. + */ + inline bool isChanged() const { return b_changed; } + /** + * Sets the changed status to false. + */ + inline void setUnchanged() { b_changed=false; } + +private: + Vector( const Vector & ); + Vector & operator= ( const Vector & ); + + bool b_changed; + double *m_vec; + int m_size; +}; + +/** +@short Container for Vector of Vectors +@author David Saxton +*/ +class matrix +{ +public: + matrix( const uint size ); + ~matrix(); + + Vector & operator[]( const uint i ) { return *(m_mat[i]); } + const Vector & operator[]( const uint i ) const { return *(m_mat[i]); } + /** + * Swaps the pointers to the given rows + */ + void swapRows( const uint a, const uint b ); + +private: + matrix( const matrix & ); + matrix & operator= ( const matrix & ); + + Vector **m_mat; + uint m_size; +}; + +#endif diff --git a/src/electronics/simulation/voltagepoint.cpp b/src/electronics/simulation/voltagepoint.cpp new file mode 100644 index 0000000..44d3b01 --- /dev/null +++ b/src/electronics/simulation/voltagepoint.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "matrix.h" +#include "voltagepoint.h" +#include "elementset.h" + +VoltagePoint::VoltagePoint( const double voltage ) + : Element::Element() +{ + m_voltage = -voltage; + m_numCBranches = 1; + m_numCNodes = 1; +} + + +VoltagePoint::~VoltagePoint() +{ +} + + +void VoltagePoint::setVoltage( const double v ) +{ + if ( -v == m_voltage ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_voltage = -v; + add_initial_dc(); +} + + +void VoltagePoint::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } +} + + +void VoltagePoint::add_initial_dc() +{ + if (!b_status) return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + + b_v( 0 ) = m_voltage; +} + + +void VoltagePoint::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = p_cbranch[0]->i; +} + + + + + diff --git a/src/electronics/simulation/voltagepoint.h b/src/electronics/simulation/voltagepoint.h new file mode 100644 index 0000000..924b0af --- /dev/null +++ b/src/electronics/simulation/voltagepoint.h @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VOLTAGEPOINT_H +#define VOLTAGEPOINT_H + +#include "element.h" + +/** +@short VoltagePoint +@author David saxton +*/ +class VoltagePoint : public Element +{ +public: + VoltagePoint( const double voltage ); + virtual ~VoltagePoint(); + + virtual Type type() const { return Element_VoltagePoint; } + void setVoltage( const double voltage ); + double voltage() { return m_voltage; } + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_voltage; // Conductance +}; + +#endif diff --git a/src/electronics/simulation/voltagesignal.cpp b/src/electronics/simulation/voltagesignal.cpp new file mode 100644 index 0000000..d7a5839 --- /dev/null +++ b/src/electronics/simulation/voltagesignal.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "matrix.h" +#include "voltagesignal.h" +#include "elementset.h" + +VoltageSignal::VoltageSignal( const double delta, const double voltage ) + : Reactive::Reactive(delta) +{ + m_voltage = voltage; + m_numCNodes = 2; + m_numCBranches = 1; +} + + +VoltageSignal::~VoltageSignal() +{ +} + + +void VoltageSignal::setVoltage( const double v ) +{ + m_voltage = v; +} + + +void VoltageSignal::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } +} + + +void VoltageSignal::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + A_b( 1, 0 ) = 1; + A_c( 0, 1 ) = 1; +} + + +void VoltageSignal::time_step() +{ + if (!b_status) return; + b_v( 0 ) = m_voltage*advance(); +} + + +void VoltageSignal::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[1] = p_cbranch[0]->i; + m_cnodeI[0] = -m_cnodeI[1]; +} + + diff --git a/src/electronics/simulation/voltagesignal.h b/src/electronics/simulation/voltagesignal.h new file mode 100644 index 0000000..5e35e72 --- /dev/null +++ b/src/electronics/simulation/voltagesignal.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VOLTAGESIGNAL_H +#define VOLTAGESIGNAL_H + +#include "reactive.h" +#include "elementsignal.h" + +/** +@short VoltageSignal +@author David saxton +*/ +class VoltageSignal : public Reactive, public ElementSignal +{ +public: + VoltageSignal( const double delta, const double voltage ); + virtual ~VoltageSignal(); + + virtual Element::Type type() const { return Element_VoltageSignal; } + void setVoltage( const double voltage ); + double voltage() { return m_voltage; } + virtual void time_step(); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_voltage; // Voltage +}; + +#endif diff --git a/src/electronics/simulation/voltagesource.cpp b/src/electronics/simulation/voltagesource.cpp new file mode 100644 index 0000000..b4fca64 --- /dev/null +++ b/src/electronics/simulation/voltagesource.cpp @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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 "matrix.h" +#include "voltagesource.h" +#include "elementset.h" + +VoltageSource::VoltageSource( const double voltage ) + : Element::Element() +{ + m_v = voltage; + m_numCBranches = 1; + m_numCNodes = 2; +} + +VoltageSource::~VoltageSource() +{ +} + +void VoltageSource::setVoltage( const double v ) +{ + if ( m_v == v ) return; + + if (p_eSet) + p_eSet->setCacheInvalidated(); + + m_v = v; + add_initial_dc(); +} + + +void VoltageSource::add_map() +{ + if (!b_status) return; + + if ( !p_cnode[0]->isGround ) + { + p_A->setUse_b( p_cnode[0]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[0]->n(), Map::et_constant, true ); + } + + if ( !p_cnode[1]->isGround ) + { + p_A->setUse_b( p_cnode[1]->n(), p_cbranch[0]->n(), Map::et_constant, true ); + p_A->setUse_c( p_cbranch[0]->n(), p_cnode[1]->n(), Map::et_constant, true ); + } +} + + +void VoltageSource::add_initial_dc() +{ + if (!b_status) + return; + + A_b( 0, 0 ) = -1; + A_c( 0, 0 ) = -1; + A_b( 1, 0 ) = 1; + A_c( 0, 1 ) = 1; + + b_v( 0 ) = m_v; +} + +void VoltageSource::updateCurrents() +{ + if (!b_status) return; + m_cnodeI[0] = p_cbranch[0]->i; + m_cnodeI[1] = -m_cnodeI[0]; +} + + diff --git a/src/electronics/simulation/voltagesource.h b/src/electronics/simulation/voltagesource.h new file mode 100644 index 0000000..9b27b0d --- /dev/null +++ b/src/electronics/simulation/voltagesource.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2003-2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef VOLTAGESOURCE_H +#define VOLTAGESOURCE_H + +#include "element.h" + +/** +CNode n0 is the negative terminal, CNode n1 is the positive terminal +@short Voltage Source +*/ +class VoltageSource : public Element +{ +public: + VoltageSource( const double voltage ); + virtual ~VoltageSource(); + + virtual Type type() const { return Element_VoltageSource; } + void setVoltage( const double v ); + virtual void add_map(); + +protected: + virtual void updateCurrents(); + virtual void add_initial_dc(); + +private: + double m_v; // Voltage +}; + +#endif diff --git a/src/electronics/subcircuits.cpp b/src/electronics/subcircuits.cpp new file mode 100644 index 0000000..65ae8e3 --- /dev/null +++ b/src/electronics/subcircuits.cpp @@ -0,0 +1,175 @@ +/*************************************************************************** + * Copyright (C) 2004-2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "circuitdocument.h" +#include "ecsubcircuit.h" +#include "itemdocumentdata.h" +#include "itemlibrary.h" +#include "itemselector.h" +#include "subcircuits.h" + +#include <kapplication.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kiconloader.h> +#include <kstandarddirs.h> +#include <qfile.h> +#include <qtextstream.h> + +Subcircuits::Subcircuits() + : QObject() +{ + connect( ComponentSelector::self(), SIGNAL(itemRemoved(const QString& )), this, SLOT(slotItemRemoved(const QString& )) ); +} + + +Subcircuits::~Subcircuits() +{ +} + + +void Subcircuits::initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ) +{ + const QString fileName = genFileName(subcircuitId); + if ( !QFile::exists(fileName) ) + { + kdDebug() << "Subcircuits::createSubcircuit: Subcircuit \""<<fileName<<"\" was not found."<<endl; + return; + } + + SubcircuitData subcircuit; + if (!subcircuit.loadData( genFileName(subcircuitId) ) ) + return; + + subcircuit.initECSubcircuit(ecSubcircuit); +} + + +ECSubcircuit* Subcircuits::createSubcircuit( int id, CircuitDocument *circuitDocument, bool newItem, const char *newId ) +{ + //I pass finishCreation = false here because the subcircuit was getting + //finished twice, causing a segfault in CircuitDocument::assignCircuits() + //--electronerd + ECSubcircuit *ecSubcircuit = static_cast<ECSubcircuit*>(itemLibrary()->createItem( "ec/subcircuit", circuitDocument, newItem, newId, false )); + ecSubcircuit->property("id")->setValue(id); + return ecSubcircuit; +} + + +void Subcircuits::loadSubcircuits() +{ + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + + QValueList<int> idList = config->readIntListEntry("Ids"); + const QValueList<int>::iterator idListEnd = idList.end(); + for ( QValueList<int>::iterator it = idList.begin(); it != idListEnd; ++it ) + { + QFile file( genFileName(*it) ); + if ( file.open(IO_ReadOnly) == false ) + { + // File has mysteriously disappeared.... + *it = -1; + } + else + { + config->setGroup("Subcircuit_"+QString::number(*it)); + updateComponentSelector( *it, config->readEntry("Name") ); + } + file.close(); + } + idList.remove(-1); + + // Update the config file if any ids have been removed + config->setGroup("Subcircuits"); + config->writeEntry( "Ids", idList ); +} + + +QString Subcircuits::genFileName( const int nextId ) +{ + return locateLocal( "appdata", "subcircuit_"+QString::number(nextId)+".circuit" ); +} + + +void Subcircuits::updateComponentSelector( int id, const QString &name ) +{ + if ( name.isEmpty() ) + return; + + ComponentSelector::self()->addItem( name, "sc/"+QString::number(id), "Subcircuits", KGlobal::iconLoader()->loadIcon( "ktechlab_circuit", KIcon::Small ), true ); +} + + +void Subcircuits::addSubcircuit( const QString &name, const QString &subcircuitXml ) +{ + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + + int nextId = config->readNumEntry( "NextId", 0 ); + + while ( QFile::exists( genFileName(nextId) ) ) { + nextId++; + } + + const int id = nextId; + + const QString fileName = genFileName(id); + QFile file(fileName); + + if ( file.open(IO_WriteOnly) == false ) + { + kdError() << "Subcircuits::addSubcircuit: couldn't open subcircuit save file: "<<fileName<<endl; + return; + } + + QTextStream stream(&file); + stream << subcircuitXml; + file.close(); + + QValueList<int> idList = config->readIntListEntry("Ids"); + idList += id; + config->writeEntry( "Ids", idList ); + config->writeEntry( "NextId", ++nextId ); + + config->setGroup("Subcircuit_"+QString::number(id)); + config->writeEntry( "Name", name ); + + // It's important that we write the configuration *now*, lest the subcircuits be lost + config->sync(); + + updateComponentSelector( id, name ); +} + + +void Subcircuits::slotItemRemoved( const QString &id ) +{ + if ( !id.startsWith("sc/") ) { + return; + } + + QString temp = id; + temp.remove("sc/"); + const int id_num = temp.toInt(); + const QString fileName = genFileName(id_num); + QFile file(fileName); + file.remove(); + + KConfig *config = kapp->config(); + config->setGroup("Subcircuits"); + QValueList<int> idList = config->readIntListEntry("Ids"); + idList.remove(id_num); + config->writeEntry( "Ids", idList ); +} + + +#include "subcircuits.moc" + + diff --git a/src/electronics/subcircuits.h b/src/electronics/subcircuits.h new file mode 100644 index 0000000..3f08dd9 --- /dev/null +++ b/src/electronics/subcircuits.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2004 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef SUBCIRCUITS_H +#define SUBCIRCUITS_H + +#include <qobject.h> + +class CircuitDocument; +class ECSubcircuit; +class Subcircuits; +inline Subcircuits *subcircuits(); + +/** +Interface for dealing with loading / saving / etc of subcircuits +@author David Saxton +*/ +class Subcircuits : public QObject +{ +Q_OBJECT +public: + ~Subcircuits(); + /** + * Handles subcircuit creation when the user selects the subcircuit to be + * created. + * @param id Id of subcircuit; e.g. "sc/10" + */ + static ECSubcircuit* createSubcircuit( int id, CircuitDocument *circuitDocument, bool newItem, const char *newId ); + /** + * Loads a subcircuit into a subcircuit component + */ + static void initECSubcircuit( int subcircuitId, ECSubcircuit *ecSubcircuit ); + /** + * Reads in the config entries and adds the subcircuits found to the + * component selector + */ + static void loadSubcircuits(); + /** + * Saves the given subcircuit to the appdata dir, updates the appropriate + * config entries, and adds the subcircuit to the component selector. + */ + static void addSubcircuit( const QString &name, const QString &subcircuitXml ); + /** + * returns a path to the appdata dir, e.g. genFileName(2) might return + * ~/.kde/share/apps/ktechlab/subcircuit_2.circuit + */ + static QString genFileName( const int nextId ); + /** + * Adds the given entry to the component selector + */ + static void updateComponentSelector( int id, const QString &name ); + +protected slots: + void slotItemRemoved( const QString &id ); + +private: + Subcircuits(); + + friend Subcircuits* subcircuits(); +}; + + +inline Subcircuits* subcircuits() +{ + static Subcircuits *_subcircuits = new Subcircuits(); + return _subcircuits; +} + +#endif diff --git a/src/electronics/switch.cpp b/src/electronics/switch.cpp new file mode 100644 index 0000000..7cbda70 --- /dev/null +++ b/src/electronics/switch.cpp @@ -0,0 +1,221 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "circuitdocument.h" +#include "component.h" +#include "ecnode.h" +#include "pin.h" +#include "resistance.h" +#include "simulator.h" +#include "switch.h" + +#include <kdebug.h> +#include <qtimer.h> + +#include <cmath> +#include <stdlib.h> // for rand +#include <time.h> + +Switch::Switch( Component * parent, Pin * p1, Pin * p2, State state ) +{ + m_bouncePeriod_ms = 5; + m_bBounce = false; + m_bounceStart = 0; + m_pBounceResistance = 0l; + m_pP1 = p1; + m_pP2 = p2; + m_pComponent = parent; + m_pStopBouncingTimer = new QTimer( this ); + connect( m_pStopBouncingTimer, SIGNAL(timeout()), this, SLOT(stopBouncing()) ); + + // Force update + m_state = (state == Open) ? Closed : Open; + setState(state); +} + + +Switch::~ Switch( ) +{ + if (m_pP1) + m_pP1->setSwitchConnected( m_pP2, false ); + + if (m_pP2) + m_pP2->setSwitchConnected( m_pP1, false ); +} + + +void Switch::setState( State state ) +{ + if ( m_state == state ) + return; + + m_state = state; + + if ( m_bBounce ) + startBouncing(); + else + { + // I'm being lazy...calling stopBouncing will connect the stuff + stopBouncing(); + } +} + + +void Switch::setBounce( bool bounce, int msec ) +{ + m_bBounce = bounce; + m_bouncePeriod_ms = msec; +} + + +void Switch::startBouncing() +{ + if ( m_pBounceResistance ) + { + // Already active? + return; + } + + CircuitDocument * cd = m_pComponent->circuitDocument(); + if ( !cd ) + return; + +// kdDebug() << k_funcinfo << endl; + + m_pBounceResistance = m_pComponent->createResistance( m_pP1, m_pP2, 10000 ); + m_bounceStart = Simulator::self()->time(); + Simulator::self()->attachSwitch( this ); +// kdDebug() << "m_bounceStart="<<m_bounceStart<<" m_bouncePeriod_ms="<<m_bouncePeriod_ms<<endl; + + // initialize random generator + srand ( time(NULL) ); + + // Give our bounce resistor an initial value + bounce(); +} + + +void Switch::bounce() +{ + int bounced_ms = (( Simulator::self()->time() - m_bounceStart ) * 1000) / LOGIC_UPDATE_RATE; + if ( bounced_ms >= m_bouncePeriod_ms ) + { + if ( !m_pStopBouncingTimer->isActive() ) + m_pStopBouncingTimer->start( 0, true ); + return; + } + + double g = double(rand())/double(RAND_MAX); + + // 4th power of the conductance seems to give a nice distribution + g = g * g * g * g; + + m_pBounceResistance->setConductance( g ); +} + + +void Switch::stopBouncing() +{ + Simulator::self()->detachSwitch( this ); + m_pComponent->removeElement( m_pBounceResistance, true ); + m_pBounceResistance = 0l; + + bool connected = (m_state == Closed); + + if ( m_pP1 && m_pP2 ) + { + m_pP1->setSwitchConnected( m_pP2, connected ); + m_pP2->setSwitchConnected( m_pP1, connected ); + } + + if ( CircuitDocument * cd = m_pComponent->circuitDocument() ) + cd->requestAssignCircuits(); +} + + +bool Switch::calculateCurrent() +{ + if ( !m_pP1 || !m_pP2 ) + return false; + + if ( state() == Open ) + { + m_pP1->setSwitchCurrentKnown( this ); + m_pP2->setSwitchCurrentKnown( this ); + return true; + } + + Pin * pins[2] = { m_pP1, m_pP2 }; + + double current = 0.0; + bool currentKnown = false; + + int pol; + for ( unsigned i = 0; i < 2; ++i ) + { + pol = (i == 0) ? 1 : -1; + + const WireList inputs = pins[i]->inputWireList(); + const WireList outputs = pins[i]->outputWireList(); + + currentKnown = true; + current = 0.0; + + WireList::const_iterator end = inputs.end(); + for ( WireList::const_iterator it = inputs.begin(); it != end; ++it ) + { + if ( !(*it) ) + continue; + + if ( !(*it)->currentIsKnown() ) + { + currentKnown = false; + break; + } + + current += (*it)->current(); + } + + if ( !currentKnown ) + continue; + + end = outputs.end(); + for ( WireList::const_iterator it = outputs.begin(); it != end; ++it ) + { + if ( !(*it) ) + continue; + + if ( !(*it)->currentIsKnown() ) + { + currentKnown = false; + break; + } + + current -= (*it)->current(); + } + + if ( currentKnown ) + break; + } + + if ( !currentKnown ) + return false; + + m_pP1->setSwitchCurrentKnown( this ); + m_pP2->setSwitchCurrentKnown( this ); + m_pP1->mergeCurrent( -current * pol ); + m_pP2->mergeCurrent( current * pol ); + + return true; +} + +#include "switch.moc" + + diff --git a/src/electronics/switch.h b/src/electronics/switch.h new file mode 100644 index 0000000..40247c9 --- /dev/null +++ b/src/electronics/switch.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef SWITCH_H +#define SWITCH_H + +#include <qguardedptr.h> +#include <qobject.h> + +class CircuitDocument; +class Component; +class Pin; +class Resistance; +class QTimer; + +/** +@author David Saxton +*/ +class Switch : public QObject +{ + Q_OBJECT + + public: + enum State + { + Open, + Closed, + }; + + Switch( Component * parent, Pin * p1, Pin * p2, State state ); + ~Switch(); + /** + * If bouncing has been set to true, then the state will not switch + * immediately to that given. + */ + void setState( State state ); + State state() const { return m_state; } + /** + * Tell the switch whether to bounce or not, for the given duration, + * when the state is changed. + */ + void setBounce( bool bounce, int msec = 5 ); + /** + * Tell the switch to continue bouncing (updates the resistance value). + * Called from the simulator. + */ + void bounce(); + /** + * Attempts to calculate the current that is flowing through the switch. + * (If all the connectors at one of the ends know their currents, then + * this switch will give the current to the pins at either end). + * @return whether it was successful. + * @see CircuitDocument::calculateConnectorCurrents + */ + bool calculateCurrent(); + + protected slots: + /** + * Called from a QTimer timeout - our bouncing period has come to an + * end. This will then fully disconnect or connect the pins depending + * on the current state. + */ + void stopBouncing(); + + protected: + void startBouncing(); + + bool m_bBounce; + int m_bouncePeriod_ms; + unsigned long long m_bounceStart; // Simulator time that bouncing started + Resistance * m_pBounceResistance; + State m_state; + Component * m_pComponent; + QGuardedPtr<Pin> m_pP1; + QGuardedPtr<Pin> m_pP2; + QTimer * m_pStopBouncingTimer; +}; + +#endif diff --git a/src/electronics/wire.cpp b/src/electronics/wire.cpp new file mode 100644 index 0000000..619295d --- /dev/null +++ b/src/electronics/wire.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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 "pin.h" +#include "wire.h" +#include <assert.h> + +Wire::Wire( Pin * startPin, Pin * endPin ) +{ + assert(startPin); + assert(endPin); + + m_pStartPin = startPin; + m_pEndPin = endPin; + m_current = 0.; + m_bCurrentIsKnown = false; + + m_pStartPin->addOutputWire(this); + m_pEndPin->addInputWire(this); +} + + +Wire::~Wire() +{ +} + + +bool Wire::calculateCurrent() +{ + if ( m_pStartPin->currentIsKnown() && m_pStartPin->numWires() < 2 ) + { + m_current = m_pStartPin->current(); + m_bCurrentIsKnown = true; + return true; + } + + if ( m_pEndPin->currentIsKnown() && m_pEndPin->numWires() < 2 ) + { + m_current = -m_pEndPin->current(); + m_bCurrentIsKnown = true; + return true; + } + + if ( m_pStartPin->currentIsKnown() ) + { + double i = m_pStartPin->current(); + bool ok = true; + const WireList outlist = m_pStartPin->outputWireList(); + WireList::const_iterator end = outlist.end(); + for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i -= (*it)->current(); + + else + ok = false; + } + } + const WireList inlist = m_pStartPin->inputWireList(); + end = inlist.end(); + for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i += (*it)->current(); + + else + ok = false; + } + } + + if (ok) + { + m_current = i; + m_bCurrentIsKnown = true; + return true; + } + } + + if ( m_pEndPin->currentIsKnown() ) + { + double i = -m_pEndPin->current(); + bool ok = true; + const WireList outlist = m_pEndPin->outputWireList(); + WireList::const_iterator end = outlist.end(); + for ( WireList::const_iterator it = outlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i += (*it)->current(); + + else + ok = false; + } + } + const WireList inlist = m_pEndPin->inputWireList(); + end = inlist.end(); + for ( WireList::const_iterator it = inlist.begin(); it != end && ok; ++it ) + { + if ( *it && (Wire*)*it != this ) + { + if ( (*it)->currentIsKnown() ) + i -= (*it)->current(); + + else + ok = false; + } + } + + if (ok) + { + m_current = i; + m_bCurrentIsKnown = true; + return true; + } + } + + m_bCurrentIsKnown = false; + return false; +} + + +double Wire::voltage() const +{ + return ( m_pStartPin->voltage() + m_pEndPin->voltage() )/2; +} + + +void Wire::setCurrentKnown( bool known ) +{ + m_bCurrentIsKnown = known; + if (!known) + m_current = 0.; +} + diff --git a/src/electronics/wire.h b/src/electronics/wire.h new file mode 100644 index 0000000..368004c --- /dev/null +++ b/src/electronics/wire.h @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2005 by David Saxton * + * david@bluehaze.org * + * * + * 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. * + ***************************************************************************/ + +#ifndef WIRE_H +#define WIRE_H + +#include <qguardedptr.h> +#include <qobject.h> + +class Pin; + +/** +@author David Saxton +*/ +class Wire : public QObject +{ + public: + Wire( Pin * startPin, Pin * endPin ); + ~Wire(); + + /** + * Attempts to calculate the current that is flowing through + * the connector. Returns true if successfuly, otherwise returns false + */ + bool calculateCurrent(); + /** + * Returns true if the current flowing through the connector is known + */ + bool currentIsKnown() const { return m_bCurrentIsKnown; } + /** + * Set whether the actual current flowing into this node is known (in some + * cases - such as this node being ground - it is not known, and so the + * value returned by current() cannot be relied on. + */ + void setCurrentKnown( bool known ); + /** + * Returns the current flowing through the connector. + * This only applies for electronic connectors + */ + double current() const { return m_current; } + /** + * Returns the voltage at the connector. This is an average of the + * voltages at either end. + */ + double voltage() const; + + Pin * startPin() const { return m_pStartPin; } + Pin * endPin() const { return m_pEndPin; } + + protected: + double m_current; + bool m_bCurrentIsKnown; + QGuardedPtr<Pin> m_pStartPin; + QGuardedPtr<Pin> m_pEndPin; +}; + +#endif |