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