You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
11 KiB
C++

/***************************************************************************
* Copyright (C) 2009 by gempa GmbH *
* *
* All Rights Reserved. *
* *
* NOTICE: All information contained herein is, and remains *
* the property of gempa GmbH and its suppliers, if any. The intellectual *
* and technical concepts contained herein are proprietary to gempa GmbH *
* and its suppliers. *
* Dissemination of this information or reproduction of this material *
* is strictly forbidden unless prior written permission is obtained *
* from gempa GmbH. *
***************************************************************************/
#include <gempa/caps/rawpacket.h>
#include <gempa/caps/log.h>
#include <gempa/caps/riff.h>
#include <gempa/caps/utils.h>
using namespace std;
namespace {
#define DT2STR(prefix, dt) \
do { \
switch ( dt ) { \
case DT_DOUBLE: \
return prefix"DOUBLE"; \
case DT_FLOAT: \
return prefix"FLOAT"; \
case DT_INT64: \
return prefix"INT64"; \
case DT_INT32: \
return prefix"INT32"; \
case DT_INT16: \
return prefix"INT16"; \
case DT_INT8: \
return prefix"INT8"; \
default: \
break; \
} \
} \
while (0); \
return prefix"UNKWN"; \
}
namespace Gempa {
namespace CAPS {
namespace {
inline int sizeOf(DataType dt) {
switch ( dt ) {
case DT_DOUBLE:
return sizeof(double);
case DT_FLOAT:
return sizeof(float);
case DT_INT64:
return sizeof(int64_t);
case DT_INT32:
return sizeof(int32_t);
case DT_INT16:
return sizeof(int16_t);
case DT_INT8:
return sizeof(int8_t);
default:
break;
};
return 0;
}
inline Time getEndTime(const Time &stime, size_t count, const DataRecord::Header &header) {
if ( header.samplingFrequencyNumerator == 0 ||
header.samplingFrequencyDenominator == 0 ) return stime;
return stime + samplesToTimeSpan(header, count);
}
}
RawDataRecord::RawDataRecord() : _dataOfs(0), _dataSize(0), _dirty(false) {}
const char *RawDataRecord::formatName() const {
DT2STR("RAW/", _currentHeader.dataType)
}
bool RawDataRecord::readMetaData(std::streambuf &buf, int size, Header &header,
Time &startTime, Time &endTime) {
if ( !header.get(buf) ) return false;
_currentHeader.dataType = header.dataType;
size -= header.dataSize();
int dtsize = sizeOf(header.dataType);
if ( dtsize == 0 ) {
CAPS_WARNING("unknown data type: %d", header.dataType);
return false;
}
int count = size / dtsize;
startTime = timestampToTime(header.samplingTime);
endTime = getEndTime(startTime, count, header);
return true;
}
void RawDataRecord::setHeader(const Header &header) {
_header = header;
_currentHeader = _header;
_startTime = timestampToTime(_header.samplingTime);
_dirty = true;
}
const DataRecord::Header *RawDataRecord::header() const {
return &_header;
}
void RawDataRecord::setDataType(DataType dt) {
_header.dataType = dt;
_currentHeader = _header;
_dirty = true;
}
void RawDataRecord::setStartTime(const Time &time) {
_header.setSamplingTime(time);
_currentHeader = _header;
_startTime = time;
_dirty = true;
}
void RawDataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denominator) {
_header.samplingFrequencyNumerator = numerator;
_header.samplingFrequencyDenominator = denominator;
_currentHeader = _header;
_dirty = true;
}
Time RawDataRecord::startTime() const {
return _startTime;
}
Time RawDataRecord::endTime() const {
if ( _dirty ) {
_endTime = getEndTime(_startTime, _data.size() / sizeOf(_header.dataType), _header);
_dirty = false;
}
return _endTime;
}
bool RawDataRecord::canTrim() const {
return true;
}
bool RawDataRecord::canMerge() const {
return true;
}
bool RawDataRecord::trim(const Time &start, const Time &end) const {
if ( _header.samplingFrequencyNumerator <= 0 || _header.samplingFrequencyDenominator <= 0 )
return false;
// If the start time is behind the end time return false
if ( start > end ) return false;
// Initialize original values
int dataTypeSize = sizeOf(_header.dataType);
_dataSize = _data.size();
if ( dataTypeSize == 0 ) {
CAPS_WARNING("unknown data type: %d", _header.dataType);
return false;
}
_startTime = timestampToTime(_header.samplingTime);
_endTime = _startTime + samplesToTimeSpan(_header, _dataSize / dataTypeSize);
_dirty = false;
// Trim front
if ( start > _startTime ) {
TimeSpan ofs = start - _startTime;
int sampleOfs = timeSpanToSamplesCeil(_header, ofs);
_dataOfs = sampleOfs * dataTypeSize;
if ( _dataSize > _dataOfs )
_dataSize -= _dataOfs;
else
_dataSize = 0;
_startTime += samplesToTimeSpan(_header, sampleOfs);
timeToTimestamp(_currentHeader.samplingTime, _startTime);
}
else {
_currentHeader.samplingTime = _header.samplingTime;
_dataOfs = 0;
}
// Trim back
if ( end.valid() && (end < _endTime) ) {
TimeSpan ofs = _endTime - end;
int sampleOfs = timeSpanToSamplesFloor(_header, ofs);
_dataSize -= sampleOfs * dataTypeSize;
}
size_t sampleCount = _dataSize / dataTypeSize;
_endTime = _startTime + samplesToTimeSpan(_currentHeader, sampleCount);
CAPS_DEBUG("trimmed: %s ~ %s: %d samples",
_startTime.iso().c_str(),
_endTime.iso().c_str(), (int)sampleCount);
return true;
}
size_t RawDataRecord::dataSize(bool withHeader) const {
if ( withHeader )
return _dataSize + _header.dataSize();
else
return _dataSize;
}
DataRecord::ReadStatus RawDataRecord::get(streambuf &buf, int size,
const Time &start, const Time &end,
int maxBytes) {
size -= _header.dataSize();
if ( size < 0 ) return RS_Error;
if ( !_header.get(buf) ) {
CAPS_WARNING("invalid raw header");
return RS_Error;
}
return getData(buf, size, start, end, maxBytes);
}
DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
const Time &start, const Time &end,
int maxBytes) {
bool partial = false;
int sampleOfs;
int sampleCount;
int dataTypeSize = sizeOf(_header.dataType);
if ( dataTypeSize == 0 ) {
CAPS_WARNING("unknown data type: %d", _header.dataType);
return RS_Error;
}
sampleCount = size / dataTypeSize;
_startTime = timestampToTime(_header.samplingTime);
if ( _header.samplingFrequencyDenominator > 0 &&
_header.samplingFrequencyNumerator > 0 )
_endTime = _startTime + samplesToTimeSpan(_header, sampleCount);
else
_endTime = Time();
if ( (start.valid() || end.valid()) && _endTime.valid() ) {
// Out of bounds?
if ( end.valid() && (end <= _startTime) )
return RS_AfterTimeWindow;
if ( start.valid() && (start >= _endTime) )
return RS_BeforeTimeWindow;
// Trim packet front
if ( _startTime < start ) {
TimeSpan ofs = start - _startTime;
sampleOfs = timeSpanToSamplesCeil(_header, ofs);
sampleCount -= sampleOfs;
CAPS_DEBUG("Triming packet start: added offset of %d samples", sampleOfs);
_startTime += samplesToTimeSpan(_header, sampleOfs);
// Update header timespan
timeToTimestamp(_header.samplingTime, _startTime);
}
else
sampleOfs = 0;
if ( maxBytes > 0 ) {
int maxSamples = maxBytes / dataTypeSize;
// Here we need to clip the complete dataset and only
// return a partial record
if ( maxSamples < sampleCount ) {
CAPS_DEBUG("Clip %d available samples to %d", sampleCount, maxSamples);
_endTime -= samplesToTimeSpan(_header, sampleCount-maxSamples);
sampleCount = maxSamples;
partial = true;
}
}
// Trim back
if ( end.valid() && (end < _endTime) ) {
TimeSpan ofs = _endTime - end;
int trimEnd = timeSpanToSamplesFloor(_header, ofs);
sampleCount -= trimEnd;
_endTime = _startTime + samplesToTimeSpan(_header, sampleCount);
CAPS_DEBUG("Triming packet end: added offset of %d samples", trimEnd);
}
}
else
sampleOfs = 0;
if ( sampleCount == 0 ) return RS_Error;
_currentHeader = _header;
switch ( _header.dataType ) {
case DT_INT8:
{
// Stay with little endian data
RIFF::VectorChunk<1,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
}
break;
case DT_INT16:
{
// Stay with little endian data
RIFF::VectorChunk<2,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
}
break;
case DT_INT32:
case DT_FLOAT:
{
// Stay with little endian data
RIFF::VectorChunk<4,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
}
break;
case DT_INT64:
case DT_DOUBLE:
{
// Stay with little endian data
RIFF::VectorChunk<8,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
}
break;
default:
CAPS_ERROR("THIS SHOULD NEVER HAPPEN: ignored invalid data with type %d",
_header.dataType);
return RS_Error;
};
// Initialize indicies
_dataOfs = 0;
_dataSize = _data.size();
return partial?RS_Partial:RS_Complete;
}
bool RawDataRecord::put(std::streambuf &buf, bool withHeader) const {
if ( withHeader && !_currentHeader.put(buf) ) return false;
switch ( _header.dataType ) {
case DT_INT8:
{
// Data is already in little endian
RIFF::CPtrChunk<1,false> dataChunk(&_data[_dataOfs], _dataSize);
if ( !dataChunk.put(buf) )
return false;
}
break;
case DT_INT16:
{
// Data is already in little endian
RIFF::CPtrChunk<2,false> dataChunk(&_data[_dataOfs], _dataSize);
if ( !dataChunk.put(buf) )
return false;
}
break;
case DT_INT32:
case DT_FLOAT:
{
// Data is already in little endian
RIFF::CPtrChunk<4,false> dataChunk(&_data[_dataOfs], _dataSize);
if ( !dataChunk.put(buf) )
return false;
}
break;
case DT_INT64:
case DT_DOUBLE:
{
// Data is already in little endian
RIFF::CPtrChunk<8,false> dataChunk(&_data[_dataOfs], _dataSize);
if ( !dataChunk.put(buf) )
return false;
}
break;
default:
CAPS_ERROR("THIS SHOULD NEVER HAPPEN: ignored invalid data with type %d",
_header.dataType);
return false;
};
return true;
}
void RawDataRecord::setBuffer(const void *data, size_t size) {
_data.resize(size);
_dataSize = size;
_dataOfs = 0;
memcpy(_data.data(), data, size);
_dirty = true;
}
const char *FixedRawDataRecord::formatName() const {
DT2STR("FIXEDRAW/", _currentHeader.dataType)
}
}
}