00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "oscilloscope.h"
00012 #include "oscilloscopedata.h"
00013 #include "oscilloscopeview.h"
00014 #include "probepositioner.h"
00015 #include "simulator.h"
00016
00017 #include <kconfig.h>
00018 #include <kdebug.h>
00019 #include <klocale.h>
00020 #include <kglobal.h>
00021 #include <kpopupmenu.h>
00022 #include <qcheckbox.h>
00023 #include <qcursor.h>
00024 #include <qevent.h>
00025 #include <qlabel.h>
00026 #include <qpainter.h>
00027 #include <qpixmap.h>
00028 #include <qscrollbar.h>
00029 #include <qtimer.h>
00030
00031 #include <algorithm>
00032 #include <cmath>
00033
00034 inline uint64_t min( uint64_t a, uint64_t b )
00035 {
00036 return a < b ? a : b;
00037 }
00038
00039
00040 OscilloscopeView::OscilloscopeView( QWidget *parent, const char *name )
00041 : QFrame( parent, name, WNoAutoErase ),
00042 b_needRedraw(true),
00043 m_pixmap(0),
00044 m_fps(10),
00045 m_sliderValueAtClick(-1),
00046 m_clickOffsetPos(-1),
00047 m_pSimulator( Simulator::self() ),
00048 m_halfOutputHeight(0.0)
00049 {
00050 KGlobal::config()->setGroup("Oscilloscope");
00051 m_fps = KGlobal::config()->readNumEntry( "FPS", 25 );
00052
00053 setBackgroundMode(NoBackground);
00054 setMouseTracking(true);
00055
00056 m_updateViewTmr = new QTimer(this);
00057 connect( m_updateViewTmr, SIGNAL(timeout()), this, SLOT(updateViewTimeout()) );
00058 }
00059
00060 OscilloscopeView::~OscilloscopeView()
00061 {
00062 delete m_pixmap;
00063 }
00064
00065 void OscilloscopeView::updateView()
00066 {
00067 if (m_updateViewTmr->isActive() ) return;
00068
00069 m_updateViewTmr->start( 1000/m_fps, true );
00070 }
00071
00072 void OscilloscopeView::updateViewTimeout()
00073 {
00074 b_needRedraw = true;
00075 repaint(false);
00076 updateTimeLabel();
00077 }
00078
00079
00080 void OscilloscopeView::updateTimeLabel()
00081 {
00082 if ( hasMouse() ) {
00083 int x = mapFromGlobal( QCursor::pos() ).x();
00084 double time = (double(Oscilloscope::self()->scrollTime()) / LOGIC_UPDATE_RATE) + (x / Oscilloscope::self()->pixelsPerSecond());
00085 Oscilloscope::self()->timeLabel->setText( QString::number( time, 'f', 6 ) );
00086 } else Oscilloscope::self()->timeLabel->setText( QString::null );
00087 }
00088
00089
00090 void OscilloscopeView::resizeEvent( QResizeEvent *e )
00091 {
00092 delete m_pixmap;
00093 m_pixmap = new QPixmap( e->size() );
00094 b_needRedraw = true;
00095 QFrame::resizeEvent(e);
00096 }
00097
00098
00099 void OscilloscopeView::mousePressEvent( QMouseEvent *event )
00100 {
00101 switch ( event->button() )
00102 {
00103 case Qt::LeftButton:
00104 {
00105 event->accept();
00106 m_clickOffsetPos = event->pos().x();
00107 m_sliderValueAtClick = Oscilloscope::self()->horizontalScroll->value();
00108 setCursor( Qt::SizeAllCursor );
00109 return;
00110 }
00111
00112 case Qt::RightButton:
00113 {
00114 event->accept();
00115
00116 KPopupMenu fpsMenu;
00117 fpsMenu.insertTitle( i18n("Framerate") );
00118
00119 const int fps[] = { 10, 25, 50, 75, 100 };
00120
00121 for ( uint i=0; i<5; ++i )
00122 {
00123 const int num = fps[i];
00124 fpsMenu.insertItem( i18n("%1 fps").arg(num), num );
00125 fpsMenu.setItemChecked( num, num == m_fps );
00126 }
00127
00128 connect( &fpsMenu, SIGNAL(activated(int )), this, SLOT(slotSetFrameRate(int )) );
00129 fpsMenu.exec( event->globalPos() );
00130 return;
00131 }
00132
00133 default:
00134 {
00135 QFrame::mousePressEvent(event);
00136 return;
00137 }
00138 }
00139 }
00140
00141
00142 void OscilloscopeView::mouseMoveEvent( QMouseEvent *event )
00143 {
00144 event->accept();
00145 updateTimeLabel();
00146
00147 if ( m_sliderValueAtClick != -1 )
00148 {
00149 int dx = event->pos().x() - m_clickOffsetPos;
00150 int dTick = int( dx * Oscilloscope::self()->sliderTicksPerSecond() / Oscilloscope::self()->pixelsPerSecond() );
00151 Oscilloscope::self()->horizontalScroll->setValue( m_sliderValueAtClick - dTick );
00152 }
00153 }
00154
00155
00156 void OscilloscopeView::mouseReleaseEvent( QMouseEvent *event )
00157 {
00158 if ( m_sliderValueAtClick == -1 )
00159 return QFrame::mouseReleaseEvent(event);
00160
00161 event->accept();
00162 m_sliderValueAtClick = -1;
00163 setCursor( Qt::ArrowCursor );
00164 }
00165
00166
00167 void OscilloscopeView::slotSetFrameRate( int fps )
00168 {
00169 m_fps = fps;
00170 KGlobal::config()->setGroup("Oscilloscope");
00171 KGlobal::config()->writeEntry( "FPS", m_fps );
00172 }
00173
00174
00175
00176 static double lld_modulus( int64_t a, double b )
00177 {
00178 return double(a) - int64_t(a/b)*b;
00179 }
00180
00181
00182 void OscilloscopeView::paintEvent( QPaintEvent *e )
00183 {
00184 QRect r = e->rect();
00185
00186 if (b_needRedraw)
00187 {
00188 updateOutputHeight();
00189 const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond();
00190
00191 QPainter p;
00192 m_pixmap->fill( paletteBackgroundColor() );
00193 p.begin(m_pixmap);
00194 p.setClipRegion(e->region());
00195
00196
00197 const double divisions = 5.0;
00198 const double min_sep = 10.0;
00199
00200 double spacing = pixelsPerSecond/(std::pow( divisions, std::floor(std::log(pixelsPerSecond/min_sep)/std::log(divisions)) ));
00201
00202
00203 const int64_t pixelsOffset = int64_t(Oscilloscope::self()->scrollTime()*pixelsPerSecond/LOGIC_UPDATE_RATE);
00204 double linesOffset = - lld_modulus( pixelsOffset, spacing );
00205
00206 int blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions));
00207 p.setPen( QColor( blackness, blackness, blackness ) );
00208
00209 for ( double i = linesOffset; i <= frameRect().width(); i += spacing )
00210 p.drawLine( int(i), 1, int(i), frameRect().height()-2 );
00211
00212
00213
00214 spacing *= divisions;
00215 linesOffset = - lld_modulus( pixelsOffset, spacing );
00216
00217 blackness = 256 - int(184.0 * spacing / (min_sep*divisions*divisions));
00218 p.setPen( QColor( blackness, blackness, blackness ) );
00219
00220 for ( double i = linesOffset; i <= frameRect().width(); i += spacing )
00221 p.drawLine( int(i), 1, int(i), frameRect().height()-2 );
00222
00223
00224
00225 spacing *= divisions;
00226 linesOffset = - lld_modulus( pixelsOffset, spacing );
00227
00228 blackness = 256 - int(184.0);
00229 p.setPen( QColor( blackness, blackness, blackness ) );
00230
00231 for ( double i = linesOffset; i <= frameRect().width(); i += spacing )
00232 p.drawLine( int(i), 1, int(i), frameRect().height()-2 );
00233
00234
00235 drawLogicData(p);
00236 drawFloatingData(p);
00237
00238 p.setPen(Qt::black);
00239 p.drawRect( frameRect() );
00240
00241 b_needRedraw = false;
00242 }
00243
00244 bitBlt( this, r.x(), r.y(), m_pixmap, r.x(), r.y(), r.width(), r.height() );
00245 }
00246
00247
00248 void OscilloscopeView::updateOutputHeight()
00249 {
00250 m_halfOutputHeight = int((Oscilloscope::self()->probePositioner->probeOutputHeight() - (probeArrowWidth/Oscilloscope::self()->numberOfProbes()))/2)-1;
00251 }
00252
00253
00254 void OscilloscopeView::drawLogicData( QPainter & p )
00255 {
00256 const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond();
00257
00258 const LogicProbeDataMap::iterator end = Oscilloscope::self()->m_logicProbeDataMap.end();
00259 for ( LogicProbeDataMap::iterator it = Oscilloscope::self()->m_logicProbeDataMap.begin(); it != end; ++it )
00260 {
00261
00262
00263
00264
00265
00266
00267 int64_t deltaAt = 1;
00268 int totalDeltaAt = 1;
00269
00270 LogicProbeData * probe = it.data();
00271
00272 vector<LogicDataPoint> *data = probe->m_data;
00273 if(!data->size()) continue;
00274
00275 const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe);
00276 const int64_t timeOffset = Oscilloscope::self()->scrollTime();
00277
00278
00279 p.setPen( QColor( 228, 228, 228 ) );
00280 p.drawLine( 0, midHeight, width(), midHeight );
00281
00282
00283 p.setPen( probe->color() );
00284
00285
00286 const int minTimeStep = int(LOGIC_UPDATE_RATE/pixelsPerSecond);
00287
00288 int64_t at = probe->findPos(timeOffset);
00289 const int64_t maxAt = probe->m_data->size();
00290 int64_t prevTime = (*data)[at].time;
00291 int prevX = (at > 0) ? 0 : int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE));
00292 bool prevHigh = (*data)[at].value;
00293 int prevY = midHeight + int(prevHigh ? -m_halfOutputHeight : +m_halfOutputHeight);
00294
00295 while ( at < maxAt ) {
00296
00297 int64_t previousAt = at;
00298 int64_t dAt = deltaAt / totalDeltaAt;
00299
00300 while ( (dAt > 1) && (at < maxAt) && ( (int64_t((*data)[at].time) - prevTime) != minTimeStep ) )
00301 {
00302
00303 while ( at < maxAt && ( int64_t((*data)[at].time) - prevTime ) < minTimeStep )
00304 at += dAt;
00305 dAt /= 2;
00306
00307
00308 while ( (at < maxAt) && ( int64_t((*data)[at].time) - prevTime ) > minTimeStep )
00309 {
00310 at -= dAt;
00311 if ( at < 0 )
00312 at = 0;
00313 }
00314 dAt /= 2;
00315 }
00316
00317
00318 while ( (previousAt == at) || ((at < maxAt) && ( int64_t((*data)[at].time) - prevTime ) < minTimeStep) )
00319 at++;
00320
00321 if ( at >= maxAt ) break;
00322
00323
00324 deltaAt += at - previousAt;
00325 totalDeltaAt++;
00326
00327 bool nextHigh = (*data)[at].value;
00328 if ( nextHigh == prevHigh ) continue;
00329
00330 int64_t nextTime = (*data)[at].time;
00331 int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE));
00332 int nextY = midHeight + int(nextHigh ? -m_halfOutputHeight : +m_halfOutputHeight);
00333
00334 p.drawLine( prevX, prevY, nextX, prevY );
00335 p.drawLine( nextX, prevY, nextX, nextY );
00336
00337 prevHigh = nextHigh;
00338 prevTime = nextTime;
00339 prevX = nextX;
00340 prevY = nextY;
00341
00342 if ( nextX > width() ) break;
00343 };
00344
00345
00346
00347 if ( prevX < width() )
00348 p.drawLine( prevX, prevY, width(), prevY );
00349 }
00350 }
00351
00352 #define v_to_y int(midHeight - (logarithmic ? ( (v>0) ? log(v/lowerAbsValue) : -log(-v/lowerAbsValue) ) : v) * sf)
00353
00354 void OscilloscopeView::drawFloatingData(QPainter &p)
00355 {
00356 const double pixelsPerSecond = Oscilloscope::self()->pixelsPerSecond();
00357
00358 const FloatingProbeDataMap::iterator end = Oscilloscope::self()->m_floatingProbeDataMap.end();
00359 for(FloatingProbeDataMap::iterator it = Oscilloscope::self()->m_floatingProbeDataMap.begin(); it != end; ++it ) {
00360 FloatingProbeData * probe = it.data();
00361 vector<float> *data = probe->m_data;
00362
00363 if(!data->size()) continue;
00364
00365 bool logarithmic = probe->scaling() == FloatingProbeData::Logarithmic;
00366 double lowerAbsValue = probe->lowerAbsValue();
00367 double sf = m_halfOutputHeight / (logarithmic ? log(probe->upperAbsValue()/lowerAbsValue) : probe->upperAbsValue());
00368
00369 const int midHeight = Oscilloscope::self()->probePositioner->probePosition(probe);
00370 const int64_t timeOffset = Oscilloscope::self()->scrollTime();
00371
00372
00373 p.setPen( QColor( 228, 228, 228 ) );
00374 p.drawLine( 0, midHeight, width(), midHeight );
00375
00376
00377 p.setPen( probe->color() );
00378
00379 int64_t at = probe->findPos(timeOffset);
00380 const int64_t maxAt = probe->m_data->size();
00381 if(at > maxAt) at = maxAt;
00382 int64_t prevTime = probe->toTime(at);
00383
00384 double v = (*data)[(at>0)?at:0];
00385 int prevY = v_to_y;
00386 int prevX = int((prevTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE));
00387
00388 while ( at < maxAt ) {
00389 at++;
00390
00391 uint64_t nextTime = prevTime + uint64_t(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE);
00392
00393 double v = (*data)[(at>0)?at:0];
00394 int nextY = v_to_y;
00395 int nextX = int((nextTime - timeOffset)*(pixelsPerSecond/LOGIC_UPDATE_RATE));
00396
00397 p.drawLine( prevX, prevY, nextX, nextY );
00398
00399 prevTime = nextTime;
00400 prevX = nextX;
00401 prevY = nextY;
00402
00403 if ( nextX > width() ) break;
00404 };
00405
00406
00407
00408 if ( prevX < width() )
00409 p.drawLine( prevX, prevY, width(), prevY );
00410 }
00411 }
00412
00413 #include "oscilloscopeview.moc"