5 Commits
master ... v3

Author SHA1 Message Date
44609d367f Update to version 3.2 2026-03-18 15:18:05 +01:00
f593487c77 [cmake] Use CAPS logging by default 2026-03-18 14:56:01 +01:00
ed6649e947 [cmake] Force the usage of C++17
- Set minimum CMake version to 3.10
2026-03-18 14:55:20 +01:00
4bd69a81fc [cmake] Force the usage of C++11 2023-02-01 10:16:34 +01:00
8934eeac6b Update to version 2 2022-11-18 13:42:30 +01:00
58 changed files with 17049 additions and 8639 deletions

View File

@@ -1,5 +1,11 @@
PROJECT(LIBCAPS)
CMAKE_MINIMUM_REQUIRED(VERSION 2.4)
CMAKE_MINIMUM_REQUIRED(VERSION 3.10 FATAL_ERROR)
IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "7.0")
ADD_DEFINITIONS(-std=c++17 -DBOOST_NO_CXX11_SCOPED_ENUM -DBOOST_NO_SCOPED_ENUMS)
ELSE()
MESSAGE(ERROR "The CAPS client library requires gcc version 4.7 or higher")
ENDIF()
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Modules)
@@ -15,6 +21,7 @@ INCLUDE_DIRECTORIES(libs)
#ADD_DEFINITIONS("-DCAPS_FEATURES_RTCM2=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_BACKFILLING=0")
#ADD_DEFINITIONS("-DCAPS_FEATURES_JOURNAL=0")
ADD_DEFINITIONS("-DCAPS_SC_LOGGING=0")
OPTION(LIBCAPS_PYTHON_WRAPPER "Create Python wrappers" ON)
OPTION(LIBCAPS_EXAMPLES "Build and install example applications" OFF)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,661 @@
/***************************************************************************
* Documentation and helpers for miniSEED structures.
*
* This file is part of the miniSEED Library.
*
* Copyright (c) 2024 Chad Trabant, EarthScope Data Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#ifndef MSEEDFORMAT_H
#define MSEEDFORMAT_H 1
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "libmseed.h"
/* Length of Fixed Section of Data Header for miniSEED v3 */
#define MS3FSDH_LENGTH 40
/***************************************************************************
* miniSEED 3.0 Fixed Section of Data Header
* 40 bytes, plus length of identifier, plus length of extra headers
*
* # FIELD TYPE OFFSET
* 1 record indicator char[2] 0
* 2 format version uint8_t 2
* 3 flags uint8_t 3
* 4a nanosecond uint32_t 4
* 4b year uint16_t 8
* 4c day uint16_t 10
* 4d hour uint8_t 12
* 4e min uint8_t 13
* 4f sec uint8_t 14
* 5 data encoding uint8_t 15
* 6 sample rate/period float64 16
* 7 number of samples uint32_t 24
* 8 CRC of record uint32_t 28
* 9 publication version uint8_t 32
* 10 length of identifer uint8_t 33
* 11 length of extra headers uint16_t 34
* 12 length of data payload uint32_t 36
* 13 source identifier char 40
* 14 extra headers char 40 + field 10
* 15 data payload encoded 40 + field 10 + field 11
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS3FSDH_INDICATOR(record) ((char*)record)
#define pMS3FSDH_FORMATVERSION(record) ((uint8_t*)((uint8_t*)record+2))
#define pMS3FSDH_FLAGS(record) ((uint8_t*)((uint8_t*)record+3))
#define pMS3FSDH_NSEC(record) ((uint32_t*)((uint8_t*)record+4))
#define pMS3FSDH_YEAR(record) ((uint16_t*)((uint8_t*)record+8))
#define pMS3FSDH_DAY(record) ((uint16_t*)((uint8_t*)record+10))
#define pMS3FSDH_HOUR(record) ((uint8_t*)((uint8_t*)record+12))
#define pMS3FSDH_MIN(record) ((uint8_t*)((uint8_t*)record+13))
#define pMS3FSDH_SEC(record) ((uint8_t*)((uint8_t*)record+14))
#define pMS3FSDH_ENCODING(record) ((uint8_t*)((uint8_t*)record+15))
#define pMS3FSDH_SAMPLERATE(record) ((double*)((uint8_t*)record+16))
#define pMS3FSDH_NUMSAMPLES(record) ((uint32_t*)((uint8_t*)record+24))
#define pMS3FSDH_CRC(record) ((uint32_t*)((uint8_t*)record+28))
#define pMS3FSDH_PUBVERSION(record) ((uint8_t*)((uint8_t*)record+32))
#define pMS3FSDH_SIDLENGTH(record) ((uint8_t*)((uint8_t*)record+33))
#define pMS3FSDH_EXTRALENGTH(record) ((uint16_t*)((uint8_t*)record+34))
#define pMS3FSDH_DATALENGTH(record) ((uint32_t*)((uint8_t*)record+36))
#define pMS3FSDH_SID(record) ((char*)((uint8_t*)record+40))
/* Length of Fixed Section of Data Header for miniSEED v2 */
#define MS2FSDH_LENGTH 48
/***************************************************************************
* miniSEED 2.4 Fixed Section of Data Header
* 48 bytes total
*
* FIELD TYPE OFFSET
* sequence_number char[6] 0
* dataquality char 6
* reserved char 7
* station char[5] 8
* location char[2] 13
* channel char[3] 15
* network char[2] 18
* year uint16_t 20
* day uint16_t 22
* hour uint8_t 24
* min uint8_t 25
* sec uint8_t 26
* unused uint8_t 27
* fract uint16_t 28
* numsamples uint16_t 30
* samprate_fact int16_t 32
* samprate_mult int16_t 34
* act_flags uint8_t 36
* io_flags uint8_t 37
* dq_flags uint8_t 38
* numblockettes uint8_t 39
* time_correct int32_t 40
* data_offset uint16_t 44
* blockette_offset uint16_t 46
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2FSDH_SEQNUM(record) ((char*)record)
#define pMS2FSDH_DATAQUALITY(record) ((char*)((uint8_t*)record+6))
#define pMS2FSDH_RESERVED(record) ((char*)((uint8_t*)record+7))
#define pMS2FSDH_STATION(record) ((char*)((uint8_t*)record+8))
#define pMS2FSDH_LOCATION(record) ((char*)((uint8_t*)record+13))
#define pMS2FSDH_CHANNEL(record) ((char*)((uint8_t*)record+15))
#define pMS2FSDH_NETWORK(record) ((char*)((uint8_t*)record+18))
#define pMS2FSDH_YEAR(record) ((uint16_t*)((uint8_t*)record+20))
#define pMS2FSDH_DAY(record) ((uint16_t*)((uint8_t*)record+22))
#define pMS2FSDH_HOUR(record) ((uint8_t*)((uint8_t*)record+24))
#define pMS2FSDH_MIN(record) ((uint8_t*)((uint8_t*)record+25))
#define pMS2FSDH_SEC(record) ((uint8_t*)((uint8_t*)record+26))
#define pMS2FSDH_UNUSED(record) ((uint8_t*)((uint8_t*)record+27))
#define pMS2FSDH_FSEC(record) ((uint16_t*)((uint8_t*)record+28))
#define pMS2FSDH_NUMSAMPLES(record) ((uint16_t*)((uint8_t*)record+30))
#define pMS2FSDH_SAMPLERATEFACT(record) ((int16_t*)((uint8_t*)record+32))
#define pMS2FSDH_SAMPLERATEMULT(record) ((int16_t*)((uint8_t*)record+34))
#define pMS2FSDH_ACTFLAGS(record) ((uint8_t*)((uint8_t*)record+36))
#define pMS2FSDH_IOFLAGS(record) ((uint8_t*)((uint8_t*)record+37))
#define pMS2FSDH_DQFLAGS(record) ((uint8_t*)((uint8_t*)record+38))
#define pMS2FSDH_NUMBLOCKETTES(record) ((uint8_t*)((uint8_t*)record+39))
#define pMS2FSDH_TIMECORRECT(record) ((int32_t*)((uint8_t*)record+40))
#define pMS2FSDH_DATAOFFSET(record) ((uint16_t*)((uint8_t*)record+44))
#define pMS2FSDH_BLOCKETTEOFFSET(record) ((uint16_t*)((uint8_t*)record+46))
/***************************************************************************
* miniSEED 2.4 Blockette 100 - sample rate
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* samprate float 4
* flags uint8_t 8
* reserved uint8_t[3] 9
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B100_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B100_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B100_SAMPRATE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B100_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B100_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+9))
/***************************************************************************
* miniSEED 2.4 Blockette 200 - generic event detection
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* amplitude float 4
* period float 8
* background_est float 12
* flags uint8_t 16
* reserved uint8_t 17
* year uint16_t 18
* day uint16_t 20
* hour uint8_t 22
* min uint8_t 23
* sec uint8_t 24
* unused uint8_t 25
* fract uint16_t 26
* detector char[24] 28
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B200_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B200_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B200_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B200_PERIOD(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B200_BACKGROUNDEST(blockette) ((float*)((uint8_t*)blockette+12))
#define pMS2B200_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+16))
#define pMS2B200_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+17))
#define pMS2B200_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+18))
#define pMS2B200_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+20))
#define pMS2B200_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+22))
#define pMS2B200_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+23))
#define pMS2B200_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+24))
#define pMS2B200_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+25))
#define pMS2B200_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+26))
#define pMS2B200_DETECTOR(blockette) ((char*)((uint8_t*)blockette+28))
/***************************************************************************
* miniSEED 2.4 Blockette 201 - Murdock event detection
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* amplitude float 4
* period float 8
* background_est float 12
* flags uint8_t 16
* reserved uint8_t 17
* year uint16_t 18
* day uint16_t 20
* hour uint8_t 22
* min uint8_t 23
* sec uint8_t 24
* unused uint8_t 25
* fract uint16_t 26
* snr_values uint8_t[6] 28
* loopback uint8_t 34
* pick_algorithm uint8_t 35
* detector char[24] 36
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B201_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B201_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B201_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B201_PERIOD(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B201_BACKGROUNDEST(blockette) ((float*)((uint8_t*)blockette+12))
#define pMS2B201_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+16))
#define pMS2B201_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+17))
#define pMS2B201_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+18))
#define pMS2B201_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+20))
#define pMS2B201_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+22))
#define pMS2B201_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+23))
#define pMS2B201_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+24))
#define pMS2B201_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+25))
#define pMS2B201_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+26))
#define pMS2B201_MEDSNR(blockette) ((uint8_t*)((uint8_t*)blockette+28))
#define pMS2B201_LOOPBACK(blockette) ((uint8_t*)((uint8_t*)blockette+34))
#define pMS2B201_PICKALGORITHM(blockette) ((uint8_t*)((uint8_t*)blockette+35))
#define pMS2B201_DETECTOR(blockette) ((char*)((uint8_t*)blockette+36))
/***************************************************************************
* miniSEED 2.4 Blockette 300 - step calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* num calibrations uint8_t 14
* flags uint8_t 15
* step duration uint32_t 16
* interval duration uint32_t 20
* amplitude float 24
* input channel char[3] 28
* reserved uint8_t 31
* reference amplitude uint32_t 32
* coupling char[12] 36
* rolloff char[12] 48
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B300_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B300_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B300_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B300_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B300_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B300_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B300_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B300_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B300_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B300_NUMCALIBRATIONS(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B300_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B300_STEPDURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B300_INTERVALDURATION(blockette) ((uint32_t*)((uint8_t*)blockette+20))
#define pMS2B300_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+24))
#define pMS2B300_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+28))
#define pMS2B300_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+31))
#define pMS2B300_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+32))
#define pMS2B300_COUPLING(blockette) ((char*)((uint8_t*)blockette+36))
#define pMS2B300_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+48))
/***************************************************************************
* miniSEED 2.4 Blockette 310 - sine calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* period float 20
* amplitude float 24
* input channel char[3] 28
* reserved2 uint8_t 31
* reference amplitude uint32_t 32
* coupling char[12] 36
* rolloff char[12] 48
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B310_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B310_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B310_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B310_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B310_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B310_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B310_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B310_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B310_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B310_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B310_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B310_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B310_PERIOD(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B310_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+24))
#define pMS2B310_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+28))
#define pMS2B310_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+31))
#define pMS2B310_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+32))
#define pMS2B310_COUPLING(blockette) ((char*)((uint8_t*)blockette+36))
#define pMS2B310_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+48))
/***************************************************************************
* miniSEED 2.4 Blockette 320 - pseudo-random calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* PtP amplitude float 20
* input channel char[3] 24
* reserved2 uint8_t 27
* reference amplitude uint32_t 28
* coupling char[12] 32
* rolloff char[12] 44
* noise type char[8] 56
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B320_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B320_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B320_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B320_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B320_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B320_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B320_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B320_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B320_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B320_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B320_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B320_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B320_PTPAMPLITUDE(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B320_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+24))
#define pMS2B320_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+27))
#define pMS2B320_REFERENCEAMPLITUDE(blockette) ((uint32_t*)((uint8_t*)blockette+28))
#define pMS2B320_COUPLING(blockette) ((char*)((uint8_t*)blockette+32))
#define pMS2B320_ROLLOFF(blockette) ((char*)((uint8_t*)blockette+44))
#define pMS2B320_NOISETYPE(blockette) ((char*)((uint8_t*)blockette+56))
/***************************************************************************
* miniSEED 2.4 Blockette 390 - generic calibration
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved1 uint8_t 14
* flags uint8_t 15
* duration uint32_t 16
* amplitude float 20
* input channel char[3] 24
* reserved2 uint8_t 27
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B390_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B390_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B390_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B390_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B390_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B390_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B390_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B390_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B390_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B390_RESERVED1(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B390_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B390_DURATION(blockette) ((uint32_t*)((uint8_t*)blockette+16))
#define pMS2B390_AMPLITUDE(blockette) ((float*)((uint8_t*)blockette+20))
#define pMS2B390_INPUTCHANNEL(blockette) ((char *)((uint8_t*)blockette+24))
#define pMS2B390_RESERVED2(blockette) ((uint8_t*)((uint8_t*)blockette+27))
/***************************************************************************
* miniSEED 2.4 Blockette 395 - calibration abort
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* year uint16_t 4
* day uint16_t 6
* hour uint8_t 8
* min uint8_t 9
* sec uint8_t 10
* unused uint8_t 11
* fract uint16_t 12
* reserved uint8_t[2] 14
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B395_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B395_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B395_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B395_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B395_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+8))
#define pMS2B395_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+9))
#define pMS2B395_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+10))
#define pMS2B395_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+11))
#define pMS2B395_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B395_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+14))
/***************************************************************************
* miniSEED 2.4 Blockette 400 - beam
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* azimuth float 4
* slowness float 8
* configuration uint16_t 12
* reserved uint8_t[2] 14
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B400_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B400_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B400_AZIMUTH(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B400_SLOWNESS(blockette) ((float*)((uint8_t*)blockette+8))
#define pMS2B400_CONFIGURATION(blockette) ((uint16_t*)((uint8_t*)blockette+12))
#define pMS2B400_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+14))
/***************************************************************************
* miniSEED 2.4 Blockette 405 - beam delay
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* delay values uint16_t[1] 4
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B405_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B405_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B405_DELAYVALUES(blockette) ((uint16_t*)((uint8_t*)blockette+4))
/***************************************************************************
* miniSEED 2.4 Blockette 500 - timing
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* VCO correction float 4
* year uint16_t 8
* day uint16_t 10
* hour uint8_t 12
* min uint8_t 13
* sec uint8_t 14
* unused uint8_t 15
* fract uint16_t 16
* microsecond int8_t 18
* reception quality uint8_t 19
* exception count uint32_t 20
* exception type char[16] 24
* clock model char[32] 40
* clock status char[128] 72
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B500_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B500_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B500_VCOCORRECTION(blockette) ((float*)((uint8_t*)blockette+4))
#define pMS2B500_YEAR(blockette) ((uint16_t*)((uint8_t*)blockette+8))
#define pMS2B500_DAY(blockette) ((uint16_t*)((uint8_t*)blockette+10))
#define pMS2B500_HOUR(blockette) ((uint8_t*)((uint8_t*)blockette+12))
#define pMS2B500_MIN(blockette) ((uint8_t*)((uint8_t*)blockette+13))
#define pMS2B500_SEC(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B500_UNUSED(blockette) ((uint8_t*)((uint8_t*)blockette+15))
#define pMS2B500_FSEC(blockette) ((uint16_t*)((uint8_t*)blockette+16))
#define pMS2B500_MICROSECOND(blockette) ((int8_t*)((uint8_t*)blockette+18))
#define pMS2B500_RECEPTIONQUALITY(blockette) ((uint8_t*)((uint8_t*)blockette+19))
#define pMS2B500_EXCEPTIONCOUNT(blockette) ((uint32_t*)((uint8_t*)blockette+20))
#define pMS2B500_EXCEPTIONTYPE(blockette) ((char*)((uint8_t*)blockette+24))
#define pMS2B500_CLOCKMODEL(blockette) ((char*)((uint8_t*)blockette+40))
#define pMS2B500_CLOCKSTATUS(blockette) ((char*)((uint8_t*)blockette+72))
/***************************************************************************
* miniSEED 2.4 Blockette 1000 - data only SEED (miniSEED)
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* encoding uint8_t 4
* byteorder uint8_t 5
* reclen uint8_t 6
* reserved uint8_t 7
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B1000_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B1000_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B1000_ENCODING(blockette) ((uint8_t*)((uint8_t*)blockette+4))
#define pMS2B1000_BYTEORDER(blockette) ((uint8_t*)((uint8_t*)blockette+5))
#define pMS2B1000_RECLEN(blockette) ((uint8_t*)((uint8_t*)blockette+6))
#define pMS2B1000_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+7))
/***************************************************************************
* miniSEED 2.4 Blockette 1001 - data extension
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* timing quality uint8_t 4
* microsecond int8_t 5
* reserved uint8_t 6
* frame count uint8_t 7
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B1001_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B1001_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B1001_TIMINGQUALITY(blockette) ((uint8_t*)((uint8_t*)blockette+4))
#define pMS2B1001_MICROSECOND(blockette) ((int8_t*)((uint8_t*)blockette+5))
#define pMS2B1001_RESERVED(blockette) ((uint8_t*)((uint8_t*)blockette+6))
#define pMS2B1001_FRAMECOUNT(blockette) ((uint8_t*)((uint8_t*)blockette+7))
/***************************************************************************
* miniSEED 2.4 Blockette 2000 - opaque data
*
* FIELD TYPE OFFSET
* type uint16_t 0
* next offset uint16_t 2
* length uint16_t 4
* data offset uint16_t 6
* recnum uint32_t 8
* byteorder uint8_t 12
* flags uint8_t 13
* numheaders uint8_t 14
* payload char[1] 15
*
* Convenience macros for accessing the fields via typed pointers follow:
***************************************************************************/
#define pMS2B2000_TYPE(blockette) ((uint16_t*)(blockette))
#define pMS2B2000_NEXT(blockette) ((uint16_t*)((uint8_t*)blockette+2))
#define pMS2B2000_LENGTH(blockette) ((uint16_t*)((uint8_t*)blockette+4))
#define pMS2B2000_DATAOFFSET(blockette) ((uint16_t*)((uint8_t*)blockette+6))
#define pMS2B2000_RECNUM(blockette) ((uint32_t*)((uint8_t*)blockette+8))
#define pMS2B2000_BYTEORDER(blockette) ((uint8_t*)((uint8_t*)blockette+12))
#define pMS2B2000_FLAGS(blockette) ((uint8_t*)((uint8_t*)blockette+13))
#define pMS2B2000_NUMHEADERS(blockette) ((uint8_t*)((uint8_t*)blockette+14))
#define pMS2B2000_PAYLOAD(blockette) ((char*)((uint8_t*)blockette+15))
/***************************************************************************
* Simple static inline convenience functions to swap bytes to "host
* order", as determined by the swap flag.
***************************************************************************/
static inline int16_t
HO2d (int16_t value, int swapflag)
{
if (swapflag)
{
ms_gswap2 (&value);
}
return value;
}
static inline uint16_t
HO2u (uint16_t value, int swapflag)
{
if (swapflag)
{
ms_gswap2 (&value);
}
return value;
}
static inline int32_t
HO4d (int32_t value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline uint32_t
HO4u (uint32_t value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline float
HO4f (float value, int swapflag)
{
if (swapflag)
{
ms_gswap4 (&value);
}
return value;
}
static inline double
HO8f (double value, int swapflag)
{
if (swapflag)
{
ms_gswap8 (&value);
}
return value;
}
/* Macro to test for sane year and day values, used primarily to
* determine if byte order swapping is needed for miniSEED 2.x.
*
* 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.
* If you are using this in 2056 to determine the byte order of miniSEED 2
* you have my deepest sympathies.
*/
#define MS_ISVALIDYEARDAY(Y,D) (Y >= 1900 && Y <= 2100 && D >= 1 && D <= 366)
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -2,25 +2,185 @@
All notable changes to the CAPS client library will be documented in this file.
## 2021-04-21 1.0.0
## 2026.069 3.2.0
### Added
- Allow to override host operating system information
## 2026.068 3.1.1
### Added
- Support system load reporting
## 2026.057 3.1.0
### Added
- Support for CAPS API 7 features
## 2025.206 3.0.1
### Added
- Stream ID check. Before sending data, the stream ID is checked to ensure
that it meets the server requirements. Specical characters like '?' are
not allowed.
## 2025.163 3.0.0
### Changed
- Make session table item start and end time optional
## 2024.352 2.7.4
### Changed
- Optimize package encoding
## 2024.226 2.7.3
### Fixed
- Parse server output address correctly
## 2024.162 2.7.2
### Fixed
- Do not crash when the journal file does not follow the format
## 2024.162 2.7.1
### Changed
- Redirect log messages to SeisComP log by default
## 2024.151 2.7.0
### Added
- Add plugin instance pointer to log messages
## 2023.184 2.6.0
### Added
- More convenience push functions
## 2023.184 2.5.0
### Added
- Direct API access to last acknowledged packet end time
## 2023.107 2.4.0
### Added
- PluginApplication class logs CAPS connection settings
### Changed
- Reworked connection handling to perfom better in poor network environments
- Removed unnecessary debug output
## 2022.284 2.3.1
### Changed
- Minor changes
## 2022-04-20 2.3.0
### Added
- CAPS agent support
## 2022-04-20 2.2.0
### Added
- API function to access internal packet buffer that keeps packets until
they have been acknowledged by CAPS
## 2021-09-01 2.1.0
### Added
- Extended plugin class by the possibility to dump **outgoing** RAW and MSEED
packets to stdout. RAW data ouput is in SLIST format and MSEED packets are
dumped as binary MSEED. Applications derived from the plugin application class
can enable this feature with the command line option ``--dump-packets``.
## 2021-06-21 2.0.3
### Fixed
- CAPS Python encoder factory ownership
## 2021-06-15 2.0.2
### Fixed
- Broken uncompressed MSEED support
## 2021-05-10 2.0.1
### Fixed
- Unit tests that work on 64-bit systems only
## 2021-05-10 2.0.0
### Added
- Log status responses from server in plugin application
- SSL and authentication support for plugin application.
With this version the data output URL can be set with the
config option ``output.address``. The formal definition of
the field is: [[caps|capss]://][user:pass@]host[:port] e.g.
```script
output.address = capss://caps:caps@localhost:18003
```
The new output.address parameter superseds the output.host and
output.port parameter of previous versions and takes precedence.
The old parameters are kept for compatibility reasons but are
marked as deprecated.
## 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.
```
```script
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.
@@ -30,15 +190,21 @@ All notable changes to the CAPS client library will be documented in this file.
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
@@ -49,102 +215,144 @@ All notable changes to the CAPS client library will be documented in this file.
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.
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
### Fixed
- SSL support
## 2017-11-20
### Added
- float and double support for Steim encoders. All values will be converted implicitly
to int 32 values
- 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.
- 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
- Do not flush encoders after reconnect
## 2017-10-26
### Added
- SSL support
## 2017-10-24
### Fixed
- packet synchronization error after reconnect
- Packet synchronization error after reconnect

View File

@@ -16,7 +16,7 @@ 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)
SET_TARGET_PROPERTIES(${LIB_NAME} PROPERTIES VERSION 2.0.0 SOVERSION 2)
TARGET_LINK_LIBRARIES(${LIB_NAME} mseed)
INSTALL(TARGETS ${LIB_NAME} DESTINATION lib)

View File

@@ -84,6 +84,11 @@ void AnyDataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denominato
}
DataRecord *AnyDataRecord::clone() const {
return new AnyDataRecord(*this);
}
const char *AnyDataRecord::formatName() const {
return "ANY";
}

View File

@@ -20,11 +20,11 @@
#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;
@@ -74,37 +74,37 @@ class AnyDataRecord : public DataRecord {
bool setType(const char *type);
const char *type() const;
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start, const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return ANYPacket; }
PacketType packetType() const override { return ANYPacket; }
/**
* @brief Sets the start time of the record
@@ -129,14 +129,15 @@ class AnyDataRecord : public DataRecord {
* @brief Returns the data vector to be filled by the caller
* @return The pointer to the internal buffer
*/
Buffer *data() { return &_data; }
Buffer *data() override { 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);
void setData(char *data, size_t size);
protected:
AnyHeader _header;

View File

@@ -16,11 +16,14 @@
#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);
@@ -99,6 +102,7 @@ class SC_GEMPA_CAPS_API Application {
static Application *_app;
};
}
}

