diff --git a/src/seedlink/plugins/CMakeLists.txt b/src/seedlink/plugins/CMakeLists.txt index 055aa57f7c..8cab54fe4e 100644 --- a/src/seedlink/plugins/CMakeLists.txt +++ b/src/seedlink/plugins/CMakeLists.txt @@ -26,6 +26,7 @@ IF ( NOT MACOSX ) sockplugin wave24_plugin win_plugin + seismicpi_plugin ) ENDIF ( NOT MACOSX ) diff --git a/src/seedlink/plugins/descriptions/seedlink_seismicpi.xml b/src/seedlink/plugins/descriptions/seedlink_seismicpi.xml new file mode 100644 index 0000000000..329ea41aa1 --- /dev/null +++ b/src/seedlink/plugins/descriptions/seedlink_seismicpi.xml @@ -0,0 +1,79 @@ + + + + SeismicPI HAT Interface + + + + Device path and name of port for USB Seismometer Interface. + If the specified port cannot be opened or is not a USB Seismometer + Interface device, all available ports will be scanned. + + + + + Allow low-level setting of port interface attributes when available + ports are scanned to find a USB Seismometer Interface device, 0=NO, 1=Yes. + Setting 1 (=Yes) may help successful detection and correct reading of the + USB Seismometer Interface device, particularly for the RasberryPi, but can + have adverse effects on other devices, terminals, etc. open on the system. + + + + + Sets a fixed sample rate to report in the miniseed file header. + The default (32) sets the sample rate to 32 samples per second. + See also: nominal_sample_rate + + + + + SEED data encoding type for writing miniseed files. + Supported values are: INT16, INT32, STEIM1, STEIM2 + + + + + The initial letters to set for the miniseed header 'channel', will be + prepended to the component. + + + + + The initial code to set for the miniseed header 'location', will be + prepended to the component. If empty JamaSeis software will not be able to acquire data. + + + + + Component of seismogram, one of Z, N or E. + + + + + Set sample rate and gain on SeismicPi HAT device, 0=NO, 1=Yes. + + + + + Nominal sample rate per second, one of 32, 64 or 128. + + + + + Nominal gain, one of 1, 2, 4 or 8. + + + + + Single (1) or Multi Channel (2) acquisition. + + + + + Select as source the internal accelerometer (1) or an external devicce (2). + + + + + diff --git a/src/seedlink/plugins/seismicpi_plugin/CMakeLists.txt b/src/seedlink/plugins/seismicpi_plugin/CMakeLists.txt new file mode 100644 index 0000000000..564b943e86 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/CMakeLists.txt @@ -0,0 +1,16 @@ +SET(SEISMICPI_PLUGIN_SOURCES + seismicpi_writer.c + seismicpi_writer.h + seismicpidevice.c + seismicpidevice.h + settings/settings.c + settings/settings.h + settings/strmap.c + settings/strmap.h +) + +INCLUDE_DIRECTORIES(../../libs/plugin ${LIBMSEED_INCLUDE_DIR}) +ADD_EXECUTABLE(seismicpi_plugin ${SEISMICPI_PLUGIN_SOURCES}) +TARGET_LINK_LIBRARIES(seismicpi_plugin rt slplugin ${LIBMSEED_LIBRARY}) +INSTALL(TARGETS seismicpi_plugin RUNTIME DESTINATION ${SEEDLINK_PLUGIN_OUTPUT_DIR}) + diff --git a/src/seedlink/plugins/seismicpi_plugin/Makefile b/src/seedlink/plugins/seismicpi_plugin/Makefile new file mode 100644 index 0000000000..20dcd7cb74 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/Makefile @@ -0,0 +1,86 @@ + +# Build environment can be configured the following +# environment variables: +# CC : Specify the C compiler to use +# CFLAGS : Specify compiler options to use + +# IMPORTANT: change here to set the directory for binary executable files +ifdef MYBIN +export BINDIR=${MYBIN} +else +# with the following, binary executables will be placed in bin subdirectory of your home directory +#BINDIR=~/bin/ +# if in doubt, use the following - binary executables will be placed in the current directory +export BINDIR=. +endif + +# Options specific for GCC +ifndef CC +export CC = gcc +endif +# +ifndef CCFLAGS +# +export CCFLAGS_BASIC = -Wall -fPIC -I.. +# +# optimized +export CCFLAGS = -O3 $(CCFLAGS_BASIC) +# +# profile +#CCFLAGS=-O3 -pg $(CCFLAGS_BASIC) +# +# debug - gdb, valgrind, ... +#CCFLAGS = $(CCFLAGS_BASIC) -g +# valgrind --leak-check=yes exe_name +# valgrind --leak-check=full --show-reachable=yes exe_name exe_name +endif + +#LDFLAGS = -L.. +#LDLIBS = -lm + +# variables to support compiling in different configurations (called by higher level Makefile in distribution) +ifndef LIB_MSEED + export LIB_MSEED = -lmseed +endif +ifndef LIB_RT + export LIB_RT = +endif + +OBJ=seismicpidevice.o settings/settings.o settings/strmap.o + + +all: $(BINDIR)/seismicpi_writer + +$(BINDIR)/seismicpi_writer: seismicpi_writer.o modules $(OBJ) + $(CC) $(CCFLAGS) -o $(BINDIR)/seismicpi_writer seismicpi_writer.o $(OBJ) $(LIB_MSEED) $(LIB_RT) -lm + + +MODULES = \ + settings + +modules: + @for x in $(MODULES); \ + do \ + (echo ------; cd $$x; echo Making $@ in:; pwd; \ + make -f Makefile); \ + done + +clean: + @for x in $(MODULES); \ + do \ + (cd $$x; echo Cleaning in:; pwd; \ + make -f Makefile clean); \ + done + rm -f $(BINDIR)/seismicpi_writer + rm -f *.o + rm -f *.a + + +# Implicit rule for building object files +%.o: %.c + $(CC) $(CCFLAGS) -c $< + +install: + @echo + @echo "No install target, copy the executable(s) yourself" + @echo diff --git a/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.c b/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.c new file mode 100644 index 0000000000..96335ef312 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.c @@ -0,0 +1,1065 @@ +/*************************************************************************** + * seismicpi_writer.c + * + * Read serial data from a serial port and write 512-byte Mini-SEED data records in mseed files. + * + * Written by Anthony Lomax + * ALomax Scientific www.alomax.net + * + * created: 2013.10.28 + * + * Modified by Kostas Boukouras (kbouk@noa.gr) for 3-Channel Seismic Pi support + * created: 2020.07.10 + ***************************************************************************/ + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +//#include +#include + +#ifndef WIN32 +#include +static void term_handler(int sig); +#endif + +#include "libmseed.h" +#include "seismicpi_writer.h" +#include "seismicpidevice.h" +#include "settings/settings.h" +#include "plugin.h" + +#define DEBUG 0 + +#define STANDARD_STRLEN 4096 + + +#ifdef EXTERN_MODE +#define EXTERN_TXT extern +#else +#define EXTERN_TXT +#endif + +EXTERN_TXT int verbose; +EXTERN_TXT char propfile[STANDARD_STRLEN]; + +// properties +// Logging +EXTERN_TXT char port_path_hint[STANDARD_STRLEN]; +#define PORT_PATH_HINT_DEFAULT "/dev/ttyS0" // rasberry pi +//#define PORT_PATH_HINT_DEFAULT "/dev/usbdev1.1" // iMac +EXTERN_TXT int allow_set_interface_attribs; +#define ALLOW_SET_INTERFACE_ATTRIBS_DEFAULT 1 + +EXTERN_TXT double mswrite_header_sample_rate; +#define MSWRITE_HEADER_SAMPLE_RATE_DEFAULT -1 +EXTERN_TXT char mswrite_data_encoding_type[STANDARD_STRLEN]; +#define MSWRITE_DATA_ENCODING_TYPE_DEFAULT "DE_INT32" +EXTERN_TXT int mswrite_data_encoding_type_code; + +// Station +EXTERN_TXT char station_network[STANDARD_STRLEN]; +#define STA_NETWORK_DEFAULT "UK" +EXTERN_TXT char station_name[STANDARD_STRLEN]; +#define STA_NAME_DEFAULT "TEST" +EXTERN_TXT char locationcode_prefix[STANDARD_STRLEN]; +#define STA_LOCATION_CODE_PREFIX_DEFAULT "00" +EXTERN_TXT char channel_prefix[STANDARD_STRLEN]; +#define STA_CHANNEL_PREFIX_DEFAULT "BH" +EXTERN_TXT char component[STANDARD_STRLEN]; +#define STA_COMPONENT_DEFAULT "Z" +// +EXTERN_TXT int nominal_sample_rate; +#define STA_NOMINAL_SAMPLE_RATE_DEFAULT 32 +//Nominal sample rate in seconds, one of 32, 64 or 128. type=string default=20 +// For SEISMOMETER INTERFACE (SeismicPi HAT): +// The SPS can be adjusted by sending single characters to the Virtual Com Port: +// ‘a’: 32 SPS +// ‘b’: 64 SPS +// ‘c’: 128 SPS +// +EXTERN_TXT int nominal_gain; +#define STA_NOMINAL_GAIN_DEFAULT 4 +//Nominal gain, one of 1, 2, 4 or 8. type=string default=1 +// For SEISMOMETER INTERFACE seismicPI: +// The gain can be adjusted by sending single characters to the Virtual Com Port: +// ‘1’: ×1 = 0.64μV/count +// ‘2’: ×2 = 0.32μV/count +// ‘4’: ×2 = 0.16μV/count +// ‘8’: ×2 = 0.08μV/count +// +EXTERN_TXT int single_multi; +#define STA_SINGLE_MULTI_DEFAULT 1 +//Single or Multi Channel, one of 1, 2. type=string default=1 +// For SEISMOMETER INTERFACE seismicPI: +// The gain can be adjusted by sending single characters to the Virtual Com Port: +// ‘1’: ×1 = Single Channel +// ‘2’: ×2 = Multi Channea +EXTERN_TXT int source_select; +#define STA_SOURCE_SELECT_DEFAULT 1 +//--- User internal accelerometer (1) or external source (2) type=int default=1 +// ‘1’: Internal Accelerometer +// ‘2’: External Source + +EXTERN_TXT int do_settings_pihat; +#define DO_SETTINGS_PIHAT_DEFAULT 1 + + +#define PROP_FILE_NAME_DEFAULT "seismicpi_writer.prop" +static Settings *settings = NULL; + +static char* port_path; + +#define SLRECSIZE 512 // Mini-SEED record size +#define SLREC_DATA_SIZE 456 // apparent size of time-seris data in a 512 byte mseed record written by libmseed +//static int num_samples_in_record = SLRECSIZE / sizeof (int); +static int num_samples_in_record = -1; + +int find_device_and_connect(char *port_path_hint); +int collect_and_write(); + +/*************************************************************************** + * usage(): + * Print the usage message and exit. + ***************************************************************************/ +static void usage(void) { + + fprintf(stdout, "%s version: %s (%s)\n", PACKAGE, VERSION, VERSION_DATE); + fprintf(stdout, "Usage: %s [options]\n", PACKAGE); + fprintf(stdout, + " Options:\n" + " -p propfile properties file name (default: config.prop)\n" + " -V Report program version\n" + " -h Show this usage message\n" + " -v Be more verbose, multiple flags can be used\n" + ); + +} /* End of usage() */ + +/*************************************************************************** + * parameter_proc(): + * Process the command line parameters. + * + * Returns 0 on success, and -1 on failure + ***************************************************************************/ +static int parameter_proc(int argcount, char **argvec) { + + int optind = 1; + int error = 0; + + if (argcount <= 1) + error++; + + // Process all command line arguments + for (optind = 1; optind < argcount; optind++) { + + if (strcmp(argvec[optind], "-V") == 0) { + fprintf(stdout, "%s version: %s (%s)\n", PACKAGE, VERSION, VERSION_DATE); + exit(0); + } else if (strcmp(argvec[optind], "-h") == 0) { + (*usage)(); + exit(0); + } else if (strncmp(argvec[optind], "-v", 2) == 0) { + verbose += strspn(&argvec[optind][1], "v"); + } else if (strcmp(argvec[optind], "-p") == 0) { + strcpy(propfile, argvec[++(optind)]); + } else if (strncmp(argvec[optind], "-", 1) == 0) { + fprintf(stdout, "%s: Unknown option: %s\n", PACKAGE, argvec[optind]); + exit(1); + } + } + + // Report the program version + if (verbose) { + logprintf(MSG_FLAG, "%s version: %s (%s)\n", PACKAGE, VERSION, VERSION_DATE); + } + + return 0; + +} /* End of parameter_proc() */ + +/*************************************************************************** + * init_properties(): + * Initialize properties from properties file + ***************************************************************************/ +int init_properties(char *propfile) { + + // read properties file + FILE *fp_prop = fopen(propfile, "r"); + if (fp_prop == NULL) { + logprintf(MSG_FLAG, "Info: Cannot open application properties file: %s\n", propfile); + settings = NULL; + return (0); + } + settings = settings_open(fp_prop); + fclose(fp_prop); + if (settings == NULL) { + logprintf(ERROR_FLAG, "Reading application properties file: %s\n", propfile); + return (-1); + } + + // + if (settings_get_helper(settings, + "Logging", "port_path_hint", port_path_hint, sizeof (port_path_hint), PORT_PATH_HINT_DEFAULT, + verbose) == 0) { + ; // handle error + } + // + if (settings_get_int_helper(settings, + "Logging", "allow_set_interface_attribs", &allow_set_interface_attribs, ALLOW_SET_INTERFACE_ATTRIBS_DEFAULT, + verbose) == 0) { + ; // handle error + } + + // + if (settings_get_double_helper(settings, + "Logging", "mswrite_header_sample_rate", &mswrite_header_sample_rate, MSWRITE_HEADER_SAMPLE_RATE_DEFAULT, + verbose + ) == DBL_INVALID) { + ; // handle error + } + // + if (settings_get_helper(settings, + "Logging", "mswrite_data_encoding_type", mswrite_data_encoding_type, sizeof (mswrite_data_encoding_type), MSWRITE_DATA_ENCODING_TYPE_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + + // + if (settings_get_helper(settings, + "Station", "station_network", station_network, sizeof (station_network), STA_NETWORK_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + // + if (settings_get_helper(settings, + "Station", "station_name", station_name, sizeof (station_name), STA_NAME_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + // + if (settings_get_helper(settings, + "Station", "channel_prefix", channel_prefix, sizeof (channel_prefix), STA_CHANNEL_PREFIX_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + if (settings_get_helper(settings, + "Station", "locationcode_prefix", locationcode_prefix, sizeof (locationcode_prefix), STA_LOCATION_CODE_PREFIX_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + + // + if (settings_get_helper(settings, + "Station", "component", component, sizeof (component), STA_COMPONENT_DEFAULT, + verbose + ) == 0) { + ; // handle error + } + // + if (settings_get_int_helper(settings, + "Station", "nominal_sample_rate", &nominal_sample_rate, STA_NOMINAL_SAMPLE_RATE_DEFAULT, + verbose + ) == DBL_INVALID) { + ; // handle error + } + // + if (settings_get_int_helper(settings, + "Station", "nominal_gain", &nominal_gain, STA_NOMINAL_GAIN_DEFAULT, + verbose + ) == DBL_INVALID) { + ; // handle error + } + // + if (settings_get_int_helper(settings, + "Station", "single_multi", &single_multi, STA_SINGLE_MULTI_DEFAULT, + verbose + ) == DBL_INVALID) { + ; // handle error + } + // + + if (settings_get_int_helper(settings, + "Station", "source_select", &source_select, STA_SOURCE_SELECT_DEFAULT, + verbose + ) == DBL_INVALID) { + ; // handle error + } + // + + if (settings_get_int_helper(settings, + "Station", "do_settings_pihat", &do_settings_pihat, DO_SETTINGS_PIHAT_DEFAULT, + verbose) == 0) { + ; // handle error + } + + return (0); + +} + +#ifndef WIN32 + +/** ************************************************************************* + * term_handler: + * Signal handler routine. + ************************************************************************* **/ +static void term_handler(int sig) { + disconnect(verbose); + exit(0); +} +#endif + +int main(int argc, char **argv) { + +#ifndef WIN32 + // Signal handling, use POSIX calls with standardized semantics + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sa.sa_handler = term_handler; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = SIG_IGN; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); +#endif + + /* + printf("LONG_MIN %ld\n", LONG_MIN); + printf("LONG_MAX %ld\n", LONG_MAX); + printf("INT_MIN %d\n", INT_MIN); + printf("INT_MAX %d\n", INT_MAX); + */ + + // set default error message prefix + ms_loginit(NULL, NULL, NULL, "ERROR: "); + + // defaults + verbose = 0; + strcpy(port_path_hint, "/dev/usbdev1.1"); + strcpy(propfile, PROP_FILE_NAME_DEFAULT); + + // Process input parameters + if (parameter_proc(argc, argv) < 0) + return 1; + init_properties(propfile); + + // set encoding type + // possible: DE_ASCII, DE_INT16, DE_INT32, DE_FLOAT32, DE_FLOAT64, DE_STEIM1, DE_STEIM2 + // supported: DE_INT16, DE_INT32, DE_STEIM1, DE_STEIM2 + if (strcmp(mswrite_data_encoding_type, "DE_INT16") == 0) { + mswrite_data_encoding_type_code = DE_INT16; + num_samples_in_record = (SLREC_DATA_SIZE) / 2; + } else if (strcmp(mswrite_data_encoding_type, "DE_INT32") == 0) { + mswrite_data_encoding_type_code = DE_INT32; + num_samples_in_record = (SLREC_DATA_SIZE) / 4; + /* + } else if (strcmp(mswrite_data_encoding_type, "DE_ASCII") == 0) { + mswrite_data_encoding_type_code = DE_ASCII; + } else if (strcmp(mswrite_data_encoding_type, "DE_FLOAT32") == 0) { + mswrite_data_encoding_type_code = DE_FLOAT32; + } else if (strcmp(mswrite_data_encoding_type, "DE_FLOAT64") == 0) { + mswrite_data_encoding_type_code = DE_FLOAT64; + */ + } else if (strcmp(mswrite_data_encoding_type, "DE_STEIM1") == 0) { + mswrite_data_encoding_type_code = DE_STEIM1; + num_samples_in_record = (SLREC_DATA_SIZE) / 4; // estimate, inefficient, assumes int32 data + } else if (strcmp(mswrite_data_encoding_type, "DE_STEIM2") == 0) { + mswrite_data_encoding_type_code = DE_STEIM2; + num_samples_in_record = (SLREC_DATA_SIZE) / 4; // estimate, inefficient, assumes int32 data + } + + // enter infinite loop, term_handler() performs cleanup + while (1) { + + // find device and connect + find_device_and_connect(port_path_hint); + + // set sample rate and gain for PIHAT + if (do_settings_pihat) { + if (set_pihat_sample_rate_and_gain(nominal_sample_rate, nominal_gain, single_multi, source_select, TIMEOUT_SMALL, verbose)) { + continue; + } + } + + // collect data and write + if (collect_and_write()) { // collect_and_write() returned error + logprintf(ERROR_FLAG, "Reading from %s, will try reconnecting...\n", port_path); + disconnect(verbose); + } else { + break; // collect_and_write() returned normally + } + + } + + return (0); + +} /* End of main() */ + +/*************************************************************************** + * find_device_and_connect: + * + * Attempt to connect to a device, slows down the loop checking + * after 20 attempts with a larger delay to reduce pointless + * work being done. + * + * Returns 0 on success and -1 otherwise. + ***************************************************************************/ +int find_device_and_connect(char *port_path_hint) { + + int counter = 0; + int slow_mode = 0; + if (verbose) + logprintf(MSG_FLAG, "Initializing, Searching for device...\n"); + if (!find_device(port_path_hint, verbose, &port_path, allow_set_interface_attribs,single_multi,source_select)) { + if (verbose) + while (!find_device(port_path_hint, verbose, &port_path, allow_set_interface_attribs,single_multi,source_select)) { + //logprintf(MSG_FLAG, "Still searching for device...\n"); + logprintf(ERROR_FLAG, "Still searching for device. Try reconnecting (unplug and plug in) the USB Seismometer Interface device.\n"); + if (counter < 20) { + counter++; + sleep(5); + } else if (!slow_mode) { + if (verbose) + logprintf(MSG_FLAG, "Entering slow mode after 20 attempts.\n"); + slow_mode = 1; + sleep(30); + } else { + sleep(30); + } + } + } + if (verbose) + logprintf(MSG_FLAG, "Device connected successfully: %s\n", port_path); + + return (0); + +} + +/*************************************************************************** + * hptime2timestr: + * + * Build a time string in filename format from a high precision + * epoch time. + * + * The provided isostimestr must have enough room for the resulting time + * string of 24 characters, i.e. '2001.07.29.12.38.00.000' + NULL. + * + * The 'subseconds' flag controls whether the sub second portion of the + * time is included or not. + * + * Returns a pointer to the resulting string or NULL on error. + ***************************************************************************/ +char * +hptime2timestr(hptime_t hptime, char *timestr, flag subseconds, char* datepath) { + struct tm tms; + time_t isec; + int ifract; + int ret; + + if (timestr == NULL) + return NULL; + + /* Reduce to Unix/POSIX epoch time and fractional seconds */ + isec = MS_HPTIME2EPOCH(hptime); + ifract = (int) (hptime - (isec * HPTMODULUS)); + + /* Adjust for negative epoch times */ + if (hptime < 0 && ifract != 0) { + isec -= 1; + ifract = HPTMODULUS - (-ifract); + } + + if (!(gmtime_r(&isec, &tms))) + return NULL; + + if (subseconds) { + ifract /= (HPTMODULUS / 1000); // tp milliseconds (assumes HPTMODULUS mulitple of 1000) + /* Assuming ifract has millisecond precision */ + ret = snprintf(timestr, 24, "%4d.%02d.%02d.%02d.%02d.%02d.%03d", + tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, + tms.tm_hour, tms.tm_min, tms.tm_sec, ifract); + } else { + ret = snprintf(timestr, 20, "%4d.%02d.%02d.%02d.%02d.%02d", + tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, + tms.tm_hour, tms.tm_min, tms.tm_sec); + } + //printf("DEBUG: timestr= %s\n", timestr); + + if (ret != 23 && ret != 19) + return NULL; + + ret = snprintf(datepath, 17, "%4d/%02d/%02d/%02d/%02d", + tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday, + tms.tm_hour, tms.tm_min); + + if (ret != 16) + return NULL; + + return timestr; + +} /* End of ms_hptime2isotimestr() */ + +static void record_handler (char *record, int reclen, void *handlerdata) { + char sta_id[11]; + snprintf(sta_id, 11, "%s.%s", station_network, station_name); + if (send_mseed(sta_id, record, reclen) < 0) { + logprintf(ERROR_FLAG, "Error sending data to seedlink!\n"); + exit(1); + } +} + +#define DOUBLE long double +//#define DOUBLE double + +/*************************************************************************** + * collect_and_write: + * + * Attempt to connect to a device, slows down the loop checking + * after 20 attempts with a larger delay to reduce pointless + * work being done. + * + * Returns 0 on success and -1 otherwise. + ***************************************************************************/ +int collect_and_write() { + + int32_t idata[2000],idata_X[2000],idata_Y[2000],idata_Z[2000],idata_BH1[2000],idata_BH2[2000],idata_BH3[2000],idata_BH4[2000]; // enough space for data of 2 records + hptime_t hptime; + hptime_t start_hptime_est = 0; + hptime_t last_hptime; + DOUBLE dt, dt_est, sample_rate_est; + DOUBLE start_hptime_current, record_window_current, record_window_est; + DOUBLE prev_start_hptime_est = -1; + int n_start_hptime_est; + + // debug + hptime_t start_hptime_nominal = 0; + hptime_t prev_start_next_hptime_est = 0; + double diff_end, diff_end_cumul = 0.0; + char seedtimestr[64]; + + // decay constant depends on required decay time and sample rate + //double decay_minutes = 60.0; // 1 hour + double decay_minutes = 1.0; + double decay_consant = 1.0 / (decay_minutes * 60.0 * (double) nominal_sample_rate); + + // initialize last_hptime to current time + last_hptime = current_utc_hptime(); + + // initialize dt_est based on nominal sample rate + dt_est = (nominal_sample_rate == 128) ? 1.0 / SAMP_PER_SEC_128 : (nominal_sample_rate == 64) ? 1.0 / SAMP_PER_SEC_64 : 1.0 / SAMP_PER_SEC_32; + // ‘a’: 32 SPS + // ‘b’: 64 SPS + // ‘c’: 128 SPS + // initialize record_window_est based on nominal sample rate and record length + record_window_est = dt_est * num_samples_in_record; + + if (DEBUG) { + logprintf(MSG_FLAG, "Initialize: last_hptime=%lld, dt_est=%lld, dt=%lf, dt_end=%lf, dt_end_cumul=%lf)\n", + last_hptime, dt_est, record_window_est); + } + + int first = 1; + + if (single_multi == 1) { + MSRecord *pmsrecord = msr_init(NULL); + strcpy(pmsrecord->network, station_network); + strcpy(pmsrecord->station, station_name); + strcpy(pmsrecord->location, locationcode_prefix); + sprintf(pmsrecord->channel, "%s%s", channel_prefix, component); + + pmsrecord->samprate = 1.0; + pmsrecord->reclen = SLRECSIZE; + pmsrecord->encoding = mswrite_data_encoding_type_code; + pmsrecord->byteorder = 1; + pmsrecord->datasamples = idata; + pmsrecord->numsamples = 0; + pmsrecord->sampletype = 'i'; + + while (1) { + // load data up to SLRECSIZE + long ivalue; + int nsamp = 0; + start_hptime_current = 0; + n_start_hptime_est = 0; + + while (nsamp < num_samples_in_record) { + long *p; + p = read_next_value(&hptime, single_multi,TIMEOUT_LARGE,source_select); + ivalue = *(p+0); + if (verbose) + logprintf(MSG_FLAG, "Sample:=%1d\n", ivalue); + if (ivalue == READ_ERROR || ivalue < MIN_DATA || ivalue > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue=%ld\n", port_path, nsamp, ivalue); + pmsrecord->datasamples = NULL; + msr_free(&pmsrecord); + return (-1); + } + if (DEBUG && nsamp == 0) { + start_hptime_nominal = hptime; + } + idata[pmsrecord->numsamples + nsamp] = (int32_t) ivalue; + dt = (DOUBLE) (hptime - last_hptime) / (DOUBLE) HPTMODULUS; + last_hptime = hptime; + + if (verbose > 3) { + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + } + // estimate start time and dt + // use only later samples in record since writing previous record may delay reading of first samples of this record + if (nsamp >= num_samples_in_record / 2) { + // 20131107 AJL - use all samples, may give better start time estimate, since buffering should compensate for any delay of first samples + //if (1) { + // start time estimate is timestamp of current data minus dt_est*nsamp + start_hptime_current += (hptime - (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp)); + n_start_hptime_est++; + // accumulate dt_est using low-pass filter + //dt_est = dt_est + (DOUBLE) decay_consant * (dt - dt_est); + } + nsamp++; + } + + start_hptime_current /= n_start_hptime_est; + if (prev_start_hptime_est > 0) { + record_window_current = (DOUBLE) (start_hptime_current - prev_start_hptime_est) / (DOUBLE) HPTMODULUS; + } else { + record_window_current = record_window_est; + } + // accumulate record_window_est using low-pass filter + record_window_est = record_window_est + (DOUBLE) decay_consant * (record_window_current - record_window_est); + if (prev_start_hptime_est > 0) { + start_hptime_est = prev_start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + record_window_est * (DOUBLE) HPTMODULUS); + } else { + start_hptime_est = start_hptime_current; + } + prev_start_hptime_est = start_hptime_est; + // test - truncate dt to 1/10000 s to match precision of miniseed btime + //logprintf(MSG_FLAG, "0 sample_rate_est=%lf (dt=%lfs)\n", (double) ((DOUBLE) 1.0 / dt_est), (double) dt_est); + dt_est = record_window_est / (DOUBLE) num_samples_in_record; + sample_rate_est = (DOUBLE) 1.0 / dt_est; + if (DEBUG) { + diff_end = (double) (start_hptime_est - prev_start_next_hptime_est) / (double) HPTMODULUS; + if (!first) + diff_end_cumul += diff_end; + logprintf(MSG_FLAG, "sample_rate_est=%lf (dt=%lfs)\n", (double) sample_rate_est, (double) dt_est); + logprintf(MSG_FLAG, "start_hptime_est=%lld, start_hptime_nominal=%lld, dt=%lf, dt_end=%lf, dt_end_cumul=%lf)\n", start_hptime_est, start_hptime_nominal, (double) ((DOUBLE) (start_hptime_est - start_hptime_nominal) / (DOUBLE) HPTMODULUS), diff_end, diff_end_cumul); + prev_start_next_hptime_est = start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp); + } + pmsrecord->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord->numsamples / pmsrecord->samprate; + pmsrecord->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord->numsamples += nsamp; + + int64_t npackedsamples = 0; + + if (msr_pack(pmsrecord, record_handler, NULL, &npackedsamples, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error encoding data!\n"); + exit(1); + } + + pmsrecord->numsamples -= npackedsamples; + //logprintf(MSG_FLAG, "nsamp: %i\n",nsamp); + + memmove(&idata[0], &idata[npackedsamples], pmsrecord->numsamples * 4); + } + } + else { + + if (source_select == 1) { + MSRecord *pmsrecord_X = msr_init(NULL); + MSRecord *pmsrecord_Y = msr_init(NULL); + MSRecord *pmsrecord_Z = msr_init(NULL); + strcpy(pmsrecord_X->network, station_network); + strcpy(pmsrecord_X->station, station_name); + strcpy(pmsrecord_X->location, locationcode_prefix); + sprintf(pmsrecord_X->channel, "%s%s", channel_prefix, "N"); + + pmsrecord_X->samprate = 1.0; + pmsrecord_X->reclen = SLRECSIZE; + pmsrecord_X->encoding = mswrite_data_encoding_type_code; + pmsrecord_X->byteorder = 1; + pmsrecord_X->datasamples = idata_X; + pmsrecord_X->numsamples = 0; + pmsrecord_X->sampletype = 'i'; + + strcpy(pmsrecord_Y->network, station_network); + strcpy(pmsrecord_Y->station, station_name); + strcpy(pmsrecord_Y->location, locationcode_prefix); + sprintf(pmsrecord_Y->channel, "%s%s", channel_prefix, "E"); + + pmsrecord_Y->samprate = 1.0; + pmsrecord_Y->reclen = SLRECSIZE; + pmsrecord_Y->encoding = mswrite_data_encoding_type_code; + pmsrecord_Y->byteorder = 1; + pmsrecord_Y->datasamples = idata_Y; + pmsrecord_Y->numsamples = 0; + pmsrecord_Y->sampletype = 'i'; + + strcpy(pmsrecord_Z->network, station_network); + strcpy(pmsrecord_Z->station, station_name); + strcpy(pmsrecord_Z->location, locationcode_prefix); + sprintf(pmsrecord_Z->channel, "%s%s", channel_prefix, "Z"); + + pmsrecord_Z->samprate = 1.0; + pmsrecord_Z->reclen = SLRECSIZE; + pmsrecord_Z->encoding = mswrite_data_encoding_type_code; + pmsrecord_Z->byteorder = 1; + pmsrecord_Z->datasamples = idata_Z; + pmsrecord_Z->numsamples = 0; + pmsrecord_Z->sampletype = 'i'; + + while (1) { + // load data up to SLRECSIZE + long ivalue_X, ivalue_Y, ivalue_Z; + int nsamp = 0; + start_hptime_current = 0; + n_start_hptime_est = 0; + while (nsamp < num_samples_in_record) { + long *p; + p = read_next_value(&hptime, single_multi, TIMEOUT_LARGE,source_select); + ivalue_X = *(p+0); + ivalue_Y = *(p+1); + ivalue_Z = *(p+2); + if (verbose) { + logprintf(MSG_FLAG, "Sample_X:=%1d\n", ivalue_X); + logprintf(MSG_FLAG, "Sample_Y:=%1d\n", ivalue_Y); + logprintf(MSG_FLAG, "Sample_Z:=%1d\n", ivalue_Z); + } + if (ivalue_X == READ_ERROR || ivalue_X < MIN_DATA || ivalue_X > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalua_xe=%ld\n", port_path, nsamp, ivalue_X); + pmsrecord_X->datasamples = NULL; + msr_free(&pmsrecord_X); + return (-1); + } + if (ivalue_Y == READ_ERROR || ivalue_Y < MIN_DATA || ivalue_Y > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue_y=%ld\n", port_path, nsamp, ivalue_Y); + pmsrecord_Y->datasamples = NULL; + msr_free(&pmsrecord_Y); + return (-1); + } + if (ivalue_Z == READ_ERROR || ivalue_Z < MIN_DATA || ivalue_Z > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue_z=%ld\n", port_path, nsamp, ivalue_Z); + pmsrecord_Z->datasamples = NULL; + msr_free(&pmsrecord_Z); + return (-1); + } + + if (DEBUG && nsamp == 0) { + start_hptime_nominal = hptime; + } + + idata_X[pmsrecord_X->numsamples + nsamp] = (int32_t) ivalue_X; + idata_Y[pmsrecord_Y->numsamples + nsamp] = (int32_t) ivalue_Y; + idata_Z[pmsrecord_Z->numsamples + nsamp] = (int32_t) ivalue_Z; + + dt = (DOUBLE) (hptime - last_hptime) / (DOUBLE) HPTMODULUS; + last_hptime = hptime; + if (verbose > 3) { + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_X, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_Y, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_Z, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + } + // estimate start time and dt + // use only later samples in record since writing previous record may delay reading of first samples of this record + if (nsamp >= num_samples_in_record / 2) { + // 20131107 AJL - use all samples, may give better start time estimate, since buffering should compensate for any delay of first samples + //if (1) { + // start time estimate is timestamp of current data minus dt_est*nsamp + start_hptime_current += (hptime - (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp)); + n_start_hptime_est++; + // accumulate dt_est using low-pass filter + //dt_est = dt_est + (DOUBLE) decay_consant * (dt - dt_est); + } + nsamp++; + } + start_hptime_current /= n_start_hptime_est; + if (prev_start_hptime_est > 0) { + record_window_current = (DOUBLE) (start_hptime_current - prev_start_hptime_est) / (DOUBLE) HPTMODULUS; + } else { + record_window_current = record_window_est; + } + // accumulate record_window_est using low-pass filter + record_window_est = record_window_est + (DOUBLE) decay_consant * (record_window_current - record_window_est); + if (prev_start_hptime_est > 0) { + start_hptime_est = prev_start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + record_window_est * (DOUBLE) HPTMODULUS); + } else { + start_hptime_est = start_hptime_current; + } + prev_start_hptime_est = start_hptime_est; + // test - truncate dt to 1/10000 s to match precision of miniseed btime + //logprintf(MSG_FLAG, "0 sample_rate_est=%lf (dt=%lfs)\n", (double) ((DOUBLE) 1.0 / dt_est), (double) dt_est); + dt_est = record_window_est / (DOUBLE) num_samples_in_record; + sample_rate_est = (DOUBLE) 1.0 / dt_est; + if (DEBUG) { + diff_end = (double) (start_hptime_est - prev_start_next_hptime_est) / (double) HPTMODULUS; + if (!first) + diff_end_cumul += diff_end; + logprintf(MSG_FLAG, "sample_rate_est=%lf (dt=%lfs)\n", (double) sample_rate_est, (double) dt_est); + logprintf(MSG_FLAG, "start_hptime_est=%lld, start_hptime_nominal=%lld, dt=%lf, dt_end=%lf, dt_end_cumul=%lf)\n", start_hptime_est, start_hptime_nominal, + (double) ((DOUBLE) (start_hptime_est - start_hptime_nominal) / (DOUBLE) HPTMODULUS), diff_end, diff_end_cumul); + prev_start_next_hptime_est = start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp); + } + pmsrecord_X->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_X->numsamples / pmsrecord_X->samprate; + pmsrecord_X->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_X->numsamples += nsamp; + //logprintf(MSG_FLAG, "nsamp: %i\n",nsamp); + + pmsrecord_Y->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_Y->numsamples / pmsrecord_Y->samprate; + pmsrecord_Y->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_Y->numsamples += nsamp; + + pmsrecord_Z->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_Z->numsamples / pmsrecord_Z->samprate; + pmsrecord_Z->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_Z->numsamples += nsamp; + + int64_t npackedsamples_X = 0; + int64_t npackedsamples_Y = 0; + int64_t npackedsamples_Z = 0; + + if (msr_pack(pmsrecord_X, record_handler, NULL, &npackedsamples_X, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error encoding X data!\n"); + exit(1); + } + if (msr_pack(pmsrecord_Y, record_handler, NULL, &npackedsamples_Y, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error encoding Y data!\n"); + exit(1); + } + if (msr_pack(pmsrecord_Z, record_handler, NULL, &npackedsamples_Z, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error Z encoding data!\n"); + exit(1); + } + pmsrecord_X->numsamples -= npackedsamples_X; + memmove(&idata_X[0], &idata_X[npackedsamples_X], pmsrecord_X->numsamples * 4); + pmsrecord_Y->numsamples -= npackedsamples_Y; + memmove(&idata_Y[0], &idata_Y[npackedsamples_Y], pmsrecord_Y->numsamples * 4); + pmsrecord_Z->numsamples -= npackedsamples_Z; + memmove(&idata_Z[0], &idata_Z[npackedsamples_Z], pmsrecord_Z->numsamples * 4); + } + } + else { + MSRecord *pmsrecord_BH1 = msr_init(NULL); + MSRecord *pmsrecord_BH2 = msr_init(NULL); + MSRecord *pmsrecord_BH3 = msr_init(NULL); + MSRecord *pmsrecord_BH4 = msr_init(NULL); + strcpy(pmsrecord_BH1->network, station_network); + strcpy(pmsrecord_BH1->station, station_name); + strcpy(pmsrecord_BH1->location, locationcode_prefix); + sprintf(pmsrecord_BH1->channel, "%s%s", channel_prefix, "1"); + + pmsrecord_BH1->samprate = 1.0; + pmsrecord_BH1->reclen = SLRECSIZE; + pmsrecord_BH1->encoding = mswrite_data_encoding_type_code; + pmsrecord_BH1->byteorder = 1; + pmsrecord_BH1->datasamples = idata_BH1; + pmsrecord_BH1->numsamples = 0; + pmsrecord_BH1->sampletype = 'i'; + + strcpy(pmsrecord_BH2->network, station_network); + strcpy(pmsrecord_BH2->station, station_name); + strcpy(pmsrecord_BH2->location, locationcode_prefix); + sprintf(pmsrecord_BH2->channel, "%s%s", channel_prefix, "2"); + + pmsrecord_BH2->samprate = 1.0; + pmsrecord_BH2->reclen = SLRECSIZE; + pmsrecord_BH2->encoding = mswrite_data_encoding_type_code; + pmsrecord_BH2->byteorder = 1; + pmsrecord_BH2->datasamples = idata_BH2; + pmsrecord_BH2->numsamples = 0; + pmsrecord_BH2->sampletype = 'i'; + + strcpy(pmsrecord_BH3->network, station_network); + strcpy(pmsrecord_BH3->station, station_name); + strcpy(pmsrecord_BH3->location, locationcode_prefix); + sprintf(pmsrecord_BH3->channel, "%s%s", channel_prefix, "3"); + + pmsrecord_BH3->samprate = 1.0; + pmsrecord_BH3->reclen = SLRECSIZE; + pmsrecord_BH3->encoding = mswrite_data_encoding_type_code; + pmsrecord_BH3->byteorder = 1; + pmsrecord_BH3->datasamples = idata_BH3; + pmsrecord_BH3->numsamples = 0; + pmsrecord_BH3->sampletype = 'i'; + + strcpy(pmsrecord_BH4->network, station_network); + strcpy(pmsrecord_BH4->station, station_name); + strcpy(pmsrecord_BH4->location, locationcode_prefix); + sprintf(pmsrecord_BH4->channel, "%s%s", channel_prefix, "4"); + + pmsrecord_BH4->samprate = 1.0; + pmsrecord_BH4->reclen = SLRECSIZE; + pmsrecord_BH4->encoding = mswrite_data_encoding_type_code; + pmsrecord_BH4->byteorder = 1; + pmsrecord_BH4->datasamples = idata_BH4; + pmsrecord_BH4->numsamples = 0; + pmsrecord_BH4->sampletype = 'i'; + + while (1) { + // load data up to SLRECSIZE + long ivalue_BH1, ivalue_BH2, ivalue_BH3, ivalue_BH4; + int nsamp = 0; + start_hptime_current = 0; + n_start_hptime_est = 0; + while (nsamp < num_samples_in_record) { + long *p; + p = read_next_value(&hptime, single_multi,TIMEOUT_LARGE,source_select); + ivalue_BH1 = *(p+0); + ivalue_BH2 = *(p+1); + ivalue_BH3 = *(p+2); + ivalue_BH4 = *(p+3); + + if (verbose) { + logprintf(MSG_FLAG, "Sample_BH1:=%1d\n", ivalue_BH1); + logprintf(MSG_FLAG, "Sample_BH2:=%1d\n", ivalue_BH2); + logprintf(MSG_FLAG, "Sample_BH3:=%1d\n", ivalue_BH3); + logprintf(MSG_FLAG, "Sample_BH4:=%1d\n", ivalue_BH4); + } + + if (ivalue_BH1 == READ_ERROR || ivalue_BH1 < MIN_DATA || ivalue_BH1 > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalua_xe=%ld\n", port_path, nsamp, ivalue_BH1); + pmsrecord_BH1->datasamples = NULL; + msr_free(&pmsrecord_BH1); + return (-1); + } + + if (ivalue_BH2 == READ_ERROR || ivalue_BH2 < MIN_DATA || ivalue_BH2 > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue_y=%ld\n", port_path, nsamp, ivalue_BH2); + pmsrecord_BH2->datasamples = NULL; + msr_free(&pmsrecord_BH2); + return (-1); + } + if (ivalue_BH3 == READ_ERROR || ivalue_BH3 < MIN_DATA || ivalue_BH3 > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue_z=%ld\n", port_path, nsamp, ivalue_BH3); + pmsrecord_BH3->datasamples = NULL; + msr_free(&pmsrecord_BH3); + return (-1); + } + + if (ivalue_BH4 == READ_ERROR || ivalue_BH4 < MIN_DATA || ivalue_BH4 > MAX_DATA) { + logprintf(MSG_FLAG, "READ_ERROR: port=%s, nsamp=%d, ivalue_z=%ld\n", port_path, nsamp, ivalue_BH4); + pmsrecord_BH4->datasamples = NULL; + msr_free(&pmsrecord_BH4); + return (-1); + } + + if (DEBUG && nsamp == 0) { + start_hptime_nominal = hptime; + } + idata_BH1[pmsrecord_BH1->numsamples + nsamp] = (int32_t) ivalue_BH1; + idata_BH2[pmsrecord_BH2->numsamples + nsamp] = (int32_t) ivalue_BH2; + idata_BH3[pmsrecord_BH3->numsamples + nsamp] = (int32_t) ivalue_BH3; + idata_BH4[pmsrecord_BH4->numsamples + nsamp] = (int32_t) ivalue_BH4; + + dt = (DOUBLE) (hptime - last_hptime) / (DOUBLE) HPTMODULUS; + last_hptime = hptime; + if (verbose > 3) { + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_BH1, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_BH2, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_BH3, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + logprintf(MSG_FLAG, "%d %ld %s (dt=%lf)\n", nsamp, ivalue_BH4, ms_hptime2seedtimestr(hptime, seedtimestr, 1), (double) dt); + } + // estimate start time and dt + // use only later samples in record since writing previous record may delay reading of first samples of this record + if (nsamp >= num_samples_in_record / 2) { + // 20131107 AJL - use all samples, may give better start time estimate, since buffering should compensate for any delay of first samples + //if (1) { + // start time estimate is timestamp of current data minus dt_est*nsamp + start_hptime_current += (hptime - (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp)); + n_start_hptime_est++; + // accumulate dt_est using low-pass filter + //dt_est = dt_est + (DOUBLE) decay_consant * (dt - dt_est); + } + nsamp++; + } + + start_hptime_current /= n_start_hptime_est; + + if (prev_start_hptime_est > 0) { + record_window_current = (DOUBLE) (start_hptime_current - prev_start_hptime_est) / (DOUBLE) HPTMODULUS; + } else { + record_window_current = record_window_est; + } + // accumulate record_window_est using low-pass filter + record_window_est = record_window_est + (DOUBLE) decay_consant * (record_window_current - record_window_est); + if (prev_start_hptime_est > 0) { + start_hptime_est = prev_start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + record_window_est * (DOUBLE) HPTMODULUS); + } else { + start_hptime_est = start_hptime_current; + } + prev_start_hptime_est = start_hptime_est; + // test - truncate dt to 1/10000 s to match precision of miniseed btime + //logprintf(MSG_FLAG, "0 sample_rate_est=%lf (dt=%lfs)\n", (double) ((DOUBLE) 1.0 / dt_est), (double) dt_est); + dt_est = record_window_est / (DOUBLE) num_samples_in_record; + sample_rate_est = (DOUBLE) 1.0 / dt_est; + if (DEBUG) { + diff_end = (double) (start_hptime_est - prev_start_next_hptime_est) / (double) HPTMODULUS; + if (!first) + diff_end_cumul += diff_end; + logprintf(MSG_FLAG, "sample_rate_est=%lf (dt=%lfs)\n", (double) sample_rate_est, (double) dt_est); + logprintf(MSG_FLAG, "start_hptime_est=%lld, start_hptime_nominal=%lld, dt=%lf, dt_end=%lf, dt_end_cumul=%lf)\n", start_hptime_est, start_hptime_nominal, + (double) ((DOUBLE) (start_hptime_est - start_hptime_nominal) / (DOUBLE) HPTMODULUS), diff_end, diff_end_cumul); + prev_start_next_hptime_est = start_hptime_est + (hptime_t) ((DOUBLE) 0.5 + dt_est * (DOUBLE) HPTMODULUS * (DOUBLE) nsamp); + } + + pmsrecord_BH1->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_BH1->numsamples / pmsrecord_BH1->samprate; + pmsrecord_BH1->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_BH1->numsamples += nsamp; + + pmsrecord_BH2->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_BH2->numsamples / pmsrecord_BH2->samprate; + pmsrecord_BH2->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_BH2->numsamples += nsamp; + + pmsrecord_BH3->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_BH3->numsamples / pmsrecord_BH3->samprate; + pmsrecord_BH3->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_BH3->numsamples += nsamp; + + pmsrecord_BH4->starttime = start_hptime_est - (DOUBLE) HPTMODULUS * pmsrecord_BH4->numsamples / pmsrecord_BH4->samprate; + pmsrecord_BH4->samprate = mswrite_header_sample_rate > 0.0 ? mswrite_header_sample_rate : sample_rate_est; + pmsrecord_BH4->numsamples += nsamp; + + int64_t npackedsamples_BH1 = 0; + int64_t npackedsamples_BH2 = 0; + int64_t npackedsamples_BH3 = 0; + int64_t npackedsamples_BH4 = 0; + + if (msr_pack(pmsrecord_BH1, record_handler, NULL, &npackedsamples_BH1, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error encoding BH1 data!\n"); + exit(1); + } + if (msr_pack(pmsrecord_BH2, record_handler, NULL, &npackedsamples_BH2, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error encoding BH2 data!\n"); + exit(1); + } + if (msr_pack(pmsrecord_BH3, record_handler, NULL, &npackedsamples_BH3, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error BH3 encoding data!\n"); + exit(1); + } + if (msr_pack(pmsrecord_BH4, record_handler, NULL, &npackedsamples_BH4, 0, verbose) < 0) { + logprintf(ERROR_FLAG, "Error BH4 encoding data!\n"); + exit(1); + } + pmsrecord_BH1->numsamples -= npackedsamples_BH1; + memmove(&idata_BH1[0], &idata_BH1[npackedsamples_BH1], pmsrecord_BH1->numsamples * 4); + pmsrecord_BH2->numsamples -= npackedsamples_BH2; + memmove(&idata_BH2[0], &idata_BH2[npackedsamples_BH2], pmsrecord_BH2->numsamples * 4); + pmsrecord_BH3->numsamples -= npackedsamples_BH3; + memmove(&idata_BH3[0], &idata_BH3[npackedsamples_BH3], pmsrecord_BH3->numsamples * 4); + pmsrecord_BH4->numsamples -= npackedsamples_BH4; + memmove(&idata_BH4[0], &idata_BH4[npackedsamples_BH4], pmsrecord_BH4->numsamples * 4); + } + } + } + + return (0); +} + diff --git a/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.h b/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.h new file mode 100644 index 0000000000..e35f8397af --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/seismicpi_writer.h @@ -0,0 +1,16 @@ +/*************************************************************************** + * seismicpi_writer.c + * + * Read serial data from a serial port and write 512-byte Mini-SEED data records in mseed files. + * + * Written by Anthony Lomax + * ALomax Scientific www.alomax.net + * + * created: 2013.11.04 + ***************************************************************************/ + + +#define VERSION "0.10" +#define VERSION_DATE "2014.01.28" +#define PACKAGE "seismicpi_writer" + diff --git a/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.c b/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.c new file mode 100644 index 0000000000..0a00d4fd89 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.c @@ -0,0 +1,753 @@ +/** ************************************************************************* + * seismicpidevice.c + * + * Interface for managing SeismicPi HAT over serial connection. + * + * Modified from: + * device/seismicpidevice.py + * Jon Gilbert, 27/01/2011 + * ALomax Scientific www.alomax.net for SEP device (minilogger_plugin), 28/10/2013 + * + * By Kostas Boukouras (kbouk@noa.gr) + * created: 2020.10.15 + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// +#include +#include +//#include +#include +#include +#ifdef __MACH__ +#include +#include +#endif + +#ifdef __linux +#include + +#endif + +#include "libmseed.h" +#include "seismicpi_writer.h" +#include "seismicpidevice.h" + +// Device constants +#define DEVICE_RATE 32 // (Hz) + +// Excluded ports +// If for any reason some port is off limits, place it in this comma separated list. +//#define EXCLUDED_PORTS "/dev/console,/dev/null,/dev/zero,/dev/full,/dev/random,/dev/urandom" +#define EXCLUDED_PORTS "/dev/stdin,/dev/stdout,/dev/stderr,/dev/console,/dev/null,/dev/zero,/dev/full,/dev/random,/dev/urandom" + +#define MAX_NUM_AVAIL_PORTS 4096 +static char avail_ports[MAX_NUM_AVAIL_PORTS][512]; +static int num_avail_ports = 0; +static char seismometer_port[512]; + +static int fd_port = -1; +static struct termios termios_orig; +// Serial configuration +#define SER_BAUD B115200 +#define SER_PARITY 0 +#define SER_TIMEOUT 1 +#define SER_RTS 0 + +int reset_interface_attribs(int fn_io, struct termios *ptermios_orig) { + + if (tcsetattr(fn_io, TCSANOW, ptermios_orig) != 0) { + fprintf(stderr, "error %d from reset_interface_attribs:tcsetattr", errno); + return -1; + } + return 0; +} + +int set_interface_attribs(int fn_io, int speed, int parity, int timeout, int rts, struct termios *ptermios_orig) { + + // save copy of original attributes + //if (tcgetattr(fn_io, ptermios_orig) != 0) { + // fprintf(stderr, "error %d from set_interface_attribs:tcgetattr", errno); + // return -1; + //} + + struct termios tty; + memset(&tty, 0, sizeof tty); + + cfsetospeed(&tty, speed); + cfsetispeed(&tty, speed); + + // Set in/out baud rate to be 9600 + //cfsetispeed(&tty, B9600); + //cfsetospeed(&tty, B9600); + + tty.c_cflag &= ~PARENB; /* no parity */ + tty.c_cflag &= ~CSTOPB; /* 1 stop bit */ + tty.c_cflag &= ~CSIZE; + tty.c_cflag |= CS8; /* 8 bits */ + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) + tty.c_lflag &= ~ICANON; /* non canonical mode */ + tty.c_lflag &= ~ECHO; // Disable echo + tty.c_lflag &= ~ECHOE; // Disable erasure + tty.c_lflag &= ~ECHONL; // Disable new-line echo + tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP + tty.c_lflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl + tty.c_lflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes + tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) + tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed + //tty.c_oflag &= ~OPOST; /* raw output */ + + tcsetattr(fn_io, TCSANOW, &tty); /* apply the settings */ + tcflush(fn_io, TCOFLUSH); + + //if (tcgetattr(fn_io, &tty) != 0) { + // fprintf(stderr, "error_0 %d from set_interface_attribs:tcgetattr", errno); + // return -1; + //} + + + return 0; +} + +/** function to look for available ports and store them in an array of strings. + */ +int scan_for_ports(char* port_path_hint, int verbose) { + + num_avail_ports = 0; + + if (verbose > 2) { + logprintf(MSG_FLAG, "Adding ports to avail_ports:\n"); + } + + // Make sure set port is at front of list + strcpy(avail_ports[num_avail_ports], port_path_hint); + num_avail_ports++; + if (verbose > 2) + logprintf(MSG_FLAG, "%s", port_path_hint); + + char dirpath[] = "/dev"; + char device_path[512]; + DIR* pDir = opendir(dirpath); + struct dirent *pFile = NULL; + while ((pFile = readdir(pDir)) && num_avail_ports < MAX_NUM_AVAIL_PORTS) { + + sprintf(device_path, "%s/%s", dirpath, pFile->d_name); + + if (!strcmp(pFile->d_name, ".") || !strcmp(pFile->d_name, "..")) { + continue; + } + + // Remove any excluded entries. + if (strstr(EXCLUDED_PORTS, device_path) != NULL) { + continue; + } + + // Add port to list + strcpy(avail_ports[num_avail_ports], device_path); + num_avail_ports++; + + if (verbose > 2) + logprintf(MSG_FLAG, "%s\n", device_path); + + } + closedir(pDir); + + if (verbose > 1) { + logprintf(MSG_FLAG, "num_avail_ports=%d\n", num_avail_ports); + } + + return (num_avail_ports); + +} + +int inputAvailable(int fn_io) { + + static struct timeval tv; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(fn_io, &rfds); + //select(fn_io + 1, &rfds, NULL, NULL, &tv); + //return (FD_ISSET(fn_io, &rfds)); + int retval = select(fn_io + 1, &rfds, NULL, NULL, &tv); + if (retval == -1) { + //perror("select()"); + return (0); + } else if (retval) { + return (1); + } else { + return (0); + } + + +} + +/* function to return the value in the next line in the buffer. Timeouts based on settings. + * On a timeout or no connection present, returns READ_ERROR. + */ +long * read_next_value(hptime_t *phptime, int single_multi, long timeout, int source_select) { + +#define MAX_LINE_DATA 32 + + static char line_data[MAX_LINE_DATA + 1]; + static char line_data1[MAX_LINE_DATA + 1]; + static char line_data2[MAX_LINE_DATA + 1]; + static char line_data3[MAX_LINE_DATA + 1]; + static char line_data4[MAX_LINE_DATA + 1]; + static int n_line_data; + char buf[1]; + + n_line_data = 0; + memset(line_data, 0, sizeof line_data1); + long total_sleep = 0; + + // skip any leading \r\n chars + // if (total_sleep >= timeout) { + // return (READ_ERROR); + // } + + // skip any leading \r\n chars + while (total_sleep < timeout && n_line_data < MAX_LINE_DATA) { + //if (timeout == TIMEOUT_LARGE) + // printf("inputAvailable(fd_port):%d\n", inputAvailable(fd_port)); + if (inputAvailable(fd_port) && read(fd_port, buf, sizeof buf) == 1) { // read up to 1 characters if ready to read) + if (buf[0] == '\r' || buf[0] == '\n') { + //if (buf[0] > -32769 && buf[0] < 327678) + // continue; + } + line_data[n_line_data] = buf[0]; + n_line_data++; + break; + } else { + usleep(10000); + total_sleep += 10000; + } + } + + // if (total_sleep >= timeout) { + // return (READ_ERROR); + // } + + // get data timestamp + *phptime = current_utc_hptime(); + + // read value chars + while (total_sleep < timeout && n_line_data < MAX_LINE_DATA) { + if (inputAvailable(fd_port) && read(fd_port, buf, sizeof buf)) { // read up to 1 characters if ready to read) + if (buf[0] == '\r' || buf[0] == '\n') { + break; + } + line_data[n_line_data] = buf[0]; + + n_line_data++; + } else { + usleep(10000); + total_sleep += 10000; + } + } + + if (single_multi != 1) { + memset(line_data1, 0, sizeof line_data1); + memset(line_data2, 0, sizeof line_data2); + memset(line_data3, 0, sizeof line_data3); + memset(line_data4, 0, sizeof line_data4); + int ind0=0; + int ind1=0; + int ind2=0; + int ind3=0; + int ind4=0; + int sel_ch1=1; + int sel_ch2=0; + int sel_ch3=0; + int sel_ch4=0; + /* Write 4-Channel Data*/ + + while (ind0 <= n_line_data) { + + if ((line_data[ind0] != ',')) { + + if ((sel_ch1==1) && (sel_ch2==0) && (sel_ch3==0) && (sel_ch4==0)) { + line_data1[ind1]=line_data[ind0]; + //logprintf(MSG_FLAG, "LINE_DATA1: %s\n", line_data1); + ind1++; + } + else if ((sel_ch1==0) && (sel_ch2==1) && (sel_ch3==0) && (sel_ch4==0)) { + line_data2[ind2]=line_data[ind0]; + //logprintf(MSG_FLAG, "LINE_DATA2: %s\n", line_data2); + ind2++; + } + else if ((sel_ch1==0) && (sel_ch2==0) && (sel_ch3==1) && (sel_ch4==0)) { + line_data3[ind3]=line_data[ind0]; + //logprintf(MSG_FLAG, "LINE_DATA3: %s\n", line_data3); + ind3++; + } + else if ((sel_ch1==0) && (sel_ch2==0) && (sel_ch3==0) && (sel_ch4==1)) { + line_data4[ind4]=line_data[ind0]; + //logprintf(MSG_FLAG, "LINE_DATA4: %s\n", line_data4); + ind4++; + } + } + + if (line_data[ind0]==',') { + if ((sel_ch1==1) && (sel_ch2==0) && (sel_ch3==0) && (sel_ch4==0)) { + sel_ch1=0; + sel_ch2=1; + sel_ch3=0; + sel_ch4=0; + } + else if ((sel_ch1==0) && (sel_ch2==1) && (sel_ch3==0) && (sel_ch4==0)) { + sel_ch1=0; + sel_ch2=0; + sel_ch3=1; + sel_ch4=0; + } + else if ((sel_ch1==0) && (sel_ch2==0) && (sel_ch3==1) && (sel_ch4==0)) { + sel_ch1=0; + sel_ch2=0; + sel_ch3=0; + sel_ch4=1; + } + } + + ind0++; + } + } + + //if (total_sleep >= timeout) { + // return (READ_ERROR); + // } + + if (single_multi != 1) { + static long values[4]; + long value1 = atol(line_data1); + long value2 = atol(line_data2); + long value3 = atol(line_data3); + long value4 = atol(line_data4); + values[0]=value1; + values[1]=value2; + values[2]=value3; + values[3]=value4; + + return values; + } + else { + static long value[1]; + value[0] = atol(line_data); + //logprintf(MSG_FLAG, "N_LINE_DATA: %d\n", n_line_data); + //logprintf(MSG_FLAG, "LINE_DATA: %s\n", line_data); + //logprintf(MSG_FLAG, "VALUE: %d\n", value); + + return value; + } +} + +#define NUM_DATA_TEST 20 +#define MAX_NUM_TEST_READS 1000 +#define SAMP_RATE_TOLERANCE 0.05 // 5% + +/* function to find a serial or serial/usb connection to the seismometer. + */ +int find_device(char* port_path_hint, int verbose, char** pport_path, int allow_set_interface_attribs, int single_multi, int source_select) { + + hptime_t hptime; + hptime_t last_hptime; + double dt, dt_sum; + int n_dt, n_dt_sum; + + *pport_path = NULL; + + //init_usb(); + + scan_for_ports(port_path_hint, verbose); + + // check ports one at a time. Will return when the device is found or when all are checked. + int n; + for (n = 0; n < num_avail_ports; n++) { + //for (n = num_avail_ports - 1; n >= 0; n--) { + + if (verbose > 1) { + logprintf(MSG_FLAG, "Trying port: %s\n", avail_ports[n]); + } + + // 20131219 AJL fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK | O_NOCTTY | O_DSYNC); + // 20140108 AJL fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK | O_NOCTTY | O_SYNC); + fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK); + if (fd_port < 0) { + //fprintf(stderr, "error %d opening %s: %s", errno, portname, strerror(errno)); + if (verbose > 1) + logprintf(MSG_FLAG, " Failed to open port: %s\n", avail_ports[n]); + //continue; + } + /*#ifdef __linux + // + printf("Resetting USB device %s: ", avail_ports[n]); + int rc = ioctl(fd_port, USBDEVFS_RESET, 0); + if (rc < 0) { + printf(" error: %d\n", rc); + } else { + printf(" success\n"); + } + #endif*/ + // close and re-open port as this may help re-set port hardware (???) + close(fd_port); + fd_port = -1; + // 20131219 AJL fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK | O_NOCTTY | O_DSYNC); + // 20140108 AJL fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK | O_NOCTTY | O_SYNC); + fd_port = open(avail_ports[n], O_RDWR | O_NONBLOCK); + if (fd_port < 0) { + //fprintf(stderr, "error %d opening %s: %s", errno, portname, strerror(errno)); + if (verbose > 1) + logprintf(MSG_FLAG, " Failed to re-open port: %s\n", avail_ports[n]); + //continue; + } + // 20140108 AJL - added: + //if (allow_set_interface_attribs) + set_interface_attribs(fd_port, SER_BAUD, SER_PARITY, SER_TIMEOUT, SER_RTS, &termios_orig); + + if (verbose > 1) { + logprintf(MSG_FLAG, " Port successfully opened: %s\n", avail_ports[n]); + } + //sleep(2); + + // Test, take 3 values, if they are all good, return true. + if (verbose > 1) + logprintf(MSG_FLAG, " Testing serial connection...\n"); + dt_sum = 0.0; + n_dt = 0; + n_dt_sum = 0; + last_hptime = -1; + int i = 0; + for (i = 0; i < MAX_NUM_TEST_READS && n_dt < NUM_DATA_TEST; i++) { + long *testd; + testd = read_next_value(&hptime, single_multi, TIMEOUT_SMALL, source_select); + long testdata = *(testd); + // message + if (verbose > 1) { + printf(" i=%d testdata=%ld", i, testdata); + } + // check value + if (testdata == READ_ERROR || testdata < MIN_DATA || testdata > MAX_DATA) { + if (allow_set_interface_attribs) + //reset_interface_attribs(fd_port, &termios_orig); + close(fd_port); + fd_port = -1; + if (verbose > 1) { + printf("\n"); + } + break; + } + // get dt + dt = (double) (hptime - last_hptime) / (double) HPTMODULUS; + if (verbose > 1) { + printf(" dt=%lf", dt); + } + // if dt much less than expected, may be buffered data + if (n_dt < 1 && dt < DT_MIN_EXPECTED / 10.0) { + if (verbose > 1) { + printf("\n"); + } + continue; + } + // accumulate dt + if (n_dt > NUM_DATA_TEST / 2 && last_hptime > 0) { + dt_sum += dt; + n_dt_sum++; + } else { + if (verbose > 1) { + printf("X"); + } + } + n_dt++; + if (verbose > 1) { + printf("\n"); + } + last_hptime = hptime; + } + // Got some data, connection may be good. + if (n_dt >= NUM_DATA_TEST) { + // check sample rate against USB Seismometer Interface rates + double sample_rate_mean = (double) n_dt_sum / dt_sum; + if (verbose > 1) + logprintf(MSG_FLAG, " sample_rate_mean=%lf\n", sample_rate_mean); + + if ( + (fabs(sample_rate_mean - SAMP_PER_SEC_32) / SAMP_PER_SEC_32 < SAMP_RATE_TOLERANCE) || + (fabs(sample_rate_mean - SAMP_PER_SEC_64) / SAMP_PER_SEC_64 < SAMP_RATE_TOLERANCE) || + (fabs(sample_rate_mean - SAMP_PER_SEC_128) / SAMP_PER_SEC_128 < SAMP_RATE_TOLERANCE) + ) { + if (verbose > 1) + logprintf(MSG_FLAG, " Serial connection good.\n"); + strcpy(seismometer_port, avail_ports[n]); + *pport_path = seismometer_port; + return (1); + } + + } + if (verbose > 1) + logprintf(MSG_FLAG, " Serial connection failed.\n"); + + if (allow_set_interface_attribs) + //reset_interface_attribs(fd_port, &termios_orig); + close(fd_port); + fd_port = -1; + + } + + return (0); + +} + +/* Disconnect from any opened ports. + */ +void disconnect(int verbose) { + + if (fd_port >= 0) { + //reset_interface_attribs(fd_port, &termios_orig); + if (close(fd_port)) { + logprintf(ERROR_FLAG, "closing port: %s : %s\n", seismometer_port, strerror(errno)); + } else { + if (verbose) + logprintf(MSG_FLAG, "Port successfully closed: %s\n", seismometer_port); + } + } + +} + +/* function to set device sample rate and gain on device port. + */ +int set_pihat_sample_rate_and_gain(int nominal_sample_rate, int nominal_gain, int single_multi, int source_select, long timeout, int verbose) { + + char buf[1]; + + long total_sleep = 0; + + // nominal_gain + //Nominal gain, one of 1, 2 or 4. type=string default=1 + // The gain can be adjusted by sending single characters to the Virtual Com Port: + // ‘1’: ×1 = 0.64μV/count + // ‘2’: ×2 = 0.32μV/count + // ‘4’: ×2 = 0.16μV/count + // ‘8’: ×2 = 0.08μV/count + + if (nominal_gain == 8) { + buf[0] = '8'; + } else if (nominal_gain == 4) { + buf[0] = '4'; + } else if (nominal_gain == 2) { + buf[0] = '2'; + } else { + buf[0] = '1'; + } + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + if (verbose) { + logprintf(MSG_FLAG, "Set nominal_gain to: %d (\'%c\' on port)\n", nominal_gain, buf[0]); + } + break; + } + usleep(10000); + //tal_sleep += 10000; + } + + buf[0] = '\n'; + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + if (verbose) { + logprintf(MSG_FLAG, "Set nominal_gain to: %d (\'%c\' on port)\n", nominal_gain, buf[0]); + } + break; + } + usleep(10000); + //tal_sleep += 10000; + } + + if (nominal_sample_rate == 32) { + buf[0] = 'a'; + } else if (nominal_sample_rate == 64) { + buf[0] = 'b'; + } else { + buf[0] = 'c'; + } + + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + if (verbose) { + logprintf(MSG_FLAG, "Set nominal_sample_rate to: %d (\'%c\' on port)\n", nominal_sample_rate, buf[0]); + } + break; + } + usleep(10000); + //total_sleep += 10000; + } + + buf[0] = '\n'; + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + break; + } + usleep(10000); + //total_sleep += 10000; + } + + if (source_select == 1) { + buf[0] = 'i'; + } + else { + buf[0] = 'e'; + } + + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + if (verbose) { + logprintf(MSG_FLAG, "Set source_select to: %d (\'%c\' on port)\n", source_select, buf[0]); + } + break; + } + usleep(10000); + //total_sleep += 10000; + } + + buf[0] = '\n'; + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + break; + } + usleep(10000); + //total_sleep += 10000; + } + + if (single_multi == 1) { + buf[0] = 's'; + } + else { + buf[0] = 'm'; + } + + while (total_sleep < timeout) { + if (write(fd_port, buf, sizeof buf) == 1) { + if (verbose) { + logprintf(MSG_FLAG, "Set single_multi to: %d (\'%c\' on port)\n", single_multi, buf[0]); + } + break; + } + usleep(10000); + //total_sleep += 10000; + } + + + //total_sleep = 0; + + return (0); + +} + +void current_utc_time(struct timespec * ts) { + +#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts->tv_sec = mts.tv_sec; + ts->tv_nsec = mts.tv_nsec; +#else + clock_gettime(CLOCK_REALTIME, ts); +#endif + +} + + +#define NANO 1000000000 + +hptime_t timespec2hptime(struct timespec* ts) { + + time_t itime_sec = (time_t) ts->tv_sec; + struct tm* tm_time = gmtime(&itime_sec); + + long hptime_sec_frac = (long) ((double) ts->tv_nsec / ((double) NANO / (double) HPTMODULUS)); + int year = tm_time->tm_year + 1900; + int month = tm_time->tm_mon + 1; + int mday = tm_time->tm_mday; + int jday; + ms_md2doy(year, month, mday, &jday); + + //printf("DEBUG: timespec2hptime(): %d.%d-%d:%d:%d.(%ld) -> %lld\n", + // year, jday, tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec, hptime_sec_frac, + // ms_time2hptime(year, jday, tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec, hptime_sec_frac)); + return (ms_time2hptime(year, jday, tm_time->tm_hour, tm_time->tm_min, tm_time->tm_sec, hptime_sec_frac)); + +} + +hptime_t current_utc_hptime() { + + static struct timespec time_spec; + + // get data timestamp + current_utc_time(&time_spec); + //printf("DEBUG: time_spec.tv_sec %ld, time_spec.tv_nsec %ld, timespec2hptime(&time_spec) %lld\n", time_spec.tv_sec, time_spec.tv_nsec, timespec2hptime(&time_spec)); + return (timespec2hptime(&time_spec)); + +} + +static char NO_PREFIX[] = ""; +static char ERROR_PREFIX[] = "ERROR: "; + +/*************************************************************************** + * logprintf: + * + * A generic log message handler, pre-pends a current date/time string + * to each message. This routine add a newline to the final output + * message if it is not included with the message. + * + * Returns the number of characters in the formatted message. + ***************************************************************************/ +int logprintf(int is_error, char *fmt, ...) { + int rv = 0; + char message[200]; + va_list argptr; + struct tm *tp; + time_t curtime; + + char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec"}; + + /* Build local time string and generate final output */ + curtime = time(NULL); + tp = localtime(&curtime); + + va_start(argptr, fmt); + rv = vsnprintf(message, sizeof (message), fmt, argptr); + va_end(argptr); + + FILE* fpout = stdout; + char* prefix = NO_PREFIX; + if (is_error) { + fpout = stderr; + prefix = ERROR_PREFIX; + } + fprintf(fpout, "%3.3s %3.3s %2.2d %2.2d:%2.2d:%2.2d %4.4d - %s%s", + day[tp->tm_wday], month[tp->tm_mon], tp->tm_mday, + tp->tm_hour, tp->tm_min, tp->tm_sec, tp->tm_year + 1900, + prefix, message); + if (message[strlen(message) - 1] != '\n') + fprintf(fpout, "\n"); + fflush(fpout); + + return rv; +} /* End of lprintf() */ + + diff --git a/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.h b/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.h new file mode 100644 index 0000000000..12c72adaef --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/seismicpidevice.h @@ -0,0 +1,46 @@ +/** ************************************************************************* + * seismicpidevice.h + * + * Interface for managing SEP seismometer over serial connection. + * + * Modified from: + * device/seismicpidevice.py + * Jon Gilbert 27/01/2011 + * + * Written by Anthony Lomax + * ALomax Scientific www.alomax.net + * + * created: 2013.10.28 + * Updated for Mindhat Seismic PI HAT by Kostas Boukouras (kbouk@noa.gr): 2020.11.08 + ***************************************************************************/ + +// device settings for Seicmic PI HAT +// PI HAT 16 bit data +#define MIN_DATA -32768 +#define MAX_DATA 32767 +// a device with <= 32bit data +//#define MIN_DATA -2147483648 +//#define MAX_DATA 2147483647 +// SEP 064 specific nominal sample rates +// TC1 MIN/MAX Settings +//#define MIN_DATA 0 +//#define MAX_DATA 65000 + +#define SAMP_PER_SEC_32 32 +#define SAMP_PER_SEC_64 64 +#define SAMP_PER_SEC_128 128 +#define DT_MIN_EXPECTED (1.0 / SAMP_PER_SEC_32) + +#define TIMEOUT_SMALL 200000 // read timout in microseconds +#define TIMEOUT_LARGE 2000000 +#define READ_ERROR LONG_MIN + +#define MSG_FLAG 2 +#define ERROR_FLAG 2 + +int find_device(char* port_path_hint, int verbose, char** pport_path, int allow_set_interface_attribs, int single_multi, int source_select); +long * read_next_value(hptime_t *phptime, int single_multi, long timeout, int source_select); +hptime_t current_utc_hptime(); +int logprintf(int is_error, char *fmt, ...); +void disconnect(int verbose); +int set_pihat_sample_rate_and_gain(int nominal_sample_rate, int nominal_gain, int single_multi, int source_select, int long timeout, int verbose); diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/Makefile b/src/seedlink/plugins/seismicpi_plugin/settings/Makefile new file mode 100644 index 0000000000..081c102f78 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/Makefile @@ -0,0 +1,52 @@ +# Makefile for picker test +# +# Invocation: +# Solaris: make -f Make_picker_func_test +# Linux: gmake -Rf Make_picker_func_test + +# IMPORTANT: change here to set the directory for binary executable files +ifdef MYBIN +BINDIR=${MYBIN} +else +# with the following, binary executables will be placed in bin subdirectory of your home directory +#BINDIR=~/bin/ +# if in doubt, use the following - binary executables will be placed in the current directory +BINDIR=. +endif + +INCLUDE_DIR=./ + +# Options specific for GCC +ifndef CC +CC = gcc +endif +# +ifndef CCFLAGS +# +CCFLAGS_BASIC = -Wall -fPIC -I.. +# +# optimized +CCFLAGS = -O3 $(CCFLAGS_BASIC) +# +# profile +#CCFLAGS=-O3 -pg $(CCFLAGS_BASIC) +# +# debug - gdb, valgrind, ... +#CCFLAGS = $(CCFLAGS_BASIC) -g +# valgrind --leak-check=yes exe_name +# valgrind --leak-check=full --show-reachable=yes exe_name exe_name +endif + + +OBJS=settings.o strmap.o + +all: $(OBJS) + +%.o : %.c + $(CC) -c $(CCFLAGS) $< -o $@ + + +clean : + rm -f *.o + + diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/settings.c b/src/seedlink/plugins/seismicpi_plugin/settings/settings.c new file mode 100644 index 0000000000..e76628e113 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/settings.c @@ -0,0 +1,980 @@ +/* + * settings version 1.0.1 + * + * ANSI C implementation for managing application settings. + * + * Version History: + * 1.0.0 (2009) - Initial release + * 1.0.1 (2010) - Fixed small memory leak in settings_delete + * (Thanks to Edwin van den Oetelaar) + * 1.0.2 (2011) - Adapted code for new strmap API + * + * settings.c + * + * Copyright (c) 2009-2011 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * settings is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * settings is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with settings. If not, see . + */ +#include "settings.h" + +#define MAX_SECTIONCHARS 256 +#define MAX_KEYCHARS 256 +#define MAX_VALUECHARS 256 +//#define MAX_LINECHARS (MAX_KEYCHARS + MAX_VALUECHARS + 10) +#define MAX_LINECHARS 16384 // handle long comments + +#define COMMENT_CHAR '#' +#define SECTION_START_CHAR '[' +#define SECTION_END_CHAR ']' +#define KEY_VALUE_SEPARATOR_CHAR '=' + +#define DEFAULT_STRMAP_CAPACITY 256 + +typedef struct Section Section; +typedef struct ParseState ParseState; +typedef enum ConvertMode ConvertMode; + +struct Settings { + Section *sections; + unsigned int section_count; +}; + +struct Section { + char *name; + StrMap *map; +}; + +struct ParseState { + char *current_section; + unsigned int current_section_n; + int has_section; +}; + +enum ConvertMode { + CONVERT_MODE_INT, + CONVERT_MODE_LONG, + CONVERT_MODE_DOUBLE, +}; + +static void trim_str(const char *str, char *out_buf); +static int parse_str(Settings *settings, char *str, ParseState *parse_state); +static int is_blank_char(char c); +static int is_blank_str(const char *str); +static int is_comment_str(const char *str); +static int is_section_str(const char *str); +static int is_key_value_str(const char *str); +static int is_key_without_value_str(const char *str); +static const char * get_token(char *str, char delim, char **last); +static int get_section_from_str(const char *str, char *out_buf, unsigned int out_buf_n); +static int get_key_value_from_str(const char *str, char *out_buf1, unsigned int out_buf1_n, char *out_buf2, unsigned int out_buf2_n); +static int get_key_without_value_from_str(const char *str, char *out_buf, unsigned int out_buf_n); +static int get_converted_value(const Settings *settings, const char *section, const char *key, ConvertMode mode, void *out); +static int get_converted_tuple(const Settings *settings, const char *section, const char *key, char delim, ConvertMode mode, void *out, unsigned int n_max_out); +static Section * get_section(Section *sections, unsigned int n, const char *name); +static void enum_map(const char *key, const char *value, const void *obj); + +Settings * settings_new() { + Settings *settings; + + settings = malloc(sizeof (Settings)); + if (settings == NULL) { + return NULL; + } + settings->section_count = 0; + settings->sections = NULL; + return settings; +} + +void settings_delete(Settings *settings) { + unsigned int i, n; + Section *section; + + if (settings == NULL) { + return; + } + section = settings->sections; + n = settings->section_count; + i = 0; + while (i < n) { + sm_delete(section->map); + if (section->name != NULL) { + free(section->name); + } + section++; + i++; + } + free(settings->sections); + free(settings); +} + +Settings * settings_open(FILE *stream) { + Settings *settings; + char buf[MAX_LINECHARS]; + char trimmed_buf[MAX_LINECHARS]; + char section_buf[MAX_LINECHARS]; + ParseState parse_state; + + if (stream == NULL) { + printf("Error: settings: NULL stream pointer.\n"); + return NULL; + } + settings = settings_new(); + if (settings == NULL) { + printf("Error: settings: allocating memory for settings.\n"); + return NULL; + } + parse_state.current_section = section_buf; + parse_state.current_section_n = sizeof (section_buf); + parse_state.has_section = 0; + trim_str("", trimmed_buf); + while (fgets(buf, MAX_LINECHARS, stream) != NULL) { + trim_str(buf, trimmed_buf); + if (!parse_str(settings, trimmed_buf, &parse_state)) { + printf("Error: settings: parsing application properties file at: [%s] %s\n", parse_state.current_section, trimmed_buf); + return NULL; + } + } + return settings; +} + +int settings_save(const Settings *settings, FILE *stream) { + unsigned int i, n; + Section *section; + char buf[MAX_LINECHARS]; + + if (settings == NULL) { + return 0; + } + if (stream == NULL) { + return 0; + } + section = settings->sections; + n = settings->section_count; + i = 0; + while (i < n) { + sprintf(buf, "[%s]\n", section->name); + fputs(buf, stream); + sm_enum(section->map, enum_map, stream); + section++; + i++; + fputs("\n", stream); + } + return 0; +} + +int settings_get_helper(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf, char *default_value, int verbose) { + + if (settings_get(settings, section, key, out_buf, n_out_buf) == 0) { + strcpy(out_buf, default_value); + } + if (verbose) + printf("Info: property set: [%s] %s = %s\n", section, key, out_buf); + + return 1; +} + +int settings_get(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf) { + Section *s; + + if (settings == NULL) { + return 0; + } + s = get_section(settings->sections, settings->section_count, section); + if (s == NULL) { + return 0; + } + return sm_get(s->map, key, out_buf, n_out_buf); +} + +int settings_get_int_helper(const Settings *settings, const char *section, const char *key, int *pvalue, int default_value, int verbose) { + + *pvalue = default_value; + int int_param; + if ((int_param = settings_get_int(settings, section, key)) != INT_INVALID) { + *pvalue = int_param; + } + if (verbose) + printf("Info: property set: [%s] %s = %d\n", section, key, *pvalue); + + return int_param; +} + +int settings_get_int(const Settings *settings, const char *section, const char *key) { + int i; + + if (get_converted_value(settings, section, key, CONVERT_MODE_INT, &i)) { + return i; + } + // 20130221 AJL + //return 0; + return INT_INVALID; +} + +long settings_get_long(const Settings *settings, const char *section, const char *key) { + long l; + + if (get_converted_value(settings, section, key, CONVERT_MODE_LONG, &l)) { + return l; + } + // 20130221 AJL + //return 0; + return LONG_INVALID; +} + +int settings_get_double_helper(const Settings *settings, const char *section, const char *key, double *pvalue, double default_value, int verbose) { + + *pvalue = default_value; + double double_param; + if ((double_param = settings_get_double(settings, section, key)) != DBL_INVALID) { + *pvalue = double_param; + } + if (verbose) + printf("Info: property set: [%s] %s = %lf\n", section, key, *pvalue); + + return double_param; +} + +double settings_get_double(const Settings *settings, const char *section, const char *key) { + double d; + + if (get_converted_value(settings, section, key, CONVERT_MODE_DOUBLE, &d)) { + return d; + } + // 20130221 AJL + //return 0; + return DBL_INVALID; +} + +int settings_get_int_tuple(const Settings *settings, const char *section, const char *key, int *out, unsigned int n_max_out) { + return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_INT, out, n_max_out); +} + +long settings_get_long_tuple(const Settings *settings, const char *section, const char *key, long *out, unsigned int n_max_out) { + return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_LONG, out, n_max_out); +} + +double settings_get_double_tuple(const Settings *settings, const char *section, const char *key, double *out, unsigned int n_max_out) { + return get_converted_tuple(settings, section, key, ',', CONVERT_MODE_DOUBLE, out, n_max_out); +} + +int settings_set(Settings *settings, const char *section, const char *key, const char *value) { + Section *s; + + if (settings == NULL) { + return 0; + } + if (section == NULL || key == NULL || value == NULL) { + return 0; + } + if (strlen(section) == 0) { + return 0; + } + /* Get a pointer to the section */ + s = get_section(settings->sections, settings->section_count, section); + if (s == NULL) { + /* The section is not created---create it */ + s = realloc(settings->sections, (settings->section_count + 1) * sizeof (Section)); + if (s == NULL) { + return 0; + } + settings->sections = s; + settings->section_count++; + s = &(settings->sections[settings->section_count - 1]); + s->map = sm_new(DEFAULT_STRMAP_CAPACITY); + if (s->map == NULL) { + free(s); + return 0; + } + s->name = malloc((strlen(section) + 1) * sizeof (char)); + if (s->name == NULL) { + sm_delete(s->map); + free(s); + return 0; + } + strcpy(s->name, section); + } + return sm_put(s->map, key, value); +} + +int settings_section_get_count(const Settings *settings, const char *section) { + Section *sect; + + if (settings == NULL) { + return 0; + } + sect = get_section(settings->sections, settings->section_count, section); + if (sect == NULL) { + return 0; + } + return sm_get_count(sect->map); +} + +int settings_section_enum(const Settings *settings, const char *section, settings_section_enum_func enum_func, const void *obj) { + Section *sect; + + sect = get_section(settings->sections, settings->section_count, section); + if (sect == NULL) { + return 0; + } + return sm_enum(sect->map, enum_func, obj); +} + +/* Copies a trimmed variant without leading and trailing blank characters + * of the input string into the output buffer. The output buffer is assumed + * to be large enough to contain the entire input string. + */ +static void trim_str(const char *str, char *out_buf) { + unsigned int len; + const char *s0; + + while (*str != '\0' && is_blank_char(*str)) { + str++; + } + s0 = str; + len = 0; + while (*str != '\0') { + len++; + str++; + } + if (len > 0) { + str--; + } + while (is_blank_char(*str)) { + str--; + len--; + } + memcpy(out_buf, s0, len); + out_buf[len] = '\0'; +} + +/* Parses a single input string and updates the provided settings object. + * The given parse state may be updated following a call. It is assumed this + * function is called in repeated succession for each input line read. The + * provided parse state should be initialized to the following before this + * function is called for the first time for an intended parse: + * + * parse_state->current_section: a pre-allocated character buffer this function + * can read and write to + * parse_state->current_section_n: sizeof(parse_state->current_section) + * parse_state->has_section: 0 (false) + */ +static int parse_str(Settings *settings, char *str, ParseState *parse_state) { + char buf[MAX_LINECHARS]; + char buf1[MAX_LINECHARS]; + char buf2[MAX_LINECHARS]; + int result; + + if (*str == '\0') { + return 1; + } else if (is_blank_str(str)) { + return 1; + } else if (is_comment_str(str)) { + return 1; + } else if (is_section_str(str)) { + result = get_section_from_str(str, buf, sizeof (buf)); + if (!result) { + return 0; + } + if (strlen(buf) + 1 > parse_state->current_section_n) { + return 0; + } + strcpy(parse_state->current_section, buf); + parse_state->has_section = 1; + return 1; + } else if (is_key_value_str(str)) { + result = get_key_value_from_str(str, buf1, sizeof (buf1), buf2, sizeof (buf2)); + if (!result) { + return 0; + } + if (!parse_state->has_section) { + return 0; + } + return settings_set(settings, parse_state->current_section, buf1, buf2); + } else if (is_key_without_value_str(str)) { + result = get_key_without_value_from_str(str, buf, sizeof (buf)); + if (!result) { + return 0; + } + if (!parse_state->has_section) { + return 0; + } + return settings_set(settings, parse_state->current_section, buf, ""); + } else { + return 0; + } +} + +/* Returns true if the input character is blank, + * false otherwise. + */ +static int is_blank_char(char c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +/* Returns true if the input string is blank, + * false otherwise. + */ +static int is_blank_str(const char *str) { + while (*str != '\0') { + if (!is_blank_char(*str)) { + return 0; + } + str++; + } + return 1; +} + +/* Returns true if the input string denotes a comment, + * false otherwise. + */ +static int is_comment_str(const char *str) { + if (*str == COMMENT_CHAR) { + /* To be a comment the first character must be the + * comment character. + */ + return 1; + } + return 0; +} + +/* Returns true if the input string denotes a section name, + * false otherwise. + */ +static int is_section_str(const char *str) { + if (*str != SECTION_START_CHAR) { + /* The first character must be the section start character */ + return 0; + } + while (*str != '\0' && *str != SECTION_END_CHAR) { + str++; + } + if (*str != SECTION_END_CHAR) { + /* The section end character must be present somewhere thereafter */ + return 0; + } + return 1; +} + +/* Returns true if the input string denotes a key-value pair, + * false otherwise. + */ +static int is_key_value_str(const char *str) { + if (*str == KEY_VALUE_SEPARATOR_CHAR) { + /* It is illegal to start with the key-value separator */ + return 0; + } + while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { + str++; + } + if (*str != KEY_VALUE_SEPARATOR_CHAR) { + /* The key-value separator must be present after the key part */ + return 0; + } + return 1; +} + +/* Returns true if the input string denotes a key without a value, + * false otherwise. + */ +static int is_key_without_value_str(const char *str) { + if (*str == KEY_VALUE_SEPARATOR_CHAR) { + /* It is illegal to start with the key-value separator */ + return 0; + } + while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { + str++; + } + if (*str == KEY_VALUE_SEPARATOR_CHAR) { + /* The key-value separator must not be present after the key part */ + return 0; + } + return 1; +} + +/* + * Parses a section name from an input string. The input string is assumed to + * already have been identified as a valid input string denoting a section name. + */ +static int get_section_from_str(const char *str, char *out_buf, unsigned int out_buf_n) { + unsigned int count; + + count = 0; + /* Jump past the section begin character */ + str++; + while (*str != '\0' && *str != SECTION_END_CHAR) { + /* Read in the section name into the output buffer */ + if (count == out_buf_n) { + return 0; + } + *out_buf = *str; + out_buf++; + str++; + count++; + } + /* Terminate the output buffer */ + if (count == out_buf_n) { + return 0; + } + *out_buf = '\0'; + return 1; +} + +/* + * Parses a key and value from an input string. The input string is assumed to + * already have been identified as a valid input string denoting a key-value pair. + */ +static int get_key_value_from_str(const char *str, char *out_buf1, unsigned int out_buf1_n, char *out_buf2, unsigned int out_buf2_n) { + unsigned int count1; + unsigned int count2; + + count1 = 0; + count2 = 0; + /* Read the key value from the input string and write it sequentially + * to the first output buffer by walking the input string until we either hit + * the null-terminator or the key-value separator. + */ + while (*str != '\0' && *str != KEY_VALUE_SEPARATOR_CHAR) { + /* Ensure the first output buffer is large enough. */ + if (count1 == out_buf1_n) { + return 0; + } + /* Copy the character to the first output buffer */ + *out_buf1 = *str; + out_buf1++; + str++; + count1++; + } + /* Terminate the first output buffer */ + if (count1 == out_buf1_n) { + return 0; + } + *out_buf1 = '\0'; + /* Now trace the first input buffer backwards until we hit a non-blank character */ + while (is_blank_char(*(out_buf1 - 1))) { + out_buf1--; + } + *out_buf1 = '\0'; + /* Try to proceed one more character, past the last read key-value + * delimiter, in the input string. + */ + if (*str != '\0') { + str++; + } + /* Now find start of the value in the input string by walking the input + * string until we either hit the null-terminator or a blank character. + */ + while (*str != '\0' && is_blank_char(*str)) { + str++; + } + while (*str != '\0') { + /* Fail if there is a possibility that we are overwriting the second + * input buffer. + */ + if (count2 == out_buf2_n) { + return 0; + } + /* Copy the character to the second output buffer */ + *out_buf2 = *str; + out_buf2++; + str++; + count2++; + } + /* Terminate the second output buffer */ + if (count2 == out_buf2_n) { + return 0; + } + *out_buf2 = '\0'; + return 1; +} + +/* + * Parses a key from an input string. The input string is assumed to already + * have been identified as a valid input string denoting a key without a value. + */ +static int get_key_without_value_from_str(const char *str, char *out_buf, unsigned int out_buf_n) { + unsigned int count; + + count = 0; + /* Now read the key value from the input string and write it sequentially + * to the output buffer by walking the input string until we either hit + * the null-terminator or the key-value separator. + */ + while (*str != '\0') { + /* Ensure the output buffer is large enough. */ + if (count == out_buf_n) { + return 0; + } + /* Copy the character to the input buffer */ + *out_buf = *str; + out_buf++; + str++; + count++; + } + /* Terminate the output buffer */ + if (count == out_buf_n) { + return 0; + } + *out_buf = '\0'; + return 1; +} + +/* Returns a pointer to the next token in the input string delimited + * by the specified delimiter or null if no such token exist. The provided + * last pointer will be changed to point one position after the pointed + * token. The currently ouputted token will be null-terminated. + * + * An idiom for tokenizing a (in this case, comma-separated) string is: + * + * char test_string[] = "Token1,Token2,Token3"; + * char token[255]; + * char *str; + * + * str = test_string; + * while ((token = get_token(str, ',', &str) != NULL) { + * printf("token: %s", token); + * } + */ +static const char * get_token(char *str, char delim, char **last) { + + char *s0; + + s0 = str; + /* If we hit the null-terminator the string + * is exhausted and another token does not + * exist. + */ + if (*str == '\0') { + return NULL; + } + /* Walk the string until we encounter a + * null-terminator or the delimiter. + */ + while (*str != '\0' && *str != delim) { + str++; + } + /* Terminate the return token, if necessary */ + if (*str != '\0') { + *str = '\0'; + str++; + } + *last = str; + return s0; +} + +/* Returns a converted value pointed to by the provided key in the given section. + * The mode specifies which conversion takes place and dictates what value out + * is pointing to. The value out is pointing to will be replaced by the converted + * value assuming conversion is succesful. The function returns 1 if conversion + * is succsessful and 0 if the convertion could not be carried out. + */ +static int get_converted_value(const Settings *settings, const char *section, const char *key, ConvertMode mode, void *out) { + char value[MAX_VALUECHARS]; + + if (!settings_get(settings, section, key, value, MAX_VALUECHARS)) { + return 0; + } + // check is number + // 20130221 AJL - added + int c, i = 0; + while ((c = value[i++])) { + if (!isdigit(c) && c != '-' && !((mode == CONVERT_MODE_DOUBLE) && c == '.')) + return (0); + } + switch (mode) { + case CONVERT_MODE_INT: + *((int *) out) = atoi(value); + return 1; + case CONVERT_MODE_LONG: + *((long *) out) = atol(value); + return 1; + case CONVERT_MODE_DOUBLE: + *((double *) out) = atof(value); + return 1; + } + return 0; +} + +/* Returns a converted tuple pointed to by the provided key in the given section. + * The tuple is created by splitting the value by the supplied delimiter and then + * converting each token after the split according to the specified mode. + * The array out is pointing to will be replaced by the converted tuple + * assuming conversion is successful. The function returns 1 if conversion + * is successful and 0 if the conversion could not be carried out. + */ +static int get_converted_tuple(const Settings *settings, const char *section, const char *key, char delim, ConvertMode mode, void *out, unsigned int n_max_out) { + unsigned int count; + const char *token; + static char value[MAX_VALUECHARS]; + char *v; + + if (out == NULL) { + return 0; + } + if (n_max_out == 0) { + return 0; + } + if (!settings_get(settings, section, key, value, MAX_VALUECHARS)) { + return 0; + } + v = value; + count = 0; + /* Walk over all tokens in the value, and convert them and assign them + * to the output array as specified by the mode. + */ + while ((token = get_token(v, delim, &v)) != NULL && count < n_max_out) { + // check is number + // 20130221 AJL - added + int c, i = 0; + while ((c = token[i++])) { + if (!isdigit(c) && !((mode == CONVERT_MODE_DOUBLE) && c == '.')) + return (0); + } + switch (mode) { + case CONVERT_MODE_INT: + ((int *) out)[count] = atoi(token); + break; + case CONVERT_MODE_LONG: + ((long *) out)[count] = atol(token); + break; + case CONVERT_MODE_DOUBLE: + ((double *) out)[count] = atof(token); + break; + default: + return 0; + } + count++; + } + return count; +} + +/* Returns a pointer to the section or null if the named section does not + * exist. + */ +static Section * get_section(Section *sections, unsigned int n, const char *name) { + unsigned int i; + Section *section; + + if (name == NULL) { + return NULL; + } + section = sections; + i = 0; + while (i < n) { + if (strcmp(section->name, name) == 0) { + return section; + } + section++; + i++; + } + return NULL; +} + +/* Callback function that is passed into the enumeration function in the + * string map. It casts the passed into object into a FILE pointer and + * writes out the key and value to the file. + */ +static void enum_map(const char *key, const char *value, const void *obj) { + FILE *stream; + char buf[MAX_LINECHARS]; + + if (key == NULL || value == NULL) { + return; + } + if (obj == NULL) { + return; + } + stream = (FILE *) obj; + if (strlen(key) < MAX_KEYCHARS && strlen(value) < MAX_VALUECHARS) { + sprintf(buf, "%s%c%s\n", key, KEY_VALUE_SEPARATOR_CHAR, value); + fputs(buf, stream); + } +} + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + + */ diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/settings.h b/src/seedlink/plugins/seismicpi_plugin/settings/settings.h new file mode 100644 index 0000000000..e5d6f4790a --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/settings.h @@ -0,0 +1,522 @@ +/* + * settings version 1.0.0 + * + * ANSI C implementation for managing application settings. + * + * settings.h + * + * Copyright (c) 2009 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * settings is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * settings is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with settings. If not, see . + */ +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include +#include +#include +#include +#include +#include + +#include "strmap.h" + +// 20130624 AJL +#define SETTINGS_MAX_STR_LEN 4096 + +// 20130221 AJL - added so that numeric error return value is not 0 +#define DBL_INVALID (-FLT_MAX) +#define INT_INVALID (INT_MIN) +#define LONG_INVALID INT_INVALID + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct Settings Settings; + +/* + * This callback function is called once per key-value when enumerating + * all keys inside a section. + * + * Parameters: + * + * key: A pointer to a null-terminated C string. The string must not + * be modified by the client. + * + * value: A pointer to a null-terminated C string. The string must + * not be modified by the client. + * + * obj: A pointer to a client-specific object. This parameter may be + * null. + * + * Return value: None. + */ +typedef void(*settings_section_enum_func)(const char *key, const char *value, const void *obj); + +/* + * Creates a settings object. + * + * Return value: A pointer to a settings object, + * or null if a new settings object could not be allocated. + */ +Settings * settings_new(); + +/* + * Releases all memory held by a settings object. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * If the supplied settings object has been previously released, the + * behaviour of this function is undefined. + * + * Return value: None. + */ +void settings_delete(Settings *settings); + +/* + * Constructs a settings object by loading settings in textual form + * from the given stream. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * stream: A pointer to a stream. This parameter cannot be null. + * + * Return value: A pointer to a settings object, + * or null if an error occurred. + */ +Settings * settings_open(FILE *stream); + +/* + * Saves the current settings object in textual form to the given stream. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * stream: A pointer to a stream. This parameter cannot be null. + * + * Return value: 1 if the operation succeeded, 0 otherwise. + */ +int settings_save(const Settings *settings, FILE *stream); + + + /* + * Returns the value associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out_buf: A pointer to an output buffer which will contain the value, + * if it exists and fits into the buffer. + * + * n_out_buf: The size of the output buffer in bytes. + * + * Return value: If out_buf is set to null and n_out_buf is set to 0 the return + * value will be the number of bytes required to store the value (if it exists) + * and its null-terminator. For all other parameter configurations the return value + * is 1 if an associated value was found and completely copied into the output buffer, + * 0 otherwise. + */ +int settings_get(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf); +int settings_get_helper(const Settings *settings, const char *section, const char *key, char *out_buf, unsigned int n_out_buf, char *default_value, int verbose); + + + /* + * Returns the integer value associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: The integer value associated to the provided section and + * key, or 0 if no such value exists. + */ +int settings_get_int(const Settings *settings, const char *section, const char *key); +int settings_get_int_helper(const Settings *settings, const char *section, const char *key, int *pvalue, int default_value, int verbose); + +/* + * Returns the long integer value associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: The long integer value associated to the provided section and + * key, or 0 if no such value exists. + */ +long settings_get_long(const Settings *settings, const char *section, const char *key); + +/* + * Returns the double value associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: The double value associated to the provided section and + * key, or 0 if no such value exists. + */ +double settings_get_double(const Settings *settings, const char *section, const char *key); +int settings_get_double_helper(const Settings *settings, const char *section, const char *key, double *pvalue, double default_value, int verbose); + +/* + * Returns the integer tuple associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out: A pointer to an output buffer. + * + * n_out: The maximum number of elements the output buffer can hold. + * + * Return value: 1 if the entire tuple was copied into the output buffer, + * 0 otherwise. + */ +int settings_get_int_tuple(const Settings *settings, const char *section, const char *key, int *out, unsigned int n_out); + +/* + * Returns the long tuple associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out: A pointer to an output buffer. + * + * n_out: The maximum number of elements the output buffer can hold. + * + * Return value: 1 if the entire tuple was copied into the output buffer, + * 0 otherwise. + */ +long settings_get_long_tuple(const Settings *settings, const char *section, const char *key, long *out, unsigned int n_out); + +/* + * Returns the double tuple associated with the supplied key in the + * provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out: A pointer to an output buffer. + * + * n_out: The maximum number of elements the output buffer can hold. + * + * Return value: 1 if the entire tuple was copied into the output buffer, + * 0 otherwise. + */ +double settings_get_double_tuple(const Settings *settings, const char *section, const char *key, double *out, unsigned int n_out); + +/* + * Associates a value with the supplied key in the provided section. + * If the key is already associated with a value, the previous value + * is replaced. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. The string must have a string length > 0. The string will + * be copied. + * + * key: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * value: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * Return value: 1 if the association succeeded, 0 otherwise. + */ +int settings_set(Settings *setting, const char *section, const char *key, const char *value); + +/* + * Returns the number of associations between keys and values that exist + * in the provided section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: The number of associations between keys and values in + * the provided section. + */ +int settings_section_get_count(const Settings *settings, const char *section); + +/* + * Enumerates all associations between keys and values in the provided + * section. + * + * Parameters: + * + * settings: A pointer to a settings object. This parameter cannot be null. + * + * section: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * enum_func: A pointer to a callback function that will be + * called by this procedure once for every key associated + * with a value. This parameter cannot be null. + * + * obj: A pointer to a client-specific object. This parameter will be + * passed back to the client's callback function. This parameter can + * be null. + * + * Return value: 1 if enumeration completed, 0 otherwise. + */ +int settings_section_enum(const Settings *settings, const char *section, settings_section_enum_func enum_func, const void *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +*/ diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/settings.html b/src/seedlink/plugins/seismicpi_plugin/settings/settings.html new file mode 100644 index 0000000000..158614c375 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/settings.html @@ -0,0 +1,245 @@ + + + + An ANSI C implementation for managing application settings + + +
+
+

