Arduino Forum

Development => Other Software Development => Topic started by: robtillaart on Jan 26, 2011, 08:44 pm

Title: multiMap() function - integer linear interpolation
Post by: robtillaart on 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
Title: Re: multiMap() function - integer linear interpolation
Post by: graynomad on Jan 27, 2011, 04:25 am
Sounds like a good idea, where's the code?

______
Rob
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on Jan 28, 2011, 05:27 pm
Sorry - code and example snippets can be found on the playground - http://arduino.cc/playground/Main/MultiMap (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.

Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on Mar 23, 2011, 11:23 am
[update 23-03-2011]
- added float version
Title: Re: multiMap() function - integer linear interpolation - decremented IN values
Post by: jmcgihon on Oct 30, 2014, 11:21 pm
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]);
}
Title: Re: multiMap() function - integer linear interpolation
Post by: HazardsMind on 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?
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on 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...
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on 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,
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on Oct 31, 2014, 04:57 pm
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.
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on Oct 31, 2014, 05:03 pm
updated the playground to have a reference
Title: Re: multiMap() function - integer linear interpolation
Post by: HazardsMind on Oct 31, 2014, 10:44 pm
Thanks for the library Rob.

If you recall my old post (http://forum.arduino.cc/index.php?topic=265085.0) 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;
}
Title: Re: multiMap() function - integer linear interpolation
Post by: PaulS on 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);
Title: Re: multiMap() function - integer linear interpolation
Post by: HazardsMind on 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
Title: Re: multiMap() function - integer linear interpolation
Post by: PaulS on 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.
Title: Re: multiMap() function - integer linear interpolation
Post by: robtillaart on 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.

Title: Re: multiMap() function - integer linear interpolation
Post by: HazardsMind on Nov 01, 2014, 07:40 pm
The whole point to allowing multiple types was for decltype to choose the largest type and make it the returning result.

Originally I only allowed one type, but then you lose precision if the first type (valueToMap) is smaller then the rest. So allowing multiple types solves that, but as Paul pointed out, it can also cause problems if the data entered is foolish. A possible scenario for new programmers, i'm sure, but we can't prevent everything.
Title: Re: multiMap() function - integer linear interpolation
Post by: PaulS on Nov 02, 2014, 04:34 pm
Quote
but as Paul pointed out, it can also cause problems
I wasn't picking on your efforts. I've never had the need for templates, so I've not studied up on them. I don't know if you can create a template that takes different types, and still require that some of the function arguments must all be the same type. I guess it's time to get over the lack of knowledge of templates.
Title: Re: multiMap() function - integer linear interpolation
Post by: HazardsMind on Nov 03, 2014, 02:35 am
Quote
I wasn't picking on your efforts.
I wasn't saying you were, its just helpful criticism for me and others who read these posts.
Title: Re: multiMap() function - integer linear interpolation
Post by: pYro_65 on Nov 03, 2014, 09:41 am
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.
Silly use of any code is a bad idea, and yes it does make sense if you understand the declaration.

You can read the post where I created this function (apart from the single map algo line that hazardsmind changed) and you will see why the different template parameters matter. http://forum.arduino.cc/index.php?topic=265085.msg1874470#msg1874470 (http://forum.arduino.cc/index.php?topic=265085.msg1874470#msg1874470)

And there is no reason why you cannot expand on type safety allowing only the types that you want. There is always a bigger picture :)
Title: Re: multiMap() function - integer linear interpolation
Post by: stevedarman on Feb 17, 2015, 12:58 pm
How do I set up a library for multiMap
I have a error multiMap was not declared in this scope
Title: Re: multiMap() function - integer linear interpolation
Post by: paulnd on Apr 10, 2018, 02:07 pm
Hello robtillaart,

I was wondering if you ever got round to reviewing the to-do list on Arduino Playground?

Quote
- Check if binary search is faster
- Implement a simple cache that holds the last used value (some projects would benefit)
I'll be grateful for any suggestions to optimise multimap to make it any faster if possible?

Paul
Title: Re: multiMap() function - integer linear interpolation
Post by: itsalive13 on May 06, 2018, 12:28 am
Hi,
I've been trying to get multiMap to work, but it seems to be all over the place for me.
Code I'm using is below. I've simplified it down, and set the map data so input and output is the same, and it still doesn't output consistently. What am I doing wrong?
Thanks

#include <LiquidCrystal.h>

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

int outputreading = 0;
// note: the _in array should have increasing values
int multiMap(int val, int* _in, int* _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];
}

void setup() {
Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop() {
lcd.setCursor(0, 0);
int out[] = {0,205,409,615,819,1021};
int in[] = {0,205,409,615,819,1021};
int val = analogRead(A0);
outputreading = multiMap(val, in, out, 6);
lcd.print(outputreading);
}
Title: Re: multiMap() function - integer linear interpolation
Post by: itsalive13 on May 22, 2018, 12:44 am
16 days, I finally figured out the issue.

32,767 is exceeded during calculations