File indexing completed on 2024-06-16 11:34:24

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"