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