Re: [PATCH] RFT: iio: gp2ap002: Replace LUT with math
From: Gregor Riepl <hidden>
Date: 2020-02-10 19:33:20
Also in:
linux-iio
quoted
Also: It looks like int_pow doesn't saturate, so even though it uses 64bit integer math, it might be better to move the range check before the calculation.How do you mean I should be doing that without actually doing the power calculation? (Maybe a dumb question but math was never my best subject.)
Well, if you clamp the input value to a valid range, there is no risk of
under- or overflow:
#define GP2AP002_ADC_MIN 5
#define GP2AP002_ADC_MAX 47
/* ensure lux stays in a valid range
lux > 10^(5/10)
lux < 10^(47/10)
*/
clamp(res, GP2AP002_ADC_MIN, GP2AP002_ADC_MAX);
lux = int_pow(10, (res/10));
However, there is another problem with this solution:
If you divide the input value by 10 before raising it to the power of 10, you
lose a lot of precision. Keep in mind that you're doing integer math here.
The input range is very limited, so reducing it further will also reduce the
number of lux steps: int((47-5)/10) = 4, so you will end up with only 4
luminance steps.
Instead of messing with the precision, I propose simplifying the original code
to a simple table lookup.
This will reduce constant memory usage to 42 values * 16 bit = 84 bytes and
computational complexity to one single memory reference.
While I'm sure there is a more optimal solution, I think it's the easiest to
understand with the least impact on accuracy and performance:
#define GP2AP002_ADC_MIN 5
#define GP2AP002_ADC_MAX 47
/*
* This array maps current and lux.
*
* Ambient light sensing range is 3 to 55000 lux.
*
* This mapping is based on the following formula.
* illuminance = 10 ^ (current[mA] / 10)
*/
static const u16 gp2ap002_illuminance_table[] = {
3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79, 100, 126, 158,
200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585, 1995, 2512, 3162,
3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953, 25119, 31623,
39811, 50119,
};
static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
{
const struct gp2ap002_illuminance *ill1;
const struct gp2ap002_illuminance *ill2;
int ret, res;
u16 lux;
ret = iio_read_channel_processed(gp2ap002->alsout, &res);
if (ret < 0)
return ret;
dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
/* ensure we're staying inside the boundaries of the lookup table */
clamp(res, GP2AP002_ADC_MIN, GP2AP002_ADC_MAX);
lux = gp2ap002_illuminance_table[res - GP2AP002_ADC_MIN];
return (int)lux;
}