PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusNDITracker.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"
46 #include "vtkPlusNDITracker.h"
47 
48 // NDI includes
49 #include <ndicapi.h>
50 #include <ndicapi_math.h>
51 #include <ndicapi_serial.h>
52 
53 // VTK includes
54 #include <vtkCharArray.h>
55 #include <vtkMath.h>
56 #include <vtkMatrix4x4.h>
57 #include <vtkObjectFactory.h>
58 #include <vtkSocketCommunicator.h>
59 #include <vtkTimerLog.h>
60 #include <vtkTransform.h>
61 
62 // igsio includes
63 #include <igsioCommon.h>
64 
65 // System includes
66 #include <ctype.h>
67 #include <float.h>
68 #include <math.h>
69 #include <stdarg.h>
70 
71 #if defined(HAVE_FUTURE)
72  #include <future>
73 #endif
74 
75 namespace
76 {
77  const int VIRTUAL_SROM_SIZE = 1024;
78 }
79 
80 //----------------------------------------------------------------------------
81 
83 
84 //----------------------------------------------------------------------------
86  : LastFrameNumber(0)
87  , Device(nullptr)
88  , SerialDevice("")
89  , SerialPort(-1)
90  , BaudRate(0)
91  , IsDeviceTracking(0)
92  , LeaveDeviceOpenAfterProbe(false)
93  , CheckDSR(true)
94  , MeasurementVolumeNumber(0)
95  , NetworkHostname("")
96  , NetworkPort(8765)
97  , TrackingFrequencyNumber(0)
98  , FirmwareMajorRevision(1)
99  , FirmwareMinorRevision(1)
100  , HardwareDataAveragingDepth(1)
101  , CommandMutex(vtkIGSIORecursiveCriticalSection::New())
102 {
103  memset(this->CommandReply, 0, VTK_NDI_REPLY_LEN);
104 
105  // PortName for data source is not required if RomFile is specified, so we don't need to enable this->RequirePortNameInDeviceSetConfiguration
106 
107  // No callback function provided by the device, so the data capture thread will be used to poll the hardware and add new items to the buffer
108  this->StartThreadForInternalUpdates = true;
109  this->AcquisitionRate = 50;
110 }
111 
112 //----------------------------------------------------------------------------
114 {
115  if (this->Recording)
116  {
117  this->StopRecording();
118  }
119  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
120  {
121  delete [] toolDescriptorIt->second.VirtualSROM;
122  toolDescriptorIt->second.VirtualSROM = NULL;
123  }
124  this->CommandMutex->Delete();
125  this->CommandMutex = nullptr;
126 }
127 
128 //----------------------------------------------------------------------------
129 void vtkPlusNDITracker::PrintSelf(ostream& os, vtkIndent indent)
130 {
131  Superclass::PrintSelf(os, indent);
132 
133  char ndiLog[USHRT_MAX];
134  ndiLogState(this->Device, ndiLog);
135 
136  os << indent << "SerialDevice: " << this->SerialDevice << std::endl;
137  os << indent << "SerialPort: " << this->SerialPort << std::endl;
138  os << indent << "BaudRate: " << this->BaudRate << std::endl;
139  os << indent << "IsDeviceTracking: " << this->IsDeviceTracking << std::endl;
140  os << indent << "MeasurementVolumeNumber: " << this->MeasurementVolumeNumber << std::endl;
141  os << indent << "TrackingFrequencyNumber: " << this->TrackingFrequencyNumber << std::endl;
142  os << indent << "CommandReply: " << this->CommandReply << std::endl;
143  os << indent << "LastFrameNumber: " << this->LastFrameNumber << std::endl;
144  os << indent << "LeaveDeviceOpenAfterProbe: " << this->LeaveDeviceOpenAfterProbe << std::endl;
145  os << indent << "CheckDSR: " << this->CheckDSR << std::endl;
146  os << indent << "HardwareDataAveragingDepth" << this->HardwareDataAveragingDepth << std::endl;
147  os << indent << "FirmwareMajorRevision" << this->FirmwareMajorRevision << std::endl;
148  os << indent << "FirmwareMinorRevision" << this->FirmwareMinorRevision << std::endl;
149  for (auto iter = this->NdiToolDescriptors.begin(); iter != this->NdiToolDescriptors.end(); ++iter)
150  {
151  os << indent << iter->first << ": " << std::endl;
152  os << indent << " " << "PortEnabled: " << iter->second.PortEnabled << std::endl;
153  os << indent << " " << "PortHandle: " << iter->second.PortHandle << std::endl;
154  os << indent << " " << "VirtualSROM: " << iter->second.VirtualSROM << std::endl;
155  os << indent << " " << "WiredPortNumber: " << iter->second.WiredPortNumber << std::endl;
156  }
157 }
158 
159 //----------------------------------------------------------------------------
161 {
162  std::ostringstream version;
163  version << "NDICAPI-" << NDICAPI_MAJOR_VERSION << "." << NDICAPI_MINOR_VERSION;
164  return version.str();
165 }
166 
167 //----------------------------------------------------------------------------
169 {
170  if (this->IsDeviceTracking)
171  {
172  return PLUS_SUCCESS;
173  }
174 
175  if (this->SerialPort > 0)
176  {
177  const char* devicename = NULL;
178  int errnum = NDI_OPEN_ERROR;
179  devicename = ndiSerialDeviceName(this->SerialPort - 1);
180  if (devicename)
181  {
182  errnum = ndiSerialProbe(devicename, this->CheckDSR);
183  LOG_DEBUG("Serial port " << devicename << " probe error (" << errnum << "): " << ndiErrorString(errnum));
184  }
185 
186  if (errnum != NDI_OKAY)
187  {
188  return PLUS_FAIL;
189  }
190 
191  this->Device = ndiOpenSerial(devicename);
192  if (this->Device && !this->LeaveDeviceOpenAfterProbe)
193  {
194  CloseDevice(this->Device);
195  }
196  return PLUS_SUCCESS;
197  }
198  else if (this->NetworkHostname.empty())
199  {
200 #if defined(HAVE_FUTURE)
201  return ProbeSerialInternal();
202 #else
203  // if SerialPort is set to -1 (default), then probe the first N serial ports
204  const char* devicename = NULL;
205  int errnum = NDI_OPEN_ERROR;
206 
207  const int MAX_SERIAL_PORT_NUMBER = 20; // the serial port is almost surely less than this number
208  for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++)
209  {
210  devicename = ndiSerialDeviceName(i);
211  LOG_DEBUG("Testing serial port: " << devicename);
212  if (devicename)
213  {
214  errnum = ndiSerialProbe(devicename, this->CheckDSR);
215  LOG_DEBUG("Serial port " << devicename << " probe error (" << errnum << "): " << ndiErrorString(errnum));
216  if (errnum == NDI_OKAY)
217  {
218  this->SerialPort = i + 1;
219  this->Device = ndiOpenSerial(devicename);
220  LOG_DEBUG("device: " << (this->Device == nullptr));
221  if (this->Device && !this->LeaveDeviceOpenAfterProbe)
222  {
223  CloseDevice(this->Device);
224  }
225  return PLUS_SUCCESS;
226  }
227  }
228  }
229 #endif
230  }
231  else
232  {
233  // TODO: probe network?
234  }
235 
236  return PLUS_FAIL;
237 }
238 
239 //----------------------------------------------------------------------------
240 // Send a raw command to the tracking unit.
241 // If communication has already been opened with the NDI,
242 // then lock the mutex to get exclusive access and then
243 // send the command.
244 // Otherwise, open communication with the unit, send the command,
245 // and close communication.
246 std::string vtkPlusNDITracker::Command(const char* format, ...)
247 {
248  va_list ap; // see stdarg.h
249  va_start(ap, format);
250 
251  this->CommandReply[0] = '\0';
252 
253  char command[2048];
254  if (format != nullptr)
255  {
256  vsprintf(command, format, ap);
257  LOG_DEBUG("NDI Command:" << command);
258  }
259  else
260  {
261  LOG_DEBUG("NDI Command:send serial break");
262  }
263 
264  // Linux and MacOSX require resetting of va parameters
265  va_end(ap);
266  va_start(ap, format);
267 
268  if (this->Device)
269  {
270  igsioLockGuard<vtkIGSIORecursiveCriticalSection> lock(this->CommandMutex);
271  strncpy(this->CommandReply, ndiCommandVA(this->Device, format, ap), VTK_NDI_REPLY_LEN - 1);
272  this->CommandReply[VTK_NDI_REPLY_LEN - 1] = '\0';
273  }
274 
275  std::string cmd(command);
276  if (cmd.find(':') != std::string::npos)
277  {
278  cmd = cmd.substr(0, cmd.find(':'));
279  }
280  else if (cmd.find(' ') != std::string::npos)
281  {
282  cmd = cmd.substr(0, cmd.find(' '));
283  }
284  bool isBinary = (cmd == "BX" || cmd == "BX2" || cmd == "GETLOG" || cmd == "VGET");
285 
286  if (vtkIGSIOLogger::Instance()->GetLogLevel() >= vtkIGSIOLogger::LOG_LEVEL_DEBUG)
287  {
288  if (!isBinary)
289  {
290  LOG_DEBUG("NDI Reply: " << this->CommandReply);
291  }
292  else
293  {
294  std::stringstream ss;
295  ss << "NDI Reply: ";
296  ss << std::hex;
297  for (unsigned int i = 0; i < ndiGetBX2ReplyLength(this->Device); ++i)
298  {
299  ss << std::setfill('0') << std::setw(2) << (unsigned int)(unsigned char)this->CommandReply[i];
300  }
301  ss << std::endl;
302  LOG_DEBUG(ss.str());
303  }
304  }
305 
306  va_end(ap);
307 
308  return this->CommandReply;
309 }
310 
311 //----------------------------------------------------------------------------
313 {
314  PlusStatus result;
315  if (!this->NetworkHostname.empty())
316  {
317  result = InternalConnectNetwork();
318  }
319  else
320  {
321  result = InternalConnectSerial();
322  }
323 
324  if (result != PLUS_SUCCESS)
325  {
326  LOG_ERROR("Unable to connect to NDI device.");
327  return result;
328  }
329 
331 
333 
336 
337  if (this->EnableToolPorts() != PLUS_SUCCESS)
338  {
339  LOG_ERROR("Failed to enable tool ports");
340  return PLUS_FAIL;
341  }
342 
343  return PLUS_SUCCESS;
344 }
345 
346 //----------------------------------------------------------------------------
348 {
349  this->FirmwareMajorRevision = 1;
350  std::string revision = "";
351  revision = this->Command("GET:%s", "Features.Firmware.API Revision");
352  int errnum = ndiGetError(this->Device);
353  if (errnum)
354  {
355  revision = this->Command("APIREV ");
356  int errnum = ndiGetError(this->Device);
357  if (errnum)
358  {
359  LOG_ERROR(ndiErrorString(errnum));
360  return PLUS_FAIL;
361  }
362  else
363  {
364  // Parse APIREV reply
365  // revision format "G.00X.00Y"
366  std::vector<std::string> result = igsioCommon::SplitStringIntoTokens(revision, '.', false);
367  uint32_t ver;
368  igsioCommon::StringToNumber<uint32_t>(result[1].c_str(), ver);
369  this->FirmwareMajorRevision = ver;
370  igsioCommon::StringToNumber<uint32_t>(result[2].c_str(), ver);
371  this->FirmwareMinorRevision = ver;
372  }
373  }
374  else
375  {
376  // Parse GET reply
377  // revision format "G.00X.00Y"
378  std::vector<std::string> answers = igsioCommon::SplitStringIntoTokens(revision, '=', false);
379  std::vector<std::string> result = igsioCommon::SplitStringIntoTokens(answers[1], '.', false);
380  uint32_t ver;
381  igsioCommon::StringToNumber<uint32_t>(result[1].c_str(), ver);
382  this->FirmwareMajorRevision = ver;
383  igsioCommon::StringToNumber<uint32_t>(result[2].c_str(), ver);
384  this->FirmwareMinorRevision = ver;
385  }
386 
387  return PLUS_SUCCESS;
388 }
389 
390 //----------------------------------------------------------------------------
392 {
393  this->LeaveDeviceOpenAfterProbe = true;
394  if (this->Probe() == PLUS_FAIL)
395  {
396  LOG_ERROR("Failed to detect device" << (this->SerialPort < 0 ? ". Port scanning failed. " : " on serial port " + std::string(ndiSerialDeviceName(this->SerialPort)) + " (index " + igsioCommon::ToString<int>(this->SerialPort) + "). ") << ndiErrorString(NDI_OPEN_ERROR));
397  return PLUS_FAIL;
398  }
399 
400  if (this->BaudRate != 0)
401  {
403  // BaudRate has been requested, let's attempt it before falling back to best available
404  this->Command("COMM:%X%03d%d", baudVal, NDI_8N1, NDI_NOHANDSHAKE);
405  int errnum = ndiGetError(this->Device);
406  if (errnum == NDI_OKAY)
407  {
408  return PLUS_SUCCESS;
409  }
410  else
411  {
412  LOG_WARNING("Unable to set requested baud rate. Reverting to auto-select.");
413  }
414  }
415 
416 #if defined(WIN32)
417  // TODO: use the lines in this comment to replace next 5 lines when VS2010 support is dropped:
418  //auto baudRates = { NDI_1228739, NDI_921600, NDI_230400, NDI_115200, NDI_57600, NDI_38400, NDI_19200, NDI_14400, NDI_9600 };
419  //for (auto baud : baudRates)
420  const unsigned int numberOfBaudRates = 9;
421  int baudRates[numberOfBaudRates] = { NDI_1228739, NDI_921600, NDI_230400, NDI_115200, NDI_57600, NDI_38400, NDI_19200, NDI_14400, NDI_9600 };
422 #elif defined(__APPLE__)
423  const unsigned int numberOfBaudRates = 6;
424  int baudRates[numberOfBaudRates] = { NDI_115200, NDI_57600, NDI_38400, NDI_19200, NDI_14400, NDI_9600 };
425 #elif defined(__linux__)
426  const unsigned int numberOfBaudRates = 6;
427  int baudRates[numberOfBaudRates] = { NDI_115200, NDI_57600, NDI_38400, NDI_19200, NDI_14400, NDI_9600 };
428 #else
429  const unsigned int numberOfBaudRates = 6;
430  int baudRates[numberOfBaudRates] = { NDI_115200, NDI_57600, NDI_38400, NDI_19200, NDI_14400, NDI_9600 };
431 #endif
432  for (unsigned int baudIndex = 0; baudIndex < numberOfBaudRates; baudIndex++)
433  {
434  int baud = baudRates[baudIndex];
435 
436  this->Command("COMM:%X%03d%d", baud, NDI_8N1, NDI_NOHANDSHAKE);
437  int errnum = ndiGetError(this->Device);
438  if (errnum != NDI_OKAY && errnum != NDI_COMM_FAIL)
439  {
440  // A real problem, abort
441  LOG_ERROR(ndiErrorString(errnum));
442  ndiCloseSerial(this->Device);
443  this->Device = nullptr;
444  return PLUS_FAIL;
445  }
446  if (errnum == NDI_OKAY)
447  {
448  // Fastest baud rate set, end
449  break;
450  }
451  }
452 
453  return PLUS_SUCCESS;
454 }
455 
456 //----------------------------------------------------------------------------
458 {
459  this->Device = ndiOpenNetwork(this->NetworkHostname.c_str(), this->NetworkPort);
460 
461  if (this->Device == nullptr)
462  {
463  LOG_ERROR("Unable to connect to " << this->NetworkHostname << ":" << this->NetworkPort);
464  return PLUS_FAIL;
465  }
466 
467  // First init can take up to 5 seconds
468  ndiTimeoutSocket(this->Device, 5000);
469 
470  auto reply = this->Command("INIT");
471 
472  // Subsequent commands should be much faster
473  ndiTimeoutSocket(this->Device, 100);
474 
475  return PLUS_SUCCESS;
476 }
477 
478 //----------------------------------------------------------------------------
480 {
481  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
482  {
483  this->ClearVirtualSromInTracker(toolDescriptorIt->second);
484  }
485 
486  this->DisableToolPorts();
487 
488  // return to default comm settings
489  this->Command("COMM:00000");
490  int errnum = ndiGetError(this->Device);
491  if (errnum)
492  {
493  LOG_ERROR(ndiErrorString(errnum));
494  }
495  CloseDevice(this->Device);
496 
497  return PLUS_SUCCESS;
498 }
499 
500 
501 //----------------------------------------------------------------------------
503 {
504  if (this->IsDeviceTracking)
505  {
506  return PLUS_SUCCESS;
507  }
508 
509  this->Command("TSTART:");
510  int errnum = ndiGetError(this->Device);
511  if (errnum)
512  {
513  LOG_ERROR("Failed TSTART: " << ndiErrorString(errnum));
514  CloseDevice(this->Device);
515  return PLUS_FAIL;
516  }
517 
518  this->IsDeviceTracking = 1;
519 
520  return PLUS_SUCCESS;
521 }
522 
523 //----------------------------------------------------------------------------
525 {
526  if (this->Device == 0)
527  {
528  return PLUS_FAIL;
529  }
530 
531  this->Command("TSTOP:");
532  int errnum = ndiGetError(this->Device);
533  if (errnum)
534  {
535  LOG_ERROR(ndiErrorString(errnum));
536  }
537  this->IsDeviceTracking = 0;
538 
539  return PLUS_SUCCESS;
540 }
541 
542 //----------------------------------------------------------------------------
544 {
545  if (!this->IsDeviceTracking)
546  {
547  LOG_ERROR("called Update() when NDI was not tracking");
548  return PLUS_FAIL;
549  }
550 
551  if (this->FirmwareMajorRevision < 3)
552  {
553  return this->DoBXUpdate();
554  }
555  else
556  {
557  return this->DoBX2Update();
558  }
559 
560  return PLUS_SUCCESS;
561 }
562 
563 //----------------------------------------------------------------------------
564 PlusStatus vtkPlusNDITracker::ReadSromFromFile(NdiToolDescriptor& toolDescriptor, const char* filename)
565 {
566  FILE* file = fopen(filename, "rb");
567  if (file == NULL)
568  {
569  LOG_ERROR("Couldn't find srom file " << filename);
570  return PLUS_FAIL;
571  }
572 
573  if (toolDescriptor.VirtualSROM == 0)
574  {
575  toolDescriptor.VirtualSROM = new unsigned char[VIRTUAL_SROM_SIZE];
576  }
577 
578  memset(toolDescriptor.VirtualSROM, 0, VIRTUAL_SROM_SIZE);
579  fread(toolDescriptor.VirtualSROM, 1, VIRTUAL_SROM_SIZE, file);
580  fclose(file);
581  return PLUS_SUCCESS;
582 }
583 
584 //----------------------------------------------------------------------------
586 {
587  PlusStatus status = PLUS_SUCCESS;
588 
589  // stop tracking
590  if (this->IsDeviceTracking)
591  {
592  this->Command("TSTOP:");
593  int errnum = ndiGetError(this->Device);
594  if (errnum)
595  {
596  LOG_ERROR(ndiErrorString(errnum));
597  status = PLUS_FAIL;
598  }
599  }
600 
601  // free ports that are waiting to be freed
602  this->Command("PHSR:01");
603  int errnum = ndiGetError(this->Device);
604  if (errnum != NDI_OKAY)
605  {
606  LOG_ERROR("Unable to get the number of handles. Error: " << ndiErrorString(errnum));
607  return PLUS_FAIL;
608  }
609 
610  int ntools = ndiGetPHSRNumberOfHandles(this->Device);
611  for (int ndiToolIndex = 0; ndiToolIndex < ntools; ndiToolIndex++)
612  {
613  int portHandle = ndiGetPHSRHandle(this->Device, ndiToolIndex);
614  this->Command("PHF:%02X", portHandle);
615  int errnum = ndiGetError(this->Device);
616  if (errnum)
617  {
618  LOG_ERROR(ndiErrorString(errnum));
619  status = PLUS_FAIL;
620  }
621  }
622 
623  // Set port handles and send SROM files to tracker
624  // We need to do this before initializing and enabling
625  // the ports waiting to be initialized.
626  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
627  {
628  if (toolDescriptorIt->second.VirtualSROM != NULL) // wireless tool (or wired tool with virtual rom)
629  {
630  if (this->UpdatePortHandle(toolDescriptorIt->second) != PLUS_SUCCESS)
631  {
632  LOG_ERROR("Failed to determine NDI port handle for tool " << toolDescriptorIt->first);
633  return PLUS_FAIL;
634  }
635  if (this->SendSromToTracker(toolDescriptorIt->second) != PLUS_SUCCESS)
636  {
637  LOG_ERROR("Failed send SROM to NDI tool " << toolDescriptorIt->first);
638  return PLUS_FAIL;
639  }
640  }
641  }
642 
643  // initialize ports waiting to be initialized
644  errnum = 0;
645  ntools = 0;
646  do // repeat as necessary (in case multi-channel tools are used)
647  {
648  this->Command("PHSR:02");
649  ntools = ndiGetPHSRNumberOfHandles(this->Device);
650  for (int ndiToolIndex = 0; ndiToolIndex < ntools; ndiToolIndex++)
651  {
652  int portHandle = ndiGetPHSRHandle(this->Device, ndiToolIndex);
653  this->Command("PINIT:%02X", portHandle);
654  errnum = ndiGetError(this->Device);
655  if (errnum)
656  {
657  std::stringstream ss;
658  ss << ndiErrorString(errnum) << ". errnum: " << errnum;
659  LOG_ERROR(ss.str());
660  status = PLUS_FAIL;
661  }
662  }
663  }
664  while (ntools > 0 && errnum == 0);
665 
666  // enable initialized tools
667  this->Command("PHSR:03");
668  ntools = ndiGetPHSRNumberOfHandles(this->Device);
669  for (int ndiToolIndex = 0; ndiToolIndex < ntools; ndiToolIndex++)
670  {
671  int portHandle = ndiGetPHSRHandle(this->Device, ndiToolIndex);
672  this->Command("PHINF:%02X0001", portHandle);
673  char identity[34];
674  ndiGetPHINFToolInfo(this->Device, identity);
675  int mode = 'D'; // default
676  if (identity[1] == 0x03) // button-box
677  {
678  mode = 'B';
679  }
680  else if (identity[1] == 0x01) // reference
681  {
682  mode = 'S';
683  }
684  // enable the tool
685  this->Command("PENA:%02X%c", portHandle, mode);
686  int errnum = ndiGetError(this->Device);
687  if (errnum)
688  {
689  LOG_ERROR(ndiErrorString(errnum));
690  status = PLUS_FAIL;
691  }
692  }
693 
694  // Set wired port handles and send SROM files to tracker
695  // We need to do this after enabling all the tools because tools on
696  // splitters (two 5-DOF tools with one connector) only appear after the tool is enabled.
697  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
698  {
699  if (toolDescriptorIt->second.WiredPortNumber >= 0 && toolDescriptorIt->second.VirtualSROM == NULL) //wired tool, no virtual ROM
700  {
701  if (this->UpdatePortHandle(toolDescriptorIt->second) != PLUS_SUCCESS)
702  {
703  LOG_ERROR("Failed to determine NDI port handle for tool " << toolDescriptorIt->first);
704  return PLUS_FAIL;
705  }
706  if (this->SendSromToTracker(toolDescriptorIt->second) != PLUS_SUCCESS)
707  {
708  LOG_ERROR("Failed send SROM to NDI tool " << toolDescriptorIt->first);
709  return PLUS_FAIL;
710  }
711  }
712  }
713 
714  // Update tool info
715  this->Command("PHSR:00");
716 
717  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
718  {
719  vtkPlusDataSource* trackerTool = NULL;
720  if (this->GetTool(toolDescriptorIt->first, trackerTool) != PLUS_SUCCESS)
721  {
722  LOG_ERROR("Failed to get NDI tool: " << toolDescriptorIt->first);
723  status = PLUS_FAIL;
724  continue;
725  }
726 
727  this->Command("PHINF:%02X0025", toolDescriptorIt->second.PortHandle);
728  int errnum = ndiGetError(this->Device);
729  if (errnum)
730  {
731  LOG_ERROR(ndiErrorString(errnum));
732  status = PLUS_FAIL;
733  continue;
734  }
735 
736  // decompose identity string from end to front
737  char identity[34];
738  ndiGetPHINFToolInfo(this->Device, identity);
739  identity[31] = '\0';
740  std::string serialNumber(&identity[23]);
741  igsioCommon::Trim(serialNumber);
742  trackerTool->SetCustomProperty("SerialNumber", serialNumber);
743  identity[23] = '\0';
744  std::string toolRevision(&identity[20]);
745  igsioCommon::Trim(toolRevision);
746  trackerTool->SetCustomProperty("Revision", toolRevision);
747  identity[20] = '\0';
748  std::string toolManufacturer(&identity[8]);
749  igsioCommon::Trim(toolManufacturer);
750  trackerTool->SetCustomProperty("Manufacturer", toolManufacturer);
751  identity[8] = '\0';
752  std::string ndiIdentity(&identity[0]);
753  igsioCommon::Trim(ndiIdentity);
754  trackerTool->SetCustomProperty("NdiIdentity", ndiIdentity);
755  char partNumber[24];
756  ndiGetPHINFPartNumber(this->Device, partNumber);
757  partNumber[20] = '\0';
758  std::string toolPartNumber(&partNumber[0]);
759  igsioCommon::Trim(toolPartNumber);
760  trackerTool->SetCustomProperty("PartNumber", toolPartNumber);
761  int status = ndiGetPHINFPortStatus(this->Device);
762 
763  toolDescriptorIt->second.PortEnabled = ((status & NDI_ENABLED) != 0);
764  if (!toolDescriptorIt->second.PortEnabled)
765  {
766  LOG_ERROR("Failed to enable NDI tool " << toolDescriptorIt->first);
767  status = PLUS_FAIL;
768  }
769  }
770 
771  // re-start the tracking
772  if (this->IsDeviceTracking)
773  {
774  this->Command("TSTART:");
775  int errnum = ndiGetError(this->Device);
776  if (errnum)
777  {
778  LOG_ERROR("Failed TSTART: " << ndiErrorString(errnum));
779  status = PLUS_FAIL;
780  }
781  }
782 
783  return status;
784 }
785 
786 //----------------------------------------------------------------------------
787 // Disable all enabled tool ports.
789 {
790  // stop tracking
791  if (this->IsDeviceTracking)
792  {
793  this->Command("TSTOP:");
794  int errnum = ndiGetError(this->Device);
795  if (errnum)
796  {
797  LOG_ERROR(ndiErrorString(errnum));
798  }
799  }
800 
801  // disable all enabled tools
802  this->Command("PHSR:04");
803  int ntools = ndiGetPHSRNumberOfHandles(this->Device);
804  for (int ndiToolIndex = 0; ndiToolIndex < ntools; ndiToolIndex++)
805  {
806  int portHandle = ndiGetPHSRHandle(this->Device, ndiToolIndex);
807  this->Command("PDIS:%02X", portHandle);
808  int errnum = ndiGetError(this->Device);
809  if (errnum)
810  {
811  LOG_ERROR(ndiErrorString(errnum));
812  }
813  }
814 
815  // disable the enabled ports
816  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
817  {
818  toolDescriptorIt->second.PortEnabled = false;
819  }
820 
821  // re-start the tracking
822  if (this->IsDeviceTracking)
823  {
824  this->Command("TSTART:");
825  int errnum = ndiGetError(this->Device);
826  if (errnum)
827  {
828  LOG_ERROR(ndiErrorString(errnum));
829  }
830  }
831 }
832 
833 //----------------------------------------------------------------------------
835 {
836  if (this->Recording)
837  {
838  LOG_ERROR("vtkPlusNDITracker::Beep failed: not connected to the device");
839  return PLUS_FAIL;
840  }
841  if (n > 9)
842  {
843  n = 9;
844  }
845  if (n < 0)
846  {
847  n = 0;
848  }
849  this->Command("BEEP:%i", n);
850  int errnum = ndiGetError(this->Device);
851 
852  return PLUS_SUCCESS;
853 }
854 
855 //----------------------------------------------------------------------------
857 {
858  if (!this->Recording)
859  {
860  LOG_ERROR("vtkPlusNDITracker::InternalSetToolLED failed: not recording");
861  return PLUS_FAIL;
862  }
863  NdiToolDescriptorsType::iterator ndiToolDescriptorIt = this->NdiToolDescriptors.find(sourceId);
864  if (ndiToolDescriptorIt == this->NdiToolDescriptors.end())
865  {
866  LOG_ERROR("InternalSetToolLED failed: Tool descriptor is not found for tool " << sourceId);
867  return PLUS_FAIL;
868  }
869  int portHandle = ndiToolDescriptorIt->second.PortHandle;
870  if (portHandle <= 0)
871  {
872  LOG_ERROR("vtkPlusNDITracker::InternalSetToolLED failed: invalid port handle");
873  return PLUS_FAIL;
874  }
875 
876  int plstate = NDI_BLANK;
877  switch (state)
878  {
879  case TR_LED_OFF:
880  plstate = NDI_BLANK;
881  break;
882  case TR_LED_ON:
883  plstate = NDI_SOLID;
884  break;
885  case TR_LED_FLASH:
886  plstate = NDI_FLASH;
887  break;
888  default:
889  LOG_ERROR("vtkPlusNDITracker::InternalSetToolLED failed: unsupported LED state: " << state);
890  return PLUS_FAIL;
891  }
892 
893  this->Command("LED:%02X%d%c", portHandle, led + 1, plstate);
894  int errnum = ndiGetError(this->Device);
895 
896  return PLUS_SUCCESS;
897 }
898 
899 //----------------------------------------------------------------------------
900 PlusStatus vtkPlusNDITracker::UpdatePortHandle(NdiToolDescriptor& toolDescriptor)
901 {
902  if (toolDescriptor.WiredPortNumber >= 0) // wired tool
903  {
904  this->Command("PHSR:00");
905  int ntools = ndiGetPHSRNumberOfHandles(this->Device);
906  int ndiToolIndex = 0;
907  for (; ndiToolIndex < ntools; ndiToolIndex++)
908  {
909  if (ndiGetPHSRInformation(this->Device, ndiToolIndex) & NDI_TOOL_IN_PORT)
910  {
911  int portHandle = ndiGetPHSRHandle(this->Device, ndiToolIndex);
912  this->Command("PHINF:%02X0021", portHandle);
913  char location[14];
914  ndiGetPHINFPortLocation(this->Device, location);
915  int foundWiredPortNumber = (location[10] - '0') * 10 + (location[11] - '0') - 1;
916  int foundWiredPortChannel = (location[12] - '0') * 10 + (location[13] - '0'); // this is nonzero if 5-DOF tools with splitter
917  int combinedPortAndChannelNumber = foundWiredPortChannel * 100 + foundWiredPortNumber;
918  if (toolDescriptor.WiredPortNumber == combinedPortAndChannelNumber)
919  {
920  // found the portHandle
921  toolDescriptor.PortHandle = portHandle;
922  break;
923  }
924  }
925  }
926  if (ndiToolIndex == ntools)
927  {
928  LOG_ERROR("Active NDI tool not found in port " << toolDescriptor.WiredPortNumber << ". Make sure the tool is plugged in.");
929  return PLUS_FAIL;
930  }
931  }
932  else // wireless tool
933  {
934  this->Command("PHRQ:*********1****");
935  int portHandle = ndiGetPHRQHandle(this->Device);
936  toolDescriptor.PortHandle = portHandle;
937  }
938 
939  int errnum = ndiGetError(this->Device);
940  if (errnum)
941  {
942  LOG_ERROR(ndiErrorString(errnum));
943  return PLUS_FAIL;
944  }
945 
946  return PLUS_SUCCESS;
947 }
948 
949 
950 //----------------------------------------------------------------------------
951 PlusStatus vtkPlusNDITracker::SendSromToTracker(const NdiToolDescriptor& toolDescriptor)
952 {
953  if (toolDescriptor.VirtualSROM == NULL)
954  {
955  // nothing to load
956  return PLUS_SUCCESS;
957  }
958 
959  igsioLockGuard<vtkIGSIORecursiveCriticalSection> lock(this->CommandMutex);
960  const int TRANSFER_BLOCK_SIZE = 64; // in bytes
961  char hexbuffer[TRANSFER_BLOCK_SIZE * 2];
962  for (int i = 0; i < VIRTUAL_SROM_SIZE; i += TRANSFER_BLOCK_SIZE)
963  {
964  RETRY_UNTIL_TRUE(
965  strcmp(this->Command("PVWR:%02X%04X%.128s", toolDescriptor.PortHandle, i,
966  ndiHexEncode(hexbuffer, &(toolDescriptor.VirtualSROM[i]), TRANSFER_BLOCK_SIZE)).c_str(), "OKAY") == 0,
967  10, 0.1);
968 
969  int errnum = ndiGetError(this->Device);
970  if (errnum)
971  {
972  std::stringstream ss;
973  ss << "Failed to send SROM to NDI tracker: " << ndiErrorString(errnum);
974  LOG_ERROR(ss.str());
975  return PLUS_FAIL;
976  }
977  }
978 
979  return PLUS_SUCCESS;
980 }
981 
982 //----------------------------------------------------------------------------
984 {
985  if (toolDescriptor.VirtualSROM == NULL)
986  {
987  // nothing to clear
988  return PLUS_SUCCESS;
989  }
990 
991  this->Command("PHF:%02X", toolDescriptor.PortHandle);
992  toolDescriptor.PortEnabled = false;
993  toolDescriptor.PortHandle = 0;
994 
995  return PLUS_SUCCESS;
996 }
997 
998 //----------------------------------------------------------------------------
999 PlusStatus vtkPlusNDITracker::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
1000 {
1001  // Clean up any previously read config data
1002  for (NdiToolDescriptorsType::iterator toolDescriptorIt = this->NdiToolDescriptors.begin(); toolDescriptorIt != this->NdiToolDescriptors.end(); ++toolDescriptorIt)
1003  {
1004  delete [] toolDescriptorIt->second.VirtualSROM;
1005  toolDescriptorIt->second.VirtualSROM = NULL;
1006  }
1007  this->NdiToolDescriptors.clear();
1008 
1009  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
1010 
1011  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(unsigned long, SerialPort, deviceConfig);
1012  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(unsigned long, BaudRate, deviceConfig);
1013  if (deviceConfig->GetAttribute("BaudRate") != NULL && vtkPlusNDITracker::ConvertBaudToNDIEnum(this->BaudRate) == -1)
1014  {
1015  LOG_WARNING("Invalid baud rate specified, reverting to auto-select.");
1016  this->BaudRate = 0;
1017  }
1018  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, MeasurementVolumeNumber, deviceConfig);
1019  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, TrackingFrequencyNumber, deviceConfig);
1020  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, HardwareDataAveragingDepth, deviceConfig);
1021 
1022  XML_READ_STRING_ATTRIBUTE_OPTIONAL(NetworkHostname, deviceConfig);
1023  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, NetworkPort, deviceConfig);
1024  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(CheckDSR, deviceConfig);
1025 
1026  XML_FIND_NESTED_ELEMENT_REQUIRED(dataSourcesElement, deviceConfig, "DataSources");
1027 
1028  for (int nestedElementIndex = 0; nestedElementIndex < dataSourcesElement->GetNumberOfNestedElements(); nestedElementIndex++)
1029  {
1030  vtkXMLDataElement* toolDataElement = dataSourcesElement->GetNestedElement(nestedElementIndex);
1031  if (STRCASECMP(toolDataElement->GetName(), "DataSource") != 0)
1032  {
1033  // if this is not a data source element, skip it
1034  continue;
1035  }
1036  bool isEqual(false);
1037  if (igsioCommon::XML::SafeCheckAttributeValueInsensitive(*toolDataElement, "Type", vtkPlusDataSource::DATA_SOURCE_TYPE_TOOL_TAG, isEqual) != PLUS_SUCCESS || !isEqual)
1038  {
1039  // if this is not a Tool element, skip it
1040  continue;
1041  }
1042  const char* toolId = toolDataElement->GetAttribute("Id");
1043  if (toolId == NULL)
1044  {
1045  LOG_ERROR("Failed to initialize NDI tool: DataSource Id is missing");
1046  continue;
1047  }
1048  igsioTransformName toolTransformName(toolId, this->GetToolReferenceFrameName());
1049  std::string toolSourceId = toolTransformName.GetTransformName();
1050  vtkPlusDataSource* trackerTool = NULL;
1051  if (this->GetTool(toolSourceId, trackerTool) != PLUS_SUCCESS || trackerTool == NULL)
1052  {
1053  LOG_ERROR("Failed to get NDI tool: " << toolSourceId);
1054  continue;
1055  }
1056 
1057  int wiredPortNumber = -1;
1058  if (toolDataElement->GetAttribute("PortName") != NULL)
1059  {
1060  if (!toolDataElement->GetScalarAttribute("PortName", wiredPortNumber))
1061  {
1062  LOG_WARNING("NDI wired tool's PortName attribute has to be an integer >=0");
1063  continue;
1064  }
1065  }
1066 
1067  NdiToolDescriptor toolDescriptor;
1068  toolDescriptor.PortEnabled = false;
1069  toolDescriptor.PortHandle = 0;
1070  toolDescriptor.VirtualSROM = NULL;
1071  toolDescriptor.WiredPortNumber = wiredPortNumber;
1072 
1073  const char* romFileName = toolDataElement->GetAttribute("RomFile");
1074  if (romFileName)
1075  {
1076  // Passive (wireless) tool or wired tool with virtual ROM
1077  if (wiredPortNumber >= 0)
1078  {
1079  LOG_WARNING("NDI PortName and RomFile are both specified for tool " << toolSourceId << ". Assuming broken wired rom, using virtual rom instead");
1080  }
1081  std::string romFilePath = vtkPlusConfig::GetInstance()->GetDeviceSetConfigurationPath(romFileName);
1082  this->ReadSromFromFile(toolDescriptor, romFilePath.c_str());
1083  }
1084 
1085  this->NdiToolDescriptors[toolSourceId] = toolDescriptor;
1086  }
1087 
1088  return PLUS_SUCCESS;
1089 }
1090 
1091 //----------------------------------------------------------------------------
1093 {
1094  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(trackerConfig, rootConfig);
1095  if (this->SerialPort > 0)
1096  {
1097  trackerConfig->SetIntAttribute("SerialPort", this->SerialPort);
1098  }
1099 
1100  XML_WRITE_STRING_ATTRIBUTE_IF_NOT_EMPTY(NetworkHostname, trackerConfig);
1101  if (!this->NetworkHostname.empty())
1102  {
1103  trackerConfig->SetIntAttribute("NetworkPort", this->NetworkPort);
1104  }
1105 
1106  if (this->HardwareDataAveragingDepth != 1)
1107  {
1108  trackerConfig->SetIntAttribute("HardwareDataAveragingDepth", this->HardwareDataAveragingDepth);
1109  }
1110 
1111  if (this->BaudRate > 0)
1112  {
1113  trackerConfig->SetIntAttribute("BaudRate", this->BaudRate);
1114  }
1115  if (this->MeasurementVolumeNumber > 0)
1116  {
1117  trackerConfig->SetIntAttribute("MeasurementVolumeNumber", this->MeasurementVolumeNumber);
1118  }
1119  if (this->TrackingFrequencyNumber > 0)
1120  {
1121  trackerConfig->SetIntAttribute("TrackingFrequencyNumber", this->TrackingFrequencyNumber);
1122  }
1123  trackerConfig->SetAttribute("CheckDSR", this->CheckDSR ? "true" : "false");
1124 
1125  return PLUS_SUCCESS;
1126 }
1127 
1128 //----------------------------------------------------------------------------
1129 void vtkPlusNDITracker::LogVolumeList(int selectedVolume, vtkPlusLogger::LogLevelType logLevel)
1130 {
1131  auto reply = this->Command("GETINFO:Param.Tracking.Available Volumes");
1132 
1133  unsigned int numVolumes(0);
1134  int errnum = ndiGetError(this->Device);
1135  if (errnum)
1136  {
1137  const unsigned char MODE_GET_VOLUMES_LIST = 0x03; // list of volumes available
1138  auto volumeListCommandReply = this->Command("SFLIST:%02X", MODE_GET_VOLUMES_LIST);
1139  numVolumes = ndiHexToUnsignedInt(volumeListCommandReply.c_str(), 1);
1140  }
1141  else
1142  {
1143  auto tokens = igsioCommon::SplitStringIntoTokens(reply, '=', false);
1144  auto dataFields = igsioCommon::SplitStringIntoTokens(tokens[1], ';', true);
1145  numVolumes = dataFields.size() / 7;
1146  }
1147 
1148  if (selectedVolume == 0)
1149  {
1150  LOG_DYNAMIC("Number of available measurement volumes: " << numVolumes, logLevel);
1151  }
1152 
1153  reply = this->Command("GETINFO:Features.Volumes.*");
1154  if (reply.find("ERROR") != std::string::npos)
1155  {
1156  // Most likely error 34 "Parameter not found", revert to SFLIST method
1157  LogVolumeListSFLIST(numVolumes, selectedVolume, logLevel);
1158  return;
1159  }
1160 
1161  auto lines = igsioCommon::SplitStringIntoTokens(reply, '\n', false);
1162 
1163  for (auto iter = begin(lines); iter != end(lines); ++iter)
1164  {
1165  auto tokens = igsioCommon::SplitStringIntoTokens(*iter, '=', true);
1166  auto dataFields = igsioCommon::SplitStringIntoTokens(tokens[1], ';', true);
1167  if (selectedVolume > 0 &&
1168  tokens[0].find("Features.Volumes.Index") != std::string::npos &&
1169  dataFields[0][0] - '0' == selectedVolume - 1) // NDI index by 0, Plus index by 1
1170  {
1171  LOG_DYNAMIC("Measurement volume " << selectedVolume, logLevel);
1172 
1173  // Selected index, next 13 lines are details about the selected volume
1174  // Features.Volumes.Name The volume name Read
1175  // Features.Volumes.Shape The shape type Read
1176  // Features.Volumes.Wavelengths Which wavelengths are supported in the volume Read
1177  // Features.Volumes.Paramn Shape parameters as described in SFLIST Read
1178  for (int i = 0; i < 13; ++i)
1179  {
1180  auto tokens = igsioCommon::SplitStringIntoTokens(*(iter + i), '=', true);
1181  auto names = igsioCommon::SplitStringIntoTokens(tokens[0], '.', false);
1182  auto dataFields = igsioCommon::SplitStringIntoTokens(tokens[1], ';', true);
1183  LOG_DYNAMIC(" " << names[2] << ": " << dataFields[0], logLevel);
1184  }
1185 
1186  return;
1187  }
1188 
1189  auto names = igsioCommon::SplitStringIntoTokens(tokens[0], '.', false);
1190  LOG_DYNAMIC(names[2] << ": " << dataFields[0], logLevel);
1191  }
1192 }
1193 
1194 //----------------------------------------------------------------------------
1195 void vtkPlusNDITracker::LogVolumeListSFLIST(unsigned int numVolumes, int selectedVolume, vtkPlusLogger::LogLevelType logLevel)
1196 {
1197  std::string volumeListCommandReply = this->Command("SFLIST:03");
1198  for (unsigned int volIndex = 0; volIndex < numVolumes; ++volIndex)
1199  {
1200  const char* volDescriptor = volumeListCommandReply.c_str() + 1 + volIndex * 74;
1201 
1202  LOG_DYNAMIC("Measurement volume " << volIndex + 1, logLevel);
1203 
1204  std::string shapeType;
1205  bool isOptical(false);
1206  switch (volDescriptor[0])
1207  {
1208  case '9':
1209  shapeType = "Cube";
1210  break;
1211  case 'A':
1212  shapeType = "Dome";
1213  break;
1214  case '5':
1215  shapeType = "Polaris (Pyramid or extended pyramid)";
1216  isOptical = true;
1217  break;
1218  case '7':
1219  shapeType = "Vicra (Pyramid)";
1220  isOptical = true;
1221  break;
1222  default:
1223  shapeType = "unknown";
1224  }
1225  LOG_DYNAMIC(" Shape type: " << shapeType << " (" << volDescriptor[0] << ")", logLevel);
1226 
1227  LOG_DYNAMIC(" D1 = " << ndiSignedToLong(volDescriptor + 1, 7) / 100, logLevel);
1228  LOG_DYNAMIC(" D2 = " << ndiSignedToLong(volDescriptor + 8, 7) / 100, logLevel);
1229  LOG_DYNAMIC(" D3 = " << ndiSignedToLong(volDescriptor + 15, 7) / 100, logLevel);
1230  LOG_DYNAMIC(" D4 = " << ndiSignedToLong(volDescriptor + 22, 7) / 100, logLevel);
1231  LOG_DYNAMIC(" D5 = " << ndiSignedToLong(volDescriptor + 29, 7) / 100, logLevel);
1232  LOG_DYNAMIC(" D6 = " << ndiSignedToLong(volDescriptor + 36, 7) / 100, logLevel);
1233  LOG_DYNAMIC(" D7 = " << ndiSignedToLong(volDescriptor + 43, 7) / 100, logLevel);
1234  LOG_DYNAMIC(" D8 = " << ndiSignedToLong(volDescriptor + 50, 7) / 100, logLevel);
1235  LOG_DYNAMIC(" D9 = " << ndiSignedToLong(volDescriptor + 57, 7) / 100, logLevel);
1236  LOG_DYNAMIC(" D10 = " << ndiSignedToLong(volDescriptor + 64, 7) / 100, logLevel);
1237 
1238  if (!isOptical)
1239  {
1240  LOG_DYNAMIC(" Reserved: " << volDescriptor[71], logLevel);
1241 
1242  std::string metalResistant;
1243  switch (volDescriptor[72])
1244  {
1245  case '0':
1246  metalResistant = "no information";
1247  break;
1248  case '1':
1249  metalResistant = "metal resistant";
1250  break;
1251  case '2':
1252  metalResistant = "not metal resistant";
1253  break;
1254  default:
1255  metalResistant = "unknown";
1256  }
1257  LOG_DYNAMIC(" Metal resistant: " << metalResistant << " (" << volDescriptor[72] << ")", logLevel);
1258  }
1259  else
1260  {
1261  unsigned int numWavelengths = volDescriptor[71] - '0';
1262  LOG_DYNAMIC(" Number of wavelengths supported: " << numWavelengths, logLevel);
1263  for (unsigned int i = 0; i < numWavelengths; ++i)
1264  {
1265  switch (volDescriptor[72 + i])
1266  {
1267  case '0':
1268  LOG_DYNAMIC(" Wavelength " << i << ": 930nm", logLevel);
1269  break;
1270  case '1':
1271  LOG_DYNAMIC(" Wavelength " << i << ": 880nm", logLevel);
1272  break;
1273  case '4':
1274  LOG_DYNAMIC(" Wavelength " << i << ": 870nm", logLevel);
1275  break;
1276  default:
1277  LOG_DYNAMIC(" Wavelength " << i << ": unknown (" << volDescriptor[72 + i] << ")", logLevel);
1278  break;
1279  }
1280  }
1281  }
1282  }
1283 }
1284 
1285 //----------------------------------------------------------------------------
1287 {
1288  if (this->NetworkHostname.empty())
1289  {
1290  ndiCloseSerial(device);
1291  }
1292  else
1293  {
1294  ndiCloseNetwork(device);
1295  }
1296  device = nullptr;
1297 
1298  return PLUS_SUCCESS;
1299 }
1300 
1301 //----------------------------------------------------------------------------
1303 {
1304  if (this->MeasurementVolumeNumber > 0)
1305  {
1306  std::string reply = this->Command("GETINFO:Param.Tracking.Available Volumes");
1307  int errnum = ndiGetError(this->Device);
1308  if (errnum)
1309  {
1311  }
1312  else
1313  {
1314  auto tokens = igsioCommon::SplitStringIntoTokens(reply, '=', false);
1315  auto dataFields = igsioCommon::SplitStringIntoTokens(tokens[1], ';', true);
1316 
1317  // <Value>;<Type>;<Attribute>;<Minimum>;<Maximum>;<Enumeration>;<Description>
1318  if (static_cast<unsigned int>(this->MeasurementVolumeNumber) - 1 > dataFields.size() / 7)
1319  {
1320  LOG_ERROR("Selected measurement volume does not exist. Using default.");
1321  LogVolumeList(this->MeasurementVolumeNumber, vtkPlusLogger::LOG_LEVEL_DEBUG);
1322  return PLUS_FAIL;
1323  }
1324  else
1325  {
1326  // Use SET command with parameter
1327  std::string volumeSelectCommandReply = this->Command("SET:Param.Tracking.Selected Volume=%d", this->MeasurementVolumeNumber);
1328  int errnum = ndiGetError(this->Device);
1329  if (errnum)
1330  {
1331  LOG_ERROR("Failed to set measurement volume " << this->MeasurementVolumeNumber << ": " << ndiErrorString(errnum));
1332  LogVolumeList(0, vtkPlusLogger::LOG_LEVEL_INFO);
1333  CloseDevice(this->Device);
1334  return PLUS_FAIL;
1335  }
1336  }
1337  }
1338  }
1339 
1340  return PLUS_SUCCESS;
1341 }
1342 
1343 //----------------------------------------------------------------------------
1345 {
1346  auto volumeSelectCommandReply = this->Command("VSEL:%d", this->MeasurementVolumeNumber);
1347  int errnum = ndiGetError(this->Device);
1348  if (errnum)
1349  {
1350  LOG_ERROR("Failed to set measurement volume " << this->MeasurementVolumeNumber << ": " << ndiErrorString(errnum));
1351  LogVolumeList(this->MeasurementVolumeNumber, vtkPlusLogger::LOG_LEVEL_DEBUG);
1352  return PLUS_FAIL;
1353  }
1354 
1355  return PLUS_SUCCESS;
1356 }
1357 
1358 //----------------------------------------------------------------------------
1360 {
1361  /*
1362  Frame Frequency (or Frame Rate)
1363 
1364  The frequency in [Hz] at which
1365  the image sensors are read
1366  out.
1367 
1368  Param.Tracking.Frame Frequency
1369  Vega ST/VT: fixed at 60 Hz
1370  Vega XT: configurable 60-400 Hz
1371  Lyra: configurable 10-120 Hz (10-250 Hz with the MR125 option)
1372 
1373  Track Frequency
1374  The frequency at which unaveraged tracking data is reported. The actual track frequency is influenced by:
1375  • The parameter setting
1376  • The number of different tool types tracked (active wired/active wireless/passive)
1377 
1378  For Vega, when tracking a single tool type, the Track Frequency is equal to the
1379  Frame Frequency. If more than one tool type is tracked, the Track Frequency is a fraction
1380  (1/2 or 1/3) of that, even if the parameter is set to 1/1 of Frame Frequency.
1381 
1382  For Lyra, when tracking one or two tool types, the Track Frequency is 1/2. When
1383  tracking three tool types, the Track Frequency is 1/3.
1384 
1385  Param.Tracking.Track Frequency
1386  This parameter is an enumeration whose values are derived from the frame frequency.
1387  For Vega, this can be:
1388  • 1/3 of frame frequency
1389  • 1/2 of frame frequency
1390  • 1/1 of frame frequency
1391  For Lyra, this can be:
1392  • 1/3 of frame frequency
1393  • 1/2 of frame frequency
1394 
1395  Reporting
1396  Frequency The result of the actual track frequency divided by the averaging depth specified by Param.Data Averaging.Depth.
1397  */
1398 
1399  if (this->TrackingFrequencyNumber > 0)
1400  {
1401  std::string reply = this->Command("GET:Param.Tracking.Track Frequency");
1402 
1403  int errnum = ndiGetError(this->Device);
1404  if (errnum)
1405  {
1407  }
1408  else
1409  {
1410  // Use SET command with parameter
1411  std::string frequencySelectCommandReply = this->Command("SET:Param.Tracking.Track Frequency=%d", this->TrackingFrequencyNumber);
1412  int errnum = ndiGetError(this->Device);
1413  if (errnum)
1414  {
1415  LOG_ERROR("Failed to set tracking frequency " << this->TrackingFrequencyNumber << ": " << ndiErrorString(errnum));
1416  return PLUS_FAIL;
1417  }
1418  }
1419  }
1420 
1421  return PLUS_SUCCESS;
1422 }
1423 
1424 //----------------------------------------------------------------------------
1426 {
1427  auto frequencySelectCommandReply = this->Command("IRATE:%d", this->TrackingFrequencyNumber);
1428  int errnum = ndiGetError(this->Device);
1429  if (errnum)
1430  {
1431  LOG_ERROR("Failed to set tracking frequency " << this->TrackingFrequencyNumber << ": " << ndiErrorString(errnum));
1432  return PLUS_FAIL;
1433  }
1434 
1435  return PLUS_SUCCESS;
1436 }
1437 
1438 //----------------------------------------------------------------------------
1440 {
1441  if (this->GetFirmwareMajorRevision() >= 3 && this->GetFirmwareMinorRevision() >= 6)
1442  {
1443  std::string model = this->Command("GET:%s", "Features.Hardware.Model");
1444 
1445  this->Command("SET:%s=%d", "Param.Data Averaging.Depth", this->HardwareDataAveragingDepth);
1446  int errnum = ndiGetError(this->Device);
1447  if (errnum)
1448  {
1449  LOG_ERROR("Unable to set data averaging depth: " << ndiErrorString(errnum));
1450  return PLUS_FAIL;
1451  }
1452 
1453  if (this->HardwareDataAveragingDepth > 1)
1454  {
1455  // Calculate the new timeout
1456  std::string result = this->Command("GET:%s", "Param.Tracking.Frame Frequency");
1457  std::vector<std::string> tokens = igsioCommon::SplitStringIntoTokens(result, '=');
1458  unsigned int frameFreq;
1459  igsioCommon::StringToNumber<unsigned int>(tokens[1], frameFreq);
1460 
1461  result = this->Command("GET:%s", "Param.Tracking.Track Frequency");
1462  tokens = igsioCommon::SplitStringIntoTokens(result, '=');
1463  unsigned int trackFreqEnum;
1464  igsioCommon::StringToNumber<unsigned int>(tokens[1], trackFreqEnum);
1465 
1466  /*
1467  Param.Tracking.Track Frequency
1468  This parameter is an enumeration whose values are derived from the frame frequency.
1469  For Vega, this can be:
1470  • 1/3 of frame frequency
1471  • 1/2 of frame frequency
1472  • 1/1 of frame frequency
1473  For Lyra, this can be:
1474  • 1/3 of frame frequency
1475  • 1/2 of frame frequency
1476  */
1477  double trackFreq(0);
1478  if (model.find("Vega") != std::string::npos)
1479  {
1480  switch (trackFreqEnum)
1481  {
1482  case 0:
1483  trackFreq = frameFreq / 3;
1484  break;
1485  case 1:
1486  trackFreq = frameFreq / 2;
1487  break;
1488  case 2:
1489  trackFreq = frameFreq;
1490  break;
1491  default:
1492  break;
1493  }
1494  }
1495  else if (model.find("Lyra") != std::string::npos)
1496  {
1497  switch (trackFreqEnum)
1498  {
1499  case 0:
1500  trackFreq = frameFreq / 3;
1501  break;
1502  case 1:
1503  trackFreq = frameFreq / 2;
1504  break;
1505  default:
1506  break;
1507  }
1508  }
1509 
1510  if (trackFreq != 0)
1511  {
1512  double timeoutMs = 1. / trackFreq * 1000 * this->HardwareDataAveragingDepth;
1513  ndiTimeoutSocket(this->Device, static_cast<unsigned int>(timeoutMs) + 25); // Add 25 ms buffer for kicks
1514  }
1515  }
1516  }
1517 
1518  return PLUS_SUCCESS;
1519 }
1520 
1521 //----------------------------------------------------------------------------
1523 {
1524  switch (baudRate)
1525  {
1526  case 9600:
1527  return NDI_9600;
1528  case 14400:
1529  return NDI_14400;
1530  case 19200:
1531  return NDI_19200;
1532  case 38400:
1533  return NDI_38400;
1534  case 57600:
1535  return NDI_57600;
1536  case 115200:
1537  return NDI_115200;
1538  case 230400:
1539  return NDI_230400;
1540  case 921600:
1541  return NDI_921600;
1542  case 1228739:
1543  return NDI_1228739;
1544  default:
1545  return -1;
1546  }
1547 }
1548 
1549 //----------------------------------------------------------------------------
1551 {
1552  int errnum = 0;
1553  // get the transforms for all tools from the NDI
1554  this->Command("BX:0801");
1555  errnum = ndiGetError(this->Device);
1556  if (errnum)
1557  {
1558  if (errnum == NDI_BAD_CRC || errnum == NDI_TIMEOUT) // common errors
1559  {
1560  LOG_WARNING(ndiErrorString(errnum));
1561  }
1562  else
1563  {
1564  LOG_ERROR(ndiErrorString(errnum));
1565  }
1566  return PLUS_FAIL;
1567  }
1568 
1569  // default to incrementing frame count by one (in case a frame index cannot be retrieved from the tracker for a specific tool)
1570  this->LastFrameNumber++;
1571  int defaultToolFrameNumber = this->LastFrameNumber;
1572  const double toolTimestamp = vtkIGSIOAccurateTimer::GetSystemTime(); // unfiltered timestamp
1573  vtkSmartPointer<vtkMatrix4x4> toolToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
1574  for (DataSourceContainerConstIterator it = this->GetToolIteratorBegin(); it != this->GetToolIteratorEnd(); ++it)
1575  {
1576  ToolStatus toolFlags = TOOL_OK;
1577  toolToTrackerTransform->Identity();
1578  unsigned long toolFrameNumber = defaultToolFrameNumber;
1579  vtkPlusDataSource* trackerTool = it->second;
1580  std::string toolSourceId = trackerTool->GetId();
1581  NdiToolDescriptorsType::iterator ndiToolDescriptorIt = this->NdiToolDescriptors.find(toolSourceId);
1582  if (ndiToolDescriptorIt == this->NdiToolDescriptors.end())
1583  {
1584  LOG_ERROR("Tool descriptor is not found for tool " << toolSourceId);
1585  this->ToolTimeStampedUpdate(trackerTool->GetId(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1586  continue;
1587  }
1588  int portHandle = ndiToolDescriptorIt->second.PortHandle;
1589  if (portHandle <= 0)
1590  {
1591  LOG_ERROR("Port handle is invalid for tool " << toolSourceId);
1592  this->ToolTimeStampedUpdate(toolSourceId.c_str(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1593  continue;
1594  }
1595 
1596  float ndiTransform[8] = { 1, 0, 0, 0, 0, 0, 0, 0 };
1597  int ndiToolAbsent = ndiGetBXTransform(this->Device, portHandle, ndiTransform);
1598  int ndiPortStatus = ndiGetBXPortStatus(this->Device, portHandle);
1599  unsigned long ndiFrameIndex = ndiGetBXFrame(this->Device, portHandle);
1600 
1601  // convert status flags from NDI to Plus format
1602  const unsigned long ndiPortStatusValidFlags = NDI_TOOL_IN_PORT | NDI_INITIALIZED | NDI_ENABLED;
1603  if ((ndiPortStatus & ndiPortStatusValidFlags) != ndiPortStatusValidFlags)
1604  {
1605  toolFlags = TOOL_MISSING;
1606  }
1607  else
1608  {
1609  if (ndiToolAbsent)
1610  {
1611  toolFlags = TOOL_OUT_OF_VIEW;
1612  }
1613  if (ndiPortStatus & NDI_OUT_OF_VOLUME)
1614  {
1615  toolFlags = TOOL_OUT_OF_VOLUME;
1616  }
1617  // TODO all these button state toolFlags are on regardless of the actual state
1618  //if (ndiPortStatus & NDI_SWITCH_1_ON) { toolFlags = TOOL_SWITCH1_IS_ON; }
1619  //if (ndiPortStatus & NDI_SWITCH_2_ON) { toolFlags = TOOL_SWITCH2_IS_ON; }
1620  //if (ndiPortStatus & NDI_SWITCH_3_ON) { toolFlags = TOOL_SWITCH3_IS_ON; }
1621  }
1622 
1623  ndiTransformToMatrixfd(ndiTransform, *toolToTrackerTransform->Element);
1624  toolToTrackerTransform->Transpose();
1625 
1626  // by default (if there is no camera frame number associated with
1627  // the tool transformation) the most recent timestamp is used.
1628  if (!ndiToolAbsent && ndiFrameIndex)
1629  {
1630  // this will create a timestamp from the frame number
1631  toolFrameNumber = ndiFrameIndex;
1632  if (ndiFrameIndex > this->LastFrameNumber)
1633  {
1634  this->LastFrameNumber = ndiFrameIndex;
1635  }
1636  }
1637 
1638  // send the matrix and status to the tool's vtkPlusDataBuffer
1639  this->ToolTimeStampedUpdate(toolSourceId.c_str(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1640  }
1641 
1642  // Update tool connections if a wired tool is plugged in
1643  if (ndiGetBXSystemStatus(this->Device) & NDI_PORT_OCCUPIED)
1644  {
1645  LOG_WARNING("A wired tool has been plugged into tracker " << (this->GetDeviceId().empty() ? this->GetDeviceId() : "(unknown NDI tracker"));
1646  // Make the newly connected tools available
1647  this->EnableToolPorts();
1648  }
1649 
1650  return PLUS_SUCCESS;
1651 }
1652 
1653 //----------------------------------------------------------------------------
1655 {
1656  int errnum = 0;
1657  // get the transforms for all tools from the NDI
1658  this->Command("BX2:--6d=tools --1d=none");
1659  errnum = ndiGetError(this->Device);
1660  if (errnum)
1661  {
1662  if (errnum == NDI_BAD_CRC || errnum == NDI_TIMEOUT) // common errors
1663  {
1664  LOG_WARNING(ndiErrorString(errnum));
1665  }
1666  else
1667  {
1668  LOG_ERROR(ndiErrorString(errnum));
1669  }
1670  return PLUS_FAIL;
1671  }
1672 
1673  // default to incrementing frame count by one (in case a frame index cannot be retrieved from the tracker for a specific tool)
1674  this->LastFrameNumber++;
1675  int defaultToolFrameNumber = this->LastFrameNumber;
1676  const double toolTimestamp = vtkIGSIOAccurateTimer::GetSystemTime(); // unfiltered timestamp
1677  vtkSmartPointer<vtkMatrix4x4> toolToTrackerTransform = vtkSmartPointer<vtkMatrix4x4>::New();
1678  unsigned long ndiFrameIndex = ndiGetBX2Frame(this->Device);
1679  for (DataSourceContainerConstIterator it = this->GetToolIteratorBegin(); it != this->GetToolIteratorEnd(); ++it)
1680  {
1681  ToolStatus toolFlags = TOOL_OK;
1682  toolToTrackerTransform->Identity();
1683  unsigned long toolFrameNumber = defaultToolFrameNumber;
1684  vtkPlusDataSource* trackerTool = it->second;
1685  std::string toolSourceId = trackerTool->GetId();
1686  NdiToolDescriptorsType::iterator ndiToolDescriptorIt = this->NdiToolDescriptors.find(toolSourceId);
1687  if (ndiToolDescriptorIt == this->NdiToolDescriptors.end())
1688  {
1689  LOG_ERROR("Tool descriptor is not found for tool " << toolSourceId);
1690  this->ToolTimeStampedUpdate(trackerTool->GetId(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1691  continue;
1692  }
1693  int portHandle = ndiToolDescriptorIt->second.PortHandle;
1694  if (portHandle <= 0)
1695  {
1696  LOG_ERROR("Port handle is invalid for tool " << toolSourceId);
1697  this->ToolTimeStampedUpdate(toolSourceId.c_str(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1698  continue;
1699  }
1700 
1701  float ndiTransform[8] = { 1, 0, 0, 0, 0, 0, 0, 0 };
1702  int ndiToolAbsent = ndiGetBX2Transform(this->Device, portHandle, ndiTransform);
1703  int ndiPortStatus = ndiGetBX2PortStatus(this->Device, portHandle);
1704 
1705  // convert status flags from NDI to Plus format
1706  if (ndiPortStatus & NDI_BX2_MISSING_BIT)
1707  {
1708  toolFlags = TOOL_MISSING;
1709  }
1710  else
1711  {
1712  if (ndiPortStatus == NDI_BX2_PARTIAL_VIEW)
1713  {
1714  toolFlags = TOOL_OUT_OF_VIEW;
1715  }
1716  if (ndiPortStatus == NDI_BX2_OUT_OF_VOLUME)
1717  {
1718  toolFlags = TOOL_OUT_OF_VOLUME;
1719  }
1720  // TODO all these button state toolFlags are on regardless of the actual state
1721  //if (ndiPortStatus & NDI_SWITCH_1_ON) { toolFlags = TOOL_SWITCH1_IS_ON; }
1722  //if (ndiPortStatus & NDI_SWITCH_2_ON) { toolFlags = TOOL_SWITCH2_IS_ON; }
1723  //if (ndiPortStatus & NDI_SWITCH_3_ON) { toolFlags = TOOL_SWITCH3_IS_ON; }
1724  }
1725 
1726  ndiTransformToMatrixfd(ndiTransform, *toolToTrackerTransform->Element);
1727  toolToTrackerTransform->Transpose();
1728 
1729  // by default (if there is no camera frame number associated with
1730  // the tool transformation) the most recent timestamp is used.
1731  if (ndiToolAbsent == NDI_BX2_ENABLED && ndiFrameIndex)
1732  {
1733  // this will create a timestamp from the frame number
1734  toolFrameNumber = ndiFrameIndex;
1735  if (ndiFrameIndex > this->LastFrameNumber)
1736  {
1737  this->LastFrameNumber = ndiFrameIndex;
1738  }
1739  }
1740 
1741  // send the matrix and status to the tool's vtkPlusDataBuffer
1742  this->ToolTimeStampedUpdate(toolSourceId.c_str(), toolToTrackerTransform, toolFlags, toolFrameNumber, toolTimestamp);
1743  }
1744 
1745  int alerts = ndiGetBX2SystemAlertsCount(this->Device);
1746  for (int i = 0; i < alerts; ++i)
1747  {
1748  unsigned short* alert = ndiGetBX2SystemAlert(this->Device, i);
1749  if (alert[1] == NDI_SYS_EVENT_TOOL_CONNECTED)
1750  {
1751  LOG_WARNING("A wired tool has been plugged into tracker " << (this->GetDeviceId().empty() ? this->GetDeviceId() : "(unknown NDI tracker"));
1752  // Make the newly connected tools available
1753  this->EnableToolPorts();
1754  }
1755  }
1756 
1757  return PLUS_SUCCESS;
1758 }
1759 
1760 #if defined(HAVE_FUTURE)
1761 //----------------------------------------------------------------------------
1762 PlusStatus vtkPlusNDITracker::ProbeSerialInternal()
1763 {
1764  const int MAX_SERIAL_PORT_NUMBER = 20; // the serial port is almost surely less than this number
1765  std::vector<bool> deviceExists(MAX_SERIAL_PORT_NUMBER);
1766  std::fill(begin(deviceExists), end(deviceExists), false);
1767  std::vector<std::future<void>> tasks;
1768  for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++)
1769  {
1770  const char* dev = ndiSerialDeviceName(i);
1771  if (dev != nullptr)
1772  {
1773  LOG_DEBUG("Testing serial port: " << dev);
1774  std::string devName = std::string(dev);
1775  std::future<void> result = std::async([this, i, &deviceExists, devName]()
1776  {
1777  int errnum = ndiSerialProbe(devName.c_str(), this->CheckDSR);
1778  LOG_DEBUG("Serial port " << devName << " probe error (" << errnum << "): " << ndiErrorString(errnum));
1779  if (errnum == NDI_OKAY)
1780  {
1781  deviceExists[i] = true;
1782  }
1783  });
1784  tasks.push_back(std::move(result));
1785  }
1786  }
1787  for (std::vector<std::future<void>>::size_type i = 0; i < tasks.size(); i++)
1788  {
1789  tasks[i].wait();
1790  }
1791  for (int i = 0; i < MAX_SERIAL_PORT_NUMBER; i++)
1792  {
1793  // use first device found
1794  if (deviceExists[i] == true)
1795  {
1796  const char* devicename = ndiSerialDeviceName(i);
1797  this->SerialPort = i + 1;
1798  if (this->LeaveDeviceOpenAfterProbe)
1799  {
1800  this->Device = ndiOpenSerial(devicename);
1801  }
1802  return PLUS_SUCCESS;
1803  }
1804  }
1805 
1806  return PLUS_FAIL;
1807 }
1808 #endif
DataSourceContainer::const_iterator DataSourceContainerConstIterator
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
vtkStandardNewMacro(vtkPlusNDITracker)
PlusStatus SendSromToTracker(const NdiToolDescriptor &toolDescriptor)
PlusStatus InternalStartRecording()
NdiToolDescriptorsType NdiToolDescriptors
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
std::string Command(const char *format,...)
PlusStatus InternalUpdate()
Phidget_MeshMode mode
Definition: phidget22.h:1332
void LogVolumeList(int selectedVolume, vtkPlusLogger::LogLevelType logLevel)
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
std::string SerialDevice
void LogVolumeListSFLIST(unsigned int numVolumes, int selectedVolume, vtkPlusLogger::LogLevelType logLevel)
virtual std::string GetDeviceId() const
PlusStatus SelectDataAveraging()
virtual PlusStatus ToolTimeStampedUpdate(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, unsigned long frameNumber, double unfilteredtimestamp, const igsioFieldMapType *customFields=NULL)
PlusStatus Beep(int n)
for i
double AcquisitionRate
PlusStatus InternalConnectSerial()
uint32_t FirmwareMajorRevision
#define PLUS_FAIL
Definition: PlusCommon.h:43
static int ConvertBaudToNDIEnum(int baudRate)
virtual std::string GetSdkVersion()
PlusStatus InternalConnectNetwork()
static vtkPlusConfig * GetInstance()
PlusStatus InternalStopRecording()
uint32_t timeoutMs
Definition: phidget22.h:1277
void SetCustomProperty(const std::string &propertyName, const std::string &propertyValue)
iter
Definition: algo3.m:29
PlusStatus SelectTrackingFrequency()
PlusStatus InternalDisconnect()
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
virtual uint32_t GetFirmwareMinorRevision()
PlusStatus SelectMeasurementVolume()
PlusStatus ClearVirtualSromInTracker(NdiToolDescriptor &toolDescriptor)
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
void PrintSelf(ostream &os, vtkIndent indent)
PlusStatus InternalConnect()
int * state
Definition: phidget22.h:3207
virtual PlusStatus StopRecording()
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
PlusStatus SelectMeasurementVolumeDeprecated()
PlusStatus ReadSromFromFile(NdiToolDescriptor &toolDescriptor, const char *filename)
virtual uint32_t GetFirmwareMajorRevision()
static std::string DATA_SOURCE_TYPE_TOOL_TAG
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
PlusStatus SelectTrackingFrequencyDeprecated()
bool StartThreadForInternalUpdates
DataSourceContainerConstIterator GetToolIteratorBegin() const
char CommandReply[VTK_NDI_REPLY_LEN]
std::string NetworkHostname
vtkIGSIORecursiveCriticalSection * CommandMutex
PlusStatus GetFirmwareRevision()
PlusStatus UpdatePortHandle(NdiToolDescriptor &toolDescriptor)
std::string GetDeviceSetConfigurationPath(const std::string &subPath)
uint32_t FirmwareMinorRevision
std::string GetToolReferenceFrameName() const
PlusStatus SetToolLED(const char *portName, int led, LedState state)
unsigned long LastFrameNumber
PlusStatus EnableToolPorts()
PlusStatus GetTool(const char *aToolSourceId, vtkPlusDataSource *&aTool) const
DataSourceContainerConstIterator GetToolIteratorEnd() const
PlusStatus CloseDevice(ndicapi *&device)
#define VTK_NDI_REPLY_LEN
Interface class for Northern Digital's tracking devices.
Interface to a 3D positioning tool, video source, or generalized data stream.