Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (4789): Lgdt330x: SNR and signal strength reporting

Update the SNR calculations to use the new dvb_math log function, and add
SNR calculations for all supported modulations for both lg dt3302 and dt3303.
The QAM equations don't appear in the dt3302 datasheet, so the ones from the
dt3303 datasheet were used.
SNR returned is the actual value in dB as 8.8 fixed point.
Reporting of real signal strength isn't supported, so rather than return 0,
which confuses some software and users, a re-scaled SNR value is returned.
Code originally by Rusty Scott.

Signed-off-by: Trent Piepho <xyzzy@speakeasy.org>
Signed-off-by: Rusty Scott <rustys@ieee.org>
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

authored by

Trent Piepho and committed by
Mauro Carvalho Chehab
19be685a dbb2e639

+132 -126
+122 -121
drivers/media/dvb/frontends/lgdt330x.c
··· 31 31 * Air2PC/AirStar 2 ATSC 3rd generation (HD5000) 32 32 * pcHDTV HD5500 33 33 * 34 - * TODO: 35 - * signal strength always returns 0. 36 - * 37 34 */ 38 35 39 36 #include <linux/kernel.h> ··· 43 46 #include <asm/byteorder.h> 44 47 45 48 #include "dvb_frontend.h" 49 + #include "dvb_math.h" 46 50 #include "lgdt330x_priv.h" 47 51 #include "lgdt330x.h" 52 + 53 + /* Use Equalizer Mean Squared Error instead of Phaser Tracker MSE */ 54 + /* #define USE_EQMSE */ 48 55 49 56 static int debug = 0; 50 57 module_param(debug, int, 0644); ··· 69 68 70 69 /* Demodulator private data */ 71 70 fe_modulation_t current_modulation; 71 + u32 snr; /* Result of last SNR calculation */ 72 72 73 73 /* Tuner private data */ 74 74 u32 current_frequency; ··· 545 543 return 0; 546 544 } 547 545 548 - static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) 546 + /* Calculate SNR estimation (scaled by 2^24) 547 + 548 + 8-VSB SNR equations from LGDT3302 and LGDT3303 datasheets, QAM 549 + equations from LGDT3303 datasheet. VSB is the same between the '02 550 + and '03, so maybe QAM is too? Perhaps someone with a newer datasheet 551 + that has QAM information could verify? 552 + 553 + For 8-VSB: (two ways, take your pick) 554 + LGDT3302: 555 + SNR_EQ = 10 * log10(25 * 24^2 / EQ_MSE) 556 + LGDT3303: 557 + SNR_EQ = 10 * log10(25 * 32^2 / EQ_MSE) 558 + LGDT3302 & LGDT3303: 559 + SNR_PT = 10 * log10(25 * 32^2 / PT_MSE) (we use this one) 560 + For 64-QAM: 561 + SNR = 10 * log10( 688128 / MSEQAM) 562 + For 256-QAM: 563 + SNR = 10 * log10( 696320 / MSEQAM) 564 + 565 + We re-write the snr equation as: 566 + SNR * 2^24 = 10*(c - intlog10(MSE)) 567 + Where for 256-QAM, c = log10(696320) * 2^24, and so on. */ 568 + 569 + static u32 calculate_snr(u32 mse, u32 c) 549 570 { 550 - /* not directly available. */ 551 - *strength = 0; 552 - return 0; 571 + if (mse == 0) /* No signal */ 572 + return 0; 573 + 574 + mse = intlog10(mse); 575 + if (mse > c) { 576 + /* Negative SNR, which is possible, but realisticly the 577 + demod will lose lock before the signal gets this bad. The 578 + API only allows for unsigned values, so just return 0 */ 579 + return 0; 580 + } 581 + return 10*(c - mse); 553 582 } 554 583 555 584 static int lgdt3302_read_snr(struct dvb_frontend* fe, u16* snr) 556 585 { 557 - #ifdef SNR_IN_DB 558 - /* 559 - * Spec sheet shows formula for SNR_EQ = 10 log10(25 * 24**2 / noise) 560 - * and SNR_PH = 10 log10(25 * 32**2 / noise) for equalizer and phase tracker 561 - * respectively. The following tables are built on these formulas. 562 - * The usual definition is SNR = 20 log10(signal/noise) 563 - * If the specification is wrong the value retuned is 1/2 the actual SNR in db. 564 - * 565 - * This table is a an ordered list of noise values computed by the 566 - * formula from the spec sheet such that the index into the table 567 - * starting at 43 or 45 is the SNR value in db. There are duplicate noise 568 - * value entries at the beginning because the SNR varies more than 569 - * 1 db for a change of 1 digit in noise at very small values of noise. 570 - * 571 - * Examples from SNR_EQ table: 572 - * noise SNR 573 - * 0 43 574 - * 1 42 575 - * 2 39 576 - * 3 37 577 - * 4 36 578 - * 5 35 579 - * 6 34 580 - * 7 33 581 - * 8 33 582 - * 9 32 583 - * 10 32 584 - * 11 31 585 - * 12 31 586 - * 13 30 587 - */ 588 - 589 - static const u32 SNR_EQ[] = 590 - { 1, 2, 2, 2, 3, 3, 4, 4, 5, 7, 591 - 9, 11, 13, 17, 21, 26, 33, 41, 52, 65, 592 - 81, 102, 129, 162, 204, 257, 323, 406, 511, 644, 593 - 810, 1020, 1284, 1616, 2035, 2561, 3224, 4059, 5110, 6433, 594 - 8098, 10195, 12835, 16158, 20341, 25608, 32238, 40585, 51094, 64323, 595 - 80978, 101945, 128341, 161571, 203406, 256073, 0x40000 596 - }; 597 - 598 - static const u32 SNR_PH[] = 599 - { 1, 2, 2, 2, 3, 3, 4, 5, 6, 8, 600 - 10, 12, 15, 19, 23, 29, 37, 46, 58, 73, 601 - 91, 115, 144, 182, 229, 288, 362, 456, 574, 722, 602 - 909, 1144, 1440, 1813, 2282, 2873, 3617, 4553, 5732, 7216, 603 - 9084, 11436, 14396, 18124, 22817, 28724, 36161, 45524, 57312, 72151, 604 - 90833, 114351, 143960, 181235, 228161, 0x080000 605 - }; 606 - 607 - static u8 buf[5];/* read data buffer */ 608 - static u32 noise; /* noise value */ 609 - static u32 snr_db; /* index into SNR_EQ[] */ 610 586 struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; 587 + u8 buf[5]; /* read data buffer */ 588 + u32 noise; /* noise value */ 589 + u32 c; /* per-modulation SNR calculation constant */ 611 590 612 - /* read both equalizer and phase tracker noise data */ 613 - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); 614 - 615 - if (state->current_modulation == VSB_8) { 616 - /* Equalizer Mean-Square Error Register for VSB */ 591 + switch(state->current_modulation) { 592 + case VSB_8: 593 + i2c_read_demod_bytes(state, LGDT3302_EQPH_ERR0, buf, 5); 594 + #ifdef USE_EQMSE 595 + /* Use Equalizer Mean-Square Error Register */ 596 + /* SNR for ranges from -15.61 to +41.58 */ 617 597 noise = ((buf[0] & 7) << 16) | (buf[1] << 8) | buf[2]; 618 - 619 - /* 620 - * Look up noise value in table. 621 - * A better search algorithm could be used... 622 - * watch out there are duplicate entries. 623 - */ 624 - for (snr_db = 0; snr_db < sizeof(SNR_EQ); snr_db++) { 625 - if (noise < SNR_EQ[snr_db]) { 626 - *snr = 43 - snr_db; 627 - break; 628 - } 629 - } 630 - } else { 631 - /* Phase Tracker Mean-Square Error Register for QAM */ 632 - noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; 633 - 634 - /* Look up noise value in table. */ 635 - for (snr_db = 0; snr_db < sizeof(SNR_PH); snr_db++) { 636 - if (noise < SNR_PH[snr_db]) { 637 - *snr = 45 - snr_db; 638 - break; 639 - } 640 - } 641 - } 598 + c = 69765745; /* log10(25*24^2)*2^24 */ 642 599 #else 643 - /* Return the raw noise value */ 644 - static u8 buf[5];/* read data buffer */ 645 - static u32 noise; /* noise value */ 646 - struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; 647 - 648 - /* read both equalizer and pase tracker noise data */ 649 - i2c_read_demod_bytes(state, EQPH_ERR0, buf, sizeof(buf)); 650 - 651 - if (state->current_modulation == VSB_8) { 652 - /* Phase Tracker Mean-Square Error Register for VSB */ 600 + /* Use Phase Tracker Mean-Square Error Register */ 601 + /* SNR for ranges from -13.11 to +44.08 */ 653 602 noise = ((buf[0] & 7<<3) << 13) | (buf[3] << 8) | buf[4]; 654 - } else { 655 - 656 - /* Carrier Recovery Mean-Square Error for QAM */ 657 - i2c_read_demod_bytes(state, 0x1a, buf, 2); 603 + c = 73957994; /* log10(25*32^2)*2^24 */ 604 + #endif 605 + break; 606 + case QAM_64: 607 + case QAM_256: 608 + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); 658 609 noise = ((buf[0] & 3) << 8) | buf[1]; 610 + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; 611 + /* log10(688128)*2^24 and log10(696320)*2^24 */ 612 + break; 613 + default: 614 + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", 615 + __FUNCTION__); 616 + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ 659 617 } 660 618 661 - /* Small values for noise mean signal is better so invert noise */ 662 - *snr = ~noise; 663 - #endif 619 + state->snr = calculate_snr(noise, c); 620 + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ 664 621 665 - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); 622 + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, 623 + state->snr >> 24, (((state->snr>>8) & 0xffff) * 100) >> 16); 666 624 667 625 return 0; 668 626 } 669 627 670 628 static int lgdt3303_read_snr(struct dvb_frontend* fe, u16* snr) 671 629 { 672 - /* Return the raw noise value */ 673 - static u8 buf[5];/* read data buffer */ 674 - static u32 noise; /* noise value */ 675 630 struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; 631 + u8 buf[5]; /* read data buffer */ 632 + u32 noise; /* noise value */ 633 + u32 c; /* per-modulation SNR calculation constant */ 676 634 677 - if (state->current_modulation == VSB_8) { 678 - 679 - i2c_read_demod_bytes(state, 0x6e, buf, 5); 680 - /* Phase Tracker Mean-Square Error Register for VSB */ 635 + switch(state->current_modulation) { 636 + case VSB_8: 637 + i2c_read_demod_bytes(state, LGDT3303_EQPH_ERR0, buf, 5); 638 + #ifdef USE_EQMSE 639 + /* Use Equalizer Mean-Square Error Register */ 640 + /* SNR for ranges from -16.12 to +44.08 */ 641 + noise = ((buf[0] & 0x78) << 13) | (buf[1] << 8) | buf[2]; 642 + c = 73957994; /* log10(25*32^2)*2^24 */ 643 + #else 644 + /* Use Phase Tracker Mean-Square Error Register */ 645 + /* SNR for ranges from -13.11 to +44.08 */ 681 646 noise = ((buf[0] & 7) << 16) | (buf[3] << 8) | buf[4]; 682 - } else { 683 - 684 - /* Carrier Recovery Mean-Square Error for QAM */ 685 - i2c_read_demod_bytes(state, 0x1a, buf, 2); 647 + c = 73957994; /* log10(25*32^2)*2^24 */ 648 + #endif 649 + break; 650 + case QAM_64: 651 + case QAM_256: 652 + i2c_read_demod_bytes(state, CARRIER_MSEQAM1, buf, 2); 686 653 noise = (buf[0] << 8) | buf[1]; 654 + c = state->current_modulation == QAM_64 ? 97939837 : 98026066; 655 + /* log10(688128)*2^24 and log10(696320)*2^24 */ 656 + break; 657 + default: 658 + printk(KERN_ERR "lgdt330x: %s: Modulation set to unsupported value\n", 659 + __FUNCTION__); 660 + return -EREMOTEIO; /* return -EDRIVER_IS_GIBBERED; */ 687 661 } 688 662 689 - /* Small values for noise mean signal is better so invert noise */ 690 - *snr = ~noise; 663 + state->snr = calculate_snr(noise, c); 664 + *snr = (state->snr) >> 16; /* Convert from 8.24 fixed-point to 8.8 */ 691 665 692 - dprintk("%s: noise = 0x%05x, snr = %idb\n",__FUNCTION__, noise, *snr); 666 + dprintk("%s: noise = 0x%08x, snr = %d.%02d dB\n", __FUNCTION__, noise, 667 + state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16); 668 + 669 + return 0; 670 + } 671 + 672 + static int lgdt330x_read_signal_strength(struct dvb_frontend* fe, u16* strength) 673 + { 674 + /* Calculate Strength from SNR up to 35dB */ 675 + /* Even though the SNR can go higher than 35dB, there is some comfort */ 676 + /* factor in having a range of strong signals that can show at 100% */ 677 + struct lgdt330x_state* state = (struct lgdt330x_state*) fe->demodulator_priv; 678 + u16 snr; 679 + int ret; 680 + 681 + ret = fe->ops.read_snr(fe, &snr); 682 + if (ret != 0) 683 + return ret; 684 + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ 685 + /* scale the range 0 - 35*2^24 into 0 - 65535 */ 686 + if (state->snr >= 8960 * 0x10000) 687 + *strength = 0xffff; 688 + else 689 + *strength = state->snr / 8960; 693 690 694 691 return 0; 695 692 }
+10 -5
drivers/media/dvb/frontends/lgdt330x_priv.h
··· 51 51 AGC_RFIF_ACC2= 0x3b, 52 52 AGC_STATUS= 0x3f, 53 53 SYNC_STATUS_VSB= 0x43, 54 - EQPH_ERR0= 0x47, 55 - EQ_ERR1= 0x48, 56 - EQ_ERR2= 0x49, 57 - PH_ERR1= 0x4a, 58 - PH_ERR2= 0x4b, 59 54 DEMUX_CONTROL= 0x66, 55 + LGDT3302_EQPH_ERR0= 0x47, 56 + LGDT3302_EQ_ERR1= 0x48, 57 + LGDT3302_EQ_ERR2= 0x49, 58 + LGDT3302_PH_ERR1= 0x4a, 59 + LGDT3302_PH_ERR2= 0x4b, 60 60 LGDT3302_PACKET_ERR_COUNTER1= 0x6a, 61 61 LGDT3302_PACKET_ERR_COUNTER2= 0x6b, 62 + LGDT3303_EQPH_ERR0= 0x6e, 63 + LGDT3303_EQ_ERR1= 0x6f, 64 + LGDT3303_EQ_ERR2= 0x70, 65 + LGDT3303_PH_ERR1= 0x71, 66 + LGDT3303_PH_ERR2= 0x72, 62 67 LGDT3303_PACKET_ERR_COUNTER1= 0x8b, 63 68 LGDT3303_PACKET_ERR_COUNTER2= 0x8c, 64 69 };