├── .github ├── FUNDING.yml └── workflows │ ├── arduino-lint.yml │ ├── jsoncheck.yml │ └── arduino_test_runner.yml ├── keywords.txt ├── examples ├── multimap_timing │ ├── output_0.2.1.txt │ └── multimap_timing.ino ├── multimap_distance_two_types │ ├── output_0.2.0.txt │ └── multimap_distance_two_types.ino ├── multimap_demo │ └── multimap_demo.ino ├── multimap_distance │ └── multimap_distance.ino ├── multimap_2d │ └── multimap_2d.ino ├── multimap_BS_compare │ ├── multimap_BS_compare.ino │ └── output_0.2.1.txt ├── multimap_reverse_log │ └── multimap_reverse_log.ino ├── multimap_NTC │ └── multimap_NTC.ino ├── multimap_NTC_int_FAIL │ └── multimap_NTC_int_FAIL.ino └── multimap_functions │ └── multimap_functions.ino ├── library.properties ├── library.json ├── .arduino-ci.yml ├── LICENSE ├── CHANGELOG.md ├── test └── unit_test_001.cpp ├── MultiMap.h └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: RobTillaart 4 | custom: "https://www.paypal.me/robtillaart" 5 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax Colouring Map For multiMap 2 | 3 | # Data types (KEYWORD1) 4 | 5 | 6 | # Methods and Functions (KEYWORD2) 7 | multiMap KEYWORD2 8 | multiMapCache KEYWORD2 9 | multiMapBS KEYWORD2 10 | 11 | 12 | # Constants (LITERAL1) 13 | MULTIMAP_LIB_VERSION LITERAL1 14 | -------------------------------------------------------------------------------- /examples/multimap_timing/output_0.2.1.txt: -------------------------------------------------------------------------------- 1 | 2 | BOARD: UNO R3 3 | IDE: 1.8.19 4 | 5 | ...Arduino\libraries\MultiMap\examples\multimap_timing\multimap_timing.ino 6 | MULTIMAP_LIB_VERSION: 0.2.1 7 | 8 | time : 20 9 | 121.0000 10 | time : 88 11 | 121.0909 12 | 13 | done... 14 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=MultiMap 2 | version=0.2.1 3 | author=Rob Tillaart 4 | maintainer=Rob Tillaart 5 | sentence=Library for fast non-linear interpolation by means of two arrays. 6 | paragraph= 7 | category=Data Processing 8 | url=https://github.com/RobTillaart/MultiMap 9 | architectures=* 10 | includes=MultiMap.h 11 | depends= 12 | -------------------------------------------------------------------------------- /.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$" -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MultiMap", 3 | "keywords": "MultiMap, Map, interpolation, non-linear, linear", 4 | "description": "Library for fast non-linear interpolation by means of two arrays.", 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/MultiMap.git" 17 | }, 18 | "version": "0.2.1", 19 | "license": "MIT", 20 | "frameworks": "*", 21 | "platforms": "*", 22 | "headers": "MultiMap.h" 23 | } 24 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /examples/multimap_distance_two_types/output_0.2.0.txt: -------------------------------------------------------------------------------- 1 | BOARD: UNO R3 2 | IDE: 1.8.19 3 | 4 | Arduino\libraries\MultiMap\examples\multimap_distance_two_types\multimap_distance_two_types.ino 5 | MULTIMAP_LIB_VERSION: 0.2.0 6 | 7 | 80 150.00 150.00 150.00 8 | 81 150.00 150.00 150.00 9 | 82 150.00 150.00 150.00 10 | 83 150.00 150.00 150.00 11 | 84 150.00 150.00 150.00 12 | 85 150.00 150.00 150.00 13 | 86 150.00 150.00 150.00 14 | 87 150.00 150.00 150.00 15 | 88 150.00 150.00 150.00 16 | 89 150.00 150.00 150.00 17 | 90 150.00 150.00 150.00 18 | 19 | ... (reduced for readability) 20 | 21 | 500 20.61 20.61 20.61 22 | 501 20.51 20.51 20.51 23 | 502 20.41 20.41 20.41 24 | 503 20.31 20.31 20.31 25 | 504 20.20 20.20 20.20 26 | 505 20.10 20.10 20.10 27 | 506 20.00 20.00 20.00 28 | 507 20.00 20.00 20.00 29 | 508 20.00 20.00 20.00 30 | 509 20.00 20.00 20.00 31 | 510 20.00 20.00 20.00 32 | 511 20.00 20.00 20.00 33 | 34 | TIME1: 195.55 35 | TIME2: 116.33 36 | TIME3: 116.21 37 | 38 | Done... 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011-2025 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 | -------------------------------------------------------------------------------- /examples/multimap_demo/multimap_demo.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_demo.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: minimal demo 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // URL: https://forum.arduino.cc/t/messy-math-with-map-and-pow/1275821 7 | // https://wokwi.com/projects/401812192306752513 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | // not the IN array is not equidistant. 13 | // layout is to see the points of the graph. 14 | float in[] = { 0, 20, 40, 50, 60, 80, 90, 100, 105 }; 15 | float out[] = { 1, 2.8, 4.5, 7.0, 10.4, 33.5, 57.5, 100, 120 }; 16 | 17 | int size = 9; 18 | 19 | 20 | void setup() 21 | { 22 | // while(!Serial); 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println(__FILE__); 26 | Serial.print("MULTIMAP_LIB_VERSION: "); 27 | Serial.println(MULTIMAP_LIB_VERSION); 28 | Serial.println(); 29 | 30 | Serial.println("X\tY"); 31 | 32 | for (int i = 0; i <= 500; i++) 33 | { 34 | float x = i * 0.2; 35 | float y = multiMap(x, in, out, size); 36 | Serial.print(x); 37 | Serial.print("\t"); 38 | Serial.println(y, 1); // 1 decimal 39 | } 40 | } 41 | 42 | 43 | void loop() 44 | { 45 | } 46 | 47 | 48 | // -- END OF FILE -- 49 | -------------------------------------------------------------------------------- /examples/multimap_distance/multimap_distance.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_distance.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // example simulates the lookup graph of a distance sensor 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | 13 | void setup() 14 | { 15 | // while(!Serial); 16 | Serial.begin(115200); 17 | Serial.println(); 18 | Serial.println(__FILE__); 19 | Serial.print("MULTIMAP_LIB_VERSION: "); 20 | Serial.println(MULTIMAP_LIB_VERSION); 21 | Serial.println(); 22 | 23 | for (int i = 80; i < 512; i++) 24 | { 25 | float distance = sharp2cm(i); 26 | // Serial.print('\t'); 27 | // Serial.print(i); 28 | // Serial.print('\t'); 29 | Serial.println(distance, 1); 30 | } 31 | Serial.println("Done..."); 32 | } 33 | 34 | 35 | void loop() 36 | { 37 | } 38 | 39 | 40 | // for a sharp distance range finder 41 | float sharp2cm(int val) 42 | { 43 | // out[] holds the distances in cm 44 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 45 | 46 | // in[] holds the measured analogRead() values for that distance 47 | float in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 48 | 49 | float dist = multiMap(val, in, out, 14); 50 | return dist; 51 | } 52 | 53 | 54 | // -- END OF FILE -- 55 | 56 | -------------------------------------------------------------------------------- /examples/multimap_timing/multimap_timing.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_timing.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // example measures the performance of multiMap vs 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | int in[] = { 11, 22, 33}; 13 | int out[] = {111, 222, 555}; 14 | 15 | float fin[] = { 11, 22, 33}; 16 | float fout[] = {111, 222, 555}; 17 | 18 | uint32_t start; 19 | uint32_t stop; 20 | 21 | 22 | void setup() 23 | { 24 | // while(!Serial); 25 | Serial.begin(115200); 26 | Serial.println(); 27 | Serial.println(__FILE__); 28 | Serial.print("MULTIMAP_LIB_VERSION: "); 29 | Serial.println(MULTIMAP_LIB_VERSION); 30 | Serial.println(); 31 | delay(100); 32 | 33 | start = micros(); 34 | float x = multiMap(12, in, out, 3); 35 | stop = micros(); 36 | Serial.print("time : \t"); 37 | Serial.println(stop - start); 38 | Serial.println(x, 4); 39 | delay(10); // make sure print has ended 40 | 41 | start = micros(); 42 | float y = multiMap(12, fin, fout, 3); 43 | stop = micros(); 44 | Serial.print("time : \t"); 45 | Serial.println(stop - start); 46 | Serial.println(y, 4); 47 | delay(10); // make sure print has ended 48 | 49 | Serial.println("\ndone..."); 50 | } 51 | 52 | 53 | void loop() 54 | { 55 | } 56 | 57 | 58 | // -- END OF FILE -- 59 | 60 | -------------------------------------------------------------------------------- /examples/multimap_2d/multimap_2d.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_2d.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo mapping an angle on a semi-circle (2D {x, y} position) 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // when printing into a plotter one sees a rough sine and cosine 8 | // with an amplitude of 5, approximated with interpolation. 9 | 10 | #include "MultiMap.h" 11 | 12 | float in[] = { 0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330, 360 }; 13 | float xc[] = { 5, 4, 3, 0, -3, -4, -5, -4, -3, 0, 3, 4, 5 }; 14 | float yc[] = { 0, 3, 4, 5, 4, 3, 0, -3, -4, -5, -4, -3, 0 }; 15 | 16 | 17 | int size = 13; 18 | 19 | 20 | void setup() 21 | { 22 | // while(!Serial); 23 | Serial.begin(115200); 24 | Serial.println(); 25 | Serial.println(__FILE__); 26 | Serial.print("MULTIMAP_LIB_VERSION: "); 27 | Serial.println(MULTIMAP_LIB_VERSION); 28 | Serial.println(); 29 | 30 | Serial.println("I\tX\tY"); 31 | 32 | for (int i = 0; i <= 360; i++) 33 | { 34 | // 2D mapping is doen by mapping twice (not too efficient but it works sort of. 35 | float x = multiMap(i, in, xc, size); 36 | float y = multiMap(i, in, yc, size); 37 | // Serial.print(i); 38 | // Serial.print("\t"); 39 | Serial.print(x, 1); // 1 decimal 40 | Serial.print("\t"); 41 | Serial.println(y, 1); // 1 decimal 42 | } 43 | } 44 | 45 | 46 | void loop() 47 | { 48 | } 49 | 50 | 51 | // -- END OF FILE -- 52 | -------------------------------------------------------------------------------- /examples/multimap_BS_compare/multimap_BS_compare.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_BS_compare.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo binary search - performance 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | 7 | 8 | #include "MultiMap.h" 9 | 10 | long in[100]; 11 | long out[100]; 12 | 13 | volatile int x; 14 | 15 | 16 | void setup() 17 | { 18 | // while(!Serial); 19 | Serial.begin(115200); 20 | Serial.println(); 21 | Serial.println(__FILE__); 22 | Serial.print("MULTIMAP_LIB_VERSION: "); 23 | Serial.println(MULTIMAP_LIB_VERSION); 24 | Serial.println(); 25 | 26 | for (int i = 0; i < 100; i++) 27 | { 28 | in[i] = i * 11; 29 | out[i] = i * 7; 30 | } 31 | 32 | Serial.println("size\tt1\tt2\tratio"); 33 | // make sure print has ended 34 | delay(100); 35 | for (int size = 4; size < 100; size++) 36 | { 37 | compare(size); 38 | delay(10); 39 | } 40 | } 41 | 42 | 43 | void loop() 44 | { 45 | } 46 | 47 | 48 | void compare(uint8_t size) 49 | { 50 | uint32_t start, dur1, dur2; 51 | 52 | start = micros(); 53 | for (int i = 0; i < 1024; i++) 54 | { 55 | x = multiMap(i, in, out, size); 56 | } 57 | dur1 = micros() - start; 58 | 59 | 60 | start = micros(); 61 | for (int i = 0; i < 1024; i++) 62 | { 63 | x = multiMapBS(i, in, out, size); 64 | } 65 | dur2 = micros() - start; 66 | Serial.print(size); 67 | Serial.print("\t"); 68 | Serial.print(dur1); 69 | Serial.print("\t"); 70 | Serial.print(dur2); 71 | Serial.print("\t"); 72 | Serial.println(100.0 * dur2 / dur1); 73 | delay(25); 74 | } 75 | 76 | 77 | // -- END OF FILE -- 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log MultiMap 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.2.1] - 2025-07-17 10 | - update readme.md 11 | - add multiMap_demo.ino 12 | - add multiMap_2d.ino; simulates map float => {x, y} position. 13 | - minor edits 14 | 15 | ## [0.2.0] - 2023-11-12 16 | - add multi type **multiMap** to reduce RAM and speed up lookup. 17 | - add multi type **multiMapBS** binary search version. 18 | - add example for multi type 19 | - update examples 20 | - update readme.md 21 | - minor edits 22 | 23 | ---- 24 | 25 | ## [0.1.7] - 2023-06-24 26 | - add **multiMapCache()**, experimental version that caches the last value. 27 | to be used with input that do not change often. 28 | - add **multiMapBS()**, experimental version that uses binary search. 29 | to be used with arrays > 10 (rule of thumb) 30 | - add examples 31 | - major rewrite readme.md 32 | 33 | ## [0.1.6] - 2022-11-17 34 | - add RP2040 in build-CI 35 | - add changelog.md 36 | - update readme.md 37 | - clean up unit test 38 | 39 | ## [0.1.5] - 2021-12-22 40 | - update library.json 41 | - update readme 42 | - update license 43 | - minor edits 44 | 45 | ## [0.1.4] - 2021-05-27 46 | - fix Arduino-lint 47 | 48 | ## [0.1.3] - 2021-01-02 49 | - add Arduino-CI 50 | 51 | ## [0.1.2] - 2020-06-19 52 | - fix library.json 53 | 54 | ## [0.1.1] - 2020-04-09 55 | 56 | ## [0.1.0] - 2015-03-29 57 | 58 | ---- 59 | 60 | ..... eons passed ... 61 | 62 | ----- 63 | 64 | ## [0.0.1] - 2011-01-26 65 | - initial version. 66 | 67 | -------------------------------------------------------------------------------- /examples/multimap_reverse_log/multimap_reverse_log.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_reverse_logarithmic.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | 7 | 8 | #include "MultiMap.h" 9 | 10 | 11 | long in[10] = { 0, 150, 300, 550, 850, 970, 1009, 1017, 1021, 1023 }; 12 | long rlog10[10] = { 1023, 1000, 972, 909, 761, 586, 390, 264, 102, 0}; 13 | 14 | 15 | volatile int x; 16 | 17 | 18 | // as formula 19 | int revlog10(int raw) 20 | { 21 | return round((1023 / 3.01) * log10(1023 - raw)); 22 | } 23 | 24 | 25 | void setup() 26 | { 27 | // while(!Serial); 28 | Serial.begin(115200); 29 | Serial.println(); 30 | Serial.println(__FILE__); 31 | Serial.print("MULTIMAP_LIB_VERSION: "); 32 | Serial.println(MULTIMAP_LIB_VERSION); 33 | Serial.println(); 34 | delay(100); 35 | 36 | // determine gain 37 | performance(); 38 | delay(5000); 39 | 40 | // test range input values 41 | for (int i = 0; i < 1024; i++) 42 | { 43 | int y = multiMap(i, in, rlog10, 10); 44 | Serial.print(i); 45 | Serial.print("\t"); 46 | Serial.print(y); 47 | Serial.print("\t"); 48 | Serial.print(revlog10(i)); 49 | Serial.println(); 50 | } 51 | Serial.println(); 52 | delay(5000); 53 | } 54 | 55 | 56 | void loop() 57 | { 58 | int raw = analogRead(A0); 59 | int val = multiMap(raw, in, rlog10, 10); 60 | Serial.print(raw); 61 | Serial.print("\t"); 62 | Serial.print(val); 63 | Serial.println(); 64 | delay(10); 65 | } 66 | 67 | 68 | void performance() 69 | { 70 | uint32_t start, dur1, dur2; 71 | 72 | start = micros(); 73 | for (int i = 0; i < 1024; i++) 74 | { 75 | x = multiMap(i, in, rlog10, 10); 76 | } 77 | dur1 = micros() - start; 78 | Serial.print("TIME MM: \t"); 79 | Serial.println(dur1); 80 | delay(100); 81 | 82 | start = micros(); 83 | for (int i = 0; i < 1024; i++) 84 | { 85 | x = revlog10(i); 86 | } 87 | dur2 = micros() - start; 88 | Serial.print("TIME FUNC: \t"); 89 | Serial.println(dur2); 90 | Serial.print("FACTOR: \t"); 91 | Serial.println(1.0 * dur2 / dur1); 92 | Serial.println(); 93 | delay(100); 94 | } 95 | 96 | 97 | // -- END OF FILE -- 98 | -------------------------------------------------------------------------------- /examples/multimap_NTC/multimap_NTC.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_NTC.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // example uses multiMap to calculate the temperature from an 10K NTC. 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | uint32_t start; 13 | uint32_t stop; 14 | 15 | volatile float x, y, z; 16 | 17 | // Note this is a bit an extreme example, 18 | // normally you only make a multimap of the working range 19 | 20 | 21 | float in[] = { 22 | 0, 1, 3, 8, 13, 20, 25, 32, 50, 60, 72, 85, 100, 145, 200, 250, 300, 400, 500, 600, 650, 700, 753, 800, 830, 870, 900, 936, 964, 985, 1000, 1017, 1023 23 | }; 24 | 25 | float out[] = { 26 | -273.15, -71.65, -60.69, -49.81, -43.97, -38.50, -35.54, -32.16, -25.72, -22.95, -20.08, -17.37, -14.62, -7.90, -1.43, 3.57, 27 | 8.08, 16.34, 24.30, 32.64, 37.17, 42.13, 48.05, 54.19, 58.75, 66.03, 72.87, 83.85, 96.51, 111.46, 129.49, 182.82, 301.82 28 | }; 29 | 30 | int sz = 33; 31 | 32 | 33 | void setup() 34 | { 35 | // while(!Serial); 36 | Serial.begin(115200); 37 | Serial.println(); 38 | Serial.println(__FILE__); 39 | Serial.print("MULTIMAP_LIB_VERSION: "); 40 | Serial.println(MULTIMAP_LIB_VERSION); 41 | Serial.println(); 42 | delay(100); 43 | 44 | start = micros(); 45 | x = val(z); 46 | stop = micros(); 47 | Serial.println(stop - start); 48 | delay(10); // make sure print has ended 49 | 50 | start = micros(); 51 | x = multiMap(z, in, out, sz); 52 | stop = micros(); 53 | Serial.println(stop - start); 54 | delay(10); // make sure print has ended 55 | 56 | for (int i = 0; i < 1024; i++) 57 | { 58 | x = val(i); 59 | y = multiMap(i, in, out, sz); 60 | z = abs(x - y); 61 | 62 | Serial.print(i); 63 | Serial.print('\t'); 64 | Serial.print(x); 65 | Serial.print('\t'); 66 | Serial.print(y); 67 | Serial.print('\t'); 68 | Serial.print(z); 69 | Serial.println(); 70 | } 71 | 72 | } 73 | 74 | 75 | void loop() 76 | { 77 | } 78 | 79 | 80 | // NTC formula 81 | float val(int sensorValueA1) 82 | { 83 | int R10k_ntc = 9870; 84 | float U10k_ntc = sensorValueA1 * (5.0 / 1024.0); 85 | float Untc = 5.0 - U10k_ntc; 86 | float Rntc = (R10k_ntc * Untc) / U10k_ntc; 87 | float Temp = (298.15 / (1 - (298.15 / 4300.0) * log(10000.0 / Rntc))) - (273.15 + 0); 88 | 89 | return Temp; 90 | } 91 | 92 | 93 | // -- END OF FILE -- 94 | 95 | -------------------------------------------------------------------------------- /examples/multimap_NTC_int_FAIL/multimap_NTC_int_FAIL.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_NTC_int_FAIL.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo of faulty optimizing 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // NOTE: 8 | // use integers instead of floats to minimize RAM. uses ~320 bytes PROGMEM ~120 bytes RAM less on UNO than float version 9 | // 10 | // this example is added to show how to reduce memory but also how it can FAIL due to math overflow 11 | // E.g. see around 196-200; 340-400 12 | // to prevent this one must have more values which increases the memory usage again. 13 | 14 | 15 | 16 | #include "MultiMap.h" 17 | 18 | uint32_t start; 19 | uint32_t stop; 20 | 21 | volatile float x, y, z; 22 | 23 | 24 | int in[] = { 25 | 0, 1, 3, 8, 13, 20, 25, 32, 50, 60, 72, 85, 100, 145, 200, 250, 300, 400, 500, 600, 650, 700, 753, 800, 830, 870, 900, 936, 964, 985, 1000, 1017, 1023 26 | }; 27 | 28 | int out[] = { 29 | -27315, -7165, -6069, -4981, -4397, -3850, -3554, -3216, -2572, -2295, -2008, -1737, -1462, -790, -143, 357, 808, 1634, 2430, 3264, 30 | 3717, 4213, 4805, 5419, 5875, 6603, 7287, 8385, 9651, 11146, 12949, 18282, 30182 31 | }; 32 | 33 | int sz = 33; 34 | 35 | 36 | void setup() 37 | { 38 | // while(!Serial); 39 | Serial.begin(115200); 40 | Serial.println(); 41 | Serial.println(__FILE__); 42 | Serial.print("MULTIMAP_LIB_VERSION: "); 43 | Serial.println(MULTIMAP_LIB_VERSION); 44 | Serial.println(); 45 | delay(100); // make sure print has ended 46 | 47 | start = micros(); 48 | x = val(z); 49 | stop = micros(); 50 | Serial.println(stop - start); 51 | delay(10); // make sure print has ended 52 | 53 | start = micros(); 54 | x = 0.01 * multiMap(z, in, out, sz); 55 | stop = micros(); 56 | Serial.println(stop - start); 57 | delay(10); // make sure print has ended 58 | 59 | for (int i = 0; i < 1024; i++) 60 | { 61 | x = val(i); 62 | y = 0.01 * multiMap(i, in, out, sz); 63 | z = abs(x - y); 64 | 65 | Serial.print(i); 66 | Serial.print('\t'); 67 | Serial.print(x); 68 | Serial.print('\t'); 69 | Serial.print(y); 70 | Serial.print('\t'); 71 | Serial.print(z); 72 | Serial.println(); 73 | } 74 | } 75 | 76 | 77 | void loop() 78 | { 79 | } 80 | 81 | 82 | // NTC formula 83 | float val(int sensorValueA1) 84 | { 85 | int R10k_ntc = 9870; 86 | float U10k_ntc = sensorValueA1 * (5.0 / 1024.0); 87 | float Untc = 5.0 - U10k_ntc; 88 | float Rntc = (R10k_ntc * Untc) / U10k_ntc; 89 | float Temp = (298.15 / (1 - (298.15 / 4300.0) * log(10000.0 / Rntc))) - (273.15 + 0); 90 | 91 | return Temp; 92 | } 93 | 94 | 95 | // -- END OF FILE -- 96 | 97 | -------------------------------------------------------------------------------- /examples/multimap_BS_compare/output_0.2.1.txt: -------------------------------------------------------------------------------- 1 | BOARD: UNO R3 2 | IDE: 1.8.19 3 | 4 | 5 | Arduino\libraries\MultiMap\examples\multimap_BS_compare\multimap_BS_compare.ino 6 | MULTIMAP_LIB_VERSION: 0.2.1 7 | 8 | size t1 t2 ratio 9 | 4 5616 5596 99.64 10 | 5 6204 6204 100.00 11 | 6 6804 6832 100.41 12 | 7 7436 7464 100.38 13 | 8 8096 8088 99.90 14 | 9 8772 8720 99.41 15 | 10 9476 9364 98.82 16 | 11 10204 10020 98.20 17 | 12 10956 10664 97.33 18 | 13 11728 11320 96.52 19 | 14 12528 11968 95.53 20 | 15 13348 12628 94.61 21 | 16 14196 13276 93.52 22 | 17 15060 13924 92.46 23 | 18 15948 14600 91.55 24 | 19 16860 15276 90.60 25 | 20 17808 15948 89.56 26 | 21 18772 16628 88.58 27 | 22 19752 17300 87.59 28 | 23 20760 17972 86.57 29 | 24 21792 18652 85.59 30 | 25 22852 19332 84.60 31 | 26 23924 20004 83.61 32 | 27 25032 20676 82.60 33 | 28 26156 21356 81.65 34 | 29 27312 22028 80.65 35 | 30 28476 22712 79.76 36 | 31 29680 23388 78.80 37 | 32 30896 24064 77.89 38 | 33 32140 24736 76.96 39 | 34 33408 25436 76.14 40 | 35 34704 26132 75.30 41 | 36 36016 26828 74.49 42 | 37 37360 27524 73.67 43 | 38 38720 28220 72.88 44 | 39 40104 28916 72.10 45 | 40 41516 29620 71.35 46 | 41 42944 30316 70.59 47 | 42 44400 31016 69.86 48 | 43 45880 31716 69.13 49 | 44 47388 32408 68.39 50 | 45 48904 33116 67.72 51 | 46 50456 33808 67.00 52 | 47 52036 34512 66.32 53 | 48 53632 35208 65.65 54 | 49 55252 35908 64.99 55 | 50 56896 36608 64.34 56 | 51 58560 37308 63.71 57 | 52 60256 38004 63.07 58 | 53 61964 38708 62.47 59 | 54 63708 39400 61.84 60 | 55 65468 40104 61.26 61 | 56 67248 40804 60.68 62 | 57 69064 41500 60.09 63 | 58 70892 42200 59.53 64 | 59 72748 42900 58.97 65 | 60 74628 43604 58.43 66 | 61 76532 44300 57.88 67 | 62 78460 45000 57.35 68 | 63 80408 45700 56.84 69 | 64 82380 46404 56.33 70 | 65 84376 47108 55.83 71 | 66 86392 47828 55.36 72 | 67 88440 48544 54.89 73 | 68 90508 49264 54.43 74 | 69 92596 49984 53.98 75 | 70 94716 50700 53.53 76 | 71 96844 51424 53.10 77 | 72 99008 52148 52.67 78 | 73 101196 52872 52.25 79 | 74 103400 53588 51.83 80 | 75 105636 54308 51.41 81 | 76 107892 55028 51.00 82 | 77 110164 55760 50.62 83 | 78 112472 56476 50.21 84 | 79 114796 57200 49.83 85 | 80 117148 57916 49.44 86 | 81 119520 58644 49.07 87 | 82 121912 59364 48.69 88 | 83 124332 60088 48.33 89 | 84 126780 60812 47.97 90 | 85 129244 61532 47.61 91 | 86 131740 62252 47.25 92 | 87 134248 62976 46.91 93 | 88 136784 63700 46.57 94 | 89 139348 64420 46.23 95 | 90 141936 65136 45.89 96 | 91 144540 65864 45.57 97 | 92 147172 66588 45.25 98 | 93 149832 67304 44.92 99 | 94 152508 68028 44.61 100 | 95 152704 68132 44.62 101 | 96 152708 68156 44.63 102 | 97 152708 68208 44.67 103 | 98 152704 68208 44.67 104 | 99 152704 68236 44.69 105 | -------------------------------------------------------------------------------- /examples/multimap_distance_two_types/multimap_distance_two_types.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_distance_two_types.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo two types 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // example simulates the lookup graph of a distance sensor 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | uint32_t start, stop; 13 | 14 | volatile float dist; 15 | 16 | void setup() 17 | { 18 | // while(!Serial); 19 | Serial.begin(115200); 20 | Serial.println(); 21 | Serial.println(__FILE__); 22 | Serial.print("MULTIMAP_LIB_VERSION: "); 23 | Serial.println(MULTIMAP_LIB_VERSION); 24 | Serial.println(); 25 | delay(100); 26 | 27 | for (int i = 80; i < 512; i++) 28 | { 29 | float distance1 = sharp2cm1(i); 30 | float distance2 = sharp2cm2(i); 31 | float distance3 = sharp2cm3(i); 32 | Serial.print(i); 33 | Serial.print('\t'); 34 | Serial.print(distance1, 2); 35 | Serial.print('\t'); 36 | Serial.print(distance2, 2); 37 | Serial.print('\t'); 38 | Serial.println(distance3, 2); 39 | } 40 | Serial.println(); 41 | delay(1000); 42 | 43 | 44 | start = micros(); 45 | for (int i = 100; i < 500; i++) 46 | { 47 | dist = sharp2cm1(i); 48 | } 49 | stop = micros(); 50 | Serial.print("TIME1: "); 51 | Serial.println((stop - start) / 400.0, 2); 52 | delay(100); 53 | 54 | 55 | start = micros(); 56 | for (int i = 100; i < 500; i++) 57 | { 58 | dist = sharp2cm2(i); 59 | } 60 | stop = micros(); 61 | Serial.print("TIME2: "); 62 | Serial.println((stop - start) / 400.0, 2); 63 | delay(100); 64 | 65 | 66 | start = micros(); 67 | for (int i = 100; i < 500; i++) 68 | { 69 | dist = sharp2cm3(i); 70 | } 71 | stop = micros(); 72 | Serial.print("TIME3: "); 73 | Serial.println((stop - start) / 400.0, 2); 74 | delay(100); 75 | 76 | Serial.println("\nDone..."); 77 | } 78 | 79 | 80 | void loop() 81 | { 82 | } 83 | 84 | 85 | // for a sharp distance range finder 86 | float sharp2cm1(int val) 87 | { 88 | // out[] holds the distances in cm 89 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 90 | 91 | // in[] holds the measured analogRead() values for that distance 92 | float in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 93 | 94 | float dist = multiMap(val, in, out, 14); 95 | return dist; 96 | } 97 | 98 | 99 | // for a sharp distance range finder 100 | float sharp2cm2(int val) 101 | { 102 | // out[] holds the distances in cm 103 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 104 | 105 | // in[] holds the measured analogRead() values for that distance 106 | int in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 107 | 108 | float dist = multiMap(val, in, out, 14); 109 | return dist; 110 | } 111 | 112 | 113 | // for a sharp distance range finder 114 | float sharp2cm3(int val) 115 | { 116 | // out[] holds the distances in cm 117 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 118 | 119 | // in[] holds the measured analogRead() values for that distance 120 | int in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 121 | 122 | float dist = multiMapBS(val, in, out, 14); 123 | return dist; 124 | } 125 | 126 | 127 | // -- END OF FILE -- 128 | -------------------------------------------------------------------------------- /test/unit_test_001.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: unit_test_001.cpp 3 | // AUTHOR: Rob Tillaart 4 | // DATE: 2021-01-02 5 | // PURPOSE: unit tests for the multiMap 6 | // https://github.com/RobTillaart/MultiMap 7 | // https://github.com/Arduino-CI/arduino_ci/blob/master/REFERENCE.md 8 | // 9 | 10 | // supported assertions 11 | // ---------------------------- 12 | // assertEqual(expected, actual); // a == b 13 | // assertNotEqual(unwanted, actual); // a != b 14 | // assertComparativeEquivalent(expected, actual); // abs(a - b) == 0 or (!(a > b) && !(a < b)) 15 | // assertComparativeNotEquivalent(unwanted, actual); // abs(a - b) > 0 or ((a > b) || (a < b)) 16 | // assertLess(upperBound, actual); // a < b 17 | // assertMore(lowerBound, actual); // a > b 18 | // assertLessOrEqual(upperBound, actual); // a <= b 19 | // assertMoreOrEqual(lowerBound, actual); // a >= b 20 | // assertTrue(actual); 21 | // assertFalse(actual); 22 | // assertNull(actual); 23 | 24 | // // special cases for floats 25 | // assertEqualFloat(expected, actual, epsilon); // fabs(a - b) <= epsilon 26 | // assertNotEqualFloat(unwanted, actual, epsilon); // fabs(a - b) >= epsilon 27 | // assertInfinity(actual); // isinf(a) 28 | // assertNotInfinity(actual); // !isinf(a) 29 | // assertNAN(arg); // isnan(a) 30 | // assertNotNAN(arg); // !isnan(a) 31 | 32 | #include 33 | 34 | 35 | #include "MultiMap.h" 36 | 37 | 38 | unittest_setup() 39 | { 40 | fprintf(stderr, "MULTIMAP_LIB_VERSION: %s\n", (char *) MULTIMAP_LIB_VERSION); 41 | } 42 | 43 | 44 | unittest_teardown() 45 | { 46 | } 47 | 48 | 49 | unittest(test_float) 50 | { 51 | // based on the distance example 52 | // out[] holds the distances in cm 53 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 54 | // in[] holds the measured analogRead() values for that distance 55 | float in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 56 | 57 | assertEqualFloat(150.000, multiMap(80, in, out, 14), 0.001); 58 | assertEqualFloat(136.250, multiMap(100, in, out, 14), 0.001); 59 | assertEqualFloat(65.4545, multiMap(200, in, out, 14), 0.001); 60 | assertEqualFloat(42.7419, multiMap(300, in, out, 14), 0.001); 61 | assertEqualFloat(30.8791, multiMap(400, in, out, 14), 0.001); 62 | assertEqualFloat(20.6122, multiMap(500, in, out, 14), 0.001); 63 | assertEqualFloat(20.0000, multiMap(600, in, out, 14), 0.001); 64 | } 65 | 66 | 67 | /* todo 68 | unittest(test_uint32_t) 69 | { 70 | // based on the distance example 71 | // out[] holds the distances in cm 72 | uint32_t out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 73 | // in[] holds the measured analogRead() values for that distance 74 | uint32_t in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 75 | 76 | assertEqual(150, multiMap(80, in, out, 14) ); 77 | assertEqual(136, multiMap(100, in, out, 14) ); 78 | assertEqual( 65, multiMap(200, in, out, 14) ); 79 | assertEqual( 42, multiMap(300, in, out, 14) ); 80 | assertEqual( 30, multiMap(400, in, out, 14) ); 81 | assertEqual( 20, multiMap(500, in, out, 14) ); 82 | assertEqual( 20, multiMap(600, in, out, 14) ); 83 | } 84 | */ 85 | 86 | unittest_main() 87 | 88 | 89 | // -------- 90 | -------------------------------------------------------------------------------- /examples/multimap_functions/multimap_functions.ino: -------------------------------------------------------------------------------- 1 | // 2 | // FILE: multimap_functions.ino 3 | // AUTHOR: Rob Tillaart 4 | // PURPOSE: demo (use serial plotter)... 5 | // URL: https://github.com/RobTillaart/MultiMap 6 | // 7 | // example show use of multiMap to approximate some well known functions. 8 | 9 | 10 | #include "MultiMap.h" 11 | 12 | 13 | void setup() 14 | { 15 | // while(!Serial); 16 | Serial.begin(115200); 17 | Serial.println(); 18 | Serial.println(__FILE__); 19 | Serial.print("MULTIMAP_LIB_VERSION: "); 20 | Serial.println(MULTIMAP_LIB_VERSION); 21 | Serial.println(); 22 | delay(100); 23 | 24 | test_normal_distribution(); 25 | test_sinus(); 26 | lest_log10(); 27 | test_exp2(); 28 | test_exp3(); 29 | test_sawtooth(); 30 | 31 | Serial.println("\nDone..."); 32 | } 33 | 34 | 35 | void loop() 36 | { 37 | } 38 | 39 | 40 | void test_normal_distribution() 41 | { 42 | // sort of normal distribution 43 | long norm_dist[] = { 0, 5, 20, 50, 80, 95, 100, 95, 80, 50, 20, 5, 0 }; // 13 44 | long in[13]; 45 | for (int i = 0; i < 13; i++) in[i] = round(i * 1000.0 / 12); 46 | for (int i = 0; i <= 1000; i += 10) 47 | { 48 | long y = multiMap(i, in, norm_dist, 13); 49 | // Serial.print(i); 50 | // Serial.print(" => "); 51 | Serial.println(y); 52 | delay(10); 53 | } 54 | } 55 | 56 | 57 | void test_sinus() 58 | { 59 | // one sinus wave, amplitude 1023 60 | long sinus[] = {0, 316, 601, 827, 972, 1023, 972, 827, 601, 316, 0, -316, -601, -827, -972, -1023, -972, -827, -601, -316, 0 }; //21 61 | long in[21]; 62 | for (int i = 0; i < 21; i++) in[i] = round(i * 1000.0 / 20); 63 | for (int i = 0; i <= 1000; i += 10) 64 | { 65 | long y = multiMap(i, in, sinus, 21); 66 | // Serial.print(i); 67 | // Serial.print(" => "); 68 | Serial.println(y); 69 | delay(10); 70 | } 71 | } 72 | 73 | 74 | void lest_log10() 75 | { 76 | // log10 * 100 77 | long _log10[] = { -1000000, 460, 529, 570, 599, 621, 639, 655, 668, 680, 690}; // size 11 78 | long in[11]; 79 | for (int i = 0; i < 11; i++) in[i] = round(i * 1000.0 / 10); 80 | for (int i = 100; i <= 1000; i += 10) 81 | { 82 | long y = multiMap(i, in, _log10, 11); 83 | // Serial.print(i); 84 | // Serial.print(" => "); 85 | Serial.println(y); 86 | delay(10); 87 | } 88 | } 89 | 90 | 91 | void test_exp2() 92 | { 93 | // 2^x 94 | long _exp2[] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; // size 12 95 | long in[12]; 96 | for (int i = 0; i < 12; i++) in[i] = round(i * 1000.0 / 11); 97 | for (int i = 0; i <= 1000; i += 10) 98 | { 99 | long y = multiMap(i, in, _exp2, 12); 100 | // Serial.print(i); 101 | // Serial.print(" => "); 102 | Serial.println(y); 103 | delay(10); 104 | } 105 | } 106 | 107 | 108 | void test_exp3() 109 | { 110 | // 3^x 111 | long _exp3[] = { 0, 1, 3, 9, 27, 81, 243, 729, 2187, 6561 }; // size 10 112 | long in[10]; 113 | for (int i = 0; i < 10; i++) in[i] = round(i * 1000.0 / 9); 114 | for (int i = 0; i <= 1000; i += 10) 115 | { 116 | long y = multiMap(i, in, _exp3, 10); 117 | // Serial.print(i); 118 | // Serial.print(" => "); 119 | Serial.println(y); 120 | delay(10); 121 | } 122 | } 123 | 124 | 125 | void test_sawtooth() 126 | { 127 | long sawtooth[] = { 0, 1000, 0, -1000, 0, 1000, -1000, 0 }; // size 8 128 | long in[8]; 129 | for (int i = 0; i < 8; i++) in[i] = round(i * 1000.0 / 7); 130 | for (int i = 0; i <= 1000; i += 10) 131 | { 132 | long y = multiMap(i, in, sawtooth, 8); 133 | // Serial.print(i); 134 | // Serial.print(" => "); 135 | Serial.println(y); 136 | delay(10); 137 | } 138 | } 139 | 140 | 141 | // -- END OF FILE -- 142 | 143 | -------------------------------------------------------------------------------- /MultiMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // FILE: MultiMap.h 4 | // AUTHOR: Rob Tillaart 5 | // VERSION: 0.2.1 6 | // DATE: 2011-01-26 7 | // PURPOSE: Arduino library for fast non-linear mapping or interpolation of values 8 | // URL: https://github.com/RobTillaart/MultiMap 9 | // URL: http://playground.arduino.cc/Main/MultiMap 10 | 11 | 12 | 13 | #define MULTIMAP_LIB_VERSION (F("0.2.1")) 14 | 15 | 16 | #include "Arduino.h" 17 | 18 | 19 | //////////////////////////////////////////////////////////////////////// 20 | // 21 | // SINGLE TYPE MULTIMAP - LINEAR SEARCH - the reference 22 | // 23 | // note: the in array must have increasing values 24 | template 25 | T multiMap(T value, T* _in, T* _out, uint8_t size) 26 | { 27 | // output is constrained to out array 28 | if (value <= _in[0]) return _out[0]; 29 | if (value >= _in[size-1]) return _out[size-1]; 30 | 31 | // search right interval 32 | uint8_t pos = 1; // _in[0] already tested 33 | while(value > _in[pos]) pos++; 34 | 35 | // this will handle all exact "points" in the _in array 36 | if (value == _in[pos]) return _out[pos]; 37 | 38 | // interpolate in the right segment for the rest 39 | return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1]; 40 | } 41 | 42 | 43 | //////////////////////////////////////////////////////////////////////// 44 | // 45 | // SINGLE TYPE MULTIMAP CACHE - LINEAR SEARCH 46 | // 47 | // note: the in array must have increasing values 48 | // performance optimized version if inputs do not change often 49 | // e.g. 2 2 2 2 2 3 3 3 3 5 5 5 5 5 5 8 8 8 8 5 5 5 5 5 50 | // implements a minimal cache of the lastValue. 51 | template 52 | T multiMapCache(T value, T* _in, T* _out, uint8_t size) 53 | { 54 | static T lastValue = -1; // possible bug for 1st call 55 | static T cache = -1; 56 | 57 | if (value == lastValue) 58 | { 59 | return cache; 60 | } 61 | lastValue = value; 62 | 63 | // output is constrained to out array 64 | if (value <= _in[0]) 65 | { 66 | cache = _out[0]; 67 | } 68 | else if (value >= _in[size-1]) 69 | { 70 | cache = _out[size-1]; 71 | } 72 | else 73 | { 74 | // search right interval; index 0 _in[0] already tested 75 | uint8_t pos = 1; 76 | while(value > _in[pos]) pos++; 77 | 78 | // this will handle all exact "points" in the _in array 79 | if (value == _in[pos]) 80 | { 81 | cache = _out[pos]; 82 | } 83 | else 84 | { 85 | // interpolate in the right segment for the rest 86 | cache = (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1]; 87 | } 88 | } 89 | return cache; 90 | } 91 | 92 | 93 | //////////////////////////////////////////////////////////////////////// 94 | // 95 | // SINGLE TYPE MULTIMAP - BINARY SEARCH 96 | // 97 | // should be faster for size >= 10 98 | // (rule of thumb) 99 | // 100 | // note: the in array must have increasing values 101 | template 102 | T multiMapBS(T value, T* _in, T* _out, uint8_t size) 103 | { 104 | // output is constrained to out array 105 | if (value <= _in[0]) return _out[0]; 106 | if (value >= _in[size-1]) return _out[size-1]; 107 | 108 | // Binary Search, uint16_t needed to prevent overflow. 109 | uint16_t lower = 0; 110 | uint16_t upper = size - 1; 111 | while (lower < upper - 1) 112 | { 113 | uint8_t mid = (lower + upper) / 2; 114 | if (value >= _in[mid]) lower = mid; 115 | else upper = mid; 116 | } 117 | 118 | return (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]) + _out[lower]; 119 | } 120 | 121 | 122 | //////////////////////////////////////////////////////////////////////// 123 | // 124 | // MULTITYPE MULTIMAP - LINEAR SEARCH 125 | // 126 | // note: the in array must have increasing values 127 | template 128 | T2 multiMap(T1 value, T1* _in, T2* _out, uint8_t size) 129 | { 130 | // output is constrained to out array 131 | if (value <= _in[0]) return _out[0]; 132 | if (value >= _in[size-1]) return _out[size-1]; 133 | 134 | // search right interval 135 | uint16_t pos = 1; // _in[0] already tested 136 | while(value > _in[pos]) pos++; 137 | 138 | // this will handle all exact "points" in the _in array 139 | if (value == _in[pos]) return _out[pos]; 140 | 141 | // interpolate in the right segment for the rest 142 | return (value - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1]; 143 | } 144 | 145 | 146 | //////////////////////////////////////////////////////////////////////// 147 | // 148 | // MULTITYPE MULTIMAP - BINARY SEARCH 149 | // should be faster for size >= 10 150 | // (rule of thumb) 151 | // 152 | // note: the in array must have increasing values 153 | template 154 | T2 multiMapBS(T1 value, T1* _in, T2* _out, uint8_t size) 155 | { 156 | // output is constrained to out array 157 | if (value <= _in[0]) return _out[0]; 158 | if (value >= _in[size-1]) return _out[size-1]; 159 | 160 | // Binary Search, uint16_t needed to prevent overflow. 161 | uint16_t lower = 0; 162 | uint16_t upper = size - 1; 163 | while (lower < upper - 1) 164 | { 165 | uint16_t mid = (lower + upper) / 2; 166 | if (value >= _in[mid]) lower = mid; 167 | else upper = mid; 168 | } 169 | 170 | return (value - _in[lower]) * (_out[upper] - _out[lower]) / (_in[upper] - _in[lower]) + _out[lower]; 171 | } 172 | 173 | 174 | // -- END OF FILE -- 175 | 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Arduino CI](https://github.com/RobTillaart/MultiMap/workflows/Arduino%20CI/badge.svg)](https://github.com/marketplace/actions/arduino_ci) 3 | [![Arduino-lint](https://github.com/RobTillaart/MultiMap/actions/workflows/arduino-lint.yml/badge.svg)](https://github.com/RobTillaart/MultiMap/actions/workflows/arduino-lint.yml) 4 | [![JSON check](https://github.com/RobTillaart/MultiMap/actions/workflows/jsoncheck.yml/badge.svg)](https://github.com/RobTillaart/MultiMap/actions/workflows/jsoncheck.yml) 5 | [![GitHub issues](https://img.shields.io/github/issues/RobTillaart/MultiMap.svg)](https://github.com/RobTillaart/MultiMap/issues) 6 | 7 | [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/RobTillaart/MultiMap/blob/master/LICENSE) 8 | [![GitHub release](https://img.shields.io/github/release/RobTillaart/MultiMap.svg?maxAge=3600)](https://github.com/RobTillaart/MultiMap/releases) 9 | [![PlatformIO Registry](https://badges.registry.platformio.org/packages/robtillaart/library/MultiMap.svg)](https://registry.platformio.org/libraries/robtillaart/MultiMap) 10 | 11 | 12 | # MultiMap 13 | 14 | Arduino library for fast non-linear mapping or interpolation of values. 15 | 16 | 17 | ## Description 18 | 19 | In Arduino applications often the 'raw' value of a sensor is mapped upon a more 20 | usable value. E.g. the value of analogRead() 0 .. 1023 is mapped onto 0 .. 5.0 Volt. 21 | This is often done by the **map()** function which does a linear interpolation 22 | with integer truncating. 23 | 24 | This means in code: 25 | 26 | ```cpp 27 | output = C1 + input * C2 28 | ``` 29 | 30 | As C1 and C2 are to be determined, Arduino has the **map()** function that calculates the 31 | two variables runtime from two given mapping points (I1, O1) and (I2, O2). 32 | 33 | ```cpp 34 | output = map(input, I1, I2, O1, O2): 35 | ``` 36 | 37 | In many cases when there is no linear mapping possible as the 'points' are not 38 | on a single straight line. Think a logarithmic or some sine wave form. 39 | To solve this one needs non-linear math to calculate the output from a given input. 40 | 41 | The **multiMap()** function simulates this math by approximating the non-linear function with multiple 42 | linear line segments. 43 | Of course this approximation introduces an error. 44 | By increasing the number of points and choose their position strategically the average error 45 | can and will be reduced. 46 | An important feature of the **multiMap()** is that the points do not need to have the same 47 | distance (non-equidistant). This allows to have more pointe where needed (curvy line) and 48 | less point where possible (straight lines). 49 | 50 | Note: some functions are hard to approximate even with **multiMap()** as they go to infinity 51 | or have a singularity. 52 | Think of **tan(x)** around x = PI/2 (90°) or **sin(1/x)** around zero. 53 | 54 | The function to approximate must be (mathematically) an injection. 55 | This means that for every input value (in the given range) there is an output value. 56 | However there might be more than one input value mapping onto the same output value. 57 | See - https://en.wikipedia.org/wiki/Bijection,_injection_and_surjection 58 | 59 | 60 | ### Related 61 | 62 | Other mapping libraries and interesting links. 63 | 64 | - https://github.com/RobTillaart/FastMap 65 | - https://github.com/RobTillaart/Gamma 66 | - https://github.com/RobTillaart/map2bits 67 | - https://github.com/RobTillaart/map2colour 68 | - https://github.com/RobTillaart/moduloMap 69 | - https://github.com/RobTillaart/MultiMap 70 | - https://mycurvefit.com/ 71 | - https://www.desmos.com/calculator 72 | 73 | 74 | ## Interface 75 | 76 | ```cpp 77 | #include "MultiMap.h" 78 | ``` 79 | 80 | 81 | ### Usage 82 | 83 | The basic call for **multiMap()** is: 84 | 85 | ```cpp 86 | output = Multimap(input, inputArray, outputArray, size); 87 | ``` 88 | 89 | **multiMap()** needs two equally sized arrays representing the reference 'points' named 90 | **inputArray\[\]** and **outputArray\[\]** both of the **datatype**. 91 | 92 | **multiMap()** will do a lookup of the input value in the **inputArray\[\]**. 93 | If it cannot find the index of an exact point it will determine a weighted position between two points. 94 | This optional weighted point is used to interpolate a value from the data in the **outputArray\[\]**. 95 | 96 | - The **inputArray\[\]** must have increasing values, 97 | there is no such restriction for the **output\[\]** array. 98 | - The values of the **inputArray\[\]** do not need to have the same distance (non-equidistant). 99 | E.g a sort of logarithmic input array like { 1, 10, 100, 1000 } is valid. 100 | - **multiMap()** automatically constrains the output to the first and last value in the **outputArray\[\]**. 101 | This is a explicit difference with the **map()** function. 102 | Therefore it is important to extend the range of the arrays to cover all possible input and output values. 103 | 104 | 105 | ## Performance 106 | 107 | The **multiMap()** function does a linear search for the inputValue in the **inputArray\[\]**. 108 | This implies that usage of larger and more precise arrays will take more time. 109 | Furthermore "low" input values will be found faster than "high" values, so the function 110 | has no constant time execution. 111 | 112 | To optimize performance a binary search version exists, see **multiMapBS()** below. 113 | 114 | As every usage of **multiMap()** is unique one should always do a performance check to see 115 | if there is a substantial gain in the case at hand. In my experience there often is. 116 | 117 | 118 | ### MultiMapBS 119 | 120 | Experimental 0.1.7 => use with care. 121 | 122 | **multiMapBS()** stands for MultiMapBinarySearch or MMBS for short. 123 | It is a very similar function as **multiMap()** with the same interface. 124 | The main difference is that MMBS uses binary search instead of linear search. 125 | 126 | Performance tests indicate that for array sizes of about 10 elements, 127 | the **multiMapBS()** is on par with **multiMap()**. 128 | This is an expected value as both need on average about 5 steps to find 129 | the right interval to interpolate. 130 | 131 | Be sure to do your own tests to see if MMBS improves your performance. 132 | 133 | 134 | ### MultiMapCache 135 | 136 | Experimental 0.1.7 => use with care. 137 | 138 | **multiMapCache()** or MMC for short, is a very similar function as **multiMap()**. 139 | The main difference is that MMC caches the last input and output value. 140 | The goal is to improve the performance by preventing searching a repeating input 141 | value over and over again. 142 | 143 | If the input sequence has a lot of repeating values e.g. 2 2 2 2 2 2 5 5 5 5 5 4 4 4 4 2 2 2 2 2 2 144 | MMC will be able to return the value from cache often. 145 | Otherwise keeping cache is overhead. 146 | 147 | Be sure to do your own tests to see if MMC improves your performance. 148 | 149 | A possible variation is to cache the last interval - lower and upper index. 150 | It would allow a to test that value and improve the linear search. 151 | (not implemented, to be investigated). 152 | 153 | 154 | ### MultiMap two types 155 | 156 | Experimental 0.2.0 => use with care. 157 | 158 | **multiMap()** or MMTT for short, is a very similar function as **multiMap()**. 159 | The main difference is that MMTT uses two different types, typical the input 160 | is an integer type and the output is a float or double type. 161 | It is expected that there will be a gain if two different sized integer types are used. 162 | So performance is platform specific. 163 | 164 | See the example **multimap_distance_two_types.ino** 165 | 166 | ```cpp 167 | // for a sharp distance range finder (based upon graph datasheet). 168 | float sharp2cm2(int value) 169 | { 170 | // out[] holds the distances in cm 171 | float out[] = {150, 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20}; 172 | 173 | // in[] holds the measured analogRead() values for that distance 174 | int in[] = { 90, 97, 105, 113, 124, 134, 147, 164, 185, 218, 255, 317, 408, 506}; 175 | 176 | float distance = multiMap(value, in, out, 14); 177 | return distance; 178 | } 179 | ``` 180 | 181 | First tests indicate that using the int type for the input in the example 182 | is substantial (~37%) faster per call. Tested on UNO R3, time in micros per call. 183 | 184 | | types | time us | call | 185 | |:-------:|:---------:|:-------| 186 | | 1 | 194.93 | ```float dist = multiMap(value, in, out, 14);``` | 187 | | 2 | 121.97 | ```float dist = multiMap(value, in, out, 14);``` | 188 | 189 | Furthermore it is obvious that there is less need for RAM if the integer type is smaller 190 | in size than the float type. 191 | 192 | Be sure to do your own tests to see if MMTT improves your performance. 193 | 194 | 195 | ## Operation 196 | 197 | See examples 198 | 199 | Please note the fail example as this shows that in the intern math overflow can happen. 200 | 201 | 202 | ## Future 203 | 204 | #### Must 205 | 206 | - improve documentation 207 | 208 | #### Should 209 | 210 | - investigate multiMapCache behaviour 211 | - determine overhead. 212 | - extend unit tests 213 | - multi type versions 214 | 215 | #### Could 216 | 217 | - Investigate class implementation 218 | - basic call ```out = mm.map(value);``` 219 | - runtime adjusting input and output array **begin(in[], out[])** 220 | - performance / footprint 221 | - less parameter passing 222 | - **isInRange(value)**? 223 | - caching last value / position / index (does that help?) 224 | - flag if input value was "IN_MIN" < input < "IN_MAX", 225 | now it is constrained without user being informed. 226 | - Investigate a 2D multiMap, 3D multiMap 227 | - is it possible / feasible? (YES | ??) 228 | - float ==> complex (Y) 229 | - e.g. time ==> XY position (Y) 230 | - complex ==> complex (?) 231 | - X ==> { Y, Z } in fact 2 output arrays. 232 | - needs other API call. 233 | - can be implemented with multiple single MM calls, 234 | one per dimension. 235 | - can the single type version be implemented with the two type version? 236 | 237 | #### Wont 238 | 239 | - should the lookup tables be merged into one array of pairs? 240 | - you cannot reuse e.g. the input array or the output array then. 241 | this would not improve the memory footprint. 242 | 243 | 244 | ## Support 245 | 246 | If you appreciate my libraries, you can support the development and maintenance. 247 | Improve the quality of the libraries by providing issues and Pull Requests, or 248 | donate through PayPal or GitHub sponsors. 249 | 250 | Thank you, 251 | 252 | --------------------------------------------------------------------------------