Per Ola Kristensson | ANSI C application settings management

+
+
+

+ + Blog
+
+ + Publications
+
+ + Software
+
+ + Other Stuff
+
+

+
+
+

Description

+

+ This is a module for managing application settings. It has the following features: +

+
    +
  1. + Loading and saving application settings to/from a textual format that is easy to read and edit by humans +
  2. +
  3. + Relatively robust to corruptions in the textual format +
  4. +
  5. + Simple structure that is sufficiently expressive for most actual usage scenarios: a particular setting is a triple (section, key, value) +
  6. +
  7. + Convenient interface for accessing string, integer, long and double values +
  8. +
  9. + Convenient interface for accessing string, integer, long and double tuples of arbitrary lengths +
  10. +
  11. + Supports iteration over all key-value pairs for a particular section +
  12. +
+

Source code

+

+ The API is documented in the header file (settings.h). +

+

+ settings.h +

+

+ settings.c +

+

+ You will also need strmap (an ANSI C hash table implementation for strings). +

+

+ strmap.h +

+

+ strmap.c +

+

License

+

+ The code ("settings" and "strmap") is licensed under the GNU Lesser General Public License. +

+

Syntax

+

+ The structure of a settings entity is quite simple. A settings entity is a tree of depth 2. At the top level are the section nodes that represent isolated sections of application settings (e.g. general application properties, font attributes, color attributes, and so on). At the bottom level are the key-value pairs that represent set values for keys inside a particular section. +

