Initial commit based on common repo commit ffeb9c9b

master
Enrico Ellguth 3 years ago
commit 8b2a408e6f
No known key found for this signature in database
GPG Key ID: 1A47A728433D2185

13
.gitignore vendored

@ -0,0 +1,13 @@
/*.config
/*.creator
/*.files
/*.includes
/*.user
*.pyc
nbproject
.DS_Store
*bower*
CMakeCache.txt
CMakeFiles/
.idea/
.sass-cache/

@ -0,0 +1,26 @@
PROJECT(LIBCAPS)
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Modules)
FIND_PACKAGE(Boost REQUIRED)
FIND_PACKAGE(OpenSSL REQUIRED)
INCLUDE_DIRECTORIES(libs)
#ADD_DEFINITIONS("-DCAPS_FEATURES_ANY=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_MSEED=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_RAW=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_FIXED_RAW=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_RTCM2=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_BACKFILLING=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_JOURNAL=0")
OPTION(LIBCAPS_PYTHON_WRAPPER "Create Python wrappers" ON)
OPTION(LIBCAPS_EXAMPLES "Build and install example applications" OFF)
SUBDIRS(libs)
IF(LIBCAPS_EXAMPLES)
SUBDIRS(examples)
ENDIF()

@ -0,0 +1,341 @@
# - Find the Boost includes and libraries.
# The following variables are set if Boost is found. If Boost is not
# found, Boost_FOUND is set to false.
# Boost_FOUND - True when the Boost include directory is found.
# Boost_INCLUDE_DIRS - the path to where the boost include files are.
# Boost_LIBRARY_DIRS - The path to where the boost library files are.
# Boost_LIB_DIAGNOSTIC_DEFINITIONS - Only set if using Windows.
# Boost_<library>_FOUND - True if the Boost <library> is found.
# Boost_<library>_INCLUDE_DIRS - The include path for Boost <library>.
# Boost_<library>_LIBRARIES - The libraries to link to to use Boost <library>.
# Boost_LIBRARIES - The libraries to link to to use all Boost libraries.
#
# The following variables can be set to configure how Boost is found:
# Boost_LIB_PREFIX - Look for Boost libraries prefixed with this, e.g. "lib"
# Boost_LIB_SUFFIX - Look for Boost libraries ending with this, e.g. "vc80-mt"
# Boost_LIB_SUFFIX_DEBUG - As for Boost_LIB_SUFFIX but for debug builds, e.g. "vs80-mt-gd"
# ----------------------------------------------------------------------------
# If you have installed Boost in a non-standard location or you have
# just staged the boost files using bjam then you have three
# options. In the following comments, it is assumed that <Your Path>
# points to the root directory of the include directory of Boost. e.g
# If you have put boost in C:\development\Boost then <Your Path> is
# "C:/development/Boost" and in this directory there will be two
# directories called "include" and "lib".
# 1) After CMake runs, set Boost_INCLUDE_DIR to <Your Path>/include/boost<-version>
# 2) Use CMAKE_INCLUDE_PATH to set a path to <Your Path>/include. This will allow FIND_PATH()
# to locate Boost_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g.
# SET(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "<Your Path>/include")
# 3) Set an environment variable called ${BOOST_ROOT} that points to the root of where you have
# installed Boost, e.g. <Your Path>. It is assumed that there is at least a subdirectory called
# include in this path.
#
# Note:
# 1) If you are just using the boost headers, then you do not need to use
# Boost_LIBRARY_DIRS in your CMakeLists.txt file.
# 2) If Boost has not been installed, then when setting Boost_LIBRARY_DIRS
# the script will look for /lib first and, if this fails, then for /stage/lib.
#
# Usage:
# In your CMakeLists.txt file do something like this:
# ...
# # Boost
# FIND_PACKAGE(Boost)
# ...
# INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
# LINK_DIRECTORIES(${Boost_LIBRARY_DIRS})
#
# In Windows, we make the assumption that, if the Boost files are installed, the default directory
# will be C:\boost.
#
# TODO:
#
# 1) Automatically find the Boost library files and eliminate the need
# to use Link Directories.
#
IF(WIN32)
# In windows, automatic linking is performed, so you do not have to specify the libraries.
# If you are linking to a dynamic runtime, then you can choose to link to either a static or a
# dynamic Boost library, the default is to do a static link. You can alter this for a specific
# library "whatever" by defining BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to
# be linked dynamically. Alternatively you can force all Boost libraries to dynamic link by
# defining BOOST_ALL_DYN_LINK.
# This feature can be disabled for Boost library "whatever" by defining BOOST_WHATEVER_NO_LIB,
# or for all of Boost by defining BOOST_ALL_NO_LIB.
# If you want to observe which libraries are being linked against then defining
# BOOST_LIB_DIAGNOSTIC will cause the auto-linking code to emit a #pragma message each time
# a library is selected for linking.
SET(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC")
ENDIF(WIN32)
SET(BOOST_INCLUDE_PATH_DESCRIPTION "directory containing the boost include files. E.g /usr/local/include/boost-1_33_1 or c:\\boost\\include\\boost-1_33_1")
SET(BOOST_DIR_MESSAGE "Set the Boost_INCLUDE_DIR cmake cache entry to the ${BOOST_INCLUDE_PATH_DESCRIPTION}")
SET(BOOST_DIR_SEARCH $ENV{BOOST_ROOT})
IF(BOOST_DIR_SEARCH)
FILE(TO_CMAKE_PATH ${BOOST_DIR_SEARCH} BOOST_DIR_SEARCH)
SET(BOOST_DIR_SEARCH ${BOOST_DIR_SEARCH}/include)
ENDIF(BOOST_DIR_SEARCH)
IF(WIN32)
SET(BOOST_DIR_SEARCH
${BOOST_DIR_SEARCH}
C:/boost/include
D:/boost/include
)
ENDIF(WIN32)
# Add in some path suffixes. These will have to be updated whenever a new Boost version comes out.
SET(SUFFIX_FOR_PATH
boost-1_35_1
boost-1_35_0
boost-1_35
boost-1_34_1
boost-1_34_0
boost-1_34
boost-1_33_1
boost-1_33_0
)
#
# Look for an installation.
#
FIND_PATH(Boost_INCLUDE_DIR NAMES boost/config.hpp PATH_SUFFIXES ${SUFFIX_FOR_PATH} PATHS
# Look in other places.
${BOOST_DIR_SEARCH}
# Help the user find it if we cannot.
DOC "The ${BOOST_INCLUDE_PATH_DESCRIPTION}"
)
# Assume we didn't find it.
SET(Boost_FOUND 0)
# Now try to get the include and library path.
IF(Boost_INCLUDE_DIR)
# Look for the boost library path.
# Note that the user may not have installed any libraries
# so it is quite possible the Boost_LIBRARY_PATH may not exist.
SET(Boost_LIBRARY_DIR ${Boost_INCLUDE_DIR})
IF("${Boost_LIBRARY_DIR}" MATCHES "boost-[0-9]+")
GET_FILENAME_COMPONENT(Boost_LIBRARY_DIR ${Boost_LIBRARY_DIR} PATH)
ENDIF ("${Boost_LIBRARY_DIR}" MATCHES "boost-[0-9]+")
IF("${Boost_LIBRARY_DIR}" MATCHES "/include$")
# Strip off the trailing "/include" in the path.
GET_FILENAME_COMPONENT(Boost_LIBRARY_DIR ${Boost_LIBRARY_DIR} PATH)
ENDIF("${Boost_LIBRARY_DIR}" MATCHES "/include$")
IF(EXISTS "${Boost_LIBRARY_DIR}/lib")
SET (Boost_LIBRARY_DIR ${Boost_LIBRARY_DIR}/lib)
ELSE(EXISTS "${Boost_LIBRARY_DIR}/lib")
IF(EXISTS "${Boost_LIBRARY_DIR}/stage/lib")
SET(Boost_LIBRARY_DIR ${Boost_LIBRARY_DIR}/stage/lib)
ELSE(EXISTS "${Boost_LIBRARY_DIR}/stage/lib")
SET(Boost_LIBRARY_DIR "")
ENDIF(EXISTS "${Boost_LIBRARY_DIR}/stage/lib")
ENDIF(EXISTS "${Boost_LIBRARY_DIR}/lib")
IF(EXISTS "${Boost_INCLUDE_DIR}")
SET(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR})
# We have found boost. It is possible that the user has not
# compiled any libraries so we set Boost_FOUND to be true here.
SET(Boost_FOUND 1)
MARK_AS_ADVANCED(Boost_INCLUDE_DIR)
ENDIF(EXISTS "${Boost_INCLUDE_DIR}")
IF(Boost_LIBRARY_DIR AND EXISTS "${Boost_LIBRARY_DIR}")
SET(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR})
ENDIF(Boost_LIBRARY_DIR AND EXISTS "${Boost_LIBRARY_DIR}")
ENDIF(Boost_INCLUDE_DIR)
#
# Find boost libraries
#
# List of library suffixes to search, e.g. libboost_date_time-gcc
SET(BOOST_SUFFIX_SEARCH
gcc
il
mt
s
mt-s
mgw
mgw-mt
mgw-s
mgw-mt-s
)
# List of all boost libraries
SET(BOOST_ALL_LIBRARIES
date_time
filesystem
graph
iostreams
program_options
python
regex
serialization
signals
system
test
thread
wave
)
MACRO(LIST_CONTAINS LIST value var)
SET(${var})
FOREACH (value2 ${${LIST}})
IF (${value} STREQUAL ${value2})
SET(${var} TRUE)
ENDIF (${value} STREQUAL ${value2})
ENDFOREACH (value2)
ENDMACRO(LIST_CONTAINS)
# Macro to find boost library called name
MACRO(BOOST_FIND_LIBRARY name)
# User can specify a particular build variant via the variables:
# Boost_LIB_PREFIX, Boost_LIB_SUFFIX, Boost_LIB_SUFFIX_DEBUG
# otherwise we'll search the BOOST_SUFFIX_SEARCH list
SET(BOOST_LIB_NAMES ${Boost_LIB_PREFIX}boost_${name})
IF(NOT Boost_LIB_SUFFIX)
FOREACH(suffix ${BOOST_SUFFIX_SEARCH})
SET(BOOST_LIB_NAMES ${BOOST_LIB_NAMES} ${Boost_LIB_PREFIX}boost_${name}-${suffix})
ENDFOREACH(suffix)
ELSE(NOT Boost_LIB_SUFFIX)
SET(BOOST_LIB_NAMES ${Boost_LIB_PREFIX}boost_${name}-${Boost_LIB_SUFFIX})
ENDIF(NOT Boost_LIB_SUFFIX)
# Find the library in the Boost_LIBRARY_DIRS. We exclude the default path to
# support cross compilation
FIND_LIBRARY(Boost_${name}_LIBRARY
NAMES ${BOOST_LIB_NAMES}
PATHS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH)
IF(NOT Boost_${name}_LIBRARY)
# Find the library in the Boost_LIBRARY_DIRS
FIND_LIBRARY(Boost_${name}_LIBRARY
NAMES ${BOOST_LIB_NAMES}
PATHS ${Boost_LIBRARY_DIRS})
ENDIF(NOT Boost_${name}_LIBRARY)
# For MSVC builds find debug library
IF(WIN32 AND MSVC AND Boost_${name}_LIBRARY)
FIND_LIBRARY(Boost_${name}_LIBRARY_DEBUG ${Boost_LIB_PREFIX}boost_${name}-${Boost_LIB_SUFFIX_DEBUG})
IF(MSVC_IDE)
IF(Boost_${name}_LIBRARY AND Boost_${name}_LIBRARY_DEBUG)
SET(Boost_${name}_LIBRARIES debug ${Boost_${name}_LIBRARY_DEBUG} optimized ${Boost_${name}_LIBRARY})
ELSE(Boost_${name}_LIBRARY AND Boost_${name}_LIBRARY_DEBUG)
MESSAGE(FATAL_ERROR "Could not find the debug and release version of Boost ${name} library.")
ENDIF(Boost_${name}_LIBRARY AND Boost_${name}_LIBRARY_DEBUG)
ELSE(MSVC_IDE)
STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER)
IF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
SET(Boost_${name}_LIBRARIES ${Boost_${name}_LIBRARY_DEBUG})
ELSE(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
SET(Boost_${name}_LIBRARIES ${Boost_${name}_LIBRARY})
ENDIF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
ENDIF(MSVC_IDE)
ELSE(WIN32 AND MSVC AND Boost_${name}_LIBRARY)
SET(Boost_${name}_LIBRARIES ${Boost_${name}_LIBRARY})
ENDIF(WIN32 AND MSVC AND Boost_${name}_LIBRARY)
# If we've got it setup appropriate variables or issue error message
IF(Boost_${name}_LIBRARY)
SET(Boost_${name}_FOUND 1)
SET(Boost_${name}_INCLUDE_DIRS ${Boost_INCLUDE_DIR})
MARK_AS_ADVANCED(Boost_${name}_LIBRARY Boost_${name}_LIBRARY_DEBUG)
IF(NOT Boost_FIND_QUIETLY)
MESSAGE(STATUS "Found boost_${name} library.")
ENDIF(NOT Boost_FIND_QUIETLY)
ELSE(Boost_${name}_LIBRARY)
IF(NOT Boost_FIND_QUIETLY)
#MESSAGE(STATUS "Boost ${name} library was not found.")
ELSE(NOT Boost_FIND_QUIETLY)
IF(Boost_FIND_REQUIRED_${name})
MESSAGE(FATAL_ERROR "Could NOT find required Boost ${name} library.")
ENDIF(Boost_FIND_REQUIRED_${name})
ENDIF(NOT Boost_FIND_QUIETLY)
ENDIF(Boost_${name}_LIBRARY)
ENDMACRO(BOOST_FIND_LIBRARY)
IF(Boost_LIBRARY_DIRS)
# If the user specified required components e.g. via
# FIND_PACKAGE(Boost REQUIRED date_time regex)
# find (just) those libraries. Otherwise find all libraries.
IF(Boost_FIND_COMPONENTS)
SET(Boost_FIND_LIBRARIES ${Boost_FIND_COMPONENTS})
# Boost_filesystem is a special case and needs the additional
# linkage of Boost_system as well on some systems. So we search for
# Boost_system as well and add it later to Boost_filesystem_LIBRARIES.
LIST_CONTAINS(Boost_FIND_LIBRARIES "filesystem" has_fs)
IF(has_fs)
LIST_CONTAINS(Boost_FIND_LIBRARIES "system" has_s)
IF(NOT has_s)
SET(Boost_FIND_LIBRARIES ${Boost_FIND_LIBRARIES} system)
ENDIF(NOT has_s)
ENDIF(has_fs)
ELSE(Boost_FIND_COMPONENTS)
SET(Boost_FIND_LIBRARIES ${BOOST_ALL_LIBRARIES})
ENDIF(Boost_FIND_COMPONENTS)
SET(Boost_MISSING_REQUIRED_COMPONENTS)
SET(Boost_LIBRARIES)
FOREACH(library ${Boost_FIND_LIBRARIES})
BOOST_FIND_LIBRARY(${library})
IF(Boost_${library}_FOUND)
SET(Boost_LIBRARIES ${Boost_LIBRARIES} ${Boost_${library}_LIBRARIES})
ELSE(Boost_${library}_FOUND)
IF(Boost_FIND_REQUIRED AND Boost_FIND_COMPONENTS)
IF(Boost_FIND_REQUIRED_${library})
SET(Boost_MISSING_COMPONENTS 1)
SET(
Boost_MISSING_REQUIRED_COMPONENTS
${Boost_MISSING_REQUIRED_COMPONENTS}
${library}
)
ENDIF(Boost_FIND_REQUIRED_${library})
ENDIF(Boost_FIND_REQUIRED AND Boost_FIND_COMPONENTS)
ENDIF(Boost_${library}_FOUND)
ENDFOREACH(library)
ENDIF(Boost_LIBRARY_DIRS)
IF(Boost_filesystem_FOUND)
IF(Boost_system_FOUND)
SET(Boost_filesystem_LIBRARIES ${Boost_filesystem_LIBRARIES} ${Boost_system_LIBRARIES})
ENDIF(Boost_system_FOUND)
ENDIF(Boost_filesystem_FOUND)
IF(NOT Boost_FOUND)
IF(NOT Boost_FIND_QUIETLY)
MESSAGE(STATUS "Boost was not found. ${BOOST_DIR_MESSAGE}")
ELSE(NOT Boost_FIND_QUIETLY)
IF(Boost_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Boost was not found. ${BOOST_DIR_MESSAGE}")
ENDIF(Boost_FIND_REQUIRED)
ENDIF(NOT Boost_FIND_QUIETLY)
ELSE(NOT Boost_FOUND)
IF(Boost_MISSING_COMPONENTS)
MESSAGE(STATUS "The following required Boost components are missing:")
FOREACH(component ${Boost_MISSING_REQUIRED_COMPONENTS})
MESSAGE(STATUS " * boost_${component}")
ENDFOREACH(component)
MESSAGE(FATAL_ERROR "The Boost libraries (${Boost_FIND_COMPONENTS}) are required!")
ENDIF(Boost_MISSING_COMPONENTS)
ENDIF(NOT Boost_FOUND)

@ -0,0 +1,23 @@
# Try to find numarray python package
# Once done this will define
#
# PYTHON_NUMPY_FOUND - system has numarray development package and it should be used
# PYTHON_NUMPY_INCLUDE_DIR - directory where the arrayobject.h header file can be found
IF(PYTHON_EXECUTABLE)
FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/det_npp.py "from __future__ import print_function\ntry: import numpy; print(numpy.get_include())\nexcept: pass\n")
EXEC_PROGRAM("${PYTHON_EXECUTABLE}"
ARGS "\"${CMAKE_CURRENT_BINARY_DIR}/det_npp.py\""
OUTPUT_VARIABLE NUMPY_PATH
)
FILE(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/det_npp.py)
ENDIF(PYTHON_EXECUTABLE)
FIND_PATH(PYTHON_NUMPY_INCLUDE_DIR numpy/arrayobject.h
"${NUMPY_PATH}/"
DOC "Directory where the numpy/arrayobject.h header file can be found. This file is part of the numpy package"
)
IF(PYTHON_NUMPY_INCLUDE_DIR)
SET (PYTHON_NUMPY_FOUND 1 CACHE INTERNAL "Python numpy development package is available")
ENDIF(PYTHON_NUMPY_INCLUDE_DIR)

@ -0,0 +1,74 @@
# - Try to find the OpenSSL encryption library
# Once done this will define
#
# OPENSSL_FOUND - system has the OpenSSL library
# OPENSSL_INCLUDE_DIR - the OpenSSL include directory
# OPENSSL_LIBRARIES - The libraries needed to use OpenSSL
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
IF(OPENSSL_LIBRARIES)
SET(OpenSSL_FIND_QUIETLY TRUE)
ENDIF(OPENSSL_LIBRARIES)
IF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)
SET(LIB_FOUND 1)
ENDIF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)
FIND_PATH(OPENSSL_INCLUDE_DIR openssl/ssl.h )
IF(WIN32 AND MSVC)
# /MD and /MDd are the standard values - if somone wants to use
# others, the libnames have to change here too
# use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b
FIND_LIBRARY(SSL_EAY_DEBUG NAMES ssleay32MDd ssl ssleay32)
FIND_LIBRARY(SSL_EAY_RELEASE NAMES ssleay32MD ssl ssleay32)
IF(MSVC_IDE)
IF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)
SET(OPENSSL_LIBRARIES optimized ${SSL_EAY_RELEASE} debug ${SSL_EAY_DEBUG})
ELSE(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)
SET(OPENSSL_LIBRARIES NOTFOUND)
MESSAGE(STATUS "Could not find the debug and release version of openssl")
ENDIF(SSL_EAY_DEBUG AND SSL_EAY_RELEASE)
ELSE(MSVC_IDE)
STRING(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_TOLOWER)
IF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
SET(OPENSSL_LIBRARIES ${SSL_EAY_DEBUG})
ELSE(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
SET(OPENSSL_LIBRARIES ${SSL_EAY_RELEASE})
ENDIF(CMAKE_BUILD_TYPE_TOLOWER MATCHES debug)
ENDIF(MSVC_IDE)
MARK_AS_ADVANCED(SSL_EAY_DEBUG SSL_EAY_RELEASE)
ENDIF ( WIN32 AND MSVC )
FIND_LIBRARY(SSL NAMES ssl ssleay32 ssleay32MD )
FIND_LIBRARY(CRYPTO NAMES crypto)
IF ( SSL AND CRYPTO )
SET(OPENSSL_LIBRARIES ${SSL} ${CRYPTO})
ENDIF ( SSL AND CRYPTO )
IF(OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)
SET(OPENSSL_FOUND TRUE)
ELSE(OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)
SET(OPENSSL_FOUND FALSE)
ENDIF (OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES)
IF (OPENSSL_FOUND)
IF (NOT OpenSSL_FIND_QUIETLY)
MESSAGE(STATUS "Found OpenSSL: ${OPENSSL_LIBRARIES}")
ENDIF (NOT OpenSSL_FIND_QUIETLY)
ELSE (OPENSSL_FOUND)
IF (OpenSSL_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could NOT find OpenSSL")
ENDIF (OpenSSL_FIND_REQUIRED)
ENDIF (OPENSSL_FOUND)
MARK_AS_ADVANCED(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES)

@ -0,0 +1,85 @@
# CAPS client library
This file describes how to build the CAPS client library from source.
## Runtime Dependencies
* Boost
* Program options
* Filesystem
* System
* OpenSSL
* Python*
### Installation
#### Ubuntu 14.04
```
sudo apt-get install libboost-filesystem1.54.0 libboost-program-options1.54.0 libboost-system1.54.0 libssl0.9.8
```
#### Ubuntu 16.04
```
sudo apt-get install libboost-filesystem1.58.0 libboost-program-options1.58.0 libboost-system1.58.0 libssl1.0.0
```
#### Ubuntu 18.04
```
sudo apt-get install libboost-filesystem1.65.1 libboost-program-options1.65.1 libboost-system1.65.0 libssl1.1
```
#### Ubuntu 20.04
```
sudo apt install libboost-filesystem1.71.0 libboost-program-options1.71.0 libboost-system1.71.0 libssl1.1
```
#### RHEL/CentOS
```
yum install boost-filesystem boost-program-options boost-system openssl
```
## Build Dependencies
* CMake
* GCC, G++
* Boost
* OpenSSL
* Swig*
### Installation
#### Ubuntu
```
sudo apt-get install cmake gcc g++ libboost-all-dev cmake-curses-gui make libssl-dev
```
#### RHEL/CentOS
```
yum install cmake gcc-c++ openssl-devel boost-devel
```
## Build
1. Extract the package
```
tar xf libcapsclient-1.0.0.tar.gz
```
2. Change working directory and create a build directory
```
cd libcapsclient
mkdir build
cd build
```
3. Setup build environment
```
ccmake ..
```
4. Adjust the CMake settings to your needs
1. Press 'c'
2. Change the installation directory
3. Switch on/off features e.g. examples
4. ...
5. Press 'c' two time followed by 'g' to generate the build configuration
6. Build and install
```
make install
```

@ -0,0 +1,19 @@
SET(APP_NAME raw2caps)
SET(SOURCES
raw2caps.cpp
)
ADD_EXECUTABLE(${APP_NAME} ${SOURCES})
TARGET_LINK_LIBRARIES(${APP_NAME}
capsclient
mseed
${Boost_program_options_LIBRARY}
${Boost_filesystem_LIBRARY}
${Boost_system_LIBRARY}
${OPENSSL_LIBRARIES}
)
INSTALL(TARGETS ${APP_NAME} DESTINATION examples)
INSTALL(DIRECTORY python DESTINATION examples)

@ -0,0 +1,114 @@
############################################################################
# Copyright (C) 2021 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. #
############################################################################
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import getopt
import sys
import signal
import numpy as np
from gempa import CAPS
usage_info = """
raw2caps - pushes raw samples into CAPS.
Usage: capstool [options]
Options:
-H, --host host to connect to
-h, --help display this help message and exit
"""
def usage(exitcode=0):
sys.stderr.write(usage_info)
return exitcode
output = CAPS.Plugin("raw2caps")
def signal_handler(sig, frame): #pylint: disable=W0613
print('Caught Ctrl+C!')
output.quit()
sys.exit(0)
def main():
try:
opts, _ = getopt.getopt(sys.argv[1:], "hH:d:n:",
["help", "host=", "directory=", "network="])
except getopt.GetoptError as err:
# print help information and exit:
print(str(err)) # will print something like "option -a not recognized"
return usage(2)
addr = None
signal.signal(signal.SIGINT, signal_handler)
for o, a in opts:
if o in ["-h", "--help"]:
return usage()
if o in ["-H", "--host"]:
addr = a
else:
assert False, "unhandled option"
if addr:
try:
host, port = addr.split(":")
except BaseException:
sys.stderr.write("invalid host address given: %s\n" % addr)
return 1
else:
host = None
port = None
if port:
try:
port = int(port)
except BaseException:
sys.stderr.write("invalid port given: %s\n" % port)
return 1
else:
port = 18003
if not host:
host = "localhost"
output.setHost(host)
output.setPort(port)
output.setBufferSize(1 << 30)
output.enableLogging()
startTime = CAPS.Time.GMT()
x = np.array([1, 2], dtype=np.int32)
res = output.push("AB", "HMA", "", "BHZ", startTime, 1, 1, "m", x,
CAPS.DT_INT32)
if res != CAPS.Plugin.Success:
sys.stderr.write("failed to send packet\n")
output.close()
return 0
if __name__ == "__main__":
sys.exit(main())

@ -0,0 +1,112 @@
/***************************************************************************
* Copyright (C) 2021 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 <iostream>
#include <string>
#include <gempa/caps/log.h>
#include <gempa/caps/plugin.h>
#include <gempa/caps/mseed/steim2.h>
#include <gempa/caps/utils.h>
using namespace std;
using namespace Gempa::CAPS;
namespace {
#define LOG_CHANNEL(out, fmt) \
va_list ap;\
va_start(ap, fmt);\
fprintf(stderr, #out" "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");\
va_end(ap)
void LogError(const char *fmt, ...) {
LOG_CHANNEL(ERROR, fmt);
}
void LogWarning(const char *fmt, ...) {
LOG_CHANNEL(WARNING, fmt);
}
void LogNotice(const char *fmt, ...) {
LOG_CHANNEL(NOTICE, fmt);
}
void LogInfo(const char *fmt, ...) {
LOG_CHANNEL(INFO, fmt);
}
void LogDebug(const char *fmt, ...) {
LOG_CHANNEL(DEBUG, fmt);
}
}
int main(int argc, char **argv) {
if ( argc != 1 ) {
cerr << "Pushes samples to CAPS" << endl << endl;
cerr << "Usage: raw2caps" << endl;
return 1;
}
// Setup log handlers
int verbosity = 2;
switch ( verbosity ) {
case 4: Gempa::CAPS::SetLogHandler(LL_DEBUG, LogDebug);
case 3: Gempa::CAPS::SetLogHandler(LL_INFO, LogInfo);
case 2: Gempa::CAPS::SetLogHandler(LL_WARNING, LogWarning);
case 1: Gempa::CAPS::SetLogHandler(LL_ERROR, LogError);
default: Gempa::CAPS::SetLogHandler(LL_NOTICE, LogNotice);
}
Plugin plugin("raw2caps");
plugin.setHost("localhost");
plugin.setPort(18003);
// Enable on-the-fly MSEED Encoding
// Steim2EncoderFactory *factory = new Steim2EncoderFactory();
// plugin.setEncoderFactory(factory);
string networkCode = "AB";
string stationCode = "TEST";
string locationCode = "";
string channelCode = "HHZ";
uint16_t numerator = 100;
uint16_t denominator = 1;
Time startTime = Time::FromString("2021-01-01 00:00:00", "%F %T");
int timingQuality = 100;
string uom = "m";
vector<int> samples;
for ( int i = 0; i < 100; ++i ) {
samples.push_back(i);
}
Plugin::Status ret =
plugin.push(networkCode, stationCode, locationCode, channelCode, startTime,
numerator, denominator, uom, samples.data(), samples.size(),
DT_INT32, timingQuality);
if ( ret != Plugin::Success ) {
cerr << "Failed to send packet to CAPS" << endl;
return 1;
}
return 0;
}

@ -0,0 +1 @@
subdirs(mseed)

@ -0,0 +1,7 @@
SET(LIB_NAME mseed)
FILE(GLOB SOURCES "*.cpp" "*.c" "*.h")
ADD_LIBRARY(${LIB_NAME} STATIC ${SOURCES})
SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES COMPILE_FLAGS -fPIC)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,150 @@
/***************************************************************************
* gswap.c:
*
* Functions for generalized, in-pace byte swapping between LSBF and
* MSBF byte orders.
*
* Some standard integer types are needed, namely uint8_t and
* uint32_t, (these are normally declared by including inttypes.h or
* stdint.h). Each function expects it's input to be a void pointer
* to a quantity of the appropriate size.
*
* There are two versions of most routines, one that works on
* quantities regardless of alignment (gswapX) and one that works on
* memory aligned quantities (gswapXa). The memory aligned versions
* (gswapXa) are much faster than the other versions (gswapX), but the
* memory *must* be aligned.
*
* Written by Chad Trabant,
* IRIS Data Management Center
*
* Version: 2010.006
***************************************************************************/
#include "lmplatform.h"
/* Swap routines that work on any (aligned or not) quantities */
void
ms_gswap2 ( void *data2 )
{
uint8_t temp;
union
{
uint8_t c[2];
} dat;
memcpy( &dat, data2, 2 );
temp = dat.c[0];
dat.c[0] = dat.c[1];
dat.c[1] = temp;
memcpy( data2, &dat, 2 );
}
void
ms_gswap3 ( void *data3 )
{
uint8_t temp;
union
{
uint8_t c[3];
} dat;
memcpy( &dat, data3, 3 );
temp = dat.c[0];
dat.c[0] = dat.c[2];
dat.c[2] = temp;
memcpy( data3, &dat, 3 );
}
void
ms_gswap4 ( void *data4 )
{
uint8_t temp;
union {
uint8_t c[4];
} dat;
memcpy( &dat, data4, 4 );
temp = dat.c[0];
dat.c[0] = dat.c[3];
dat.c[3] = temp;
temp = dat.c[1];
dat.c[1] = dat.c[2];
dat.c[2] = temp;
memcpy( data4, &dat, 4 );
}
void
ms_gswap8 ( void *data8 )
{
uint8_t temp;
union
{
uint8_t c[8];
} dat;
memcpy( &dat, data8, 8 );
temp = dat.c[0];
dat.c[0] = dat.c[7];
dat.c[7] = temp;
temp = dat.c[1];
dat.c[1] = dat.c[6];
dat.c[6] = temp;
temp = dat.c[2];
dat.c[2] = dat.c[5];
dat.c[5] = temp;
temp = dat.c[3];
dat.c[3] = dat.c[4];
dat.c[4] = temp;
memcpy( data8, &dat, 8 );
}
/* Swap routines that work on memory aligned quantities */
void
ms_gswap2a ( void *data2 )
{
uint16_t *data = data2;
*data=(((*data>>8)&0xff) | ((*data&0xff)<<8));
}
void
ms_gswap4a ( void *data4 )
{
uint32_t *data = data4;
*data=(((*data>>24)&0xff) | ((*data&0xff)<<24) |
((*data>>8)&0xff00) | ((*data&0xff00)<<8));
}
void
ms_gswap8a ( void *data8 )
{
uint32_t *data4 = data8;
uint32_t h0, h1;
h0 = data4[0];
h0 = (((h0>>24)&0xff) | ((h0&0xff)<<24) |
((h0>>8)&0xff00) | ((h0&0xff00)<<8));
h1 = data4[1];
h1 = (((h1>>24)&0xff) | ((h1&0xff)<<24) |
((h1>>8)&0xff00) | ((h1&0xff00)<<8));
data4[0] = h1;
data4[1] = h0;
}

