196 lines
5.8 KiB
C++
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
|