RTXI 1.3
plugins/oscilloscope/scope.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines