PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusAgilentScopeTracker.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 /*=========================================================================
8 The following copyright notice is applicable to parts of this file:
9 
10 Copyright (c) 2000-2005 Atamai, Inc.
11 
12 Use, modification and redistribution of the software, in source or
13 binary forms, are permitted provided that the following terms and
14 conditions are met:
15 
16 1) Redistribution of the source code, in verbatim or modified
17 form, must retain the above copyright notice, this license,
18 the following disclaimer, and any notices that refer to this
19 license and/or the following disclaimer.
20 
21 2) Redistribution in binary form must include the above copyright
22 notice, a copy of this license and the following disclaimer
23 in the documentation or with other materials provided with the
24 distribution.
25 
26 3) Modified copies of the source code must be clearly marked as such,
27 and must not be misrepresented as verbatim copies of the source code.
28 
29 THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS"
30 WITHOUT EXPRESSED OR IMPLIED WARRANTY INCLUDING, BUT NOT LIMITED TO,
31 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE. IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR OTHER PARTY WHO MAY
33 ODIFY AND/OR REDISTRIBUTE THE SOFTWARE UNDER THE TERMS OF THIS LICENSE
34 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES
35 (INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR DATA BECOMING INACCURATE
36 OR LOSS OF PROFIT OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF
37 THE USE OR INABILITY TO USE THE SOFTWARE, EVEN IF ADVISED OF THE
38 POSSIBILITY OF SUCH DAMAGES.
39 
40 =========================================================================*/
41 
42 // Local includes
43 #include "PlusConfigure.h"
44 #include "vtkIGSIORecursiveCriticalSection.h"
45 #include "vtkPlusDataSource.h"
47 
48 // Agilent includes
49 #include "AgMD1Fundamental.h"
50 
51 // VTK includes
52 #include <vtkCharArray.h>
53 #include <vtkMath.h>
54 #include <vtkMatrix4x4.h>
55 #include <vtkObjectFactory.h>
56 #include <vtkSocketCommunicator.h>
57 #include <vtkTimerLog.h>
58 #include <vtkTransform.h>
59 
60 // STL includes
61 #include <fstream>
62 #include <iostream>
63 
64 // System includes
65 #include <assert.h>
66 #include <ctype.h>
67 #include <float.h>
68 #include <math.h>
69 #include <queue>
70 #include <vector>
71 
72 using namespace std;
73 
74 namespace
75 {
76  const int VIRTUAL_SROM_SIZE = 1024;
77 
78  bool AgilentError(const std::string& functionName, ViStatus status)
79  {
80  char ErrMsg[256];
81  if (status)
82  {
83  Acqrs_errorMessage(VI_NULL, status, ErrMsg, 256);
84  cout << functionName << ": " << ErrMsg << endl;
85  return true;
86  }
87 
88  return false;
89  }
90 
91 }
92 
93 //----------------------------------------------------------------------------
94 
96 
97 //----------------------------------------------------------------------------
99  : InstrumentCount(0)
100  , Status(0)
101  , InstrumentID(0)
102  , SpeedOfSound(1480)
103  , SampleFrequencyHz(420000000.0)
104  , DelayTimeSec(0.000002)
105  , SampleCountPerAcquisition(11350)
106  , SegmentCountPerAcquisition(1) // Currently fixed to 1, as we only support single segment acquisition
107  , FullScale(1.0)
108  , Offset(0.0)
109  , Coupling(3) // Currently fixed to 3
110  , Bandwidth(0)
111  , TrigCoupling(0)
112  , Slope(0)
113  , Level(20.0) // In % of vertical full scale when using internal trigger
114  , FirstPeakToNeedleTip(vtkSmartPointer<vtkMatrix4x4>::New())
115  , SecondPeakToNeedleTip(vtkSmartPointer<vtkMatrix4x4>::New())
116  , ThirdPeakToNeedleTip(vtkSmartPointer<vtkMatrix4x4>::New())
117  , SignalImage(vtkSmartPointer<vtkImageData>::New())
118  , EnvelopeArray(NULL)
119  , DataArray(NULL)
120  , HannWindow(NULL)
121  , KernelSize(153)
122  , PeakIdxArray(NULL)
123  , MinPeakDistance(280)
124 {
125  this->FirstPeakToNeedleTip->Identity();
126  this->SecondPeakToNeedleTip->Identity();
127  this->ThirdPeakToNeedleTip->Identity();
128 
129  this->SampleIntervalSec = 1.0 / (this->SampleFrequencyHz);
130 
131  // No callback function provided by the scope's SDK, so the data capture thread will be used to poll the hardware and add new items to the buffer
132  this->StartThreadForInternalUpdates = true;
133  this->AcquisitionRate = 50;
134 
135  // Zero out data structures
136  this->DataDescription.returnedSamplesPerSeg = -1;
137  this->DataDescription.indexFirstPoint = -1;
138  this->DataDescription.sampTime = -1.0;
139  this->DataDescription.vGain = -1.0;
140  this->DataDescription.vOffset = -1.0;
141  this->DataDescription.returnedSegments = -1;
142  this->DataDescription.nbrAvgWforms = -1;
143  this->DataDescription.actualTriggersInAcqLo = 0;
144  this->DataDescription.actualTriggersInAcqHi = 0;
145  this->DataDescription.actualDataSize = 0;
146  this->DataDescription.reserved2 = -1;
147  this->DataDescription.reserved3 = -1.0;
148  this->DataDescription.actualDataSize = 0;
149 
150  this->ReadParameters.dataType = -1;
151  this->ReadParameters.readMode = -1;
152  this->ReadParameters.firstSegment = -1;
153  this->ReadParameters.nbrSegments = -1;
154  this->ReadParameters.firstSampleInSeg = -1;
155  this->ReadParameters.nbrSamplesInSeg = -1;
156  this->ReadParameters.segmentOffset = -1;
157  this->ReadParameters.dataArraySize = -1;
158  this->ReadParameters.segDescArraySize = -1;
159  this->ReadParameters.flags = -1;
160  this->ReadParameters.reserved = -1;
161  this->ReadParameters.reserved2 = -1.0;
162  this->ReadParameters.reserved3 = -1.0;
163 
164  this->SegmentDescription.horPos = -1.0;
165  this->SegmentDescription.timeStampLo = 0;
166  this->SegmentDescription.timeStampHi = 0;
167 }
168 
169 //----------------------------------------------------------------------------
171 {
172  if (this->DataArray != NULL)
173  {
174  delete this->DataArray;
175  }
176  if (this->EnvelopeArray != NULL)
177  {
178  delete this->EnvelopeArray;
179  }
180  if (this->HannWindow != NULL)
181  {
182  delete this->HannWindow;
183  }
184  if (this->PeakIdxArray != NULL)
185  {
186  delete this->PeakIdxArray;
187  }
188 }
189 
190 //----------------------------------------------------------------------------
191 void vtkPlusAgilentScopeTracker::PrintSelf(std::ostream& os, vtkIndent indent)
192 {
193  Superclass::PrintSelf(os, indent);
194 
195  os << indent << "LastFrameNumber: " << this->LastFrameNumber << std::endl;
196  os << indent << "InstrumentCount: " << this->InstrumentCount << std::endl;
197  os << indent << "Status: " << this->Status << std::endl;
198  os << indent << "ResourceName: " << this->ResourceName << std::endl;
199  os << indent << "OptionsString: " << this->OptionString << std::endl;
200  os << indent << "InstrumentID: " << this->InstrumentID << std::endl;
201  os << indent << "SpeedOfSound: " << this->SpeedOfSound << std::endl;
202  os << indent << "SampleFrequencyHz: " << this->SampleFrequencyHz << std::endl;
203  os << indent << "SampleIntervalSec: " << this->SampleIntervalSec << std::endl;
204  os << indent << "DelayTimeSec: " << this->DelayTimeSec << std::endl;
205  os << indent << "SampleCountPerAcquisition: " << this->SampleCountPerAcquisition << std::endl;
206  os << indent << "SegmentCountPerAcquisition: " << this->SegmentCountPerAcquisition << std::endl;
207  os << indent << "FullScale: " << this->FullScale << std::endl;
208  os << indent << "Offset: " << this->Offset << std::endl;
209  os << indent << "Coupling: " << this->Coupling << std::endl;
210  os << indent << "Bandwidth: " << this->Bandwidth << std::endl;
211  os << indent << "TrigCoupling: " << this->TrigCoupling << std::endl;
212  os << indent << "Slope: " << this->Slope << std::endl;
213  os << indent << "Level: " << this->Level << std::endl;
214  os << indent << "MinPeakDistance: " << this->MinPeakDistance << std::endl;
215  this->FirstPeakToNeedleTip->PrintSelf(os, indent);
216  this->SecondPeakToNeedleTip->PrintSelf(os, indent);
217  this->ThirdPeakToNeedleTip->PrintSelf(os, indent);
218 
219  // 1D signal is packaged into an image with data type int1
220  this->SignalImage->PrintSelf(os, indent);
221 
222  // Data reading members
223  os << indent << "ReadParameters: " << std::endl;
224  os << indent << " dataType: " << this->ReadParameters.dataType << std::endl;
225  os << indent << " readMode: " << this->ReadParameters.readMode << std::endl;
226  os << indent << " firstSegment: " << this->ReadParameters.firstSegment << std::endl;
227  os << indent << " nbrSegments: " << this->ReadParameters.nbrSegments << std::endl;
228  os << indent << " firstSampleInSeg: " << this->ReadParameters.firstSampleInSeg << std::endl;
229  os << indent << " nbrSamplesInSeg: " << this->ReadParameters.nbrSamplesInSeg << std::endl;
230  os << indent << " segmentOffset: " << this->ReadParameters.segmentOffset << std::endl;
231  os << indent << " dataArraySize: " << this->ReadParameters.dataArraySize << std::endl;
232  os << indent << " segDescArraySize: " << this->ReadParameters.segDescArraySize << std::endl;
233  os << indent << " flags: " << this->ReadParameters.flags << std::endl;
234  os << indent << " reserved: " << this->ReadParameters.reserved << std::endl;
235  os << indent << " reserved2: " << this->ReadParameters.reserved2 << std::endl;
236  os << indent << " reserved3: " << this->ReadParameters.reserved3 << std::endl;
237 
238  os << indent << "DataDescription: " << std::endl;
239  os << indent << " returnedSamplesPerSeg: " << this->DataDescription.returnedSamplesPerSeg << std::endl;
240  os << indent << " indexFirstPoint: " << this->DataDescription.indexFirstPoint << std::endl;
241  os << indent << " sampTime: " << this->DataDescription.sampTime << std::endl;
242  os << indent << " vGain: " << this->DataDescription.vGain << std::endl;
243  os << indent << " vOffset: " << this->DataDescription.vOffset << std::endl;
244  os << indent << " returnedSegments: " << this->DataDescription.returnedSegments << std::endl;
245  os << indent << " nbrAvgWforms: " << this->DataDescription.nbrAvgWforms << std::endl;
246  os << indent << " actualTriggersInAcqLo: " << this->DataDescription.actualTriggersInAcqLo << std::endl;
247  os << indent << " actualTriggersInAcqHi: " << this->DataDescription.actualTriggersInAcqHi << std::endl;
248  os << indent << " actualDataSize: " << this->DataDescription.actualDataSize << std::endl;
249  os << indent << " reserved2: " << this->DataDescription.reserved2 << std::endl;
250  os << indent << " reserved3: " << this->DataDescription.reserved3 << std::endl;
251  os << indent << " actualDataSize: " << this->DataDescription.actualDataSize << std::endl;
252 
253  os << indent << "SegmentDescription: " << std::endl;
254  os << indent << " horPos: " << this->SegmentDescription.horPos << std::endl;
255  os << indent << " timeStampLo: " << this->SegmentDescription.timeStampLo << std::endl;
256  os << indent << " timeStampHi: " << this->SegmentDescription.timeStampHi << std::endl;
257 
258  // Data buffer for data that is read
259  if (this->ReadParameters.dataArraySize != -1)
260  {
261  os << indent << "DataArray: " << std::endl;
262  os << indent << " ";
263  for (int i = 0; i < this->ReadParameters.dataArraySize; ++i)
264  {
265  os << indent << this->DataArray[i];
266  }
267  os << indent << std::endl;
268 
269  os << indent << "EnvelopeArray: " << std::endl;
270  os << indent << " ";
271  for (int i = 0; i < this->ReadParameters.dataArraySize; ++i)
272  {
273  os << indent << this->EnvelopeArray[i];
274  }
275  os << indent << std::endl;
276 
277  os << indent << "PeakIdxArray: " << std::endl;
278  os << indent << " ";
279  for (int i = 0; i < this->ReadParameters.dataArraySize; ++i)
280  {
281  os << indent << this->PeakIdxArray[i];
282  }
283  os << indent << std::endl;
284  }
285 
286  // Envelope detection via a moving average filter
287  os << indent << "HannWindow: " << std::endl;
288  for (int i = 0; i < this->KernelSize; ++i)
289  {
290  os << indent << this->HannWindow[i];
291  }
292  os << indent << std::endl;
293 
294  // Peak detection
295  os << indent << "PeakEntries: " << std::endl;
296  for (auto it = this->PeakEntries.begin(); it != this->PeakEntries.end(); ++it)
297  {
298  os << indent << " X: " << it->first << ", " << "Y: " << it->second << std::endl;
299  }
300 }
301 
302 //----------------------------------------------------------------------------
304 {
305  // The following call will automatically detect ASBus connections between digitizers
306  // and combine connected digitizers (of identical model!) into multi-instruments.
307  // The call returns the number of multi-instruments and/or single instruments.
308  this->Status = AcqrsD1_multiInstrAutoDefine("", &(this->InstrumentCount));
309  if (AgilentError("AcqrsD1_multiInstrAutoDefine", this->Status))
310  {
311  return PLUS_FAIL;
312  }
313 
314  if (this->InstrumentCount < 1)
315  {
316  LOG_ERROR("No instrument found!");
317  return PLUS_FAIL; // No instrument found
318  }
319 
320  LOG_INFO(this->InstrumentCount << " Agilent Acqiris Digitizer(s) found on your PC");
321 
322  return PLUS_SUCCESS;
323 }
324 
325 //----------------------------------------------------------------------------
327 {
328  vtkPlusDataSource* firstPeakToolSource(nullptr);
329  vtkPlusDataSource* secondPeakToolSource(nullptr);
330  vtkPlusDataSource* thirdPeakToolSource(nullptr);
331 
332  std::string fullToolName = std::string("FirstPeakTo") + this->GetToolReferenceFrameName();
333  this->GetDataSource(fullToolName, firstPeakToolSource);
334  fullToolName = std::string("SecondPeakTo") + this->GetToolReferenceFrameName();
335  this->GetDataSource(fullToolName, secondPeakToolSource);
336  fullToolName = std::string("ThirdPeakTo") + this->GetToolReferenceFrameName();
337  this->GetDataSource(fullToolName, thirdPeakToolSource);
338 
339  vtkPlusDataSource* videoSource(nullptr);
340  this->GetFirstVideoSource(videoSource);
341 
342  // Start the acquisition
343  this->Status = AcqrsD1_acquire(this->InstrumentID);
344  if (AgilentError("AcqrsD1_acquire", this->Status))
345  {
346  return PLUS_FAIL;
347  }
348 
349  // Wait for interrupt to signal the end of acquisition with a timeout of 2 seconds
350  // Note: The maximum value is 10 seconds. See 'Reference Manual' for more details.
351  this->Status = AcqrsD1_waitForEndOfAcquisition(this->InstrumentID, 2000);
352  if (AgilentError("AcqrsD1_waitForEndOfAcquisition", this->Status))
353  {
354  // Clean up acquisition so we can try again
355  this->Status = AcqrsD1_stopAcquisition(this->InstrumentID);
356  AgilentError("AcqrsD1_stopAcquisition", this->Status);
357  return PLUS_FAIL;
358  }
359 
360  if (this->Status != VI_SUCCESS)
361  {
362  // Acquisition did not complete successfully
363  // Note: In case of a timeout, 'AcqrsD1_forceTrig' (software trigger) may be used.
364  // See 'Reference Manual' for more details.
365  this->StopRecording();
366  LOG_ERROR("The acquisition has been stopped - data invalid!");
367  return PLUS_FAIL;
368  }
369 
370  // Readout of the waveform
371  this->Status = AcqrsD1_readData(this->InstrumentID, 1, &(this->ReadParameters), this->DataArray, &(this->DataDescription), &(this->SegmentDescription));
372  if (AgilentError("AcqrsD1_readData", this->Status))
373  {
374  return PLUS_FAIL;
375  }
376 
377  //---- Envelope Detection via a moving average filter with a Hanning Kernel
378  ViInt32 halfWindowSize = this->KernelSize / 2;
379  for (ViInt32 s = this->DataDescription.indexFirstPoint; s < this->DataDescription.indexFirstPoint + this->DataDescription.returnedSamplesPerSeg; s++)
380  {
381  ViReal64 sum(0.0);
382  ViReal64 count = 0;
383  for (int i = -halfWindowSize; i < halfWindowSize + 1; ++i)
384  {
385  if (s + i < this->DataDescription.indexFirstPoint || s + i > this->DataDescription.indexFirstPoint + this->DataDescription.returnedSamplesPerSeg)
386  {
387  continue;
388  }
389  sum += abs(DataArray[s + i]) * this->HannWindow[i + halfWindowSize];
390  count += this->HannWindow[i + halfWindowSize]; // should be 1 if not using Hanning window
391  }
392  this->EnvelopeArray[s - this->DataDescription.indexFirstPoint] = sum / count;
393  }
394 
395  memcpy(this->SignalImage->GetScalarPointer(), this->EnvelopeArray, sizeof(short)*this->SampleCountPerAcquisition);
396 
397  // Find the absolute max (use more advanced algorithm for more robust tissue barrier detection)
398  ViReal64 absMaxValue(-1.0);
399  ViReal64 absMaxValueIndex = 0;
400 
401  for (ViInt32 i = this->DataDescription.indexFirstPoint; i < this->DataDescription.indexFirstPoint + this->DataDescription.returnedSamplesPerSeg; i++)
402  {
403  ViReal64 value = this->EnvelopeArray[i]; // *this->DataDescription.vGain - this->DataDescription.vOffset; // Volts
404  if (abs(value) > absMaxValue)
405  {
406  absMaxValue = abs(value);
407  absMaxValueIndex = i;
408  }
409  }
410 
411  // Find all peak indices
412  ViReal64 noiseLevel = 0.1 * absMaxValue;
413  ViInt32 peakIdxCount(0);
414  for (ViInt32 i = 1; i < this->DataDescription.returnedSamplesPerSeg - 1; i++)
415  {
416  if (this->EnvelopeArray[i] < noiseLevel)
417  {
418  continue;
419  }
420 
421  ViReal64 diffPrev = this->EnvelopeArray[i] - this->EnvelopeArray[i - 1];
422  ViReal64 diffNext = this->EnvelopeArray[i + 1] - this->EnvelopeArray[i];
423  if ((diffPrev * diffNext) <= 0 && diffPrev > 0)
424  {
425  this->PeakIdxArray[peakIdxCount] = i;
426  peakIdxCount++;
427  }
428  }
429 
430  if (peakIdxCount == 0)
431  {
432  LOG_INFO("No peaks found. Skipping frame.");
433  return PLUS_SUCCESS;
434  }
435 
436  // check for distances between peaks
437  ViInt32 peakIndex(0);
438  ViInt32 nextIndex(0);
439 
440  for (ViInt32 i = 0; i < peakIdxCount; i++)
441  {
442  peakIndex = this->PeakIdxArray[i];
443 
444  if (i + 1 < peakIdxCount)
445  {
446  nextIndex = this->PeakIdxArray[i + 1];
447  if ((nextIndex - peakIndex) > this->MinPeakDistance) //peaks are far away
448  {
449  PeakEntries.push_back(std::pair<ViInt16, ViInt32>(this->EnvelopeArray[peakIndex], peakIndex));
450  }
451  else if (this->EnvelopeArray[peakIndex] > this->EnvelopeArray[nextIndex]) //peaks are closer than required min distance
452  {
453  if (this->EnvelopeArray[nextIndex] > 0.5 * this->EnvelopeArray[peakIndex])
454  {
455  i++;
456  }
457  PeakEntries.push_back(std::pair<ViInt16, ViInt32>(this->EnvelopeArray[peakIndex], peakIndex));
458  }
459  }
460  else
461  {
462  PeakEntries.push_back(std::pair<ViInt16, ViInt32>(this->EnvelopeArray[peakIndex], peakIndex));
463  }
464  }
465 
466  std::sort(begin(PeakEntries), end(PeakEntries));
467  std::reverse(begin(PeakEntries), end(PeakEntries));
468 
469  auto largest = PeakEntries[0].first;
470  double largestIdx = PeakEntries[0].second;
471  // Calculate the mm offset of the abs max value
472  double mmOffset1 = (this->DelayTimeSec * this->SpeedOfSound * 1000 / 2.0) + (absMaxValueIndex * this->SpeedOfSound * 1000 / (2.0 * this->SampleFrequencyHz));
473  // Transformation based on the mm offset of the abs max values
474  this->FirstPeakToNeedleTip->SetElement(2, 3, mmOffset1);
475  this->ToolTimeStampedUpdate(firstPeakToolSource->GetId(), this->FirstPeakToNeedleTip.Get(), TOOL_OK, this->FrameNumber, UNDEFINED_TIMESTAMP);
476 
477  if (this->PeakEntries.size() > 1)
478  {
479  auto second_largest = PeakEntries[1].first;
480  double second_largestIdx = PeakEntries[1].second;
481  double mmOffset2 = (this->DelayTimeSec * this->SpeedOfSound * 1000 / 2.0) + (second_largestIdx * this->SpeedOfSound * 1000 / (2.0 * this->SampleFrequencyHz));
482  this->SecondPeakToNeedleTip->SetElement(2, 3, mmOffset2);
483  this->ToolTimeStampedUpdate(secondPeakToolSource->GetId(), this->SecondPeakToNeedleTip.Get(), TOOL_OK, this->FrameNumber, UNDEFINED_TIMESTAMP);
484  }
485  else
486  {
487  this->SecondPeakToNeedleTip->SetElement(2, 3, 0.0);
488  this->ToolTimeStampedUpdate(secondPeakToolSource->GetId(), this->SecondPeakToNeedleTip.Get(), TOOL_OK, this->FrameNumber, UNDEFINED_TIMESTAMP);
489  }
490 
491  if (this->PeakEntries.size() > 2)
492  {
493  auto third_largest = PeakEntries[2].first;
494  double third_largestIdx = PeakEntries[2].second;
495  double mmOffset3 = (this->DelayTimeSec * this->SpeedOfSound * 1000 / 2.0) + (third_largestIdx * this->SpeedOfSound * 1000 / (2.0 * this->SampleFrequencyHz));
496  this->ThirdPeakToNeedleTip->SetElement(2, 3, mmOffset3);
497  this->ToolTimeStampedUpdate(thirdPeakToolSource->GetId(), this->ThirdPeakToNeedleTip.Get(), TOOL_OK, this->FrameNumber, UNDEFINED_TIMESTAMP);
498  }
499  else
500  {
501  this->ThirdPeakToNeedleTip->SetElement(2, 3, 0.0);
502  this->ToolTimeStampedUpdate(thirdPeakToolSource->GetId(), this->ThirdPeakToNeedleTip.Get(), TOOL_OK, this->FrameNumber, UNDEFINED_TIMESTAMP);
503  }
504 
505  videoSource->AddItem(this->SignalImage, US_IMG_ORIENT_MF, US_IMG_BRIGHTNESS, this->FrameNumber);
506  this->FrameNumber++;
507 
508  this->PeakEntries.clear();
509 
510  return PLUS_SUCCESS;
511 }
512 
513 //----------------------------------------------------------------------------
514 PlusStatus vtkPlusAgilentScopeTracker::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
515 {
516  // This function will read the data from the rootConfigElement, and populate your member variables
517  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
518 
519  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViReal64, SampleFrequencyHz, deviceConfig);
520  this->SampleIntervalSec = 1.0 / (this->SampleFrequencyHz);
521 
522  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViReal64, DelayTimeSec, deviceConfig);
523  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, SampleCountPerAcquisition, deviceConfig);
524  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViReal64, FullScale, deviceConfig);
525  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViReal64, Offset, deviceConfig);
526  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, Bandwidth, deviceConfig);
527  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, TrigCoupling, deviceConfig);
528  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, Slope, deviceConfig);
529  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViReal64, Level, deviceConfig);
530  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, SpeedOfSound, deviceConfig);
531  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, KernelSize, deviceConfig);
532  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(ViInt32, MinPeakDistance, deviceConfig);
533 
534  return PLUS_SUCCESS;
535 }
536 
537 //----------------------------------------------------------------------------
539 {
540  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfig);
541 
542  deviceConfig->SetDoubleAttribute("SampleFrequencyHz", this->SampleFrequencyHz);
543  deviceConfig->SetDoubleAttribute("DelayTimeSec", this->DelayTimeSec);
544  deviceConfig->SetIntAttribute("SampleCountPerAcquisition", this->SampleCountPerAcquisition);
545  deviceConfig->SetDoubleAttribute("FullScale", this->FullScale);
546  deviceConfig->SetDoubleAttribute("Offset", this->Offset);
547  deviceConfig->SetIntAttribute("Bandwidth", this->Bandwidth);
548  deviceConfig->SetIntAttribute("TrigCoupling", this->TrigCoupling);
549  deviceConfig->SetIntAttribute("Slope", this->Slope);
550  deviceConfig->SetDoubleAttribute("Level", this->Level);
551  deviceConfig->SetIntAttribute("SpeedOfSound", this->SpeedOfSound);
552  deviceConfig->SetIntAttribute("KernelSize", this->KernelSize);
553  deviceConfig->SetIntAttribute("MinPeakDistance", this->MinPeakDistance);
554 
555  return PLUS_SUCCESS;
556 }
557 
558 //----------------------------------------------------------------------------
560 {
561  // This function makes sure the user has configured the device in the config file according to the scope's needs
562  vtkPlusDataSource* source(nullptr);
563  if (this->GetFirstVideoSource(source) != PLUS_SUCCESS)
564  {
565  LOG_ERROR("Agilent scope device requires a video data source to write 1D signal to.");
566  return PLUS_FAIL;
567  }
568 
569  std::string fullToolName = std::string("FirstPeakTo") + this->GetToolReferenceFrameName();
570  if (this->GetDataSource(fullToolName, source) != PLUS_SUCCESS)
571  {
572  LOG_ERROR("Agilent scope device requires a tool data source with ID \"FirstPeak\" to envelope detected first peak to.");
573  return PLUS_FAIL;
574  }
575 
576  fullToolName = std::string("SecondPeakTo") + this->GetToolReferenceFrameName();
577  if (this->GetDataSource(fullToolName, source) != PLUS_SUCCESS)
578  {
579  LOG_ERROR("Agilent scope device requires a tool data source with ID \"SecondPeak\" to envelope detected second peak to.");
580  return PLUS_FAIL;
581  }
582 
583  fullToolName = std::string("ThirdPeakTo") + this->GetToolReferenceFrameName();
584  if (this->GetDataSource(fullToolName, source) != PLUS_SUCCESS)
585  {
586  LOG_ERROR("Agilent scope device requires a tool data source with ID \"ThirdPeak\" to envelope detected second peak to.");
587  return PLUS_FAIL;
588  }
589 
590  return PLUS_SUCCESS;
591 }
592 
593 //----------------------------------------------------------------------------
595 {
596  // Initialization of the instrument
597  strncpy(this->ResourceName, "PCI::INSTR0", 16);
598  strncpy(this->OptionString, "", 32);
599  this->Status = Acqrs_InitWithOptions(this->ResourceName, VI_FALSE, VI_TRUE, this->OptionString, &this->InstrumentID);
600  if (AgilentError("Acqrs_InitWithOptions", this->Status))
601  {
602  return PLUS_FAIL;
603  }
604 
605  // Configuration of the digitizer
606  // Configure timebase
607  this->Status = AcqrsD1_configHorizontal(this->InstrumentID, this->SampleIntervalSec, this->DelayTimeSec);
608  if (AgilentError("AcqrsD1_configHorizontal", this->Status))
609  {
610  return PLUS_FAIL;
611  }
612 
613  this->Status = AcqrsD1_configMemory(this->InstrumentID, this->SampleCountPerAcquisition, this->SegmentCountPerAcquisition);
614  if (AgilentError("AcqrsD1_configMemory", this->Status))
615  {
616  return PLUS_FAIL;
617  }
618 
619  // Confirm valid sample count and segment count
620  ViInt32 sampleCountPerAcq;
621  ViInt32 segmentCountPerAcq;
622  this->Status = AcqrsD1_getMemory(this->InstrumentID, &sampleCountPerAcq, &segmentCountPerAcq);
623  if (AgilentError("AcqrsD1_getMemory", this->Status))
624  {
625  return PLUS_FAIL;
626  }
627  if (sampleCountPerAcq != this->SampleCountPerAcquisition || segmentCountPerAcq != this->SegmentCountPerAcquisition)
628  {
629  LOG_WARNING("Sample count or segment count per acquisition does not match requested values. Replacing with actual values.");
630  this->SampleCountPerAcquisition = sampleCountPerAcq;
631  this->SegmentCountPerAcquisition = segmentCountPerAcq;
632  }
633 
634  this->SignalImage->SetDimensions(this->SampleCountPerAcquisition, 1, 1);
635  this->SignalImage->AllocateScalars(VTK_SHORT, 1);
636 
637  vtkPlusDataSource* source(nullptr);
638  this->GetFirstVideoSource(source);
639 
640  source->SetInputFrameSize(this->SampleCountPerAcquisition, 1, 1);
641  source->SetNumberOfScalarComponents(1);
642  source->SetPixelType(VTK_SHORT);
643 
644  // Configure vertical settings of channel 1
645  this->Status = AcqrsD1_configVertical(this->InstrumentID, 1, this->FullScale, this->Offset, this->Coupling, this->Bandwidth);
646  if (AgilentError("AcqrsD1_configVertical", this->Status))
647  {
648  return PLUS_FAIL;
649  }
650 
651  // Configure edge trigger on channel 1
652  this->Status = AcqrsD1_configTrigClass(this->InstrumentID, 0, 0x00000001, 0, 0, 0.0, 0.0);
653  if (AgilentError("AcqrsD1_configTrigClass", this->Status))
654  {
655  return PLUS_FAIL;
656  }
657 
658  // Configure the trigger conditions of channel 1 (internal trigger)
659  this->Status = AcqrsD1_configTrigSource(this->InstrumentID, 1, this->TrigCoupling, this->Slope, this->Level, 0.0);
660  if (AgilentError("AcqrsD1_configTrigSource", this->Status))
661  {
662  return PLUS_FAIL;
663  }
664 
665  // Definition of the read parameters for raw ADC readout
666  this->ReadParameters.dataType = ReadInt16; // 16bit, raw ADC values data type
667  this->ReadParameters.readMode = ReadModeStdW; // Single-segment read mode
668  this->ReadParameters.firstSegment = 0;
669  this->ReadParameters.nbrSegments = 1;
670  this->ReadParameters.firstSampleInSeg = 0;
671  this->ReadParameters.nbrSamplesInSeg = this->SampleCountPerAcquisition;
672  this->ReadParameters.segmentOffset = 0;
673  this->ReadParameters.dataArraySize = (this->SampleCountPerAcquisition + 32) * sizeof(ViInt16); // Array size in bytes
674  this->ReadParameters.segDescArraySize = sizeof(AqSegmentDescriptor);
675 
676  this->ReadParameters.flags = 0;
677  this->ReadParameters.reserved = 0;
678  this->ReadParameters.reserved2 = 0;
679  this->ReadParameters.reserved3 = 0;
680 
681  // Read the channel 1 waveform as raw ADC values
682  this->DataArray = new ViInt16[this->ReadParameters.dataArraySize / sizeof(ViInt16)];
683  this->EnvelopeArray = new ViInt16[this->ReadParameters.dataArraySize / sizeof(ViInt16)];
684  this->PeakIdxArray = new ViInt32[this->ReadParameters.dataArraySize / sizeof(ViInt32)];
685 
686  //---- Hanning Window
687  this->HannWindow = new ViReal64[this->KernelSize];
688 
689  for (int k = 0; k < this->KernelSize; k++)
690  {
691  this->HannWindow[k] = 0.5 * (1 - cos((2 * vtkMath::Pi() * k) / (this->KernelSize - 1)));
692  }
693 
694  return PLUS_SUCCESS;
695 }
696 
697 //----------------------------------------------------------------------------
699 {
700  // Close the instrument
701  this->Status = Acqrs_close(this->InstrumentID);
702  if (AgilentError("Acqrs_close", this->Status))
703  {
704  return PLUS_FAIL;
705  }
706 
707  // Free remaining resources
708  this->Status = Acqrs_closeAll();
709  if (AgilentError("Acqrs_closeAll", this->Status))
710  {
711  return PLUS_FAIL;
712  }
713 
714  delete [] this->DataArray;
715  delete [] this->EnvelopeArray;
716  delete [] this->PeakIdxArray;
717 
718  return PLUS_SUCCESS;
719 }
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
PlusStatus GetDataSource(const char *aSourceId, vtkPlusDataSource *&aSource)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
unsigned long LastFrameNumber
Index of the last frame number.
vtkSmartPointer< vtkMatrix4x4 > FirstPeakToNeedleTip
const char * source
Definition: phidget22.h:2461
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
vtkSmartPointer< vtkImageData > SignalImage
vtkStandardNewMacro(vtkPlusAgilentScopeTracker)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual PlusStatus ToolTimeStampedUpdate(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, unsigned long frameNumber, double unfilteredtimestamp, const igsioFieldMapType *customFields=NULL)
virtual PlusStatus AddItem(vtkImageData *frame, US_IMAGE_ORIENTATION usImageOrientation, US_IMAGE_TYPE imageType, long frameNumber, double unfilteredTimestamp=UNDEFINED_TIMESTAMP, double filteredTimestamp=UNDEFINED_TIMESTAMP, const igsioFieldMapType *customFields=NULL)
Interface class for Agilent oscilloscope devices.
for i
double AcquisitionRate
void PrintSelf(ostream &os, vtkIndent indent)
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual PlusStatus InternalDisconnect()
Disconnect from the oscilloscope hardware.
std::vector< std::pair< ViInt16, ViInt32 > > PeakEntries
PlusStatus GetFirstVideoSource(vtkPlusDataSource *&anImage)
unsigned long FrameNumber
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual PlusStatus InternalConnect()
Connect to the oscilloscope hardware.
virtual PlusStatus StopRecording()
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
vtkSmartPointer< vtkMatrix4x4 > SecondPeakToNeedleTip
const char const char * value
Definition: phidget22.h:5111
Phidget_ChannelClass uint32_t * count
Definition: phidget22.h:1321
bool StartThreadForInternalUpdates
PlusStatus SetNumberOfScalarComponents(unsigned int numberOfScalarComponents)
std::string GetToolReferenceFrameName() const
vtkSmartPointer< vtkMatrix4x4 > ThirdPeakToNeedleTip
Interface to a 3D positioning tool, video source, or generalized data stream.