RTXI  2.4
The Real-Time eXperiment Interface Documentation
scope.cpp
Go to the documentation of this file.
1 /*
2  The Real-Time eXperiment Interface (RTXI)
3  Copyright (C) 2011 Georgia Institute of Technology, University of Utah, Weill Cornell Medical College
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <rt.h>
20 #include <debug.h>
21 #include <cmath>
22 #include <stdlib.h>
23 #include <main_window.h>
24 
25 #include <qwt_abstract_scale_draw.h>
26 
27 #include "scope.h"
28 
29 #define fs 1/(period*1e-3)
30 
31 // Constructor for a channel
33 
34 // Destructor for a channel
36 
37 // Returns channel information
39 {
40  return info;
41 }
42 
43 // Return read-only version of channel information
44 const void *Scope::Channel::getInfo(void) const
45 {
46  return info;
47 }
48 
49 // Returns channel pen
50 QPen Scope::Channel::getPen(void) const
51 {
52  return this->curve->pen();
53 }
54 
55 // Returns channel scale
56 double Scope::Channel::getScale(void) const
57 {
58  return scale;
59 }
60 
61 // Returns channel offset
62 double Scope::Channel::getOffset(void) const
63 {
64  return offset;
65 }
66 
67 // Returns channel label
68 QString Scope::Channel::getLabel(void) const
69 {
70  return label;
71 }
72 
73 // Scope constructor; inherits from QwtPlot
74 Scope::Scope(QWidget *parent) : QwtPlot(parent), legendItem(NULL)
75 {
76 
77  // Initialize vars
78  isPaused = false;
79  divX = 10;
80  divY = 10;
81  data_idx = 0;
82  data_size = 100;
83  hScl = 1.0;
84  period = 1.0;
85  dtLabel = "1ms";
86  refresh = 250;
87  triggering = false;
88  triggerDirection = Scope::NONE;
89  triggerThreshold = 0.0;
90  triggerChannel = channels.end();
91  triggerWindow = 10.0;
92 
93  // Initialize director
94  d_directPainter = new QwtPlotDirectPainter();
95  plotLayout()->setAlignCanvasToScales(true);
96  setAutoReplot(false);
97 
98  // Set scope canvas
99  setCanvas(new Canvas());
100 
101  // Setup grid
102  grid = new QwtPlotGrid();
103  grid->setPen(Qt::gray, 0, Qt::DotLine);
104  grid->attach(this);
105 
106  // Set division limits on the scope
107  setAxisMaxMajor(QwtPlot::xBottom, divX);
108  setAxisMaxMajor(QwtPlot::yLeft, divY);
109 
110  // Disable axes
111  enableAxis(QwtPlot::yLeft, false);
112  enableAxis(QwtPlot::xBottom, false);
113 
114  // Statically set y interval
115  setAxisScale(QwtPlot::yLeft, -1.0, 1.0);
116  setAxisScale(QwtPlot::xBottom, 0.0, 1000.0);
117 
118  // Disable autoscaling
119  setAxisAutoScale(QwtPlot::yLeft, false);
120  setAxisAutoScale(QwtPlot::xBottom, false);
121 
122  // Set origin markers
123  origin = new QwtPlotMarker();
124  origin->setLineStyle(QwtPlotMarker::Cross);
125  origin->setValue(500.0, 0.0);
126  origin->setLinePen(Qt::gray, 2.0, Qt::DashLine);
127  origin->attach(this);
128 
129  // Setup scaling map
130  scaleMapY = new QwtScaleMap();
131  scaleMapY->setPaintInterval(-1.0, 1.0);
132  scaleMapX = new QwtScaleMap();
133  scaleMapX->setPaintInterval(0.0, 1000.0);
134 
135  // Create and attach legend
136  legendItem = new LegendItem();
137  legendItem->attach(this);
138  legendItem->setMaxColumns(1);
139  legendItem->setAlignment(Qt::Alignment(Qt::AlignTop | Qt::AlignRight));
140  legendItem->setBorderRadius(8);
141  legendItem->setMargin(4);
142  legendItem->setSpacing(2);
143  legendItem->setItemMargin(0);
144  legendItem->setBackgroundBrush(QBrush(QColor(225,225,225)));
145 
146  // Update scope background/scales/axes
147  replot();
148 
149  // Timer controls refresh rate of scope
150  timer = new QTimer;
151  timer->setTimerType(Qt::PreciseTimer);
152  QObject::connect(timer,SIGNAL(timeout(void)),this,SLOT(timeoutEvent(void)));
153  timer->start(refresh);
154  resize(sizeHint());
155 }
156 
157 // Kill me
159 {
160  delete d_directPainter;
161 }
162 
163 // Returns pause status of scope
164 bool Scope::paused(void) const
165 {
166  return isPaused;
167 }
168 
169 // Timeout event slot
170 void Scope::timeoutEvent(void)
171 {
172  if(!triggering)
173  drawCurves();
174 }
175 
176 // Insert user specified channel into active list of channels with specified settings
177 std::list<Scope::Channel>::iterator Scope::insertChannel(QString label, double scale, double offset, const QPen &pen, QwtPlotCurve *curve, void *info)
178 {
179  struct Channel channel;
180  channel.label = label;
181  channel.scale = scale;
182  channel.offset = offset;
183  channel.info = info;
184  channel.data.resize(data_size,0.0);
185  channel.curve = curve;
186  channel.curve->setPen(pen);
187  channel.curve->setStyle(QwtPlotCurve::Lines);
188  channel.curve->setRenderHint(QwtPlotItem::RenderAntialiased, false);
189  channel.curve->attach(this);
190  channels.push_back(channel);
191  return --channels.end();
192 }
193 
194 // Remove user specified channel from active channels list
195 void *Scope::removeChannel(std::list<Scope::Channel>::iterator channel)
196 {
197  channel->curve->detach();
198  replot();
199  void *info = channel->info;
200  channels.erase(channel);
201  return info;
202 }
203 
204 // Resize event for scope
205 void Scope::resizeEvent(QResizeEvent *event)
206 {
207  d_directPainter->reset();
208  QwtPlot::resizeEvent(event);
209 }
210 
211 // Returns count of number of active channels
212 size_t Scope::getChannelCount(void) const
213 {
214  return channels.size();
215 }
216 
217 // Returns beginning of channels list
218 std::list<Scope::Channel>::iterator Scope::getChannelsBegin(void)
219 {
220  return channels.begin();
221 }
222 
223 // Returns end of channels list
224 std::list<Scope::Channel>::iterator Scope::getChannelsEnd(void)
225 {
226  return channels.end();
227 }
228 
229 // Returns read-only pointer to beginning of channel list
230 std::list<Scope::Channel>::const_iterator Scope::getChannelsBegin(void) const
231 {
232  return channels.begin();
233 }
234 
235 // Returns read-only pointer to end of channels list
236 std::list<Scope::Channel>::const_iterator Scope::getChannelsEnd(void) const
237 {
238  return channels.end();
239 }
240 
241 // Zeros data
243 {
244  for(std::list<Channel>::iterator i = channels.begin(), end = channels.end(); i != end; ++i)
245  {
246  i->data.assign(data_size, 0);
247  }
248 }
249 
250 // Scales data based upon desired settings for the channel
251 void Scope::setData(double data[],size_t size)
252 {
253  if(isPaused)
254  return;
255 
256  if(size < getChannelCount())
257  {
258  ERROR_MSG("Scope::setData() : data size mismatch detected\n");
259  return;
260  }
261 
262  size_t index = 0;
263  for(std::list<Channel>::iterator i = channels.begin(), end = channels.end(); i != end; ++i)
264  {
265  i->data[data_idx] = data[index++];
266 
267  if(triggering && i == triggerChannel &&
268  ((triggerDirection == POS && i->data[data_idx-1] < triggerThreshold && i->data[data_idx] > triggerThreshold) ||
269  (triggerDirection == NEG && i->data[data_idx-1] > triggerThreshold && i->data[data_idx] < triggerThreshold)))
270  {
271  if(data_idx > triggerWindow*fs)
272  triggerQueue.push_back(data_idx-(triggerWindow*fs));
273  }
274  }
275 
276  ++data_idx %= data_size;
277 
278  if(triggering && !triggerQueue.empty() && (data_idx+2)%data_size == triggerQueue.front())
279  {
280  triggerQueue.pop_front();
281  drawCurves();
282  }
283 }
284 
285 // Returns the data size
286 size_t Scope::getDataSize(void) const
287 {
288  return data_size;
289 }
290 
291 void Scope::setDataSize(size_t size)
292 {
293  for(std::list<Channel>::iterator i = channels.begin(), end = channels.end(); i != end; ++i)
294  i->data.resize(size,0.0);
295  data_idx = 0;
296  data_size = size;
297  triggerQueue.clear();
298 }
299 
301 {
302  return triggerDirection;
303 }
304 
306 {
307  return triggerThreshold;
308 }
309 
311 {
312  return triggerWindow;
313 }
314 
315 std::list<Scope::Channel>::iterator Scope::getTriggerChannel(void)
316 {
317  return triggerChannel;
318 }
319 
320 void Scope::setTrigger(trig_t direction,double threshold,std::list<Channel>::iterator channel, double window)
321 {
322  if(triggerChannel != channel || triggerThreshold != threshold)
323  {
324  triggerChannel = channel;
325  triggerThreshold = threshold;
326  }
327 
328  // Update if direction has changed
329  if(triggerDirection != direction)
330  {
331  if(direction == Scope::NONE)
332  {
333  triggering = false;
334  timer->start(refresh);
335  triggerQueue.clear();
336  }
337  else
338  {
339  triggering = true;
340  timer->stop();
341  }
342  triggerDirection = direction;
343  }
344  triggerWindow = window;
345 }
346 
347 double Scope::getDivT(void) const
348 {
349  return hScl;
350 }
351 
352 // Set x divisions
353 void Scope::setDivT(double divT)
354 {
355  hScl = divT;
356  if(divT >= 1000.)
357  dtLabel = QString::number(divT*1e-3)+"s";
358  else if(divT >= 1.)
359  dtLabel = QString::number(divT)+"ms";
360  else if(divT >= 1e-3)
361  dtLabel = QString::number(divT*1e3)+"┬Ás";
362  else
363  dtLabel = QString::number(divT*1e6)+"ns";
364 }
365 
366 // Set period
367 void Scope::setPeriod(double p)
368 {
369  period = p;
370 }
371 
372 // Get number of x divisions on scope
373 size_t Scope::getDivX(void) const
374 {
375  return divX;
376 }
377 
378 // Get number of y divisions on scope
379 size_t Scope::getDivY(void) const
380 {
381  return divY;
382 }
383 
384 // Get current refresh rate
385 size_t Scope::getRefresh(void) const
386 {
387  return refresh;
388 }
389 
390 // Set new refresh rate
391 void Scope::setRefresh(size_t r)
392 {
393  refresh = r;
394  timer->setInterval(refresh);
395 }
396 
397 // Set channel scale
398 void Scope::setChannelScale(std::list<Channel>::iterator channel,double scale)
399 {
400  channel->scale = scale;
401 }
402 
403 // Set channel offset
404 void Scope::setChannelOffset(std::list<Channel>::iterator channel,double offset)
405 {
406  channel->offset = offset;
407 }
408 
409 // Set pen for channel specified by user
410 void Scope::setChannelPen(std::list<Channel>::iterator channel,const QPen &pen)
411 {
412  channel->curve->setPen(pen);
413 }
414 
415 // Set channel label
416 void Scope::setChannelLabel(std::list<Channel>::iterator channel,const QString &label)
417 {
418  channel->curve->setTitle(label);
419 }
420 
421 // Draw data on the scope
422 void Scope::drawCurves(void)
423 {
424  if(isPaused)
425  return;
426 
427  // Set X scale map is same for all channels
428  scaleMapX->setScaleInterval(0, hScl*divX);
429  for(std::list<Channel>::iterator i = channels.begin(), iend = channels.end(); i != iend; ++i)
430  {
431  // Set data for channel
432  std::vector<double> x (i->data.size());
433  std::vector<double> y (i->data.size());
434  double *x_loc = x.data();
435  double *y_loc = y.data();
436 
437  // Set Y scale map for channel
438  // TODO this should not happen each iteration, instead build into channel struct
439  scaleMapY->setScaleInterval(-i->scale*divY/2, i->scale*divY/2);
440 
441  // Scale data to pixel coordinates
442  for(size_t j = 0; j < i->data.size(); ++j)
443  {
444  *x_loc = scaleMapX->transform(j*period);
445  *y_loc = scaleMapY->transform(i->data[(data_idx+j)%i->data.size()]+i->offset);
446  ++x_loc;
447  ++y_loc;
448  }
449 
450  // Append data to curve
451  // Makes deep copy - which is not optimal
452  // TODO: change to pointer based method
453  i->curve->setSamples(x.data(), y.data(), i->data.size());
454  }
455 
456  // Update plot
457  replot();
458 }
Scope::setRefresh
void setRefresh(size_t)
Definition: scope.cpp:391
Scope::getDivX
size_t getDivX(void) const
Definition: scope.cpp:373
Scope::Channel::getOffset
double getOffset(void) const
Definition: scope.cpp:62
Scope::POS
@ POS
Definition: scope.h:139
Scope::getTriggerDirection
trig_t getTriggerDirection(void)
Definition: scope.cpp:300
Scope::getDataSize
size_t getDataSize(void) const
Definition: scope.cpp:286
ERROR_MSG
void ERROR_MSG(const std::string &errmsg,...)
Definition: debug.cpp:27
Scope::insertChannel
std::list< Channel >::iterator insertChannel(QString, double, double, const QPen &, QwtPlotCurve *, void *)
Definition: scope.cpp:177
Scope::NEG
@ NEG
Definition: scope.h:140
Scope::removeChannel
void * removeChannel(std::list< Channel >::iterator)
Definition: scope.cpp:195
Scope::LegendItem
Definition: scope.h:84
Scope::getTriggerThreshold
double getTriggerThreshold(void)
Definition: scope.cpp:305
Scope::setDivT
void setDivT(double)
Definition: scope.cpp:353
Scope::Channel
Definition: scope.h:61
Scope::getRefresh
size_t getRefresh(void) const
Definition: scope.cpp:385
Scope::isPaused
bool isPaused
Definition: scope.h:177
Scope::Channel::getLabel
QString getLabel(void) const
Definition: scope.cpp:68
Scope::NONE
@ NONE
Definition: scope.h:138
Scope::Channel::Channel
Channel(void)
Definition: scope.cpp:32
Scope::setDataSize
void setDataSize(size_t)
Definition: scope.cpp:291
Scope::resizeEvent
void resizeEvent(QResizeEvent *)
Definition: scope.cpp:205
Scope::getChannelCount
size_t getChannelCount(void) const
Definition: scope.cpp:212
Scope::setChannelOffset
void setChannelOffset(std::list< Channel >::iterator, double)
Definition: scope.cpp:404
Scope::Scope
Scope(QWidget *=NULL)
Definition: scope.cpp:74
Scope::~Scope
virtual ~Scope(void)
Definition: scope.cpp:158
Scope::Channel::getInfo
void * getInfo(void)
Definition: scope.cpp:38
Scope::getDivY
size_t getDivY(void) const
Definition: scope.cpp:379
Scope::Channel::getPen
QPen getPen(void) const
Definition: scope.cpp:50
Scope::Channel::getScale
double getScale(void) const
Definition: scope.cpp:56
Scope::setData
void setData(double *, size_t)
Definition: scope.cpp:251
Scope::paused
bool paused(void) const
Definition: scope.cpp:164
scope.h
fs
#define fs
Definition: scope.cpp:29
rt.h
Scope::trig_t
trig_t
Definition: scope.h:136
Scope::setPeriod
void setPeriod(double)
Definition: scope.cpp:367
Scope::getTriggerChannel
std::list< Channel >::iterator getTriggerChannel(void)
Definition: scope.cpp:315
Scope::getChannelsEnd
std::list< Channel >::iterator getChannelsEnd(void)
Definition: scope.cpp:224
Scope::setChannelPen
void setChannelPen(std::list< Channel >::iterator, const QPen &)
Definition: scope.cpp:410
Scope::setChannelLabel
void setChannelLabel(std::list< Channel >::iterator, const QString &)
Definition: scope.cpp:416
Scope::getChannelsBegin
std::list< Channel >::iterator getChannelsBegin(void)
Definition: scope.cpp:218
Scope::clearData
void clearData(void)
Definition: scope.cpp:242
Scope::getDivT
double getDivT(void) const
Definition: scope.cpp:347
Scope::setTrigger
void setTrigger(trig_t, double, std::list< Channel >::iterator, double)
Definition: scope.cpp:320
Scope::getTriggerWindow
double getTriggerWindow(void)
Definition: scope.cpp:310
Scope::Canvas
Definition: scope.h:96
Scope::setChannelScale
void setChannelScale(std::list< Channel >::iterator, double)
Definition: scope.cpp:398
debug.h
Scope::Channel::~Channel
virtual ~Channel(void)
Definition: scope.cpp:35
main_window.h