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 #include "proxguiqt.h"
14 #include <QPainterPath>
18 #include <QCloseEvent>
19 #include <QMouseEvent>
25 #include <QHBoxLayout>
31 #include "util_darwin.h"
35 bool g_useOverlays
= false;
37 int startMax
; // Maximum offset in the graph (right side of graph)
38 int PageWidth
; // How many samples are currently visible on this 'page' / graph
41 void ProxGuiQT::ShowGraphWindow(void)
43 emit
ShowGraphWindowSignal();
46 void ProxGuiQT::RepaintGraphWindow(void)
48 emit
RepaintGraphWindowSignal();
51 void ProxGuiQT::HideGraphWindow(void)
53 emit
HideGraphWindowSignal();
56 void ProxGuiQT::Exit(void)
61 void ProxGuiQT::_ShowGraphWindow(void)
68 #if defined(__MACH__) && defined(__APPLE__)
71 plotwidget
= new ProxWidget();
77 void ProxGuiQT::_RepaintGraphWindow(void)
79 if (!plotapp
|| !plotwidget
)
85 void ProxGuiQT::_HideGraphWindow(void)
87 if (!plotapp
|| !plotwidget
)
93 void ProxGuiQT::_Exit(void) {
97 void ProxGuiQT::_StartProxmarkThread(void) {
101 // if thread finished delete self and delete application
102 QObject::connect(proxmarkThread
, SIGNAL(finished()), proxmarkThread
, SLOT(deleteLater()));
103 QObject::connect(proxmarkThread
, SIGNAL(finished()), this, SLOT(_Exit()));
104 // start proxmark thread
105 proxmarkThread
->start();
108 void ProxGuiQT::MainLoop()
110 plotapp
= new QApplication(argc
, argv
);
112 connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow()));
113 connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow()));
114 connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow()));
115 connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit()));
117 //start proxmark thread after starting event loop
118 QTimer::singleShot(200, this, SLOT(_StartProxmarkThread()));
120 #if defined(__MACH__) && defined(__APPLE__)
121 //Prevent the terminal from loosing focus during launch by making the client unfocusable
128 ProxGuiQT::ProxGuiQT(int argc
, char **argv
, WorkerThread
*wthread
) : plotapp(NULL
), plotwidget(NULL
),
129 argc(argc
), argv(argv
), proxmarkThread(wthread
)
133 ProxGuiQT::~ProxGuiQT(void)
136 //plotwidget->destroy(true,true);
137 // delete plotwidget;
138 // plotwidget = NULL;
147 //--------------------
148 void ProxWidget::applyOperation()
150 //printf("ApplyOperation()");
151 save_restoreGB(GRAPH_SAVE
);
152 memcpy(GraphBuffer
, s_Buff
, sizeof(int) * GraphTraceLen
);
153 RepaintGraphWindow();
155 void ProxWidget::stickOperation()
157 save_restoreGB(GRAPH_RESTORE
);
158 //printf("stickOperation()");
160 void ProxWidget::vchange_autocorr(int v
)
163 ans
= AutoCorrelate(GraphBuffer
, s_Buff
, GraphTraceLen
, v
, true, false);
164 if (g_debugMode
) printf("vchange_autocorr(w:%d): %d\n", v
, ans
);
165 g_useOverlays
= true;
166 RepaintGraphWindow();
168 void ProxWidget::vchange_askedge(int v
)
171 //extern int AskEdgeDetect(const int *in, int *out, int len, int threshold);
172 ans
= AskEdgeDetect(GraphBuffer
, s_Buff
, GraphTraceLen
, v
);
173 if (g_debugMode
) printf("vchange_askedge(w:%d)%d\n", v
, ans
);
174 g_useOverlays
= true;
175 RepaintGraphWindow();
177 void ProxWidget::vchange_dthr_up(int v
)
179 int down
= opsController
->horizontalSlider_dirthr_down
->value();
180 directionalThreshold(GraphBuffer
, s_Buff
, GraphTraceLen
, v
, down
);
181 //printf("vchange_dthr_up(%d)", v);
182 g_useOverlays
= true;
183 RepaintGraphWindow();
185 void ProxWidget::vchange_dthr_down(int v
)
187 //printf("vchange_dthr_down(%d)", v);
188 int up
= opsController
->horizontalSlider_dirthr_up
->value();
189 directionalThreshold(GraphBuffer
,s_Buff
, GraphTraceLen
, v
, up
);
190 g_useOverlays
= true;
191 RepaintGraphWindow();
193 ProxWidget::ProxWidget(QWidget
*parent
, ProxGuiQT
*master
) : QWidget(parent
)
195 this->master
= master
;
198 // Setup the controller widget
199 controlWidget
= new QWidget();
200 opsController
= new Ui::Form();
201 opsController
->setupUi(controlWidget
);
202 //Due to quirks in QT Designer, we need to fiddle a bit
203 opsController
->horizontalSlider_dirthr_down
->setMinimum(-128);
204 opsController
->horizontalSlider_dirthr_down
->setMaximum(0);
205 opsController
->horizontalSlider_dirthr_down
->setValue(-20);
206 opsController
->horizontalSlider_dirthr_up
->setMinimum(-40);
207 opsController
->horizontalSlider_dirthr_up
->setMaximum(128);
208 opsController
->horizontalSlider_dirthr_up
->setValue(20);
209 opsController
->horizontalSlider_askedge
->setValue(25);
210 opsController
->horizontalSlider_window
->setValue(4000);
213 QObject::connect(opsController
->pushButton_apply
, SIGNAL(clicked()), this, SLOT(applyOperation()));
214 QObject::connect(opsController
->pushButton_sticky
, SIGNAL(clicked()), this, SLOT(stickOperation()));
215 QObject::connect(opsController
->horizontalSlider_window
, SIGNAL(valueChanged(int)), this, SLOT(vchange_autocorr(int)));
216 QObject::connect(opsController
->horizontalSlider_dirthr_up
, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_up(int)));
217 QObject::connect(opsController
->horizontalSlider_dirthr_down
, SIGNAL(valueChanged(int)), this, SLOT(vchange_dthr_down(int)));
218 QObject::connect(opsController
->horizontalSlider_askedge
, SIGNAL(valueChanged(int)), this, SLOT(vchange_askedge(int)));
220 // Set up the plot widget, which does the actual plotting
221 plot
= new Plot(this);
222 QVBoxLayout
*layout
= new QVBoxLayout
;
223 layout
->addWidget(plot
);
225 show(); // places the window on the screen.
227 // Move controller widget below plot
228 controlWidget
->move(x(),y()+frameSize().height());
229 controlWidget
->resize(size().width(), controlWidget
->size().height());
230 controlWidget
->show();
233 // not 100% sure what i need in this block
234 // feel free to fix - marshmellow...
235 ProxWidget::~ProxWidget(void)
238 controlWidget
->close();
239 delete controlWidget
;
240 controlWidget
= NULL
;
244 delete opsController
;
245 opsController
= NULL
;
254 void ProxWidget::closeEvent(QCloseEvent
*event
)
258 g_useOverlays
= false;
260 void ProxWidget::hideEvent(QHideEvent
*event
) {
261 controlWidget
->hide();
264 void ProxWidget::showEvent(QShowEvent
*event
) {
265 controlWidget
->show();
269 //----------- Plotting
271 int Plot::xCoordOf(int i
, QRect r
)
273 return r
.left() + (int)((i
- GraphStart
)*GraphPixelsPerPoint
);
276 int Plot::yCoordOf(int v
, QRect r
, int maxVal
)
278 int z
= (r
.bottom() - r
.top())/2;
279 if ( maxVal
== 0 ) maxVal
++;
280 return -(z
* v
) / maxVal
+ z
;
283 int Plot::valueOf_yCoord(int y
, QRect r
, int maxVal
)
285 int z
= (r
.bottom() - r
.top())/2;
286 return (y
-z
) * maxVal
/ z
;
288 static const QColor GREEN
= QColor(100,255,100);
289 static const QColor RED
= QColor(255,100,100);
290 static const QColor BLUE
= QColor(100,100,255);
291 static const QColor GRAY
= QColor(240,240,240);
293 QColor
Plot::getColor(int graphNum
)
296 case 0: return GREEN
; //Green
297 case 1: return RED
; //Red
298 case 2: return BLUE
; //Blue
299 default: return GRAY
; //Gray
303 void Plot::setMaxAndStart(int *buffer
, int len
, QRect plotRect
)
305 if (len
== 0) return;
306 startMax
= (len
- (int)((plotRect
.right() - plotRect
.left() - 40) / GraphPixelsPerPoint
));
310 if(GraphStart
> startMax
) {
311 GraphStart
= startMax
;
313 if (GraphStart
> len
) return;
314 int vMin
= INT_MAX
, vMax
= INT_MIN
, v
= 0;
315 int sample_index
= GraphStart
;
316 for( ; sample_index
< len
&& xCoordOf(sample_index
,plotRect
) < plotRect
.right() ; sample_index
++) {
318 v
= buffer
[sample_index
];
319 if(v
< vMin
) vMin
= v
;
320 if(v
> vMax
) vMax
= v
;
324 if(fabs( (double) vMin
) > g_absVMax
) g_absVMax
= (int)fabs( (double) vMin
);
325 if(fabs( (double) vMax
) > g_absVMax
) g_absVMax
= (int)fabs( (double) vMax
);
326 g_absVMax
= (int)(g_absVMax
*1.25 + 1);
329 void Plot::PlotDemod(uint8_t *buffer
, size_t len
, QRect plotRect
, QRect annotationRect
, QPainter
*painter
, int graphNum
, int plotOffset
)
331 if (len
== 0 || PlotGridX
<= 0) return;
332 //clock_t begin = clock();
333 QPainterPath penPath
;
335 int grid_delta_x
= PlotGridX
;
336 int first_delta_x
= grid_delta_x
; //(plotOffset > 0) ? PlotGridX : (PlotGridX +);
337 if (GraphStart
> plotOffset
) first_delta_x
-= (GraphStart
-plotOffset
);
338 int DemodStart
= GraphStart
;
339 if (plotOffset
> GraphStart
) DemodStart
= plotOffset
;
343 if (DemodStart
-plotOffset
> 0) BitStart
= (int)(((DemodStart
-plotOffset
)+(PlotGridX
-1))/PlotGridX
)-1;
344 first_delta_x
+= BitStart
* PlotGridX
;
345 if (BitStart
> (int)len
) return;
348 //printf("first_delta_x %i, grid_delta_x %i, DemodStart %i, BitStart %i\n",first_delta_x,grid_delta_x,DemodStart, BitStart);
350 painter
->setPen(getColor(graphNum
));
352 int absVMax
= (int)(100*1.05+1);
353 int x
= xCoordOf(DemodStart
, plotRect
);
354 int y
= yCoordOf((buffer
[BitStart
]*200-100)*-1,plotRect
,absVMax
);
355 penPath
.moveTo(x
, y
);
357 int clk
= first_delta_x
;
358 for(int i
= BitStart
; i
< (int)len
&& xCoordOf(delta_x
+DemodStart
, plotRect
) < plotRect
.right(); i
++) {
359 for (int ii
= 0; ii
< (clk
) && i
< (int)len
&& xCoordOf(DemodStart
+delta_x
+ii
, plotRect
) < plotRect
.right() ; ii
++ ) {
360 x
= xCoordOf(DemodStart
+delta_x
+ii
, plotRect
);
361 v
= buffer
[i
]*200-100;
363 y
= yCoordOf( v
, plotRect
, absVMax
);
365 penPath
.lineTo(x
, y
);
367 if(GraphPixelsPerPoint
> 10) {
368 QRect
f(QPoint(x
- 3, y
- 3),QPoint(x
+ 3, y
+ 3));
369 painter
->fillRect(f
, QColor(100, 255, 100));
371 if (ii
== (int)clk
/2) {
373 sprintf(str
, "%u",buffer
[i
]);
374 painter
->drawText(x
-8, y
+ ((buffer
[i
] > 0) ? 18 : -6), str
);
382 painter
->drawPath(penPath
);
385 void Plot::PlotGraph(int *buffer
, int len
, QRect plotRect
, QRect annotationRect
, QPainter
*painter
, int graphNum
)
387 if (len
== 0) return;
388 //clock_t begin = clock();
389 QPainterPath penPath
;
390 int vMin
= INT_MAX
, vMax
= INT_MIN
, vMean
= 0, v
= 0, i
= 0;
391 int x
= xCoordOf(GraphStart
, plotRect
);
392 int y
= yCoordOf(buffer
[GraphStart
],plotRect
,g_absVMax
);
393 penPath
.moveTo(x
, y
);
394 for(i
= GraphStart
; i
< len
&& xCoordOf(i
, plotRect
) < plotRect
.right(); i
++) {
396 x
= xCoordOf(i
, plotRect
);
399 y
= yCoordOf( v
, plotRect
, g_absVMax
);
401 penPath
.lineTo(x
, y
);
403 if(GraphPixelsPerPoint
> 10) {
404 QRect
f(QPoint(x
- 3, y
- 3),QPoint(x
+ 3, y
+ 3));
405 painter
->fillRect(f
, QColor(100, 255, 100));
408 if(v
< vMin
) vMin
= v
;
409 if(v
> vMax
) vMax
= v
;
412 vMean
/= (i
- GraphStart
);
414 painter
->setPen(getColor(graphNum
));
417 int xo
= 5+(graphNum
*40);
418 painter
->drawLine(xo
, plotRect
.top(),xo
, plotRect
.bottom());
420 int vMarkers
= (g_absVMax
- (g_absVMax
% 10)) / 5;
421 int minYDist
= 40; //Minimum pixel-distance between markers
428 for(v
= vMarkers
; yCoordOf(v
,plotRect
,g_absVMax
) > plotRect
.top() && n
< 20; v
+= vMarkers
,n
++)
430 int y0
= yCoordOf(v
,plotRect
,g_absVMax
);
431 int y1
= yCoordOf(-v
,plotRect
,g_absVMax
);
433 if(lasty0
- y0
< minYDist
) continue;
435 painter
->drawLine(xo
-5,y0
, xo
+5, y0
);
437 sprintf(yLbl
, "%d", v
);
438 painter
->drawText(xo
+8,y0
+7,yLbl
);
440 painter
->drawLine(xo
-5, y1
, xo
+5, y1
);
441 sprintf(yLbl
, "%d",-v
);
442 painter
->drawText(xo
+8, y1
+5 , yLbl
);
447 painter
->drawPath(penPath
);
449 sprintf(str
, "max=%d min=%d mean=%d n=%d/%d CursorAVal=[%d] CursorBVal=[%d]",
450 vMax
, vMin
, vMean
, i
, len
, buffer
[CursorAPos
], buffer
[CursorBPos
]);
451 painter
->drawText(20, annotationRect
.bottom() - 23 - 20 * graphNum
, str
);
453 //clock_t end = clock();
454 //double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC;
455 //printf("Plot time %f\n", elapsed_secs);
458 void Plot::plotGridLines(QPainter
* painter
,QRect r
)
461 if (PlotGridX
<= 0) return;
462 int offset
= GridOffset
;
463 if (GridLocked
&& PlotGridX
) {
464 offset
= GridOffset
+ PlotGridX
- (GraphStart
% PlotGridX
);
465 } else if (!GridLocked
&& GraphStart
> 0 && PlotGridX
) {
466 offset
= PlotGridX
-((GraphStart
- offset
) % PlotGridX
) + GraphStart
- unlockStart
;
469 if (offset
< 0) offset
+= PlotGridX
;
472 int grid_delta_x
= (int) (PlotGridX
* GraphPixelsPerPoint
);
473 int grid_delta_y
= PlotGridY
;
474 if ((PlotGridX
> 0) && ((PlotGridX
* GraphPixelsPerPoint
) > 1)) {
475 for(i
= (offset
* GraphPixelsPerPoint
); i
< r
.right(); i
+= grid_delta_x
) {
476 painter
->drawLine(r
.left()+i
, r
.top(), r
.left()+i
, r
.bottom());
480 for(i
= 0; yCoordOf(i
,r
,g_absVMax
) > r
.top(); i
+= grid_delta_y
) {
481 painter
->drawLine( r
.left(), yCoordOf(i
,r
,g_absVMax
), r
.right(), yCoordOf(i
,r
,g_absVMax
) );
482 painter
->drawLine( r
.left(), yCoordOf(i
*-1,r
,g_absVMax
), r
.right(), yCoordOf(i
*-1,r
,g_absVMax
) );
487 #define HEIGHT_INFO 60
488 #define WIDTH_AXES 80
490 void Plot::paintEvent(QPaintEvent
*event
)
493 QPainter
painter(this);
494 QBrush
brush(QColor(100, 255, 100));
495 QPen
pen(QColor(100, 255, 100));
497 painter
.setFont(QFont("Courier New", 10));
503 if (CursorAPos
> GraphTraceLen
)
505 if(CursorBPos
> GraphTraceLen
)
507 if(CursorCPos
> GraphTraceLen
)
509 if(CursorDPos
> GraphTraceLen
)
512 QRect
plotRect(WIDTH_AXES
, 0, width() - WIDTH_AXES
, height() - HEIGHT_INFO
);
513 QRect
infoRect(0, height() - HEIGHT_INFO
, width(), HEIGHT_INFO
);
514 PageWidth
= plotRect
.width() / GraphPixelsPerPoint
;
517 painter
.fillRect(rect(), QColor(60, 60, 60));
519 painter
.fillRect(plotRect
, QColor(0, 0, 0));
521 //init graph variables
522 setMaxAndStart(GraphBuffer
,GraphTraceLen
,plotRect
);
525 int zeroHeight
= plotRect
.top() + (plotRect
.bottom() - plotRect
.top()) / 2;
526 painter
.setPen(QColor(100, 100, 100));
527 painter
.drawLine(plotRect
.left(), zeroHeight
, plotRect
.right(), zeroHeight
);
528 // plot X and Y grid lines
529 plotGridLines(&painter
, plotRect
);
531 //Start painting graph
532 PlotGraph(GraphBuffer
, GraphTraceLen
,plotRect
,infoRect
,&painter
,0);
533 if (showDemod
&& DemodBufferLen
> 8) {
534 PlotDemod(DemodBuffer
, DemodBufferLen
,plotRect
,infoRect
,&painter
,2,g_DemodStartIdx
);
537 //init graph variables
538 setMaxAndStart(s_Buff
,GraphTraceLen
,plotRect
);
539 PlotGraph(s_Buff
, GraphTraceLen
,plotRect
,infoRect
,&painter
,1);
544 if(CursorAPos
> GraphStart
&& xCoordOf(CursorAPos
, plotRect
) < plotRect
.right())
546 painter
.setPen(QColor(255, 255, 0));
547 painter
.drawLine(xCoordOf(CursorAPos
, plotRect
),plotRect
.top(),xCoordOf(CursorAPos
, plotRect
),plotRect
.bottom());
549 if(CursorBPos
> GraphStart
&& xCoordOf(CursorBPos
, plotRect
) < plotRect
.right())
551 painter
.setPen(QColor(255, 0, 255));
552 painter
.drawLine(xCoordOf(CursorBPos
, plotRect
),plotRect
.top(),xCoordOf(CursorBPos
, plotRect
),plotRect
.bottom());
554 if(CursorCPos
> GraphStart
&& xCoordOf(CursorCPos
, plotRect
) < plotRect
.right())
556 painter
.setPen(QColor(255, 153, 0)); //orange
557 painter
.drawLine(xCoordOf(CursorCPos
, plotRect
),plotRect
.top(),xCoordOf(CursorCPos
, plotRect
),plotRect
.bottom());
559 if(CursorDPos
> GraphStart
&& xCoordOf(CursorDPos
, plotRect
) < plotRect
.right())
561 painter
.setPen(QColor(0, 0, 205)); //light blue
562 painter
.drawLine(xCoordOf(CursorDPos
, plotRect
),plotRect
.top(),xCoordOf(CursorDPos
, plotRect
),plotRect
.bottom());
567 sprintf(str
, "@%d dt=%d [%2.2f] zoom=%2.2f CursorAPos=%d CursorBPos=%d GridX=%d GridY=%d (%s) GridXoffset=%d",
568 GraphStart
, CursorBPos
- CursorAPos
, (CursorBPos
- CursorAPos
)/CursorScaleFactor
,
569 GraphPixelsPerPoint
,CursorAPos
,CursorBPos
,PlotGridXdefault
,PlotGridYdefault
,GridLocked
?"Locked":"Unlocked",GridOffset
);
570 painter
.setPen(QColor(255, 255, 255));
571 painter
.drawText(20, infoRect
.bottom() - 3, str
);
575 Plot::Plot(QWidget
*parent
) : QWidget(parent
), GraphStart(0), GraphPixelsPerPoint(1)
577 //Need to set this, otherwise we don't receive keypress events
578 setFocusPolicy( Qt::StrongFocus
);
581 QPalette
palette(QColor(0,0,0,0));
582 palette
.setColor(QPalette::WindowText
, QColor(255,255,255));
583 palette
.setColor(QPalette::Text
, QColor(255,255,255));
584 palette
.setColor(QPalette::Button
, QColor(100, 100, 100));
586 setAutoFillBackground(true);
590 setWindowTitle(tr("Sliders"));
595 void Plot::closeEvent(QCloseEvent
*event
)
599 g_useOverlays
= false;
602 void Plot::mouseMoveEvent(QMouseEvent
*event
)
606 x
= (int)(x
/ GraphPixelsPerPoint
);
608 if((event
->buttons() & Qt::LeftButton
)) {
610 } else if (event
->buttons() & Qt::RightButton
) {
618 void Plot::keyPressEvent(QKeyEvent
*event
)
620 int offset
; // Left/right movement offset (in sample size)
622 if(event
->modifiers() & Qt::ShiftModifier
) {
624 offset
= PageWidth
- (PageWidth
% PlotGridX
);
628 if(event
->modifiers() & Qt::ControlModifier
)
631 offset
= (int)(20 / GraphPixelsPerPoint
);
633 switch(event
->key()) {
635 if(GraphPixelsPerPoint
<= 50) {
636 GraphPixelsPerPoint
*= 2;
641 if(GraphPixelsPerPoint
>= 0.02) {
642 GraphPixelsPerPoint
/= 2;
647 if(GraphPixelsPerPoint
< 20) {
648 GraphStart
+= offset
;
655 if(GraphPixelsPerPoint
< 20) {
656 GraphStart
-= offset
;
663 if(PlotGridX
|| PlotGridY
) {
667 PlotGridX
= PlotGridXdefault
;
668 PlotGridY
= PlotGridYdefault
;
673 puts("Plot Window Keystrokes:\n");
674 puts(" Key Action\n");
675 puts(" UP Zoom out");
676 puts(" DOWN Zoom in");
677 puts(" G Toggle grid display");
678 puts(" H Show help");
679 puts(" L Toggle lock grid relative to samples");
680 puts(" Q Hide window");
681 puts(" HOME Move to the start of the graph");
682 puts(" END Move to the end of the graph");
683 puts(" LEFT Move left");
684 puts(" <CTL>LEFT Move left 1 sample");
685 puts(" <SHIFT>LEFT Page left");
686 puts(" LEFT-MOUSE-CLICK Set yellow cursor");
687 puts(" RIGHT Move right");
688 puts(" <CTL>RIGHT Move right 1 sample");
689 puts(" <SHIFT>RIGHT Page right");
690 puts(" RIGHT-MOUSE-CLICK Set purple cursor");
692 puts("Use client window 'data help' for more plot commands\n");
696 GridLocked
= !GridLocked
;
698 GridOffset
+= (GraphStart
- unlockStart
);
700 unlockStart
= GraphStart
;
712 GraphStart
= startMax
;
716 QWidget::keyPressEvent(event
);