+

+ A settings entity is read from and written to disk using a textual format. The textual format is parsed by sequentially scanning each line. Blank lines are ignored. For all other lines, leading and trailing blanks are ignored. Sections are enclosed by square brackets ("[" and "]") on separate lines. Key-value pairs are written as the key followed by a single equal sign ("="), followed by the value, all of which must be together on one separate line. A key-value pair is associated to the last seen section. A key-value pair is not valid until at least one section has been seen. All lines that start with a hash ("#") are considered comments and are ignored. Empty sections, that is, sections that do not contain any key-value pairs, are not written to disk. Empty sections can be present in the textual format. However, they are ignored when creating a settings entity in memory. +

+

+ A value can be a single or multi-valued. A multi-valued value is a tuple. Tuples are separated by comma and are singly-typed. This means that a tuple can only contain objects of one kind (e.g. integer tuples, double tuples, etc.). +

+

+ The fragment below may serve as a reference: +

+
+ # This comment will be ignored
+ [Section Title 1]
+ # Below are two examples of key-value pairs
+ Key1 = Value1
+ Key2 = Value2
+ # Below is an example of an integer tuple
+ Key3 = 0,1,2,3
+
+ [Section Title 2]
+ # The key-value pair below is distinct from the one above
+ Key1 = Value1
+
+ [Empty Section]
+ # This section is legal but empty and will be ignored
+
+

