![]() |
RTXI 1.3
|
00001 /* 00002 Copyright (C) 2011 Georgia Institute of Technology, University of Utah, Weill Cornell Medical College 00003 00004 This program is free software: you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation, either version 3 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program. If not, see <http://www.gnu.org/licenses/>. 00016 00017 */ 00018 00019 #include "scope.h" 00020 00021 #include <debug.h> 00022 00023 #include <qpainter.h> 00024 #include <qtimer.h> 00025 00026 #include <cmath> 00027 #include <stdlib.h> 00028 00029 Scope::Channel::Channel(void) {} 00030 00031 Scope::Channel::~Channel(void) {} 00032 00033 void *Scope::Channel::getInfo(void) { 00034 return info; 00035 } 00036 00037 const void *Scope::Channel::getInfo(void) const { 00038 return info; 00039 } 00040 00041 QPen Scope::Channel::getPen(void) const { 00042 return pen; 00043 } 00044 00045 double Scope::Channel::getScale(void) const { 00046 return scale; 00047 } 00048 00049 double Scope::Channel::getOffset(void) const { 00050 return offset; 00051 } 00052 00053 QString Scope::Channel::getLabel(void) const { 00054 return label; 00055 } 00056 00057 Scope::Scope(QWidget *parent,Qt::WFlags flags) 00058 : QWidget(parent,NULL,flags) { 00059 setBackgroundMode(Qt::NoBackground); 00060 00061 setMinimumSize(16,9); 00062 00063 background.setOptimization(QPixmap::BestOptim); 00064 foreground.setOptimization(QPixmap::BestOptim); 00065 00066 isPaused = false; 00067 00068 drawZero = true; 00069 divX = 16; 00070 divY = 10; 00071 00072 data_idx = 0; 00073 data_size = 100; 00074 00075 hScl = 10.0; 00076 period = 1.0; 00077 00078 dtLabel = "10ms"; 00079 00080 refresh = 250; 00081 00082 triggering = false; 00083 triggerHolding = false; 00084 triggerDirection = NONE; 00085 triggerThreshold = 0.0; 00086 triggerHoldoff = 5.0; 00087 triggerLast = (size_t)(-1); 00088 triggerChannel = channels.end(); 00089 00090 timer = new QTimer(this); 00091 QObject::connect(timer,SIGNAL(timeout(void)),this,SLOT(timeoutEvent(void))); 00092 timer->start(refresh); 00093 } 00094 00095 Scope::~Scope(void) {} 00096 00097 bool Scope::paused(void) const { 00098 return isPaused; 00099 } 00100 00101 #include <rt.h> 00102 00103 void Scope::timeoutEvent(void) { 00104 if(!triggering) 00105 update(drawForeground()); 00106 } 00107 00108 void Scope::togglePause(void) { 00109 isPaused = !isPaused; 00110 } 00111 00112 std::list<Scope::Channel>::iterator Scope::insertChannel(QString label,double scale,double offset,const QPen &pen,void *info) { 00113 struct Channel channel; 00114 00115 channel.label = label; 00116 channel.scale = scale; 00117 channel.offset = offset; 00118 channel.pen = pen; 00119 channel.info = info; 00120 channel.data.resize(data_size,0.0); 00121 00122 channels.push_back(channel); 00123 00124 refreshBackground(); 00125 00126 return --channels.end(); 00127 } 00128 00129 void *Scope::removeChannel(std::list<Scope::Channel>::iterator channel) { 00130 void *info = channel->info; 00131 channels.erase(channel); 00132 00133 refreshBackground(); 00134 00135 return info; 00136 } 00137 00138 size_t Scope::getChannelCount(void) const { 00139 return channels.size(); 00140 } 00141 00142 std::list<Scope::Channel>::iterator Scope::getChannelsBegin(void) { 00143 return channels.begin(); 00144 } 00145 00146 std::list<Scope::Channel>::iterator Scope::getChannelsEnd(void) { 00147 return channels.end(); 00148 } 00149 00150 std::list<Scope::Channel>::const_iterator Scope::getChannelsBegin(void) const { 00151 return channels.begin(); 00152 } 00153 00154 std::list<Scope::Channel>::const_iterator Scope::getChannelsEnd(void) const { 00155 return channels.end(); 00156 } 00157 00158 void Scope::clearData(void) { 00159 for(std::list<Channel>::iterator i = channels.begin(), end = channels.end();i != end;++i) 00160 for(size_t j = 0;j < data_size;++j) 00161 i->data[j] = 0.0; 00162 } 00163 00164 void Scope::setData(double data[],size_t size) { 00165 if(isPaused) 00166 return; 00167 00168 if(size < getChannelCount()) { 00169 ERROR_MSG("Scope::setData() : data size mismatch detected\n"); 00170 return; 00171 } 00172 00173 size_t index = 0; 00174 for(std::list<Channel>::iterator i = channels.begin(), end = channels.end();i != end;++i) { 00175 i->data[data_idx] = data[index++]; 00176 00177 if(triggering && i == triggerChannel && 00178 ((triggerDirection == POS && i->data[data_idx-1] < triggerThreshold && i->data[data_idx] > triggerThreshold) || 00179 (triggerDirection == NEG && i->data[data_idx-1] > triggerThreshold && i->data[data_idx] < triggerThreshold))) { 00180 triggerQueue.push_back(data_idx); 00181 } 00182 } 00183 00184 ++data_idx %= data_size; 00185 00186 if(triggering && !triggerQueue.empty() && (data_idx+2)%data_size == triggerQueue.front()) { 00187 if(triggerLast != (size_t)(-1) && (triggerQueue.front()+data_size-triggerLast)%data_size*period < triggerHoldoff) 00188 triggerQueue.pop_front(); 00189 else { 00190 triggerLast = triggerQueue.front(); 00191 triggerQueue.pop_front(); 00192 00193 if(!triggerHolding) 00194 foreground = background; 00195 00196 QPainter painter(&foreground); 00197 00198 size_t x, y; 00199 double scale; 00200 for(std::list<Channel>::iterator i = channels.begin(), iend = channels.end();i != iend;++i) { 00201 scale = height()/(i->scale*divY); 00202 painter.setPen(i->getPen()); 00203 x = 0; 00204 y = round(height()/2-scale*(i->data[data_idx]+i->offset)); 00205 painter.moveTo(x,y); 00206 for(size_t j = 1;j<i->data.size();++j) { 00207 x = round(((j*period)*width())/(hScl*divX)); 00208 y = round(height()/2-scale*(i->data[(data_idx+j)%data_size]+i->offset)); 00209 painter.lineTo(x,y); 00210 if(x >= width()) break; 00211 } 00212 } 00213 update(); 00214 } 00215 } 00216 } 00217 00218 size_t Scope::getDataSize(void) const { 00219 return data_size; 00220 } 00221 00222 void Scope::setDataSize(size_t size) { 00223 for(std::list<Channel>::iterator i = channels.begin(), end = channels.end();i != end;++i) { 00224 i->data.clear(); 00225 i->data.resize(size,0.0); 00226 } 00227 data_idx = 0; 00228 data_size = size; 00229 triggerQueue.clear(); 00230 } 00231 00232 Scope::trig_t Scope::getTriggerDirection(void) { 00233 return triggerDirection; 00234 } 00235 00236 double Scope::getTriggerThreshold(void) { 00237 return triggerThreshold; 00238 } 00239 00240 std::list<Scope::Channel>::iterator Scope::getTriggerChannel(void) { 00241 return triggerChannel; 00242 } 00243 00244 bool Scope::getTriggerHolding(void) { 00245 return triggerHolding; 00246 } 00247 00248 double Scope::getTriggerHoldoff(void) { 00249 return triggerHoldoff; 00250 } 00251 00252 void Scope::setTrigger(trig_t direction,double threshold,std::list<Channel>::iterator channel,bool holding,double holdoff) { 00253 triggerHolding = holding; 00254 triggerHoldoff = holdoff; 00255 triggerLast = (size_t)(-1); 00256 00257 if(triggerChannel != channel || triggerThreshold != threshold) { 00258 triggerChannel = channel; 00259 triggerThreshold = threshold; 00260 00261 refreshBackground(); 00262 } 00263 00264 if(triggerDirection != direction) { 00265 if(direction == NONE) { 00266 triggering = false; 00267 timer->start(refresh); 00268 triggerQueue.clear(); 00269 } else { 00270 triggering = true; 00271 timer->stop(); 00272 00273 foreground = background; 00274 update(); 00275 } 00276 triggerDirection = direction; 00277 } 00278 } 00279 00280 double Scope::getDivT(void) const { 00281 return hScl; 00282 } 00283 00284 void Scope::setDivT(double divT) { 00285 hScl = divT; 00286 QChar mu = QChar(0x3BC); 00287 if(divT >= 1000.) 00288 dtLabel = QString::number(divT*1e-3)+"s"; 00289 else if(divT >= 1.) 00290 dtLabel = QString::number(divT)+"ms"; 00291 else if(divT >= 1e-3) 00292 dtLabel = QString::number(divT*1e3)+mu+"s"; 00293 else 00294 dtLabel = QString::number(divT*1e6)+"ns"; 00295 00296 refreshBackground(); 00297 } 00298 00299 void Scope::setPeriod(double p) { 00300 period = p; 00301 } 00302 00303 size_t Scope::getDivX(void) const { 00304 return divX; 00305 } 00306 00307 size_t Scope::getDivY(void) const { 00308 return divY; 00309 } 00310 00311 void Scope::setDivXY(size_t dx,size_t dy) { 00312 divX = dx; 00313 divY = dy; 00314 00315 drawBackground(); 00316 if(triggering) 00317 foreground = background; 00318 else 00319 drawForeground(); 00320 update(); 00321 } 00322 00323 size_t Scope::getRefresh(void) const { 00324 return refresh; 00325 } 00326 00327 void Scope::setRefresh(size_t r) { 00328 refresh = r; 00329 timer->changeInterval(refresh); 00330 } 00331 00332 void Scope::setChannelScale(std::list<Channel>::iterator channel,double scale) { 00333 channel->scale = scale; 00334 } 00335 00336 void Scope::setChannelOffset(std::list<Channel>::iterator channel,double offset) { 00337 channel->offset = offset; 00338 } 00339 00340 void Scope::setChannelPen(std::list<Channel>::iterator channel,const QPen &pen) { 00341 channel->pen = pen; 00342 refreshBackground(); 00343 } 00344 00345 void Scope::setChannelLabel(std::list<Channel>::iterator channel,const QString &label) { 00346 channel->label = label; 00347 refreshBackground(); 00348 } 00349 00350 void Scope::paintEvent(QPaintEvent *e) { 00351 bitBlt(this,e->rect().topLeft(),&foreground,e->rect(),Qt::CopyROP); 00352 } 00353 00354 void Scope::resizeEvent(QResizeEvent *) { 00355 refreshBackground(); 00356 } 00357 00358 void Scope::drawBackground(void) { 00359 int xDiv = static_cast<int>(round(1.0*width()/divX)); 00360 int yDiv = static_cast<int>(round(1.0*height()/divY)); 00361 int zero = static_cast<int>(round(height()/2.0)); 00362 00363 background.resize(width(),height()); 00364 background.fill(Qt::white); 00365 00366 QPainter painter(&background); 00367 00368 if(drawZero) { 00369 painter.setPen(QPen(Qt::black,3,Qt::DashDotLine)); 00370 painter.drawLine(0,zero,width(),zero); 00371 } 00372 00373 painter.setPen(QPen(Qt::black,1,Qt::DotLine)); 00374 for(int i=yDiv;i<height()-yDiv/2;i+=yDiv) 00375 if(!drawZero || abs(i-zero) >= yDiv/3) 00376 painter.drawLine(0,i,width(),i); 00377 for(int i=xDiv;i<width()-xDiv/2;i+=xDiv) 00378 painter.drawLine(i,0,i,height()); 00379 00380 positionLabels(painter); 00381 00382 if(triggerChannel != channels.end()) { 00383 painter.setPen(QPen(Qt::yellow,2,Qt::DashLine)); 00384 double scale = height()/(triggerChannel->scale*divY); 00385 double offset = triggerChannel->offset; 00386 int thresh = round(height()/2-scale*(triggerThreshold+offset)); 00387 painter.drawLine(0,thresh,width(),thresh); 00388 } 00389 } 00390 00391 QRect Scope::drawForeground(void) { 00392 foreground = background; 00393 QPainter painter(&foreground); 00394 00395 int x, y; 00396 int miny = height(), maxy = 0; 00397 double scale; 00398 for(std::list<Channel>::iterator i = channels.begin(), iend = channels.end();i != iend;++i) { 00399 scale = height()/(i->scale*divY); 00400 painter.setPen(i->getPen()); 00401 x = 0; 00402 y = round(height()/2-scale*(i->data[(data_idx)%i->data.size()]+i->offset)); 00403 if(y < miny) miny = y; 00404 if(y > maxy) maxy = y; 00405 painter.moveTo(x,y); 00406 for(size_t j = 1;j<i->data.size();++j) { 00407 x = round(((j*period)*width())/(hScl*divX)); 00408 y = round(height()/2-scale*(i->data[(data_idx+j)%i->data.size()]+i->offset)); 00409 if(y < miny) miny = y; 00410 if(y > maxy) maxy = y; 00411 painter.lineTo(x,y); 00412 if(x >= width()) break; 00413 } 00414 } 00415 00416 QRect newDrawRect; 00417 if(miny <= maxy) 00418 newDrawRect.setRect(0,miny-1,width(),maxy-miny+2); 00419 QRect redrawRect = newDrawRect.unite(drawRect); 00420 drawRect = newDrawRect; 00421 00422 return redrawRect; 00423 } 00424 00425 void Scope::positionLabels(QPainter &painter) { 00426 QRect bound; 00427 00428 if(getChannelCount()) { 00429 int maxh = 1, maxw = 1; 00430 for(std::list<Channel>::iterator i = channels.begin(),end = channels.end();i != end;++i) { 00431 bound = painter.boundingRect(rect(),0,i->label); 00432 if(maxw < bound.width()+25) maxw = bound.width()+25; 00433 if(maxh < bound.height()) maxh = bound.height(); 00434 } 00435 00436 size_t cols = (width()-painter.boundingRect(rect(),0,dtLabel).width()-25)/maxw; 00437 00438 size_t col = 0, row = 0; 00439 for(std::list<Channel>::iterator i = channels.begin(),end = channels.end();i != end;++i) { 00440 painter.setPen(i->getPen()); 00441 painter.drawText(col*maxw,static_cast<int>(floor(height()-(row+1)*1.5*maxh)),i->label); 00442 if(++col >= cols) { 00443 ++row; 00444 col = 0; 00445 } 00446 } 00447 } 00448 00449 bound = painter.boundingRect(rect(),0,dtLabel); 00450 painter.setPen(QPen(Qt::black,1,Qt::SolidLine)); 00451 painter.drawText(static_cast<int>(floor(width()-bound.width()-10)),static_cast<int>(floor(height()-1.5*bound.height())),dtLabel); 00452 } 00453 00454 void Scope::refreshBackground(void) { 00455 drawBackground(); 00456 if(triggering) 00457 foreground = background; 00458 else 00459 drawForeground(); 00460 update(); 00461 }