@ -0,0 +1,106 @@
LIBRARY libmseed.dll
EXPORTS
msr_parse
msr_parse_selection
msr_unpack
msr_pack
msr_pack_header
msr_init
msr_free
msr_free_blktchain
msr_addblockette
msr_normalize_header
msr_duplicate
msr_samprate
msr_nomsamprate
msr_starttime
msr_starttime_uc
msr_endtime
msr_srcname
msr_print
msr_host_latency
ms_detect
ms_parse_raw
mst_init
mst_free
mst_initgroup
mst_freegroup
mst_findmatch
mst_findadjacent
mst_addmsr
mst_addspan
mst_addmsrtogroup
mst_addtracetogroup
mst_groupheal
mst_groupsort
mst_srcname
mst_printtracelist
mst_printsynclist
mst_printgaplist
mst_pack
mst_packgroup
mstl_init
mstl_free
mstl_addmsr
mstl_printtracelist
mstl_printsynclist
mstl_printgaplist
ms_readmsr
ms_readmsr_r
ms_readmsr_main
ms_readtraces
ms_readtraces_timewin
ms_readtraces_selection
ms_readtracelist
ms_readtracelist_timewin
ms_readtracelist_selection
msr_writemseed
mst_writemseed
mst_writemseedgroup
ms_recsrcname
ms_splitsrcname
ms_strncpclean
ms_strncpopen
ms_doy2md
ms_md2doy
ms_btime2hptime
ms_btime2isotimestr
ms_btime2mdtimestr
ms_btime2seedtimestr
ms_hptime2btime
ms_hptime2isotimestr
ms_hptime2mdtimestr
ms_hptime2seedtimestr
ms_time2hptime
ms_seedtimestr2hptime
ms_timestr2hptime
ms_nomsamprate
ms_genfactmult
ms_ratapprox
ms_bigendianhost
ms_dabs
ms_samplesize
ms_encodingstr
ms_blktdesc
ms_blktlen
ms_errorstr
ms_log
ms_log_l
ms_loginit
ms_loginit_l
ms_matchselect
msr_matchselect
ms_addselect
ms_addselect_comp
ms_readselectionsfile
ms_freeselections
ms_printselections
ms_gswap2
ms_gswap3
ms_gswap4
ms_gswap8
ms_gswap2a
ms_gswap4a
ms_gswap8a
msr_unpack_steim2
msr_unpack_steim1

