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.

389 lines
10 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/mseedpacket.h>
#include <gempa/caps/log.h>
#include <gempa/caps/utils.h>
#include <cstdio>
#include <streambuf>
#include <iostream>
#include <libmseed.h>
namespace {
#define LOG_SID(FUNC, format,...)\
do {\
char n[6];\
char s[6];\
char c[6];\
char l[6];\
ms_strncpclean(n, head.network, 2);\
ms_strncpclean(s, head.station, 5);\
ms_strncpclean(l, head.location, 2);\
ms_strncpclean(c, head.channel, 3);\
FUNC("[%s.%s.%s.%s] " format, n, s, l, c, ##__VA_ARGS__);\
} while(0)
}
namespace Gempa {
namespace CAPS {
MSEEDDataRecord::MSEEDDataRecord() {}
const char *MSEEDDataRecord::formatName() const {
return "MSEED";
}
bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime) {
fsdh_s head;
if ( size <= 0 ) {
CAPS_WARNING("read metadata: invalid size of record: %d", size);
return false;
}
if ( size < MINRECLEN || size > MAXRECLEN ) {
CAPS_WARNING("read metadata: invalid MSEED record size: %d", size);
return false;
}
// Read first 32 byte
size_t read = buf.sgetn((char*)&head, sizeof(head));
if ( read < sizeof(head) ) {
CAPS_WARNING("read metadata: input buffer underflow: only %d/%d bytes read",
(int)read, (int)sizeof(head));
return false;
}
if ( !MS_ISVALIDHEADER(((char*)&head)) ) {
CAPS_WARNING("read metadata: invalid MSEED header");
return false;
}
bool headerswapflag = false;
if ( !MS_ISVALIDYEARDAY(head.start_time.year, head.start_time.day) )
headerswapflag = true;
/* Swap byte order? */
if ( headerswapflag ) {
MS_SWAPBTIME(&head.start_time);
ms_gswap2a(&head.numsamples);
ms_gswap2a(&head.samprate_fact);
ms_gswap2a(&head.samprate_mult);
ms_gswap4a(&head.time_correct);
ms_gswap2a(&head.data_offset);
ms_gswap2a(&head.blockette_offset);
}
header.dataType = DT_Unknown;
hptime_t hptime = ms_btime2hptime(&head.start_time);
if ( hptime == HPTERROR ) {
LOG_SID(CAPS_DEBUG, "read metadata: invalid start time");
return false;
}
header.quality.ID = 0;
header.quality.str[0] = head.dataquality;
if ( head.time_correct != 0 && !(head.act_flags & 0x02) )
hptime += (hptime_t)head.time_correct * (HPTMODULUS / 10000);
// Parse blockettes
uint32_t blkt_offset = head.blockette_offset;
uint32_t blkt_length;
uint16_t blkt_type;
uint16_t next_blkt;
if ( blkt_offset < sizeof(head) ) {
LOG_SID(CAPS_DEBUG, "read metadata: blockette "
"offset points into header");
return false;
}
uint32_t coffs = 0;
while ( (blkt_offset != 0) && ((int)blkt_offset < size) &&
(blkt_offset < MAXRECLEN) ) {
char bhead[6];
int seek_ofs = blkt_offset-sizeof(head)-coffs;
buf.pubseekoff(seek_ofs, std::ios_base::cur, std::ios_base::in);
coffs += seek_ofs;
if ( buf.sgetn(bhead, 6) != 6 ) {
LOG_SID(CAPS_DEBUG, "read metadata: "
"failed to read blockette header");
break;
}
coffs += 6;
memcpy(&blkt_type, bhead, 2);
memcpy(&next_blkt, bhead+2, 2);
if ( headerswapflag ) {
ms_gswap2(&blkt_type);
ms_gswap2(&next_blkt);
}
blkt_length = ms_blktlen(blkt_type, bhead, headerswapflag);
if ( blkt_length == 0 ) {
LOG_SID(CAPS_DEBUG, "read metadata: "
"unknown blockette length for type %d",
blkt_type);
break;
}
/* Make sure blockette is contained within the msrecord buffer */
if ( (int)(blkt_offset - 4 + blkt_length) > size ) {
LOG_SID(CAPS_DEBUG, "read metadata: blockette "
"%d extends beyond record size, truncated?",
blkt_type);
break;
}
if ( blkt_type == 1000 ) {
switch ( (int)bhead[4] ) {
case DE_ASCII:
header.dataType = DT_INT8;
break;
case DE_INT16:
header.dataType = DT_INT16;
break;
case DE_INT32:
case DE_STEIM1:
case DE_STEIM2:
case DE_CDSN:
case DE_DWWSSN:
case DE_SRO:
header.dataType = DT_INT32;
break;
case DE_FLOAT32:
header.dataType = DT_FLOAT;
break;
case DE_FLOAT64:
header.dataType = DT_DOUBLE;
break;
case DE_GEOSCOPE24:
case DE_GEOSCOPE163:
case DE_GEOSCOPE164:
header.dataType = DT_FLOAT;
break;
default:
break;
}
}
else if ( blkt_type == 1001 ) {
// Add usec correction
hptime += ((hptime_t)bhead[5]) * (HPTMODULUS / 1000000);
}
/* Check that the next blockette offset is beyond the current blockette */
if ( next_blkt && next_blkt < (blkt_offset + blkt_length - 4) ) {
LOG_SID(CAPS_DEBUG, "read metadata: offset to "
"next blockette (%d) is within current blockette "
"ending at byte %d",
blkt_type, (blkt_offset + blkt_length - 4));
break;
}
/* Check that the offset is within record length */
else if ( next_blkt && next_blkt > size ) {
LOG_SID(CAPS_DEBUG, "read metadata: offset to "
"next blockette (%d) from type %d is beyond record "
"length", next_blkt, blkt_type);
break;
}
else
blkt_offset = next_blkt;
}
startTime = Time((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
endTime = startTime;
if ( head.samprate_fact > 0 ) {
header.samplingFrequencyNumerator = head.samprate_fact;
header.samplingFrequencyDenominator = 1;
}
else {
header.samplingFrequencyNumerator = 1;
header.samplingFrequencyDenominator = -head.samprate_fact;
}
if ( head.samprate_mult > 0 )
header.samplingFrequencyNumerator *= head.samprate_mult;
else
header.samplingFrequencyDenominator *= -head.samprate_mult;
if ( header.samplingFrequencyNumerator > 0.0 && head.numsamples > 0 ) {
hptime = (hptime_t)head.numsamples * HPTMODULUS * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
}
timeToTimestamp(header.samplingTime, startTime);
return true;
}
const DataRecord::Header *MSEEDDataRecord::header() const {
return &_header;
}
Time MSEEDDataRecord::startTime() const {
return _startTime;
}
Time MSEEDDataRecord::endTime() const {
return _endTime;
}
bool MSEEDDataRecord::canTrim() const {
return false;
}
bool MSEEDDataRecord::canMerge() const {
return false;
}
bool MSEEDDataRecord::trim(const Time &start,
const Time &end) const {
return false;
}
size_t MSEEDDataRecord::dataSize(bool /*withHeader*/) const {
return _data.size();
}
DataRecord::ReadStatus MSEEDDataRecord::get(std::streambuf &buf, int size,
const Time &start,
const Time &end,
int) {
//MSRecord *ms_rec = NULL;
if ( size <= 0 ) {
CAPS_WARNING("get: invalid size of record: %d", size);
return RS_Error;
}
_data.resize(size);
size_t read = buf.sgetn(&_data[0], _data.size());
if ( read != _data.size() ) {
CAPS_WARNING("get: input buffer underflow: only %d/%d bytes read",
(int)read, (int)_data.size());
return RS_Error;
}
arraybuf tmp(&_data[0], size);
readMetaData(tmp, size, _header, _startTime, _endTime);
if ( start.valid() || end.valid() ) {
// Out of scope?
if ( end.valid() && (end <= _startTime) )
return RS_AfterTimeWindow;
if ( start.valid() && (start >= _endTime) )
return RS_BeforeTimeWindow;
}
return RS_Complete;
}
bool MSEEDDataRecord::put(std::streambuf &buf, bool /*withHeader*/) const {
return buf.sputn(&_data[0], _data.size()) == (int)_data.size();
}
void MSEEDDataRecord::setData(const void *data, size_t size) {
_data.resize(size);
memcpy(_data.data(), data, size);
unpackHeader();
}
void MSEEDDataRecord::unpackHeader(char *data, size_t size) {
// Only unpack the header structure
MSRecord *ms_rec = NULL;
int state = msr_unpack(data, size, &ms_rec, 0, 0);
if ( state != MS_NOERROR ) {
CAPS_WARNING("read metadata: read error: %d", state);
if ( ms_rec != NULL )
msr_free(&ms_rec);
return;
}
hptime_t hptime = msr_starttime(ms_rec);
_startTime = Time((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
_endTime = _startTime;
if ( ms_rec->samprate > 0.0 && ms_rec->samplecnt > 0 ) {
hptime = (hptime_t)(((double)(ms_rec->samplecnt) / ms_rec->samprate * HPTMODULUS) + 0.5);
_endTime += TimeSpan((hptime_t)hptime/HPTMODULUS,(hptime_t)hptime%HPTMODULUS);
}
_header.dataType = DT_Unknown;
timeToTimestamp(_header.samplingTime, _startTime);
if ( ms_rec->fsdh->samprate_fact > 0 ) {
_header.samplingFrequencyNumerator = ms_rec->fsdh->samprate_fact;
_header.samplingFrequencyDenominator = 1;
}
else {
_header.samplingFrequencyNumerator = 1;
_header.samplingFrequencyDenominator = -ms_rec->fsdh->samprate_fact;
}
if ( ms_rec->fsdh->samprate_mult > 0 )
_header.samplingFrequencyNumerator *= ms_rec->fsdh->samprate_mult;
else
_header.samplingFrequencyDenominator *= -ms_rec->fsdh->samprate_mult;
switch ( ms_rec->sampletype ) {
case 'a':
_header.dataType = DT_INT8;
break;
case 'i':
_header.dataType = DT_INT32;
break;
case 'f':
_header.dataType = DT_FLOAT;
break;
case 'd':
_header.dataType = DT_DOUBLE;
break;
default:
_header.dataType = DT_Unknown;
break;
}
msr_free(&ms_rec);
}
}
}