ses.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "ses.h"
  3. // ----------------------------------------------------------------------------
  4. // single exponential smoothing
  5. struct grouping_ses {
  6. calculated_number alpha;
  7. calculated_number alpha_other;
  8. calculated_number level;
  9. size_t count;
  10. };
  11. static size_t max_window_size = 15;
  12. void grouping_init_ses(void) {
  13. long long ret = config_get_number(CONFIG_SECTION_WEB, "ses max window", (long long)max_window_size);
  14. if(ret <= 1) {
  15. config_set_number(CONFIG_SECTION_WEB, "ses max window", (long long)max_window_size);
  16. }
  17. else {
  18. max_window_size = (size_t) ret;
  19. }
  20. }
  21. static inline calculated_number window(RRDR *r, struct grouping_ses *g) {
  22. (void)g;
  23. calculated_number points;
  24. if(r->group == 1) {
  25. // provide a running DES
  26. points = r->internal.points_wanted;
  27. }
  28. else {
  29. // provide a SES with flush points
  30. points = r->group;
  31. }
  32. return (points > max_window_size) ? max_window_size : points;
  33. }
  34. static inline void set_alpha(RRDR *r, struct grouping_ses *g) {
  35. // https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
  36. // A commonly used value for alpha is 2 / (N + 1)
  37. g->alpha = 2.0 / (window(r, g) + 1.0);
  38. g->alpha_other = 1.0 - g->alpha;
  39. }
  40. void grouping_create_ses(RRDR *r) {
  41. struct grouping_ses *g = (struct grouping_ses *)callocz(1, sizeof(struct grouping_ses));
  42. set_alpha(r, g);
  43. g->level = 0.0;
  44. r->internal.grouping_data = g;
  45. }
  46. // resets when switches dimensions
  47. // so, clear everything to restart
  48. void grouping_reset_ses(RRDR *r) {
  49. struct grouping_ses *g = (struct grouping_ses *)r->internal.grouping_data;
  50. g->level = 0.0;
  51. g->count = 0;
  52. }
  53. void grouping_free_ses(RRDR *r) {
  54. freez(r->internal.grouping_data);
  55. r->internal.grouping_data = NULL;
  56. }
  57. void grouping_add_ses(RRDR *r, calculated_number value) {
  58. struct grouping_ses *g = (struct grouping_ses *)r->internal.grouping_data;
  59. if(unlikely(!g->count))
  60. g->level = value;
  61. g->level = g->alpha * value + g->alpha_other * g->level;
  62. g->count++;
  63. }
  64. calculated_number grouping_flush_ses(RRDR *r, RRDR_VALUE_FLAGS *rrdr_value_options_ptr) {
  65. struct grouping_ses *g = (struct grouping_ses *)r->internal.grouping_data;
  66. if(unlikely(!g->count || !calculated_number_isnumber(g->level))) {
  67. *rrdr_value_options_ptr |= RRDR_VALUE_EMPTY;
  68. return 0.0;
  69. }
  70. return g->level;
  71. }