PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusSonixPortaVideoSource.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 Copyright (c) Siddharth Vikal, Elvis Chen, 2008
10 All rights reserved.
11 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
12 Authors include: Siddharth Vikal (Queen's University),
13 Elvis Chen (Queen's University), Danielle Pace (Robarts Research Institute
14 and The University of Western Ontario)
15 =========================================================================*/
16 
17 #include "PlusConfigure.h"
18 
19 // Porta includes
20 #include <porta_params_def.h>
21 #include <ImagingModes.h>
22 #include <porta.h>
23 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 6)
24  #include <porta_std_includes.h>
25 #else
26  #include <porta_def.h>
27  #include <porta_wrapper.h>
28  #include "Objbase.h" // required for CoInitialize
29 #endif
30 
31 // Local includes
32 #include "vtkPlusChannel.h"
33 #include "vtkPlusDataSource.h"
35 
36 // OS includes
37 #include <ctype.h>
38 
39 // VTK includes
40 #include <vtkStreamingDemandDrivenPipeline.h>
41 #include <vtkTimerLog.h>
42 #include <vtkUnsignedCharArray.h>
43 #include <vtksys/SystemTools.hxx>
44 #include <vtkMatrix4x4.h>
45 #include <vtkTransform.h>
46 #include <vtkImageData.h>
47 #include <vtkInformation.h>
48 #include <vtkInformationVector.h>
49 #include <vtkMultiThreader.h>
50 #include <vtkObjectFactory.h>
51 
52 // because of warnings in windows header push and pop the warning level
53 #ifdef _MSC_VER
54  #pragma warning (push, 3)
55 #endif
56 
57 // STL includes
58 #include <vector>
59 #include <string>
60 
61 #ifdef _MSC_VER
62  #pragma warning (pop)
63 #endif
64 
65 #if ( _MSC_VER >= 1300 ) // Visual studio .NET
66  #pragma warning ( disable : 4311 )
67  #pragma warning ( disable : 4312 )
68  #define vtkGetWindowLong GetWindowLongPtr
69  #define vtkSetWindowLong SetWindowLongPtr
70  #define vtkGWL_USERDATA GWLP_USERDATA
71 #else // regular Visual studio
72  #define vtkGetWindowLong GetWindowLong
73  #define vtkSetWindowLong SetWindowLong
74  #define vtkGWL_USERDATA GWL_USERDATA
75 #endif //
76 
77 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION >= 6)
78 
79 // This is a workaround to avoid linker error due to missing portaImportChromaMap (caused by Ultrasonix SDK 6.1.0 bug)
80 int portaImportChromaMap(int index, const unsigned int* lut)
81 {
82  LOG_ERROR("portaImportChromaMap function is missing from Ultrasonix Porta SDK");
83  return 0;
84 }
85 
86 // The porta wrapper implementation is not included in porta.lib, so we have to include it here
87 #include "porta_wrapper.cpp"
88 
89 #endif
90 
91 //----------------------------------------------------------------------------
92 // The singleton, and the singleton cleanup
93 vtkPlusSonixPortaVideoSource* vtkPlusSonixPortaVideoSource::Instance = 0;
94 vtkPlusSonixPortaVideoSourceCleanup vtkPlusSonixPortaVideoSource::Cleanup;
95 
96 //----------------------------------------------------------------------------
98 {
99 }
100 
101 //----------------------------------------------------------------------------
103 {
104  // Destroy any remaining output window.
105  vtkPlusSonixPortaVideoSource::SetInstance(NULL);
106 }
107 
108 //----------------------------------------------------------------------------
110  : DisplayIndex(0)
111  , AutoClipEnabled(false)
112  , CurrentDepthMm(-1)
113  , ImageGeometryChanged(false)
114 {
115  this->Porta = new porta;
116  this->PortaConnected = false;
117  this->ProbeInformation = new probeInfo;
118 
119  this->Usm = 0;
120  this->Pci = 0;
121  this->PortaBModeWidth = 480;
122  this->PortaBModeHeight = 436;
123  this->ImageBuffer = NULL;
124  this->ImagingMode = (int)BMode;
125  this->PortaProbeName = NULL;
126  this->PortaSettingPath = NULL;
127  this->PortaLicensePath = NULL;
128  this->PortaFirmwarePath = NULL;
129  this->PortaLUTPath = NULL;
130  this->PortaCineSize = 256 * 1024 * 1024; // defaults to 256MB of Cine
131  this->FirstCallToAddFrameToBuffer = true;
132  this->VolumeIndex = 0;
133  this->FrameIndexInVolume = 0;
134 
135  this->FramePerVolume = -1;
136  this->StepPerFrame = -1;
137  this->MotorRotationRangeDeg = -1;
138 
139  this->Zoom = -1;
140  this->Depth = -1;
141  this->Frequency = -1;
142  this->Gain = -1;
143 
144  this->LastRotationCcw = false;
145 
147 }
148 
149 //----------------------------------------------------------------------------
151 {
152  // Release all previously allocated memory
153  this->SetPortaProbeName(NULL);
154  this->SetPortaSettingPath(NULL);
155  this->SetPortaLicensePath(NULL);
156  this->SetPortaFirmwarePath(NULL);
157  this->SetPortaLUTPath(NULL);
158  this->SetImageToTransducerTransformName(NULL);
159 
160  delete [] this->ImageBuffer;
161  this->ImageBuffer = NULL;
162 
163  delete this->ProbeInformation;
164  this->ProbeInformation = NULL;
165 
166  delete this->Porta;
167  this->Porta = NULL;
168 }
169 
170 //----------------------------------------------------------------------------
171 // up the reference count so it behaves like New
172 vtkPlusSonixPortaVideoSource* vtkPlusSonixPortaVideoSource::New()
173 {
174  vtkPlusSonixPortaVideoSource* ret = vtkPlusSonixPortaVideoSource::GetInstance();
175  ret->Register(NULL);
176  return (ret);
177 }
178 
179 //----------------------------------------------------------------------------
180 // Return the single instance of the vtkOutputWindow
181 vtkPlusSonixPortaVideoSource* vtkPlusSonixPortaVideoSource::GetInstance()
182 {
183  if (!vtkPlusSonixPortaVideoSource::Instance)
184  {
185  // try the factory first
186  vtkPlusSonixPortaVideoSource::Instance = (vtkPlusSonixPortaVideoSource*)vtkObjectFactory::CreateInstance("vtkPlusSonixPortaVideoSource");
187 
188  if (!vtkPlusSonixPortaVideoSource::Instance)
189  {
190  vtkPlusSonixPortaVideoSource::Instance = new vtkPlusSonixPortaVideoSource();
191  }
192 
193  if (!vtkPlusSonixPortaVideoSource::Instance)
194  {
195  int error = 0;
196  }
197  }
198 
199  return (vtkPlusSonixPortaVideoSource::Instance);
200 }
201 
202 //----------------------------------------------------------------------------
203 void vtkPlusSonixPortaVideoSource::SetInstance(vtkPlusSonixPortaVideoSource* instance)
204 {
205  if (vtkPlusSonixPortaVideoSource::Instance == instance)
206  {
207  return;
208  }
209 
210  // preferably this will be NULL
211  if (vtkPlusSonixPortaVideoSource::Instance)
212  {
213  vtkPlusSonixPortaVideoSource::Instance->Delete();
214  vtkPlusSonixPortaVideoSource::Instance = NULL;
215  }
216 
217  vtkPlusSonixPortaVideoSource::Instance = instance;
218 
219  if (!instance)
220  {
221  return;
222  }
223 
224  //user will call ->Delete() after setting instance
225  instance->Register(NULL);
226 }
227 
228 //----------------------------------------------------------------------------
229 std::string vtkPlusSonixPortaVideoSource::GetSdkVersion()
230 {
231  std::ostringstream version;
232  version << "UltrasonixSDK-" << PLUS_ULTRASONIX_SDK_MAJOR_VERSION << "." << PLUS_ULTRASONIX_SDK_MINOR_VERSION << "." << PLUS_ULTRASONIX_SDK_PATCH_VERSION;
233  return version.str();
234 }
235 
236 //----------------------------------------------------------------------------
237 void vtkPlusSonixPortaVideoSource::PrintSelf(ostream& os, vtkIndent indent)
238 {
239  this->Superclass::PrintSelf(os, indent);
240  os << indent << "Imaging mode: " << this->ImagingMode << "\n";
241  os << indent << "Frequency: " << this->Frequency << "MHz\n";
242 }
243 
244 //----------------------------------------------------------------------------
245 // the callback function used when there is a new frame of data received
246 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 5) || (PLUS_ULTRASONIX_SDK_MAJOR_VERSION == 5 && PLUS_ULTRASONIX_SDK_MINOR_VERSION < 7)
247  // SDK version < 5.7.x
248  bool vtkPlusSonixPortaVideoSource::vtkPlusSonixPortaVideoSourceNewFrameCallback(void* param, int id)
249 #elif (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 6)
250  // 5.7.x <= SDK version < 6.x
251  bool vtkPlusSonixPortaVideoSource::vtkPlusSonixPortaVideoSourceNewFrameCallback(void* param, int id, int header)
252 #else
253  int vtkPlusSonixPortaVideoSource::vtkPlusSonixPortaVideoSourceNewFrameCallback(void* param, int id, int header)
254 #endif
255 {
256  if (id == 0)
257  {
258  // no actual data received
259  return (false);
260  }
261 
262  bool motorRotationCcw = (header & 0x00000002) > 0;
263  int motorStepCount = (header & 0xFFFC) >> 2; // always starts from 0, no matter what the starting angle was (not an absolute position but from the FOV start position)
264 
265  vtkPlusSonixPortaVideoSource::GetInstance()->AddFrameToBuffer(param, id, motorRotationCcw, motorStepCount);
266 
267  return true;
268 }
269 
270 //----------------------------------------------------------------------------
271 // copy the Device Independent Bitmap from the VFW framebuffer into the
272 // vtkVideoSource framebuffer (don't do the unpacking yet)
273 PlusStatus vtkPlusSonixPortaVideoSource::AddFrameToBuffer(void* param, int id, bool motorRotationCcw, int motorStepCount)
274 {
275  if (!this->Recording)
276  {
277  // drop the frame, we are not recording data now
278  return PLUS_SUCCESS;
279  }
280 
281  if (this->OutputChannels.empty())
282  {
283  LOG_ERROR("No output channels defined for vtkPlusSonixPortaVideoSource");
284  return PLUS_FAIL;
285  }
286  vtkPlusChannel* outputChannel = this->OutputChannels[0];
287 
288  FrameSizeType frameSize = {0, 0, 0};
289  this->GetInputFrameSize(*outputChannel, frameSize);
290  vtkPlusDataSource* aSource(NULL);
291  if (outputChannel->GetVideoSource(aSource) != PLUS_SUCCESS)
292  {
293  LOG_ERROR("Unable to retrieve the video source in the SonixPorta device.");
294  return PLUS_FAIL;
295  }
296 
297  // for frame containing FC (frame count) in the beginning for data coming from cine, jump 2 bytes
298  int numberOfBytesToSkip = 4;
299 
300  // Aligns the motor for correct acquisition of its angle
301  if (this->FirstCallToAddFrameToBuffer && this->MotorRotationRangeDeg > 0)
302  {
303  this->Porta->setParam(prmMotorStatus, 0);
304  this->Porta->setParam(prmMotorStatus, 1);
305  this->FirstCallToAddFrameToBuffer = false;
306  this->LastRotationCcw = !motorRotationCcw; // force initialization of a new volume
307  // First volume index will be 0 for CCW rotation, 1 for CW rotation.
308  // Even volume index is always CCW, odd index is always CW rotation direction.
309  this->VolumeIndex = motorRotationCcw ? -1 : 0;
310  this->FrameIndexInVolume = 0;
311  }
312 
313  if (this->ImageBuffer == NULL)
314  {
315  LOG_ERROR("vtkPlusSonixPortaVideoSource::AddFrameToBuffer failed: no image buffer is allocated");
316  return PLUS_FAIL;
317  }
318  this->Porta->getBwImage(0, this->ImageBuffer, false);
319 
320  // get the pointer to the actual incoming data onto a local pointer
321  unsigned char* deviceDataPtr = static_cast<unsigned char*>(this->ImageBuffer);
322 
323  double motorAngleDeg = 0;
324  if (this->ProbeInformation->motorized)
325  {
326  if (motorRotationCcw)
327  {
328  motorAngleDeg = -this->MotorRotationStartAngleDeg + motorStepCount * this->MotorRotationPerStepDeg;
329  }
330  else
331  {
332  motorAngleDeg = this->MotorRotationStartAngleDeg - motorStepCount * this->MotorRotationPerStepDeg;
333  }
334  if (this->LastRotationCcw != motorRotationCcw)
335  {
336  // the motor changed rotation direction
337  this->LastRotationCcw = motorRotationCcw;
338  this->VolumeIndex++;
339  this->FrameIndexInVolume = 0;
340  }
341  }
342 
343  if (this->ImageGeometryChanged)
344  {
345  this->ImageGeometryChanged = false;
346  int currentDepth = -1;
347  if (!this->Porta->getParam("b-depth", currentDepth))
348  {
349  LOG_WARNING("Failed to retrieve b-depth parameter");
350  }
351 
352  int currentPixelSpacingMicron[2] = {-1, -1};
353  if (!this->Porta->getMicronsPerPixel(this->DisplayIndex, currentPixelSpacingMicron[0], currentPixelSpacingMicron[1]))
354  {
355  LOG_WARNING("Failed to retrieve pixel spacing from Porta");
356  }
357 
358  int line = this->ProbeInformation->elements / 2; // middle transducer element
359  int sample = 0; // transducer surface
360  int currentTransducerOriginPixels[2] = {-1, -1};
361  if (!this->Porta->getPixelCoordinates(this->DisplayIndex, line, sample, currentTransducerOriginPixels[0], currentTransducerOriginPixels[1], false))
362  {
363  LOG_WARNING("Failed to retrieve transducer origin parameter from Porta");
364  }
365 
366  this->CurrentDepthMm = currentDepth;
367 
368  this->CurrentPixelSpacingMm[0] = 0.001 * currentPixelSpacingMicron[0];
369  this->CurrentPixelSpacingMm[1] = 0.001 * currentPixelSpacingMicron[1];
370  this->CurrentPixelSpacingMm[2] = (this->CurrentPixelSpacingMm[0] + this->CurrentPixelSpacingMm[1]) / 2.0;
371 
372  std::array<int, 3> clipRectangleOrigin = aSource->GetClipRectangleOrigin();
373  this->CurrentTransducerOriginPixels[0] = currentTransducerOriginPixels[0] - clipRectangleOrigin[0];
374  this->CurrentTransducerOriginPixels[1] = currentTransducerOriginPixels[1] - clipRectangleOrigin[1];
375  }
376 
377  igsioFieldMapType customFields;
378 
379  std::ostringstream frameIndexInVolumeStr;
380  frameIndexInVolumeStr << this->FrameIndexInVolume;
381  customFields["FrameIndex"].first = FRAMEFIELD_FORCE_SERVER_SEND;
382  customFields["FrameIndex"].second = frameIndexInVolumeStr.str();
383  std::ostringstream volumeIndexStr;
384  volumeIndexStr << this->VolumeIndex;
385  customFields["VolumeIndex"].first = FRAMEFIELD_FORCE_SERVER_SEND;
386  customFields["VolumeIndex"].second = volumeIndexStr.str();
387  std::ostringstream motorAngleDegStr;
388  motorAngleDegStr << motorAngleDeg;
389  customFields["MotorAngleDeg"].first = FRAMEFIELD_FORCE_SERVER_SEND;
390  customFields["MotorAngleDeg"].second = motorAngleDegStr.str();
391  customFields["MotorToMotorRotatedTransform"].first = FRAMEFIELD_NONE;
392  customFields["MotorToMotorRotatedTransform"].second = this->GetMotorToMotorRotatedTransform(motorAngleDeg);
393  customFields["MotorToMotorRotatedTransformStatus"].first = FRAMEFIELD_NONE;
394  customFields["MotorToMotorRotatedTransformStatus"].second = "OK";
395  std::ostringstream motorRotatedToTransducerTransformStr;
396  motorRotatedToTransducerTransformStr << "1 0 0 0 0 1 0 " << -this->ProbeInformation->motorRadius * 0.001 << " 0 0 1 0 0 0 0 1";
397  customFields["MotorRotatedToTransducerTransform"].second = motorRotatedToTransducerTransformStr.str();
398  customFields["MotorRotatedToTransducerTransformStatus"].first = FRAMEFIELD_NONE;
399  customFields["MotorRotatedToTransducerTransformStatus"].second = "OK";
400 
401  PlusStatus status = this->AddVideoItemToVideoSource(*aSource, deviceDataPtr, aSource->GetInputImageOrientation(), frameSize, VTK_UNSIGNED_CHAR, 1, US_IMG_BRIGHTNESS, numberOfBytesToSkip, id, UNDEFINED_TIMESTAMP, UNDEFINED_TIMESTAMP, &customFields);
402 
403  this->Modified();
404  return status;
405 }
406 
407 //----------------------------------------------------------------------------
408 std::string vtkPlusSonixPortaVideoSource::GetMotorToMotorRotatedTransform(double angle)
409 {
410  vtkSmartPointer<vtkMatrix4x4> matrix = vtkSmartPointer<vtkMatrix4x4>::New();
411 
412  vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
413  transform->SetMatrix(matrix);
414  transform->RotateX(angle);
415  transform->GetMatrix(matrix);
416 
417  std::ostringstream matrixToString;
418  matrixToString <<
419  matrix->GetElement(0, 0) << " " << matrix->GetElement(0, 1) << " " << matrix->GetElement(0, 2) << " " << matrix->GetElement(0, 3) << " " <<
420  matrix->GetElement(1, 0) << " " << matrix->GetElement(1, 1) << " " << matrix->GetElement(1, 2) << " " << matrix->GetElement(1, 3) << " " <<
421  matrix->GetElement(2, 0) << " " << matrix->GetElement(2, 1) << " " << matrix->GetElement(2, 2) << " " << matrix->GetElement(2, 3) << " " <<
422  matrix->GetElement(3, 0) << " " << matrix->GetElement(3, 1) << " " << matrix->GetElement(3, 2) << " " << matrix->GetElement(3, 3) ;
423 
424  return matrixToString.str();
425 }
426 
427 //----------------------------------------------------------------------------
429 {
430  vtkPlusDataSource* aSource(NULL);
431  if (this->GetFirstVideoSource(aSource) != PLUS_SUCCESS)
432  {
433  LOG_ERROR(this->GetDeviceId() << ": Unable to retrieve video source.");
434  return PLUS_FAIL;
435  }
436 
437  // Set B-mode image size
438  delete [] this->ImageBuffer;
439  this->ImageBuffer = new unsigned char [ this->PortaBModeWidth * this->PortaBModeHeight * 4 ];
440  if (this->ImageBuffer == NULL)
441  {
442  LOG_ERROR("vtkPlusSonixPortaVideoSource constructor: not enough memory for ImageBuffer");
443  return PLUS_FAIL;
444  }
445 
446  LOG_TRACE("InternalConnect start");
447  if (this->PortaSettingPath == 0 ||
448  this->PortaFirmwarePath == 0 ||
449  this->PortaLicensePath == 0 ||
450  this->PortaLUTPath == 0)
451  {
452  LOG_ERROR("One of the Porta paths has not been set (check PortaSettingPath, PortaFirmwarePath, PortaLicensePath, PortaLUTPath attributes)");
453  return PLUS_FAIL;
454  }
455 
456 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION >= 6)
457  // CoInitialize required. Without that this->Porta->loadPreset fails.
458  CoInitialize(NULL);
459 #endif
460 
461  int channels = (this->Usm == 2) ? 32 : 64; // from Ultrasonix SDK PortaDemo.cpp
462 
463  LOG_TRACE("Porta initialization:"
464  << " PortaFirmwarePath=" << this->PortaFirmwarePath
465  << " PortaSettingPath=" << this->PortaSettingPath
466  << " PortaLicensePath=" << this->PortaLicensePath
467  << " PortaLUTPath=" << this->PortaLUTPath
468  << " Usm=" << this->Usm
469  << " Pci=" << this->Pci
470  << " channels=" << channels);
471  if (!this->Porta->init(this->PortaCineSize,
472  this->PortaFirmwarePath,
473  this->PortaSettingPath,
474  this->PortaLicensePath,
475  this->PortaLUTPath,
476  this->Usm,
477  this->Pci,
478  0, // high voltage (from Ultrasonix SDK PortaDemo.cpp)
479  1, // DDR (from Ultrasonix SDK PortaDemo.cpp)
480  channels))
481  {
482  LOG_ERROR("Initialize: Porta could not be initialized: (" << this->GetLastPortaError() << ")"
483  << " PortaFirmwarePath=" << this->PortaFirmwarePath
484  << " PortaSettingPath=" << this->PortaSettingPath
485  << " PortaLicensePath=" << this->PortaLicensePath
486  << " PortaLUTPath=" << this->PortaLUTPath
487  << " Usm=" << this->Usm
488  << " Pci=" << this->Pci
489  << " channels=" << channels);
490  this->PortaConnected = false;
491  return PLUS_FAIL;
492  }
493 
494  this->PortaConnected = true;
495  this->FirstCallToAddFrameToBuffer = true;
496 
497  this->ImageGeometryChanged = true; // trigger an initial update of geometry info
498 
499  // select the probe
500 
501  if (!this->Porta->isConnected())
502  {
503  LOG_ERROR("Porta is not connected");
504  this->InternalDisconnect();
505  return PLUS_FAIL;
506  }
507 
508  // the 3D/4D probe is always connected to port 0
509  const int connectorId = 0;
510  int probeId = (char)this->Porta->getProbeID(connectorId);
511  if (!this->Porta->selectProbe(probeId))
512  {
513  LOG_ERROR("Porta connect error: failed to select probe plugged into connector " << connectorId);
514  this->InternalDisconnect();
515  return PLUS_FAIL;
516  }
517 
518  // select the probeId read and see if it is motorized
519  if (!this->Porta->getProbeInfo(*this->ProbeInformation))
520  {
521  LOG_ERROR("Porta connect error: failed to get probe information");
522  this->InternalDisconnect();
523  return PLUS_FAIL;
524  }
525 
526  const int MAX_NAME_LENGTH = 100;
527  char name[MAX_NAME_LENGTH + 1];
528  name[MAX_NAME_LENGTH] = 0;
529  this->Porta->activateProbeConnector(connectorId);
530  this->Porta->getProbeName(name, MAX_NAME_LENGTH, probeId);
531  LOG_DEBUG("Probe connected to connector " << probeId << ": " << name);
532 
533  // store the probe name
534  SetPortaProbeName(name);
535  if (!this->Porta->findMasterPreset(name, MAX_NAME_LENGTH, probeId))
536  {
537  LOG_ERROR("Porta connect error: master preset cannot be found");
538  this->InternalDisconnect();
539  return PLUS_FAIL;
540  }
541 
542  if (!this->Porta->loadPreset(name))
543  {
544  LOG_ERROR("Porta connect error: master preset could not be loaded");
545  this->InternalDisconnect();
546  return PLUS_FAIL;
547  }
548 
549  if (!this->SetInputFrameSize(*aSource, this->PortaBModeWidth, this->PortaBModeHeight, 1))
550  {
551  LOG_ERROR("Initializer: can not set the frame size");
552  }
553 
554  // It is necessary to enclose the portaInitMode and custom display initialization by
555  // a freeze-unfreeze for proper operation. This is because if the imaging was
556  // running prior to the call to portaInitMode, the function will try to restart
557  // imaging at the end of the call, but the custom displays are not initialized
558  // yet - Ali B, Apr 2014
559  int isRunning = this->Porta->isImaging();
560  if (isRunning)
561  {
562  this->Porta->stopImage();
563  }
564 
565  // this is from propello
566  if (!this->Porta->initImagingMode(BMode))
567  {
568  LOG_ERROR("Initialize: cannot initialize imagingMode (" << this->GetLastPortaError() << ")");
569  return PLUS_FAIL;
570  }
571 
572  this->Porta->setDisplayDimensions(this->DisplayIndex, this->PortaBModeWidth, this->PortaBModeHeight);
573 
574  // successfully set to bmode
575 
576  // Set up imaging parameters
577  // Parameter value <0 means that the parameter should be kept unchanged
578  if (this->Frequency >= 0) { SetFrequency(this->Frequency); }
579  if (this->Depth >= 0) { SetDepth(this->Depth); }
580  if (this->Gain >= 0) { SetGain(this->Gain); }
581  if (this->Zoom >= 0) { SetZoom(this->Zoom); }
582 
583  this->MotorRotationPerStepDeg = 0;
584  this->MotorRotationStartAngleDeg = 0;
585  if (this->ProbeInformation->motorized)
586  {
587  if (this->MotorRotationRangeDeg > 0)
588  {
589  // Compute the angle per step
590  if (this->ProbeInformation->motorSteps > 0)
591  {
592  this->MotorRotationPerStepDeg = static_cast<double>(this->ProbeInformation->motorFov) * 0.001 / static_cast<double>(this->ProbeInformation->motorSteps);
593  }
594 
595  if (this->MotorRotationRangeDeg >= 0) { SetMotorRotationRangeDeg(this->MotorRotationRangeDeg); }
596  if (this->StepPerFrame >= 0) { SetStepPerFrame(this->StepPerFrame); }
597  if (this->FramePerVolume >= 0) { SetFramePerVolume(this->FramePerVolume); }
598  int actualFramePerVolume = 0;
599  GetFramePerVolume(actualFramePerVolume);
600  int actualStepPerFrame = 0;
601  GetStepPerFrame(actualStepPerFrame);
602  double actualMotorRotationRangeDeg = 0;
603  GetMotorRotationRangeDeg(actualMotorRotationRangeDeg);
604  this->MotorRotationStartAngleDeg = (actualFramePerVolume - 1) / 2 * actualStepPerFrame * this->MotorRotationPerStepDeg;
605  LOG_INFO("actual values in hw: FramePerVolume=" << actualFramePerVolume << " StepPerFrame=" << actualStepPerFrame << " MotorRotationRangeDeg=" << actualMotorRotationRangeDeg << " MotorRotationPerStepDeg=" << this->MotorRotationPerStepDeg << " MotorRotationStartAngleDeg=" << this->MotorRotationStartAngleDeg);
606 
607  // Turn on the motor
608  this->Porta->setParam(prmMotorStatus, 1);
609  }
610  else
611  {
612  // Motor is available but motor motion is disabled, so turn off the motor
613  this->Porta->setParam(prmMotorStatus, 0);
614  }
615  }
616  else
617  {
618  if (this->FramePerVolume >= 0 || this->StepPerFrame >= 0 || this->MotorRotationRangeDeg >= 0)
619  {
620  LOG_WARNING("Porta motor parameters are specified but the currently connected probe is not motorized");
621  }
622  }
623 
624  // finally, update all the parameters
625  if (!this->UpdateSonixPortaParams())
626  {
627  LOG_ERROR("Initialize: cannot update sonix params");
628  }
629 
630  // Set up the callback function which is invocked upon arrival of a new frame
631  this->Porta->setDisplayCallback(0, vtkPlusSonixPortaVideoSourceNewFrameCallback, (void*)this);
632 
633  LOG_DEBUG("Successfully connected to sonix porta video device");
634  return PLUS_SUCCESS;
635 }
636 
637 //----------------------------------------------------------------------------
639 {
640  this->Porta->setParam(prmMotorStatus, 0);
641  this->Porta->stopImage();
642  this->PortaConnected = false;
643  this->Porta->shutdown();
644 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION >= 6)
645  CoUninitialize();
646 #endif
647  return PLUS_SUCCESS;
648 }
649 //----------------------------------------------------------------------------
651 {
652  if (!this->Porta->isImaging())
653  {
654  this->Porta->runImage();
655  }
656  return PLUS_SUCCESS;
657 }
658 
659 //----------------------------------------------------------------------------
661 {
662  if (this->Porta->isImaging())
663  {
664  this->Porta->stopImage();
665  }
666  return PLUS_SUCCESS;
667 }
668 
669 //----------------------------------------------------------------------------
670 PlusStatus vtkPlusSonixPortaVideoSource::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
671 {
672  LOG_TRACE("vtkPlusSonixPortaVideoSource::ReadConfiguration");
673  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
674 
675  XML_READ_ENUM1_ATTRIBUTE_OPTIONAL(ImagingMode, deviceConfig, "BMode", BMode)
676 
677  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, Depth, deviceConfig);
678  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, Gain, deviceConfig);
679  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, Zoom, deviceConfig);
680  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, Frequency, deviceConfig);
681 
682  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, PortaBModeWidth, deviceConfig);
683  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, PortaBModeHeight, deviceConfig);
684 
685  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, StepPerFrame, deviceConfig);
686  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, FramePerVolume, deviceConfig);
687  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(double, MotorRotationRangeDeg, deviceConfig);
688 
689  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, Usm, deviceConfig);
690  XML_READ_SCALAR_ATTRIBUTE_REQUIRED(int, Pci, deviceConfig);
691 
692  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(PortaLUTPath, deviceConfig);
693  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(PortaSettingPath, deviceConfig);
694  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(PortaLicensePath, deviceConfig);
695  XML_READ_CSTRING_ATTRIBUTE_REQUIRED(PortaFirmwarePath, deviceConfig);
696 
697  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(AutoClipEnabled, deviceConfig);
698 
699  double obsolete = 0;
700  if (deviceConfig->GetScalarAttribute("HighVoltage", obsolete))
701  {
702  LOG_WARNING("SonixPortaVideo HighVoltage attribute is ignored (HighVoltage is always disabled)");
703  }
704  if (deviceConfig->GetScalarAttribute("Channels", obsolete))
705  {
706  LOG_WARNING("SonixPortaVideo Channels attribute is ignored (number of channels is now automatically determined from USM (ultrasound module version) attribute.");
707  }
708 
709  LOG_DEBUG("Porta read the XML configuration");
710  return PLUS_SUCCESS;
711 }
712 
713 //-----------------------------------------------------------------------------
714 PlusStatus vtkPlusSonixPortaVideoSource::WriteConfiguration(vtkXMLDataElement* rootConfigElement)
715 {
716  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
717 
718  if (this->ImagingMode == BMode)
719  {
720  deviceConfig->SetAttribute("ImagingMode", "BMode");
721  }
722  else
723  {
724  LOG_ERROR("Writing of unsupported ImagingMode requested");
725  }
726 
727  deviceConfig->SetIntAttribute("Depth", this->Depth);
728  deviceConfig->SetIntAttribute("Gain", this->Gain);
729  deviceConfig->SetIntAttribute("Zoom", this->Zoom);
730  deviceConfig->SetIntAttribute("Frequency", this->Frequency);
731  deviceConfig->SetIntAttribute("StepPerFrame", this->StepPerFrame);
732  // only save frame per volume or rotation range if specified (depends on SDK version
733  // which one is actually used)
734  if (this->FramePerVolume >= 0)
735  {
736  deviceConfig->SetIntAttribute("FramePerVolume", this->FramePerVolume);
737  }
738  if (this->MotorRotationRangeDeg >= 0)
739  {
740  deviceConfig->SetDoubleAttribute("MotorRotationRangeDeg", this->MotorRotationRangeDeg);
741  }
742  deviceConfig->SetIntAttribute("USM", this->Usm);
743  deviceConfig->SetIntAttribute("PCI", this->Pci);
744  deviceConfig->SetAttribute("PortaLUTPath", this->PortaLUTPath);
745  deviceConfig->SetAttribute("PortaSettingPath", this->PortaSettingPath);
746  deviceConfig->SetAttribute("PortaLicensePath", this->PortaLicensePath);
747  deviceConfig->SetAttribute("PortaFirmwarePath", this->PortaFirmwarePath);
748 
749  XML_WRITE_BOOL_ATTRIBUTE(AutoClipEnabled, deviceConfig);
750 
751  return PLUS_SUCCESS;
752 }
753 
754 //----------------------------------------------------------------------------
756 {
757 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 6)
758  const unsigned int MAX_PORTA_ERROR_MSG_LENGTH = 256;
759  char err[MAX_PORTA_ERROR_MSG_LENGTH + 1];
760  err[MAX_PORTA_ERROR_MSG_LENGTH] = 0; // make sure the string is null-terminated
761  this->Porta->getLastError(err, MAX_PORTA_ERROR_MSG_LENGTH);
762  return err;
763 #else
764  return "unknown";
765 #endif
766 }
767 
768 //----------------------------------------------------------------------------
769 PlusStatus vtkPlusSonixPortaVideoSource::SetParamValue(char* paramId, int paramValue, int& validatedParamValue)
770 {
771  if (!this->PortaConnected)
772  {
773  // Connection has not been established yet. Parameter value will be set upon connection.
774  validatedParamValue = paramValue;
775  return PLUS_SUCCESS;
776  }
777  if (!this->Porta->setParam(paramId, paramValue))
778  {
779  LOG_ERROR("vtkPlusSonixPortaVideoSource::SetParamValue failed (paramId=" << paramId << ", paramValue=" << paramValue << ") " << GetLastPortaError());
780  return PLUS_FAIL;
781  }
782  validatedParamValue = paramValue;
783  return PLUS_SUCCESS;
784 }
785 
786 //----------------------------------------------------------------------------
787 PlusStatus vtkPlusSonixPortaVideoSource::GetParamValue(char* paramId, int& paramValue, int& validatedParamValue)
788 {
789  if (!this->PortaConnected)
790  {
791  // Connection has not been established yet. Returned the cached value.
792  paramValue = validatedParamValue;
793  return PLUS_SUCCESS;
794  }
795  paramValue = -1;
796  if (!this->Porta->getParam(paramId, paramValue))
797  {
798  LOG_ERROR("vtkPlusSonixPortaVideoSource::GetParamValue failed (paramId=" << paramId << ", paramValue=" << paramValue << ") " << GetLastPortaError());
799  return PLUS_FAIL;
800  }
801  return PLUS_SUCCESS;
802 }
803 
804 
805 //----------------------------------------------------------------------------
806 PlusStatus vtkPlusSonixPortaVideoSource::SetFrequency(int aFrequency)
807 {
808  return SetParamValue(prmBTxFreq, aFrequency, this->Frequency);
809 }
810 
811 //----------------------------------------------------------------------------
812 PlusStatus vtkPlusSonixPortaVideoSource::GetFrequency(int& aFrequency)
813 {
814  return GetParamValue(prmBTxFreq, aFrequency, this->Frequency);
815 }
816 
817 //----------------------------------------------------------------------------
818 PlusStatus vtkPlusSonixPortaVideoSource::SetDepth(int aDepth)
819 {
820  return SetParamValue(prmBImageDepth, aDepth, this->Depth);
821 }
822 
823 //----------------------------------------------------------------------------
824 PlusStatus vtkPlusSonixPortaVideoSource::GetDepth(int& aDepth)
825 {
826  return GetParamValue(prmBImageDepth, aDepth, this->Depth);
827 }
828 
829 //----------------------------------------------------------------------------
830 PlusStatus vtkPlusSonixPortaVideoSource::SetGain(int aGain)
831 {
832  return SetParamValue(prmBGain, aGain, this->Gain);
833 }
834 
835 //----------------------------------------------------------------------------
836 PlusStatus vtkPlusSonixPortaVideoSource::GetGain(int& aGain)
837 {
838  return GetParamValue(prmBGain, aGain, this->Gain);
839 }
840 
841 //----------------------------------------------------------------------------
842 PlusStatus vtkPlusSonixPortaVideoSource::SetZoom(int aZoom)
843 {
844  return SetParamValue(prmZoom, aZoom, this->Zoom);
845 }
846 
847 //----------------------------------------------------------------------------
848 PlusStatus vtkPlusSonixPortaVideoSource::GetZoom(int& aZoom)
849 {
850  return GetParamValue(prmZoom, aZoom, this->Zoom);
851 }
852 
853 //----------------------------------------------------------------------------
854 PlusStatus vtkPlusSonixPortaVideoSource::SetFramePerVolume(int aFramePerVolume)
855 {
856 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION >= 6)
857  LOG_WARNING("FramePerVolume parameter cannot be set in Ultrasonix SDK-6.x. Use MotorRotationRangeDeg parameter instead.");
858 #endif
859  return SetParamValue(prmMotorFrames, aFramePerVolume, this->FramePerVolume);
860 }
861 
862 //----------------------------------------------------------------------------
863 PlusStatus vtkPlusSonixPortaVideoSource::GetFramePerVolume(int& aFramePerVolume)
864 {
865  return GetParamValue(prmMotorFrames, aFramePerVolume, this->FramePerVolume);
866 }
867 
868 //----------------------------------------------------------------------------
869 PlusStatus vtkPlusSonixPortaVideoSource::SetMotorRotationRangeDeg(double aMotorRotationRangeDeg)
870 {
871 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 6)
872  LOG_WARNING("MotorRotationRangeDeg parameter is not available in Ultrasonix SDK-6.x. Use FramePerVolume parameter instead.");
873  return PLUS_FAIL;
874 #else
875  int aMotorRotationRangeMilliDeg = int(aMotorRotationRangeDeg * 1000);
876  int aMotorRotationRangeMilliDegStored = int(this->MotorRotationRangeDeg * 1000);
877  if (SetParamValue(prmMotorFOV, aMotorRotationRangeMilliDeg, aMotorRotationRangeMilliDegStored) == PLUS_FAIL)
878  {
879  return PLUS_FAIL;
880  }
881  this->MotorRotationRangeDeg = aMotorRotationRangeMilliDegStored * 0.001;
882  return PLUS_SUCCESS;
883 #endif
884 }
885 
886 //----------------------------------------------------------------------------
887 PlusStatus vtkPlusSonixPortaVideoSource::GetMotorRotationRangeDeg(double& aMotorRotationRangeDeg)
888 {
889 #if (PLUS_ULTRASONIX_SDK_MAJOR_VERSION < 6)
890  LOG_WARNING("MotorRotationRangeDeg parameter is not available in Ultrasonix SDK-6.x. Use FramePerVolume parameter instead.");
891  return PLUS_FAIL;
892 #else
893  int aMotorRotationRangeMilliDeg = int(aMotorRotationRangeDeg * 1000);
894  int aMotorRotationRangeMilliDegStored = int(this->MotorRotationRangeDeg * 1000);
895  if (GetParamValue(prmMotorFOV, aMotorRotationRangeMilliDeg, aMotorRotationRangeMilliDegStored) == PLUS_FAIL)
896  {
897  return PLUS_FAIL;
898  }
899  aMotorRotationRangeDeg = aMotorRotationRangeMilliDegStored * 0.001;
900  return PLUS_SUCCESS;
901 #endif
902 }
903 
904 
905 //----------------------------------------------------------------------------
906 PlusStatus vtkPlusSonixPortaVideoSource::SetStepPerFrame(int aStepPerFrame)
907 {
908  return SetParamValue(prmMotorSteps, aStepPerFrame, this->StepPerFrame);
909 }
910 
911 //----------------------------------------------------------------------------
912 PlusStatus vtkPlusSonixPortaVideoSource::GetStepPerFrame(int& aStepPerFrame)
913 {
914  return GetParamValue(prmMotorSteps, aStepPerFrame, this->StepPerFrame);
915 }
916 
917 //----------------------------------------------------------------------------
918 PlusStatus vtkPlusSonixPortaVideoSource::UpdateSonixPortaParams()
919 {
920  bool bRunning = this->Porta->isImaging();
921 
922  if (bRunning)
923  {
924  this->Porta->stopImage();
925  }
926 
927  // update VTK FrameRate with SonixRP's hardware frame rate
928  //
929  // The reasons we update it here is because the SonixRP's hardware
930  // frame rate is a function of several parameters, such as
931  // bline density and image-depths.
932  //
933  this->AcquisitionRate = (float)(this->Porta->getFrameRate());
934 
935  if (bRunning)
936  {
937  this->Porta->runImage();
938  }
939 
940  this->Modified();
941 
942  return PLUS_SUCCESS;
943 }
944 
945 //----------------------------------------------------------------------------
946 PlusStatus vtkPlusSonixPortaVideoSource::NotifyConfigured()
947 {
948  if (this->OutputChannels.size() > 1)
949  {
950  LOG_WARNING("vtkPlusSonixPortaVideoSource is expecting one output channel and there are " << this->OutputChannels.size() << " channels. First output channel will be used.");
951  return PLUS_FAIL;
952  }
953 
954  if (this->OutputChannels.empty())
955  {
956  LOG_ERROR("No output channels defined for vtkPlusSonixPortaVideoSource. Cannot proceed.");
957  this->SetCorrectlyConfigured(false);
958  return PLUS_FAIL;
959  }
960 
961  return PLUS_SUCCESS;
962 }
virtual PlusStatus SetInputFrameSize(vtkPlusDataSource &aSource, unsigned int x, unsigned int y, unsigned int z)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
virtual PlusStatus GetInputFrameSize(vtkPlusChannel &aChannel, unsigned int &x, unsigned int &y, unsigned int &z) const
PlusStatus SetParamValue(char *paramId, int paramValue, int &validatedParamValue)
const char int line
Definition: phidget22.h:2458
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual std::string GetDeviceId() const
Class that cleans up (deletes singleton instance of) vtkPlusSonixPortaVideoSource when destroyed.
PlusStatus AddFrameToBuffer(void *param, int id, bool motorRotationCcw, int motorStepCount)
bool RequireImageOrientationInConfiguration
double AcquisitionRate
#define PLUS_FAIL
Definition: PlusCommon.h:43
std::array< int, 3 > GetClipRectangleOrigin() const
PlusStatus GetFirstVideoSource(vtkPlusDataSource *&anImage)
virtual PlusStatus AddVideoItemToVideoSource(vtkPlusDataSource &videoSource, const igsioVideoFrame &frame, long frameNumber, double unfilteredTimestamp=UNDEFINED_TIMESTAMP, double filteredTimestamp=UNDEFINED_TIMESTAMP, const igsioFieldMapType *customFields=NULL)
Class providing VTK video input interface for SonixRP.
virtual void SetCorrectlyConfigured(bool)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
virtual US_IMAGE_ORIENTATION GetInputImageOrientation()
Contains an optional timestamped circular buffer containing the video images and a number of timestam...
ChannelContainer OutputChannels
PlusStatus GetVideoSource(vtkPlusDataSource *&aVideoSource) const
PlusStatus GetParamValue(char *paramId, int &paramValue, int &validatedParamValue)
Interface to a 3D positioning tool, video source, or generalized data stream.