Advertisement
ripred

No-Array Running Averages!

Aug 7th, 2022 (edited)
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.76 KB | None | 0 0
  1. /*\
  2. |*| This code demonstrates how to calculate averages over an
  3. |*| incoming series of values without requiring an array of
  4. |*| any previous values and it also starts producing the average
  5. |*| immdiately for an N-sample-size window before N samples
  6. |*| have arrived.
  7. |*|
  8. |*| It also contains functions and methods to perform the same operations
  9. |*| using an N member array the old-fashioned way in order to be able to
  10. |*| compare the results of the two approaches.
  11. |*|
  12. |*| Original code (c) Trent M. Wyatt (ripred)
  13. \*/
  14.  
  15. #include <stdlib.h>
  16. #include <memory.h>
  17. #include <math.h>
  18.  
  19. // ========================================================================================
  20. // runtime parameters and test array data for comparison
  21.  
  22. // set to  true to randomly drift the samples up and down
  23. // set to false to select purely random sample values
  24. bool drift = true;
  25.  
  26. // running average set size
  27. size_t const  set_size = 101;
  28.  
  29. // number of samples to test
  30. size_t const test_size = 100;
  31.  
  32. // amount to scale up/down when drifting
  33. long scale = 1;
  34.  
  35. // variables and functions used for brute force averaging comparison
  36. double   val_set[set_size];
  37. double   val_total;
  38. double   val_avg;
  39. size_t   val_num;
  40. size_t   head;
  41. size_t   tail;
  42. // ========================================================================================
  43.  
  44. // variables used for array-less averaging
  45. volatile double avg;
  46. size_t count;
  47.  
  48. // standard deviation
  49. double deviation;
  50.  
  51. // ==============================================================
  52.  
  53. #ifndef UNUSED
  54. #define UNUSED(A) do{(void)(A);}while(false);
  55. #endif
  56.  
  57. inline long min(const long &a, const long &b) {
  58.     if (a < b) return a;
  59.     return b;
  60. }
  61.  
  62. inline long max(const long &a, const long &b) {
  63.     if (a > b) return a;
  64.     return b;
  65. }
  66.  
  67. /// Fake Serial port methods for use on a PC when writing,
  68. /// testing, and using this code.
  69.  
  70. #ifndef ARDUINO
  71. #include <iostream>
  72. struct Serial {
  73.     static void print(double r, int prec=2) {
  74.         char buff[32];
  75.         sprintf(buff, "%.*f", prec, r);
  76.         std::cout << buff;
  77.     }
  78.  
  79.     static void println(double r, int prec=2) {
  80.         char buff[32];
  81.         sprintf(buff, "%.*f\n", prec, r);
  82.         std::cout << buff;
  83.     }
  84.  
  85.     static void print(const char *s) {
  86.         std::cout << s;
  87.     }
  88.  
  89.     static void println(const char *s="") {
  90.         std::cout << s << "\n";
  91.     }
  92.  
  93.     static void print(char *s) {
  94.         std::cout << s;
  95.     }
  96.  
  97.     static void println(char *s) {
  98.         std::cout << s << "\n";
  99.     }
  100.  
  101. } Serial;
  102.  
  103. long random(unsigned long min, unsigned long max) {
  104.     return min + (rand() % max);
  105. }
  106. long random(unsigned long max) {
  107.     return rand() % max;
  108. }
  109. void randomSeed(unsigned long seed) {
  110.     srand(seed);
  111. }
  112. #endif // end of fake PC-side proxy functions, classes, and methods
  113.  
  114. // ========================================================================================
  115. // This is code for the inefficient way of calculating an average for
  116. // the last N samples of a series of incoming values.
  117.  
  118. // functions used for brute force averaging
  119.  
  120. // add a new value to the sample array
  121. double val_add(double val) {
  122.     val_total += val;
  123.     val_set[head++] = val;
  124.     head %= set_size;
  125.     if (++val_num >= set_size) {
  126.         val_total -= val_set[tail++];
  127.         tail %= set_size;
  128.     }
  129.  
  130.     val_avg = val_total / ((val_num >= set_size) ? set_size : val_num);
  131.  
  132.     return val_avg;
  133. }
  134.  
  135.  
  136. // calculate the average value for the sample array
  137. double val_calc() {
  138.     return val_avg;
  139. }
  140.  
  141. // add a new value to the array-less smoothing
  142. double val_add_smooth(double val) {
  143.     size_t num = ++count;
  144.     if (num > set_size) {
  145.         num = set_size;
  146.     }
  147.  
  148.     double run_coef = double(num - 1) / num;
  149.     double val_coef = 1.0 / num;
  150.  
  151.     avg = (avg * run_coef) + (val * val_coef);
  152.  
  153.     return avg;
  154. }
  155.  
  156. double val_calc_smooth() {
  157.     return avg;
  158. }
  159.  
  160.  
  161. void restart(uint32_t seed) {
  162.     // initialize the brute force variables
  163.     for (size_t i=0; i < set_size; i++) {
  164.         val_set[i] = drift ? 50.0 : 0.0;
  165.     }
  166.     val_avg = drift ? 50.0 : 0.0;
  167.     val_num = 0;
  168.     head = 0;
  169.     tail = 0;
  170.  
  171.     // initialize the array-less variables
  172.     avg = drift ? 50.0 : 0.0;
  173.     count = 0;
  174.  
  175.     randomSeed(seed);
  176. }
  177.  
  178.  
  179. double rnd_sample() {
  180.     long val = 0;
  181.  
  182.     if (drift) {
  183.         long incr = random(3) - 1;    // -1 / 0 / +1
  184.         val = val_avg + (incr * scale);
  185.         val = (val <  0) ?  0 : val;
  186.         val = (val > 99) ? 99 : val;
  187.     }
  188.     else {
  189.         val = random(100);
  190.     }
  191.  
  192.     return val;
  193. }
  194.  
  195.  
  196. void run_test(uint32_t seed) {
  197.     char buff[64];
  198.  
  199.     Serial.println("Value, Array, NoArray");
  200.  
  201.     restart(seed);
  202.     for (size_t i=0; i < test_size; i++) {
  203.         double val = rnd_sample();
  204.  
  205.         val_add(val);
  206.         val_add_smooth(val);
  207.  
  208.         sprintf(buff, "%6.2f %6.2f %6.2f", val, val_avg, avg);
  209.         Serial.println(buff);
  210.     }
  211.  
  212.     // Calculate the standard deviation:
  213.  
  214.     // 1) get mean average
  215.     double meanAvg = val_total / test_size;
  216.  
  217.     // 2) get squared average
  218.     double meanSqr = 0.0;
  219.     randomSeed(seed);
  220.     for (size_t i=0; i < test_size; i++) {
  221.         double val = rnd_sample() - meanAvg;
  222.         meanSqr += val * val;
  223.     }
  224.  
  225.     // 3) get mean of the squared differences
  226.     meanSqr /= test_size;
  227.  
  228.     // 4) get the square root
  229.     deviation = sqrt(meanSqr);
  230. }
  231.  
  232.  
  233.  
  234. int main(int argc, char *argv[]) {
  235.     UNUSED(argc);
  236.     UNUSED(argv);
  237.  
  238.     run_test(time(nullptr));
  239.  
  240.     Serial.println();
  241.  
  242.     Serial.print("   Brute Force Avg: ");
  243.     Serial.println(val_avg, 5);
  244.  
  245.     Serial.print("        Smooth Avg: ");
  246.     Serial.println(    avg, 5);
  247.  
  248.     Serial.print("Standard Deviation: ");
  249.     Serial.println(deviation, 5);
  250.  
  251.     return 0;
  252. }
  253.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement