Initial commit based on common repo commit ffeb9c9b
This commit is contained in:
150
libs/gempa/caps/CHANGELOG.md
Normal file
150
libs/gempa/caps/CHANGELOG.md
Normal file
@ -0,0 +1,150 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to the CAPS client library will be documented in this file.
|
||||
|
||||
## 2021-04-21 1.0.0
|
||||
### Changed
|
||||
- Set library version to 1.0.0
|
||||
|
||||
## 2021-02-16
|
||||
### Added
|
||||
- Allow to set maximum allowed future end time via plugin API. In additon
|
||||
add commandline and config support to the plugin application class e.g.
|
||||
```
|
||||
output.maxFutureEndTime = 120
|
||||
```
|
||||
By default the option is set to 120 seconds.
|
||||
|
||||
## 2020-12-14
|
||||
### Added
|
||||
- Support to set miniSEED record length via API
|
||||
|
||||
## 2020-09-22
|
||||
### Changed
|
||||
- Use last sample time as reference time for maximum future time check. Before
|
||||
we used the packet end time as reference time but this makes no sense in case
|
||||
of low sampled data.
|
||||
- So far we used the current time as start time if journal entries exceeded the
|
||||
maximum allowed future time. With this release we use the journal time no
|
||||
matter what its value but display a warning when the journal time is more than
|
||||
one day in the future.
|
||||
|
||||
## 2020-06-22
|
||||
### Added
|
||||
- Python3 byte array support to any record push
|
||||
|
||||
## 2020-02-21
|
||||
### Changed
|
||||
- Increase default timeout for acknowledgement messages from 5s to 60s
|
||||
|
||||
## 2019-09-24
|
||||
### Fixed
|
||||
- Fix high load if packets could not be sent to CAPS. In that case the plugin
|
||||
automatically reconnects after some amount of time. If triggered under certain
|
||||
circumstances this delay was not in effect and caused unnecessarily high
|
||||
amount of connection attempts. Under some circumstances the plugin could have
|
||||
crashed due to a stack overflow.
|
||||
- The quit method has now the semantics to continue pushing packets as long
|
||||
as the connection is established and only abort in case of an error without
|
||||
attempting a reconnect.
|
||||
|
||||
## 2019-09-20
|
||||
### Fixed
|
||||
- Fix error string if not all data could be sent to the server
|
||||
|
||||
## 2019-08-19
|
||||
### Changed
|
||||
- Discard packets whose end time is more than 120 seconds before the system time.
|
||||
|
||||
## 2019-08-06
|
||||
### Added
|
||||
- new config option ``output.addr``
|
||||
|
||||
### Fixed
|
||||
- ambiguous command line option ``-h``. With this version of the library the
|
||||
host and port can be set via the command line option ``--addr``.
|
||||
- wrong config option parsing
|
||||
|
||||
## 2019-08-05
|
||||
### Fixed
|
||||
- seg fault in date time parser
|
||||
|
||||
## 2019-08-02
|
||||
### Fixed
|
||||
- Hanging TCP connections. In case of the remote side does not shutdown cleanly
|
||||
the plugin did not notice that the connection is no longer available. With this
|
||||
version the plugin reconnects to the server when the TCP send buffer is full and
|
||||
tries to send all not acknowledged packets again.
|
||||
- Do not discard packets if the packet buffer is full. Instead we block until
|
||||
the server acknowledges some packets.
|
||||
|
||||
### Changed
|
||||
- The plugin application class checks whether the configured buffer size is
|
||||
below the minimum.
|
||||
|
||||
## 2019-07-05
|
||||
### Changed
|
||||
- Ignore journal entries where the timestamp is more than 10 seconds
|
||||
before the system time.
|
||||
|
||||
## 2018-12-19
|
||||
### Fixed
|
||||
- Read journal from file in plugin application.
|
||||
|
||||
## 2018-12-18
|
||||
### Fixed
|
||||
- Do not reconnect if the plugin buffer is full. Instead of we try to read
|
||||
acknowledgements from the CAPS server until the plugin buffer is below the
|
||||
threshold.
|
||||
|
||||
## 2018-12-17
|
||||
### Added
|
||||
- Support to retrieve status information e.g. the number of buffered bytes from
|
||||
plugin.
|
||||
|
||||
## 2018-09-06
|
||||
### Changed
|
||||
- Enable more verbose logging for MSEED packets
|
||||
|
||||
## 2018-07-25
|
||||
### Fixed
|
||||
- unset variable of the raw data record
|
||||
- trim function will return false in case of an unknown datatype
|
||||
|
||||
## 2018-05-30
|
||||
### Fixed
|
||||
- Fixed unexpected closed SSL connections
|
||||
|
||||
## 2018-06-05
|
||||
### Fixed
|
||||
- Fix RawDataRecord::setHeader
|
||||
|
||||
## 2018-05-16
|
||||
### Fixed
|
||||
- RAW data end time calculation
|
||||
|
||||
## 2018-03-19
|
||||
### Added
|
||||
- SSL support
|
||||
|
||||
## 2017-11-20
|
||||
### Added
|
||||
- float and double support for Steim encoders. All values will be converted implicitly
|
||||
to int 32 values
|
||||
|
||||
## 2017-11-08
|
||||
### Added
|
||||
- timing quality parameter to push call. By default the timing quality is set to -1.
|
||||
|
||||
## 2017-11-07
|
||||
### Fixed
|
||||
- do not flush encoders after reconnect
|
||||
|
||||
## 2017-10-26
|
||||
### Added
|
||||
- SSL support
|
||||
|
||||
|
||||
## 2017-10-24
|
||||
### Fixed
|
||||
- packet synchronization error after reconnect
|
22
libs/gempa/caps/CMakeLists.txt
Normal file
22
libs/gempa/caps/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
SET(PACKAGE_NAME LIB_CAPS)
|
||||
SET(LIB_NAME capsclient)
|
||||
|
||||
FILE(GLOB ${PACKAGE_NAME}_SOURCES
|
||||
"*.cpp"
|
||||
"mseed/*.cpp"
|
||||
)
|
||||
|
||||
LIST(FILTER ${PACKAGE_NAME}_SOURCES EXCLUDE REGEX pluginapplication.cpp)
|
||||
|
||||
IF(WIN32)
|
||||
SET(${PACKAGE_NAME}_SOURCES ${${PACKAGE_NAME}_SOURCES} strptime.c)
|
||||
ENDIF(WIN32)
|
||||
|
||||
INCLUDE_DIRECTORIES(../../3rd-party/mseed)
|
||||
|
||||
ADD_LIBRARY(${LIB_NAME} SHARED ${${PACKAGE_NAME}_SOURCES})
|
||||
SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES COMPILE_FLAGS -fPIC)
|
||||
SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES VERSION 1.0.0 SOVERSION 1)
|
||||
TARGET_LINK_LIBRARIES(${LIB_NAME} mseed)
|
||||
|
||||
INSTALL(TARGETS ${LIB_NAME} DESTINATION lib)
|
197
libs/gempa/caps/anypacket.cpp
Normal file
197
libs/gempa/caps/anypacket.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 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/anypacket.h>
|
||||
#include <gempa/caps/riff.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <streambuf>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
bool AnyDataRecord::AnyHeader::put(std::streambuf &buf) const {
|
||||
Endianess::Writer put(buf);
|
||||
put(type, sizeof(type)-1);
|
||||
dataHeader.put(buf);
|
||||
|
||||
put(endTime.year);
|
||||
put(endTime.yday);
|
||||
put(endTime.hour);
|
||||
put(endTime.minute);
|
||||
put(endTime.second);
|
||||
put(endTime.usec);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
|
||||
AnyDataRecord::AnyDataRecord() {
|
||||
strncpy(_header.type, "ANY", sizeof(_header.type));
|
||||
_header.dataHeader.samplingFrequencyDenominator = 0;
|
||||
_header.dataHeader.samplingFrequencyNumerator = 0;
|
||||
// Just a bunch of bytes
|
||||
_header.dataHeader.dataType = DT_INT8;
|
||||
}
|
||||
|
||||
bool AnyDataRecord::setType(const char *type) {
|
||||
strncpy(_header.type, type, sizeof(_header.type));
|
||||
|
||||
// Input clipped?
|
||||
if ( _header.type[sizeof(_header.type)-1] != '\0' ) {
|
||||
_header.type[sizeof(_header.type)-1] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const char *AnyDataRecord::type() const {
|
||||
return _header.type;
|
||||
}
|
||||
|
||||
void AnyDataRecord::setStartTime(const Time &time) {
|
||||
timeToTimestamp(_header.dataHeader.samplingTime, time);
|
||||
_startTime = time;
|
||||
}
|
||||
|
||||
|
||||
void AnyDataRecord::setEndTime(const Time &time) {
|
||||
timeToTimestamp(_header.endTime, time);
|
||||
_endTime = time;
|
||||
}
|
||||
|
||||
|
||||
void AnyDataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denominator) {
|
||||
_header.dataHeader.samplingFrequencyNumerator = numerator;
|
||||
_header.dataHeader.samplingFrequencyDenominator = denominator;
|
||||
}
|
||||
|
||||
|
||||
const char *AnyDataRecord::formatName() const {
|
||||
return "ANY";
|
||||
}
|
||||
|
||||
|
||||
bool AnyDataRecord::readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) {
|
||||
// Read record type
|
||||
buf.sgetn(_header.type, 4);
|
||||
_header.type[sizeof(_header.type)-1] = '\0';
|
||||
|
||||
size -= sizeof(_header.type)-1;
|
||||
|
||||
if ( !header.get(buf) ) return false;
|
||||
|
||||
TimeStamp tmp;
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(tmp.year);
|
||||
get(tmp.yday);
|
||||
get(tmp.hour);
|
||||
get(tmp.minute);
|
||||
get(tmp.second);
|
||||
get(tmp.usec);
|
||||
|
||||
startTime = timestampToTime(header.samplingTime);
|
||||
endTime = timestampToTime(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const DataRecord::Header *AnyDataRecord::header() const {
|
||||
return &_header.dataHeader;
|
||||
}
|
||||
|
||||
|
||||
Time AnyDataRecord::startTime() const {
|
||||
return _startTime;
|
||||
}
|
||||
|
||||
|
||||
Time AnyDataRecord::endTime() const {
|
||||
return _endTime;
|
||||
}
|
||||
|
||||
|
||||
bool AnyDataRecord::canTrim() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool AnyDataRecord::canMerge() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool AnyDataRecord::trim(const Time &start,
|
||||
const Time &end) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t AnyDataRecord::dataSize(bool withHeader) const {
|
||||
if ( withHeader )
|
||||
return _data.size() + _header.dataSize();
|
||||
else
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
|
||||
DataRecord::ReadStatus AnyDataRecord::get(std::streambuf &buf, int size,
|
||||
const Time &start, const Time &end,
|
||||
int) {
|
||||
_data.clear();
|
||||
size -= _header.dataSize();
|
||||
if ( size < 0 ) return RS_Error;
|
||||
if ( !_header.get(buf) ) return RS_Error;
|
||||
|
||||
_startTime = timestampToTime(_header.dataHeader.samplingTime);
|
||||
_endTime = timestampToTime(_header.endTime);
|
||||
|
||||
if ( start.valid() ) {
|
||||
if ( _endTime < start || (_startTime < start && _endTime == start) )
|
||||
return RS_BeforeTimeWindow;
|
||||
}
|
||||
|
||||
if ( end.valid() ) {
|
||||
if ( _startTime >= end ) return RS_AfterTimeWindow;
|
||||
}
|
||||
|
||||
RIFF::VectorChunk<1,false> dataChunk(_data, 0, size);
|
||||
if ( !dataChunk.get(buf, size) ) return RS_Error;
|
||||
|
||||
return RS_Complete;
|
||||
}
|
||||
|
||||
|
||||
bool AnyDataRecord::put(std::streambuf &buf, bool withHeader) const {
|
||||
if ( withHeader && !_header.put(buf) ) return false;
|
||||
return (int)buf.sputn(_data.data(), _data.size()) == (int)_data.size();
|
||||
}
|
||||
|
||||
void AnyDataRecord::setData(char *data, size_t size) {
|
||||
_data.resize(size);
|
||||
memcpy(_data.data(), data, size);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
154
libs/gempa/caps/anypacket.h
Normal file
154
libs/gempa/caps/anypacket.h
Normal file
@ -0,0 +1,154 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_CUSTOMPACKET_H
|
||||
#define GEMPA_CAPS_CUSTOMPACKET_H
|
||||
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
#include <gempa/caps/endianess.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class AnyDataRecord : public DataRecord {
|
||||
public:
|
||||
typedef std::vector<char> Buffer;
|
||||
|
||||
struct AnyHeader {
|
||||
char type[5];
|
||||
Header dataHeader;
|
||||
TimeStamp endTime;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
get(type, sizeof(type)-1);
|
||||
type[sizeof(type)-1] = '\0';
|
||||
dataHeader.get(buf);
|
||||
|
||||
get(endTime.year);
|
||||
get(endTime.yday);
|
||||
get(endTime.hour);
|
||||
get(endTime.minute);
|
||||
get(endTime.second);
|
||||
get(endTime.usec);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const;
|
||||
|
||||
// 4 additional bytes (type) with respect to the original
|
||||
// data header
|
||||
int dataSize() const {
|
||||
return sizeof(type)-1 +
|
||||
dataHeader.dataSize() +
|
||||
sizeof(endTime.year) +
|
||||
sizeof(endTime.yday) +
|
||||
sizeof(endTime.hour) +
|
||||
sizeof(endTime.minute) +
|
||||
sizeof(endTime.second) +
|
||||
sizeof(endTime.usec);
|
||||
}
|
||||
};
|
||||
|
||||
AnyDataRecord();
|
||||
|
||||
//! Sets the format of the any record. The format can be
|
||||
//! anything that fits into 4 characters. If more than
|
||||
//! 4 characters are given, false is returned.
|
||||
bool setType(const char *type);
|
||||
const char *type() const;
|
||||
|
||||
virtual const char *formatName() const;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime,
|
||||
Time &endTime);
|
||||
|
||||
virtual const Header *header() const;
|
||||
virtual Time startTime() const;
|
||||
virtual Time endTime() const;
|
||||
|
||||
virtual bool canTrim() const;
|
||||
virtual bool canMerge() const;
|
||||
|
||||
virtual bool trim(const Time &start,
|
||||
const Time &end) const;
|
||||
|
||||
virtual size_t dataSize(bool withHeader) const;
|
||||
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start = Time(),
|
||||
const Time &end = Time(),
|
||||
int maxSize = -1);
|
||||
|
||||
virtual bool put(std::streambuf &buf, bool withHeader) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the packet type
|
||||
* @return The packet type
|
||||
*/
|
||||
PacketType packetType() const { return ANYPacket; }
|
||||
|
||||
/**
|
||||
* @brief Sets the start time of the record
|
||||
* @param The start time
|
||||
*/
|
||||
void setStartTime(const Time &time);
|
||||
|
||||
/**
|
||||
* @brief Sets the end time of the record
|
||||
* @param The end time
|
||||
*/
|
||||
void setEndTime(const Time &time);
|
||||
|
||||
/**
|
||||
* @brief Sets the sampling frequency of the record
|
||||
* @param numerator The numerator
|
||||
* @param denominator The denomintor
|
||||
*/
|
||||
void setSamplingFrequency(uint16_t numerator, uint16_t denominator);
|
||||
|
||||
/**
|
||||
* @brief Returns the data vector to be filled by the caller
|
||||
* @return The pointer to the internal buffer
|
||||
*/
|
||||
Buffer *data() { return &_data; }
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal data vector from the given buffer
|
||||
* @param The buffer to read the data from
|
||||
* @param The buffer size
|
||||
*/
|
||||
virtual void setData(char *data, size_t size);
|
||||
|
||||
protected:
|
||||
AnyHeader _header;
|
||||
Buffer _data;
|
||||
|
||||
Time _startTime;
|
||||
Time _endTime;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
17
libs/gempa/caps/api.h
Normal file
17
libs/gempa/caps/api.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef SC_GEMPA_CAPS_API_H
|
||||
#define SC_GEMPA_CAPS_API_H
|
||||
|
||||
#if defined(WIN32) && (defined(SC_GEMPA_CAPS_SHARED) || defined(SC_ALL_SHARED))
|
||||
# if defined(SC_GEMPA_CAPS_EXPORTS)
|
||||
# define SC_GEMPA_CAPS_API __declspec(dllexport)
|
||||
# define SC_GEMPA_CAPS_TEMPLATE_EXPORT
|
||||
# else
|
||||
# define SC_GEMPA_CAPS_API __declspec(dllimport)
|
||||
# define SC_GEMPA_CAPS_TEMPLATE_EXPORT extern
|
||||
# endif
|
||||
#else
|
||||
# define SC_GEMPA_CAPS_API
|
||||
# define SC_GEMPA_CAPS_TEMPLATE_EXPORT
|
||||
#endif
|
||||
|
||||
#endif
|
141
libs/gempa/caps/application.cpp
Normal file
141
libs/gempa/caps/application.cpp
Normal file
@ -0,0 +1,141 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2015 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/application.h>
|
||||
#include <gempa/caps/log.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <locale.h>
|
||||
|
||||
namespace {
|
||||
|
||||
void signalHandler(int signal) {
|
||||
Gempa::CAPS::Application::Interrupt(signal);
|
||||
}
|
||||
|
||||
|
||||
void registerSignalHandler() {
|
||||
CAPS_DEBUG("Registering signal handler");
|
||||
|
||||
signal(SIGTERM, signalHandler);
|
||||
signal(SIGINT, signalHandler);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
Application* Application::_app = NULL;
|
||||
|
||||
Application::Application(int argc, char **argv) {
|
||||
_exitRequested = false;
|
||||
_argc = argc;
|
||||
_argv = argv;
|
||||
|
||||
_app = this;
|
||||
|
||||
registerSignalHandler();
|
||||
}
|
||||
|
||||
void Application::done() {
|
||||
_exitRequested = true;
|
||||
CAPS_DEBUG("leaving ::done");
|
||||
}
|
||||
|
||||
int Application::exec() {
|
||||
_returnCode = 1;
|
||||
if ( init() ) {
|
||||
_returnCode = 0;
|
||||
|
||||
if ( !run() && _returnCode == 0 )
|
||||
_returnCode = 1;
|
||||
|
||||
done();
|
||||
}
|
||||
else
|
||||
done();
|
||||
|
||||
return _returnCode;
|
||||
}
|
||||
|
||||
void Application::exit(int returnCode) {
|
||||
_returnCode = returnCode;
|
||||
_exitRequested = true;
|
||||
}
|
||||
|
||||
void Application::handleInterrupt(int signal) {
|
||||
switch ( signal ) {
|
||||
case SIGABRT:
|
||||
exit(-1);
|
||||
|
||||
case SIGSEGV:
|
||||
exit(-1);
|
||||
|
||||
default:
|
||||
this->exit(_returnCode);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::Interrupt(int signal) {
|
||||
if ( _app ) _app->handleInterrupt(signal);
|
||||
}
|
||||
|
||||
bool Application::init() {
|
||||
setlocale(LC_ALL, "C");
|
||||
|
||||
if ( !initCommandLine() ) {
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !initConfiguration() ) {
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !initCommandLine() ) {
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !validateParameters() ) {
|
||||
exit(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::initCommandLine() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::initConfiguration() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::run() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Application::validateParameters() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
106
libs/gempa/caps/application.h
Normal file
106
libs/gempa/caps/application.h
Normal file
@ -0,0 +1,106 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2015 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_APPLICATION_H
|
||||
#define GEMPA_CAPS_APPLICATION_H
|
||||
|
||||
#include <gempa/caps/api.h>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class SC_GEMPA_CAPS_API Application {
|
||||
public:
|
||||
Application(int argc, char **argv);
|
||||
virtual ~Application() {}
|
||||
|
||||
/**
|
||||
* Exit the application and set the returnCode.
|
||||
* @param returnCode The value returned from exec()
|
||||
*/
|
||||
virtual void exit(int returnCode);
|
||||
|
||||
/**
|
||||
* @brief Conventient function to simplify usage
|
||||
* @return The value returned from exec()
|
||||
*/
|
||||
int operator()() { return exec(); }
|
||||
|
||||
/**
|
||||
* @brief In case of an interrupt this method can be used to
|
||||
* forward the signal to the internal signal handling.
|
||||
* @param signal
|
||||
*/
|
||||
static void Interrupt(int signal);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Cleanup method called before exec() returns.
|
||||
*/
|
||||
virtual void done();
|
||||
|
||||
/**
|
||||
* Execs the mainloop and waits until exit() is called
|
||||
* or a appropriate signal has been fired (e.g. SIGTERM).
|
||||
* @return The value that was set with to exit()
|
||||
*/
|
||||
int exec();
|
||||
/**
|
||||
* @brief This method can be used to implement custom
|
||||
* signal handling.
|
||||
* @param signal The emitted signal
|
||||
*/
|
||||
virtual void handleInterrupt(int signal);
|
||||
|
||||
/**
|
||||
* @brief Initialization method. This method calls the initCommandLine
|
||||
* initConfiguration and validateParameters function
|
||||
*/
|
||||
virtual bool init();
|
||||
/**
|
||||
* @brief Handles commandline arguments
|
||||
*/
|
||||
virtual bool initCommandLine();
|
||||
/**
|
||||
* @brief Handles configuration files
|
||||
*/
|
||||
virtual bool initConfiguration();
|
||||
|
||||
/**
|
||||
* @brief This method must be implemented in the inherited class to
|
||||
* execute the plugin specific code.
|
||||
*/
|
||||
virtual bool run();
|
||||
/**
|
||||
* @brief This method can be used to verify custom configuration or
|
||||
commandline parameters
|
||||
*/
|
||||
virtual bool validateParameters();
|
||||
|
||||
protected:
|
||||
bool _exitRequested;
|
||||
int _argc;
|
||||
char **_argv;
|
||||
|
||||
private:
|
||||
int _returnCode;
|
||||
static Application *_app;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
621
libs/gempa/caps/connection.cpp
Normal file
621
libs/gempa/caps/connection.cpp
Normal file
@ -0,0 +1,621 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2014 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/connection.h>
|
||||
#include <gempa/caps/anypacket.h>
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/mseedpacket.h>
|
||||
#include <gempa/caps/metapacket.h>
|
||||
#include <gempa/caps/rawpacket.h>
|
||||
#include <gempa/caps/sessiontable.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <functional>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <cerrno>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
namespace {
|
||||
|
||||
const Time InvalidTime = Time();
|
||||
|
||||
template <typename T>
|
||||
bool fromString(T &value, const string &str);
|
||||
|
||||
template<> inline bool fromString(int &value, const string &str) {
|
||||
stringstream ss(str);
|
||||
ss >> value;
|
||||
return !ss.bad();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Connection::Connection() {
|
||||
_port = 18002;
|
||||
_server = "localhost";
|
||||
|
||||
_metaMode = false;
|
||||
_realtime = true;
|
||||
_ssl = false;
|
||||
|
||||
_sessionTable = SessionTablePtr(new SessionTable());
|
||||
_sessionTable->setItemAboutToBeRemovedFunc(
|
||||
bind(&Connection::onItemAboutToBeRemoved, this, placeholders::_1)
|
||||
);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
Connection::~Connection() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool Connection::setServer(const string &server) {
|
||||
close();
|
||||
reset();
|
||||
|
||||
size_t pos = server.rfind(':');
|
||||
string addr = server;
|
||||
int timeout = 300;
|
||||
|
||||
if ( pos == string::npos )
|
||||
_server = addr;
|
||||
else {
|
||||
_server = addr.substr(0, pos);
|
||||
if ( !fromString(_port, addr.substr(pos+1)) ) {
|
||||
CAPS_ERROR("invalid source address: %s", addr.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( timeout > 0 ) {
|
||||
CAPS_DEBUG("setting socket timeout to %ds", timeout);
|
||||
//_socket->setSocketTimeout(timeout,0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connection::setCredentials(const std::string &user,
|
||||
const std::string &password) {
|
||||
if ( user.empty() || password.empty() ) {
|
||||
_auth.clear();
|
||||
CAPS_DEBUG("authentication deactivated");
|
||||
}
|
||||
else {
|
||||
_auth = "AUTH " + user + " " + password;
|
||||
CAPS_DEBUG("credentials set: %s:***", user.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
Socket* Connection::createSocket() const {
|
||||
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
|
||||
return _ssl? new SSLSocket() : new Socket();
|
||||
#else
|
||||
return new Socket();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::disconnect() {
|
||||
if ( _socket == NULL ) return;
|
||||
|
||||
CAPS_DEBUG("disconnecting");
|
||||
if ( _state != Aborted )
|
||||
_state = Error;
|
||||
|
||||
_socket->shutdown();
|
||||
_socket->close();
|
||||
_sessionTable->reset();
|
||||
_currentID = - 1;
|
||||
_currentItem = NULL;
|
||||
}
|
||||
|
||||
void Connection::close() {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
// _state = Aborted;
|
||||
disconnect();
|
||||
_state = Aborted;
|
||||
}
|
||||
|
||||
void Connection::abort() {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
if ( _state == Aborted ) {
|
||||
CAPS_WARNING("abort already requested");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( _socket->isValid() && _state == Active ) {
|
||||
static string line("ABORT\n");
|
||||
if ( _socket->write(line.c_str(), line.size()) != (int) line.size() ) {
|
||||
CAPS_ERROR("could not send abort request");
|
||||
disconnect();
|
||||
}
|
||||
else {
|
||||
CAPS_DEBUG("abort command sent");
|
||||
}
|
||||
}
|
||||
_state = Aborted;
|
||||
}
|
||||
|
||||
bool Connection::setTimeout(int /*seconds*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Connection::addStream(const string &net, const string &sta,
|
||||
const string &loc, const string &cha) {
|
||||
return addRequest(net, sta, loc, cha, _startTime, _endTime, false);
|
||||
}
|
||||
|
||||
bool Connection::addStream(const string &net, const string &sta,
|
||||
const string &loc, const string &cha,
|
||||
const Time &stime,
|
||||
const Time &etime) {
|
||||
return addRequest(net, sta, loc, cha, stime, etime, false);
|
||||
}
|
||||
|
||||
bool Connection::addRequest(const string &net, const string &sta,
|
||||
const string &loc, const string &cha,
|
||||
const Time &stime,
|
||||
const Time &etime,
|
||||
bool receivedData) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
if ( _state != EOD ) {
|
||||
if ( _state == Active )
|
||||
CAPS_WARNING("cannot add streams to an active session, invoke "
|
||||
"abort() or close() first");
|
||||
else
|
||||
CAPS_WARNING("cannot add streams to an erroneous or aborted "
|
||||
"session, invoke reset() first");
|
||||
return false;
|
||||
}
|
||||
string streamID = net + "." + sta + "." + loc + "." + cha;
|
||||
Request &req = _requests[streamID];
|
||||
req.net = net;
|
||||
req.sta = sta;
|
||||
req.loc = loc;
|
||||
req.cha = cha;
|
||||
req.start = stime;
|
||||
req.end = etime;
|
||||
req.receivedData = receivedData;
|
||||
return true;
|
||||
}
|
||||
|
||||
DataRecord* Connection::next() {
|
||||
if ( !handshake() ) return NULL;
|
||||
|
||||
while ( true ) {
|
||||
{
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
// skip unread bytes of previous iteration and check connection state
|
||||
if ( !seekToReadLimit() || _state == Error || _state == EOD )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ResponseHeader responseHeader;
|
||||
if ( !responseHeader.get(_socketBuf) ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
if ( _state != Aborted)
|
||||
CAPS_ERROR("could not read header");
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CAPS_DEBUG("read header (id/size): %i/%lu", responseHeader.id,
|
||||
(unsigned long)responseHeader.size);
|
||||
_socketBuf.set_read_limit(responseHeader.size);
|
||||
|
||||
// if ( _abortRequested ) break;
|
||||
|
||||
// State or session table update
|
||||
if ( responseHeader.id == 0 ) {
|
||||
while ( responseHeader.size > 0 /*&& _state == Active*/) {
|
||||
istream is(&_socketBuf);
|
||||
if ( is.getline(_lineBuf, 200).fail() ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
CAPS_ERROR("header line exceeds maximum of 200 characters");
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
responseHeader.size -= is.gcount();
|
||||
|
||||
SessionTable::Status status =
|
||||
_sessionTable->handleResponse(_lineBuf, is.gcount());
|
||||
if ( status == SessionTable::Error ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( status == SessionTable::EOD ) {
|
||||
_state = EOD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
CAPS_DEBUG("reading data record");
|
||||
// data
|
||||
if ( _currentID != responseHeader.id ) {
|
||||
_currentItem = _sessionTable->getItem(responseHeader.id);
|
||||
_currentID = responseHeader.id;
|
||||
|
||||
if ( _currentItem == NULL ) {
|
||||
CAPS_WARNING("unknown data request ID %d", responseHeader.id);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int size = responseHeader.size;
|
||||
|
||||
// To improve performance CAPS uses an optimized protocol
|
||||
// to deliver RAW packets. The RAW packet class can't be used
|
||||
// to read the header from socket.
|
||||
DataRecord *dataRecord = NULL;
|
||||
if ( _currentItem->type == RawDataPacket ) {
|
||||
// Read optimized header
|
||||
RawResponseHeader rawResponseHeader;
|
||||
if ( !rawResponseHeader.get(_socketBuf) ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
CAPS_ERROR("failed to extract raw response header %s",
|
||||
_currentItem->streamID.c_str());
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Time time(rawResponseHeader.timeSeconds, rawResponseHeader.timeMicroSeconds);
|
||||
size -= rawResponseHeader.dataSize();
|
||||
|
||||
// Create raw record, set header and read payload
|
||||
RawDataRecord *record = new RawDataRecord;
|
||||
record->setStartTime(time);
|
||||
record->setSamplingFrequency(_currentItem->samplingFrequency,
|
||||
_currentItem->samplingFrequencyDivider);
|
||||
record->setDataType(_currentItem->dataType);
|
||||
record->getData(_socketBuf, size);
|
||||
|
||||
dataRecord = record;
|
||||
}
|
||||
else if ( _currentItem->type == ANYPacket ) {
|
||||
AnyDataRecord *record = new AnyDataRecord;
|
||||
record->get(_socketBuf, size, InvalidTime, InvalidTime, size);
|
||||
dataRecord = record;
|
||||
}
|
||||
else if ( _currentItem->type == MSEEDPacket ) {
|
||||
MSEEDDataRecord *record = new MSEEDDataRecord;
|
||||
record->get(_socketBuf, size, InvalidTime, InvalidTime, size);
|
||||
dataRecord = record;
|
||||
}
|
||||
else if ( _currentItem->type == MetaDataPacket ) {
|
||||
// Read start time from optimized header
|
||||
MetaResponseHeader metaResponseHeader;
|
||||
if ( !metaResponseHeader.get(_socketBuf) ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
CAPS_ERROR("failed to extract meta information from stream %s",
|
||||
_currentItem->streamID.c_str());
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Time stime(metaResponseHeader.startTime.seconds,
|
||||
metaResponseHeader.endTime.microSeconds),
|
||||
etime(metaResponseHeader.endTime.seconds,
|
||||
metaResponseHeader.endTime.microSeconds);
|
||||
|
||||
size -= metaResponseHeader.dataSize();
|
||||
|
||||
MetaDataRecord::MetaHeader recHeader;
|
||||
recHeader.dataHeader.setSamplingTime(stime);
|
||||
recHeader.setEndTime(etime);
|
||||
recHeader.dataHeader.samplingFrequencyNumerator = _currentItem->samplingFrequency;
|
||||
recHeader.dataHeader.samplingFrequencyDenominator = _currentItem->samplingFrequencyDivider;
|
||||
recHeader.dataHeader.dataType = _currentItem->dataType;
|
||||
|
||||
MetaDataRecord *record = new MetaDataRecord;
|
||||
record->setHeader(recHeader);
|
||||
dataRecord = record;
|
||||
}
|
||||
else {
|
||||
CAPS_ERROR("received unknown packet type %d in stream %s",
|
||||
_currentItem->type, _currentItem->streamID.c_str());
|
||||
}
|
||||
|
||||
if ( dataRecord == NULL ) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CAPS_DEBUG("data record read");
|
||||
|
||||
RequestList::iterator it = _requests.find(_currentItem->streamID);
|
||||
if ( it == _requests.end() ) {
|
||||
// TODO: Search request map for wildcard match. Add entry with
|
||||
// streamID -> (record->endTime, wildcardItem->endTime) to
|
||||
// restrict data query in case of reconnect
|
||||
|
||||
// CAPS_WARNING("received unrequested record: %s: %s - %s",
|
||||
// _currentItem->streamID.c_str(),
|
||||
// dataRecord->startTime().iso().c_str(),
|
||||
// dataRecord->endTime().iso().c_str());
|
||||
}
|
||||
// Update request map to reflect current stream state
|
||||
else if ( dataRecord->endTime() > it->second.start ) {
|
||||
it->second.start = dataRecord->endTime();
|
||||
it->second.receivedData = true;
|
||||
}
|
||||
|
||||
return dataRecord;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Connection::handshake() {
|
||||
{
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
if ( _state == Error || _state == Aborted) {
|
||||
CAPS_ERROR("cannot read from an erroneous or aborted session, invoke "
|
||||
"reset() first");
|
||||
return false;
|
||||
}
|
||||
else if ( _state == Active )
|
||||
return true;
|
||||
// _state is set to EOD
|
||||
else if ( _requests.empty() ) {
|
||||
CAPS_WARNING("no stream requested, invoke addStream() first");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( _socket == NULL ) {
|
||||
_socket = SocketPtr(createSocket());
|
||||
if ( _socket == NULL ) return false;
|
||||
}
|
||||
|
||||
// Connect to server if necessary
|
||||
while ( !_socket->isValid() && _state == EOD ) {
|
||||
if ( _socket->connect(_server, _port) == Socket::Success ) {
|
||||
_socketBuf.setsocket(_socket.get());
|
||||
CAPS_DEBUG("connection to %s:%d established", _server.c_str(), _port);
|
||||
// _state = ios_base::eofbit;
|
||||
break;
|
||||
}
|
||||
|
||||
CAPS_WARNING("unable to connect to %s:%d, retrying in 5 seconds",
|
||||
_server.c_str(), _port);
|
||||
|
||||
// Wait 5 seconds and keep response latency low
|
||||
for ( int i = 0; (i < 10) && _state == EOD; ++i )
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
|
||||
if ( _state != EOD ) return false;
|
||||
|
||||
// if ( _socket->isValid() ) {
|
||||
// // Read all data from session
|
||||
// if ( !seekToReadLimit() ) return false;
|
||||
//// fd_set set;
|
||||
//// FD_ZERO(&set);
|
||||
//// FD_SET(_socket->fd(), &set);
|
||||
//// timeval t = (struct timeval) {0};
|
||||
|
||||
//// /* select returns 0 on timeout, 1 if input/output is available, -1 on error. */
|
||||
//// int retn = TEMP_FAILURE_RETRY(select(_socket->fd()+1, &set, NULL, NULL, &t));
|
||||
//// if ( retn != 0 ) CAPS_ERROR("UUPS");
|
||||
// }
|
||||
|
||||
// if ( _state == Aborted ) return false;
|
||||
|
||||
// Request streams
|
||||
stringstream req;
|
||||
|
||||
if ( !_auth.empty() )
|
||||
req << _auth << endl;
|
||||
|
||||
req << "BEGIN REQUEST" << endl
|
||||
<< "META " << (_metaMode ? "ON" : "OFF") << endl
|
||||
<< "REALTIME " << ( _realtime ? "ON" : "OFF") << endl;
|
||||
|
||||
// First pass: continue all previous streams
|
||||
for ( RequestList::const_iterator it = _requests.begin();
|
||||
it != _requests.end() && _state == EOD; ++it ) {
|
||||
if ( it->second.receivedData )
|
||||
formatRequest(req, it);
|
||||
}
|
||||
|
||||
// Second pass: subscribe to remaining streams
|
||||
for ( RequestList::iterator it = _requests.begin();
|
||||
it != _requests.end() && _state == EOD; ++it ) {
|
||||
if ( !it->second.receivedData )
|
||||
formatRequest(req, it);
|
||||
}
|
||||
|
||||
req << "END" << endl;
|
||||
|
||||
return sendRequest(req.str());
|
||||
}
|
||||
|
||||
bool Connection::sendRequest(const string &req) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
|
||||
if ( _state != EOD ) return false;
|
||||
|
||||
CAPS_DEBUG("%s", req.c_str());
|
||||
if ( _socket->write(req.c_str(), req.size()) != (int) req.size() ) {
|
||||
CAPS_ERROR("could not send data request");
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
Gempa::CAPS::ResponseHeader header;
|
||||
if ( !header.get(_socketBuf) ) {
|
||||
// Case to retry, connection closed by peer
|
||||
CAPS_ERROR("could not read data request response header");
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
_socketBuf.set_read_limit(header.size);
|
||||
if ( header.id != 0 ) {
|
||||
CAPS_ERROR("invalid data request response header id, expected 0, got %d", header.id);
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
CAPS_DEBUG("data request response size: %lu", (unsigned long) header.size);
|
||||
|
||||
istream is(&_socketBuf);
|
||||
// check line length
|
||||
if ( is.getline(_lineBuf, 200).fail() )
|
||||
CAPS_ERROR("data request response line exceeds maximum of 200 characters");
|
||||
// skip remaining header data
|
||||
else if ( !seekToReadLimit(false) ) {
|
||||
CAPS_ERROR("could not seek to data request response header end");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( strncasecmp(_lineBuf, "ERROR:", 6) == 0 )
|
||||
CAPS_ERROR("server responded to data request with: %s", _lineBuf);
|
||||
else if ( strncasecmp(_lineBuf, "STATUS OK", 9) != 0 )
|
||||
CAPS_ERROR("invalid data request response: %s", _lineBuf);
|
||||
else {
|
||||
CAPS_DEBUG("handshake complete");
|
||||
_state = Active;
|
||||
return true;
|
||||
}
|
||||
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Connection::seekToReadLimit(bool log) {
|
||||
if ( !_socket->isValid() ) {
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip unread bytes from previous record
|
||||
int skippies = _socketBuf.read_limit();
|
||||
if ( skippies > 0 ) {
|
||||
if ( log )
|
||||
CAPS_WARNING("no seemless reading, skipping %d bytes", skippies);
|
||||
if ( _socketBuf.pubseekoff(skippies, ios_base::cur, ios_base::in) < 0 ) {
|
||||
CAPS_ERROR("could not seek to next header");
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_socketBuf.set_read_limit(-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connection::formatRequest(stringstream& req, RequestList::const_iterator it) {
|
||||
req << "STREAM ADD " << it->first << endl;
|
||||
req << "TIME ";
|
||||
|
||||
int year, mon, day, hour, minute, second;
|
||||
|
||||
if ( it->second.start.valid() ) {
|
||||
it->second.start.get(&year, &mon, &day, &hour, &minute, &second);
|
||||
req << year << "," << mon << "," << day << ","
|
||||
<< hour << "," << minute << "," << second;
|
||||
if ( it->second.start.microseconds() > 0 ) {
|
||||
req << "," << setfill('0') << setw(6)
|
||||
<< it->second.start.microseconds() << setw(0);
|
||||
}
|
||||
}
|
||||
|
||||
req << ":";
|
||||
|
||||
if ( it->second.end.valid() ) {
|
||||
it->second.end.get(&year, &mon, &day, &hour, &minute, &second);
|
||||
req << year << "," << mon << "," << day << ","
|
||||
<< hour << "," << minute << "," << second;
|
||||
if ( it->second.end.microseconds() > 0 ) {
|
||||
req << "," << setfill('0') << setw(6)
|
||||
<< it->second.end.microseconds() << setw(0);
|
||||
}
|
||||
}
|
||||
|
||||
req << endl;
|
||||
}
|
||||
|
||||
void Connection::onItemAboutToBeRemoved(const SessionTableItem *item) {
|
||||
if ( _currentItem == item) {
|
||||
_currentID = -1;
|
||||
_currentItem = NULL;
|
||||
}
|
||||
|
||||
// remove request since all data has been received
|
||||
if ( item != NULL ) _requests.erase(item->streamID);
|
||||
}
|
||||
|
||||
void Connection::reset(bool clearStreams) {
|
||||
boost::mutex::scoped_lock l(_mutex);
|
||||
CAPS_DEBUG("resetting connection");
|
||||
if ( _state == Active ) {
|
||||
CAPS_WARNING("cannot reset an active connection, invoking close()");
|
||||
close();
|
||||
}
|
||||
// else if ( _state == Aborted ) {
|
||||
// fd_set set;
|
||||
// FD_ZERO(&set);
|
||||
// FD_SET(_socket->fd(), &set);
|
||||
// timeval t = (struct timeval) {0};
|
||||
|
||||
// /* select returns 0 on timeout, 1 if input/output is available, -1 on error. */
|
||||
// while ( _socket->isValid() ) {
|
||||
// int retn = TEMP_FAILURE_RETRY(select(_socket->fd()+1, &set, NULL, NULL, &t));
|
||||
// if ( retn == 0 ) break;
|
||||
|
||||
// if ( retn != 0 ) CAPS_ERROR("UUPS");
|
||||
|
||||
// }
|
||||
|
||||
_state = EOD;
|
||||
if ( clearStreams )
|
||||
_requests.clear();
|
||||
}
|
||||
|
||||
void Connection::setStartTime(const Time &stime) {
|
||||
_startTime = stime;
|
||||
CAPS_DEBUG("set global start time to %s", _startTime.toString("%F %T.%f").c_str());
|
||||
}
|
||||
|
||||
void Connection::setEndTime(const Time &etime) {
|
||||
_endTime = etime;
|
||||
CAPS_DEBUG("set global end time to %s", _endTime.toString("%F %T.%f").c_str());
|
||||
}
|
||||
|
||||
void Connection::setTimeWindow(const Time &stime, const Time &etime) {
|
||||
_startTime = stime;
|
||||
_endTime = etime;
|
||||
CAPS_DEBUG("set global timewindow to %s~%s", _startTime.toString("%F %T.%f").c_str(),
|
||||
_endTime.toString("%F %T.%f").c_str());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
180
libs/gempa/caps/connection.h
Normal file
180
libs/gempa/caps/connection.h
Normal file
@ -0,0 +1,180 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2014 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_CONNECTION_H
|
||||
#define GEMPA_CAPS_CONNECTION_H
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
#include <gempa/caps/sessiontable.h>
|
||||
#include <gempa/caps/socket.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
//#include <iostream>
|
||||
#include <map>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class SessionTableItem;
|
||||
class Time;
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
//! ConnectionStates:
|
||||
//! EOD -> connection not yet established or all streams finished (or aborted)
|
||||
//! Active -> connection was successfully established, data request was sent
|
||||
//! Error -> connection aborted due to server error
|
||||
//! Aborted -> connection aborted by user
|
||||
enum State { EOD, Active, Error, Aborted };
|
||||
|
||||
//! Constructor
|
||||
Connection();
|
||||
|
||||
//! Destructor
|
||||
virtual ~Connection();
|
||||
|
||||
//! Sets server parameter
|
||||
bool setServer(const std::string &server);
|
||||
|
||||
//! Enables SSL feature
|
||||
void enableSSL(bool enable) { _ssl = enable; }
|
||||
|
||||
//! Sets user name and password
|
||||
void setCredentials(const std::string &user, const std::string &password);
|
||||
|
||||
//! Sets meta mode. If enabled only the packet header information is
|
||||
//! transmitted.
|
||||
void setMetaMode(bool enable) { _metaMode = enable; }
|
||||
|
||||
//! Sets realtime mode.
|
||||
void setRealtime(bool enable) { _realtime = enable; }
|
||||
|
||||
//! Disconnect from server, connection unuseable until reset() is
|
||||
//! called. Thread safe.
|
||||
void close();
|
||||
|
||||
//! Send abort command to server, keep socket open, requires reset.
|
||||
//! Thread safe.
|
||||
void abort();
|
||||
|
||||
//! Adds a stream request
|
||||
bool addStream(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha);
|
||||
|
||||
//! Adds a seismic stream requests
|
||||
bool addStream(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
const Time &stime,
|
||||
const Time &etime);
|
||||
|
||||
//! Resets connection state to eof
|
||||
void reset(bool clearStreams = false);
|
||||
|
||||
//! Sets the given start time
|
||||
void setStartTime(const Time &stime);
|
||||
|
||||
//! Sets the given end time
|
||||
void setEndTime(const Time &etime);
|
||||
|
||||
//! Sets the given time window
|
||||
void setTimeWindow(const Time &stime, const Time &etime);
|
||||
|
||||
//! Sets timeout
|
||||
bool setTimeout(int seconds);
|
||||
|
||||
//! Returns the next record. If the record is NULL the caller has
|
||||
//! to check the state of the connection. Automatically creates a new
|
||||
//! connection if required, sends data requests and evaluates updates
|
||||
//! of session table. If the connection state is neither 'good' nor
|
||||
//!'eof' the connection has to be resetted before invoking this method.
|
||||
DataRecord* next();
|
||||
|
||||
|
||||
State state() { return _state; }
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// protected methods
|
||||
// ----------------------------------------------------------------------
|
||||
protected:
|
||||
virtual Socket* createSocket() const;
|
||||
|
||||
private:
|
||||
typedef boost::shared_ptr<SessionTable> SessionTablePtr;
|
||||
|
||||
struct Request {
|
||||
std::string net;
|
||||
std::string sta;
|
||||
std::string loc;
|
||||
std::string cha;
|
||||
Time start;
|
||||
Time end;
|
||||
bool receivedData;
|
||||
};
|
||||
typedef std::map<std::string, Request> RequestList;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// private methods
|
||||
// ----------------------------------------------------------------------
|
||||
private:
|
||||
bool addRequest(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
const Time &stime,
|
||||
const Time &etime,
|
||||
bool receivedData);
|
||||
void onItemAboutToBeRemoved(const SessionTableItem *item);
|
||||
|
||||
void disconnect();
|
||||
bool handshake();
|
||||
bool sendRequest(const std::string &req);
|
||||
|
||||
bool seekToReadLimit(bool log = true);
|
||||
void formatRequest(std::stringstream& req, RequestList::const_iterator it);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// private data members
|
||||
// ----------------------------------------------------------------------
|
||||
protected:
|
||||
SessionTablePtr _sessionTable;
|
||||
RequestList _requests;
|
||||
SocketPtr _socket;
|
||||
socketbuf<Socket, 512> _socketBuf;
|
||||
char _lineBuf[201];
|
||||
volatile State _state;
|
||||
SessionTableItem *_currentItem;
|
||||
uint16_t _currentID;
|
||||
boost::mutex _mutex;
|
||||
|
||||
std::string _server;
|
||||
int _port;
|
||||
std::string _auth;
|
||||
Time _startTime;
|
||||
Time _endTime;
|
||||
bool _realtime;
|
||||
bool _metaMode;
|
||||
bool _ssl;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<Connection> ConnectionPtr;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
1063
libs/gempa/caps/datetime.cpp
Normal file
1063
libs/gempa/caps/datetime.cpp
Normal file
File diff suppressed because it is too large
Load Diff
245
libs/gempa/caps/datetime.h
Normal file
245
libs/gempa/caps/datetime.h
Normal file
@ -0,0 +1,245 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_DATETIME_H
|
||||
#define GEMPA_CAPS_DATETIME_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
struct tm;
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
class TimeSpan {
|
||||
// ----------------------------------------------------------------------
|
||||
// Xstruction
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
TimeSpan();
|
||||
TimeSpan(struct timeval*);
|
||||
TimeSpan(const struct timeval&);
|
||||
TimeSpan(double);
|
||||
TimeSpan(long secs, long usecs);
|
||||
|
||||
//! Copy constructor
|
||||
TimeSpan(const TimeSpan&);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Operators
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Comparison
|
||||
bool operator==(const TimeSpan&) const;
|
||||
bool operator!=(const TimeSpan&) const;
|
||||
bool operator< (const TimeSpan&) const;
|
||||
bool operator<=(const TimeSpan&) const;
|
||||
bool operator> (const TimeSpan&) const;
|
||||
bool operator>=(const TimeSpan&) const;
|
||||
|
||||
//! Conversion
|
||||
operator double() const;
|
||||
operator const timeval&() const;
|
||||
|
||||
//! Assignment
|
||||
TimeSpan& operator=(long t);
|
||||
TimeSpan& operator=(double t);
|
||||
|
||||
//! Arithmetic
|
||||
TimeSpan operator+(const TimeSpan&) const;
|
||||
TimeSpan operator-(const TimeSpan&) const;
|
||||
|
||||
TimeSpan& operator+=(const TimeSpan&);
|
||||
TimeSpan& operator-=(const TimeSpan&);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Interface
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Returns the absolute value of time
|
||||
TimeSpan abs() const;
|
||||
|
||||
//! Returns the seconds of the timespan
|
||||
long seconds() const;
|
||||
//! Returns the microseconds of the timespan
|
||||
long microseconds() const;
|
||||
|
||||
//! Returns the (possibly negative) length of the timespan in seconds
|
||||
double length() const;
|
||||
|
||||
//! Sets the seconds
|
||||
TimeSpan& set(long seconds);
|
||||
|
||||
//! Sets the microseconds
|
||||
TimeSpan& setUSecs(long);
|
||||
|
||||
//! Assigns the elapsed time to the passed out parameters
|
||||
void elapsedTime(int* days , int* hours = NULL,
|
||||
int* minutes = NULL, int* seconds = NULL) const;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Implementation
|
||||
// ----------------------------------------------------------------------
|
||||
protected:
|
||||
struct timeval _timeval;
|
||||
};
|
||||
|
||||
|
||||
class Time : public TimeSpan {
|
||||
// ----------------------------------------------------------------------
|
||||
// Public static data members
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
static const Time Null;
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Xstruction
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
Time();
|
||||
Time(long secs, long usecs);
|
||||
|
||||
explicit Time(const TimeSpan&);
|
||||
explicit Time(const struct timeval&);
|
||||
explicit Time(struct timeval*);
|
||||
explicit Time(double);
|
||||
|
||||
Time(int year, int month, int day,
|
||||
int hour = 0, int min = 0, int sec = 0,
|
||||
int usec = 0);
|
||||
|
||||
//! Copy constructor
|
||||
Time(const Time&);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Operators
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Conversion
|
||||
operator bool() const;
|
||||
operator time_t() const;
|
||||
|
||||
//! Assignment
|
||||
Time& operator=(const struct timeval& t);
|
||||
Time& operator=(struct timeval* t);
|
||||
Time& operator=(time_t t);
|
||||
Time& operator=(double t);
|
||||
|
||||
//! Arithmetic
|
||||
Time operator+(const TimeSpan&) const;
|
||||
Time operator-(const TimeSpan&) const;
|
||||
TimeSpan operator-(const Time&) const;
|
||||
|
||||
Time& operator+=(const TimeSpan&);
|
||||
Time& operator-=(const TimeSpan&);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Interface
|
||||
// ----------------------------------------------------------------------
|
||||
public:
|
||||
//! Sets the time
|
||||
Time& set(int year, int month, int day,
|
||||
int hour, int min, int sec,
|
||||
int usec);
|
||||
|
||||
//! Fill the parameters with the currently set time values
|
||||
//! @return The error flag
|
||||
bool get(int *year, int *month = NULL, int *day = NULL,
|
||||
int *hour = NULL, int *min = NULL, int *sec = NULL,
|
||||
int *usec = NULL) const;
|
||||
|
||||
//! Fill the parameters with the currently set time values
|
||||
//! @return The error flag
|
||||
bool get2(int *year, int *yday = NULL,
|
||||
int *hour = NULL, int *min = NULL, int *sec = NULL,
|
||||
int *usec = NULL) const;
|
||||
|
||||
//! Returns the current localtime
|
||||
static Time LocalTime();
|
||||
|
||||
//! Returns the current gmtime
|
||||
static Time GMT();
|
||||
|
||||
/** Creates a time from the year and the day of the year
|
||||
@param year The year, including the century (for example, 1988)
|
||||
@param year_day The day of the year [0..365]
|
||||
@return The time value
|
||||
*/
|
||||
static Time FromYearDay(int year, int year_day);
|
||||
|
||||
//! Saves the current localtime in the calling object
|
||||
Time& localtime();
|
||||
|
||||
//! Saves the current gmtime in the calling object
|
||||
Time& gmt();
|
||||
|
||||
//! Converts the time to localtime
|
||||
Time toLocalTime() const;
|
||||
|
||||
//! Converts the time to gmtime
|
||||
Time toGMT() const;
|
||||
|
||||
//! Returns whether the date is valid or not
|
||||
bool valid() const;
|
||||
|
||||
/** Converts the time to string using format fmt.
|
||||
@param fmt The format string can contain any specifiers
|
||||
as allowed for strftime. Additional the '%f'
|
||||
specifier is replaced by the fraction of the seconds.
|
||||
Example:
|
||||
toString("%FT%T.%fZ") = "1970-01-01T00:00:00.0000Z"
|
||||
@return A formatted string
|
||||
*/
|
||||
std::string toString(const char* fmt) const;
|
||||
|
||||
/**
|
||||
* Converts the time to a string using the ISO time description
|
||||
* @return A formatted string
|
||||
*/
|
||||
std::string iso() const;
|
||||
|
||||
/**
|
||||
* Converts a string into a time representation.
|
||||
* @param str The string representation of the time
|
||||
* @param fmt The format string containing the conversion
|
||||
* specification (-> toString)
|
||||
* @return The conversion result
|
||||
*/
|
||||
bool fromString(const char* str, const char* fmt);
|
||||
|
||||
/**
|
||||
* Static method to create a time value from a string.
|
||||
* The parameters are the same as in Time::fromString.
|
||||
*/
|
||||
static Time FromString(const char* str, const char* fmt);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
246
libs/gempa/caps/encoderfactory.cpp
Normal file
246
libs/gempa/caps/encoderfactory.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2016 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 "encoderfactory.h"
|
||||
|
||||
#include "mseed/mseed.h"
|
||||
#include "mseed/steim1.h"
|
||||
#include "mseed/steim2.h"
|
||||
#include "mseed/uncompressed.h"
|
||||
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
EncoderFactory::EncoderFactory() {}
|
||||
|
||||
EncoderFactory::~EncoderFactory() {}
|
||||
|
||||
const string& EncoderFactory::errorString() const {
|
||||
return _errorString;
|
||||
}
|
||||
|
||||
MSEEDEncoderFactory::MSEEDEncoderFactory()
|
||||
: _recordLength(9) {}
|
||||
|
||||
bool MSEEDEncoderFactory::setRecordLength(uint recordLength) {
|
||||
if ( recordLength < 7 || recordLength > 32) {
|
||||
_errorString = "MSEED record length out of range [7, 32]";
|
||||
return false;
|
||||
}
|
||||
|
||||
_recordLength = uint8_t(recordLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SteimEncoderFactory::supportsRecord(DataRecord *rec) {
|
||||
/*
|
||||
DataType dt = rec->header()->dataType;
|
||||
if ( (dt == DT_INT64) || (dt == DT_FLOAT) ||
|
||||
(dt == DT_DOUBLE) ) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
PacketType type = rec->packetType();
|
||||
if ( type == RawDataPacket || type == FixedRawDataPacket ) {
|
||||
return true;
|
||||
}
|
||||
else if ( type == MSEEDPacket ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IdentityEncoderFactory::supportsRecord(DataRecord *rec) {
|
||||
PacketType type = rec->packetType();
|
||||
return type == RawDataPacket || type == FixedRawDataPacket;
|
||||
}
|
||||
|
||||
Encoder* IdentityEncoderFactory::create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator) {
|
||||
if ( dt == DT_INT8 ) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_ASCII,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
return new UncompressedMSEED<int8_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT16 ) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_INT16,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
return new UncompressedMSEED<int16_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT32 ) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_INT32,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
return new UncompressedMSEED<int32_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_FLOAT ) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_FLOAT32,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
return new UncompressedMSEED<float>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_DOUBLE ) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_FLOAT64,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
return new UncompressedMSEED<double>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Encoder* Steim1EncoderFactory::create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_STEIM1,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
if ( dt == DT_INT8 ) {
|
||||
return new Steim1Encoder<int8_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT16 ) {
|
||||
return new Steim1Encoder<int16_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT32 ) {
|
||||
return new Steim1Encoder<int32_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_FLOAT ) {
|
||||
return new Steim1Encoder<float>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_DOUBLE ) {
|
||||
return new Steim1Encoder<double>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
|
||||
delete format;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Encoder* Steim2EncoderFactory::create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator) {
|
||||
MSEEDFormat *format = new MSEEDFormat(networkCode, stationCode,
|
||||
locationCode, channelCode,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator,
|
||||
DE_STEIM2,
|
||||
_recordLength);
|
||||
if ( format == NULL ) return NULL;
|
||||
|
||||
if ( dt == DT_INT8 ) {
|
||||
return new Steim2Encoder<int8_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT16 ) {
|
||||
return new Steim2Encoder<int16_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_INT32 ) {
|
||||
return new Steim2Encoder<int32_t>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_FLOAT ) {
|
||||
return new Steim2Encoder<float>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
else if ( dt == DT_DOUBLE ) {
|
||||
return new Steim2Encoder<double>(format,
|
||||
samplingFrequencyNumerator,
|
||||
samplingFrequencyDenominator);
|
||||
}
|
||||
|
||||
delete format;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
164
libs/gempa/caps/encoderfactory.h
Normal file
164
libs/gempa/caps/encoderfactory.h
Normal file
@ -0,0 +1,164 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2016 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef GEMPA_CAPS_ENCODERFACTORY_H
|
||||
#define GEMPA_CAPS_ENCODERFACTORY_H
|
||||
|
||||
#include "mseed/encoder.h"
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
/**
|
||||
* @brief Abstract base class of the encoder factory. Each
|
||||
* derived class must implement the create and the
|
||||
* supportsRecord method.
|
||||
*/
|
||||
class EncoderFactory {
|
||||
public:
|
||||
EncoderFactory();
|
||||
virtual ~EncoderFactory();
|
||||
/**
|
||||
* @brief Creates encoder from given parameter set
|
||||
* @param networkCode The nework code
|
||||
* @param stationCode The station code
|
||||
* @param locationCode The location code
|
||||
* @param channelCode The channel code
|
||||
* @param dt The data encoding
|
||||
* @param samplingFrequencyNumerator The numerator
|
||||
* @param samplingFrequencyDenominator The denominator
|
||||
* @param samplingFrequencyDenominator The timing quality
|
||||
* @return The encoder object or NULL if creation fails
|
||||
*/
|
||||
virtual Encoder* create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator) = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if an encoder factory
|
||||
* supports the given record.
|
||||
* @param rec The record to check
|
||||
* @return True if the data type is supported
|
||||
*/
|
||||
virtual bool supportsRecord(DataRecord *rec) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns a human readable error description
|
||||
* of the last error occured.
|
||||
* @return Error description
|
||||
*/
|
||||
const std::string& errorString() const;
|
||||
|
||||
protected:
|
||||
std::string _errorString;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Base class for all MSEED encoders
|
||||
*/
|
||||
class MSEEDEncoderFactory : public EncoderFactory {
|
||||
public:
|
||||
MSEEDEncoderFactory();
|
||||
|
||||
/**
|
||||
* @brief Sets the volume logical record length expressed as a
|
||||
* power of 2. A 512 byte record would be 9.
|
||||
* @param recLen The record length expressed as a power of 2
|
||||
* @return True if the record length is valid
|
||||
*/
|
||||
bool setRecordLength(uint recordLength);
|
||||
|
||||
protected:
|
||||
uint8_t _recordLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a general Steim encoder factory
|
||||
* interface which implements the supportsRecord method
|
||||
* only.
|
||||
*/
|
||||
class SteimEncoderFactory : public MSEEDEncoderFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if an encoder factory
|
||||
* supports the given record. In case of data type float or double we
|
||||
* return true but the values will casted implicitly to int32.
|
||||
* @param rec The record to check
|
||||
* @return True if the data type is supported
|
||||
*/
|
||||
bool supportsRecord(DataRecord *rec);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The IdentiyEncoderFactory class implements the
|
||||
* encoder factory interface and supports the creation of
|
||||
* identiy encoders.
|
||||
*/
|
||||
class IdentityEncoderFactory : public MSEEDEncoderFactory {
|
||||
public:
|
||||
Encoder* create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator);
|
||||
bool supportsRecord(DataRecord *rec);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief The Steim1EncoderFactory class implements the
|
||||
* encoder factory interface and supports the creation of
|
||||
* Steim2 encoders.
|
||||
*/
|
||||
class Steim1EncoderFactory : public SteimEncoderFactory {
|
||||
public:
|
||||
Encoder* create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The Steim2EncoderFactory class implements the
|
||||
* encoder factory interface and supports the creation of
|
||||
* Steim2 encoders.
|
||||
*/
|
||||
class Steim2EncoderFactory : public SteimEncoderFactory {
|
||||
public:
|
||||
Encoder* create(const std::string &networkCode,
|
||||
const std::string &stationCode,
|
||||
const std::string &locationCode,
|
||||
const std::string &channelCode,
|
||||
int dt,
|
||||
int samplingFrequencyNumerator,
|
||||
int samplingFrequencyDenominator);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
210
libs/gempa/caps/endianess.h
Normal file
210
libs/gempa/caps/endianess.h
Normal file
@ -0,0 +1,210 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_ENDIANESS_H
|
||||
#define GEMPA_CAPS_ENDIANESS_H
|
||||
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
#include <streambuf>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
namespace Endianess {
|
||||
|
||||
template <typename T1, typename T2> inline const T1* lvalue(const T2 &value) {
|
||||
return reinterpret_cast<const T1*>(&value);
|
||||
}
|
||||
|
||||
template <typename T, int swap, int size>
|
||||
struct Swapper {
|
||||
static T Take(const T &v) {
|
||||
return v;
|
||||
}
|
||||
|
||||
static void Take(T *ar, int len) {}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Swapper<T,1,2> {
|
||||
static T Take(const T &v) {
|
||||
return *lvalue<T, uint16_t>((*reinterpret_cast<const uint16_t*>(&v) >> 0x08) |
|
||||
(*reinterpret_cast<const uint16_t*>(&v) << 0x08));
|
||||
}
|
||||
|
||||
static void Take(T *ar, int len) {
|
||||
for ( int i = 0; i < len; ++i )
|
||||
ar[i] = Take(ar[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Swapper<T,1,4> {
|
||||
static T Take(const T &v) {
|
||||
return *lvalue<T, uint32_t>(((*reinterpret_cast<const uint32_t*>(&v) << 24) & 0xFF000000) |
|
||||
((*reinterpret_cast<const uint32_t*>(&v) << 8) & 0x00FF0000) |
|
||||
((*reinterpret_cast<const uint32_t*>(&v) >> 8) & 0x0000FF00) |
|
||||
((*reinterpret_cast<const uint32_t*>(&v) >> 24) & 0x000000FF));
|
||||
}
|
||||
|
||||
static void Take(T *ar, int len) {
|
||||
for ( int i = 0; i < len; ++i )
|
||||
ar[i] = Take(ar[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct Swapper<T,1,8> {
|
||||
static T Take(const T &v) {
|
||||
return *lvalue<T, uint64_t>(((*reinterpret_cast<const uint64_t*>(&v) << 56) & 0xFF00000000000000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) << 40) & 0x00FF000000000000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) << 24) & 0x0000FF0000000000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) << 8) & 0x000000FF00000000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) >> 8) & 0x00000000FF000000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) >> 24) & 0x0000000000FF0000LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) >> 40) & 0x000000000000FF00LL) |
|
||||
((*reinterpret_cast<const uint64_t*>(&v) >> 56) & 0x00000000000000FFLL));
|
||||
}
|
||||
|
||||
static void Take(T *ar, int len) {
|
||||
for ( int i = 0; i < len; ++i )
|
||||
ar[i] = Take(ar[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <int size>
|
||||
struct TypeMap {
|
||||
typedef void ValueType;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct TypeMap<1> {
|
||||
typedef uint8_t ValueType;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct TypeMap<2> {
|
||||
typedef uint16_t ValueType;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct TypeMap<4> {
|
||||
typedef uint32_t ValueType;
|
||||
};
|
||||
|
||||
|
||||
template <>
|
||||
struct TypeMap<8> {
|
||||
typedef uint64_t ValueType;
|
||||
};
|
||||
|
||||
|
||||
template <int swap, int size>
|
||||
struct ByteSwapper {
|
||||
static void Take(void *ar, int len) {}
|
||||
};
|
||||
|
||||
|
||||
template <int size>
|
||||
struct ByteSwapper<1,size> {
|
||||
static void Take(void *ar, int len) {
|
||||
typedef typename TypeMap<size>::ValueType T;
|
||||
Swapper<T,1,size>::Take(reinterpret_cast<T*>(ar), len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Current {
|
||||
enum {
|
||||
LittleEndian = ((0x1234 >> 8) == 0x12?1:0),
|
||||
BigEndian = 1-LittleEndian
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
struct Converter {
|
||||
template <typename T>
|
||||
static T ToLittleEndian(const T &v) {
|
||||
return Swapper<T,Current::BigEndian,sizeof(T)>::Take(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void ToLittleEndian(T *data, int len) {
|
||||
Swapper<T,Current::BigEndian,sizeof(T)>::Take(data, len);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T FromLittleEndian(const T &v) {
|
||||
return Swapper<T,Current::BigEndian,sizeof(T)>::Take(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T ToBigEndian(const T &v) {
|
||||
return Swapper<T,Current::LittleEndian,sizeof(T)>::Take(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T FromBigEndian(const T &v) {
|
||||
return Swapper<T,Current::LittleEndian,sizeof(T)>::Take(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct Reader {
|
||||
Reader(std::streambuf &input) : stream(input), good(true) {}
|
||||
|
||||
void operator()(void *data, int size) {
|
||||
good = stream.sgetn((char*)data, size) == size;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator()(T &v) {
|
||||
good = stream.sgetn((char*)&v, sizeof(T)) == sizeof(T);
|
||||
v = Converter::FromLittleEndian(v);
|
||||
}
|
||||
|
||||
std::streambuf &stream;
|
||||
bool good;
|
||||
};
|
||||
|
||||
struct Writer {
|
||||
Writer(std::streambuf &output) : stream(output), good(true) {}
|
||||
|
||||
bool operator()(const void *data, int size) {
|
||||
return (good = stream.sputn((char*)data, size) == size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(T &v) {
|
||||
T tmp = Converter::ToLittleEndian(v);
|
||||
return (good = stream.sputn((char*)&tmp, sizeof(T)) == sizeof(T));
|
||||
}
|
||||
|
||||
std::streambuf &stream;
|
||||
bool good;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
31
libs/gempa/caps/log.cpp
Normal file
31
libs/gempa/caps/log.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/***************************************************************************
|
||||
* 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 <stdio.h>
|
||||
#include <gempa/caps/log.h>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
void SetLogHandler(LogLevel l, LogOutput o) {
|
||||
LogHandler[l] = o;
|
||||
}
|
||||
|
||||
LogOutput LogHandler[LL_QUANTITY] = { NULL, NULL, NULL, NULL, NULL, NULL };
|
||||
|
||||
}
|
||||
}
|
61
libs/gempa/caps/log.h
Normal file
61
libs/gempa/caps/log.h
Normal file
@ -0,0 +1,61 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_LOG_H
|
||||
#define GEMPA_CAPS_LOG_H
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
enum LogLevel {
|
||||
LL_UNDEFINED = 0,
|
||||
LL_ERROR,
|
||||
LL_WARNING,
|
||||
LL_NOTICE,
|
||||
LL_INFO,
|
||||
LL_DEBUG,
|
||||
LL_QUANTITY
|
||||
};
|
||||
|
||||
|
||||
#define CAPS_LOG_MSG(LogLevel, ...) \
|
||||
do {\
|
||||
if ( Gempa::CAPS::LogHandler[LogLevel] != NULL )\
|
||||
Gempa::CAPS::LogHandler[LogLevel](__VA_ARGS__);\
|
||||
} while(0)
|
||||
|
||||
|
||||
#define CAPS_ERROR(...) CAPS_LOG_MSG(Gempa::CAPS::LL_ERROR, __VA_ARGS__)
|
||||
#define CAPS_WARNING(...) CAPS_LOG_MSG(Gempa::CAPS::LL_WARNING, __VA_ARGS__)
|
||||
#define CAPS_NOTICE(...) CAPS_LOG_MSG(Gempa::CAPS::LL_NOTICE, __VA_ARGS__)
|
||||
#define CAPS_INFO(...) CAPS_LOG_MSG(Gempa::CAPS::LL_INFO, __VA_ARGS__)
|
||||
#define CAPS_DEBUG(...) CAPS_LOG_MSG(Gempa::CAPS::LL_DEBUG, __VA_ARGS__)
|
||||
|
||||
|
||||
typedef void (*LogOutput)(const char *, ...);
|
||||
|
||||
extern LogOutput LogHandler[LL_QUANTITY];
|
||||
|
||||
|
||||
void SetLogHandler(LogLevel, LogOutput);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
81
libs/gempa/caps/metapacket.cpp
Normal file
81
libs/gempa/caps/metapacket.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/***************************************************************************
|
||||
* 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/metapacket.h>
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/riff.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
void MetaDataRecord::MetaHeader::setEndTime(const Time &ts) {
|
||||
int year, yday, hour, min, sec, usec;
|
||||
ts.get2(&year, &yday, &hour, &min, &sec, &usec);
|
||||
endTime.year = year;
|
||||
endTime.yday = yday;
|
||||
endTime.hour = hour;
|
||||
endTime.minute = min;
|
||||
endTime.second = sec;
|
||||
endTime.usec = usec;
|
||||
}
|
||||
|
||||
MetaDataRecord::MetaDataRecord() {}
|
||||
|
||||
|
||||
MetaDataRecord::Buffer *MetaDataRecord::data() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *MetaDataRecord::formatName() const {
|
||||
return "META";
|
||||
}
|
||||
|
||||
const DataRecord::Header *MetaDataRecord::header() const {
|
||||
return &_header.dataHeader;
|
||||
}
|
||||
|
||||
|
||||
Time MetaDataRecord::startTime() const {
|
||||
return _startTime;
|
||||
}
|
||||
|
||||
|
||||
Time MetaDataRecord::endTime() const {
|
||||
return _endTime;
|
||||
}
|
||||
|
||||
size_t MetaDataRecord::dataSize(bool /*withHeader*/) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
DataRecord::ReadStatus MetaDataRecord::get(std::streambuf &buf, int size,
|
||||
const Time &start, const Time &end,
|
||||
int maxBytes) {
|
||||
return RS_Error;
|
||||
}
|
||||
|
||||
void MetaDataRecord::setHeader(const MetaHeader &header) {
|
||||
_header = header;
|
||||
_startTime = timestampToTime(header.dataHeader.samplingTime);
|
||||
_endTime = timestampToTime(header.endTime);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
143
libs/gempa/caps/metapacket.h
Normal file
143
libs/gempa/caps/metapacket.h
Normal file
@ -0,0 +1,143 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_METAPACKET_H
|
||||
#define GEMPA_CAPS_METAPACKET_H
|
||||
|
||||
#include <gempa/caps/api.h>
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
struct SC_GEMPA_CAPS_API MetaResponseHeader {
|
||||
struct Time {
|
||||
int64_t seconds;
|
||||
int32_t microSeconds;
|
||||
};
|
||||
|
||||
Time startTime;
|
||||
Time endTime;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(startTime.seconds);
|
||||
get(startTime.microSeconds);
|
||||
|
||||
get(endTime.seconds);
|
||||
get(endTime.microSeconds);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
int dataSize() const {
|
||||
return sizeof(startTime.seconds) + sizeof(startTime.microSeconds) +
|
||||
sizeof(endTime.seconds) + sizeof(endTime.microSeconds);
|
||||
}
|
||||
};
|
||||
|
||||
class MetaDataRecord : public DataRecord {
|
||||
public:
|
||||
struct MetaHeader {
|
||||
Header dataHeader;
|
||||
TimeStamp endTime;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(endTime.year);
|
||||
get(endTime.yday);
|
||||
get(endTime.hour);
|
||||
get(endTime.minute);
|
||||
get(endTime.second);
|
||||
get(endTime.usec);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const;
|
||||
|
||||
int dataSize() const {
|
||||
return dataHeader.dataSize() +
|
||||
sizeof(endTime.year) +
|
||||
sizeof(endTime.yday) +
|
||||
sizeof(endTime.hour) +
|
||||
sizeof(endTime.minute) +
|
||||
sizeof(endTime.second) +
|
||||
sizeof(endTime.usec);
|
||||
}
|
||||
|
||||
void setEndTime(const Time ×tamp);
|
||||
};
|
||||
|
||||
public:
|
||||
MetaDataRecord();
|
||||
|
||||
//! Returns the data vector to be filled by the caller
|
||||
Buffer *data();
|
||||
|
||||
virtual const char *formatName() const;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const Header *header() const;
|
||||
|
||||
//! Sets a custom header and updates all internal structures
|
||||
//! based on the current data. If the data has changed, make
|
||||
//! sure to call this method again. If the flag skip reading
|
||||
//! is set get will not read the header information from stream
|
||||
void setHeader(const MetaHeader &header);
|
||||
|
||||
virtual Time startTime() const;
|
||||
virtual Time endTime() const;
|
||||
|
||||
virtual bool canTrim() const { return false; }
|
||||
virtual bool canMerge() const { return false; }
|
||||
|
||||
virtual bool trim(const Time &start, const Time &end) const { return false; }
|
||||
|
||||
virtual size_t dataSize(bool withHeader) const;
|
||||
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start, const Time &end,
|
||||
int maxBytes);
|
||||
|
||||
virtual bool put(std::streambuf &buf, bool withHeader) const { return false; }
|
||||
|
||||
PacketType packetType() const { return MetaDataPacket; }
|
||||
|
||||
private:
|
||||
MetaHeader _header;
|
||||
|
||||
mutable Time _startTime;
|
||||
mutable Time _endTime;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
75
libs/gempa/caps/mseed/encoder.h
Normal file
75
libs/gempa/caps/mseed/encoder.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*****************************************************************************
|
||||
* encoder.h
|
||||
*
|
||||
* Abstract Encoder interface
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_ENCODER_H
|
||||
#define CAPS_MSEED_ENCODER_H
|
||||
|
||||
#include "packet.h"
|
||||
#include "spclock.h"
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
#include <gempa/caps/pluginpacket.h>
|
||||
|
||||
#include <list>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
Encoder(int freqn, int freqd) : _clk(freqn, freqd),
|
||||
_sampleCount(0), _timingQuality(-1) {}
|
||||
virtual ~Encoder() {}
|
||||
|
||||
virtual void push(void *sample) = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual void reset() { _sampleCount = 0; }
|
||||
virtual int type() const = 0;
|
||||
|
||||
const SPClock& clk() { return _clk; }
|
||||
|
||||
void setStartTime(const SPClock::INT_TIME &time) { _clk.sync_time(time); }
|
||||
const SPClock::INT_TIME currentTime() const { return _clk.get_time(0); }
|
||||
|
||||
int timingQuality() { return _timingQuality; }
|
||||
void setTimingQuality(int quality) { _timingQuality = quality; }
|
||||
|
||||
PacketPtr pop() {
|
||||
if ( _packetQueue.empty() ) return PacketPtr();
|
||||
|
||||
PacketPtr rec = _packetQueue.front();
|
||||
_packetQueue.pop_front();
|
||||
return rec;
|
||||
}
|
||||
|
||||
protected:
|
||||
typedef std::list<PacketPtr> PacketQueue;
|
||||
SPClock _clk;
|
||||
int _sampleCount;
|
||||
PacketQueue _packetQueue;
|
||||
int _timingQuality;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __ENCODER_H__
|
||||
|
162
libs/gempa/caps/mseed/mseed.cpp
Normal file
162
libs/gempa/caps/mseed/mseed.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/*****************************************************************************
|
||||
* mseed.cpp
|
||||
*
|
||||
* Mini-SEED format implementation
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#include <gempa/caps/mseedpacket.h>
|
||||
#include <gempa/caps/rawpacket.h>
|
||||
|
||||
#include "mseed.h"
|
||||
#include "slink.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
MSEEDFormat::MSEEDFormat(const std::string &netcode, const std::string &stacode,
|
||||
const std::string &loccode, const std::string &chacode,
|
||||
unsigned short freqn, unsigned short freqd,
|
||||
unsigned short packtype_init,
|
||||
uint8_t recordLength)
|
||||
: networkCode(netcode), stationCode(stacode), locationCode(loccode),
|
||||
channelCode(chacode), packType(packtype_init), recordLength(recordLength)
|
||||
{
|
||||
if(freqn == 0 || freqd == 0) {
|
||||
sample_rate_factor = 0;
|
||||
sample_rate_multiplier = 0;
|
||||
}
|
||||
else if(!(freqn % freqd)) {
|
||||
sample_rate_factor = freqn / freqd;
|
||||
sample_rate_multiplier = 1;
|
||||
}
|
||||
else if(!(freqd % freqn)) {
|
||||
sample_rate_factor = -freqd / freqn;
|
||||
sample_rate_multiplier = 1;
|
||||
}
|
||||
else {
|
||||
sample_rate_factor = -freqd;
|
||||
sample_rate_multiplier = freqn;
|
||||
}
|
||||
}
|
||||
|
||||
MSEEDDataRecord *MSEEDFormat::get_buffer(const SPClock::INT_TIME &it, int usec_correction,
|
||||
int timing_quality, void *&dataptr, int &datalen) {
|
||||
size_t buflen = 1 << recordLength;
|
||||
MSEEDDataRecord *record = new MSEEDDataRecord();
|
||||
record->data()->resize(buflen);
|
||||
|
||||
char *buf = record->data()->data();
|
||||
int n;
|
||||
|
||||
sl_fsdh_s* fsdh = (sl_fsdh_s *)buf;
|
||||
|
||||
memset(fsdh, 0, buflen);
|
||||
|
||||
const int start_frames = (sizeof(sl_fsdh_s) + sizeof(sl_blkt_1000_s) +
|
||||
sizeof(sl_blkt_1001_s) + 63) & 0xffffffc0; // align to 64 bytes
|
||||
|
||||
dataptr = (void *)((char *) fsdh + start_frames);
|
||||
datalen = buflen - start_frames;
|
||||
|
||||
fsdh->dhq_indicator = 'D';
|
||||
fsdh->reserved = ' ';
|
||||
|
||||
strncpy(fsdh->station, stationCode.c_str(), 5);
|
||||
if((n = stationCode.length()) < 5) memset(fsdh->station + n, 32, 5 - n);
|
||||
strncpy(fsdh->location, locationCode.c_str(), 2);
|
||||
if((n = locationCode.length()) < 2) memset(fsdh->location + n, 32, 2 - n);
|
||||
strncpy(fsdh->channel, channelCode.c_str(), 3);
|
||||
if((n = channelCode.length()) < 3) memset(fsdh->channel + n, 32, 3 - n);
|
||||
strncpy(fsdh->network, networkCode.c_str(), 2);
|
||||
if((n = networkCode.length()) < 2) memset(fsdh->network + n, 32, 2 - n);
|
||||
|
||||
int year, doy, hour, min, sec;
|
||||
it.get2(&year, &doy, &hour, &min, &sec);
|
||||
|
||||
fsdh->start_time.year = htons(year);
|
||||
fsdh->start_time.day = htons(doy+1);
|
||||
fsdh->start_time.hour = hour;
|
||||
fsdh->start_time.min = min;
|
||||
fsdh->start_time.sec = sec;
|
||||
fsdh->start_time.fract = htons(it.microseconds() / 100);
|
||||
fsdh->samprate_fact = (int16_t)htons(sample_rate_factor);
|
||||
fsdh->samprate_mult = (int16_t)htons(sample_rate_multiplier);
|
||||
fsdh->num_blockettes = 1;
|
||||
|
||||
div_t d_corr = div(usec_correction + ((usec_correction < 0) ? -50: 50), 100);
|
||||
fsdh->time_correct = (int32_t)htonl(d_corr.quot);
|
||||
|
||||
fsdh->begin_data = htons(start_frames);
|
||||
fsdh->begin_blockette = htons(sizeof(sl_fsdh_s));
|
||||
|
||||
sl_blkt_1000_s* blkt_1000 = (sl_blkt_1000_s *)((char *) fsdh + sizeof(sl_fsdh_s));
|
||||
blkt_1000->blkt_type = htons(1000); // Data Only SEED Blockette
|
||||
|
||||
blkt_1000->encoding = packType;
|
||||
blkt_1000->word_swap = 1; // big endian
|
||||
blkt_1000->rec_len = recordLength; // 9 = 512 bytes
|
||||
|
||||
if ( timing_quality >= 0 ) {
|
||||
blkt_1000->next_blkt = htons(sizeof(sl_fsdh_s) + sizeof(sl_blkt_1000_s));
|
||||
++fsdh->num_blockettes;
|
||||
|
||||
sl_blkt_1001_s* blkt_1001 = (sl_blkt_1001_s *)((char *) fsdh +
|
||||
sizeof(sl_fsdh_s) + sizeof(sl_blkt_1000_s));
|
||||
|
||||
blkt_1001->blkt_type = htons(1001); // Data Extension Blockette
|
||||
blkt_1001->timing_qual = timing_quality;
|
||||
|
||||
blkt_1001->usec = 0;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
void MSEEDFormat::updateBuffer(MSEEDDataRecord *rec, int samples, int frames) {
|
||||
sl_fsdh_s* fsdh = (sl_fsdh_s *)rec->data()->data();
|
||||
char temp[7];
|
||||
|
||||
sprintf(temp, "%06d", (int)0);
|
||||
memcpy(fsdh->sequence_number,temp,6);
|
||||
fsdh->dhq_indicator = 'D';
|
||||
fsdh->num_samples = htons(samples);
|
||||
|
||||
sl_blkt_1000_s* blkt_1000 = (sl_blkt_1000_s *)((char *) fsdh + sizeof(sl_fsdh_s));
|
||||
|
||||
if ( ntohs(blkt_1000->next_blkt) != 0 ) {
|
||||
sl_blkt_1001_s* blkt_1001 = (sl_blkt_1001_s *)((char *) fsdh +
|
||||
sizeof(sl_fsdh_s) + sizeof(sl_blkt_1000_s));
|
||||
|
||||
blkt_1001->frame_cnt = frames;
|
||||
}
|
||||
|
||||
rec->unpackHeader();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
90
libs/gempa/caps/mseed/mseed.h
Normal file
90
libs/gempa/caps/mseed/mseed.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*****************************************************************************
|
||||
* mseed.h
|
||||
*
|
||||
* Mini-SEED format implementation
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_MSEED_H
|
||||
#define CAPS_MSEED_MSEED_H
|
||||
|
||||
#include "packet.h"
|
||||
#include "spclock.h"
|
||||
|
||||
#include <gempa/caps/mseedpacket.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
/* SEED data encoding types */
|
||||
enum SEEDDataEncodingType {
|
||||
DE_ASCII = 0,
|
||||
DE_INT16 = 1,
|
||||
DE_INT32 = 3,
|
||||
DE_FLOAT32 = 4,
|
||||
DE_FLOAT64 = 5,
|
||||
DE_STEIM1 = 10,
|
||||
DE_STEIM2 = 11,
|
||||
DE_GEOSCOPE24 = 12,
|
||||
DE_GEOSCOPE163 = 13,
|
||||
DE_GEOSCOPE164 = 14,
|
||||
DE_CDSN = 16,
|
||||
DE_SRO = 30,
|
||||
DE_DWWSSN = 32
|
||||
};
|
||||
|
||||
|
||||
struct MSEEDFormat {
|
||||
MSEEDFormat(const std::string &networkCode, const std::string &stationCode,
|
||||
const std::string &locationCode, const std::string &channelCode,
|
||||
unsigned short freqn, unsigned short freqd,
|
||||
unsigned short packtype_init,
|
||||
uint8_t recLen);
|
||||
|
||||
template<class T>
|
||||
MSEEDEncoderPacket<T>
|
||||
get_packet(const SPClock::INT_TIME &it, int usec_correction, int timing_quality) {
|
||||
void *dataptr = NULL;
|
||||
int datalen = 0;
|
||||
unsigned int size = 0;
|
||||
MSEEDDataRecord *rec = get_buffer(it, usec_correction, timing_quality, dataptr, datalen);
|
||||
|
||||
return MSEEDEncoderPacket<T>(rec, size, dataptr, datalen);
|
||||
}
|
||||
|
||||
MSEEDDataRecord *get_buffer(const SPClock::INT_TIME &it, int usec_correction,
|
||||
int timing_quality,
|
||||
void *&dataptr, int &datalen);
|
||||
|
||||
void updateBuffer(MSEEDDataRecord *rec, int samples, int frames);
|
||||
|
||||
std::string networkCode;
|
||||
std::string stationCode;
|
||||
std::string locationCode;
|
||||
std::string channelCode;
|
||||
int sample_rate_factor;
|
||||
int sample_rate_multiplier;
|
||||
unsigned short packType;
|
||||
int timingQuality;
|
||||
uint8_t recordLength;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
44
libs/gempa/caps/mseed/packet.h
Normal file
44
libs/gempa/caps/mseed/packet.h
Normal file
@ -0,0 +1,44 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_PACKET_H
|
||||
#define CAPS_MSEED_PACKET_H
|
||||
|
||||
#include <gempa/caps/mseedpacket.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
template<typename T> struct MSEEDEncoderPacket {
|
||||
MSEEDEncoderPacket(): record(NULL), data(NULL), datalen(0) {}
|
||||
MSEEDEncoderPacket(MSEEDDataRecord *rec, unsigned int size_init,
|
||||
void *data_init, unsigned short datalen_init)
|
||||
: record(rec), data(static_cast<T*>(data_init)),
|
||||
datalen(datalen_init) {}
|
||||
~MSEEDEncoderPacket() {}
|
||||
|
||||
void reset() { record = NULL; data = NULL; datalen = 0; }
|
||||
bool valid() const { return record != NULL; }
|
||||
|
||||
MSEEDDataRecord *record;
|
||||
T *data;
|
||||
unsigned short datalen;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
131
libs/gempa/caps/mseed/slink.h
Normal file
131
libs/gempa/caps/mseed/slink.h
Normal file
@ -0,0 +1,131 @@
|
||||
/*****************************************************************************
|
||||
* slink.h
|
||||
*
|
||||
* SeedLink protocol constants
|
||||
*
|
||||
* Token from libslink
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_SLINK_H
|
||||
#define CAPS_MSEED_SLINK_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Portability to the XScale (ARM) architecture
|
||||
* requires a packed attribute in certain places
|
||||
* but this only works with GCC for now.
|
||||
*/
|
||||
#if defined (__GNUC__)
|
||||
#define SLP_PACKED __attribute__ ((packed))
|
||||
#else
|
||||
#define SLP_PACKED
|
||||
#endif
|
||||
|
||||
#define SIGNATURE "SL" /* SeedLink header signature */
|
||||
#define INFOSIGNATURE "SLINFO" /* SeedLink INFO packet signature */
|
||||
|
||||
|
||||
/* SeedLink packet types */
|
||||
#define SLDATA 0 /* waveform data record */
|
||||
#define SLDET 1 /* detection record */
|
||||
#define SLCAL 2 /* calibration record */
|
||||
#define SLTIM 3 /* timing record */
|
||||
#define SLMSG 4 /* message record */
|
||||
#define SLBLK 5 /* general record */
|
||||
#define SLNUM 6 /* used as the error indicator (same as SLCHA) */
|
||||
#define SLCHA 6 /* for requesting channel info or detectors */
|
||||
#define SLINF 7 /* a non-terminating XML formatted message in a miniSEED
|
||||
log record, used for INFO responses */
|
||||
#define SLINFT 8 /* a terminating XML formatted message in a miniSEED log
|
||||
record, used for INFO responses */
|
||||
#define SLKEEP 9 /* an XML formatted message in a miniSEED log
|
||||
record, used for keepalive/heartbeat responses */
|
||||
|
||||
|
||||
/* SEED binary time (10 bytes) */
|
||||
struct sl_btime_s
|
||||
{
|
||||
uint16_t year;
|
||||
uint16_t day;
|
||||
uint8_t hour;
|
||||
uint8_t min;
|
||||
uint8_t sec;
|
||||
uint8_t unused;
|
||||
uint16_t fract;
|
||||
} SLP_PACKED;
|
||||
|
||||
|
||||
/* Fixed section data of header (48 bytes) */
|
||||
struct sl_fsdh_s
|
||||
{
|
||||
char sequence_number[6];
|
||||
char dhq_indicator;
|
||||
char reserved;
|
||||
char station[5];
|
||||
char location[2];
|
||||
char channel[3];
|
||||
char network[2];
|
||||
struct sl_btime_s start_time;
|
||||
uint16_t num_samples;
|
||||
int16_t samprate_fact;
|
||||
int16_t samprate_mult;
|
||||
uint8_t act_flags;
|
||||
uint8_t io_flags;
|
||||
uint8_t dq_flags;
|
||||
uint8_t num_blockettes;
|
||||
int32_t time_correct;
|
||||
uint16_t begin_data;
|
||||
uint16_t begin_blockette;
|
||||
} SLP_PACKED;
|
||||
|
||||
|
||||
/* 1000 Blockette (8 bytes) */
|
||||
struct sl_blkt_1000_s
|
||||
{
|
||||
uint16_t blkt_type;
|
||||
uint16_t next_blkt;
|
||||
uint8_t encoding;
|
||||
uint8_t word_swap;
|
||||
uint8_t rec_len;
|
||||
uint8_t reserved;
|
||||
} SLP_PACKED;
|
||||
|
||||
/* 1001 Blockette (8 bytes) */
|
||||
struct sl_blkt_1001_s
|
||||
{
|
||||
uint16_t blkt_type;
|
||||
uint16_t next_blkt;
|
||||
int8_t timing_qual;
|
||||
int8_t usec;
|
||||
uint8_t reserved;
|
||||
int8_t frame_cnt;
|
||||
} SLP_PACKED;
|
||||
|
||||
/* Generic struct for head of blockettes */
|
||||
struct sl_blkt_head_s
|
||||
{
|
||||
uint16_t blkt_type;
|
||||
uint16_t next_blkt;
|
||||
} SLP_PACKED;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
78
libs/gempa/caps/mseed/spclock.h
Normal file
78
libs/gempa/caps/mseed/spclock.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*****************************************************************************
|
||||
* spclock.h
|
||||
*
|
||||
* Stream Processor Clock
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_SPCLOCK_H
|
||||
#define CAPS_MSEED_SPCLOCK_H
|
||||
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
class SPClock
|
||||
{
|
||||
public:
|
||||
typedef Gempa::CAPS::Time INT_TIME;
|
||||
|
||||
private:
|
||||
INT_TIME itime;
|
||||
int ticks;
|
||||
int corr;
|
||||
|
||||
public:
|
||||
const int freqn;
|
||||
const int freqd;
|
||||
|
||||
SPClock(int freqn_init, int freqd_init): ticks(0), corr(0),
|
||||
freqn(freqn_init), freqd(freqd_init)
|
||||
{}
|
||||
|
||||
void sync_time(const INT_TIME &time)
|
||||
{
|
||||
itime = time;
|
||||
ticks = 0;
|
||||
corr = 0;
|
||||
}
|
||||
|
||||
void tick()
|
||||
{
|
||||
++ticks;
|
||||
}
|
||||
|
||||
INT_TIME get_time(int tick_diff) const
|
||||
{
|
||||
int64_t correctness = (double)freqd / (double)freqn * 1000000 * (ticks - tick_diff - corr);
|
||||
return itime + Gempa::CAPS::TimeSpan(long(correctness/1000000),long(correctness%1000000));
|
||||
}
|
||||
|
||||
int correction() const
|
||||
{
|
||||
return corr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // SPCLOCK_H
|
90
libs/gempa/caps/mseed/steim1.h
Normal file
90
libs/gempa/caps/mseed/steim1.h
Normal file
@ -0,0 +1,90 @@
|
||||
/*****************************************************************************
|
||||
* steim1.h
|
||||
*
|
||||
* Steim1 encoder
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_STEIM1_H
|
||||
#define CAPS_MSEED_STEIM1_H
|
||||
|
||||
#include "encoder.h"
|
||||
#include "mseed.h"
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
//*****************************************************************************
|
||||
// Steim1Frame
|
||||
//*****************************************************************************
|
||||
|
||||
struct Steim1Frame {
|
||||
u_int32_t nibble_word;
|
||||
u_int32_t sample_word[15];
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// Steim1Encoder
|
||||
//*****************************************************************************
|
||||
|
||||
template<typename T>
|
||||
class Steim1Encoder: public Encoder {
|
||||
private:
|
||||
MSEEDFormat *format;
|
||||
int frame_count;
|
||||
int bp;
|
||||
int fp;
|
||||
int spw;
|
||||
int32_t last_sample;
|
||||
int32_t buf[5];
|
||||
u_int32_t nibble_word;
|
||||
MSEEDEncoderPacket<Steim1Frame> current_packet;
|
||||
|
||||
void update_spw(int bp);
|
||||
void store(int32_t value);
|
||||
void init_packet();
|
||||
void finish_packet();
|
||||
void update_packet();
|
||||
|
||||
MSEEDEncoderPacket<Steim1Frame> get_packet() {
|
||||
return format->get_packet<Steim1Frame>(_clk.get_time(bp),
|
||||
_clk.correction(), _timingQuality);
|
||||
}
|
||||
|
||||
void queue_packet(MSEEDEncoderPacket<Steim1Frame> &pckt);
|
||||
|
||||
int number_of_frames(const MSEEDEncoderPacket<Steim1Frame> &packet) {
|
||||
return (packet.datalen >> 6);
|
||||
}
|
||||
|
||||
public:
|
||||
Steim1Encoder(MSEEDFormat *format, int freqn, int freqd)
|
||||
: Encoder(freqn, freqd), format(format), frame_count(0),
|
||||
bp(0), fp(0), spw(4), last_sample(0), nibble_word(0) {}
|
||||
virtual ~Steim1Encoder();
|
||||
virtual void flush();
|
||||
virtual void push(void *value);
|
||||
virtual int type() const { return DE_STEIM1; }
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#include "steim1.ipp"
|
||||
|
||||
#endif // __STEIM1_H__
|
184
libs/gempa/caps/mseed/steim1.ipp
Normal file
184
libs/gempa/caps/mseed/steim1.ipp
Normal file
@ -0,0 +1,184 @@
|
||||
/*****************************************************************************
|
||||
* steim1.cc
|
||||
*
|
||||
* Steim1 encoder
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
template<typename T> Steim1Encoder<T>::~Steim1Encoder() {
|
||||
if ( format != NULL ) delete format;
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::update_spw(int bp) {
|
||||
int spw1 = 4;
|
||||
|
||||
assert(bp < 4);
|
||||
if(buf[bp] < -32768 || buf[bp] > 32767) spw1 = 1;
|
||||
else if(buf[bp] < -128 || buf[bp] > 127) spw1 = 2;
|
||||
if(spw1 < spw) spw = spw1;
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::store(int32_t value) {
|
||||
assert(bp < 4);
|
||||
buf[bp] = value - last_sample;
|
||||
last_sample = value;
|
||||
update_spw(bp);
|
||||
++bp;
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::init_packet() {
|
||||
int i;
|
||||
int32_t begin_sample = last_sample;
|
||||
|
||||
for(i = 1; i < bp; ++i) {
|
||||
begin_sample -= buf[i];
|
||||
}
|
||||
|
||||
reset();
|
||||
current_packet.data[0].sample_word[0] = htonl(begin_sample);
|
||||
frame_count = 0;
|
||||
nibble_word = 0;
|
||||
fp = 2;
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::finish_packet() {
|
||||
int i;
|
||||
int32_t end_sample = last_sample;
|
||||
|
||||
for(i = 0; i < bp; ++i) {
|
||||
end_sample -= buf[i];
|
||||
}
|
||||
|
||||
current_packet.data[0].sample_word[1] = htonl(end_sample);
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::update_packet() {
|
||||
unsigned int nibble = 0;
|
||||
u_int32_t sample_word = 0;
|
||||
|
||||
assert(bp < 5);
|
||||
|
||||
int used = bp;
|
||||
|
||||
while(used > spw) {
|
||||
--used;
|
||||
spw = 4;
|
||||
for(int i = 0; i < used; ++i) update_spw(i);
|
||||
}
|
||||
|
||||
while(used < spw) spw >>= 1;
|
||||
|
||||
used = spw;
|
||||
|
||||
switch(spw) {
|
||||
case 4:
|
||||
nibble = 1;
|
||||
sample_word = ((buf[0] & 0xff) << 24) | ((buf[1] & 0xff) << 16) |
|
||||
((buf[2] & 0xff) << 8) | (buf[3] & 0xff);
|
||||
break;
|
||||
case 2:
|
||||
nibble = 2;
|
||||
sample_word = ((buf[0] & 0xffff) << 16) | (buf[1] & 0xffff);
|
||||
break;
|
||||
case 1:
|
||||
nibble = 3;
|
||||
sample_word = buf[0];
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
nibble_word |= (nibble << (30 - ((fp + 1) << 1)));
|
||||
|
||||
spw = 4;
|
||||
for(int i = 0; i < bp - used; ++i) {
|
||||
buf[i] = buf[i + used];
|
||||
update_spw(i);
|
||||
}
|
||||
|
||||
bp -= used;
|
||||
_sampleCount += used;
|
||||
|
||||
current_packet.data[frame_count].nibble_word = htonl(nibble_word);
|
||||
current_packet.data[frame_count].sample_word[fp] = htonl(sample_word);
|
||||
if(++fp < 15) return;
|
||||
|
||||
nibble_word = 0;
|
||||
fp = 0;
|
||||
++frame_count;
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::queue_packet(MSEEDEncoderPacket<Steim1Frame> &pckt) {
|
||||
format->updateBuffer(pckt.record, _sampleCount, frame_count + (fp > 0));
|
||||
|
||||
Packet *packet = new Packet(DataRecordPtr(pckt.record), format->networkCode, format->stationCode,
|
||||
format->locationCode, format->channelCode);
|
||||
_packetQueue.push_back(PacketPtr(packet));
|
||||
pckt.reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::push(void *value) {
|
||||
int32_t sample_val = *static_cast<T*>(value);
|
||||
store(sample_val);
|
||||
_clk.tick();
|
||||
|
||||
while(bp >= spw) {
|
||||
if(!current_packet.valid()) {
|
||||
current_packet = get_packet();
|
||||
init_packet();
|
||||
}
|
||||
|
||||
update_packet();
|
||||
if(frame_count == number_of_frames(current_packet)) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void Steim1Encoder<T>::flush() {
|
||||
while(bp) {
|
||||
if(!current_packet.valid()) {
|
||||
current_packet = get_packet();
|
||||
init_packet();
|
||||
}
|
||||
|
||||
update_packet();
|
||||
if(frame_count == number_of_frames(current_packet)) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
|
||||
if(current_packet.valid()) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
94
libs/gempa/caps/mseed/steim2.h
Normal file
94
libs/gempa/caps/mseed/steim2.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*****************************************************************************
|
||||
* steim2.h
|
||||
*
|
||||
* Steim2 encoder
|
||||
*
|
||||
* (c) 2000 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_STEIM2_H
|
||||
#define CAPS_MSEED_STEIM2_H
|
||||
|
||||
#include "encoder.h"
|
||||
#include "mseed.h"
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
//*****************************************************************************
|
||||
// Steim2Frame
|
||||
//*****************************************************************************
|
||||
|
||||
struct Steim2Frame {
|
||||
u_int32_t nibble_word;
|
||||
u_int32_t sample_word[15];
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
// Steim2Encoder
|
||||
//*****************************************************************************
|
||||
|
||||
template<typename T>
|
||||
class Steim2Encoder : public Encoder {
|
||||
public:
|
||||
Steim2Encoder(MSEEDFormat *format, int freqn, int freqd)
|
||||
: Encoder(freqn, freqd), format(format), frame_count(0),
|
||||
bp(0), fp(0), spw(4), last_sample(0), nibble_word(0) {
|
||||
}
|
||||
virtual ~Steim2Encoder();
|
||||
virtual void flush();
|
||||
|
||||
virtual void push(void *value);
|
||||
virtual int type() const { return DE_STEIM2; }
|
||||
|
||||
private:
|
||||
void update_spw(int bp);
|
||||
void store(int32_t value);
|
||||
void init_packet();
|
||||
void finish_packet();
|
||||
void update_packet();
|
||||
|
||||
MSEEDEncoderPacket<Steim2Frame> get_packet() {
|
||||
return format->get_packet<Steim2Frame>(_clk.get_time(bp),
|
||||
_clk.correction(), _timingQuality);
|
||||
}
|
||||
|
||||
void queue_packet(MSEEDEncoderPacket<Steim2Frame> &pckt);
|
||||
|
||||
int number_of_frames(const MSEEDEncoderPacket<Steim2Frame> &packet) {
|
||||
return (packet.datalen >> 6);
|
||||
}
|
||||
|
||||
private:
|
||||
MSEEDFormat *format;
|
||||
int frame_count;
|
||||
int bp;
|
||||
int fp;
|
||||
int32_t last_s;
|
||||
int spw;
|
||||
int32_t last_sample;
|
||||
int32_t buf[8];
|
||||
u_int32_t nibble_word;
|
||||
MSEEDEncoderPacket<Steim2Frame> current_packet;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#include "steim2.ipp"
|
||||
|
||||
#endif // __STEIM2_H__
|
||||
|
241
libs/gempa/caps/mseed/steim2.ipp
Normal file
241
libs/gempa/caps/mseed/steim2.ipp
Normal file
@ -0,0 +1,241 @@
|
||||
/*****************************************************************************
|
||||
* steim2.cc
|
||||
*
|
||||
* Steim2 encoder
|
||||
*
|
||||
* (c) 2004 Andres Heinloo, GFZ Potsdam
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2, or (at your option) any later
|
||||
* version. For more information, see http://www.gnu.org/
|
||||
*
|
||||
* ================
|
||||
* Change log
|
||||
* ===============
|
||||
*
|
||||
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
|
||||
*****************************************************************************/
|
||||
|
||||
#include "../log.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
template<typename T> Steim2Encoder<T>::~Steim2Encoder() {
|
||||
if ( format != NULL ) delete format;
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::update_spw(int bp) {
|
||||
assert(bp < 7);
|
||||
|
||||
if(buf[bp] < -536870912) {
|
||||
CAPS_WARNING("%s.%s.%s.%s: value %d is too large for Steim2 encoding",
|
||||
format->networkCode.c_str(), format->stationCode.c_str(),
|
||||
format->locationCode.c_str(), format->channelCode.c_str(),
|
||||
buf[bp]);
|
||||
buf[bp] = -536870912;
|
||||
spw = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(buf[bp] > 536870911) {
|
||||
CAPS_WARNING("%s.%s.%s.%s: value %d is too large for Steim2 encoding",
|
||||
format->networkCode.c_str(), format->stationCode.c_str(),
|
||||
format->locationCode.c_str(), format->channelCode.c_str(),
|
||||
buf[bp]);
|
||||
buf[bp] = 536870911;
|
||||
spw = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
int spw1 = 7;
|
||||
if(buf[bp] < -16384 || buf[bp] > 16383) spw1 = 1;
|
||||
else if(buf[bp] < -512 || buf[bp] > 511) spw1 = 2;
|
||||
else if(buf[bp] < -128 || buf[bp] > 127) spw1 = 3;
|
||||
else if(buf[bp] < -32 || buf[bp] > 31) spw1 = 4;
|
||||
else if(buf[bp] < -16 || buf[bp] > 15) spw1 = 5;
|
||||
else if(buf[bp] < -8 || buf[bp] > 7) spw1 = 6;
|
||||
if(spw1 < spw) spw = spw1;
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::store(int32_t value) {
|
||||
assert(bp < 7);
|
||||
buf[bp] = value - last_sample;
|
||||
last_sample = value;
|
||||
update_spw(bp);
|
||||
++bp;
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::init_packet() {
|
||||
int i;
|
||||
int32_t begin_sample = last_sample;
|
||||
|
||||
for(i = 1; i < bp; ++i) {
|
||||
begin_sample -= buf[i];
|
||||
}
|
||||
|
||||
reset();
|
||||
current_packet.data[0].sample_word[0] = htonl(begin_sample);
|
||||
frame_count = 0;
|
||||
nibble_word = 0;
|
||||
fp = 2;
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::finish_packet() {
|
||||
int i;
|
||||
int32_t end_sample = last_sample;
|
||||
|
||||
for(i = 0; i < bp; ++i) {
|
||||
end_sample -= buf[i];
|
||||
}
|
||||
|
||||
current_packet.data[0].sample_word[1] = htonl(end_sample);
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::update_packet() {
|
||||
unsigned int nibble = 0;
|
||||
u_int32_t sample_word = 0;
|
||||
|
||||
assert(bp < 8);
|
||||
|
||||
int used = bp;
|
||||
|
||||
while(used > spw) {
|
||||
--used;
|
||||
spw = 7;
|
||||
for(int i = 0; i < used; ++i) update_spw(i);
|
||||
}
|
||||
|
||||
spw = used;
|
||||
|
||||
switch(spw) {
|
||||
case 7:
|
||||
nibble = 3;
|
||||
sample_word = (2U << 30) | ((buf[0] & 0xf) << 24) |
|
||||
((buf[1] & 0xf) << 20) | ((buf[2] & 0xf) << 16) |
|
||||
((buf[3] & 0xf) << 12) | ((buf[4] & 0xf) << 8) |
|
||||
((buf[5] & 0xf) << 4) | (buf[6] & 0xf);
|
||||
break;
|
||||
case 6:
|
||||
nibble = 3;
|
||||
sample_word = (1U << 30) | ((buf[0] & 0x1f) << 25) |
|
||||
((buf[1] & 0x1f) << 20) | ((buf[2] & 0x1f) << 15) |
|
||||
((buf[3] & 0x1f) << 10) | ((buf[4] & 0x1f) << 5) |
|
||||
(buf[5] & 0x1f);
|
||||
break;
|
||||
case 5:
|
||||
nibble = 3;
|
||||
sample_word = ((buf[0] & 0x3f) << 24) | ((buf[1] & 0x3f) << 18) |
|
||||
((buf[2] & 0x3f) << 12) | ((buf[3] & 0x3f) << 6) |
|
||||
(buf[4] & 0x3f);
|
||||
break;
|
||||
case 4:
|
||||
nibble = 1;
|
||||
sample_word = ((buf[0] & 0xff) << 24) | ((buf[1] & 0xff) << 16) |
|
||||
((buf[2] & 0xff) << 8) | (buf[3] & 0xff);
|
||||
break;
|
||||
case 3:
|
||||
nibble = 2;
|
||||
sample_word = (3U << 30) | ((buf[0] & 0x3ff) << 20) |
|
||||
((buf[1] & 0x3ff) << 10) | (buf[2] & 0x3ff);
|
||||
break;
|
||||
case 2:
|
||||
nibble = 2;
|
||||
sample_word = (2U << 30) | ((buf[0] & 0x7fff) << 15) |
|
||||
(buf[1] & 0x7fff);
|
||||
break;
|
||||
case 1:
|
||||
nibble = 2;
|
||||
sample_word = (1U << 30) | (buf[0] & 0x3fffffff);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
nibble_word |= (nibble << (30 - ((fp + 1) << 1)));
|
||||
|
||||
spw = 7;
|
||||
for(int i = 0; i < bp - used; ++i) {
|
||||
buf[i] = buf[i + used];
|
||||
update_spw(i);
|
||||
}
|
||||
|
||||
bp -= used;
|
||||
_sampleCount += used;
|
||||
|
||||
current_packet.data[frame_count].nibble_word = htonl(nibble_word);
|
||||
current_packet.data[frame_count].sample_word[fp] = htonl(sample_word);
|
||||
if(++fp < 15) return;
|
||||
|
||||
nibble_word = 0;
|
||||
fp = 0;
|
||||
++frame_count;
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::queue_packet(MSEEDEncoderPacket<Steim2Frame> &pckt) {
|
||||
format->updateBuffer(pckt.record, _sampleCount, frame_count + (fp > 0));
|
||||
|
||||
Packet *packet = new Packet(DataRecordPtr(pckt.record), format->networkCode, format->stationCode,
|
||||
format->locationCode, format->channelCode);
|
||||
_packetQueue.push_back(PacketPtr(packet));
|
||||
pckt.reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::push(void *value) {
|
||||
int32_t sample_val = *static_cast<T*>(value);
|
||||
store(sample_val);
|
||||
_clk.tick();
|
||||
|
||||
while ( bp >= spw ) {
|
||||
if( !current_packet.valid() ) {
|
||||
current_packet = get_packet();
|
||||
init_packet();
|
||||
}
|
||||
|
||||
update_packet();
|
||||
if ( frame_count == number_of_frames(current_packet) ) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> void Steim2Encoder<T>::flush() {
|
||||
while ( bp ) {
|
||||
if ( !current_packet.valid() ) {
|
||||
current_packet = get_packet();
|
||||
init_packet();
|
||||
}
|
||||
|
||||
update_packet();
|
||||
if( frame_count == number_of_frames(current_packet) ) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
|
||||
if ( current_packet.valid() ) {
|
||||
finish_packet();
|
||||
queue_packet(current_packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
84
libs/gempa/caps/mseed/uncompressed.h
Normal file
84
libs/gempa/caps/mseed/uncompressed.h
Normal file
@ -0,0 +1,84 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef CAPS_MSEED_UNCOMPRESSED_H
|
||||
#define CAPS_MSEED_UNCOMPRESSED_H
|
||||
|
||||
#include "encoder.h"
|
||||
#include "mseed.h"
|
||||
|
||||
#include <gempa/caps/endianess.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
template<typename T> class UncompressedMSEED : public Encoder {
|
||||
MSEEDEncoderPacket<T> get_packet() {
|
||||
return _format->get_packet<T>(_clk.get_time(-_bp),
|
||||
_clk.correction(), _timingQuality);
|
||||
}
|
||||
|
||||
void queue_packet(MSEEDEncoderPacket<T> &pckt) {
|
||||
_format->updateBuffer(pckt.record, _sampleCount, 1);
|
||||
|
||||
Packet *packet = new Packet(DataRecordPtr(pckt.record), _format->networkCode, _format->stationCode,
|
||||
_format->locationCode, _format->channelCode);
|
||||
_packetQueue.push_back(PacketPtr(packet));
|
||||
pckt.reset();
|
||||
reset();
|
||||
}
|
||||
|
||||
public:
|
||||
UncompressedMSEED(MSEEDFormat *format, int freqn, int freqd)
|
||||
: Encoder(freqn, freqd),
|
||||
_format(format), _bp(0) {
|
||||
}
|
||||
virtual ~UncompressedMSEED() { if ( _format ) delete _format; }
|
||||
virtual void flush() {
|
||||
if ( _current_packet.valid() ) {
|
||||
queue_packet(_current_packet);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void push(void *value) {
|
||||
if ( !_current_packet.valid() )
|
||||
_current_packet = get_packet();
|
||||
else if ( _sampleCount * sizeof(T) >= _current_packet.datalen ) {
|
||||
flush();
|
||||
_current_packet = get_packet();
|
||||
}
|
||||
|
||||
_current_packet.data[_sampleCount] =
|
||||
Gempa::CAPS::Endianess::Converter::ToBigEndian<T>(*(T*)value);
|
||||
++_sampleCount;
|
||||
++_bp;
|
||||
}
|
||||
|
||||
virtual int type() const { return _format->packType; }
|
||||
|
||||
private:
|
||||
MSEEDFormat *_format;
|
||||
MSEEDEncoderPacket<T> _current_packet;
|
||||
int _bp;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __STEIM1_H__
|
491
libs/gempa/caps/mseedpacket.cpp
Normal file
491
libs/gempa/caps/mseedpacket.cpp
Normal file
@ -0,0 +1,491 @@
|
||||
/***************************************************************************
|
||||
* 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) {
|
||||
#if 1 // Set this to 1 to enable no-malloc fast MSeed meta parser
|
||||
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);
|
||||
#else
|
||||
std::vector<char> data(size);
|
||||
size_t read = buf.sgetn(&data[0], data.size());
|
||||
if ( read != data.size() ) {
|
||||
CAPS_WARNING("read metadata: input buffer underflow: only %d/%d bytes read",
|
||||
(int)read, (int)data.size());
|
||||
return;
|
||||
}
|
||||
|
||||
unpackHeader(&data[0], data.size());
|
||||
|
||||
header = _header;
|
||||
startTime = _startTime;
|
||||
endTime = _endTime;
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
// Only unpack the header structure
|
||||
int state = msr_unpack(&_data[0], _data.size(), &ms_rec, 0, 0);
|
||||
if ( state != MS_NOERROR ) {
|
||||
switch ( state ) {
|
||||
case MS_GENERROR:
|
||||
CAPS_WARNING("get: generic libmseed error");
|
||||
break;
|
||||
case MS_NOTSEED:
|
||||
CAPS_WARNING("get: input data is not seed");
|
||||
break;
|
||||
case MS_WRONGLENGTH:
|
||||
CAPS_WARNING("get: length of data read was not correct");
|
||||
break;
|
||||
case MS_OUTOFRANGE:
|
||||
CAPS_WARNING("get: SEED record length out of range");
|
||||
break;
|
||||
case MS_UNKNOWNFORMAT:
|
||||
CAPS_WARNING("get: unknown data encoding format");
|
||||
break;
|
||||
case MS_STBADCOMPFLAG:
|
||||
CAPS_WARNING("get: invalid Steim compression flag(s)");
|
||||
break;
|
||||
}
|
||||
if ( ms_rec != NULL )
|
||||
msr_free(&ms_rec);
|
||||
return RS_Error;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
92
libs/gempa/caps/mseedpacket.h
Normal file
92
libs/gempa/caps/mseedpacket.h
Normal file
@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_MSEEDPACKET_H
|
||||
#define GEMPA_CAPS_MSEEDPACKET_H
|
||||
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
class MSEEDDataRecord : public DataRecord {
|
||||
public:
|
||||
MSEEDDataRecord();
|
||||
|
||||
virtual const char *formatName() const;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime,
|
||||
Time &endTime);
|
||||
|
||||
virtual const Header *header() const;
|
||||
virtual Time startTime() const;
|
||||
virtual Time endTime() const;
|
||||
|
||||
virtual bool canTrim() const;
|
||||
virtual bool canMerge() const;
|
||||
|
||||
virtual bool trim(const Time &start,
|
||||
const Time &end) const;
|
||||
|
||||
virtual size_t dataSize(bool withHeader) const;
|
||||
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start = Time(),
|
||||
const Time &end = Time(),
|
||||
int maxSize = -1);
|
||||
|
||||
virtual bool put(std::streambuf &buf, bool withHeader) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the packet type
|
||||
* @return The packet type
|
||||
*/
|
||||
PacketType packetType() const { return MSEEDPacket; }
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal data vector from the given buffer
|
||||
* @param The buffer to read the data from
|
||||
* @param The buffer size
|
||||
*/
|
||||
virtual void setData(const void *data, size_t size);
|
||||
|
||||
void unpackHeader() { unpackHeader(_data.data(), _data.size()); }
|
||||
|
||||
|
||||
protected:
|
||||
Header _header;
|
||||
|
||||
Time _startTime;
|
||||
Time _endTime;
|
||||
|
||||
int _dataType;
|
||||
|
||||
|
||||
private:
|
||||
void unpackHeader(char *data, size_t size);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
125
libs/gempa/caps/packet.cpp
Normal file
125
libs/gempa/caps/packet.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/***************************************************************************
|
||||
* 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/packet.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
bool PacketDataHeader::setUOM(const char *type) {
|
||||
int i;
|
||||
|
||||
if ( type != NULL ) {
|
||||
for ( i = 0; i < 4; ++i ) {
|
||||
if ( type[i] == '\0' ) break;
|
||||
unitOfMeasurement.str[i] = type[i];
|
||||
}
|
||||
|
||||
// Input type must not have more than 4 characters
|
||||
if ( i == 3 && type[i] != '\0' && type[i+1] != '\0' ) {
|
||||
memset(unitOfMeasurement.str, '\0', 4);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
// Pad with null bytes
|
||||
for ( ; i < 4; ++i )
|
||||
unitOfMeasurement.str[i] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string PacketDataHeader::uom(char fill) const {
|
||||
std::string s;
|
||||
for ( int i = 0; i < 4; ++i ) {
|
||||
if ( unitOfMeasurement.str[i] == '\0' ) break;
|
||||
s += unitOfMeasurement.str[i];
|
||||
}
|
||||
|
||||
if ( s.size() < 4 && fill != '\0' ) {
|
||||
for ( int i = s.size(); i < 4; ++i )
|
||||
s += fill;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
bool PacketDataHeader::operator!=(const PacketDataHeader &other) const {
|
||||
return version != other.version ||
|
||||
packetType != other.packetType ||
|
||||
unitOfMeasurement.ID != other.unitOfMeasurement.ID;
|
||||
}
|
||||
|
||||
bool PacketDataHeaderV2::operator!=(const PacketDataHeaderV2 &other) const {
|
||||
return PacketDataHeader::operator!=(other) ||
|
||||
samplingFrequencyNumerator != other.samplingFrequencyNumerator ||
|
||||
samplingFrequencyDenominator != other.samplingFrequencyDenominator ||
|
||||
quality.ID != other.quality.ID;
|
||||
}
|
||||
|
||||
bool DataRecord::Header::put(std::streambuf &buf) const {
|
||||
Endianess::Writer put(buf);
|
||||
char dt = (char)dataType;
|
||||
put(dt);
|
||||
|
||||
put(samplingTime.year);
|
||||
put(samplingTime.yday);
|
||||
put(samplingTime.hour);
|
||||
put(samplingTime.minute);
|
||||
put(samplingTime.second);
|
||||
put(samplingTime.usec);
|
||||
put(samplingFrequencyNumerator);
|
||||
put(samplingFrequencyDenominator);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
|
||||
void DataRecord::Header::setSamplingTime(const Time &ts) {
|
||||
int year, yday, hour, min, sec, usec;
|
||||
ts.get2(&year, &yday, &hour, &min, &sec, &usec);
|
||||
samplingTime.year = year;
|
||||
samplingTime.yday = yday;
|
||||
samplingTime.hour = hour;
|
||||
samplingTime.minute = min;
|
||||
samplingTime.second = sec;
|
||||
samplingTime.usec = usec;
|
||||
}
|
||||
|
||||
|
||||
bool DataRecord::Header::compatible(const Header &other) const {
|
||||
return dataType == other.dataType &&
|
||||
samplingFrequencyNumerator == other.samplingFrequencyNumerator &&
|
||||
samplingFrequencyDenominator == other.samplingFrequencyDenominator;
|
||||
}
|
||||
|
||||
|
||||
bool DataRecord::Header::operator!=(const Header &other) const {
|
||||
return dataType != other.dataType ||
|
||||
samplingFrequencyNumerator != other.samplingFrequencyNumerator ||
|
||||
samplingFrequencyDenominator != other.samplingFrequencyDenominator;
|
||||
}
|
||||
|
||||
DataRecord::~DataRecord() {}
|
||||
|
||||
}
|
||||
}
|
420
libs/gempa/caps/packet.h
Normal file
420
libs/gempa/caps/packet.h
Normal file
@ -0,0 +1,420 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_PACKET_H
|
||||
#define GEMPA_CAPS_PACKET_H
|
||||
|
||||
#include <gempa/caps/api.h>
|
||||
|
||||
#include <gempa/caps/endianess.h>
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
|
||||
enum PacketType {
|
||||
UnknownPacket = 0,
|
||||
RawDataPacket,
|
||||
MSEEDPacket,
|
||||
ANYPacket,
|
||||
RTCM2Packet,
|
||||
MetaDataPacket,
|
||||
FixedRawDataPacket,
|
||||
PacketTypeCount
|
||||
};
|
||||
|
||||
|
||||
enum DataType {
|
||||
DT_Unknown = 0,
|
||||
DT_DOUBLE = 1,
|
||||
DT_FLOAT = 2,
|
||||
DT_INT64 = 100,
|
||||
DT_INT32 = 101,
|
||||
DT_INT16 = 102,
|
||||
DT_INT8 = 103
|
||||
};
|
||||
|
||||
|
||||
union UOM {
|
||||
char str[4];
|
||||
int32_t ID;
|
||||
};
|
||||
|
||||
|
||||
union Quality {
|
||||
char str[4];
|
||||
int32_t ID;
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API TimeStamp {
|
||||
int16_t year; /* year, eg. 2003 */
|
||||
uint16_t yday; /* day of year (1-366) */
|
||||
uint8_t hour; /* hour (0-23) */
|
||||
uint8_t minute; /* minute (0-59) */
|
||||
uint8_t second; /* second (0-59), 60 if leap second */
|
||||
uint8_t unused; /* unused byte */
|
||||
int32_t usec; /* microsecond (0-999999) */
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API PacketDataHeader {
|
||||
PacketDataHeader()
|
||||
: version(1), packetType(UnknownPacket) {
|
||||
unitOfMeasurement.ID = 0;
|
||||
}
|
||||
|
||||
PacketDataHeader(uint16_t version)
|
||||
: version(version)
|
||||
, packetType(UnknownPacket) {
|
||||
unitOfMeasurement.ID = 0;
|
||||
}
|
||||
|
||||
uint16_t version;
|
||||
PacketType packetType;
|
||||
UOM unitOfMeasurement;
|
||||
|
||||
bool setUOM(const char *type);
|
||||
std::string uom(char fill = '\0') const;
|
||||
|
||||
bool operator!=(const PacketDataHeader &other) const;
|
||||
|
||||
int dataSize() const {
|
||||
return sizeof(version) + sizeof((char)packetType) +
|
||||
sizeof(unitOfMeasurement.ID);
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const {
|
||||
Endianess::Writer put(buf);
|
||||
|
||||
char type = char(packetType);
|
||||
|
||||
put(version);
|
||||
put(type);
|
||||
put(unitOfMeasurement.ID);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
char type;
|
||||
|
||||
get(version);
|
||||
get(type); packetType = PacketType(type);
|
||||
get(unitOfMeasurement.ID);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API PacketDataHeaderV2 : PacketDataHeader {
|
||||
PacketDataHeaderV2()
|
||||
: PacketDataHeader(2)
|
||||
, samplingFrequencyNumerator(0)
|
||||
, samplingFrequencyDenominator(0) {}
|
||||
|
||||
uint16_t samplingFrequencyNumerator;
|
||||
uint16_t samplingFrequencyDenominator;
|
||||
Quality quality;
|
||||
|
||||
bool operator!=(const PacketDataHeaderV2 &other) const;
|
||||
|
||||
int dataSize() const {
|
||||
return PacketDataHeader::dataSize() +
|
||||
sizeof(samplingFrequencyNumerator) +
|
||||
sizeof(samplingFrequencyDenominator) +
|
||||
sizeof(quality);
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const {
|
||||
Endianess::Writer put(buf);
|
||||
|
||||
PacketDataHeader::put(buf);
|
||||
|
||||
put(samplingFrequencyNumerator);
|
||||
put(samplingFrequencyDenominator);
|
||||
put(quality);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
PacketDataHeader::get(buf);
|
||||
|
||||
get(samplingFrequencyNumerator);
|
||||
get(samplingFrequencyDenominator);
|
||||
get(quality);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
enum StreamIDComponent {
|
||||
NetworkCode = 0,
|
||||
StationCode = 1,
|
||||
LocationCode = 2,
|
||||
ChannelCode = 3,
|
||||
StreamIDComponentSize
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API PacketHeaderV1 {
|
||||
uint8_t SIDSize[4]; /* number of bytes of stream ID components */
|
||||
uint16_t size; /* number of data bytes */
|
||||
|
||||
bool put(std::streambuf &buf) {
|
||||
Endianess::Writer put(buf);
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
put(SIDSize[i]);
|
||||
|
||||
put(size);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
size_t dataSize() const {
|
||||
return sizeof(uint8_t) * 4 + sizeof(size);
|
||||
}
|
||||
};
|
||||
|
||||
struct SC_GEMPA_CAPS_API PacketHeaderV2 {
|
||||
uint8_t SIDSize[4]; /* number of bytes of stream ID components */
|
||||
uint32_t size; /* number of data bytes */
|
||||
|
||||
bool put(std::streambuf &buf) {
|
||||
Endianess::Writer put(buf);
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
put(SIDSize[i]);
|
||||
|
||||
put(size);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
size_t dataSize() const {
|
||||
return sizeof(uint8_t) * 4 + sizeof(size);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API ResponseHeader {
|
||||
uint16_t id;
|
||||
int32_t size;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(id);
|
||||
get(size);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class SC_GEMPA_CAPS_API DataRecord {
|
||||
public:
|
||||
typedef std::vector<char> Buffer;
|
||||
|
||||
struct Header {
|
||||
DataType dataType{DT_Unknown};
|
||||
TimeStamp samplingTime;
|
||||
uint16_t samplingFrequencyNumerator;
|
||||
uint16_t samplingFrequencyDenominator;
|
||||
Quality quality{};
|
||||
|
||||
Header() = default;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
char dt;
|
||||
get(dt);
|
||||
dataType = (DataType)dt;
|
||||
quality.ID = 0; // Quality is not yet part of the header stream
|
||||
|
||||
get(samplingTime.year);
|
||||
get(samplingTime.yday);
|
||||
get(samplingTime.hour);
|
||||
get(samplingTime.minute);
|
||||
get(samplingTime.second);
|
||||
get(samplingTime.usec);
|
||||
get(samplingFrequencyNumerator);
|
||||
get(samplingFrequencyDenominator);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const;
|
||||
|
||||
int dataSize() const {
|
||||
return sizeof(samplingTime.year) +
|
||||
sizeof(samplingTime.yday) +
|
||||
sizeof(samplingTime.hour) +
|
||||
sizeof(samplingTime.minute) +
|
||||
sizeof(samplingTime.second) +
|
||||
sizeof(samplingTime.usec) +
|
||||
sizeof(samplingFrequencyNumerator) +
|
||||
sizeof(samplingFrequencyDenominator) +
|
||||
sizeof(char);
|
||||
}
|
||||
|
||||
bool compatible(const Header &other) const;
|
||||
bool operator!=(const Header &other) const;
|
||||
|
||||
void setSamplingTime(const Time ×tamp);
|
||||
};
|
||||
|
||||
enum ReadStatusCode {
|
||||
RS_Error = 0,
|
||||
RS_Complete = 1,
|
||||
RS_Partial = 2,
|
||||
RS_BeforeTimeWindow = 3,
|
||||
RS_AfterTimeWindow = 4,
|
||||
RS_Max
|
||||
};
|
||||
|
||||
class ReadStatus {
|
||||
public:
|
||||
ReadStatus() {}
|
||||
ReadStatus(ReadStatusCode code) : _code(code) {}
|
||||
ReadStatus(const ReadStatus &other) : _code(other._code) {}
|
||||
|
||||
public:
|
||||
ReadStatus &operator=(const ReadStatus &other) { _code = other._code; return *this; }
|
||||
operator ReadStatusCode() const { return _code; }
|
||||
|
||||
private:
|
||||
operator bool() { return _code != RS_Error; }
|
||||
|
||||
private:
|
||||
ReadStatusCode _code;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual ~DataRecord();
|
||||
|
||||
virtual const char *formatName() const = 0;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) = 0;
|
||||
|
||||
//! Returns if data trimming is supported
|
||||
virtual bool canTrim() const = 0;
|
||||
|
||||
//! Returns if data merging is possible without modifying
|
||||
//! preceding data
|
||||
virtual bool canMerge() const = 0;
|
||||
|
||||
//! Trims a record to start and end time. Trimming
|
||||
//! should not modify any data but give access to trimmed
|
||||
//! start and end times and the resulting data size. The trimming
|
||||
//! also influences writing the record to a stream.
|
||||
//! Trimming requires the resulting start time being greater
|
||||
//! or equal than the requested start time.
|
||||
//! (Un)Trimming to the original record is semantically
|
||||
//! equal to passing an invalid start and end time.
|
||||
//! It returns true if trimming has been done or false
|
||||
//! if an error occured or trimming is not supported.
|
||||
virtual bool trim(const Time &start, const Time &end) const = 0;
|
||||
|
||||
//! Returns the data size in bytes if the current state would
|
||||
//! be written to a stream. Trimming has also to be taken into
|
||||
//! account while calculating the size.
|
||||
virtual size_t dataSize(bool withHeader = true) const = 0;
|
||||
|
||||
//! Reads the packet from a streambuf and trims the data
|
||||
//! if possible to start and end. If maxBytes is greater
|
||||
//! than 0 then the record should not use more than this
|
||||
//! size of memory (in bytes). If not the complete record
|
||||
//! has been read, RS_Partial must be returned, RS_Complete
|
||||
//! otherwise.
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start = Time(), const Time &end = Time(),
|
||||
int maxBytes = -1) = 0;
|
||||
|
||||
//! writes the packet to a streambuf and trims the data
|
||||
//! if possible to start and end
|
||||
virtual bool put(std::streambuf &buf, bool withHeader = true) const = 0;
|
||||
|
||||
//! Returns the meta information of the data if supported
|
||||
virtual const Header *header() const = 0;
|
||||
|
||||
//! Returns the start time of the record
|
||||
virtual Time startTime() const = 0;
|
||||
|
||||
//! Returns the end time of the record
|
||||
virtual Time endTime() const = 0;
|
||||
|
||||
//! Returns the packet type of the record
|
||||
virtual PacketType packetType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the data vector to be filled by the caller
|
||||
* @return The pointer to the internal buffer
|
||||
*/
|
||||
virtual Buffer *buffer() { return &_data; }
|
||||
|
||||
/**
|
||||
* @brief Returns pointer to raw data. If the record
|
||||
* is compressed it will be uncompressed
|
||||
* @return The pointer to the raw data
|
||||
*/
|
||||
virtual Buffer *data() { return &_data; }
|
||||
|
||||
protected:
|
||||
Buffer _data;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<DataRecord> DataRecordPtr;
|
||||
|
||||
|
||||
struct RawPacket {
|
||||
PacketDataHeader header;
|
||||
std::string SID[4];
|
||||
std::vector<char> data;
|
||||
DataRecord *record;
|
||||
};
|
||||
|
||||
struct MetaPacket {
|
||||
std::string SID[4];
|
||||
PacketDataHeader packetDataHeader;
|
||||
DataRecord *record;
|
||||
DataRecord::Header recordHeader;
|
||||
Time startTime;
|
||||
Time endTime;
|
||||
Time timestamp;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
1120
libs/gempa/caps/plugin.cpp
Normal file
1120
libs/gempa/caps/plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
324
libs/gempa/caps/plugin.h
Normal file
324
libs/gempa/caps/plugin.h
Normal file
@ -0,0 +1,324 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_PLUGIN_H
|
||||
#define GEMPA_CAPS_PLUGIN_H
|
||||
|
||||
/*
|
||||
// Enable/disable journal
|
||||
#define CAPS_FEATURES_JOURNAL 1
|
||||
// Enable/disable backfilling of packets
|
||||
#define CAPS_FEATURES_BACKFILLING 1
|
||||
#define CAPS_FEATURES_SSL 1
|
||||
|
||||
// Supportted CAPS packets
|
||||
#define CAPS_FEATURES_ANY 1
|
||||
#define CAPS_FEATURES_MSEED 1
|
||||
#define CAPS_FEATURES_RAW 1
|
||||
#define CAPS_FEATURES_RTCM2 1
|
||||
*/
|
||||
|
||||
#include "encoderfactory.h"
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
#include <gempa/caps/socket.h>
|
||||
#include <gempa/caps/version.h>
|
||||
|
||||
#include <gempa/caps/pluginpacket.h>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class SC_GEMPA_CAPS_API Plugin {
|
||||
public:
|
||||
enum Status {
|
||||
Success,
|
||||
PacketSize, /* Packet is bigger than the buffer size */
|
||||
PacketLoss, /* The other end didn't acknowledge
|
||||
transmitted data after some time */
|
||||
PacketNotValid, /* Read meta data failed*/
|
||||
PacketNotSupported,
|
||||
MaxFutureEndTimeExceeded
|
||||
};
|
||||
|
||||
struct Stats {
|
||||
Stats() : maxBytesBuffered(0) {}
|
||||
size_t maxBytesBuffered;
|
||||
};
|
||||
|
||||
typedef std::vector<char> Buffer;
|
||||
typedef boost::shared_ptr<Buffer> BufferPtr;
|
||||
|
||||
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
|
||||
typedef std::list<PacketPtr> BackfillingBuffer;
|
||||
|
||||
#endif
|
||||
struct StreamState {
|
||||
Time lastEndTime;
|
||||
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
|
||||
Time lastCommitEndTime;
|
||||
BackfillingBuffer backfillingBuffer;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef std::map<std::string, StreamState> StreamStates;
|
||||
|
||||
typedef boost::function<void (const std::string &, const CAPS::Time &,
|
||||
const CAPS::Time &)> PacketAckFunc;
|
||||
|
||||
|
||||
public:
|
||||
Plugin(const std::string &name, const std::string &options = "",
|
||||
const std::string &description = "");
|
||||
~Plugin();
|
||||
|
||||
void close();
|
||||
void quit();
|
||||
|
||||
void enableLogging();
|
||||
|
||||
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
|
||||
void setBackfillingBufferSize(int secs) { _backfillingBufferSize = secs; }
|
||||
int backfillingBufferSize() const { return _backfillingBufferSize; }
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Returns whether the plugin has been
|
||||
* requested to quit or not.
|
||||
* @return True, if the plugin has been requested to quit.
|
||||
*/
|
||||
bool isExitRequested() {
|
||||
return _isExitRequested;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the stat object
|
||||
* @return The stat object
|
||||
*/
|
||||
const Stats& stats() const {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resets the max bytes buffered counter
|
||||
*/
|
||||
void resetMaxBytesBuffered() {
|
||||
_stats.maxBytesBuffered = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the encoder factory used to created encoders for packet
|
||||
* encoding, e.g. miniSEED.
|
||||
* @param factory The ownership of the factory goes to the plugin and
|
||||
* will be deleted if necessary.
|
||||
*/
|
||||
void setEncoderFactory(EncoderFactory *factory);
|
||||
|
||||
void setHost(const std::string &host) { _host = host; }
|
||||
const std::string &host() const { return _host; }
|
||||
|
||||
void setPort(unsigned short port) { _port = port; }
|
||||
unsigned short port() const { return _port; }
|
||||
|
||||
void setBufferSize(size_t bufferSize) { _bufferSize = bufferSize; }
|
||||
size_t bufferSize() const { return _bufferSize; }
|
||||
|
||||
//! Enables SSL feature
|
||||
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
|
||||
void setSSLEnabled(bool enable) { _useSSL = enable; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sets username and password
|
||||
* @param user The username
|
||||
* @param password The password
|
||||
*/
|
||||
void setCredentials(const std::string &user, const std::string &password) {
|
||||
_user = user;
|
||||
_password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum allowed relative end time for packets. If
|
||||
* the packet end time is greater than the current time plus this
|
||||
* value the packet will be discarded. By default this value is
|
||||
* set to 120 seconds.
|
||||
* @param time The time span
|
||||
*/
|
||||
void setMaxFutureEndTime(const TimeSpan &span) {
|
||||
_maxFutureEndTime = span;
|
||||
}
|
||||
|
||||
void setPacketAckFunc(const PacketAckFunc &func) { _packetAckFunc = func; }
|
||||
|
||||
void setSendTimeout(int timeout) {
|
||||
_sendTimeout = timeout;
|
||||
}
|
||||
|
||||
void setTimeouts(int ack, int lastAck) {
|
||||
_ackTimeout = ack;
|
||||
_lastAckTimeout = lastAck;
|
||||
}
|
||||
|
||||
void setTimeouts(int ack, int lastAck, int send) {
|
||||
_ackTimeout = ack;
|
||||
_lastAckTimeout = lastAck;
|
||||
_sendTimeout = send;
|
||||
}
|
||||
|
||||
#if !defined(CAPS_FEATURES_JOURNAL) || CAPS_FEATURES_JOURNAL
|
||||
bool readJournal();
|
||||
void setJournal(const std::string &filename) { _journalFile = filename; }
|
||||
void setFlushInterval(int interval) { _flushInterval = interval; }
|
||||
|
||||
const StreamStates &streamStates() const { return _states; }
|
||||
bool writeJournal();
|
||||
bool writeJournal(std::ostream &ostream);
|
||||
#endif
|
||||
Status push(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
DataRecordPtr rec, const std::string &uom,
|
||||
int timingQuality = -1);
|
||||
|
||||
Status push(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
const Time &stime,
|
||||
uint16_t numerator, uint16_t denominator,
|
||||
const std::string &uom,
|
||||
void *data, size_t count,
|
||||
DataType dt, int timingQuality = -1);
|
||||
|
||||
/*
|
||||
* Sends given data as any packet. Before sending the data will be
|
||||
* copied into an internal buffer. We introduced this method
|
||||
* to simplify the creation of the python wrappers.
|
||||
*/
|
||||
#if !defined(CAPS_FEATURES_ANY) || CAPS_FEATURES_ANY
|
||||
Status push(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
const Time &stime, uint16_t numerator,
|
||||
uint16_t denominator, const std::string &format,
|
||||
char *data, size_t count);
|
||||
|
||||
Status push(const std::string &net, const std::string &sta,
|
||||
const std::string &loc, const std::string &cha,
|
||||
const Time &stime, uint16_t numerator,
|
||||
uint16_t denominator, const std::string &format,
|
||||
const std::string &data);
|
||||
#endif
|
||||
|
||||
const char *version() {
|
||||
return LIB_CAPS_VERSION_NAME;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::deque<PacketPtr> PacketBuffer;
|
||||
typedef boost::shared_ptr<Encoder> EncoderPtr;
|
||||
|
||||
struct EncoderItem {
|
||||
EncoderItem() : dataType(DT_Unknown) {}
|
||||
EncoderPtr encoder;
|
||||
DataType dataType;
|
||||
};
|
||||
typedef std::map<std::string, EncoderItem> EncoderItems;
|
||||
|
||||
private:
|
||||
bool connect();
|
||||
void disconnect();
|
||||
bool isConnected() const;
|
||||
Encoder* getEncoder(PacketPtr packet);
|
||||
bool readResponse(unsigned int sec = 0);
|
||||
#if !defined(CAPS_FEATURES_JOURNAL) || CAPS_FEATURES_JOURNAL
|
||||
bool readJournal(std::istream &istream);
|
||||
void updateJournal();
|
||||
#endif
|
||||
void sendBye();
|
||||
bool commitPacket(PacketPtr packet);
|
||||
bool encodePacket(PacketPtr packet);
|
||||
bool sendPacket(Packet *packet);
|
||||
void serializePacket(Packet *packet);
|
||||
void tryFlushBackfillingBuffer(StreamState &state);
|
||||
void trimBackfillingBuffer(StreamState &state);
|
||||
bool flush();
|
||||
void flushEncoders();
|
||||
bool send(char *data, int len, int timeout = 60);
|
||||
void wait();
|
||||
|
||||
private:
|
||||
SocketPtr _socket;
|
||||
std::string _name;
|
||||
std::string _options;
|
||||
std::string _description;
|
||||
size_t _bufferSize;
|
||||
StreamStates _states;
|
||||
PacketBuffer _packetBuffer;
|
||||
bool _packetBufferDirty;
|
||||
size_t _bytesBuffered;
|
||||
std::string _host;
|
||||
unsigned short _port;
|
||||
char _responseBuf[512];
|
||||
int _responseBufIdx;
|
||||
fd_set _readFDs;
|
||||
fd_set _writeFDs;
|
||||
int _sendTimeout;
|
||||
#if !defined(CAPS_FEATURES_JOURNAL) || CAPS_FEATURES_JOURNAL
|
||||
std::string _journalFile;
|
||||
bool _journalDirty;
|
||||
Time _lastWrite;
|
||||
int _flushInterval;
|
||||
#endif
|
||||
bool _isExitRequested;
|
||||
bool _closed;
|
||||
bool _wasConnected;
|
||||
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
|
||||
int _backfillingBufferSize;
|
||||
#endif
|
||||
int _ackTimeout;
|
||||
int _lastAckTimeout;
|
||||
|
||||
EncoderFactory *_encoderFactory;
|
||||
EncoderItems _encoderItems;
|
||||
|
||||
PacketAckFunc _packetAckFunc;
|
||||
|
||||
std::string _user;
|
||||
std::string _password;
|
||||
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
|
||||
bool _useSSL;
|
||||
#endif
|
||||
Stats _stats;
|
||||
TimeSpan _maxFutureEndTime;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<Plugin> PluginPtr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
383
libs/gempa/caps/pluginapplication.cpp
Normal file
383
libs/gempa/caps/pluginapplication.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2015 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_COMPONENT PluginApplication
|
||||
|
||||
#include <gempa/caps/pluginapplication.h>
|
||||
#include <gempa/caps/encoderfactory.h>
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <seiscomp3/core/system.h>
|
||||
#include <seiscomp3/logging/log.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef SC_GEMPA_SEATTLE
|
||||
#include <seiscomp3/system/environment.h>
|
||||
#else
|
||||
#include <seiscomp3/config/environment.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace sc = Seiscomp::Core;
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef SEISCOMP_LOG_VA
|
||||
#define LOG_CAPS_CHANNEL(out, fmt) \
|
||||
va_list ap;\
|
||||
va_start(ap, fmt);\
|
||||
out(fmt, ap);\
|
||||
va_end(ap)
|
||||
#else
|
||||
#define LOG_CAPS_CHANNEL(out, fmt) \
|
||||
va_list ap;\
|
||||
va_start(ap, fmt);\
|
||||
fprintf(stderr, #out" "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");\
|
||||
va_end(ap)
|
||||
#endif
|
||||
|
||||
|
||||
void LogError(const char *fmt, ...) {
|
||||
LOG_CAPS_CHANNEL(SEISCOMP_VERROR, fmt);
|
||||
}
|
||||
|
||||
void LogWarning(const char *fmt, ...) {
|
||||
LOG_CAPS_CHANNEL(SEISCOMP_VWARNING, fmt);
|
||||
}
|
||||
|
||||
void LogNotice(const char *fmt, ...) {
|
||||
LOG_CAPS_CHANNEL(SEISCOMP_VNOTICE, fmt);
|
||||
}
|
||||
|
||||
void LogInfo(const char *fmt, ...) {
|
||||
LOG_CAPS_CHANNEL(SEISCOMP_VINFO, fmt);
|
||||
}
|
||||
|
||||
void LogDebug(const char *fmt, ...) {
|
||||
LOG_CAPS_CHANNEL(SEISCOMP_VDEBUG, fmt);
|
||||
}
|
||||
|
||||
const size_t MIN_BUFFER_SIZE = 1024*16;
|
||||
const uint16_t DEFAULT_PORT = 18003;
|
||||
|
||||
}
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
PluginApplication::PluginApplication(int argc, char **argv, const string &desc)
|
||||
: Seiscomp::Client::StreamApplication(argc, argv)
|
||||
, _plugin(Plugin(desc)) {
|
||||
_bufferSize = 1 << 20;
|
||||
_backfillingBufferSize = 180;
|
||||
_flushInterval = 10;
|
||||
_ackTimeout = 60;
|
||||
_lastAckTimeout = 5;
|
||||
_sendTimeout = 60;
|
||||
_logStatus = false;
|
||||
_statusFlushInterval = 10;
|
||||
|
||||
_host = "localhost";
|
||||
_port = DEFAULT_PORT;
|
||||
|
||||
_strAddr = "localhost:" + sc::toString(DEFAULT_PORT);
|
||||
|
||||
SC_FS_DECLARE_PATH(path, "@ROOTDIR@/var/run/" + SCCoreApp->name() + "/journal");
|
||||
_journalFile = path.string();
|
||||
|
||||
_mseedEnabled = false;
|
||||
_mseedEncoding = Steim2;
|
||||
_mseedRecordLength = 9;
|
||||
_strMseedEncoding = "Steim2";
|
||||
_maxFutureEndTime = 120;
|
||||
|
||||
// By default we disable the acquisition autostart because not all plugins
|
||||
// require this feature. It must be enabled explicitly if required.
|
||||
setAutoAcquisitionStart(false);
|
||||
}
|
||||
|
||||
void PluginApplication::createCommandLineDescription() {
|
||||
Seiscomp::Client::StreamApplication::createCommandLineDescription();
|
||||
commandline().addGroup("Output");
|
||||
commandline().addOption("Output", "addr,a", "Data output address, format is [HOST:PORT]", &_strAddr);
|
||||
commandline().addOption("Output", "buffer-size,b", "Size (bytes) of the packet buffer", &_bufferSize);
|
||||
commandline().addOption("Output", "backfilling",
|
||||
"Enable backfilling for out-of-order records. The backfilling buffer size is "
|
||||
"in seconds", &_backfillingBufferSize);
|
||||
commandline().addOption("Output", "mseed", "Enable on-the-fly MiniSeed "
|
||||
"encoding. If the encoder does not support the input"
|
||||
"type of a packet it will be forwarded. Re encoding of"
|
||||
"MiniSEED packets is not supported.");
|
||||
commandline().addOption("Output", "encoding", "MiniSEED encoding to use. (Uncompressed, Steim1 or Steim2)",
|
||||
&_strMseedEncoding);
|
||||
commandline().addOption("Output", "rec-len", "MiniSEED record length expressed as a power of 2."
|
||||
"A 512 byte record would be 9.",
|
||||
&_mseedRecordLength);
|
||||
commandline().addOption("Output", "max-future-endtime", "Maximum allowed relative end time for packets. If "
|
||||
"the packet end time is greater than the current time plus this "
|
||||
"value the packet will be discarded. By default this value is set to 120 seconds.",
|
||||
&_maxFutureEndTime);
|
||||
commandline().addGroup("Journal");
|
||||
commandline().addOption("Journal", "journal,j",
|
||||
"File to store stream states. Use an empty string to disable this feature.", &_journalFile);
|
||||
commandline().addOption("Journal", "flush",
|
||||
"Flush stream states every n seconds to disk", &_flushInterval);
|
||||
commandline().addOption("Journal", "wait-for-ack",
|
||||
"Wait when a sync has been forced, up to n seconds", &_ackTimeout);
|
||||
commandline().addOption("Journal", "wait-for-last-ack,w",
|
||||
"Wait on shutdown to receive acknownledgement messages, up to n seconds", &_lastAckTimeout);
|
||||
|
||||
commandline().addGroup("Status");
|
||||
commandline().addOption("Status", "status-log", "Log information status "
|
||||
"information e.g. max bytes buffered");
|
||||
commandline().addOption("Status", "status-flush", "Flush status every n "
|
||||
"seconds to disk",
|
||||
&_statusFlushInterval);
|
||||
}
|
||||
|
||||
void PluginApplication::done() {
|
||||
LogInfo("Statistics of transmitted data:\n"
|
||||
" records : %d\n"
|
||||
" samples : %d\n"
|
||||
" gaps : %d\n"
|
||||
" start time: %s\n"
|
||||
" end time : %s\n"
|
||||
" files : %d",
|
||||
_stats.records, _stats.samples, _stats.gaps,
|
||||
_stats.startTime.valid()?_stats.startTime.iso().c_str():"",
|
||||
_stats.endTime.valid()?_stats.endTime.iso().c_str():"",
|
||||
_stats.files);
|
||||
|
||||
Seiscomp::Client::StreamApplication::done();
|
||||
}
|
||||
|
||||
void PluginApplication::exit(int returnCode) {
|
||||
Seiscomp::Client::StreamApplication::exit(returnCode);
|
||||
|
||||
_plugin.quit();
|
||||
}
|
||||
|
||||
void PluginApplication::handleTimeout() {
|
||||
sc::Time time = sc::Time::GMT().toLocalTime();
|
||||
|
||||
Plugin::Stats stats = _plugin.stats();
|
||||
_statusFile.stream() << time.toString("%Y/%m/%d %T") << " " << stats.maxBytesBuffered << endl;
|
||||
|
||||
|
||||
_plugin.resetMaxBytesBuffered();
|
||||
}
|
||||
|
||||
bool PluginApplication::init() {
|
||||
if ( !Seiscomp::Client::StreamApplication::init() ) return false;
|
||||
|
||||
// Setup log handlers
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_ERROR, LogError);
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_WARNING, LogWarning);
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_NOTICE, LogNotice);
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_INFO, LogInfo);
|
||||
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_DEBUG, LogDebug);
|
||||
|
||||
_plugin.setHost(_host);
|
||||
_plugin.setPort(_port);
|
||||
_plugin.setBufferSize(_bufferSize);
|
||||
_plugin.setFlushInterval(_flushInterval);
|
||||
_plugin.setTimeouts(_ackTimeout, _lastAckTimeout, _sendTimeout);
|
||||
_plugin.setMaxFutureEndTime(_maxFutureEndTime);
|
||||
|
||||
if ( _mseedEnabled ) {
|
||||
MSEEDEncoderFactory *factory = nullptr;
|
||||
if ( _mseedEncoding == Uncompressed ) {
|
||||
SEISCOMP_INFO("Output stream encoding set to MiniSEED/Uncompressed");
|
||||
factory = new IdentityEncoderFactory();
|
||||
_plugin.setEncoderFactory(factory);
|
||||
}
|
||||
else if ( _mseedEncoding == Steim1 ) {
|
||||
SEISCOMP_INFO("Output stream encoding set to MiniSEED/Steim1");
|
||||
factory = new Steim1EncoderFactory();
|
||||
_plugin.setEncoderFactory(factory);
|
||||
}
|
||||
if ( _mseedEncoding == Steim2 ) {
|
||||
SEISCOMP_INFO("Output stream encoding set to MiniSEED/Steim2");
|
||||
factory = new Steim2EncoderFactory();
|
||||
_plugin.setEncoderFactory(factory);
|
||||
}
|
||||
else {
|
||||
SEISCOMP_ERROR("Unsupported MiniSEED encoding");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !factory->setRecordLength(_mseedRecordLength) ) {
|
||||
SEISCOMP_ERROR("%s", factory->errorString().c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SEISCOMP_INFO("MiniSEED encoding is disabled.");
|
||||
}
|
||||
|
||||
if ( _backfillingBufferSize > 0 ) {
|
||||
_plugin.setBackfillingBufferSize(_backfillingBufferSize);
|
||||
LogInfo("Backfilling buffer size set to %d", _backfillingBufferSize);
|
||||
}
|
||||
|
||||
if ( !_journalFile.empty() ) {
|
||||
_journalFile = Seiscomp::Environment::Instance()->absolutePath(_journalFile);
|
||||
// Recover states
|
||||
LogInfo("Reading journal from %s", _journalFile.c_str());
|
||||
_plugin.setJournal(_journalFile);
|
||||
_plugin.readJournal();
|
||||
LogInfo("Recovered %d streams", (int)_plugin.streamStates().size());
|
||||
}
|
||||
|
||||
if ( _logStatus ) {
|
||||
string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
|
||||
if ( !_statusFile.open(filename.c_str()) ) {
|
||||
LogError("Could not open status file %s.", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
enableTimer(_statusFlushInterval);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::initConfiguration() {
|
||||
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) return false;
|
||||
|
||||
try { _host = configGetString("output.host"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _port = configGetInt("output.port"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _sendTimeout = configGetInt("output.timeout"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try {
|
||||
string addr = configGetString("output.addr");
|
||||
if ( !splitAddress(_host, _port, addr, DEFAULT_PORT) ) {
|
||||
SEISCOMP_ERROR("%s: Invalid CAPS address, format is [HOST:PORT]",
|
||||
addr.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
try {
|
||||
_mseedEnabled = configGetBool("output.mseed.enable");
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
try {
|
||||
int length = configGetInt("output.mseed.recordLength");
|
||||
if ( length < 0 ) {
|
||||
SEISCOMP_ERROR("'output.mseed.recordLength' must be a positive integer");
|
||||
return false;
|
||||
}
|
||||
|
||||
_mseedRecordLength = uint(length);
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
try {
|
||||
string str = configGetString("output.mseed.encoding");
|
||||
if ( !fromString(_mseedEncoding, str)) return false;
|
||||
}
|
||||
catch ( ... ) {}
|
||||
|
||||
try { _bufferSize = configGetInt("output.bufferSize"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _backfillingBufferSize = configGetInt("output.backfillingBufferSize"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _journalFile = configGetString("journal.file"); }
|
||||
catch ( ... ) {}
|
||||
|
||||
try { _flushInterval = configGetInt("journal.flush"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _ackTimeout = configGetInt("journal.waitForAck"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _lastAckTimeout = configGetInt("journal.waitForLastAck"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _logStatus = configGetBool("statusLog.enable"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
try { _statusFlushInterval = configGetInt("statusLog.flush"); }
|
||||
catch ( ... ) {}
|
||||
|
||||
try { _maxFutureEndTime= configGetInt("output.maxFutureEndTime"); }
|
||||
catch ( ... ) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::validateParameters() {
|
||||
if ( !Seiscomp::Client::StreamApplication::validateParameters() ) return false;
|
||||
|
||||
if ( commandline().hasOption("mseed") ) {
|
||||
_mseedEnabled = true;
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("status-log") ) {
|
||||
_logStatus = true;
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("encoding") ) {
|
||||
if ( !fromString(_mseedEncoding, _strMseedEncoding)) return false;
|
||||
}
|
||||
|
||||
if ( _bufferSize < MIN_BUFFER_SIZE ) {
|
||||
SEISCOMP_ERROR("The plugin buffer size must be at least %ld bytes.",
|
||||
MIN_BUFFER_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( commandline().hasOption("addr") ) {
|
||||
if ( !splitAddress(_host, _port, _strAddr, DEFAULT_PORT) ) {
|
||||
SEISCOMP_ERROR("%s: Invalid CAPS address, format is [HOST:PORT]",
|
||||
_strAddr.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PluginApplication::fromString(MseedEncoding &enc, string str) {
|
||||
boost::to_lower(str);
|
||||
if( str == "uncompressed" )
|
||||
enc = Uncompressed;
|
||||
else if ( str == "steim1" )
|
||||
enc = Steim1;
|
||||
else if ( str == "steim2" )
|
||||
enc = Steim2;
|
||||
else {
|
||||
SEISCOMP_ERROR("Unsupported encoding %s", str.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
114
libs/gempa/caps/pluginapplication.h
Normal file
114
libs/gempa/caps/pluginapplication.h
Normal file
@ -0,0 +1,114 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2015 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_PLUGINAPPLICATION_H
|
||||
#define GEMPA_CAPS_PLUGINAPPLICATION_H
|
||||
|
||||
#include <gempa/caps/api.h>
|
||||
#include <gempa/caps/plugin.h>
|
||||
|
||||
#include <seiscomp3/client/streamapplication.h>
|
||||
#include <seiscomp3/logging/filerotator.h>
|
||||
#include <seiscomp3/utils/timer.h>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class SC_GEMPA_CAPS_API PluginApplication : public Seiscomp::Client::StreamApplication {
|
||||
public:
|
||||
PluginApplication(int argc, char **argv,
|
||||
const std::string &desc = "");
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Adds common plugin commandline options.
|
||||
*/
|
||||
virtual void createCommandLineDescription();
|
||||
|
||||
virtual void done();
|
||||
|
||||
virtual void exit(int returnCode);
|
||||
|
||||
virtual void handleRecord(Seiscomp::Record *record) {}
|
||||
virtual void handleTimeout();
|
||||
|
||||
/**
|
||||
* @brief Initialization method.
|
||||
*/
|
||||
virtual bool init();
|
||||
|
||||
/**
|
||||
* @brief Reads common plugin configuration options.
|
||||
*/
|
||||
virtual bool initConfiguration();
|
||||
|
||||
/**
|
||||
* @brief Validates command line parameters.
|
||||
*/
|
||||
virtual bool validateParameters();
|
||||
|
||||
protected:
|
||||
struct Statistics {
|
||||
uint32_t records;
|
||||
uint32_t samples;
|
||||
Time startTime;
|
||||
Time endTime;
|
||||
uint32_t gaps;
|
||||
uint32_t files;
|
||||
|
||||
Statistics() : records(0), samples(0), gaps(0), files(0) {}
|
||||
};
|
||||
|
||||
enum MseedEncoding {Uncompressed, Steim1, Steim2};
|
||||
|
||||
bool fromString(MseedEncoding &enc, std::string str);
|
||||
|
||||
protected:
|
||||
class FileRotator : public Seiscomp::Logging::FileRotatorOutput {
|
||||
public:
|
||||
std::ofstream& stream() { return _stream; }
|
||||
};
|
||||
|
||||
protected:
|
||||
Plugin _plugin;
|
||||
std::string _host;
|
||||
ushort _port;
|
||||
std::string _strAddr;
|
||||
size_t _bufferSize;
|
||||
size_t _backfillingBufferSize;
|
||||
bool _mseed;
|
||||
std::string _journalFile;
|
||||
int _flushInterval;
|
||||
int _ackTimeout;
|
||||
int _lastAckTimeout;
|
||||
int _sendTimeout;
|
||||
int _maxFutureEndTime;
|
||||
Statistics _stats;
|
||||
bool _mseedEnabled;
|
||||
MseedEncoding _mseedEncoding;
|
||||
uint _mseedRecordLength;
|
||||
std::string _strMseedEncoding;
|
||||
std::string _strPacketType;
|
||||
Seiscomp::Util::Timer _timer;
|
||||
FileRotator _statusFile;
|
||||
bool _logStatus;
|
||||
uint _statusFlushInterval;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
69
libs/gempa/caps/pluginpacket.h
Normal file
69
libs/gempa/caps/pluginpacket.h
Normal file
@ -0,0 +1,69 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2016 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_PLUGINPACKET_H
|
||||
#define GEMPA_CAPS_PLUGINPACKET_H
|
||||
|
||||
#include "mseed/encoder.h"
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
struct Packet {
|
||||
Packet() : timingQuality(-1) {}
|
||||
Packet(DataRecordPtr rec,
|
||||
const std::string &networkCode, const std::string &stationCode,
|
||||
const std::string &locationCode, const std::string &channelCode)
|
||||
: record(rec), networkCode(networkCode), stationCode(stationCode),
|
||||
locationCode(locationCode), channelCode(channelCode), timingQuality(-1) {
|
||||
streamID = networkCode + "." + stationCode + "." +
|
||||
locationCode + "." + channelCode;
|
||||
}
|
||||
|
||||
Packet* clone() {
|
||||
Packet *packet = new Packet(record, networkCode, stationCode,
|
||||
locationCode, channelCode);
|
||||
packet->buffer = buffer;
|
||||
packet->dt_us = dt_us;
|
||||
packet->streamID = streamID;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
typedef std::vector<char> Buffer;
|
||||
typedef boost::shared_ptr<Buffer> BufferPtr;
|
||||
|
||||
BufferPtr buffer; // PacketDataHeader, PacketHeader[V1|V2], Data
|
||||
DataRecordPtr record;
|
||||
std::string networkCode;
|
||||
std::string stationCode;
|
||||
std::string locationCode;
|
||||
std::string channelCode;
|
||||
std::string streamID;
|
||||
DataType dataType;
|
||||
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
|
||||
int64_t dt_us; // Length of one sample in microseconds
|
||||
#endif
|
||||
std::string uom;
|
||||
int timingQuality;
|
||||
size_t size() const { return buffer->size(); }
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<Packet> PacketPtr;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
418
libs/gempa/caps/rawpacket.cpp
Normal file
418
libs/gempa/caps/rawpacket.cpp
Normal file
@ -0,0 +1,418 @@
|
||||
/***************************************************************************
|
||||
* 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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
237
libs/gempa/caps/rawpacket.h
Normal file
237
libs/gempa/caps/rawpacket.h
Normal file
@ -0,0 +1,237 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_RAWPACKET_H
|
||||
#define GEMPA_CAPS_RAWPACKET_H
|
||||
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
struct SC_GEMPA_CAPS_API RawResponseHeader {
|
||||
int64_t timeSeconds;
|
||||
int32_t timeMicroSeconds;
|
||||
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(timeSeconds);
|
||||
get(timeMicroSeconds);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
int dataSize() const {
|
||||
return sizeof(timeSeconds) + sizeof(timeMicroSeconds);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class RawDataRecord : public DataRecord {
|
||||
public:
|
||||
RawDataRecord();
|
||||
|
||||
const char *formatName() const;
|
||||
|
||||
/**
|
||||
* @brief Reads metadata from data record header
|
||||
* @param The streambuf object
|
||||
* @param The size of the data record in bytes
|
||||
* @param The data record header object
|
||||
* @param The startTime
|
||||
* @param The endTime
|
||||
*/
|
||||
bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime);
|
||||
|
||||
void setHeader(const Header &header);
|
||||
|
||||
/**
|
||||
* @brief Returns the meta information of the data if supported
|
||||
* @return The data record header
|
||||
*/
|
||||
const Header *header() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the start time of the record
|
||||
* @return The start time
|
||||
*/
|
||||
Time startTime() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the end time of the record
|
||||
* @return The end time
|
||||
*/
|
||||
Time endTime() const;
|
||||
|
||||
/**
|
||||
* @brief canTrim checks if data trimming is possible
|
||||
* without modifying preceding data
|
||||
* @return True, if data trimming is possible
|
||||
*/
|
||||
bool canTrim() const;
|
||||
|
||||
/**
|
||||
* @brief canMerge checks if data merging is possible
|
||||
* without modifying preceding data
|
||||
* @return True, if data merging is possible
|
||||
*/
|
||||
bool canMerge() const;
|
||||
|
||||
/**
|
||||
* @brief Trims a record to start and end time. Trimming
|
||||
* should not modify any data but give access to trimmed
|
||||
* start and end times and the resulting data size. The trimming
|
||||
* also influences writing the record to a stream.
|
||||
* Trimming requires the resulting start time being greater
|
||||
* or equal than the requested start time.
|
||||
* (Un)Trimming to the original record is semantically
|
||||
* equal to passing an invalid start and end time.
|
||||
* @param start The requested start time
|
||||
* @param end The requested end time
|
||||
* @return It returns true if trimming has been done or false
|
||||
* if an error occured or trimming is not supported.
|
||||
*/
|
||||
bool trim(const Time &start, const Time &end) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the data size in bytes if the current state would
|
||||
* be written to a stream. Trimming has also to be taken into
|
||||
* account while calculating the size.
|
||||
* @param withHeader Take header into account
|
||||
* @return Returns the data size in bytes
|
||||
*/
|
||||
size_t dataSize(bool withHeader) const;
|
||||
|
||||
/**
|
||||
* @brief Reads the packet data including header from a streambuf
|
||||
* and trims the data if possible to start and end.
|
||||
* If maxBytes is greater than 0 then the record should
|
||||
* not use more than this size of memory (in bytes).
|
||||
*
|
||||
* @param The streambuf object
|
||||
* @param The buffer size
|
||||
* @param The requested start time
|
||||
* @param The requested end time
|
||||
* @param The Max bytes to use
|
||||
* @return If not the complete record has been read, RS_Partial
|
||||
* must be returned, RS_Complete otherwise.
|
||||
*/
|
||||
ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start = Time(),
|
||||
const Time &end = Time(),
|
||||
int maxBytes = -1);
|
||||
|
||||
/**
|
||||
* @brief Reads the packet data without header from a streambuf
|
||||
* and trims the data if possible to start and end.
|
||||
* If maxBytes is greater than 0 then the record should
|
||||
* not use more than this size of memory (in bytes).
|
||||
*
|
||||
* @param The streambuf object
|
||||
* @param The buffer size
|
||||
* @param The requested start time
|
||||
* @param The requested end time
|
||||
* @param The Max bytes to use
|
||||
* @return If not the complete record has been read, RS_Partial
|
||||
* must be returned, RS_Complete otherwise.
|
||||
*/
|
||||
ReadStatus getData(std::streambuf &buf, int size,
|
||||
const Time &start = Time(),
|
||||
const Time &end = Time(),
|
||||
int maxBytes = -1);
|
||||
|
||||
//! writes the packet to a streambuf and trims the data
|
||||
//! if possible to start and end
|
||||
/**
|
||||
* @brief Writes the packet to a streambuf and trims the data
|
||||
* if possible to start and end
|
||||
* @param The streambuf object
|
||||
* @param Take header into account
|
||||
* @return True, if the data has been written
|
||||
*/
|
||||
bool put(std::streambuf &buf, bool withHeader) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the packet type
|
||||
* @return The packet type
|
||||
*/
|
||||
PacketType packetType() const { return RawDataPacket; }
|
||||
|
||||
/**
|
||||
* @brief Sets the start time of the record
|
||||
* @param The start time
|
||||
*/
|
||||
void setStartTime(const Time &time);
|
||||
|
||||
/**
|
||||
* @brief Sets the sampling frequency of the record
|
||||
* @param numerator The numerator
|
||||
* @param denominator The denomintor
|
||||
*/
|
||||
void setSamplingFrequency(uint16_t numerator, uint16_t denominator);
|
||||
|
||||
/**
|
||||
* @brief Sets the data type of the record
|
||||
* @param The datatype to use
|
||||
*/
|
||||
void setDataType(DataType dt);
|
||||
|
||||
/**
|
||||
* @brief Initializes the internal data vector from the given buffer
|
||||
* @param The buffer to read the data from
|
||||
* @param The buffer size
|
||||
*/
|
||||
void setBuffer(const void *data, size_t size);
|
||||
|
||||
|
||||
protected:
|
||||
Header _header;
|
||||
|
||||
mutable Header _currentHeader;
|
||||
mutable size_t _dataOfs;
|
||||
mutable size_t _dataSize;
|
||||
|
||||
mutable Time _startTime;
|
||||
mutable Time _endTime;
|
||||
|
||||
mutable bool _dirty;
|
||||
};
|
||||
|
||||
|
||||
class FixedRawDataRecord : public RawDataRecord {
|
||||
public:
|
||||
virtual bool canTrim() const { return false; }
|
||||
virtual bool canMerge() const { return false; }
|
||||
virtual bool trim(const Time &start, const Time &end) const { return false; }
|
||||
virtual const char *formatName() const;
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &/*start*/, const Time &/*end*/,
|
||||
int maxBytes) {
|
||||
return RawDataRecord::get(buf, size, Time(), Time(), maxBytes);
|
||||
}
|
||||
PacketType packetType() const { return FixedRawDataPacket; }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
137
libs/gempa/caps/recordbuilder.h
Normal file
137
libs/gempa/caps/recordbuilder.h
Normal file
@ -0,0 +1,137 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_RECORD_SAMPLER_H
|
||||
#define GEMPA_CAPS_RECORD_SAMPLER_H
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
template<typename T> class RecordBuilder {
|
||||
public:
|
||||
struct Record {
|
||||
Record() : samplingInterval(0.0), sampleCount(0), userData(NULL) {}
|
||||
std::string netcode;
|
||||
std::string stacode;
|
||||
std::string loccode;
|
||||
std::string chacode;
|
||||
Time startTime;
|
||||
double samplingInterval;
|
||||
size_t sampleCount;
|
||||
std::vector<T> data;
|
||||
void *userData;
|
||||
};
|
||||
typedef boost::shared_ptr<Record> RecordPtr;
|
||||
typedef boost::function<void (Record *rec, void*)> FlushCallback;
|
||||
|
||||
public:
|
||||
RecordBuilder() : _bufSize(64) {}
|
||||
RecordBuilder(const std::string &netcode, const std::string &stacode,
|
||||
const std::string &loccode, const std::string &chacode,
|
||||
void *userData = NULL, size_t bufSize = 64) {
|
||||
_networkCode = netcode;
|
||||
_stationCode = stacode;
|
||||
_locationCode = loccode;
|
||||
_channelCode = chacode;
|
||||
_userData = userData;
|
||||
_bufSize = bufSize > 0? bufSize:1;
|
||||
_record = NULL;
|
||||
}
|
||||
|
||||
void flush(bool lastSample = false) {
|
||||
if ( _record != 0 ) {
|
||||
if ( lastSample ) push();
|
||||
|
||||
_flushCallback(_record, _userData);
|
||||
_record = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void push(T value, const Time &time) {
|
||||
if ( _time.valid() ) {
|
||||
double interval = time - _time;
|
||||
if ( interval <= 0.0 ) {
|
||||
_time = Time();
|
||||
flush(true);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if ( _record == NULL) {
|
||||
_record = new Record;
|
||||
if ( _record ) {
|
||||
_record->netcode = _networkCode;
|
||||
_record->stacode = _stationCode;
|
||||
_record->loccode = _locationCode;
|
||||
_record->chacode = _channelCode;
|
||||
_record->startTime = _time;
|
||||
_record->samplingInterval = interval;
|
||||
|
||||
_record->data.reserve(_bufSize);
|
||||
_record->data[_record->sampleCount] = _value;
|
||||
++_record->sampleCount;
|
||||
}
|
||||
}
|
||||
else if ( interval != _record->samplingInterval ) {
|
||||
flush(true);
|
||||
}
|
||||
else {
|
||||
push();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_time = time;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
void setFlushCallback(const FlushCallback &cb) { _flushCallback = cb; }
|
||||
void setNetworkCode(const std::string &netcode) { _networkCode = netcode; }
|
||||
void setStationCode(const std::string &stacode) { _stationCode = stacode; }
|
||||
void setLocationCode(const std::string &loccode) { _locationCode = loccode; }
|
||||
void setChannelCode(const std::string &chacode) { _channelCode = chacode; }
|
||||
void setUserData(void *userData) { _userData = userData; }
|
||||
|
||||
private:
|
||||
void push() {
|
||||
if ( _record->sampleCount % _bufSize == 0 )
|
||||
_record->data.reserve(_record->sampleCount + _bufSize);
|
||||
_record->data[_record->sampleCount] = _value;
|
||||
++_record->sampleCount;
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
Time _time;
|
||||
Record *_record;
|
||||
FlushCallback _flushCallback;
|
||||
size_t _bufSize;
|
||||
std::string _networkCode;
|
||||
std::string _stationCode;
|
||||
std::string _locationCode;
|
||||
std::string _channelCode;
|
||||
void *_userData;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
398
libs/gempa/caps/riff.cpp
Normal file
398
libs/gempa/caps/riff.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
/***************************************************************************
|
||||
* 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/riff.h>
|
||||
#include <gempa/caps/endianess.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
namespace RIFF {
|
||||
|
||||
|
||||
bool ChunkHeader::setChunkType(const char *t) {
|
||||
int i;
|
||||
|
||||
if ( t != NULL ) {
|
||||
for ( i = 0; i < 4; ++i ) {
|
||||
if ( t[i] == '\0' ) break;
|
||||
chunkType[i] = t[i];
|
||||
}
|
||||
|
||||
// Input type must not have more than 4 characters
|
||||
if ( i == 3 && t[i] != '\0' && t[i+1] != '\0' ) {
|
||||
memset(chunkType, ' ', 4);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
i = 0;
|
||||
|
||||
// Pad with whitespaces
|
||||
for ( ; i < 4; ++i )
|
||||
chunkType[i] = ' ';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ChunkHeader::isChunkType(const char *t) const {
|
||||
for ( int i = 0; i < 4; ++i ) {
|
||||
if ( t[i] == '\0' && chunkType[i] == ' ' ) break;
|
||||
if ( t[i] != chunkType[i] ) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ChunkHeader::validChunkType() const {
|
||||
for ( int i = 0; i < 4; ++i ) {
|
||||
if ( chunkType[i] == '\0' ) continue;
|
||||
if ( chunkType[i] >= 'a' && chunkType[i] <= 'z' ) continue;
|
||||
if ( chunkType[i] >= 'A' && chunkType[i] <= 'Z' ) continue;
|
||||
if ( chunkType[i] >= '0' && chunkType[i] <= '9' ) continue;
|
||||
if ( chunkType[i] == '-' ) continue;
|
||||
if ( chunkType[i] == '_' ) continue;
|
||||
if ( chunkType[i] == ' ' ) continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool ChunkHeader::get(std::streambuf &input) {
|
||||
Endianess::Reader get(input);
|
||||
get(chunkType, 4);
|
||||
get(chunkSize);
|
||||
return get.good;
|
||||
}
|
||||
|
||||
|
||||
bool ChunkHeader::put(std::streambuf &output) const {
|
||||
Endianess::Writer put(output);
|
||||
put(chunkType, 4);
|
||||
put(chunkSize);
|
||||
return put.good;
|
||||
}
|
||||
|
||||
|
||||
ChunkIterator::ChunkIterator() : _stream(&_own) {}
|
||||
|
||||
|
||||
ChunkIterator::ChunkIterator(const std::string &filename) {
|
||||
begin(filename);
|
||||
}
|
||||
|
||||
|
||||
ChunkIterator::ChunkIterator(std::istream &input) {
|
||||
begin(input);
|
||||
}
|
||||
|
||||
|
||||
void ChunkIterator::begin(const std::string &filename) {
|
||||
_stream = &_own;
|
||||
_index = 0;
|
||||
_own.open(filename.c_str());
|
||||
memset(&_header, 0, sizeof(_header));
|
||||
}
|
||||
|
||||
|
||||
void ChunkIterator::begin(std::istream &input) {
|
||||
_stream = &input;
|
||||
_index = input.tellg();
|
||||
_header.chunkSize = 0;
|
||||
memset(&_header, 0, sizeof(_header));
|
||||
}
|
||||
|
||||
|
||||
bool ChunkIterator::next() {
|
||||
while ( _stream->good() ) {
|
||||
// Jump to next header
|
||||
_stream->seekg(_index + _header.chunkSize);
|
||||
|
||||
if ( !_header.read(*_stream) )
|
||||
break;
|
||||
|
||||
//std::cout << " - "; std::cout.write(_header.chunkType, 4); std::cout << " : " << _header.chunkSize << std::endl;
|
||||
|
||||
_index = _stream->tellg();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const ChunkHeader &ChunkIterator::header() const {
|
||||
return _header;
|
||||
}
|
||||
|
||||
|
||||
size_t ChunkIterator::headerPos() const {
|
||||
return _index - _header.dataSize();
|
||||
}
|
||||
|
||||
|
||||
size_t ChunkIterator::contentPos() const {
|
||||
return _index;
|
||||
}
|
||||
|
||||
|
||||
Chunk::~Chunk() {}
|
||||
|
||||
|
||||
bool HeadChunk::get(std::streambuf &input, int size) {
|
||||
Endianess::Reader r(input);
|
||||
|
||||
r(data.version);
|
||||
|
||||
char dt;
|
||||
r(dt);
|
||||
data.packetType = static_cast<PacketType>(dt);
|
||||
|
||||
r(data.unitOfMeasurement.str, 4);
|
||||
|
||||
return r.good;
|
||||
}
|
||||
|
||||
|
||||
bool HeadChunk::put(std::streambuf &output) const {
|
||||
Endianess::Writer w(output);
|
||||
|
||||
w(data.version);
|
||||
|
||||
char dt = data.packetType;
|
||||
w(dt);
|
||||
|
||||
w(data.unitOfMeasurement.str, 4);
|
||||
|
||||
return w.good;
|
||||
}
|
||||
|
||||
|
||||
int HeadChunk::chunkSize() const {
|
||||
return sizeof(data.version) +
|
||||
sizeof(data.unitOfMeasurement.str) +
|
||||
sizeof(char);
|
||||
}
|
||||
|
||||
|
||||
bool SIDChunk::put(std::streambuf &output) const {
|
||||
char null = '\0';
|
||||
Endianess::Writer w(output);
|
||||
|
||||
if ( !networkCode.empty() )
|
||||
w(networkCode.c_str(), networkCode.size());
|
||||
w(&null, 1);
|
||||
if ( !stationCode.empty() )
|
||||
w(stationCode.c_str(), stationCode.size());
|
||||
w(&null, 1);
|
||||
if ( !locationCode.empty() )
|
||||
w(locationCode.c_str(), locationCode.size());
|
||||
w(&null, 1);
|
||||
if ( !channelCode.empty() )
|
||||
w(channelCode.c_str(), channelCode.size());
|
||||
|
||||
return w.good;
|
||||
}
|
||||
|
||||
|
||||
bool SIDChunk::get(std::streambuf &input, int size) {
|
||||
char tmp;
|
||||
int count = size;
|
||||
Endianess::Reader r(input);
|
||||
|
||||
r(&tmp, 1);
|
||||
networkCode.clear();
|
||||
while ( r.good && count-- ) {
|
||||
if ( tmp == '\0' ) break;
|
||||
networkCode += tmp;
|
||||
r(&tmp, 1);
|
||||
}
|
||||
|
||||
if ( !r.good ) return false;
|
||||
|
||||
r(&tmp, 1);
|
||||
stationCode.clear();
|
||||
while ( r.good && count-- ) {
|
||||
if ( tmp == '\0' ) break;
|
||||
stationCode += tmp;
|
||||
r(&tmp, 1);
|
||||
}
|
||||
|
||||
if ( !r.good ) return false;
|
||||
|
||||
r(&tmp, 1);
|
||||
locationCode.clear();
|
||||
while ( r.good && count-- ) {
|
||||
if ( tmp == '\0' ) break;
|
||||
locationCode += tmp;
|
||||
r(&tmp, 1);
|
||||
}
|
||||
|
||||
if ( !r.good ) return false;
|
||||
|
||||
r(&tmp, 1);
|
||||
channelCode.clear();
|
||||
while ( r.good && count-- ) {
|
||||
if ( tmp == '\0' ) break;
|
||||
channelCode += tmp;
|
||||
r(&tmp, 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int SIDChunk::chunkSize() const {
|
||||
return networkCode.size() + 1 +
|
||||
stationCode.size() + 1 +
|
||||
locationCode.size() + 1 +
|
||||
channelCode.size();
|
||||
}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
CPtrChunk<SIZE_T,BigEndian>::CPtrChunk(const char* d, int len) : data(d), size(len) {}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
int CPtrChunk<SIZE_T,BigEndian>::chunkSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
bool CPtrChunk<SIZE_T,BigEndian>::get(std::streambuf &, int) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, int SIZE_T, bool BigEndian>
|
||||
struct CPtrWriter {
|
||||
static bool Take(std::streambuf &output, const T*, int count);
|
||||
};
|
||||
|
||||
|
||||
// To little endian
|
||||
template <typename T, int SIZE_T>
|
||||
struct CPtrWriter<T,SIZE_T,true> {
|
||||
static bool Take(std::streambuf &output, const T *data, int count) {
|
||||
Endianess::Writer w(output);
|
||||
for ( int i = 0; i < count; ++i ) {
|
||||
T tmp = Endianess::Swapper<T,true,SIZE_T>::Take(data[i]);
|
||||
w((const char*)&tmp, SIZE_T);
|
||||
}
|
||||
return w.good;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename T, int SIZE_T>
|
||||
struct CPtrWriter<T,SIZE_T,false> {
|
||||
static bool Take(std::streambuf &output, const T *data, int count) {
|
||||
return (int)output.sputn((const char*)data, SIZE_T*count) == SIZE_T*count;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
bool CPtrChunk<SIZE_T,BigEndian>::put(std::streambuf &output) const {
|
||||
typedef typename Endianess::TypeMap<SIZE_T>::ValueType T;
|
||||
return CPtrWriter<T,SIZE_T,BigEndian>::Take(output, (const T*)data, size/SIZE_T);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
VectorChunk<SIZE_T,BigEndian>::VectorChunk(std::vector<char> &d)
|
||||
: data(d), startOfs(-1), len(-1) {}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
VectorChunk<SIZE_T,BigEndian>::VectorChunk(std::vector<char> &d, int ofs, int count)
|
||||
: data(d), startOfs(ofs), len(count) {}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
int VectorChunk<SIZE_T,BigEndian>::chunkSize() const {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
bool VectorChunk<SIZE_T,BigEndian>::get(std::streambuf &input, int size) {
|
||||
int count = size/SIZE_T;
|
||||
|
||||
if ( len >= 0 ) {
|
||||
if ( len > count )
|
||||
return false;
|
||||
|
||||
count = len;
|
||||
}
|
||||
|
||||
// Skip first samples (bytes = samples*sizeof(T))
|
||||
if ( startOfs > 0 )
|
||||
input.pubseekoff(startOfs*SIZE_T, std::ios_base::cur, std::ios_base::in);
|
||||
|
||||
Endianess::Reader r(input);
|
||||
|
||||
data.resize(count*SIZE_T);
|
||||
r(&data[0], data.size());
|
||||
|
||||
// Convert array to little endian
|
||||
Endianess::ByteSwapper<BigEndian,SIZE_T>::Take(&data[0], count);
|
||||
|
||||
// Go the end of chunk
|
||||
if ( (int)data.size() < size )
|
||||
input.pubseekoff(size-data.size(), std::ios_base::cur, std::ios_base::in);
|
||||
|
||||
return r.good;
|
||||
}
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
bool VectorChunk<SIZE_T,BigEndian>::put(std::streambuf &output) const {
|
||||
typedef typename Endianess::TypeMap<SIZE_T>::ValueType T;
|
||||
return CPtrWriter<T,SIZE_T,BigEndian>::Take(output, (const T*)data.data(), data.size()/SIZE_T);
|
||||
}
|
||||
|
||||
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<1,false>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<1,true>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<2,false>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<2,true>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<4,false>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<4,true>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<8,false>;
|
||||
template struct SC_GEMPA_CAPS_API CPtrChunk<8,true>;
|
||||
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<1,false>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<1,true>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<2,false>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<2,true>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<4,false>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<4,true>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<8,false>;
|
||||
template struct SC_GEMPA_CAPS_API VectorChunk<8,true>;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
193
libs/gempa/caps/riff.h
Normal file
193
libs/gempa/caps/riff.h
Normal file
@ -0,0 +1,193 @@
|
||||
/***************************************************************************
|
||||
* 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef CAPS_IO_RIFF_H
|
||||
#define CAPS_IO_RIFF_H
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
namespace RIFF {
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API ChunkHeader {
|
||||
union {
|
||||
char chunkType[4];
|
||||
uint32_t chunkID;
|
||||
};
|
||||
|
||||
int chunkSize; /* Chunk size in bytes */
|
||||
|
||||
bool setChunkType(const char *type);
|
||||
bool isChunkType(const char *type) const;
|
||||
bool validChunkType() const;
|
||||
|
||||
int dataSize() const { return 8; }
|
||||
|
||||
bool read(std::istream &input) { return get(*input.rdbuf()); }
|
||||
bool write(std::ostream &output) const { return put(*output.rdbuf()); }
|
||||
|
||||
bool get(std::streambuf &input);
|
||||
bool put(std::streambuf &output) const;
|
||||
};
|
||||
|
||||
const int ChunkHeaderSize = 8;
|
||||
|
||||
|
||||
class SC_GEMPA_CAPS_API ChunkIterator {
|
||||
public:
|
||||
ChunkIterator();
|
||||
ChunkIterator(const std::string &filename);
|
||||
ChunkIterator(std::istream &input);
|
||||
|
||||
//! Starts iterating over a file or stream
|
||||
void begin(const std::string &filename);
|
||||
void begin(std::istream &input);
|
||||
|
||||
//! Jumps to the next chunk. Returns false,
|
||||
//! if no chunk is available
|
||||
bool next();
|
||||
|
||||
//! Returns the current chunk header
|
||||
const ChunkHeader &header() const;
|
||||
|
||||
//! Returns the file position pointing
|
||||
//! to the current chunk header
|
||||
size_t headerPos() const;
|
||||
|
||||
//! Returns the file position pointing
|
||||
//! to the current chunk content
|
||||
size_t contentPos() const;
|
||||
|
||||
//! Returns the current input stream
|
||||
std::istream &istream() const { return *_stream; }
|
||||
|
||||
|
||||
private:
|
||||
ChunkHeader _header;
|
||||
std::istream *_stream;
|
||||
size_t _index;
|
||||
std::ifstream _own;
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API Chunk {
|
||||
virtual ~Chunk();
|
||||
|
||||
bool read(std::istream &input, int size) { return get(*input.rdbuf(), size); }
|
||||
bool write(std::ostream &output) const { return put(*output.rdbuf()); }
|
||||
|
||||
virtual bool get(std::streambuf &input, int size) = 0;
|
||||
virtual bool put(std::streambuf &output) const = 0;
|
||||
|
||||
virtual int chunkSize() const = 0;
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API HeadChunk : Chunk {
|
||||
PacketDataHeader data;
|
||||
|
||||
int chunkSize() const;
|
||||
|
||||
bool get(std::streambuf &input, int size);
|
||||
bool put(std::streambuf &output) const;
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API SID {
|
||||
std::string networkCode;
|
||||
std::string stationCode;
|
||||
std::string locationCode;
|
||||
std::string channelCode;
|
||||
|
||||
bool operator==(const SID &other ) const {
|
||||
return networkCode == other.networkCode &&
|
||||
stationCode == other.stationCode &&
|
||||
locationCode == other.locationCode &&
|
||||
channelCode == other.channelCode;
|
||||
}
|
||||
|
||||
bool operator!=(const SID &other ) const {
|
||||
return networkCode != other.networkCode ||
|
||||
stationCode != other.stationCode ||
|
||||
locationCode != other.locationCode ||
|
||||
channelCode != other.channelCode;
|
||||
}
|
||||
|
||||
std::string toString() const {
|
||||
return networkCode + "." + stationCode + "." +
|
||||
locationCode + "." + channelCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SC_GEMPA_CAPS_API SIDChunk : Chunk, SID {
|
||||
int chunkSize() const;
|
||||
|
||||
bool get(std::streambuf &input, int size);
|
||||
bool put(std::streambuf &output) const;
|
||||
};
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
struct CPtrChunk : Chunk {
|
||||
const char *data;
|
||||
int size;
|
||||
|
||||
CPtrChunk(const char* d, int len);
|
||||
virtual ~CPtrChunk() {}
|
||||
|
||||
int chunkSize() const;
|
||||
|
||||
bool get(std::streambuf &input, int size);
|
||||
bool put(std::streambuf &output) const;
|
||||
};
|
||||
|
||||
|
||||
template <int SIZE_T, bool BigEndian>
|
||||
struct VectorChunk : Chunk {
|
||||
std::vector<char> &data;
|
||||
|
||||
VectorChunk(std::vector<char> &d);
|
||||
|
||||
// sampleOfs and sampleCount are not byte offsets but elements of
|
||||
// type T
|
||||
VectorChunk(std::vector<char> &d, int sampleOfs, int sampleCount);
|
||||
virtual ~VectorChunk() {}
|
||||
|
||||
int chunkSize() const;
|
||||
|
||||
bool get(std::streambuf &input, int size);
|
||||
bool put(std::streambuf &output) const;
|
||||
|
||||
int startOfs;
|
||||
int len;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
174
libs/gempa/caps/rtcm2packet.cpp
Normal file
174
libs/gempa/caps/rtcm2packet.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 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/rtcm2packet.h>
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/riff.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <streambuf>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
bool RTCM2DataRecord::RTCM2Header::put(std::streambuf &buf) const {
|
||||
Endianess::Writer put(buf);
|
||||
|
||||
put(samplingTime.year);
|
||||
put(samplingTime.yday);
|
||||
put(samplingTime.hour);
|
||||
put(samplingTime.minute);
|
||||
put(samplingTime.second);
|
||||
put(samplingTime.usec);
|
||||
put(samplingFrequencyNumerator);
|
||||
put(samplingFrequencyDenominator);
|
||||
|
||||
return put.good;
|
||||
}
|
||||
|
||||
|
||||
RTCM2DataRecord::RTCM2DataRecord() {
|
||||
_header.samplingFrequencyDenominator = 0;
|
||||
_header.samplingFrequencyNumerator = 0;
|
||||
// Just a bunch of bytes
|
||||
_header.dataType = DT_Unknown;
|
||||
}
|
||||
|
||||
void RTCM2DataRecord::setTimeStamp(const Time &ts) {
|
||||
timeToTimestamp(_header.samplingTime, ts);
|
||||
_startTime = ts;
|
||||
}
|
||||
|
||||
|
||||
void RTCM2DataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denominator) {
|
||||
_header.samplingFrequencyNumerator = numerator;
|
||||
_header.samplingFrequencyDenominator = denominator;
|
||||
}
|
||||
|
||||
|
||||
const char *RTCM2DataRecord::formatName() const {
|
||||
return "RTC2";
|
||||
}
|
||||
|
||||
|
||||
bool RTCM2DataRecord::readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(header.samplingTime.year);
|
||||
get(header.samplingTime.yday);
|
||||
get(header.samplingTime.hour);
|
||||
get(header.samplingTime.minute);
|
||||
get(header.samplingTime.second);
|
||||
get(header.samplingTime.usec);
|
||||
get(header.samplingFrequencyNumerator);
|
||||
get(header.samplingFrequencyDenominator);
|
||||
|
||||
startTime = timestampToTime(header.samplingTime);
|
||||
|
||||
if ( header.samplingFrequencyDenominator == 0 ||
|
||||
header.samplingFrequencyNumerator == 0 ) {
|
||||
endTime = startTime;
|
||||
}
|
||||
else {
|
||||
TimeSpan ts =
|
||||
header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
|
||||
endTime = startTime + ts;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const DataRecord::Header *RTCM2DataRecord::header() const {
|
||||
return &_header;
|
||||
}
|
||||
|
||||
|
||||
Time RTCM2DataRecord::startTime() const {
|
||||
return _startTime;
|
||||
}
|
||||
|
||||
|
||||
Time RTCM2DataRecord::endTime() const {
|
||||
return _endTime;
|
||||
}
|
||||
|
||||
|
||||
bool RTCM2DataRecord::canTrim() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool RTCM2DataRecord::canMerge() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool RTCM2DataRecord::trim(const Time &start,
|
||||
const Time &end) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t RTCM2DataRecord::dataSize(bool withHeader) const {
|
||||
if ( withHeader )
|
||||
return _data.size() + _header.dataSize();
|
||||
else
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
|
||||
DataRecord::ReadStatus RTCM2DataRecord::get(std::streambuf &buf, int size,
|
||||
const Time &start, const Time &end,
|
||||
int) {
|
||||
size -= _header.dataSize();
|
||||
if ( size < 0 ) return RS_Error;
|
||||
if ( !_header.get(buf) ) return RS_Error;
|
||||
|
||||
if ( _header.samplingFrequencyNumerator == 0 ||
|
||||
_header.samplingFrequencyDenominator == 0 ) {
|
||||
CAPS_ERROR("Sampling frequency not set");
|
||||
return RS_Error;
|
||||
}
|
||||
|
||||
TimeSpan ts =
|
||||
_header.samplingFrequencyDenominator / _header.samplingFrequencyNumerator;
|
||||
_startTime = timestampToTime(_header.samplingTime);
|
||||
_endTime = _startTime + ts;
|
||||
|
||||
if ( end.valid() && (end <= _startTime) ) return RS_AfterTimeWindow;
|
||||
if ( start.valid() && (start >= _endTime) ) return RS_BeforeTimeWindow;
|
||||
|
||||
RIFF::VectorChunk<1,false> dataChunk(_data, 0, size);
|
||||
|
||||
return dataChunk.get(buf, size)?RS_Complete:RS_Error;
|
||||
}
|
||||
|
||||
|
||||
bool RTCM2DataRecord::put(std::streambuf &buf, bool withHeader) const {
|
||||
if ( withHeader && !_header.put(buf) ) return false;
|
||||
|
||||
RIFF::CPtrChunk<1,false> dataChunk(&_data[0], _data.size());
|
||||
return dataChunk.put(buf);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
109
libs/gempa/caps/rtcm2packet.h
Normal file
109
libs/gempa/caps/rtcm2packet.h
Normal file
@ -0,0 +1,109 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2012 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_RTCM2PACKET_H
|
||||
#define GEMPA_CAPS_RTCM2PACKET_H
|
||||
|
||||
|
||||
#include <gempa/caps/endianess.h>
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class RTCM2DataRecord : public DataRecord {
|
||||
public:
|
||||
struct RTCM2Header : Header {
|
||||
bool get(std::streambuf &buf) {
|
||||
Endianess::Reader get(buf);
|
||||
|
||||
get(samplingTime.year);
|
||||
get(samplingTime.yday);
|
||||
get(samplingTime.hour);
|
||||
get(samplingTime.minute);
|
||||
get(samplingTime.second);
|
||||
get(samplingTime.usec);
|
||||
get(samplingFrequencyNumerator);
|
||||
get(samplingFrequencyDenominator);
|
||||
|
||||
return get.good;
|
||||
}
|
||||
|
||||
bool put(std::streambuf &buf) const;
|
||||
|
||||
// size of data header
|
||||
int dataSize() const {
|
||||
return
|
||||
sizeof(samplingTime.year) +
|
||||
sizeof(samplingTime.yday) +
|
||||
sizeof(samplingTime.hour) +
|
||||
sizeof(samplingTime.minute) +
|
||||
sizeof(samplingTime.second) +
|
||||
sizeof(samplingTime.usec) +
|
||||
sizeof(samplingFrequencyNumerator) +
|
||||
sizeof(samplingFrequencyDenominator);
|
||||
}
|
||||
};
|
||||
|
||||
RTCM2DataRecord();
|
||||
|
||||
const char* formatName() const;
|
||||
|
||||
void setTimeStamp(const Time &ts);
|
||||
|
||||
void setSamplingFrequency(uint16_t numerator, uint16_t denominator);
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime,
|
||||
Time &endTime);
|
||||
|
||||
virtual const Header *header() const;
|
||||
virtual Time startTime() const;
|
||||
virtual Time endTime() const;
|
||||
|
||||
virtual bool canTrim() const;
|
||||
virtual bool canMerge() const;
|
||||
|
||||
virtual bool trim(const Time &start,
|
||||
const Time &end) const;
|
||||
|
||||
virtual size_t dataSize(bool withHeader) const;
|
||||
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start,
|
||||
const Time &end,
|
||||
int maxSize = -1);
|
||||
|
||||
virtual bool put(std::streambuf &buf, bool withHeader) const;
|
||||
|
||||
PacketType packetType() const { return RTCM2Packet; }
|
||||
|
||||
|
||||
private:
|
||||
RTCM2Header _header;
|
||||
Buffer _data;
|
||||
Time _startTime;
|
||||
Time _endTime;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
404
libs/gempa/caps/sessiontable.cpp
Normal file
404
libs/gempa/caps/sessiontable.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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/sessiontable.h>
|
||||
|
||||
#include <gempa/caps/log.h>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
namespace {
|
||||
|
||||
void setDataType(const char *data, int len, Gempa::CAPS::SessionTableItem &item) {
|
||||
if ( CHECK_STRING(data, "FLOAT", len) ) {
|
||||
item.dataType = DT_FLOAT;
|
||||
item.dataSize = 4;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 32bit floats", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "DOUBLE", len) ) {
|
||||
item.dataType = DT_DOUBLE;
|
||||
item.dataSize = 8;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 64bit doubles", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT64", len) ) {
|
||||
item.dataType = DT_INT64;
|
||||
item.dataSize = 8;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 64bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT32", len) ) {
|
||||
item.dataType = DT_INT32;
|
||||
item.dataSize = 4;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 32bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT16", len) ) {
|
||||
item.dataType = DT_INT16;
|
||||
item.dataSize = 2;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 16bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT8", len) ) {
|
||||
item.dataType = DT_INT8;
|
||||
item.dataSize = 1;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 8bit integers", item.streamID.c_str());
|
||||
}
|
||||
else {
|
||||
item.dataType = DT_Unknown;
|
||||
CAPS_WARNING("Unknown raw data type '%s', all packages will be ignored",
|
||||
string(data, len).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool SessionTableItem::splitStreamID() {
|
||||
const char *tok;
|
||||
int tok_len;
|
||||
int tok_count = 0;
|
||||
|
||||
const char* str = streamID.c_str();
|
||||
int len = strlen(str);
|
||||
|
||||
string *items[] = {&net, &sta, &loc, &cha};
|
||||
|
||||
for ( ; (tok = tokenize(str, ".", len, tok_len)) != NULL;
|
||||
++tok_count ) {
|
||||
if ( tok_count > 4 ) return false;
|
||||
|
||||
items[tok_count]->assign(tok, tok_len);
|
||||
}
|
||||
|
||||
if ( tok_count == 3 ) {
|
||||
cha = loc;
|
||||
loc = "";
|
||||
tok_count = 4;
|
||||
}
|
||||
|
||||
return tok_count == 4;
|
||||
}
|
||||
|
||||
SessionTable::SessionTable() : _state(Unspecific) {}
|
||||
|
||||
SessionTable::Status SessionTable::handleResponse(const char *src_data, int data_len) {
|
||||
enum StreamHeaderToken {
|
||||
REQ_ID = 0,
|
||||
SID = 1,
|
||||
SF = 2,
|
||||
UOM = 3,
|
||||
FORMAT = 4,
|
||||
COUNT = 5
|
||||
};
|
||||
|
||||
int src_len = data_len;
|
||||
const char *src = src_data;
|
||||
|
||||
int len;
|
||||
const char *data;
|
||||
if ( (data = tokenize(src_data, " ", data_len, len)) == NULL ) {
|
||||
CAPS_WARNING("server returned empty line, ignoring");
|
||||
return Success;
|
||||
}
|
||||
|
||||
switch ( _state ) {
|
||||
case Unspecific:
|
||||
if ( CHECK_STRING(data, "STATUS", len) ) {
|
||||
if ( (data = tokenize(src_data, " ", data_len, len)) != NULL ) {
|
||||
if ( CHECK_STRING(data, "OK", len) ) {
|
||||
CAPS_DEBUG("server responds OK");
|
||||
}
|
||||
else {
|
||||
CAPS_ERROR("received unknown status from server: %s", data);
|
||||
return Error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
CAPS_ERROR("received empty status from server");
|
||||
return Error;
|
||||
}
|
||||
}
|
||||
else if ( CHECK_STRING(data, "REQUESTS", len) ) {
|
||||
if ( (data = tokenize(src_data, " ", data_len, len)) != NULL ) {
|
||||
CAPS_WARNING("received unknown REQUESTS parameter: %s", data);
|
||||
}
|
||||
CAPS_DEBUG("stream table update started");
|
||||
_state = Requests;
|
||||
}
|
||||
else if ( CHECK_STRING(data, "EOD", len) ) {
|
||||
CAPS_DEBUG("server sent EOD");
|
||||
return EOD;
|
||||
}
|
||||
else {
|
||||
CAPS_ERROR("received unknown response: %s", data);
|
||||
return Error;
|
||||
}
|
||||
break;
|
||||
case Requests:
|
||||
if ( CHECK_STRING(data, "END", len) ) {
|
||||
if ( (data = tokenize(src_data, " ", data_len, len)) != NULL ) {
|
||||
CAPS_WARNING("received unknown END parameter: %s", data);
|
||||
}
|
||||
CAPS_DEBUG("stream table update complete");
|
||||
_state = Unspecific;
|
||||
break;
|
||||
}
|
||||
|
||||
CAPS_DEBUG("request response data: %s", string(data, len).c_str());
|
||||
|
||||
typedef std::pair<const char *, int> Buffer;
|
||||
const char *state = src;
|
||||
Buffer toks[COUNT];
|
||||
for ( int i = 0; i < COUNT; ++i )
|
||||
toks[i].second = 0;
|
||||
|
||||
// Update request map
|
||||
while ( (data = tokenize(src, ",", src_len, len)) != NULL ) {
|
||||
trim(data, len);
|
||||
if ( !strncasecmp(data, "ID:", 3) )
|
||||
toks[REQ_ID] = Buffer(data+3, len-3);
|
||||
else if ( !strncasecmp(data, "SID:", 4) )
|
||||
toks[SID] = Buffer(data+4, len-4);
|
||||
else if ( !strncasecmp(data, "SFREQ:", 6) )
|
||||
toks[SF] = Buffer(data+6, len-6);
|
||||
else if ( !strncasecmp(data, "UOM:", 4) )
|
||||
toks[UOM] = Buffer(data+4, len-4);
|
||||
else if ( !strncasecmp(data, "FMT:", 4) )
|
||||
toks[FORMAT] = Buffer(data+4, len-4);
|
||||
}
|
||||
|
||||
SessionTableItem item;
|
||||
char buffer[7];
|
||||
if ( toks[REQ_ID].second > 6 ) {
|
||||
CAPS_ERROR("request state ID too high: %s", state);
|
||||
return Error;
|
||||
}
|
||||
strncpy(buffer, toks[REQ_ID].first, toks[REQ_ID].second);
|
||||
buffer[toks[REQ_ID].second] = '\0';
|
||||
int req_id;
|
||||
|
||||
if ( (sscanf(buffer, "%d", &req_id) != 1) || req_id == 0 ) {
|
||||
CAPS_ERROR("invalid request ID: %s", buffer);
|
||||
return Error;
|
||||
}
|
||||
|
||||
if ( toks[SID].second == 0 ) {
|
||||
CAPS_ERROR("missing SID in request response");
|
||||
return Error;
|
||||
}
|
||||
|
||||
item.streamID.assign(toks[SID].first, toks[SID].second);
|
||||
|
||||
if ( req_id < 0 ) {
|
||||
removeStream(item.streamID);
|
||||
CAPS_DEBUG("stream %s has finished", item.streamID.c_str());
|
||||
return Success;
|
||||
}
|
||||
else {
|
||||
CAPS_DEBUG("new request ID %d for stream %s received", req_id, item.streamID.c_str());
|
||||
std::string tmp;
|
||||
|
||||
if ( (data = tokenize(toks[SF].first, "/", toks[SF].second, len)) != NULL ) {
|
||||
trim(data, len);
|
||||
tmp.assign(data, len);
|
||||
if ( !str2int(item.samplingFrequency, tmp.c_str()) ) {
|
||||
CAPS_ERROR("request state 'samplingFrequency' is not a number: %s", state);
|
||||
item.samplingFrequency = 0;
|
||||
}
|
||||
else {
|
||||
item.samplingFrequencyDivider = 1;
|
||||
if ( (data = tokenize(toks[SF].first, "/", toks[SF].second, len)) != NULL ) {
|
||||
trim(data, len);
|
||||
tmp.assign(data, len);
|
||||
if ( !str2int(item.samplingFrequencyDivider, tmp.c_str()) ) {
|
||||
CAPS_ERROR("request state 'samplingFrequencyDivider' "
|
||||
"is not a number: %s", state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
item.samplingFrequency = 0;
|
||||
}
|
||||
|
||||
if ( toks[UOM].second > 4 ) {
|
||||
CAPS_ERROR("request state 'unit of measurement' is invalid, "
|
||||
"too many characters: %s", state);
|
||||
item.uom.ID = 0;
|
||||
}
|
||||
else {
|
||||
memcpy(item.uom.str, toks[UOM].first, toks[UOM].second);
|
||||
}
|
||||
|
||||
if ( (data = tokenize(toks[FORMAT].first, "/", toks[FORMAT].second, len)) != NULL ) {
|
||||
trim(data, len);
|
||||
if ( CHECK_STRING(data, "RAW", len) ) {
|
||||
item.type = RawDataPacket;
|
||||
if ( (data = tokenize(toks[FORMAT].first, "/", toks[FORMAT].second, len)) != NULL ) {
|
||||
setDataType(data, len, item);
|
||||
}
|
||||
|
||||
CAPS_DEBUG("%s: samplingFrequency=%d/%d",
|
||||
item.streamID.c_str(), item.samplingFrequency,
|
||||
item.samplingFrequencyDivider);
|
||||
}
|
||||
else if ( CHECK_STRING(data, "FIXEDRAW", len) ) {
|
||||
item.type = FixedRawDataPacket;
|
||||
if ( (data = tokenize(toks[FORMAT].first, "/", toks[FORMAT].second, len)) != NULL ) {
|
||||
setDataType(data, len, item);
|
||||
}
|
||||
|
||||
CAPS_DEBUG("%s: samplingFrequency=%d/%d",
|
||||
item.streamID.c_str(), item.samplingFrequency,
|
||||
item.samplingFrequencyDivider);
|
||||
}
|
||||
else if ( CHECK_STRING(data, "MSEED", len) ) {
|
||||
item.type = MSEEDPacket;
|
||||
}
|
||||
else if ( CHECK_STRING(data, "META", len ) ) {
|
||||
item.type = MetaDataPacket;
|
||||
}
|
||||
else {
|
||||
string tmp(data, len);
|
||||
item.type = ANYPacket;
|
||||
}
|
||||
}
|
||||
|
||||
// Register item
|
||||
registerItem(req_id, item);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
void setDataType(const char *data, int len, Gempa::CAPS::SessionTableItem &item) {
|
||||
if ( CHECK_STRING(data, "FLOAT", len) ) {
|
||||
item.dataType = DT_FLOAT;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 32bit floats", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "DOUBLE", len) ) {
|
||||
item.dataType = DT_DOUBLE;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 64bit doubles", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT64", len) ) {
|
||||
item.dataType = DT_INT64;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 64bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT32", len) ) {
|
||||
item.dataType = DT_INT32;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 32bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT16", len) ) {
|
||||
item.dataType = DT_INT16;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 16bit integers", item.streamID.c_str());
|
||||
}
|
||||
else if ( CHECK_STRING(data, "INT8", len) ) {
|
||||
item.dataType = DT_INT8;
|
||||
//SEISCOMP_DEBUG("%s: raw format with 8bit integers", item.streamID.c_str());
|
||||
}
|
||||
else {
|
||||
string tmp;
|
||||
item.dataType = DT_Unknown;
|
||||
tmp.assign(data, len);
|
||||
CAPS_WARNING("Unknown raw data type '%s', all packages will be ignored",
|
||||
tmp.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void SessionTable::registerItem(int id, SessionTableItem &item) {
|
||||
item.fSamplingFrequency = (double)item.samplingFrequency /
|
||||
(double)item.samplingFrequencyDivider;
|
||||
|
||||
SessionTable::iterator it = find(id);
|
||||
if ( it == end() ) {
|
||||
if ( !item.splitStreamID() ) {
|
||||
CAPS_WARNING("invalid streamID received: %s", item.streamID.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the item
|
||||
SessionTableItem &target = operator[](id);
|
||||
target = item;
|
||||
|
||||
_streamIDLookup[item.streamID] = id;
|
||||
|
||||
if ( _itemAddedFunc ) _itemAddedFunc(&target);
|
||||
}
|
||||
else {
|
||||
bool res = item.splitStreamID();
|
||||
|
||||
// streamID changed for the same sessionID?
|
||||
if ( it->second.streamID != item.streamID ) {
|
||||
CAPS_WARNING("inconsistent state: streamID '%s' for id %d, "
|
||||
"but streamID '%s' has not been closed before",
|
||||
item.streamID.c_str(), id, it->second.streamID.c_str());
|
||||
// Update lookup table
|
||||
_streamIDLookup.erase(_streamIDLookup.find(it->second.streamID));
|
||||
|
||||
if ( !res ) {
|
||||
CAPS_WARNING("invalid streamID received: %s", item.streamID.c_str());
|
||||
erase(it);
|
||||
return;
|
||||
}
|
||||
|
||||
_streamIDLookup[item.streamID] = id;
|
||||
|
||||
// TODO: How to update the request list?
|
||||
}
|
||||
|
||||
it->second = item;
|
||||
|
||||
if ( _itemAddedFunc ) _itemAddedFunc(&it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void SessionTable::removeStream(const std::string &streamID) {
|
||||
StreamIDLookupTable::iterator it = _streamIDLookup.find(streamID);
|
||||
if ( it == _streamIDLookup.end() ) {
|
||||
CAPS_WARNING("internal: tried to remove unknown stream '%s'",
|
||||
streamID.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
SessionTable::iterator sessionIt = find(it->second);
|
||||
if ( sessionIt != end() ) {
|
||||
// Remove session table row
|
||||
if ( _itemAboutToBeRemovedFunc )
|
||||
_itemAboutToBeRemovedFunc(&sessionIt->second);
|
||||
erase(sessionIt);
|
||||
}
|
||||
|
||||
// Remove lookup entry
|
||||
_streamIDLookup.erase(it);
|
||||
}
|
||||
|
||||
void SessionTable::reset() {
|
||||
clear();
|
||||
_streamIDLookup.clear();
|
||||
_state = Unspecific;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
104
libs/gempa/caps/sessiontable.h
Normal file
104
libs/gempa/caps/sessiontable.h
Normal file
@ -0,0 +1,104 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_SESSIONTABLE_H
|
||||
#define GEMPA_CAPS_SESSIONTABLE_H
|
||||
|
||||
#include "packet.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
struct SessionTableItem {
|
||||
SessionTableItem() : samplingFrequency(0), samplingFrequencyDivider(0),
|
||||
fSamplingFrequency(0.0), dataType(DT_Unknown),
|
||||
dataSize(0), userData(NULL) {}
|
||||
std::string streamID;
|
||||
std::string net;
|
||||
std::string sta;
|
||||
std::string loc;
|
||||
std::string cha;
|
||||
uint16_t samplingFrequency;
|
||||
uint16_t samplingFrequencyDivider;
|
||||
double fSamplingFrequency;
|
||||
PacketType type;
|
||||
DataType dataType;
|
||||
int dataSize;
|
||||
UOM uom;
|
||||
Time startTime;
|
||||
Time endTime;
|
||||
void *userData;
|
||||
|
||||
bool splitStreamID();
|
||||
};
|
||||
|
||||
|
||||
class SC_GEMPA_CAPS_API SessionTable : public std::map<int, SessionTableItem> {
|
||||
public:
|
||||
enum Status {Success, Error, EOD};
|
||||
|
||||
typedef std::function<void (SessionTableItem*)> CallbackFunc;
|
||||
|
||||
public:
|
||||
//! Default constructor
|
||||
SessionTable();
|
||||
virtual ~SessionTable() {}
|
||||
|
||||
//! Resets state
|
||||
void reset();
|
||||
|
||||
SessionTableItem* getItem(int id) {
|
||||
SessionTable::iterator it = find(id);
|
||||
if ( it == end() ) return NULL;
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
Status handleResponse(const char *src_data, int data_len);
|
||||
|
||||
void setItemAddedFunc(const CallbackFunc &func) { _itemAddedFunc = func; }
|
||||
void setItemAboutToBeRemovedFunc(const CallbackFunc &func) {
|
||||
_itemAboutToBeRemovedFunc = func;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ResponseState {
|
||||
Unspecific,
|
||||
Requests
|
||||
};
|
||||
|
||||
typedef std::map<std::string, int> StreamIDLookupTable;
|
||||
|
||||
private:
|
||||
void registerItem(int id, SessionTableItem &item);
|
||||
void removeStream(const std::string & streamID);
|
||||
|
||||
private:
|
||||
ResponseState _state;
|
||||
StreamIDLookupTable _streamIDLookup;
|
||||
CallbackFunc _itemAddedFunc;
|
||||
CallbackFunc _itemAboutToBeRemovedFunc;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
545
libs/gempa/caps/socket.cpp
Normal file
545
libs/gempa/caps/socket.cpp
Normal file
@ -0,0 +1,545 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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/socket.h>
|
||||
#include <gempa/caps/log.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#ifndef SHUT_RDWR
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#endif
|
||||
#define _WIN32_WINNT 0x0501 // Older versions does not support getaddrinfo
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
inline string toString(unsigned short value) {
|
||||
stringstream ss;
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Socket() {
|
||||
_fd = -1;
|
||||
_bytesSent = _bytesReceived = 0;
|
||||
_timeOutSecs = _timeOutUsecs = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2,0),&wsa);
|
||||
#endif
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::~Socket() {
|
||||
close();
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
const char *Socket::toString(Status stat) {
|
||||
switch ( stat ) {
|
||||
case Success:
|
||||
return "success";
|
||||
default:
|
||||
case Error:
|
||||
return "error";
|
||||
case AllocationError:
|
||||
return "allocation error";
|
||||
case ReuseAdressError:
|
||||
return "reusing address failed";
|
||||
case BindError:
|
||||
return "bind error";
|
||||
case ListenError:
|
||||
return "listen error";
|
||||
case AcceptError:
|
||||
return "accept error";
|
||||
case ConnectError:
|
||||
return "connect error";
|
||||
case AddrInfoError:
|
||||
return "address info error";
|
||||
case Timeout:
|
||||
return "timeout";
|
||||
case InvalidSocket:
|
||||
return "invalid socket";
|
||||
case InvalidPort:
|
||||
return "invalid port";
|
||||
case InvalidAddressFamily:
|
||||
return "invalid address family";
|
||||
case InvalidAddress:
|
||||
return "invalid address";
|
||||
case InvalidHostname:
|
||||
return "invalid hostname";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
void Socket::shutdown() {
|
||||
if ( _fd == -1 ) return;
|
||||
//CAPS_DEBUG("Socket::shutdown");
|
||||
::shutdown(_fd, SHUT_RDWR);
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
void Socket::close() {
|
||||
if ( _fd != -1 ) {
|
||||
//CAPS_DEBUG("[socket] close %lX with fd = %d", (long int)this, _fd);
|
||||
int fd = _fd;
|
||||
_fd = -1;
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Status Socket::setSocketTimeout(int secs, int usecs) {
|
||||
_timeOutSecs = secs;
|
||||
_timeOutUsecs = usecs;
|
||||
|
||||
if ( _fd != -1 )
|
||||
return applySocketTimeout(_timeOutSecs, _timeOutUsecs);
|
||||
|
||||
return Success;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Status Socket::applySocketTimeout(int secs, int usecs) {
|
||||
if ( _fd != -1 ) {
|
||||
struct timeval timeout;
|
||||
void *opt;
|
||||
int optlen;
|
||||
|
||||
if ( secs >= 0 ) {
|
||||
timeout.tv_sec = secs;
|
||||
timeout.tv_usec = usecs;
|
||||
opt = &timeout;
|
||||
optlen = sizeof(timeout);
|
||||
}
|
||||
else {
|
||||
opt = NULL;
|
||||
optlen = 0;
|
||||
}
|
||||
|
||||
CAPS_DEBUG("set socket timeout to %d.%ds", secs, usecs);
|
||||
|
||||
if ( setsockopt(_fd, SOL_SOCKET, SO_RCVTIMEO, opt, optlen) )
|
||||
return Error;
|
||||
|
||||
if ( setsockopt(_fd, SOL_SOCKET, SO_SNDTIMEO, opt, optlen) )
|
||||
return Error;
|
||||
}
|
||||
else
|
||||
return InvalidSocket;
|
||||
|
||||
return Success;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Device::Status Socket::setNonBlocking(bool nb) {
|
||||
if ( !isValid() )
|
||||
return Device::InvalidDevice;
|
||||
|
||||
#ifndef WIN32
|
||||
int flags = fcntl(_fd, F_GETFL, 0);
|
||||
|
||||
if ( nb )
|
||||
flags |= O_NONBLOCK;
|
||||
else
|
||||
flags &= ~O_NONBLOCK;
|
||||
|
||||
if ( fcntl(_fd, F_SETFL, flags) == -1 )
|
||||
return Device::Error;
|
||||
#else
|
||||
u_long arg = nb?1:0;
|
||||
if ( ioctlsocket(_fd, FIONBIO, &arg) != 0 )
|
||||
return Device::Error;
|
||||
#endif
|
||||
|
||||
return Device::Success;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
|
||||
if ( _fd != -1 ) {
|
||||
//CAPS_WARNING("closing stale socket");
|
||||
close();
|
||||
}
|
||||
|
||||
struct sockaddr addr;
|
||||
size_t addrlen;
|
||||
|
||||
struct addrinfo *res;
|
||||
struct addrinfo hints;
|
||||
|
||||
memset (&hints, 0, sizeof(hints));
|
||||
hints.ai_family = PF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
string strPort = ::toString(port);
|
||||
|
||||
int ret = getaddrinfo(hostname.c_str(), strPort.c_str(), &hints, &res);
|
||||
if ( ret ) {
|
||||
CAPS_DEBUG("Test3 Socket::connect(%s:%d): %s",
|
||||
hostname.c_str(), port,
|
||||
#ifndef WIN32
|
||||
strerror(errno));
|
||||
#else
|
||||
gai_strerror(ret));
|
||||
#endif
|
||||
return AddrInfoError;
|
||||
}
|
||||
|
||||
addr = *(res->ai_addr);
|
||||
addrlen = res->ai_addrlen;
|
||||
freeaddrinfo(res);
|
||||
|
||||
if ( (_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) {
|
||||
/*CAPS_DEBUG("Socket::connect(%s:%d): %s",
|
||||
hostname.c_str(), port, strerror(errno));*/
|
||||
return AllocationError;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == -1 ) {
|
||||
if ( errno != EINPROGRESS ) {
|
||||
/*CAPS_DEBUG("Socket::connect(%s:%d): %s",
|
||||
hostname.c_str(), port, strerror(errno));*/
|
||||
close();
|
||||
return errno == ETIMEDOUT?Timeout:ConnectError;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == SOCKET_ERROR ) {
|
||||
int err = WSAGetLastError();
|
||||
if (err != WSAEINPROGRESS && err != WSAEWOULDBLOCK) {
|
||||
CAPS_DEBUG("Socket::connect(%s:%d): %s",
|
||||
hostname.c_str(), port, gai_strerror(err));
|
||||
close();
|
||||
return ConnectError;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return Success;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int Socket::send(const char *data) {
|
||||
return write(data, strlen(data));
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int Socket::write(const char *data, int len) {
|
||||
#if !defined(MACOSX) && !defined(WIN32)
|
||||
int sent = (int)::send(_fd, data, len, MSG_NOSIGNAL);
|
||||
#else
|
||||
int sent = (int)::send(_fd, data, len, 0);
|
||||
#endif
|
||||
if ( sent > 0 ) {
|
||||
_bytesSent += sent;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int Socket::read(char *data, int len) {
|
||||
int recvd = (int)::recv(_fd, data, len, 0);
|
||||
if ( recvd > 0 ) _bytesReceived += recvd;
|
||||
return recvd;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int Socket::flush() { return 1; }
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
bool Socket::isValid() {
|
||||
return _fd != -1;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
SSLSocket::SSLSocket() : _ssl(NULL), _ctx(NULL) {}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
SSLSocket::SSLSocket(SSL_CTX *ctx) : _ssl(NULL), _ctx(ctx) {}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
SSLSocket::~SSLSocket() {
|
||||
close();
|
||||
cleanUp();
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int SSLSocket::write(const char *data, int len) {
|
||||
int ret = SSL_write(_ssl, data, len);
|
||||
if ( ret > 0 ) {
|
||||
_bytesSent += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int err = SSL_get_error(_ssl, ret);
|
||||
|
||||
switch ( err ) {
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
int SSLSocket::read(char *data, int len) {
|
||||
int ret = SSL_read(_ssl, data, len);
|
||||
if ( ret > 0 ) {
|
||||
_bytesReceived += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int err = SSL_get_error(_ssl, ret);
|
||||
|
||||
switch ( err ) {
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port) {
|
||||
cleanUp();
|
||||
|
||||
_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if ( _ctx == NULL ) {
|
||||
CAPS_DEBUG("Invalid SSL context");
|
||||
return ConnectError;
|
||||
}
|
||||
|
||||
SSL_CTX_set_mode(_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
|
||||
Status s = Socket::connect(hostname, port);
|
||||
if ( s != Success )
|
||||
return s;
|
||||
|
||||
_ssl = SSL_new(_ctx);
|
||||
if ( _ssl == NULL ) {
|
||||
CAPS_DEBUG("Failed to create SSL context");
|
||||
return ConnectError;
|
||||
}
|
||||
|
||||
SSL_set_fd(_ssl, _fd);
|
||||
SSL_set_shutdown(_ssl, 0);
|
||||
SSL_set_connect_state(_ssl);
|
||||
int err = SSL_connect(_ssl);
|
||||
if ( err < 0 ) {
|
||||
CAPS_ERROR("Failed to connect with SSL, error %d",
|
||||
SSL_get_error(_ssl, err));
|
||||
close();
|
||||
return ConnectError;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
const unsigned char *SSLSocket::sessionID() const {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
return _ssl?_ssl->session->session_id:NULL;
|
||||
#else
|
||||
return _ssl?SSL_SESSION_get0_id_context(SSL_get0_session(_ssl), NULL):NULL;
|
||||
#endif
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
unsigned int SSLSocket::sessionIDLength() const {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
return _ssl?_ssl->session->session_id_length:0;
|
||||
#else
|
||||
unsigned int len;
|
||||
if ( !_ssl ) return 0;
|
||||
SSL_SESSION_get0_id_context(SSL_get0_session(_ssl), &len);
|
||||
return len;
|
||||
#endif
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
X509 *SSLSocket::peerCertificate() {
|
||||
if ( _ssl == NULL ) return NULL;
|
||||
return SSL_get_peer_certificate(_ssl);
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
void SSLSocket::cleanUp() {
|
||||
if ( _ssl ) {
|
||||
SSL_free(_ssl);
|
||||
_ssl = NULL;
|
||||
}
|
||||
|
||||
if ( _ctx ) {
|
||||
SSL_CTX_free(_ctx);
|
||||
_ctx = NULL;
|
||||
}
|
||||
}
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
}
|
||||
}
|
273
libs/gempa/caps/socket.h
Normal file
273
libs/gempa/caps/socket.h
Normal file
@ -0,0 +1,273 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2013 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_SOCKET_H
|
||||
#define GEMPA_CAPS_SOCKET_H
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <streambuf>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class SC_GEMPA_CAPS_API Socket {
|
||||
public:
|
||||
typedef uint64_t count_t;
|
||||
|
||||
enum Status {
|
||||
Success = 0,
|
||||
Error,
|
||||
AllocationError,
|
||||
ReuseAdressError,
|
||||
BindError,
|
||||
ListenError,
|
||||
AcceptError,
|
||||
ConnectError,
|
||||
AddrInfoError,
|
||||
Timeout,
|
||||
InvalidSocket,
|
||||
InvalidPort,
|
||||
InvalidAddressFamily,
|
||||
InvalidAddress,
|
||||
InvalidHostname
|
||||
};
|
||||
|
||||
struct Device {
|
||||
enum Status {
|
||||
Success = 0,
|
||||
Error,
|
||||
InvalidDevice,
|
||||
AllocationError
|
||||
};
|
||||
};
|
||||
|
||||
public:
|
||||
Socket();
|
||||
virtual ~Socket();
|
||||
|
||||
|
||||
public:
|
||||
static const char *toString(Status);
|
||||
|
||||
int fd() { return _fd; }
|
||||
|
||||
bool isValid();
|
||||
|
||||
void shutdown();
|
||||
void close();
|
||||
|
||||
|
||||
int send(const char *data);
|
||||
|
||||
virtual int write(const char *data, int len);
|
||||
virtual int read(char *data, int len);
|
||||
virtual int flush();
|
||||
|
||||
//! Sets the socket timeout. This utilizes setsockopt which does not
|
||||
//! work in non blocking sockets.
|
||||
Status setSocketTimeout(int secs, int usecs);
|
||||
|
||||
Device::Status setNonBlocking(bool nb);
|
||||
|
||||
virtual Status connect(const std::string &hostname, uint16_t port);
|
||||
|
||||
count_t rx() const { return _bytesReceived; }
|
||||
count_t tx() const { return _bytesSent; }
|
||||
|
||||
protected:
|
||||
Status applySocketTimeout(int secs, int usecs);
|
||||
|
||||
|
||||
protected:
|
||||
int _fd;
|
||||
|
||||
count_t _bytesSent;
|
||||
count_t _bytesReceived;
|
||||
|
||||
int _timeOutSecs;
|
||||
int _timeOutUsecs;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<Socket> SocketPtr;
|
||||
|
||||
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
|
||||
|
||||
class SSLSocket : public Socket {
|
||||
public:
|
||||
SSLSocket();
|
||||
SSLSocket(SSL_CTX *ctx);
|
||||
~SSLSocket();
|
||||
|
||||
public:
|
||||
int write(const char *data, int len);
|
||||
int read(char *data, int len);
|
||||
|
||||
Status connect(const std::string &hostname, uint16_t port);
|
||||
|
||||
virtual const unsigned char *sessionID() const;
|
||||
virtual unsigned int sessionIDLength() const;
|
||||
|
||||
X509 *peerCertificate();
|
||||
|
||||
private:
|
||||
void cleanUp();
|
||||
|
||||
private:
|
||||
SSL *_ssl;
|
||||
SSL_CTX *_ctx;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<SSLSocket> SSLSocketPtr;
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T, int N>
|
||||
class socketbuf : public std::streambuf {
|
||||
public:
|
||||
socketbuf() {
|
||||
setsocket(NULL);
|
||||
}
|
||||
|
||||
socketbuf(T *sock) {
|
||||
setsocket(sock);
|
||||
}
|
||||
|
||||
void setsocket(T *sock) {
|
||||
_allowed_reads = -1;
|
||||
_real_buffer_size = 0;
|
||||
_block_write = false;
|
||||
setg(_in, _in, _in);
|
||||
setp(_out, _out + N);
|
||||
_sock = sock;
|
||||
}
|
||||
|
||||
void settimeout(const struct timeval &tv) {
|
||||
_timeout = tv;
|
||||
}
|
||||
|
||||
void set_read_limit(int bytes) {
|
||||
_allowed_reads = bytes;
|
||||
|
||||
if ( _allowed_reads >= 0 ) {
|
||||
if ( egptr() - gptr() > _allowed_reads )
|
||||
setg(eback(), gptr(), gptr() + _allowed_reads);
|
||||
|
||||
// Set the number of read bytes to the
|
||||
// remaining bytes in the buffer
|
||||
_allowed_reads -= egptr() - gptr();
|
||||
}
|
||||
else
|
||||
setg(eback(), gptr(), eback() + _real_buffer_size);
|
||||
|
||||
//std::cout << "[" << (void*)eback() << ", " << (void*)gptr() << ", " << (void*)egptr() << "]" << " = " << (egptr() - gptr()) << std::endl;
|
||||
}
|
||||
|
||||
int read_limit() const {
|
||||
if ( _allowed_reads < 0 ) return -1;
|
||||
return egptr() - gptr() + _allowed_reads;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
virtual int underflow() {
|
||||
// No more reads allowed?
|
||||
if ( !_allowed_reads )
|
||||
return traits_type::eof();
|
||||
|
||||
// Read available data from socket
|
||||
int res = _sock->read(_in, N);
|
||||
if ( res <= 0 ) {
|
||||
set_read_limit(0);
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
// Set input sequence pointers
|
||||
_real_buffer_size = res;
|
||||
setg(_in, _in, _in + _real_buffer_size);
|
||||
|
||||
// clip to limit
|
||||
set_read_limit(_allowed_reads);
|
||||
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
virtual int overflow(int c) {
|
||||
if ( _block_write ) return traits_type::eof();
|
||||
|
||||
if ( pptr() - pbase() == N ) {
|
||||
if ( sync() != 0 ) return traits_type::eof();
|
||||
}
|
||||
|
||||
if ( !traits_type::eq_int_type(traits_type::eof(), c)) {
|
||||
traits_type::assign(*pptr(), traits_type::to_char_type(c));
|
||||
|
||||
pbump(1);
|
||||
}
|
||||
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
virtual int sync() {
|
||||
if ( pbase() == pptr() ) return 0;
|
||||
|
||||
int res = _sock->write(pbase(), pptr() - pbase());
|
||||
if ( res == pptr() - pbase() ) {
|
||||
setp(_out, _out + N);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Only forward seeking is supported
|
||||
virtual std::streampos
|
||||
seekoff(std::streamoff off, std::ios_base::seekdir way,
|
||||
std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) {
|
||||
if ( way != std::ios_base::cur || which != std::ios_base::in || off < 0 )
|
||||
return -1;
|
||||
|
||||
while ( off > 0 ) {
|
||||
int ch = sbumpc();
|
||||
if ( ch == traits_type::eof() )
|
||||
return -1;
|
||||
--off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
T *_sock;
|
||||
timeval _timeout;
|
||||
char _in[N];
|
||||
char _out[N];
|
||||
bool _block_write;
|
||||
int _real_buffer_size;
|
||||
int _allowed_reads;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
1009
libs/gempa/caps/strptime.c
Normal file
1009
libs/gempa/caps/strptime.c
Normal file
File diff suppressed because it is too large
Load Diff
22
libs/gempa/caps/strptime.h
Normal file
22
libs/gempa/caps/strptime.h
Normal file
@ -0,0 +1,22 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) by GFZ Potsdam *
|
||||
* *
|
||||
* You can redistribute and/or modify this program under the *
|
||||
* terms of the SeisComP Public License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* SeisComP Public License for more details. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef STRPTIME_H
|
||||
#define STRPTIME_H
|
||||
|
||||
/*
|
||||
* Version of "strptime()", for the benefit of OSes that don't have it.
|
||||
*/
|
||||
extern char *strptime(const char *, const char *, struct tm *);
|
||||
|
||||
#endif
|
23
libs/gempa/caps/test/CMakeLists.txt
Normal file
23
libs/gempa/caps/test/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
SET(TESTS
|
||||
datetime.cpp
|
||||
rawpacket.cpp
|
||||
packet.cpp
|
||||
utils.cpp
|
||||
datetime_time.cpp
|
||||
endianess.cpp
|
||||
mseedpacket.cpp
|
||||
)
|
||||
|
||||
FOREACH(testSrc ${TESTS})
|
||||
GET_FILENAME_COMPONENT(testName ${testSrc} NAME_WE)
|
||||
SET(testName test_caps_${testName})
|
||||
ADD_EXECUTABLE(${testName} ${testSrc})
|
||||
SC_LINK_LIBRARIES_INTERNAL(${testName} unittest)
|
||||
SC_LINK_LIBRARIES(${testName} ${CURL_LIBRARIES} caps_client)
|
||||
|
||||
ADD_TEST(
|
||||
NAME ${testName}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMAND ${testName}
|
||||
)
|
||||
ENDFOREACH(testSrc)
|
BIN
libs/gempa/caps/test/data/AM.RFE4F.00.SHZ.20180912.mseed
Normal file
BIN
libs/gempa/caps/test/data/AM.RFE4F.00.SHZ.20180912.mseed
Normal file
Binary file not shown.
566
libs/gempa/caps/test/datetime.cpp
Normal file
566
libs/gempa/caps/test/datetime.cpp
Normal file
@ -0,0 +1,566 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace gc = Gempa::CAPS;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
|
||||
|
||||
bool isClose(gc::TimeSpan time, long sec, long micro, int offset = 1) {
|
||||
long microSeconds = time.microseconds();
|
||||
|
||||
long secDiff = time.seconds() - sec;
|
||||
if ( secDiff > 0 )
|
||||
microSeconds += secDiff * 1000000;
|
||||
else if ( secDiff < 0 )
|
||||
micro += abs(secDiff) * 1000000;
|
||||
|
||||
if ( abs(microSeconds - micro) <= offset )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_datetime)
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(construction) {
|
||||
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
struct timeval tvPositive;
|
||||
tvPositive.tv_sec = 60000;
|
||||
tvPositive.tv_usec = 123456;
|
||||
gc::TimeSpan k(tvPositive);
|
||||
BOOST_CHECK(k.seconds() == tvPositive.tv_sec);
|
||||
BOOST_CHECK(k.microseconds() == tvPositive.tv_usec);
|
||||
|
||||
struct timeval tvNegativeUsec;
|
||||
tvNegativeUsec.tv_sec = 300;
|
||||
tvNegativeUsec.tv_usec = -13456;
|
||||
gc::TimeSpan ki(tvNegativeUsec);
|
||||
BOOST_CHECK_EQUAL(ki.seconds() , tvNegativeUsec.tv_sec);
|
||||
BOOST_CHECK_EQUAL(ki.microseconds() , tvNegativeUsec.tv_usec);
|
||||
|
||||
struct timeval tvNegativeSec;
|
||||
tvNegativeSec.tv_sec = -300;
|
||||
tvNegativeSec.tv_usec = 13456;
|
||||
|
||||
gc::TimeSpan kj(tvNegativeSec);
|
||||
BOOST_CHECK_EQUAL(kj.seconds() , tvNegativeSec.tv_sec);
|
||||
BOOST_CHECK_EQUAL(kj.microseconds() , tvNegativeSec.tv_usec);
|
||||
|
||||
struct timeval tvNegative;
|
||||
tvNegative.tv_sec = -3000;
|
||||
tvNegative.tv_usec = -123456;
|
||||
gc::TimeSpan kk(tvNegative);
|
||||
BOOST_CHECK_EQUAL(kk.seconds() , tvNegative.tv_sec);
|
||||
BOOST_CHECK_EQUAL(kk.microseconds() , tvNegative.tv_usec);
|
||||
|
||||
struct timeval tvNull;
|
||||
gc::TimeSpan kl(tvNull);
|
||||
BOOST_CHECK_EQUAL(kl.seconds() , tvNull.tv_sec);
|
||||
BOOST_CHECK_EQUAL(kl.microseconds() , tvNull.tv_usec);
|
||||
|
||||
// copy
|
||||
gc::TimeSpan copyPositive(gc::TimeSpan(79743.123456));
|
||||
BOOST_CHECK(copyPositive.seconds() == 79743);
|
||||
BOOST_CHECK(copyPositive.microseconds() == 123456);
|
||||
|
||||
gc::TimeSpan copyNegative(gc::TimeSpan(-98765.123456));
|
||||
long sec = -98765;
|
||||
long micro = -123456;
|
||||
BOOST_CHECK(isClose(copyNegative, sec, micro,20) == true);
|
||||
|
||||
gc::TimeSpan copyNegativeTest(gc::TimeSpan(-98765.000070));
|
||||
sec = -98765 ;
|
||||
micro = 70;
|
||||
BOOST_CHECK_EQUAL(isClose(copyNegativeTest,sec, micro,500), true);
|
||||
|
||||
// long
|
||||
gc::TimeSpan longPositive(765432, 456789);
|
||||
BOOST_CHECK(longPositive.seconds() == 765432);
|
||||
BOOST_CHECK(longPositive.microseconds() == 456789);
|
||||
|
||||
gc::TimeSpan longNegativeUsec(200, -732);
|
||||
BOOST_CHECK_EQUAL(longNegativeUsec.seconds(), 200);
|
||||
BOOST_CHECK_EQUAL(longNegativeUsec.microseconds(), -732);
|
||||
|
||||
gc::TimeSpan longNegativeSec(-800, 73265);
|
||||
BOOST_CHECK_EQUAL(longNegativeSec.seconds(), -800);
|
||||
BOOST_CHECK_EQUAL(longNegativeSec.microseconds(), 73265);
|
||||
|
||||
gc::TimeSpan longNegative(-500, -732650);
|
||||
BOOST_CHECK_EQUAL(longNegative.seconds(), -500);
|
||||
BOOST_CHECK_EQUAL(longNegative.microseconds(), -732650);
|
||||
|
||||
// double
|
||||
double number = 123456.98765;
|
||||
gc::TimeSpan doublePositive(number);
|
||||
BOOST_CHECK_EQUAL(doublePositive.seconds(), 123456);
|
||||
BOOST_CHECK_EQUAL(doublePositive.microseconds(), 987650);
|
||||
|
||||
number = -98765.123470;
|
||||
gc::TimeSpan doubleNegative(number);
|
||||
BOOST_CHECK_EQUAL(doubleNegative.seconds(), -98765);
|
||||
BOOST_CHECK_CLOSE((double)doubleNegative.microseconds(), -123470, 0.01);
|
||||
|
||||
number = -98765.000080;
|
||||
gc::TimeSpan doubleNegativeTest(number);
|
||||
sec = -98765;
|
||||
micro = 80;
|
||||
BOOST_CHECK_EQUAL(isClose(doubleNegativeTest,sec, micro,500), true);
|
||||
|
||||
// pointer
|
||||
timeval n;
|
||||
n.tv_sec = 123;
|
||||
n.tv_usec = 123456;
|
||||
|
||||
gc::TimeSpan pointerPositive(&n);
|
||||
BOOST_CHECK_EQUAL(pointerPositive.seconds(), 123);
|
||||
BOOST_CHECK_EQUAL(pointerPositive.microseconds(), 123456);
|
||||
|
||||
n.tv_sec = -123;
|
||||
n.tv_usec = 123456;
|
||||
|
||||
gc::TimeSpan pointerNegativeSec(&n);
|
||||
BOOST_CHECK_EQUAL(pointerNegativeSec.seconds(), -123);
|
||||
BOOST_CHECK_EQUAL(pointerNegativeSec.microseconds(), 123456);
|
||||
|
||||
n.tv_sec = 123;
|
||||
n.tv_usec = -123456;
|
||||
|
||||
gc::TimeSpan pointerNegativeUsec(&n);
|
||||
BOOST_CHECK_EQUAL(pointerNegativeUsec.seconds(), 123);
|
||||
BOOST_CHECK_EQUAL(pointerNegativeUsec.microseconds(), -123456);
|
||||
|
||||
n.tv_sec = -123;
|
||||
n.tv_usec = -123456;
|
||||
|
||||
gc::TimeSpan pointerNegative(&n);
|
||||
BOOST_CHECK_EQUAL(pointerNegative.seconds(), -123);
|
||||
BOOST_CHECK_EQUAL(pointerNegative.microseconds(), -123456);
|
||||
|
||||
timeval *nullPointer = NULL;
|
||||
gc::TimeSpan pointerNull(nullPointer);
|
||||
BOOST_CHECK_EQUAL(pointerNull.seconds(), 0);
|
||||
BOOST_CHECK_EQUAL(pointerNull.microseconds(), 0);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(addition) {
|
||||
gc::TimeSpan k = 5, l = 7;
|
||||
BOOST_CHECK(k + l == gc::TimeSpan(12));
|
||||
|
||||
gc::TimeSpan m = 320.5, n = 60.2;
|
||||
BOOST_CHECK(m + n == gc::TimeSpan(380.7));
|
||||
|
||||
gc::TimeSpan g = 55, d = -50;
|
||||
BOOST_CHECK(d + g == gc::TimeSpan(5));
|
||||
|
||||
gc::TimeSpan t = -80.0053, s = -70.0044;
|
||||
gc::TimeSpan result = t + s;
|
||||
long sec = t.seconds() + s.seconds();
|
||||
long micro = t.microseconds() + s.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result, sec,micro),true);
|
||||
|
||||
gc::TimeSpan u = -5.035, v = -60.044;
|
||||
result = u + v;
|
||||
sec = u.seconds() + v.seconds();
|
||||
micro = u.microseconds() + v.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
|
||||
gc::TimeSpan w = -5.0885, x = -6.01111;
|
||||
result = w + x;
|
||||
sec = w.seconds() + x.seconds();
|
||||
micro = w.microseconds() + x.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(subtraction) {
|
||||
gc::TimeSpan k = 5, l = 6;
|
||||
BOOST_CHECK(k - l == gc::TimeSpan(-1));
|
||||
|
||||
gc::TimeSpan t = 58, i = 68.05;
|
||||
gc::TimeSpan result = t - i;
|
||||
long sec = t.seconds() - i.seconds();
|
||||
long micro = t.microseconds() - i.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
|
||||
gc::TimeSpan e(30,4);
|
||||
gc::TimeSpan o(45,3);
|
||||
result = e - o;
|
||||
sec = e.seconds() - o.seconds();
|
||||
micro = e.microseconds() - o.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
|
||||
gc::TimeSpan f = 30.00004, g = -45.00003;
|
||||
result = f - g;
|
||||
sec = f.seconds() - g.seconds();
|
||||
micro = f.microseconds() - g.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(setSec) {
|
||||
gc::TimeSpan h, k, l;
|
||||
BOOST_CHECK_EQUAL(h.set(4), gc::TimeSpan(4));
|
||||
BOOST_CHECK_EQUAL(k.set(2), gc::TimeSpan(2));
|
||||
BOOST_CHECK_EQUAL(l.set(1), gc::TimeSpan(1));
|
||||
BOOST_CHECK_EQUAL(l.set(-10), gc::TimeSpan(-10));
|
||||
BOOST_CHECK_EQUAL(k.set(-9876), gc::TimeSpan(-9876));
|
||||
BOOST_CHECK_EQUAL(l.set(0), gc::TimeSpan(0.));
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(setMicro) {
|
||||
gc::TimeSpan h, k, l, ts;
|
||||
BOOST_CHECK_EQUAL(h.setUSecs(9), gc::TimeSpan(0.000009));
|
||||
BOOST_CHECK_EQUAL(k.setUSecs(2), gc::TimeSpan(0.0000020));
|
||||
BOOST_CHECK_EQUAL(l.setUSecs(3), gc::TimeSpan(0.000003));
|
||||
BOOST_CHECK_EQUAL(ts.setUSecs(4), gc::TimeSpan(0.000004));
|
||||
BOOST_CHECK_EQUAL(l.setUSecs(0), gc::TimeSpan(0.0));
|
||||
BOOST_CHECK_EQUAL(h.setUSecs(2000000), gc::TimeSpan(2.00));
|
||||
BOOST_CHECK_EQUAL(k.setUSecs(-3000000), gc::TimeSpan(-3.0));
|
||||
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
|
||||
gc::TimeSpan test = l.setUSecs(-7262);
|
||||
BOOST_WARN_EQUAL(test.microseconds(), -7262);
|
||||
|
||||
gc::TimeSpan test2 = l.setUSecs(-98744);
|
||||
BOOST_WARN_EQUAL(test2.microseconds(), -98744);
|
||||
|
||||
gc::TimeSpan test3 = l.setUSecs(-98);
|
||||
BOOST_WARN_EQUAL(isClose(test3,0, -98), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(secAndMicro) {
|
||||
gc::TimeSpan ts(2.000002);
|
||||
BOOST_CHECK(ts.seconds() == 2 && ts.microseconds() == 2);
|
||||
|
||||
gc::TimeSpan h(4.000009);
|
||||
BOOST_CHECK(h.seconds() == 4 && h.microseconds() == 9);
|
||||
|
||||
gc::TimeSpan t(0.000004);
|
||||
BOOST_CHECK(t.seconds() == 0 && t.microseconds() == 4);
|
||||
|
||||
gc::TimeSpan k(0.000000);
|
||||
BOOST_CHECK(k.seconds() == 0 && k.microseconds() == 0);
|
||||
|
||||
gc::TimeSpan m(-8.123456);
|
||||
long sec = -8;
|
||||
long micro = -123456;
|
||||
BOOST_WARN_EQUAL(isClose(m,sec, micro,20), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(absolute) {
|
||||
gc::TimeSpan k(-2.567);
|
||||
gc::TimeSpan result = k.abs();
|
||||
long sec = result.seconds();
|
||||
long micro = result.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result,2, 567000,20), true);
|
||||
|
||||
gc::TimeSpan m(-2, -5);
|
||||
gc::TimeSpan n(2, 5);
|
||||
BOOST_CHECK_EQUAL(m.abs(), n.abs());
|
||||
BOOST_CHECK_EQUAL(m.abs(), n);
|
||||
|
||||
gc::TimeSpan i(600, -700000);
|
||||
result = i.abs();
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 600);
|
||||
BOOST_CHECK_EQUAL(result.microseconds(),700000);
|
||||
|
||||
gc::TimeSpan r(200, -5);
|
||||
gc::TimeSpan s(200.000005);
|
||||
gc::TimeSpan absR = r.abs();
|
||||
BOOST_CHECK_EQUAL(r.abs(), s.abs());
|
||||
BOOST_CHECK_EQUAL(absR.seconds(), s.seconds());
|
||||
BOOST_CHECK_EQUAL(absR.microseconds(), s.microseconds());
|
||||
|
||||
gc::TimeSpan l = (double)-1.000678;
|
||||
result = l.abs();
|
||||
sec = 1;
|
||||
micro = 678;
|
||||
BOOST_CHECK_EQUAL(isClose(result,sec, micro), true);
|
||||
|
||||
gc::TimeSpan h = -4;
|
||||
BOOST_CHECK_EQUAL(h.abs(), gc::TimeSpan(4));
|
||||
|
||||
gc::TimeSpan ts;
|
||||
BOOST_CHECK_EQUAL(ts.abs(), gc::TimeSpan(0.0));
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(length) {
|
||||
gc::TimeSpan k = 2.000002;
|
||||
BOOST_CHECK(k.length() == 2.000002);
|
||||
|
||||
gc::TimeSpan l = 1.000003;
|
||||
BOOST_CHECK(l.length() == 1.000003);
|
||||
|
||||
gc::TimeSpan h = 4.000009;
|
||||
BOOST_CHECK(h.length() == 4.000009);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(notEqual) {
|
||||
gc::TimeSpan k = 2.000002, l = 1.000003;
|
||||
BOOST_CHECK(k != l);
|
||||
|
||||
gc::TimeSpan ts, t = 0.000007;
|
||||
BOOST_CHECK(ts != t);
|
||||
|
||||
gc::TimeSpan h = 4.000009, j = 2845687.000004;
|
||||
BOOST_CHECK(h != j);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(assign) {
|
||||
gc::TimeSpan k = 2.000002;
|
||||
BOOST_CHECK(k == gc::TimeSpan(2.000002));
|
||||
|
||||
gc::TimeSpan t;
|
||||
BOOST_CHECK(t == gc::TimeSpan(0.0));
|
||||
|
||||
gc::TimeSpan h = 4.000009;
|
||||
BOOST_CHECK(h == gc::TimeSpan(4.000009));
|
||||
|
||||
gc::TimeSpan ts = 0.000004;
|
||||
BOOST_CHECK(ts == gc::TimeSpan(0.000004));
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(plus) {
|
||||
gc::TimeSpan k = 2.000002, l = 1.000003;
|
||||
BOOST_CHECK_EQUAL(k += l, gc::TimeSpan(3.000005));
|
||||
|
||||
gc::TimeSpan h = 4.000009, t;
|
||||
BOOST_CHECK_EQUAL(t += h, gc::TimeSpan(4.000009));
|
||||
|
||||
gc::TimeSpan ts = 0.000004, j = 80005.000004;
|
||||
BOOST_CHECK_EQUAL(ts += j, gc::TimeSpan(80005.000008));
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(minus) {
|
||||
gc::TimeSpan k = 2.000002, l = 1.000003;
|
||||
BOOST_CHECK_EQUAL(k -= l, gc::TimeSpan(0.999999));
|
||||
|
||||
gc::TimeSpan t, j = 6897.098772;
|
||||
BOOST_CHECK_EQUAL(j -= t, gc::TimeSpan(6897.098772));
|
||||
|
||||
gc::TimeSpan h = 4.000009, ts = 0.000004;
|
||||
BOOST_CHECK_EQUAL(h -= ts, gc::TimeSpan(4.000005));
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(lower) {
|
||||
gc::TimeSpan k = 2.000002, t;
|
||||
BOOST_CHECK_EQUAL(t < k, true);
|
||||
|
||||
gc::TimeSpan h = 4.000009, j = 2.897665;
|
||||
BOOST_CHECK_EQUAL(j < h, true);
|
||||
|
||||
gc::TimeSpan ts = 0.000004, z = 7893648.987645;
|
||||
BOOST_CHECK_EQUAL(z < ts, false);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(greater) {
|
||||
gc::TimeSpan k = 2.000002, t;
|
||||
BOOST_CHECK_EQUAL(k > t, true);
|
||||
|
||||
gc::TimeSpan h = 4.000009, j = 3.909888;
|
||||
BOOST_CHECK_EQUAL(h > j, true);
|
||||
|
||||
gc::TimeSpan ts = 0.000004, g = 0.000001;
|
||||
BOOST_CHECK_EQUAL(g > ts, false);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(lowerEqual) {
|
||||
gc::TimeSpan k = 2.000002;
|
||||
BOOST_CHECK_EQUAL(k <= gc::TimeSpan(2.000002), true);
|
||||
|
||||
gc::TimeSpan t;
|
||||
BOOST_CHECK_EQUAL(t <= gc::TimeSpan(2), true);
|
||||
|
||||
gc::TimeSpan h = 4.000009;
|
||||
BOOST_CHECK_EQUAL(h <= gc::TimeSpan(2), false);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(greaterEqual) {
|
||||
gc::TimeSpan h = 4.000009;
|
||||
BOOST_CHECK_EQUAL(h >= gc::TimeSpan(2), true);
|
||||
|
||||
gc::TimeSpan ts = 0.000004;
|
||||
BOOST_CHECK_EQUAL(ts >= gc::TimeSpan(0.000001), true);
|
||||
|
||||
gc::TimeSpan k = 2.000002;
|
||||
BOOST_CHECK_EQUAL(k >= gc::TimeSpan(2.000002), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(inDouble) {
|
||||
gc::TimeSpan k = 6.000003;
|
||||
double kd = k.operator double();
|
||||
BOOST_CHECK_EQUAL(kd, 6.000003);
|
||||
|
||||
gc::TimeSpan t = 0.000008;
|
||||
double td = t.operator double();
|
||||
BOOST_CHECK_EQUAL(td, 0.000008);
|
||||
|
||||
gc::TimeSpan ts = 2.000004;
|
||||
double tsd = ts.operator double();
|
||||
BOOST_CHECK_EQUAL(tsd, 2.000004);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(toTimeval) {
|
||||
const timeval tv = gc::Time(6.000003);
|
||||
gc::TimeSpan k = 6.000003;
|
||||
timeval kv = k.operator const timeval &();
|
||||
BOOST_CHECK_EQUAL(kv.tv_sec, tv.tv_sec);
|
||||
BOOST_CHECK_EQUAL(kv.tv_usec, tv.tv_usec);
|
||||
|
||||
const timeval ti = gc::Time(0.000008);
|
||||
gc::TimeSpan t = 0.000008;
|
||||
timeval tvi = t.operator const timeval &();
|
||||
BOOST_CHECK_EQUAL(tvi.tv_sec, ti.tv_sec);
|
||||
BOOST_CHECK_EQUAL(tvi.tv_usec, ti.tv_usec);
|
||||
|
||||
const timeval tl = gc::Time(2.000004);
|
||||
gc::TimeSpan ts = 2.000004;
|
||||
timeval tsv = ts.operator const timeval &();
|
||||
BOOST_CHECK_EQUAL(tsv.tv_sec, tl.tv_sec);
|
||||
BOOST_CHECK_EQUAL(tsv.tv_usec, tl.tv_usec);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(fromString) {
|
||||
gc::Time time = gc::Time::FromString("2019-01-01 10:01:59", "%F %T");
|
||||
|
||||
BOOST_CHECK(time.valid());
|
||||
|
||||
int year, month, day, hour, min, sec;
|
||||
BOOST_CHECK(time.get(&year, &month, &day, &hour, &min, &sec));
|
||||
BOOST_CHECK_EQUAL(year, 2019);
|
||||
BOOST_CHECK_EQUAL(month, 1);
|
||||
BOOST_CHECK_EQUAL(day, 1);
|
||||
BOOST_CHECK_EQUAL(hour, 10);
|
||||
BOOST_CHECK_EQUAL(min, 01);
|
||||
BOOST_CHECK_EQUAL(sec, 59);
|
||||
|
||||
// Buffer overflow test
|
||||
std::string str;
|
||||
str.resize(1024);
|
||||
time = gc::Time::FromString(str.c_str(), "%F %T");
|
||||
BOOST_CHECK(!time.valid());
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
511
libs/gempa/caps/test/datetime_time.cpp
Normal file
511
libs/gempa/caps/test/datetime_time.cpp
Normal file
@ -0,0 +1,511 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
|
||||
namespace gc = Gempa::CAPS;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_datetime_time)
|
||||
|
||||
|
||||
bool isClose(gc::TimeSpan time, long sec, long micro, int offset = 1) {
|
||||
long microSeconds = time.microseconds();
|
||||
|
||||
long secDiff = time.seconds() - sec;
|
||||
if ( secDiff > 0 )
|
||||
microSeconds += secDiff * 1000000;
|
||||
else if ( secDiff < 0 )
|
||||
micro += abs(secDiff) * 1000000;
|
||||
|
||||
if ( abs(microSeconds - micro) <= offset )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
BOOST_AUTO_TEST_CASE(construction) {
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
gc::Time time;
|
||||
BOOST_CHECK(time == gc::Time(0.0));
|
||||
|
||||
// long
|
||||
gc::Time tPositive(200, 600);
|
||||
BOOST_CHECK(tPositive == gc::Time(200.000600));
|
||||
|
||||
gc::Time tNegativeUsec(3000, -789);
|
||||
BOOST_WARN_EQUAL(tNegativeUsec.seconds(), 3000);
|
||||
BOOST_WARN_EQUAL(tNegativeUsec.microseconds(), -789);
|
||||
|
||||
gc::Time tNegativeSec(-12, 12345);
|
||||
BOOST_WARN_EQUAL(tNegativeSec.seconds(), -12);
|
||||
BOOST_WARN_EQUAL(tNegativeSec.microseconds(), 12345);
|
||||
|
||||
gc::Time tNegative(-15,-9876);
|
||||
BOOST_WARN_EQUAL(tNegative.seconds(), -15);
|
||||
BOOST_WARN_EQUAL(tNegative.microseconds(), -9876);
|
||||
|
||||
// TimeSpan
|
||||
gc::Time tsPositive(gc::TimeSpan(5.345));
|
||||
BOOST_WARN_EQUAL(tsPositive.seconds(), 5);
|
||||
BOOST_WARN_EQUAL(tsPositive.microseconds(), 345000);
|
||||
|
||||
// timeval
|
||||
timeval number;
|
||||
number.tv_sec = 150;
|
||||
number.tv_usec = 6000;
|
||||
gc::Time tvPositive(number);
|
||||
BOOST_WARN_EQUAL(tvPositive.seconds(), 150);
|
||||
BOOST_WARN_EQUAL(tvPositive.microseconds(), 6000);
|
||||
|
||||
number.tv_sec = -150;
|
||||
number.tv_usec = 9000;
|
||||
gc::Time tvNegativeSec(number);
|
||||
BOOST_WARN_EQUAL(tvNegativeSec.seconds(), -150);
|
||||
BOOST_WARN_EQUAL(tvNegativeSec.microseconds(),9000);
|
||||
|
||||
number.tv_sec = 4000;
|
||||
number.tv_usec = -98876;
|
||||
gc::Time tvNegativeUsec(number);
|
||||
BOOST_WARN_EQUAL(tvNegativeUsec.seconds(), 4000);
|
||||
BOOST_WARN_EQUAL(tvNegativeUsec.microseconds(), -98876);
|
||||
|
||||
number.tv_sec = -9877;
|
||||
number.tv_usec = -874547;
|
||||
gc::Time tvNegative(number);
|
||||
BOOST_WARN_EQUAL(tvNegative.seconds(), -9877);
|
||||
BOOST_WARN_EQUAL(tvNegative.microseconds(), -874547);
|
||||
|
||||
// double
|
||||
double val = 5678.9864;
|
||||
gc::Time tdPositive(val);
|
||||
BOOST_WARN_EQUAL(tdPositive.seconds(), 5678);
|
||||
BOOST_CHECK_EQUAL(tdPositive.microseconds(), 986400);
|
||||
|
||||
val = -89765.745377;
|
||||
gc::Time tdNegative(val);
|
||||
BOOST_WARN_EQUAL(isClose(tdNegative, -89765, -745377), true);
|
||||
|
||||
// pointer
|
||||
timeval pointer;
|
||||
pointer.tv_sec = 76656;
|
||||
pointer.tv_usec = 8900;
|
||||
|
||||
gc::Time tpPositive(&pointer);
|
||||
BOOST_WARN_EQUAL(tpPositive.seconds(), 76656);
|
||||
BOOST_WARN_EQUAL(tpPositive.microseconds(), 8900);
|
||||
|
||||
pointer.tv_sec = -76656;
|
||||
pointer.tv_usec = 8900;
|
||||
gc::Time tpNegativeSec(&pointer);
|
||||
BOOST_WARN_EQUAL(tpNegativeSec.seconds(), -76656);
|
||||
BOOST_WARN_EQUAL(tpNegativeSec.microseconds(), 8900);
|
||||
|
||||
pointer.tv_sec = 98744;
|
||||
pointer.tv_usec = -8965;
|
||||
gc::Time tpNegativeUsec(&pointer);
|
||||
BOOST_WARN_EQUAL(tpNegativeUsec.seconds(), 98744);
|
||||
BOOST_WARN_EQUAL(tpNegativeUsec.microseconds(), -8965);
|
||||
|
||||
pointer.tv_sec = -44;
|
||||
pointer.tv_usec = -895;
|
||||
gc::Time tpNegative(&pointer);
|
||||
BOOST_WARN_EQUAL(tpNegative.seconds(), -44);
|
||||
BOOST_WARN_EQUAL(tpNegative.microseconds(), -895);
|
||||
|
||||
// copy
|
||||
gc::Time copyPositive(gc::Time(758.9975));
|
||||
BOOST_CHECK_EQUAL(copyPositive.seconds(), 758);
|
||||
BOOST_CHECK_EQUAL(copyPositive.microseconds(), 997500);
|
||||
|
||||
gc::Time copyNegative(gc::Time(-877.963));
|
||||
BOOST_WARN_EQUAL(isClose(copyNegative, -877, -963000), true);
|
||||
|
||||
// date
|
||||
gc::Time date(1971,1,3,1,1,4,6544);
|
||||
double dayInSeconds = 86400;
|
||||
double yearInSeconds = 31536000;
|
||||
BOOST_WARN_CLOSE(double(date), dayInSeconds*2 + yearInSeconds,0.3);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(addition) {
|
||||
gc::Time a(7,5);
|
||||
gc::TimeSpan b = 9.000004;
|
||||
gc::TimeSpan result = a + b;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(), 9);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 16);
|
||||
|
||||
gc::Time c(7,5);
|
||||
gc::TimeSpan d = -3.000004;
|
||||
result = c + d;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(), 2);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 4);
|
||||
|
||||
gc::Time e(-7,5);
|
||||
gc::TimeSpan f = 9.000004;
|
||||
result = e + f;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(),9);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 2);
|
||||
|
||||
gc::Time g(900,789);
|
||||
gc::TimeSpan h;
|
||||
result = h += g;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(),789);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 900);
|
||||
|
||||
gc::Time i(455, -355);
|
||||
gc::TimeSpan j = 80.000444;
|
||||
i += j;
|
||||
BOOST_CHECK_EQUAL(i.microseconds(),89);
|
||||
BOOST_CHECK_EQUAL(i.seconds(), 535);
|
||||
|
||||
gc::Time k(-899, 22255);
|
||||
gc::TimeSpan l = 773.992;
|
||||
l += k;
|
||||
BOOST_WARN_EQUAL(l.seconds(), -125);
|
||||
BOOST_WARN_EQUAL(l.microseconds(), 14255);
|
||||
|
||||
gc::Time m(500, 987);
|
||||
gc::TimeSpan n(-30, 876);
|
||||
int result2 = m.microseconds() + n.microseconds();
|
||||
int result3 = m.seconds() + n.seconds();
|
||||
m += n;
|
||||
BOOST_WARN_EQUAL(m.microseconds(),result2);
|
||||
BOOST_WARN_EQUAL(m.seconds(),result3);
|
||||
|
||||
gc::Time o(-60, 47);
|
||||
gc::TimeSpan p(-44,5);
|
||||
long sec = o.seconds() + p.seconds();
|
||||
long micro = o.microseconds() + p.microseconds();
|
||||
o += p;
|
||||
BOOST_CHECK_EQUAL(isClose(o, sec, micro), true);
|
||||
|
||||
gc::Time q(9876, -6748);
|
||||
gc::TimeSpan r = -876.987;
|
||||
q += r;
|
||||
BOOST_WARN_EQUAL(q.microseconds(), 6253);
|
||||
BOOST_CHECK_EQUAL(q.seconds(),8999);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(subtraction) {
|
||||
gc::Time a(7,5);
|
||||
gc::TimeSpan b(9,000004);
|
||||
gc::TimeSpan result = a - b;
|
||||
long sec = a.seconds() - b.seconds();
|
||||
long micro = a.microseconds() - b.microseconds();
|
||||
BOOST_WARN_EQUAL(isClose(result, sec, micro),true);
|
||||
|
||||
gc::Time c(7,5);
|
||||
gc::TimeSpan d = -3.000004;
|
||||
result = c - d;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(), 8);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 10);
|
||||
|
||||
gc::Time e(-7,5);
|
||||
gc::TimeSpan f(9,000004);
|
||||
result = e - f;
|
||||
sec = e.seconds() - f.seconds();
|
||||
micro = e.microseconds() -f.microseconds();
|
||||
BOOST_WARN_EQUAL(isClose(result, sec, micro),true);
|
||||
|
||||
gc::Time g(900,789);
|
||||
gc::TimeSpan h;
|
||||
sec = h.seconds() - g.seconds();
|
||||
micro = h.microseconds() - g.microseconds();
|
||||
h -= g;
|
||||
BOOST_CHECK_EQUAL(isClose(h, sec, micro), true);
|
||||
|
||||
gc::Time i(455, -355);
|
||||
gc::TimeSpan j(80, 444);
|
||||
sec = i.seconds() - j.seconds();
|
||||
micro = i.microseconds() - j.microseconds();
|
||||
i -= j;
|
||||
BOOST_CHECK_EQUAL(isClose(i, sec, micro), true);
|
||||
|
||||
gc::Time k(-899, 22255);
|
||||
gc::TimeSpan l(773, 992);
|
||||
sec = l.seconds() - k.seconds();
|
||||
micro = l.microseconds() - k.microseconds();
|
||||
l -= k;
|
||||
BOOST_CHECK_EQUAL(isClose(l, sec, micro), true);
|
||||
|
||||
gc::Time m(500,987);
|
||||
gc::TimeSpan n = -30.876;
|
||||
m -= n;
|
||||
BOOST_CHECK_EQUAL(m.microseconds(),876986);
|
||||
BOOST_CHECK_EQUAL(m.seconds(), 530);
|
||||
|
||||
gc::Time o(-60, 47);
|
||||
gc::TimeSpan p = -44.05;
|
||||
sec = o.seconds() - p.seconds();
|
||||
micro = o.microseconds() - p.microseconds();
|
||||
o -= p;
|
||||
BOOST_CHECK_EQUAL(isClose(o, sec, micro), true);
|
||||
|
||||
gc::Time q(9876, -6748);
|
||||
gc::TimeSpan r = -876.987;
|
||||
sec = q.seconds() -r.seconds();
|
||||
micro = q.microseconds() - r.microseconds();
|
||||
q -= r;
|
||||
BOOST_CHECK_EQUAL(isClose(q, sec, micro), true);
|
||||
|
||||
gc::Time s(50, 778), t(4, 221);
|
||||
result = s - t;
|
||||
BOOST_CHECK_EQUAL(result.microseconds(), 557);
|
||||
BOOST_CHECK_EQUAL(result.seconds(), 46);
|
||||
|
||||
gc::Time u(-30,0),v(60,66);
|
||||
result = u - v;
|
||||
sec = u.seconds() -v.seconds();
|
||||
micro = u.microseconds() - v.microseconds();
|
||||
BOOST_CHECK_EQUAL(isClose(result, sec, micro), true);
|
||||
|
||||
gc::Time w(798, -444),x(6, 0321);
|
||||
sec = w.seconds() - x.seconds();
|
||||
micro = w.microseconds() - x.microseconds();
|
||||
result = w - x;
|
||||
BOOST_CHECK_EQUAL(isClose(result, sec, micro), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(setAndGet) {
|
||||
gc::Time date;
|
||||
gc::TimeSpan oneDay (86400); // one day in seconds
|
||||
gc::TimeSpan oneYear (31536000); // one year in seconds
|
||||
gc::TimeSpan toNextYear (26524800); // seconds to the next year
|
||||
int year = 1970, month = 8, day = 5,h = 7,min = 50,sec = 33,uSec= 80;
|
||||
date.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(year == 1970);
|
||||
BOOST_CHECK(month == 8);
|
||||
BOOST_CHECK(day == 5);
|
||||
BOOST_CHECK(h == 7);
|
||||
BOOST_CHECK(min = 50);
|
||||
BOOST_CHECK(sec = 33);
|
||||
BOOST_CHECK(uSec = 80);
|
||||
|
||||
date -= oneYear;
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(year == 1969);
|
||||
BOOST_CHECK(month == 8);
|
||||
BOOST_CHECK_EQUAL(day , 5);
|
||||
|
||||
year = 2017, month = 2, day = 28;
|
||||
date.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(year == 2017);
|
||||
BOOST_CHECK(month == 2);
|
||||
BOOST_CHECK(day == 28);
|
||||
|
||||
date += oneDay;
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(month , 3);
|
||||
BOOST_CHECK_EQUAL(day , 1);
|
||||
|
||||
year = 2018, month = 2, day = 28;
|
||||
date.set(year,month,day,h,min,sec,uSec);
|
||||
date += oneDay;
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(month , 3);
|
||||
BOOST_CHECK_EQUAL(day, 1);
|
||||
|
||||
date += oneYear;
|
||||
BOOST_CHECK(date.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(year , 2019);
|
||||
BOOST_CHECK_EQUAL(day, 1);
|
||||
BOOST_CHECK_EQUAL(month , 3);
|
||||
|
||||
gc::Time leapYear;
|
||||
year = 1956, month = 2, day = 28;
|
||||
leapYear.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(leapYear.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(year == 1956);
|
||||
BOOST_CHECK(month == 2);
|
||||
BOOST_CHECK(day == 28);
|
||||
|
||||
leapYear += oneDay;
|
||||
BOOST_CHECK(leapYear.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(month == 2);
|
||||
BOOST_CHECK(day == 29);
|
||||
|
||||
leapYear += oneDay;
|
||||
BOOST_CHECK(leapYear.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(month == 3);
|
||||
BOOST_CHECK(day == 1);
|
||||
|
||||
gc::Time time;
|
||||
year = 2011, month = 2, day = 28;
|
||||
int yday ;
|
||||
time.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(time.get2(&year,&yday,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK(year == 2011);
|
||||
BOOST_CHECK_EQUAL(yday , 58);
|
||||
|
||||
time += toNextYear;
|
||||
BOOST_CHECK(time.get2(&year,&yday,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(year , 2012);
|
||||
BOOST_CHECK_EQUAL(yday , 0);
|
||||
|
||||
year = 1964, month = 2, day = 29;
|
||||
leapYear.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(leapYear.get2(&year,&yday,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(yday , 59);
|
||||
|
||||
leapYear += toNextYear;
|
||||
BOOST_CHECK(leapYear.get2(&year,&yday,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(year, 1965);
|
||||
BOOST_CHECK_EQUAL(yday , 0);
|
||||
|
||||
gc::Time before1900;
|
||||
day = 28, month = 2, year = 1900;
|
||||
before1900.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(year , 1900);
|
||||
BOOST_CHECK_EQUAL(day , 28);
|
||||
BOOST_CHECK_EQUAL(month, 2);
|
||||
|
||||
gc::Time pure;
|
||||
pure.get(&year,&month,&day,&h,&min,&sec,&uSec);
|
||||
BOOST_CHECK_EQUAL(year, 1970);
|
||||
|
||||
pure -= oneYear;
|
||||
pure.get(&year,&month,&day,&h,&min,&sec,&uSec);
|
||||
BOOST_CHECK_EQUAL(year, 1969);
|
||||
|
||||
day = 50, month = 4, year = 1566;
|
||||
before1900.set(year,month,day,h,min,sec,uSec);
|
||||
BOOST_CHECK(before1900.get(&year,&month,&day,&h,&min,&sec,&uSec) == true);
|
||||
BOOST_CHECK_EQUAL(year , 1566);
|
||||
BOOST_CHECK_EQUAL(day , 20);
|
||||
BOOST_CHECK_EQUAL(month, 5);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(localTime) {
|
||||
gc::Time local;
|
||||
local.set(1970,3,14,5,30,3,39);
|
||||
gc::Time time(local);
|
||||
BOOST_CHECK_EQUAL(double(local), double(time));
|
||||
std::string check1 = local.toString("%FT%T.%fZ");
|
||||
std::string check2 = "1970-03-14T05:30:03.000039Z";
|
||||
bool equal = boost::iequals(check1,check2);
|
||||
BOOST_CHECK_EQUAL(equal, true);
|
||||
gc::Time localtest = local.LocalTime();
|
||||
local = local.LocalTime();
|
||||
localtest.setUSecs(0);
|
||||
local.setUSecs(0);
|
||||
check1 = local.iso();
|
||||
check2 = localtest.iso();
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
local.set(1970,3,14,5,30,3,39);
|
||||
check1 = "1970-03-14T05:30:03.000039Z";
|
||||
check2 = local.toString("%FT%T.%fZ");
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
local.set(1981,9,14,5,30,3,39);
|
||||
check1 = "1981-09-14T05:30:03.000039Z";
|
||||
check2 = local.toString("%FT%T.%fZ");
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
local.set(2014,3,14,5,30,3,39);
|
||||
check1 = "2014-03-14T05:30:03.000039Z";
|
||||
check2 = local.toString("%FT%T.%fZ");
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
local.set(2000,8,14,5,30,3,39);
|
||||
check1 = local.toString("%FT%T.%fZ");
|
||||
check2 = "2000-08-14T05:30:03.000039Z";
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
// before 1970
|
||||
gc::Time before1970;
|
||||
before1970.set(1950,6,4,15,8,66,11);
|
||||
gc::Time t(before1970);
|
||||
gc::Time time1 = local.LocalTime();
|
||||
time1.setUSecs(0);
|
||||
gc::Time time2 = before1970.LocalTime();
|
||||
time2.setUSecs(0);
|
||||
check1 = time1.toString("%FT%T.%fZ");
|
||||
check2 = time2.toString("%FT%T.%fZ");
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
before1970.set(1914,9,4,7,8,66,11);
|
||||
check1 = "1914-09-04T07:09:06.000011Z";
|
||||
check2 = before1970.toString("%FT%T.%fZ");
|
||||
BOOST_CHECK_EQUAL(check1, check2);
|
||||
|
||||
gc::Time yearDay = yearDay.FromYearDay(1971, 3);
|
||||
double dayInSeconds = 86400;
|
||||
double yearInSeconds = 31536000;
|
||||
BOOST_CHECK_EQUAL(double(yearDay),dayInSeconds*2 + yearInSeconds);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(validStrings) {
|
||||
|
||||
gc::Time date(2016,8,26,15,44,9,644);
|
||||
std::string test = date.toString("%FT%T.%fZ");
|
||||
std::string check = "2016-08-26T15:44:09.000644Z";
|
||||
bool equal = boost::iequals(test,check);
|
||||
BOOST_CHECK_EQUAL(equal, true);
|
||||
BOOST_CHECK(date.FromString(test.c_str(),"%FT%T.%fZ") == date);
|
||||
|
||||
BOOST_CHECK(test == date.iso());
|
||||
|
||||
BOOST_CHECK(date.fromString(test.c_str(),"%FT%T.%fZ") == true);
|
||||
|
||||
BOOST_CHECK_EQUAL(date.valid(), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
105
libs/gempa/caps/test/endianess.cpp
Normal file
105
libs/gempa/caps/test/endianess.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/endianess.h>
|
||||
#include <gempa/caps/rawpacket.cpp>
|
||||
|
||||
|
||||
namespace gce = Gempa::CAPS::Endianess;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_endianess)
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(converter) {
|
||||
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
// Check little_endian int16_t.
|
||||
const int16_t k16Value{0x0123};
|
||||
|
||||
if(gce::Current::LittleEndian) {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToLittleEndian(k16Value), k16Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(k16Value), k16Value);
|
||||
int16_t g16Value = gce::Converter::ToBigEndian(k16Value);
|
||||
BOOST_CHECK_EQUAL(g16Value, 8961);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(g16Value), k16Value);
|
||||
gce::Converter::ToLittleEndian(&k16Value, 4);
|
||||
BOOST_CHECK_EQUAL(291, k16Value);
|
||||
}
|
||||
else {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToBigEndian(k16Value), k16Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(k16Value), k16Value);
|
||||
int16_t g16Value = gce::Converter::ToLittleEndian(k16Value);
|
||||
BOOST_CHECK_EQUAL(g16Value, 291);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(g16Value), k16Value);
|
||||
}
|
||||
|
||||
// Check little_endian int32_t.
|
||||
const int32_t k32Value{0x01234567};
|
||||
if(gce::Current::LittleEndian) {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToLittleEndian(k32Value), k32Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(k32Value), k32Value);
|
||||
int32_t g32Value = gce::Converter::ToBigEndian(k32Value);
|
||||
BOOST_CHECK_EQUAL(g32Value, 1732584193);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(g32Value), k32Value);
|
||||
gce::Converter::ToLittleEndian(&k32Value, 11);
|
||||
BOOST_CHECK_EQUAL(19088743, k32Value);
|
||||
}
|
||||
else {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToBigEndian(k32Value), k32Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(k32Value), k32Value);
|
||||
int16_t g32Value = gce::Converter::ToLittleEndian(k32Value);
|
||||
BOOST_CHECK_EQUAL(g32Value, 19088743);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(g32Value), k32Value);
|
||||
}
|
||||
|
||||
// Check little_endian int64_t.
|
||||
const int64_t k64Value{0x0123456789abcdef};
|
||||
if(gce::Current::LittleEndian) {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToLittleEndian(k64Value), k64Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(k64Value), k64Value);
|
||||
int64_t g32Value = gce::Converter::ToBigEndian(k64Value);
|
||||
BOOST_CHECK_EQUAL(g32Value, -1167088121787636991);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(g32Value), k64Value);
|
||||
gce::Converter::ToLittleEndian(&k64Value, 11);
|
||||
BOOST_CHECK_EQUAL(81985529216486895, k64Value);
|
||||
}
|
||||
else {
|
||||
BOOST_CHECK_EQUAL(gce::Converter::ToBigEndian(k64Value), k64Value);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromBigEndian(k64Value), k64Value);
|
||||
int16_t g64Value = gce::Converter::ToLittleEndian(k64Value);
|
||||
BOOST_CHECK_EQUAL(g64Value, 19088743);
|
||||
BOOST_CHECK_EQUAL(gce::Converter::FromLittleEndian(g64Value), k64Value);
|
||||
}
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
29
libs/gempa/caps/test/errors_find
Normal file
29
libs/gempa/caps/test/errors_find
Normal file
@ -0,0 +1,29 @@
|
||||
Bei utils.h
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Bei datetime.h
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Bei rawpacket.cpp
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Bei packet.h
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Bei endianess.h
|
||||
|
||||
1) Bei reader ist der Pointer nicht an der ersten stelle des Streams. Siehe endianess.cpp (test) Zeile 114 und folgende.
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Bei mseedpacket.cpp
|
||||
|
||||
1)
|
||||
---------------------------------------------------------------------------------------------------------------------------
|
227
libs/gempa/caps/test/mseedpacket.cpp
Normal file
227
libs/gempa/caps/test/mseedpacket.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include "test_utils.h"
|
||||
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/mseedpacket.h>
|
||||
#include <gempa/caps/rawpacket.cpp>
|
||||
#include <gempa/caps/utils.h>
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <streambuf>
|
||||
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
using namespace std;
|
||||
using namespace Gempa::CAPS;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Record {
|
||||
Record() {
|
||||
vector<char> mseed;
|
||||
mseed.resize(512);
|
||||
|
||||
ifstream ifs("data/AM.RFE4F.00.SHZ.20180912.mseed");
|
||||
if( ifs.is_open() ) {
|
||||
ifs.read(mseed.data(), 512);
|
||||
testRec.setData(mseed.data(), 512);
|
||||
}
|
||||
else
|
||||
BOOST_TEST_MESSAGE("unable to open test data file.");
|
||||
}
|
||||
MSEEDDataRecord testRec;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_mseedpacket)
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(equal_functions) {
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
MSEEDDataRecord::Header header;
|
||||
MSEEDDataRecord::Header otherHeader;
|
||||
|
||||
header.setSamplingTime(fromString("2018-01-01 00:00:01"));
|
||||
header.samplingFrequencyNumerator = 30;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
BOOST_CHECK_EQUAL(header != otherHeader, true);
|
||||
|
||||
bool valid = header.compatible(otherHeader);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
otherHeader = header;
|
||||
valid = header.compatible(otherHeader);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
BOOST_CHECK_EQUAL(header != otherHeader, false);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(header_functions) {
|
||||
|
||||
MSEEDDataRecord::Header header;
|
||||
header.dataType = DT_INT64;
|
||||
header.samplingFrequencyNumerator = 1;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
|
||||
char buf[1024];
|
||||
arraybuf abuf(buf, 1024);
|
||||
BOOST_CHECK_EQUAL(header.put(abuf), true);
|
||||
|
||||
MSEEDDataRecord::Header otherHeader;
|
||||
BOOST_CHECK_EQUAL(otherHeader.get(abuf), true);
|
||||
BOOST_CHECK_EQUAL(otherHeader.samplingFrequencyNumerator, 1);
|
||||
BOOST_CHECK_EQUAL(otherHeader.samplingFrequencyDenominator, 1);
|
||||
BOOST_CHECK_EQUAL(otherHeader.dataType, DT_INT64);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(trim_function) {
|
||||
// MSEEDs records can not be trimmed -> always false
|
||||
MSEEDDataRecord testRec;
|
||||
BOOST_CHECK(!testRec.canTrim());
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(merge_function) {
|
||||
// MSEEDs records can not be merged -> always false
|
||||
MSEEDDataRecord testRec;
|
||||
BOOST_CHECK(!testRec.canMerge());
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(getter_and_setter, Record) {
|
||||
BOOST_CHECK_EQUAL("MSEED", testRec.formatName());
|
||||
|
||||
// get data size with and without header
|
||||
size_t sizeWithout = testRec.dataSize(false);
|
||||
BOOST_CHECK_EQUAL(sizeWithout, 512);
|
||||
|
||||
size_t sizeWith = testRec.dataSize(true);
|
||||
BOOST_CHECK_EQUAL(512, sizeWith);
|
||||
|
||||
// check start end end time
|
||||
BOOST_CHECK_EQUAL(testRec.startTime().iso(), "2018-09-12T10:55:56.751Z");
|
||||
BOOST_CHECK_EQUAL(testRec.endTime().iso(), "2018-09-12T10:56:03.511Z");
|
||||
|
||||
// check sampling frequency
|
||||
BOOST_CHECK_EQUAL(testRec.header()->samplingFrequencyNumerator, 50);
|
||||
BOOST_CHECK_EQUAL(testRec.header()->samplingFrequencyDenominator, 1);
|
||||
|
||||
// check packet type
|
||||
BOOST_CHECK_EQUAL(testRec.packetType(), MSEEDPacket);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(read_header_from_buffer, Record) {
|
||||
char data[512];
|
||||
|
||||
// write test record data into a buffer
|
||||
arraybuf abuf(data, 512);
|
||||
BOOST_CHECK(testRec.put(abuf, true));
|
||||
|
||||
Time startTime, endTime;
|
||||
MSEEDDataRecord::Header header;
|
||||
|
||||
// read start, end time and further information from buffer
|
||||
MSEEDDataRecord rec;
|
||||
rec.readMetaData(abuf, 512, header, startTime, endTime);
|
||||
|
||||
BOOST_CHECK_EQUAL(startTime.iso(), "2018-09-12T10:55:56.751Z");
|
||||
BOOST_CHECK_EQUAL(endTime.iso(), "2018-09-12T10:56:03.511Z");
|
||||
BOOST_CHECK_EQUAL(header.dataType, DT_INT32);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(read_data_from_file, Record) {
|
||||
ifstream ifs("data/AM.RFE4F.00.SHZ.20180912.mseed");
|
||||
BOOST_CHECK(ifs.is_open());
|
||||
|
||||
vector<char> buf;
|
||||
buf.resize(512);
|
||||
ifs.read(buf.data(), 512);
|
||||
|
||||
MSEEDDataRecord rec;
|
||||
rec.setData(buf.data(),512);
|
||||
|
||||
BOOST_CHECK_EQUAL(rec.header()->samplingFrequencyDenominator, 1);
|
||||
BOOST_CHECK_EQUAL(rec.header()->samplingFrequencyNumerator, 50);
|
||||
|
||||
TimeSpan timeSpan = samplesToTimeSpan(*rec.header(), 338);
|
||||
|
||||
// check that the record start time + all samples is equal to
|
||||
// the record end time
|
||||
Time endTime = rec.startTime() + timeSpan;
|
||||
BOOST_CHECK(endTime == rec.endTime());
|
||||
|
||||
// check both header are equal
|
||||
BOOST_CHECK_EQUAL(rec.header()->compatible(*testRec.header()), true);
|
||||
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
97
libs/gempa/caps/test/packet.cpp
Normal file
97
libs/gempa/caps/test/packet.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
#include <gempa/caps/rawpacket.cpp>
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
|
||||
namespace gc = Gempa::CAPS;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_packet)
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(equal_function) {
|
||||
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
gc::DataRecord::Header header;
|
||||
gc::DataRecord::Header other;
|
||||
gc::Time t(1004791084,45368);
|
||||
|
||||
BOOST_CHECK_NO_THROW(header.setSamplingTime(t));
|
||||
header.samplingFrequencyNumerator = 3;
|
||||
header.samplingFrequencyDenominator = 7;
|
||||
BOOST_CHECK_EQUAL(header != other, true);
|
||||
|
||||
bool valid = header.compatible(other);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
other = header;
|
||||
valid = header.compatible(other);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
BOOST_CHECK_EQUAL(header != other, false);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(getAndPut) {
|
||||
|
||||
gc::DataRecord::Header header;
|
||||
header.dataType = gc::DT_INT64;
|
||||
header.samplingFrequencyNumerator = 1;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
char buf[1024];
|
||||
gc::arraybuf abuf(buf, 1024);
|
||||
|
||||
BOOST_CHECK_EQUAL(header.samplingFrequencyNumerator, 1);
|
||||
BOOST_CHECK_EQUAL(header.samplingFrequencyDenominator, 1);
|
||||
BOOST_CHECK_EQUAL(header.dataType, gc::DT_INT64);
|
||||
bool valid = header.put(abuf);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
|
||||
gc::DataRecord::Header otherHeader;
|
||||
valid = otherHeader.get(abuf);
|
||||
BOOST_CHECK_EQUAL(valid,true);
|
||||
BOOST_CHECK_EQUAL(otherHeader.samplingFrequencyNumerator, 1);
|
||||
BOOST_CHECK_EQUAL(otherHeader.samplingFrequencyDenominator, 1);
|
||||
BOOST_CHECK_EQUAL(otherHeader.dataType, gc::DT_INT64);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
288
libs/gempa/caps/test/rawpacket.cpp
Normal file
288
libs/gempa/caps/test/rawpacket.cpp
Normal file
@ -0,0 +1,288 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265359
|
||||
#endif
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include "test_utils.h"
|
||||
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/rawpacket.cpp>
|
||||
#include <gempa/caps/mseedpacket.cpp>
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
#include <streambuf>
|
||||
|
||||
|
||||
|
||||
namespace gc = Gempa::CAPS;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Record {
|
||||
Record() {
|
||||
gc::DataRecord::Header header;
|
||||
header.setSamplingTime(fromString("2018-01-01 00:00:01"));
|
||||
header.dataType = gc::DT_INT32;
|
||||
header.samplingFrequencyNumerator = 1;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
|
||||
rdr.setHeader(header);
|
||||
}
|
||||
|
||||
gc::RawDataRecord rdr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T> void fillRecord(T *data, size_t len , gc::Time stime, int sample_microsecs,
|
||||
double amplitude, double period) {
|
||||
double periodScale = (2 * M_PI) / period;
|
||||
double x = (double)stime * periodScale;
|
||||
double xOffset = sample_microsecs * 1E-6 * periodScale;
|
||||
|
||||
for ( size_t i = 0; i < len; ++i ) {
|
||||
*data = amplitude * sin(x);
|
||||
++data;
|
||||
x += xOffset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_rawpacket)
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(getAndSet, Record) {
|
||||
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
vector<int> data;
|
||||
data.resize(256);
|
||||
rdr.setBuffer(data.data(), 256);
|
||||
|
||||
// Set data type and get size of it
|
||||
rdr.setDataType(gc::DT_FLOAT);
|
||||
BOOST_CHECK_EQUAL(sizeof(float), gc::sizeOf(rdr.header()->dataType));
|
||||
BOOST_CHECK_EQUAL("RAW/FLOAT", rdr.formatName());
|
||||
|
||||
rdr.setDataType(gc::DT_INT64);
|
||||
BOOST_CHECK_EQUAL(sizeof(int64_t), gc::sizeOf(rdr.header()->dataType));
|
||||
BOOST_CHECK_EQUAL("RAW/INT64", rdr.formatName());
|
||||
|
||||
// get data size with and without header
|
||||
size_t sizeWithout = rdr.dataSize(false);
|
||||
BOOST_CHECK_EQUAL(sizeWithout, 256);
|
||||
|
||||
size_t sizeWith = rdr.dataSize(true);
|
||||
BOOST_CHECK_EQUAL(sizeWithout + rdr.header()->dataSize(), sizeWith);
|
||||
|
||||
data.resize(65536);
|
||||
rdr.setBuffer(data.data(), 65536);
|
||||
sizeWithout = rdr.dataSize(false);
|
||||
BOOST_CHECK_EQUAL(sizeWithout, 65536);
|
||||
sizeWith = rdr.dataSize(true);
|
||||
BOOST_CHECK_EQUAL(sizeWithout + rdr.header()->dataSize(), sizeWith);
|
||||
|
||||
// Set new FrequencyNumerator and FrequencyDenominator
|
||||
rdr.setSamplingFrequency(100,1);
|
||||
BOOST_CHECK_EQUAL(rdr.header()->samplingFrequencyNumerator, 100);
|
||||
BOOST_CHECK_EQUAL(rdr.header()->samplingFrequencyDenominator, 1);
|
||||
|
||||
rdr.setSamplingFrequency(20,1);
|
||||
BOOST_CHECK_EQUAL(rdr.header()->samplingFrequencyNumerator, 20);
|
||||
BOOST_CHECK_EQUAL(rdr.header()->samplingFrequencyDenominator, 1);
|
||||
gc::Time t = gc::getEndTime(rdr.startTime(),20, *rdr.header());
|
||||
BOOST_CHECK_EQUAL(t.iso(), "2018-01-01T00:00:02.0000Z");
|
||||
|
||||
gc::DataRecord::Header otherHeader;
|
||||
BOOST_CHECK_NO_THROW(rdr.setHeader(otherHeader));
|
||||
BOOST_CHECK_EQUAL("RAW/UNKWN", rdr.formatName());
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(checkTime, Record) {
|
||||
|
||||
gc::Time t = gc::getEndTime(rdr.startTime(), 3, *rdr.header());
|
||||
|
||||
BOOST_CHECK_EQUAL(t.iso(), "2018-01-01T00:00:04.0000Z");
|
||||
|
||||
rdr.setStartTime(fromString("2018-02-01 00:00:03.0000"));
|
||||
vector<int> data;
|
||||
data.resize(1024);
|
||||
rdr.setBuffer(data.data(), 1024);
|
||||
t = gc::getEndTime(rdr.startTime(), 3, *rdr.header());
|
||||
|
||||
BOOST_CHECK_EQUAL(rdr.startTime().iso(), "2018-02-01T00:00:03.0000Z");
|
||||
BOOST_CHECK_EQUAL(t.iso(), "2018-02-01T00:00:06.0000Z");
|
||||
|
||||
rdr.setStartTime(fromString("1988-03-14 14:22:57.322"));
|
||||
t = gc::getEndTime(rdr.startTime(), 2, *rdr.header());
|
||||
|
||||
BOOST_CHECK_EQUAL(t.iso(), "1988-03-14T14:22:59.0000Z");
|
||||
|
||||
rdr.setStartTime(fromString("1970-01-01 00:00:00.0000"));
|
||||
rdr.setSamplingFrequency(20,1);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
|
||||
rdr.setDataType(gc::DT_FLOAT);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
|
||||
rdr.setDataType(gc::DT_DOUBLE);
|
||||
data.resize(2048);
|
||||
rdr.setBuffer(data.data(), 2048);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
|
||||
rdr.setDataType(gc::DT_INT64);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
|
||||
rdr.setDataType(gc::DT_INT8);
|
||||
data.resize(256);
|
||||
rdr.setBuffer(data.data(), 256);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
|
||||
rdr.setDataType(gc::DT_INT16);
|
||||
data.resize(512);
|
||||
rdr.setBuffer(data.data(),512);
|
||||
BOOST_CHECK_EQUAL(rdr.endTime().iso(), "1970-01-01T00:00:12.8Z");
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(trimTest, Record) {
|
||||
// Raw records are trimable -> always true
|
||||
BOOST_CHECK(rdr.canTrim());
|
||||
|
||||
const gc::Time endT = gc::getEndTime(rdr.startTime(), 1, *rdr.header());
|
||||
BOOST_CHECK_EQUAL(rdr.canTrim(), true);
|
||||
|
||||
bool trimTest = rdr.trim(rdr.startTime(), endT);
|
||||
BOOST_CHECK_EQUAL(trimTest, true);
|
||||
BOOST_CHECK_EQUAL(rdr.startTime().iso(), "2018-01-01T00:00:01.0000Z");
|
||||
BOOST_CHECK_EQUAL(endT.iso(), "2018-01-01T00:00:02.0000Z");
|
||||
|
||||
trimTest = rdr.trim(endT, rdr.startTime());
|
||||
BOOST_CHECK_EQUAL(trimTest, false);
|
||||
|
||||
rdr.setSamplingFrequency(0,0);
|
||||
trimTest = rdr.trim(rdr.startTime(), endT);
|
||||
BOOST_CHECK_EQUAL(trimTest, false);
|
||||
|
||||
BOOST_CHECK_EQUAL(rdr.canMerge(), true);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_FIXTURE_TEST_CASE(getAndPut, Record) {
|
||||
|
||||
char data[500000];
|
||||
uint64_t sample_microsecs = uint64_t(rdr.header()->samplingFrequencyDenominator) * 1000000 / rdr.header()->samplingFrequencyNumerator;
|
||||
fillRecord<float>((float*)&data, 1024, rdr.startTime(), (int)sample_microsecs, 4.2, 2.1);
|
||||
gc::arraybuf abuf(data, 1024);
|
||||
|
||||
vector<int> arr;
|
||||
arr.resize(1024);
|
||||
rdr.setBuffer(arr.data(), 1024);
|
||||
bool check = rdr.put(abuf, true);
|
||||
BOOST_CHECK_EQUAL(check, false);
|
||||
|
||||
// RS_PARTIAL = 2
|
||||
gc::RawDataRecord rdrOther;
|
||||
gc::DataRecord::ReadStatusCode ret = rdrOther.get(abuf, 1024, rdr.startTime(),rdr.endTime(), 10);
|
||||
BOOST_CHECK_EQUAL(ret, gc::DataRecord::RS_Partial);
|
||||
BOOST_CHECK_EQUAL(rdrOther.startTime() == rdr.startTime(), true);
|
||||
|
||||
// RS_COMPLETE = 1
|
||||
gc::arraybuf abuf2(data, 1024);
|
||||
check = rdrOther.put(abuf2, true);
|
||||
BOOST_CHECK_EQUAL(check, true);
|
||||
|
||||
gc::RawDataRecord rdrOther2;
|
||||
ret = rdrOther2.get(abuf2, 1024, rdrOther2.startTime(),rdrOther2.endTime(), 10);
|
||||
BOOST_CHECK_EQUAL(ret, gc::DataRecord::RS_Complete);
|
||||
|
||||
gc::DataRecord::Header header = *rdrOther2.header();
|
||||
gc::Time start = rdrOther2.startTime();
|
||||
gc::Time end = gc::getEndTime(rdrOther2.startTime(), 2, header);
|
||||
rdrOther2.readMetaData(abuf, 1024, header, start, end);
|
||||
BOOST_CHECK_EQUAL(rdrOther2.startTime().iso(), "2018-01-01T00:00:01.0000Z");
|
||||
BOOST_CHECK_EQUAL(rdrOther2.endTime().iso(), "2018-01-01T00:04:13.0000Z");
|
||||
|
||||
|
||||
// RS_BEFORE_TIME_WINDOW = 3
|
||||
gc::arraybuf abuf3(data, 1024);
|
||||
gc::RawDataRecord rdrBefore;
|
||||
header.setSamplingTime(fromString("2018-01-01 00:00:00"));
|
||||
header.dataType = gc::DT_INT32;
|
||||
header.samplingFrequencyNumerator = 1;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
rdrBefore.setHeader(header);
|
||||
check = rdrBefore.put(abuf3, true);
|
||||
BOOST_CHECK_EQUAL(check, true);
|
||||
|
||||
ret = rdrOther.get(abuf3, 1024, rdr.endTime(),rdr.startTime(), 10);
|
||||
BOOST_CHECK_EQUAL(ret, gc::DataRecord::RS_BeforeTimeWindow);
|
||||
|
||||
// RS_AFTER_TIME_WINDOW = 4
|
||||
gc::arraybuf abuf4(data, 1024);
|
||||
gc::RawDataRecord rdrAfter;
|
||||
header.setSamplingTime(fromString("2020-01-01 00:00:00"));
|
||||
header.dataType = gc::DT_INT32;
|
||||
header.samplingFrequencyNumerator = 1;
|
||||
header.samplingFrequencyDenominator = 1;
|
||||
rdrAfter.setHeader(header);
|
||||
check = rdrAfter.put(abuf4, true);
|
||||
BOOST_CHECK_EQUAL(check, true);
|
||||
|
||||
ret = rdrOther.get(abuf4, 1024, rdrBefore.startTime(),rdrBefore.endTime(), 10);
|
||||
BOOST_CHECK_EQUAL(ret, gc::DataRecord::RS_AfterTimeWindow);
|
||||
|
||||
// RS_ERROR = 0
|
||||
gc::RawDataRecord rdrError;
|
||||
ret = rdrError.get(abuf, 1248, rdr.startTime(),rdr.endTime(), 10);
|
||||
BOOST_CHECK_EQUAL(ret, gc::DataRecord::RS_Error);
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
10
libs/gempa/caps/test/test_utils.h
Normal file
10
libs/gempa/caps/test/test_utils.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __TEST_UTILS_H__
|
||||
#define __TEST_UTILS_H__
|
||||
|
||||
#include <gempa/caps/datetime.h>
|
||||
|
||||
Gempa::CAPS::Time fromString(const char *str) {
|
||||
return Gempa::CAPS::Time::FromString(str, "%F %T");
|
||||
}
|
||||
|
||||
#endif
|
289
libs/gempa/caps/test/utils.cpp
Normal file
289
libs/gempa/caps/test/utils.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 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. *
|
||||
* *
|
||||
* Author: Tracey Werner, Enrico Ellguth *
|
||||
* Email: tracey.werner@gempa.de, enrico.ellguth@gempa.de *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#define SEISCOMP_TEST_MODULE gempa
|
||||
#include <seiscomp3/unittest/unittests.h>
|
||||
|
||||
#include <gempa/caps/utils.h>
|
||||
|
||||
|
||||
namespace gc = Gempa::CAPS;
|
||||
namespace bu = boost::unit_test;
|
||||
|
||||
using namespace gc;
|
||||
using namespace std;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(gempa_common_caps_utils)
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_CASE(split_address_function) {
|
||||
bu::unit_test_log.set_threshold_level(bu::log_warnings);
|
||||
bu::unit_test_log.set_threshold_level(bu::log_messages);
|
||||
|
||||
string name;
|
||||
unsigned short port;
|
||||
int defaultPort = 18002;
|
||||
|
||||
string hostname = "localhost";
|
||||
bool valid = splitAddress(name, port, hostname,defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
BOOST_CHECK_EQUAL(hostname, "localhost");
|
||||
BOOST_CHECK_EQUAL(port, 18002);
|
||||
|
||||
hostname = "data.gempa.de:18000";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
BOOST_CHECK_EQUAL(name, "data.gempa.de");
|
||||
BOOST_CHECK_EQUAL(port, 18000);
|
||||
|
||||
hostname = "";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
|
||||
hostname = "localhost:abcde";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
// port range check. By definition allowed ports are 1 - 65535
|
||||
hostname = "localhost:0";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
hostname = "localhost:1";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, true);
|
||||
|
||||
hostname = "localhost:65536";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
hostname = "localhost:-1";
|
||||
valid = splitAddress(name, port, hostname, defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
|
||||
hostname = "localhost:";
|
||||
valid = splitAddress(name, port, hostname,defaultPort);
|
||||
BOOST_CHECK_EQUAL(valid, false);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BOOST_AUTO_TEST_CASE(trim_function) {
|
||||
// delete whitespaces before and after string
|
||||
string text = " This is a test text 1";
|
||||
BOOST_CHECK_EQUAL(trim(text), "This is a test text 1");
|
||||
|
||||
const char *str = text.c_str();
|
||||
int len = text.length();
|
||||
|
||||
const char *res = trim(str,len);
|
||||
BOOST_CHECK_EQUAL(string(res, len), "This is a test text 1");
|
||||
|
||||
text = " This is a test text 2";
|
||||
BOOST_CHECK_EQUAL(trim(text), "This is a test text 2");
|
||||
|
||||
str = text.c_str();
|
||||
len = text.length();
|
||||
res = trim(str,len);
|
||||
BOOST_CHECK_EQUAL(string(res,len), "This is a test text 2");
|
||||
|
||||
text = "This is a test text ";
|
||||
BOOST_CHECK_EQUAL(trim(text), "This is a test text");
|
||||
|
||||
str = text.c_str();
|
||||
len = text.length();
|
||||
res = trim(str,len);
|
||||
BOOST_CHECK_EQUAL(string(res,len), "This is a test text");
|
||||
|
||||
text = " Hello World ";
|
||||
BOOST_CHECK_EQUAL(trim(text), "Hello World");
|
||||
|
||||
str = text.c_str();
|
||||
len = text.length();
|
||||
res = trim(str,len);
|
||||
BOOST_CHECK_EQUAL(string(res,len), "Hello World");
|
||||
|
||||
text = " Hello : world";
|
||||
BOOST_CHECK_EQUAL(trim(text), "Hello : world");
|
||||
|
||||
str = text.c_str();
|
||||
len = text.length();
|
||||
res = trim(str, len);
|
||||
BOOST_CHECK_EQUAL(string(res,len), "Hello : world");
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BOOST_AUTO_TEST_CASE(converts_time) {
|
||||
// Converts time object to timestamp
|
||||
const Time t(2014,4,14,6,34,39,7389);
|
||||
TimeStamp ts;
|
||||
timeToTimestamp(ts, t);
|
||||
BOOST_CHECK_EQUAL(ts.year, 2014);
|
||||
BOOST_CHECK_EQUAL(ts.second, 39);
|
||||
|
||||
const Time t2(0,0);
|
||||
timeToTimestamp(ts,t2);
|
||||
BOOST_CHECK_EQUAL(ts.year, 1970);
|
||||
BOOST_CHECK_EQUAL(ts.hour, 0);
|
||||
BOOST_CHECK_EQUAL(ts.yday, 0);
|
||||
|
||||
// Converts timestamp to time object
|
||||
Time t3 = timestampToTime(ts);
|
||||
int year, yday, hour, min, sec, usec;
|
||||
t3.get2(&year, &yday, &hour, &min, &sec, &usec);
|
||||
BOOST_CHECK_EQUAL(year, 1970);
|
||||
BOOST_CHECK_EQUAL(min, 0);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BOOST_AUTO_TEST_CASE(samples) {
|
||||
DataRecord::Header head;
|
||||
head.samplingFrequencyNumerator = 1;
|
||||
head.samplingFrequencyDenominator = 1;
|
||||
|
||||
// Convert samples to timespan
|
||||
TimeSpan ts = samplesToTimeSpan(head, 2);
|
||||
BOOST_CHECK_EQUAL(ts, TimeSpan(2,0));
|
||||
|
||||
// Convert time span to samples
|
||||
BOOST_CHECK_EQUAL(timeSpanToSamples(head, ts), 2);
|
||||
|
||||
// Convert time span to samples and round up/down
|
||||
BOOST_CHECK_EQUAL(timeSpanToSamplesCeil(head, TimeSpan(0, 500000)), 1);
|
||||
BOOST_CHECK_EQUAL(timeSpanToSamplesFloor(head, TimeSpan(0, 600000)), 0);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BOOST_AUTO_TEST_CASE(str_to_int) {
|
||||
// int tests
|
||||
|
||||
const char *str = "1352";
|
||||
int i;
|
||||
bool ret = str2int(i, str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
BOOST_CHECK_EQUAL(i, 1352);
|
||||
|
||||
str = "";
|
||||
ret = str2int(i, str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
|
||||
str = "478a2";
|
||||
ret = str2int(i, str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
|
||||
str = " 57721";
|
||||
ret = str2int(i, str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
BOOST_CHECK_EQUAL(i, 57721);
|
||||
|
||||
str = "4876 9742";
|
||||
ret = str2int(i, str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
BOOST_CHECK_EQUAL(i, 4876);
|
||||
|
||||
str = "-98256";
|
||||
ret = str2int(i,str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
BOOST_CHECK_EQUAL(i, -98256);
|
||||
|
||||
str = "-2147483649";
|
||||
ret = str2int(i,str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
|
||||
str = "2147483648";
|
||||
ret = str2int(i,str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
|
||||
// uint16_t tests
|
||||
|
||||
str = "1";
|
||||
uint16_t value;
|
||||
ret = str2int(value, str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
|
||||
str = "-1";
|
||||
ret = str2int(value, str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
|
||||
str = "65535";
|
||||
ret = str2int(value, str);
|
||||
BOOST_CHECK_EQUAL(ret, true);
|
||||
|
||||
str = "65536";
|
||||
ret = str2int(value, str);
|
||||
BOOST_CHECK_EQUAL(ret, false);
|
||||
}
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
|
||||
|
||||
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BOOST_AUTO_TEST_CASE(tokenize_function) {
|
||||
const char *text = "Hello, World!";
|
||||
int len = strlen(text), tok_len;
|
||||
|
||||
const char *tok = tokenize(text, ",", len, tok_len);
|
||||
BOOST_CHECK_EQUAL(string(tok, tok_len), "Hello");
|
||||
|
||||
tok = tokenize(text, ",", len, tok_len);
|
||||
BOOST_CHECK_EQUAL(string(tok,tok_len)," World!");
|
||||
|
||||
text = "This is a book";
|
||||
len = strlen(text);
|
||||
int tok_count = 0;
|
||||
while ( (tok = tokenize(text, ",", len, tok_len)) != NULL ) {
|
||||
if ( tok_count == 3 ) {
|
||||
BOOST_CHECK_EQUAL(string(tok, tok_len), "book");
|
||||
}
|
||||
++tok_count;
|
||||
}
|
||||
|
||||
text = "";
|
||||
len = strlen(text);
|
||||
tok = tokenize(text, ";", len,tok_len);
|
||||
BOOST_CHECK_EQUAL(string(tok,tok_len), "");
|
||||
}
|
||||
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
|
||||
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
377
libs/gempa/caps/utils.h
Normal file
377
libs/gempa/caps/utils.h
Normal file
@ -0,0 +1,377 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2014 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. *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef GEMPA_CAPS_UTILS_H
|
||||
#define GEMPA_CAPS_UTILS_H
|
||||
|
||||
#include <gempa/caps/packet.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace Gempa {
|
||||
namespace CAPS {
|
||||
|
||||
class arraybuf : public std::streambuf {
|
||||
public:
|
||||
typedef std::streambuf::pos_type pos_type;
|
||||
typedef std::streambuf::off_type off_type;
|
||||
|
||||
public:
|
||||
arraybuf(const char *buf, int len) {
|
||||
char *tmp = const_cast<char*>(buf);
|
||||
setp(tmp, tmp + len);
|
||||
setg(tmp, tmp, tmp + len);
|
||||
}
|
||||
|
||||
arraybuf(const std::string &buf) {
|
||||
if ( buf.size() > 0 ) {
|
||||
char *tmp = const_cast<char*>(&buf[0]);
|
||||
setg(tmp, tmp, tmp + buf.size());
|
||||
setp(tmp, tmp + buf.size());
|
||||
}
|
||||
}
|
||||
|
||||
void reset(const char *buf, int len) {
|
||||
char *tmp = const_cast<char*>(buf);
|
||||
setp(tmp, tmp + len);
|
||||
}
|
||||
|
||||
virtual pos_type seekoff(off_type ofs, std::ios_base::seekdir dir,
|
||||
std::ios_base::openmode mode) {
|
||||
if ( mode & std::ios_base::in ) {
|
||||
char *next;
|
||||
|
||||
switch ( dir ) {
|
||||
case std::ios_base::beg:
|
||||
next = eback() + ofs;
|
||||
break;
|
||||
case std::ios_base::cur:
|
||||
next = gptr() + ofs;
|
||||
break;
|
||||
case std::ios_base::end:
|
||||
next = egptr() + ofs;
|
||||
break;
|
||||
default:
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
if ( next > egptr() || next < eback() )
|
||||
return pos_type(off_type(-1));
|
||||
|
||||
gbump(next-gptr());
|
||||
}
|
||||
|
||||
if ( mode & std::ios_base::out ) {
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
|
||||
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode mode) {
|
||||
if ( mode & std::ios_base::in ) {
|
||||
char *next = eback() + pos;
|
||||
if ( next > egptr() )
|
||||
return pos_type(off_type(-1));
|
||||
gbump(next-gptr());
|
||||
}
|
||||
|
||||
if ( mode & std::ios_base::out ) {
|
||||
char *next = pbase() + pos;
|
||||
if ( next > epptr() ) {
|
||||
return pos_type(off_type(-1));
|
||||
}
|
||||
pbump(pos);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
virtual std::streamsize xsgetn(char* s, std::streamsize n) {
|
||||
char *next = gptr() + n;
|
||||
if ( next >= egptr() )
|
||||
n = n - (next - egptr());
|
||||
|
||||
if ( n == 0 ) return 0;
|
||||
|
||||
memcpy(s, gptr(), n);
|
||||
setg(eback(), next, egptr());
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
std::streampos tellg() {
|
||||
return gptr() - eback();
|
||||
}
|
||||
|
||||
std::streampos tellp() {
|
||||
return pptr() - pbase();
|
||||
}
|
||||
};
|
||||
|
||||
#define CHECK_STRING(data, str, len) \
|
||||
((len == sizeof(str)-1) && (strncasecmp(data, str, len) == 0))
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Splits an address string into hostname and port
|
||||
* @param host The host
|
||||
* @param port The port
|
||||
* @param address The address
|
||||
* @param default_port The default port which will be used
|
||||
* if the addrees contains no port
|
||||
* @return True, if the address is valid
|
||||
*/
|
||||
inline bool splitAddress(std::string &host, unsigned short &port,
|
||||
const std::string &address, unsigned short default_port) {
|
||||
size_t pos = address.find(':');
|
||||
if ( pos != std::string::npos ) {
|
||||
int p = -1;
|
||||
host = address.substr(0, pos);
|
||||
std::stringstream ss(address.substr(pos+1));
|
||||
ss >> p;
|
||||
if ( p > 0 && p <= 65535 )
|
||||
port = p;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
host = address;
|
||||
port = default_port;
|
||||
}
|
||||
|
||||
return !host.empty();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns substring that contains leftmost characters up to the delimiter
|
||||
*/
|
||||
inline const char *tokenize(const char *&str, const char *delim,
|
||||
int &len_source, int &len_tok) {
|
||||
len_tok = 0;
|
||||
for ( ; len_source; --len_source, ++str ) {
|
||||
// Hit first non delimiter?
|
||||
if ( strchr(delim, *str) == NULL ) {
|
||||
const char *tok = str;
|
||||
|
||||
++str; --len_source;
|
||||
len_tok = 1;
|
||||
|
||||
// Hit first delimiter?
|
||||
for ( ; len_source; --len_source, ++str, ++len_tok ) {
|
||||
if ( strchr(delim, *str) != NULL )
|
||||
break;
|
||||
}
|
||||
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes whitespaces from start and end of string
|
||||
*/
|
||||
inline const char *trim(const char *&str, int &len) {
|
||||
int i = len;
|
||||
while ( i > 0 ) {
|
||||
if ( isspace(*str) ) {
|
||||
++str;
|
||||
--len;
|
||||
}
|
||||
else
|
||||
break;
|
||||
++i;
|
||||
}
|
||||
|
||||
i = len-1;
|
||||
while ( i > 0 ) {
|
||||
if ( !isspace(str[i]) )
|
||||
break;
|
||||
|
||||
--len;
|
||||
--i;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
inline std::string trim(const std::string &s) {
|
||||
int l = (int)s.size();
|
||||
const char *str = &s[0];
|
||||
trim(str, l);
|
||||
return std::string(str, l);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Converts time object to timestamp
|
||||
*/
|
||||
inline void timeToTimestamp(TimeStamp &ts, const Time &t) {
|
||||
int year, yday, hour, min, sec, usec;
|
||||
t.get2(&year, &yday, &hour, &min, &sec, &usec);
|
||||
ts.year = year;
|
||||
ts.yday = yday;
|
||||
ts.hour = hour;
|
||||
ts.minute = min;
|
||||
ts.second = sec;
|
||||
ts.usec = usec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts string to integer value
|
||||
* @param v The integer value
|
||||
* @param s The input string
|
||||
* @param len The string length. If len is 0 the length
|
||||
* is calculated from string
|
||||
* @param base The numeric base
|
||||
* @return True on success
|
||||
*/
|
||||
template <typename T>
|
||||
bool str2int(T &v, char const *s, int len = 0, int base = 10) {
|
||||
// Return false in case of empty string
|
||||
if (*s == '\0' ) return false;
|
||||
|
||||
char *end;
|
||||
long l;
|
||||
errno = 0;
|
||||
l = strtol(s, &end, base);
|
||||
if ( errno == ERANGE ||
|
||||
l < std::numeric_limits<T>::min() ||
|
||||
l > std::numeric_limits<T>::max() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the string contains invalid chars
|
||||
if ( len == 0 ) len = strlen(s);
|
||||
|
||||
int bytes_read = end - s;
|
||||
if ( bytes_read < len && *end != ' ' ) return false;
|
||||
|
||||
v = static_cast<T>(l);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts string to integer value
|
||||
* @param v The integer value
|
||||
* @param s The input string
|
||||
* @param len The string length. If len is 0 the length
|
||||
* is calculated from string
|
||||
* @param base The numeric base
|
||||
* @return True on success
|
||||
*/
|
||||
inline bool str2int(int &v, char const *s, int len = 0, int base = 10) {
|
||||
return str2int<int>(v, s, len, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts string to unsigned int16 value
|
||||
* @param v The integer value
|
||||
* @param s The input string
|
||||
* @param len The string length. If len is 0 the length
|
||||
* is calculated from string
|
||||
* @param base The numeric base
|
||||
* @return True on success
|
||||
*/
|
||||
inline bool str2int(uint16_t &v, char const *s, int len = 0, int base = 10) {
|
||||
return str2int<uint16_t>(v, s, len, base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Converts timestamp to time object
|
||||
*/
|
||||
inline Time
|
||||
timestampToTime(const TimeStamp &ts) {
|
||||
Time t;
|
||||
t.set(ts.year, 1, 1, ts.hour, ts.minute, ts.second, ts.usec);
|
||||
// Add the day of the year in seconds
|
||||
t += TimeSpan(ts.yday*86400);
|
||||
return t;
|
||||
}
|
||||
|
||||
inline TimeSpan
|
||||
samplesToTimeSpan(const DataRecord::Header &head, int sampleCount) {
|
||||
int64_t ms = ((int64_t)(sampleCount)) * 1000000 * head.samplingFrequencyDenominator / head.samplingFrequencyNumerator;
|
||||
return TimeSpan(ms/1000000, ms%1000000);
|
||||
}
|
||||
|
||||
inline int
|
||||
timeSpanToSamples(const DataRecord::Header &head, const TimeSpan &span) {
|
||||
int64_t ms = ((int64_t)span.seconds())*1000000 + span.microseconds();
|
||||
return ms * head.samplingFrequencyNumerator / head.samplingFrequencyDenominator / 1000000;
|
||||
}
|
||||
|
||||
inline int
|
||||
timeSpanToSamplesCeil(const DataRecord::Header &head, const TimeSpan &span) {
|
||||
int64_t ms = ((int64_t)span.seconds())*1000000 + span.microseconds();
|
||||
return (ms * head.samplingFrequencyNumerator / head.samplingFrequencyDenominator + 999999) / 1000000;
|
||||
}
|
||||
|
||||
inline int
|
||||
timeSpanToSamplesFloor(const DataRecord::Header &head, const TimeSpan &span) {
|
||||
int64_t ms = ((int64_t)span.seconds())*1000000 + span.microseconds();
|
||||
return ms * head.samplingFrequencyNumerator / head.samplingFrequencyDenominator / 1000000;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
inline void usleep(__int64 usec)
|
||||
{
|
||||
HANDLE timer;
|
||||
LARGE_INTEGER ft;
|
||||
|
||||
ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time
|
||||
|
||||
timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
CloseHandle(timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline uint8_t dataTypeSize(DataType dt) {
|
||||
if ( dt == DT_INT8 )
|
||||
return sizeof(int8_t);
|
||||
if ( dt == DT_INT16 )
|
||||
return sizeof(int16_t);
|
||||
else if ( dt == DT_INT32 )
|
||||
return sizeof(int32_t);
|
||||
else if ( dt == DT_INT64 )
|
||||
return sizeof(DT_INT64);
|
||||
else if ( dt == DT_FLOAT )
|
||||
return sizeof(float);
|
||||
else if ( dt == DT_DOUBLE )
|
||||
return sizeof(double);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
32
libs/gempa/caps/version.h
Normal file
32
libs/gempa/caps/version.h
Normal file
@ -0,0 +1,32 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2018 by gempa GmbH
|
||||
*
|
||||
* Author: Stephan Herrnkind
|
||||
* Email: herrnkidn@gempa.de
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef __GEMPA_CAPS_VERSION_H__
|
||||
#define __GEMPA_CAPS_VERSION_H__
|
||||
|
||||
|
||||
/* #if (LIB_CAPS_VERSION >= LIB_CAPS_VERSION_CHECK(1, 0, 0)) */
|
||||
#define LIB_CAPS_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
|
||||
|
||||
#define LIB_CAPS_VERSION_MAJOR(v) (v >> 16)
|
||||
#define LIB_CAPS_VERSION_MINOR(v) ((v >> 8) & 0xff)
|
||||
#define LIB_CAPS_VERSION_PATCH(v) (v & 0xff)
|
||||
|
||||
/* LIB_CAPS_VERSION is (major << 16) + (minor << 8) + patch. */
|
||||
#define LIB_CAPS_VERSION 0x010000
|
||||
#define LIB_CAPS_VERSION_NAME "1.0.0"
|
||||
|
||||
/******************************************************************************
|
||||
API Changelog
|
||||
******************************************************************************
|
||||
"1.0.0" 0x010000
|
||||
- Initial version
|
||||
|
||||
*/
|
||||
|
||||
#endif // __GEMPA_CAPS_VERSION_H__
|
Reference in New Issue
Block a user