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