/*************************************************************************** * 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 #include #include #include 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) } } }