/* CompassFile.cpp Wrapper class around a shared pointer to an ifstream. Here the shared pointer is used to overcome limitations of the ifstream class, namely that it is written such that ifstream cannot be modified by move semantics. Contains all information needed to parse a single binary CompassFile. Currently has a class wide defined buffer size; may want to make this user input in the future. Written by G.W. McCann Oct. 2020 */ #include "EventBuilder.h" #include "CompassFile.h" namespace EventBuilder { CompassFile::CompassFile() : m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true), m_hitsize(0), m_buffersize(0), m_file(std::make_shared()), m_eofFlag(false) { } CompassFile::CompassFile(const std::string& filename) : m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true), m_hitsize(0), m_buffersize(0), m_file(std::make_shared()), m_eofFlag(false) { Open(filename); } CompassFile::CompassFile(const std::string& filename, int bsize) : m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true), m_bufsize(bsize), m_hitsize(0), m_buffersize(0), m_file(std::make_shared()), m_eofFlag(false) { Open(filename); } CompassFile::~CompassFile() { Close(); } void CompassFile::Open(const std::string& filename) { m_eofFlag = false; m_hitUsedFlag = true; m_filename = filename; m_file->open(m_filename, std::ios::binary | std::ios::in); m_file->seekg(0, std::ios_base::end); m_size = m_file->tellg(); if(m_size == 0) { m_eofFlag = true; } else { m_file->seekg(0, std::ios_base::beg); ReadHeader(); m_nHits = m_size/m_hitsize; m_buffersize = m_hitsize*m_bufsize; m_hitBuffer.resize(m_buffersize); } } void CompassFile::Close() { if(IsOpen()) { m_file->close(); } } void CompassFile::ReadHeader() { if(!IsOpen()) { EVB_WARN("Unable to read header from file. State not validated", m_filename); return; } char* header = new char[2]; m_file->read(header, 2); m_header = *((uint16_t*)header); m_hitsize = 16; //default hitsize of 16 bytes if(IsEnergy()) m_hitsize += 2; if(IsEnergyCalibrated()) m_hitsize += 8; if(IsEnergyShort()) m_hitsize += 2; if(IsWaves()) { EVB_ERROR("Waveforms are not supported by the SPS_SABRE_EventBuilder. The wave data will be skipped."); m_hitsize += 5; char* firstHit = new char[m_hitsize]; //A compass hit by default has 24 bytes (at least in our setup) m_file->read(firstHit, 24); firstHit += m_hitsize - 4; uint32_t nsamples = *((uint32_t*) firstHit); m_hitsize += nsamples * 2; //Each sample is a 2 byte data value m_file->seekg(0, std::ios_base::beg); m_file->read(header, 2); delete[] firstHit; } delete[] header; } /* GetNextHit() is the function which... gets the next hit Has to check if the buffer needs refilled/filled for the first time Upon pulling a hit, sets the UsedFlag to false, letting the next level know that the hit should be free game. If the file cannot be opened, signals as though file is EOF */ bool CompassFile::GetNextHit() { if(!IsOpen()) return true; if((m_bufferIter == nullptr || m_bufferIter == m_bufferEnd) && !IsEOF()) { GetNextBuffer(); } if(!IsEOF()) { ParseNextHit(); m_hitUsedFlag = false; } return m_eofFlag; } /* GetNextBuffer() ... self-explanatory name Note tht this is where the EOF flag is set. The EOF is only singaled after the LAST buffer is completely read (i.e literally no more data). ifstream sets its eof bit upon pulling the last buffer, but this class waits until that entire last buffer is read to singal EOF (the true end of file). */ void CompassFile::GetNextBuffer() { if(m_file->eof()) { m_eofFlag = true; return; } m_file->read(m_hitBuffer.data(), m_hitBuffer.size()); m_bufferIter = m_hitBuffer.data(); m_bufferEnd = m_bufferIter + m_file->gcount(); //one past the last datum } void CompassFile::ParseNextHit() { m_currentHit.board = *((uint16_t*)m_bufferIter); m_bufferIter += 2; m_currentHit.channel = *((uint16_t*)m_bufferIter); m_bufferIter += 2; m_currentHit.timestamp = *((uint64_t*)m_bufferIter); m_bufferIter += 8; if(IsEnergy()) { m_currentHit.energy = *((uint16_t*)m_bufferIter); m_bufferIter += 2; } if(IsEnergyCalibrated()) { m_currentHit.energyCalibrated = *((uint64_t*)m_bufferIter); m_bufferIter += 8; } if(IsEnergyShort()) { m_currentHit.energyShort = *((uint16_t*)m_bufferIter); m_bufferIter += 2; } m_currentHit.flags = *((uint32_t*)m_bufferIter); m_bufferIter += 4; if(IsWaves()) { m_currentHit.waveCode = *((uint8_t*)m_bufferIter); m_bufferIter += 1; m_currentHit.Ns = *((uint32_t*)m_bufferIter); m_bufferIter += 4; m_bufferIter += 2*m_currentHit.Ns; //Skip wavedata for SPS_SABRE_EventBuilder } if(m_smap != nullptr) { //memory safety int gchan = m_currentHit.channel + m_currentHit.board*16; m_currentHit.timestamp += m_smap->GetShift(gchan); } } }