You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

129 lines
5.6 KiB

  1. // Copyright 2021 Victor Toni (@vitoni)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "utils.h"
  4. #include <lib/lib8tion/lib8tion.h>
  5. /**
  6. * @brief Changes `*value` to `new_value`.
  7. * @param[in,out] value Pointer to variable to be changed.
  8. * @param[in] new_value Value to be changed.
  9. * @param[in,out] changed Flag indicating `*value` and `new_value` were different.
  10. */
  11. void update_value(uint8_t *value, const uint8_t new_value, bool *changed) {
  12. if (new_value != (*value)) {
  13. (*changed) = true;
  14. (*value) = new_value;
  15. }
  16. }
  17. /**
  18. * @brief Checks whether a value is in the given range.
  19. * @param[in] value Value to be checked.
  20. * @param[in] range_min Lower bound of range (inclusive).
  21. * @param[in] range_max Upper bound of range (inclusive).
  22. * @return `true` if (range_min <= value <= range_max), `false` otherwise
  23. */
  24. bool in_range(const uint8_t value, const uint8_t range_min, const uint8_t range_max) {
  25. return range_min <= value && value <= range_max;
  26. }
  27. /**
  28. * @brief Calculates the sine value based on sin8() and scales it to the given range (unsigned).
  29. *
  30. * Table of values for unscaled sin8() eg. a theta of 0 results to 128 and a theta of 255 (240+15) results to 125.
  31. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
  32. +----------------------------------------------------------------
  33. 0: 128 131 134 137 140 143 146 149 152 155 158 161 164 167 170 173
  34. 16: 177 179 182 184 187 189 192 194 197 200 202 205 207 210 212 215
  35. 32: 218 219 221 223 224 226 228 229 231 233 234 236 238 239 241 243
  36. 48: 245 245 246 246 247 248 248 249 250 250 251 251 252 253 253 254
  37. 64: 255 254 253 253 252 251 251 250 250 249 248 248 247 246 246 245
  38. 80: 245 243 241 239 238 236 234 233 231 229 228 226 224 223 221 219
  39. 96: 218 215 212 210 207 205 202 200 197 194 192 189 187 184 182 179
  40. 112: 177 173 170 167 164 161 158 155 152 149 146 143 140 137 134 131
  41. 128: 128 125 122 119 116 113 110 107 104 101 98 95 92 89 86 83
  42. 144: 79 77 74 72 69 67 64 62 59 56 54 51 49 46 44 41
  43. 160: 38 37 35 33 32 30 28 27 25 23 22 20 18 17 15 13
  44. 176: 11 11 10 10 9 8 8 7 6 6 5 5 4 3 3 2
  45. 192: 1 2 3 3 4 5 5 6 6 7 8 8 9 10 10 11
  46. 208: 11 13 15 17 18 20 22 23 25 27 28 30 32 33 35 37
  47. 224: 38 41 44 46 49 51 54 56 59 62 64 67 69 72 74 77
  48. 240: 79 83 86 89 92 95 98 101 104 107 110 113 116 119 122 125
  49. *
  50. * @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation.
  51. * @param[in] range_min Lower bound of range (inclusive).
  52. * @param[in] range_max Upper bound of range (inclusive).
  53. * @return Calculated sine value mapped to the given range.
  54. */
  55. uint8_t scaled_sin(const uint8_t theta, const uint8_t range_min, const uint8_t range_max) {
  56. const uint8_t range = range_max - range_min;
  57. return scale8(sin8(theta), range) + range_min;
  58. }
  59. /**
  60. * @brief Increases the given value until reaching range_max.
  61. * The increments occur following an upwards sine wave (scaled from range_min to range_max).
  62. * @param[in] theta Angle (a full circle mapped to 0-255) used as input for sine calculation.
  63. * @param[in] range_min Lower bound of range (inclusive).
  64. * @param[in] range_max Upper bound of range (inclusive).
  65. * @param[in] max_delta Maximum delta between value and range_max (due to values being integers and eventually not fully matching).
  66. * @param[in,out] value Reference of variable to be increased
  67. * @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise
  68. * @see scaled_sin()
  69. */
  70. bool scaled_sin_up(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value) {
  71. // ensure upper range bound
  72. if (range_max <= (*value)) {
  73. (*value) = range_max;
  74. return true;
  75. }
  76. const uint8_t new_value = scaled_sin(theta, range_min, range_max);
  77. if (in_range(new_value, range_min, range_max) && (*value) < new_value) {
  78. (*value) = new_value;
  79. return range_max == (*value);
  80. }
  81. const uint8_t delta = range_max - (*value);
  82. if (delta <= max_delta) {
  83. (*value) = range_max;
  84. }
  85. return delta <= max_delta;
  86. }
  87. /**
  88. * @brief Decreases the given value until reaching range_min.
  89. * The decrements occur following an downwards sinus wave (scaled from range_min to range_max).
  90. * @param[in] theta Angle (a full circle mapped to 0-255) used as input for sinus calculation.
  91. * @param[in] range_min Lower bound of range (inclusive).
  92. * @param[in] range_max Upper bound of range (inclusive).
  93. * @param[in] max_delta Maximum delta between value and range_min (due to values being integers and eventually not fully matching).
  94. * @param[in,out] value Reference of variable to be decreased
  95. * @return `true` if value and range_max are within a delta of 3 (chosen by fair dice rolling), `false` otherwise
  96. * @see scaled_sin()
  97. */
  98. bool scaled_sin_down(const uint8_t theta, const uint8_t range_min, const uint8_t range_max, const uint8_t max_delta, uint8_t *value) {
  99. // ensure lower range bound
  100. if ((*value) <= range_min) {
  101. (*value) = range_min;
  102. return true;
  103. }
  104. const uint8_t new_value = scaled_sin(theta, range_min, range_max);
  105. if (in_range(new_value, range_min, range_max) && new_value < (*value)) {
  106. (*value) = new_value;
  107. return range_min == (*value);
  108. }
  109. const uint8_t delta = (*value) - range_min;
  110. if (delta <= max_delta) {
  111. (*value) = range_min;
  112. }
  113. return delta <= max_delta;
  114. }