diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a82d7b5..17470a6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,8 +9,8 @@ jobs: env: PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: '3.9' - name: Install PlatformIO Core diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 041ed11..661b010 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,8 +10,8 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: | ~/.cache/pip @@ -26,3 +26,8 @@ jobs: pip install platformio - name: Run Tests on the Desktop Environment run: platformio test -e desktop + - name: Run Build Test for Arduino Uno + run: platformio run -e arduino_uno -t build_test.cpp + - name: Run Build Test for ESP32 Dev Module + run: platformio run -e esp32dev -t build_test.cpp + diff --git a/.vscode/settings.json b/.vscode/settings.json index 63a6748..7af60fd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,6 +58,8 @@ "test", "readme", "library_metadata", - "DataTome" + "DataTome", + "DataTomeCumulative", + "DataTomeExpAvg" ] } \ No newline at end of file diff --git a/README.md b/README.md index f92f318..12933ad 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ Data Tome is a C++ library for data analysis and data filtering on embedded devices (IoT). Focus on the developer's experience and performance. +- Simple Moving Average (SMA). +- Exponential Moving Average (EMA). +- Simple Moving Median (implemented on DataTomeAnalysis). +- Variance, Standard Deviation, and more. + ## Getting Started - This library is listed in the official [Arduino Library Manager](https://www.arduino.cc/reference/en/libraries/datatome/). @@ -36,6 +41,8 @@ This library calculates statistical functions using a time-series sample impleme [Read here how to contribute](https://github.com/AlexandreHiroyuki/DataTome/blob/master/CONTRIBUTING.md). +It describes how to report issues, code conventions, testing, and how to publish a package on the PlatformIO Registry. + ## Developed by **Alexandre Hiroyuki** – [GitHub](https://github.com/AlexandreHiroyuki) – [LinkedIn](https://www.linkedin.com/in/alexandre-hiroyuki-yamauchi/) diff --git a/build_test.cpp b/build_test.cpp new file mode 100644 index 0000000..5fa92bd --- /dev/null +++ b/build_test.cpp @@ -0,0 +1,59 @@ +// Include lib: +#include +#include + +// Create an Arithmetic Moving Average object of unsigned int type, +// 10 in size +DataTomeMvAvg test(10); +DataTomeAnalysis test2(10); +DataTomeCumulative test3; +DataTomeExpAvg test4; + +// This variable just generates input for average test +int delta_x = 0; + +void setup() { + // Initialize serial interface + Serial.begin(9600); +} + +void loop() { + // Pushes the input in the moving average object + test.push(delta_x); + test2.push(delta_x); + test3.push(delta_x); + test4.push(delta_x); + + // Generates the next input + delta_x += 3; + if (delta_x > 1000) delta_x = 0; + + // Prints each value stored in the moving average + for (uint8_t i = 0; i < test.size(); i++) { + Serial.print(test[i]); + Serial.print(" "); + } + // Prints the result of the average + Serial.print("= "); + Serial.print(test.get()); + // Prints the value stored in the first and last indexes + Serial.print(" | f: "); + Serial.print(test.front()); + Serial.print(" b: "); + Serial.println(test.back()); + + Serial.print("Analysis: "); + Serial.print(test2.mean()); + Serial.print(" | Std: "); + Serial.print(test2.std()); + Serial.print(" | Median: "); + Serial.println(test2.median()); + + Serial.print("Cumulative: "); + Serial.println(test3.get()); + + Serial.print(" | ExpAvg: "); + Serial.println(test4.get()); + + delay(1000); +} diff --git a/examples/moving_average_print/moving_average_print.ino b/examples/moving_average_print/moving_average_print.ino index 7505e45..54d0fdd 100644 --- a/examples/moving_average_print/moving_average_print.ino +++ b/examples/moving_average_print/moving_average_print.ino @@ -4,7 +4,7 @@ // Create an Arithmetic Moving Average object of unsigned int type, // 10 in size -DataTomeMvAvg test(10); +DataTomeMvAvg test(10); // This variable just generates input for average test unsigned delta_x = 0; diff --git a/examples/partials_example/partials_example.ino b/examples/partials_example/partials_example.ino index d8392f0..6369349 100644 --- a/examples/partials_example/partials_example.ino +++ b/examples/partials_example/partials_example.ino @@ -4,7 +4,7 @@ // Create an Arithmetic Moving Average object of unsigned int type, // 10 in size -DataTomeMvAvg integer_mv(10); +DataTomeMvAvg integer_mv(10); // This variable just generates input for average integer_mv unsigned delta_x = 0; diff --git a/include/README b/include/README deleted file mode 100644 index 194dcd4..0000000 --- a/include/README +++ /dev/null @@ -1,39 +0,0 @@ - -This directory is intended for project header files. - -A header file is a file containing C declarations and macro definitions -to be shared between several project source files. You request the use of a -header file in your project source file (C, C++, etc) located in `src` folder -by including it, with the C preprocessing directive `#include'. - -```src/main.c - -#include "header.h" - -int main (void) -{ - ... -} -``` - -Including a header file produces the same results as copying the header file -into each source file that needs it. Such copying would be time-consuming -and error-prone. With a header file, the related declarations appear -in only one place. If they need to be changed, they can be changed in one -place, and programs that include the header file will automatically use the -new version when next recompiled. The header file eliminates the labor of -finding and changing all the copies as well as the risk that a failure to -find one copy will result in inconsistencies within a program. - -In C, the usual convention is to give header files names that end with `.h'. -It is most portable to use only letters, digits, dashes, and underscores in -header file names, and at most one dot. - -Read more about using header files in official GCC documentation: - -* Include Syntax -* Include Operation -* Once-Only Headers -* Computed Includes - -https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README deleted file mode 100644 index 6debab1..0000000 --- a/lib/README +++ /dev/null @@ -1,46 +0,0 @@ - -This directory is intended for project specific (private) libraries. -PlatformIO will compile them to static libraries and link into executable file. - -The source code of each library should be placed in a an own separate directory -("lib/your_library_name/[here are source files]"). - -For example, see a structure of the following two libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -and a contents of `src/main.c`: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -PlatformIO Library Dependency Finder will find automatically dependent -libraries scanning project source files. - -More information about PlatformIO Library Dependency Finder -- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/library.json b/library.json index 04b6f29..79180b5 100644 --- a/library.json +++ b/library.json @@ -1,9 +1,9 @@ { "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", "name": "DataTome", - "version": "1.7.0", + "version": "1.8.0", "description": "Data analysis and filtering using time series for embedded devices (IoT). All in a single C++ library, Data Tome. Focus on the developer's experience and performance.", - "keywords": "sensors, input, data, processing, analysis, arduino, library, filter, moving average, smooth, standard, deviation, mean, partial, tome, datatome", + "keywords": "sensors, input, data, processing, analysis, arduino, library, filter, moving average, smooth, standard, deviation, mean, median, partial, tome, datatome", "repository": { "type": "git", "url": "https://github.com/AlexandreHiroyuki/DataTome" @@ -22,8 +22,11 @@ "platforms": "*", "headers": [ "DataTome.h", + "DataTomeAnalysis.h", + "DataTomeCumulative.h", + "DataTomeExpAvg", "DataTomeMvAvg.h", - "DataTomeAnalysis.h" + "DataTomeUtils.h" ], "examples": [ { @@ -43,9 +46,12 @@ ], "export": { "exclude": [ + "test/", ".github", ".gitignore", - ".vscode" + ".vscode", + "build_test.cpp", + "docs/icon.png" ] }, "srcDir": "src" diff --git a/library.properties b/library.properties index 53dfc04..2591048 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DataTome -version=1.7.0 +version=1.8.0 author=Alexandre Hiroyuki Yamauchi maintainer=Alexandre Hiroyuki Yamauchi sentence=Data analysis and filtering using time series for embedded devices (IoT). All in a single C++ library, Data Tome. diff --git a/platformio.ini b/platformio.ini index 3ee1e86..523d740 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ platform = espressif32 board = esp32dev framework = arduino -[env:arduino_avr] +[env:arduino_uno] framework = arduino platform = atmelavr board = uno diff --git a/src/DataTome.h b/src/DataTome.h index 1098627..ff90e54 100644 --- a/src/DataTome.h +++ b/src/DataTome.h @@ -8,6 +8,8 @@ #define DATA_TOME_H #include +#include +#include #include #include diff --git a/src/DataTomeCumulative.h b/src/DataTomeCumulative.h new file mode 100644 index 0000000..34958f3 --- /dev/null +++ b/src/DataTomeCumulative.h @@ -0,0 +1,38 @@ +/*************************************************************** + DataTomeCumulative.h + Created by Alexandre Hiroyuki Yamauchi, September 23, 2024. +***************************************************************/ + +#ifndef DATA_TOME_CUMULATIVE_H +#define DATA_TOME_CUMULATIVE_H + +#include + +// RECOMMENDED: use double type for TypeOfSum +// WARNING: using this class with integer types may result in a loss of +// precision due to cumulative rounding of integer divisions. +template +class DataTomeCumulative { + protected: + TypeOfSum _cumulative_average; + unsigned long int _count; + + public: + DataTomeCumulative() : _cumulative_average(0), _count(0) {} + + DataTomeCumulative &push(TypeOfSum input) { + if (_count >= ULONG_MAX) { + _cumulative_average = 0; + _count = 0; + } + _count++; + + _cumulative_average += (input - _cumulative_average) / (_count); + + return *this; + } + + TypeOfSum get() { return _cumulative_average; } +}; + +#endif // DATA_TOME_CUMULATIVE_H \ No newline at end of file diff --git a/src/DataTomeExpAvg.h b/src/DataTomeExpAvg.h new file mode 100644 index 0000000..209dc6f --- /dev/null +++ b/src/DataTomeExpAvg.h @@ -0,0 +1,39 @@ +/*************************************************************** + DataTomeExpAvg.h + Created by Alexandre Hiroyuki Yamauchi, August 19, 2024. +***************************************************************/ + +#ifndef DATA_TOME_EXP_AVG_H +#define DATA_TOME_EXP_AVG_H + +#include + +// RECOMMENDED: use double type for TypeOfSum +// WARNING: using this class with integer types may result in a loss of +// precision due to cumulative rounding of integer divisions. +template +class DataTomeExpAvg { + protected: + TypeOfSum _exp_avg; + unsigned long int _count; + + public: + DataTomeExpAvg() : _exp_avg(0), _count(0) {} + + DataTomeExpAvg &push(TypeOfSum input) { + if (_count >= ULONG_MAX) { + _exp_avg = 0; + _count = 0; + } + _count++; + + TypeOfSum multiplier = 2 / (_count); + _exp_avg = (input * multiplier) + (_exp_avg * (1 - multiplier)); + + return *this; + } + + TypeOfSum get() { return _exp_avg; } +}; + +#endif // DATA_TOME_EXP_AVG_H \ No newline at end of file diff --git a/src/DataTomeMvAvg.h b/src/DataTomeMvAvg.h index aa189df..c817261 100644 --- a/src/DataTomeMvAvg.h +++ b/src/DataTomeMvAvg.h @@ -71,11 +71,11 @@ class DataTomeMvAvg { TypeOfArray get() { return (_average_sum / ((_average_counter == 0) ? 1 : _average_counter)); } - TypeOfArray get(size_t n_points) { + inline TypeOfArray get(size_t n_points) { // @alias get_by_brute return get_by_brute(n_points); } - TypeOfArray mean() { + inline TypeOfArray mean() { // @alias get return get(); } @@ -131,7 +131,7 @@ class DataTomeMvAvg { size_t size_of_memory() { return _array_size * sizeof(TypeOfArray); } size_t point_count() { return _average_counter; } - size_t count() { + inline size_t count() { // @alias point_count return point_count(); } @@ -151,7 +151,7 @@ class DataTomeMvAvg { _array_size = new_size; return *this; } - DataTomeMvAvg &resize(size_t new_size) { + inline DataTomeMvAvg &resize(size_t new_size) { // @alias grow return grow(new_size); } @@ -207,7 +207,7 @@ class DataTomeMvAvg { return _partial_sums[id] / ((_average_counter == 0) ? 1 : _average_counter); } - TypeOfArray partial_mean(size_t id) { + inline TypeOfArray partial_mean(size_t id) { // @alias partial_get return partial_get(id); } @@ -228,7 +228,7 @@ class DataTomeMvAvg { else return _average_counter; } - size_t partial_count(size_t id) { + inline size_t partial_count(size_t id) { // @alias partial_point_count return partial_point_count(id); } diff --git a/test/test_DataTomeCumulative/DataTomeCumulative.test.cpp b/test/test_DataTomeCumulative/DataTomeCumulative.test.cpp new file mode 100644 index 0000000..728fccd --- /dev/null +++ b/test/test_DataTomeCumulative/DataTomeCumulative.test.cpp @@ -0,0 +1,52 @@ +#include +#include +#include + +void setUp(void) {} // before test + +void tearDown(void) {} // after test + +void test_getCumulativeAverage(void) { + DataTomeCumulative testMV; + + testMV.push(1); + testMV.push(2); + testMV.push(3); + + TEST_ASSERT_EQUAL(2, testMV.get()); +} + +void process() { + UNITY_BEGIN(); + + RUN_TEST(test_getCumulativeAverage); + + UNITY_END(); +} + +#ifdef ARDUINO + +#include +void setup() { + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + process(); +} + +void loop() { + digitalWrite(13, HIGH); + delay(200); + digitalWrite(13, LOW); + delay(200); +} + +#else + +int main(int argc, char **argv) { + process(); + return 0; +} + +#endif \ No newline at end of file diff --git a/test/test_DataTomeExpAvg/DataTomeExpAvg.test.cpp b/test/test_DataTomeExpAvg/DataTomeExpAvg.test.cpp new file mode 100644 index 0000000..efb36f4 --- /dev/null +++ b/test/test_DataTomeExpAvg/DataTomeExpAvg.test.cpp @@ -0,0 +1,51 @@ +#include +#include + +void setUp(void) {} // before test + +void tearDown(void) {} // after test + +void test_getExpAverage(void) { + DataTomeExpAvg testMV; + + testMV.push(1); + testMV.push(2); + testMV.push(3); + + TEST_ASSERT_EQUAL(2, testMV.get()); +} + +void process() { + UNITY_BEGIN(); + + RUN_TEST(test_getExpAverage); + + UNITY_END(); +} + +#ifdef ARDUINO + +#include +void setup() { + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + process(); +} + +void loop() { + digitalWrite(13, HIGH); + delay(200); + digitalWrite(13, LOW); + delay(200); +} + +#else + +int main(int argc, char **argv) { + process(); + return 0; +} + +#endif \ No newline at end of file