SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
levelmeter.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  * Copyright (C) 2016 Edouard Griffiths, F4EXB
3  * Modifications made to:
4  * - use the widget horizontally
5  * - differentiate each area with a different color
6  * - allow overload by 25% with indication of 100% threshold and overload
7  * - make it generic to fit many cases: VU, signal strength ...
8 **
9 ** Copyright (C) 2015 The Qt Company Ltd.
10 ** Contact: http://www.qt.io/licensing/
11 **
12 ** This file is part of the examples of the Qt Toolkit.
13 **
14 ** $QT_BEGIN_LICENSE:BSD$
15 ** You may use this file under the terms of the BSD license as follows:
16 **
17 ** "Redistribution and use in source and binary forms, with or without
18 ** modification, are permitted provided that the following conditions are
19 ** met:
20 ** * Redistributions of source code must retain the above copyright
21 ** notice, this list of conditions and the following disclaimer.
22 ** * Redistributions in binary form must reproduce the above copyright
23 ** notice, this list of conditions and the following disclaimer in
24 ** the documentation and/or other materials provided with the
25 ** distribution.
26 ** * Neither the name of The Qt Company Ltd nor the names of its
27 ** contributors may be used to endorse or promote products derived
28 ** from this software without specific prior written permission.
29 **
30 **
31 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
42 **
43 ** $QT_END_LICENSE$
44 **
45 ****************************************************************************/
46 
47 #include "gui/levelmeter.h"
48 
49 #include <math.h>
50 
51 #include <QPainter>
52 #include <QTimer>
53 #include <QDebug>
54 
55 // Constants
56 const int RedrawInterval = 100; // ms
57 const qreal PeakDecayRate = 0.001;
58 const int PeakHoldLevelDuration = 2000; // ms
59 
60 LevelMeter::LevelMeter(QWidget *parent)
61  : QWidget(parent)
62  , m_avgLevel(0.0)
63  , m_peakLevel(0.0)
64  , m_decayedPeakLevel(0.0)
65  , m_peakDecayRate(PeakDecayRate)
66  , m_peakHoldLevel(0.0)
67  , m_avgSmoothing(256)
68  , m_redrawTimer(new QTimer(this))
69  , m_avgColor(0xff, 0x8b, 0x00, 128) // color mapper foreground
70  , m_peakColor(Qt::red) // just red 100% opaque
71  , m_decayedPeakColor(0x97, 0x54, 0x00, 128) // color mapper 59%
72  , m_backgroundPixmap(0)
73 {
74  setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
75  setMinimumWidth(30);
76 
77  connect(m_redrawTimer, SIGNAL(timeout()), this, SLOT(redrawTimerExpired()));
79 }
80 
82 {
83  if (m_backgroundPixmap) {
84  delete m_backgroundPixmap;
85  }
86 }
87 
89 {
90  m_avgLevel = 0.0;
91  m_peakLevel = 0.0;
92  update();
93 }
94 
95 void LevelMeter::levelChanged(qreal avgLevel, qreal peakLevel, int numSamples)
96 {
97  // Smooth the RMS signal
98  const qreal smooth = pow(qreal(0.9), static_cast<qreal>(numSamples) / m_avgSmoothing);
99  m_avgLevel = (m_avgLevel * smooth) + (avgLevel * (1.0 - smooth));
100 
101  if (peakLevel > m_decayedPeakLevel) {
102  m_peakLevel = peakLevel;
103  m_decayedPeakLevel = peakLevel;
104  m_peakLevelChanged.start();
105  }
106 
107  if (peakLevel > m_peakHoldLevel) {
108  m_peakHoldLevel = peakLevel;
109  m_peakHoldLevelChanged.start();
110  }
111 
112  update();
113 }
114 
116 {
117  // Decay the peak signal
118  const int elapsedMs = m_peakLevelChanged.elapsed();
119  const qreal decayAmount = m_peakDecayRate * elapsedMs;
120  if (decayAmount < m_peakLevel)
121  m_decayedPeakLevel = m_peakLevel - decayAmount;
122  else
123  m_decayedPeakLevel = 0.0;
124 
125  // Check whether to clear the peak hold level
127  m_peakHoldLevel = 0.0;
128 
129  update();
130 }
131 
132 void LevelMeter::paintEvent(QPaintEvent *event)
133 {
134  Q_UNUSED(event)
135 
136  QPainter painter(this);
137  render(&painter);
138 }
139 
140 void LevelMeter::resizeEvent(QResizeEvent * event)
141 {
142  Q_UNUSED(event)
143 
144  resized();
145 }
146 
148 {
149  m_avgSmoothing = smoothingFactor < 1 ? 1 : smoothingFactor > 256 ? 256 : smoothingFactor;
150 }
151 
152 // ====================================================================
153 
154 LevelMeterVU::LevelMeterVU(QWidget *parent) :
155  LevelMeter(parent)
156 {
157  m_scaleEngine.setFont(font());
158  m_scaleEngine.setOrientation(Qt::Horizontal);
160 
161  resized();
162 }
163 
165 {
166 }
167 
169 {
170  if (m_backgroundPixmap)
171  {
172  delete m_backgroundPixmap;
173  }
174 
175  m_backgroundPixmap = new QPixmap(rect().width(), rect().height());
176  m_backgroundPixmap->fill(QColor(42, 42, 42, 255));
177 
178  QPainter painter(m_backgroundPixmap);
179  QRect barTop = m_backgroundPixmap->rect();
180  barTop.setBottom(0.5 * rect().height() - 2);
181  barTop.setTop(2);
182  barTop.setLeft(0.75* rect().width());
183  painter.fillRect(barTop, Qt::red);
184 
185  QRect bar = m_backgroundPixmap->rect();
186 
187  // 100% full height white line
188  painter.setPen(Qt::white);
189  painter.setFont(font());
190 // painter.drawLine(0.75*bar.width(), 0, 0.75*bar.width(), bar.height());
191 
192  m_scaleEngine.setSize(0.75*bar.width());
193  const ScaleEngine::TickList& scaleTickList = m_scaleEngine.getTickList();
194 
195 
196  for (int i = 0; i < scaleTickList.count(); i++)
197  {
198 // qDebug() << "LevelMeterVU::resized: tick #" << i
199 // << " major: " << scaleTickList[i].major
200 // << " pos: " << scaleTickList[i].pos
201 // << " text: " << scaleTickList[i].text
202 // << " textPos: " << scaleTickList[i].textPos
203 // << " textSize: " << scaleTickList[i].textSize;
204  const ScaleEngine::Tick tick = scaleTickList[i];
205 
206  if(tick.major)
207  {
208  if ((tick.textSize > 0) && (tick.textPos > 0))
209  {
210  painter.drawText(QPointF(tick.textPos - (tick.textSize/2) - 4, bar.height()/2 - 3), tick.text);
211  }
212 
213  painter.drawLine(shiftx(tick.pos, bar.width()), 0, shiftx(scaleTickList[i].pos, bar.width()), bar.height());
214  }
215  else
216  {
217  painter.drawLine(tick.pos, bar.height()/2 - 2, scaleTickList[i].pos, bar.height()/2);
218  }
219  }
220 }
221 
222 void LevelMeterVU::render(QPainter *painter)
223 {
224  painter->drawPixmap(rect(), *m_backgroundPixmap);
225 
226  QRect bar = rect();
227 
228  // Bottom moving gauge
229 
230  bar.setTop(0.5 * rect().height() + 2);
231  bar.setBottom(rect().height() - 1);
232 
233  bar.setRight(rect().right() - (1.0 - 0.75*m_peakHoldLevel) * rect().width());
234  bar.setLeft(bar.right() - 2);
235  painter->fillRect(bar, m_peakColor);
236 
237  bar.setBottom(0.75*rect().height());
238 
239  bar.setRight(rect().right() - (1.0 - 0.75*m_avgLevel) * rect().width());
240  bar.setLeft(1);
241  painter->fillRect(bar, m_avgColor);
242 
243  bar.setTop(0.75 * rect().height() + 1);
244  bar.setBottom(rect().height() - 1);
245 
246  bar.setRight(rect().right() - (1.0 - 0.75*m_decayedPeakLevel) * rect().width());
247  painter->fillRect(bar, m_decayedPeakColor);
248 
249  // borders
250  painter->setPen(QColor(0,0,0));
251  painter->drawLine(0, 0, rect().width() - 2, 0);
252  painter->drawLine(0, rect().height() - 1, 0, 0);
253  painter->setPen(QColor(80,80,80));
254  painter->drawLine(1, rect().height() - 1, rect().width() - 1, rect().height() - 1);
255  painter->drawLine(rect().width() - 1, rect().height() - 1, rect().width() - 1, 0);
256 }
257 
258 // ====================================================================
259 
260 const QColor LevelMeterSignalDB::m_avgColor[4] = {
261  QColor(0xff, 0x8b, 0x00, 128),
262  QColor(0x8c, 0xff, 0x00, 128),
263  QColor(0x8c, 0xff, 0x00, 128),
264  QColor(0x8c, 0xbf, 0xff, 128),
265 };
266 
267 const QColor LevelMeterSignalDB::m_decayedPeakColor[4] = {
268  QColor(0x97, 0x54, 0x00, 128),
269  QColor(0x53, 0x96, 0x00, 128),
270  QColor(0x00, 0x96, 0x53, 128),
271  QColor(0x00, 0x94, 0x94, 128),
272 };
273 
274 const QColor LevelMeterSignalDB::m_peakColor[4] = {
275  Qt::red,
276  Qt::green,
277  Qt::green,
278  Qt::cyan
279 };
280 
282  LevelMeter(parent),
283  m_colorTheme(ColorGold)
284 {
285  m_scaleEngine.setFont(font());
286  m_scaleEngine.setOrientation(Qt::Horizontal);
288 
289  resized();
290 }
291 
293 {
294 }
295 
297 {
299 }
300 
302 {
303  if (m_backgroundPixmap)
304  {
305  delete m_backgroundPixmap;
306  }
307 
308  m_backgroundPixmap = new QPixmap(rect().width(), rect().height());
309  m_backgroundPixmap->fill(QColor(42, 42, 42, 255));
310 
311  QPainter painter(m_backgroundPixmap);
312  QRect bar = m_backgroundPixmap->rect();
313 
314  // 100% full height white line
315  painter.setPen(Qt::white);
316  painter.setFont(font());
317 
318  m_scaleEngine.setSize(bar.width());
319  const ScaleEngine::TickList& scaleTickList = m_scaleEngine.getTickList();
320 
321 
322  for (int i = 0; i < scaleTickList.count(); i++)
323  {
324 // qDebug() << "LevelMeterVU::resized: tick #" << i
325 // << " major: " << scaleTickList[i].major
326 // << " pos: " << scaleTickList[i].pos
327 // << " text: " << scaleTickList[i].text
328 // << " textPos: " << scaleTickList[i].textPos
329 // << " textSize: " << scaleTickList[i].textSize;
330  const ScaleEngine::Tick tick = scaleTickList[i];
331 
332  if(tick.major)
333  {
334  if ((tick.textSize > 0) && (tick.textPos > 0))
335  {
336  painter.drawText(QPointF(tick.textPos - (tick.textSize/2) - 4, bar.height()/2 - 3), tick.text);
337  }
338 
339  painter.drawLine(shiftx(tick.pos, bar.width()), 0, shiftx(scaleTickList[i].pos,bar.width()), bar.height());
340  }
341  else
342  {
343  painter.drawLine(tick.pos, bar.height()/2 - 2, scaleTickList[i].pos, bar.height()/2);
344  }
345  }
346 }
347 
348 void LevelMeterSignalDB::render(QPainter *painter)
349 {
350  painter->drawPixmap(rect(), *m_backgroundPixmap);
351 
352  QRect bar = rect();
353 
354  // Bottom moving gauge
355 
356  bar.setTop(0.5 * rect().height() + 2);
357  bar.setBottom(0.75*rect().height());
358 
359  bar.setRight(rect().right() - (1.0 - m_avgLevel) * rect().width());
360  bar.setLeft(1);
361  painter->fillRect(bar, m_avgColor[m_colorTheme]);
362 
363  bar.setTop(0.75 * rect().height() + 1);
364  bar.setBottom(rect().height() - 1);
365 
366  bar.setRight(rect().right() - (1.0 - m_decayedPeakLevel) * rect().width());
367  painter->fillRect(bar, m_decayedPeakColor[m_colorTheme]);
368 
369  bar.setTop(0.5 * rect().height() + 2);
370  bar.setBottom(rect().height() - 1);
371 
372  bar.setRight(rect().right() - (1.0 - m_peakHoldLevel) * rect().width());
373  bar.setLeft(bar.right() - 2);
374  painter->fillRect(bar, m_peakColor[m_colorTheme]);
375 
376  // borders
377  painter->setPen(QColor(0,0,0));
378  painter->drawLine(0, 0, rect().width() - 2, 0);
379  painter->drawLine(0, rect().height() - 1, 0, 0);
380  painter->setPen(QColor(80,80,80));
381  painter->drawLine(1, rect().height() - 1, rect().width() - 1, rect().height() - 1);
382  painter->drawLine(rect().width() - 1, rect().height() - 1, rect().width() - 1, 0);
383 }
384 
qreal m_avgLevel
Definition: levelmeter.h:87
LevelMeter(QWidget *parent=0)
Definition: levelmeter.cpp:60
void setSize(float size)
virtual void resized()
Definition: levelmeter.cpp:301
QPixmap * m_backgroundPixmap
Definition: levelmeter.h:136
void redrawTimerExpired()
Definition: levelmeter.cpp:115
virtual void resized()
Definition: levelmeter.cpp:168
static const QColor m_peakColor[4]
Definition: levelmeter.h:177
virtual ~LevelMeterSignalDB()
Definition: levelmeter.cpp:292
qreal m_peakHoldLevel
Definition: levelmeter.h:117
virtual void render(QPainter *painter)=0
LevelMeterSignalDB(QWidget *parent=0)
Definition: levelmeter.cpp:281
QTime m_peakLevelChanged
Definition: levelmeter.h:105
unsigned int uint32_t
Definition: rtptypes_win.h:46
void setAverageSmoothing(uint32_t smoothingFactor)
Definition: levelmeter.cpp:147
ScaleEngine m_scaleEngine
Definition: levelmeter.h:135
QTime m_peakHoldLevelChanged
Definition: levelmeter.h:122
void resizeEvent(QResizeEvent *event)
Definition: levelmeter.cpp:140
QColor m_avgColor
Definition: levelmeter.h:131
ColorTheme m_colorTheme
Definition: levelmeter.h:183
void reset()
Definition: levelmeter.cpp:88
uint32_t m_avgSmoothing
Definition: levelmeter.h:127
int32_t i
Definition: decimators.h:244
void setFont(const QFont &font)
int shiftx(int val, int width)
Definition: levelmeter.h:142
static const QColor m_decayedPeakColor[4]
Definition: levelmeter.h:176
virtual ~LevelMeterVU()
Definition: levelmeter.cpp:164
const TickList & getTickList()
QList< Tick > TickList
Definition: scaleengine.h:37
const qreal PeakDecayRate
Definition: levelmeter.cpp:57
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples)
Definition: levelmeter.cpp:95
LevelMeterVU(QWidget *parent=0)
Definition: levelmeter.cpp:154
void setRange(Unit::Physical physicalUnit, float rangeMin, float rangeMax)
QColor m_peakColor
Definition: levelmeter.h:132
const int PeakHoldLevelDuration
Definition: levelmeter.cpp:58
virtual void render(QPainter *painter)
Definition: levelmeter.cpp:348
static const QColor m_avgColor[4]
Definition: levelmeter.h:175
qreal m_peakLevel
Definition: levelmeter.h:93
qreal m_peakDecayRate
Definition: levelmeter.h:111
virtual void resized()=0
QColor m_decayedPeakColor
Definition: levelmeter.h:133
const int RedrawInterval
Definition: levelmeter.cpp:56
void setOrientation(Qt::Orientation orientation)
void setRange(int min, int max)
Definition: levelmeter.cpp:296
virtual ~LevelMeter()
Definition: levelmeter.cpp:81
QTimer * m_redrawTimer
Definition: levelmeter.h:129
T max(const T &x, const T &y)
Definition: framework.h:446
virtual void render(QPainter *painter)
Definition: levelmeter.cpp:222
void paintEvent(QPaintEvent *event)
Definition: levelmeter.cpp:132
qreal m_decayedPeakLevel
Definition: levelmeter.h:100
T min(const T &x, const T &y)
Definition: framework.h:440