Class from map function - FastMap

I've been thinking to make a class of the map() function. Goal is to perform substantial faster as a big part of the map() function is often constant but includes an expensive division.

map.init(in_min, in_max, out_min, out_max)
{
  _in_min = in_min;
  _in_max = in_max;
  _out_min = out_min;
  _out_max = out_max;
  _factor = float(out_max - out_min)/(in_max - in_min);
}

map.map(value)
{
  return (value - _in_min) * factor;
}

map.constrainedMap(value)
{
  if (value <= _in_min) return _out_min;
  if (value >= _in_max) return _out_max;
  return (value - _in_min) * factor;
}

map.lowerConstrainedMap()...
map.upperConstrainedMap()...

any thoughts what could also be in it?

any thoughts what could also be in it?

Some types for the variables?
Proper syntax for the function declaration (map::init()).
A return type?

An overloaded method that deals with floats?

The fastmap class can be found on GITHUB

The fastmap class minimizes the time to do a mapping by doing most math in advance. The code "removes" three subtractions and one division from the map formula. The code is using float as that gave the fastest performance and is usable for all datatypes. Using floats results in slightly different values as the map() function truncates its math.

The test application fastmapper.ino does a first order timing in which the - float based - fastMap class is approx 60 times faster than the - long based - map() function. The price for this is 24 bytes RAM. Note that 16 bytes of RAM are used for three constrainedMap() methods that are also part of the class.

The three constrainedMap() methods do mapping with a constrain on the lower input, upper input or both. These are not in the sample sketch below.

test application

//
//    FILE: fastMapDemo.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.02
// PURPOSE: demo of FastMap class ==> a faster map function
//    DATE: 2014-11-02
//     URL:
//
// Released to the public domain
//

#include "FastMap.h"

uint32_t start;
uint32_t stop;
uint32_t reference;

volatile long zz = 3000, yy = 20000;
volatile float x;

FastMap mapper;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start fastMapDemo\nlib version: ");
  Serial.println(FASTMAP_LIB_VERSION);
  Serial.println();

  // Get a non optimizable value;
  int z = analogRead(A0);

  // REFERENCE
  start = micros();
  for (int i = 0; i < 10000; i++)
  {
    x = map(z, 0, 1023, yy, zz);
  }
  stop = micros();
  reference = stop-start;
  Serial.println(reference);
  Serial.print(z);
  Serial.print(" -> ");
  Serial.println(x);
  Serial.println();

  // FASTMAP
  mapper.init(0, 1023, yy, zz);
  start = micros();
  for (int i = 0; i < 10000; i++)
  {
    x = mapper.map(z);
  }
  stop = micros();
  Serial.println(stop-start);
  Serial.print(z);
  Serial.print(" -> ");
  Serial.println(x);

  // GAIN
  Serial.print("Performance factor: ");
  Serial.println(reference/(stop-start));
}

void loop()
{
}
//
// END OF FILE
//

OUTPUT:

Start fastmap demo
lib version: 0.1.02

572960
250 -> 15846.00

9512
250 -> 15845.55
Performance factor: 60

updated code + link

One final comment on the name of the class. The Arduino (and many other systems) convention is that class names start with capital letters, and all words are capitalized. So, your class should be FastMap, not fastmap.

good point, will rename it a.s.a.p.

a simple constrainedMap demo

//
//    FILE: constrainedMap.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.01
// PURPOSE: demo of FastMap class ==> merge map and constrain functions
//    DATE: 2014-11-02
//     URL: 
//
// Released to the public domain
//

#include "FastMap.h"

FastMap mapper;

void setup() 
{
  Serial.begin(115200);
  Serial.print("Start constrainedMap demo\nlib version: ");
  Serial.println(FASTMAP_LIB_VERSION);
  Serial.println();

  mapper.init(0, 10, 0, 300);

  Serial.println("I\tMAP\tLCM\tUCM\tCM");
  for (int i = -5; i < 20; i++)
  {
    float a = map(i, 0, 10, 0, 300);
    float b = mapper.lowerConstrainedMap(i);
    float c = mapper.upperConstrainedMap(i);
    float d = mapper.constrainedMap(i);
    Serial.print(i);
    Serial.print("\t");
    Serial.print(a);
    Serial.print("\t");
    Serial.print(b);
    Serial.print("\t");
    Serial.print(c);
    Serial.print("\t");
    Serial.println(d);
  }
  Serial.println("\ndone...");
}

void loop(){}

OUTPUT:

Start constrainedMap demo
lib version: 0.1.03

I	MAP	LCM	UCM	CM
-5	-150.00	0.00	-150.00	0.00
-4	-120.00	0.00	-120.00	0.00
-3	-90.00	0.00	-90.00	0.00
-2	-60.00	0.00	-60.00	0.00
-1	-30.00	0.00	-30.00	0.00
0	0.00	0.00	0.00	0.00
1	30.00	30.00	30.00	30.00
2	60.00	60.00	60.00	60.00
3	90.00	90.00	90.00	90.00
4	120.00	120.00	120.00	120.00
5	150.00	150.00	150.00	150.00
6	180.00	180.00	180.00	180.00
7	210.00	210.00	210.00	210.00
8	240.00	240.00	240.00	240.00
9	270.00	270.00	270.00	270.00
10	300.00	300.00	300.00	300.00
11	330.00	330.00	300.00	300.00
12	360.00	360.00	300.00	300.00
13	390.00	390.00	300.00	300.00
14	420.00	420.00	300.00	300.00
15	450.00	450.00	300.00	300.00
16	480.00	480.00	300.00	300.00
17	510.00	510.00	300.00	300.00
18	540.00	540.00	300.00	300.00
19	570.00	570.00	300.00	300.00

done...

LCM = lowerConstrainedMap() - only constrain lower side
UCM = upperConstrainedMap() - only constrain upper side
CM = constrainedMap() - constrain both ends

some remarks wrt the constrain functions

the lowerConstrainMap() can be used to prevent the result go below a certain value.
E.g mapping some sensors on the speed of a motor that cannot go below zero.

the upperConstrainMap() can be used to prevent the result go above a certain value.
E.g. preventing that a frequency goes beyond 20000Hz (human limit)

the constrainMap() can be used to keep the result between certain values.
E.g. to keep a PWM signal between 0 and 255;

I think the names are well descriptive but a bit long.


Thinking about this class gave me several ideas how to continue with this class.

  • create a single map function with constrain parameters -> bit mask...

y = map(x); // default is no constrain
y = map(x, CONSTRAIN_NONE); // CONSTRAIN_NONE = 0; default
y = map(x, CONSTRAIN_LOWER); // CONSTRAIN_LOWER = 1
y = map(x, CONSTRAIN_UPPER); // CONSTRAIN_UPPER = 2
y = map(x, CONSTRAIN_BOTH); // CONSTRAIN_BOTH = 3 = CONSTRAIN_LOWER | CONSTRAIN_UPPER

strong point is that one function supports all constrain types.


the FastMap class can at the cost of 2 floats RAM (and a bit of flash) create an inverse method, which is very similar to map itself. It allows to "map the other way, in reverse". e.g. one instance of FastMap can do both Celsius2Fahrenheit or Fahrenheit2Celsius...

c = temperature.map(f);
f = temperature.back(c);

or mapping between voltage and RPM and back.

Another example, using 2 mappers side by side (and the one that inspired the "back" function above)

//
//    FILE: fastMapDemo2.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.02
// PURPOSE: demo of FastMap class ==> a faster map function
//    DATE: 2014-11-02
//     URL:
//
// Released to the public domain
//

#include "FastMap.h"

FastMap CtoF;
FastMap FtoC;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start fastMapDemo2\nlib version: ");
  Serial.println(FASTMAP_LIB_VERSION);
  Serial.println();

  CtoF.init(0, 100, 32, 212);
  FtoC.init(32, 212, 0, 100);

  float f = FtoC.map(163);
  Serial.print(f);
  Serial.print(char(176));
  Serial.println('C');

  float c = CtoF.map(f);
  Serial.print(c);
  Serial.print(char(176)); // degree symbol
  Serial.println('F');
}

void loop()
{
}
//
// END OF FILE
//

implemented the back function. Works like a charm (code will be asap on github)

//    FILE: fastMapDemo3.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo of FastMap class ==> a faster map function
//    DATE: 2014-11-02
//     URL:
//
// Released to the public domain
//

#include "FastMap.h"

FastMap CtoF;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start fastMapDemo3\nlib version: ");
  Serial.println(FASTMAP_LIB_VERSION);
  Serial.println();

  CtoF.init(0, 100, 32, 212);

  float f = CtoF.map(163);
  Serial.print(f);
  Serial.print(char(176));
  Serial.println('F');

  float c = CtoF.back(f);
  Serial.print(c);
  Serial.print(char(176));
  Serial.println('C');
}

void loop()
{
}
//
// END OF FILE
//

OUTPUT:

Start fastMapDemo3
lib version: 0.1.04

325.40°F
163.00°C

draft implementation of the constrain bit mask demo

//    FILE: fastMapDemo3.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: demo of FastMap class ==> a faster map function
//    DATE: 2014-11-02
//     URL: http://forum.arduino.cc/index.php?topic=276194
//
// Released to the public domain
//

#include "FastMap.h"

FastMap CtoF;

void setup()
{
  Serial.begin(115200);
  Serial.print("Start fastMapDemo3\nlib version: ");
  Serial.println(FASTMAP_LIB_VERSION);
  Serial.println();

  CtoF.init(0, 100, 32, 212);

  float f = CtoF.map(163);
  Serial.print(f);
  Serial.print(char(176));
  Serial.println('F');

  float c = CtoF.back(f);
  Serial.print(c);
  Serial.print(char(176));
  Serial.println('C');

  f = CtoF.map(163, CONSTRAIN_UPPER);
  Serial.print(f);
  Serial.print(char(176));
  Serial.println('F');

  c = CtoF.back(f, CONSTRAIN_BOTH);
  Serial.print(c);
  Serial.print(char(176));
  Serial.println('C');

}

void loop()
{
}
//
// END OF FILE
//

OUTPUT:
Start fastMapDemo3
lib version: 0.1.04

325.40°F
163.00°C
212.00°F
100.00°C

so the draft 0.1.04 works quite well,

tomorrow investigate speed and footprint ...

perfomance is decreased extremely, so this 0.1.04 version will not come out of the labs soon :wink:

Start fastMapDemo
lib version: 0.1.04

572956
255 -> 15763.00

233340
255 -> 15762.46
Performance factor: 2

Created an 0.1.05 version that supports the back() function. (bit-mask experiment removed)
See post #8 for demo code.

Posted the 0.1.05 version on - Arduino/libraries/FastMap at master · RobTillaart/Arduino · GitHub -

As always remarks and comments are welcome.

Created an 0.1.06 version

  • replaced float with double to support ARM (DUE) boards.

Performance test on MEGA2560

Start fastMapDemo
lib version: 0.1.06

593276
357 -> 14068.00

7676
357 -> 14067.45
Performance factor: 77

Latest version on - Arduino/libraries/FastMap at master · RobTillaart/Arduino · GitHub -

As always remarks and comments are welcome.