summaryrefslogtreecommitdiffstats
path: root/src/electronics
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 01:49:02 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-24 01:49:02 +0000
commit5de3dd4762ca33a0f92e79ffa4fe2ff67069d531 (patch)
treebad482b7afa4cdf47422d60a5dd2c61c7e333b09 /src/electronics
downloadktechlab-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')
-rw-r--r--src/electronics/Makefile.am18
-rw-r--r--src/electronics/component.cpp1017
-rw-r--r--src/electronics/component.h362
-rw-r--r--src/electronics/components/Makefile.am33
-rw-r--r--src/electronics/components/addac.cpp281
-rw-r--r--src/electronics/components/addac.h93
-rw-r--r--src/electronics/components/bidirled.cpp157
-rw-r--r--src/electronics/components/bidirled.h45
-rw-r--r--src/electronics/components/binarycounter.cpp186
-rw-r--r--src/electronics/components/binarycounter.h63
-rw-r--r--src/electronics/components/bussplitter.cpp133
-rw-r--r--src/electronics/components/bussplitter.h45
-rw-r--r--src/electronics/components/demultiplexer.cpp175
-rw-r--r--src/electronics/components/demultiplexer.h46
-rw-r--r--src/electronics/components/dependentsource.cpp314
-rw-r--r--src/electronics/components/dependentsource.h112
-rw-r--r--src/electronics/components/discretelogic.cpp308
-rw-r--r--src/electronics/components/discretelogic.h110
-rw-r--r--src/electronics/components/ec555.cpp172
-rw-r--r--src/electronics/components/ec555.h56
-rw-r--r--src/electronics/components/ecbcdto7segment.cpp158
-rw-r--r--src/electronics/components/ecbcdto7segment.h41
-rw-r--r--src/electronics/components/ecbjt.cpp153
-rw-r--r--src/electronics/components/ecbjt.h42
-rw-r--r--src/electronics/components/eccapacitor.cpp92
-rw-r--r--src/electronics/components/eccapacitor.h40
-rw-r--r--src/electronics/components/ecclockinput.cpp185
-rw-r--r--src/electronics/components/ecclockinput.h56
-rw-r--r--src/electronics/components/eccurrentsignal.cpp92
-rw-r--r--src/electronics/components/eccurrentsignal.h36
-rw-r--r--src/electronics/components/eccurrentsource.cpp94
-rw-r--r--src/electronics/components/eccurrentsource.h36
-rw-r--r--src/electronics/components/ecdiode.cpp120
-rw-r--r--src/electronics/components/ecdiode.h35
-rw-r--r--src/electronics/components/ecfixedvoltage.cpp77
-rw-r--r--src/electronics/components/ecfixedvoltage.h35
-rw-r--r--src/electronics/components/ecground.cpp66
-rw-r--r--src/electronics/components/ecground.h33
-rw-r--r--src/electronics/components/eckeypad.cpp199
-rw-r--r--src/electronics/components/eckeypad.h42
-rw-r--r--src/electronics/components/ecled.cpp134
-rw-r--r--src/electronics/components/ecled.h48
-rw-r--r--src/electronics/components/ecopamp.cpp85
-rw-r--r--src/electronics/components/ecopamp.h35
-rw-r--r--src/electronics/components/ecpotentiometer.cpp119
-rw-r--r--src/electronics/components/ecpotentiometer.h43
-rw-r--r--src/electronics/components/ecresistor.cpp76
-rw-r--r--src/electronics/components/ecresistor.h36
-rw-r--r--src/electronics/components/ecsevensegment.cpp210
-rw-r--r--src/electronics/components/ecsevensegment.h50
-rw-r--r--src/electronics/components/ecsignallamp.cpp86
-rw-r--r--src/electronics/components/ecsignallamp.h40
-rw-r--r--src/electronics/components/ecsubcircuit.cpp130
-rw-r--r--src/electronics/components/ecsubcircuit.h61
-rw-r--r--src/electronics/components/ecvoltagesignal.cpp95
-rw-r--r--src/electronics/components/ecvoltagesignal.h36
-rw-r--r--src/electronics/components/ecvoltagesource.cpp93
-rw-r--r--src/electronics/components/ecvoltagesource.h37
-rw-r--r--src/electronics/components/externalconnection.cpp78
-rw-r--r--src/electronics/components/externalconnection.h35
-rw-r--r--src/electronics/components/flipflop.cpp347
-rw-r--r--src/electronics/components/flipflop.h107
-rw-r--r--src/electronics/components/fulladder.cpp91
-rw-r--r--src/electronics/components/fulladder.h37
-rw-r--r--src/electronics/components/inductor.cpp84
-rw-r--r--src/electronics/components/inductor.h35
-rw-r--r--src/electronics/components/magnitudecomparator.cpp206
-rw-r--r--src/electronics/components/magnitudecomparator.h50
-rw-r--r--src/electronics/components/matrixdisplay.cpp291
-rw-r--r--src/electronics/components/matrixdisplay.h60
-rw-r--r--src/electronics/components/matrixdisplaydriver.cpp380
-rw-r--r--src/electronics/components/matrixdisplaydriver.h42
-rw-r--r--src/electronics/components/meter.cpp265
-rw-r--r--src/electronics/components/meter.h103
-rw-r--r--src/electronics/components/multiinputgate.cpp530
-rw-r--r--src/electronics/components/multiinputgate.h160
-rw-r--r--src/electronics/components/multiplexer.cpp176
-rw-r--r--src/electronics/components/multiplexer.h46
-rw-r--r--src/electronics/components/parallelportcomponent.cpp241
-rw-r--r--src/electronics/components/parallelportcomponent.h49
-rw-r--r--src/electronics/components/piccomponent.cpp437
-rw-r--r--src/electronics/components/piccomponent.h97
-rw-r--r--src/electronics/components/piccomponentpin.cpp172
-rw-r--r--src/electronics/components/piccomponentpin.h68
-rw-r--r--src/electronics/components/probe.cpp291
-rw-r--r--src/electronics/components/probe.h113
-rw-r--r--src/electronics/components/pushswitch.cpp204
-rw-r--r--src/electronics/components/pushswitch.h62
-rw-r--r--src/electronics/components/ram.cpp232
-rw-r--r--src/electronics/components/ram.h51
-rw-r--r--src/electronics/components/resistordip.cpp132
-rw-r--r--src/electronics/components/resistordip.h43
-rw-r--r--src/electronics/components/rotoswitch.cpp317
-rw-r--r--src/electronics/components/rotoswitch.h70
-rw-r--r--src/electronics/components/serialportcomponent.cpp242
-rw-r--r--src/electronics/components/serialportcomponent.h61
-rw-r--r--src/electronics/components/toggleswitch.cpp407
-rw-r--r--src/electronics/components/toggleswitch.h116
-rw-r--r--src/electronics/ecnode.cpp239
-rw-r--r--src/electronics/ecnode.h74
-rw-r--r--src/electronics/gpsimprocessor.cpp880
-rw-r--r--src/electronics/gpsimprocessor.h393
-rw-r--r--src/electronics/pin.cpp175
-rw-r--r--src/electronics/pin.h211
-rw-r--r--src/electronics/port.cpp514
-rw-r--r--src/electronics/port.h248
-rw-r--r--src/electronics/simulation/Makefile.am11
-rw-r--r--src/electronics/simulation/bjt.cpp257
-rw-r--r--src/electronics/simulation/bjt.h75
-rw-r--r--src/electronics/simulation/capacitance.cpp117
-rw-r--r--src/electronics/simulation/capacitance.h55
-rw-r--r--src/electronics/simulation/cccs.cpp89
-rw-r--r--src/electronics/simulation/cccs.h41
-rw-r--r--src/electronics/simulation/ccvs.cpp91
-rw-r--r--src/electronics/simulation/ccvs.h41
-rw-r--r--src/electronics/simulation/circuit.cpp550
-rw-r--r--src/electronics/simulation/circuit.h137
-rw-r--r--src/electronics/simulation/currentsignal.cpp67
-rw-r--r--src/electronics/simulation/currentsignal.h43
-rw-r--r--src/electronics/simulation/currentsource.cpp64
-rw-r--r--src/electronics/simulation/currentsource.h39
-rw-r--r--src/electronics/simulation/diode.cpp198
-rw-r--r--src/electronics/simulation/diode.h73
-rw-r--r--src/electronics/simulation/element.cpp193
-rw-r--r--src/electronics/simulation/element.h255
-rw-r--r--src/electronics/simulation/elementset.cpp253
-rw-r--r--src/electronics/simulation/elementset.h131
-rw-r--r--src/electronics/simulation/elementsignal.cpp64
-rw-r--r--src/electronics/simulation/elementsignal.h45
-rw-r--r--src/electronics/simulation/inductance.cpp126
-rw-r--r--src/electronics/simulation/inductance.h55
-rw-r--r--src/electronics/simulation/logic.cpp319
-rw-r--r--src/electronics/simulation/logic.h211
-rw-r--r--src/electronics/simulation/matrix.cpp546
-rw-r--r--src/electronics/simulation/matrix.h248
-rw-r--r--src/electronics/simulation/nonlinear.cpp97
-rw-r--r--src/electronics/simulation/nonlinear.h48
-rw-r--r--src/electronics/simulation/opamp.cpp77
-rw-r--r--src/electronics/simulation/opamp.h36
-rw-r--r--src/electronics/simulation/reactive.cpp36
-rw-r--r--src/electronics/simulation/reactive.h42
-rw-r--r--src/electronics/simulation/resistance.cpp95
-rw-r--r--src/electronics/simulation/resistance.h43
-rw-r--r--src/electronics/simulation/vccs.cpp93
-rw-r--r--src/electronics/simulation/vccs.h40
-rw-r--r--src/electronics/simulation/vcvs.cpp93
-rw-r--r--src/electronics/simulation/vcvs.h40
-rw-r--r--src/electronics/simulation/vec.cpp166
-rw-r--r--src/electronics/simulation/vec.h109
-rw-r--r--src/electronics/simulation/voltagepoint.cpp73
-rw-r--r--src/electronics/simulation/voltagepoint.h39
-rw-r--r--src/electronics/simulation/voltagesignal.cpp79
-rw-r--r--src/electronics/simulation/voltagesignal.h41
-rw-r--r--src/electronics/simulation/voltagesource.cpp77
-rw-r--r--src/electronics/simulation/voltagesource.h38
-rw-r--r--src/electronics/subcircuits.cpp175
-rw-r--r--src/electronics/subcircuits.h76
-rw-r--r--src/electronics/switch.cpp221
-rw-r--r--src/electronics/switch.h86
-rw-r--r--src/electronics/wire.cpp146
-rw-r--r--src/electronics/wire.h64
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>&gt;</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 &gt; B</SUB>, I1 for I<SUB>A &lt; B</SUB>, and I2 for I<SUB>A = B</SUB> and 3 outputs: O0 for O<SUB>A &gt; B</SUB>, O1 for O<SUB>A &lt; 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 &gt; B</li>"
+ "<li>I: A &lt; B</li>"
+ "<li>I: A = B</li></ul>"
+ "and 3 outputs:"
+ "<ul><li>O: A &gt; B</li>"
+ "<li>O: A &lt; 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