+ The parser is relatively robust to leading and trailing blank characters around section names, keys, values, tuple elements, etc. +

+

Notes

+

+ First, the code is not thread-safe. Use external synchronization. +

+

+ Second, the implementation copies string values upon insertion and retrieval. This guarantees that no internal references are exposed to the client after the settings entity has been deleted from memory. There is one exception, however. When iterating over all key-value associations in a section, the internal strings are temporarily exposed as constant pointers to the internal string buffers. This is because it is very slow and cumbersome to copy all key-value associations to a buffer via a callback function interface. It is recommended not to keep the pointers to the internal string buffers around out of scope of the callback function. Otherwise, you risk having wild pointers in your code after you have deleted the settings entity. +

+

Example usage

+

+ The settings file below will be used in the examples: +

+
+ [Application]
+ Title = Test Title
+ Version = 1.0.5
+
+ [Background]
+ Color = 255, 127, 127, 127
+
+ [Foreground]
+ Color = 255, 255, 255, 255
+ Text Color = 255, 0, 0, 0
+
+ [Edit Menu Options]
+ C&ut  Ctrl+X = 1
+ &Copy  Ctrl+C = 2
+ P&aste  Ctrl+V = 3
+
+

+ The code fragments below create a Settings object, insert and retrieve a couple of string associations, write out the Settings object to disk, and finally destroy the Settings object. +

