1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
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
7 //-----------------------------------------------------------------------------
9 //-----------------------------------------------------------------------------
10 #define __STDC_FORMAT_MACROS
11 #include "proxguiqt.h"
15 #include <QPainterPath>
19 #include <QCloseEvent>
20 #include <QMouseEvent>
26 #include <QHBoxLayout>
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) {
61 void ProxGuiQT::_ShowGraphWindow(void) {
67 #if defined(__MACH__) && defined(__APPLE__)
71 plotwidget
= new ProxWidget();
76 void ProxGuiQT::_RepaintGraphWindow(void) {
77 if (!plotapp
|| !plotwidget
)
83 void ProxGuiQT::_HideGraphWindow(void) {
84 if (!plotapp
|| !plotwidget
)
90 void ProxGuiQT::_Exit(void) {
94 void ProxGuiQT::_StartProxmarkThread(void) {
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
125 ProxGuiQT::ProxGuiQT(int argc
, char **argv
, WorkerThread
*wthread
) : plotapp(NULL
), plotwidget(NULL
),
126 argc(argc
), argv(argv
), proxmarkThread(wthread
) {
129 ProxGuiQT::~ProxGuiQT(void) {
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
);
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
);
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
);
238 QString pt
= QString("[*]Plot [ %1 ]").arg(conn
.serial_port_name
);
241 // shows plot window on the screen.
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) {
267 controlWidget
->close();
268 delete controlWidget
;
269 controlWidget
= NULL
;
273 delete opsController
;
274 opsController
= NULL
;
284 void ProxWidget::closeEvent(QCloseEvent
*event
) {
287 g_useOverlays
= false;
289 void ProxWidget::hideEvent(QHideEvent
*event
) {
290 controlWidget
->hide();
293 void ProxWidget::showEvent(QShowEvent
*event
) {
294 if (session
.overlay_sliders
)
295 controlWidget
->show();
297 controlWidget
->hide();
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
) {
336 return GREEN
; //Green
346 void Plot::setMaxAndStart(int *buffer
, size_t len
, QRect plotRect
) {
347 if (len
== 0) return;
349 if (plotRect
.right() >= plotRect
.left() + 40) {
350 uint32_t t
= (plotRect
.right() - plotRect
.left() - 40) / GraphPixelsPerPoint
;
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
;
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
;
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;
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
));
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
);
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) {
416 sprintf(str
, "%u", buffer
[i
]);
417 painter
->drawText(x
- 8, y
+ ((buffer
[i
] > 0) ? 18 : -6), str
);
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;
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
);
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));
452 if (v
< vMin
) vMin
= v
;
453 if (v
> vMax
) vMax
= v
;
457 vMean
/= (GraphStop
- GraphStart
);
459 painter
->setPen(getColor(graphNum
));
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
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
);
491 painter
->drawPath(penPath
);
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
) {
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
;
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());
526 for (i
= 0; yCoordOf(i
, r
, g_absVMax
) > r
.top(); i
+= grid_delta_y
) {
528 painter
->drawLine(r
.left(), yCoordOf(i
, r
, g_absVMax
), r
.right(), yCoordOf(i
, r
, g_absVMax
));
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
)
547 if (CursorBPos
> GraphTraceLen
)
549 if (CursorCPos
> GraphTraceLen
)
551 if (CursorDPos
> GraphTraceLen
)
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
;
559 painter
.fillRect(rect(), QColor(60, 60, 60));
561 painter
.fillRect(plotRect
, QColor(0, 0, 0));
563 //init graph variables
564 setMaxAndStart(GraphBuffer
, GraphTraceLen
, plotRect
);
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
);
579 //init graph variables
580 setMaxAndStart(s_Buff
, GraphTraceLen
, plotRect
);
581 PlotGraph(s_Buff
, GraphTraceLen
, plotRect
, infoRect
, &painter
, 1);
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());
605 char scalestr
[30] = {0};
606 if (CursorScaleFactor
!= 1) {
607 if (CursorScaleFactorUnit
[0] == '\x00') {
608 sprintf(scalestr
, "[%2.2f] ", ((int32_t)(CursorBPos
- CursorAPos
)) / CursorScaleFactor
);
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",
616 CursorBPos
- CursorAPos
,
623 GridLocked
? "Locked" : "Unlocked",
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
);
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));
640 setAutoFillBackground(true);
646 setWindowTitle(tr("Sliders"));
650 void Plot::closeEvent(QCloseEvent
*event
) {
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
);
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
);
678 void Plot::Move(int offset
) {
679 if (offset
> 0) { // Move right
680 if (GraphPixelsPerPoint
< 20) {
681 GraphStart
+= offset
;
685 } else { // Move left
686 if (GraphPixelsPerPoint
< 20) {
687 if (GraphStart
>= (uint
) - offset
) {
688 GraphStart
+= offset
;
693 if (GraphStart
> 0) {
700 void Plot::Trim(void) {
702 const double zoom_offset
= 1.148698354997035; // 2**(1/5)
703 if ((CursorAPos
== 0) || (CursorBPos
== 0)) { // if we don't have both cursors set
706 if (CursorAPos
>= lref
) {
711 if (CursorBPos
>= lref
) {
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
;
728 g_DemodStartIdx
-= lref
;
729 for (uint32_t i
= lref
; i
< rref
; ++i
)
730 GraphBuffer
[i
- lref
] = GraphBuffer
[i
];
731 GraphTraceLen
= rref
- lref
;
735 void Plot::wheelEvent(QWheelEvent
*event
) {
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();
748 uint32_t x
= event
->x();
751 x
= (int)(x
/ GraphPixelsPerPoint
);
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();
757 float delta
= event
->delta();
760 Zoom(zoom_offset
, x
);
762 Zoom(1.0 / zoom_offset
, x
);
765 #if QT_VERSION >= 0x050d00
766 Move(PageWidth
* (-(float)event
->angleDelta().y() / (120 / move_offset
)));
768 Move(PageWidth
* (-(float)event
->delta() / (120 / move_offset
)));
774 void Plot::mouseMoveEvent(QMouseEvent
*event
) {
777 x
= (int)(x
/ GraphPixelsPerPoint
);
779 if ((event
->buttons() & Qt::LeftButton
)) {
781 } else if (event
->buttons() & Qt::RightButton
) {
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
) {
793 offset
= PageWidth
- fmod(PageWidth
, PlotGridX
);
797 if (event
->modifiers() & Qt::ControlModifier
)
800 offset
= (int)(20 / GraphPixelsPerPoint
);
803 switch (event
->key()) {
805 if (event
->modifiers() & Qt::ShiftModifier
) {
806 if (event
->modifiers() & Qt::ControlModifier
) {
807 Zoom(zoom_offset
, CursorBPos
);
812 if (event
->modifiers() & Qt::ControlModifier
) {
813 Zoom(zoom_offset
, CursorAPos
);
821 if (event
->modifiers() & Qt::ShiftModifier
) {
822 if (event
->modifiers() & Qt::ControlModifier
) {
823 Zoom(1.0 / zoom_offset
, CursorBPos
);
825 Zoom(0.5, CursorBPos
);
828 if (event
->modifiers() & Qt::ControlModifier
) {
829 Zoom(1.0 / zoom_offset
, CursorAPos
);
831 Zoom(0.5, CursorAPos
);
844 case Qt::Key_Greater
:
845 g_DemodStartIdx
+= 1;
849 g_DemodStartIdx
-= 1;
853 if (PlotGridX
|| PlotGridY
) {
857 if (PlotGridXdefault
< 0)
858 PlotGridXdefault
= 64;
859 if (PlotGridYdefault
< 0)
860 PlotGridYdefault
= 0;
862 PlotGridX
= PlotGridXdefault
;
863 PlotGridY
= PlotGridYdefault
;
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
;
898 GridLocked
= !GridLocked
;
900 GridOffset
+= (GraphStart
- unlockStart
);
902 unlockStart
= GraphStart
;
918 GraphStart
= startMax
;
922 if (GraphStart
>= PageWidth
) {
923 GraphStart
-= PageWidth
;
929 case Qt::Key_PageDown
:
930 GraphStart
+= PageWidth
;
931 if (GraphStart
> startMax
)
932 GraphStart
= startMax
;
936 QWidget::keyPressEvent(event
);