Re: [PATCH] RFT: iio: gp2ap002: Replace LUT with math

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



>> 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;
}



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux