1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/lib/hooks/dp.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

289 lines
7.5 KiB
C++
Raw Permalink Normal View History

2019-03-26 15:33:47 +01:00
/* Dynamic Phasor Interface Algorithm hook.
*
2022-03-15 09:18:01 -04:00
* Author: Steffen Vogel <post@steffenvogel.de>
2022-03-15 09:28:57 -04:00
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
2022-07-04 18:20:03 +02:00
* SPDX-License-Identifier: Apache-2.0
2019-03-26 15:33:47 +01:00
*/
#include <cmath>
#include <cstring>
2019-03-26 15:33:47 +01:00
#include <complex>
2019-04-15 10:03:13 +02:00
#include <villas/dsp/window.hpp>
2019-03-26 15:33:47 +01:00
#include <villas/hook.hpp>
#include <villas/sample.hpp>
#include <villas/utils.hpp>
2019-03-26 15:33:47 +01:00
using namespace std::complex_literals;
2019-06-04 16:55:38 +02:00
using namespace villas::utils;
2019-03-26 15:33:47 +01:00
namespace villas {
namespace node {
class DPHook : public Hook {
protected:
char *signal_name;
unsigned signal_index;
int offset;
int inverse;
double f0;
double timestep;
double time;
double steps;
std::complex<double> *coeffs;
int *fharmonics;
int fharmonics_len;
2019-04-15 10:03:13 +02:00
dsp::Window<double> window;
2019-03-26 15:33:47 +01:00
void step(double *in, std::complex<float> *out) {
2022-06-01 18:15:29 +02:00
int N = window.size();
2019-03-26 15:33:47 +01:00
__attribute__((unused)) std::complex<double> om_k, corr;
double newest = *in;
2019-04-15 10:03:13 +02:00
__attribute__((unused)) double oldest = window.update(newest);
2019-03-26 15:33:47 +01:00
for (int k = 0; k < fharmonics_len; k++) {
om_k = 2.0i * M_PI * (double)fharmonics[k] / (double)N;
// Correction for stationary phasor
2019-04-15 10:03:13 +02:00
corr = std::exp(-om_k * (steps - (N + 1)));
2019-03-26 15:33:47 +01:00
//corr = 1;
#if 0
// Recursive update
coeffs[k] = std::exp(om) * (coeffs[k] + (newest - oldest));
2019-04-15 10:03:13 +02:00
out[k] = (2.0 / N) * (coeffs[i] * corr);
2019-03-26 15:33:47 +01:00
// DC component
if (fharmonics[k] == 0)
out[k] /= 2.0;
#else
// Full DFT
std::complex<double> X_k = 0;
for (int n = 0; n < N; n++) {
2019-04-23 00:13:11 +02:00
double x_n = window[n];
2019-03-26 15:33:47 +01:00
X_k += x_n * std::exp(om_k * (double)n);
}
out[k] = X_k / (corr * (double)N);
#endif
}
}
void istep(std::complex<float> *in, double *out) {
std::complex<double> value = 0;
// Reconstruct the original signal
for (int k = 0; k < fharmonics_len; k++) {
double freq = fharmonics[k];
2021-09-17 18:19:06 +02:00
// cppcheck-suppress objectIndex
2019-03-26 15:33:47 +01:00
std::complex<double> coeff = in[k];
2019-03-27 14:12:34 +01:00
std::complex<double> om = 2.0i * M_PI * freq * time;
2019-03-26 15:33:47 +01:00
2019-03-27 14:12:34 +01:00
value += coeff * std::exp(om);
2019-03-26 15:33:47 +01:00
}
*out = std::real(value);
}
public:
DPHook(Path *p, Node *n, int fl, int prio, bool en = true)
: Hook(p, n, fl, prio, en), signal_name(nullptr), signal_index(0),
offset(0), inverse(0), f0(50.0), timestep(50e-6), time(), steps(0),
coeffs(), fharmonics(), fharmonics_len(0) {}
2019-03-26 15:33:47 +01:00
virtual ~DPHook() {
// Release memory
if (fharmonics)
delete fharmonics;
2019-03-26 15:33:47 +01:00
if (coeffs)
delete coeffs;
2019-03-26 15:33:47 +01:00
if (signal_name)
free(signal_name);
}
2019-03-26 15:33:47 +01:00
virtual void start() {
2019-06-23 16:13:23 +02:00
assert(state == State::PREPARED);
2019-03-26 15:33:47 +01:00
time = 0;
steps = 0;
2019-03-26 15:33:47 +01:00
for (int i = 0; i < fharmonics_len; i++)
coeffs[i] = 0;
2019-04-15 10:03:13 +02:00
window = dsp::Window<double>((1.0 / f0) / timestep, 0.0);
2019-06-23 16:13:23 +02:00
state = State::STARTED;
}
2021-02-16 14:15:14 +01:00
virtual void parse(json_t *json) {
int ret;
2019-03-26 15:33:47 +01:00
json_error_t err;
json_t *json_harmonics, *json_harmonic, *json_signal;
size_t i;
2021-02-16 14:15:14 +01:00
Hook::parse(json);
2019-03-26 15:33:47 +01:00
double rate = -1, dt = -1;
2021-02-16 14:15:14 +01:00
ret = json_unpack_ex(json, &err, 0,
"{ s: o, s: F, s?: F, s?: F, s: o, s?: b }", "signal",
2019-03-26 15:33:47 +01:00
&json_signal, "f0", &f0, "dt", &dt, "rate", &rate,
"harmonics", &json_harmonics, "inverse", &inverse);
if (ret)
2021-02-16 14:15:14 +01:00
throw ConfigError(json, err, "node-config-hook-dp");
2019-03-26 15:33:47 +01:00
if (rate > 0)
timestep = 1. / rate;
else if (dt > 0)
timestep = dt;
else
2021-02-16 14:15:14 +01:00
throw ConfigError(
json, "node-config-hook-dp",
"Either on of the settings 'dt' or 'rate' must be given");
2019-03-26 15:33:47 +01:00
if (!json_is_array(json_harmonics))
throw ConfigError(json_harmonics, "node-config-hook-dp-harmonics",
"Setting 'harmonics' must be a list of integers");
2019-03-26 15:33:47 +01:00
switch (json_typeof(json_signal)) {
case JSON_STRING:
signal_name = strdup(json_string_value(json_signal));
break;
2019-03-26 15:33:47 +01:00
case JSON_INTEGER:
2019-04-07 13:23:43 +02:00
signal_name = nullptr;
2019-03-26 15:33:47 +01:00
signal_index = json_integer_value(json_signal);
break;
default:
2019-03-26 15:33:47 +01:00
throw ConfigError(json_signal, "node-config-hook-dp-signal",
"Invalid value for setting 'signal'");
}
2019-03-26 15:33:47 +01:00
fharmonics_len = json_array_size(json_harmonics);
fharmonics = new int[fharmonics_len];
coeffs = new std::complex<double>[fharmonics_len];
if (!fharmonics || !coeffs)
throw MemoryAllocationError();
2019-03-26 15:33:47 +01:00
json_array_foreach(json_harmonics, i, json_harmonic) {
if (!json_is_integer(json_harmonic))
throw ConfigError(json_harmonic, "node-config-hook-dp-harmonics",
"Setting 'harmonics' must be a list of integers");
2019-03-26 15:33:47 +01:00
fharmonics[i] = json_integer_value(json_harmonic);
}
2019-06-23 16:13:23 +02:00
state = State::PARSED;
}
2019-03-26 15:33:47 +01:00
virtual void prepare() {
2019-06-23 16:13:23 +02:00
assert(state == State::CHECKED);
2019-03-26 15:33:47 +01:00
char *new_sig_name;
2019-06-23 16:13:23 +02:00
assert(state != State::STARTED);
2019-03-26 15:33:47 +01:00
if (signal_name) {
signal_index = signals->getIndexByName(signal_name);
2019-03-26 15:33:47 +01:00
if (signal_index < 0)
throw RuntimeError("Failed to find signal: {}", signal_name);
}
2019-03-26 15:33:47 +01:00
if (inverse) {
// Remove complex-valued coefficient signals
for (int i = 0; i < fharmonics_len; i++) {
auto orig_sig = signals->getByIndex(signal_index + i);
2019-03-26 15:33:47 +01:00
if (!orig_sig)
throw RuntimeError("Failed to find signal");
2019-06-23 16:13:23 +02:00
if (orig_sig->type != SignalType::COMPLEX)
throw RuntimeError("Signal is not complex");
2019-03-26 15:33:47 +01:00
2019-06-23 16:13:23 +02:00
signals->erase(signals->begin() + signal_index + i);
2019-03-26 15:33:47 +01:00
}
// Add new real-valued reconstructed signals
auto new_sig = std::make_shared<Signal>("dp", "idp", SignalType::FLOAT);
2019-03-26 15:33:47 +01:00
if (!new_sig)
throw RuntimeError("Failed to create signal");
signals->insert(signals->begin() + offset, new_sig);
} else {
auto orig_sig = signals->getByIndex(signal_index);
2019-03-26 15:33:47 +01:00
if (!orig_sig)
throw RuntimeError("Failed to find signal");
2019-06-23 16:13:23 +02:00
if (orig_sig->type != SignalType::FLOAT)
throw RuntimeError("Signal is not float");
signals->erase(signals->begin() + signal_index);
2019-03-26 15:33:47 +01:00
for (int i = 0; i < fharmonics_len; i++) {
new_sig_name = strf("%s_harm%d", orig_sig->name, i);
auto new_sig = std::make_shared<Signal>(new_sig_name, orig_sig->unit,
SignalType::COMPLEX);
2019-03-26 15:33:47 +01:00
if (!new_sig)
throw RuntimeError("Failed to create new signal");
signals->insert(signals->begin() + offset, new_sig);
}
}
2019-06-23 16:13:23 +02:00
state = State::PREPARED;
}
virtual Hook::Reason process(struct Sample *smp) {
2021-09-17 18:19:06 +02:00
if (signal_index >= smp->length)
2019-06-23 16:13:23 +02:00
return Hook::Reason::ERROR;
2019-03-26 15:33:47 +01:00
if (inverse) {
double signal;
std::complex<float> *coeffs =
reinterpret_cast<std::complex<float> *>(&smp->data[signal_index].z);
istep(coeffs, &signal);
sample_data_remove(smp, signal_index, fharmonics_len);
sample_data_insert(smp, (union SignalData *)&signal, offset, 1);
2019-03-26 15:33:47 +01:00
} else {
double signal = smp->data[signal_index].f;
std::complex<float> coeffs[fharmonics_len];
step(&signal, coeffs);
sample_data_remove(smp, signal_index, 1);
sample_data_insert(smp, (union SignalData *)coeffs, offset,
fharmonics_len);
2019-03-26 15:33:47 +01:00
}
time += timestep;
steps++;
2019-06-23 16:13:23 +02:00
return Reason::OK;
2019-03-26 15:33:47 +01:00
}
};
// Register hook
static char n[] = "dp";
static char d[] = "Transform to/from dynamic phasor domain";
static HookPlugin<DPHook, n, d,
(int)Hook::Flags::PATH | (int)Hook::Flags::NODE_READ |
(int)Hook::Flags::NODE_WRITE>
p;
2019-03-26 15:33:47 +01:00
} // namespace node
} // namespace villas