mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-30 00:00:11 +01:00
signal-generator: add support for configuring different paramenters for each generated signal value
This commit is contained in:
parent
d96afb3b13
commit
bfe60465f5
2 changed files with 121 additions and 47 deletions
|
@ -55,10 +55,10 @@ struct signal_generator {
|
||||||
} type; /**< Signal type */
|
} type; /**< Signal type */
|
||||||
|
|
||||||
double rate; /**< Sampling rate. */
|
double rate; /**< Sampling rate. */
|
||||||
double frequency; /**< Frequency of the generated signals. */
|
double *frequency; /**< Frequency of the generated signals. */
|
||||||
double amplitude; /**< Amplitude of the generated signals. */
|
double *amplitude; /**< Amplitude of the generated signals. */
|
||||||
double stddev; /**< Standard deviation of random signals (normal distributed). */
|
double *stddev; /**< Standard deviation of random signals (normal distributed). */
|
||||||
double offset; /**< A constant bias. */
|
double *offset; /**< A constant bias. */
|
||||||
int monitor_missed; /**< Boolean, if set, node counts missed steps and warns user. */
|
int monitor_missed; /**< Boolean, if set, node counts missed steps and warns user. */
|
||||||
|
|
||||||
double *last; /**< The values from the previous period which are required for random walk. */
|
double *last; /**< The values from the previous period which are required for random walk. */
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*********************************************************************************/
|
*********************************************************************************/
|
||||||
|
|
||||||
|
#include <list>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
@ -86,6 +87,47 @@ static const char * signal_generator_type_str(enum signal_generator::SignalType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int signal_generator_init(struct node *n)
|
||||||
|
{
|
||||||
|
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
||||||
|
|
||||||
|
new (&s->task) Task(CLOCK_MONOTONIC);
|
||||||
|
|
||||||
|
s->rt = 1;
|
||||||
|
s->limit = -1;
|
||||||
|
s->values = 1;
|
||||||
|
s->rate = 10;
|
||||||
|
s->monitor_missed = 1;
|
||||||
|
|
||||||
|
s->frequency = nullptr;
|
||||||
|
s->amplitude = nullptr;
|
||||||
|
s->stddev = nullptr;
|
||||||
|
s->offset = nullptr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int signal_generator_destroy(struct node *n)
|
||||||
|
{
|
||||||
|
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
||||||
|
|
||||||
|
s->task.~Task();
|
||||||
|
|
||||||
|
if (s->frequency)
|
||||||
|
delete s->frequency;
|
||||||
|
|
||||||
|
if (s->amplitude)
|
||||||
|
delete s->amplitude;
|
||||||
|
|
||||||
|
if (s->stddev)
|
||||||
|
delete s->stddev;
|
||||||
|
|
||||||
|
if (s->offset)
|
||||||
|
delete s->offset;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int signal_generator_prepare(struct node *n)
|
int signal_generator_prepare(struct node *n)
|
||||||
{
|
{
|
||||||
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
||||||
|
@ -112,31 +154,82 @@ int signal_generator_parse(struct node *n, json_t *cfg)
|
||||||
|
|
||||||
json_error_t err;
|
json_error_t err;
|
||||||
|
|
||||||
s->rt = 1;
|
json_t *json_amplitude = nullptr;
|
||||||
s->limit = -1;
|
json_t *json_offset = nullptr;
|
||||||
s->values = 1;
|
json_t *json_frequency = nullptr;
|
||||||
s->rate = 10;
|
json_t *json_stddev = nullptr;
|
||||||
s->frequency = 1;
|
|
||||||
s->amplitude = 1;
|
|
||||||
s->stddev = 0.2;
|
|
||||||
s->offset = 0;
|
|
||||||
s->monitor_missed = 1;
|
|
||||||
|
|
||||||
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: b, s?: i, s?: i, s?: F, s?: F, s?: F, s?: F, s?: F, s?: b}",
|
ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: b, s?: i, s?: i, s?: F, s?: o, s?: o, s?: o, s?: o, s?: b }",
|
||||||
"signal", &type,
|
"signal", &type,
|
||||||
"realtime", &s->rt,
|
"realtime", &s->rt,
|
||||||
"limit", &s->limit,
|
"limit", &s->limit,
|
||||||
"values", &s->values,
|
"values", &s->values,
|
||||||
"rate", &s->rate,
|
"rate", &s->rate,
|
||||||
"frequency", &s->frequency,
|
"frequency", &json_frequency,
|
||||||
"amplitude", &s->amplitude,
|
"amplitude", &json_amplitude,
|
||||||
"stddev", &s->stddev,
|
"stddev", &json_stddev,
|
||||||
"offset", &s->offset,
|
"offset", &json_offset,
|
||||||
"monitor_missed", &s->monitor_missed
|
"monitor_missed", &s->monitor_missed
|
||||||
);
|
);
|
||||||
if (ret)
|
if (ret)
|
||||||
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
jerror(&err, "Failed to parse configuration of node %s", node_name(n));
|
||||||
|
|
||||||
|
struct desc {
|
||||||
|
json_t *json;
|
||||||
|
double **array;
|
||||||
|
double def_value;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::list<struct desc> arrays = {
|
||||||
|
{ json_frequency, &s->frequency, 1, "frequency" },
|
||||||
|
{ json_amplitude, &s->amplitude, 1, "amplitude" },
|
||||||
|
{ json_stddev, &s->stddev, 0.2, "stddev" },
|
||||||
|
{ json_offset, &s->offset, 0, "offset" }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &a : arrays) {
|
||||||
|
if (*a.array)
|
||||||
|
delete *a.array;
|
||||||
|
|
||||||
|
*a.array = new double[s->values];
|
||||||
|
|
||||||
|
if (a.json) {
|
||||||
|
switch (json_typeof(a.json)) {
|
||||||
|
case JSON_ARRAY:
|
||||||
|
if (json_array_size(a.json) != s->values)
|
||||||
|
throw ConfigError(a.json, "node-config-node-signal", "Length of values must match");
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
json_t *json_value;
|
||||||
|
json_array_foreach(a.json, i, json_value) {
|
||||||
|
if (!json_is_real(json_value))
|
||||||
|
throw ConfigError(json_value, "node-config-node-signal", "Values must gives as array of float values!");
|
||||||
|
|
||||||
|
(*a.array)[i] = json_real_value(json_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JSON_REAL:
|
||||||
|
if (!json_is_real(a.json))
|
||||||
|
throw ConfigError(a.json, "node-config-node-signal", "Values must gives as array of float values!");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->values; i++)
|
||||||
|
(*a.array)[i] = json_real_value(a.json);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw ConfigError(a.json, "node-config-node-signal", "Values must given as array or scalar float value!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t i = 0; i < s->values; i++)
|
||||||
|
(*a.array)[i] = a.def_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type)
|
if (type)
|
||||||
s->type = signal_generator_lookup_type(type);
|
s->type = signal_generator_lookup_type(type);
|
||||||
else
|
else
|
||||||
|
@ -157,7 +250,7 @@ int signal_generator_start(struct node *n)
|
||||||
throw MemoryAllocationError();
|
throw MemoryAllocationError();
|
||||||
|
|
||||||
for (unsigned i = 0; i < s->values; i++)
|
for (unsigned i = 0; i < s->values; i++)
|
||||||
s->last[i] = s->offset;
|
s->last[i] = s->offset[i];
|
||||||
|
|
||||||
/* Setup task */
|
/* Setup task */
|
||||||
if (s->rt)
|
if (s->rt)
|
||||||
|
@ -223,31 +316,31 @@ int signal_generator_read(struct node *n, struct sample *smps[], unsigned cnt, u
|
||||||
|
|
||||||
switch (rtype) {
|
switch (rtype) {
|
||||||
case signal_generator::SignalType::CONSTANT:
|
case signal_generator::SignalType::CONSTANT:
|
||||||
t->data[i].f = s->offset + s->amplitude;
|
t->data[i].f = s->offset[i] + s->amplitude[i];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::SINE:
|
case signal_generator::SignalType::SINE:
|
||||||
t->data[i].f = s->offset + s->amplitude * sin(running * s->frequency * 2 * M_PI);
|
t->data[i].f = s->offset[i] + s->amplitude[i] * sin(running * s->frequency[i] * 2 * M_PI);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::TRIANGLE:
|
case signal_generator::SignalType::TRIANGLE:
|
||||||
t->data[i].f = s->offset + s->amplitude * (fabs(fmod(running * s->frequency, 1) - .5) - 0.25) * 4;
|
t->data[i].f = s->offset[i] + s->amplitude[i] * (fabs(fmod(running * s->frequency[i], 1) - .5) - 0.25) * 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::SQUARE:
|
case signal_generator::SignalType::SQUARE:
|
||||||
t->data[i].f = s->offset + s->amplitude * ( (fmod(running * s->frequency, 1) < .5) ? -1 : 1);
|
t->data[i].f = s->offset[i] + s->amplitude[i] * ( (fmod(running * s->frequency[i], 1) < .5) ? -1 : 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::RAMP:
|
case signal_generator::SignalType::RAMP:
|
||||||
t->data[i].f = s->offset + s->amplitude * fmod(running, s->frequency);
|
t->data[i].f = s->offset[i] + s->amplitude[i] * fmod(running, s->frequency[i]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::COUNTER:
|
case signal_generator::SignalType::COUNTER:
|
||||||
t->data[i].f = s->offset + s->amplitude * s->counter;
|
t->data[i].f = s->offset[i] + s->amplitude[i] * s->counter;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case signal_generator::SignalType::RANDOM:
|
case signal_generator::SignalType::RANDOM:
|
||||||
s->last[i] += box_muller(0, s->stddev);
|
s->last[i] += box_muller(0, s->stddev[i]);
|
||||||
t->data[i].f = s->last[i];
|
t->data[i].f = s->last[i];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -275,8 +368,8 @@ char * signal_generator_print(struct node *n)
|
||||||
char *buf = nullptr;
|
char *buf = nullptr;
|
||||||
const char *type = signal_generator_type_str(s->type);
|
const char *type = signal_generator_type_str(s->type);
|
||||||
|
|
||||||
strcatf(&buf, "signal=%s, rt=%s, rate=%.2f, values=%d, frequency=%.2f, amplitude=%.2f, stddev=%.2f, offset=%.2f",
|
strcatf(&buf, "signal=%s, rt=%s, rate=%.2f, values=%d",
|
||||||
type, s->rt ? "yes" : "no", s->rate, s->values, s->frequency, s->amplitude, s->stddev, s->offset);
|
type, s->rt ? "yes" : "no", s->rate, s->values);
|
||||||
|
|
||||||
if (s->limit > 0)
|
if (s->limit > 0)
|
||||||
strcatf(&buf, ", limit=%d", s->limit);
|
strcatf(&buf, ", limit=%d", s->limit);
|
||||||
|
@ -293,24 +386,6 @@ int signal_generator_poll_fds(struct node *n, int fds[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_generator_init(struct node *n)
|
|
||||||
{
|
|
||||||
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
|
||||||
|
|
||||||
new (&s->task) Task(CLOCK_MONOTONIC);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int signal_generator_destroy(struct node *n)
|
|
||||||
{
|
|
||||||
struct signal_generator *s = (struct signal_generator *) n->_vd;
|
|
||||||
|
|
||||||
s->task.~Task();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct plugin p;
|
static struct plugin p;
|
||||||
|
|
||||||
__attribute__((constructor(110)))
|
__attribute__((constructor(110)))
|
||||||
|
@ -332,7 +407,6 @@ static void register_plugin() {
|
||||||
p.node.read = signal_generator_read;
|
p.node.read = signal_generator_read;
|
||||||
p.node.poll_fds = signal_generator_poll_fds;
|
p.node.poll_fds = signal_generator_poll_fds;
|
||||||
|
|
||||||
|
|
||||||
vlist_init(&p.node.instances);
|
vlist_init(&p.node.instances);
|
||||||
vlist_push(&plugins, &p);
|
vlist_push(&plugins, &p);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue