PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusPicoScopeDataSource.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
8 
9 // Local includes
10 #include "vtkIGSIOAccurateTimer.h"
12 #include "vtkPlusDataSource.h"
13 
14 // VTK includes
15 #include <vtkMatrix4x4.h>
16 #include <vtkSmartPointer.h>
17 
18 // STL includes
19 #include <string>
20 #include <sstream>
21 
22 // PicoScope includes
23 #include "ps2000.h"
24 
26 
27 static const int MAX_NUM_SAMPLES_PER_FRAME = 50000;
28 
29 //-------------------------------------------------------------------------------------------------
30 class vtkPlusPicoScopeDataSource::vtkInternal
31 {
32 public:
33  friend class vtkPlusPicoScopeDataSource;
34 
36 
37  vtkInternal(vtkPlusPicoScopeDataSource* external)
38  : External(external)
39  {
40  ChannelA.channelNumber = 0;
41  ChannelB.channelNumber = 1;
42  }
43 
44  virtual ~vtkInternal()
45  {
46  }
47 
48  // PICO Scope internals
49 
50  typedef enum {
51  MODEL_NONE = 0,
52  MODEL_PS2104 = 2104,
53  MODEL_PS2105 = 2105,
54  MODEL_PS2202 = 2202,
55  MODEL_PS2203 = 2203,
56  MODEL_PS2204 = 2204,
57  MODEL_PS2205 = 2205,
58  MODEL_PS2204A = 0xA204,
59  MODEL_PS2205A = 0xA205
60  } MODEL_TYPE;
61 
62  typedef struct
63  {
64  PS2000_THRESHOLD_DIRECTION channelA;
65  PS2000_THRESHOLD_DIRECTION channelB;
66  PS2000_THRESHOLD_DIRECTION channelC;
67  PS2000_THRESHOLD_DIRECTION channelD;
68  PS2000_THRESHOLD_DIRECTION ext;
69  } DIRECTIONS;
70 
71  typedef struct
72  {
73  PS2000_PWQ_CONDITIONS * conditions;
74  int16_t nConditions;
75  PS2000_THRESHOLD_DIRECTION direction;
76  uint32_t lower;
77  uint32_t upper;
78  PS2000_PULSE_WIDTH_TYPE type;
79  } PULSE_WIDTH_QUALIFIER;
80 
81  typedef struct
82  {
83  PS2000_CHANNEL channel;
84  float threshold;
85  int16_t direction;
86  float delay;
87  } SIMPLE;
88 
89  typedef struct
90  {
91  int16_t hysteresis;
92  DIRECTIONS directions;
93  int16_t nProperties;
94  PS2000_TRIGGER_CONDITIONS * conditions;
95  PS2000_TRIGGER_CHANNEL_PROPERTIES * channelProperties;
96  PULSE_WIDTH_QUALIFIER pwq;
97  uint32_t totalSamples;
98  int16_t autoStop;
99  int16_t triggered;
100  } ADVANCED;
101 
102  typedef struct
103  {
104  SIMPLE simple;
105  ADVANCED advanced;
106  } TRIGGER_CHANNEL;
107 
108  // struct to hold scope handle
109  typedef struct {
110  int16_t handle;
111  MODEL_TYPE model;
112  PS2000_RANGE firstRange;
113  PS2000_RANGE lastRange;
114  TRIGGER_CHANNEL trigger;
115  int16_t maxTimebase;
116  int16_t timebases;
117  int16_t noOfChannels;
118  int16_t hasAdvancedTriggering;
119  int16_t hasFastStreaming;
120  int16_t hasEts;
121  int16_t hasSignalGenerator;
122  int16_t awgBufferSize;
123  } UNIT_MODEL;
124 
125  // PLUS structs
126  typedef enum {
127  AC = 0,
128  DC = 1
129  } COUPLING;
130 
131  typedef struct {
132  bool enabled = false;
133  int16_t channelNumber = -1; // 0=A, 1=B
134  COUPLING coupling = DC;
135  PS2000_RANGE voltageRange = PS2000_20V;
136  } PICO_CHANNEL_SETTINGS;
137 
138  // METHODS
139 
140  // Get info about the connected PicoScope device
141  std::string GetPicoScopeInfo(void);
142 
143  // Convert integer mv amount into PS2000_RANGE
144  PlusStatus RangeIntToEnum(const int voltageRangeMv, PS2000_RANGE& voltRange);
145 
146  int RangeEnumToInt(const PS2000_RANGE voltRange);
147 
148  std::string RangeEnumToStr(const PS2000_RANGE voltRange);
149 
150  // Get the units the ADC is set to
151  std::string GetAdcUnits(int16_t time_units);
152 
153  // Convert ADC counts to volts / mV
154  float AdcToMv(int32_t rawCount, int32_t rangeMv);
155 
156  // Initalize a PicoScope channel
157  PlusStatus SetupPicoScopeChannel(PICO_CHANNEL_SETTINGS channel);
158 
159  // Print channel settings
160  void PrintChannelInfo(PICO_CHANNEL_SETTINGS channelSettings);
161 
162 protected:
163  // MEMBER VARIABLES
164 
165  UNIT_MODEL Scope; // scope handle
166 
167  int PSInputRanges[PS2000_MAX_RANGES] = { 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000 };
168 
169  // channel handles
170  PICO_CHANNEL_SETTINGS ChannelA;
171  PICO_CHANNEL_SETTINGS ChannelB;
172 
173  // scope acquisition timing variables
174  int AppliedNumberOfSamples;
175  int AppliedTimeUnits;
176  int AppliedTimebase;
177 
178  int16_t SignalARaw[MAX_NUM_SAMPLES_PER_FRAME];
179  int16_t SignalBRaw[MAX_NUM_SAMPLES_PER_FRAME];
180  int32_t Times[MAX_NUM_SAMPLES_PER_FRAME];
181  float SignalBuffer[3 * MAX_NUM_SAMPLES_PER_FRAME];
182 
183  // PLUS channel members
184  std::string PlusSourceId = "";
185  vtkPlusDataSource* PlusSource = nullptr;
186 };
187 
188 //-------------------------------------------------------------------------------------------------
189 std::string vtkPlusPicoScopeDataSource::vtkInternal::GetPicoScopeInfo()
190 {
191  int8_t description[8][25] = { "Driver Version ",
192  "USB Version ",
193  "Hardware Version ",
194  "Variant Info ",
195  "Serial ",
196  "Cal Date ",
197  "Error Code ",
198  "Kernel Driver "
199  };
200  int16_t i;
201  int8_t line[80];
202  int32_t variant;
203 
204  // stringstream to contain device info
205  std::stringstream deviceInfoSS;
206 
207  if (this->Scope.handle)
208  {
209  for (i = 0; i < 8; i++)
210  {
211  ps2000_get_unit_info(this->Scope.handle, line, sizeof(line), i);
212 
213  if (i == 3)
214  {
215  variant = atoi((const char*)line);
216 
217  if (strlen((const char*)line) == 5) // Identify if 2204A or 2205A
218  {
219  line[4] = toupper(line[4]);
220 
221  if (line[1] == '2' && line[4] == 'A') // i.e 2204A -> 0xA204
222  {
223  variant += 0x9968;
224  }
225  }
226  }
227 
228  if (i != 6) // No need to print error code
229  {
230  deviceInfoSS << description[i] << " " << line << std::endl;
231  }
232  }
233 
234  switch (variant)
235  {
236  case MODEL_PS2104:
237  this->Scope.model = MODEL_PS2104;
238  this->Scope.firstRange = PS2000_100MV;
239  this->Scope.lastRange = PS2000_20V;
240  this->Scope.maxTimebase = PS2104_MAX_TIMEBASE;
241  this->Scope.timebases = this->Scope.maxTimebase;
242  this->Scope.noOfChannels = 1;
243  this->Scope.hasAdvancedTriggering = false;
244  this->Scope.hasSignalGenerator = false;
245  this->Scope.hasEts = true;
246  this->Scope.hasFastStreaming = false;
247  break;
248 
249  case MODEL_PS2105:
250  this->Scope.model = MODEL_PS2105;
251  this->Scope.firstRange = PS2000_100MV;
252  this->Scope.lastRange = PS2000_20V;
253  this->Scope.maxTimebase = PS2105_MAX_TIMEBASE;
254  this->Scope.timebases = this->Scope.maxTimebase;
255  this->Scope.noOfChannels = 1;
256  this->Scope.hasAdvancedTriggering = false;
257  this->Scope.hasSignalGenerator = false;
258  this->Scope.hasEts = true;
259  this->Scope.hasFastStreaming = false;
260  break;
261 
262  case MODEL_PS2202:
263  this->Scope.model = MODEL_PS2202;
264  this->Scope.firstRange = PS2000_100MV;
265  this->Scope.lastRange = PS2000_20V;
266  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
267  this->Scope.timebases = this->Scope.maxTimebase;
268  this->Scope.noOfChannels = 2;
269  this->Scope.hasAdvancedTriggering = true;
270  this->Scope.hasSignalGenerator = false;
271  this->Scope.hasEts = false;
272  this->Scope.hasFastStreaming = true;
273  break;
274 
275  case MODEL_PS2203:
276  this->Scope.model = MODEL_PS2203;
277  this->Scope.firstRange = PS2000_50MV;
278  this->Scope.lastRange = PS2000_20V;
279  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
280  this->Scope.timebases = this->Scope.maxTimebase;
281  this->Scope.noOfChannels = 2;
282  this->Scope.hasAdvancedTriggering = false;
283  this->Scope.hasSignalGenerator = true;
284  this->Scope.hasEts = true;
285  this->Scope.hasFastStreaming = true;
286  break;
287 
288  case MODEL_PS2204:
289  this->Scope.model = MODEL_PS2204;
290  this->Scope.firstRange = PS2000_50MV;
291  this->Scope.lastRange = PS2000_20V;
292  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
293  this->Scope.timebases = this->Scope.maxTimebase;
294  this->Scope.noOfChannels = 2;
295  this->Scope.hasAdvancedTriggering = true;
296  this->Scope.hasSignalGenerator = true;
297  this->Scope.hasEts = true;
298  this->Scope.hasFastStreaming = true;
299  break;
300 
301  case MODEL_PS2204A:
302  this->Scope.model = MODEL_PS2204A;
303  this->Scope.firstRange = PS2000_50MV;
304  this->Scope.lastRange = PS2000_20V;
305  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
306  this->Scope.timebases = this->Scope.maxTimebase;
307  this->Scope.noOfChannels = 2;
308  this->Scope.hasAdvancedTriggering = true;
309  this->Scope.hasSignalGenerator = true;
310  this->Scope.hasEts = true;
311  this->Scope.hasFastStreaming = true;
312  this->Scope.awgBufferSize = 4096;
313  break;
314 
315  case MODEL_PS2205:
316  this->Scope.model = MODEL_PS2205;
317  this->Scope.firstRange = PS2000_50MV;
318  this->Scope.lastRange = PS2000_20V;
319  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
320  this->Scope.timebases = this->Scope.maxTimebase;
321  this->Scope.noOfChannels = 2;
322  this->Scope.hasAdvancedTriggering = true;
323  this->Scope.hasSignalGenerator = true;
324  this->Scope.hasEts = true;
325  this->Scope.hasFastStreaming = true;
326  break;
327 
328  case MODEL_PS2205A:
329  this->Scope.model = MODEL_PS2205A;
330  this->Scope.firstRange = PS2000_50MV;
331  this->Scope.lastRange = PS2000_20V;
332  this->Scope.maxTimebase = PS2200_MAX_TIMEBASE;
333  this->Scope.timebases = this->Scope.maxTimebase;
334  this->Scope.noOfChannels = 2;
335  this->Scope.hasAdvancedTriggering = true;
336  this->Scope.hasSignalGenerator = true;
337  this->Scope.hasEts = true;
338  this->Scope.hasFastStreaming = true;
339  this->Scope.awgBufferSize = 4096;
340  break;
341 
342  default:
343  LOG_ERROR("PicoScope model not supported.");
344  }
345  }
346  else
347  {
348  LOG_ERROR("Failed to open PicoScope device.");
349 
350  ps2000_get_unit_info(this->Scope.handle, line, sizeof(line), 5);
351 
352  deviceInfoSS << description[5] << " " << line;
353  this->Scope.model = MODEL_NONE;
354  this->Scope.firstRange = PS2000_100MV;
355  this->Scope.lastRange = PS2000_20V;
356  this->Scope.timebases = PS2105_MAX_TIMEBASE;
357  this->Scope.noOfChannels = 1;
358  }
359 
360  return deviceInfoSS.str();
361 }
362 
363 //-------------------------------------------------------------------------------------------------
364 PlusStatus vtkPlusPicoScopeDataSource::vtkInternal::RangeIntToEnum(const int voltageRangeMv, PS2000_RANGE& voltRange)
365 {
366  switch (voltageRangeMv)
367  {
368  case 10:
369  voltRange = PS2000_10MV; return PLUS_SUCCESS;
370  case 20:
371  voltRange = PS2000_20MV; return PLUS_SUCCESS;
372  case 50:
373  voltRange = PS2000_50MV; return PLUS_SUCCESS;
374  case 100:
375  voltRange = PS2000_100MV; return PLUS_SUCCESS;
376  case 200:
377  voltRange = PS2000_200MV; return PLUS_SUCCESS;
378  case 500:
379  voltRange = PS2000_500MV; return PLUS_SUCCESS;
380  case 1000:
381  voltRange = PS2000_1V; return PLUS_SUCCESS;
382  case 2000:
383  voltRange = PS2000_2V; return PLUS_SUCCESS;
384  case 5000:
385  voltRange = PS2000_5V; return PLUS_SUCCESS;
386  case 10000:
387  voltRange = PS2000_10V; return PLUS_SUCCESS;
388  case 20000:
389  voltRange = PS2000_20V; return PLUS_SUCCESS;
390  case 50000:
391  voltRange = PS2000_50V; return PLUS_SUCCESS;
392  default:
393  LOG_ERROR("Invalid voltage range: " << voltageRangeMv << ". Ensure you are providing the voltage range in mV.");
394  return PLUS_FAIL;
395  }
396 }
397 
398 //-------------------------------------------------------------------------------------------------
399 int vtkPlusPicoScopeDataSource::vtkInternal::RangeEnumToInt(const PS2000_RANGE voltRange)
400 {
401  switch (voltRange)
402  {
403  case PS2000_10MV:
404  return 10;
405  case PS2000_20MV:
406  return 20;
407  case PS2000_50MV:
408  return 50;
409  case PS2000_100MV:
410  return 100;
411  case PS2000_200MV:
412  return 200;
413  case PS2000_500MV:
414  return 500;
415  case PS2000_1V:
416  return 1000;
417  case PS2000_2V:
418  return 2000;
419  case PS2000_5V:
420  return 5000;
421  case PS2000_10V:
422  return 10000;
423  case PS2000_20V:
424  return 20000;
425  case PS2000_50V:
426  return 50000;
427  }
428 }
429 
430 //-------------------------------------------------------------------------------------------------
431 std::string vtkPlusPicoScopeDataSource::vtkInternal::RangeEnumToStr(const PS2000_RANGE voltRange)
432 {
433  switch (voltRange)
434  {
435  case PS2000_10MV:
436  return "PS2000_10MV";
437  case PS2000_20MV:
438  return "PS2000_20MV";
439  case PS2000_50MV:
440  return "PS2000_50MV";
441  case PS2000_100MV:
442  return "PS2000_100MV";
443  case PS2000_200MV:
444  return "PS2000_200MV";
445  case PS2000_500MV:
446  return "PS2000_500MV";
447  case PS2000_1V:
448  return "PS2000_1V";
449  case PS2000_2V:
450  return "PS2000_2V";
451  case PS2000_5V:
452  return "PS2000_5V";
453  case PS2000_10V:
454  return "PS2000_10V";
455  case PS2000_20V:
456  return "PS2000_20V";
457  case PS2000_50V:
458  return "PS2000_20V";
459  }
460 }
461 
462 //-------------------------------------------------------------------------------------------------
463 std::string vtkPlusPicoScopeDataSource::vtkInternal::GetAdcUnits(int16_t time_units)
464 {
465  time_units++;
466  //printf ( "time unit: %d\n", time_units ) ;
467  switch (time_units)
468  {
469  case 0:
470  return "ADC";
471  case 1:
472  return "fs";
473  case 2:
474  return "ps";
475  case 3:
476  return "ns";
477  case 4:
478  return "us";
479  case 5:
480  return "ms";
481  }
482 
483  return "Not Known";
484 }
485 
486 //-------------------------------------------------------------------------------------------------
487 float vtkPlusPicoScopeDataSource::vtkInternal::AdcToMv(int32_t rawCount, int32_t rangeMv)
488 {
489  return ((float) (rawCount * rangeMv)) / (PS2000_MAX_VALUE - 1);
490 }
491 
492 //-------------------------------------------------------------------------------------------------
493 PlusStatus vtkPlusPicoScopeDataSource::vtkInternal::SetupPicoScopeChannel(vtkInternal::PICO_CHANNEL_SETTINGS settings)
494 {
495  // check this channel is enabled
496  if (!settings.enabled)
497  {
498  return PLUS_SUCCESS;
499  }
500 
501  // check channel is valid
502  if (!(settings.channelNumber == 0 || (settings.channelNumber == 1 && this->Scope.noOfChannels == 2)))
503  {
504  LOG_ERROR("Unsupported channel number: " << settings.channelNumber << ". Check the number of channels your oscilloscope supports. Note: channel numbers start at 0.");
505  return PLUS_FAIL;
506  }
507 
508  // set channel values
509  if (!ps2000_set_channel(this->Scope.handle, settings.channelNumber, TRUE, settings.coupling, settings.voltageRange))
510  {
511  LOG_ERROR("Failed to set PicoScope channel settings for channel: " << settings.channelNumber << ".");
512  return PLUS_FAIL;
513  }
514 
515  return PLUS_SUCCESS;
516 }
517 
518 //-------------------------------------------------------------------------------------------------
519 void vtkPlusPicoScopeDataSource::vtkInternal::PrintChannelInfo(PICO_CHANNEL_SETTINGS channelSettings)
520 {
521  if (channelSettings.channelNumber == 0)
522  {
523  LOG_INFO("CHANNEL A Settings:");
524  }
525  else if (channelSettings.channelNumber == 1)
526  {
527  LOG_INFO("CHANNEL B Settings:");
528  }
529  LOG_INFO("Enabled: " << (channelSettings.enabled ? "TRUE" : "FALSE"));
530  LOG_INFO("Coupling: " << ((channelSettings.coupling == vtkInternal::DC) ? "DC" : "AC"));
531  LOG_INFO("Voltage Range: " << this->RangeEnumToStr(channelSettings.voltageRange));
532 }
533 
534 //-------------------------------------------------------------------------------------------------
536  : vtkPlusDevice()
537  , Internal(new vtkInternal(this))
538 {
539  LOG_TRACE("vtkPlusPicoScopeDataSource::vtkPlusPicoScopeDataSource()");
540 
541  this->FrameNumber = 0;
542  this->StartThreadForInternalUpdates = true;
543  this->InternalUpdateRate = 30;
544 }
545 
546 //-------------------------------------------------------------------------------------------------
548 {
549  LOG_TRACE("vtkPlusPicoScopeDataSource::~vtkPlusPicoScopeDataSource()");
550 
551  delete Internal;
552  Internal = nullptr;
553 }
554 
555 //-------------------------------------------------------------------------------------------------
556 void vtkPlusPicoScopeDataSource::PrintSelf(ostream& os, vtkIndent indent)
557 {
558  Superclass::PrintSelf(os, indent);
559 }
560 
561 //-------------------------------------------------------------------------------------------------
562 PlusStatus vtkPlusPicoScopeDataSource::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
563 {
564  LOG_TRACE("vtkPlusPicoScopeDataSource::ReadConfiguration");
565 
566  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
567 
568  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, NumberOfSamples, deviceConfig);
569  if (this->NumberOfSamples > MAX_NUM_SAMPLES_PER_FRAME)
570  {
571  LOG_ERROR("Requested number of samples (" << this->NumberOfSamples << "( is larger than the maximum number of samples per frame (" << MAX_NUM_SAMPLES_PER_FRAME << ").");
572  return PLUS_FAIL;
573  }
574 
575  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
576  for (int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); nestedElementIndex++)
577  {
578  vtkXMLDataElement* channelDataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
579  if (STRCASECMP(channelDataElement->GetName(), "DataSource") != 0)
580  {
581  // if this is not a data source element, skip it
582  continue;
583  }
584  if (channelDataElement->GetAttribute("Type") != NULL && STRCASECMP(channelDataElement->GetAttribute("Type"), "Video") != 0)
585  {
586  // if this is not a Video element, skip it, we use the image to store the scope values
587  continue;
588  }
589  std::string sourceId(channelDataElement->GetAttribute("Id"));
590  if (sourceId.empty())
591  {
592  // Channel doesn't have ID needed to generate signal output
593  LOG_ERROR("Failed to initialize PicoScope channel: DataSource Id is missing.");
594  continue;
595  }
596 
597  // set output source id
598  this->Internal->PlusSourceId = sourceId;
599 
600  // read Channel A settings
601  vtkInternal::COUPLING chA_coupling = vtkInternal::DC;
602  XML_READ_ENUM2_ATTRIBUTE_NONMEMBER_REQUIRED(ChACoupling, chA_coupling, channelDataElement, "AC", vtkInternal::AC, "DC", vtkInternal::DC);
603  int chA_voltageRangeMv;
604  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(int, ChAVoltageRangeMv, chA_voltageRangeMv, channelDataElement);
605  PS2000_RANGE chA_voltageRange;
606  if (this->Internal->RangeIntToEnum(chA_voltageRangeMv, chA_voltageRange) != PLUS_SUCCESS)
607  {
608  return PLUS_FAIL;
609  }
610  this->Internal->ChannelA.enabled = true;
611  this->Internal->ChannelA.coupling = chA_coupling;
612  this->Internal->ChannelA.voltageRange = chA_voltageRange;
613 
614  // read Channel B settings
615  vtkInternal::COUPLING chB_coupling = vtkInternal::DC;
616  XML_READ_ENUM2_ATTRIBUTE_NONMEMBER_REQUIRED(ChBCoupling, chB_coupling, channelDataElement, "AC", vtkInternal::AC, "DC", vtkInternal::DC);
617  int chB_voltageRangeMv;
618  XML_READ_SCALAR_ATTRIBUTE_NONMEMBER_REQUIRED(int, ChBVoltageRangeMv, chB_voltageRangeMv, channelDataElement);
619  PS2000_RANGE chB_voltageRange;
620  if (this->Internal->RangeIntToEnum(chB_voltageRangeMv, chB_voltageRange) != PLUS_SUCCESS)
621  {
622  return PLUS_FAIL;
623  }
624  this->Internal->ChannelB.enabled = true;
625  this->Internal->ChannelB.coupling = chB_coupling;
626  this->Internal->ChannelB.voltageRange = chB_voltageRange;
627  }
628 
629  return PLUS_SUCCESS;
630 }
631 
632 //-------------------------------------------------------------------------------------------------
633 PlusStatus vtkPlusPicoScopeDataSource::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
634 {
635  LOG_TRACE("vtkPlusPicoScopeDataSource::WriteConfiguration");
636 
637  return PLUS_SUCCESS;
638 }
639 
640 //-------------------------------------------------------------------------------------------------
642 {
643  LOG_TRACE("vtkPlusPicoScopeDataSource::Probe");
644  if (!this->Internal->Scope.handle)
645  {
646  return PLUS_FAIL;
647  }
648  return PLUS_SUCCESS;
649 }
650 
651 //-------------------------------------------------------------------------------------------------
653 {
654  LOG_TRACE("vtkPlusPicoScopeDataSource::InternalConnect");
655 
656  // connect to scope
657  this->Internal->Scope.handle = ps2000_open_unit();
658 
659  if (!this->Internal->Scope.handle)
660  {
661  // scope is not connected
662  LOG_ERROR("Failed to connect to PicoScope PS2000.");
663  return PLUS_FAIL;
664  }
665 
666  // print device info
667  std::string psDeviceInfo;
668  psDeviceInfo += this->Internal->GetPicoScopeInfo();
669  LOG_INFO("PicoScope device info: " << std::endl << psDeviceInfo);
670 
671  // print channel info
672  LOG_INFO("");
673  this->Internal->PrintChannelInfo(this->Internal->ChannelA);
674  LOG_INFO("");
675  this->Internal->PrintChannelInfo(this->Internal->ChannelB);
676 
677  // configure channels
678  if (this->Internal->SetupPicoScopeChannel(this->Internal->ChannelA) != PLUS_SUCCESS)
679  {
680  return PLUS_FAIL;
681  }
682  if (this->Internal->SetupPicoScopeChannel(this->Internal->ChannelB) != PLUS_SUCCESS)
683  {
684  return PLUS_FAIL;
685  }
686 
687  // disable PicoScope trigger
688  ps2000_set_trigger(this->Internal->Scope.handle, PS2000_NONE, 0, PS2000_RISING, 0, 0);
689 
690  // disable ETS (equivalent time sampling) mode
691  ps2000_set_ets(this->Internal->Scope.handle, PS2000_ETS_OFF, 0, 0);
692 
693  /* Find the maximum number of samples, the time interval (in time_units),
694  * the most suitable time units, and the maximum oversample at the current timebase
695  */
696 
697  bool timebaseSelected = false;
698  int16_t timebase;
699  int32_t timeInterval;
700  int16_t timeUnits;
701  int16_t oversample;
702  int32_t maxSamples;
703  for (timebase = 0; timebase <= this->Internal->Scope.maxTimebase; ++timebase)
704  {
705  int16_t timebaseResult = ps2000_get_timebase(
706  this->Internal->Scope.handle,
707  timebase,
708  this->NumberOfSamples,
709  &timeInterval,
710  &timeUnits,
711  0, // no oversampling
712  &maxSamples
713  );
714 
715  if (timeInterval == NULL || timeUnits == NULL || maxSamples == NULL)
716  {
717  continue;
718  }
719 
720  if (timebaseResult != 0 && maxSamples > this->NumberOfSamples)
721  {
722  timebaseSelected = true;
723  this->Internal->AppliedNumberOfSamples = maxSamples;
724  this->Internal->AppliedTimeUnits = timeUnits;
725  this->Internal->AppliedTimebase = timebase;
726  break;
727  }
728  }
729 
730  if (!timebaseSelected)
731  {
732  LOG_ERROR("Failed to select PicoScope timebase. Please choose a different number of samples.");
733  return PLUS_FAIL;
734  }
735 
736  // configure the output source
737  this->GetVideoSource(this->Internal->PlusSourceId.c_str(), this->Internal->PlusSource);
738  if (this->Internal->PlusSource->GetNumberOfItems() == 0)
739  {
740  this->Internal->PlusSource->SetInputImageOrientation(US_IMG_ORIENT_MF);
741  this->Internal->PlusSource->SetImageType(US_IMG_BRIGHTNESS);
742  this->Internal->PlusSource->SetPixelType(VTK_FLOAT);
743  this->Internal->PlusSource->SetNumberOfScalarComponents(1);
744  this->Internal->PlusSource->SetInputFrameSize(this->NumberOfSamples, 3, 1);
745  }
746 
747  return PLUS_SUCCESS;
748 }
749 
750 //-------------------------------------------------------------------------------------------------
752 {
753  LOG_TRACE("vtkPlusPicoScopeDataSource::InternalDisconnect");
754  ps2000_close_unit(this->Internal->Scope.handle);
755  return PLUS_SUCCESS;
756 }
757 
758 //-------------------------------------------------------------------------------------------------
759 PlusStatus vtkPlusPicoScopeDataSource::InternalStartRecording()
760 {
761  LOG_TRACE("vtkPlusPicoScopeDataSource::InternalStartRecording");
762 
763  return PLUS_SUCCESS;
764 }
765 
766 //-------------------------------------------------------------------------------------------------
767 PlusStatus vtkPlusPicoScopeDataSource::InternalStopRecording()
768 {
769  LOG_TRACE("vtkPlusPicoScopeDataSource::InternalStopRecording");
770 
771  return PLUS_SUCCESS;
772 }
773 
774 //-------------------------------------------------------------------------------------------------
776 {
777  LOG_TRACE("vtkPlusPicoScopeDataSource::InternalUpdate");
778 
779  // Start scope collecting
780  int32_t timeIndisposedMs;
781  ps2000_run_block(
782  this->Internal->Scope.handle,
783  this->Internal->AppliedNumberOfSamples,
784  this->Internal->AppliedTimebase,
785  0, // no oversampling
786  &timeIndisposedMs
787  );
788 
789  while (!ps2000_ready(this->Internal->Scope.handle))
790  {
791  // no fresh data yet
792  }
793 
794  const double unfilteredTimestamp = vtkIGSIOAccurateTimer::GetSystemTime();
795 
796  ps2000_stop(this->Internal->Scope.handle);
797 
798  int16_t overflow;
799  ps2000_get_times_and_values(
800  this->Internal->Scope.handle,
801  this->Internal->Times,
802  this->Internal->SignalARaw,
803  this->Internal->SignalBRaw,
804  NULL,
805  NULL,
806  &overflow,
807  this->Internal->AppliedTimeUnits,
808  this->Internal->AppliedNumberOfSamples
809  );
810 
811  if (overflow != 0)
812  {
813  LOG_WARNING("PicoScope overflow occurred in InternalUpdate.");
814  }
815 
816  int channelARangeMv = this->Internal->RangeEnumToInt(this->Internal->ChannelA.voltageRange);
817  int channelBRangeMv = this->Internal->RangeEnumToInt(this->Internal->ChannelB.voltageRange);
818 
819  // update buffers
820  for (int i = 0; i < this->NumberOfSamples; i++)
821  {
822  this->Internal->SignalBuffer[i] = (float)this->Internal->Times[i];
823  this->Internal->SignalBuffer[i + this->NumberOfSamples] = this->Internal->AdcToMv(this->Internal->SignalARaw[i], channelARangeMv);
824  this->Internal->SignalBuffer[i + 2*this->NumberOfSamples] = this->Internal->AdcToMv(this->Internal->SignalBRaw[i], channelBRangeMv);
825  }
826 
828  if (this->GetVideoSource(this->Internal->PlusSourceId.c_str(), source) != PLUS_SUCCESS)
829  {
830  LOG_ERROR("couldnt find source");
831  }
832 
833  FrameSizeType frameSize = { this->NumberOfSamples, 3, 1 };
834  PlusStatus res = source->AddItem(
835  this->Internal->SignalBuffer,
836  US_IMG_ORIENT_MF,
837  frameSize,
838  VTK_FLOAT,
839  1,
840  US_IMG_BRIGHTNESS,
841  0,
842  this->FrameNumber,
843  unfilteredTimestamp
844  );
845 
846  this->FrameNumber++;
847 
848  return PLUS_FAIL;
849 }
static const int MAX_NUM_SAMPLES_PER_FRAME
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
int * channel
Definition: phidget22.h:1303
Abstract interface for tracker and video devices.
Definition: vtkPlusDevice.h:60
const char * source
Definition: phidget22.h:2461
vtkStandardNewMacro(vtkPlusPicoScopeDataSource)
const char int line
Definition: phidget22.h:2458
igsioStatus PlusStatus
Definition: PlusCommon.h:40
for i
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
#define PLUS_FAIL
Definition: PlusCommon.h:43
void PrintSelf(ostream &os, vtkIndent indent)
unsigned long FrameNumber
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
int enabled
Definition: phidget22.h:3369
double InternalUpdateRate
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
Interface to the Atracsys trackers This class talks with a Atracsys Tracker over the sTk Passive Trac...
PhidgetVoltageInput_VoltageRange voltageRange
Definition: phidget22.h:3491
bool StartThreadForInternalUpdates
#define TRUE
Definition: ATC3DGm.h:219
PlusStatus GetVideoSource(const char *aSourceId, vtkPlusDataSource *&aVideoSource)
Interface to a 3D positioning tool, video source, or generalized data stream.