View File

@@ -16,6 +16,7 @@
#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>
@@ -27,12 +28,15 @@
//#include <iostream>
#include <map>
namespace Gempa {
namespace CAPS {
class SessionTableItem;
struct SessionTableItem;
class Time;
class Connection {
public:
//! ConnectionStates:
@@ -171,10 +175,12 @@ class Connection {
bool _ssl;
};
typedef boost::shared_ptr<Connection> ConnectionPtr;
using ConnectionPtr = boost::shared_ptr<Connection>;
}
}
#endif

View File

@@ -18,9 +18,10 @@
#include <sstream>
#include <cmath>
#include <stdexcept>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <chrono>
#ifdef WIN32
#include <time.h>
@@ -168,6 +169,26 @@ inline void normalize(T &sec, U &usec) {
}
}
const char *timeFormats[] = {
"%FT%T.%fZ", // YYYY-MM-DDThh:mm:ss.ssssssZ
"%FT%T.%f", // YYYY-MM-DDThh:mm:ss.ssssss
"%FT%TZ", // YYYY-MM-DDThh:mm:ssZ
"%FT%T", // YYYY-MM-DDThh:mm:ss
"%FT%R", // YYYY-MM-DDThh:mm
"%FT%H", // YYYY-MM-DDThh
"%Y-%jT%T.%f", // YYYY-DDDThh:mm:ss.ssssss
"%Y-%jT%T", // YYYY-DDDThh:mm:ss
"%Y-%jT%R", // YYYY-DDDThh:mm
"%Y-%jT%H", // YYYY-DDDThh
"%F %T.%f", // YYYY-MM-DD hh:mm:ss.ssssss
"%F %T", // YYYY-MM-DD hh:mm:ss
"%F %R", // YYYY-MM-DD hh:mm
"%F %H", // YYYY-MM-DD hh
"%F", // YYYY-MM-DD
"%Y-%j", // YYYY-DDD
"%Y", // YYYY
};
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -342,6 +363,17 @@ TimeSpan& TimeSpan::operator=(long t) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TimeSpan& TimeSpan::operator=(const TimeSpan& t) {
_timeval = t._timeval;
return *this;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TimeSpan& TimeSpan::operator=(double t) {
if( t > (double)0x7fffffff || t < -(double)0x80000000 )
@@ -802,16 +834,9 @@ Time& Time::localtime() {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time &Time::gmt() {
gettimeofday(&_timeval, NULL);
time_t secs = (time_t)_timeval.tv_sec;
struct tm _tm;
#ifndef WIN32
_timeval.tv_sec = (long)mktime(::localtime_r(&secs, &_tm));
#else
// We use the native localtime function of windows, which is thread safe. (But it's not reentrant)
_timeval.tv_sec = (long)timegm(::localtime(&secs));
#endif
auto us = chrono::duration_cast<chrono::microseconds>(chrono::high_resolution_clock::now().time_since_epoch()).count();
_timeval.tv_sec = us / 1000000;
_timeval.tv_usec = us % 1000000;
return *this;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
@@ -1055,9 +1080,50 @@ bool Time::fromString(const char* str, const char* fmt) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bool Time::fromString(const char* str) {
for ( size_t i = 0; i < sizeof(timeFormats) / sizeof(const char*); ++i ) {
if ( fromString(str, timeFormats[i]) ) {
return true;
}
}
return false;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
bool Time::fromString(const std::string& str) {
return fromString(str.c_str());
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const char* str, const char* fmt) {
Time t;
t.fromString(str, fmt);
return t;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const char* str) {
Time t;
t.fromString(str);
return t;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Time Time::FromString(const std::string& str) {
return FromString(str.c_str());
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_DATETIME_H
#define GEMPA_CAPS_DATETIME_H
#ifdef WIN32
#include <winsock2.h>
#else
@@ -24,8 +25,10 @@
#include <string>
struct tm;
namespace Gempa {
namespace CAPS {
@@ -64,6 +67,7 @@ class TimeSpan {
//! Assignment
TimeSpan& operator=(long t);
TimeSpan& operator=(double t);
TimeSpan& operator=(const TimeSpan& t);
//! Arithmetic
TimeSpan operator+(const TimeSpan&) const;
@@ -205,24 +209,25 @@ class Time : public TimeSpan {
//! 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
/**
* @brief 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
* @brief 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.
* @brief 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)
@@ -230,11 +235,37 @@ class Time : public TimeSpan {
*/
bool fromString(const char* str, const char* fmt);
/**
* @brief Converts a string into a time representation trying a common
* set of date formats.
* @param str The string representation of the time.
* @return The conversion result
*/
bool fromString(const char* str);
/**
* @brief Convenience method for fromString(const char*).
*/
bool fromString(const std::string& str);
/**
* 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);
/**
* @brief Static methods that converts a string into a time
* representation trying a common set of date formats.
* @param str The string representation of the time.
* @return The conversion result
*/
static Time FromString(const char* str);
/**
* @brief Convenience method for FromString(const char*).
*/
static Time FromString(const std::string& str);
};

View File

@@ -39,7 +39,7 @@ const string& EncoderFactory::errorString() const {
MSEEDEncoderFactory::MSEEDEncoderFactory()
: _recordLength(9) {}
bool MSEEDEncoderFactory::setRecordLength(uint recordLength) {
bool MSEEDEncoderFactory::setRecordLength(unsigned int recordLength) {
if ( recordLength < 7 || recordLength > 32) {
_errorString = "MSEED record length out of range [7, 32]";
return false;

View File

@@ -12,16 +12,19 @@
* from gempa GmbH. *
***************************************************************************/
#ifndef GEMPA_CAPS_ENCODERFACTORY_H
#define GEMPA_CAPS_ENCODERFACTORY_H
#include "mseed/encoder.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
@@ -84,7 +87,7 @@ class MSEEDEncoderFactory : public EncoderFactory {
* @param recLen The record length expressed as a power of 2
* @return True if the record length is valid
*/
bool setRecordLength(uint recordLength);
bool setRecordLength(unsigned int recordLength);
protected:
uint8_t _recordLength;
@@ -158,7 +161,9 @@ class Steim2EncoderFactory : public SteimEncoderFactory {
int samplingFrequencyDenominator);
};
}
}
#endif

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_ENDIANESS_H
#define GEMPA_CAPS_ENDIANESS_H
#include <iostream>
#include <stdint.h>
#include <streambuf>
@@ -25,6 +26,7 @@ namespace Gempa {
namespace CAPS {
namespace Endianess {
template <typename T1, typename T2> inline const T1* lvalue(const T2 &value) {
return reinterpret_cast<const T1*>(&value);
}
@@ -203,8 +205,10 @@ struct Writer {
bool good;
};
}
}
}
#endif

View File

@@ -42,6 +42,10 @@ MetaDataRecord::Buffer *MetaDataRecord::data() {
return NULL;
}
DataRecord *MetaDataRecord::clone() const {
return new MetaDataRecord(*this);
}
const char *MetaDataRecord::formatName() const {
return "META";
}

View File

@@ -16,18 +16,17 @@
#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>
#include <cstdint>
namespace Gempa {
namespace CAPS {
struct SC_GEMPA_CAPS_API MetaResponseHeader {
struct Time {
int64_t seconds;
@@ -95,15 +94,16 @@ class MetaDataRecord : public DataRecord {
//! Returns the data vector to be filled by the caller
Buffer *data();
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime, Time &endTime) {
Time &startTime, Time &endTime) override {
return false;
}
virtual const Header *header() const;
const Header *header() const override;
//! Sets a custom header and updates all internal structures
//! based on the current data. If the data has changed, make
@@ -111,23 +111,24 @@ class MetaDataRecord : public DataRecord {
//! is set get will not read the header information from stream
void setHeader(const MetaHeader &header);
virtual Time startTime() const;
virtual Time endTime() const;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const { return false; }
virtual bool canMerge() const { return false; }
bool canTrim() const override { return false; }
bool canMerge() const override { return false; }
virtual bool trim(const Time &start, const Time &end) const { return false; }
bool trim(const Time &start, const Time &end) const override { return false; }
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start, const Time &end,
int maxBytes);
int maxBytes) override;
virtual bool put(std::streambuf &buf, bool withHeader) const { return false; }
bool put(std::streambuf &buf, bool withHeader) const override { return false; }
PacketType packetType() const override { return MetaDataPacket; }
PacketType packetType() const { return MetaDataPacket; }
private:
MetaHeader _header;
@@ -136,6 +137,7 @@ class MetaDataRecord : public DataRecord {
mutable Time _endTime;
};
}
}

View File

@@ -35,8 +35,11 @@ namespace CAPS {
class Encoder {
public:
Encoder(int freqn, int freqd) : _clk(freqn, freqd),
_sampleCount(0), _timingQuality(-1) {}
Encoder(int freqn, int freqd)
: _clk(freqn, freqd)
, _sampleCount(0), _timingQuality(-1)
, _context{nullptr} {}
virtual ~Encoder() {}
virtual void push(void *sample) = 0;
@@ -46,16 +49,21 @@ class Encoder {
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); }
void setStartTime(const Time &time) { _clk.syncTime(time); }
const Time currentTime() const { return _clk.getTime(0); }
int timingQuality() { return _timingQuality; }
void setTimingQuality(int quality) { _timingQuality = quality; }
int timingQuality() { return _timingQuality; }
void setContext(void *context) { _context = context; }
PacketPtr pop() {
if ( _packetQueue.empty() ) return PacketPtr();
if ( _packetQueue.empty() ) {
return PacketPtr();
}
PacketPtr rec = _packetQueue.front();
rec->context = _context;
_packetQueue.pop_front();
return rec;
}
@@ -66,6 +74,7 @@ class Encoder {
int _sampleCount;
PacketQueue _packetQueue;
int _timingQuality;
void *_context;
};
}

View File

@@ -18,7 +18,6 @@
*****************************************************************************/
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/rawpacket.h>
#include "mseed.h"
#include "slink.h"
@@ -27,23 +26,26 @@
#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,
unsigned short packType,
uint8_t recordLength)
: networkCode(netcode), stationCode(stacode), locationCode(loccode),
channelCode(chacode), packType(packtype_init), recordLength(recordLength)
: networkCode(netcode)
, stationCode(stacode)
, locationCode(loccode)
, channelCode(chacode)
, packType(packType)
, recordLength(recordLength)
{
if(freqn == 0 || freqd == 0) {
sample_rate_factor = 0;
@@ -63,8 +65,10 @@ MSEEDFormat::MSEEDFormat(const std::string &netcode, const std::string &stacode,
}
}
MSEEDDataRecord *MSEEDFormat::get_buffer(const SPClock::INT_TIME &it, int usec_correction,
int timing_quality, void *&dataptr, int &datalen) {
MSEEDDataRecord *MSEEDFormat::getBuffer(const 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);
@@ -139,9 +143,8 @@ MSEEDDataRecord *MSEEDFormat::get_buffer(const SPClock::INT_TIME &it, int usec_c
void MSEEDFormat::updateBuffer(MSEEDDataRecord *rec, int samples, int frames) {
sl_fsdh_s* fsdh = (sl_fsdh_s *)rec->data()->data();
char temp[7];
char temp[6] = { 0, 0, 0, 0, 0, 0 };
sprintf(temp, "%06d", (int)0);
memcpy(fsdh->sequence_number, temp, 6);
fsdh->dhq_indicator = 'D';
fsdh->num_samples = htons(samples);
@@ -151,12 +154,12 @@ void MSEEDFormat::updateBuffer(MSEEDDataRecord *rec, int samples, int frames) {
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();
}
}
}

View File

@@ -58,16 +58,16 @@ struct MSEEDFormat {
template<class T>
MSEEDEncoderPacket<T>
get_packet(const SPClock::INT_TIME &it, int usec_correction, int timing_quality) {
getPacket(const 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);
MSEEDDataRecord *rec = getBuffer(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,
MSEEDDataRecord *getBuffer(const Time &it, int usec_correction,
int timing_quality,
void *&dataptr, int &datalen);

View File

@@ -17,6 +17,7 @@
* 01.01.2013 Adapted code to CAPS client library requirements (gempa GmbH)
*****************************************************************************/
#ifndef CAPS_MSEED_SPCLOCK_H
#define CAPS_MSEED_SPCLOCK_H
@@ -28,46 +29,38 @@ namespace Gempa {
namespace CAPS {
class SPClock
{
class SPClock {
public:
typedef Gempa::CAPS::Time INT_TIME;
SPClock(int freqn, int freqd)
: freqn(freqn), freqd(freqd) {}
private:
INT_TIME itime;
int ticks;
int corr;
void syncTime(const Time &time) {
_itime = time;
_ticks = 0;
_corr = 0;
}
void tick() {
++_ticks;
}
Time getTime(int tickDiff) const {
int64_t correctness = (double)freqd / (double)freqn * 1000000 * (_ticks - tickDiff - _corr);
return _itime + TimeSpan(long(correctness / 1000000), long(correctness % 1000000));
}
int correction() const {
return _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;
}
private:
Time _itime;
int _ticks{0};
int _corr{0};
};
@@ -75,4 +68,4 @@ class SPClock
}
#endif // SPCLOCK_H
#endif

View File

@@ -33,8 +33,8 @@ namespace CAPS {
//*****************************************************************************
struct Steim1Frame {
u_int32_t nibble_word;
u_int32_t sample_word[15];
u_int32_t nibbleWord;
u_int32_t sampleWord[15];
};
//*****************************************************************************
@@ -43,42 +43,44 @@ struct Steim1Frame {
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) {}
: Encoder(freqn, freqd), _format(format), _frameCount(0)
, _bp(0), _fp(0), _spw(4), _lastSample(0), _nibbleWord(0) {}
virtual ~Steim1Encoder();
virtual void flush();
virtual void push(void *value);
virtual int type() const { return DE_STEIM1; }
private:
void updateSpw(int bp);
void store(int32_t value);
void initPacket();
void updatePacket();
void finishPacket();
MSEEDEncoderPacket<Steim1Frame> getPacket() {
return _format->getPacket<Steim1Frame>(_clk.getTime(_bp),
_clk.correction(), _timingQuality);
}
void queuePacket(MSEEDEncoderPacket<Steim1Frame> &pckt);
int numberOfFrames(const MSEEDEncoderPacket<Steim1Frame> &packet) {
return (packet.datalen >> 6);
}
private:
MSEEDFormat *_format;
int _frameCount;
int _bp;
int _fp;
int _spw;
int32_t _lastSample;
int32_t _buf[5];
u_int32_t _nibbleWord;
MSEEDEncoderPacket<Steim1Frame> _currentPacket;
};

View File

@@ -27,158 +27,181 @@
namespace Gempa {
namespace CAPS {
template<typename T> Steim1Encoder<T>::~Steim1Encoder() {
if ( format != NULL ) delete format;
template<typename T>
Steim1Encoder<T>::~Steim1Encoder() {
if ( _format ) {
delete _format;
}
}
template<typename T> void Steim1Encoder<T>::update_spw(int bp) {
int spw1 = 4;
template<typename T>
void Steim1Encoder<T>::updateSpw(int bp) {
int spw = 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;
if ( _buf[bp] < -32768 || _buf[bp] > 32767 ) spw = 1;
else if ( _buf[bp] < -128 || _buf[bp] > 127 ) spw = 2;
if ( spw < _spw ) _spw = spw;
}
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>::store(int32_t value) {
assert(_bp < 4);
_buf[_bp] = value - _lastSample;
_lastSample = value;
updateSpw(_bp);
++_bp;
}
template<typename T> void Steim1Encoder<T>::init_packet() {
template<typename T>
void Steim1Encoder<T>::initPacket() {
int i;
int32_t begin_sample = last_sample;
int32_t beginSample = _lastSample;
for(i = 1; i < bp; ++i) {
begin_sample -= buf[i];
for ( i = 1; i < _bp; ++i ) {
beginSample -= _buf[i];
}
reset();
current_packet.data[0].sample_word[0] = htonl(begin_sample);
frame_count = 0;
nibble_word = 0;
fp = 2;
_currentPacket.data[0].sampleWord[0] = htonl(beginSample);
_frameCount = 0;
_nibbleWord = 0;
_fp = 2;
}
template<typename T> void Steim1Encoder<T>::finish_packet() {
template<typename T>
void Steim1Encoder<T>::finishPacket() {
int i;
int32_t end_sample = last_sample;
int32_t endSample = _lastSample;
for(i = 0; i < bp; ++i) {
end_sample -= buf[i];
for ( i = 0; i < _bp; ++i ) {
endSample -= _buf[i];
}
current_packet.data[0].sample_word[1] = htonl(end_sample);
_currentPacket.data[0].sampleWord[1] = htonl(endSample);
}
template<typename T> void Steim1Encoder<T>::update_packet() {
template<typename T>
void Steim1Encoder<T>::updatePacket() {
unsigned int nibble = 0;
u_int32_t sample_word = 0;
u_int32_t sampleWord = 0;
assert(bp < 5);
assert(_bp < 5);
int used = bp;
int used = _bp;
while(used > spw) {
while ( used > _spw ) {
--used;
spw = 4;
for(int i = 0; i < used; ++i) update_spw(i);
_spw = 4;
for ( int i = 0; i < used; ++i ) {
updateSpw(i);
}
}
while(used < spw) spw >>= 1;
while ( used < _spw ) {
_spw >>= 1;
}
used = spw;
used = _spw;
switch(spw) {
switch ( _spw ) {
case 4:
nibble = 1;
sample_word = ((buf[0] & 0xff) << 24) | ((buf[1] & 0xff) << 16) |
((buf[2] & 0xff) << 8) | (buf[3] & 0xff);
sampleWord = ((_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);
sampleWord = ((_buf[0] & 0xffff) << 16) | (_buf[1] & 0xffff);
break;
case 1:
nibble = 3;
sample_word = buf[0];
sampleWord = _buf[0];
break;
default:
assert(0);
}
nibble_word |= (nibble << (30 - ((fp + 1) << 1)));
_nibbleWord |= (nibble << (30 - ((_fp + 1) << 1)));
spw = 4;
for(int i = 0; i < bp - used; ++i) {
buf[i] = buf[i + used];
update_spw(i);
_spw = 4;
for ( int i = 0; i < _bp - used; ++i ) {
_buf[i] = _buf[i + used];
updateSpw(i);
}
bp -= used;
_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;
_currentPacket.data[_frameCount].nibbleWord = htonl(_nibbleWord);
_currentPacket.data[_frameCount].sampleWord[_fp] = htonl(sampleWord);
nibble_word = 0;
fp = 0;
++frame_count;
if ( ++_fp < 15 ) {
return;
}
template<typename T> void Steim1Encoder<T>::queue_packet(MSEEDEncoderPacket<Steim1Frame> &pckt) {
format->updateBuffer(pckt.record, _sampleCount, frame_count + (fp > 0));
_nibbleWord = 0;
_fp = 0;
++_frameCount;
Packet *packet = new Packet(DataRecordPtr(pckt.record), format->networkCode, format->stationCode,
format->locationCode, format->channelCode);
return;
}
template<typename T>
void Steim1Encoder<T>::queuePacket(MSEEDEncoderPacket<Steim1Frame> &pckt) {
_format->updateBuffer(pckt.record, _sampleCount, _frameCount + (_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) {
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();
while ( _bp >= _spw ) {
if ( !_currentPacket.valid() ) {
_currentPacket = getPacket();
initPacket();
}
update_packet();
if(frame_count == number_of_frames(current_packet)) {
finish_packet();
queue_packet(current_packet);
updatePacket();
if ( _frameCount == numberOfFrames(_currentPacket) ) {
finishPacket();
queuePacket(_currentPacket);
}
}
}
template<typename T> void Steim1Encoder<T>::flush() {
while(bp) {
if(!current_packet.valid()) {
current_packet = get_packet();
init_packet();
template<typename T>
void Steim1Encoder<T>::flush() {
while ( _bp ) {
if ( !_currentPacket.valid() ) {
_currentPacket = getPacket();
initPacket();
}
update_packet();
if(frame_count == number_of_frames(current_packet)) {
finish_packet();
queue_packet(current_packet);
updatePacket();
if ( _frameCount == numberOfFrames(_currentPacket) ) {
finishPacket();
queuePacket(_currentPacket);
}
}
if(current_packet.valid()) {
finish_packet();
queue_packet(current_packet);
if ( _currentPacket.valid() ) {
finishPacket();
queuePacket(_currentPacket);
}
}
}
}

View File

@@ -31,8 +31,8 @@ namespace CAPS {
//*****************************************************************************
struct Steim2Frame {
u_int32_t nibble_word;
u_int32_t sample_word[15];
u_int32_t nibbleWord;
u_int32_t sampleWord[15];
};
//*****************************************************************************
@@ -43,8 +43,8 @@ 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) {
: Encoder(freqn, freqd), _format(format), _frameCount(0)
, _bp(0), _fp(0), _spw(4), _lastSample(0), _nibbleWord(0) {
}
virtual ~Steim2Encoder();
virtual void flush();
@@ -53,34 +53,34 @@ class Steim2Encoder : public Encoder {
virtual int type() const { return DE_STEIM2; }
private:
void update_spw(int bp);
void updateSpw(int bp);
void store(int32_t value);
void init_packet();
void finish_packet();
void update_packet();
void initPacket();
void updatePacket();
void finishPacket();
MSEEDEncoderPacket<Steim2Frame> get_packet() {
return format->get_packet<Steim2Frame>(_clk.get_time(bp),
MSEEDEncoderPacket<Steim2Frame> getPacket() {
return _format->getPacket<Steim2Frame>(_clk.getTime(_bp),
_clk.correction(), _timingQuality);
}
void queue_packet(MSEEDEncoderPacket<Steim2Frame> &pckt);
void queuePacket(MSEEDEncoderPacket<Steim2Frame> &pckt);
int number_of_frames(const MSEEDEncoderPacket<Steim2Frame> &packet) {
int numberOfFrames(const MSEEDEncoderPacket<Steim2Frame> &packet) const {
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;
MSEEDFormat *_format;
int _frameCount;
int _bp;
int _fp;
int32_t _last_s;
int _spw;
int32_t _lastSample;
int32_t _buf[8];
u_int32_t _nibbleWord;
MSEEDEncoderPacket<Steim2Frame> _currentPacket;
};

View File

@@ -32,163 +32,174 @@ namespace CAPS {
template<typename T> Steim2Encoder<T>::~Steim2Encoder() {
if ( format != NULL ) delete format;
if ( _format ) {
delete _format;
}
}
template<typename T> void Steim2Encoder<T>::update_spw(int bp) {
template<typename T> void Steim2Encoder<T>::updateSpw(int bp) {
assert(bp < 7);
if(buf[bp] < -536870912) {
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;
_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) {
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;
_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;
int spw = 7;
if ( _buf[bp] < -16384 || _buf[bp] > 16383 ) spw = 1;
else if ( _buf[bp] < -512 || _buf[bp] > 511 ) spw = 2;
else if ( _buf[bp] < -128 || _buf[bp] > 127 ) spw = 3;
else if ( _buf[bp] < -32 || _buf[bp] > 31 ) spw = 4;
else if ( _buf[bp] < -16 || _buf[bp] > 15 ) spw = 5;
else if ( _buf[bp] < -8 || _buf[bp] > 7 ) spw = 6;
if ( spw < _spw ) _spw = spw;
}
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;
assert(_bp < 7);
_buf[_bp] = value - _lastSample;
_lastSample = value;
updateSpw(_bp);
++_bp;
}
template<typename T> void Steim2Encoder<T>::init_packet() {
template<typename T> void Steim2Encoder<T>::initPacket() {
int i;
int32_t begin_sample = last_sample;
int32_t begin_sample = _lastSample;
for(i = 1; i < bp; ++i) {
begin_sample -= buf[i];
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;
_currentPacket.data[0].sampleWord[0] = htonl(begin_sample);
_frameCount = 0;
_nibbleWord = 0;
_fp = 2;
}
template<typename T> void Steim2Encoder<T>::finish_packet() {
template<typename T> void Steim2Encoder<T>::finishPacket() {
int i;
int32_t end_sample = last_sample;
int32_t endSample = _lastSample;
for(i = 0; i < bp; ++i) {
end_sample -= buf[i];
for ( i = 0; i < _bp; ++i ) {
endSample -= _buf[i];
}
current_packet.data[0].sample_word[1] = htonl(end_sample);
_currentPacket.data[0].sampleWord[1] = htonl(endSample);
}
template<typename T> void Steim2Encoder<T>::update_packet() {
template<typename T> void Steim2Encoder<T>::updatePacket() {
unsigned int nibble = 0;
u_int32_t sample_word = 0;
u_int32_t sampleWord = 0;
assert(bp < 8);
assert(_bp < 8);
int used = bp;
int used = _bp;
while(used > spw) {
while ( used > _spw ) {
--used;
spw = 7;
for(int i = 0; i < used; ++i) update_spw(i);
_spw = 7;
for ( int i = 0; i < used; ++i ) {
updateSpw(i);
}
}
spw = used;
_spw = used;
switch(spw) {
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);
sampleWord = (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);
sampleWord = (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);
sampleWord = ((_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);
sampleWord = ((_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);
sampleWord = (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);
sampleWord = (2U << 30) | ((_buf[0] & 0x7fff) << 15) |
(_buf[1] & 0x7fff);
break;
case 1:
nibble = 2;
sample_word = (1U << 30) | (buf[0] & 0x3fffffff);
sampleWord = (1U << 30) | (_buf[0] & 0x3fffffff);
break;
default:
assert(0);
break;
}
nibble_word |= (nibble << (30 - ((fp + 1) << 1)));
_nibbleWord |= (nibble << (30 - ((_fp + 1) << 1)));
spw = 7;
for(int i = 0; i < bp - used; ++i) {
buf[i] = buf[i + used];
update_spw(i);
_spw = 7;
for ( int i = 0; i < _bp - used; ++i ) {
_buf[i] = _buf[i + used];
updateSpw(i);
}
bp -= used;
_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;
_currentPacket.data[_frameCount].nibbleWord = htonl(_nibbleWord);
_currentPacket.data[_frameCount].sampleWord[_fp] = htonl(sampleWord);
nibble_word = 0;
fp = 0;
++frame_count;
if ( ++_fp < 15 ) {
return;
}
template<typename T> void Steim2Encoder<T>::queue_packet(MSEEDEncoderPacket<Steim2Frame> &pckt) {
format->updateBuffer(pckt.record, _sampleCount, frame_count + (fp > 0));
_nibbleWord = 0;
_fp = 0;
++_frameCount;
Packet *packet = new Packet(DataRecordPtr(pckt.record), format->networkCode, format->stationCode,
format->locationCode, format->channelCode);
return;
}
template<typename T> void Steim2Encoder<T>::queuePacket(MSEEDEncoderPacket<Steim2Frame> &pckt) {
_format->updateBuffer(pckt.record, _sampleCount, _frameCount + (_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();
@@ -200,37 +211,37 @@ template<typename T> void Steim2Encoder<T>::push(void *value) {
store(sample_val);
_clk.tick();
while ( bp >= spw ) {
if( !current_packet.valid() ) {
current_packet = get_packet();
init_packet();
while ( _bp >= _spw ) {
if ( !_currentPacket.valid() ) {
_currentPacket = getPacket();
initPacket();
}
update_packet();
if ( frame_count == number_of_frames(current_packet) ) {
finish_packet();
queue_packet(current_packet);
updatePacket();
if ( _frameCount == numberOfFrames(_currentPacket) ) {
finishPacket();
queuePacket(_currentPacket);
}
}
}
template<typename T> void Steim2Encoder<T>::flush() {
while ( bp ) {
if ( !current_packet.valid() ) {
current_packet = get_packet();
init_packet();
while ( _bp ) {
if ( !_currentPacket.valid() ) {
_currentPacket = getPacket();
initPacket();
}
update_packet();
if( frame_count == number_of_frames(current_packet) ) {
finish_packet();
queue_packet(current_packet);
updatePacket();
if ( _frameCount == numberOfFrames(_currentPacket) ) {
finishPacket();
queuePacket(_currentPacket);
}
}
if ( current_packet.valid() ) {
finish_packet();
queue_packet(current_packet);
if ( _currentPacket.valid() ) {
finishPacket();
queuePacket(_currentPacket);
}
}

View File

@@ -12,28 +12,29 @@
* 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),
return _format->getPacket<T>(_clk.getTime(-_bp),
_clk.correction(), _timingQuality);
}
void queue_packet(MSEEDEncoderPacket<T> &pckt) {
void queuePacket(MSEEDEncoderPacket<T> &pckt) {
_format->updateBuffer(pckt.record, _sampleCount, 1);
Packet *packet = new Packet(DataRecordPtr(pckt.record), _format->networkCode, _format->stationCode,
@@ -51,7 +52,7 @@ template<typename T> class UncompressedMSEED : public Encoder {
virtual ~UncompressedMSEED() { if ( _format ) delete _format; }
virtual void flush() {
if ( _current_packet.valid() ) {
queue_packet(_current_packet);
queuePacket(_current_packet);
}
}
@@ -78,7 +79,9 @@ template<typename T> class UncompressedMSEED : public Encoder {
};
}
}
#endif // __STEIM1_H__

View File

@@ -13,16 +13,24 @@
***************************************************************************/
#include <gempa/caps/endianess.h>
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/log.h>
#include <gempa/caps/utils.h>
#include <cmath>
#include <climits>
#include <cstdio>
#include <numeric>
#include <streambuf>
#include <iostream>
#include <libmseed.h>
namespace {
// Force local include
#include "./3rd-party/libmseed/libmseed.h"
#include "./3rd-party/libmseed/mseedformat.h"
#define MS_ISVALIDYEARDAY(Y,D) (Y >= 1900 && Y <= 2100 && D >= 1 && D <= 366)
#define LOG_SID(FUNC, format,...)\
do {\
@@ -30,44 +38,151 @@ do {\
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);\
ms_strncpclean(n, pMS2FSDH_NETWORK(head), 2);\
ms_strncpclean(s, pMS2FSDH_STATION(head), 5);\
ms_strncpclean(l, pMS2FSDH_LOCATION(head), 2);\
ms_strncpclean(c, pMS2FSDH_CHANNEL(head), 3);\
FUNC("[%s.%s.%s.%s] " format, n, s, l, c, ##__VA_ARGS__);\
} while(0)
}
namespace Gempa {
namespace CAPS {
namespace {
uint16_t ms2_blktlen(uint16_t blkttype, const char *blkt, int8_t 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 ) {
blktlen = *reinterpret_cast<const uint16_t*>(blkt + 4);
if ( swapflag ) {
ms_gswap2(&blktlen);
}
}
break;
}
return blktlen;
}
bool double2frac(uint16_t &numerator, uint16_t &denominator, double value) {
using namespace std;
// Check numeric limits
if ( value > numeric_limits<uint16_t>::max() ) {
numerator = numeric_limits<uint16_t>::max();
denominator = 1;
return false;
}
if ( value < numeric_limits<uint16_t>::min() ) {
numerator = numeric_limits<uint16_t>::min();
denominator = 1;
return false;
}
// Operate on positive numbers
int sign = 1;
if ( value < 0 ) {
sign = -1;
value = -value;
}
// Calculatate the largest possible power of 10 giving numeric integer
// limits and the current input number
static auto max_exp = floor(log10(numeric_limits<uint16_t>::max())) - 1.0;
auto exp = max_exp;
if ( value >= 10 ) {
exp -= floor(log10(value));
}
// Expand input number with power of 10
denominator = static_cast<int>(pow(10, exp));
numerator = static_cast<int>(round(value * denominator));
// Simplify the fraction by calculating the greatest common divisor
int gcd_ = gcd(numerator, denominator);
numerator = sign * numerator / gcd_;
denominator = denominator / gcd_;
return true;
}
}
MSEEDDataRecord::MSEEDDataRecord() {}
DataRecord *MSEEDDataRecord::clone() const {
return new MSEEDDataRecord(*this);
}
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;
Header &header, Time &startTime, Time &endTime) {
char head[48];
if ( size <= 0 ) {
CAPS_WARNING("read metadata: invalid size of record: %d", size);
return false;
}
if ( size < MINRECLEN || size > MAXRECLEN ) {
if ( (size < MINRECLEN) || (size > MAXRECLEN) ) {
CAPS_WARNING("read metadata: invalid MSEED record size: %d", size);
return false;
}
// Read first 32 byte
// Read first 40 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",
@@ -75,48 +190,52 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
return false;
}
if ( !MS_ISVALIDHEADER(((char*)&head)) ) {
CAPS_WARNING("read metadata: invalid MSEED header");
return false;
if ( MS2_ISVALIDHEADER(((char*)&head)) ) {
int headerswapflag = 0;
// Swap byte order?
if ( !MS_ISVALIDYEARDAY(*pMS2FSDH_YEAR(head), *pMS2FSDH_DAY(head)) ) {
headerswapflag = 1;
ms_gswap2(pMS2FSDH_YEAR(head));
ms_gswap2(pMS2FSDH_DAY(head));
ms_gswap2(pMS2FSDH_FSEC(head));
ms_gswap2(pMS2FSDH_NUMSAMPLES(head));
ms_gswap2(pMS2FSDH_SAMPLERATEFACT(head));
ms_gswap2(pMS2FSDH_SAMPLERATEMULT(head));
ms_gswap4(pMS2FSDH_TIMECORRECT(head));
ms_gswap2(pMS2FSDH_DATAOFFSET(head));
ms_gswap2(pMS2FSDH_BLOCKETTEOFFSET(head));
}
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);
}
auto year = *pMS2FSDH_YEAR(head);
auto yday = *pMS2FSDH_DAY(head);
auto hours = *pMS2FSDH_HOUR(head);
auto minutes = *pMS2FSDH_MIN(head);
auto seconds = *pMS2FSDH_SEC(head);
auto fsec = *pMS2FSDH_FSEC(head) * 100;
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;
}
startTime = Time::FromYearDay(year, yday);
startTime += TimeSpan(hours * 3600 + minutes * 60 + seconds, fsec);
header.quality.ID = 0;
header.quality.str[0] = head.dataquality;
header.quality.str[0] = *pMS2FSDH_DATAQUALITY(head);
if ( head.time_correct != 0 && !(head.act_flags & 0x02) )
hptime += (hptime_t)head.time_correct * (HPTMODULUS / 10000);
if ( *pMS2FSDH_TIMECORRECT(head) != 0 && !(*pMS2FSDH_ACTFLAGS(head) & 0x02) ) {
startTime += TimeSpan(0, *pMS2FSDH_TIMECORRECT(head) * 100);
}
// Parse blockettes
uint32_t blkt_offset = head.blockette_offset;
uint32_t blkt_offset = *pMS2FSDH_BLOCKETTEOFFSET(head);
uint32_t blkt_length;
uint16_t blkt_type;
uint16_t next_blkt;
if ( blkt_offset < sizeof(head) ) {
LOG_SID(CAPS_DEBUG, "read metadata: blockette "
LOG_SID(CAPS_DEBUG, "%s", "read metadata: blockette "
"offset points into header");
return false;
}
@@ -124,13 +243,13 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
uint32_t coffs = 0;
while ( (blkt_offset != 0) && ((int)blkt_offset < size) &&
(blkt_offset < MAXRECLEN) ) {
(blkt_offset < static_cast<uint32_t>(size)) ) {
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: "
LOG_SID(CAPS_DEBUG, "%s", "read metadata: "
"failed to read blockette header");
break;
}
@@ -145,7 +264,7 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
ms_gswap2(&next_blkt);
}
blkt_length = ms_blktlen(blkt_type, bhead, headerswapflag);
blkt_length = ms2_blktlen(blkt_type, bhead, headerswapflag);
if ( blkt_length == 0 ) {
LOG_SID(CAPS_DEBUG, "read metadata: "
@@ -195,7 +314,7 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
}
else if ( blkt_type == 1001 ) {
// Add usec correction
hptime += ((hptime_t)bhead[5]) * (HPTMODULUS / 1000000);
startTime += TimeSpan(0, *reinterpret_cast<int8_t*>(bhead + 5));
}
/* Check that the next blockette offset is beyond the current blockette */
@@ -217,44 +336,102 @@ bool MSEEDDataRecord::readMetaData(std::streambuf &buf, int size,
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;
if ( *pMS2FSDH_SAMPLERATEFACT(head) > 0 ) {
header.samplingFrequencyNumerator = *pMS2FSDH_SAMPLERATEFACT(head);
header.samplingFrequencyDenominator = 1;
}
else {
header.samplingFrequencyNumerator = 1;
header.samplingFrequencyDenominator = -head.samprate_fact;
header.samplingFrequencyDenominator = -*pMS2FSDH_SAMPLERATEFACT(head);
}
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);
if ( *pMS2FSDH_SAMPLERATEMULT(head) > 0 ) {
header.samplingFrequencyNumerator *= *pMS2FSDH_SAMPLERATEMULT(head);
}
else {
header.samplingFrequencyDenominator *= -*pMS2FSDH_SAMPLERATEMULT(head);
}
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;
if ( header.samplingFrequencyNumerator > 0.0 && *pMS2FSDH_NUMSAMPLES(head) > 0 ) {
int64_t dt = static_cast<int64_t>(*pMS2FSDH_NUMSAMPLES(head)) * 1000000 * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan(0, dt);
}
unpackHeader(&data[0], data.size());
timeToTimestamp(header.samplingTime, startTime);
}
else if ( MS3_ISVALIDHEADER(((char*)&head)) ) {
startTime = Time::FromYearDay(
Endianess::Converter::ToLittleEndian(*pMS3FSDH_YEAR(head)),
Endianess::Converter::ToLittleEndian(*pMS3FSDH_DAY(head))
);
header = _header;
startTime = _startTime;
endTime = _endTime;
#endif
startTime += TimeSpan(
Endianess::Converter::ToLittleEndian(*pMS3FSDH_HOUR(head)) * 3600 +
Endianess::Converter::ToLittleEndian(*pMS3FSDH_MIN(head)) * 60 +
Endianess::Converter::ToLittleEndian(*pMS3FSDH_SEC(head)),
Endianess::Converter::ToLittleEndian(*pMS3FSDH_NSEC(head)) / 1000
);
auto nsamp = Endianess::Converter::ToLittleEndian(*pMS3FSDH_NUMSAMPLES(head));
auto fsamp = Endianess::Converter::ToLittleEndian(*pMS3FSDH_SAMPLERATE(head));
if ( fsamp < 0 ) {
fsamp = -1.0 / fsamp;
}
auto encoding = Endianess::Converter::ToLittleEndian(*pMS3FSDH_ENCODING(head));
header.dataType = DT_Unknown;
switch ( encoding ) {
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;
}
header.quality.ID = 0;
if ( !double2frac(header.samplingFrequencyNumerator, header.samplingFrequencyDenominator, fsamp) ) {
CAPS_WARNING("read metadata: invalid sampling rate: %f", fsamp);
return false;
}
endTime = startTime;
if ( header.samplingFrequencyNumerator > 0.0 && *pMS3FSDH_NUMSAMPLES(head) > 0 ) {
int64_t dt = static_cast<int64_t>(nsamp) * 1000000 * header.samplingFrequencyDenominator / header.samplingFrequencyNumerator;
endTime += TimeSpan(0, dt);
}
timeToTimestamp(header.samplingTime, startTime);
return false;
}
else {
CAPS_WARNING("read metadata: invalid MSEED header");
return false;
}
return true;
}
@@ -328,93 +505,6 @@ DataRecord::ReadStatus MSEEDDataRecord::get(std::streambuf &buf, int size,
}
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;
*/
}
@@ -422,68 +512,19 @@ 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;
void MSEEDDataRecord::unpackHeader() {
arraybuf tmp(_data.data(), _data.size());
if ( !readMetaData(tmp, _data.size(), _header, _startTime, _endTime) ) {
CAPS_WARNING("invalid MSEED header");
}
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);
}

View File

@@ -18,7 +18,6 @@
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
@@ -29,46 +28,47 @@ class MSEEDDataRecord : public DataRecord {
public:
MSEEDDataRecord();
virtual const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start,
const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return MSEEDPacket; }
PacketType packetType() const override { 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 setData(const void *data, size_t size);
void unpackHeader() { unpackHeader(_data.data(), _data.size()); }
void unpackHeader();
protected:
@@ -78,10 +78,6 @@ class MSEEDDataRecord : public DataRecord {
Time _endTime;
int _dataType;
private:
void unpackHeader(char *data, size_t size);
};

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_PACKET_H
#define GEMPA_CAPS_PACKET_H
#include <gempa/caps/api.h>
#include <gempa/caps/endianess.h>
@@ -24,12 +25,11 @@
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <cstdint>
#include <string>
#include <vector>
namespace Gempa {
namespace CAPS {
@@ -320,6 +320,7 @@ class SC_GEMPA_CAPS_API DataRecord {
public:
virtual ~DataRecord();
virtual DataRecord *clone() const = 0;
virtual const char *formatName() const = 0;
virtual bool readMetaData(std::streambuf &buf, int size,
@@ -393,7 +394,8 @@ class SC_GEMPA_CAPS_API DataRecord {
Buffer _data;
};
typedef boost::shared_ptr<DataRecord> DataRecordPtr;
using DataRecordPtr = boost::shared_ptr<DataRecord>;
struct RawPacket {
@@ -403,6 +405,7 @@ struct RawPacket {
DataRecord *record;
};
struct MetaPacket {
std::string SID[4];
PacketDataHeader packetDataHeader;
@@ -413,6 +416,7 @@ struct MetaPacket {
Time timestamp;
};
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_PLUGIN_H
#define GEMPA_CAPS_PLUGIN_H
/*
// Enable/disable journal
#define CAPS_FEATURES_JOURNAL 1
@@ -34,8 +35,8 @@
#include <gempa/caps/packet.h>
#include <gempa/caps/socket.h>
#include <gempa/caps/url.h>
#include <gempa/caps/version.h>
#include <gempa/caps/pluginpacket.h>
#include <boost/function.hpp>
@@ -49,10 +50,10 @@
#include <list>
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API Plugin {
public:
enum Status {
@@ -70,8 +71,25 @@ class SC_GEMPA_CAPS_API Plugin {
size_t maxBytesBuffered;
};
struct HostInfo {
std::string agent;
std::string agentVersion;
std::string os;
int64_t totalMem{-1};
int64_t totalDisc{-1};
};
struct RuntimeInfo {
int64_t availableMem{-1};
int64_t procUsedMem{-1};
int64_t availableDisc{-1};
int cpuUsage{-1};
double systemLoad{-1};
};
typedef std::vector<char> Buffer;
typedef boost::shared_ptr<Buffer> BufferPtr;
typedef std::deque<PacketPtr> PacketBuffer;
#if !defined(CAPS_FEATURES_BACKFILLING) || CAPS_FEATURES_BACKFILLING
typedef std::list<PacketPtr> BackfillingBuffer;
@@ -88,13 +106,15 @@ class SC_GEMPA_CAPS_API Plugin {
typedef std::map<std::string, StreamState> StreamStates;
typedef boost::function<void (const std::string &, const CAPS::Time &,
const CAPS::Time &)> PacketAckFunc;
const CAPS::Time &, void *context)> PacketAckFunc;
typedef boost::function<void ()> ConnectedCallback;
typedef boost::function<void ()> DisconnectedCallback;
public:
Plugin(const std::string &name, const std::string &options = "",
const std::string &description = "");
~Plugin();
virtual ~Plugin();
void close();
void quit();
@@ -139,11 +159,19 @@ class SC_GEMPA_CAPS_API Plugin {
*/
void setEncoderFactory(EncoderFactory *factory);
void setHost(const std::string &host) { _host = host; }
const std::string &host() const { return _host; }
/**
* @brief Parses connection parameters from address string. Format
* is [[caps|capss]://][user:pass@]host[:port]
* @param addr The address of the caps server as string
* @param defaultPort The default port used when the port is omitted
*/
bool setAddress(const std::string &addr, uint16_t defaultPort = 18003);
void setPort(unsigned short port) { _port = port; }
unsigned short port() const { return _port; }
void setHost(const std::string &host) { _url.host = host; }
const std::string &host() const { return _url.host; }
void setPort(unsigned short port) { _url.port = port; }
unsigned short port() const { return _url.port; }
void setBufferSize(size_t bufferSize) { _bufferSize = bufferSize; }
size_t bufferSize() const { return _bufferSize; }
@@ -159,8 +187,8 @@ class SC_GEMPA_CAPS_API Plugin {
* @param password The password
*/
void setCredentials(const std::string &user, const std::string &password) {
_user = user;
_password = password;
_url.user = user;
_url.password = password;
}
/**
@@ -176,15 +204,31 @@ class SC_GEMPA_CAPS_API Plugin {
void setPacketAckFunc(const PacketAckFunc &func) { _packetAckFunc = func; }
void setSendTimeout(int timeout) {
_sendTimeout = timeout;
/**
* @brief Sets the connection timout used when the plugin
* tries to establish a connection to CAPS
* @param sec The seconds
*/
void setConnectionTimeout(int sec) {
_connectionTimeout = sec;
}
void setTimeouts(int ack, int lastAck) {
_ackTimeout = ack;
_lastAckTimeout = lastAck;
/**
* @brief Sets the maximum number of seconds to wait until
* the file descriptor is ready for writing.
* @param sec The seconds
*/
void setSendTimeout(int sec) {
_sendTimeout = sec;
}
/**
* @brief Sets timeout values
* @param ack The acknowledgement message value
* @param lastAck The last acknowledgement message value
* @param send See setSendTimeout
* @
*/
void setTimeouts(int ack, int lastAck, int send) {
_ackTimeout = ack;
_lastAckTimeout = lastAck;
@@ -199,11 +243,18 @@ class SC_GEMPA_CAPS_API Plugin {
const StreamStates &streamStates() const { return _states; }
bool writeJournal();
bool writeJournal(std::ostream &ostream);
/**
* @brief Gets end time of the last acknowledged packet
* @param id The stream ID, e.g., GE.APE..BHZ
* @return The last end time or an invalid time
*/
Gempa::CAPS::Time lastEndTime(const std::string &id);
#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);
int timingQuality = -1, void *context = nullptr);
Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
@@ -211,7 +262,53 @@ class SC_GEMPA_CAPS_API Plugin {
uint16_t numerator, uint16_t denominator,
const std::string &uom,
void *data, size_t count,
DataType dt, int timingQuality = -1);
DataType dt, int timingQuality = -1,
void *context = nullptr);
/**
* @brief Sends CAPS data record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param rec CAPS data record
* @param uom Unit of measurement
* @param timingQuality Data timing quality. Ususally between 0 and 100 or -1
* if unset
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushRecord(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, void *context = nullptr);
/**
* @brief Sends C array data as RAW record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param numerator Sampling frequency numerator
* @param denominator Sampling frequency denominator
* @param uom Unit of measurement
* @param data Pointer to data array
* @param count Number of samples
* @param dt Data type, e.g., DT_DOUBLE
* @param timingQuality Data timing quality. Ususally between 0 and 100 or -1
* if unset
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushRaw(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,
void *context = nullptr);
/*
* Sends given data as any packet. Before sending the data will be
@@ -223,21 +320,151 @@ class SC_GEMPA_CAPS_API Plugin {
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);
char *data, size_t count, void *context = nullptr);
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);
const std::string &data, void *context = nullptr);
/**
* @brief Sends C array data as any record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param etime Packet end time
* @param numerator Sampling frequency numerator. Use 1 / 0
* for packets without sampling frequency.
* @param denominator Sampling frequency denominator
* @param format Data format of the provided data
* @param uom Unit of measurement, e.g., px
* @param data Pointer to data array
* @param count Number of data elements
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
char *data, size_t count, void *context = nullptr);
/**
* @brief Sends STL string as any record to CAPS
* @param net Network code
* @param sta Station code
* @param loc Location code
* @param cha Channel code
* @param stime Packet start time
* @param etime Packet end time
* @param numerator Sampling frequency numerator. Use 1 / 0
* for packets without sampling frequency.
* @param denominator Sampling frequency denominator
* @param format Data format of the provided data
* @param uom Unit of measurement, e.g., px
* @param data Data as string
* @param context Pointer to user-defined context data
* @return Status Packet status
*/
Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
const std::string &data, void *context = nullptr);
#endif
void flushEncoders();
void dumpPackets(bool enable);
/**
* @brief Returns the internal packet buffer that
* keeps packets until they have been acknowledged
* by CAPS
* @return The interal packet buffer
*/
const PacketBuffer &packetBuffer() const {
return _packetBuffer;
}
/**
* @brief Set host information like agent or total mem
* @param info The host info struct
*/
void setHostInfo(const HostInfo &info);
/**
* @brief Set runtime information like CPU load or process memory usage.
* This methods blocks until sent.
* @param info The runtime info struct
*/
void setRuntimeInfo(const RuntimeInfo &info);
/**
* @brief Sets the connection ID. If not set, the server generates an ID
* during the first connection attempt.
* @param id The connection ID string
*/
void setConnectionID(const std::string &id);
/**
* @brief Set the connection ID file to read the connection ID from
* @param filename The filename
* @return True if the connection could be read from the given file
*/
bool setConnectionIDFile(const std::string &filename);
/**
* @brief Returns the connection ID
* @return Returns the connection ID
*/
const std::string &connectionID() const;
/**
* @brief Set function that gets called if the connection to the server
* has been established.
*/
void setConnectedCallback(ConnectedCallback f);
/**
* @brief Set function that gets called if the connection to the server
* was disconnected.
*/
void setDisconnectedCallback(DisconnectedCallback f);
const char *version() {
return LIB_CAPS_VERSION_NAME;
}
protected:
/**
* @brief Sends HELLO request and parses API version
* from server response. If the server does not support
* the API feature the method sets the version to 0.
* @param version The CAPS API version, e.g., 5
* @return True on success
*/
bool getAPIVersion(int &version);
/**
* @brief Get connection ID from server. If the plugin sends no
* connection ID the server generates one.
* @return True on success
*/
bool getConnectionID();
/**
* brief Send runtime information
*/
void sendRuntimeInfo();
private:
typedef std::deque<PacketPtr> PacketBuffer;
typedef boost::shared_ptr<Encoder> EncoderPtr;
struct EncoderItem {
@@ -250,6 +477,7 @@ class SC_GEMPA_CAPS_API Plugin {
private:
bool connect();
void disconnect();
void dumpPacket(Packet *packet);
bool isConnected() const;
Encoder* getEncoder(PacketPtr packet);
bool readResponse(unsigned int sec = 0);
@@ -265,7 +493,6 @@ class SC_GEMPA_CAPS_API Plugin {
void tryFlushBackfillingBuffer(StreamState &state);
void trimBackfillingBuffer(StreamState &state);
bool flush();
void flushEncoders();
bool send(char *data, int len, int timeout = 60);
void wait();
@@ -279,13 +506,13 @@ class SC_GEMPA_CAPS_API Plugin {
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;
int _readTimeout;
int _connectionTimeout;
#if !defined(CAPS_FEATURES_JOURNAL) || CAPS_FEATURES_JOURNAL
std::string _journalFile;
bool _journalDirty;
@@ -306,16 +533,25 @@ class SC_GEMPA_CAPS_API Plugin {
PacketAckFunc _packetAckFunc;
std::string _user;
std::string _password;
Url _url;
#if !defined(CAPS_FEATURES_SSL) || CAPS_FEATURES_SSL
bool _useSSL;
#endif
Stats _stats;
TimeSpan _maxFutureEndTime;
bool _dumpPackets;
HostInfo _hostInfo;
RuntimeInfo _runtimeInfo;
std::string _connectionID;
std::string _connectionIDFile;
ConnectedCallback _connectedCallback;
DisconnectedCallback _disconnectedCallback;
};
typedef boost::shared_ptr<Plugin> PluginPtr;
using PluginPtr = boost::shared_ptr<Plugin>;
}
}

View File

@@ -20,19 +20,17 @@
#include <gempa/caps/log.h>
#include <gempa/caps/utils.h>
#include <seiscomp3/core/system.h>
#include <seiscomp3/logging/log.h>
#include <seiscomp/core/system.h>
#include <seiscomp/logging/log.h>
#include <boost/algorithm/string.hpp>
#ifdef SC_GEMPA_SEATTLE
#include <seiscomp3/system/environment.h>
#else
#include <seiscomp3/config/environment.h>
#endif
#include <seiscomp/system/environment.h>
using namespace std;
#include <cstdlib>
#include <filesystem>
namespace fs = std::filesystem;
namespace sc = Seiscomp::Core;
namespace {
@@ -73,49 +71,93 @@ void LogDebug(const char *fmt, ...) {
}
const size_t MIN_BUFFER_SIZE = 1024*16;
const uint16_t DEFAULT_PORT = 18003;
bool readKeyValueFile(std::map<std::string, std::string> &data,
const std::string &filename, const char *sep) {
std::ifstream ifs(filename.c_str());
if ( !ifs.is_open() ) {
return false;
}
std::string line;
while ( getline(ifs, line) ) {
const char* str = line.c_str();
int len = strlen(str);
const char *tok = nullptr;
int tok_len = 0;
int tok_count = 0;
std::string key;
for ( ; (tok = Gempa::CAPS::tokenize(str, sep, len, tok_len)) != nullptr;
++tok_count ) {
Gempa::CAPS::trim(tok, tok_len);
if ( tok_count == 0 ) {
if ( len == 0) {
break;
}
key.assign(tok, tok_len);
}
else if ( tok_count == 1 ) {
data.insert({key, std::string(tok, tok_len)});
}
}
}
return true;
}
int64_t getAvailableMemory() {
std::map<std::string, std::string> data;
if ( !readKeyValueFile(data, "/proc/meminfo", ":") ) {
return -1;
}
auto it = data.find("MemAvailable");
if ( it == data.end() ) {
return -1;
}
std::vector<std::string> tokens;
sc::split(tokens, it->second.c_str(), "kB");
if ( tokens.empty() ) {
return -1;
}
sc::trim(tokens[0]);
int64_t availableMemory = 0;
sc::fromString(availableMemory, tokens[0]);
return availableMemory;
}
}
namespace Gempa {
namespace CAPS {
namespace Gempa::CAPS {
PluginApplication::PluginApplication(int argc, char **argv, const string &desc)
PluginApplication::PluginApplication(int argc, char **argv, const std::string &name)
: 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;
, _plugin(Plugin(name)) {
_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;
fs::path path("@ROOTDIR@/var/run/" + SCCoreApp->name());
_journalFile = (path / "journal").string();
// By default we disable the acquisition autostart because not all plugins
// require this feature. It must be enabled explicitly if required.
setAutoAcquisitionStart(false);
setRecordStreamEnabled(true);
}
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", "output,O",
"Data output address. Format:\n"
"[[caps|capss]://][user:pass@]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);
@@ -128,10 +170,13 @@ void PluginApplication::createCommandLineDescription() {
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 "
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.",
"value the packet will be discarded. By default this value is set to 120 seconds. "
"A negative value disables the check.",
&_maxFutureEndTime);
commandline().addOption("Output", "dump-packets", "Dump packets to stdout");
commandline().addGroup("Journal");
commandline().addOption("Journal", "journal,j",
"File to store stream states. Use an empty string to disable this feature.", &_journalFile);
@@ -148,6 +193,14 @@ void PluginApplication::createCommandLineDescription() {
commandline().addOption("Status", "status-flush", "Flush status every n "
"seconds to disk",
&_statusFlushInterval);
commandline().addGroup("Host");
commandline().addOption("Host", "host-storage",
"Determine disc capacity and available space from this path",
&_host.storage);
commandline().addOption("Host", "host-os",
"Set host operating system information",
&_host.os);
}
void PluginApplication::done() {
@@ -163,6 +216,8 @@ void PluginApplication::done() {
_stats.endTime.valid()?_stats.endTime.iso().c_str():"",
_stats.files);
_plugin.close();
Seiscomp::Client::StreamApplication::done();
}
@@ -173,17 +228,29 @@ void PluginApplication::exit(int returnCode) {
}
void PluginApplication::handleTimeout() {
sc::Time time = sc::Time::GMT().toLocalTime();
auto time = Time::GMT();
auto seconds = time.seconds();
if ( _logStatus && (seconds % _statusFlushInterval == 0) ) {
Plugin::Stats stats = _plugin.stats();
_statusFile.stream() << time.toString("%Y/%m/%d %T") << " " << stats.maxBytesBuffered << endl;
_statusFile.stream() << time.toLocalTime().toString("%Y/%m/%d %T") << " "
<< stats.maxBytesBuffered << std::endl;
_plugin.resetMaxBytesBuffered();
}
if ( seconds % 3 == 0 ) {
updateRuntimeInfo();
}
if ( seconds % 10 == 0 ) {
sendRuntimeInfo();
}
}
bool PluginApplication::init() {
if ( !Seiscomp::Client::StreamApplication::init() ) return false;
if ( !Seiscomp::Client::StreamApplication::init() ) {
return false;
}
// Setup log handlers
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_ERROR, LogError);
@@ -192,12 +259,31 @@ bool PluginApplication::init() {
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_INFO, LogInfo);
Gempa::CAPS::SetLogHandler(Gempa::CAPS::LL_DEBUG, LogDebug);
_plugin.setHost(_host);
_plugin.setPort(_port);
Plugin::HostInfo hostInfo;
if ( !getHostInfo(hostInfo) ) {
return false;
}
_plugin.setBufferSize(_bufferSize);
_plugin.setFlushInterval(_flushInterval);
_plugin.setConnectionTimeout(_connectionTimeout);
_plugin.setTimeouts(_ackTimeout, _lastAckTimeout, _sendTimeout);
_plugin.setMaxFutureEndTime(_maxFutureEndTime);
_plugin.dumpPackets(_dumpPackets);
_plugin.setHostInfo(hostInfo);
LogInfo("CAPS connection settings\n"
" Output CAPS server : %s\n"
" Buffer size : %zu bytes\n"
" Backfilling buffer size : %zu s\n"
" Max future end time : %d s\n"
" Connection timeout : %d s\n"
" Timeouts Ack/LastAck/Send : %d s/%d s/%d s\n"
" Agent : %s\n"
" Agent version : %s",
_strAddr.c_str(), _bufferSize, _backfillingBufferSize, _maxFutureEndTime,
_connectionTimeout, _ackTimeout, _lastAckTimeout, _sendTimeout,
hostInfo.agent.data(), hostInfo.agentVersion.data());
if ( _mseedEnabled ) {
MSEEDEncoderFactory *factory = nullptr;
@@ -211,7 +297,7 @@ bool PluginApplication::init() {
factory = new Steim1EncoderFactory();
_plugin.setEncoderFactory(factory);
}
if ( _mseedEncoding == Steim2 ) {
else if ( _mseedEncoding == Steim2 ) {
SEISCOMP_INFO("Output stream encoding set to MiniSEED/Steim2");
factory = new Steim2EncoderFactory();
_plugin.setEncoderFactory(factory);
@@ -232,7 +318,13 @@ bool PluginApplication::init() {
if ( _backfillingBufferSize > 0 ) {
_plugin.setBackfillingBufferSize(_backfillingBufferSize);
LogInfo("Backfilling buffer size set to %d", _backfillingBufferSize);
}
std::string connectionIDFile = "@ROOTDIR@/var/run/" + name() + "/id";
connectionIDFile = Seiscomp::Environment::Instance()->absolutePath(connectionIDFile);
LogInfo("Reading connection ID from %s", connectionIDFile.c_str());
if ( !_plugin.setConnectionIDFile(connectionIDFile) ) {
return false;
}
if ( !_journalFile.empty() ) {
@@ -245,35 +337,46 @@ bool PluginApplication::init() {
}
if ( _logStatus ) {
string filename = Seiscomp::Environment::Instance()->logDir() + "/" + SCCoreApp->name() + "-stats.log";
std::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);
}
// This causes a connect
updateRuntimeInfo();
_plugin.setRuntimeInfo(_runtimeInfo);
enableTimer(1);
return true;
}
bool PluginApplication::initConfiguration() {
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) return false;
if ( !Seiscomp::Client::StreamApplication::initConfiguration() ) {
return false;
}
try { _host = configGetString("output.host"); }
catch ( ... ) { }
try {
_plugin.setHost(configGetString("output.host"));
}
catch ( ... ) {
}
try { _port = configGetInt("output.port"); }
try {
_plugin.setPort(configGetInt("output.port"));
}
catch ( ... ) { }
try { _sendTimeout = configGetInt("output.timeout"); }
catch ( ... ) { }
try { _connectionTimeout = configGetInt("output.connectionTimeout"); }
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());
std::string addr = configGetString("output.address");
if ( !_plugin.setAddress(addr) ) {
return false;
}
}
@@ -296,8 +399,10 @@ bool PluginApplication::initConfiguration() {
catch ( ... ) {}
try {
string str = configGetString("output.mseed.encoding");
if ( !fromString(_mseedEncoding, str)) return false;
std::string str = configGetString("output.mseed.encoding");
if ( !fromString(_mseedEncoding, str)) {
return false;
}
}
catch ( ... ) {}
@@ -307,6 +412,9 @@ bool PluginApplication::initConfiguration() {
try { _backfillingBufferSize = configGetInt("output.backfillingBufferSize"); }
catch ( ... ) { }
try { _maxFutureEndTime = configGetInt("output.maxFutureEndTime"); }
catch ( ... ) { }
try { _journalFile = configGetString("journal.file"); }
catch ( ... ) {}
@@ -319,20 +427,27 @@ bool PluginApplication::initConfiguration() {
try { _lastAckTimeout = configGetInt("journal.waitForLastAck"); }
catch ( ... ) { }
_host.agent = name();
try { _host.storage = configGetString("host.storage"); }
catch ( ... ) { }
try { _host.os = configGetString("host.os"); }
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 ( !Seiscomp::Client::StreamApplication::validateParameters() ) {
return false;
}
if ( commandline().hasOption("mseed") ) {
_mseedEnabled = true;
@@ -342,8 +457,14 @@ bool PluginApplication::validateParameters() {
_logStatus = true;
}
if ( commandline().hasOption("dump-packets") ) {
_dumpPackets = true;
}
if ( commandline().hasOption("encoding") ) {
if ( !fromString(_mseedEncoding, _strMseedEncoding)) return false;
if ( !fromString(_mseedEncoding, _strMseedEncoding)) {
return false;
}
}
if ( _bufferSize < MIN_BUFFER_SIZE ) {
@@ -352,25 +473,28 @@ bool PluginApplication::validateParameters() {
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());
if ( commandline().hasOption("output") ) {
if ( !_plugin.setAddress(_strAddr, 18003) ) {
return false;
}
}
_host.storage = Seiscomp::Environment::Instance()->absolutePath(_host.storage);
return true;
}
bool PluginApplication::fromString(MseedEncoding &enc, string str) {
bool PluginApplication::fromString(MseedEncoding &enc, std::string str) {
boost::to_lower(str);
if( str == "uncompressed" )
if( str == "uncompressed" ) {
enc = Uncompressed;
else if ( str == "steim1" )
}
else if ( str == "steim1" ) {
enc = Steim1;
else if ( str == "steim2" )
}
else if ( str == "steim2" ) {
enc = Steim2;
}
else {
SEISCOMP_ERROR("Unsupported encoding %s", str.c_str());
return false;
@@ -379,5 +503,60 @@ bool PluginApplication::fromString(MseedEncoding &enc, string str) {
return true;
}
bool PluginApplication::getHostInfo(Plugin::HostInfo &hostInfo) {
hostInfo.agent = _host.agent;
hostInfo.agentVersion = version() ? version() : "";
hostInfo.totalMem = _hostInfo.totalMemory();
try {
auto spaceInfo = fs::space(_host.storage);
hostInfo.totalDisc = spaceInfo.capacity / 1024;
}
catch ( const fs::filesystem_error& e ) {
SEISCOMP_ERROR("Failed to determine filesystem information: %s", e.what());
return false;
}
hostInfo.os = _host.os;
if ( hostInfo.os.empty() ) {
std::map<std::string, std::string> osRelease;
if ( !readKeyValueFile(osRelease, "/etc/os-release", "=") ) {
SEISCOMP_WARNING("Unable to read file /etc/os-release");
}
auto it = osRelease.find("PRETTY_NAME");
if ( it != osRelease.end() ) {
auto unquote = [](const std::string& str) {
if ( str.length() >= 2 && str.front() == '"' && str.back() == '"' ) {
return str.substr(1, str.length() - 2);
}
return str;
};
hostInfo.os = unquote(it->second);
}
}
return true;
}
void PluginApplication::updateRuntimeInfo() {
_runtimeInfo.cpuUsage = std::max(0, static_cast<int>(_hostInfo.getCurrentCpuUsage() * 1000));
_runtimeInfo.procUsedMem = _hostInfo.getCurrentMemoryUsage();
_runtimeInfo.availableMem = getAvailableMemory();
getloadavg(&_runtimeInfo.systemLoad, 1);
try {
auto spaceInfo = fs::space(_host.storage);
_runtimeInfo.availableDisc = spaceInfo.available / 1024;
}
catch ( const fs::filesystem_error& e ) {
SEISCOMP_WARNING("Failed to determine filesystem information: %s", e.what());
}
}
void PluginApplication::sendRuntimeInfo() {
_plugin.setRuntimeInfo(_runtimeInfo);
}
}

View File

@@ -16,98 +16,153 @@
#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>
#include <seiscomp/core/version.h>
#include <seiscomp/client/streamapplication.h>
#if SC_API_VERSION < SC_API_VERSION_CHECK(17, 0, 0)
#include <seiscomp/logging/filerotator.h>
#else
#include <seiscomp/logging/output/filerotator.h>
#endif
#include <seiscomp/system/hostinfo.h>
#include <seiscomp/utils/timer.h>
namespace Gempa::CAPS {
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API PluginApplication : public Seiscomp::Client::StreamApplication {
public:
PluginApplication(int argc, char **argv,
const std::string &desc = "");
const std::string &name = "");
protected:
struct Statistics {
Statistics() = default;
uint32_t records{0};
uint32_t samples{0};
Time startTime;
Time endTime;
uint32_t gaps{0};
uint32_t files{0};
};
/**
* @brief The MseedEncoding enum defines supported MSEED encodings.
*/
enum MseedEncoding {Uncompressed, Steim1, Steim2};
/**
* @brief Adds common plugin commandline options.
*/
virtual void createCommandLineDescription();
void createCommandLineDescription() override;
virtual void done();
/**
* @brief Cleanup method called before exec() returns.
*/
void done() override;
virtual void exit(int returnCode);
/**
* @brief This method is called before the applications exits.
* @param returnCode The exit code
*/
void exit(int returnCode) override;
virtual void handleRecord(Seiscomp::Record *record) {}
virtual void handleTimeout();
/**
* @brief Handles records. This is dummy implemenation as the base
* class requires to implement the method.
* @param record Pointer to record
*/
void handleRecord(Seiscomp::Record *record) override {}
/**
* @brief Handles timer events. For instance writes the plugin states
* information to file.
*/
void handleTimeout() override;
/**
* @brief Initialization method.
*/
virtual bool init();
bool init() override;
/**
* @brief Reads common plugin configuration options.
*/
virtual bool initConfiguration();
bool initConfiguration() override;
/**
* @brief Validates command line parameters.
*/
virtual bool validateParameters();
bool validateParameters() override;
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; }
};
/**
* @brief Get MSEED encoding value from string
* @param enc The encoding
* @param str The encoding as string
* @return True on success
*/
static bool fromString(MseedEncoding &enc, std::string str);
/**
* @brief Get host information like total mem or OS
* @param hostInfo Host info struct
* @return True on success
*/
bool getHostInfo(Plugin::HostInfo &hostInfo);
/**
* @brief Get runtime information
*/
void updateRuntimeInfo();
virtual void sendRuntimeInfo();
protected:
struct Host {
std::string agent;
std::string storage{"@LOGDIR@"};
std::string os;
};
Plugin _plugin;
std::string _host;
ushort _port;
std::string _strAddr;
size_t _bufferSize;
size_t _backfillingBufferSize;
std::string _strAddr{"localhost:18003"};
size_t _bufferSize{1 << 20};
size_t _backfillingBufferSize{180};
bool _mseed;
std::string _journalFile;
int _flushInterval;
int _ackTimeout;
int _lastAckTimeout;
int _sendTimeout;
int _maxFutureEndTime;
int _flushInterval{10};
int _ackTimeout{60};
int _lastAckTimeout{5};
int _sendTimeout{60};
int _connectionTimeout{30};
int _maxFutureEndTime{120};
Statistics _stats;
bool _mseedEnabled;
MseedEncoding _mseedEncoding;
uint _mseedRecordLength;
std::string _strMseedEncoding;
bool _mseedEnabled{false};
MseedEncoding _mseedEncoding{Steim2};
uint _mseedRecordLength{9};
std::string _strMseedEncoding{"Steim2"};
std::string _strPacketType;
Seiscomp::Util::Timer _timer;
FileRotator _statusFile;
bool _logStatus;
uint _statusFlushInterval;
bool _logStatus{false};
uint _statusFlushInterval{10};
bool _dumpPackets{false};
Seiscomp::System::HostInfo _hostInfo;
Host _host;
Plugin::RuntimeInfo _runtimeInfo;
};
}
}

View File

@@ -16,18 +16,28 @@
#ifndef GEMPA_CAPS_PLUGINPACKET_H
#define GEMPA_CAPS_PLUGINPACKET_H
#include "mseed/encoder.h"
#include "packet.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) {
const std::string &locationCode, const std::string &channelCode,
void *context = nullptr)
: record(rec)
, networkCode(networkCode)
, stationCode(stationCode)
, locationCode(locationCode)
, channelCode(channelCode)
, timingQuality(-1)
, context(context)
{
streamID = networkCode + "." + stationCode + "." +
locationCode + "." + channelCode;
}
@@ -42,8 +52,8 @@ struct Packet {
return packet;
}
typedef std::vector<char> Buffer;
typedef boost::shared_ptr<Buffer> BufferPtr;
using Buffer = std::vector<char>;
using BufferPtr = boost::shared_ptr<Buffer>;
BufferPtr buffer; // PacketDataHeader, PacketHeader[V1|V2], Data
DataRecordPtr record;
@@ -58,12 +68,17 @@ struct Packet {
#endif
std::string uom;
int timingQuality;
void *context;
size_t size() const { return buffer->size(); }
};
typedef boost::shared_ptr<Packet> PacketPtr;
using PacketPtr = boost::shared_ptr<Packet>;
}
}
#endif

View File

@@ -83,6 +83,10 @@ inline Time getEndTime(const Time &stime, size_t count, const DataRecord::Header
RawDataRecord::RawDataRecord() : _dataOfs(0), _dataSize(0), _dirty(false) {}
DataRecord *RawDataRecord::clone() const {
return new RawDataRecord(*this);
}
const char *RawDataRecord::formatName() const {
DT2STR("RAW/", _currentHeader.dataType)
}
@@ -249,31 +253,36 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
_startTime = timestampToTime(_header.samplingTime);
if ( _header.samplingFrequencyDenominator > 0 &&
_header.samplingFrequencyNumerator > 0 )
_header.samplingFrequencyNumerator > 0 ) {
_endTime = _startTime + samplesToTimeSpan(_header, sampleCount);
else
}
else {
_endTime = Time();
}
if ( (start.valid() || end.valid()) && _endTime.valid() ) {
// Out of bounds?
if ( end.valid() && (end <= _startTime) )
if ( end.valid() && (end <= _startTime) ) {
return RS_AfterTimeWindow;
}
if ( start.valid() && (start >= _endTime) )
if ( start.valid() && (start >= _endTime) ) {
return RS_BeforeTimeWindow;
}
// Trim packet front
if ( _startTime < start ) {
TimeSpan ofs = start - _startTime;
sampleOfs = timeSpanToSamplesCeil(_header, ofs);
sampleOfs = timeSpanToSamplesFloor(_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
else {
sampleOfs = 0;
}
if ( maxBytes > 0 ) {
int maxSamples = maxBytes / dataTypeSize;
@@ -296,10 +305,13 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
CAPS_DEBUG("Triming packet end: added offset of %d samples", trimEnd);
}
}
else
else {
sampleOfs = 0;
}
if ( sampleCount == 0 ) return RS_Error;
if ( sampleCount == 0 ) {
return RS_Error;
}
_currentHeader = _header;
@@ -308,7 +320,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<1,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -316,7 +330,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<2,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -325,7 +341,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<4,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;
@@ -334,7 +352,9 @@ DataRecord::ReadStatus RawDataRecord::getData(streambuf &buf, int size,
{
// Stay with little endian data
RIFF::VectorChunk<8,false> dataChunk(_data, sampleOfs, sampleCount);
if ( !dataChunk.get(buf, size) ) return RS_Error;
if ( !dataChunk.get(buf, size) ) {
return RS_Error;
}
}
break;

View File

@@ -18,12 +18,12 @@
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
namespace CAPS {
struct SC_GEMPA_CAPS_API RawResponseHeader {
int64_t timeSeconds;
int32_t timeMicroSeconds;
@@ -47,7 +47,8 @@ class RawDataRecord : public DataRecord {
public:
RawDataRecord();
const char *formatName() const;
DataRecord *clone() const override;
const char *formatName() const override;
/**
* @brief Reads metadata from data record header
@@ -59,7 +60,7 @@ class RawDataRecord : public DataRecord {
*/
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime, Time &endTime);
Time &startTime, Time &endTime) override;
void setHeader(const Header &header);
@@ -67,33 +68,33 @@ class RawDataRecord : public DataRecord {
* @brief Returns the meta information of the data if supported
* @return The data record header
*/
const Header *header() const;
const Header *header() const override;
/**
* @brief Returns the start time of the record
* @return The start time
*/
Time startTime() const;
Time startTime() const override;
/**
* @brief Returns the end time of the record
* @return The end time
*/
Time endTime() const;
Time endTime() const override;
/**
* @brief canTrim checks if data trimming is possible
* without modifying preceding data
* @return True, if data trimming is possible
*/
bool canTrim() const;
bool canTrim() const override;
/**
* @brief canMerge checks if data merging is possible
* without modifying preceding data
* @return True, if data merging is possible
*/
bool canMerge() const;
bool canMerge() const override;
/**
* @brief Trims a record to start and end time. Trimming
@@ -109,7 +110,7 @@ class RawDataRecord : public DataRecord {
* @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;
bool trim(const Time &start, const Time &end) const override;
/**
* @brief Returns the data size in bytes if the current state would
@@ -118,7 +119,7 @@ class RawDataRecord : public DataRecord {
* @param withHeader Take header into account
* @return Returns the data size in bytes
*/
size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
/**
* @brief Reads the packet data including header from a streambuf
@@ -137,7 +138,7 @@ class RawDataRecord : public DataRecord {
ReadStatus get(std::streambuf &buf, int size,
const Time &start = Time(),
const Time &end = Time(),
int maxBytes = -1);
int maxBytes = -1) override;
/**
* @brief Reads the packet data without header from a streambuf
@@ -167,13 +168,13 @@ class RawDataRecord : public DataRecord {
* @param Take header into account
* @return True, if the data has been written
*/
bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
/**
* @brief Returns the packet type
* @return The packet type
*/
PacketType packetType() const { return RawDataPacket; }
PacketType packetType() const override { return RawDataPacket; }
/**
* @brief Sets the start time of the record
@@ -218,18 +219,19 @@ class RawDataRecord : public DataRecord {
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,
bool canTrim() const override { return false; }
bool canMerge() const override { return false; }
bool trim(const Time &start, const Time &end) const override { return false; }
const char *formatName() const override;
ReadStatus get(std::streambuf &buf, int size,
const Time &/*start*/, const Time &/*end*/,
int maxBytes) {
int maxBytes) override {
return RawDataRecord::get(buf, size, Time(), Time(), maxBytes);
}
PacketType packetType() const { return FixedRawDataPacket; }
PacketType packetType() const override { return FixedRawDataPacket; }
};
}
}

View File

@@ -16,15 +16,18 @@
#ifndef GEMPA_CAPS_RECORD_SAMPLER_H
#define GEMPA_CAPS_RECORD_SAMPLER_H
#include <gempa/caps/datetime.h>
#include <gempa/caps/datetime.h>
#include <boost/shared_ptr.hpp>
#include <functional>
#include <vector>
namespace Gempa {
namespace CAPS {
template<typename T> class RecordBuilder {
public:
struct Record {
@@ -40,7 +43,8 @@ template<typename T> class RecordBuilder {
void *userData;
};
typedef boost::shared_ptr<Record> RecordPtr;
typedef boost::function<void (Record *rec, void*)> FlushCallback;
typedef std::function<void (Record *rec, void*)> FlushCallback;
public:
RecordBuilder() : _bufSize(64) {}
@@ -130,6 +134,7 @@ template<typename T> class RecordBuilder {
void *_userData;
};
}
}

View File

@@ -61,6 +61,11 @@ void RTCM2DataRecord::setSamplingFrequency(uint16_t numerator, uint16_t denomina
}
DataRecord *RTCM2DataRecord::clone() const {
return new RTCM2DataRecord(*this);
}
const char *RTCM2DataRecord::formatName() const {
return "RTC2";
}

View File

@@ -20,11 +20,11 @@
#include <gempa/caps/endianess.h>
#include <gempa/caps/packet.h>
#include <vector>
namespace Gempa {
namespace CAPS {
class RTCM2DataRecord : public DataRecord {
public:
struct RTCM2Header : Header {
@@ -61,37 +61,38 @@ class RTCM2DataRecord : public DataRecord {
RTCM2DataRecord();
const char* formatName() const;
DataRecord *clone() const override;
const char* formatName() const override;
void setTimeStamp(const Time &ts);
void setSamplingFrequency(uint16_t numerator, uint16_t denominator);
virtual bool readMetaData(std::streambuf &buf, int size,
bool readMetaData(std::streambuf &buf, int size,
Header &header,
Time &startTime,
Time &endTime);
Time &endTime) override;
virtual const Header *header() const;
virtual Time startTime() const;
virtual Time endTime() const;
const Header *header() const override;
Time startTime() const override;
Time endTime() const override;
virtual bool canTrim() const;
virtual bool canMerge() const;
bool canTrim() const override;
bool canMerge() const override;
virtual bool trim(const Time &start,
const Time &end) const;
bool trim(const Time &start,
const Time &end) const override;
virtual size_t dataSize(bool withHeader) const;
size_t dataSize(bool withHeader) const override;
virtual ReadStatus get(std::streambuf &buf, int size,
ReadStatus get(std::streambuf &buf, int size,
const Time &start,
const Time &end,
int maxSize = -1);
int maxSize = -1) override;
virtual bool put(std::streambuf &buf, bool withHeader) const;
bool put(std::streambuf &buf, bool withHeader) const override;
PacketType packetType() const { return RTCM2Packet; }
PacketType packetType() const override { return RTCM2Packet; }
private:

View File

@@ -16,9 +16,11 @@
#ifndef GEMPA_CAPS_SESSIONTABLE_H
#define GEMPA_CAPS_SESSIONTABLE_H
#include "packet.h"
#include <map>
#include <optional>
#include <string>
#include <functional>
@@ -26,6 +28,7 @@
namespace Gempa {
namespace CAPS {
struct SessionTableItem {
SessionTableItem() : samplingFrequency(0), samplingFrequencyDivider(0),
fSamplingFrequency(0.0), dataType(DT_Unknown),
@@ -42,8 +45,8 @@ struct SessionTableItem {
DataType dataType;
int dataSize;
UOM uom;
Time startTime;
Time endTime;
std::optional<Time> startTime;
std::optional<Time> endTime;
void *userData;
bool splitStreamID();
@@ -54,7 +57,7 @@ class SC_GEMPA_CAPS_API SessionTable : public std::map<int, SessionTableItem> {
public:
enum Status {Success, Error, EOD};
typedef std::function<void (SessionTableItem*)> CallbackFunc;
using CallbackFunc = std::function<void (SessionTableItem*)>;
public:
//! Default constructor
@@ -97,6 +100,7 @@ class SC_GEMPA_CAPS_API SessionTable : public std::map<int, SessionTableItem> {
CallbackFunc _itemAboutToBeRemovedFunc;
};
}
}

View File

@@ -230,7 +230,7 @@ Socket::Device::Status Socket::setNonBlocking(bool nb) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
Socket::Status Socket::connect(const std::string &hostname, uint16_t port, int timeout) {
if ( _fd != -1 ) {
//CAPS_WARNING("closing stale socket");
close();
@@ -250,7 +250,7 @@ Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
int ret = getaddrinfo(hostname.c_str(), strPort.c_str(), &hints, &res);
if ( ret ) {
CAPS_DEBUG("Test3 Socket::connect(%s:%d): %s",
CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port,
#ifndef WIN32
strerror(errno));
@@ -271,14 +271,46 @@ Socket::Status Socket::connect(const std::string &hostname, uint16_t port) {
}
#ifndef WIN32
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == -1 ) {
if ( errno != EINPROGRESS ) {
setNonBlocking(true);
int res2 = ::connect(_fd, &addr, addrlen);
if ( res2 == -1 ) {
if ( errno == EINPROGRESS || errno == EAGAIN ) {
fd_set wfds;
FD_ZERO(&wfds);
FD_SET(_fd, &wfds);
struct timeval tv{timeout, 0};
int res = select(_fd + 1, nullptr, &wfds, nullptr, &tv);
if ( res == -1 ) {
close();
return ConnectError;
}
else if ( res ) {
res = ::connect(_fd, &addr, addrlen);
if ( res == -1 ) {
close();
CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port, strerror(errno));
return ConnectError;
}
}
else {
close();
return Timeout;
}
}
else {
/*CAPS_DEBUG("Socket::connect(%s:%d): %s",
hostname.c_str(), port, strerror(errno));*/
close();
return errno == ETIMEDOUT ? Timeout : ConnectError;
}
}
setNonBlocking(false);
#else
if ( ::connect(_fd, (struct sockaddr *)&addr, addrlen) == SOCKET_ERROR ) {
int err = WSAGetLastError();
@@ -445,7 +477,8 @@ int SSLSocket::read(char *data, int len) {
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port) {
Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port,
int timeout) {
cleanUp();
_ctx = SSL_CTX_new(SSLv23_client_method());
@@ -469,14 +502,52 @@ Socket::Status SSLSocket::connect(const std::string &hostname, uint16_t port) {
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));
setNonBlocking(true);
int res = 0;
while ( (res = SSL_connect(_ssl)) != 1 ) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(_fd, &fds);
struct timeval tv{timeout, 0};
switch ( SSL_get_error(_ssl, res) ) {
case SSL_ERROR_WANT_READ:
res = select(_fd + 1, &fds, nullptr, nullptr, &tv);
if ( res == 0 ) {
close();
return Timeout;
}
else if ( res == - 1 ) {
close();
return ConnectError;
}
break;
case SSL_ERROR_WANT_WRITE:
res = select(_fd + 1, nullptr, &fds, nullptr, &tv);
if ( res == 0 ) {
close();
return Timeout;
}
else if ( res == - 1 ) {
close();
return ConnectError;
}
break;
default: {
CAPS_ERROR("Failed to connect with SSL, error %d",
SSL_get_error(_ssl, res));
close();
return ConnectError;
}
}
}
setNonBlocking(false);
return Success;
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

View File

@@ -22,14 +22,14 @@
#include <openssl/ssl.h>
#include <stdint.h>
#include <cstdint>
#include <streambuf>
#include <iostream>
#include <fstream>
namespace Gempa {
namespace CAPS {
class SC_GEMPA_CAPS_API Socket {
public:
typedef uint64_t count_t;
@@ -89,7 +89,8 @@ class SC_GEMPA_CAPS_API Socket {
Device::Status setNonBlocking(bool nb);
virtual Status connect(const std::string &hostname, uint16_t port);
virtual Status connect(const std::string &hostname, uint16_t port,
int timeout = 30);
count_t rx() const { return _bytesReceived; }
count_t tx() const { return _bytesSent; }
@@ -122,7 +123,7 @@ class SSLSocket : public Socket {
int write(const char *data, int len);
int read(char *data, int len);
Status connect(const std::string &hostname, uint16_t port);
Status connect(const std::string &hostname, uint16_t port, int timeout = 30);
virtual const unsigned char *sessionID() const;
virtual unsigned int sessionIDLength() const;
@@ -161,10 +162,6 @@ class socketbuf : public std::streambuf {
_sock = sock;
}
void settimeout(const struct timeval &tv) {
_timeout = tv;
}
void set_read_limit(int bytes) {
_allowed_reads = bytes;
@@ -258,7 +255,6 @@ class socketbuf : public std::streambuf {
private:
T *_sock;
timeval _timeout;
char _in[N];
char _out[N];
bool _block_write;
@@ -266,6 +262,7 @@ class socketbuf : public std::streambuf {
int _allowed_reads;
};
}
}

View File

@@ -14,9 +14,12 @@
#ifndef STRPTIME_H
#define STRPTIME_H
struct tm;
/*
* Version of "strptime()", for the benefit of OSes that don't have it.
*/
extern char *strptime(const char *, const char *, struct tm *);
#endif

View File

@@ -6,6 +6,7 @@ SET(TESTS
datetime_time.cpp
endianess.cpp
mseedpacket.cpp
url.cpp
)
FOREACH(testSrc ${TESTS})

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/datetime.h>
#include <string>

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/datetime.h>
@@ -146,9 +146,9 @@ BOOST_AUTO_TEST_CASE(construction) {
// 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);
double secondsPerDay = 86400;
double secondsPerYear = 31536000;
BOOST_WARN_CLOSE(double(date), secondsPerDay*2 + secondsPerYear,0.3);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -393,13 +393,14 @@ BOOST_AUTO_TEST_CASE(setAndGet) {
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);
// Won't work on 32-bit systems
// 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);
@@ -409,12 +410,12 @@ BOOST_AUTO_TEST_CASE(setAndGet) {
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);
// 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);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -477,9 +478,9 @@ BOOST_AUTO_TEST_CASE(localTime) {
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);
double secondsPerDay = 86400;
double secondsPerYear = 31536000;
BOOST_CHECK_EQUAL(double(yearDay),secondsPerDay*2 + secondsPerYear);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/endianess.h>
#include <gempa/caps/rawpacket.cpp>

View File

@@ -19,7 +19,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include "test_utils.h"
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/mseedpacket.h>
#include <gempa/caps/rawpacket.cpp>
@@ -27,10 +27,8 @@
#include <gempa/caps/datetime.h>
#include <fstream>
#include <iostream>
#include <math.h>
#include <string>
#include <streambuf>
#include <cmath>
namespace bu = boost::unit_test;

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/packet.h>
#include <gempa/caps/rawpacket.cpp>

View File

@@ -23,7 +23,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include "test_utils.h"
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/rawpacket.cpp>
#include <gempa/caps/mseedpacket.cpp>

View File

@@ -0,0 +1,101 @@
/***************************************************************************
* Copyright (C) 2021 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_TEST_MODULE gempa
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/url.h>
namespace gc = Gempa::CAPS;
namespace bu = boost::unit_test;
using namespace gc;
using namespace std;
BOOST_AUTO_TEST_SUITE(gempa_common_caps_url)
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_CASE(url_construct) {
Url url;
BOOST_CHECK_EQUAL(url.host, "");
BOOST_CHECK_EQUAL(url.user, "");
BOOST_CHECK_EQUAL(url.password, "");
BOOST_CHECK_EQUAL(url.port, 0);
BOOST_CHECK_EQUAL(url.protocol, "");
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_CASE(url_parse) {
{
Url url;
BOOST_CHECK(url.parse("localhost"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.port, 18002);
}
{
Url url;
BOOST_CHECK(url.parse("localhost:18003"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.port, 18003);
BOOST_CHECK_EQUAL(url.protocol, "caps");
}
{
Url url;
BOOST_CHECK(!url.parse("localhost:65536"));
BOOST_CHECK(!url.parse("localhost:-1"));
BOOST_CHECK(!url.parse("localhost:0"));
}
{
Url url;
BOOST_CHECK(url.parse("caps:gempa@localhost:18003"));
BOOST_CHECK_EQUAL(url.host, "localhost");
BOOST_CHECK_EQUAL(url.user, "caps");
BOOST_CHECK_EQUAL(url.password, "gempa");
BOOST_CHECK_EQUAL(url.port, 18003);
BOOST_CHECK_EQUAL(url.protocol, "caps");
}
{
Url url;
BOOST_CHECK(url.parse("capss://guest:guest@caps.gempa.de:18008"));
BOOST_CHECK_EQUAL(url.host, "caps.gempa.de");
BOOST_CHECK_EQUAL(url.user, "guest");
BOOST_CHECK_EQUAL(url.password, "guest");
BOOST_CHECK_EQUAL(url.port, 18008);
BOOST_CHECK_EQUAL(url.protocol, "capss");
}
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOST_AUTO_TEST_SUITE_END()

View File

@@ -17,7 +17,7 @@
#define SEISCOMP_TEST_MODULE gempa
#include <seiscomp3/unittest/unittests.h>
#include <seiscomp/unittest/unittests.h>
#include <gempa/caps/utils.h>

77
libs/gempa/caps/url.cpp Normal file
View File

@@ -0,0 +1,77 @@
/***************************************************************************
* Copyright (C) 2021 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 "url.h"
#include "utils.h"
#include <boost/algorithm/string.hpp>
using namespace std;
namespace {
const string CAPS_PROTOCOL = "caps";
const string CAPS_SSL_PROTOCOL = "capss";
}
namespace Gempa {
namespace CAPS {
Url::Url()
: port(0) {}
bool Url::parse(const string &address, uint16_t defaultPort) {
string addr = trim(address);
// step 1: protocol
size_t pos = addr.find("://");
if ( pos == string::npos ) {
protocol = CAPS_PROTOCOL;
}
else {
protocol = addr.substr(0, pos);
addr = addr.substr(pos + 3);
}
if ( !boost::iequals(protocol, CAPS_PROTOCOL) &&
!boost::iequals(protocol, CAPS_SSL_PROTOCOL) ) {
errorString = "Unsupported protocol: %" + protocol;
return false;
}
// step 2: user:pass
vector<string> toks;
boost::split(toks, addr, boost::is_any_of("@"), boost::token_compress_on);
if ( toks.size() >= 2 ) {
string login = toks[0];
addr = toks[1];
boost::split(toks, login, boost::is_any_of(":"), boost::token_compress_on);
user = toks.size() > 0 ? toks[0] : "";
password = toks.size() > 1 ? toks[1] : "";
}
// step 3: address
if ( !splitAddress(host, port, addr, defaultPort) ) {
errorString = "Wrong address format, expected "
"[[caps|capss]://][user:pass@]host[:port]";
return false;
}
return true;
}
}
}

53
libs/gempa/caps/url.h Normal file
View File

@@ -0,0 +1,53 @@
/***************************************************************************
* Copyright (C) 2021 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_URL_H
#define GEMPA_CAPS_URL_H
#include <string>
#include <cstdint>
namespace Gempa {
namespace CAPS {
struct Url {
Url();
/**
* @brief Reads url parameters from address string. The
* addr format is [[caps|capss]://][user:pass@]host[:port].
* @param address The address as string
* @param defaultPort Default port
* @return True if the address could be parsed
*/
bool parse(const std::string &address, uint16_t defaultPort = 18002);
std::string host;
uint16_t port;
std::string user;
std::string password;
std::string protocol;
std::string errorString;
};
}
}
#endif

View File

@@ -16,6 +16,7 @@
#ifndef GEMPA_CAPS_UTILS_H
#define GEMPA_CAPS_UTILS_H
#include <gempa/caps/packet.h>
#include <cerrno>
@@ -32,6 +33,7 @@
namespace Gempa {
namespace CAPS {
class arraybuf : public std::streambuf {
public:
typedef std::streambuf::pos_type pos_type;
@@ -371,7 +373,9 @@ inline uint8_t dataTypeSize(DataType dt) {
return 0;
}
}
}
#endif

View File

@@ -6,8 +6,8 @@
***************************************************************************/
#ifndef __GEMPA_CAPS_VERSION_H__
#define __GEMPA_CAPS_VERSION_H__
#ifndef GEMPA_CAPS_VERSION_H
#define GEMPA_CAPS_VERSION_H
/* #if (LIB_CAPS_VERSION >= LIB_CAPS_VERSION_CHECK(1, 0, 0)) */
@@ -18,15 +18,36 @@
#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"
#define LIB_CAPS_VERSION 0x030200
#define LIB_CAPS_VERSION_NAME "3.2.0"
/******************************************************************************
API Changelog
******************************************************************************
"3.2.0" 0x030200
- Add Plugin::lastEndTime
- Add Plugin::setConnectionIDFile
- Add Plugin::connectionID
- Add Plugin::setConnectedCallback
- Add Plugin::setDisconnectedCallback
- Add Plugin::setHostInfo
- Add Plugin::setRuntimeInfo
- Add Plugin::pushAny
- Add Plugin::pushRaw
- Add Plugin::pushRecord
- Change Plugin push signature
"2.0.0" 0x020000
- Add Plugin::setAddress
- Add Plugin::flushEncoders
- Add Plugin::dumpPackets
- Add Plugin::packetBuffer
- Add Plugin::setAgent
- Add Plugin::getAPIVersion
- Change Plugin memory layout
"1.0.0" 0x010000
- Initial version
*/
#endif // __GEMPA_CAPS_VERSION_H__
#endif // GEMPA_CAPS_VERSION_H

View File

@@ -1,4 +1,4 @@
%feature("notabstract") Gempa::CAPS::AnyDataRecord;
%feature("notabstract") Gempa::CAPS::AnyDataRecord;
%include "stdint.i"
%init
@@ -31,14 +31,13 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
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
) {
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);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT8, 1, 1);
if ( arr == NULL )
break;
@@ -46,7 +45,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int8_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT16:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT16, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT16, 1, 1);
if ( arr == NULL )
break;
@@ -54,7 +53,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int16_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_INT32:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_INT32, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_INT32, 1, 1);
if ( arr == NULL )
break;
@@ -62,7 +61,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (int32_t*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_FLOAT:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT32, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_FLOAT32, 1, 1);
if ( arr == NULL )
break;
@@ -70,7 +69,7 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
uom, (float*)(arr->data), arr->dimensions[0], dataType);
break;
case Gempa::CAPS::DT_DOUBLE:
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, PyArray_FLOAT64, 1, 1);
arr = (PyArrayObject*) PyArray_ContiguousFromObject(obj, NPY_FLOAT64, 1, 1);
if ( arr == NULL )
break;
@@ -85,6 +84,21 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
return status;
}
Gempa::CAPS::Plugin::Status push(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const std::string &uom, PyObject *obj) {
char *data;
Py_ssize_t len;
if ( PyBytes_AsStringAndSize(obj, &data, &len) == -1 )
return Gempa::CAPS::Plugin::PacketLoss;
Gempa::CAPS::MSEEDDataRecord *mseed = new Gempa::CAPS::MSEEDDataRecord;
Gempa::CAPS::DataRecordPtr rec(mseed);
mseed->setData(data ,len);
return self->push(net, sta, loc, cha, rec, uom);
}
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,
@@ -98,12 +112,37 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
return self->push(net, sta, loc, cha, stime, numerator,
denominator, format, data, len);
}
Gempa::CAPS::Plugin::Status pushAny(const std::string &net, const std::string &sta,
const std::string &loc, const std::string &cha,
const Time &stime, const Time &etime,
uint16_t numerator, uint16_t denominator,
const std::string &format, const std::string &uom,
PyObject *obj) {
char *data;
Py_ssize_t len;
if ( PyBytes_AsStringAndSize(obj, &data, &len) == -1 )
return Gempa::CAPS::Plugin::PacketLoss;
return self->pushAny(net, sta, loc, cha, stime, etime,
numerator, denominator, format, uom, data, len);
}
};
%include "exception.i"
%include "std_string.i"
%include "numpy.i"
%exception Gempa::CAPS::Plugin::push {
Py_BEGIN_ALLOW_THREADS
$action
Py_END_ALLOW_THREADS
}
%include "gempa/caps/api.h"
%include "gempa/caps/datetime.h"
%include "gempa/caps/packet.h"
@@ -115,7 +154,9 @@ typedef Gempa::CAPS::Plugin::Buffer Buffer;
%include "gempa/caps/encoderfactory.h"
%include "gempa/caps/mseedpacket.h"
typedef Gempa::CAPS::Plugin::Buffer Buffer;
%apply SWIGTYPE *DISOWN {Gempa::CAPS::EncoderFactory *factory};
%include "gempa/caps/plugin.h"
%clear Gempa::CAPS::EncoderFactory *factory;
%include "gempa/caps/rawpacket.h"
%include "gempa/caps/riff.h"
%include "gempa/caps/rtcm2packet.h"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff