Last Modified: | August 31, 2017, at 04:50 AM |
By: | |
Platforms: | All |
One of the main applications for the Arduino board is measuring. Often the raw measurements e.g. from the analogRead() function must be converted to get a physical meaning. This convertion function is often linear but can also be more complex. In fact it can even be no function: meaning that a measured value can have two meanings. This is the case with the - SHARP 2Y0A02 F 9Y - distance sensor. Almost every output voltage can mean 2 different distances [graph]. Fortunely only the part from 20-150 cm is the "working range", so there is a function. In practice - using real measured points from the sensor - this function is not approximatable accurate enough with one mathematical function. I found the reMap() function on the playground http://interface.khm.de/index.php/lab/experiments/nonlinear-mapping/ but I wrote a new version to optimize it for speed and to make it a bit more robust.
The code:
code for floats see at the end.
As I only needed integers the function returns an int. The parameters are the int val which comes from the analogRead() function, an array of input values and an array of corresponding output values and a parameter to indicate the size of the array's used. Of course the arrays need to be equal in length (for the part used). NOTE: the input array must be a monotone increasing array of values.
First the input value is constrained to the input range, so we can do a search for the right interpolation segment (while loop). If the input value equals the lower bound of the array (index 0), the mapping in the last line cannot be one. So its handled separately. In fact this check is expanded to all known exact values. Finally a call to the well known map() function is made to do a linear interpolation in the right segment.
In a small test with an input array of 14 elements, 10.000 worst case calls took 1003 millis, 10.000 best case calls took 358 millis so on average 1361/2 => ~68 micros per call. This performance is most affected by the size of the array it has to search, so the general advice is keep the array as small as possible. Note these numbers are only indicative.
The performance can be improved by using binary search, especially when the array is "larger". A discussion about bin search is done on the forum - http://forum.arduino.cc/index.php?topic=205281 -
Some snippets shows how multiMap() can be used:
The multiMap function is not completely optimized, see TODO section. Note the parameter: - size - can be a smaller as the size of the array, in the normal distribution snippet it uses only half the array for the y value. I considered using a 2 dimensional array as that guarantees the arrays have equal length, but it would prevent reuse of arrays like in the trapezium snippet. Think an input array {0 - 1023} in ten steps would be very reusable.
Enjoy tinkering,
rob.tillaart@removethisgmail.com
See discussion on forum thread
(31 Oct 2014) added template version in discussion thread @top