├── .arduino-ci.yml ├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── arduino_test_runner.yml │ └── jsoncheck.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── hist_array │ └── hist_array.ino ├── hist_find_performance │ ├── AVR_UNO.txt │ └── hist_find_performance.ino ├── hist_pointer │ └── hist_pointer.ino ├── hist_test │ └── hist_test.ino ├── hist_test_big │ └── hist_test_big.ino ├── hist_test_cdf │ └── hist_test_cdf.ino ├── hist_test_graph │ └── hist_test_graph.ino ├── hist_test_level │ └── hist_test_level.ino ├── hist_test_performance │ ├── hist_test_performance.ino │ ├── performance_0.3.2.txt │ └── performance_0.3.3.txt ├── hist_test_pmf │ └── hist_test_pmf.ino └── hist_test_val │ └── hist_test_val.ino ├── histogram.cpp ├── histogram.h ├── keywords.txt ├── library.json ├── library.properties └── test └── unit_test_001.cpp /.arduino-ci.yml: -------------------------------------------------------------------------------- 1 | platforms: 2 | rpipico: 3 | board: rp2040:rp2040:rpipico 4 | package: rp2040:rp2040 5 | gcc: 6 | features: 7 | defines: 8 | - ARDUINO_ARCH_RP2040 9 | warnings: 10 | flags: 11 | 12 | packages: 13 | rp2040:rp2040: 14 | url: https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 15 | 16 | compile: 17 | # Choosing to run compilation tests on 2 different Arduino platforms 18 | platforms: 19 | - uno 20 | # - due 21 | # - zero 22 | # - leonardo 23 | - m4 24 | - esp32 25 | # - esp8266 26 | # - mega2560 27 | - rpipico 28 | libraries: 29 | - "PrintHelpers" 30 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /.github/workflows/arduino-lint.yml: -------------------------------------------------------------------------------- 1 | name: Arduino-lint 2 | 3 | on: [push, pull_request] 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | timeout-minutes: 5 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: arduino/arduino-lint-action@v1 11 | with: 12 | library-manager: update 13 | compliance: strict -------------------------------------------------------------------------------- /.github/workflows/arduino_test_runner.yml: -------------------------------------------------------------------------------- 1 | name: Arduino CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | runTest: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 20 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: 2.6 15 | - run: | 16 | gem install arduino_ci 17 | arduino_ci.rb 18 | -------------------------------------------------------------------------------- /.github/workflows/jsoncheck.yml: -------------------------------------------------------------------------------- 1 | name: JSON check 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.json' 7 | pull_request: 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 5 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: json-syntax-check 16 | uses: limitusus/json-syntax-check@v2 17 | with: 18 | pattern: "\\.json$" -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log Histogram 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | 9 | ## [0.3.7] - 2024-05-25 10 | - add examples **hist_pointer.ino** and **hist_array.ino** 11 | - issue #10 spin off library https://github.com/RobTillaart/Kurtosis 12 | - minor edits 13 | 14 | ## [0.3.6] - 2024-01-03 15 | - add **float saturation()** 16 | - fix examples 17 | - minor edits 18 | 19 | ## [0.3.5] - 2023-11-04 20 | - update readme.md 21 | - minor fix in changelog.md 22 | - update keywords.txt 23 | 24 | ## [0.3.4] - 2023-06-30 25 | - change return type **uint8_t clear()**, return status bucket 26 | - change return type **uint8_t setBucket(value)**, return status bucket 27 | - change return type **uint8_t add(value)**, return status bucket 28 | - change return type **uint8_t sub(value)**, return status bucket 29 | - add **uint8_t status()** last known status. 30 | - add **int32_t sum()**, adds up all buckets. 31 | - made **setBucket()** virtual. 32 | - update / clean up readme.md 33 | - minor improvements derived classes - still experimental 34 | 35 | ## [0.3.3] - 2023-02-21 36 | - optimize loops 37 | - update readme.md 38 | - update GitHub actions 39 | - update license 2023 40 | - minor edits 41 | 42 | ## [0.3.2] - 2022-11-09 43 | - add changelog.md 44 | - add rp2040 to build-CI 45 | - update readme.md 46 | - clean-up code 47 | - update documentation 48 | 49 | ## [0.3.1] - 2021-12-19 50 | - update library.json 51 | - update license, 52 | - minor edits 53 | 54 | ## [0.3.0] - 2021-11-02 55 | - update build-CI 56 | - add badges 57 | - refactor readability 58 | - add parameter for clear(value = 0) 59 | - add findMin(), findMax() 60 | - add countAbove(), countLevel(), countBelow(). 61 | - add setBucket(), 62 | - change length to uint16_t ==> 65534 63 | 64 | ---- 65 | 66 | ## [0.2.1] - 2020-12-24 67 | - add Arduino-CI + unit tests 68 | 69 | ## [0.2.0] - 2020-06-12 70 | - #pragma once 71 | - removed pre 1.0 support 72 | 73 | ---- 74 | 75 | ## [0.1.6] - 2017-07-27 76 | - revert double to float (issue #33) 77 | 78 | ## [0.1.5] - 2017-07-16 79 | - refactor 80 | - support for > 256 buckets 81 | - prevent alloc() errors 82 | 83 | ## [0.1.4] - 2015-03-06 84 | - stricter interface 85 | 86 | ## [0.1.3] - 2013-09-29 87 | - testing a lot 88 | - refactoring 89 | 90 | ## [0.1.2] - 2012-12-23 91 | - changed float to double 92 | - some comments 93 | 94 | ## [0.1.1] - 2012-11-10 95 | - added PMF() and CDF() 96 | 97 | ## [0.1.0] - 2012-11-10 98 | - initial version 99 | 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2024 Rob Tillaart 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/Histogram/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/Histogram/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/Histogram/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/Histogram/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/Histogram/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/Histogram.svg)](https://github.com/RobTillaart/Histogram/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/Histogram/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/Histogram.svg?maxAge=3600)](https://github.com/RobTillaart/Histogram/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/Histogram.svg)](https://registry.platformio.org/libraries/robtillaart/Histogram) 10 | 11 | 12 | # Histogram 13 | 14 | Arduino library for creating histograms math. 15 | 16 | 17 | ## Description 18 | 19 | One of the main applications for the Arduino board is reading and logging of sensor data. 20 | We often want to make a histogram of this data to get insight of the distribution of the 21 | measurements. This is where this Histogram library comes in. 22 | 23 | The Histogram distributes the values added to it into buckets and keeps count per bucket. 24 | 25 | If you need more quantitative analysis, you might need the statistics library, 26 | - https://github.com/RobTillaart/Statistic 27 | 28 | 29 | #### Related 30 | 31 | - https://github.com/RobTillaart/Correlation 32 | - https://github.com/RobTillaart/GST - Golden standard test metrics 33 | - https://github.com/RobTillaart/Histogram 34 | - https://github.com/RobTillaart/Kurtosis 35 | - https://github.com/RobTillaart/RunningAngle 36 | - https://github.com/RobTillaart/RunningAverage 37 | - https://github.com/RobTillaart/RunningMedian 38 | - https://github.com/RobTillaart/statHelpers - combinations & permutations 39 | - https://github.com/RobTillaart/Statistic 40 | 41 | 42 | #### Working 43 | 44 | When the class is initialized an array of the boundaries to define the borders of the 45 | buckets is passed to the constructor. This array should be declared global as the 46 | Histogram class does not copy the values to keep memory usage low. This allows to change 47 | the boundaries runtime, so after a **clear()**, a new Histogram can be created. 48 | 49 | The values in the boundary array do not need to be equidistant (equal in size) 50 | but they need to be in ascending order. 51 | 52 | Internally the library does not record the individual values, only the count per bucket. 53 | If a new value is added - **add(value)** - the class checks in which bucket it 54 | belongs and the buckets counter is increased. 55 | 56 | The **sub(value)** function is used to decrease the count of a bucket and it can 57 | cause the count to become below zero. 58 | Although seldom used but still depending on the application it can be useful. 59 | E.g. when you want to compare two value generating streams, you let 60 | one stream **add()** and the other **sub()**. If the histogram of both streams is 61 | similar they should cancel each other out (more or less), and the value of all buckets 62 | should be around 0. \[not tried\]. 63 | 64 | The **frequency()** function may be removed to reduce footprint as it can be calculated 65 | with the formula **(1.0 \* bucket(i))/count()**. 66 | 67 | 68 | #### Experimental: Histogram8 Histogram16 69 | 70 | Histogram8 and Histogram16 are derived classes with same interface but smaller buckets. 71 | Histogram can count to ± 2^31 while often ± 2^15 or even ± 2^7 is sufficient. 72 | Saves substantial memory. 73 | 74 | | class name | length | count/bucket | max memory | 75 | |:--------------|---------:|---------------:|-------------:| 76 | | Histogram | 65534 | ± 2147483647 | 260 KB | 77 | | Histogram8 | 65534 | ± 127 | 65 KB | 78 | | Histogram16 | 65534 | ± 32767 | 130 KB | 79 | 80 | 81 | The difference is the **\_data** array, to reduce the memory footprint. 82 | 83 | Note: max memory is without the boundary array. 84 | 85 | Performance optimizations are possible too however not essential for 86 | the experimental version. 87 | 88 | 89 | ## Interface 90 | 91 | ```cpp 92 | #include "histogram.h" 93 | ``` 94 | 95 | #### Constructor 96 | 97 | - **Histogram(uint16_t length, float \*bounds)** constructor, get an array of boundary values and array length. 98 | Length should be less than 65534. 99 | - **Histogram8(uint16_t length, float \*bounds)** idem as above. 100 | - **Histogram16(uint16_t length, float \*bounds)** idem as above. 101 | - **~Histogram()** destructor. 102 | - **~Histogram8()** destructor. 103 | - **~Histogram16()** destructor. 104 | 105 | 106 | #### MaxBucket 107 | 108 | Default the maxBucket size is defined as 255 (8 bit), 65535 (16 bit) or 109 | 2147483647 (32 bit) depending on class used. 110 | The functions below allow to set and get the maxBucket so the **add()** and 111 | **sub()** function will reach **FULL** faster. 112 | Useful in some applications e.g. games. 113 | 114 | - **void setMaxBucket(uint32_t value)** to have a user defined maxBucket level e.g 25 115 | - **uint32_t getMaxBucket()** returns the current maxBucket. 116 | 117 | Please note it makes no sense to set maxBucket to a value larger than 118 | the histogram type can handle. 119 | Setting maxBucket to 300 for **Histogram8** will always fail as data can only 120 | handle values between 0 .. 255. 121 | 122 | 123 | #### Base 124 | 125 | - **uint8_t clear(float value = 0)** reset all bucket counters to value (default 0). 126 | Returns status, see below. 127 | - **uint8_t setBucket(const uint16_t index, int32_t value = 0)** store / overwrite a value of bucket. 128 | Returns status, see below. 129 | - **uint8_t add(float value)** add a value, increase count of bucket. 130 | Returns status, see below. 131 | - **uint8_t sub(float value)** 'add' a value, decrease (subtract) count of bucket. 132 | This is less used and has some side effects, see **frequency()**. 133 | Returns status, see below. 134 | 135 | 136 | | Status | Value | Description | 137 | |:------------------:|:-------:|:------------:| 138 | | HISTO_OK | 0x00 | all is well 139 | | HISTO_FULL | 0x01 | add() / sub() caused bucket full ( + or - ) 140 | | HISTO_ERR_FULL | 0xFF | cannot add() / sub(), overflow / underflow 141 | | HISTO_ERR_LENGTH | 0xFE | length = 0 error (constructor) 142 | 143 | 144 | - **uint16_t size()** returns number of buckets. 145 | - **uint32_t count()** returns total number of values added (or subtracted). 146 | - **int32_t bucket(uint16_t index)** returns the count of single bucket. 147 | Can be negative if one uses **sub()** 148 | - **float frequency(uint16_t index)** returns the relative frequency of a bucket. 149 | This is always between -1.0 and 1.0. 150 | 151 | Some notes about **frequency()** 152 | - can return a negative value if an application uses **sub()** 153 | - sum of all buckets will not add up to 1.0 if one uses **sub()** 154 | - value (and thus sum) will deviate if **HISTO_ERR_FULL** has occurred. 155 | 156 | 157 | #### Helper functions 158 | 159 | - **uint16_t find(float value)** returns the index of the bucket for value. 160 | - **uint16_t findMin()** returns the (first) index of the bucket with the minimum value. 161 | - **uint16_t findMax()** returns the (first) index of the bucket with the maximum value. 162 | - **uint16_t countLevel(int32_t level)** returns the number of buckets with exact that level (count). 163 | - **uint16_t countAbove(int32_t level)** returns the number of buckets above level. 164 | - **uint16_t countBelow(int32_t level)** returns the number of buckets below level. 165 | 166 | 167 | #### Probability Distribution Functions 168 | 169 | There are three experimental functions: 170 | 171 | - **float PMF(float value)** Probability Mass Function. 172 | Quite similar to **frequency()**, but uses a value as parameter. 173 | - **float CDF(float value)** Cumulative Distribution Function. 174 | Returns the sum of frequencies <= value. Always between 0.0 and 1.0. 175 | - **float VAL(float probability)** Value Function, is **CDF()** inverted. 176 | Returns the value of the original array for which the CDF is at least probability. 177 | - **int32_t sum()** returns the sum of all buckets. (not experimental). 178 | Just as with **frequency()** it is affected by the use of **sub()**, 179 | including returning a negative value. 180 | 181 | As most Arduino sketches typical uses a small number of buckets these functions 182 | are quite coarse and/or inaccurate, so indicative at best. 183 | Linear interpolation within "last" bucket needs to be investigated, however it 184 | introduces its own uncertainty. Alternative is to add last box for 50%. 185 | 186 | Note **PDF()** is a continuous function and therefore not applicable in a discrete histogram. 187 | 188 | 189 | - https://en.wikipedia.org/wiki/Probability_mass_function PMF() 190 | - https://en.wikipedia.org/wiki/Cumulative_distribution_function CDF() + VAL() 191 | - https://en.wikipedia.org/wiki/Probability_density_function PDF() 192 | 193 | 194 | #### Experimental 195 | 196 | An additional helper function. 197 | 198 | - **float saturation()** returns the **count()** / nr of bins. 199 | Is an indicator of how "filled" the histogram is. 200 | 201 | Might need to calculate the average level. 202 | 203 | Note: **findMax()** gives an indication for the topmost individual bucket. 204 | 205 | 206 | ## Future 207 | 208 | 209 | #### Must 210 | 211 | - improve documentation 212 | 213 | #### Should 214 | 215 | - investigate performance - **find()** the right bucket. 216 | - Binary search is faster (above 20) 217 | - need testing. 218 | - mixed search, last part (< 20) linear? 219 | - improve accuracy - linear interpolation for **PMF()**, **CDF()** and **VAL()** 220 | - performance - merge loops in **PMF()** 221 | - performance - reverse loops - compare to zero. 222 | 223 | 224 | #### Could 225 | 226 | - **saturation()** indication of the whole histogram 227 | - count / nr of bins? 228 | - percentage readOut == frequency() 229 | - **float getBucketPercent(idx)** 230 | - template class . 231 | 232 | 233 | #### Wont 234 | 235 | - merge bins 236 | - 2D histograms ? e.g. positions on a grid. 237 | - see SparseMatrix 238 | 239 | 240 | ## Support 241 | 242 | If you appreciate my libraries, you can support the development and maintenance. 243 | Improve the quality of the libraries by providing issues and Pull Requests, or 244 | donate through PayPal or GitHub sponsors. 245 | 246 | Thank you, 247 | 248 | 249 | -------------------------------------------------------------------------------- /examples/hist_array/hist_array.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_array.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo histogram array 5 | // URL: https://github.com/RobTillaart/Histogram 6 | 7 | 8 | #include "histogram.h" 9 | 10 | 11 | // float b[] = { 0, 100, 200, 300, 325, 350, 375, 400, 500, 600, 700, 800, 900, 1000 }; 12 | 13 | // boundaries does not need to be equally distributed. 14 | float a[] = { 15 | 0, 50, 125, 200, 250, 350, 500 16 | }; 17 | float b[] = { 18 | 0, 100, 200, 300, 325, 350, 375, 450 19 | }; 20 | 21 | Histogram hist[2] = { Histogram(7, a), Histogram(8, b)}; 22 | 23 | uint32_t lastTime = 0; 24 | const uint32_t threshold = 25; // in milliseconds, for updating display 25 | 26 | 27 | void setup() 28 | { 29 | Serial.begin(115200); 30 | Serial.println(__FILE__); 31 | Serial.print("HISTOGRAM_LIB_VERSION: "); 32 | Serial.println(HISTOGRAM_LIB_VERSION); 33 | Serial.println(); 34 | 35 | } 36 | 37 | 38 | void loop() 39 | { 40 | // choose a "generator" for histogram data 41 | // int x = analogRead(A0); 42 | 43 | int x = random(600) - 50; // below lower limit 44 | 45 | hist[0].add(x); 46 | hist[1].add(x); 47 | 48 | // update output 49 | uint32_t now = millis(); 50 | if (now - lastTime > threshold) 51 | { 52 | lastTime = now; 53 | Serial.print(hist[0].count()); 54 | for (uint16_t i = 0; i < hist[0].size(); i++) 55 | { 56 | Serial.print("\t"); 57 | Serial.print(hist[0].frequency(i), 2); 58 | } 59 | Serial.println(); 60 | 61 | Serial.print(hist[1].count()); 62 | for (uint16_t i = 0; i < hist[1].size(); i++) 63 | { 64 | Serial.print("\t"); 65 | Serial.print(hist[1].frequency(i), 2); 66 | } 67 | Serial.println(); 68 | 69 | if (hist[0].count() > 10000UL) hist[0].clear(); 70 | if (hist[1].count() > 10000UL) hist[1].clear(); 71 | } 72 | } 73 | 74 | 75 | // -- END OF FILE -- 76 | -------------------------------------------------------------------------------- /examples/hist_find_performance/AVR_UNO.txt: -------------------------------------------------------------------------------- 1 | 2 | preliminary results of test for binary search version of find(value) 3 | ===================================================================== 4 | 5 | LINEAR SEARCH 6 | 7 | Histogram version: 0.3.0 8 | # buckets: 101 9 | Duration: 16 10 | Bucket: 1 11 | Duration: 40 12 | Bucket: 6 13 | Duration: 292 14 | Bucket: 66 15 | Duration: 432 16 | Bucket: 100 17 | 18 | 19 | BINARY SEARCH 20 | 21 | Histogram version: 0.3.0 22 | # buckets: 101 23 | Duration: 52 24 | Bucket: 1 25 | Duration: 52 26 | Bucket: 100 27 | Duration: 44 28 | Bucket: 66 29 | Duration: 48 30 | Bucket: 100 31 | 32 | 33 | based upon above 34 | average linear search: 195 35 | average binary search: 49 36 | roughly factor 4. 37 | 38 | expect linear search will average around 50 if # buckets is approx. 20. 39 | so for small histograms there is no gain. 40 | 41 | 42 | -------------------------------------------------------------------------------- /examples/hist_find_performance/hist_find_performance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_find_performance.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2021-11-03 5 | // PURPOSE: indication histogram find performance 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | float b[100]; 12 | 13 | Histogram hist(100, b); 14 | 15 | uint32_t start, duration; 16 | uint32_t lastTime = 0; 17 | 18 | 19 | void setup() 20 | { 21 | Serial.begin(115200); 22 | Serial.println(__FILE__); 23 | Serial.print("HISTOGRAM_LIB_VERSION: "); 24 | Serial.println(HISTOGRAM_LIB_VERSION); 25 | Serial.println(); 26 | 27 | // fill boundary array 28 | for (int i = 0; i < 100; i++) b[i] = i * 10.0; 29 | 30 | Serial.print("# buckets: "); 31 | Serial.println(hist.size()); 32 | 33 | int x = 4; 34 | start = micros(); 35 | int y = hist.find(x); 36 | duration = micros() - start; 37 | Serial.print("Duration: "); 38 | Serial.println(duration); 39 | Serial.print(" Bucket: "); 40 | Serial.println(y); 41 | delay(10); 42 | 43 | x = 54; 44 | start = micros(); 45 | y = hist.find(x); 46 | duration = micros() - start; 47 | Serial.print("Duration: "); 48 | Serial.println(duration); 49 | Serial.print(" Bucket: "); 50 | Serial.println(y); 51 | delay(10); 52 | 53 | x = 654; 54 | start = micros(); 55 | y = hist.find(x); 56 | duration = micros() - start; 57 | Serial.print("Duration: "); 58 | Serial.println(duration); 59 | Serial.print(" Bucket: "); 60 | Serial.println(y); 61 | delay(10); 62 | 63 | x = 7654; 64 | start = micros(); 65 | y = hist.find(x); 66 | duration = micros() - start; 67 | Serial.print("Duration: "); 68 | Serial.println(duration); 69 | Serial.print(" Bucket: "); 70 | Serial.println(y); 71 | delay(10); 72 | 73 | Serial.println(); 74 | } 75 | 76 | 77 | void loop() 78 | { 79 | } 80 | 81 | 82 | // -- END OF FILE -- 83 | 84 | -------------------------------------------------------------------------------- /examples/hist_pointer/hist_pointer.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_pointer.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo histogram pointer 5 | // URL: https://github.com/RobTillaart/Histogram 6 | 7 | 8 | #include "histogram.h" 9 | 10 | // boundaries does not need to be equally distributed. 11 | float a[] = { 12 | 0, 50, 125, 200, 250, 350, 500 13 | }; 14 | float b[] = { 15 | 0, 100, 200, 300, 325, 350, 375, 400 16 | }; 17 | 18 | Histogram histA(7, a); 19 | Histogram histB(8, b); 20 | Histogram *phist; 21 | 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | Serial.println(__FILE__); 27 | Serial.print("HISTOGRAM_LIB_VERSION: "); 28 | Serial.println(HISTOGRAM_LIB_VERSION); 29 | Serial.println(); 30 | 31 | phist = &histA; 32 | 33 | } 34 | 35 | 36 | void loop() 37 | { 38 | phist->clear(); 39 | for (int i = 0; i < 10000; i++) 40 | { 41 | int x = random(600) - 50; 42 | phist->add(x); 43 | } 44 | 45 | Serial.print(phist->count()); 46 | for (uint16_t i = 0; i < phist->size(); i++) 47 | { 48 | Serial.print("\t"); 49 | Serial.print(phist->frequency(i), 2); 50 | } 51 | Serial.println(); 52 | 53 | // swap 54 | if (phist == &histA) phist = &histB; 55 | else phist = &histA; 56 | } 57 | 58 | 59 | // -- END OF FILE -- 60 | -------------------------------------------------------------------------------- /examples/hist_test/hist_test.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-12-23 5 | // PURPOSE: test histogram frequency 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | // float b[] = { 0, 100, 200, 300, 325, 350, 375, 400, 500, 600, 700, 800, 900, 1000 }; 13 | 14 | // boundaries does not need to be equally distributed. 15 | float b[] = { 16 | 0, 100, 200, 300, 325, 350, 375 }; 17 | 18 | Histogram hist(7, b); 19 | 20 | uint32_t lastTime = 0; 21 | const uint32_t threshold = 25; // in milliseconds, for updating display 22 | 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.println(__FILE__); 28 | Serial.print("HISTOGRAM_LIB_VERSION: "); 29 | Serial.println(HISTOGRAM_LIB_VERSION); 30 | Serial.println(); 31 | 32 | Serial.print("# buckets: "); 33 | Serial.println(hist.size()); 34 | 35 | for (uint16_t i = 0; i < hist.size() - 1; i++) 36 | { 37 | Serial.print("\t"); 38 | Serial.print(b[i], 2); 39 | } 40 | Serial.println(); 41 | 42 | for (uint16_t i = 0; i < hist.size() - 1; i++) 43 | { 44 | Serial.print("\t"); 45 | Serial.print(hist.find(b[i])); 46 | } 47 | Serial.println(); 48 | } 49 | 50 | 51 | void loop() 52 | { 53 | // choose a "generator" for histogram data 54 | // int x = analogRead(A0); 55 | 56 | int x = random(600) - 50; // below lower limit 57 | 58 | // int x = random(25); 59 | // x = x*x; 60 | 61 | 62 | // Serial.print(x); 63 | // Serial.print("\t"); 64 | // Serial.println(hist.find(x)); 65 | if (hist.add(x) == false) 66 | { 67 | Serial.print("ERR: \t"); 68 | Serial.print(x); 69 | Serial.print("\t"); 70 | Serial.println(hist.find(x)); 71 | } 72 | 73 | // update output 74 | uint32_t now = millis(); 75 | if (now - lastTime > threshold) 76 | { 77 | lastTime = now; 78 | Serial.print(hist.count()); 79 | for (uint16_t i = 0; i < hist.size(); i++) 80 | { 81 | Serial.print("\t"); 82 | // gives percentage per bucket 83 | // Serial.print(hist.bucket(i)); 84 | Serial.print(hist.frequency(i), 2); 85 | } 86 | // quartiles 87 | // to get at least 25% of the values you must count all values < hist.VAL(0.25); 88 | Serial.print("\t"); 89 | Serial.print(hist.VAL(0.25), 2); 90 | // to get at least 50% of the values you must count all values < hist.VAL(0.50); 91 | Serial.print("\t"); 92 | Serial.print(hist.VAL(0.50), 2); 93 | Serial.print("\t"); 94 | Serial.print(hist.VAL(0.75), 2); 95 | Serial.print("\t"); 96 | Serial.print(hist.VAL(1.0), 2); 97 | Serial.println(); 98 | 99 | if (hist.count() > 10000UL) hist.clear(); 100 | } 101 | } 102 | 103 | 104 | // -- END OF FILE -- 105 | -------------------------------------------------------------------------------- /examples/hist_test_big/hist_test_big.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_big.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-12-23 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | // 8 | // run on ESP32 - 9 | // view in Serial plotter to graph distribution 10 | 11 | 12 | #include "histogram.h" 13 | 14 | 15 | float b[200]; // MIGHT NOT WORK ON AVR !! 16 | 17 | Histogram hist(200, b); 18 | 19 | uint32_t lastTime = 0; 20 | const uint32_t threshold = 1000; // milliseconds, for updating display 21 | 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | Serial.println(__FILE__); 27 | Serial.print("HISTOGRAM_LIB_VERSION: "); 28 | Serial.println(HISTOGRAM_LIB_VERSION); 29 | Serial.println(); 30 | 31 | // fill the boundaries as first step 32 | for (int i = 0; i < 200; i++) 33 | { 34 | b[i] = i; 35 | } 36 | } 37 | 38 | 39 | void loop() 40 | { 41 | // bigger chance on 1 than on 0 42 | // int x = 180 * sin(random(10000) * PI / 20000); 43 | 44 | // float x = 180 * sqrt(random(1000) * 0.001); 45 | 46 | // n = 1 gives flat line = uniform distribution. 47 | // n = 2 gives "triangles" 48 | // n = 3 and higher some normal distribution. 49 | // higher is smaller peaks. 50 | int x = 0; 51 | int n = 5; 52 | for (int i = 0; i < n; i++) 53 | { 54 | x += random(180); 55 | } 56 | x /= n; 57 | hist.add(x); 58 | 59 | 60 | // update output 61 | uint32_t now = millis(); 62 | if (now - lastTime > threshold) 63 | { 64 | lastTime = now; 65 | for (int i = 0; i < hist.size() - 1; i++) 66 | { 67 | Serial.println(hist.bucket(i)); 68 | } 69 | } 70 | } 71 | 72 | 73 | // -- END OF FILE -- 74 | 75 | -------------------------------------------------------------------------------- /examples/hist_test_cdf/hist_test_cdf.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_cdf.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-11-10 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | float b[] = 13 | { 14 | 0, 300, 325, 350, 375, 400, 1000 15 | }; 16 | 17 | Histogram hist(7, b); 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(__FILE__); 24 | Serial.print("HISTOGRAM_LIB_VERSION: "); 25 | Serial.println(HISTOGRAM_LIB_VERSION); 26 | Serial.println(); 27 | } 28 | 29 | 30 | void loop() 31 | { 32 | int x = random(1000); 33 | hist.add(x); 34 | 35 | Serial.print(hist.count()); 36 | Serial.print("\t"); 37 | for (int i = 0; i < (hist.size() - 1); i++) 38 | { 39 | Serial.print(hist.CDF(b[i]), 2); 40 | Serial.print("\t"); 41 | } 42 | Serial.println(); 43 | 44 | if (hist.count() > 1000) 45 | { 46 | hist.clear(); 47 | } 48 | 49 | delay(10); 50 | } 51 | 52 | 53 | // -- END OF FILE -- 54 | 55 | -------------------------------------------------------------------------------- /examples/hist_test_graph/hist_test_graph.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_graph.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2017-07-16 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | // boundaries array does not need to be equally distributed. 13 | float bounds[] = { 0, 100, 200, 300, 325, 350, 375, 400, 500, 600, 700, 800, 900, 1000 }; 14 | 15 | Histogram hist(14, bounds); 16 | 17 | uint32_t lastTime = 0; 18 | const uint32_t threshold = 5000; // in milliseconds, for updating display 19 | 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | Serial.println(__FILE__); 25 | Serial.print("HISTOGRAM_LIB_VERSION: "); 26 | Serial.println(HISTOGRAM_LIB_VERSION); 27 | Serial.println(); 28 | 29 | Serial.print("# buckets: "); 30 | Serial.println(hist.size()); 31 | Serial.println(); 32 | } 33 | 34 | 35 | void loop() 36 | { 37 | // "generator" for histogram data 38 | // int x = analogRead(A0); 39 | int x = random(1024); 40 | hist.add(x); 41 | 42 | // update output 43 | uint32_t now = millis(); 44 | if (now - lastTime > threshold) 45 | { 46 | lastTime = now; 47 | 48 | for (uint16_t i = 0; i < hist.size(); i++) 49 | { 50 | Serial.print(i); 51 | Serial.print("\t"); 52 | Serial.print(hist.frequency(i), 2); 53 | Serial.print("\t"); 54 | 55 | int n = hist.frequency(i) * 50; // 0..50 56 | Serial.print(n); 57 | Serial.print("\t"); 58 | for (int p = 0; p < n; p++) 59 | { 60 | Serial.print(']'); 61 | } 62 | Serial.println(); 63 | } 64 | if (hist.count() > 1000000UL) 65 | { 66 | hist.clear(); 67 | } 68 | } 69 | } 70 | 71 | 72 | // -- END OF FILE -- 73 | -------------------------------------------------------------------------------- /examples/hist_test_level/hist_test_level.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_level.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2021-11-04 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | // boundaries does not need to be equally distributed. 13 | float b[100]; 14 | 15 | Histogram hist(100, b); 16 | 17 | uint32_t lastTime = 0; 18 | const uint32_t threshold = 25; // in milliseconds, for updating display 19 | 20 | 21 | void setup() 22 | { 23 | Serial.begin(115200); 24 | Serial.println(__FILE__); 25 | Serial.print("HISTOGRAM_LIB_VERSION: "); 26 | Serial.println(HISTOGRAM_LIB_VERSION); 27 | Serial.println(); 28 | 29 | // fill boundary array 30 | for (int i = 0; i < 100; i++) 31 | { 32 | b[i] = i * 10.0; 33 | } 34 | Serial.print("# buckets: "); 35 | Serial.println(hist.size()); 36 | 37 | Serial.print("BELOW 0: "); 38 | Serial.println(hist.countBelow(0)); 39 | Serial.print("LEVEL 0: "); 40 | Serial.println(hist.countLevel(0)); 41 | Serial.print("ABOVE 0: "); 42 | Serial.println(hist.countAbove(0)); 43 | Serial.print("COUNT : "); 44 | Serial.println(hist.count()); 45 | Serial.println(); 46 | 47 | Serial.println("add 100 random numbers"); 48 | for (int i = 0; i < 100; i++) 49 | { 50 | int x = random(1000); 51 | hist.add(x); 52 | } 53 | 54 | Serial.print("BELOW 0: "); 55 | Serial.println(hist.countBelow(0)); 56 | Serial.print("LEVEL 0: "); 57 | Serial.println(hist.countLevel(0)); 58 | Serial.print("ABOVE 0: "); 59 | Serial.println(hist.countAbove(0)); 60 | Serial.print("COUNT : "); 61 | Serial.println(hist.count()); 62 | Serial.println(); 63 | 64 | Serial.println("sub 100 random numbers"); 65 | for (int i = 0; i < 100; i++) 66 | { 67 | int x = random(1000); 68 | hist.sub(x); 69 | } 70 | 71 | Serial.print("BELOW 0: "); 72 | Serial.println(hist.countBelow(0)); 73 | Serial.print("LEVEL 0: "); 74 | Serial.println(hist.countLevel(0)); 75 | Serial.print("ABOVE 0: "); 76 | Serial.println(hist.countAbove(0)); 77 | Serial.print("COUNT : "); 78 | Serial.println(hist.count()); 79 | Serial.println(); 80 | 81 | int minidx = hist.findMin(); 82 | int maxidx = hist.findMax(); 83 | Serial.print("MIN INDEX: "); 84 | Serial.print(minidx); 85 | Serial.print("\t"); 86 | Serial.println(hist.bucket(minidx)); 87 | Serial.print("MAX INDEX: "); 88 | Serial.print(maxidx); 89 | Serial.print("\t"); 90 | Serial.println(hist.bucket(maxidx)); 91 | Serial.println(); 92 | 93 | Serial.println("done..."); 94 | } 95 | 96 | 97 | void loop() 98 | { 99 | } 100 | 101 | 102 | // -- END OF FILE -- 103 | 104 | -------------------------------------------------------------------------------- /examples/hist_test_performance/hist_test_performance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_performance.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2023-02-21 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | // boundaries does not need to be equally distributed. 13 | float b[100]; 14 | 15 | Histogram hist(100, b); 16 | 17 | 18 | uint32_t lastTime = 0; 19 | const uint32_t threshold = 25; // in milliseconds, for updating display 20 | 21 | uint32_t start, stop; 22 | volatile int x; 23 | float y; 24 | 25 | 26 | void setup() 27 | { 28 | Serial.begin(115200); 29 | Serial.println(__FILE__); 30 | Serial.print("HISTOGRAM_LIB_VERSION: "); 31 | Serial.println(HISTOGRAM_LIB_VERSION); 32 | Serial.println(); 33 | 34 | // fill boundary array 35 | for (int i = 0; i < 100; i++) 36 | { 37 | b[i] = i * 10.0; 38 | } 39 | Serial.print("# buckets: "); 40 | Serial.println(hist.size()); 41 | 42 | Serial.print("BELOW 0: "); 43 | Serial.println(hist.countBelow(0)); 44 | Serial.print("LEVEL 0: "); 45 | Serial.println(hist.countLevel(0)); 46 | Serial.print("ABOVE 0: "); 47 | Serial.println(hist.countAbove(0)); 48 | Serial.print("COUNT : "); 49 | Serial.println(hist.count()); 50 | Serial.println(); 51 | 52 | Serial.println("add 1000 random numbers"); 53 | for (int i = 0; i < 1000; i++) 54 | { 55 | int x = random(1000); 56 | hist.add(x); 57 | } 58 | 59 | start = micros(); 60 | y = hist.CDF(500); 61 | stop = micros(); 62 | Serial.print(" CDF: "); 63 | Serial.println(y); 64 | Serial.print(" TIME: "); 65 | Serial.println(stop - start); 66 | delay(20); 67 | 68 | 69 | start = micros(); 70 | y = hist.VAL(0.9); 71 | stop = micros(); 72 | Serial.print(" VAL: "); 73 | Serial.println(y); 74 | Serial.print(" TIME: "); 75 | Serial.println(stop - start); 76 | delay(20); 77 | 78 | 79 | start = micros(); 80 | x = hist.findMax(); 81 | stop = micros(); 82 | Serial.print("FINDMIN: "); 83 | Serial.println(hist.findMin()); 84 | Serial.print(" TIME: "); 85 | Serial.println(stop - start); 86 | delay(20); 87 | 88 | 89 | start = micros(); 90 | x = hist.findMax(); 91 | stop = micros(); 92 | Serial.print("FINDMIN: "); 93 | Serial.println(hist.findMin()); 94 | Serial.print(" TIME: "); 95 | Serial.println(stop - start); 96 | delay(20); 97 | 98 | 99 | start = micros(); 100 | x = hist.findMax(); 101 | stop = micros(); 102 | Serial.print("FINDMAX: "); 103 | Serial.println(hist.findMax()); 104 | Serial.print(" TIME: "); 105 | Serial.println(stop - start); 106 | delay(20); 107 | 108 | 109 | start = micros(); 110 | x = hist.countBelow(0); 111 | stop = micros(); 112 | Serial.print("BELOW 0: "); 113 | Serial.println(hist.countBelow(0)); 114 | Serial.print(" TIME: "); 115 | Serial.println(stop - start); 116 | delay(20); 117 | 118 | start = micros(); 119 | x = hist.countLevel(0); 120 | stop = micros(); 121 | Serial.print("LEVEL 0: "); 122 | Serial.println(hist.countLevel(0)); 123 | Serial.print(" TIME: "); 124 | Serial.println(stop - start); 125 | delay(20); 126 | 127 | start = micros(); 128 | x = hist.countAbove(0); 129 | stop = micros(); 130 | Serial.print("ABOVE 0: "); 131 | Serial.println(hist.countAbove(0)); 132 | Serial.print(" TIME: "); 133 | Serial.println(stop - start); 134 | delay(20); 135 | 136 | Serial.println("done..."); 137 | } 138 | 139 | 140 | void loop() 141 | { 142 | } 143 | 144 | 145 | // -- END OF FILE -- 146 | -------------------------------------------------------------------------------- /examples/hist_test_performance/performance_0.3.2.txt: -------------------------------------------------------------------------------- 1 | Board: UNO 2 | IDE: 1.8.19 3 | 4 | hist_test_level_performance.ino 5 | 6 | Histogram version: 0.3.2 7 | 8 | # buckets: 101 9 | BELOW 0: 0 10 | LEVEL 0: 101 11 | ABOVE 0: 0 12 | COUNT : 0 13 | 14 | add 1000 random numbers 15 | FINDMIN: 19 16 | TIME: 232 17 | FINDMAX: 51 18 | TIME: 232 19 | BELOW 0: 0 20 | TIME: 108 21 | LEVEL 0: 0 22 | TIME: 124 23 | ABOVE 0: 101 24 | TIME: 140 25 | done... 26 | -------------------------------------------------------------------------------- /examples/hist_test_performance/performance_0.3.3.txt: -------------------------------------------------------------------------------- 1 | Board: UNO 2 | IDE: 1.8.19 3 | 4 | hist_test_performance.ino 5 | 6 | Histogram version: 0.3.3 7 | 8 | # buckets: 101 9 | BELOW 0: 0 10 | LEVEL 0: 101 11 | ABOVE 0: 0 12 | COUNT : 0 13 | 14 | add 1000 random numbers 15 | VAL: 910.00 16 | TIME: 872 17 | FINDMIN: 19 18 | TIME: 220 19 | FINDMAX: 88 20 | TIME: 220 21 | BELOW 0: 0 22 | TIME: 96 23 | LEVEL 0: 0 24 | TIME: 112 25 | ABOVE 0: 101 26 | TIME: 124 27 | done... 28 | -------------------------------------------------------------------------------- /examples/hist_test_pmf/hist_test_pmf.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_pmf.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-11-10 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | float b[] = { 13 | 0, 50, 100, 150, 200, 250, 14 | 300, 350, 400, 450, 500, 15 | 600, 700, 800, 900, 1000 }; 16 | 17 | Histogram hist(16, b); 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(__FILE__); 24 | Serial.print("HISTOGRAM_LIB_VERSION: "); 25 | Serial.println(HISTOGRAM_LIB_VERSION); 26 | Serial.println(); 27 | } 28 | 29 | 30 | void loop() 31 | { 32 | int x = random(1000); 33 | hist.add(x); 34 | 35 | Serial.print(hist.count()); 36 | Serial.print("\t"); 37 | for (int i = 0; i < hist.size()-1; i++) 38 | { 39 | Serial.print(hist.PMF(b[i]), 2); 40 | Serial.print("\t"); 41 | } 42 | Serial.println(); 43 | 44 | if (hist.count() > 1000) 45 | { 46 | hist.clear(); 47 | } 48 | 49 | delay(10); 50 | } 51 | 52 | 53 | // -- END OF FILE -- 54 | 55 | -------------------------------------------------------------------------------- /examples/hist_test_val/hist_test_val.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: hist_test_val.ino 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-11-11 5 | // PURPOSE: test histogram library 6 | // URL: https://github.com/RobTillaart/Histogram 7 | 8 | 9 | #include "histogram.h" 10 | 11 | 12 | float b[] = { 13 | 0, 50, 100, 150, 200, 250, 14 | 300, 350, 400, 450, 500, 15 | 600, 700, 800, 900, 1000 }; 16 | 17 | Histogram hist(16, b); 18 | 19 | 20 | void setup() 21 | { 22 | Serial.begin(115200); 23 | Serial.println(__FILE__); 24 | Serial.print("HISTOGRAM_LIB_VERSION: "); 25 | Serial.println(HISTOGRAM_LIB_VERSION); 26 | Serial.println(); 27 | } 28 | 29 | 30 | void loop() 31 | { 32 | int x = random(800); 33 | hist.add(x); 34 | 35 | Serial.print(hist.count()); 36 | Serial.print(", "); 37 | float f = 0.5; 38 | Serial.print(f, 2); 39 | Serial.print(" : "); 40 | 41 | Serial.print(hist.VAL(f), 2); 42 | Serial.print("\t"); 43 | 44 | float sum = 0; 45 | uint16_t i = 0; 46 | for (i = 0; i < hist.size(); i++) 47 | { 48 | sum += hist.frequency(i); 49 | Serial.print(sum, 2); 50 | Serial.print("\t"); 51 | if (sum >= f) break; 52 | } 53 | Serial.print(b[i]); 54 | Serial.println(); 55 | 56 | if (hist.count() > 1000) 57 | { 58 | hist.clear(); 59 | } 60 | 61 | delay(10); 62 | } 63 | 64 | 65 | // -- END OF FILE -- 66 | 67 | -------------------------------------------------------------------------------- /histogram.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: Histogram.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2012-11-10 5 | // VERSION: 0.3.7 6 | // PURPOSE: Histogram library for Arduino 7 | // URL: https://github.com/RobTillaart/Histogram 8 | 9 | 10 | #include "histogram.h" 11 | 12 | 13 | Histogram::Histogram(const uint16_t length, float *bounds) 14 | { 15 | _bounds = bounds; 16 | _length = length + 1; 17 | _data = (int32_t *) malloc((_length) * sizeof(int32_t)); 18 | if (_data != NULL) 19 | { 20 | clear(); 21 | } 22 | else 23 | { 24 | _length = 0; 25 | } 26 | _count = 0; 27 | _status = HISTO_OK; 28 | _maxBucket = 2147483647; 29 | } 30 | 31 | 32 | Histogram::~Histogram() 33 | { 34 | if (_data != NULL) 35 | { 36 | free(_data); 37 | } 38 | } 39 | 40 | 41 | // resets all counters to value (default 0) 42 | uint8_t Histogram::clear(int32_t value) 43 | { 44 | for (uint16_t i = 0; i < _length; i++) 45 | { 46 | _data[i] = value; 47 | } 48 | _count = 0; 49 | _status = HISTO_OK; 50 | if (value == _maxBucket) _status = HISTO_FULL; 51 | return _status; 52 | } 53 | 54 | 55 | uint8_t Histogram::setBucket(const uint16_t index, int32_t value) 56 | { 57 | _data[index] = value; 58 | _status = HISTO_OK; 59 | if (value == _maxBucket) _status = HISTO_FULL; 60 | return _status; 61 | } 62 | 63 | 64 | // adds a new value to the histogram - increasing 65 | uint8_t Histogram::add(const float value) 66 | { 67 | if (_length == 0) 68 | { 69 | _status = HISTO_ERR_LENGTH; 70 | return _status; 71 | } 72 | uint16_t index = find(value); 73 | if (_data[index] == _maxBucket) 74 | { 75 | _status = HISTO_ERR_FULL; 76 | return _status; 77 | } 78 | _data[index]++; 79 | _count++; 80 | _status = HISTO_OK; 81 | if (_data[index] == _maxBucket) _status = HISTO_FULL; 82 | return _status; 83 | } 84 | 85 | 86 | // adds a new value to the histogram - decreasing 87 | uint8_t Histogram::sub(const float value) 88 | { 89 | if (_length == 0) 90 | { 91 | _status = HISTO_ERR_LENGTH; 92 | return _status; 93 | } 94 | uint16_t index = find(value); 95 | if (_data[index] == -_maxBucket) 96 | { 97 | _status = HISTO_ERR_FULL; 98 | return _status; 99 | } 100 | _data[index]--; 101 | _count++; 102 | _status = HISTO_OK; 103 | if (_data[index] == _maxBucket) _status = HISTO_FULL; 104 | return _status; 105 | } 106 | 107 | 108 | uint8_t Histogram::status() 109 | { 110 | return _status; 111 | } 112 | 113 | 114 | // number of buckets 115 | uint16_t Histogram::size() 116 | { 117 | return _length; 118 | } 119 | 120 | 121 | // number of values added to all buckets 122 | uint32_t Histogram::count() 123 | { 124 | return _count; 125 | } 126 | 127 | 128 | // returns the count of a bucket 129 | int32_t Histogram::bucket(const uint16_t index) 130 | { 131 | if (index > _length) return 0; 132 | return _data[index]; 133 | } 134 | 135 | 136 | // returns the relative frequency of a bucket 137 | float Histogram::frequency(const uint16_t index) 138 | { 139 | if ((_count == 0) || (_length == 0)) return NAN; 140 | 141 | if (index > _length) return 0; // differs from PMF() 142 | return (1.0 * _data[index]) / _count; 143 | } 144 | 145 | 146 | // EXPERIMENTAL 147 | // returns the probability of the bucket of a value 148 | float Histogram::PMF(const float value) 149 | { 150 | if ((_count == 0) || (_length == 0)) return NAN; 151 | 152 | uint16_t index = find(value); 153 | return (1.0 * _data[index]) / _count; 154 | } 155 | 156 | 157 | // EXPERIMENTAL 158 | // returns the cumulative probability of 159 | // values <= value 160 | float Histogram::CDF(const float value) 161 | { 162 | if ((_count == 0) || (_length == 0)) return NAN; 163 | 164 | // TODO: could be done in one loop? 165 | uint16_t index = find(value); 166 | int32_t sum = 0; 167 | for (uint16_t i = 0; i <= index; i++) 168 | { 169 | sum += _data[i]; 170 | } 171 | return (1.0 * sum) / _count; 172 | } 173 | 174 | 175 | // EXPERIMENTAL 176 | // returns the value of the original array for 177 | // which the CDF is at least probability. 178 | // must start at 0. 179 | float Histogram::VAL(const float probability) 180 | { 181 | if ((_count == 0) || (_length == 0)) return NAN; 182 | 183 | float prob = probability; 184 | if (prob < 0.0) prob = 0.0; 185 | else if (prob > 1.0) prob = 1.0; 186 | 187 | prob *= _count; 188 | int32_t sum = 0; 189 | for (uint16_t i = 0; i < _length; i++) 190 | { 191 | sum += _data[i]; 192 | if ((sum >= prob) && (i < (_length - 1))) 193 | { 194 | return _bounds[i]; 195 | } 196 | } 197 | return INFINITY; 198 | } 199 | 200 | 201 | int32_t Histogram::sum() 202 | { 203 | int32_t _sum = 0; 204 | for (uint16_t i = 0; i < _length; i++) 205 | { 206 | _sum += _data[i]; 207 | } 208 | return _sum; 209 | } 210 | 211 | 212 | // returns the bucket number for value 213 | // - binary search, more memory ; faster for #buckets > 20 ? 214 | // uint16_t Histogram::find(const float value) 215 | // { 216 | // if (_length <= 0) return -1; 217 | 218 | // uint16_t low = 0, high = _length; 219 | // uint16_t mid; 220 | // while (high - low > 1) 221 | // { 222 | // mid = (low + high)/2; 223 | // if (_bounds[mid] > value) 224 | // { 225 | // high = mid; 226 | // } 227 | // else 228 | // { 229 | // low = mid; 230 | // } 231 | // } 232 | // if (_bounds[mid] > value) return mid; 233 | // return _length - 1; 234 | // } 235 | 236 | 237 | // returns the bucket number for value 238 | // must start at 0. 239 | uint16_t Histogram::find(const float value) 240 | { 241 | if (_length <= 0) return -1; 242 | 243 | for (uint16_t i = 0; i < (_length - 1); i++) 244 | { 245 | if (_bounds[i] >= value) 246 | { 247 | return i; 248 | } 249 | } 250 | return _length - 1; 251 | } 252 | 253 | 254 | // returns the (first) index of the bucket with minimum value. 255 | uint16_t Histogram::findMin() 256 | { 257 | if (_length <= 0) return -1; 258 | uint16_t index = 0; 259 | uint16_t n = _length; 260 | while (n > 1) 261 | { 262 | n--; 263 | if (_data[n] < _data[index]) index = n; 264 | } 265 | return index; 266 | } 267 | 268 | 269 | // returns the (first) index of the bucket with maximum value. 270 | uint16_t Histogram::findMax() 271 | { 272 | if (_length <= 0) return -1; 273 | uint16_t index = 0; 274 | uint16_t n = _length; 275 | while (n > 1) 276 | { 277 | n--; 278 | if (_data[n] > _data[index]) index = n; 279 | } 280 | return index; 281 | } 282 | 283 | 284 | // returns the number of buckets with an exact level. 285 | uint16_t Histogram::countLevel(const int32_t level) 286 | { 287 | if (_length <= 0) return -1; 288 | 289 | uint16_t buckets = 0; 290 | uint16_t n = _length; 291 | while (n > 0) 292 | { 293 | n--; 294 | if (_data[n] == level) buckets++; 295 | } 296 | return buckets; 297 | } 298 | 299 | 300 | // returns the number of buckets above a certain level. 301 | uint16_t Histogram::countAbove(const int32_t level) 302 | { 303 | if (_length <= 0) return -1; 304 | 305 | uint16_t buckets = 0; 306 | uint16_t n = _length; 307 | while (n > 0) 308 | { 309 | n--; 310 | if (_data[n] > level) buckets++; 311 | } 312 | return buckets; 313 | } 314 | 315 | 316 | // returns the number of buckets below a certain level. 317 | uint16_t Histogram::countBelow(const int32_t level) 318 | { 319 | if (_length <= 0) return -1; 320 | 321 | uint16_t buckets = 0; 322 | uint16_t n = _length; 323 | while (n > 0) 324 | { 325 | n--; 326 | if (_data[n] < level) buckets++; 327 | } 328 | return buckets; 329 | } 330 | 331 | 332 | 333 | /////////////////////////////////////////////////// 334 | // 335 | // experimental 336 | // 337 | // use with care 338 | float Histogram::saturation() 339 | { 340 | return (1.0 * _count) / _length; 341 | } 342 | 343 | 344 | // experimental use with care 345 | int32_t Histogram::getMaxBucket() 346 | { 347 | return _maxBucket; 348 | } 349 | 350 | 351 | void Histogram::setMaxBucket(int32_t value) 352 | { 353 | _maxBucket = value; 354 | } 355 | 356 | 357 | ////////////////////////////////////////////////////////////// 358 | // 359 | // DERIVED CLASS - HISTOGRAM16 360 | // 361 | Histogram16::Histogram16(const uint16_t length, float *bounds) : Histogram(length, bounds) 362 | { 363 | _bounds = bounds; 364 | _length = length + 1; 365 | _data = (int16_t *) malloc((_length) * sizeof(int16_t)); 366 | if (_data != NULL) 367 | { 368 | clear(); 369 | } 370 | else 371 | { 372 | _length = 0; 373 | } 374 | _count = 0; 375 | _status = HISTO_OK; 376 | _maxBucket = 32767; 377 | } 378 | 379 | 380 | Histogram16::~Histogram16() 381 | { 382 | if (_data) free(_data); 383 | } 384 | 385 | 386 | uint8_t Histogram16::setBucket(const uint16_t index, int16_t value) 387 | { 388 | _data[index] = value; 389 | _status = HISTO_OK; 390 | if (value == _maxBucket) _status = HISTO_FULL; 391 | return _status; 392 | } 393 | 394 | 395 | ////////////////////////////////////////////////////////////// 396 | // 397 | // DERIVED CLASS - HISTOGRAM8 398 | // 399 | Histogram8::Histogram8(const uint16_t length, float *bounds) : Histogram(length, bounds) 400 | { 401 | _bounds = bounds; 402 | _length = length + 1; 403 | _data = (int8_t *) malloc((_length) * sizeof(int8_t)); 404 | if (_data != NULL) 405 | { 406 | clear(); 407 | } 408 | else 409 | { 410 | _length = 0; 411 | } 412 | _count = 0; 413 | _status = HISTO_OK; 414 | _maxBucket = 127; 415 | } 416 | 417 | 418 | Histogram8::~Histogram8() 419 | { 420 | if (_data) free(_data); 421 | } 422 | 423 | 424 | uint8_t Histogram8::setBucket(const uint16_t index, int8_t value) 425 | { 426 | _data[index] = value; 427 | _status = HISTO_OK; 428 | if (value == _maxBucket) _status = HISTO_FULL; 429 | return _status; 430 | } 431 | 432 | 433 | // -- END OF FILE -- 434 | 435 | -------------------------------------------------------------------------------- /histogram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: Histogram.h 4 | // AUTHOR: Rob Tillaart 5 | // DATE: 2012-11-10 6 | // VERSION: 0.3.7 7 | // PURPOSE: Histogram library for Arduino 8 | // URL: https://github.com/RobTillaart/Histogram 9 | 10 | 11 | #include "Arduino.h" 12 | 13 | 14 | #define HISTOGRAM_LIB_VERSION (F("0.3.6")) 15 | 16 | // return STATUS add(), sub(), clear(), setBucket(); 17 | #define HISTO_OK 0x00 // idem 18 | #define HISTO_FULL 0x01 // just got full 19 | #define HISTO_ERR_FULL 0xFF // over- underflow 20 | #define HISTO_ERR_LENGTH 0xFE // constructor issue. 21 | 22 | 23 | class Histogram 24 | { 25 | public: 26 | Histogram(const uint16_t length, float * bounds); 27 | ~Histogram(); 28 | 29 | uint8_t clear(int32_t value = 0); 30 | uint8_t add(const float value); 31 | uint8_t sub(const float value); 32 | 33 | virtual uint8_t setBucket(const uint16_t index, int32_t value = 0); 34 | 35 | // returns last known status 36 | uint8_t status(); 37 | 38 | // number of buckets 39 | uint16_t size(); 40 | 41 | // number of values added to all buckets 42 | uint32_t count(); 43 | 44 | // number of values added to single bucket 45 | int32_t bucket(const uint16_t index); 46 | 47 | 48 | float frequency(const uint16_t index); 49 | float PMF(const float value); 50 | float CDF(const float value); 51 | float VAL(const float probability); 52 | int32_t sum(); 53 | 54 | 55 | uint16_t find(const float value); 56 | uint16_t findMin(); 57 | uint16_t findMax(); 58 | uint16_t countLevel(const int32_t level); 59 | uint16_t countAbove(const int32_t level); 60 | uint16_t countBelow(const int32_t level); 61 | 62 | 63 | /////////////////////////////////////////////////// 64 | // 65 | // experimental 66 | // 67 | float saturation(); 68 | 69 | // use with care 70 | int32_t getMaxBucket(); 71 | void setMaxBucket(int32_t value); 72 | 73 | 74 | protected: 75 | float * _bounds; 76 | int32_t * _data; 77 | uint16_t _length; 78 | uint32_t _count; 79 | int32_t _maxBucket; 80 | uint8_t _status; 81 | }; 82 | 83 | 84 | ////////////////////////////////////////////////////////////// 85 | // 86 | // DERIVED CLASS 87 | // 88 | class Histogram16 : public Histogram 89 | { 90 | public: 91 | Histogram16(const uint16_t length, float * bounds); 92 | ~Histogram16(); 93 | 94 | uint8_t setBucket(const uint16_t index, int16_t value = 0); 95 | 96 | protected: 97 | int16_t * _data; 98 | }; 99 | 100 | 101 | class Histogram8 : public Histogram 102 | { 103 | public: 104 | Histogram8(const uint16_t length, float * bounds); 105 | ~Histogram8(); 106 | 107 | uint8_t setBucket(const uint16_t index, int8_t value = 0); 108 | 109 | protected: 110 | int8_t * _data; 111 | }; 112 | 113 | 114 | // -- END OF FILE -- 115 | 116 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map For Histogram 2 | 3 | 4 | # Data types (KEYWORD1) 5 | Histogram KEYWORD1 6 | Histogram8 KEYWORD1 7 | Histogram16 KEYWORD1 8 | 9 | 10 | # Methods and Functions (KEYWORD2) 11 | clear KEYWORD2 12 | setBucket KEYWORD2 13 | add KEYWORD2 14 | sub KEYWORD2 15 | status KEYWORD2 16 | 17 | size KEYWORD2 18 | count KEYWORD2 19 | bucket KEYWORD2 20 | frequency KEYWORD2 21 | 22 | PMF KEYWORD2 23 | CDF KEYWORD2 24 | VAL KEYWORD2 25 | sum KEYWORD2 26 | 27 | find KEYWORD2 28 | findMin KEYWORD2 29 | findMax KEYWORD2 30 | 31 | countLevel KEYWORD2 32 | countAbove KEYWORD2 33 | countBelow KEYWORD2 34 | 35 | getMaxBucket KEYWORD2 36 | setMaxBucket KEYWORD2 37 | 38 | 39 | # Constants (LITERAL1) 40 | HISTOGRAM_LIB_VERSION LITERAL1 41 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Histogram", 3 | "keywords": "Histogram,VAL,CDF,PMF,frequency", 4 | "description": "Arduino library for creating histograms math.", 5 | "authors": 6 | [ 7 | { 8 | "name": "Rob Tillaart", 9 | "email": "Rob.Tillaart@gmail.com", 10 | "maintainer": true 11 | } 12 | ], 13 | "repository": 14 | { 15 | "type": "git", 16 | "url": "https://github.com/RobTillaart/Histogram.git" 17 | }, 18 | "version": "0.3.7", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "histogram.h" 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Histogram 2 | version=0.3.7 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Arduino library for creating histograms math. 6 | paragraph= 7 | category=Data Processing 8 | url=https://github.com/RobTillaart/Histogram 9 | architectures=* 10 | includes=histogram.h 11 | depends= 12 | -------------------------------------------------------------------------------- /test/unit_test_001.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: unit_test_001.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2020-12-03 5 | // PURPOSE: unit tests for the Histogram 6 | // https://github.com/RobTillaart/Histogram 7 | // https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md 8 | // 9 | 10 | // supported assertions 11 | // https://github.com/Arduino-CI/arduino_ci/blob/master/cpp/unittest/Assertion.h#L33-L42 12 | // ---------------------------- 13 | // assertEqual(expected, actual) 14 | // assertNotEqual(expected, actual) 15 | // assertLess(expected, actual) 16 | // assertMore(expected, actual) 17 | // assertLessOrEqual(expected, actual) 18 | // assertMoreOrEqual(expected, actual) 19 | // assertTrue(actual) 20 | // assertFalse(actual) 21 | // assertNull(actual) 22 | // assertNotNull(actual) 23 | 24 | #include 25 | 26 | 27 | #include "Arduino.h" 28 | #include "histogram.h" 29 | 30 | 31 | 32 | unittest_setup() 33 | { 34 | fprintf(stderr, "HISTOGRAM_LIB_VERSION: %s\n", (char *) HISTOGRAM_LIB_VERSION); 35 | } 36 | 37 | 38 | unittest_teardown() 39 | { 40 | } 41 | 42 | 43 | unittest(test_constructor) 44 | { 45 | float diceValues[] = {0.5, 1.5, 2.5, 3.5, 4.5, 5.5 }; 46 | Histogram hist(6, diceValues); 47 | assertEqual(7, hist.size()); 48 | assertEqual(0, hist.count()); 49 | 50 | for (int i = 0; i < 7; i++) 51 | { 52 | fprintf(stderr, "%d\t", i); 53 | assertEqual(0, hist.bucket(i)); 54 | } 55 | } 56 | 57 | 58 | unittest(test_dice) 59 | { 60 | float diceValues[] = { 0.5, 1.5, 2.5, 3.5, 4.5, 5.5 }; 61 | Histogram hist(6, diceValues); 62 | assertEqual(7, hist.size()); 63 | assertEqual(0, hist.count()); 64 | 65 | for (int d = 0; d < 70; d++) 66 | { 67 | hist.add( d % 7 ); // simulate dice 68 | } 69 | 70 | assertEqual(7, hist.size()); 71 | assertEqual(70, hist.count()); 72 | 73 | for (int i = 0; i < 7; i++) 74 | { 75 | fprintf(stderr, "%d\t", i); 76 | assertEqual(10, hist.bucket(i)); 77 | } 78 | 79 | hist.clear(); 80 | assertEqual(7, hist.size()); 81 | assertEqual(0, hist.count()); 82 | 83 | for (int i = 0; i < 7; i++) 84 | { 85 | fprintf(stderr, "%d\t", i); 86 | assertEqual(0, hist.bucket(i)); 87 | } 88 | } 89 | 90 | 91 | unittest_main() 92 | 93 | 94 | // -- END OF FILE -- 95 | 96 | --------------------------------------------------------------------------------