File indexing completed on 2025-04-13 11:19:28
0001 /*************************************************************************** 0002 * Copyright (C) 2005 by David Saxton * 0003 * david@bluehaze.org * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 ***************************************************************************/ 0010 0011 #include <cmath> 0012 #include <stdlib.h> // for rand 0013 #include <time.h> 0014 0015 #include <QTimer> 0016 0017 #include "circuitdocument.h" 0018 #include "component.h" 0019 #include "ecnode.h" 0020 #include "pin.h" 0021 #include "resistance.h" 0022 #include "simulator.h" 0023 #include "switch.h" 0024 0025 #include <ktechlab_debug.h> 0026 0027 Switch::Switch(Component *parent, Pin *p1, Pin *p2, State state) 0028 { 0029 m_bouncePeriod_ms = 5; 0030 m_bBounce = false; 0031 m_bounceStart = 0; 0032 m_pBounceResistance = nullptr; 0033 m_pP1 = p1; 0034 m_pP2 = p2; 0035 m_pComponent = parent; 0036 m_pStopBouncingTimer = new QTimer(this); 0037 connect(m_pStopBouncingTimer, SIGNAL(timeout()), this, SLOT(stopBouncing())); 0038 0039 // Force update 0040 m_state = (state == Open) ? Closed : Open; 0041 setState(state); 0042 } 0043 0044 Switch::~Switch() 0045 { 0046 if (m_pP1) 0047 m_pP1->setSwitchConnected(m_pP2, false); 0048 if (m_pP2) 0049 m_pP2->setSwitchConnected(m_pP1, false); 0050 } 0051 0052 void Switch::setState(State state) 0053 { 0054 if (m_state == state) 0055 return; 0056 0057 m_state = state; 0058 0059 if (m_bBounce) 0060 startBouncing(); 0061 else { 0062 // I'm being lazy...calling stopBouncing will connect the stuff 0063 stopBouncing(); 0064 } 0065 } 0066 0067 void Switch::setBounce(bool bounce, int msec) 0068 { 0069 m_bBounce = bounce; 0070 m_bouncePeriod_ms = msec; 0071 } 0072 0073 void Switch::startBouncing() 0074 { 0075 if (m_pBounceResistance) { 0076 // Already active? 0077 return; 0078 } 0079 0080 if (!m_pComponent->circuitDocument()) 0081 return; 0082 0083 // qCDebug(KTL_LOG); 0084 0085 m_pBounceResistance = m_pComponent->createResistance(m_pP1, m_pP2, 10000); 0086 m_bounceStart = Simulator::self()->time(); 0087 0088 // FIXME: I broke the bounce feature when I cleaned this out of the simulator, 0089 // should probably be put into circuit document or some other solution which doesn't 0090 // contaminate that many other classes. 0091 0092 // Simulator::self()->attachSwitch( this ); 0093 // qCDebug(KTL_LOG) << "m_bounceStart="<<m_bounceStart<<" m_bouncePeriod_ms="<<m_bouncePeriod_ms; 0094 0095 // initialize random generator 0096 srand(time(nullptr)); 0097 0098 // Give our bounce resistor an initial value 0099 bounce(); 0100 } 0101 0102 void Switch::bounce() 0103 { 0104 int bounced_ms = ((Simulator::self()->time() - m_bounceStart) * 1000) / LOGIC_UPDATE_RATE; 0105 0106 if (bounced_ms >= m_bouncePeriod_ms) { 0107 if (!m_pStopBouncingTimer->isActive()) { 0108 m_pStopBouncingTimer->setSingleShot(true); 0109 m_pStopBouncingTimer->start(0 /*, true */); 0110 } 0111 0112 return; 0113 } 0114 0115 double g = double(rand()) / double(RAND_MAX); 0116 0117 // 4th power of the conductance seems to give a nice distribution 0118 g = pow(g, 4); 0119 m_pBounceResistance->setConductance(g); 0120 } 0121 0122 void Switch::stopBouncing() 0123 { 0124 // Simulator::self()->detachSwitch( this ); 0125 m_pComponent->removeElement(m_pBounceResistance, true); 0126 m_pBounceResistance = nullptr; 0127 0128 bool connected = (m_state == Closed); 0129 0130 if (m_pP1 && m_pP2) { 0131 m_pP1->setSwitchConnected(m_pP2, connected); 0132 m_pP2->setSwitchConnected(m_pP1, connected); 0133 } 0134 0135 if (CircuitDocument *cd = m_pComponent->circuitDocument()) 0136 cd->requestAssignCircuits(); 0137 } 0138 0139 bool Switch::calculateCurrent() 0140 { 0141 if (!m_pP1 || !m_pP2) 0142 return false; 0143 0144 if (state() == Open) { 0145 m_pP1->setSwitchCurrentKnown(this); 0146 m_pP2->setSwitchCurrentKnown(this); 0147 return true; 0148 } 0149 0150 Pin *pins[2] = {m_pP1, m_pP2}; 0151 0152 double current = 0.0; 0153 bool currentKnown = false; 0154 int pol; 0155 0156 for (unsigned i = 0; i < 2; ++i) { 0157 pol = (i == 0) ? 1 : -1; 0158 0159 const WireList inputs = pins[i]->inputWireList(); 0160 const WireList outputs = pins[i]->outputWireList(); 0161 0162 currentKnown = true; 0163 current = 0.0; 0164 0165 WireList::const_iterator end = inputs.end(); 0166 0167 for (WireList::const_iterator it = inputs.begin(); it != end; ++it) { 0168 if (!(*it)) 0169 continue; 0170 0171 if (!(*it)->currentIsKnown()) { 0172 currentKnown = false; 0173 break; 0174 } 0175 0176 current += (*it)->current(); 0177 } 0178 0179 if (!currentKnown) 0180 continue; 0181 0182 end = outputs.end(); 0183 0184 for (WireList::const_iterator it = outputs.begin(); it != end; ++it) { 0185 if (!(*it)) 0186 continue; 0187 0188 if (!(*it)->currentIsKnown()) { 0189 currentKnown = false; 0190 break; 0191 } 0192 0193 current -= (*it)->current(); 0194 } 0195 0196 if (currentKnown) 0197 break; 0198 } 0199 0200 if (!currentKnown) 0201 return false; 0202 0203 m_pP1->setSwitchCurrentKnown(this); 0204 m_pP2->setSwitchCurrentKnown(this); 0205 m_pP1->mergeCurrent(-current * pol); 0206 m_pP2->mergeCurrent(current * pol); 0207 0208 return true; 0209 } 0210 0211 #include "moc_switch.cpp"