des.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include <web/api/queries/rrdr.h>
  3. #include "des.h"
  4. // ----------------------------------------------------------------------------
  5. // single exponential smoothing
  6. struct grouping_des {
  7. calculated_number alpha;
  8. calculated_number alpha_other;
  9. calculated_number beta;
  10. calculated_number beta_other;
  11. calculated_number level;
  12. calculated_number trend;
  13. size_t count;
  14. };
  15. static size_t max_window_size = 15;
  16. void grouping_init_des(void) {
  17. long long ret = config_get_number(CONFIG_SECTION_WEB, "des max window", (long long)max_window_size);
  18. if(ret <= 1) {
  19. config_set_number(CONFIG_SECTION_WEB, "des max window", (long long)max_window_size);
  20. }
  21. else {
  22. max_window_size = (size_t) ret;
  23. }
  24. }
  25. static inline calculated_number window(RRDR *r, struct grouping_des *g) {
  26. (void)g;
  27. calculated_number points;
  28. if(r->group == 1) {
  29. // provide a running DES
  30. points = r->internal.points_wanted;
  31. }
  32. else {
  33. // provide a SES with flush points
  34. points = r->group;
  35. }
  36. // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  37. // A commonly used value for alpha is 2 / (N + 1)
  38. return (points > max_window_size) ? max_window_size : points;
  39. }
  40. static inline void set_alpha(RRDR *r, struct grouping_des *g) {
  41. // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  42. // A commonly used value for alpha is 2 / (N + 1)
  43. g->alpha = 2.0 / (window(r, g) + 1.0);
  44. g->alpha_other = 1.0 - g->alpha;
  45. //info("alpha for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->alpha);
  46. }
  47. static inline void set_beta(RRDR *r, struct grouping_des *g) {
  48. // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  49. // A commonly used value for alpha is 2 / (N + 1)
  50. g->beta = 2.0 / (window(r, g) + 1.0);
  51. g->beta_other = 1.0 - g->beta;
  52. //info("beta for chart '%s' is " CALCULATED_NUMBER_FORMAT, r->st->name, g->beta);
  53. }
  54. void grouping_create_des(RRDR *r) {
  55. struct grouping_des *g = (struct grouping_des *)mallocz(sizeof(struct grouping_des));
  56. set_alpha(r, g);
  57. set_beta(r, g);
  58. g->level = 0.0;
  59. g->trend = 0.0;
  60. g->count = 0;
  61. r->internal.grouping_data = g;
  62. }
  63. // resets when switches dimensions
  64. // so, clear everything to restart
  65. void grouping_reset_des(RRDR *r) {
  66. struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;
  67. g->level = 0.0;
  68. g->trend = 0.0;
  69. g->count = 0;
  70. // fprintf(stderr, "\nDES: ");
  71. }
  72. void grouping_free_des(RRDR *r) {
  73. freez(r->internal.grouping_data);
  74. r->internal.grouping_data = NULL;
  75. }
  76. void grouping_add_des(RRDR *r, calculated_number value) {
  77. struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;
  78. if(likely(g->count > 0)) {
  79. // we have at least a number so far
  80. if(unlikely(g->count == 1)) {
  81. // the second value we got
  82. g->trend = value - g->trend;
  83. g->level = value;
  84. }
  85. // for the values, except the first
  86. calculated_number last_level = g->level;
  87. g->level = (g->alpha * value) + (g->alpha_other * (g->level + g->trend));
  88. g->trend = (g->beta * (g->level - last_level)) + (g->beta_other * g->trend);
  89. }
  90. else {
  91. // the first value we got
  92. g->level = g->trend = value;
  93. }
  94. g->count++;
  95. //fprintf(stderr, "value: " CALCULATED_NUMBER_FORMAT ", level: " CALCULATED_NUMBER_FORMAT ", trend: " CALCULATED_NUMBER_FORMAT "\n", value, g->level, g->trend);
  96. }
  97. calculated_number grouping_flush_des(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  98. struct grouping_des *g = (struct grouping_des *)r->internal.grouping_data;
  99. if(unlikely(!g->count || !calculated_number_isnumber(g->level))) {
  100. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  101. return 0.0;
  102. }
  103. //fprintf(stderr, " RESULT for %zu values = " CALCULATED_NUMBER_FORMAT " \n", g->count, g->level);
  104. return g->level;
  105. }