From 007ff9a776f71258f6423b38bbaac43be19d3e9c Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 6 Jul 2017 21:15:46 +0200 Subject: [PATCH] added new node-type based on villas-signal tool --- etc/example.conf | 10 ++ include/villas/nodes/signal.h | 82 ++++++++++++++++ lib/Makefile.villas.inc | 2 +- lib/nodes/signal.c | 178 ++++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 include/villas/nodes/signal.h create mode 100644 lib/nodes/signal.c diff --git a/etc/example.conf b/etc/example.conf index 9f212df34..e3fb886f0 100644 --- a/etc/example.conf +++ b/etc/example.conf @@ -189,6 +189,16 @@ nodes = { "tcp://localhost:1235", "tcp://localhost:12444" ], + }, + signal_node = { + type = "signal", + + signal = "sine", # One of "sine", "ramp", "triangle", "random", "mixed" + values = 4, # Number of values per sample + amplitude = 2.3, # Amplitude of generated signals + frequency = 10, # Frequency of generated signals + stddev = 2, # Standard deviation of random signals (normal distributed) + rate = 10.0, # Sample rate } }; diff --git a/include/villas/nodes/signal.h b/include/villas/nodes/signal.h new file mode 100644 index 000000000..d3d262c1f --- /dev/null +++ b/include/villas/nodes/signal.h @@ -0,0 +1,82 @@ +/** Node-type for signal generation. + * + * @file + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +/** + * @ingroup node + * @addtogroup signal Signal generation node-type- + * @{ + */ + +#pragma once + +#include + +#include "timing.h" + +/* Forward declarations */ +struct node; +struct sample; + +/** Node-type for signal generation. + * @see node_type + */ +struct signal { + int tfd; /**< timerfd file descriptor. */ + + enum { + TYPE_RANDOM, + TYPE_SINE, + TYPE_SQUARE, + TYPE_TRIANGLE, + TYPE_RAMP, + TYPE_MIXED + } type; /**< Signal type */ + + double rate; /**< Sampling rate. */ + double frequency; /**< Frequency of the generated signals. */ + double amplitude; /**< Amplitude of the generated signals. */ + double stddev; /**< Standard deviation of random signals (normal distributed). */ + + int values; /**< The number of values which will be emitted by this node. */ + int limit; /**< The number of values which should be generated by this node. <0 for infinitve. */ + + struct timespec started; /**< Point in time when this node was started. */ + int counter; /**< The number of packets already emitted. */ +}; + +/** @see node_type::print */ +char * signal_print(struct node *n); + +/** @see node_type::parse */ +int signal_parse(struct node *n, config_setting_t *cfg); + +/** @see node_type::open */ +int signal_open(struct node *n); + +/** @see node_type::close */ +int signal_close(struct node *n); + +/** @see node_type::read */ +int signal_read(struct node *n, struct sample *smps[], unsigned cnt); + +/** @} */ diff --git a/lib/Makefile.villas.inc b/lib/Makefile.villas.inc index 414f3f5ee..59849fcf1 100644 --- a/lib/Makefile.villas.inc +++ b/lib/Makefile.villas.inc @@ -25,7 +25,7 @@ LIB_ABI_VERSION = 1 LIB = $(BUILDDIR)/$(LIB_NAME).so.$(LIB_ABI_VERSION) # Object files for libvillas -LIB_SRCS += $(addprefix lib/nodes/, file.c cbuilder.c shmem.c) \ +LIB_SRCS += $(addprefix lib/nodes/, file.c cbuilder.c shmem.c signal.c) \ $(addprefix lib/kernel/, kernel.c rt.c) \ $(addprefix lib/, sample.c path.c node.c hook.c \ log.c log_config.c utils.c super_node.c hist.c timing.c pool.c \ diff --git a/lib/nodes/signal.c b/lib/nodes/signal.c new file mode 100644 index 000000000..45b4a2290 --- /dev/null +++ b/lib/nodes/signal.c @@ -0,0 +1,178 @@ +/** Node-type for signal generation. + * + * @file + * @author Steffen Vogel + * @copyright 2017, Institute for Automation of Complex Power Systems, EONERC + * @license GNU General Public License (version 3) + * + * VILLASnode + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include +#include + +#include "node.h" +#include "plugin.h" +#include "nodes/signal.h" + +int signal_parse(struct node *n, config_setting_t *cfg) +{ + struct signal *s = n->_vd; + + const char *type; + + if (!config_setting_lookup_string(cfg, "signal", &type)) + s->type = TYPE_MIXED; + else { + if (!strcmp(type, "random")) + s->type = TYPE_RANDOM; + else if (!strcmp(type, "sine")) + s->type = TYPE_SINE; + else if (!strcmp(type, "square")) + s->type = TYPE_SQUARE; + else if (!strcmp(type, "triangle")) + s->type = TYPE_TRIANGLE; + else if (!strcmp(type, "ramp")) + s->type = TYPE_RAMP; + else if (!strcmp(type, "mixed")) + s->type = TYPE_MIXED; + else + cerror(cfg, "Invalid signal type: %s", type); + } + + if (!config_setting_lookup_int(cfg, "limit", &s->limit)) + s->limit = -1; + + if (!config_setting_lookup_int(cfg, "values", &s->values)) + s->values = 1; + + if (!config_setting_lookup_float(cfg, "rate", &s->rate)) + s->rate = 10; + + if (!config_setting_lookup_float(cfg, "frequency", &s->frequency)) + s->frequency = 1; + + if (!config_setting_lookup_float(cfg, "amplitude", &s->amplitude)) + s->amplitude = 1; + + if (!config_setting_lookup_float(cfg, "stddev", &s->stddev)) + s->stddev = 0.02; + + + return 0; +} + +int signal_open(struct node *n) +{ + struct signal *s = n->_vd; + + s->counter = 0; + s->started = time_now(); + + s->tfd = timerfd_create_rate(s->rate); + + return 0; +} + +int signal_close(struct node *n) +{ + struct signal* s = n->_vd; + + close(s->tfd); + + return 0; +} + +int signal_read(struct node *n, struct sample *smps[], unsigned cnt) +{ + struct signal *s = n->_vd; + + assert(cnt == 1); + + uint64_t steps = timerfd_wait(s->tfd); + if (steps > 1) + warn("Missed steps: %" PRIu64, steps); + + struct timespec now = time_now(); + + double running = time_delta(&s->started, &now); + + smps[0]->ts.origin = + smps[0]->ts.received = now; + smps[0]->sequence = s->counter; + smps[0]->length = s->values; + + for (int i = 0; i < MIN(s->values, smps[0]->capacity); i++) { + int rtype = (s->type != TYPE_MIXED) ? s->type : i % 4; + switch (rtype) { + case TYPE_RANDOM: smps[0]->data[i].f += box_muller(0, s->stddev); break; + case TYPE_SINE: smps[0]->data[i].f = s->amplitude * sin(running * s->frequency * 2 * M_PI); break; + case TYPE_TRIANGLE: smps[0]->data[i].f = s->amplitude * (fabs(fmod(running * s->frequency, 1) - .5) - 0.25) * 4; break; + case TYPE_SQUARE: smps[0]->data[i].f = s->amplitude * ( (fmod(running * s->frequency, 1) < .5) ? -1 : 1); break; + case TYPE_RAMP: smps[0]->data[i].f = fmod(s->counter, s->rate / s->frequency); /** @todo send as integer? */ break; + } + } + + s->counter++; + if (s->limit > 0 && s->counter >= s->limit) { + info("Reached limit of node %s", node_name(n)); + killme(SIGTERM); + } + + return 1; +} + +char * signal_print(struct node *n) +{ + struct signal *s = n->_vd; + char *type, *buf = NULL; + + switch (s->type) { + case TYPE_MIXED: type = "mixed"; break; + case TYPE_RAMP: type = "ramp"; break; + case TYPE_TRIANGLE: type = "triangle"; break; + case TYPE_SQUARE: type = "square"; break; + case TYPE_SINE: type = "sine"; break; + case TYPE_RANDOM: type = "random"; break; + default: return NULL; + } + + strcatf(&buf, "signal=%s, rate=%.2f, values=%d, frequency=%.2f, amplitude=%.2f, stddev=%.2f", + type, s->rate, s->values, s->frequency, s->amplitude, s->stddev); + + if (s->limit > 0) + strcatf(&buf, ", limit=%d", s->limit); + + return buf; +}; + +static struct plugin p = { + .name = "signal", + .description = "Signal generation", + .type = PLUGIN_TYPE_NODE, + .node = { + .vectorize = 1, + .size = sizeof(struct signal), + .parse = signal_parse, + .print = signal_print, + .start = signal_open, + .stop = signal_close, + .read = signal_read, + } +}; + +REGISTER_PLUGIN(&p) +LIST_INIT_STATIC(&p.node.instances) \ No newline at end of file