Skip to content

Commit

Permalink
Merge pull request #1300 from gqrx-sdr/refactor-meter
Browse files Browse the repository at this point in the history
Refactor the signal meter
  • Loading branch information
argilo authored Oct 3, 2023
2 parents db7fdc0 + 05f94ed commit 2b76a91
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 151 deletions.
173 changes: 43 additions & 130 deletions src/qtgui/meter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
#include "meter.h"

// ratio to total control width or height
#define CTRL_MARGIN 0.07 // left/right margin
#define CTRL_MAJOR_START 0.3 // top of major tic line
#define CTRL_MINOR_START 0.3 // top of minor tic line
#define CTRL_XAXIS_HEGHT 0.4 // vertical position of horizontal axis
#define CTRL_NEEDLE_TOP 0.4 // vertical position of top of needle triangle
#define CTRL_MARGIN 0.07 // left/right margin
#define CTRL_MAJOR_START 0.3 // top of major tic line
#define CTRL_MINOR_START 0.34 // top of minor tic line
#define CTRL_XAXIS_HEGHT 0.4 // vertical position of horizontal axis
#define CTRL_NEEDLE_TOP 0.4 // vertical position of top of needle triangle

#define MIN_DB -100.0f
#define MAX_DB +0.0f
Expand All @@ -50,23 +50,9 @@
CMeter::CMeter(QWidget *parent) : QFrame(parent)
{
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setFocusPolicy(Qt::StrongFocus);
setAttribute(Qt::WA_PaintOnScreen,false);
setAutoFillBackground(false);
setAttribute(Qt::WA_OpaquePaintEvent, false);
setAttribute(Qt::WA_NoSystemBackground, true);
setMouseTracking(true);

m_Font = QFont("Arial");
m_Font.setWeight(QFont::Normal);
m_2DPixmap = QPixmap(0,0);
m_OverlayPixmap = QPixmap(0,0);
m_Size = QSize(0,0);
m_pixperdb = 0.0;
m_Siglevel = 0;

m_dBFS = MIN_DB;
m_Sql = -150.0;
m_SqlLevel = 0.0;
}

CMeter::~CMeter()
Expand All @@ -83,167 +69,94 @@ QSize CMeter::sizeHint() const
return QSize(100, 30);
}

void CMeter::resizeEvent(QResizeEvent *)
{
if (!size().isValid())
return;

if (m_Size != size())
{
// if size changed, resize pixmaps to new screensize
m_Size = size();
qreal dpr = devicePixelRatioF();
m_OverlayPixmap = QPixmap(m_Size.width() * dpr, m_Size.height() * dpr);
m_OverlayPixmap.setDevicePixelRatio(dpr);
m_OverlayPixmap.fill(Qt::black);
m_2DPixmap = QPixmap(m_Size.width() * dpr, m_Size.height() * dpr);
m_2DPixmap.setDevicePixelRatio(dpr);
m_2DPixmap.fill(Qt::black);

qreal w = (m_2DPixmap.width() / dpr) - 2 * CTRL_MARGIN * (m_2DPixmap.width() / dpr);
m_pixperdb = w / fabs((double)(MAX_DB - MIN_DB));
setSqlLevel(m_Sql);
}

DrawOverlay();
draw();
}

void CMeter::setLevel(float dbfs)
{
if (dbfs < MIN_DB)
dbfs = MIN_DB;
else if (dbfs > MAX_DB)
dbfs = MAX_DB;

float level = m_dBFS;
float alpha = dbfs < level ? ALPHA_DECAY : ALPHA_RISE;
m_dBFS -= alpha * (level - dbfs);
m_Siglevel = (qreal)(level - MIN_DB) * m_pixperdb;

draw();
float alpha = dbfs < m_dBFS ? ALPHA_DECAY : ALPHA_RISE;
m_dBFS -= alpha * (m_dBFS - dbfs);
update();
}

void CMeter::setSqlLevel(float dbfs)
{
if (dbfs >= 0.f)
m_SqlLevel = 0.0;
else
m_SqlLevel = (qreal)(dbfs - MIN_DB) * m_pixperdb;

if (m_SqlLevel < 0.0)
m_SqlLevel = 0.0;

m_Sql = (qreal)dbfs;
m_Sql = dbfs;
update();
}

// Called by QT when screen needs to be redrawn
void CMeter::paintEvent(QPaintEvent *)
{
QPainter painter(this);

painter.drawPixmap(0, 0, m_2DPixmap);
return;
drawOverlay(painter);
draw(painter);
}

