PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
vtkPlusPhidgetSpatialTracker.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
7 #include "PlusConfigure.h"
9 
10 #include "MadgwickAhrsAlgo.h"
11 #include "MahonyAhrsAlgo.h"
12 #include "PlusMath.h"
13 #include "vtkMath.h"
14 #include "vtkMatrix4x4.h"
15 #include "vtkObjectFactory.h"
16 #include "vtkPlusDataSource.h"
17 #include "vtkTransform.h"
18 #include "vtkXMLDataElement.h"
19 #include "vtksys/SystemTools.hxx"
20 #include <math.h>
21 #include <sstream>
22 
23 #include <phidget22.h>
24 
25 static const int SERIAL_NUMBER_UNDEFINED = -1;
26 
28 
29 class PhidgetSpatialCallbackClass
30 {
31 public:
32 
33  //callback that will run if the Spatial is attached to the computer
34  static void CCONV AttachHandler(PhidgetHandle spatial, void* trackerPtr)
35  {
36  int serialNo = 0;
37  Phidget_getDeviceSerialNumber(spatial, &serialNo);
39  LOG_DEBUG("Phidget spatial sensor attached to device " << tracker->GetDeviceId() << ". Serial number: " << serialNo);
40  }
41 
42  //callback that will run if the Spatial is detached from the computer
43  static void CCONV DetachHandler(PhidgetHandle spatial, void* trackerPtr)
44  {
45  int serialNo;
46  Phidget_getDeviceSerialNumber(spatial, &serialNo);
47  LOG_DEBUG("Phidget spatial sensor detached: " << serialNo);
48  }
49 
50  //callback that will run if the Spatial generates an error
51  static void CCONV ErrorHandler(PhidgetHandle spatial, void* trackerPtr, Phidget_ErrorEventCode ErrorCode, const char* unknown)
52  {
53  LOG_ERROR("Phidget spatial sensor error: " << ErrorCode << " (" << unknown << ")");
54  }
55 
56  //callback that is called when a data sample is acquired
57  static void CCONV SpatialDataHandler(PhidgetSpatialHandle spatial, void* trackerPtr,
58  const double acceleration[3], const double angularRate[3], const double magneticField[3], double timeTrackerMsec)
59  {
61  if (! tracker->IsRecording())
62  {
63  // Received phidget tracking data when not tracking
64  return;
65  }
66 
67  if (!tracker->TrackerTimeToSystemTimeComputed)
68  {
69  const double timeSystemSec = vtkIGSIOAccurateTimer::GetSystemTime();
70  tracker->TrackerTimeToSystemTimeSec = timeSystemSec - timeTrackerMsec/1000.0;
71  tracker->TrackerTimeToSystemTimeComputed = true;
72  }
73 
74  const double timeSystemSec = timeTrackerMsec/1000.0 + tracker->TrackerTimeToSystemTimeSec;
75  if (tracker->AccelerometerTool != NULL)
76  {
77  tracker->LastAccelerometerToTrackerTransform->Identity();
78  tracker->LastAccelerometerToTrackerTransform->SetElement(0, 3, acceleration[0]);
79  tracker->LastAccelerometerToTrackerTransform->SetElement(1, 3, acceleration[1]);
80  tracker->LastAccelerometerToTrackerTransform->SetElement(2, 3, acceleration[2]);
81  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->AccelerometerTool->GetId(), tracker->LastAccelerometerToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
82  }
83  if (tracker->GyroscopeTool != NULL)
84  {
85  tracker->LastGyroscopeToTrackerTransform->Identity();
86  tracker->LastGyroscopeToTrackerTransform->SetElement(0, 3, angularRate[0]);
87  tracker->LastGyroscopeToTrackerTransform->SetElement(1, 3, angularRate[1]);
88  tracker->LastGyroscopeToTrackerTransform->SetElement(2, 3, angularRate[2]);
89  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->GyroscopeTool->GetId(), tracker->LastGyroscopeToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
90  }
91  if (tracker->MagnetometerTool != NULL)
92  {
93  if (magneticField[0] > 1e100)
94  {
95  // magnetometer data is not available, use the last transform with an invalid status to not have any missing transform
96  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->MagnetometerTool->GetId(), tracker->LastMagnetometerToTrackerTransform, TOOL_INVALID, timeSystemSec, timeSystemSec);
97  }
98  else
99  {
100  // magnetometer data is valid
101  tracker->LastMagnetometerToTrackerTransform->Identity();
102  tracker->LastMagnetometerToTrackerTransform->SetElement(0, 3, magneticField[0]);
103  tracker->LastMagnetometerToTrackerTransform->SetElement(1, 3, magneticField[1]);
104  tracker->LastMagnetometerToTrackerTransform->SetElement(2, 3, magneticField[2]);
105  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->MagnetometerTool->GetId(), tracker->LastMagnetometerToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
106  }
107  }
108 
109  if (tracker->TiltSensorTool != NULL)
110  {
111  // Compose matrix that transforms the x axis to the input vector by rotations around two orthogonal axes
112  vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
113 
114  double downVector_Sensor[4] = {acceleration[0], acceleration[1], acceleration[2], 0}; // provided by the sensor
115  vtkMath::Normalize(downVector_Sensor);
116 
117  igsioMath::ConstrainRotationToTwoAxes(downVector_Sensor, tracker->TiltSensorWestAxisIndex, tracker->LastTiltSensorToTrackerTransform);
118 
119  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->TiltSensorTool->GetId(), tracker->LastTiltSensorToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
120  }
121 
122  if (tracker->OrientationSensorTool != NULL)
123  {
124  if (magneticField[0] > 1e100)
125  {
126  // magnetometer data is not available, use the last transform with an invalid status to not have any missing transform
127  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->OrientationSensorTool->GetId(), tracker->LastOrientationSensorToTrackerTransform, TOOL_INVALID, timeSystemSec, timeSystemSec);
128  }
129  else
130  {
131  // magnetometer data is valid
132 
133  //LOG_TRACE("samplingTime(msec)="<<1000.0*timeSinceLastAhrsUpdateSec<<", packetCount="<<count);
134  //LOG_TRACE("gyroX="<<std::fixed<<std::setprecision(2)<<std::setw(6)<<angularRate[0]<<", gyroY="<<angularRate[1]<<", gyroZ="<<angularRate[2]);
135  //LOG_TRACE("magX="<<std::fixed<<std::setprecision(2)<<std::setw(6)<<magneticField[0]<<", magY="<<magneticField[1]<<", magZ="<<magneticField[2]);
136 
137  if (tracker->AhrsUseMagnetometer)
138  {
139  tracker->AhrsAlgo->UpdateWithTimestamp(
140  vtkMath::RadiansFromDegrees(angularRate[0]), vtkMath::RadiansFromDegrees(angularRate[1]), vtkMath::RadiansFromDegrees(angularRate[2]),
142  magneticField[0], magneticField[1], magneticField[2], timeSystemSec);
143  }
144  else
145  {
146  tracker->AhrsAlgo->UpdateIMUWithTimestamp(
147  vtkMath::RadiansFromDegrees(angularRate[0]), vtkMath::RadiansFromDegrees(angularRate[1]), vtkMath::RadiansFromDegrees(angularRate[2]),
148  acceleration[0], acceleration[1], acceleration[2], timeSystemSec);
149  }
150 
151 
152  double rotQuat[4] = { 0 };
153  tracker->AhrsAlgo->GetOrientation(rotQuat[0], rotQuat[1], rotQuat[2], rotQuat[3]);
154 
155  double rotMatrix[3][3] = { 0 };
156  vtkMath::QuaternionToMatrix3x3(rotQuat, rotMatrix);
157 
158  for (int c = 0; c < 3; c++)
159  {
160  for (int r = 0; r < 3; r++)
161  {
162  tracker->LastOrientationSensorToTrackerTransform->SetElement(r, c, rotMatrix[r][c]);
163  }
164  }
165 
166  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->OrientationSensorTool->GetId(), tracker->LastOrientationSensorToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
167  }
168  }
169  if (tracker->FilteredTiltSensorTool != NULL)
170  {
171  tracker->FilteredTiltSensorAhrsAlgo->UpdateIMUWithTimestamp(
172  vtkMath::RadiansFromDegrees(angularRate[0]), vtkMath::RadiansFromDegrees(angularRate[1]), vtkMath::RadiansFromDegrees(angularRate[2]),
173  acceleration[0], acceleration[1], acceleration[2], timeSystemSec);
174 
175  double rotQuat[4] = { 0 };
176  tracker->AhrsAlgo->GetOrientation(rotQuat[0], rotQuat[1], rotQuat[2], rotQuat[3]);
177 
178  double rotMatrix[3][3] = { 0 };
179  vtkMath::QuaternionToMatrix3x3(rotQuat, rotMatrix);
180 
181  double filteredDownVector_Sensor[4] = { rotMatrix[2][0], rotMatrix[2][1], rotMatrix[2][2], 0 };
182  vtkMath::Normalize(filteredDownVector_Sensor);
183 
184  igsioMath::ConstrainRotationToTwoAxes(filteredDownVector_Sensor, tracker->FilteredTiltSensorWestAxisIndex, tracker->LastFilteredTiltSensorToTrackerTransform);
185 
186  tracker->ToolTimeStampedUpdateWithoutFiltering(tracker->FilteredTiltSensorTool->GetId(), tracker->LastFilteredTiltSensorToTrackerTransform, TOOL_OK, timeSystemSec, timeSystemSec);
187 
188  // write back the results to the FilteredTiltSensor_AHRS algorithm
189  for (int c = 0; c < 3; c++)
190  {
191  for (int r = 0; r < 3; r++)
192  {
193  rotMatrix[r][c] = tracker->LastFilteredTiltSensorToTrackerTransform->GetElement(r, c);
194  }
195  }
196  double filteredTiltSensorRotQuat[4] = { 0 };
197  vtkMath::Matrix3x3ToQuaternion(rotMatrix, filteredTiltSensorRotQuat);
198  tracker->FilteredTiltSensorAhrsAlgo->SetOrientation(filteredTiltSensorRotQuat[0], filteredTiltSensorRotQuat[1], filteredTiltSensorRotQuat[2], filteredTiltSensorRotQuat[3]);
199  }
200 
201  } // SpatialDataHandler
202 
203 }; // PhidgetSpatialCallbackClass
204 
205 //-------------------------------------------------------------------------
207 {
208  this->SpatialDeviceHandle = NULL;
209  this->SerialNumber = SERIAL_NUMBER_UNDEFINED;
210  this->TrackerTimeToSystemTimeSec = 0;
211  this->TrackerTimeToSystemTimeComputed = false;
212  this->ZeroGyroscopeOnConnect = false;
213 
214  this->AccelerometerTool = NULL;
215  this->GyroscopeTool = NULL;
216  this->MagnetometerTool = NULL;
217  this->TiltSensorTool = NULL;
218  this->FilteredTiltSensorTool = NULL;
219  this->OrientationSensorTool = NULL;
220 
221  this->LastAccelerometerToTrackerTransform = vtkMatrix4x4::New();
222  this->LastGyroscopeToTrackerTransform = vtkMatrix4x4::New();
223  this->LastMagnetometerToTrackerTransform = vtkMatrix4x4::New();
224  this->LastTiltSensorToTrackerTransform = vtkMatrix4x4::New();
225  this->LastFilteredTiltSensorToTrackerTransform = vtkMatrix4x4::New();
226  this->LastOrientationSensorToTrackerTransform = vtkMatrix4x4::New();
227  this->FilteredTiltSensorWestAxisIndex = 1;
228  this->TiltSensorWestAxisIndex = 1; // the sensor plane is horizontal (axis 2 points down, axis 1 points West)
229 
230  // Set up the AHRS algorithm used by the orientation sensor tool
231  this->AhrsAlgo = new MadgwickAhrsAlgo;
232  this->AhrsUseMagnetometer = true;
233  this->AhrsAlgorithmGain[0] = 1.5; // proportional
234  this->AhrsAlgorithmGain[1] = 0.0; // integral
235  this->AhrsLastUpdateTime = -1;
236 
237  // set up the AHRS algorithm used by the FilteredTiltSensor sensor tool
238  this->FilteredTiltSensorAhrsAlgo = new MadgwickAhrsAlgo;
239  this->FilteredTiltSensorAhrsAlgorithmGain[0] = 1.5; // proportional
240  this->FilteredTiltSensorAhrsAlgorithmGain[1] = 0.0; // integral
241  this->FilteredTiltSensorAhrsLastUpdateTime = -1;
242 
244 
245  this->AcquisitionRate = 125; // set to the maximum speed by default
246 
248  {
249  this->CompassCorrectionParameters[i] = 0;
250  }
251 
252  // No need for StartThreadForInternalUpdates, as we are notified about each new frame through a callback function
253 }
254 
255 //-------------------------------------------------------------------------
257 {
258  if (this->Recording)
259  {
260  this->StopRecording();
261  }
262  this->LastAccelerometerToTrackerTransform->Delete();
263  this->LastAccelerometerToTrackerTransform = NULL;
264  this->LastGyroscopeToTrackerTransform->Delete();
265  this->LastGyroscopeToTrackerTransform = NULL;
266  this->LastMagnetometerToTrackerTransform->Delete();
267  this->LastMagnetometerToTrackerTransform = NULL;
268  this->LastTiltSensorToTrackerTransform->Delete();
269  this->LastTiltSensorToTrackerTransform = NULL;
270  this->LastFilteredTiltSensorToTrackerTransform->Delete();
271  this->LastFilteredTiltSensorToTrackerTransform = NULL;
272  this->LastOrientationSensorToTrackerTransform->Delete();
273  this->LastOrientationSensorToTrackerTransform = NULL;
274 }
275 
276 //-------------------------------------------------------------------------
277 void vtkPlusPhidgetSpatialTracker::PrintSelf(ostream& os, vtkIndent indent)
278 {
279  Superclass::PrintSelf(os, indent);
280 
281  if (this->SpatialDeviceHandle != NULL)
282  {
283  const char* deviceType = NULL;
284  Phidget_getDeviceClassName((PhidgetHandle)this->SpatialDeviceHandle, &deviceType);
285  os << "Device type: " << deviceType << std::endl;
286  int serialNo = 0;
287  Phidget_getDeviceSerialNumber((PhidgetHandle)this->SpatialDeviceHandle, &serialNo);
288  os << "Serial Number: " << serialNo << std::endl;
289  int version = 0;
290  Phidget_getDeviceVersion((PhidgetHandle)this->SpatialDeviceHandle, &version);
291  os << "Version: " << version << std::endl;
292  int numAccelAxes = 0;
293  PhidgetAccelerometer_getAxisCount((PhidgetAccelerometerHandle)this->SpatialDeviceHandle, &numAccelAxes);
294  os << "Number of Accel Axes: " << numAccelAxes << std::endl;
295  int numGyroAxes = 0;
296  PhidgetGyroscope_getAxisCount((PhidgetGyroscopeHandle)this->SpatialDeviceHandle, &numGyroAxes);
297  os << "Number of Gyro Axes: " << numGyroAxes << std::endl;
298  int numCompassAxes = 0;
299  PhidgetMagnetometer_getAxisCount((PhidgetMagnetometerHandle)this->SpatialDeviceHandle, &numCompassAxes);
300  os << "Number of Compass Axes: " << numCompassAxes << std::endl;
301  uint32_t dataRateMax = 0;
302  PhidgetSpatial_getMaxDataInterval((PhidgetSpatialHandle)this->SpatialDeviceHandle, &dataRateMax);
303  os << "Maximum data rate: " << dataRateMax << std::endl;
304  uint32_t dataRateMin = 0;
305  PhidgetSpatial_getMinDataInterval((PhidgetSpatialHandle)this->SpatialDeviceHandle, &dataRateMin);
306  os << "Minimum data rate: " << dataRateMin << std::endl;
307  uint32_t dataRate = 0;
308  PhidgetSpatial_getDataInterval((PhidgetSpatialHandle)this->SpatialDeviceHandle, &dataRate);
309  os << "Current data rate: " << dataRate << std::endl;
310  }
311  else
312  {
313  os << "Spatial device is not available" << std::endl;
314  }
315 
316 }
317 
318 //-------------------------------------------------------------------------
320 {
321  LOG_TRACE("vtkPlusPhidgetSpatialTracker::Connect");
322 
323  this->AccelerometerTool = NULL;
324  GetToolByPortName("Accelerometer", this->AccelerometerTool);
325 
326  this->GyroscopeTool = NULL;
327  GetToolByPortName("Gyroscope", this->GyroscopeTool);
328 
329  this->MagnetometerTool = NULL;
330  GetToolByPortName("Magnetometer", this->MagnetometerTool);
331 
332  this->TiltSensorTool = NULL;
333  GetToolByPortName("TiltSensor", this->TiltSensorTool);
334 
335  this->FilteredTiltSensorTool = NULL;
336  GetToolByPortName("FilteredTiltSensor", this->FilteredTiltSensorTool);
337 
338  this->OrientationSensorTool = NULL;
339  GetToolByPortName("OrientationSensor", this->OrientationSensorTool);
340 
341 
342 
343 
344 
345 
346  //Create the communicator object to the PhidgetSpatial device
347  //PhidgetSpatial_create((PhidgetSpatialHandle*)(&this->SpatialDeviceHandle));
348 
349 
350  PhidgetReturnCode res = PhidgetSpatial_create((PhidgetSpatialHandle*)&this->SpatialDeviceHandle);
351  if (res != EPHIDGET_OK)
352  {
353  LOG_ERROR("Device " << this->GetDeviceId() << " failed to create Spatial channel");
354  return PLUS_FAIL;
355  }
356 
357  //Set the handlers to be run when the device is plugged in or opened from software, unplugged or closed from software, or generates an error.
358  Phidget_setOnAttachHandler((PhidgetHandle)this->SpatialDeviceHandle, PhidgetSpatialCallbackClass::AttachHandler, this);
359  Phidget_setOnDetachHandler((PhidgetHandle)this->SpatialDeviceHandle, PhidgetSpatialCallbackClass::DetachHandler, this);
360  Phidget_setOnErrorHandler((PhidgetHandle)this->SpatialDeviceHandle, PhidgetSpatialCallbackClass::ErrorHandler, this);
361 
362  if (this->SerialNumber != SERIAL_NUMBER_UNDEFINED)
363  {
364  Phidget_setDeviceSerialNumber((PhidgetHandle)this->SpatialDeviceHandle, this->SerialNumber);
365  }
366 
367  //Registers a callback that will run according to the set data rate that will return the spatial data changes
368  PhidgetSpatial_setOnSpatialDataHandler((PhidgetSpatialHandle)this->SpatialDeviceHandle, PhidgetSpatialCallbackClass::SpatialDataHandler, this);
369 
370  //This will initiate the SystemTime (time reference in Plus) to TrackerTime (time reference of the internal clock of the device) offset computation
371  this->TrackerTimeToSystemTimeSec = 0;
372  this->TrackerTimeToSystemTimeComputed = false;
373 
374  //get the program to wait for a spatial device to be attached
375  LOG_DEBUG("Waiting for phidget spatial device to be attached...");
376  if ((res = Phidget_openWaitForAttachment((PhidgetHandle)this->SpatialDeviceHandle, 10000)) != EPHIDGET_OK)
377  {
378  const char* err = NULL;
379  Phidget_getErrorDescription(res, &err);
380  if (this->SerialNumber == SERIAL_NUMBER_UNDEFINED)
381  {
382  LOG_ERROR("Device " << this->GetDeviceId() << " cannot connect to PhidgetSpatial hardware device (serial number: any). Problem waiting for attachment (" << err << ")");
383  }
384  else
385  {
386  LOG_ERROR("Device " << this->GetDeviceId() << " cannot connect to PhidgetSpatial hardware device (serial number: " << this->SerialNumber << "). Problem waiting for attachment (" << err << ")");
387  }
388  return PLUS_FAIL;
389  }
390 
391  if (this->SerialNumber == SERIAL_NUMBER_UNDEFINED)
392  {
393  // The user has not specified a serial number, so let him know which device we are actually connected to
394  int32_t serialNo = 0;
395  Phidget_getDeviceSerialNumber((PhidgetHandle)this->SpatialDeviceHandle, &serialNo);
396  LOG_INFO("Phidget spatial sensor attached to device " << this->GetDeviceId() << ". Serial number: " << serialNo);
397  }
398 
399  //Set the data rate for the spatial events
400  uint32_t requestedUserDataRateMsec = int(1000.0 / this->GetAcquisitionRate());
401  // Allowed userDataRateMsec values: 8, 16, 32, 64, 128, 256, 512, 1000, set the closest one
402  if (requestedUserDataRateMsec < 12) { requestedUserDataRateMsec = 8; }
403  else if (requestedUserDataRateMsec < 24) { requestedUserDataRateMsec = 16; }
404  else if (requestedUserDataRateMsec < 48) { requestedUserDataRateMsec = 32; }
405  else if (requestedUserDataRateMsec < 96) { requestedUserDataRateMsec = 64; }
406  else if (requestedUserDataRateMsec < 192) { requestedUserDataRateMsec = 128; }
407  else if (requestedUserDataRateMsec < 384) { requestedUserDataRateMsec = 256; }
408  else if (requestedUserDataRateMsec < 756) { requestedUserDataRateMsec = 512; }
409  else { requestedUserDataRateMsec = 1000; }
410  PhidgetSpatial_setDataInterval((PhidgetSpatialHandle)this->SpatialDeviceHandle, requestedUserDataRateMsec);
411 
412  uint32_t userDataRateMsec = 0;
413  PhidgetSpatial_getDataInterval((PhidgetSpatialHandle)this->SpatialDeviceHandle, &userDataRateMsec);
414 
415  LOG_DEBUG("DataRate (msec):" << userDataRateMsec << " (requested: " << requestedUserDataRateMsec << ")");
416 
417  // Initialize AHRS algorithm
418  this->AhrsAlgo->SetSampleFreqHz(1000.0 / userDataRateMsec); // more accurate value will be set at each update step anyway
419  this->AhrsAlgo->SetGain(this->AhrsAlgorithmGain[0], this->AhrsAlgorithmGain[1]);
420  this->FilteredTiltSensorAhrsAlgo->SetSampleFreqHz(1000.0 / userDataRateMsec);
421  this->FilteredTiltSensorAhrsAlgo->SetGain(this->FilteredTiltSensorAhrsAlgorithmGain[0], this->FilteredTiltSensorAhrsAlgorithmGain[1]);
422 
423  // Set compass correction parameters
425  {
426  if (PHIDGET_NUMBER_OF_COMPASS_CORRECTION_PARAMETERS == 13) // we expect exactly 13 parameters here
427  {
428  double magField = this->CompassCorrectionParameters[0];
429  double offset0 = this->CompassCorrectionParameters[1];
430  double offset1 = this->CompassCorrectionParameters[2];
431  double offset2 = this->CompassCorrectionParameters[3];
432  double gain0 = this->CompassCorrectionParameters[4];
433  double gain1 = this->CompassCorrectionParameters[5];
434  double gain2 = this->CompassCorrectionParameters[6];
435  double T0 = this->CompassCorrectionParameters[7];
436  double T1 = this->CompassCorrectionParameters[8];
437  double T2 = this->CompassCorrectionParameters[9];
438  double T3 = this->CompassCorrectionParameters[10];
439  double T4 = this->CompassCorrectionParameters[11];
440  double T5 = this->CompassCorrectionParameters[12];
441  PhidgetMagnetometer_setCorrectionParameters((PhidgetMagnetometerHandle)this->SpatialDeviceHandle,
442  magField, offset0, offset1, offset2, gain0, gain1, gain2, T0, T1, T2, T3, T4, T5);
443  }
444  else
445  {
446  LOG_ERROR("Failed to set compass correction parameters: 13 parameters were expected");
447  }
448  }
449  else
450  {
451  LOG_DEBUG("No compass correction parameters are specified");
452  }
453 
454  if (this->ZeroGyroscopeOnConnect)
455  {
456  ZeroGyroscope();
457  }
458 
459  return PLUS_SUCCESS;
460 }
461 
462 //-------------------------------------------------------------------------
464 {
465  LOG_TRACE("vtkPlusPhidgetSpatialTracker::Disconnect");
466  this->StopRecording();
467  Phidget_close((PhidgetHandle)this->SpatialDeviceHandle);
468  Phidget_delete((PhidgetHandle*)&this->SpatialDeviceHandle);
469  this->SpatialDeviceHandle = NULL;
470  this->AccelerometerTool = NULL;
471  this->GyroscopeTool = NULL;
472  this->MagnetometerTool = NULL;
473  this->TiltSensorTool = NULL;
474  this->FilteredTiltSensorTool = NULL;
475  this->OrientationSensorTool = NULL;
476  return PLUS_SUCCESS;
477 }
478 
479 //-------------------------------------------------------------------------
481 {
482  LOG_TRACE("vtkPlusPhidgetSpatialTracker::Probe");
483 
484  return PLUS_SUCCESS;
485 }
486 
487 //-------------------------------------------------------------------------
489 {
490  LOG_TRACE("vtkPlusPhidgetSpatialTracker::InternalStartRecording");
491  return PLUS_SUCCESS;
492 }
493 
494 //-------------------------------------------------------------------------
496 {
497  LOG_TRACE("vtkPlusPhidgetSpatialTracker::InternalStopRecording");
498  return PLUS_SUCCESS;
499 }
500 
501 //----------------------------------------------------------------------------
502 PlusStatus vtkPlusPhidgetSpatialTracker::ReadConfiguration(vtkXMLDataElement* rootConfigElement)
503 {
504  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement);
505 
506  XML_READ_BOOL_ATTRIBUTE_OPTIONAL(ZeroGyroscopeOnConnect, deviceConfig);
507 
508  XML_READ_SCALAR_ATTRIBUTE_OPTIONAL(int, SerialNumber, deviceConfig);
509 
510  int tiltSensorWestAxisIndex = 0;
511  if (deviceConfig->GetScalarAttribute("TiltSensorWestAxisIndex", tiltSensorWestAxisIndex))
512  {
513  if (tiltSensorWestAxisIndex < 0 || tiltSensorWestAxisIndex > 2)
514  {
515  LOG_ERROR("TiltSensorWestAxisIndex is invalid. Specified value: " << tiltSensorWestAxisIndex << ". Valid values: 0, 1, 2. Keep using the default value: "
516  << this->TiltSensorWestAxisIndex);
517  }
518  else
519  {
520  this->TiltSensorWestAxisIndex = tiltSensorWestAxisIndex;
521  }
522  }
523 
524  int FilteredTiltSensorWestAxisIndex = 0;
525  if (deviceConfig->GetScalarAttribute("FilteredTiltSensorWestAxisIndex", FilteredTiltSensorWestAxisIndex))
526  {
527  if (FilteredTiltSensorWestAxisIndex < 0 || FilteredTiltSensorWestAxisIndex > 2)
528  {
529  LOG_ERROR("FilteredTiltSensorWestAxisIndex is invalid. Specified value: " << FilteredTiltSensorWestAxisIndex << ". Valid values: 0, 1, 2. Keep using the default value: "
530  << this->FilteredTiltSensorWestAxisIndex);
531  }
532  else
533  {
534  this->FilteredTiltSensorWestAxisIndex = FilteredTiltSensorWestAxisIndex;
535  }
536  }
537 
538  XML_READ_VECTOR_ATTRIBUTE_OPTIONAL(double, 2, AhrsAlgorithmGain, deviceConfig);
539  XML_READ_VECTOR_ATTRIBUTE_OPTIONAL(double, 2, FilteredTiltSensorAhrsAlgorithmGain, deviceConfig);
540 
541  const char* ahrsAlgoName = deviceConfig->GetAttribute("AhrsAlgorithm");
542  if (ahrsAlgoName != NULL)
543  {
544  if (STRCASECMP("MADGWICK_MARG", ahrsAlgoName) == 0 || STRCASECMP("MADGWICK_IMU", ahrsAlgoName) == 0)
545  {
546  if (dynamic_cast<MadgwickAhrsAlgo*>(this->AhrsAlgo) == 0)
547  {
548  // not the requested type
549  // delete the old algo and create a new one with the correct type
550  delete this->AhrsAlgo;
551  this->AhrsAlgo = new MadgwickAhrsAlgo;
552  }
553  if (STRCASECMP("MADGWICK_MARG", ahrsAlgoName) == 0)
554  {
555  this->AhrsUseMagnetometer = true;
556  }
557  else
558  {
559  this->AhrsUseMagnetometer = false;
560  }
561  }
562  else if (STRCASECMP("MAHONY_MARG", ahrsAlgoName) == 0 || STRCASECMP("MAHONY_IMU", ahrsAlgoName) == 0)
563  {
564  if (dynamic_cast<MahonyAhrsAlgo*>(this->AhrsAlgo) == 0)
565  {
566  // not the requested type
567  // delete the old algo and create a new one with the correct type
568  delete this->AhrsAlgo;
569  this->AhrsAlgo = new MahonyAhrsAlgo;
570  }
571  if (STRCASECMP("MAHONY_MARG", ahrsAlgoName) == 0)
572  {
573  this->AhrsUseMagnetometer = true;
574  }
575  else
576  {
577  this->AhrsUseMagnetometer = false;
578  }
579  }
580  else
581  {
582  LOG_ERROR("Unable to recognize AHRS algorithm type: " << ahrsAlgoName << ". Supported types: MADGWICK_MARG, MAHONY_MARG, MADGWICK_IMU, MAHONY_IMU");
583  return PLUS_FAIL;
584  }
585  }
586  const char* FilteredTiltSensorAhrsAlgoName = deviceConfig->GetAttribute("FilteredTiltSensorAhrsAlgorithm");
587  if (FilteredTiltSensorAhrsAlgoName != NULL)
588  {
589  if (STRCASECMP("MADGWICK_IMU", FilteredTiltSensorAhrsAlgoName) == 0)
590  {
591  if (dynamic_cast<MadgwickAhrsAlgo*>(this->FilteredTiltSensorAhrsAlgo) == 0)
592  {
593  // not the requested type
594  // delete the old algo and create a new one with the correct type
595  delete this->FilteredTiltSensorAhrsAlgo;
596  this->FilteredTiltSensorAhrsAlgo = new MadgwickAhrsAlgo;
597  }
598  }
599  else if (STRCASECMP("MAHONY_IMU", FilteredTiltSensorAhrsAlgoName) == 0)
600  {
601  if (dynamic_cast<MahonyAhrsAlgo*>(this->FilteredTiltSensorAhrsAlgo) == 0)
602  {
603  // not the requested type
604  // delete the old algo and create a new one with the correct type
605  delete this->FilteredTiltSensorAhrsAlgo;
606  this->FilteredTiltSensorAhrsAlgo = new MahonyAhrsAlgo;
607  }
608  }
609  else
610  {
611  LOG_ERROR("Unable to recognize AHRS algorithm type for Filtered Tilt: " << FilteredTiltSensorAhrsAlgoName << ". Supported types: MADGWICK_IMU, MAHONY_IMU");
612  return PLUS_FAIL;
613  }
614  }
615 
616  XML_READ_VECTOR_ATTRIBUTE_EXACT_OPTIONAL(double, PHIDGET_NUMBER_OF_COMPASS_CORRECTION_PARAMETERS, CompassCorrectionParameters, deviceConfig);
617 
618  return PLUS_SUCCESS;
619 }
620 
621 //----------------------------------------------------------------------------
623 {
624  XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement);
625 
626  if (this->ZeroGyroscopeOnConnect)
627  {
628  deviceConfig->SetAttribute("ZeroGyroscopeOnConnect", "TRUE");
629  }
630 
631  if (this->SerialNumber != SERIAL_NUMBER_UNDEFINED)
632  {
633  deviceConfig->SetIntAttribute("SerialNumber", this->SerialNumber);
634  }
635 
636  if (this->TiltSensorTool)
637  {
638  deviceConfig->SetIntAttribute("TiltSensorWestAxisIndex", this->TiltSensorWestAxisIndex);
639  }
640 
641  if (this->FilteredTiltSensorTool)
642  {
643  deviceConfig->SetIntAttribute("FilteredTiltSensorWestAxisIndex", this->FilteredTiltSensorWestAxisIndex);
644  if (this->FilteredTiltSensorAhrsAlgorithmGain[1] == 0.0)
645  {
646  // if the second gain parameter is zero then just write the first value
647  deviceConfig->SetDoubleAttribute("FilteredTiltSensorAhrsAlgorithmGain", this->FilteredTiltSensorAhrsAlgorithmGain[0]);
648  }
649  else
650  {
651  deviceConfig->SetVectorAttribute("FilteredTiltSensorAhrsAlgorithmGain", 2, this->FilteredTiltSensorAhrsAlgorithmGain);
652  }
653 
654  if (dynamic_cast<MadgwickAhrsAlgo*>(this->FilteredTiltSensorAhrsAlgo) != 0)
655  {
656  deviceConfig->SetAttribute("FilteredTiltSensorAhrsAlgorithm", "MADGWICK_IMU");
657  }
658  else if (dynamic_cast<MahonyAhrsAlgo*>(this->FilteredTiltSensorAhrsAlgo) != 0)
659  {
660  deviceConfig->SetAttribute("FilteredTiltSensorAhrsAlgorithm", "MAHONY_IMU");
661  }
662  else
663  {
664  LOG_ERROR("Unknown AHRS algorithm type for Filtered Tilt Sensor. Cannot write name to XML.");
665  }
666  }
667 
668  if (this->OrientationSensorTool)
669  {
670  if (this->AhrsAlgorithmGain[1] == 0.0)
671  {
672  // if the second gain parameter is zero then just write the first value
673  deviceConfig->SetDoubleAttribute("AhrsAlgorithmGain", this->AhrsAlgorithmGain[0]);
674  }
675  else
676  {
677  deviceConfig->SetVectorAttribute("AhrsAlgorithmGain", 2, this->AhrsAlgorithmGain);
678  }
679 
680  if (dynamic_cast<MadgwickAhrsAlgo*>(this->AhrsAlgo) != 0)
681  {
682  if (this->AhrsUseMagnetometer)
683  {
684  deviceConfig->SetAttribute("AhrsAlgorithm", "MADGWICK_MARG");
685  }
686  else
687  {
688  deviceConfig->SetAttribute("AhrsAlgorithm", "MADGWICK_IMU");
689  }
690  }
691  else if (dynamic_cast<MahonyAhrsAlgo*>(this->AhrsAlgo) != 0)
692  {
693  if (this->AhrsUseMagnetometer)
694  {
695  deviceConfig->SetAttribute("AhrsAlgorithm", "MAHONY_MARG");
696  }
697  else
698  {
699  deviceConfig->SetAttribute("AhrsAlgorithm", "MAHONY_IMU");
700  }
701  }
702  else
703  {
704  LOG_ERROR("Unknown AHRS algorithm type. Cannot write name to XML.");
705  }
706  }
707 
709  {
710  deviceConfig->SetVectorAttribute("CompassCorrectionParameters", PHIDGET_NUMBER_OF_COMPASS_CORRECTION_PARAMETERS, this->CompassCorrectionParameters);
711  }
712  else
713  {
714  deviceConfig->RemoveAttribute("CompassCorrectionParameters");
715  }
716 
717  return PLUS_SUCCESS;
718 }
719 
720 //----------------------------------------------------------------------------
722 {
723  LOG_INFO("Zeroing the gyroscope. Keep the sensor stationary for 2 seconds.");
724  PhidgetSpatial_zeroGyro((PhidgetSpatialHandle)this->SpatialDeviceHandle);
725 }
726 
727 //----------------------------------------------------------------------------
729 {
730  return true;
731 }
732 
733 //----------------------------------------------------------------------------
735 {
736  if (this->IsRecording())
737  {
738  ZeroGyroscope();
739  }
740 
741  return PLUS_SUCCESS;
742 }
743 
744 //----------------------------------------------------------------------------
745 /*
746 void vtkPlusPhidgetSpatialTracker::Get3x3RotMatrixFromIMUQuat(double rotMatrix[3][3], AhrsAlgo* AhrsAlgo)
747 {
748  double rotQuat[4]={0};
749  AhrsAlgo->GetOrientation(rotQuat[0],rotQuat[1],rotQuat[2],rotQuat[3]);
750  vtkMath::QuaternionToMatrix3x3(rotQuat, rotMatrix);
751 
752  return;
753 }
754 */
755 
756 //----------------------------------------------------------------------------
758 {
760  {
761  if (this->CompassCorrectionParameters[i] != 0)
762  {
763  return true;
764  }
765  }
766  return false;
767 }
vtkStandardNewMacro(vtkPlusPhidgetSpatialTracker)
virtual void PrintSelf(ostream &os, vtkIndent indent) VTK_OVERRIDE
struct _PhidgetMagnetometer * PhidgetMagnetometerHandle
Definition: phidget22.h:3694
PhidgetReturnCode
Definition: phidget22.h:419
double double double double double double double double double double double double T4
Definition: phidget22.h:3700
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_WRITING(deviceConfig, rootConfigElement)
virtual PlusStatus ToolTimeStampedUpdateWithoutFiltering(const std::string &aToolSourceId, vtkMatrix4x4 *matrix, ToolStatus status, double unfilteredtimestamp, double filteredtimestamp, const igsioFieldMapType *customFields=NULL)
struct _PhidgetAccelerometer * PhidgetAccelerometerHandle
Definition: phidget22.h:3396
int
Definition: phidget22.h:3069
igsioStatus PlusStatus
Definition: PlusCommon.h:40
virtual std::string GetDeviceId() const
struct _PhidgetGyroscope * PhidgetGyroscopeHandle
Definition: phidget22.h:3651
bool RequirePortNameInDeviceSetConfiguration
void PrintSelf(ostream &os, vtkIndent indent)
for i
double AcquisitionRate
double(* angularRate)[3]
Definition: phidget22.h:3659
virtual PlusStatus ReadConfiguration(vtkXMLDataElement *config)
double double double double double double double gain2
Definition: phidget22.h:3700
#define PHIDGET_NUMBER_OF_COMPASS_CORRECTION_PARAMETERS
#define PLUS_FAIL
Definition: PlusCommon.h:43
virtual double GetAcquisitionRate() const
#define CCONV
Definition: phidget22.h:15
struct _Phidget * PhidgetHandle
Definition: phidget22.h:1157
double double double double double double double double double double double double double T5
Definition: phidget22.h:3700
double double double double double double gain1
Definition: phidget22.h:3700
double double double double double double double double double double T2
Definition: phidget22.h:3700
double double double double double double double double double double double T3
Definition: phidget22.h:3700
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PlusStatus GetToolByPortName(const char *aPortName, vtkPlusDataSource *&aSource)
virtual PlusStatus WriteConfiguration(vtkXMLDataElement *config)
double double double double offset2
Definition: phidget22.h:3700
double double offset0
Definition: phidget22.h:3700
double magneticField
Definition: phidget22.h:3700
double double double double double double double double T0
Definition: phidget22.h:3700
double double double double double double double double double T1
Definition: phidget22.h:3700
virtual PlusStatus StopRecording()
double double double double double gain0
Definition: phidget22.h:3700
#define XML_FIND_DEVICE_ELEMENT_REQUIRED_FOR_READING(deviceConfig, rootConfigElement)
Interface for the Phidget 3/3/3 tracker.
virtual bool IsRecording() const
Phidget_ErrorEventCode
Definition: phidget22.h:467
struct _PhidgetSpatial * PhidgetSpatialHandle
Definition: phidget22.h:3745
static const int SERIAL_NUMBER_UNDEFINED
double acceleration
Definition: phidget22.h:3290
double double double offset1
Definition: phidget22.h:3700