SpacePoint Fusion plugin 1.0

src/Fusion.cpp

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 }