# How to map 10-bit ADC value to a range with restrictions

I need to map a 10-bit ADC i.e. [0-1023] to a range [1-100]. The mapping formula itself is the direct "y=(x-a)/(b-a)*(d-c)+c" to map x in [a-b] to y in [c-d].

Input x is calculated as the average of say 1000 ADC readings.

My problem is this. The input to ADC is a potentiometer and i need to calculate x ONLY when the potentiometer is stationary. How do you detect this? The way i am currently doing it is to read sets of 1000 ADC values (on a timer), calculate their average and compare the "current avg" with the "previous avg". If "current avg - previous avg" is some "+-delta" i assume the potentiometer is stationary and do the calculation.

Is there a better way of doing this?

Edit: To provide some more data;

• I am using a C8051F850 MCU from SiLabs running at 24.5MHz
• ADC samples are collected every 100us
• Avg of 2500 samples ~ 250ms is calculated per set. Thus two avgs are compared every 500ms.
• My "delta" is 10 units (approx of 1024/100)
• Integer arithmetic so rounding errors
• I don't understand what the range and equation has to do with the problem. Maybe you should just remove it and focus on your actual problem here. I think the actual problem you're facing is some sort of XY problem though.
– pipe
Jun 10, 2017 at 4:21
• @pipe - I presented the complete problem and my current solution so that the entire context is clear. It works but i am not happy with it and hence my question. If there something specific that you would like me to clarify? Jun 10, 2017 at 5:27
• use software low pass filter Jun 10, 2017 at 8:09

This is 'hypothesis testing in the presence of noise'.

When we are hypothesis testing, we define a pair of conditions we want to discriminate between, then define a test to best do that.

You have defined a condition 'is stationary', and a test |(current_avg - previous_avg)| < delta. Implicit in that definition is another two conditions, moving up and moving down.

While the definition of 'is stationary' is not well defined, if we reverse engineer the test you've devised to find out what that really means, the change in average position taken between the first 1000 and the next 1000 of a 2000 long sample of positions, we find it's quite a reasonable thing to do.

The average of 1000 samples is the best way to compute an average position, if we assume the noise on each sample is independent. It's also quick, uses low resources, is easy to understand, and uses low memory (does not need to store all the inputs).

The difference between the first and second set gives you the best estimate of the mean speed of the pot over that time.

Now consider this test case. For the first 1000 samples, the pot is slewing quickly up, for the next 1000 it's slewing quickly down. The mean movement over that period, as computed by your estimator, will be zero.

Are you happy that this condition would be detected as stationary? If no, then we have to change our definition of stationary. According to your comment, you are not happy with something. But what?

As you see, the problem is not with the test itself, but with (a) your poor definition of what 'stationary' means and (b) your poor definition of what you find unsatisfactory about the performance of the present test.

The test you have defined is the most reasonable test there is for detecting average velocity in the presence of noise. Whether this works well with your real world physics and user behaviour is another matter.

If you find you actually need to detect acceleration, to make it better in your case, then it's quite easy to take three successive averaged samples, compute both deltas, then compute the delta-delta.

If you find that a change of average size, to change the rate at which it can detect changes, works better, then do that.

There are other alternatives that test different hypotheses. The first would be to compute two running means with different time constants. This is a common filter to use as an edge detector in image processing, but will also detect 'edges' in one dimensional data. As the filters are recursive, this is quite cheap to implement. However, you would have to tune the two time constants to your situation, so how would you get it tuned better than the one time constant of your present tester?

The second is to do a FFT of your samples. You can then compare the energy in the DC bin (amount of stationary) with the energy in the low frequency bins (amount of movement, acceleration, change of acceleration ... ) while discarding the energy in the high frequency bins (reading noise). But that needs significant memory and processing power, and seems like overkill.

A third, based on your comment about latency being the problem, would be to simultaneously estimate the position and the slope of the line by linear regression. There is a recursive formula for this, so it's relatively efficient for you to compute in real time with no more memory. This would allow you to 'correct' for the delay in collecting the data by extrapolating both estimates forward to get the averaged position 'now'. Note that linear regression can have dynamic range problems if done for large data sets, and as you're using integer arithmetic, you'd have to think carefully about implementation. One way to improve things would be to decimate, and run linear regression at a much lower sample rate, more consistent with the speed that your real world pot is being moved, and the response time you want the program to have.

• Great explanation! Thank you. My problem is that when the pot is moving down (i.e. i get values from 1023 downwards) vs when the pot is moving up (i.e. i get values from 0 on up) the avg will be skewed towards the upper or lower ends. This will be magnified if the person turns the pot very quickly. How do i remove this skew? One approach is to use the above test to detect "stationary", throw away both avgs, collect and use a new set of avgs (say over 100ms) and use that as input x. This obviously adds more latency to my response time which i would like to avoid if possible. Jun 10, 2017 at 7:45
• I edited my original post with some more data. Jun 10, 2017 at 7:52
• @RamanathanR your OP edit appears to add nothing relevant, but your comment above suggests latency is the problem. I've updated my question to address this in the last paragraph. You might also like to consider decimating the sample rate down to something more commensurate with your desired response times with a linear filter, and doing 'smart' stuff at a much lower sample rate. This will partition the problem nicely and result in a better implementation. The details are beyond a discussion in this forum, but we can talk offline if you'd like detailed help. Jun 10, 2017 at 9:04

Averaging does a few things; and there are better ways.

1. Adds latency = n * sample interval
2. Reduces random jitter by Vpp/$\sqrt n$
3. Acts as a 1st low pass filter, where a weighted filter can have same noise BW with steeper skirts and lower group delay (latency) just like active filters.

4. Can cause aliasing errors if signals exceed Nyquist frequency before after averaging, so some filtering is needed but a >=5th order brick wall filter is better for low latency which is essentially weighting values for averaging.

5. If you wait for idle and the pot has dither noise from vibration, then it will add excessive latency.

6. If you design a different continuous weighted average, then you can optimize group delay and motion error using parameters such as a Kalman filter. This is used in GPS and other motion position, velocity and acceleration sensors.

6b. ... as @Neil_UK suggested you can do an FFT on the values (or export and analyze) then I might use a matched filter on the fundamental signal ( slew rate 10~90% = tR=0.35/f-3dB) in order to reject the noise using weighting values on roving averages.

1. You can use a velocity weight by the mean difference in values to predict position due to latency and arrive at the smooth result with less delay. Then you can reduce the number of samples needed to decimate ( reduce resolution) to 1% values (0~99)

I leave the decimation algorithm for others to outline, which is basic algebra.

p.s. I would use an Incremental quadrature encoder with detents instead of a Pot, for this application as used in modern systems.

• Thank you for all the ideas. I have a lot to think (and read) about :-) Jun 10, 2017 at 17:28