Esempio n. 1
0
 void BSFFileIO::extractInstrumentHeader(IEEELittleEndianBinaryFileStream* bifs, BSFFileIO::InstrumentHeader* header) const
 {
   header->headerSize = bifs->ReadI32();
   header->serialNumber = bifs->ReadI32();
   header->layoutPlatformNumber = bifs->ReadI32();
   header->name = btkTrimString(bifs->ReadString(20));
   header->numberOfChannels = bifs->ReadI32();
   header->hardwareStartChannel = bifs->ReadI32();
   header->hardwareEndChannel = bifs->ReadI32();
   header->softwareStartChannel = bifs->ReadI32();
   header->softwareEndChannel = bifs->ReadI32();
   header->length = bifs->ReadFloat();
   header->width = bifs->ReadFloat();
   bifs->ReadFloat(3, header->offset);
   bifs->ReadFloat(_btk_bsf_maximum_channel_number, header->sensitivity);
   for (int i = 0 ; i < _btk_bsf_maximum_channel_number ; ++i)
   {
     header->channels[i] = bifs->ReadI16();
     bifs->SeekRead(2, BinaryFileStream::Current);
   }
   bifs->ReadFloat(16, header->transformation);
   bifs->ReadFloat(3, header->interDistance);
   bifs->ReadFloat(_btk_bsf_maximum_channel_number, header->amplifierGain);
   bifs->ReadFloat(_btk_bsf_maximum_channel_number, header->excitationVoltage);
   bifs->ReadFloat(_btk_bsf_maximum_channel_number, header->acquisitionCardRange);
   header->zeroPeriod = bifs->ReadFloat();
   header->latencyPeriod = bifs->ReadFloat();
   header->triggerTime = bifs->ReadFloat();
   header->endTime = bifs->ReadFloat();
   header->postTrigTime = bifs->ReadFloat();
   bifs->ReadI32(_btk_bsf_maximum_channel_number, header->zero);
   header->rate = bifs->ReadI32();
   header->triggerValue = bifs->ReadFloat();
   header->endValue = bifs->ReadFloat();
   /*
   std::cout << "Length x Width: " << ToString(header->length) << " x " << ToString(header->width) << "\n";
   std::cout << "Offset: ";
   for (size_t i = 0 ; i < 3 ; ++i)
     std::cout << ToString(header->offset[i]) << ", ";
   std::cout << "\n";
   std::cout << "Interdistance: ";
   for (size_t i = 0 ; i < 3 ; ++i)
     std::cout << ToString(header->interDistance[i]) << ", ";
   std::cout << "\n";
   std::cout << "Transformation: ";
   for (size_t i = 0 ; i < 16 ; ++i)
     std::cout << ToString(header->transformation[i]) << ", ";
   std::cout << std::endl;
   */
 };
 /**
  * Read the file designated by @a filename and fill @a output.
  */
 void ANGFileIO::Read(const std::string& filename, Acquisition::Pointer output)
 {
   output->Reset();
   IEEELittleEndianBinaryFileStream bifs;
   bifs.SetExceptions(BinaryFileStream::EndFileBit | BinaryFileStream::FailBit | BinaryFileStream::BadBit);
   try
   {
     bifs.Open(filename, BinaryFileStream::In);
     ReadEliteHeader_p(output, &bifs);
     ReadEliteMarkersValues_p(output, &bifs);
     // Labels
     std::vector<AngleLabelConverter> labels = std::vector<AngleLabelConverter>(8);
     labels[0] = AngleLabelConverter("R_PELV", "RPelvisAngle", "Pelvis relative to Global/Body axes (Right)");
     labels[1] = AngleLabelConverter("R_HIP", "RHipAngle", "Right Hip Rotation");
     labels[2] = AngleLabelConverter("L_HIP", "LHipAngle", "Left Hip Rotation");
     labels[3] = AngleLabelConverter("R_KNEE", "RKneeAngle", "Right Knee Rotation");
     labels[4] = AngleLabelConverter("L_KNEE", "LKneeAngle", "Left Knee Rotation");
     labels[5] = AngleLabelConverter("R_SHOU", "RShoulderAngle", "Right Shoulder Rotation");
     labels[6] = AngleLabelConverter("L_PELV", "LPelvisAngle", "Pelvis relative to Global/Body axes (Left)");
     labels[7] = AngleLabelConverter("L_SHOU", "LShoulderAngle", "Left Shoulder Rotation");
     
     btk::Point::Pointer RFPA, LFPA;
     for (Acquisition::PointIterator it = output->BeginPoint() ; it != output->EndPoint() ; ++it)
     {
       bifs.SeekRead(10, BinaryFileStream::Current);
       std::string label = btkTrimString(btkTrimString(bifs.ReadString(6)), static_cast<char>(0x00));
       // Known case
       size_t j = 0;
       for (j = 0 ; j < labels.size() ; ++j)
       {
         if (label.compare(labels[j].current) == 0)
         {
           (*it)->SetLabel(labels[j].future);
           (*it)->SetDescription(labels[j].description);
           break;
         }
       }
       // Special *_ANKL
       if (j >= labels.size())
       {
         if (label.compare("R_ANKL") == 0)
         {
           (*it)->SetLabel("RAnkleAngle");
           (*it)->SetDescription("Right Ankle Rotation");
           RFPA = btk::Point::New("RFootProgressAngle", output->GetPointFrameNumber(), Point::Angle, "Right Foot relative to Global/Body axes");
           RFPA->GetValues().col(1) = (*it)->GetValues().col(1);
           RFPA->GetResiduals() = (*it)->GetResiduals();
           (*it)->GetValues().col(1).setZero();
           // Clean the -9999 which were not on the 3 coordinates
           // The column #0 contains only -9999
           (*it)->GetValues().col(0).setZero();
           for (int i = 0 ; i < output->GetPointFrameNumber() ; ++i)
           {
             if (((*it)->GetValues().coeff(i, 2) + 9999.0) < std::numeric_limits<double>::epsilon())
             {
               (*it)->GetValues().coeffRef(i, 2) = 0.0;
               (*it)->GetResiduals().coeffRef(i) = -1.0;
             }
             if ((RFPA->GetValues().coeff(i, 1) + 9999.0) < std::numeric_limits<double>::epsilon())
             {
               RFPA->GetValues().coeffRef(i, 1) = 0.0;
               RFPA->GetResiduals().coeffRef(i) = -1.0;
             }
           }
         }
         else if (label.compare("L_ANKL") == 0)
         {
           (*it)->SetLabel("LAnkleAngle");
           (*it)->SetDescription("Left Ankle Rotation");
           LFPA = btk::Point::New("LFootProgressAngle", output->GetPointFrameNumber(), Point::Angle, "Left Foot relative to Global/Body axes");
           LFPA->GetValues().col(1) = (*it)->GetValues().col(1);
           LFPA->GetResiduals() = (*it)->GetResiduals();
           (*it)->GetValues().col(1).setZero();
           // Clean the -9999 which were not on the 3 coordinates
           // The column #0 contains only -9999
           (*it)->GetValues().col(0).setZero();
           for (int i = 0 ; i < output->GetPointFrameNumber() ; ++i)
           {
             if (((*it)->GetValues().coeff(i, 2) + 9999.0) < std::numeric_limits<double>::epsilon())
             {
               (*it)->GetValues().coeffRef(i, 2) = 0.0;
               (*it)->GetResiduals().coeffRef(i) = -1.0;
             }
             if ((LFPA->GetValues().coeff(i, 1) + 9999.0) < std::numeric_limits<double>::epsilon())
             {
               LFPA->GetValues().coeffRef(i, 1) = 0.0;
               LFPA->GetResiduals().coeffRef(i) = -1.0;
             }
           }
         }
         // No known translation
         else
           (*it)->SetLabel(label);
       }
       (*it)->SetType(Point::Angle);
     }
     if (RFPA != Point::Null)
       output->AppendPoint(RFPA);
     if (LFPA != Point::Null)
       output->AppendPoint(LFPA);
     
   }
   catch (BinaryFileStreamFailure& )
   {
     std::string excmsg; 
     if (bifs.EndFile())
       excmsg = "Unexpected end of file.";
     else if (!bifs.IsOpen())
       excmsg = "Invalid file path.";
     else if(bifs.Bad())
       excmsg = "Loss of integrity of the file stream.";
     else if(bifs.Fail())
       excmsg = "Internal logic operation error on the stream associated with the file.";
     else
       excmsg = "Unknown error associated with the file stream.";
     
     if (bifs.IsOpen()) bifs.Close();
     throw(ANGFileIOException(excmsg));
   }
   catch (ANGFileIOException& )
   {
     if (bifs.IsOpen()) bifs.Close();
     throw;
   }
   catch (std::exception& e)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(ANGFileIOException("Unexpected exception occurred: " + std::string(e.what())));
   }
   catch(...)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(ANGFileIOException("Unknown exception"));
   }
 };
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  if(nrhs < 2)
    mexErrMsgTxt("No enough inputs.");
  if (nlhs > 2)
    mexErrMsgTxt("Too many output arguments.");

  if (!mxIsChar(prhs[1]) && mxIsEmpty(prhs[1]) && ((mxGetClassID(prhs[1]) != mxDOUBLE_CLASS) || mxIsComplex(prhs[1]) || (mxGetNumberOfElements(prhs[1]) != 1)))
    mexErrMsgTxt("Parameter's context must be a non-empty string or a double value representing an integer.");

  if (mxIsChar(prhs[1]) && (nrhs < 3))
     mexErrMsgTxt("No enough inputs to remove parameter from its context and name.");
  if (mxIsChar(prhs[1]) && (!mxIsChar(prhs[2]) && mxIsEmpty(prhs[2]) && ((mxGetClassID(prhs[2]) != mxDOUBLE_CLASS) || mxIsComplex(prhs[2]) || (mxGetNumberOfElements(prhs[2]) != 1))))
    mexErrMsgTxt("Parameter's context must be a non-empty string.");

  btk::Acquisition::Pointer acq = btk_MOH_get_object<btk::Acquisition>(prhs[0]);
  btk::MetaData::Iterator itAnalysis = acq->GetMetaData()->FindChild("ANALYSIS");
  if (itAnalysis != acq->GetMetaData()->End())
  {
    std::vector<btk::MetaData::Iterator> its = std::vector<btk::MetaData::Iterator>(5);

    btk::MetaData::Iterator itUsed = (*itAnalysis)->FindChild("USED");
    its[0] = (*itAnalysis)->FindChild("NAMES");
    its[1] = (*itAnalysis)->FindChild("DESCRIPTIONS");
    its[2] = (*itAnalysis)->FindChild("SUBJECTS");
    its[3] = (*itAnalysis)->FindChild("CONTEXTS");
    its[4] = (*itAnalysis)->FindChild("UNITS");
    btk::MetaData::Iterator itValues = (*itAnalysis)->FindChild("VALUES");
    
    if ((itUsed == (*itAnalysis)->End())
        || (its[0] == (*itAnalysis)->End())
        || (its[1] == (*itAnalysis)->End())
        || (its[2] == (*itAnalysis)->End())
        || (its[3] == (*itAnalysis)->End())
        || (its[4] == (*itAnalysis)->End())
        || (itValues == (*itAnalysis)->End()))
      mexErrMsgTxt("Corrupted analysis. Some metadata are missing.");
      
    if (!(*itUsed)->HasInfo()
       || !(*its[0])->HasInfo()
       || !(*its[1])->HasInfo()
       || !(*its[2])->HasInfo()
       || !(*its[3])->HasInfo()
       || !(*its[4])->HasInfo()
       || !(*itValues)->HasInfo())
      mexErrMsgTxt("Corrupted analysis. Some metadata informations are missing.");
    std::vector<int16_t> valUsed = (*itUsed)->GetInfo()->ToInt16();
    if (!valUsed.empty())
    {
      int16_t used = valUsed[0];
      if (used != 0)
      {
        int idx = -1;
        if (mxIsChar(prhs[1]))
        {
          std::vector<std::string> valNames;
          (*its[0])->GetInfo()->ToString(valNames);
          std::vector<std::string> valContexts;
          (*its[3])->GetInfo()->ToString(valContexts);
          // paremeter's context
          size_t strlen_ = (mxGetM(prhs[1]) * mxGetN(prhs[1]) * sizeof(mxChar)) + 1;
          char* context = (char*)mxMalloc(strlen_);
          mxGetString(prhs[1], context, strlen_);
          std::string strContext = std::string(context);
          mxFree(context);
          // parameter's name
          strlen_ = (mxGetM(prhs[2]) * mxGetN(prhs[2]) * sizeof(mxChar)) + 1;
          char* name = (char*)mxMalloc(strlen_);
          mxGetString(prhs[2], name, strlen_);
          std::string strName = std::string(name);
          mxFree(name);
          for (size_t i = 0 ; i < valNames.size() ; ++i)
          {
            btkTrimString(&(valNames[i]));
            btkTrimString(&(valContexts[i]));
            if ((valNames[i].compare(strName) == 0) && (valContexts[i].compare(strContext) == 0))
            {
              idx = (int)i;
              break;
            }
          }
        }
        else
          idx = static_cast<int>(mxGetScalar(prhs[1])) - 1;
        if ((idx >= 0) && (idx < used))
        {
          (*itUsed)->GetInfo()->SetValues(static_cast<int16_t>(used - 1));
          // Char
          std::vector<std::string> dataStr;
          for (int i = 0 ; i < 5 ; ++i)
          {
            (*its[i])->GetInfo()->ToString(dataStr);
            std::vector<std::string>::iterator it = dataStr.begin();
            std::advance(it, idx);
            if (it != dataStr.end())
            {
              dataStr.erase(it);
              (*its[i])->GetInfo()->SetValues(dataStr);
            }
          }
          // Real
          std::vector<float> dataFlt;
          (*itValues)->GetInfo()->ToFloat(dataFlt);
          std::vector<float>::iterator it = dataFlt.begin();
          std::advance(it, idx);
          if (it != dataFlt.end())
          {
            dataFlt.erase(it);
            (*itValues)->GetInfo()->SetValues(dataFlt);
          }
        }
      }
    }
  }

  // Return updated analysis
  btkMXCreateAnalysisStructure(acq, nlhs, plhs);
};
// btkAppendEvent(h, label, time, context)
// btkAppendEvent(h, label, time, context, subject)
// btkAppendEvent(h, label, time, context, subject, description)
// btkAppendEvent(h, label, time, context, subject, description, id)
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
  if(nrhs < 4)
    mexErrMsgTxt("Minimum of four inputs required.");
  if (nlhs > 2)
    mexErrMsgTxt("Too many output arguments.");
  
  btk::Event::Pointer evt = btk::Event::New();
  btk::Acquisition::Pointer acq = btk_MOH_get_object<btk::Acquisition>(prhs[0]);
  btk::Acquisition::EventConstIterator it;
  
  // Label
  if (!mxIsChar(prhs[1]) || mxIsEmpty(prhs[1]))
    mexErrMsgTxt("The label must be a non-empty string.");
  else
  {
    size_t strlen_ = (mxGetM(prhs[1]) * mxGetN(prhs[1]) * sizeof(mxChar)) + 1;
    char* buffer = (char*)mxMalloc(strlen_);
    mxGetString(prhs[1], buffer, strlen_);
    evt->SetLabel(buffer);
    mxFree(buffer);
    it = acq->FindEvent(evt->GetLabel());
  }
  // Time
  if ((mxGetClassID(prhs[2]) != mxDOUBLE_CLASS) || mxIsEmpty(prhs[2]) || mxIsComplex(prhs[2]) || (mxGetNumberOfElements(prhs[2]) != 1))
    mexErrMsgTxt("The time must be set by a numerical (double) value.");
  else
  {
    double t = mxGetScalar(prhs[2]);
    evt->SetTime(t);
  }
  // Context
  if (!mxIsChar(prhs[3]))
    mexErrMsgTxt("The context must be a string.");
  else
  {
    size_t strlen_ = (mxGetM(prhs[3]) * mxGetN(prhs[3]) * sizeof(mxChar)) + 1;
    char* buffer = (char*)mxMalloc(strlen_);
    mxGetString(prhs[3], buffer, strlen_);
    evt->SetContext(buffer);
    mxFree(buffer);
  }
  // Subject (optional)
  if (nrhs >= 5) 
  {
    if (!mxIsChar(prhs[4]))
      mexErrMsgTxt("The subject must be a string.");
    else
    {
      size_t strlen_ = (mxGetM(prhs[4]) * mxGetN(prhs[4]) * sizeof(mxChar)) + 1;
      char* buffer = (char*)mxMalloc(strlen_);
      mxGetString(prhs[4], buffer, strlen_);
      evt->SetSubject(buffer);
      mxFree(buffer);
    }
  }
  else // Use the first name in the SUBJECTS:NAMES parameter.
  {
    btk::MetaData::ConstIterator subjects = acq->GetMetaData()->FindChild("SUBJECTS");
    if (subjects != acq->GetMetaData()->End())
    {
      btk::MetaData::ConstIterator names = (*subjects)->FindChild("NAMES");
      if (names != (*subjects)->End())
      {
        if ((*names)->GetInfo() != btk::MetaDataInfo::Null)
        {
          if ((*names)->GetInfo()->GetDimensionsProduct(1) != 0)
          {
            evt->SetSubject(btkTrimString((*names)->GetInfo()->ToString(0)));
          }
        }
      }
    } 
  }
  // Description (optional)
  if (nrhs >= 6)
  {
    if (!mxIsChar(prhs[5]))
      mexErrMsgTxt("The description must be a string.");
    else
    {
      size_t strlen_ = (mxGetM(prhs[5]) * mxGetN(prhs[5]) * sizeof(mxChar)) + 1;
      char* buffer = (char*)mxMalloc(strlen_);
      mxGetString(prhs[5], buffer, strlen_);
      evt->SetDescription(buffer);
      mxFree(buffer);
    }
  }
  else // Use the description of the first event with the same label.
  {
    if (it != acq->EndEvent())
      evt->SetDescription((*it)->GetDescription());
  }
  // ID (optional)
  // If the function was called from btkEmulateC3Dserver, the ID was set to a fake
  // value (-65536) used later to store correctly some events in the C3D header section
  if (nrhs >= 7)
  {
    if ((mxGetClassID(prhs[6]) != mxDOUBLE_CLASS) || mxIsEmpty(prhs[6]) || mxIsComplex(prhs[6]) || (mxGetNumberOfElements(prhs[6]) != 1))
      mexErrMsgTxt("The ID must be set by a single double representing an integer value.");
    else
    {
      int id = static_cast<int>(mxGetScalar(prhs[6]));
      if (id == -65536)
      {
        evt->SetDetectionFlags(0x10000);
        id = 0;
      }
      evt->SetId(id);
    }
  }
  else // Use the ID of the first event with the same label.
  {
    if (it != acq->EndEvent())
      evt->SetId((*it)->GetId());
  }
  
  acq->AppendEvent(evt);
  
  btkMXCreateEventsStructure(acq, nlhs, plhs);
};
Esempio n. 5
0
 /**
  * Read the file designated by @a filename and fill @a output.
  */
 void BSFFileIO::Read(const std::string& filename, Acquisition::Pointer output)
 {
   output->Reset();
   IEEELittleEndianBinaryFileStream bifs;
   bifs.SetExceptions(BinaryFileStream::EndFileBit | BinaryFileStream::FailBit | BinaryFileStream::BadBit);
   try
   {
     bifs.Open(filename, BinaryFileStream::In);
     // Main header (SMnHeaderTag)
     if (bifs.ReadI32() != 100)
       throw BSFFileIOException("Invalid BSF file.");
     int32_t headerSize = bifs.ReadI32();
     int32_t numberOfActivePlatforms = bifs.ReadI32();
     int32_t numberOfActiveInstruments = bifs.ReadI32();
     std::string subjectName = btkTrimString(bifs.ReadString(100));
     std::string testDate = btkTrimString(bifs.ReadString(12));
     std::string subjectDateOfBirth = btkTrimString(bifs.ReadString(12));
     double weight = bifs.ReadDouble();
     double height = bifs.ReadDouble();
     std::string sex = btkTrimString(bifs.ReadString(1));
     bifs.SeekRead(3, BinaryFileStream::Current); // Because of "pragma pack(4)": minimum of 4 bytes in the alignment of a member.
     int32_t numberOfTrials = bifs.ReadI32();
     double totaleTimeTrial = bifs.ReadDouble(); // seconds
     int32_t zeroMethod = bifs.ReadI32();
     int32_t weightMethod = bifs.ReadI32();
     int32_t delayAfterKeystroke = bifs.ReadI32();
     int32_t triggerMethod = bifs.ReadI32();
     int32_t triggerPlatform = bifs.ReadI32();
     int32_t preTriggerValue = bifs.ReadI32();
     int32_t postTriggerValue = bifs.ReadI32();
     double triggerValue = bifs.ReadDouble();
     bifs.SeekRead(4, BinaryFileStream::Current); // FIXME: There is 4 extra bytes in the file used to test this reader! What are they?
     int32_t rate = bifs.ReadI32();
     std::string protocol = btkTrimString(bifs.ReadString(150));
     std::string testType = btkTrimString(bifs.ReadString(200));
     std::string commentFile = btkTrimString(bifs.ReadString(150));
     std::string trialDescriptionFile = btkTrimString(bifs.ReadString(150));
     std::string examinerName = btkTrimString(bifs.ReadString(100));
     bifs.SeekRead(2, BinaryFileStream::Current); // WARNING: Two (2) extra bytes in the file tested. Could be for alignment?
     int32_t units = bifs.ReadI32(); // 0: english, 1: metric
     
     if (rate == 0)
       throw BSFFileIOException("Invalid frame rate.");
     
     // Instrument headers (SInstrHeaderTag)
     std::vector<InstrumentHeader> instrumentHeaders(numberOfActivePlatforms + numberOfActiveInstruments);
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
       this->extractInstrumentHeader(&bifs, &(instrumentHeaders[i]));
     for (int i = 0 ; i < numberOfActiveInstruments ; ++i)
       this->extractInstrumentHeader(&bifs, &(instrumentHeaders[i+numberOfActivePlatforms]));
     
     // Internal check to verify the compatibility between BTK acquisition's format and data
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
     {
       if ((instrumentHeaders[i].rate != 0) && (instrumentHeaders[i].rate != rate))
         throw BSFFileIOException("Unsupported file: An instrument has a different rate. Contact the developers to improve this reader.");
     }
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
     {
       if (instrumentHeaders[i].numberOfChannels != 6)
         throw BSFFileIOException("Unsupported file: A force platform has more than 6 channels. Contact the developers to improve this reader.");
     }
     
     // Init
     int totalNumberOfChannels = 0;
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
       totalNumberOfChannels += instrumentHeaders[i].numberOfChannels;
     double* scale = new double[totalNumberOfChannels]; // array to transform ADC values to real values
     size_t inc = 0;
     for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
     {
       for (int j = 0 ; j < instrumentHeaders[i].numberOfChannels ; ++j)
         scale[j+inc] = 1000000.0 / (instrumentHeaders[i].sensitivity[j] * instrumentHeaders[i].amplifierGain[j] * instrumentHeaders[i].excitationVoltage[j] * instrumentHeaders[i].acquisitionCardRange[j]);
       inc += instrumentHeaders[i].numberOfChannels;
     }
     output->Init(0, static_cast<int>(totaleTimeTrial * static_cast<double>(rate)), totalNumberOfChannels);
     output->SetPointFrequency(static_cast<double>(rate));
     if (units == 0) // english
     {
       output->SetPointUnit(Point::Marker, "in");
       output->SetPointUnit(Point::Force, "lb");
       output->SetPointUnit(Point::Moment, "in-lb");
     }
     else
     {
       // The FP length/width seems to be set everytime in inch even if the header is set 
       // to metric units (so only used for measured value?).
       for (size_t i = 0 ; i < instrumentHeaders.size() ; ++i)
       {
          // Convert from inch to meter
         instrumentHeaders[i].length *= 0.0254f;
         instrumentHeaders[i].width *= 0.0254f;
         instrumentHeaders[i].offset[0] *= 0.0254f;
         instrumentHeaders[i].offset[1] *= 0.0254f;
         instrumentHeaders[i].offset[2] *= 0.0254f;
       }
       // Same for the content of the measure. The BSF file format seems to save the data in pound and pound-inch
       for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
       {
         scale[i*6] *= 4.4482216152605;
         scale[i*6+1] *= 4.4482216152605;
         scale[i*6+2] *= 4.4482216152605;
         scale[i*6+3] *= 0.1129848290276167;
         scale[i*6+4] *= 0.1129848290276167;
         scale[i*6+5] *= 0.1129848290276167;
       }
       output->SetPointUnit(Point::Marker, "m");
       output->SetPointUnit(Point::Force, "N");
       output->SetPointUnit(Point::Moment, "Nm");
     }
     Acquisition::AnalogIterator it = output->BeginAnalog();
     std::vector<float> corners(12*numberOfActivePlatforms, 0.0f);
     std::vector<float> origin(3*numberOfActivePlatforms, 0.0f);
     std::vector<int16_t> channel(6*numberOfActivePlatforms);
     std::string suffix = ((numberOfActivePlatforms == 1) ? "" : "1");
     float globalOrigin[3] = {0.0f, 0.0f, 0.0f};
     int numToAdaptChannelIndex = 0;
     for (int i = 0 ; i < numberOfActivePlatforms ; ++i)
     {
       (*it)->SetLabel("Fx" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6]);
       ++it;
       (*it)->SetLabel("Fy" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6+1]);
       ++it;
       (*it)->SetLabel("Fz" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Force));
       (*it)->SetScale(scale[i*6+2]);
       ++it;
       (*it)->SetLabel("Mx" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+3]);
       ++it;
       (*it)->SetLabel("My" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+4]);
       ++it;
       (*it)->SetLabel("Mz" + suffix);
       (*it)->SetUnit(output->GetPointUnit(Point::Moment));
       (*it)->SetScale(scale[i*6+5]);
       ++it;
       if (i > 0)
       {
         if ((instrumentHeaders[i].interDistance[0] == 0.0f) && (instrumentHeaders[i].interDistance[1] == 0.0f) && (instrumentHeaders[i].interDistance[2] == 0.0f))
         {
           btkWarningMacro(filename, "The distance with the previous force platform is set to 0. The platform is automatically shifted in the front of the previous. You might have to modify the origin of the force platform #" + ToString(i) + "in the metadata FORCE_PLATFORM:ORIGIN to locate it correctly in the global frame.");
           instrumentHeaders[i].interDistance[1] = static_cast<float>(instrumentHeaders[i].length + instrumentHeaders[i-1].length) / 2.0f;
         }
       }
       globalOrigin[0] += instrumentHeaders[i].interDistance[0];
       globalOrigin[1] += instrumentHeaders[i].interDistance[1];
       globalOrigin[2] += instrumentHeaders[i].interDistance[2];
       this->extractConfiguration(&(instrumentHeaders[i]), globalOrigin, &(channel[i*6]), &(corners[i*12]), &(origin[i*3]));
       for (int j = 0 ; j < 6 ; ++j)
         channel[i*6+j] += numToAdaptChannelIndex;
       numToAdaptChannelIndex += instrumentHeaders[i].numberOfChannels;
       suffix = ToString(i+2);
     }
     // Create the metadata FORCE_PLATFORM
     btk::MetaData::Pointer forcePlatform = btk::MetaData::New("FORCE_PLATFORM");
     output->GetMetaData()->AppendChild(forcePlatform);
     // - FORCE_PLATFORM:USED
     forcePlatform->AppendChild(btk::MetaData::New("USED", static_cast<int16_t>(numberOfActivePlatforms)));
     // - FORCE_PLATFORM:TYPE
     forcePlatform->AppendChild(btk::MetaData::New("TYPE", std::vector<int16_t>(numberOfActivePlatforms,2)));
     // - FORCE_PLATFORM:ZERO
     std::vector<int16_t> zero(2,0); zero[0] = 1;
     forcePlatform->AppendChild(btk::MetaData::New("ZERO", zero));
     // - FORCE_PLATFORM:CORNERS
     std::vector<uint8_t> dims(3); dims[0] = 3; dims[1] = 4; dims[2] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("CORNERS", dims, corners));
     // - FORCE_PLATFORM:ORIGIN
     dims.resize(2); dims[0] = 3; dims[1] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("ORIGIN", dims, origin));
     // - FORCE_PLATFORM:CHANNEL
     dims.resize(2); dims[0] = 6; dims[1] = numberOfActivePlatforms;
     forcePlatform->AppendChild(btk::MetaData::New("CHANNEL", dims, channel));
     // - FORCE_PLATFORM:CAL_MATRIX
     dims.resize(3); dims[0] = 6; dims[1] = 6; dims[2] = 0;
     forcePlatform->AppendChild(btk::MetaData::New("CAL_MATRIX", dims, std::vector<float>()));
     // Add a metadata to notify that the first frame was not set.
     MetaData::Pointer btkPointConfig = MetaDataCreateChild(output->GetMetaData(), "BTK_POINT_CONFIG");
     MetaDataCreateChild(btkPointConfig, "NO_FIRST_FRAME", static_cast<int8_t>(1));
     
     // Data
     // Note: We want the reaction of the measure, so all the data are multiplied by -1.
     for (int i = 0 ; i < output->GetAnalogFrameNumber() ; ++i)
     {
       int inc = 0;
       for (Acquisition::AnalogIterator it = output->BeginAnalog() ; it != output->EndAnalog() ; ++it)
         (*it)->GetValues().coeffRef(i) = -1.0f * static_cast<double>(bifs.ReadI16()) * scale[inc++]; 
     }
     
     // Cleaning
     delete[] scale;
   }
   catch (BinaryFileStreamFailure& )
   {
     std::string excmsg; 
     if (bifs.EndFile())
       excmsg = "Unexpected end of file.";
     else if (!bifs.IsOpen())
       excmsg = "Invalid file path.";
     else if(bifs.Bad())
       excmsg = "Loss of integrity of the file stream.";
     else if(bifs.Fail())
       excmsg = "Internal logic operation error on the stream associated with the file.";
     else
       excmsg = "Unknown error associated with the file stream.";
     
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException(excmsg));
   }
   catch (BSFFileIOException& )
   {
     if (bifs.IsOpen()) bifs.Close();
     throw;
   }
   catch (std::exception& e)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException("Unexpected exception occurred: " + std::string(e.what())));
   }
   catch(...)
   {
     if (bifs.IsOpen()) bifs.Close();
     throw(BSFFileIOException("Unknown exception"));
   }
 };