//---------------------------------------------------------------------------// // // @file raweat.hpp // @author Mario Fink // @date Feb 2020, 2020-07-03 // @brief header only class for decoding the imc ".raw" format // //---------------------------------------------------------------------------// #ifndef RAW_EATER #define RAW_EATER #include #include #include #include #include #include #include #include #include // #include "endian.hpp" #include "hexshow.hpp" // support for 16bit floats // #include // #include // #include // #include "half.hpp" // #include "half_precision_floating_point.hpp" //---------------------------------------------------------------------------// class raw_eater { private: // filename and path std::string rawfile_; // raw buffer std::vector rawdata_; // file format markers std::map> markers_ = { {"intro marker",{0x7c,0x43,0x46}}, {"fileo marker",{0x7c,0x43,0x4b}}, {"vendo marker",{0x7c,0x4e,0x4f}}, {"param marker",{0x7c,0x43,0x47}}, {"sampl marker",{0x7c,0x43,0x44}}, {"typei marker",{0x7c,0x4e,0x54}}, {"dimen marker",{0x7c,0x43,0x43}}, {"datyp marker",{0x7c,0x43,0x50}}, {"punit marker",{0x7c,0x43,0x52}}, {"ename marker",{0x7c,0x43,0x4e}}, {"minma marker",{0x7c,0x43,0x62}}, {"datas marker",{0x7c,0x43,0x53}} }; // data sections corresponding to markers std::map> datasec_; // split segments into arrays of simple number/string element std::map> segments_; // length of data array unsigned long int datsize_; // save all data, i.e. physical values of measured entities as 64bit double std::vector datmes_; // check format validity bool valid_ = true; public: // constructor raw_eater(std::string rawfile, bool showlog = false) : rawfile_(rawfile) { // open file std::ifstream fin(rawfile_.c_str(),std::ifstream::binary); assert ( fin.good() && "failed to open file" ); // put data in buffer std::vector rawdata((std::istreambuf_iterator(fin)), (std::istreambuf_iterator())); rawdata_ = rawdata; // close file fin.close(); if ( showlog ) { // show raw data this->show_buffer(); // display predefined markers this->show_markers(); } // extract data corresponding to predefined markers from buffer find_markers(); // proceed only, if markers suggest data is valid *.raw format if ( valid_ ) { // show detected markers if ( showlog ) display_markers(); // split data corresponding to markers into segments split_segments(); // convert binary data to arrays of intrinsic data types convert_data(showlog); // check result if ( segments_.size() == 0 || datmes_.size() == 0 ) valid_ = false; } } // destructor ~raw_eater() { } // display buffer/data properties void show_buffer(int numel = 32) { hex::show(rawdata_,numel,0,0); // this->show_hex(rawdata_,32,0); } // show predefined markers void show_markers() { std::cout<<"\n"; for ( auto el: markers_ ) { std::cout<> mrk : markers_ ) { assert( mrk.second.size() > 0 && "please don't define any empty markers" ); // find marker's byte sequence in buffer for ( unsigned long int idx = 0; idx < rawdata_.size(); idx++ ) { // for every byte in buffer, check, if the three subsequent bytes // correspond to required predefined marker bool gotit = true; for ( unsigned long int mrkidx = 0; mrkidx < mrk.second.size() && gotit; mrkidx ++ ) { if ( ! (mrk.second[mrkidx] == rawdata_[idx+mrkidx]) ) gotit = false; } // if we got the marker, collect following bytes until end of marker byte 0x3b if ( gotit ) { // array of data associated to marker std::vector markseq; if ( mrk.first != "datas marker" ) { // collect bytes until we find semicolon ";", i.e. 0x3b int seqidx = 0; while ( rawdata_[idx+seqidx] != 0x3b ) { markseq.push_back(rawdata_[idx+seqidx]); seqidx++; } } else { // marker 'datas' is the data marker and is supposed to be the last // and should extend until end of file for ( unsigned long int didx = idx; didx < rawdata_.size()-1; didx++ ) { markseq.push_back(rawdata_[didx]); } // obtain length of data segment datsize_ = markseq.size(); } // save segment corresponding to marker datasec_.insert(std::pair>(mrk.first,markseq)); } } } // check length of all markers, i.e. check if we actually have a valid .raw file unsigned long int totalmarksize = 0; for (std::pair> mrk : markers_ ) { //assert ( datasec_[mrk.first].size() > 0 && "marker segment of length zero" ); if ( datasec_[mrk.first].size() == 0 ) { std::cout<<"warning: "< 0 && "didn't find any predefined marker => probably not a valid .raw-file" ); if ( totalmarksize < 100 ) valid_ = false; } // display content of found markers void display_markers() { for (std::pair> mrk : markers_ ) { // extract data of marker std::vector mrkdat = this->datasec_[mrk.first]; // show name of marker and its data std::cout<> get_markers() { return markers_; } // get data associated to specific marker std::vector get_marker_data(std::string marker) { return datasec_[marker]; } // split data segments into arrays void split_segments() { assert( datasec_.size() > 0 ); assert( segments_.size() == 0 ); // split segments of all markers for (std::pair> mrk : markers_ ) { // declare empty array for this segment and auxiliary string std::vector segvec; std::string elstr(""); // only start collecting after first comma in segment bool parse = false; // count number of commata long int commcount = 0; // parse data segment for ( unsigned char el: datasec_[mrk.first] ) { // note that data segment of "datas marker" may contain any number // of 0x2c's (0x2c = comma ',') if ( ( el != 0x2c && parse ) || ( mrk.first == "datas marker" && commcount > 2 ) ) { elstr.push_back(el); } else if ( el == 0x2c && parse ) { // comma marks end of element of segment: save string and reset it segvec.push_back(elstr); elstr = std::string(""); commcount++; } else { // enable parsing after first comma if ( el == 0x2c ) parse = true; } } // include last element segvec.push_back(elstr); // save array of elements segments_.insert(std::pair>(mrk.first,segvec));; } } //-------------------------------------------------------------------------// // convert actual measurement data void convert_data(bool showlog = false) { assert( segments_.size() > 0 && "extract markers and separate into segments before conversion!" ); assert( datmes_.size() == 0 ); // by convention, the actual data is the 4th element in respective segment std::string datstr = segments_["datas marker"][3]; std::vector datbuf(datstr.begin(),datstr.end()); // retrieve datatype from datatype segment int dattype = std::stoi(segments_["datyp marker"][4]); int typesize = std::stoi(segments_["datyp marker"][5]); // retrieve transformation index, factor and offset int trafo = 0; double factor = 1., offset = 0.; if ( segments_["punit marker"].size() > 4 ) { trafo = std::stoi(segments_["punit marker"][2]); factor = std::stod(segments_["punit marker"][3]); offset = std::stod(segments_["punit marker"][4]); } if ( showlog ) { std::cout<<"dattype: "< 2 && dattype < 12 ); if ( dattype == 9 || dattype == 10 || dattype > 11 ) { valid_ = false; } else { // switch for datatypes switch ( dattype ) { case 1 : assert ( sizeof(unsigned char)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 2 : assert ( sizeof(signed char)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 3 : assert ( sizeof(unsigned short int)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 4 : assert ( sizeof(signed short int)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 5 : assert ( sizeof(unsigned long int)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 6 : std::cout<<"warning: 'signed long int' datatype with experimental support\n"; // assert ( sizeof(signed long int)*8 == typesize ); // convert_data_as_type(datbuf,factor,offset); assert ( sizeof(int)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 7 : assert ( sizeof(float)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 8 : assert ( sizeof(double)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; case 9 : std::cerr<<"'imc Devices Transitional Recording' datatype not supported\n"; break; case 10 : std::cerr<<"'Timestamp Ascii' datatype not supported\n"; break; case 11 : std::cout<<"warning: '2-Byte-Word digital' datatype with experimental support\n"; assert ( sizeof(short int)*8 == typesize ); convert_data_as_type(datbuf,factor,offset); break; } // show excerpt of result if ( showlog ) { std::cout<<"\nlength of data: "< void convert_data_as_type(std::vector &datbuf, double factor, double offset) { // check consistency of bufffer size with size of datatype assert ( datbuf.size()%sizeof(dattype) == 0 && "length of buffer is not a multiple of size of datatype" ); // get number of numbers in buffer unsigned long int totnum = datbuf.size()/sizeof(dattype); for ( unsigned long int numfl = 0; numfl < totnum; numfl++ ) { // declare instance of required datatype and perform recast in terms of uint8_t dattype num; uint8_t* pnum = reinterpret_cast(&num); // parse all bytes of the number for ( int byi = 0; byi < (int)sizeof(dattype); byi++ ) { pnum[byi] = (int)datbuf[(unsigned long int)(numfl*sizeof(dattype)+byi)]; } // add number to array datmes_.push_back((double)num * factor + offset); } } //-------------------------------------------------------------------------// // show hex dump void show_hex(std::vector &datavec, int width = 32, unsigned long int maxchars = 512) { // compose hex string and encoded string std::stringstream hex, enc; for ( unsigned long int i = 0; i < datavec.size() && i < maxchars; i++ ) { // accumulate in stringstreams hex< 0x20 && ic < 0x7f ) { enc<<(char)(datavec[i]); } else { enc<<"."; } // every 'width' number of chars constitute a row if ( (int)(i+1)%width == 0 ) { // print both strings std::cout< 0 ); return valid_ ? std::stod(segments_["sampl marker"][2]) : 0.0; } // get time unit std::string get_temp_unit() { // assert ( segments_.size() > 0 ); return valid_ ? segments_["sampl marker"][5] : std::string("None"); } // get name of measured entity std::string get_name() { // assert ( segments_.size() > 0 ); return valid_ ? segments_["ename marker"][6] : std::string("None"); } // get unit of measured entity std::string get_unit() { // assert ( segments_.size() > 0 ); if ( datasec_["punit marker"].size() > 0 && segments_["punit marker"].size() > 6 ) { return valid_ ? segments_["punit marker"][7] : std::string("None"); } else { return std::string("None"); } } // get time offset double get_time_offset() { // assert ( segments_.size() > 0 ); return valid_ ? std::stod(segments_["minma marker"][11]) : -1.0; } // get time array std::vector get_time() { // assert ( datmes_.size() > 0 ); if ( valid_ ) { // declare array of time std::vector timearr; // get time step and offset double dt = get_dt(); double timoff = get_time_offset(); // fill array for ( unsigned long int t = 0; t < datmes_.size(); t++ ) { timearr.push_back(timoff + t*dt); } return timearr; } else { return std::vector(); } } // get size/length of data unsigned long int get_n() { return datmes_.size(); } // get data array encoded as floats/doubles std::vector& get_data() { // assert ( datmes_.size() > 0 ); return datmes_; } // get segment's array of elements std::vector get_segment(std::string marker) { assert ( segments_.size() > 0 ); return segments_[marker]; } // write csv file void write_table(std::string filename, char delimiter = ',', int precision = 6, int width = 25) { if ( valid_ ) { // open file std::ofstream fout(filename.c_str()); // define column names (including units) std::string colA = std::string("Time [") + get_temp_unit() + std::string("]"); std::string colB = get_name() + std::string(" [") + get_unit() + std::string("]"); // write header if ( delimiter != ' ' ) { fout<