@ -0,0 +1,735 @@
/***************************************************************************
* libmseed.h:
*
* Interface declarations for the Mini-SEED library (libmseed).
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library 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 GNU
* Library General Public License (GNU-LGPL) for more details. The
* GNU-LGPL and further information can be found here:
* http://www.gnu.org/
*
* Written by Chad Trabant
* IRIS Data Management Center
***************************************************************************/
#ifndef LIBMSEED_H
#define LIBMSEED_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include "lmplatform.h"
#define LIBMSEED_VERSION "2.13"
#define LIBMSEED_RELEASE "2014.234"
#define MINRECLEN 128 /* Minimum Mini-SEED record length, 2^7 bytes */
/* Note: the SEED specification minimum is 256 */
#define MAXRECLEN 1048576 /* Maximum Mini-SEED record length, 2^20 bytes */
/* SEED data encoding types */
#define DE_ASCII 0
#define DE_INT16 1
#define DE_INT32 3
#define DE_FLOAT32 4
#define DE_FLOAT64 5
#define DE_STEIM1 10
#define DE_STEIM2 11
#define DE_GEOSCOPE24 12
#define DE_GEOSCOPE163 13
#define DE_GEOSCOPE164 14
#define DE_CDSN 16
#define DE_SRO 30
#define DE_DWWSSN 32
/* Library return and error code values, error values should always be negative */
#define MS_ENDOFFILE 1 /* End of file reached return value */
#define MS_NOERROR 0 /* No error */
#define MS_GENERROR -1 /* Generic unspecified error */
#define MS_NOTSEED -2 /* Data not SEED */
#define MS_WRONGLENGTH -3 /* Length of data read was not correct */
#define MS_OUTOFRANGE -4 /* SEED record length out of range */
#define MS_UNKNOWNFORMAT -5 /* Unknown data encoding format */
#define MS_STBADCOMPFLAG -6 /* Steim, invalid compression flag(s) */
/* Define the high precision time tick interval as 1/modulus seconds */
/* Default modulus of 1000000 defines tick interval as a microsecond */
#define HPTMODULUS 1000000
/* Error code for routines that normally return a high precision time.
* The time value corresponds to '1902/1/1 00:00:00.000000' with the
* default HPTMODULUS */
#define HPTERROR -2145916800000000LL
/* Macros to scale between Unix/POSIX epoch time & high precision time */
#define MS_EPOCH2HPTIME(X) X * (hptime_t) HPTMODULUS
#define MS_HPTIME2EPOCH(X) X / HPTMODULUS
/* Macro to test a character for data record indicators */
#define MS_ISDATAINDICATOR(X) (X=='D' || X=='R' || X=='Q' || X=='M')
/* Macro to test default sample rate tolerance: abs(1-sr1/sr2) < 0.0001 */
#define MS_ISRATETOLERABLE(A,B) (ms_dabs (1.0 - (A / B)) < 0.0001)
/* Macro to test for sane year and day values, used primarily to
* determine if byte order swapping is needed.
*
* Year : between 1900 and 2100
* Day : between 1 and 366
*
* This test is non-unique (non-deterministic) for days 1, 256 and 257
* in the year 2056 because the swapped values are also within range.
*/
#define MS_ISVALIDYEARDAY(Y,D) (Y >= 1900 && Y <= 2100 && D >= 1 && D <= 366)
/* Macro to test memory for a SEED data record signature by checking
* SEED data record header values at known byte offsets to determine
* if the memory contains a valid record.
*
* Offset = Value
* [0-5] = Digits, spaces or NULL, SEED sequence number
* 6 = Data record quality indicator
* 7 = Space or NULL [not valid SEED]
* 24 = Start hour (0-23)
* 25 = Start minute (0-59)
* 26 = Start second (0-60)
*
* Usage:
* MS_ISVALIDHEADER ((char *)X) X buffer must contain at least 27 bytes
*/
#define MS_ISVALIDHEADER(X) ( \
(isdigit ((unsigned char) *(X)) || *(X) == ' ' || !*(X) ) && \
(isdigit ((unsigned char) *(X+1)) || *(X+1) == ' ' || !*(X+1) ) && \
(isdigit ((unsigned char) *(X+2)) || *(X+2) == ' ' || !*(X+2) ) && \
(isdigit ((unsigned char) *(X+3)) || *(X+3) == ' ' || !*(X+3) ) && \
(isdigit ((unsigned char) *(X+4)) || *(X+4) == ' ' || !*(X+4) ) && \
(isdigit ((unsigned char) *(X+5)) || *(X+5) == ' ' || !*(X+5) ) && \
MS_ISDATAINDICATOR(*(X+6)) && \
(*(X+7) == ' ' || *(X+7) == '\0') && \
(int)(*(X+24)) >= 0 && (int)(*(X+24)) <= 23 && \
(int)(*(X+25)) >= 0 && (int)(*(X+25)) <= 59 && \
(int)(*(X+26)) >= 0 && (int)(*(X+26)) <= 60)
/* Macro to test memory for a blank/noise SEED data record signature
* by checking for a valid SEED sequence number and padding characters
* to determine if the memory contains a valid blank/noise record.
*
* Offset = Value
* [0-5] = Digits or NULL, SEED sequence number
* [6-47] = Space character (ASCII 32), remainder of fixed header
*
* Usage:
* MS_ISVALIDBLANK ((char *)X) X buffer must contain at least 27 bytes
*/
#define MS_ISVALIDBLANK(X) ((isdigit ((unsigned char) *(X)) || !*(X) ) && \
(isdigit ((unsigned char) *(X+1)) || !*(X+1) ) && \
(isdigit ((unsigned char) *(X+2)) || !*(X+2) ) && \
(isdigit ((unsigned char) *(X+3)) || !*(X+3) ) && \
(isdigit ((unsigned char) *(X+4)) || !*(X+4) ) && \
(isdigit ((unsigned char) *(X+5)) || !*(X+5) ) && \
(*(X+6)==' ')&&(*(X+7)==' ')&&(*(X+8)==' ') && \
(*(X+9)==' ')&&(*(X+10)==' ')&&(*(X+11)==' ') && \
(*(X+12)==' ')&&(*(X+13)==' ')&&(*(X+14)==' ') && \
(*(X+15)==' ')&&(*(X+16)==' ')&&(*(X+17)==' ') && \
(*(X+18)==' ')&&(*(X+19)==' ')&&(*(X+20)==' ') && \
(*(X+21)==' ')&&(*(X+22)==' ')&&(*(X+23)==' ') && \
(*(X+24)==' ')&&(*(X+25)==' ')&&(*(X+26)==' ') && \
(*(X+27)==' ')&&(*(X+28)==' ')&&(*(X+29)==' ') && \
(*(X+30)==' ')&&(*(X+31)==' ')&&(*(X+32)==' ') && \
(*(X+33)==' ')&&(*(X+34)==' ')&&(*(X+35)==' ') && \
(*(X+36)==' ')&&(*(X+37)==' ')&&(*(X+38)==' ') && \
(*(X+39)==' ')&&(*(X+40)==' ')&&(*(X+41)==' ') && \
(*(X+42)==' ')&&(*(X+43)==' ')&&(*(X+44)==' ') && \
(*(X+45)==' ')&&(*(X+46)==' ')&&(*(X+47)==' ') )
/* A simple bitwise AND test to return 0 or 1 */
#define bit(x,y) (x&y)?1:0
/* Require a large (>= 64-bit) integer type for hptime_t */
typedef int64_t hptime_t;
/* A single byte flag type */
typedef int8_t flag;
/* SEED binary time */
typedef struct btime_s
{
uint16_t year;
uint16_t day;
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t unused;
uint16_t fract;
} LMP_PACKED
BTime;
/* Fixed section data of header */
struct fsdh_s
{
char sequence_number[6];
char dataquality;
char reserved;
char station[5];
char location[2];
char channel[3];
char network[2];
BTime start_time;
uint16_t numsamples;
int16_t samprate_fact;
int16_t samprate_mult;
uint8_t act_flags;
uint8_t io_flags;
uint8_t dq_flags;
uint8_t numblockettes;
int32_t time_correct;
uint16_t data_offset;
uint16_t blockette_offset;
} LMP_PACKED;
/* Blockette 100, Sample Rate (without header) */
struct blkt_100_s
{
float samprate;
int8_t flags;
uint8_t reserved[3];
} LMP_PACKED;
/* Blockette 200, Generic Event Detection (without header) */
struct blkt_200_s
{
float amplitude;
float period;
float background_estimate;
uint8_t flags;
uint8_t reserved;
BTime time;
char detector[24];
} LMP_PACKED;
/* Blockette 201, Murdock Event Detection (without header) */
struct blkt_201_s
{
float amplitude;
float period;
float background_estimate;
uint8_t flags;
uint8_t reserved;
BTime time;
uint8_t snr_values[6];
uint8_t loopback;
uint8_t pick_algorithm;
char detector[24];
} LMP_PACKED;
/* Blockette 300, Step Calibration (without header) */
struct blkt_300_s
{
BTime time;
uint8_t numcalibrations;
uint8_t flags;
uint32_t step_duration;
uint32_t interval_duration;
float amplitude;
char input_channel[3];
uint8_t reserved;
uint32_t reference_amplitude;
char coupling[12];
char rolloff[12];
} LMP_PACKED;
/* Blockette 310, Sine Calibration (without header) */
struct blkt_310_s
{
BTime time;
uint8_t reserved1;
uint8_t flags;
uint32_t duration;
float period;
float amplitude;
char input_channel[3];
uint8_t reserved2;
uint32_t reference_amplitude;
char coupling[12];
char rolloff[12];
} LMP_PACKED;
/* Blockette 320, Pseudo-random Calibration (without header) */
struct blkt_320_s
{
BTime time;
uint8_t reserved1;
uint8_t flags;
uint32_t duration;
float ptp_amplitude;
char input_channel[3];
uint8_t reserved2;
uint32_t reference_amplitude;
char coupling[12];
char rolloff[12];
char noise_type[8];
} LMP_PACKED;
/* Blockette 390, Generic Calibration (without header) */
struct blkt_390_s
{
BTime time;
uint8_t reserved1;
uint8_t flags;
uint32_t duration;
float amplitude;
char input_channel[3];
uint8_t reserved2;
} LMP_PACKED;
/* Blockette 395, Calibration Abort (without header) */
struct blkt_395_s
{
BTime time;
uint8_t reserved[2];
} LMP_PACKED;
/* Blockette 400, Beam (without header) */
struct blkt_400_s
{
float azimuth;
float slowness;
uint16_t configuration;
uint8_t reserved[2];
} LMP_PACKED;
/* Blockette 405, Beam Delay (without header) */
struct blkt_405_s
{
uint16_t delay_values[1];
};
/* Blockette 500, Timing (without header) */
struct blkt_500_s
{
float vco_correction;
BTime time;
int8_t usec;
uint8_t reception_qual;
uint32_t exception_count;
char exception_type[16];
char clock_model[32];
char clock_status[128];
} LMP_PACKED;
/* Blockette 1000, Data Only SEED (without header) */
struct blkt_1000_s
{
uint8_t encoding;
uint8_t byteorder;
uint8_t reclen;
uint8_t reserved;
} LMP_PACKED;
/* Blockette 1001, Data Extension (without header) */
struct blkt_1001_s
{
uint8_t timing_qual;
int8_t usec;
uint8_t reserved;
uint8_t framecnt;
} LMP_PACKED;
/* Blockette 2000, Opaque Data (without header) */
struct blkt_2000_s
{
uint16_t length;
uint16_t data_offset;
uint32_t recnum;
uint8_t byteorder;
uint8_t flags;
uint8_t numheaders;
char payload[1];
} LMP_PACKED;
/* Blockette chain link, generic linkable blockette index */
typedef struct blkt_link_s
{
uint16_t blktoffset; /* Offset to this blockette */
uint16_t blkt_type; /* Blockette type */
uint16_t next_blkt; /* Offset to next blockette */
void *blktdata; /* Blockette data */
uint16_t blktdatalen; /* Length of blockette data in bytes */
struct blkt_link_s *next;
}
BlktLink;
typedef struct StreamState_s
{
int64_t packedrecords; /* Count of packed records */
int64_t packedsamples; /* Count of packed samples */
int32_t lastintsample; /* Value of last integer sample packed */
flag comphistory; /* Control use of lastintsample for compression history */
}
StreamState;
typedef struct MSRecord_s {
char *record; /* Mini-SEED record */
int32_t reclen; /* Length of Mini-SEED record in bytes */
/* Pointers to SEED data record structures */
struct fsdh_s *fsdh; /* Fixed Section of Data Header */
BlktLink *blkts; /* Root of blockette chain */
struct blkt_100_s *Blkt100; /* Blockette 100, if present */
struct blkt_1000_s *Blkt1000; /* Blockette 1000, if present */
struct blkt_1001_s *Blkt1001; /* Blockette 1001, if present */
/* Common header fields in accessible form */
int32_t sequence_number; /* SEED record sequence number */
char network[11]; /* Network designation, NULL terminated */
char station[11]; /* Station designation, NULL terminated */
char location[11]; /* Location designation, NULL terminated */
char channel[11]; /* Channel designation, NULL terminated */
char dataquality; /* Data quality indicator */
hptime_t starttime; /* Record start time, corrected (first sample) */
double samprate; /* Nominal sample rate (Hz) */
int64_t samplecnt; /* Number of samples in record */
int8_t encoding; /* Data encoding format */
int8_t byteorder; /* Original/Final byte order of record */
/* Data sample fields */
void *datasamples; /* Data samples, 'numsamples' of type 'sampletype'*/
int64_t numsamples; /* Number of data samples in datasamples */
char sampletype; /* Sample type code: a, i, f, d */
/* Stream oriented state information */
StreamState *ststate; /* Stream processing state information */
}
MSRecord;
/* Container for a continuous trace, linkable */
typedef struct MSTrace_s {
char network[11]; /* Network designation, NULL terminated */
char station[11]; /* Station designation, NULL terminated */
char location[11]; /* Location designation, NULL terminated */
char channel[11]; /* Channel designation, NULL terminated */
char dataquality; /* Data quality indicator */
char type; /* MSTrace type code */
hptime_t starttime; /* Time of first sample */
hptime_t endtime; /* Time of last sample */
double samprate; /* Nominal sample rate (Hz) */
int64_t samplecnt; /* Number of samples in trace coverage */
void *datasamples; /* Data samples, 'numsamples' of type 'sampletype' */
int64_t numsamples; /* Number of data samples in datasamples */
char sampletype; /* Sample type code: a, i, f, d */
void *prvtptr; /* Private pointer for general use, unused by libmseed */
StreamState *ststate; /* Stream processing state information */
struct MSTrace_s *next; /* Pointer to next trace */
}
MSTrace;
/* Container for a group (chain) of traces */
typedef struct MSTraceGroup_s {
int32_t numtraces; /* Number of MSTraces in the trace chain */
struct MSTrace_s *traces; /* Root of the trace chain */
}
MSTraceGroup;
/* Container for a continuous trace segment, linkable */
typedef struct MSTraceSeg_s {
hptime_t starttime; /* Time of first sample */
hptime_t endtime; /* Time of last sample */
double samprate; /* Nominal sample rate (Hz) */
int64_t samplecnt; /* Number of samples in trace coverage */
void *datasamples; /* Data samples, 'numsamples' of type 'sampletype'*/
int64_t numsamples; /* Number of data samples in datasamples */
char sampletype; /* Sample type code: a, i, f, d */
void *prvtptr; /* Private pointer for general use, unused by libmseed */
struct MSTraceSeg_s *prev; /* Pointer to previous segment */
struct MSTraceSeg_s *next; /* Pointer to next segment */
}
MSTraceSeg;
/* Container for a trace ID, linkable */
typedef struct MSTraceID_s {
char network[11]; /* Network designation, NULL terminated */
char station[11]; /* Station designation, NULL terminated */
char location[11]; /* Location designation, NULL terminated */
char channel[11]; /* Channel designation, NULL terminated */
char dataquality; /* Data quality indicator */
char srcname[45]; /* Source name (Net_Sta_Loc_Chan_Qual), NULL terminated */
char type; /* Trace type code */
hptime_t earliest; /* Time of earliest sample */
hptime_t latest; /* Time of latest sample */
void *prvtptr; /* Private pointer for general use, unused by libmseed */
int32_t numsegments; /* Number of segments for this ID */
struct MSTraceSeg_s *first; /* Pointer to first of list of segments */
struct MSTraceSeg_s *last; /* Pointer to last of list of segments */
struct MSTraceID_s *next; /* Pointer to next trace */
}
MSTraceID;
/* Container for a continuous trace segment, linkable */
typedef struct MSTraceList_s {
int32_t numtraces; /* Number of traces in list */
struct MSTraceID_s *traces; /* Pointer to list of traces */
struct MSTraceID_s *last; /* Pointer to last used trace in list */
}
MSTraceList;
/* Data selection structure time window definition containers */
typedef struct SelectTime_s {
hptime_t starttime; /* Earliest data for matching channels */
hptime_t endtime; /* Latest data for matching channels */
struct SelectTime_s *next;
} SelectTime;
/* Data selection structure definition containers */
typedef struct Selections_s {
char srcname[100]; /* Matching (globbing) source name: Net_Sta_Loc_Chan_Qual */
struct SelectTime_s *timewindows;
struct Selections_s *next;
} Selections;
/* Global variables (defined in pack.c) and macros to set/force
* pack byte orders */
extern flag packheaderbyteorder;
extern flag packdatabyteorder;
#define MS_PACKHEADERBYTEORDER(X) (packheaderbyteorder = X);
#define MS_PACKDATABYTEORDER(X) (packdatabyteorder = X);
/* Global variables (defined in unpack.c) and macros to set/force
* unpack byte orders */
extern flag unpackheaderbyteorder;
extern flag unpackdatabyteorder;
#define MS_UNPACKHEADERBYTEORDER(X) (unpackheaderbyteorder = X);
#define MS_UNPACKDATABYTEORDER(X) (unpackdatabyteorder = X);
/* Global variables (defined in unpack.c) and macros to set/force
* encoding and fallback encoding */
extern int unpackencodingformat;
extern int unpackencodingfallback;
#define MS_UNPACKENCODINGFORMAT(X) (unpackencodingformat = X);
#define MS_UNPACKENCODINGFALLBACK(X) (unpackencodingfallback = X);
/* Mini-SEED record related functions */
extern int msr_parse (char *record, int recbuflen, MSRecord **ppmsr, int reclen,
flag dataflag, flag verbose);
extern int msr_parse_selection ( char *recbuf, int recbuflen, int64_t *offset,
MSRecord **ppmsr, int reclen,
Selections *selections, flag dataflag, flag verbose );
extern int msr_unpack (char *record, int reclen, MSRecord **ppmsr,
flag dataflag, flag verbose);
extern int msr_pack (MSRecord *msr, void (*record_handler) (char *, int, void *),
void *handlerdata, int64_t *packedsamples, flag flush, flag verbose );
extern int msr_pack_header (MSRecord *msr, flag normalize, flag verbose);
extern int msr_unpack_data (MSRecord *msr, int swapflag, flag verbose);
extern MSRecord* msr_init (MSRecord *msr);
extern void msr_free (MSRecord **ppmsr);
extern void msr_free_blktchain (MSRecord *msr);
extern BlktLink* msr_addblockette (MSRecord *msr, char *blktdata, int length,
int blkttype, int chainpos);
extern int msr_normalize_header (MSRecord *msr, flag verbose);
extern MSRecord* msr_duplicate (MSRecord *msr, flag datadup);
extern double msr_samprate (MSRecord *msr);
extern double msr_nomsamprate (MSRecord *msr);
extern hptime_t msr_starttime (MSRecord *msr);
extern hptime_t msr_starttime_uc (MSRecord *msr);
extern hptime_t msr_endtime (MSRecord *msr);
extern char* msr_srcname (MSRecord *msr, char *srcname, flag quality);
extern void msr_print (MSRecord *msr, flag details);
extern double msr_host_latency (MSRecord *msr);
extern int ms_detect (const char *record, int recbuflen);
extern int ms_parse_raw (char *record, int maxreclen, flag details, flag swapflag);
/* MSTrace related functions */
extern MSTrace* mst_init (MSTrace *mst);
extern void mst_free (MSTrace **ppmst);
extern MSTraceGroup* mst_initgroup (MSTraceGroup *mstg);
extern void mst_freegroup (MSTraceGroup **ppmstg);
extern MSTrace* mst_findmatch (MSTrace *startmst, char dataquality,
char *network, char *station, char *location, char *channel);
extern MSTrace* mst_findadjacent (MSTraceGroup *mstg, flag *whence, char dataquality,
char *network, char *station, char *location, char *channel,
double samprate, double sampratetol,
hptime_t starttime, hptime_t endtime, double timetol);
extern int mst_addmsr (MSTrace *mst, MSRecord *msr, flag whence);
extern int mst_addspan (MSTrace *mst, hptime_t starttime, hptime_t endtime,
void *datasamples, int64_t numsamples,
char sampletype, flag whence);
extern MSTrace* mst_addmsrtogroup (MSTraceGroup *mstg, MSRecord *msr, flag dataquality,
double timetol, double sampratetol);
extern MSTrace* mst_addtracetogroup (MSTraceGroup *mstg, MSTrace *mst);
extern int mst_groupheal (MSTraceGroup *mstg, double timetol, double sampratetol);
extern int mst_groupsort (MSTraceGroup *mstg, flag quality);
extern int mst_convertsamples (MSTrace *mst, char type, flag truncate);
extern char * mst_srcname (MSTrace *mst, char *srcname, flag quality);
extern void mst_printtracelist (MSTraceGroup *mstg, flag timeformat,
flag details, flag gaps);
extern void mst_printsynclist ( MSTraceGroup *mstg, char *dccid, flag subsecond );
extern void mst_printgaplist (MSTraceGroup *mstg, flag timeformat,
double *mingap, double *maxgap);
extern int mst_pack (MSTrace *mst, void (*record_handler) (char *, int, void *),
void *handlerdata, int reclen, flag encoding, flag byteorder,
int64_t *packedsamples, flag flush, flag verbose,
MSRecord *mstemplate);
extern int mst_packgroup (MSTraceGroup *mstg, void (*record_handler) (char *, int, void *),
void *handlerdata, int reclen, flag encoding, flag byteorder,
int64_t *packedsamples, flag flush, flag verbose,
MSRecord *mstemplate);
/* MSTraceList related functions */
extern MSTraceList * mstl_init ( MSTraceList *mstl );
extern void mstl_free ( MSTraceList **ppmstl, flag freeprvtptr );
extern MSTraceSeg * mstl_addmsr ( MSTraceList *mstl, MSRecord *msr, flag dataquality,
flag autoheal, double timetol, double sampratetol );
extern int mstl_convertsamples ( MSTraceSeg *seg, char type, flag truncate );
extern void mstl_printtracelist ( MSTraceList *mstl, flag timeformat,
flag details, flag gaps );
extern void mstl_printsynclist ( MSTraceList *mstl, char *dccid, flag subsecond );
extern void mstl_printgaplist (MSTraceList *mstl, flag timeformat,
double *mingap, double *maxgap);
/* Reading Mini-SEED records from files */
typedef struct MSFileParam_s
{
FILE *fp;
char filename[512];
char *rawrec;
int readlen;
int readoffset;
int packtype;
off_t packhdroffset;
off_t filepos;
off_t filesize;
int recordcount;
} MSFileParam;
extern int ms_readmsr (MSRecord **ppmsr, const char *msfile, int reclen, off_t *fpos, int *last,
flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readmsr_r (MSFileParam **ppmsfp, MSRecord **ppmsr, const char *msfile, int reclen,
off_t *fpos, int *last, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readmsr_main (MSFileParam **ppmsfp, MSRecord **ppmsr, const char *msfile, int reclen,
off_t *fpos, int *last, flag skipnotdata, flag dataflag, Selections *selections, flag verbose);
extern int ms_readtraces (MSTraceGroup **ppmstg, const char *msfile, int reclen, double timetol, double sampratetol,
flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readtraces_timewin (MSTraceGroup **ppmstg, const char *msfile, int reclen, double timetol, double sampratetol,
hptime_t starttime, hptime_t endtime, flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readtraces_selection (MSTraceGroup **ppmstg, const char *msfile, int reclen, double timetol, double sampratetol,
Selections *selections, flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readtracelist (MSTraceList **ppmstl, const char *msfile, int reclen, double timetol, double sampratetol,
flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readtracelist_timewin (MSTraceList **ppmstl, const char *msfile, int reclen, double timetol, double sampratetol,
hptime_t starttime, hptime_t endtime, flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int ms_readtracelist_selection (MSTraceList **ppmstl, const char *msfile, int reclen, double timetol, double sampratetol,
Selections *selections, flag dataquality, flag skipnotdata, flag dataflag, flag verbose);
extern int msr_writemseed ( MSRecord *msr, const char *msfile, flag overwrite, int reclen,
flag encoding, flag byteorder, flag verbose );
extern int mst_writemseed ( MSTrace *mst, const char *msfile, flag overwrite, int reclen,
flag encoding, flag byteorder, flag verbose );
extern int mst_writemseedgroup ( MSTraceGroup *mstg, const char *msfile, flag overwrite,
int reclen, flag encoding, flag byteorder, flag verbose );
/* General use functions */
extern char* ms_recsrcname (char *record, char *srcname, flag quality);
extern int ms_splitsrcname (char *srcname, char *net, char *sta, char *loc, char *chan, char *qual);
extern int ms_strncpclean (char *dest, const char *source, int length);
extern int ms_strncpcleantail (char *dest, const char *source, int length);
extern int ms_strncpopen (char *dest, const char *source, int length);
extern int ms_doy2md (int year, int jday, int *month, int *mday);
extern int ms_md2doy (int year, int month, int mday, int *jday);
extern hptime_t ms_btime2hptime (BTime *btime);
extern char* ms_btime2isotimestr (BTime *btime, char *isotimestr);
extern char* ms_btime2mdtimestr (BTime *btime, char *mdtimestr);
extern char* ms_btime2seedtimestr (BTime *btime, char *seedtimestr);
extern int ms_hptime2btime (hptime_t hptime, BTime *btime);
extern char* ms_hptime2isotimestr (hptime_t hptime, char *isotimestr, flag subsecond);
extern char* ms_hptime2mdtimestr (hptime_t hptime, char *mdtimestr, flag subsecond);
extern char* ms_hptime2seedtimestr (hptime_t hptime, char *seedtimestr, flag subsecond);
extern hptime_t ms_time2hptime (int year, int day, int hour, int min, int sec, int usec);
extern hptime_t ms_seedtimestr2hptime (char *seedtimestr);
extern hptime_t ms_timestr2hptime (char *timestr);
extern double ms_nomsamprate (int factor, int multiplier);
extern int ms_genfactmult (double samprate, int16_t *factor, int16_t *multiplier);
extern int ms_ratapprox (double real, int *num, int *den, int maxval, double precision);
extern int ms_bigendianhost (void);
extern double ms_dabs (double val);
/* Lookup functions */
extern uint8_t ms_samplesize (const char sampletype);
extern char* ms_encodingstr (const char encoding);
extern char* ms_blktdesc (uint16_t blkttype);
extern uint16_t ms_blktlen (uint16_t blkttype, const char *blktdata, flag swapflag);
extern char * ms_errorstr (int errorcode);
/* Logging facility */
#define MAX_LOG_MSG_LENGTH 200 /* Maximum length of log messages */
/* Logging parameters */
typedef struct MSLogParam_s
{
void (*log_print)(char*);
const char *logprefix;
void (*diag_print)(char*);
const char *errprefix;
} MSLogParam;
extern int ms_log (int level, ...);
extern int ms_log_l (MSLogParam *logp, int level, ...);
extern void ms_loginit (void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix);
extern MSLogParam *ms_loginit_l (MSLogParam *logp,
void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix);
/* Selection functions */
extern Selections *ms_matchselect (Selections *selections, char *srcname,
hptime_t starttime, hptime_t endtime, SelectTime **ppselecttime);
extern Selections *msr_matchselect (Selections *selections, MSRecord *msr, SelectTime **ppselecttime);
extern int ms_addselect (Selections **ppselections, char *srcname,
hptime_t starttime, hptime_t endtime);
extern int ms_addselect_comp (Selections **ppselections, char *net, char* sta, char *loc,
char *chan, char *qual, hptime_t starttime, hptime_t endtime);
extern int ms_readselectionsfile (Selections **ppselections, char *filename);
extern void ms_freeselections (Selections *selections);
extern void ms_printselections (Selections *selections);
/* Generic byte swapping routines */
extern void ms_gswap2 ( void *data2 );
extern void ms_gswap3 ( void *data3 );
extern void ms_gswap4 ( void *data4 );
extern void ms_gswap8 ( void *data8 );
/* Generic byte swapping routines for memory aligned quantities */
extern void ms_gswap2a ( void *data2 );
extern void ms_gswap4a ( void *data4 );
extern void ms_gswap8a ( void *data8 );
/* Byte swap macro for the BTime struct */
#define MS_SWAPBTIME(x) \
ms_gswap2 (x.year); \
ms_gswap2 (x.day); \
ms_gswap2 (x.fract);
#ifdef __cplusplus
}
#endif
#endif /* LIBMSEED_H */

@ -0,0 +1,65 @@
/***************************************************************************
* lmplatform.c:
*
* Platform portability routines.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library 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 GNU
* Library General Public License (GNU-LGPL) for more details. The
* GNU-LGPL and further information can be found here:
* http://www.gnu.org/
*
* Written by Chad Trabant, IRIS Data Management Center
*
* modified: 2010.304
***************************************************************************/
/* Define _LARGEFILE_SOURCE to get ftello/fseeko on some systems (Linux) */
#define _LARGEFILE_SOURCE 1
#include "lmplatform.h"
/***************************************************************************
* lmp_ftello:
*
* Return the current file position for the specified descriptor using
* the system's closest match to the POSIX ftello.
***************************************************************************/
off_t
lmp_ftello (FILE *stream)
{
#if defined(LMP_WIN32)
return (off_t) ftell (stream);
#else
return (off_t) ftello (stream);
#endif
} /* End of lmp_ftello() */
/***************************************************************************
* lmp_fseeko:
*
* Seek to a specific file position for the specified descriptor using
* the system's closest match to the POSIX fseeko.
***************************************************************************/
int
lmp_fseeko (FILE *stream, off_t offset, int whence)
{
#if defined(LMP_WIN32)
return (int) fseek (stream, (long int) offset, whence);
#else
return (int) fseeko (stream, offset, whence);
#endif
} /* End of lmp_fseeko() */

@ -0,0 +1,166 @@
/***************************************************************************
* lmplatform.h:
*
* Platform specific headers. This file provides a basic level of platform
* portability.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* as published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library 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 GNU
* Library General Public License (GNU-LGPL) for more details. The
* GNU-LGPL and further information can be found here:
* http://www.gnu.org/
*
* Written by Chad Trabant, IRIS Data Management Center
*
* modified: 2014.074
***************************************************************************/
#ifndef LMPLATFORM_H
#define LMPLATFORM_H 1
#ifdef __cplusplus
extern "C" {
#endif
/* On some platforms (e.g. ARM) structures are aligned on word boundaries
by adding padding between the elements. This library uses structs that
map to SEED header/blockette structures that are required to have a
layout exactly as specified, i.e. no padding.
If "ATTRIBUTE_PACKED" is defined at compile time (e.g. -DATTRIBUTE_PACKED)
the preprocessor will use the define below to add the "packed" attribute
to effected structs. This attribute is supported by GCC and increasingly
more compilers.
*/
#if defined(ATTRIBUTE_PACKED)
#define LMP_PACKED __attribute__((packed))
#else
#define LMP_PACKED
#endif
/* Make some guesses about the system libraries based
* on the architecture. Currently the assumptions are:
* Linux => glibc2 libraries (LMP_GLIBC2)
* Sun => Solaris libraties (LMP_SOLARIS)
* BSD => BSD libraries, including Apple Mac OS X (LMP_BSD)
* WIN32 => WIN32 and Windows Sockets 2 (LMP_WIN32)
*/
#if defined(__linux__) || defined(__linux) || defined(__CYGWIN__)
#define LMP_GLIBC2 1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
#include <features.h>
#elif defined(__sun__) || defined(__sun)
#define LMP_SOLARIS 1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#define LMP_BSD 1
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
#elif defined(WIN32) || defined(WIN64)
#define LMP_WIN32 1
#include <windows.h>
#include <stdarg.h>
#include <winsock.h>
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#if defined(_MSC_VER)
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define strtoull _strtoui64
#define strdup _strdup
#define fileno _fileno
#endif
#if defined(__MINGW32__)
#define fstat _fstat
#define stat _stat
#endif
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int int16_t;
typedef unsigned short int uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <ctype.h>
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef signed short int int16_t;
typedef unsigned short int uint16_t;
typedef signed int int32_t;
typedef unsigned int uint32_t;
typedef signed long long int64_t;
typedef unsigned long long uint64_t;
#endif
extern off_t lmp_ftello (FILE *stream);
extern int lmp_fseeko (FILE *stream, off_t offset, int whence);
#ifdef __cplusplus
}
#endif
#endif /* LMPLATFORM_H */

@ -0,0 +1,334 @@
/***************************************************************************
* logging.c
*
* Log handling routines for libmseed
*
* Chad Trabant
* IRIS Data Management Center
*
* modified: 2014.197
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "libmseed.h"
void ms_loginit_main (MSLogParam *logp,
void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix);
int ms_log_main (MSLogParam *logp, int level, va_list *varlist);
/* Initialize the global logging parameters */
MSLogParam gMSLogParam = {NULL, NULL, NULL, NULL};
/***************************************************************************
* ms_loginit:
*
* Initialize the global logging parameters.
*
* See ms_loginit_main() description for usage.
***************************************************************************/
void
ms_loginit (void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix)
{
ms_loginit_main(&gMSLogParam, log_print, logprefix, diag_print, errprefix);
} /* End of ms_loginit() */
/***************************************************************************
* ms_loginit_l:
*
* Initialize MSLogParam specific logging parameters. If the logging parameters
* have not been initialized (log == NULL) new parameter space will
* be allocated.
*
* See ms_loginit_main() description for usage.
*
* Returns a pointer to the created/re-initialized MSLogParam struct
* on success and NULL on error.
***************************************************************************/
MSLogParam *
ms_loginit_l (MSLogParam *logp,
void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix)
{
MSLogParam *llog;
if ( logp == NULL )
{
llog = (MSLogParam *) malloc (sizeof(MSLogParam));
if ( llog == NULL )
{
ms_log (2, "ms_loginit_l(): Cannot allocate memory\n");
return NULL;
}
llog->log_print = NULL;
llog->logprefix = NULL;
llog->diag_print = NULL;
llog->errprefix = NULL;
}
else
{
llog = logp;
}
ms_loginit_main (llog, log_print, logprefix, diag_print, errprefix);
return llog;
} /* End of ms_loginit_l() */
/***************************************************************************
* ms_loginit_main:
*
* Initialize the logging subsystem. Given values determine how ms_log()
* and ms_log_l() emit messages.
*
* This function modifies the logging parameters in the passed MSLogParam.
*
* Any log/error printing functions indicated must except a single
* argument, namely a string (char *). The ms_log() and
* ms_log_r() functions format each message and then pass the result
* on to the log/error printing functions.
*
* If the log/error prefixes have been set they will be pre-pended to the
* message.
*
* Use NULL for the function pointers or the prefixes if they should not
* be changed from previously set or default values. The default behavior
* of the logging subsystem is given in the example below.
*
* Example: ms_loginit_main (0, (void*)&printf, NULL, (void*)&printf, "error: ");
***************************************************************************/
void
ms_loginit_main (MSLogParam *logp,
void (*log_print)(char*), const char *logprefix,
void (*diag_print)(char*), const char *errprefix)
{
if ( ! logp )
return;
if ( log_print )
logp->log_print = log_print;
if ( logprefix )
{
if ( strlen(logprefix) >= MAX_LOG_MSG_LENGTH )
{
ms_log_l (logp, 2, 0, "log message prefix is too large\n");
}
else
{
logp->logprefix = logprefix;
}
}
if ( diag_print )
logp->diag_print = diag_print;
if ( errprefix )
{
if ( strlen(errprefix) >= MAX_LOG_MSG_LENGTH )
{
ms_log_l (logp, 2, 0, "error message prefix is too large\n");
}
else
{
logp->errprefix = errprefix;
}
}
return;
} /* End of ms_loginit_main() */
/***************************************************************************
* ms_log:
*
* A wrapper to ms_log_main() that uses the global logging parameters.
*
* See ms_log_main() description for return values.
***************************************************************************/
int
ms_log (int level, ...)
{
int retval;
va_list varlist;
va_start (varlist, level);
retval = ms_log_main (&gMSLogParam, level, &varlist);
va_end (varlist);
return retval;
} /* End of ms_log() */
/***************************************************************************
* ms_log_l:
*
* A wrapper to ms_log_main() that uses the logging parameters in a
* supplied MSLogParam. If the supplied pointer is NULL the global logging
* parameters will be used.
*
* See ms_log_main() description for return values.
***************************************************************************/
int
ms_log_l (MSLogParam *logp, int level, ...)
{
int retval;
va_list varlist;
MSLogParam *llog;
if ( ! logp )
llog = &gMSLogParam;
else
llog = logp;
va_start (varlist, level);
retval = ms_log_main (llog, level, &varlist);
va_end (varlist);
return retval;
} /* End of ms_log_l() */
/***************************************************************************
* ms_log_main:
*
* A standard logging/printing routine.
*
* The function uses logging parameters specified in the supplied
* MSLogParam.
*
* This function expects 2+ arguments: message level, fprintf format,
* and fprintf arguments.
*
* Three levels are recognized:
* 0 : Normal log messages, printed using log_print with logprefix
* 1 : Diagnostic messages, printed using diag_print with logprefix
* 2+ : Error messagess, printed using diag_print with errprefix
*
* This function builds the log/error message and passes to it as a
* string (char *) to the functions defined with ms_loginit() or
* ms_loginit_l(). If the log/error printing functions have not been
* defined messages will be printed with fprintf, log messages to
* stdout and error messages to stderr.
*
* If the log/error prefix's have been set with ms_loginit() or
* ms_loginit_l() they will be pre-pended to the message.
*
* All messages will be truncated to the MAX_LOG_MSG_LENGTH, this includes
* any set prefix.
*
* Returns the number of characters formatted on success, and a
* a negative value on error.
***************************************************************************/
int
ms_log_main (MSLogParam *logp, int level, va_list *varlist)
{
static char message[MAX_LOG_MSG_LENGTH];
int retvalue = 0;
int presize;
const char *format;
if ( ! logp )
{
fprintf(stderr, "ms_log_main() called without specifying log parameters");
return -1;
}
message[0] = '\0';
format = va_arg (*varlist, const char *);
if ( level >= 2 ) /* Error message */
{
if ( logp->errprefix != NULL )
{
strncpy (message, logp->errprefix, MAX_LOG_MSG_LENGTH);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
}
else
{
strncpy (message, "Error: ", MAX_LOG_MSG_LENGTH);
}
presize = strlen(message);
retvalue = vsnprintf (&message[presize],
MAX_LOG_MSG_LENGTH - presize,
format, *varlist);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
if ( logp->diag_print != NULL )
{
logp->diag_print (message);
}
else
{
fprintf(stderr, "%s", message);
}
}
else if ( level == 1 ) /* Diagnostic message */
{
if ( logp->logprefix != NULL )
{
strncpy (message, logp->logprefix, MAX_LOG_MSG_LENGTH);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
}
presize = strlen(message);
retvalue = vsnprintf (&message[presize],
MAX_LOG_MSG_LENGTH - presize,
format, *varlist);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
if ( logp->diag_print != NULL )
{
logp->diag_print (message);
}
else
{
fprintf(stderr, "%s", message);
}
}
else if ( level == 0 ) /* Normal log message */
{
if ( logp->logprefix != NULL )
{
strncpy (message, logp->logprefix, MAX_LOG_MSG_LENGTH);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
}
presize = strlen(message);
retvalue = vsnprintf (&message[presize],
MAX_LOG_MSG_LENGTH - presize,
format, *varlist);
message[MAX_LOG_MSG_LENGTH - 1] = '\0';
if ( logp->log_print != NULL )
{
logp->log_print (message);
}
else
{
fprintf(stdout, "%s", message);
}
}
return retvalue;
} /* End of ms_log_main() */

@ -0,0 +1,235 @@
/***************************************************************************
* lookup.c:
*
* Generic lookup routines for Mini-SEED information.
*
* Written by Chad Trabant, ORFEUS/EC-Project MEREDIAN
*
* modified: 2006.346
***************************************************************************/
#include <string.h>
#include "libmseed.h"
/***************************************************************************
* ms_samplesize():
*
* Returns the sample size based on type code or 0 for unknown.
***************************************************************************/
uint8_t
ms_samplesize (const char sampletype)
{
switch (sampletype)
{
case 'a':
return 1;
case 'i':
case 'f':
return 4;
case 'd':
return 8;
default:
return 0;
} /* end switch */
} /* End of ms_samplesize() */
/***************************************************************************
* ms_encodingstr():
*
* Returns a string describing a data encoding format.
***************************************************************************/
char *
ms_encodingstr (const char encoding)
{
switch (encoding)
{
case 0:
return "ASCII text";
case 1:
return "16 bit integers";
case 2:
return "24 bit integers";
case 3:
return "32 bit integers";
case 4:
return "IEEE floating point";
case 5:
return "IEEE double precision float";
case 10:
return "STEIM 1 Compression";
case 11:
return "STEIM 2 Compression";
case 12:
return "GEOSCOPE Muxed 24 bit int";
case 13:
return "GEOSCOPE Muxed 16/3 bit gain/exp";
case 14:
return "GEOSCOPE Muxed 16/4 bit gain/exp";
case 15:
return "US National Network compression";
case 16:
return "CDSN 16 bit gain ranged";
case 17:
return "Graefenberg 16 bit gain ranged";
case 18:
return "IPG - Strasbourg 16 bit gain";
case 19:
return "STEIM 3 Compression";
case 30:
return "SRO Gain Ranged Format";
case 31:
return "HGLP Format";
case 32:
return "DWWSSN Format";
case 33:
return "RSTN 16 bit gain ranged";
default:
return "Unknown format code";
} /* end switch */
} /* End of ms_encodingstr() */
/***************************************************************************
* ms_blktdesc():
*
* Return a string describing a given blockette type or NULL if the
* type is unknown.
***************************************************************************/
char *
ms_blktdesc (uint16_t blkttype)
{
switch (blkttype)
{
case 100:
return "Sample Rate";
case 200:
return "Generic Event Detection";
case 201:
return "Murdock Event Detection";
case 300:
return "Step Calibration";
case 310:
return "Sine Calibration";
case 320:
return "Pseudo-random Calibration";
case 390:
return "Generic Calibration";
case 395:
return "Calibration Abort";
case 400:
return "Beam";
case 500:
return "Timing";
case 1000:
return "Data Only SEED";
case 1001:
return "Data Extension";
case 2000:
return "Opaque Data";
} /* end switch */
return NULL;
} /* End of ms_blktdesc() */
/***************************************************************************
* ms_blktlen():
*
* Returns the total length of a given blockette type in bytes or 0 if
* type unknown.
***************************************************************************/
uint16_t
ms_blktlen (uint16_t blkttype, const char *blkt, flag swapflag)
{
uint16_t blktlen = 0;
switch (blkttype)
{
case 100: /* Sample Rate */
blktlen = 12;
break;
case 200: /* Generic Event Detection */
blktlen = 28;
break;
case 201: /* Murdock Event Detection */
blktlen = 36;
break;
case 300: /* Step Calibration */
blktlen = 32;
break;
case 310: /* Sine Calibration */
blktlen = 32;
break;
case 320: /* Pseudo-random Calibration */
blktlen = 28;
break;
case 390: /* Generic Calibration */
blktlen = 28;
break;
case 395: /* Calibration Abort */
blktlen = 16;
break;
case 400: /* Beam */
blktlen = 16;
break;
case 500: /* Timing */
blktlen = 8;
break;
case 1000: /* Data Only SEED */
blktlen = 8;
break;
case 1001: /* Data Extension */
blktlen = 8;
break;
case 2000: /* Opaque Data */
/* First 2-byte field after the blockette header is the length */
if ( blkt )
{
memcpy ((void *) &blktlen, blkt+4, sizeof (int16_t));
if ( swapflag ) ms_gswap2 (&blktlen);
}
break;
} /* end switch */
return blktlen;
} /* End of ms_blktlen() */
/***************************************************************************
* ms_errorstr():
*
* Return a string describing a given libmseed error code or NULL if the
* code is unknown.
***************************************************************************/
char *
ms_errorstr (int errorcode)
{
switch (errorcode)
{
case MS_ENDOFFILE:
return "End of file reached";
case MS_NOERROR:
return "No error";
case MS_GENERROR:
return "Generic error";
case MS_NOTSEED:
return "No SEED data detected";
case MS_WRONGLENGTH:
return "Length of data read does not match record length";
case MS_OUTOFRANGE:
return "SEED record length out of range";
case MS_UNKNOWNFORMAT:
return "Unknown data encoding format";
case MS_STBADCOMPFLAG:
return "Bad Steim compression flag(s) detected";
} /* end switch */
return NULL;
} /* End of ms_blktdesc() */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,699 @@
/***********************************************************************
* Routines for packing INT_32, INT_16, FLOAT_32, FLOAT_64,
* STEIM1 and STEIM2 data records.
*
* Douglas Neuhauser
* Seismological Laboratory
* University of California, Berkeley
* doug@seismo.berkeley.edu
*
*
* modified Aug 2008:
* - Optimize Steim 1 & 2 packing routines using small, re-used buffers.
*
* modified Sep 2004:
* - Reworked and cleaned routines for use within libmseed.
* - Added float32 and float64 packing routines.
*
* Modified by Chad Trabant, IRIS Data Management Center
*
* modified: 2009.111
************************************************************************/
/*
* Copyright (c) 1996-2004 The Regents of the University of California.
* All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for educational, research and non-profit purposes,
* without fee, and without a written agreement is hereby granted,
* provided that the above copyright notice, this paragraph and the
* following three paragraphs appear in all copies.
*
* Permission to incorporate this software into commercial products may
* be obtained from the Office of Technology Licensing, 2150 Shattuck
* Avenue, Suite 510, Berkeley, CA 94704.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
* INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND
* ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
* PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
* CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
* UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "libmseed.h"
#include "packdata.h"
static int pad_steim_frame (DFRAMES*, int, int, int, int, int);
#define EMPTY_BLOCK(fn,wn) (fn+wn == 0)
#define X0 dframes->f[0].w[0].fw
#define XN dframes->f[0].w[1].fw
#define BIT4PACK(i,points_remaining) \
(points_remaining >= 7 && \
(minbits[i] <= 4) && (minbits[i+1] <= 4) && \
(minbits[i+2] <= 4) && (minbits[i+3] <= 4) && \
(minbits[i+4] <= 4) && (minbits[i+5] <= 4) && \
(minbits[i+6] <= 4))
#define BIT5PACK(i,points_remaining) \
(points_remaining >= 6 && \
(minbits[i] <= 5) && (minbits[i+1] <= 5) && \
(minbits[i+2] <= 5) && (minbits[i+3] <= 5) && \
(minbits[i+4] <= 5) && (minbits[i+5] <= 5))
#define BIT6PACK(i,points_remaining) \
(points_remaining >= 5 && \
(minbits[i] <= 6) && (minbits[i+1] <= 6) && \
(minbits[i+2] <= 6) && (minbits[i+3] <= 6) && \
(minbits[i+4] <= 6))
#define BYTEPACK(i,points_remaining) \
(points_remaining >= 4 && \
(minbits[i] <= 8) && (minbits[i+1] <= 8) && \
(minbits[i+2] <= 8) && (minbits[i+3] <= 8))
#define BIT10PACK(i,points_remaining) \
(points_remaining >= 3 && \
(minbits[i] <= 10) && (minbits[i+1] <= 10) && \
(minbits[i+2] <= 10))
#define BIT15PACK(i,points_remaining) \
(points_remaining >= 2 && \
(minbits[i] <= 15) && (minbits[i+1] <= 15))
#define HALFPACK(i,points_remaining) \
(points_remaining >= 2 && (minbits[i] <= 16) && (minbits[i+1] <= 16))
#define BIT30PACK(i,points_remaining) \
(points_remaining >= 1 && \
(minbits[i] <= 30))
#define MINBITS(diff,minbits) \
if (diff >= -8 && diff < 8) minbits = 4; \
else if (diff >= -16 && diff < 16) minbits = 5; \
else if (diff >= -32 && diff < 32) minbits = 6; \
else if (diff >= -128 && diff < 128) minbits = 8; \
else if (diff >= -512 && diff < 512) minbits = 10; \
else if (diff >= -16384 && diff < 16384) minbits = 15; \
else if (diff >= -32768 && diff < 32768) minbits = 16; \
else if (diff >= -536870912 && diff < 536870912) minbits = 30; \
else minbits = 32;
#define PACK(bits,n,m1,m2) { \
int i = 0; \
unsigned int val = 0; \
for (i=0;i<n;i++) { \
val = (val<<bits) | (diff[i]&m1); \
} \
val |= ((unsigned int)m2 << 30); \
dframes->f[fn].w[wn].fw = val; }
/************************************************************************
* msr_pack_int_16: *
* Pack integer data into INT_16 format. *
* Return: 0 on success, -1 on failure. *
************************************************************************/
int msr_pack_int_16
(int16_t *packed, /* output data array - packed */
int32_t *data, /* input data array */
int ns, /* desired number of samples to pack */
int max_bytes, /* max # of bytes for output buffer */
int pad, /* flag to specify padding to max_bytes */
int *pnbytes, /* number of bytes actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns; /* number of samples remaining to pack */
int i = 0;
while (points_remaining > 0 && max_bytes >= 2)
{ /* Pack the next available data into INT_16 format */
if ( data[i] < -32768 || data[i] > 32767 )
ms_log (2, "msr_pack_int_16(%s): input sample out of range: %d\n",
PACK_SRCNAME, data[i]);
*packed = data[i];
if ( swapflag ) ms_gswap2 (packed);
packed++;
max_bytes -= 2;
points_remaining--;
i++;
}
*pnbytes = (ns - points_remaining) * 2;
/* Pad miniSEED block if necessary */
if (pad)
{
memset ((void *)packed, 0, max_bytes);
*pnbytes += max_bytes;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* msr_pack_int_32: *
* Pack integer data into INT_32 format. *
* Return: 0 on success, -1 on failure. *
************************************************************************/
int msr_pack_int_32
(int32_t *packed, /* output data array - packed */
int32_t *data, /* input data array - unpacked */
int ns, /* desired number of samples to pack */
int max_bytes, /* max # of bytes for output buffer */
int pad, /* flag to specify padding to max_bytes */
int *pnbytes, /* number of bytes actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns; /* number of samples remaining to pack */
int i = 0;
while (points_remaining > 0 && max_bytes >= 4)
{ /* Pack the next available data into INT_32 format */
*packed = data[i];
if ( swapflag ) ms_gswap4 (packed);
packed++;
max_bytes -= 4;
points_remaining--;
i++;
}
*pnbytes = (ns - points_remaining) * 4;
/* Pad miniSEED block if necessary */
if (pad)
{
memset ((void *)packed, 0, max_bytes);
*pnbytes += max_bytes;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* msr_pack_float_32: *
* Pack float data into FLOAT32 format. *
* Return: 0 on success, -1 on error. *
************************************************************************/
int msr_pack_float_32
(float *packed, /* output data array - packed */
float *data, /* input data array - unpacked */
int ns, /* desired number of samples to pack */
int max_bytes, /* max # of bytes for output buffer */
int pad, /* flag to specify padding to max_bytes */
int *pnbytes, /* number of bytes actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns; /* number of samples remaining to pack */
int i = 0;
while (points_remaining > 0 && max_bytes >= 4)
{
*packed = data[i];
if ( swapflag ) ms_gswap4 (packed);
packed++;
max_bytes -= 4;
points_remaining--;
i++;
}
*pnbytes = (ns - points_remaining) * 4;
/* Pad miniSEED block if necessary */
if (pad)
{
memset ((void *)packed, 0, max_bytes);
*pnbytes += max_bytes;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* msr_pack_float_64: *
* Pack double data into FLOAT64 format. *
* Return: 0 on success, -1 on error. *
************************************************************************/
int msr_pack_float_64
(double *packed, /* output data array - packed */
double *data, /* input data array - unpacked */
int ns, /* desired number of samples to pack */
int max_bytes, /* max # of bytes for output buffer */
int pad, /* flag to specify padding to max_bytes */
int *pnbytes, /* number of bytes actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns; /* number of samples remaining to pack */
int i = 0;
while (points_remaining > 0 && max_bytes >= 8)
{
*packed = data[i];
if ( swapflag ) ms_gswap8 (packed);
packed++;
max_bytes -= 8;
points_remaining--;
i++;
}
*pnbytes = (ns - points_remaining) * 8;
/* Pad miniSEED block if necessary */
if (pad)
{
memset ((void *)packed, 0, max_bytes);
*pnbytes += max_bytes;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* msr_pack_steim1: *
* Pack data into STEIM1 data frames. *
* return: *
* 0 on success. *
* -1 on error. *
************************************************************************/
int msr_pack_steim1
(DFRAMES *dframes, /* ptr to data frames */
int32_t *data, /* ptr to unpacked data array */
int32_t d0, /* first difference value */
int ns, /* number of samples to pack */
int nf, /* total number of data frames */
int pad, /* flag to specify padding to nf */
int *pnframes, /* number of frames actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns;
int points_packed = 0;
int32_t diff[4]; /* array of differences */
uint8_t minbits[4]; /* array of minimum bits for diffs */
int i, j;
int mask;
int ipt = 0; /* index of initial data to pack. */
int fn = 0; /* index of initial frame to pack. */
int wn = 2; /* index of initial word to pack. */
int32_t itmp;
int16_t stmp;
/* Calculate initial difference and minbits buffers */
diff[0] = d0;
MINBITS(diff[0],minbits[0]);
for (i=1; i < 4 && i < ns; i++)
{
diff[i] = data[i] - data[i-1];
MINBITS(diff[i],minbits[i]);
}
dframes->f[fn].ctrl = 0;
/* Set X0 and XN values in first frame */
X0 = data[0];
if ( swapflag ) ms_gswap4 (&X0);
dframes->f[0].ctrl = (dframes->f[0].ctrl<<2) | STEIM1_SPECIAL_MASK;
XN = data[ns-1];
if ( swapflag ) ms_gswap4 (&XN);
dframes->f[0].ctrl = (dframes->f[0].ctrl<<2) | STEIM1_SPECIAL_MASK;
while (points_remaining > 0)
{
points_packed = 0;
/* Pack the next available data into the most compact form */
if (BYTEPACK(0,points_remaining))
{
mask = STEIM1_BYTE_MASK;
for (j=0; j<4; j++) { dframes->f[fn].w[wn].byte[j] = diff[j]; }
points_packed = 4;
}
else if (HALFPACK(0,points_remaining))
{
mask = STEIM1_HALFWORD_MASK;
for (j=0; j<2; j++)
{
stmp = diff[j];
if ( swapflag ) ms_gswap2 (&stmp);
dframes->f[fn].w[wn].hw[j] = stmp;
}
points_packed = 2;
}
else
{
mask = STEIM1_FULLWORD_MASK;
itmp = diff[0];
if ( swapflag ) ms_gswap4 (&itmp);
dframes->f[fn].w[wn].fw = itmp;
points_packed = 1;
}
/* Append mask for this word to current mask */
dframes->f[fn].ctrl = (dframes->f[fn].ctrl<<2) | mask;
points_remaining -= points_packed;
ipt += points_packed;
/* Check for full frame or full block */
if (++wn >= VALS_PER_FRAME)
{
if ( swapflag ) ms_gswap4 (&dframes->f[fn].ctrl);
/* Reset output index to beginning of frame */
wn = 0;
/* If block is full, output block and reinitialize */
if (++fn >= nf) break;
dframes->f[fn].ctrl = 0;
}
/* Shift and re-fill difference and minbits buffers */
for ( i=points_packed; i < 4; i++ )
{
/* Shift remaining buffer entries */
diff[i-points_packed] = diff[i];
minbits[i-points_packed] = minbits[i];
}
for ( i=4-points_packed,j=ipt+(4-points_packed); i < 4 && j < ns; i++,j++ )
{
/* Re-fill entries */
diff[i] = data[j] - data[j-1];
MINBITS(diff[i],minbits[i]);
}
}
/* Update XN value in first frame */
XN = data[(ns-1)-points_remaining];
if ( swapflag ) ms_gswap4 (&XN);
/* End of data. Pad current frame and optionally rest of block */
/* Do not pad and output a completely empty block */
if ( ! EMPTY_BLOCK(fn,wn) )
{
*pnframes = pad_steim_frame (dframes, fn, wn, nf, swapflag, pad);
}
else
{
*pnframes = 0;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* msr_pack_steim2: *
* Pack data into STEIM1 data frames. *
* return: *
* 0 on success. *
* -1 on error. *
************************************************************************/
int msr_pack_steim2
(DFRAMES *dframes, /* ptr to data frames */
int32_t *data, /* ptr to unpacked data array */
int32_t d0, /* first difference value */
int ns, /* number of samples to pack */
int nf, /* total number of data frames to pack */
int pad, /* flag to specify padding to nf */
int *pnframes, /* number of frames actually packed */
int *pnsamples, /* number of samples actually packed */
int swapflag) /* if data should be swapped */
{
int points_remaining = ns;
int points_packed = 0;
int32_t diff[7]; /* array of differences */
uint8_t minbits[7]; /* array of minimum bits for diffs */
int i, j;
int mask;
int ipt = 0; /* index of initial data to pack. */
int fn = 0; /* index of initial frame to pack. */
int wn = 2; /* index of initial word to pack. */
/* Calculate initial difference and minbits buffers */
diff[0] = d0;
MINBITS(diff[0],minbits[0]);
for (i=1; i < 7 && i < ns; i++)
{
diff[i] = data[i] - data[i-1];
MINBITS(diff[i],minbits[i]);
}
dframes->f[fn].ctrl = 0;
/* Set X0 and XN values in first frame */
X0 = data[0];
if ( swapflag ) ms_gswap4 (&X0);
dframes->f[0].ctrl = (dframes->f[0].ctrl<<2) | STEIM2_SPECIAL_MASK;
XN = data[ns-1];
if ( swapflag ) ms_gswap4 (&XN);
dframes->f[0].ctrl = (dframes->f[0].ctrl<<2) | STEIM2_SPECIAL_MASK;
while (points_remaining > 0)
{
points_packed = 0;
/* Pack the next available datapoints into the most compact form */
if (BIT4PACK(0,points_remaining))
{
PACK(4,7,0x0000000f,02)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_567_MASK;
points_packed = 7;
}
else if (BIT5PACK(0,points_remaining))
{
PACK(5,6,0x0000001f,01)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_567_MASK;
points_packed = 6;
}
else if (BIT6PACK(0,points_remaining))
{
PACK(6,5,0x0000003f,00)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_567_MASK;
points_packed = 5;
}
else if (BYTEPACK(0,points_remaining))
{
mask = STEIM2_BYTE_MASK;
for (j=0; j<4; j++) dframes->f[fn].w[wn].byte[j] = diff[j];
points_packed = 4;
}
else if (BIT10PACK(0,points_remaining))
{
PACK(10,3,0x000003ff,03)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_123_MASK;
points_packed = 3;
}
else if (BIT15PACK(0,points_remaining))
{
PACK(15,2,0x00007fff,02)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_123_MASK;
points_packed = 2;
}
else if (BIT30PACK(0,points_remaining))
{
PACK(30,1,0x3fffffff,01)
if ( swapflag ) ms_gswap4 (&dframes->f[fn].w[wn].fw);
mask = STEIM2_123_MASK;
points_packed = 1;
}
else
{
ms_log (2, "msr_pack_steim2(%s): Unable to represent difference in <= 30 bits\n",
PACK_SRCNAME);
return -1;
}
/* Append mask for this word to current mask */
dframes->f[fn].ctrl = (dframes->f[fn].ctrl<<2) | mask;
points_remaining -= points_packed;
ipt += points_packed;
/* Check for full frame or full block */
if (++wn >= VALS_PER_FRAME)
{
if ( swapflag ) ms_gswap4 (&dframes->f[fn].ctrl);
/* Reset output index to beginning of frame */
wn = 0;
/* If block is full, output block and reinitialize */
if (++fn >= nf) break;
dframes->f[fn].ctrl = 0;
}
/* Shift and re-fill difference and minbits buffers */
for ( i=points_packed; i < 7; i++ )
{
/* Shift remaining buffer entries */
diff[i-points_packed] = diff[i];
minbits[i-points_packed] = minbits[i];
}
for ( i=7-points_packed,j=ipt+(7-points_packed); i < 7 && j < ns; i++,j++ )
{
/* Re-fill entries */
diff[i] = data[j] - data[j-1];
MINBITS(diff[i],minbits[i]);
}
}
/* Update XN value in first frame */
XN = data[(ns-1)-points_remaining];
if ( swapflag ) ms_gswap4 (&XN);
/* End of data. Pad current frame and optionally rest of block */
/* Do not pad and output a completely empty block */
if ( ! EMPTY_BLOCK(fn,wn) )
{
*pnframes = pad_steim_frame (dframes, fn, wn, nf, swapflag, pad);
}
else
{
*pnframes = 0;
}
*pnsamples = ns - points_remaining;
return 0;
}
/************************************************************************
* pad_steim_frame: *
* Pad the rest of the data record with null values, *
* and optionally the rest of the total number of frames. *
* return: *
* total number of frames in record. *
************************************************************************/
static int pad_steim_frame
(DFRAMES *dframes,
int fn, /* current frame number. */
int wn, /* current work number. */
int nf, /* total number of data frames. */
int swapflag, /* flag to swap byte order of data. */
int pad) /* flag to pad # frames to nf. */
{
/* Finish off the current frame */
if (wn < VALS_PER_FRAME && fn < nf)
{
for (; wn < VALS_PER_FRAME; wn++)
{
dframes->f[fn].w[wn].fw = 0;
dframes->f[fn].ctrl = (dframes->f[fn].ctrl<<2) | STEIM1_SPECIAL_MASK;
}
if ( swapflag ) ms_gswap4 (&dframes->f[fn].ctrl);
fn++;
}
/* Fill the remaining frames in the block */
if (pad)
{
for (; fn<nf; fn++)
{
dframes->f[fn].ctrl = STEIM1_SPECIAL_MASK; /* mask for ctrl */
for (wn=0; wn<VALS_PER_FRAME; wn++)
{
dframes->f[fn].w[wn].fw = 0;
dframes->f[fn].ctrl = (dframes->f[fn].ctrl<<2) | STEIM1_SPECIAL_MASK;
}
if ( swapflag ) ms_gswap4 (&dframes->f[fn].ctrl);
}
}
return fn;
}
/************************************************************************
* msr_pack_text: *
* Pack text data into text format. Split input data on line *
* breaks so as to not split lines between records. *
* Return: 0 on success, -1 on error. *
************************************************************************/
int msr_pack_text
(char *packed, /* output data array - packed. */
char *data, /* input data array - unpacked. */
int ns, /* desired number of samples to pack. */
int max_bytes, /* max # of bytes for output buffer. */
int pad, /* flag to specify padding to max_bytes.*/
int *pnbytes, /* number of bytes actually packed. */
int *pnsamples) /* number of samples actually packed. */
{
int points_remaining = ns; /* number of samples remaining to pack. */
int last = -1;
int nbytes;
int i;
/* Split lines only if a single line will not fit in 1 record */
if (points_remaining > max_bytes)
{
/* Look for the last newline that will fit in output buffer */
for (i=max_bytes-1; i>=0; i--)
{
if (data[i] == '\n') {
last = i;
break;
}
}
if (last < 0) last = max_bytes - 1;
}
if (last < 0) last = points_remaining - 1;
nbytes = last + 1;
memcpy (packed, data, nbytes);
packed += nbytes;
max_bytes -= nbytes;
*pnbytes = nbytes;
*pnsamples = nbytes;
points_remaining -= nbytes;
/* Pad miniSEED block if necessary */
if (pad)
{
memset ((void *)packed, 0, max_bytes);
*pnbytes += max_bytes;
}
*pnsamples = ns - points_remaining;
return 0;
}

@ -0,0 +1,35 @@
/***************************************************************************
* packdata.h:
*
* Interface declarations for the Mini-SEED packing routines in
* packdata.c
*
* modified: 2008.220
***************************************************************************/
#ifndef PACKDATA_H
#define PACKDATA_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include "steimdata.h"
/* Pointer to srcname of record being packed, declared in pack.c */
extern char *PACK_SRCNAME;
extern int msr_pack_int_16 (int16_t*, int32_t*, int, int, int, int*, int*, int);
extern int msr_pack_int_32 (int32_t*, int32_t*, int, int, int, int*, int*, int);
extern int msr_pack_float_32 (float*, float*, int, int, int, int*, int*, int);
extern int msr_pack_float_64 (double*, double*, int, int, int, int*, int*, int);
extern int msr_pack_steim1 (DFRAMES*, int32_t*, int32_t, int, int, int, int*, int*, int);
extern int msr_pack_steim2 (DFRAMES*, int32_t*, int32_t, int, int, int, int*, int*, int);
extern int msr_pack_text (char *, char *, int, int, int, int*, int*);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,680 @@
/***************************************************************************
* selection.c:
*
* Generic routines to manage selection lists.
*
* Written by Chad Trabant unless otherwise noted
* IRIS Data Management Center
*
* modified: 2014.197
***************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "libmseed.h"
static int ms_globmatch (char *string, char *pattern);
/***************************************************************************
* ms_matchselect:
*
* Test the specified parameters for a matching selection entry. The
* srcname parameter may contain globbing characters. The NULL value
* (matching any times) for the start and end times is HPTERROR.
*
* Return Selections pointer to matching entry on successful match and
* NULL for no match or error.
***************************************************************************/
Selections *
ms_matchselect (Selections *selections, char *srcname, hptime_t starttime,
hptime_t endtime, SelectTime **ppselecttime)
{
Selections *findsl = NULL;
SelectTime *findst = NULL;
SelectTime *matchst = NULL;
if ( selections )
{
findsl = selections;
while ( findsl )
{
if ( ms_globmatch (srcname, findsl->srcname) )
{
findst = findsl->timewindows;
while ( findst )
{
if ( starttime != HPTERROR && findst->starttime != HPTERROR &&
(starttime < findst->starttime && ! (starttime <= findst->starttime && endtime >= findst->starttime)) )
{ findst = findst->next; continue; }
else if ( endtime != HPTERROR && findst->endtime != HPTERROR &&
(endtime > findst->endtime && ! (starttime <= findst->endtime && endtime >= findst->endtime)) )
{ findst = findst->next; continue; }
matchst = findst;
break;
}
}
if ( matchst )
break;
else
findsl = findsl->next;
}
}
if ( ppselecttime )
*ppselecttime = matchst;
return ( matchst ) ? findsl : NULL;
} /* End of ms_matchselect() */
/***************************************************************************
* msr_matchselect:
*
* A simple wrapper for calling ms_matchselect() using details from a
* MSRecord struct.
*
* Return Selections pointer to matching entry on successful match and
* NULL for no match or error.
***************************************************************************/
Selections *
msr_matchselect (Selections *selections, MSRecord *msr, SelectTime **ppselecttime)
{
char srcname[50];
hptime_t endtime;
if ( ! selections || ! msr )
return NULL;
msr_srcname (msr, srcname, 1);
endtime = msr_endtime (msr);
return ms_matchselect (selections, srcname, msr->starttime, endtime,
ppselecttime);
} /* End of msr_matchselect() */
/***************************************************************************
* ms_addselect:
*
* Add select parameters to a specified selection list. The srcname
* argument may contain globbing parameters. The NULL value (matching
* any value) for the start and end times is HPTERROR.
*
* Return 0 on success and -1 on error.
***************************************************************************/
int
ms_addselect (Selections **ppselections, char *srcname,
hptime_t starttime, hptime_t endtime)
{
Selections *newsl = NULL;
SelectTime *newst = NULL;
if ( ! ppselections || ! srcname )
return -1;
/* Allocate new SelectTime and populate */
if ( ! (newst = (SelectTime *) calloc (1, sizeof(SelectTime))) )
{
ms_log (2, "Cannot allocate memory\n");
return -1;
}
newst->starttime = starttime;
newst->endtime = endtime;
/* Add new Selections struct to begining of list */
if ( ! *ppselections )
{
/* Allocate new Selections and populate */
if ( ! (newsl = (Selections *) calloc (1, sizeof(Selections))) )
{
ms_log (2, "Cannot allocate memory\n");
return -1;
}
strncpy (newsl->srcname, srcname, sizeof(newsl->srcname));
newsl->srcname[sizeof(newsl->srcname) - 1] = '\0';
/* Add new Selections struct as first in list */
*ppselections = newsl;
newsl->timewindows = newst;
}
else
{
Selections *findsl = *ppselections;
Selections *matchsl = 0;
/* Search for matching Selectlink entry */
while ( findsl )
{
if ( ! strcmp (findsl->srcname, srcname) )
{
matchsl = findsl;
break;
}
findsl = findsl->next;
}
if ( matchsl )
{
/* Add time window selection to beginning of window list */
newst->next = matchsl->timewindows;
matchsl->timewindows = newst;
}
else
{
/* Allocate new Selections and populate */
if ( ! (newsl = (Selections *) calloc (1, sizeof(Selections))) )
{
ms_log (2, "Cannot allocate memory\n");
return -1;
}
strncpy (newsl->srcname, srcname, sizeof(newsl->srcname));
newsl->srcname[sizeof(newsl->srcname) - 1] = '\0';
/* Add new Selections to beginning of list */
newsl->next = *ppselections;
*ppselections = newsl;
newsl->timewindows = newst;
}
}
return 0;
} /* End of ms_addselect() */
/***************************************************************************
* ms_addselect_comp:
*
* Add select parameters to a specified selection list based on
* separate name components. The network, station, location, channel
* and quality arguments may contain globbing parameters. The NULL
* value (matching any value) for the start and end times is HPTERROR.
*
* If any of the naming parameters are not supplied (pointer is NULL)
* a wildcard for all matches is substituted. As a special case, if
* the location ID (loc) is set to "--" to match a space-space/blank
* ID it will be translated to an empty string to match libmseed's
* notation.
*
* Return 0 on success and -1 on error.
***************************************************************************/
int
ms_addselect_comp (Selections **ppselections, char *net, char* sta, char *loc,
char *chan, char *qual, hptime_t starttime, hptime_t endtime)
{
char srcname[100];
char selnet[20];
char selsta[20];
char selloc[20];
char selchan[20];
char selqual[20];
if ( ! ppselections )
return -1;
if ( net )
{
strncpy (selnet, net, sizeof(selnet));
selnet[sizeof(selnet)-1] = '\0';
}
else
strcpy (selnet, "*");
if ( sta )
{
strncpy (selsta, sta, sizeof(selsta));
selsta[sizeof(selsta)-1] = '\0';
}
else
strcpy (selsta, "*");
if ( loc )
{
/* Test for special case blank location ID */
if ( ! strcmp (loc, "--") )
selloc[0] = '\0';
else
{
strncpy (selloc, loc, sizeof(selloc));
selloc[sizeof(selloc)-1] = '\0';
}
}
else
strcpy (selloc, "*");
if ( chan )
{
strncpy (selchan, chan, sizeof(selchan));
selchan[sizeof(selchan)-1] = '\0';
}
else
strcpy (selchan, "*");
if ( qual )
{
strncpy (selqual, qual, sizeof(selqual));
selqual[sizeof(selqual)-1] = '\0';
}
else
strcpy (selqual, "?");
/* Create the srcname globbing match for this entry */
snprintf (srcname, sizeof(srcname), "%s_%s_%s_%s_%s",
selnet, selsta, selloc, selchan, selqual);
/* Add selection to list */
if ( ms_addselect (ppselections, srcname, starttime, endtime) )
return -1;
return 0;
} /* End of ms_addselect_comp() */
/***************************************************************************
* ms_readselectionsfile:
*
* Read a list of data selections from a file and them to the
* specified selections list. On errors this routine will leave
* allocated memory unreachable (leaked), it is expected that this is
* a program failing condition.
*
* As a special case if the filename is "-", selection lines will be
* read from stdin.
*
* Returns count of selections added on success and -1 on error.
***************************************************************************/
int
ms_readselectionsfile (Selections **ppselections, char *filename)
{
FILE *fp;
hptime_t starttime;
hptime_t endtime;
char selectline[200];
char *selnet;
char *selsta;
char *selloc;
char *selchan;
char *selqual;
char *selstart;
char *selend;
char *cp;
char next;
int selectcount = 0;
int linecount = 0;
if ( ! ppselections || ! filename )
return -1;
if ( strcmp (filename, "-" ) )
{
if ( ! (fp = fopen(filename, "rb")) )
{
ms_log (2, "Cannot open file %s: %s\n", filename, strerror(errno));
return -1;
}
}
else
{
/* Use stdin as special case */
fp = stdin;
}
while ( fgets (selectline, sizeof(selectline)-1, fp) )
{
selnet = 0;
selsta = 0;
selloc = 0;
selchan = 0;
selqual = 0;
selstart = 0;
selend = 0;
linecount++;
/* Guarantee termination */
selectline[sizeof(selectline)-1] = '\0';
/* End string at first newline character if any */
if ( (cp = strchr(selectline, '\n')) )
*cp = '\0';
/* Skip empty lines */
if ( ! strlen (selectline) )
continue;
/* Skip comment lines */
if ( *selectline == '#' )
continue;
/* Parse: identify components of selection and terminate */
cp = selectline;
next = 1;
while ( *cp )
{
if ( *cp == ' ' || *cp == '\t' ) { *cp = '\0'; next = 1; }
else if ( *cp == '#' ) { *cp = '\0'; break; }
else if ( next && ! selnet ) { selnet = cp; next = 0; }
else if ( next && ! selsta ) { selsta = cp; next = 0; }
else if ( next && ! selloc ) { selloc = cp; next = 0; }
else if ( next && ! selchan ) { selchan = cp; next = 0; }
else if ( next && ! selqual ) { selqual = cp; next = 0; }
else if ( next && ! selstart ) { selstart = cp; next = 0; }
else if ( next && ! selend ) { selend = cp; next = 0; }
else if ( next ) { *cp = '\0'; break; }
cp++;
}
/* Skip line if network, station, location and channel are not defined */
if ( ! selnet || ! selsta || ! selloc || ! selchan )
{
ms_log (2, "[%s] Skipping data selection line number %d\n", filename, linecount);
continue;
}
if ( selstart )
{
starttime = ms_seedtimestr2hptime (selstart);
if ( starttime == HPTERROR )
{
ms_log (2, "Cannot convert data selection start time (line %d): %s\n", linecount, selstart);
return -1;
}
}
else
{
starttime = HPTERROR;
}
if ( selend )
{
endtime = ms_seedtimestr2hptime (selend);
if ( endtime == HPTERROR )
{
ms_log (2, "Cannot convert data selection end time (line %d): %s\n", linecount, selend);
return -1;
}
}
else
{
endtime = HPTERROR;
}
/* Add selection to list */
if ( ms_addselect_comp (ppselections, selnet, selsta, selloc, selchan, selqual, starttime, endtime) )
{
ms_log (2, "[%s] Error adding selection on line %d\n", filename, linecount);
return -1;
}
selectcount++;
}
if ( fp != stdin )
fclose (fp);
return selectcount;
} /* End of ms_readselectionsfile() */
/***************************************************************************
* ms_freeselections:
*
* Free all memory associated with a Selections struct.
***************************************************************************/
void
ms_freeselections ( Selections *selections )
{
Selections *select;
Selections *selectnext;
SelectTime *selecttime;
SelectTime *selecttimenext;
if ( selections )
{
select = selections;
while ( select )
{
selectnext = select->next;
selecttime = select->timewindows;
while ( selecttime )
{
selecttimenext = selecttime->next;
free (selecttime);
selecttime = selecttimenext;
}
free (select);
select = selectnext;
}
}
} /* End of ms_freeselections() */
/***************************************************************************
* ms_printselections:
*
* Print the selections list using the ms_log() facility.
***************************************************************************/
void
ms_printselections ( Selections *selections )
{
Selections *select;
SelectTime *selecttime;
char starttime[50];
char endtime[50];
if ( ! selections )
return;
select = selections;
while ( select )
{
ms_log (0, "Selection: %s\n", select->srcname);
selecttime = select->timewindows;
while ( selecttime )
{
if ( selecttime->starttime != HPTERROR )
ms_hptime2seedtimestr (selecttime->starttime, starttime, 1);
else
strncpy (starttime, "No start time", sizeof(starttime)-1);
if ( selecttime->endtime != HPTERROR )
ms_hptime2seedtimestr (selecttime->endtime, endtime, 1);
else
strncpy (endtime, "No end time", sizeof(endtime)-1);
ms_log (0, " %30s %30s\n", starttime, endtime);
selecttime = selecttime->next;
}
select = select->next;
}
} /* End of ms_printselections() */
/***********************************************************************
* robust glob pattern matcher
* ozan s. yigit/dec 1994
* public domain
*
* glob patterns:
* * matches zero or more characters
* ? matches any single character
* [set] matches any character in the set
* [^set] matches any character NOT in the set
* where a set is a group of characters or ranges. a range
* is written as two characters seperated with a hyphen: a-z denotes
* all characters between a to z inclusive.
* [-set] set matches a literal hypen and any character in the set
* []set] matches a literal close bracket and any character in the set
*
* char matches itself except where char is '*' or '?' or '['
* \char matches char, including any pattern character
*
* examples:
* a*c ac abc abbc ...
* a?c acc abc aXc ...
* a[a-z]c aac abc acc ...
* a[-a-z]c a-c aac abc ...
*
* Revision 1.4 2004/12/26 12:38:00 ct
* Changed function name (amatch -> globmatch), variables and
* formatting for clarity. Also add matching header globmatch.h.
*
* Revision 1.3 1995/09/14 23:24:23 oz
* removed boring test/main code.
*
* Revision 1.2 94/12/11 10:38:15 oz
* charset code fixed. it is now robust and interprets all
* variations of charset [i think] correctly, including [z-a] etc.
*
* Revision 1.1 94/12/08 12:45:23 oz
* Initial revision
***********************************************************************/
#define GLOBMATCH_TRUE 1
#define GLOBMATCH_FALSE 0
#define GLOBMATCH_NEGATE '^' /* std char set negation char */
/***********************************************************************
* ms_globmatch:
*
* Check if a string matches a globbing pattern.
*
* Return 0 if string does not match pattern and non-zero otherwise.
**********************************************************************/
static int
ms_globmatch (char *string, char *pattern)
{
int negate;
int match;
int c;
while ( *pattern )
{
if ( !*string && *pattern != '*' )
return GLOBMATCH_FALSE;
switch ( c = *pattern++ )
{
case '*':
while ( *pattern == '*' )
pattern++;
if ( !*pattern )
return GLOBMATCH_TRUE;
if ( *pattern != '?' && *pattern != '[' && *pattern != '\\' )
while ( *string && *pattern != *string )
string++;
while ( *string )
{
if ( ms_globmatch(string, pattern) )
return GLOBMATCH_TRUE;
string++;
}
return GLOBMATCH_FALSE;
case '?':
if ( *string )
break;
return GLOBMATCH_FALSE;
/* set specification is inclusive, that is [a-z] is a, z and
* everything in between. this means [z-a] may be interpreted
* as a set that contains z, a and nothing in between.
*/
case '[':
if ( *pattern != GLOBMATCH_NEGATE )
negate = GLOBMATCH_FALSE;
else
{
negate = GLOBMATCH_TRUE;
pattern++;
}
match = GLOBMATCH_FALSE;
while ( !match && (c = *pattern++) )
{
if ( !*pattern )
return GLOBMATCH_FALSE;
if ( *pattern == '-' ) /* c-c */
{
if ( !*++pattern )
return GLOBMATCH_FALSE;
if ( *pattern != ']' )
{
if ( *string == c || *string == *pattern ||
( *string > c && *string < *pattern ) )
match = GLOBMATCH_TRUE;
}
else
{ /* c-] */
if ( *string >= c )
match = GLOBMATCH_TRUE;
break;
}
}
else /* cc or c] */
{
if ( c == *string )
match = GLOBMATCH_TRUE;
if ( *pattern != ']' )
{
if ( *pattern == *string )
match = GLOBMATCH_TRUE;
}
else
break;
}
}
if ( negate == match )
return GLOBMATCH_FALSE;
/*
* if there is a match, skip past the charset and continue on
*/
while ( *pattern && *pattern != ']' )
pattern++;
if ( !*pattern++ ) /* oops! */
return GLOBMATCH_FALSE;
break;
case '\\':
if ( *pattern )
c = *pattern++;
default:
if ( c != *string )
return GLOBMATCH_FALSE;
break;
}
string++;
}
return !*string;
} /* End of ms_globmatch() */

@ -0,0 +1,51 @@
/***************************************************************************
* steimdata.h:
*
* Declarations for Steim compression routines.
*
* modified: 2004.278
***************************************************************************/
#ifndef STEIMDATA_H
#define STEIMDATA_H 1
#ifdef __cplusplus
extern "C" {
#endif
#define STEIM1_FRAME_MAX_SAMPLES 60
#define STEIM2_FRAME_MAX_SAMPLES 105
#define VALS_PER_FRAME 15 /* # of ints for data per frame.*/
#define STEIM1_SPECIAL_MASK 0
#define STEIM1_BYTE_MASK 1
#define STEIM1_HALFWORD_MASK 2
#define STEIM1_FULLWORD_MASK 3
#define STEIM2_SPECIAL_MASK 0
#define STEIM2_BYTE_MASK 1
#define STEIM2_123_MASK 2
#define STEIM2_567_MASK 3
typedef union u_diff { /* union for Steim objects. */
int8_t byte[4]; /* 4 1-byte differences. */
int16_t hw[2]; /* 2 halfword differences. */
int32_t fw; /* 1 fullword difference. */
} U_DIFF;
typedef struct frame { /* frame in a seed data record. */
uint32_t ctrl; /* control word for frame. */
U_DIFF w[15]; /* compressed data. */
} FRAME;
typedef struct dframes { /* seed data frames. */
FRAME f[1]; /* data record header frames. */
} DFRAMES;
#ifdef __cplusplus
}
#endif
#endif /* STEIMDATA_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,892 @@
/************************************************************************
* Routines for unpacking INT_16, INT_32, FLOAT_32, FLOAT_64,
* STEIM1, STEIM2, GEOSCOPE (24bit and gain ranged), CDSN, SRO
* and DWWSSN encoded data records.
*
* Some routines originated and were borrowed from qlib2 by:
*
* Douglas Neuhauser
* Seismographic Station
* University of California, Berkeley
* doug@seismo.berkeley.edu
*
* Modified by Chad Trabant,
* (previously) ORFEUS/EC-Project MEREDIAN
* (currently) IRIS Data Management Center
*
* modified: 2012.357
************************************************************************/
/*
* Copyright (c) 1996 The Regents of the University of California.
* All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for educational, research and non-profit purposes,
* without fee, and without a written agreement is hereby granted,
* provided that the above copyright notice, this paragraph and the
* following three paragraphs appear in all copies.
*
* Permission to incorporate this software into commercial products may
* be obtained from the Office of Technology Licensing, 2150 Shattuck
* Avenue, Suite 510, Berkeley, CA 94704.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
* INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND
* ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
* PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
* CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
* UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include "libmseed.h"
#include "unpackdata.h"
#define MAX12 0x7ff /* maximum 12 bit positive # */
#define MAX14 0x1fff /* maximum 14 bit positive # */
#define MAX16 0x7fff /* maximum 16 bit positive # */
#define MAX24 0x7fffff /* maximum 24 bit positive # */
/* For Steim encodings */
#define X0 pf->w[0].fw
#define XN pf->w[1].fw
/************************************************************************
* msr_unpack_int_16: *
* *
* Unpack int_16 miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_int_16
(int16_t *ibuf, /* ptr to input data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int nd = 0; /* # of data points in packet. */
int16_t stmp;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++) {
stmp = ibuf[nd];
if ( swapflag ) ms_gswap2a (&stmp);
databuff[nd] = stmp;
}
return nd;
} /* End of msr_unpack_int_16() */
/************************************************************************
* msr_unpack_int_32: *
* *
* Unpack int_32 miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_int_32
(int32_t *ibuf, /* ptr to input data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int nd = 0; /* # of data points in packet. */
int32_t itmp;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++) {
itmp = ibuf[nd];
if ( swapflag) ms_gswap4a (&itmp);
databuff[nd] = itmp;
}
return nd;
} /* End of msr_unpack_int_32() */
/************************************************************************
* msr_unpack_float_32: *
* *
* Unpack float_32 miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_float_32
(float *fbuf, /* ptr to input data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
float *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int nd = 0; /* # of data points in packet. */
float ftmp;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++) {
memcpy (&ftmp, &fbuf[nd], sizeof(float));
if ( swapflag ) ms_gswap4a (&ftmp);
databuff[nd] = ftmp;
}
return nd;
} /* End of msr_unpack_float_32() */
/************************************************************************
* msr_unpack_float_64: *
* *
* Unpack float_64 miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_float_64
(double *fbuf, /* ptr to input data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
double *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int nd = 0; /* # of data points in packet. */
double dtmp;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++) {
memcpy (&dtmp, &fbuf[nd], sizeof(double));
if ( swapflag ) ms_gswap8a (&dtmp);
databuff[nd] = dtmp;
}
return nd;
} /* End of msr_unpack_float_64() */
/************************************************************************
* msr_unpack_steim1: *
* *
* Unpack STEIM1 data frames and place in supplied buffer. *
* See the SEED format manual for Steim-1 encoding details. *
* *
* Return: # of samples returned or negative error code. *
************************************************************************/
int msr_unpack_steim1
(FRAME *pf, /* ptr to Steim1 data frames. */
int nbytes, /* number of bytes in all data frames. */
int num_samples, /* number of data samples in all frames.*/
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int32_t *diffbuff, /* ptr to unpacked diff array. */
int32_t *px0, /* return X0, first sample in frame. */
int32_t *pxn, /* return XN, last sample in frame. */
int swapflag, /* if data should be swapped. */
int verbose)
{
int32_t *diff = diffbuff;
int32_t *data = databuff;
int32_t *prev;
int num_data_frames = nbytes / sizeof(FRAME);
int nd = 0; /* # of data points in packet. */
int fn; /* current frame number. */
int wn; /* current work number in the frame. */
int compflag; /* current compression flag. */
int nr, i;
int32_t last_data;
int32_t itmp;
int16_t stmp;
uint32_t ctrl;
if (num_samples < 0) return 0;
if (num_samples == 0) return 0;
if (req_samples < 0) return 0;
/* Extract forward and reverse integration constants in first frame */
*px0 = X0;
*pxn = XN;
if ( swapflag )
{
ms_gswap4a (px0);
ms_gswap4a (pxn);
}
if ( verbose > 2 )
ms_log (1, "%s: forward/reverse integration constants:\nX0: %d XN: %d\n",
UNPACK_SRCNAME, *px0, *pxn);
/* Decode compressed data in each frame */
for (fn = 0; fn < num_data_frames; fn++)
{
ctrl = pf->ctrl;
if ( swapflag ) ms_gswap4a (&ctrl);
for (wn = 0; wn < VALS_PER_FRAME; wn++)
{
if (nd >= num_samples) break;
compflag = (ctrl >> ((VALS_PER_FRAME-wn-1)*2)) & 0x3;
switch (compflag)
{
case STEIM1_SPECIAL_MASK:
/* Headers info -- skip it */
break;
case STEIM1_BYTE_MASK:
/* Next 4 bytes are 4 1-byte differences */
for (i=0; i < 4 && nd < num_samples; i++, nd++)
*diff++ = pf->w[wn].byte[i];
break;
case STEIM1_HALFWORD_MASK:
/* Next 4 bytes are 2 2-byte differences */
for (i=0; i < 2 && nd < num_samples; i++, nd++)
{
if ( swapflag )
{
stmp = pf->w[wn].hw[i];
ms_gswap2a (&stmp);
*diff++ = stmp;
}
else *diff++ = pf->w[wn].hw[i];
}
break;
case STEIM1_FULLWORD_MASK:
/* Next 4 bytes are 1 4-byte difference */
if ( swapflag )
{
itmp = pf->w[wn].fw;
ms_gswap4a (&itmp);
*diff++ = itmp;
}
else *diff++ = pf->w[wn].fw;
nd++;
break;
default:
/* Should NEVER get here */
ms_log (2, "msr_unpack_steim1(%s): invalid compression flag = %d\n",
UNPACK_SRCNAME, compflag);
return MS_STBADCOMPFLAG;
}
}
++pf;
}
/* Test if the number of samples implied by the data frames is the
* same number indicated in the header.
*/
if ( nd != num_samples )
{
ms_log (1, "Warning: msr_unpack_steim1(%s): number of samples indicated in header (%d) does not equal data (%d)\n",
UNPACK_SRCNAME, num_samples, nd);
}
/* For now, assume sample count in header to be correct. */
/* One way of "trimming" data from a block is simply to reduce */
/* the sample count. It is not clear from the documentation */
/* whether this is a valid or not, but it appears to be done */
/* by other program, so we should not complain about its effect. */
nr = req_samples;
/* Compute first value based on last_value from previous buffer. */
/* The two should correspond in all cases EXCEPT for the first */
/* record for each component (because we don't have a valid xn from */
/* a previous record). Although the Steim compression algorithm */
/* defines x(-1) as 0 for the first record, this only works for the */
/* first record created since coldstart of the datalogger, NOT the */
/* first record of an arbitrary starting record. */
/* In all cases, assume x0 is correct, since we don't have x(-1). */
data = databuff;
diff = diffbuff;
last_data = *px0;
if (nr > 0)
*data = *px0;
/* Compute all but first values based on previous value */
prev = data - 1;
while (--nr > 0 && --nd > 0)
last_data = *++data = *++diff + *++prev;
/* If a short count was requested compute the last sample in order */
/* to perform the integrity check comparison */
while (--nd > 0)
last_data = *++diff + last_data;
/* Verify that the last value is identical to xn = rev. int. constant */
if (last_data != *pxn)
{
ms_log (1, "%s: Warning: Data integrity check for Steim-1 failed, last_data=%d, xn=%d\n",
UNPACK_SRCNAME, last_data, *pxn);
}
return ((req_samples < num_samples) ? req_samples : num_samples);
} /* End of msr_unpack_steim1() */
/************************************************************************
* msr_unpack_steim2: *
* *
* Unpack STEIM2 data frames and place in supplied buffer. *
* See the SEED format manual for Steim-2 encoding details. *
* *
* Return: # of samples returned or negative error code. *
************************************************************************/
int msr_unpack_steim2
(FRAME *pf, /* ptr to Steim2 data frames. */
int nbytes, /* number of bytes in all data frames. */
int num_samples, /* number of data samples in all frames.*/
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int32_t *diffbuff, /* ptr to unpacked diff array. */
int32_t *px0, /* return X0, first sample in frame. */
int32_t *pxn, /* return XN, last sample in frame. */
int swapflag, /* if data should be swapped. */
int verbose)
{
int32_t *diff = diffbuff;
int32_t *data = databuff;
int32_t *prev;
int num_data_frames = nbytes / sizeof(FRAME);
int nd = 0; /* # of data points in packet. */
int fn; /* current frame number. */
int wn; /* current work number in the frame. */
int compflag; /* current compression flag. */
int nr, i;
int n, bits, m1, m2;
int32_t last_data;
int32_t val;
int8_t dnib;
uint32_t ctrl;
if (num_samples < 0) return 0;
if (num_samples == 0) return 0;
if (req_samples < 0) return 0;
/* Extract forward and reverse integration constants in first frame.*/
*px0 = X0;
*pxn = XN;
if ( swapflag )
{
ms_gswap4a (px0);
ms_gswap4a (pxn);
}
if ( verbose > 2 )
ms_log (1, "%s: forward/reverse integration constants: X0: %d XN: %d\n",
UNPACK_SRCNAME, *px0, *pxn);
/* Decode compressed data in each frame */
for (fn = 0; fn < num_data_frames; fn++)
{
ctrl = pf->ctrl;
if ( swapflag ) ms_gswap4a (&ctrl);
for (wn = 0; wn < VALS_PER_FRAME; wn++)
{
if (nd >= num_samples) break;
compflag = (ctrl >> ((VALS_PER_FRAME-wn-1)*2)) & 0x3;
switch (compflag)
{
case STEIM2_SPECIAL_MASK:
/* Headers info -- skip it */
break;
case STEIM2_BYTE_MASK:
/* Next 4 bytes are 4 1-byte differences */
for (i=0; i < 4 && nd < num_samples; i++, nd++)
*diff++ = pf->w[wn].byte[i];
break;
case STEIM2_123_MASK:
val = pf->w[wn].fw;
if ( swapflag ) ms_gswap4a (&val);
dnib = val >> 30 & 0x3;
switch (dnib)
{
case 1: /* 1 30-bit difference */
bits = 30; n = 1; m1 = 0x3fffffff; m2 = 0x20000000; break;
case 2: /* 2 15-bit differences */
bits = 15; n = 2; m1 = 0x00007fff; m2 = 0x00004000; break;
case 3: /* 3 10-bit differences */
bits = 10; n = 3; m1 = 0x000003ff; m2 = 0x00000200; break;
default: /* should NEVER get here */
ms_log (2, "msr_unpack_steim2(%s): invalid compflag, dnib, fn, wn = %d, %d, %d, %d\n",
UNPACK_SRCNAME, compflag, dnib, fn, wn);
return MS_STBADCOMPFLAG;
}
/* Uncompress the differences */
for (i=(n-1)*bits; i >= 0 && nd < num_samples; i-=bits, nd++)
{
*diff = (val >> i) & m1;
*diff = (*diff & m2) ? *diff | ~m1 : *diff;
diff++;
}
break;
case STEIM2_567_MASK:
val = pf->w[wn].fw;
if ( swapflag ) ms_gswap4a (&val);
dnib = val >> 30 & 0x3;
switch (dnib)
{
case 0: /* 5 6-bit differences */
bits = 6; n = 5; m1 = 0x0000003f; m2 = 0x00000020; break;
case 1: /* 6 5-bit differences */
bits = 5; n = 6; m1 = 0x0000001f; m2 = 0x00000010; break;
case 2: /* 7 4-bit differences */
bits = 4; n = 7; m1 = 0x0000000f; m2 = 0x00000008; break;
default:
ms_log (2, "msr_unpack_steim2(%s): invalid compflag, dnib, fn, wn = %d, %d, %d, %d\n",
UNPACK_SRCNAME, compflag, dnib, fn, wn);
return MS_STBADCOMPFLAG;
}
/* Uncompress the differences */
for (i=(n-1)*bits; i >= 0 && nd < num_samples; i-=bits, nd++)
{
*diff = (val >> i) & m1;
*diff = (*diff & m2) ? *diff | ~m1 : *diff;
diff++;
}
break;
default:
/* Should NEVER get here */
ms_log (2, "msr_unpack_steim2(%s): invalid compflag, fn, wn = %d, %d, %d - nsamp: %d\n",
UNPACK_SRCNAME, compflag, fn, wn, nd);
return MS_STBADCOMPFLAG;
}
}
++pf;
}
/* Test if the number of samples implied by the data frames is the
* same number indicated in the header.
*/
if ( nd != num_samples )
{
ms_log (1, "Warning: msr_unpack_steim2(%s): number of samples indicated in header (%d) does not equal data (%d)\n",
UNPACK_SRCNAME, num_samples, nd);
}
/* For now, assume sample count in header to be correct. */
/* One way of "trimming" data from a block is simply to reduce */
/* the sample count. It is not clear from the documentation */
/* whether this is a valid or not, but it appears to be done */
/* by other program, so we should not complain about its effect. */
nr = req_samples;
/* Compute first value based on last_value from previous buffer. */
/* The two should correspond in all cases EXCEPT for the first */
/* record for each component (because we don't have a valid xn from */
/* a previous record). Although the Steim compression algorithm */
/* defines x(-1) as 0 for the first record, this only works for the */
/* first record created since coldstart of the datalogger, NOT the */
/* first record of an arbitrary starting record. */
/* In all cases, assume x0 is correct, since we don't have x(-1). */
data = databuff;
diff = diffbuff;
last_data = *px0;
if (nr > 0)
*data = *px0;
/* Compute all but first values based on previous value */
prev = data - 1;
while (--nr > 0 && --nd > 0)
last_data = *++data = *++diff + *++prev;
/* If a short count was requested compute the last sample in order */
/* to perform the integrity check comparison */
while (--nd > 0)
last_data = *++diff + last_data;
/* Verify that the last value is identical to xn = rev. int. constant */
if (last_data != *pxn)
{
ms_log (1, "%s: Warning: Data integrity check for Steim-2 failed, last_data=%d, xn=%d\n",
UNPACK_SRCNAME, last_data, *pxn);
}
return ((req_samples < num_samples) ? req_samples : num_samples);
} /* End of msr_unpack_steim2() */
/* Defines for GEOSCOPE encoding */
#define GEOSCOPE_MANTISSA_MASK 0x0fff /* mask for mantissa */
#define GEOSCOPE_GAIN3_MASK 0x7000 /* mask for gainrange factor */
#define GEOSCOPE_GAIN4_MASK 0xf000 /* mask for gainrange factor */
#define GEOSCOPE_SHIFT 12 /* # bits in mantissa */
/************************************************************************
* msr_unpack_geoscope: *
* *
* Unpack GEOSCOPE gain ranged data (demultiplexed only) encoded *
* miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_geoscope
(const char *edata, /* ptr to encoded data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
float *databuff, /* ptr to unpacked data array. */
int encoding, /* specific GEOSCOPE encoding type */
int swapflag) /* if data should be swapped. */
{
int nd = 0; /* # of data points in packet. */
int mantissa; /* mantissa from SEED data */
int gainrange; /* gain range factor */
int exponent; /* total exponent */
int k;
uint64_t exp2val;
int16_t sint;
double dsample = 0.0;
union {
uint8_t b[4];
uint32_t i;
} sample32;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
/* Make sure we recognize this as a GEOSCOPE encoding format */
if ( encoding != DE_GEOSCOPE24 &&
encoding != DE_GEOSCOPE163 &&
encoding != DE_GEOSCOPE164 )
{
ms_log (2, "msr_unpack_geoscope(%s): unrecognized GEOSCOPE encoding: %d\n",
UNPACK_SRCNAME, encoding);
return -1;
}
for (nd=0; nd<req_samples && nd<num_samples; nd++)
{
switch (encoding)
{
case DE_GEOSCOPE24:
sample32.i = 0;
if ( swapflag )
for (k=0; k < 3; k++)
sample32.b[2-k] = edata[k];
else
for (k=0; k < 3; k++)
sample32.b[1+k] = edata[k];
mantissa = sample32.i;
/* Take 2's complement for mantissa for overflow */
if (mantissa > MAX24)
mantissa -= 2 * (MAX24 + 1);
/* Store */
dsample = (double) mantissa;
break;
case DE_GEOSCOPE163:
memcpy (&sint, edata, sizeof(int16_t));
if ( swapflag ) ms_gswap2a(&sint);
/* Recover mantissa and gain range factor */
mantissa = (sint & GEOSCOPE_MANTISSA_MASK);
gainrange = (sint & GEOSCOPE_GAIN3_MASK) >> GEOSCOPE_SHIFT;
/* Exponent is just gainrange for GEOSCOPE */
exponent = gainrange;
/* Calculate sample as mantissa / 2^exponent */
exp2val = (uint64_t) 1 << exponent;
dsample = ((double) (mantissa-2048)) / exp2val;
break;
case DE_GEOSCOPE164:
memcpy (&sint, edata, sizeof(int16_t));
if ( swapflag ) ms_gswap2a(&sint);
/* Recover mantissa and gain range factor */
mantissa = (sint & GEOSCOPE_MANTISSA_MASK);
gainrange = (sint & GEOSCOPE_GAIN4_MASK) >> GEOSCOPE_SHIFT;
/* Exponent is just gainrange for GEOSCOPE */
exponent = gainrange;
/* Calculate sample as mantissa / 2^exponent */
exp2val = (uint64_t) 1 << exponent;
dsample = ((double) (mantissa-2048)) / exp2val;
break;
}
/* Save sample in output array */
databuff[nd] = (float) dsample;
/* Increment edata pointer depending on size */
switch (encoding)
{
case DE_GEOSCOPE24:
edata += 3;
break;
case DE_GEOSCOPE163:
case DE_GEOSCOPE164:
edata += 2;
break;
}
}
return nd;
} /* End of msr_unpack_geoscope() */
/* Defines for CDSN encoding */
#define CDSN_MANTISSA_MASK 0x3fff /* mask for mantissa */
#define CDSN_GAINRANGE_MASK 0xc000 /* mask for gainrange factor */
#define CDSN_SHIFT 14 /* # bits in mantissa */
/************************************************************************
* msr_unpack_cdsn: *
* *
* Unpack CDSN gain ranged data encoded miniSEED data and place in *
* supplied buffer. *
* *
* Notes from original rdseed routine: *
* CDSN data are compressed according to the formula *
* *
* sample = M * (2 exp G) *
* *
* where *
* sample = seismic data sample *
* M = mantissa; biased mantissa B is written to tape *
* G = exponent of multiplier (i.e. gain range factor); *
* key K is written to tape *
* exp = exponentiation operation *
* B = M + 8191, biased mantissa, written to tape *
* K = key to multiplier exponent, written to tape *
* K may have any of the values 0 - 3, as follows: *
* 0 => G = 0, multiplier = 2 exp 0 = 1 *
* 1 => G = 2, multiplier = 2 exp 2 = 4 *
* 2 => G = 4, multiplier = 2 exp 4 = 16 *
* 3 => G = 7, multiplier = 2 exp 7 = 128 *
* Data are stored on tape in two bytes as follows: *
* fedc ba98 7654 3210 = bit number, power of two *
* KKBB BBBB BBBB BBBB = form of SEED data *
* where K = key to multiplier exponent and B = biased mantissa *
* *
* Masks to recover key to multiplier exponent and biased mantissa *
* from tape are: *
* fedc ba98 7654 3210 = bit number = power of two *
* 0011 1111 1111 1111 = 0x3fff = mask for biased mantissa *
* 1100 0000 0000 0000 = 0xc000 = mask for gain range key *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_cdsn
(int16_t *edata, /* ptr to encoded data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int32_t nd = 0; /* sample count */
int32_t mantissa; /* mantissa */
int32_t gainrange; /* gain range factor */
int32_t mult = -1; /* multiplier for gain range */
uint16_t sint;
int32_t sample;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++)
{
memcpy (&sint, &edata[nd], sizeof(int16_t));
if ( swapflag ) ms_gswap2a(&sint);
/* Recover mantissa and gain range factor */
mantissa = (sint & CDSN_MANTISSA_MASK);
gainrange = (sint & CDSN_GAINRANGE_MASK) >> CDSN_SHIFT;
/* Determine multiplier from the gain range factor and format definition
* because shift operator is used later, these are powers of two */
if ( gainrange == 0 ) mult = 0;
else if ( gainrange == 1 ) mult = 2;
else if ( gainrange == 2 ) mult = 4;
else if ( gainrange == 3 ) mult = 7;
/* Unbias the mantissa */
mantissa -= MAX14;
/* Calculate sample from mantissa and multiplier using left shift
* mantissa << mult is equivalent to mantissa * (2 exp (mult)) */
sample = (mantissa << mult);
/* Save sample in output array */
databuff[nd] = sample;
}
return nd;
} /* End of msr_unpack_cdsn() */
/* Defines for SRO encoding */
#define SRO_MANTISSA_MASK 0x0fff /* mask for mantissa */
#define SRO_GAINRANGE_MASK 0xf000 /* mask for gainrange factor */
#define SRO_SHIFT 12 /* # bits in mantissa */
/************************************************************************
* msr_unpack_sro: *
* *
* Unpack SRO gain ranged data encoded miniSEED data and place in *
* supplied buffer. *
* *
* Notes from original rdseed routine: *
* SRO data are represented according to the formula *
* *
* sample = M * (b exp {[m * (G + agr)] + ar}) *
* *
* where *
* sample = seismic data sample *
* M = mantissa *
* G = gain range factor *
* b = base to be exponentiated = 2 for SRO *
* m = multiplier = -1 for SRO *
* agr = term to be added to gain range factor = 0 for SRO *
* ar = term to be added to [m * (gr + agr)] = 10 for SRO *
* exp = exponentiation operation *
* Data are stored in two bytes as follows: *
* fedc ba98 7654 3210 = bit number, power of two *
* GGGG MMMM MMMM MMMM = form of SEED data *
* where G = gain range factor and M = mantissa *
* Masks to recover gain range and mantissa: *
* fedc ba98 7654 3210 = bit number = power of two *
* 0000 1111 1111 1111 = 0x0fff = mask for mantissa *
* 1111 0000 0000 0000 = 0xf000 = mask for gain range *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_sro
(int16_t *edata, /* ptr to encoded data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int32_t nd = 0; /* sample count */
int32_t mantissa; /* mantissa */
int32_t gainrange; /* gain range factor */
int32_t add2gr; /* added to gainrage factor */
int32_t mult; /* multiplier for gain range */
int32_t add2result; /* added to multiplied gain rage */
int32_t exponent; /* total exponent */
uint16_t sint;
int32_t sample;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
add2gr = 0;
mult = -1;
add2result = 10;
for (nd=0; nd<req_samples && nd<num_samples; nd++)
{
memcpy (&sint, &edata[nd], sizeof(int16_t));
if ( swapflag ) ms_gswap2a(&sint);
/* Recover mantissa and gain range factor */
mantissa = (sint & SRO_MANTISSA_MASK);
gainrange = (sint & SRO_GAINRANGE_MASK) >> SRO_SHIFT;
/* Take 2's complement for mantissa */
if ( mantissa > MAX12 )
mantissa -= 2 * (MAX12 + 1);
/* Calculate exponent, SRO exponent = 0..10 */
exponent = (mult * (gainrange + add2gr)) + add2result;
if ( exponent < 0 || exponent > 10 )
{
ms_log (2, "msr_unpack_sro(%s): SRO gain ranging exponent out of range: %d\n",
UNPACK_SRCNAME, exponent);
return MS_GENERROR;
}
/* Calculate sample as mantissa * 2^exponent */
sample = mantissa * ( (uint64_t) 1 << exponent );
/* Save sample in output array */
databuff[nd] = sample;
}
return nd;
} /* End of msr_unpack_sro() */
/************************************************************************
* msr_unpack_dwwssn: *
* *
* Unpack DWWSSN encoded miniSEED data and place in supplied buffer. *
* *
* Return: # of samples returned. *
************************************************************************/
int msr_unpack_dwwssn
(int16_t *edata, /* ptr to encoded data. */
int num_samples, /* number of data samples in total. */
int req_samples, /* number of data desired by caller. */
int32_t *databuff, /* ptr to unpacked data array. */
int swapflag) /* if data should be swapped. */
{
int32_t nd = 0; /* sample count */
int32_t sample;
uint16_t sint;
if (num_samples < 0) return 0;
if (req_samples < 0) return 0;
for (nd=0; nd<req_samples && nd<num_samples; nd++)
{
memcpy (&sint, &edata[nd], sizeof(uint16_t));
if ( swapflag ) ms_gswap2a(&sint);
sample = (int32_t) sint;
/* Take 2's complement for sample */
if ( sample > MAX16 )
sample -= 2 * (MAX16 + 1);
/* Save sample in output array */
databuff[nd] = sample;
}
return nd;
} /* End of msr_unpack_dwwssn() */

@ -0,0 +1,40 @@
/***************************************************************************
* unpack.h:
*
* Interface declarations for the Mini-SEED unpacking routines in
* unpackdata.c
*
* modified: 2009.111
***************************************************************************/
#ifndef UNPACKDATA_H
#define UNPACKDATA_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include "steimdata.h"
/* Pointer to srcname of record being unpacked, declared in unpack.c */
extern char *UNPACK_SRCNAME;
extern int msr_unpack_int_16 (int16_t*, int, int, int32_t*, int);
extern int msr_unpack_int_32 (int32_t*, int, int, int32_t*, int);
extern int msr_unpack_float_32 (float*, int, int, float*, int);
extern int msr_unpack_float_64 (double*, int, int, double*, int);
extern int msr_unpack_steim1 (FRAME*, int, int, int, int32_t*, int32_t*,
int32_t*, int32_t*, int, int);
extern int msr_unpack_steim2 (FRAME*, int, int, int, int32_t*, int32_t*,
int32_t*, int32_t*, int, int);
extern int msr_unpack_geoscope (const char*, int, int, float*, int, int);
extern int msr_unpack_cdsn (int16_t*, int, int, int32_t*, int);
extern int msr_unpack_sro (int16_t*, int, int, int32_t*, int);
extern int msr_unpack_dwwssn (int16_t*, int, int, int32_t*, int);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,5 @@
SUBDIRS(3rd-party gempa)
IF(LIBCAPS_PYTHON_WRAPPER)
SUBDIRS(swig)
ENDIF()

@ -0,0 +1 @@
subdirs(caps)

@ -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

@ -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)

@ -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);
}
}
}

