|
SpacePoint Fusion plugin 1.0
|
00001 //################################################################################### 00002 //# SpacePoint Fusion Object # 00003 //# Used To Handle a SpacePoint Fusion device # 00004 //# Author : # 00005 //# Aymeric SUTEAU # 00006 //# LISA - ANGERS # 00007 //################################################################################### 00008 00009 00010 /*-------------------------------- HEADER INCLUDE ---------------------------------*/ 00011 #include "Plugin.h" 00012 00013 00014 /*-------------------------- GLOBAL VARIABLES FOR HID ----------------------------*/ 00015 HIDP_CAPS Capabilities; 00016 PSP_DEVICE_INTERFACE_DETAIL_DATA detailData; 00017 HANDLE DeviceHandle, WriteHandle, hEventObject, hDevInfo, hEventObjectWrite, hEventObjectRead; 00018 GUID HidGuid; 00019 OVERLAPPED HIDOverlapped, HIDOverlappedWrite, HIDOverlappedRead; 00020 ULONG Length, Required; 00021 DWORD NumberOfBytesRead, Result; 00022 unsigned char InputReport[21], OutputReport[256]; 00023 bool MyDeviceDetected = FALSE; 00024 int nbReadBytes = 0; 00025 00026 00027 /*-------------------------- CONSTRUCTOR AND DESTRUCTOR ---------------------------*/ 00028 Fusion::Fusion() : Thread() 00029 { 00030 fAcceleration = new float[3]; 00031 iButton = new int[2]; 00032 iRawAxes = new int[11]; 00033 00034 SetConnected(0); // Device not connected yet 00035 SetNbSamples(-1); // Number of maximum samples not defined 00036 SetVID(FUSION_VID); // Define SpacePoint Fusion VID 00037 SetPID(FUSION_PID); // Define SpacePoint Fusion PID 00038 SetDataType(ORIENTATION_DATA); // Define data type we want to retrieve (RAW or ORIENTATION) 00039 00040 // Thread handling 00041 this->start(); 00042 this->isRunning = true; 00043 this->bStatus = false; 00044 } 00045 00046 Fusion::~Fusion() 00047 { 00048 // Thread handling 00049 this->isRunning = false; 00050 this->bStatus = false; 00051 this->stop(); 00052 00053 delete [] fAcceleration; 00054 delete [] iButton; 00055 delete [] iRawAxes; 00056 } 00057 00058 00059 /*------------------------------ GETTERS AND SETTERS ------------------------------*/ 00060 bool Fusion::GetConnected() 00061 { 00062 return this->bConnected; 00063 } 00064 00065 void Fusion::SetConnected(bool status) 00066 { 00067 this->bConnected = status; 00068 } 00069 00070 int Fusion::GetNbSamples() 00071 { 00072 return this->iNbSamples; 00073 } 00074 00075 void Fusion::SetNbSamples(int nbSamples) 00076 { 00077 this->iNbSamples = nbSamples; 00078 } 00079 00080 int Fusion::GetVID() 00081 { 00082 return this->iVID; 00083 } 00084 00085 void Fusion::SetVID(int VID) 00086 { 00087 this->iVID = VID; 00088 } 00089 00090 int Fusion::GetPID() 00091 { 00092 return this->iPID; 00093 } 00094 00095 void Fusion::SetPID(int PID) 00096 { 00097 this->iPID = PID; 00098 } 00099 00100 int Fusion::GetDataType() 00101 { 00102 return this->iDataType; 00103 } 00104 00105 void Fusion::SetDataType(int dataType) 00106 { 00107 this->iDataType = dataType; 00108 } 00109 00110 00111 /*--------------------------------- OTHER METHODS ---------------------------------*/ 00112 bool Fusion::OpenDevice(int dataType) 00113 { 00114 // Set data type we want to retrieve (raw or orientation mode) 00115 SetDataType(dataType); 00116 00117 // Try to connect to SpacePoint Fusion device 00118 SetConnected(FindHID(FUSION_VID, FUSION_PID, GetDataType())); 00119 if (!GetConnected()) 00120 { 00121 return false; 00122 } 00123 else 00124 { 00125 bStatus = true; 00126 return true; 00127 } 00128 } 00129 00130 void Fusion::CloseDevice() 00131 { 00132 SetConnected(false); 00133 00134 // Close handle to the HID device 00135 if (DeviceHandle != INVALID_HANDLE_VALUE) 00136 CloseHandle(DeviceHandle); 00137 00138 // Update connection status for the thread 00139 bStatus = false; 00140 } 00141 00142 // Interface : 0 = raw data, Interface 1 = Kalman filter output (meaningful orientation data) 00143 bool Fusion::ParseData() 00144 { 00145 int nbBytes = 0, i = 0, byteIndex = 1; 00146 00147 // Device opened ? 00148 if (GetConnected() != 0) 00149 { 00150 // Data read ? 00151 if (nbBytes = ReadDataFromHID() > 0) 00152 { 00153 // ------------ "Raw data" mode : retrieve raw data from Fusion device (accelerometer, gyroscope, and magnetometer data) 00154 if (GetDataType() == 0) 00155 { 00156 // Get first raw values 00157 for (i=0; i<9; i++) 00158 { 00159 iRawAxes[i] = (int)InputReport[byteIndex] + 256*(int)InputReport[byteIndex + 1]; 00160 byteIndex += 2; 00161 } 00162 00163 // Get buttons values 00164 iButton[0] = InputReport[byteIndex] & 1; 00165 iButton[1] = (InputReport[byteIndex] >> 1) & 1; 00166 00167 // Get final raw values 00168 iRawAxes[9] = (InputReport[byteIndex] >> 4) & 0xf; 00169 byteIndex++; 00170 iRawAxes[10] = InputReport[byteIndex]; 00171 00172 // Notify Scol window that new raw data (acceleration and quaternion) has been read 00173 PostMessage(HScol, FUSION_RAW_DATA_CB, (int)this, (LPARAM)NULL); 00174 00175 // Eventually notify Scol window if a button has been pressed 00176 if (iButton[0] == 1 || iButton[1] == 1) 00177 PostMessage(HScol, FUSION_BUTTON_CB, (int)this, (LPARAM)NULL); 00178 } 00179 00180 // ------------ Mode "Orientation data" : retrieve calculated orientation information (acceleration and quaternions) 00181 else if (GetDataType() == 1) 00182 { 00183 // Get first raw values 00184 for (i=0; i<7; i++) 00185 { 00186 iRawAxes[i] = (int)InputReport[byteIndex] + 256*(int)InputReport[byteIndex+1]; 00187 byteIndex += 2; 00188 } 00189 00190 // Get buttons values 00191 iButton[0] = InputReport[byteIndex]&1; 00192 iButton[1] = (InputReport[byteIndex]>>1)&1; 00193 00194 // Get final raw values 00195 iRawAxes[7] = (InputReport[byteIndex] >> 4) & 0xf; 00196 00197 // 16 bit raw values centered at 32768 00198 // acc_scaled = 6 * (acc_received - 32768)/32768; 00199 for(i=0; i<3; i++) 00200 fAcceleration[i] = 6.0f * (iRawAxes[i] - 32768) / 32768.0f; 00201 00202 // 16 bit raw values centered at 32768 00203 // q_scaled = 3.0518e - 005 * (qraw - 32768) 00204 //TODO find the good quaternion conversion 00205 fQuaternion = Quaternion(3.0518e-005f * (iRawAxes[6] - 32768), 00206 3.0518e-005f * (iRawAxes[4] - 32768), 00207 3.0518e-005f * (iRawAxes[5] - 32768), 00208 3.0518e-005f * (iRawAxes[3] - 32768)); 00209 00210 // Invert quaternion on the Y axis (initial shift is 180 degrees) 00211 fQuaternion = fQuaternion * Quaternion(0, 0, 1, 0); 00212 00213 // Notify Scol window that new orientation data has been read 00214 PostMessage(HScol, FUSION_ORIENTATION_DATA_CB, (int)this, (LPARAM)NULL); 00215 00216 // Eventually notify Scol window if a button has been pressed 00217 if (iButton[0] == 1 || iButton[1] == 1) 00218 PostMessage(HScol, FUSION_BUTTON_CB, (int)this, (LPARAM)NULL); 00219 } 00220 00221 // Data successfully read from device 00222 return true; 00223 } 00224 else 00225 return false; // Unable to read data from device 00226 } 00227 else 00228 return false; 00229 } 00230 00231 00232 /*------------------------------- USB HID HANDLING --------------------------------*/ 00233 bool Fusion::FindHID(unsigned int vendorID, unsigned int productID, unsigned int interfaceMode) 00234 { 00235 // Use a series of API calls to find a HID with a specified Vendor IF and Product ID 00236 HIDD_ATTRIBUTES Attributes; 00237 SP_DEVICE_INTERFACE_DATA devInfoData; 00238 bool LastDevice = FALSE; 00239 int MemberIndex = 0; 00240 LONG Result; 00241 00242 Length = 0; 00243 detailData = NULL; 00244 DeviceHandle = NULL; 00245 00246 /* 00247 API function: HidD_GetHidGuid 00248 Get the GUID for all system HIDs. 00249 Returns: the GUID in HidGuid. 00250 */ 00251 HidD_GetHidGuid(&HidGuid); 00252 00253 /* 00254 API function: SetupDiGetClassDevs 00255 Returns: a handle to a device information set for all installed devices. 00256 Requires: the GUID returned by GetHidGuid. 00257 */ 00258 hDevInfo = SetupDiGetClassDevs(&HidGuid, 00259 NULL, 00260 NULL, 00261 DIGCF_PRESENT|DIGCF_INTERFACEDEVICE); 00262 00263 devInfoData.cbSize = sizeof(devInfoData); 00264 00265 // Step through the available devices looking for the one we want. 00266 // Quit on detecting the desired device or checking all available devices without success 00267 MemberIndex = 0; 00268 LastDevice = FALSE; 00269 do 00270 { 00271 /* 00272 API function: SetupDiEnumDeviceInterfaces 00273 On return, MyDeviceInterfaceData contains the handle to a SP_DEVICE_INTERFACE_DATA structure for a detected device. 00274 Requires: 00275 The DeviceInfoSet returned in SetupDiGetClassDevs. 00276 The HidGuid returned in GetHidGuid. 00277 An index to specify a device. 00278 */ 00279 Result = SetupDiEnumDeviceInterfaces(hDevInfo, 00280 0, 00281 &HidGuid, 00282 MemberIndex, 00283 &devInfoData); 00284 00285 if (Result != 0) 00286 { 00287 // A device has been detected, so get more information about it 00288 /* 00289 API function: SetupDiGetDeviceInterfaceDetail 00290 Returns: an SP_DEVICE_INTERFACE_DETAIL_DATA structure containing information about a device. 00291 To retrieve the information, call this function twice: 00292 The first time returns the size of the structure in Length. 00293 The second time returns a pointer to the data in DeviceInfoSet. 00294 Requires: 00295 A DeviceInfoSet returned by SetupDiGetClassDevs 00296 The SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces. 00297 00298 The final parameter is an optional pointer to an SP_DEV_INFO_DATA structure. 00299 This application doesn't retrieve or use the structure. 00300 If retrieving the structure, set MyDeviceInfoData.cbSize = length of MyDeviceInfoData, and pass the structure's address. 00301 */ 00302 // Get the Length value 00303 // The call will return with a "buffer too small" error which can be ignored 00304 Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, 00305 &devInfoData, 00306 NULL, 00307 0, 00308 &Length, 00309 NULL); 00310 00311 // Allocate memory for the hDevInfo structure, using the returned Length 00312 detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(Length); 00313 00314 // Set cbSize in the detailData structure 00315 detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 00316 00317 // Call the function again, this time passing it the returned buffer size 00318 Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, 00319 &devInfoData, 00320 detailData, 00321 Length, 00322 &Required, 00323 NULL); 00324 00325 // Open a handle to the device 00326 // To enable retrieving information about a system mouse or keyboard, don't request Read or Write access for this handle 00327 /* 00328 API function: CreateFile 00329 Returns: a handle that enables reading and writing to the device. 00330 Requires: The DevicePath in the detailData structure returned by SetupDiGetDeviceInterfaceDetail. 00331 */ 00332 DeviceHandle = CreateFile(detailData->DevicePath, 00333 GENERIC_READ|GENERIC_WRITE, //0, 00334 FILE_SHARE_READ|FILE_SHARE_WRITE, 00335 (LPSECURITY_ATTRIBUTES)NULL, 00336 OPEN_EXISTING, 00337 FILE_FLAG_OVERLAPPED, //0, 00338 NULL); 00339 00340 /* 00341 API function: HidD_GetAttributes 00342 Requests information from the device. 00343 Requires: the handle returned by CreateFile. 00344 Returns: a HIDD_ATTRIBUTES structure containing the Vendor ID, Product ID, and Product Version Number. 00345 Use this information to decide if the detected device is the one we're looking for. 00346 */ 00347 // Set the Size to the number of bytes in the structure 00348 Attributes.Size = sizeof(Attributes); 00349 Result = HidD_GetAttributes(DeviceHandle, 00350 &Attributes); 00351 00352 // Is it the desired device? 00353 MyDeviceDetected = FALSE; 00354 00355 // Check Vendor ID (must be 'SpacePoint') 00356 if (Attributes.VendorID == vendorID) 00357 { 00358 // Check Product ID (must be 'Fusion') 00359 if (Attributes.ProductID == productID) 00360 { 00361 // Both the Vendor ID and Product ID match, we can get device capabilities to check if interface mode is correct 00362 MyDeviceDetected = TRUE; 00363 00364 // Register to receive device notifications 00365 RegisterForDeviceNotifications(); 00366 00367 // Get the device's capablities 00368 GetDeviceCapabilities(); 00369 00370 // Check interface mode (0=raw and 1=orientation) 00371 if (Capabilities.UsagePage != interfaceMode) 00372 { 00373 // Interface mode is not correct, so it's not the desired device 00374 printf(">>> Wrong interface mode.\n"); 00375 MyDeviceDetected = FALSE; 00376 CloseHandle(DeviceHandle); 00377 } 00378 else 00379 { 00380 // Send the request command using WriteFile 00381 hEventObjectWrite = CreateEvent(NULL, 00382 TRUE, 00383 TRUE, 00384 NULL); 00385 HIDOverlappedWrite.hEvent = hEventObjectWrite; 00386 HIDOverlappedWrite.Offset = 0; 00387 HIDOverlappedWrite.OffsetHigh = 0; 00388 Result = WriteFile(DeviceHandle, 00389 &OutputReport[0], 00390 Capabilities.OutputReportByteLength, 00391 &NumberOfBytesRead, 00392 (LPOVERLAPPED)&HIDOverlapped); 00393 } 00394 } 00395 else 00396 CloseHandle(DeviceHandle); // Wrong Product ID 00397 } 00398 else 00399 CloseHandle(DeviceHandle); // Wrong Vendor ID 00400 00401 // Free the memory used by the detailData structure (no longer needed) 00402 free(detailData); 00403 } 00404 else 00405 //SetupDiEnumDeviceInterfaces returned 0, so there are no more devices to check 00406 LastDevice = TRUE; 00407 00408 //If we haven't found the device yet, and haven't tried every available device, try the next one 00409 MemberIndex = MemberIndex + 1; 00410 } 00411 while ((LastDevice == FALSE) && (MyDeviceDetected == FALSE)); 00412 00413 // Create log message 00414 if (MyDeviceDetected == FALSE) 00415 printf("Device not detected\n"); 00416 else 00417 printf("Device detected\n"); 00418 00419 // Free the memory reserved for hDevInfo by SetupDiClassDevs 00420 SetupDiDestroyDeviceInfoList(hDevInfo); 00421 00422 // Return device detection status 00423 return MyDeviceDetected; 00424 } 00425 00426 void Fusion::RegisterForDeviceNotifications() 00427 { 00428 // Request to receive messages when a device is attached or removed 00429 // Also see WM_DEVICECHANGE in BEGIN_MESSAGE_MAP(CUsbhidiocDlg, CDialog) 00430 DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface; 00431 HDEVNOTIFY DeviceNotificationHandle; 00432 00433 DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface); 00434 DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; 00435 DevBroadcastDeviceInterface.dbcc_classguid = HidGuid; 00436 00437 DeviceNotificationHandle = RegisterDeviceNotification(NULL, // TODO: Handle that will catch messages (Scol window handle ?) 00438 &DevBroadcastDeviceInterface, 00439 DEVICE_NOTIFY_WINDOW_HANDLE); 00440 } 00441 00442 void Fusion::GetDeviceCapabilities() 00443 { 00444 // Get the Capabilities structure for the device 00445 PHIDP_PREPARSED_DATA PreparsedData; 00446 00447 /* 00448 API function: HidD_GetPreparsedData 00449 Returns: a pointer to a buffer containing the information about the device's capabilities. 00450 Requires: A handle returned by CreateFile. 00451 There's no need to access the buffer directly, but HidP_GetCaps and other API functions require a pointer to the buffer. 00452 */ 00453 HidD_GetPreparsedData(DeviceHandle, &PreparsedData); 00454 00455 /* 00456 API function: HidP_GetCaps 00457 Learn the device's capabilities. 00458 For standard devices such as joysticks, you can find out the specific capabilities of the device. 00459 For a custom device, the software will probably know what the device is capable of, and the call only verifies the information. 00460 Requires: the pointer to the buffer returned by HidD_GetPreparsedData. 00461 Returns: a Capabilities structure containing the information. 00462 */ 00463 HidP_GetCaps(PreparsedData, &Capabilities); 00464 00465 // No need for PreparsedData any more, so free the memory it's using 00466 HidD_FreePreparsedData(PreparsedData); 00467 } 00468 00469 int Fusion::ReadDataFromHID() 00470 { 00471 // Send the request command using ReadFile 00472 hEventObjectRead = CreateEvent(NULL, 00473 TRUE, 00474 TRUE, 00475 NULL); 00476 HIDOverlappedRead.hEvent = hEventObjectRead; 00477 HIDOverlappedRead.Offset = 0; 00478 HIDOverlappedRead.OffsetHigh = 0; 00479 Result = ReadFile(DeviceHandle, 00480 &InputReport[0], 00481 Capabilities.InputReportByteLength, 00482 &NumberOfBytesRead, 00483 (LPOVERLAPPED)&HIDOverlappedRead); 00484 Result = WaitForSingleObject(hEventObjectRead, 600); 00485 DWORD dw = GetLastError(); 00486 return dw; 00487 } 00488 00489 /*-------------------------------- THREAD HANDLING --------------------------------*/ 00490 void Fusion::run() 00491 { 00492 try 00493 { 00494 while(isRunning) 00495 { 00496 // Parse data only if device has been opened 00497 if (bStatus) 00498 ParseData(); // ORIENTATION_DATA or RAW_DATA 00499 } 00500 // Release connection to the SpacePoint Fusion device 00501 CloseDevice(); 00502 } 00503 catch (ThreadException ex) 00504 { 00505 MMechostr(MSKDEBUG, "error thread : %s\n", ex.getMessage().c_str()); 00506 } 00507 }
1.7.3