RISC-V/V203/usb/scope/software/displaywidget.cpp
2024-11-17 17:12:36 +01:00

386 lines
12 KiB
C++

#include <QPainter>
#include <QResizeEvent>
#include <QPaintEvent>
#include <QWheelEvent>
#include <QSettings>
#include <math.h>
#include "displaywidget.h"
#include "helpers.h"
static const double TimeBaseSteps [] = {
2.0e-6, 5.0e-6, 1.0e-5, 2.0e-5, 5.0e-5, 1.0e-4, 2.0e-4, 5.0e-4, 1.0e-3,
2.0e-3, 5.0e-3, 1.0e-2, 2.0e-2, 5.0e-2, 1.0e-1, 2.0e-1, 5.0e-1, 1.0,
};
static const int TimeBaseCounts [] = {
0, 0, 0, 0, 0, 0, 0, 0, 10,
5, 2, 1, 1, 1, 1, 1, 1, 1,
};
static const double ChannelsSteps [] = {
0.1, 0.2, 0.5,
1.0, 2.0, 5.0,
10.0,20.0,50.0,
};
static constexpr double REF_Y = 3.3; // maximum napětí převodníku
static constexpr double MAX_Y = double (1 << 12); // 12. bit rozlišení - 4096 steps
static constexpr double HALF_Y = 0.5 * MAX_Y;
static constexpr double QSCL_Y = 0.5 * HALF_Y;
static constexpr double STEP_Y = REF_Y / MAX_Y; // jeden krok ve Voltech
static constexpr unsigned T_SIZE = 1u << 10;
static constexpr unsigned T_MASK = T_SIZE - 1u;
DisplayWidget::DisplayWidget(QWidget * p) : QWidget(p), pcol(), ACopy(), BCopy(), m_forward(), m_inverse(), background(), m_ts(nullptr),
ChA (T_SIZE), ChB(T_SIZE), m_items (MOVE_VALUE), m_timeBase(6), m_ChBase(0), m_timeCount(0u) {
x_lenght = T_SIZE;
m_time.a = 200.0;
m_time.b = 300.0;
m_volt.a = -1.0 / STEP_Y;
m_volt.b = +1.0 / STEP_Y;
m_channels.a = ChannelsSteps[3];
m_channels.b = ChannelsSteps[3];
m_offset.a = 0;
m_offset.b = 0;
marker_type = MARKER_TIME;
m_TrgSource = 0;
int n = 0;
for (QPointF & e : ChA) { const QPointF p (n++, 0); e = p; }
n = 0;
for (QPointF & e : ChB) { const QPointF p (n++, 0); e = p; }
m_continual = false;
m_cont_limit = 0;
m_cont_pass = 0;
}
DisplayWidget::~DisplayWidget() {
}
void DisplayWidget::SendTrigerChan(int n) {
m_TrgSource = n;
drawBackground();
update();
}
void DisplayWidget::MarkerChanged (int n, bool b) {
if (!b) return;
// qDebug ("Marker : %d -> %s", n, b ? "true" : "false"); // OK
switch (n) {
case 0: marker_type = MARKER_TIME; break;
case 1: marker_type = MARKER_VOLT_A; break;
case 2: marker_type = MARKER_VOLT_B; break;
default : break;
}
drawBackground();
update();
}
void DisplayWidget::TriggerValues(int n) {
m_items = static_cast<MOVE_ITEMS>(n);
}
static constexpr double FMULT = 1.189207115002721;
static constexpr double IMULT = 1.0 / FMULT;
void DisplayWidget::wheelEvent(QWheelEvent * event) {
if (m_ts == nullptr) return;
const QPoint d = event->angleDelta();
const double increment = d.y() > 0 ? +1 : -1;
const double zy = -m_inverse.m22();
switch (m_items) {
case MOVE_VALUE: {
m_ts->value += uint16_t (increment * zy);
emit SettingsChanged (m_items);
} break;
case MOVE_OFSET: {
m_ts->offset += increment;
emit SettingsChanged (m_items);
} break;
case MOVE_MARKERA: {
if (marker_type == MARKER_TIME) m_time.a += increment;
else m_volt.a += increment * zy;
} break;
case MOVE_MARKERB: {
if (marker_type == MARKER_TIME) m_time.b += increment;
else m_volt.b += increment * zy;
} break;
case TIME_ZOOM: {
const double mx = FMULT * double (size().width()) / x_lenght;
if ((m_forward.m11() > mx) or (increment > 0)) { // limit to window
const QMatrix m = increment > 0 ? QMatrix (FMULT,0,0,1,0,0) : QMatrix (IMULT,0,0,1,0,0);
m_forward *= m;
m_inverse = m_forward.inverted();
}
} break;
case OFSET_A: {
m_offset.a += increment * zy;
} break;
case OFSET_B: {
m_offset.b += increment * zy;
} break;
default : {
qDebug ("wheelEvent : %d", int (m_items));
} break;
}
drawBackground();
update();
QWidget::wheelEvent(event);
}
void DisplayWidget::mousePressEvent(QMouseEvent * event) {
if (m_ts == nullptr) return;
const QPointF dp = m_inverse.map (event->pos ());
const double ofs = m_TrgSource ? m_offset.b : m_offset.a;
// qDebug ("dp:[%g, %g] %g", dp.x(), dp.y(), ofs);
switch (m_items) {
case MOVE_VALUE: {
m_ts->value = dp.y() - ofs + HALF_Y;
// qDebug("Set Triger : %d", m_ts->value);
emit SettingsChanged (m_items);
} break;
case MOVE_OFSET: {
m_ts->offset = dp.x();
emit SettingsChanged (m_items);
} break;
case MOVE_MARKERA: {
if (marker_type == MARKER_TIME) m_time.a = dp.x();
else m_volt.a = dp.y();
} break;
case MOVE_MARKERB: {
if (marker_type == MARKER_TIME) m_time.b = dp.x();
else m_volt.b = dp.y();
} break;
case OFSET_A: {
m_offset.a = dp.y();
} break;
case OFSET_B: {
m_offset.b = dp.y();
} break;
case TIME_ZOOM: QWidget::mousePressEvent(event); return;
default : {
qDebug ("mousePressEvent : %d", int (m_items));
} break;
}
drawBackground();
update();
QWidget::mousePressEvent(event);
}
void DisplayWidget::resizeEvent(QResizeEvent * event) {
const QSize sz = event->size();
reloadMatrix (sz);
QImage tmpi (sz, QImage::Format_ARGB32);
background = tmpi;
drawBackground();
QWidget::resizeEvent(event);
}
void DisplayWidget::paintEvent(QPaintEvent * event) {
QPainter p (this);
p.drawImage(event->rect(), background);
drawCurrent(p);
QPen pa (pcol.colA, 2);
QPen pb (pcol.colB, 2);
p.setPen(pb);
QMatrix mb (m_forward);
mb.translate(0, m_offset.b - HALF_Y);
p.drawPolyline (mb.map(ChB));
p.setPen(pa);
QMatrix ma (m_forward);
ma.translate(0, m_offset.a - HALF_Y);
p.drawPolyline (ma.map(ChA));
}
void DisplayWidget::drawCurrent(QPainter & p) {
if (!m_continual) return;
const QPen pen (QColor(192,192,192));
p.setPen (pen);
QLineF vl (QPointF(m_timeCount, -QSCL_Y), QPointF(m_timeCount, +QSCL_Y));
p.drawLine (m_forward.map (vl)); // Stávající poloha v kontinuálním módu
}
void DisplayWidget::setTrigger(TrigerSettings * ts) {
m_ts = ts;
}
void DisplayWidget::DispChannels(QVector<int> cha, QVector<int> chb) {
ACopy = cha;
BCopy = chb;
if (reloadData()) update();
}
bool DisplayWidget::reloadData () {
const size_t Alen = ACopy.size();
const size_t Blen = BCopy.size();
const size_t Amin = Alen < T_SIZE ? Alen : T_SIZE;
const size_t Bmin = Blen < T_SIZE ? Blen : T_SIZE;
if ((Alen == 1u) and (Blen == 1u)) { // kontinuální mód
m_continual = true;
const QPointF pta (m_timeCount, ACopy [0]), ptb (m_timeCount, BCopy [0]);
ChA[m_timeCount] = pta;
ChB[m_timeCount] = ptb;
m_timeCount += 1u;
m_timeCount &= T_MASK;
m_cont_pass += 1;
if (m_cont_pass >= m_cont_limit) {
m_cont_pass = 0;
return true;
} else {
return false;
}
}
m_continual = false;
for (unsigned n=0u; n<Amin; n++) {
QPointF & p = ChA [n];
p.setY (ACopy [n]);
}
for (unsigned n=0u; n<Bmin; n++) {
QPointF & p = ChB [n];
p.setY (BCopy [n]);
}
const int l = Alen > Blen ? Alen : Blen;
if (l != x_lenght) {
x_lenght = l;
reloadMatrix (size());
}
return true;
}
void DisplayWidget::reloadMatrix(const QSize & sz) {
const double xm = sz.width();
const double ym = sz.height();
const double xz = xm / (x_lenght); // převod zpět ma pixely
const double yz = ym / (MAX_Y);
const QMatrix tm (xz,0,0,-yz, 0, 0.5 * ym);
m_forward = tm;
m_inverse = m_forward.inverted();
}
void DisplayWidget::TimeBaseRange(int n) {
m_timeBase = n;
m_cont_limit = TimeBaseCounts [m_timeBase];
//qDebug("count = %d", m_cont_limit);
reloadMatrix (size());
drawBackground();
update();
}
void DisplayWidget::drawBackground() {
QPainter p (&background);
QRect r = background.rect();
p.fillRect (r, QBrush(Qt::black, Qt::SolidPattern));
if (!m_ts) return;
const double yb = 0.0, ye = HALF_Y;
const double xb = 0.0, xe = x_lenght;
// rastr
QPen pg (QColor(64,64,64), 1);
p.setPen(pg);
const double dx = 100.0;
for (double x=xb; x<xe; x+=dx) {
const QLineF yline (x, -ye, x, +ye);
p.drawLine (m_forward.map(yline));
}
const double dv = 0.5;
const double dy = dv / STEP_Y;
for (double y=yb; y<ye; y+=dy) {
const QLineF xlinep (xb, +y, xe, +y);
p.drawLine (m_forward.map(xlinep));
const QLineF xlinem (xb, -y, xe, -y);
p.drawLine (m_forward.map(xlinem));
}
QPen pg0 (QColor(128,128,128), 2);
p.setPen(pg0);
const QLineF hline0 (xb, 0, xe, 0);
p.drawLine (m_forward.map(hline0));
// triger
const double ofs = (m_TrgSource ? m_offset.b : m_offset.a) - HALF_Y;
QPointF trg (m_ts->offset, m_ts->value + ofs);
QPen pc (QColor(0,128,255), 1);
p.setPen(pc);
const qreal pos = 30.0;
const QLineF hline = m_forward.map(QLine(xb, trg.y(), xe, trg.y()));
const QLineF vline = m_forward.map(QLine(trg.x(), -ye, trg.x(), ye));
QPointF vb = vline.p1() + QPointF(0,-pos), ve = vline.p2() + QPointF(0,+pos);
p.drawLine (hline);
p.drawLine (QLineF(vb,ve));
// markers
QPen pm (QColor(255,255,0,196), 1);
p.setPen(pm);
if (marker_type == MARKER_TIME) {
const QLineF ma = m_forward.map(QLine (m_time.a, -ye, m_time.a, ye));
const QLineF mb = m_forward.map(QLine (m_time.b, -ye, m_time.b, ye));
vb = ma.p1() + QPointF(0,-pos), ve = ma.p2() + QPointF(0,+pos);
p.drawLine (QLineF(vb, ve));
vb = mb.p1() + QPointF(0,-pos), ve = mb.p2() + QPointF(0,+pos);
p.drawLine (QLineF(vb, ve));
} else {
const QLineF ma (xb, m_volt.a, xe, m_volt.a);
const QLineF mb (xb, m_volt.b, xe, m_volt.b);
p.drawLine (m_forward.map(ma));
p.drawLine (m_forward.map(mb));
}
// offsets
QPen ofpen(pcol.colA);
QVector<qreal>dashes;
const qreal gap = 10;
dashes << gap << gap;
ofpen.setDashPattern(dashes);
p.setPen (ofpen);
const QLineF oa (xb, m_offset.a, xe, m_offset.a);
p.drawLine (m_forward.map(oa));
ofpen.setColor(pcol.colB);
ofpen.setDashPattern(dashes);
ofpen.setDashOffset(gap);
p.setPen (ofpen);
const QLineF ob (xb, m_offset.b, xe, m_offset.b);
p.drawLine (m_forward.map(ob));
// text
p.setPen (QPen (pcol.colT));
QFont font = p.font();
font.setPixelSize(16);
p.setFont (font);
QString desc;
const double xz = TimeBaseSteps [m_timeBase];
const int my = r.size().height() - 10;
desc.sprintf("T=%ss/d, ChA %gV, ChB %gV", ing_fmt (xz * dx).c_str(), m_channels.a, m_channels.b);
p.drawText(10,20, desc);
if (marker_type == MARKER_TIME) {
const double delta = xz * (m_time.b - m_time.a);
const double freq = delta == 0.0 ? 0.0 : 1.0 / delta;
desc.sprintf("Marker A: %ss, Marker B: %ss, Δ=%ss, f=%sHz", ing_fmt(xz * m_time.a).c_str(), ing_fmt(xz * m_time.b).c_str(),
ing_fmt(delta).c_str(), ing_fmt(freq).c_str());
} else {
const double ofs = - (marker_type == MARKER_VOLT_A ? m_offset.a : m_offset.b);
double scl = 0.0;
if (marker_type == MARKER_VOLT_A) { p.setPen (QPen (pcol.colA)); scl = m_channels.a; }
else { p.setPen (QPen (pcol.colB)); scl = m_channels.b; }
//qDebug("scl = %g, a=%g, b=%g, oa=%g, ob=%g", scl, m_volt.a, m_volt.b, m_offset.a, m_offset.b);
const double yz = STEP_Y * scl;
const double delta = yz * (m_volt.b - m_volt.a), va = m_volt.a + ofs, vb = m_volt.b + ofs;
desc.sprintf("Marker A: %sV, Marker B: %sV, Δ=%sV", ing_fmt(yz * va).c_str(), ing_fmt(yz * vb).c_str(),
ing_fmt(delta).c_str());
}
p.drawText(10,my, desc);
}
void DisplayWidget::SendVoltage(int ch, int n) {
switch (ch) {
case 0: m_channels.a = ChannelsSteps [n]; break;
case 1: m_channels.b = ChannelsSteps [n]; break;
default: break;
}
// qDebug ("Scale: %d => %g,%g", ch, m_channels.a, m_channels.b);
drawBackground();
update();
}
void DisplayWidget::reloadChRange(int a, int b) {
m_channels.a = ChannelsSteps [a];
m_channels.b = ChannelsSteps [b];
drawBackground();
update();
}
void DisplayWidget::saveSettings(QSettings & setting) {
setting.setValue("TimeA", m_time.a);
setting.setValue("TimeB", m_time.b);
setting.setValue("VoltA", m_volt.a);
setting.setValue("VoltB", m_volt.b);
setting.setValue("OfsA" , m_offset.a);
setting.setValue("OfsB" , m_offset.b);
}
void DisplayWidget::restSettings(QSettings & setting) {
m_time.a = setting.value("TimeA").toDouble();
m_time.b = setting.value("TimeB").toDouble();
m_volt.a = setting.value("VoltA").toDouble();
m_volt.b = setting.value("VoltB").toDouble();
m_offset.a = setting.value("OfsA").toDouble();
m_offset.b = setting.value("OfsB").toDouble();
drawBackground();
update();
}