+
+ #include "settings.h"
+
+ ...
+
+ FILE *f;
+ Settings *settings;
+ char buf[255];
+ int result;
+
+ f = fopen("settings.txt", "r");
+ if (f == NULL) {
+     /* Handle error... */
+ }
+ settings = settings_open(f);
+ fclose(f);
+ if (settings == NULL) {
+     /* Handle read error... */
+ }
+ /* Insert a new key-value pair */
+ settings_set(settings, "Application", "Has Started", "True");
+ /* Retrieve a value */
+ result = settings_get(settings, "Application", "Version", buf, sizeof(buf));
+ if (result == 0) {
+     /* Handle value not found... */
+ }
+ printf("version of this application: %s\n", buf)
+ /* Insert a key-value pair that will create a new section */
+ settings_set(settings, "Window Properties", "X", "0");
+
+ ...
+
+ /* Save the settings object to disk in textual form */
+ f = fopen("settings.txt", "w");
+ if (f == NULL) {
+     /* Handle error... */
+ }
+ result = settings_save(settings, f);
+ fclose(f);
+ if (result == 0) {
+     /* Handle write error... */
+ }
+ /* When done, destroy the settings object */
+ settings_delete(settings);
+
+ ...
+
+
+

+ The code fragments below demonstrate how to read a tuple: +

