[linux-dvb] [RFC] [PATCH] Make S/N values to appear at cx24123 frontend

Trent Piepho xyzzy at speakeasy.org
Fri Apr 14 10:33:10 CEST 2006


On Thu, 13 Apr 2006, Mauro Carvalho Chehab wrote:
> +static int ber_table[][2] = {{ 100, 4798 },
> +				{  99, 5380 } };

If you changed this table to be instead:

 	{ { 1.00 * 65535, 4798},
	  { 0.99 * 65535, 5380},

and so on, then you could get rid of all the 65535 / 100 math later on.  The
constant FP expressions are converted to an interger at compile time; there
would be no floating point in the kernel.

Anyway, the linear interpolation seems harder than it needs to be, and less
accurate.  If you have two points, (x0,y0) and (x1,y1) and want to interpolate
Y given X, the formula is:  (note:  dx = x1-x0 and dy = y1-y0)

    Y = dy/dx * (X - x0) + y0
but dy/dx could be less than 1, not good for integer math.  This is better:

    Y = dy * (X - x0) / dx + y0
In this case everything is an integer until the final division, so no accuracy
is lost.

One must make sure dy*(X-x0) doesn't overflow.  The maximum value of (X-x0) is
when X = 14439082, which makes x0 = 4598482, and (X-x0) = 9840600.  The
largest absolute value of dy we can have is then floor((2^31 - 1) / 9840600),
or 218.  With the table values scaled by 65535, dy would be .4*65535 -
.3*65535 = -6553.50, much to large.  So instead of 65535, a smaller scale
factor is needed, so that (.4 - .3) * scale is less than or equal to 218.
This gives scale <= 2180, that's nice and close to 2^11 - 1 which is 2047.

So anyway, here is my version of linear interpolation.  It has a simpler
formula in the code, and much better rounding behavior, as you can see in the
plot here:  http://www.speakeasy.org/~xyzzy/ber-snr.png

static int ber_lut[][2] = {
	{ 1.00 * 2047, 4798 },
	{ 0.99 * 2047, 5380 },
	{ 0.98 * 2047, 6032 },
	{ 0.95 * 2047, 8502 },
	{ 0.90 * 2047, 15066 },
	{ 0.85 * 2047, 26696 },
	{ 0.80 * 2047, 47306 },
	{ 0.75 * 2047, 83826 },
	{ 0.70 * 2047, 148539 },
	{ 0.65 * 2047, 263210 },
	{ 0.60 * 2047, 466406 },
	{ 0.40 * 2047, 4598482 },
	{ 0.30 * 2047, 14439082 } };

static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr)
{
	struct cx24123_state *state = fe->demodulator_priv;
	int ber;

	cx24123_read_ber(fe, &ber);

	if (ber < ber_table[0][1]) {
		state->snr = *snr = 65535;
		return 0;
	}

	*snr = 0;
	for (i=1;i<ARRAY_SIZE(ber_table));i++) {
		if (ber <= ber_table[i][1]) {
			int dx = ber_table[i][1] - ber_table[i-1][1];
			int dy = ber_table[i][0] - ber_table[i-1][0];
			*snr = dy*(ber - ber_table[i-1][1])/dx +
				ber_table[i-1][0];
			/* scale was 2^11, so shift 5 bits to get to the scale
			   we want, 2^16 */
			*snr <<= 5;
			break;
		}
	}

	state->snr = *snr;
	return 0;
}



More information about the linux-dvb mailing list