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