Go Down

### Topic: multiMap() function - integer linear interpolation (Read 16284 times)previous topic - next topic

#### robtillaart

##### Jan 26, 2011, 08:44 pm

After playing with my Sharp distancesensor - SHARP 2Y0A02 F 9Y - I needed a mapping from measurements upon centimeters. As the functions I found did not cover the whole range with the accuracy needed I decided to write a linear interpolation function. There was allready one on the playground called reMap() but that used floats, so I redid it in integer to improve performance and robustness. Furthermore I added some snippets how multiMap() can be used.

All remarks, improvements or good examples are - as allways - welcome.

Rob
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### graynomad

#1
##### Jan 27, 2011, 04:25 am
Sounds like a good idea, where's the code?

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

#### robtillaart

#2
##### Jan 28, 2011, 05:27 pmLast Edit: Feb 02, 2011, 12:17 am by robtillaart Reason: 1
Sorry - code and example snippets can be found on the playground - http://arduino.cc/playground/Main/MultiMap

Rob
[update 30-1-2011]
- didn't post the code here as small optimizations are planned
[update 2-2-2011]
- code on playground updated.

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### robtillaart

#3
##### Mar 23, 2011, 11:23 am
[update 23-03-2011]
- added float version
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### jmcgihon

I know this is an old thread, but I made it work with decremented IN values:

int multiMap(int val, int* _in, int* _out, uint8_t size)
{
uint8_t pos = 1;
if(_in[0] < _in[1]) {
if (val <= _in[0]) return _out[0];
if (val >= _in[size-1]) return _out[size-1];
if (val == _in[pos]) return _out[pos];
while(val > _in[pos]) pos++;
} else {
if (val >= _in[0]) return _out[0];
if (val <= _in[size-1]) return _out[size-1];
if (val == _in[pos]) return _out[pos];
while(val < _in[pos]) pos++;
}
// interpolate in the right segment for the rest
return map(val, _in[pos-1], _in[pos], _out[pos-1], _out[pos]);
}

#### HazardsMind

#5
##### Oct 31, 2014, 12:26 am
If you have the option of using both ints and floats, why not use one function and use a template?

#### robtillaart

#6
##### Oct 31, 2014, 04:28 pm
If you have the option of using both ints and floats, why not use one function and use a template?
would be better...
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### robtillaart

#7
##### Oct 31, 2014, 04:31 pm
I know this is an old thread, but I made it work with decremented IN values:
...
Thanks, can be really valuable in some cases (e.g. the IN array is measured runtime)
unfortunately it doubles the footprint,
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### robtillaart

#8
##### Oct 31, 2014, 04:57 pmLast Edit: Nov 18, 2014, 06:53 pm by robtillaart
would be better...
put this in a file multiMap.h and use it as a library
Code: [Select]
`#include "Arduino.h"template<typename T>T multiMap(T val, T* _in, T* _out, uint8_t size){  // take care the value is within range  // val = constrain(val, _in[0], _in[size-1]);  if (val <= _in[0]) return _out[0];  if (val >= _in[size-1]) return _out[size-1];  // search right interval  uint8_t pos = 1;  // _in[0] allready tested  while(val > _in[pos]) pos++;  // this will handle all exact "points" in the _in array  if (val == _in[pos]) return _out[pos];  // interpolate in the right segment for the rest  return (val - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];}`

demo sketch
Code: [Select]
`#include "multiMap.h"int in[] = {   11,22,33};int out[] = {  111,222,555};float fin[] = {   11,22,33};float fout[] = {  111,222,555};uint32_t start;uint32_t stop;void setup() {  Serial.begin(115200);  Serial.println("Start ");  start = micros();  float  x = multiMap<int>(12, in, out, 3);    stop = micros();  Serial.println(stop-start);    Serial.println(x);  start = micros();  float  y = multiMap<float>(12, fin, fout, 3);  stop = micros();  Serial.println(stop-start);  Serial.println(y);}void loop() {}`
disclaimer, not tested extensively.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### robtillaart

#9
##### Oct 31, 2014, 05:03 pm
updated the playground to have a reference
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

#### HazardsMind

#10
##### Oct 31, 2014, 10:44 pm
Thanks for the library Rob.

If you recall my old post about adding new core functions, I made a new map function with the aid of Pyro and useful criticism of Mark and Robin.

I never posted it, but here is my modified map version.
Code: [Select]
`template< typename A, typename B, typename C, typename D, typename E > auto map( const A &x, B &&in_min, C &&in_max, D &&out_min, E &&out_max ) -> decltype( A() * B() * C() * D() * E() ) {   typedef decltype( A() * B() * C() * D() * E() ) T_out;  //((x < in_min)? in_min : ((x > in_max)? in_max : x)) -> constrain without overhead  return (T_out)( ((x < in_min)? in_min : ((x > in_max)? in_max : x)) - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;}`

#### PaulS

#11
##### Nov 01, 2014, 01:54 am
Quote
I never posted it, but here is my modified map version.
Does it really make sense to be able to specify any type for each of the 5 arguments?

How would that deal with this:
Code: [Select]
`String valueToMap = "Hello world";int fromMin = 0;float fromMax =14.7;char toMin = 'G';char *toMax = "The answer to life, the universe and everything";godOnlyKnowsWhatKind mappedValue = map(valueToMap, fromMin, fromMax, toMin, toMax);`

#### HazardsMind

#12
##### Nov 01, 2014, 04:33 am
I never noticed it won't take values assigned to variables and it doesn't like Strings or strings, but everything else works. However I reverted it back to just this.

Code: [Select]
`template< typename A, typename B, typename C, typename D, typename E > auto map( const A x, B in_min, C in_max, D out_min, E out_max ) -> decltype( x * in_min * in_max * out_min * out_max ) {   typedef decltype( x * in_min * in_max * out_min * out_max) T_out;  //((x < in_min)? in_min : ((x > in_max)? in_max : x)) -> constrain without overhead  return (T_out)( ((x < in_min)? in_min : ((x > in_max)? in_max : x)) - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;}`

So no Strings and/or strings and were good to go.

Thanks Paul

#### PaulS

#13
##### Nov 01, 2014, 12:18 pm
Even disallowing Strings and strings, does it make sense to use different types for the from range (float fromMin, long fromMax), for instance? Does it make sense to use different types for the to range (long long toMin, byte toMax)?

C allows you to shoot yourself in the foot. C++'s templates allow you to blow your whole leg off.

This is a situation, I think, where a template is NOT a good idea.

#### robtillaart

#14
##### Nov 01, 2014, 01:11 pm
Thanks for sharing!

I think it is a great example what can be done with templates, but I agree with PaulS that using different types for all parameters is overkill for 99.9% of all sketches I have seen.

If a user wants performance (s)he inlines the formula
If a user wants precision (s)he either selects float (because it rounds better in the division) or uses long.

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Go Up