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); };
/** * 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")); } };