textual
[RRG-proxmark3.git] / client / src / proxguiqt.cpp
blob6ba6b7ab7a4a4fd14fb07b75b756ad3d0921cc49
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // GUI (QT)
9 //-----------------------------------------------------------------------------
10 #define __STDC_FORMAT_MACROS
11 #include "proxguiqt.h"
12 #include <inttypes.h>
13 #include <stdbool.h>
14 #include <iostream>
15 #include <QPainterPath>
16 #include <QBrush>
17 #include <QPen>
18 #include <QTimer>
19 #include <QCloseEvent>
20 #include <QMouseEvent>
21 #include <QKeyEvent>
22 #include <math.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <QSlider>
26 #include <QHBoxLayout>
27 #include <string.h>
28 #include <QtGui>
29 #include "proxgui.h"
30 #include "ui.h"
31 #include "comms.h"
32 #include "graph.h"
33 #include "cmddata.h"
34 #include "util_darwin.h"
36 extern "C" int preferences_save(void);
38 static int s_Buff[MAX_GRAPH_TRACE_LEN];
39 static bool g_useOverlays = false;
40 static int g_absVMax = 0;
41 static uint32_t startMax; // Maximum offset in the graph (right side of graph)
42 static uint32_t PageWidth; // How many samples are currently visible on this 'page' / graph
43 static int unlockStart = 0;
45 void ProxGuiQT::ShowGraphWindow(void) {
46 emit ShowGraphWindowSignal();
49 void ProxGuiQT::RepaintGraphWindow(void) {
50 emit RepaintGraphWindowSignal();
53 void ProxGuiQT::HideGraphWindow(void) {
54 emit HideGraphWindowSignal();
57 void ProxGuiQT::Exit(void) {
58 emit ExitSignal();
61 void ProxGuiQT::_ShowGraphWindow(void) {
62 if (!plotapp)
63 return;
65 if (!plotwidget) {
67 #if defined(__MACH__) && defined(__APPLE__)
68 makeFocusable();
69 #endif
71 plotwidget = new ProxWidget();
73 plotwidget->show();
76 void ProxGuiQT::_RepaintGraphWindow(void) {
77 if (!plotapp || !plotwidget)
78 return;
80 plotwidget->update();
83 void ProxGuiQT::_HideGraphWindow(void) {
84 if (!plotapp || !plotwidget)
85 return;
87 plotwidget->hide();
90 void ProxGuiQT::_Exit(void) {
91 delete this;
94 void ProxGuiQT::_StartProxmarkThread(void) {
95 if (!proxmarkThread)
96 return;
98 // if thread finished delete self and delete application
99 QObject::connect(proxmarkThread, SIGNAL(finished()), proxmarkThread, SLOT(deleteLater()));
100 QObject::connect(proxmarkThread, SIGNAL(finished()), this, SLOT(_Exit()));
101 // start proxmark thread
102 proxmarkThread->start();
105 void ProxGuiQT::MainLoop() {
106 plotapp = new QApplication(argc, argv);
108 connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow()));
109 connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow()));
110 connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow()));
111 connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit()));
113 //start proxmark thread after starting event loop
114 QTimer::singleShot(200, this, SLOT(_StartProxmarkThread()));
116 #if defined(__MACH__) && defined(__APPLE__)
117 //Prevent the terminal from loosing focus during launch by making the client unfocusable
118 makeUnfocusable();
119 #endif
122 plotapp->exec();
125 ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NULL), plotwidget(NULL),
126 argc(argc), argv(argv), proxmarkThread(wthread) {
129 ProxGuiQT::~ProxGuiQT(void) {
130 if (plotapp) {
131 plotapp->quit();
132 plotapp = NULL;
136 // -------------------------------------------------
137 // Slider Widget form based on a class to enable
138 // Event override functions
139 // -------------------------------------------------
141 SliderWidget::SliderWidget() {
142 // Set the initail postion and size from settings
143 if (session.preferences_loaded)
144 setGeometry(session.overlay.x, session.overlay.y, session.overlay.w, session.overlay.h);
145 else
146 resize(800, 400);
149 void SliderWidget::resizeEvent(QResizeEvent *event) {
150 session.overlay.h = event->size().height();
151 session.overlay.w = event->size().width();
152 session.window_changed = true;
156 void SliderWidget::moveEvent(QMoveEvent *event) {
157 session.overlay.x = event->pos().x();
158 session.overlay.y = event->pos().y();
159 session.window_changed = true;
162 //--------------------
163 void ProxWidget::applyOperation() {
164 //printf("ApplyOperation()");
165 save_restoreGB(GRAPH_SAVE);
166 memcpy(GraphBuffer, s_Buff, sizeof(int) * GraphTraceLen);
167 RepaintGraphWindow();
169 void ProxWidget::stickOperation() {
170 save_restoreGB(GRAPH_RESTORE);
171 //printf("stickOperation()");
173 void ProxWidget::vchange_autocorr(int v) {
174 int ans = AutoCorrelate(GraphBuffer, s_Buff, GraphTraceLen, v, true, false);
175 if (g_debugMode) printf("vchange_autocorr(w:%d): %d\n", v, ans);
176 g_useOverlays = true;
177 RepaintGraphWindow();
179 void ProxWidget::vchange_askedge(int v) {
180 //extern int AskEdgeDetect(const int *in, int *out, int len, int threshold);
181 int ans = AskEdgeDetect(GraphBuffer, s_Buff, GraphTraceLen, v);
182 if (g_debugMode) printf("vchange_askedge(w:%d)%d\n", v, ans);
183 g_useOverlays = true;
184 RepaintGraphWindow();
186 void ProxWidget::vchange_dthr_up(int v) {
187 int down = opsController->horizontalSlider_dirthr_down->value();
188 directionalThreshold(GraphBuffer, s_Buff, GraphTraceLen, v, down);
189 //printf("vchange_dthr_up(%d)", v);
190 g_useOverlays = true;
191 RepaintGraphWindow();
193 void ProxWidget::vchange_dthr_down(int v) {
194 //printf("vchange_dthr_down(%d)", v);
195 int up = opsController->horizontalSlider_dirthr_up->value();
196 directionalThreshold(GraphBuffer, s_Buff, GraphTraceLen, v, up);
197 g_useOverlays = true;
198 RepaintGraphWindow();
200 ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
201 this->master = master;
202 // Set the initail postion and size from settings
203 if (session.preferences_loaded)
204 setGeometry(session.plot.x, session.plot.y, session.plot.w, session.plot.h);
205 else
206 resize(800, 400);
208 // Setup the controller widget
209 controlWidget = new SliderWidget(); //new QWidget();
210 opsController = new Ui::Form();
211 opsController->setupUi(controlWidget);
212 //Due to quirks in QT Designer, we need to fiddle a bit
213 opsController->horizontalSlider_dirthr_down->setMinimum(-128);
214 opsController->horizontalSlider_dirthr_down->setMaximum(0);
215 opsController->horizontalSlider_dirthr_down->setValue(-20);
216 opsController->horizontalSlider_dirthr_up->setMinimum(-40);
217 opsController->horizontalSlider_dirthr_up->setMaximum(128);
218 opsController->horizontalSlider_dirthr_up->setValue(20);
219 opsController->horizontalSlider_askedge->setValue(25);
220 opsController->horizontalSlider_window->setValue(4000);
222 QObject::connect(opsController->pushButton_apply, SIGNAL(clicked()), this, SLOT(applyOperation()));
223 QObject::connect(opsController->pushButton_sticky, SIGNAL(clicked()), this, SLOT(stickOperation()));
224 QObject::connect(opsController->horizontalSlider_window, SIGNAL(valueChanged(int)), this, SLOT(vchange_autocorr(int)));
225 QObject::connect(opsController->horizontalSlider_dirthr_up, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_up(int)));
226 QObject::connect(opsController->horizontalSlider_dirthr_down, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_down(int)));
227 QObject::connect(opsController->horizontalSlider_askedge, SIGNAL(valueChanged(int)), this, SLOT(vchange_askedge(int)));
229 controlWidget->setGeometry(session.overlay.x, session.overlay.y, session.overlay.w, session.overlay.h);
231 // Set up the plot widget, which does the actual plotting
232 plot = new Plot(this);
233 QVBoxLayout *layout = new QVBoxLayout;
234 layout->addWidget(plot);
235 setLayout(layout);
237 // plot window title
238 QString pt = QString("[*]Plot [ %1 ]").arg(conn.serial_port_name);
239 setWindowTitle(pt);
241 // shows plot window on the screen.
242 show();
244 // Set Slider/Overlay position if no settings.
245 if (!session.preferences_loaded) {
246 // Move controller widget below plot
247 controlWidget->move(x(), y() + frameSize().height());
248 controlWidget->resize(size().width(), 200);
251 // Olverlays / slider window title
252 QString ct = QString("[*]Slider [ %1 ]").arg(conn.serial_port_name);
253 controlWidget->setWindowTitle(ct);
255 // The hide/show event functions should take care of this.
256 // controlWidget->show();
258 // now that is up, reset pos/size change flags
259 session.window_changed = false;
263 // not 100% sure what i need in this block
264 // feel free to fix - marshmellow...
265 ProxWidget::~ProxWidget(void) {
266 if (controlWidget) {
267 controlWidget->close();
268 delete controlWidget;
269 controlWidget = NULL;
272 if (opsController) {
273 delete opsController;
274 opsController = NULL;
277 if (plot) {
278 plot->close();
279 delete plot;
280 plot = NULL;
284 void ProxWidget::closeEvent(QCloseEvent *event) {
285 event->ignore();
286 this->hide();
287 g_useOverlays = false;
289 void ProxWidget::hideEvent(QHideEvent *event) {
290 controlWidget->hide();
291 plot->hide();
293 void ProxWidget::showEvent(QShowEvent *event) {
294 if (session.overlay_sliders)
295 controlWidget->show();
296 else
297 controlWidget->hide();
298 plot->show();
300 void ProxWidget::moveEvent(QMoveEvent *event) {
301 session.plot.x = event->pos().x();
302 session.plot.y = event->pos().y();
303 session.window_changed = true;
305 void ProxWidget::resizeEvent(QResizeEvent *event) {
306 session.plot.h = event->size().height();
307 session.plot.w = event->size().width();
308 session.window_changed = true;
311 //----------- Plotting
313 int Plot::xCoordOf(int i, QRect r) {
314 return r.left() + (int)((i - GraphStart) * GraphPixelsPerPoint);
317 int Plot::yCoordOf(int v, QRect r, int maxVal) {
318 int z = (r.bottom() - r.top()) / 2;
319 if (maxVal == 0) ++maxVal;
320 return -(z * v) / maxVal + z;
323 int Plot::valueOf_yCoord(int y, QRect r, int maxVal) {
324 int z = (r.bottom() - r.top()) / 2;
325 return (y - z) * maxVal / z;
328 static const QColor GREEN = QColor(100, 255, 100);
329 static const QColor RED = QColor(255, 100, 100);
330 static const QColor BLUE = QColor(100, 100, 255);
331 static const QColor GRAY = QColor(240, 240, 240);
333 QColor Plot::getColor(int graphNum) {
334 switch (graphNum) {
335 case 0:
336 return GREEN; //Green
337 case 1:
338 return RED; //Red
339 case 2:
340 return BLUE; //Blue
341 default:
342 return GRAY; //Gray
346 void Plot::setMaxAndStart(int *buffer, size_t len, QRect plotRect) {
347 if (len == 0) return;
348 startMax = 0;
349 if (plotRect.right() >= plotRect.left() + 40) {
350 uint32_t t = (plotRect.right() - plotRect.left() - 40) / GraphPixelsPerPoint;
351 if (len >= t)
352 startMax = len - t;
354 if (GraphStart > startMax) {
355 GraphStart = startMax;
357 if (GraphStart > len) return;
358 int vMin = INT_MAX, vMax = INT_MIN;
359 uint32_t sample_index = GraphStart ;
360 for (; sample_index < len && xCoordOf(sample_index, plotRect) < plotRect.right() ; sample_index++) {
362 int v = buffer[sample_index];
363 if (v < vMin) vMin = v;
364 if (v > vMax) vMax = v;
367 g_absVMax = 0;
368 if (fabs((double) vMin) > g_absVMax) g_absVMax = (int)fabs((double) vMin);
369 if (fabs((double) vMax) > g_absVMax) g_absVMax = (int)fabs((double) vMax);
370 g_absVMax = (int)(g_absVMax * 1.25 + 1);
373 void Plot::PlotDemod(uint8_t *buffer, size_t len, QRect plotRect, QRect annotationRect, QPainter *painter, int graphNum, uint32_t plotOffset) {
374 if (len == 0 || PlotGridX <= 0) return;
375 //clock_t begin = clock();
376 QPainterPath penPath;
378 int grid_delta_x = PlotGridX;
379 int first_delta_x = grid_delta_x; //(plotOffset > 0) ? PlotGridX : (PlotGridX +);
380 if (GraphStart > plotOffset) first_delta_x -= (GraphStart - plotOffset);
381 int DemodStart = GraphStart;
382 if (plotOffset > GraphStart) DemodStart = plotOffset;
384 int BitStart = 0;
385 // round down
386 if (DemodStart - plotOffset > 0) BitStart = (int)(((DemodStart - plotOffset) + (PlotGridX - 1)) / PlotGridX) - 1;
387 first_delta_x += BitStart * PlotGridX;
388 if (BitStart > (int)len) return;
389 int delta_x = 0;
390 // int v = 0;
391 //printf("first_delta_x %i, grid_delta_x %i, DemodStart %i, BitStart %i\n",first_delta_x,grid_delta_x,DemodStart, BitStart);
393 painter->setPen(getColor(graphNum));
394 char str[5];
395 int absVMax = (int)(100 * 1.05 + 1);
396 int x = xCoordOf(DemodStart, plotRect);
397 int y = yCoordOf((buffer[BitStart] * 200 - 100) * -1, plotRect, absVMax);
398 penPath.moveTo(x, y);
399 delta_x = 0;
400 int clk = first_delta_x;
401 for (int i = BitStart; i < (int)len && xCoordOf(delta_x + DemodStart, plotRect) < plotRect.right(); i++) {
402 for (int j = 0; j < (clk) && i < (int)len && xCoordOf(DemodStart + delta_x + j, plotRect) < plotRect.right() ; j++) {
403 x = xCoordOf(DemodStart + delta_x + j, plotRect);
404 int v = buffer[i] * 200 - 100;
406 y = yCoordOf(v, plotRect, absVMax);
408 penPath.lineTo(x, y);
410 if (GraphPixelsPerPoint > 10) {
411 QRect f(QPoint(x - 3, y - 3), QPoint(x + 3, y + 3));
412 painter->fillRect(f, QColor(100, 255, 100));
414 if (j == (int)clk / 2) {
415 //print label
416 sprintf(str, "%u", buffer[i]);
417 painter->drawText(x - 8, y + ((buffer[i] > 0) ? 18 : -6), str);
420 delta_x += clk;
421 clk = grid_delta_x;
424 // Graph annotations
425 painter->drawPath(penPath);
428 void Plot::PlotGraph(int *buffer, size_t len, QRect plotRect, QRect annotationRect, QPainter *painter, int graphNum) {
429 if (len == 0) return;
430 // clock_t begin = clock();
431 QPainterPath penPath;
432 int vMin = INT_MAX, vMax = INT_MIN, v = 0;
433 int64_t vMean = 0;
434 uint32_t i = 0;
435 int x = xCoordOf(GraphStart, plotRect);
436 int y = yCoordOf(buffer[GraphStart], plotRect, g_absVMax);
437 penPath.moveTo(x, y);
438 for (i = GraphStart; i < len && xCoordOf(i, plotRect) < plotRect.right(); i++) {
440 x = xCoordOf(i, plotRect);
441 v = buffer[i];
443 y = yCoordOf(v, plotRect, g_absVMax);
445 penPath.lineTo(x, y);
447 if (GraphPixelsPerPoint > 10) {
448 QRect f(QPoint(x - 3, y - 3), QPoint(x + 3, y + 3));
449 painter->fillRect(f, QColor(100, 255, 100));
451 // catch stats
452 if (v < vMin) vMin = v;
453 if (v > vMax) vMax = v;
454 vMean += v;
456 GraphStop = i;
457 vMean /= (GraphStop - GraphStart);
459 painter->setPen(getColor(graphNum));
461 // Draw y-axis
462 int xo = 5 + (graphNum * 40);
463 painter->drawLine(xo, plotRect.top(), xo, plotRect.bottom());
465 int vMarkers = (g_absVMax - (g_absVMax % 10)) / 5;
466 int minYDist = 40; // Minimum pixel-distance between markers
468 char yLbl[20];
470 int n = 0;
471 int lasty0 = 65535;
473 for (v = vMarkers; yCoordOf(v, plotRect, g_absVMax) > plotRect.top() && n < 20; v += vMarkers, n++) {
474 int y0 = yCoordOf(v, plotRect, g_absVMax);
475 int y1 = yCoordOf(-v, plotRect, g_absVMax);
477 if (lasty0 - y0 < minYDist) continue;
479 painter->drawLine(xo - 5, y0, xo + 5, y0);
481 sprintf(yLbl, "%d", v);
482 painter->drawText(xo + 8, y0 + 7, yLbl);
484 painter->drawLine(xo - 5, y1, xo + 5, y1);
485 sprintf(yLbl, "%d", -v);
486 painter->drawText(xo + 8, y1 + 5, yLbl);
487 lasty0 = y0;
490 //Graph annotations
491 painter->drawPath(penPath);
492 char str[200];
493 sprintf(str, "max=%d min=%d mean=%" PRId64 " n=%u/%zu CursorAVal=[%d] CursorBVal=[%d]",
494 vMax, vMin, vMean, GraphStop - GraphStart, len, buffer[CursorAPos], buffer[CursorBPos]);
495 painter->drawText(20, annotationRect.bottom() - 23 - 20 * graphNum, str);
496 //clock_t end = clock();
497 //double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
498 //printf("Plot time %f\n", elapsed_secs);
501 void Plot::plotGridLines(QPainter *painter, QRect r) {
503 // set GridOffset
504 if (PlotGridX <= 0) return;
506 double offset = GridOffset;
507 if (GridLocked && PlotGridX) {
508 offset = GridOffset + PlotGridX - fmod(GraphStart, PlotGridX);
509 } else if (!GridLocked && GraphStart > 0 && PlotGridX) {
510 offset = PlotGridX - fmod(GraphStart - offset, PlotGridX) + GraphStart - unlockStart;
512 offset = fmod(offset, PlotGridX);
513 if (offset < 0) offset += PlotGridX;
515 double i;
516 double grid_delta_x = PlotGridX * GraphPixelsPerPoint;
517 int grid_delta_y = PlotGridY;
519 if ((PlotGridX > 0) && ((PlotGridX * GraphPixelsPerPoint) > 1)) {
520 for (i = (offset * GraphPixelsPerPoint); i < r.right(); i += grid_delta_x) {
521 painter->drawLine(r.left() + i, r.top(), r.left() + i, r.bottom());
525 if (PlotGridY > 0) {
526 for (i = 0; yCoordOf(i, r, g_absVMax) > r.top(); i += grid_delta_y) {
527 // line above mid
528 painter->drawLine(r.left(), yCoordOf(i, r, g_absVMax), r.right(), yCoordOf(i, r, g_absVMax));
529 // line below mid
530 painter->drawLine(r.left(), yCoordOf(-i, r, g_absVMax), r.right(), yCoordOf(-i, r, g_absVMax));
535 #define HEIGHT_INFO 60
536 #define WIDTH_AXES 80
538 void Plot::paintEvent(QPaintEvent *event) {
539 QPainter painter(this);
540 QBrush brush(QColor(100, 255, 100));
541 QPen pen(QColor(100, 255, 100));
543 painter.setFont(QFont("Courier New", 10));
545 if (CursorAPos > GraphTraceLen)
546 CursorAPos = 0;
547 if (CursorBPos > GraphTraceLen)
548 CursorBPos = 0;
549 if (CursorCPos > GraphTraceLen)
550 CursorCPos = 0;
551 if (CursorDPos > GraphTraceLen)
552 CursorDPos = 0;
554 QRect plotRect(WIDTH_AXES, 0, width() - WIDTH_AXES, height() - HEIGHT_INFO);
555 QRect infoRect(0, height() - HEIGHT_INFO, width(), HEIGHT_INFO);
556 PageWidth = plotRect.width() / GraphPixelsPerPoint;
558 //Grey background
559 painter.fillRect(rect(), QColor(60, 60, 60));
560 //Black foreground
561 painter.fillRect(plotRect, QColor(0, 0, 0));
563 //init graph variables
564 setMaxAndStart(GraphBuffer, GraphTraceLen, plotRect);
566 // center line
567 int zeroHeight = plotRect.top() + (plotRect.bottom() - plotRect.top()) / 2;
568 painter.setPen(QColor(100, 100, 100));
569 painter.drawLine(plotRect.left(), zeroHeight, plotRect.right(), zeroHeight);
570 // plot X and Y grid lines
571 plotGridLines(&painter, plotRect);
573 //Start painting graph
574 PlotGraph(GraphBuffer, GraphTraceLen, plotRect, infoRect, &painter, 0);
575 if (showDemod && DemodBufferLen > 8) {
576 PlotDemod(DemodBuffer, DemodBufferLen, plotRect, infoRect, &painter, 2, g_DemodStartIdx);
578 if (g_useOverlays) {
579 //init graph variables
580 setMaxAndStart(s_Buff, GraphTraceLen, plotRect);
581 PlotGraph(s_Buff, GraphTraceLen, plotRect, infoRect, &painter, 1);
583 // End graph drawing
585 //Draw the cursors
586 if (CursorAPos > GraphStart && xCoordOf(CursorAPos, plotRect) < plotRect.right()) {
587 painter.setPen(QColor(255, 255, 0));
588 painter.drawLine(xCoordOf(CursorAPos, plotRect), plotRect.top(), xCoordOf(CursorAPos, plotRect), plotRect.bottom());
590 if (CursorBPos > GraphStart && xCoordOf(CursorBPos, plotRect) < plotRect.right()) {
591 painter.setPen(QColor(255, 0, 255));
592 painter.drawLine(xCoordOf(CursorBPos, plotRect), plotRect.top(), xCoordOf(CursorBPos, plotRect), plotRect.bottom());
594 if (CursorCPos > GraphStart && xCoordOf(CursorCPos, plotRect) < plotRect.right()) {
595 painter.setPen(QColor(255, 153, 0)); //orange
596 painter.drawLine(xCoordOf(CursorCPos, plotRect), plotRect.top(), xCoordOf(CursorCPos, plotRect), plotRect.bottom());
598 if (CursorDPos > GraphStart && xCoordOf(CursorDPos, plotRect) < plotRect.right()) {
599 painter.setPen(QColor(100, 209, 246)); //light blue
600 painter.drawLine(xCoordOf(CursorDPos, plotRect), plotRect.top(), xCoordOf(CursorDPos, plotRect), plotRect.bottom());
603 //Draw annotations
604 char str[200];
605 char scalestr[30] = {0};
606 if (CursorScaleFactor != 1) {
607 if (CursorScaleFactorUnit[0] == '\x00') {
608 sprintf(scalestr, "[%2.2f] ", ((int32_t)(CursorBPos - CursorAPos)) / CursorScaleFactor);
609 } else {
610 sprintf(scalestr, "[%2.2f %s] ", ((int32_t)(CursorBPos - CursorAPos)) / CursorScaleFactor, CursorScaleFactorUnit);
613 sprintf(str, "@%u..%u dt=%i %szoom=%2.2f CursorAPos=%u CursorBPos=%u GridX=%lf GridY=%lf (%s) GridXoffset=%lf",
614 GraphStart,
615 GraphStop,
616 CursorBPos - CursorAPos,
617 scalestr,
618 GraphPixelsPerPoint,
619 CursorAPos,
620 CursorBPos,
621 PlotGridXdefault,
622 PlotGridYdefault,
623 GridLocked ? "Locked" : "Unlocked",
624 GridOffset
626 painter.setPen(QColor(255, 255, 255));
627 painter.drawText(20, infoRect.bottom() - 3, str);
630 Plot::Plot(QWidget *parent) : QWidget(parent), GraphStart(0), GraphPixelsPerPoint(1) {
631 //Need to set this, otherwise we don't receive keypress events
632 setFocusPolicy(Qt::StrongFocus);
633 resize(400, 200);
635 QPalette palette(QColor(0, 0, 0, 0));
636 palette.setColor(QPalette::WindowText, QColor(255, 255, 255));
637 palette.setColor(QPalette::Text, QColor(255, 255, 255));
638 palette.setColor(QPalette::Button, QColor(100, 100, 100));
639 setPalette(palette);
640 setAutoFillBackground(true);
642 CursorAPos = 0;
643 CursorBPos = 0;
644 GraphStop = 0;
646 setWindowTitle(tr("Sliders"));
647 master = parent;
650 void Plot::closeEvent(QCloseEvent *event) {
651 event->ignore();
652 this->hide();
653 g_useOverlays = false;
656 void Plot::Zoom(double factor, uint32_t refX) {
657 if (factor >= 1) { // Zoom in
658 if (GraphPixelsPerPoint <= 25 * factor) {
659 GraphPixelsPerPoint *= factor;
660 if (refX > GraphStart) {
661 GraphStart += (refX - GraphStart) - ((refX - GraphStart) / factor);
664 } else { // Zoom out
665 if (GraphPixelsPerPoint >= 0.01 / factor) {
666 GraphPixelsPerPoint *= factor;
667 if (refX > GraphStart) {
668 if (GraphStart >= ((refX - GraphStart) / factor) - (refX - GraphStart)) {
669 GraphStart -= ((refX - GraphStart) / factor) - (refX - GraphStart);
670 } else {
671 GraphStart = 0;
678 void Plot::Move(int offset) {
679 if (offset > 0) { // Move right
680 if (GraphPixelsPerPoint < 20) {
681 GraphStart += offset;
682 } else {
683 GraphStart++;
685 } else { // Move left
686 if (GraphPixelsPerPoint < 20) {
687 if (GraphStart >= (uint) - offset) {
688 GraphStart += offset;
689 } else {
690 GraphStart = 0;
692 } else {
693 if (GraphStart > 0) {
694 GraphStart--;
700 void Plot::Trim(void) {
701 uint32_t lref, rref;
702 const double zoom_offset = 1.148698354997035; // 2**(1/5)
703 if ((CursorAPos == 0) || (CursorBPos == 0)) { // if we don't have both cursors set
704 lref = GraphStart;
705 rref = GraphStop;
706 if (CursorAPos >= lref) {
707 CursorAPos -= lref;
708 } else {
709 CursorAPos = 0;
711 if (CursorBPos >= lref) {
712 CursorBPos -= lref;
713 } else {
714 CursorBPos = 0;
716 } else {
717 lref = CursorAPos < CursorBPos ? CursorAPos : CursorBPos;
718 rref = CursorAPos < CursorBPos ? CursorBPos : CursorAPos;
719 // GraphPixelsPerPoint mush remain a power of zoom_offset
720 double GPPPtarget = GraphPixelsPerPoint * (GraphStop - GraphStart) / (rref - lref);
721 while (GraphPixelsPerPoint < GPPPtarget) {
722 GraphPixelsPerPoint *= zoom_offset;
724 GraphPixelsPerPoint /= zoom_offset;
725 CursorAPos -= lref;
726 CursorBPos -= lref;
728 g_DemodStartIdx -= lref;
729 for (uint32_t i = lref; i < rref; ++i)
730 GraphBuffer[i - lref] = GraphBuffer[i];
731 GraphTraceLen = rref - lref;
732 GraphStart = 0;
735 void Plot::wheelEvent(QWheelEvent *event) {
736 // event->delta()
737 // 120 => shift right 5%
738 // -120 => shift left 5%
739 const float move_offset = 0.05;
740 // -120+shift => zoom in (5 times = *2)
741 // 120+shift => zoom out (5 times = /2)
742 const double zoom_offset = 1.148698354997035; // 2**(1/5)
743 if (event->modifiers() & Qt::ShiftModifier) {
744 // event->position doesn't exist in QT5.12.8, both exist in 5.14.2 and event->x doesn't exist in 5.15.0
745 #if QT_VERSION >= 0x050d00
746 uint32_t x = event->position().x();
747 #else
748 uint32_t x = event->x();
749 #endif
750 x -= WIDTH_AXES;
751 x = (int)(x / GraphPixelsPerPoint);
752 x += GraphStart;
753 // event->angleDelta doesn't exist in QT4, both exist in 5.12.8 and 5.14.2 and event->delta doesn't exist in 5.15.0
754 #if QT_VERSION >= 0x050d00
755 float delta = event->angleDelta().y();
756 #else
757 float delta = event->delta();
758 #endif
759 if (delta < 0) {
760 Zoom(zoom_offset, x);
761 } else {
762 Zoom(1.0 / zoom_offset, x);
764 } else {
765 #if QT_VERSION >= 0x050d00
766 Move(PageWidth * (-(float)event->angleDelta().y() / (120 / move_offset)));
767 #else
768 Move(PageWidth * (-(float)event->delta() / (120 / move_offset)));
769 #endif
771 this->update();
774 void Plot::mouseMoveEvent(QMouseEvent *event) {
775 int x = event->x();
776 x -= WIDTH_AXES;
777 x = (int)(x / GraphPixelsPerPoint);
778 x += GraphStart;
779 if ((event->buttons() & Qt::LeftButton)) {
780 CursorAPos = x;
781 } else if (event->buttons() & Qt::RightButton) {
782 CursorBPos = x;
784 this->update();
787 void Plot::keyPressEvent(QKeyEvent *event) {
788 uint32_t offset; // Left/right movement offset (in sample size)
789 const double zoom_offset = 1.148698354997035; // 2**(1/5)
791 if (event->modifiers() & Qt::ShiftModifier) {
792 if (PlotGridX)
793 offset = PageWidth - fmod(PageWidth, PlotGridX);
794 else
795 offset = PageWidth;
796 } else {
797 if (event->modifiers() & Qt::ControlModifier)
798 offset = 1;
799 else
800 offset = (int)(20 / GraphPixelsPerPoint);
803 switch (event->key()) {
804 case Qt::Key_Down:
805 if (event->modifiers() & Qt::ShiftModifier) {
806 if (event->modifiers() & Qt::ControlModifier) {
807 Zoom(zoom_offset, CursorBPos);
808 } else {
809 Zoom(2, CursorBPos);
811 } else {
812 if (event->modifiers() & Qt::ControlModifier) {
813 Zoom(zoom_offset, CursorAPos);
814 } else {
815 Zoom(2, CursorAPos);
818 break;
820 case Qt::Key_Up:
821 if (event->modifiers() & Qt::ShiftModifier) {
822 if (event->modifiers() & Qt::ControlModifier) {
823 Zoom(1.0 / zoom_offset, CursorBPos);
824 } else {
825 Zoom(0.5, CursorBPos);
827 } else {
828 if (event->modifiers() & Qt::ControlModifier) {
829 Zoom(1.0 / zoom_offset, CursorAPos);
830 } else {
831 Zoom(0.5, CursorAPos);
834 break;
836 case Qt::Key_Right:
837 Move(offset);
838 break;
840 case Qt::Key_Left:
841 Move(-offset);
842 break;
844 case Qt::Key_Greater:
845 g_DemodStartIdx += 1;
846 break;
848 case Qt::Key_Less:
849 g_DemodStartIdx -= 1;
850 break;
852 case Qt::Key_G:
853 if (PlotGridX || PlotGridY) {
854 PlotGridX = 0;
855 PlotGridY = 0;
856 } else {
857 if (PlotGridXdefault < 0)
858 PlotGridXdefault = 64;
859 if (PlotGridYdefault < 0)
860 PlotGridYdefault = 0;
862 PlotGridX = PlotGridXdefault;
863 PlotGridY = PlotGridYdefault;
865 break;
867 case Qt::Key_H:
868 g_printAndLog = PRINTANDLOG_PRINT;
869 PrintAndLogEx(NORMAL, "\n\n" _CYAN_("PLOT window keystrokes and mouse events"));
870 PrintAndLogEx(NORMAL, "\n" _GREEN_("Move:"));
871 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("Home") "/" _RED_("End"), "Move to the start/end of the graph");
872 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _YELLOW_("Mouse wheel"), "Move left/right");
873 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("Left") "/" _RED_("Right"), "Move left/right");
874 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, " + " _RED_("Ctrl"), "... by 1 sample");
875 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, " + " _RED_("Shift"), "... by 1 window");
876 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("PgUp") "/" _RED_("PgDown"), "Move left/right by 1 window");
877 PrintAndLogEx(NORMAL, "\n" _GREEN_("Zoom:"));
878 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("Shift") " + " _YELLOW_("Mouse wheel"), "Zoom in/out around mouse cursor");
879 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("Down") "/" _RED_("Up"), "Zoom in/out around yellow cursor");
880 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, " + " _RED_("Ctrl"), "... with smaller increment");
881 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, " + " _RED_("Shift"), "... around purple cursor");
882 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("h"), "Show this help");
883 PrintAndLogEx(NORMAL, "\n" _GREEN_("Trim:"));
884 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("t"), "Trim data on window or on cursors if defined");
885 PrintAndLogEx(NORMAL, "\n" _GREEN_("Grid and demod:"));
886 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("g"), "Toggle grid and demodulation plot display");
887 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("l"), "Toggle lock grid relative to samples");
888 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9 + 9, _RED_("<") "/" _RED_(">"), "Move demodulation left/right relative to samples");
889 PrintAndLogEx(NORMAL, "\n" _GREEN_("Misc:"));
890 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _YELLOW_("Left mouse click"), "Set yellow cursor");
891 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _YELLOW_("Right mouse click"), "Set purple cursor");
892 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("h"), "Show this help");
893 PrintAndLogEx(NORMAL, " %-*s%s", 25 + 9, _RED_("q"), "Close plot window");
894 g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG;
895 break;
897 case Qt::Key_L:
898 GridLocked = !GridLocked;
899 if (GridLocked)
900 GridOffset += (GraphStart - unlockStart);
901 else
902 unlockStart = GraphStart;
903 break;
905 case Qt::Key_Q:
906 master->hide();
907 break;
909 case Qt::Key_T:
910 Trim();
911 break;
913 case Qt::Key_Home:
914 GraphStart = 0;
915 break;
917 case Qt::Key_End:
918 GraphStart = startMax;
919 break;
921 case Qt::Key_PageUp:
922 if (GraphStart >= PageWidth) {
923 GraphStart -= PageWidth;
924 } else {
925 GraphStart = 0;
927 break;
929 case Qt::Key_PageDown:
930 GraphStart += PageWidth;
931 if (GraphStart > startMax)
932 GraphStart = startMax;
933 break;
935 default:
936 QWidget::keyPressEvent(event);
937 return;
938 break;
941 this->update();