SampleReceiver/src/SampleWriter.h

196 lines
5.8 KiB
C++

//
// Created by epagris on 2021. 11. 01..
//
#ifndef WFR_SAMPLEWRITER_H
#define WFR_SAMPLEWRITER_H
#include <fstream>
#include <string>
#include <memory>
#include <iostream>
#include <sys/stat.h>
#include <vector>
#include <set>
#include <queue>
#include "Timestamp.h"
#define DIRSEP '/'
template<typename T> // T: sample datatype
class SampleWriter {
public:
static const std::string TS_FILENAME; // filename for timestamp file
static const std::string SAMPLE_FILENAME_PREFIX; // filename prefix for sample file
static const std::string FILE_EXT; // datafile extensions
public:
// timestamp record
struct TimestampRecord {
public:
uint32_t ts_s, ts_ns; // timestamp
public:
TimestampRecord &operator=(const Timestamp &ts) {
ts_s = (uint32_t) ts.s;
ts_ns = (uint32_t) ts.ns;
return *this;
}
};
private:
const size_t mChN; // number of channel PAIRS, immutable
const size_t mBlockSize; // buffer size, immutable
private:
std::shared_ptr<TimestampRecord> mpTs; // timestamp records
std::vector<std::shared_ptr<T>> mpSamples; // sample buffer array
std::string mTsFileName; // timestamp filename
std::ofstream mTsFile; // timestamp file
std::vector<std::string> mSampleFileNames; // sample file names
std::vector<std::ofstream> mSampleFiles; // sample output files
private:
Timestamp mTs[2]; // previous and current timestamp
private:
unsigned long long mSamplesN; // number of samples written until now
private:
// resize sample and record buffers
void prepareBuffers() {
// allocate timestamp data buffer
mpTs = std::shared_ptr<TimestampRecord>(new TimestampRecord[mBlockSize]);
// allocate sample data buffers
mpSamples.resize(mChN);
for (size_t i = 0; i < mChN; i++) {
mpSamples[i] = std::shared_ptr<T>(new T[mBlockSize]);
}
}
// prepare dataset environment
void prepareDatadir(const std::string &datasetName) {
struct stat sb;
if (stat(datasetName.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) { // if directory exists then leave it as it is
//unlink(datasetName.c_str()); FIXME
} else if (mkdir(datasetName.c_str(), 0777) == -1) { // else: create directory for dataset
throw std::runtime_error("Could not create directory " + datasetName + "!");
}
std::string datadir_dirsep = datasetName + DIRSEP;
// generate file names
mTsFileName = TS_FILENAME + '.' + FILE_EXT; // timestamp
// samples
mSampleFileNames.resize(mChN);
for (size_t i = 0; i < mChN; i++) {
mSampleFileNames[i] = SAMPLE_FILENAME_PREFIX + std::to_string(i) + '.' + FILE_EXT;
}
// open timestamp file
mTsFile.open(datadir_dirsep + mTsFileName, std::ios_base::binary);
if (!mTsFile.is_open()) {
throw std::runtime_error("Could not open timestamp file " + mTsFileName + "!");
}
// open sample files
mSampleFiles.resize(mChN);
for (size_t i = 0; i < mChN; i++) {
mSampleFiles[i].open(datadir_dirsep + mSampleFileNames[i], std::ios_base::binary);
if (!mSampleFiles[i].is_open()) {
throw std::runtime_error("Could not open sample file " + mSampleFileNames[i] + "!");
}
}
}
// close files
void closeFiles() {
// close file storing timestamp files
mTsFile.close();
// close files stroring samples
for (size_t i = 0; i < mChN; i++) {
mSampleFiles[i].close();
}
}
public:
explicit SampleWriter(const std::string &datasetName, size_t chN, size_t bufSize) : mChN(chN), mBlockSize(bufSize), mSamplesN(0) { // constr.
prepareBuffers();
prepareDatadir(datasetName);
}
~SampleWriter() {
closeFiles();
}
// add n samples to file
void addSamples(const T *pSamples, const Timestamp &ts) {
// shift FIFO and store new timestamp
mTs[1] = mTs[0];
mTs[0] = ts;
// if previous timestamp is empty, then don't store samples since time interpolation can not be done
if (mTs[1].empty()) {
return;
}
// calculate time step size
Timestamp d = (mTs[0] - mTs[1]) / (double) mBlockSize;
// timestamp for time interpolation
Timestamp ts_interp = mTs[1];
// get channel pointers
T *ppCh[mChN];
for (size_t i = 0; i < mChN; i++) {
ppCh[i] = mpSamples[i].get();
}
// extract samples
for (size_t i = 0; i < mBlockSize; i++) {
// select record
T *pRawSamples = (short *) (pSamples + i * mChN);
// store sample for a channel
for (size_t ch = 0; ch < mChN; ch++) {
ppCh[ch][i] = pRawSamples[ch];
}
// step time
ts_interp += d;
// store timestamp
mpTs.get()[i] = ts_interp;
}
// write to file newly created records
// write timestamps
mTsFile.write((const char *) mpTs.get(), mBlockSize * sizeof(TimestampRecord));
// write samples
for (size_t ch = 0; ch < mChN; ch++) {
mSampleFiles[ch].write((const char *) ppCh[ch], mBlockSize * sizeof(T));
}
// increment counter
mSamplesN += mBlockSize;
}
// get number of samples written out
unsigned long long getSampleCnt() const {
return mSamplesN;
}
};
// static fields
template<typename T>
const std::string SampleWriter<T>::TS_FILENAME = "ts";
template<typename T>
const std::string SampleWriter<T>::SAMPLE_FILENAME_PREFIX = "ch_";
template<typename T>
const std::string SampleWriter<T>::FILE_EXT = "dat";
#endif //WFR_SAMPLEWRITER_H