Go Down

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

robtillaart


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

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 pm Last 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

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

If you have the option of using both ints and floats, why not use one function and use a template?
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

robtillaart

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

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 pm Last 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

Rob Tillaart

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

HazardsMind

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;
}
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

PaulS

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

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
My GitHub:
https://github.com/AndrewMascolo?tab=repositories

PaulS

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

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