summaryrefslogtreecommitdiffstats
path: root/web/api/queries/des/des.c
blob: a6c4e4051d3fc00ae70e12c454b09b9c99517582 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-3.0-or-later

#include <web/api/queries/rrdr.h>
#include "des.h"


// ----------------------------------------------------------------------------
// single exponential smoothing

struct grouping_des {
    NETDATA_DOUBLE alpha;
    NETDATA_DOUBLE alpha_other;
    NETDATA_DOUBLE beta;
    NETDATA_DOUBLE beta_other;

    NETDATA_DOUBLE level;
    NETDATA_DOUBLE trend;

    size_t count;
};

static size_t max_window_size = 15;

void grouping_init_des(void) {
    long long ret = config_get_number(CONFIG_SECTION_WEB, "des max window", (long long)max_window_size);
    if(ret <= 1) {
        config_set_number(CONFIG_SECTION_WEB, "des max window", (long long)max_window_size);
    }
    else {
        max_window_size = (size_t) ret;
    }
}

static inline NETDATA_DOUBLE window(RRDR *r, struct grouping_des *g) {
    (void)g;

    NETDATA_DOUBLE points;
    if(r->group == 1) {
        // provide a running DES
        points = (NETDATA_DOUBLE)r->internal.points_wanted;
    }
    else {
        // provide a SES with flush points
        points = (NETDATA_DOUBLE)r->group;
    }

    // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
    // A commonly used value for alpha is 2 / (N + 1)
    return (points > (NETDATA_DOUBLE)max_window_size) ? (NETDATA_DOUBLE)max_window_size : points;
}

static inline void set_alpha(RRDR *r, struct grouping_des *g) {
    // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
    // A commonly used value for alpha is 2 / (N + 1)

    g->alpha = 2.0 / (window(r, g) + 1.0);
    g->alpha_other = 1.0 - g->alpha;

    //info("alpha for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->alpha);
}

static inline void set_beta(RRDR *r, struct grouping_des *g) {
    // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
    // A commonly used value for alpha is 2 / (N + 1)

    g->beta = 2.0 / (window(r, g) + 1.0);
    g->beta_other = 1.0 - g->beta;

    //info("beta for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->beta);
}

void grouping_create_des(RRDR *r, const char *options __maybe_unused) {
    struct grouping_des *g = (struct grouping_des *)onewayalloc_mallocz(r->internal.owa, sizeof(struct grouping_des));
    set_alpha(r, g);
    set_beta(r, g);
    g->level = 0.0;
    g->trend = 0.0;
    g->count = 0;
    r->internal.grouping_data = g;
}

// resets when switches dimensions
// so, clear everything to restart
void grouping_reset_des(RRDR *r) {
    struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;
    g->level = 0.0;
    g->trend = 0.0;
    g->count = 0;

    // fprintf(stderr, "\nDES: ");

}

void grouping_free_des(RRDR *r) {
    onewayalloc_freez(r->internal.owa, r->internal.grouping_data);
    r->internal.grouping_data = NULL;
}

void grouping_add_des(RRDR *r, NETDATA_DOUBLE value) {
    struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;

    if(likely(g->count > 0)) {
        // we have at least a number so far

        if(unlikely(g->count == 1)) {
            // the second value we got
            g->trend = value - g->trend;
            g->level = value;
        }

        // for the values, except the first
        NETDATA_DOUBLE last_level = g->level;
        g->level = (g->alpha * value) + (g->alpha_other * (g->level + g->trend));
        g->trend = (g->beta * (g->level - last_level)) + (g->beta_other * g->trend);
    }
    else {
        // the first value we got
        g->level = g->trend = value;
    }

    g->count++;

    //fprintf(stderr, "value: " CALCULATED_NUMBER_FORMAT ", level: " CALCULATED_NUMBER_FORMAT ", trend: " CALCULATED_NUMBER_FORMAT "\n", value, g->level, g->trend);
}

NETDATA_DOUBLE grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
    struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;

    if(unlikely(!g->count || !netdata_double_isnumber(g->level))) {
        *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
        return 0.0;
    }

    //fprintf(stderr, " RESULT for %zu values = " CALCULATED_NUMBER_FORMAT " \n", g->count, g->level);

    return g->level;
}