biotac_hand_class.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2012, University of Pennsylvania
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of University of Pennsylvania nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: Ian McMahon (imcmahon@grasp.upenn.edu)
00036 *********************************************************************/
00037 
00038 #include <biotac_sensors/biotac_hand_class.h>
00039 #include <stdio.h>
00040 #include <iostream>
00041 
00042 //=========================================================================
00043 // CONSTANTS
00044 //=========================================================================
00045 
00046 static const unsigned char parity_values[] = \
00047                 { 0x01, 0x02, 0x04, 0x07, 0x08, 0x0B, 0x0D, 0x0E, \
00048                   0x10, 0x13, 0x15, 0x16, 0x19, 0x1A, 0x1C, 0x1F, \
00049                   0x20, 0x23, 0x25, 0x26, 0x29, 0x2A, 0x2C, 0x2F, \
00050                   0x31, 0x32, 0x34, 0x37, 0x38, 0x3B, 0x3D, 0x3E, \
00051                   0x40, 0x43, 0x45, 0x46, 0x49, 0x4A, 0x4C, 0x4F, \
00052                   0x51, 0x52, 0x54, 0x57, 0x58, 0x5B, 0x5D, 0x5E, \
00053                   0x61, 0x62, 0x64, 0x67, 0x68, 0x6B, 0x6D, 0x6E, \
00054                   0x70, 0x73, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x7F, \
00055                   0x80, 0x83, 0x85, 0x86, 0x89, 0x8A, 0x8C, 0x8F, \
00056                   0x91, 0x92, 0x94, 0x97, 0x98, 0x9B, 0x9D, 0x9E, \
00057                   0xA1, 0xA2, 0xA4, 0xA7, 0xA8, 0xAB, 0xAD, 0xAE, \
00058                   0xB0, 0xB3, 0xB5, 0xB6, 0xB9, 0xBA, 0xBC, 0xBF, \
00059                   0xC1, 0xC2, 0xC4, 0xC7, 0xC8, 0xCB, 0xCD, 0xCE, \
00060                   0xD0, 0xD3, 0xD5, 0xD6, 0xD9, 0xDA, 0xDC, 0xDF, \
00061                   0xE0, 0xE3, 0xE5, 0xE6, 0xE9, 0xEA, 0xEC, 0xEF, \
00062                   0xF1, 0xF2, 0xF4, 0xF7, 0xF8, 0xFB, 0xFD, 0xFE};
00063 
00064 using namespace biotac;
00065 
00066 //========================================================
00067 //BioTacHand Class constructor populating default values
00068 //========================================================
00069 BioTacHandClass::BioTacHandClass(string hand_id)
00070 {
00071   hand_id_ = hand_id;
00072   biotac_.spi_clock_speed = BT_SPI_BITRATE_KHZ_DEFAULT;
00073   biotac_.number_of_biotacs = 0;
00074   biotac_.sample_rate_Hz = BT_SAMPLE_RATE_HZ_DEFAULT;
00075   //Using the Default frame type
00076   biotac_.frame.frame_type = 0;
00077   //Only sample 1 frame every batch
00078   biotac_.batch.batch_frame_count = 1;
00079   //Configure the Cheetah to sample every 10ms
00080   biotac_.batch.batch_ms = 10;
00081 
00082   // Set the duration of the run time
00083   double length_of_data_in_second = 0.01;
00084   number_of_samples_ = (int)(BT_SAMPLE_RATE_HZ_DEFAULT * length_of_data_in_second);
00085 
00086   // Check if any initial settings are wrong
00087   if (MAX_BIOTACS_PER_CHEETAH != 3 && MAX_BIOTACS_PER_CHEETAH != 5)
00088   {
00089     BioTac bt_err_code = BT_WRONG_MAX_BIOTAC_NUMBER;
00090     displayErrors(bt_err_code);
00091   }
00092 
00093 }
00094 
00095 //========================================================
00096 //Clean up the Cheetah
00097 //========================================================
00098 BioTacHandClass::~BioTacHandClass()
00099 {
00100   bt_cheetah_close(ch_handle_);
00101 }
00102 
00103 //========================================================
00104 //Initialization of BioTac sensors & Cheetah
00105 //This function will keep retrying to find BioTac/Cheetah
00106 //devices, every second
00107 //========================================================
00108 void BioTacHandClass::initBioTacSensors()
00109 {
00110   BioTac bt_err_code = BT_NO_CHEETAH_DETECTED;
00111   ros::Rate loop_rate_cheetah(1);
00112   //Initialize the Cheetah Device 
00113   while( bt_err_code < BT_OK && ros::ok())
00114   {
00115     bt_err_code = bt_cheetah_initialize(&biotac_, &ch_handle_);
00116     if(bt_err_code < BT_OK)
00117     {
00118       displayErrors(bt_err_code);
00119       ROS_INFO("Attempting to initialize the Cheetah again in 1 second...");
00120       loop_rate_cheetah.sleep();
00121     }
00122   }
00123 
00124   bt_err_code = BT_NO_BIOTAC_DETECTED;
00125   ros::Rate loop_rate_biotac_connect(1);
00126   //Grabbing properties of the BioTac sensors
00127   while( bt_err_code < BT_OK && ros::ok())
00128   { 
00129     bt_err_code = getBioTacProperties();
00130     if(bt_err_code < BT_OK)
00131     {
00132       displayErrors(bt_err_code);
00133       ROS_INFO("Attempting to connect to BioTac sensors again in 1 second...");
00134       loop_rate_biotac_connect.sleep();
00135     }
00136   }
00137 
00138   bt_err_code = BT_DATA_SIZE_TOO_SMALL;
00139   ros::Rate loop_rate_biotac_configure(1);
00140   //Configures the SPI batch to send to the BioTac sensors
00141   while( bt_err_code < BT_OK && ros::ok())
00142   { 
00143     bt_err_code = configureBatch();
00144     if(bt_err_code < BT_OK)
00145     {
00146       displayErrors(bt_err_code);
00147       ROS_INFO("Configuring the BioTac SPI batch again, this time using default parameters.");
00148       ROS_INFO("Attempting to configure the BioTac SPI batch again in 1 second...");
00149       //Default Parameters
00150       biotac_.spi_clock_speed = BT_SPI_BITRATE_KHZ_DEFAULT;
00151       biotac_.number_of_biotacs = 0;
00152       biotac_.sample_rate_Hz = BT_SAMPLE_RATE_HZ_DEFAULT;
00153       //Using the Default frame type
00154       biotac_.frame.frame_type = 0;
00155       //Only sample 1 frame every batch
00156       biotac_.batch.batch_frame_count = 1;
00157       //Configure the Cheetah to sample every 10ms
00158       biotac_.batch.batch_ms = 10;
00159       // Set the duration of the run time
00160       double length_of_data_in_second = 0.01;
00161       number_of_samples_ = (int)(BT_SAMPLE_RATE_HZ_DEFAULT * length_of_data_in_second);
00162       //Sleep for 1 second
00163       loop_rate_biotac_configure.sleep();
00164     }
00165   }
00166 
00167   //Construct the BioTacHand Message
00168   biotac_sensors::BioTacData one_finger;
00169   for(int i = 0; i < biotac_.number_of_biotacs; i++)
00170   {
00171     bt_hand_msg_.bt_data.push_back(one_finger);
00172   }
00173   if(bt_err_code == BT_OK)
00174   {
00175     ROS_INFO("Configuring of Cheetah SPI and BioTac sensors complete.");
00176     ROS_INFO("Reading BioTac data...");
00177   }
00178 }
00179 
00180 //========================================================
00181 //Get and print properties of the BioTac(s)
00182 //========================================================
00183 BioTac BioTacHandClass::getBioTacProperties()
00184 {
00185   biotac_.number_of_biotacs = 0;
00186   bt_property biotac_property[MAX_BIOTACS_PER_CHEETAH];
00187   BioTac bt_err_code;
00188   for (int i = 0; i < MAX_BIOTACS_PER_CHEETAH; i++)
00189   {
00190     bt_err_code = bt_cheetah_get_properties(ch_handle_, i+1, &(biotac_property[i]));
00191     if (bt_err_code)
00192     {//Check to see if the properties were successfully acquired
00193       return bt_err_code;
00194     }
00195 
00196     if (biotac_property[i].bt_connected == YES)
00197     {
00198       (biotac_.number_of_biotacs)++;
00199       //Store off BioTac serial number
00200       finger_info new_finger;
00201       char serial_char_array [25];
00202       sprintf(serial_char_array, "%c%c-%c%c-%c%c.%c.%c-%c-%c%c-%c-%c%c%c%c", \
00203                     biotac_property[i].serial_number[0], biotac_property[i].serial_number[1], \
00204                     biotac_property[i].serial_number[2], biotac_property[i].serial_number[3], \
00205                     biotac_property[i].serial_number[4], biotac_property[i].serial_number[5], \
00206                     biotac_property[i].serial_number[6], biotac_property[i].serial_number[7], \
00207                     biotac_property[i].serial_number[8], biotac_property[i].serial_number[9], \
00208                     biotac_property[i].serial_number[10], biotac_property[i].serial_number[11], \
00209                     biotac_property[i].serial_number[12], biotac_property[i].serial_number[13], \
00210                     biotac_property[i].serial_number[14], biotac_property[i].serial_number[15]);
00211       new_finger.finger_serial = string(serial_char_array);
00212       //Store off BioTac position on the Multi-BioTac Board
00213       new_finger.finger_position = i+1;
00214       biotac_serial_no_info_.push_back(new_finger);
00215       ROS_INFO("Detected a BioTac at board position %d with serial number %s", \
00216                 new_finger.finger_position, serial_char_array+'\0');
00217     }
00218 
00219   }
00220   
00221   // Check if any BioTacs detected
00222   if (biotac_.number_of_biotacs == 0)
00223   {
00224     bt_err_code = BT_NO_BIOTAC_DETECTED;
00225     return bt_err_code;
00226   }
00227   //Report total number of Biotacs
00228   ROS_INFO("%d BioTac(s) detected.", biotac_.number_of_biotacs);
00229   
00230   bt_err_code = BT_OK;
00231   return bt_err_code;
00232 }
00233 
00234 //=================================
00235 //Configure the SPI batch 
00236 //=================================
00237 BioTac BioTacHandClass::configureBatch()
00238 {
00239   BioTac bt_err_code;
00240 
00241   bt_err_code = bt_cheetah_configure_batch(ch_handle_, &biotac_, number_of_samples_);
00242 
00243   if(bt_err_code == BT_OK)
00244   {
00245     ROS_INFO("Configured the Cheetah batch.");
00246     ros::Time frame_start_time_ = ros::Time::now();
00247   }
00248 
00249   return bt_err_code;
00250 }
00251 //=========================================================================
00252 // Collect a single SPI Batch
00253 //=========================================================================
00254 
00255 biotac_sensors::BioTacHand BioTacHandClass::collectBatch()
00256 {
00257   int i, j;
00258   int byte_shift = 2 + MAX_BIOTACS_PER_CHEETAH*2;  // 2 bytes of command + 2 bytes per BioTac data
00259   int spi_data_len;
00260   int number_of_samples_in_batch;
00261   static u08 *bt_raw_data;
00262   unsigned int channel_id;
00263   int finger_vector_pos;
00264 
00265   //Query the Cheetah for data, and push out a new request, save off frame end time
00266   spi_data_len = ch_spi_batch_length(ch_handle_);
00267   bt_raw_data = (u08*) malloc(spi_data_len * sizeof *bt_raw_data);
00268   ch_spi_async_collect(ch_handle_, spi_data_len, bt_raw_data);
00269   ros::Time frame_end_time = ros::Time::now();
00270   ch_spi_async_submit(ch_handle_);
00271 
00272   number_of_samples_in_batch = spi_data_len/byte_shift;
00273 
00274   //Begin constructing the BioTacHand message
00275   bt_hand_msg_.hand_id = hand_id_;
00276   //Calculate Timestamps
00277   bt_hand_msg_.header.stamp = frame_end_time;
00278   bt_hand_msg_.bt_time.frame_start_time = frame_start_time_;
00279   bt_hand_msg_.bt_time.frame_end_time = frame_end_time;
00280   double sample_total_time = (frame_end_time.sec + (double)frame_end_time.nsec/1000000) - \
00281                              (frame_start_time_.sec + (double)frame_start_time_.nsec/1000000);
00282   unsigned int time_step = (unsigned int)((sample_total_time / number_of_samples_in_batch) * 1000000.0);
00283   frame_start_time_ = frame_end_time;
00284 
00285   //Store off the serial number and biotac position
00286   for(unsigned int k = 0; k < biotac_serial_no_info_.size(); k++)
00287   {
00288     bt_hand_msg_.bt_data[k].bt_position = biotac_serial_no_info_[k].finger_position;
00289     bt_hand_msg_.bt_data[k].bt_serial = biotac_serial_no_info_[k].finger_serial;
00290   }
00291 
00292   unsigned int ns_offset = 0;
00293   unsigned int pac_index = 0;
00294   unsigned int electrode_index = 0;
00295   unsigned int spi_data;
00296   for(i = 0; i < number_of_samples_in_batch; i++)
00297   {
00298     channel_id = (biotac_.frame.frame_structure[i%(biotac_.frame.frame_size)] & 0x7E) >> 1;
00299     finger_vector_pos = 0;
00300     for(j = 0; j < MAX_BIOTACS_PER_CHEETAH; j++)
00301     {
00302       spi_data = (unsigned int) (bt_raw_data[i*byte_shift + j*2 + 2] >> 1) * 32 + (bt_raw_data[i*byte_shift + j*2 + 3] >> 3);
00303 
00304       if((parity_values[bt_raw_data[i*byte_shift + j*2 + 2] >> 1] == bt_raw_data[i*byte_shift + j*2 + 2]) && \
00305           (parity_values[bt_raw_data[i*byte_shift + j*2 + 3] >> 1] == bt_raw_data[i*byte_shift + j*2 + 3]))
00306       {//data[index].bt_parity[j] = PARITY_GOOD;
00307         //bt_hand_msg_.bt_data[finger_vector_pos].bt_position = j+1;
00308         switch (channel_id)
00309         {
00310            case TDC:
00311               //Temperature DC
00312               bt_hand_msg_.bt_data[finger_vector_pos].tdc_data = spi_data;
00313               bt_hand_msg_.bt_time.tdc_ns_offset = ns_offset;
00314               break;
00315            case TAC:
00316               //Temperature AC
00317               bt_hand_msg_.bt_data[finger_vector_pos].tac_data = spi_data;
00318               bt_hand_msg_.bt_time.tac_ns_offset = ns_offset;
00319               break;
00320            case PDC:
00321               //Pressure DC
00322               bt_hand_msg_.bt_data[finger_vector_pos].pdc_data = spi_data;
00323               bt_hand_msg_.bt_time.pdc_ns_offset = ns_offset;
00324               break;
00325            case PAC:
00326               //Pressure AC
00327               bt_hand_msg_.bt_data[finger_vector_pos].pac_data[pac_index] = spi_data;
00328               bt_hand_msg_.bt_time.pac_ns_offset[pac_index] = ns_offset;
00329               break;
00330            default:
00331               //Electrodes
00332               if(channel_id >= ELEC_LO && channel_id <= ELEC_HI)
00333               {//Electrode
00334                 electrode_index = channel_id - ELEC_LO;
00335                 bt_hand_msg_.bt_data[finger_vector_pos].electrode_data[electrode_index] = spi_data;
00336                 bt_hand_msg_.bt_time.electrode_ns_offset[electrode_index] = ns_offset;
00337               }
00338               else
00339               {//error
00340               }
00341         }  
00342 
00343         if( finger_vector_pos < (int)bt_hand_msg_.bt_data.size()-1 )
00344         {
00345           finger_vector_pos++;
00346         }
00347       }
00348       //else
00349       //{
00350       //  data[index].bt_parity[j] = PARITY_BAD;
00351       //}
00352 
00353      }
00354 
00355     //Increment PAC index
00356      if(channel_id == PAC)
00357      {
00358        pac_index++;
00359      }
00360      //Add seconds to offset
00361      ns_offset += time_step;
00362 
00363   }
00364   free(bt_raw_data);
00365   return bt_hand_msg_;
00366 }
00367 
00368 
00369 //=========================================================================
00370 // PRINT ERROR CODES
00371 //=========================================================================
00372 void BioTacHandClass::displayErrors(BioTac bt_err_code)
00373 {
00374 
00375   switch(bt_err_code)
00376   {
00377   case BT_OK:
00378     //Break, nothing is wrong
00379     break;
00380   case BT_WRONG_NUMBER_ASSIGNED:
00381     ROS_WARN("Wrong BioTac number assigned!");
00382     break;
00383   case BT_NO_BIOTAC_DETECTED:
00384     ROS_ERROR("No BioTacs are detected!");
00385     break;
00386   case BT_WRONG_MAX_BIOTAC_NUMBER:
00387     ROS_WARN("Wrong maximum number of BioTacs assigned (should be 3 or 5)!");
00388     break;
00389   case BT_DATA_SIZE_TOO_SMALL:
00390     ROS_WARN("The number of samples is too small! Using default sample size of ____.");
00391     break;
00392   case BT_NO_CHEETAH_DETECTED:
00393     ROS_ERROR("No Cheetah device detected!");
00394     break;
00395   case BT_UNABLE_TO_OPEN_CHEETAH:
00396     ROS_ERROR("Unable to open Cheetah device on current port.");
00397     break;
00398   case BT_UNABLE_TO_OPEN_FILE:
00399     ROS_ERROR("Cannot open output file.");
00400     break;
00401   default:
00402     ROS_ERROR("Unrecognized Biotac error encountered.");
00403     break;
00404   }
00405 
00406 }


biotac_sensors
Author(s): Ian McMahon
autogenerated on Thu Aug 27 2015 12:35:30