File indexing completed on 2024-05-05 05:46:07

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 "meter.h"
0012 #include "canvasitemparts.h"
0013 #include "ecnode.h"
0014 #include "element.h"
0015 #include "libraryitem.h"
0016 #include "pin.h"
0017 #include "simulator.h"
0018 #include "variant.h"
0019 #include "voltagesource.h"
0020 
0021 #include <KLocalizedString>
0022 #include <QPainter>
0023 #include <cmath>
0024 
0025 // BEGIN class Meter
0026 Meter::Meter(ICNDocument *icnDocument, bool newItem, const char *id)
0027     : Component(icnDocument, newItem, id)
0028 {
0029     b_timerStarted = false;
0030     m_timeSinceUpdate = 0.;
0031     m_old_value = 0.;
0032     m_avgValue = 0.;
0033     b_firstRun = true;
0034     m_prevProp = 0.0;
0035     setSize(-16, -16, 32, 32);
0036 
0037     p_displayText = addDisplayText("meter", QRect(-16, 16, 32, 16), displayText());
0038 
0039     createProperty("0-minValue", Variant::Type::Double);
0040     property("0-minValue")->setCaption(i18n("Minimum Value"));
0041     property("0-minValue")->setMinValue(1e-12);
0042     property("0-minValue")->setMaxValue(1e12);
0043     property("0-minValue")->setValue(1e-3);
0044 
0045     createProperty("1-maxValue", Variant::Type::Double);
0046     property("1-maxValue")->setCaption(i18n("Maximum Value"));
0047     property("1-maxValue")->setMinValue(1e-12);
0048     property("1-maxValue")->setMaxValue(1e12);
0049     property("1-maxValue")->setValue(1e3);
0050 }
0051 
0052 Meter::~Meter()
0053 {
0054 }
0055 
0056 void Meter::dataChanged()
0057 {
0058     m_minValue = dataDouble("0-minValue");
0059     m_maxValue = dataDouble("1-maxValue");
0060     setChanged();
0061 }
0062 
0063 void Meter::stepNonLogic()
0064 {
0065     if (b_firstRun) {
0066         p_displayText->setText(displayText());
0067         updateAttachedPositioning();
0068         setChanged();
0069         property("0-minValue")->setUnit(m_unit);
0070         property("1-maxValue")->setUnit(m_unit);
0071         b_firstRun = false;
0072     }
0073 
0074     const double v = meterValue();
0075     if (!b_timerStarted && std::abs(((v - m_old_value) / m_old_value)) > 1e-6) {
0076         b_timerStarted = true;
0077     }
0078 
0079     if (b_timerStarted) {
0080         m_timeSinceUpdate += LINEAR_UPDATE_PERIOD;
0081         m_avgValue += v * LINEAR_UPDATE_PERIOD;
0082         //      setChanged();
0083         if (m_timeSinceUpdate > 0.05) {
0084             if (p_displayText->setText(displayText()))
0085                 updateAttachedPositioning();
0086         }
0087     }
0088 }
0089 
0090 bool Meter::contentChanged() const
0091 {
0092     return (m_prevProp != calcProp(m_old_value));
0093 }
0094 
0095 void Meter::drawShape(QPainter &p)
0096 {
0097     initPainter(p);
0098     p.drawEllipse(int(x()) - 16, int(y()) - 16, width(), width());
0099     p.setPen(QPen(Qt::black, 2));
0100     p.setBrush(Qt::black);
0101 
0102     // The proportion between 0.1mV and 10KV, on a logarithmic scale
0103     m_prevProp = calcProp(m_old_value);
0104     double sin_prop = 10 * std::sin(m_prevProp * 1.571); // 1.571 = pi/2
0105     double cos_prop = 10 * std::cos(m_prevProp * 1.571); // 1.571 = pi/2
0106 
0107     int cx = int(x() - 16 + (width() / 2));
0108     int cy = int(y() - 16 + (height() / 2));
0109     p.drawLine(int(cx - sin_prop), int(cy - cos_prop), int(cx + sin_prop), int(cy + cos_prop));
0110 
0111     QPolygon pa(3);
0112     pa[0] = QPoint(int(cx - sin_prop), int(cy - cos_prop)); // Arrow head
0113     pa[1] = QPoint(int(cx - sin_prop + 8 * std::sin(1.571 * (-0.3 + m_prevProp))), int(cy - cos_prop + 8 * std::cos(1.571 * (-0.3 + m_prevProp))));
0114     pa[2] = QPoint(int(cx - sin_prop + 8 * std::sin(1.571 * (0.3 + m_prevProp))), int(cy - cos_prop + 8 * std::cos(1.571 * (0.3 + m_prevProp))));
0115     p.drawPolygon(pa);
0116 
0117     deinitPainter(p);
0118 }
0119 
0120 double Meter::calcProp(double v) const
0121 {
0122     double abs_value = std::abs(v);
0123 
0124     double prop;
0125     if (abs_value <= m_minValue)
0126         prop = 0.0;
0127     else if (abs_value >= m_maxValue)
0128         prop = 1.0;
0129     else
0130         prop = std::log10(abs_value / m_minValue) / std::log10(m_maxValue / m_minValue);
0131 
0132     if (m_old_value > 0)
0133         prop *= -1;
0134 
0135     return prop;
0136 }
0137 
0138 QString Meter::displayText()
0139 {
0140     double value = m_avgValue / m_timeSinceUpdate;
0141     if (!std::isfinite(value))
0142         value = m_old_value;
0143     if (std::abs((value)) < 1e-9)
0144         value = 0.;
0145     m_old_value = value;
0146     m_avgValue = 0.;
0147     m_timeSinceUpdate = 0.;
0148     b_timerStarted = false;
0149     return QString::number(value / CNItem::getMultiplier(value), 'g', 3) + CNItem::getNumberMag(value) + m_unit;
0150 }
0151 // END class Meter
0152 
0153 // BEGIN class FrequencyMeter
0154 Item *FrequencyMeter::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0155 {
0156     return new FrequencyMeter(static_cast<ICNDocument *>(itemDocument), newItem, id);
0157 }
0158 
0159 LibraryItem *FrequencyMeter::libraryItem()
0160 {
0161     return new LibraryItem(QStringList(QString("ec/frequencymeter")), i18n("Frequency Meter (TODO)"), i18n("Outputs"), "frequencymeter.png", LibraryItem::lit_component, FrequencyMeter::construct);
0162 }
0163 
0164 FrequencyMeter::FrequencyMeter(ICNDocument *icnDocument, bool newItem, const char *id)
0165     : Meter(icnDocument, newItem, id ? id : "frequencymeter")
0166 {
0167     m_name = i18n("Frequency Meter");
0168     m_unit = "Hz";
0169 
0170     m_probeNode = createPin(0, -24, 90, "n1");
0171 }
0172 
0173 FrequencyMeter::~FrequencyMeter()
0174 {
0175 }
0176 
0177 double FrequencyMeter::meterValue()
0178 {
0179     return 0;
0180 }
0181 // END class FrequencyMeter
0182 
0183 // BEGIN class ECAmmeter
0184 Item *ECAmmeter::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0185 {
0186     return new ECAmmeter(static_cast<ICNDocument *>(itemDocument), newItem, id);
0187 }
0188 
0189 LibraryItem *ECAmmeter::libraryItem()
0190 {
0191     QStringList ids;
0192     ids << "ec/ammeter"
0193         << "ec/ammmeter";
0194     return new LibraryItem(ids, i18n("Ammeter"), i18n("Outputs"), "ammeter.png", LibraryItem::lit_component, ECAmmeter::construct);
0195 }
0196 
0197 ECAmmeter::ECAmmeter(ICNDocument *icnDocument, bool newItem, const char *id)
0198     : Meter(icnDocument, newItem, id ? id : "ammeter")
0199 {
0200     m_name = i18n("Ammeter");
0201     setSize(-16, -16, 32, 32);
0202     m_unit = "A";
0203 
0204     init1PinLeft(0);
0205     init1PinRight(0);
0206 
0207     m_voltageSource = createVoltageSource(m_pNNode[0], m_pPNode[0], 0.);
0208 }
0209 
0210 ECAmmeter::~ECAmmeter()
0211 {
0212 }
0213 
0214 double ECAmmeter::meterValue()
0215 {
0216     return -m_voltageSource->cbranchCurrent(0);
0217 }
0218 // END class ECAmmeter
0219 
0220 // BEGIN class ECVoltmeter
0221 Item *ECVoltMeter::construct(ItemDocument *itemDocument, bool newItem, const char *id)
0222 {
0223     return new ECVoltMeter(static_cast<ICNDocument *>(itemDocument), newItem, id);
0224 }
0225 
0226 LibraryItem *ECVoltMeter::libraryItem()
0227 {
0228     return new LibraryItem(QStringList(QString("ec/voltmeter")), i18n("Voltmeter"), i18n("Outputs"), "voltmeter.png", LibraryItem::lit_component, ECVoltMeter::construct);
0229 }
0230 
0231 ECVoltMeter::ECVoltMeter(ICNDocument *icnDocument, bool newItem, const char *id)
0232     : Meter(icnDocument, newItem, id ? id : "voltmeter")
0233 {
0234     m_name = i18n("Voltmeter");
0235     m_unit = "V";
0236 
0237     init1PinLeft(0);
0238     init1PinRight(0);
0239 }
0240 
0241 ECVoltMeter::~ECVoltMeter()
0242 {
0243 }
0244 
0245 double ECVoltMeter::meterValue()
0246 {
0247     return m_pNNode[0]->pin()->voltage() - m_pPNode[0]->pin()->voltage();
0248 }
0249 // END class ECVoltMeter