@ -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

@ -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

@ -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;
}
}
}

@ -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

@ -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());
}
}
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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

@ -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;
}
}
}

@ -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

@ -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

@ -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 };
}
}

@ -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

@ -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);
}
}
}

@ -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 &timestamp);
};
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

@ -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__

@ -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();
}
}
}

@ -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

@ -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

@ -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

@ -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

@ -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__

@ -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);
}
}
}
}

@ -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__

@ -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);
}
}
}
}

@ -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__

@ -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);
}
}
}

@ -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

@ -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() {}
}
}

@ -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 &timestamp);
};
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

File diff suppressed because it is too large Load Diff

@ -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

@ -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;
}
}
}

@ -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

@ -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

@ -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)
}
}
}

@ -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

@ -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

@ -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>;
}
}
}

@ -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

@ -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);
}
}
}

@ -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

@ -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;
}
}
}

@ -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

@ -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
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
}
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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

@ -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)

@ -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()

@ -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()

@ -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()

@ -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)
---------------------------------------------------------------------------------------------------------------------------

@ -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);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

@ -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()

@ -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()

@ -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

@ -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()

@ -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

@ -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__

@ -0,0 +1 @@
SUBDIRS(gempa)

@ -0,0 +1,125 @@
%feature("notabstract") Gempa::CAPS::AnyDataRecord;
%include "stdint.i"
%init
%{
import_array();
%}
%module CAPS
%{
#define SWIG_FILE_WITH_INIT
#include <gempa/caps/utils.h>
#include <gempa/caps/anypacket.h>
#include <gempa/caps/datetime.h>
#include <gempa/caps/log.h>
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/packet.h>
#include <gempa/caps/plugin.h>
typedef Gempa::CAPS::Plugin::Buffer Buffer;
#include <gempa/caps/rawpacket.h>
#include <gempa/caps/riff.h>
#include <gempa/caps/rtcm2packet.h>
#include <gempa/caps/socket.h>
#include <gempa/caps/mseed/spclock.h>
#include <numpy/ndarrayobject.h>
%}
%extend Gempa::CAPS::Plugin {
Gempa::CAPS::Plugin::Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, int16_t numerator, int16_t denominator,
const std::string &uom,
PyObject *obj, int type
) {
PyArrayObject *arr = NULL;
Gempa::CAPS::DataType dataType = (Gempa::CAPS::DataType)type;
Gempa::CAPS::Plugin::Status status = Gempa::CAPS::Plugin::PacketLoss;
switch(type) {
case Gempa::CAPS::DT_INT8:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_CHAR, 1, 1);
if ( arr == NULL )
break;
status = self->push(net, sta, loc, cha, stime, numerator, denominator,
uom, (int8_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT16:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT16, 1, 1);
if ( arr == NULL )
break;
status = self->push(net, sta, loc, cha, stime, numerator, denominator,
uom, (int16_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT32:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT32, 1, 1);
if ( arr == NULL )
break;
status = self->push(net, sta, loc, cha, stime, numerator, denominator,
uom, (int32_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_FLOAT:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT32, 1, 1);
if ( arr == NULL )
break;
status = self->push(net, sta, loc, cha, stime, numerator, denominator,
uom, (float*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_DOUBLE:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT64, 1, 1);
if ( arr == NULL )
break;
status = self->push(net, sta, loc, cha, stime, numerator, denominator,
uom, (double*)(arr->data), arr->dimensions[0], dataType);
break;
default:
break;
}
Py_XDECREF(arr);
return status;
}
Gempa::CAPS::Plugin::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,
PyObject *obj) {
char *data;
Py_ssize_t len;
if ( PyBytes_AsStringAndSize(obj, &data, &len) == -1 )
return Gempa::CAPS::Plugin::PacketLoss;
return self->push(net, sta, loc, cha, stime, numerator,
denominator, format, data, len);
}
};
%include "exception.i"
%include "std_string.i"
%include "numpy.i"
%include "gempa/caps/api.h"
%include "gempa/caps/datetime.h"
%include "gempa/caps/packet.h"
%include "gempa/caps/pluginpacket.h"
%include "gempa/caps/anypacket.h"
%include "gempa/caps/log.h"
%include "gempa/caps/mseed/spclock.h"
%include "gempa/caps/mseed/encoder.h"
%include "gempa/caps/encoderfactory.h"
%include "gempa/caps/mseedpacket.h"
typedef Gempa::CAPS::Plugin::Buffer Buffer;
%include "gempa/caps/plugin.h"
%include "gempa/caps/rawpacket.h"
%include "gempa/caps/riff.h"
%include "gempa/caps/rtcm2packet.h"
%include "gempa/caps/socket.h"
%include <carrays.i>
%array_class(char, charArray);
%include "gempa/caps/utils.h"

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save