/**
  * 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"));
   }
 };
 /**
  * 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 #3
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"));
   }
 };
 /**
  * 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"));
   }
 };
  /**
   * Create STL files based on the given file prefix (see SetFilePrefix()) and fill them with the given acquisition (see SetInputAcquisition()) and the given mesh (see SetInputMesh()). You can also set the frames to extract using the method SetFramesOfInterest().
   */
  void MultiSTLFileWriter::GenerateData()
  {
    if (this->m_FilePrefix.empty())
      throw MultiSTLFileWriterException("File prefix must be specified.");
    
    Acquisition::Pointer acquisition = this->GetInputAcquisition();
    if (!acquisition)
    {
      btkErrorMacro("Impossible to write a null input into a file (acquisition).");
      return;
    }
    TriangleMesh::Pointer mesh = this->GetInputMesh();
    if (!mesh)
    {
      btkErrorMacro("Impossible to write a null input into a file (mesh).");
      return;
    }
      
    int ff = (this->m_FOI[0] == -1 ? acquisition->GetFirstFrame() : this->m_FOI[0]);
    int lf = (this->m_FOI[1] == -1 ? acquisition->GetLastFrame() : this->m_FOI[1]);
    
    if (ff < acquisition->GetFirstFrame())
      throw MultiSTLFileWriterException("First frame out of range.");
    else if (lf > acquisition->GetLastFrame())
      throw MultiSTLFileWriterException("Last frame out of range.");
    
    if (mesh->GetMaxVertexId() > acquisition->GetPointNumber())
      throw MultiSTLFileWriterException("Invalid vertex ID.");

    // Try to link the mesh with the acquisition data
    if (!mesh->ConnectPoints(acquisition->GetPoints()))
      throw MultiSTLFileWriterException("Marker index out of range.");
    
    int num = btkNumberOfDigits(lf);
    try
    { 
      IEEELittleEndianBinaryFileStream obfs;
      for (int i = ff ; i <= lf ; ++i)
      {
        std::stringstream filename("");
        filename << this->m_FilePrefix << std::setw(num) << std::setfill('0') << i << ".stl";
        obfs.Open(filename.str(), BinaryFileStream::Out | BinaryFileStream::Truncate);
        if (!obfs.IsOpen())
          throw(MultiSTLFileWriterException("No File access. Are you sure of the path? Have you the right privileges?"));
        std::string header = "STL binary file generated by BTK " + std::string(BTK_VERSION_STRING);
        header.resize(80);
        obfs.Write(header);
        obfs.Write((int32_t)0); // Fake number of triangles
        mesh->SetCurrentFrameIndex(i - acquisition->GetFirstFrame());
        int32_t validFaceNumber = 0;
        for (TriangleMesh::FaceConstIterator it = mesh->BeginFace() ;  it != mesh->EndFace() ; ++it)
        {
          if (!it->IsValid())
            continue;
          // Normal vector: set to 0 => Will be computed by the viewer program
          obfs.Write(0.0f);
          obfs.Write(0.0f);
          obfs.Write(0.0f);
          // Vertex 1
          obfs.Write(static_cast<float>(it->GetVertex1()->GetCoordinateX()));
          obfs.Write(static_cast<float>(it->GetVertex1()->GetCoordinateY()));
          obfs.Write(static_cast<float>(it->GetVertex1()->GetCoordinateZ()));
          // Vertex 2
          obfs.Write(static_cast<float>(it->GetVertex2()->GetCoordinateX()));
          obfs.Write(static_cast<float>(it->GetVertex2()->GetCoordinateY()));
          obfs.Write(static_cast<float>(it->GetVertex2()->GetCoordinateZ()));
          // Vertex 3
          obfs.Write(static_cast<float>(it->GetVertex3()->GetCoordinateX()));
          obfs.Write(static_cast<float>(it->GetVertex3()->GetCoordinateY()));
          obfs.Write(static_cast<float>(it->GetVertex3()->GetCoordinateZ()));
          // Attribute byte count
          obfs.Write((uint16_t)0);
          ++validFaceNumber;
        }
        // Write the true number of triangles
        obfs.SeekRead(80, BinaryFileStream::Begin);
        obfs.Write(validFaceNumber);
        obfs.Close();
      }
    }
    catch (MultiSTLFileWriterException& )
    {
      throw;
    }
    catch (std::exception& e)
    {
      throw(MultiSTLFileWriterException("Unexpected exception occurred: " + std::string(e.what())));
    }
    catch(...)
    {
      throw(MultiSTLFileWriterException("Unknown exception"));
    }
  };