/**
   * Read the file designated by @a filename and fill @a output.
   */
  void ANCFileIO::Read(const std::string& filename, Acquisition::Pointer output)
  {
    output->Reset();
    // Open the stream
    std::ifstream ifs;
    ifs.exceptions(std::ios_base::eofbit | std::ios_base::failbit | std::ios_base::badbit);
    try
    {
      std::string line;
      ifs.open(filename.c_str());
    // Check the first header keyword: "File_Type:"
      std::getline(ifs, line);
      if (line.substr(0,41).compare("File_Type:	Analog R/C ASCII	Generation#:	") != 0)
        throw(ANCFileIOException("Invalid ANC file."));
    // Check the file generation.
      this->m_Generation = FromString<int>(line.substr(41,1));
      if ((this->m_Generation != 1) && (this->m_Generation != 2))
        throw(ANCFileIOException("Unknown ANC file generation: " + line.substr(42,43) + "."));
    // Extract header data
      // Board_Type & Polarity
      std::getline(ifs, line);
      std::string boardType = this->ExtractKeywordValue(line, "Board_Type:	");
      std::string polarity = this->ExtractKeywordValue(line, "Polarity:	");
      // Trial_Name, Trial#, Duration(Sec.), #Channels
      std::getline(ifs, line);
      double duration = FromString<double>(this->ExtractKeywordValue(line, "Duration(Sec.):	"));
      size_t numberOfChannels = FromString<size_t>(this->ExtractKeywordValue(line, "#Channels:	"));

      // BitDepth & PreciseRate
      std::getline(ifs, line);
      int bitDepth = FromString<int>(this->ExtractKeywordValue(line, "BitDepth:	"));
      double preciseRate = FromString<double>(this->ExtractKeywordValue(line, "PreciseRate:	"));
      // Four next lines are empty
      std::getline(ifs, line);
      std::getline(ifs, line);
      std::getline(ifs, line);
      std::getline(ifs, line);

      // DEVELOPER CHECK
      // Check polarity's value. Only Bipolar is supported for the moment.
      if (polarity.compare("Bipolar") != 0)
        throw(ANCFileIOException("Unsupported ANC file. Only Bipolar board type is supported for the moment. Please, send an email to the developers to explain the problem."));

    // Extract data       
      if (numberOfChannels != 0)
      {
        // Analog channels' label
        std::list<std::string> labels, rates, ranges;
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Name", labels);
        size_t numberOfLabels = labels.size();
        if (numberOfChannels != numberOfLabels)
        {
          btkWarningMacro(filename, "Mismatch between the number of analog channels and the number of labels extracted. Final number of analog channels corresponds to the number of labels extracted.");
          numberOfChannels = numberOfLabels;
        }
        // Analog channels' rate
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Rate", rates);
        // Analog channels' range
        std::getline(ifs, line);
        this->ExtractDataInfo(line, "Range", ranges);
        double nf = duration * preciseRate; // Must be separate in two step due to some rounding errors
        size_t numberOfFrames = static_cast<size_t>(nf) + 1;
        // Data conversion
        std::vector<std::string> channelLabel(labels.size());
        std::vector<uint16_t> channelRate(rates.size());
        std::vector<uint16_t> channelRange(ranges.size());
        int inc = 0;
        for (std::list<std::string>::const_iterator it = rates.begin() ; it != rates.end() ; ++it)
          FromString(*it, channelRate[inc++]);
        inc = 0;
        for (std::list<std::string>::const_iterator it = ranges.begin() ; it != ranges.end() ; ++it)
          FromString(*it, channelRange[inc++]);
        inc = 0;
        for (std::list<std::string>::const_iterator it = labels.begin() ; it != labels.end() ; ++it)
          channelLabel[inc++] = *it;

        ANxFileIOCheckHeader_p(preciseRate, numberOfChannels, channelRate, channelRange);
        ANxFileIOStoreHeader_p(output, filename, preciseRate, numberOfFrames, numberOfChannels, channelLabel, channelRate, channelRange, boardType, bitDepth, this->m_Generation);
        
        // Extract values
        std::string buf;
        double val = 0.0;
        for(size_t i = 0 ; i < numberOfFrames ; ++i)
        {
          ifs >> buf; // Time's value          
          for (AnalogCollection::Iterator it = output->BeginAnalog() ; it != output->EndAnalog() ; ++it)
          {
            ifs >> val;
            (*it)->GetValues().coeffRef(static_cast<int>(i)) = val * (*it)->GetScale();
          }
        }
      }
    // 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));
    }
 /**
  * Read the file designated by @a filename and fill @a output.
  */
 void KistlerDATFileIO::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);
     // WARNING: The header key is maybe not a key but the number of force platforms in the file!
     if (bifs.ReadI32() != 2)
       throw(KistlerDATFileIOException("Wrong header key. It is not a proper Kistler DAT file."));
     
     // NOTE: It seems there are several headers in the file. A general header at the beginning
     // which resume acquisition's information (number of channels, number of frames, sample 
     // rate, ...) and another one for each force plate. It is not yet clear
     
     // General header?
     // ---------------
     // FIXME: What is the meaning of the next 12 bytes?
     bifs.SeekRead(12, BinaryFileStream::Current);
     // FIXME: What are the next (numel+4)*4 bytes, seems to be a table.
     int32_t tableDims[2];
     bifs.ReadI32(2,tableDims);
     int32_t numel = tableDims[0] * tableDims[1] * 4;
     bifs.SeekRead(4, BinaryFileStream::Current); // Space?
     bifs.SeekRead(numel, BinaryFileStream::Current); // Table data?
     bifs.SeekRead(4, BinaryFileStream::Current); // Space again?
     // FIXME: What is the meaning of the next 22 bytes?
     bifs.SeekRead(22, BinaryFileStream::Current);
     // The next offsets given in the comments start at 16+(numel+4)*4+22.
     // 0x00: (int32) Number of channels
     int numberOfChannels = bifs.ReadI32();
     // 0x04: (int32) Number of frames
     int numberOfFrames = bifs.ReadI32();
     // 0x08: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x12: (int32) ADC resolution? Number of ADC channels on the board?
     int digitalResolution = bifs.ReadI32();
     // 0x16: (double) Sample frequency
     double sampleFrequency = bifs.ReadDouble();
     // 0x24: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x28: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x32: Normalized force (N) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x36: Normalized length (mm) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x40: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x44: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x48: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x52: ?? (Could be an index) 
     bifs.SeekRead(2, BinaryFileStream::Current);
     // 0x54: (int16_t) Number of characters (n) in the next string
     // 0x56: (string) What is this string? - SKIPPED
     bifs.SeekRead(bifs.ReadU16(), BinaryFileStream::Current);
     
     // Force plate header?
     // -------------------
     // For simplification the adress in the description is reset to 0, but this is right after
     // the general header, so add 94+(numel+4)*4+n to find the good position in a file.
     // 0x00: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x04: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x08: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x12: (int32) Number of channels
     if (bifs.ReadI32() != numberOfChannels)
       throw(KistlerDATFileIOException("Wrong number of channels declared."));
     // 0x16: (int32) Number of frames
     if (bifs.ReadI32() != numberOfFrames)
       throw(KistlerDATFileIOException("Wrong number of frames declared."));
     // 0x20: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x24: (int32) ADC resolution
     if (bifs.ReadI32() != digitalResolution)
       throw(KistlerDATFileIOException("Wrong ADC resolution declared."));
     // 0x28: (double) sample frequency
     if (bifs.ReadDouble() != sampleFrequency)
       throw(KistlerDATFileIOException("Wrong sample frequency declared."));
     // 0x36: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x40: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x44: (int32) Normalized force (N) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x48: (int32) Normalized length (mm) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x52: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x56: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x60: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x64: (uint8) Number of characters (i) for the manufacturer of the force plateform
     // 0x65: (string) Manufacter of the force plateform - SKIPPED
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current);
     // 0x65+i: (int16) Number of characters (j) for the reference of the force plateform
     // 0x66+i: (string) Reference of the force plateform - SKIPPED
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current);
     // 0x66+i+j: (uint8) Number of characters (k) for the ? (serial number?)
     // 0x67+i+j: (string) (Serial number?) of the force plateform - SKIPPED
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current);
     // 0x67+i+j+k: (int32) Contact period start (sample #) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x71+i+j+k: (int32) Contact period end (sample #) - SKIPPED
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x75+i+j+k: ?? - Seems to be a double
     bifs.SeekRead(8, BinaryFileStream::Current);
     // 0x83+i+j+k: ??
     bifs.SeekRead(4, BinaryFileStream::Current);
     // 0x87+i+j+k: (double) FP width (mm)
     double width = bifs.ReadDouble();
     // 0x95+i+j+k: (double) FP length (mm)
     double length = bifs.ReadDouble();
     // 0x103+i+j+k: (double) FP a (mm)
     double a = bifs.ReadDouble();
     // 0x111+i+j+k: (double) FP b (mm)
     double b = bifs.ReadDouble();
     // What are the next 24 bytes? They are set to 0.
     bifs.SeekRead(24, BinaryFileStream::Current);
     // Why the parameter az0 is repeated two times?
     double az0 = bifs.ReadDouble();
     bifs.SeekRead(8, BinaryFileStream::Current); // az0 is written a second time
     // The next values are 3 strings which contain the word "INTEGRATED"
     // Maybe this is related to know if the component of each sensor is linked to built-in charge amplifier
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current); // Integrated
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current); // Integrated
     bifs.SeekRead(bifs.ReadU8(), BinaryFileStream::Current); // Integrated
     // Next double value could be the output voltage
     bifs.SeekRead(8, BinaryFileStream::Current);
     // Next 8 double values could be a scale factor for the amplifier (pC->V)
     // Blank of 64 bytes (could be for 8 other channels?)
     bifs.SeekRead(128, BinaryFileStream::Current);
     // Next 8 double values could be the calibrated partial range
     // Blank of 64 bytes (could be for 8 other channels?)
     bifs.SeekRead(128, BinaryFileStream::Current);
     // There is then 104 blank bytes
     bifs.SeekRead(104, BinaryFileStream::Current);
     
     // Initialize the output
     output->Init(0,numberOfFrames,numberOfChannels);
     output->SetPointFrequency(sampleFrequency);
     
     // Then the real (float) measured values (Fx12, Fx34, Fy14, Fy23, Fz1, Fz2, Fz4) directly in newtons
     // WARNING: These values are stored by channels and not by samples
     int inc = 0;
     const char* labels[8] = {"Fx12", "Fx34", "Fy14", "Fy23", "Fz1", "Fz2", "Fz3", "Fz4"};
     for (Acquisition::AnalogIterator it = output->BeginAnalog() ; it != output->EndAnalog() ; ++it)
     {
       (*it)->SetLabel(labels[inc++]);
       (*it)->SetUnit("N");
       for (int i = 0 ; i < numberOfFrames ; ++i)
         (*it)->GetValues().coeffRef(i) = -1.0 * bifs.ReadFloat(); // -1.0: BTK stores the reaction.
     }
     
     // Compute the origin and the coordinates of the corners
     std::vector<float> corners(12);
     std::vector<float> origin(3);
     // TODO: The next lines could be factorized with the code of the method AMTIForcePlatformFileIO::computeGeometryFromDimensions
     float cx = static_cast<float>(width) / 2.0f;
     float cy = static_cast<float>(length) / 2.0f;
     // Corners expressed in the global frame.
     // The global frame is set here as: axis X going forward, axis Y on the left and axis Z going upward.
     // The corners are set to have the corner #1 on the bottom left side, #2 on the top left side, #3 on the top right side and #4 on the bottom right side.
     corners[0] = cx;
     corners[1] = -cy;
     corners[2] = 0.0f;
     corners[3] = -cx;
     corners[4] = -cy;
     corners[5] = 0.0f;
     corners[6] = -cx;
     corners[7] = cy;
     corners[8] = 0.0f;
     corners[9] = cx;
     corners[10] = cy;
     corners[11] = 0.0f;
     // - Origin (expressed in the global frame) and centered above the origin of the global frame
     origin[0] = (float)a;
     origin[1] = (float)b;
     origin[2] = (float)az0;
     
     // 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>(1)));
     // - FORCE_PLATFORM:TYPE
     forcePlatform->AppendChild(btk::MetaData::New("TYPE", std::vector<int16_t>(1,3)));
     // - 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] = 1;
     forcePlatform->AppendChild(btk::MetaData::New("CORNERS", dims, corners));
     // - FORCE_PLATFORM:ORIGIN
     dims.resize(2); dims[0] = 3; dims[1] = 1;
     forcePlatform->AppendChild(btk::MetaData::New("ORIGIN", dims, origin));
     // - FORCE_PLATFORM:CHANNEL
     dims.resize(2); dims[0] = 8; dims[1] = 1;
     std::vector<int16_t> channel(8);
     for (int i = 0 ; i < 8 ; ++i)
       channel[i] = i+1;
     forcePlatform->AppendChild(btk::MetaData::New("CHANNEL", dims, channel));
     // 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));
   }
   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(KistlerDATFileIOException(excmsg));
   }
   catch (KistlerDATFileIOException& )
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw;
   }
   catch (std::exception& e)
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw(KistlerDATFileIOException("Unexpected exception occurred: " + std::string(e.what())));
   }
   catch(...)
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw(KistlerDATFileIOException("Unknown exception"));
   }
 };
 /**
  * Read the file designated by @a filename and fill @a output.
  */
 void GRxFileIO::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);
     uint16_t numFra = bifs.ReadU16();
     double freq = static_cast<double>(bifs.ReadU16());
     // Some values between the bytes 0x05 and 0x49...
     // Bytes 50-51: Number of frames in the main acquisition + 1 and multiplied by 10...
     // Bytes 52-53: 0x00
     bifs.SeekRead(50, BinaryFileStream::Current);
     // Bytes 54-55: First frame 
     double ff = bifs.ReadU16();
     // Bytes 56-57: 0x00
     // Bytes 58-59: Last frame
     // Bytes 60-135 Other values ...
     bifs.SeekRead(80, BinaryFileStream::Current);
     // Bytes 136-139: Length
     double length = bifs.ReadFloat();
     // Bytes 140-143: Width
     double width = bifs.ReadFloat();
     // Bytes 144-147: Height
     double height = bifs.ReadFloat();
     // Corners 1,2,3,4: coordinates X,Y,Z
     std::vector<float> cornersData = bifs.ReadFloat(3*4);
     
     output->Init(0, numFra, 6, 1);
     output->SetFirstFrame(static_cast<int>(ceil(ff / 1000.0 * freq + 1.0)));
     output->SetPointFrequency(freq);
     // Metadata FORCE_PLATFORM
     MetaData::Pointer fp = MetaDataCreateChild(output->GetMetaData(), "FORCE_PLATFORM");
     // Metadata FORCE_PLATFORM:USED
     MetaDataCreateChild(fp, "USED", (int16_t)1);
     // Metadata FORCE_PLATFORM:ZERO
     std::vector<int16_t> zeros = std::vector<int16_t>(2,0);
     MetaDataCreateChild(fp, "ZERO", zeros);
     // Metadata FORCE_PLATFORM:TYPE
     MetaDataCreateChild(fp, "TYPE")->SetInfo(MetaDataInfo::New(std::vector<uint8_t>(1,1), std::vector<int16_t>(1,1)));
     // Metadata FORCE_PLATFORM:CORNERS
     std::vector<uint8_t> cornersDim = std::vector<uint8_t>(3, 1); cornersDim[0] = 3; cornersDim[1] = 4;
     MetaDataCreateChild(fp, "CORNERS")->SetInfo(MetaDataInfo::New(cornersDim, cornersData));
     // Metadata FORCE_PLATFORM:ORIGIN
     std::vector<uint8_t> originDim = std::vector<uint8_t>(2, 1); originDim[0] = 3;
     std::vector<float> originVal = std::vector<float>(3);
     originVal[0] = static_cast<float>(length / 2.0);
     originVal[1] = static_cast<float>(width / 2.0);
     originVal[2] = static_cast<float>(-1.0 * height / 2.0);
     MetaDataCreateChild(fp, "ORIGIN")->SetInfo(MetaDataInfo::New(originDim, originVal));
     // Metadata FORCE_PLATFORM:CHANNEL
     std::vector<uint8_t> channelDim = std::vector<uint8_t>(2, 1); channelDim[0] = 6;
     std::vector<int16_t> channelData = std::vector<int16_t>(6,0);
     channelData[0] = 1; channelData[1] = 2; channelData[2] = 3; channelData[3] = 4; channelData[4] = 5; channelData[5] = 6;
     MetaDataCreateChild(fp, "CHANNEL")->SetInfo(MetaDataInfo::New(channelDim, channelData));
     
     bifs.SeekRead(512, BinaryFileStream::Begin);
     btk::Wrench::Pointer fpw = btk::Wrench::New(numFra);
     for (int i = 0 ; i < output->GetPointFrameNumber() ; ++i)
     {
       output->GetAnalog(1)->GetValues().coeffRef(i) = bifs.ReadFloat();
       output->GetAnalog(0)->GetValues().coeffRef(i) = -1.0 * bifs.ReadFloat();
       output->GetAnalog(2)->GetValues().coeffRef(i) = -1.0 * bifs.ReadFloat();
       output->GetAnalog(4)->GetValues().coeffRef(i) = bifs.ReadFloat() - width / 2.0;
       output->GetAnalog(3)->GetValues().coeffRef(i) = -1.0 * bifs.ReadFloat() + length / 2.0;
       output->GetAnalog(5)->GetValues().coeffRef(i) = bifs.ReadFloat();
     }
     
     // Label channels
     std::string str = std::string(1, 0x00); str[0] = *(filename.rbegin());
     output->GetAnalog(0)->SetLabel("Fx" + str);
     output->GetAnalog(1)->SetLabel("Fy" + str);
     output->GetAnalog(2)->SetLabel("Fz" + str);
     output->GetAnalog(3)->SetLabel("Px" + str);
     output->GetAnalog(4)->SetLabel("Py" + str);
     output->GetAnalog(5)->SetLabel("Mz" + str);
   }
   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(GRxFileIOException(excmsg));
   }
   catch (GRxFileIOException& )
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw;
   }
   catch (std::exception& e)
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw(GRxFileIOException("Unexpected exception occurred: " + std::string(e.what())));
   }
   catch(...)
   {
     if (bifs.IsOpen()) bifs.Close(); 
     throw(GRxFileIOException("Unknown exception"));
   }
 };
Example #4
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"));
   }
 };