Initial commit based on common repo commit ffeb9c9b
commit
8b2a408e6f
@ -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 ×tamp);
|
||||
};
|
||||
|
||||
public:
|
||||
MetaDataRecord();
|
||||
|
||||
//! Returns the data vector to be filled by the caller
|
||||
Buffer *data();
|
||||
|
||||
virtual const char *formatName() const;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual const Header *header() const;
|
||||
|
||||
//! Sets a custom header and updates all internal structures
|
||||
//! based on the current data. If the data has changed, make
|
||||
//! sure to call this method again. If the flag skip reading
|
||||
//! is set get will not read the header information from stream
|
||||
void setHeader(const MetaHeader &header);
|
||||
|
||||
virtual Time startTime() const;
|
||||
virtual Time endTime() const;
|
||||
|
||||
virtual bool canTrim() const { return false; }
|
||||
virtual bool canMerge() const { return false; }
|
||||
|
||||
virtual bool trim(const Time &start, const Time &end) const { return false; }
|
||||
|
||||
virtual size_t dataSize(bool withHeader) const;
|
||||
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start, const Time &end,
|
||||
int maxBytes);
|
||||
|
||||
virtual bool put(std::streambuf &buf, bool withHeader) const { return false; }
|
||||
|
||||
PacketType packetType() const { return MetaDataPacket; }
|
||||
|
||||
private:
|
||||
MetaHeader _header;
|
||||
|
||||
mutable Time _startTime;
|
||||
mutable Time _endTime;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -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 ×tamp);
|
||||
};
|
||||
|
||||
enum ReadStatusCode {
|
||||
RS_Error = 0,
|
||||
RS_Complete = 1,
|
||||
RS_Partial = 2,
|
||||
RS_BeforeTimeWindow = 3,
|
||||
RS_AfterTimeWindow = 4,
|
||||
RS_Max
|
||||
};
|
||||
|
||||
class ReadStatus {
|
||||
public:
|
||||
ReadStatus() {}
|
||||
ReadStatus(ReadStatusCode code) : _code(code) {}
|
||||
ReadStatus(const ReadStatus &other) : _code(other._code) {}
|
||||
|
||||
public:
|
||||
ReadStatus &operator=(const ReadStatus &other) { _code = other._code; return *this; }
|
||||
operator ReadStatusCode() const { return _code; }
|
||||
|
||||
private:
|
||||
operator bool() { return _code != RS_Error; }
|
||||
|
||||
private:
|
||||
ReadStatusCode _code;
|
||||
};
|
||||
|
||||
public:
|
||||
virtual ~DataRecord();
|
||||
|
||||
virtual const char *formatName() const = 0;
|
||||
|
||||
virtual bool readMetaData(std::streambuf &buf, int size,
|
||||
Header &header,
|
||||
Time &startTime, Time &endTime) = 0;
|
||||
|
||||
//! Returns if data trimming is supported
|
||||
virtual bool canTrim() const = 0;
|
||||
|
||||
//! Returns if data merging is possible without modifying
|
||||
//! preceding data
|
||||
virtual bool canMerge() const = 0;
|
||||
|
||||
//! Trims a record to start and end time. Trimming
|
||||
//! should not modify any data but give access to trimmed
|
||||
//! start and end times and the resulting data size. The trimming
|
||||
//! also influences writing the record to a stream.
|
||||
//! Trimming requires the resulting start time being greater
|
||||
//! or equal than the requested start time.
|
||||
//! (Un)Trimming to the original record is semantically
|
||||
//! equal to passing an invalid start and end time.
|
||||
//! It returns true if trimming has been done or false
|
||||
//! if an error occured or trimming is not supported.
|
||||
virtual bool trim(const Time &start, const Time &end) const = 0;
|
||||
|
||||
//! Returns the data size in bytes if the current state would
|
||||
//! be written to a stream. Trimming has also to be taken into
|
||||
//! account while calculating the size.
|
||||
virtual size_t dataSize(bool withHeader = true) const = 0;
|
||||
|
||||
//! Reads the packet from a streambuf and trims the data
|
||||
//! if possible to start and end. If maxBytes is greater
|
||||
//! than 0 then the record should not use more than this
|
||||
//! size of memory (in bytes). If not the complete record
|
||||
//! has been read, RS_Partial must be returned, RS_Complete
|
||||
//! otherwise.
|
||||
virtual ReadStatus get(std::streambuf &buf, int size,
|
||||
const Time &start = Time(), const Time &end = Time(),
|
||||
int maxBytes = -1) = 0;
|
||||
|
||||
//! writes the packet to a streambuf and trims the data
|
||||
//! if possible to start and end
|
||||
virtual bool put(std::streambuf &buf, bool withHeader = true) const = 0;
|
||||
|
||||
//! Returns the meta information of the data if supported
|
||||
virtual const Header *header() const = 0;
|
||||
|
||||
//! Returns the start time of the record
|
||||
virtual Time startTime() const = 0;
|
||||
|
||||
//! Returns the end time of the record
|
||||
virtual Time endTime() const = 0;
|
||||
|
||||
//! Returns the packet type of the record
|
||||
virtual PacketType packetType() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the data vector to be filled by the caller
|
||||
* @return The pointer to the internal buffer
|
||||
*/
|
||||
virtual Buffer *buffer() { return &_data; }
|
||||
|
||||
/**
|
||||
* @brief Returns pointer to raw data. If the record
|
||||
* is compressed it will be uncompressed
|
||||
* @return The pointer to the raw data
|
||||
*/
|
||||
virtual Buffer *data() { return &_data; }
|
||||
|
||||
protected:
|
||||
Buffer _data;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<DataRecord> DataRecordPtr;
|
||||
|
||||
|
||||
struct RawPacket {
|
||||
PacketDataHeader header;
|
||||
std::string SID[4];
|
||||
std::vector<char> data;
|
||||
DataRecord *record;
|
||||
};
|
||||
|
||||
struct MetaPacket {
|
||||
std::string SID[4];
|
||||
PacketDataHeader packetDataHeader;
|
||||
DataRecord *record;
|
||||
DataRecord::Header recordHeader;
|
||||
Time startTime;
|
||||
Time endTime;
|
||||
Time timestamp;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
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)
|
Binary file not shown.
@ -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…
Reference in New Issue