// Called to update s-meter data for displaying on the screen
void CMeter::draw()
void CMeter::draw(QPainter &painter)
{
int w;
int h;

if (m_2DPixmap.isNull())
return;

// get/draw the 2D spectrum
w = m_2DPixmap.width() / m_2DPixmap.devicePixelRatioF();
h = m_2DPixmap.height() / m_2DPixmap.devicePixelRatioF();
// Draw current position indicator
qreal hline = (qreal) height() * CTRL_XAXIS_HEGHT;
qreal marg = (qreal) width() * CTRL_MARGIN;
qreal ht = (qreal) height() * CTRL_NEEDLE_TOP;
qreal pixperdb = (width() - 2 * CTRL_MARGIN * width()) / (qreal)(MAX_DB - MIN_DB);

// first copy into 2Dbitmap the overlay bitmap.
m_2DPixmap = m_OverlayPixmap.copy(0, 0, m_OverlayPixmap.width(), m_OverlayPixmap.height());
QPainter painter(&m_2DPixmap);

// DrawCurrent position indicator
qreal hline = (qreal) h * CTRL_XAXIS_HEGHT;
qreal marg = (qreal) w * CTRL_MARGIN;
qreal ht = (qreal) h * CTRL_NEEDLE_TOP;
qreal x = marg + m_Siglevel;

if (m_Siglevel > 0.0)
if (m_dBFS > MIN_DB)
{
QColor color(0, 190, 0, 255);
QPen pen(color);
pen.setJoinStyle(Qt::MiterJoin);
painter.setPen(pen);
painter.setBrush(QBrush(color));

painter.drawRect(QRectF(marg, ht + 2, x - marg, 4));
painter.drawRect(QRectF(marg, ht + 2, (qreal)(std::min(m_dBFS, MAX_DB) - MIN_DB) * pixperdb, 4));
}

if (m_SqlLevel > 0.0)
if (m_Sql > MIN_DB)
{
x = marg + m_SqlLevel;
qreal x = marg + (qreal)(m_Sql - MIN_DB) * pixperdb;
painter.setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
painter.drawLine(QLineF(x, hline, x, hline + 8));
}

int y = (h) / 4;
m_Font.setPixelSize(y);
painter.setFont(m_Font);
QFont font("Arial");
font.setPixelSize(height() / 4);
painter.setFont(font);

painter.setPen(QColor(0xDA, 0xDA, 0xDA, 0xFF));
m_Str.setNum(m_dBFS, 'f', 1);
painter.drawText(marg, h - 2, m_Str + " dBFS" );

update();
painter.drawText(marg, height() - 2, QString::number(m_dBFS, 'f', 1) + " dBFS" );
}

// Called to draw an overlay bitmap containing items that
// does not need to be recreated every fft data update.
void CMeter::DrawOverlay()
void CMeter::drawOverlay(QPainter &painter)
{
if (m_OverlayPixmap.isNull())
return;

int w = m_OverlayPixmap.width() / m_OverlayPixmap.devicePixelRatioF();
int h = m_OverlayPixmap.height() / m_OverlayPixmap.devicePixelRatioF();
int x,y;
QRect rect;
QPainter painter(&m_OverlayPixmap);

m_OverlayPixmap.fill(QColor(0x1F, 0x1D, 0x1D, 0xFF));

// Draw scale lines
qreal marg = (qreal) w * CTRL_MARGIN;
qreal hline = (qreal)h * CTRL_XAXIS_HEGHT;
qreal magstart = (qreal) h * CTRL_MAJOR_START;
qreal minstart = (qreal) h * CTRL_MINOR_START;
qreal hstop = (qreal) w - marg;
qreal marg = (qreal) width() * CTRL_MARGIN;
qreal hline = (qreal) height() * CTRL_XAXIS_HEGHT;
qreal majstart = (qreal) height() * CTRL_MAJOR_START;
qreal minstart = (qreal) height() * CTRL_MINOR_START;
qreal hstop = (qreal) width() - marg;
painter.setPen(QPen(Qt::white, 1, Qt::SolidLine));
painter.drawLine(QLineF(marg, hline, hstop, hline)); // top line
painter.drawLine(QLineF(marg, hline+8, hstop, hline+8)); // bottom line
qreal xpos = marg;
for (x = 0; x < 11; x++) {
for (int x = 0; x <= 10; x++) {
if (x & 1)
//minor tics
painter.drawLine(QLineF(xpos, minstart, xpos, hline));
else
painter.drawLine(QLineF(xpos, magstart, xpos, hline));
painter.drawLine(QLineF(xpos, majstart, xpos, hline));
xpos += (hstop-marg) / 10.0;
}

// draw scale text
y = h / 4;
m_Font.setPixelSize(y);
painter.setFont(m_Font);
int rwidth = (int)((hstop - marg) / 5.0);
m_Str = "-100";
rect.setRect(marg / 2 - 5, 0, rwidth, magstart);

for (x = MIN_DB; x <= MAX_DB; x += 20)
QFont font("Arial");
font.setPixelSize(height() / 4);
painter.setFont(font);
qreal rwidth = (hstop - marg) / 5.0;
QRectF rect(marg - rwidth / 2, 0, rwidth, majstart);

for (int x = MIN_DB; x <= MAX_DB; x += 20)
{
m_Str.setNum(x);
painter.drawText(rect, Qt::AlignHCenter|Qt::AlignVCenter, m_Str);
painter.drawText(rect, Qt::AlignHCenter|Qt::AlignVCenter, QString::number(x));
rect.translate(rwidth, 0);
}
}
24 changes: 3 additions & 21 deletions src/qtgui/meter.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,29 @@

#include <QtGui>
#include <QFrame>
#include <QImage>

class CMeter : public QFrame
{
Q_OBJECT

public:
explicit CMeter(QWidget *parent = 0);
explicit CMeter(float min_level = -100.0, float max_level = 10.0,
QWidget *parent = 0);
~CMeter();

QSize minimumSizeHint() const;
QSize sizeHint() const;

void setMin(float min_level);
void setMax(float max_level);
void setRange(float min_level, float max_level);

void draw();
void UpdateOverlay(){DrawOverlay();}

public slots:
void setLevel(float dbfs);
void setSqlLevel(float dbfs);

protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent* event);

private:
void DrawOverlay();
void draw(QPainter &painter);
void drawOverlay(QPainter &painter);

QFont m_Font;
QPixmap m_2DPixmap;
QPixmap m_OverlayPixmap;
QSize m_Size;
QString m_Str;
qreal m_pixperdb; // pixels / dB
qreal m_Siglevel;
float m_dBFS;
qreal m_Sql;
qreal m_SqlLevel;
float m_Sql;
};

0 comments on commit 2b76a91

Please sign in to comment.