+
+ #include "settings.h"
+
+ ...
+
+ Settings *settings;
+
+ ...
+
+ int argb[4];
+ int result;
+
+ result = settings_get_int_tuple(settings, "Foreground",
+     "Text Color", argb, sizeof(argb));
+ if (result == 0) {
+     /* Handle value not found... */
+ }
+ printf("alpha: %d red: %d green: %d blue: %d\n",
+     argb[0], argb[1], argb[2], argb[3]);
+
+ ...
+
+
+

+ The code fragments below demonstrate how to iterate over all key-value associations in a section: +

+
+ #include "settings.h"
+
+ ...
+
+ Settings *settings;
+
+ ...
+
+ static void iter(const char *key, const char *value, const void *obj)
+ {
+     printf("menu item: %s menu position: %s\n", key, value);
+ }
+
+ ...
+
+ settings_enum(settings, "Edit Menu Options", iter, NULL);
+
+ ...
+
+
+
+ +
+ + diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/strmap.c b/src/seedlink/plugins/seismicpi_plugin/settings/strmap.c new file mode 100644 index 0000000000..e18d829f70 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/strmap.c @@ -0,0 +1,515 @@ +/* + * strmap version 2.0.1 + * + * ANSI C hash table for strings. + * + * Version history: + * 1.0.0 - initial release + * 2.0.0 - changed function prefix from strmap to sm to ensure + * ANSI C compatibility + * 2.0.1 - improved documentation + * + * strmap.c + * + * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * strmap is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * strmap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with strmap. If not, see . + */ +#include "strmap.h" + +typedef struct Pair Pair; + +typedef struct Bucket Bucket; + +struct Pair { + char *key; + char *value; +}; + +struct Bucket { + unsigned int count; + Pair *pairs; +}; + +struct StrMap { + unsigned int count; + Bucket *buckets; +}; + +static Pair * get_pair(Bucket *bucket, const char *key); +static unsigned long hash(const char *str); + +StrMap * sm_new(unsigned int capacity) +{ + StrMap *map; + + map = malloc(sizeof(StrMap)); + if (map == NULL) { + return NULL; + } + map->count = capacity; + map->buckets = malloc(map->count * sizeof(Bucket)); + if (map->buckets == NULL) { + free(map); + return NULL; + } + memset(map->buckets, 0, map->count * sizeof(Bucket)); + return map; +} + +void sm_delete(StrMap *map) +{ + unsigned int i, j, n, m; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return; + } + n = map->count; + bucket = map->buckets; + i = 0; + while (i < n) { + m = bucket->count; + pair = bucket->pairs; + j = 0; + while(j < m) { + free(pair->key); + free(pair->value); + pair++; + j++; + } + free(bucket->pairs); + bucket++; + i++; + } + free(map->buckets); + free(map); +} + +int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf) +{ + unsigned int index; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (key == NULL) { + return 0; + } + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + pair = get_pair(bucket, key); + if (pair == NULL) { + return 0; + } + if (out_buf == NULL && n_out_buf == 0) { + return strlen(pair->value) + 1; + } + if (out_buf == NULL) { + return 0; + } + if (strlen(pair->value) >= n_out_buf) { + return 0; + } + strcpy(out_buf, pair->value); + return 1; +} + +int sm_exists(const StrMap *map, const char *key) +{ + unsigned int index; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (key == NULL) { + return 0; + } + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + pair = get_pair(bucket, key); + if (pair == NULL) { + return 0; + } + return 1; +} + +int sm_put(StrMap *map, const char *key, const char *value) +{ + unsigned int key_len, value_len, index; + Bucket *bucket; + Pair *tmp_pairs, *pair; + char *tmp_value; + char *new_key, *new_value; + + if (map == NULL) { + return 0; + } + if (key == NULL || value == NULL) { + return 0; + } + key_len = strlen(key); + value_len = strlen(value); + /* Get a pointer to the bucket the key string hashes to */ + index = hash(key) % map->count; + bucket = &(map->buckets[index]); + /* Check if we can handle insertion by simply replacing + * an existing value in a key-value pair in the bucket. + */ + if ((pair = get_pair(bucket, key)) != NULL) { + /* The bucket contains a pair that matches the provided key, + * change the value for that pair to the new value. + */ + if (strlen(pair->value) < value_len) { + /* If the new value is larger than the old value, re-allocate + * space for the new larger value. + */ + tmp_value = realloc(pair->value, (value_len + 1) * sizeof(char)); + if (tmp_value == NULL) { + return 0; + } + pair->value = tmp_value; + } + /* Copy the new value into the pair that matches the key */ + strcpy(pair->value, value); + return 1; + } + /* Allocate space for a new key and value */ + new_key = malloc((key_len + 1) * sizeof(char)); + if (new_key == NULL) { + return 0; + } + new_value = malloc((value_len + 1) * sizeof(char)); + if (new_value == NULL) { + free(new_key); + return 0; + } + /* Create a key-value pair */ + if (bucket->count == 0) { + /* The bucket is empty, lazily allocate space for a single + * key-value pair. + */ + bucket->pairs = malloc(sizeof(Pair)); + if (bucket->pairs == NULL) { + free(new_key); + free(new_value); + return 0; + } + bucket->count = 1; + } + else { + /* The bucket wasn't empty but no pair existed that matches the provided + * key, so create a new key-value pair. + */ + tmp_pairs = realloc(bucket->pairs, (bucket->count + 1) * sizeof(Pair)); + if (tmp_pairs == NULL) { + free(new_key); + free(new_value); + return 0; + } + bucket->pairs = tmp_pairs; + bucket->count++; + } + /* Get the last pair in the chain for the bucket */ + pair = &(bucket->pairs[bucket->count - 1]); + pair->key = new_key; + pair->value = new_value; + /* Copy the key and its value into the key-value pair */ + strcpy(pair->key, key); + strcpy(pair->value, value); + return 1; +} + +int sm_get_count(const StrMap *map) +{ + unsigned int i, j, n, m; + unsigned int count; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + bucket = map->buckets; + n = map->count; + i = 0; + count = 0; + while (i < n) { + pair = bucket->pairs; + m = bucket->count; + j = 0; + while (j < m) { + count++; + pair++; + j++; + } + bucket++; + i++; + } + return count; +} + +int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj) +{ + unsigned int i, j, n, m; + Bucket *bucket; + Pair *pair; + + if (map == NULL) { + return 0; + } + if (enum_func == NULL) { + return 0; + } + bucket = map->buckets; + n = map->count; + i = 0; + while (i < n) { + pair = bucket->pairs; + m = bucket->count; + j = 0; + while (j < m) { + enum_func(pair->key, pair->value, obj); + pair++; + j++; + } + bucket++; + i++; + } + return 1; +} + +/* + * Returns a pair from the bucket that matches the provided key, + * or null if no such pair exist. + */ +static Pair * get_pair(Bucket *bucket, const char *key) +{ + unsigned int i, n; + Pair *pair; + + n = bucket->count; + if (n == 0) { + return NULL; + } + pair = bucket->pairs; + i = 0; + while (i < n) { + if (pair->key != NULL && pair->value != NULL) { + if (strcmp(pair->key, key) == 0) { + return pair; + } + } + pair++; + i++; + } + return NULL; +} + +/* + * Returns a hash code for the provided string. + */ +static unsigned long hash(const char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } + return hash; +} + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +*/ diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/strmap.h b/src/seedlink/plugins/seismicpi_plugin/settings/strmap.h new file mode 100644 index 0000000000..5dc37286b9 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/strmap.h @@ -0,0 +1,356 @@ +/* + * strmap version 2.0.1 + * + * ANSI C hash table for strings. + * + * Version history: + * 1.0.0 - initial release + * 2.0.0 - changed function prefix from strmap to sm to ensure + * ANSI C compatibility + * 2.0.1 - improved documentation + * + * strmap.h + * + * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson. + * + * Per Ola Kristensson + * Inference Group, Department of Physics + * University of Cambridge + * Cavendish Laboratory + * JJ Thomson Avenue + * CB3 0HE Cambridge + * United Kingdom + * + * strmap is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * strmap is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with strmap. If not, see . + */ +#ifndef _STRMAP_H_ +#define _STRMAP_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +typedef struct StrMap StrMap; + +/* + * This callback function is called once per key-value when iterating over + * all keys associated to values. + * + * Parameters: + * + * key: A pointer to a null-terminated C string. The string must not + * be modified by the client. + * + * value: A pointer to a null-terminated C string. The string must + * not be modified by the client. + * + * obj: A pointer to a client-specific object. This parameter may be + * null. + * + * Return value: None. + */ +typedef void(*sm_enum_func)(const char *key, const char *value, const void *obj); + +/* + * Creates a string map. + * + * Parameters: + * + * capacity: The number of top-level slots this string map + * should allocate. This parameter must be > 0. + * + * Return value: A pointer to a string map object, + * or null if a new string map could not be allocated. + */ +StrMap * sm_new(unsigned int capacity); + +/* + * Releases all memory held by a string map object. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * If the supplied string map has been previously released, the + * behaviour of this function is undefined. + * + * Return value: None. + */ +void sm_delete(StrMap *map); + +/* + * Returns the value associated with the supplied key. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * out_buf: A pointer to an output buffer which will contain the value, + * if it exists and fits into the buffer. + * + * n_out_buf: The size of the output buffer in bytes. + * + * Return value: If out_buf is set to null and n_out_buf is set to 0 the return + * value will be the number of bytes required to store the value (if it exists) + * and its null-terminator. For all other parameter configurations the return value + * is 1 if an associated value was found and completely copied into the output buffer, + * 0 otherwise. + */ +int sm_get(const StrMap *map, const char *key, char *out_buf, unsigned int n_out_buf); + +/* + * Queries the existence of a key. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter cannot + * be null. + * + * Return value: 1 if the key exists, 0 otherwise. + */ +int sm_exists(const StrMap *map, const char *key); + +/* + * Associates a value with the supplied key. If the key is already + * associated with a value, the previous value is replaced. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * key: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * value: A pointer to a null-terminated C string. This parameter + * cannot be null. The string must have a string length > 0. The + * string will be copied. + * + * Return value: 1 if the association succeeded, 0 otherwise. + */ +int sm_put(StrMap *map, const char *key, const char *value); + +/* + * Returns the number of associations between keys and values. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * Return value: The number of associations between keys and values. + */ +int sm_get_count(const StrMap *map); + +/* + * An enumerator over all associations between keys and values. + * + * Parameters: + * + * map: A pointer to a string map. This parameter cannot be null. + * + * enum_func: A pointer to a callback function that will be + * called by this procedure once for every key associated + * with a value. This parameter cannot be null. + * + * obj: A pointer to a client-specific object. This parameter will be + * passed back to the client's callback function. This parameter can + * be null. + * + * Return value: 1 if enumeration completed, 0 otherwise. + */ +int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj); + +#ifdef __cplusplus +} +#endif + +#endif + +/* + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. + +*/ diff --git a/src/seedlink/plugins/seismicpi_plugin/settings/style.css b/src/seedlink/plugins/seismicpi_plugin/settings/style.css new file mode 100644 index 0000000000..c46abda429 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/settings/style.css @@ -0,0 +1,190 @@ +.container +{ + width: 90%; + margin: 10px auto; + background-color: #fff; + color: #333; + line-height: 130%; +} + +.top +{ + padding: .5em; +} + +.vheading { + padding-top: 5px; + padding-bottom: 5px; + font-family: verdana, sans-serif; + color: black; + text-decoration: none; + font-size: 12px; +} + +.vitem { + padding-left: 25px; + padding-top: 2px; + padding-bottom: 2px; + font-family: verdana, sans-serif; + color: black; + text-decoration: none; + font-size: 12px; +} + +.vsubitem { + padding-left: 40px; + padding-top: 2px; + padding-bottom: 2px; + font-family: verdana, sans-serif; + color: black; + text-decoration: none; + font-size: 12px; +} + +.top h1 +{ + padding: 0; + margin: 0; + font-family: "Times New Roman", Times, serif; + font-size: 24px; + font-weight: normal; + color: #00529b; + text-transform: uppercase; + letter-spacing: 2px; +} + +.leftnav +{ + float: left; + width: 110px; + margin: 0; + padding: 1em; + border-left: dotted black; +} + +.navlink { + font-family: verdana, sans-serif; + font-weight: bold; + font-size: 16px; + color: #00529b; +} + +.leftnav a { + font-family: verdana, sans-serif; + color: black; + text-decoration: none; + font-size: 12px; +} + +.leftnav a:hover { + border-bottom: 1px solid #00529b; +} + +.cap { + font-style: italic; + margin: 2px; + width: 200px; +} + +.capfig2 { + float: left; + margin: 5px; +} + +.capfig2 img { + border: 1px solid black; +} + +.capfig { + float: right; + margin: 5px; +} + +.capfig img { + border: 1px solid black; +} + +.content +{ + margin-left: 150px; + padding: 1em; + max-width: 36em; +} + +.content h2 { + font-family: "Times New Roman", Times, serif; + font-size: 18px; + font-weight: normal; + color: #00529b; + text-transform: uppercase; + letter-spacing: 1px; +} + +.content h3 { + font-family: "Times New Roman", Times, serif; + font-size: 14px; + font-weight: normal; + color: #00529b; + text-transform: uppercase; + letter-spacing: 1px; +} + +.content a { + color: #000; + text-decoration: none; + border-bottom: dotted 1px #000; +} + +.content textarea { + width: 350px; +} + + +.content p { + font-family: verdana, sans-serif; + font-size: 12px; + color: black; + text-decoration: none; +} + +.content ol { + font-family: verdana, sans-serif; + font-size: 12px; + color: black; + text-decoration: none; +} + +.content ul { + font-family: verdana, sans-serif; + font-size: 12px; + color: black; + text-decoration: none; +} + +.code { + font-family: Courier, Monospace; + font-size: 12px; + overflow: auto; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 5px; + padding-right: 5px; + border: 1px black dotted; + background-color: #EEE; +} + +.footer +{ + clear: both; + margin: 0; + padding: .5em; +} + +.footer p { + font-family: verdana, sans-serif; + font-size: 12px; + color: black; + text-decoration: none; +} + + diff --git a/src/seedlink/plugins/seismicpi_plugin/setup_configuration/SETTINGS.txt b/src/seedlink/plugins/seismicpi_plugin/setup_configuration/SETTINGS.txt new file mode 100644 index 0000000000..9785df18e4 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/setup_configuration/SETTINGS.txt @@ -0,0 +1,35 @@ +# PI HAT SEISCOMP3 CONFIGURATION # +# ENTER SETTINGS AFTER '=' without using spaced # + +# STATION NAME (4 Characters maximum). Default is TEST +STATION=TES + +# NETWORK NAME (2 Characters maximum). Default is UK. +NETWORK=HL + +# CHANNEL NAME (2 Characters maximum). Default is BH. When using internal input the channels are BHE,BHN,BHZ, while when using the external input are BH1,BH2,BH3,BH4 +CHANNEL=BH + +# LOCATION CODE (2 Characters). Default is 00 +LOCATION=01 + +# GAIN. Default is '1'. +# ‘1’: ×1 = 0.64μV/count +# ‘2’: ×2 = 0.32μV/count +# ‘4’: ×2 = 0.16μV/count +# ‘8’: ×2 = 0.08μV/count +# +GAIN=1 + +#Component of seismogram, one of Z, N or E. Default is Z. This is used in single channel mode. +COMPONENT=Z + +# SINGLE or MULTI Channel Mode. Default is '2'. +# ‘1’: Single Channel Mode +# ‘2’: Multi Channel Mode +MODE=2 + +#Internal or External Source. Deault is 1. +# ‘1’: Use Internal Accelerometer +# ‘2’: Use external inputs +SOURCE=1 diff --git a/src/seedlink/plugins/seismicpi_plugin/setup_configuration/seismicpi_setup.pl b/src/seedlink/plugins/seismicpi_plugin/setup_configuration/seismicpi_setup.pl new file mode 100644 index 0000000000..5fe8c6ce14 --- /dev/null +++ b/src/seedlink/plugins/seismicpi_plugin/setup_configuration/seismicpi_setup.pl @@ -0,0 +1,220 @@ +#!/usr/bin/perl +# Seismic PI station configuration utility. +# Writte by Kostas Boukouras (kbouk@noa.gr) +# Version 1.0. 20/12/2020. +# +# Check if SEISCOMP_ROOT variable is defined +$sr = `echo \$SEISCOMP_ROOT`; chomp $sr; + +if ($sr ne "") { +# +# Shutting down SeisComP +print "Stopping SeisComP...\n"; +`$sr/bin/seiscomp stop`; +print "Updating configuration...\n"; +# Parse input settings +$config ="SETTINGS.txt"; +open(CONFIG,"<$config") or die "Cannot open $config, $!\n"; +@conf = ; +close(CONFIG); +chomp @conf; +@sta_line = grep(/^STATION/i, @conf); +$station = (split(/=/,$sta_line[0]))[1]; +@net_line = grep(/^NET/i, @conf); +$net = (split(/=/,$net_line[0]))[1]; +@comp_line = grep(/^COMPONENT/i, @conf); +$comp = (split(/=/,$comp_line[0]))[1]; +@channel_line = grep(/^CHANNEL/i, @conf); +$channel = (split(/=/,$channel_line[0]))[1]; +@loc_line = grep(/^LOCATION/i, @conf); +$loc = (split(/=/,$loc_line[0]))[1]; +@gain_line = grep(/^GAIN/i, @conf); +$gain = (split(/=/,$gain_line[0]))[1]; +@mode_line = grep(/^MODE/i, @conf); +$mode = (split(/=/,$mode_line[0]))[1]; +@src_line = grep(/^SOURCE/i, @conf); +$src = (split(/=/,$src_line[0]))[1]; + +# Validate input values +# NET +$lnet = length $net; +if ($lnet > 2) { print "\n*** Error: Network length maximum length must be 2 characters, e.g UK, exiting. ***\n"; exit; } +# STATION +$lstat = length $station; +if ($lstat > 5) { print "\n*** Error: Station name maximum length must be 4 characters, e.g TEST, exiting. ***\n"; exit; } +# COMP +$lcomp = length $comp; +if ($lcomp > 1) { print "\n*** Error: Component maximum length must be 1 character, e.g Z, exiting. ***\n"; exit; } +# CHANNEL +$lchan = length $channel; +if ($lchan > 2) { print "\n*** Error: Channel maximum length must be 2 character2, e.g BH, exiting. ***\n"; exit; } +# LOCATION +$lloc = length $loc; +if ($lloc > 2) { print "\n*** Error: Location maximum length must be 1 character, e.g 00, exiting. ***\n"; exit; } +# GAIN +if ($gain eq "1") { $gain_msg = "0.064 μV/count";} +elsif ($gain eq "2") { $gain_msg = "0.32 μV/count";} +elsif ($gain eq "4") { $gain_msg = "0.16 μV/count";} +elsif ($gain eq "8") { $gain_msg = "0.08 μV/count";} +else { print "\n*** Wrong gain value defined.\n +--- For SeismicPi HAT can be 1, 2, 4 or 8: + ‘1’: ×1 = 0.64μV/count + ‘2’: ×2 = 0.32μV/count + ‘4’: ×2 = 0.16μV/count + ‘8’: ×2 = 0.08μV/count, exiting. ***\n "; exit; } +# MODE +if ($mode eq "1") {$mode_msg = "Single Channel Mode"; } +elsif ($mode eq "2") {$mode_msg = "Multi Channel Mode"; } +else { print "\n*** Wrong Channel Mode defined.\n +--- For SeismicPI HAT can be 1 or 2: + ‘1’: Single Channel Mode + ‘2’: Multi Channel Mode, exiting. ***\n"; exit; } +# SOURCE +if ($src eq "1") {$src_msg = "Internal Accelerometer";} +elsif ($src eq "2") {$src_msg = "External Inputs"; } +else { print "\n*** Wrong Source defined,\n +--- For SeismicPI HAT can be 1 or 2: + ‘1’: Use Internal Accelerometer + ‘2’: Use external inputs, exiting. ***\n"; exit; } + +# Configure SeisComP files +# Uncomment the following to clean old key and config file +print "Cleaning old files...\n"; +print "\n*Warning* UPDATE.pl sets new configuration and old must be removed in order to work.\nDelete $sr/etc/key/station* entries (Y/N) ?\nIf (Y) selected ALL station entries will be deleted.\nIf unsure select (N) and manually delete previous SeismicPI station information.\nThen run:\n$sr/bin/seiscomp stop;$sr/bin/seiscomp update-config;$sr/bin/seiscomp start\n\nSelect (Y/N):"; +$del_choice = ; +chomp $del_choice; +if ($del_choice eq "Y") { + # Make new key file +$sc3_key = "$sr/etc/key/station\_$net\_$station"; +$sc3_arch = "$sr/etc/key/slarchive/profile_90d"; +if (!-e "$sr/etc/key/slarchive") {`mkdir -p $sr/etc/key/slarchive`;} + +if (!-e "$sc3_arch") { +open(SC3,"+>$sc3_arch") or die "Cannot open $sc3_arch\n"; +printf SC3 "keep = 90"; +} +close(SC3); + +open(SC3,"+>$sc3_key") or die "Cannot open $sc3_key\n"; +printf SC3 "seedlink:seismicpi\n"; +printf SC3 "slarchive:90d"; +close(SC3); +} elsif ($del_choice eq "N") { + print "Keeping old station info. Acquisition may not run with new settings.\n"; +} else { print "(Y/N) must be selected, exiting.\n"; exit; } + + +print "$sc3_key\n"; +`$sr/bin/seiscomp update-config`; +# Make plugin configuration file +$plugin_cfg = "$sr/var/lib/seedlink/$net.$station.ini"; +print "\n\nMaking SeismicPI $plugin_cfg config file.\nPlease delete previous SeismicPI $sr/var/lib/seedlink/..ini files.\n\n"; +open(CFG,"+>$plugin_cfg") or die "Cannot open $plugin_cfg\n"; +printf CFG " +#========================================= +# section name (do not remove) +[Logging] + +#--- Device path and name of port for SeismicPI HAT. type=string default=/dev/ttyS0 +# If the specified port cannot be opened or is not a USB Seismometer Interface device, all available ports +# will be scanned to find a USB Seismometer Interface device. +# +port_path_hint=/dev/ttyS0 + +#--- Allow low-level setting of port interface attributes when available ports are scanned +# to find a USB Seismometer Interface device, 0=NO, 1=Yes. type=int default=1 +# Setting 1 (=Yes) may help successful detection and correct reading of the SeismicPI HAT Interface device. +# +allow_set_interface_attribs=1 + +#--- Sets a fixed sample rate to report in the miniseed file header. type=real; default=-1 +# The default (value < 32) sets an estimated sample rate based on recent packet start times. +# See also: [Station] nominal_sample_rate +# +mswrite_header_sample_rate=-1 + +#--- SEED data encoding type for writing miniseed files. type=string; default=STEIM2 +# Supported values are: DE_INT16, DE_INT32, DE_STEIM1, DE_STEIM2 +mswrite_data_encoding_type=DE_STEIM2 + +#========================================= +# section name (do not remove) +[Station] + +#--- The code representing the network this station belongs to. type=string default=UK +# +station_network=$net + +#--- Descriptive name for station. Used for AmaSeis server. type=string default=TEST +# +station_name=$station + +#--- Descriptive name for the location code. Used for AmaSeis server. type=string default=00 +# +location_code=$loc + +#--- The initial letters to set for the miniseed header 'channel', will be prepended to the component. type=string default=BH +# +channel_prefix=$channel + +#--- Component of seismogram, one of Z, N or E. type=string default=Z +# +component=$comp + +#--- Set sample rate and gain on SEP 064 device, 0=NO, 1=Yes. type=int default=0 +#--- For SEISMOMETER INTERFACE (USB) (CODE: SEP 064) can be one of 20, 40 or 80 +# +# +do_settings_pihat=1 + +#--- Nominal sample rate per second. type=int default=32 +# See also: [Logging] mswrite_header_sample_rate +# +#--- For SeismicPI HAT can be one of 32, 64 or 128 +# +# +nominal_sample_rate=32 + +#--- Nominal gain, one of 1, 2 or 4. type=int default=1 +# +#--- For SeismicPi HAT can be 1, 2, 4 or 8: +# ‘1’: ×1 = 0.64μV/count +# ‘2’: ×2 = 0.32μV/count +# ‘4’: ×2 = 0.16μV/count +# ‘8’: ×2 = 0.08μV/count +# +nominal_gain=$gain + +#--- Single or Multi Channel Mode, 1 or 2. type=int default=2 +# +#--- For SeismicPI HAT can be 1 or 2: +# ‘1’: Single Channel Mode +# ‘2’: Multi Channel Mode +# +single_multi=$mode + +#--- Internal or External Source, 1 or 2. type=int default=1 +# +#--- For SeismicPI HAT can be 1 or 2: +# ‘1’: Use Internal Accelerometer +# ‘2’: Use external inputs +# +source_select=$src +"; +`$sr/bin/seiscomp start`; +print "\n +****************************************************** +Configuration update completed and SeisComP is running. +Station: $station +Network: $net +Location: $loc +Channel: $channel +Component: $comp +Gain: $gain_msg +Mode: $mode_msg +Source: $src_msg +****************************************************** +"; +sleep(5); +} else { print "Global variable \$SEISCOMP_ROOT must be defined.\n"; } + diff --git a/src/seedlink/templates/seismicpi/seedlink_plugin.tpl b/src/seedlink/templates/seismicpi/seedlink_plugin.tpl new file mode 100644 index 0000000000..c3e52d82be --- /dev/null +++ b/src/seedlink/templates/seismicpi/seedlink_plugin.tpl @@ -0,0 +1,6 @@ +* template: $template +plugin $seedlink.source.id cmd = "$seedlink.plugin_dir/seismicpi_plugin -p $seedlink.config_dir/${seedlink.station.network}.${seedlink.station.code}.ini" + timeout = 600 + start_retry = 60 + shutdown_wait = 10 + diff --git a/src/seedlink/templates/seismicpi/seismicpi.prop.tpl b/src/seedlink/templates/seismicpi/seismicpi.prop.tpl new file mode 100644 index 0000000000..8f2d42802e --- /dev/null +++ b/src/seedlink/templates/seismicpi/seismicpi.prop.tpl @@ -0,0 +1,97 @@ +#========================================= +# section name (do not remove) +[Logging] + +#--- Device path and name of port for SeismicPI HAT. type=string default=/dev/ttyS0 +# If the specified port cannot be opened or is not a USB Seismometer Interface device, all available ports +# will be scanned to find a USB Seismometer Interface device. +# +port_path_hint=$sources.seismicpi.port_path_hint + +#--- Allow low-level setting of port interface attributes when available ports are scanned +# to find a USB Seismometer Interface device, 0=NO, 1=Yes. type=int default=1 +# Setting 1 (=Yes) may help successful detection and correct reading of the SeismicPI HAT Interface device. +# +allow_set_interface_attribs=$sources.seismicpi.allow_set_interface_attribs + +#--- Sets a fixed sample rate to report in the miniseed file header. type=real; default=-1 +# The default (value < 32) sets an estimated sample rate based on recent packet start times. +# See also: [Station] nominal_sample_rate +# +# For SEISMOMETER INTERFACE (USB) (CODE: SEP 064) use: +# nominal_sample_rate: mswrite_header_sample_rate +# 32: 32 SPS +# 64: 64 SPS +# 128: 128 SPS +# +mswrite_header_sample_rate=$sources.seismicpi.mswrite_header_sample_rate + +#--- SEED data encoding type for writing miniseed files. type=string; default=STEIM2 +# Supported values are: DE_INT16, DE_INT32, DE_STEIM1, DE_STEIM2 +mswrite_data_encoding_type=DE_$sources.seismicpi.mswrite_data_encoding_type + +#========================================= +# section name (do not remove) +[Station] + +#--- The code representing the network this station belongs to. type=string default=UK +# +station_network=$seedlink.station.network + +#--- Descriptive name for station. Used for AmaSeis server. type=string default=TEST +# +station_name=$seedlink.station.code + +#--- Descriptive name for the location code. Used for AmaSeis server. type=string default=00 +# +location_code=$sources.seismicpi.locationcode_prefix + +#--- The initial letters to set for the miniseed header 'channel', will be prepended to the component. type=string default=BH +# +channel_prefix=$sources.seismicpi.channel_prefix + +#--- Component of seismogram, one of Z, N or E. type=string default=Z +# +component=$sources.seismicpi.component + + +#--- Set sample rate and gain on SEP 064 device, 0=NO, 1=Yes. type=int default=0 +#--- For SEISMOMETER INTERFACE (USB) (CODE: SEP 064) can be one of 20, 40 or 80 +# +# +do_settings_pihat=$sources.seismicpi.do_settings_pihat + +#--- Nominal sample rate per second. type=int default=32 +# See also: [Logging] mswrite_header_sample_rate +# +#--- For SeismicPI HAT can be one of 32, 64 or 128 +# +# +nominal_sample_rate=$sources.seismicpi.nominal_sample_rate + + +#--- Nominal gain, one of 1, 2 or 4. type=int default=1 +# +#--- For SeismicPi HAT can be 1, 2, 4 or 8: +# ‘1’: ×1 = 0.64μV/count +# ‘2’: ×2 = 0.32μV/count +# ‘4’: ×2 = 0.16μV/count +# ‘8’: ×2 = 0.08μV/count +# +nominal_gain=$sources.seismicpi.nominal_gain + +#--- Single or Multi Channel Mode, 1 or 2. type=int default=2 +# +#--- For SeismicPI HAT can be 1 or 2: +# ‘1’: Single Channel Mode +# ‘2’: Multi Channel Mode +# +single_multi=$sources.seismicpi.single_multi + +#--- Internal or External Source, 1 or 2. type=int default=1 +# +#--- For SeismicPI HAT can be 1 or 2: +# ‘1’: Use Internal Accelerometer +# ‘2’: Use external inputs +# +select_source=$sources.seismicpi.select_source diff --git a/src/seedlink/templates/seismicpi/setup.py b/src/seedlink/templates/seismicpi/setup.py new file mode 100644 index 0000000000..1c5ff121ac --- /dev/null +++ b/src/seedlink/templates/seismicpi/setup.py @@ -0,0 +1,58 @@ +import os + +''' +Plugin handler for the seismicpi plugin. +''' +class SeedlinkPluginHandler: + # Create defaults + def __init__(self): pass + + def push(self, seedlink): + # Check and set defaults + try: seedlink.param('sources.seismicpi.port_path_hint') + except: seedlink.setParam('sources.seismicpi.port_path_hint', '/dev/ttyS0') + + try: seedlink.param('sources.seismicpi.allow_set_interface_attribs') + except: seedlink.setParam('sources.seismicpi.allow_set_interface_attribs', 1) + + try: seedlink.param('sources.seismicpi.mswrite_header_sample_rate') + except: seedlink.setParam('sources.seismicpi.mswrite_header_sample_rate', '1') + + try: seedlink.param('sources.seismicpi.mswrite_data_encoding_type') + except: seedlink.setParam('sources.seismicpi.mswrite_data_encoding_type', 'STEIM2') + + try: seedlink.param('sources.seismicpi.channel_prefix') + except: seedlink.setParam('sources.seismicpi.channel_prefix', 'BH') + + try: seedlink.param('sources.seismicpi.locationcode_prefix') + except: seedlink.setParam('sources.seismicpi.locationcode_prefix', '00') + + try: seedlink.param('sources.seismicpi.component') + except: seedlink.setParam('sources.seismicpi.component', 'Z') + + try: seedlink.param('sources.seismicpi.do_settings_pihat') + except: seedlink.setParam('sources.seismicpi.do_settings_pihat', '1') + + try: seedlink.param('sources.seismicpi.nominal_sample_rate') + except: seedlink.setParam('sources.seismicpi.nominal_sample_rate', '32') + + try: seedlink.param('sources.seismicpi.nominal_gain') + except: seedlink.setParam('sources.seismicpi.nominal_gain', '1') + + try: seedlink.param('sources.seismicpi.single_multi') + except: seedlink.setParam('sources.seismicpi.single_multi', '1') + + try: seedlink.param('sources.seismicpi.select_source') + except: seedlink.setParam('sources.seismicpi.select_source', '1') + + tag = seedlink.net + "." + seedlink.sta + + fd = open(os.path.join(seedlink.config_dir, tag + ".ini"), "w") + fd.write(seedlink._process_template('seismicpi.prop.tpl', 'seismicpi')) + fd.close() + + return tag + + # Flush does nothing + def flush(self, seedlink): + pass