summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS3
-rw-r--r--HISTORY3
-rw-r--r--dvbdevice.c238
3 files changed, 239 insertions, 5 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index af14fe88..a9a9358f 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -3546,3 +3546,6 @@ Daniel Scheller <d.scheller@gmx.net>
Onur Sentürk <onur@sentek.org>
for making the MTD mapper avoid immediately reusing unique PIDs when switching channels
+
+Helmut Binder <cco@aon.at>
+ for improving calculating signal strength and quality
diff --git a/HISTORY b/HISTORY
index 8504cbcc..3522d34a 100644
--- a/HISTORY
+++ b/HISTORY
@@ -9162,7 +9162,7 @@ Video Disk Recorder Revision History
a subdirectory.
- SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details).
-2018-02-10: Version 2.3.9
+2018-02-12: Version 2.3.9
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
@@ -9270,3 +9270,4 @@ Video Disk Recorder Revision History
correct position in the list, rather than first being added at the end and then
jumping to the proper offset.
- Fixed getting the info of a newly edited recording (reported by Matthias Senzel).
+- Improved calculating signal strength and quality (thanks to Helmut Binder).
diff --git a/dvbdevice.c b/dvbdevice.c
index aad621a9..a92aa70b 100644
--- a/dvbdevice.c
+++ b/dvbdevice.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: dvbdevice.c 4.14 2018/01/25 15:09:17 kls Exp $
+ * $Id: dvbdevice.c 4.15 2018/02/12 14:05:22 kls Exp $
*/
#include "dvbdevice.h"
@@ -699,6 +699,212 @@ int dB1000toPercent(int dB1000, int Low, int High)
return 100 - v * v;
}
+#define REF_S1(q1) (mod == QPSK) ? q1 : 0
+#define REF_S2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == PSK_8) ? q2 : (mod == APSK_16) ? q3 : (mod == APSK_32) ? q4 : 0
+#define REF_T1(q1, q2, q3) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : 0
+#define REF_T2(q1, q2, q3, q4) (mod == QPSK) ? q1 : (mod == QAM_16) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_256) ? q4 : 0
+#define REF_C1(q1, q2, q3, q4, q5) (mod == QAM_16) ? q1 : (mod == QAM_32) ? q2 : (mod == QAM_64) ? q3 : (mod == QAM_128) ? q4 : (mod == QAM_256) ? q5: 0
+
+int StrengthToSSI(const cChannel *Channel, int Strength, int FeModulation, int FeCoderateH, int FeFec)
+{
+ // Strength in 0.001dBm (dBm x 1000)
+ cDvbTransponderParameters dtp(Channel->Parameters());
+ int ssi = 0; // 0-100
+ int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
+ int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
+ int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
+ if (Channel->IsTerr()) {
+ int pref = 0;
+ // NorDig Unified Ver. 2.6 - 3.4.4.6 Page 43 ff.
+ // reference values : pref-15dBm = 0%, pref+35dBm = 100%
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ fec = cod; // adjustment for DVB-T
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // dBm: Q4 Q16 Q64
+ case FEC_1_2: pref = REF_T1(-93, -87, -82); break;
+ default:
+ case FEC_2_3: pref = REF_T1(-91, -85, -80); break;
+ case FEC_3_4: pref = REF_T1(-90, -84, -78); break;
+ case FEC_5_6: pref = REF_T1(-89, -83, -77); break;
+ case FEC_7_8: pref = REF_T1(-88, -82, -76); break;
+ }
+ }
+ else { // DVB-T2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // dBm: Q4 Q16 Q64 Q256
+ case FEC_1_2: pref = REF_T2(-96, -91, -86, -82); break;
+ default:
+ case FEC_3_5: pref = REF_T2(-95, -89, -85, -80); break;
+ case FEC_2_3: pref = REF_T2(-94, -88, -83, -78); break;
+ case FEC_3_4: pref = REF_T2(-93, -87, -82, -76); break;
+ case FEC_4_5: pref = REF_T2(-92, -86, -81, -75); break;
+ case FEC_5_6: pref = REF_T2(-92, -86, -80, -74); break;
+ }
+ }
+ if (pref) {
+ int prel = (Strength / 1000) - pref;
+ ssi = (prel < -15) ? 0 :
+ (prel < 0) ? (prel + 15) * 2 / 3 : // 0% - 10%
+ (prel < 20) ? prel * 4 + 10 : // 10% - 90%
+ (prel < 35) ? (prel - 20) * 2 / 3 + 90 : // 90% - 100%
+ 100;
+#ifdef DEBUG_SIGNALSTRENGTH
+ fprintf(stderr, "SSI: STR:%d, Pref:%d, Prel:%d, ssi:%d%%(sys:%d, mod:%d, fec:%d)\n", Strength, pref, prel, ssi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsCable())
+ ssi = dB1000toPercent(Strength, -95000, -20000); // defaults
+ else if (Channel->IsSat())
+ ssi = dB1000toPercent(Strength, -95000, -20000); // defaults
+ return ssi;
+}
+
+#define IGNORE_BER 0
+#define BER_ERROR_FREE (1000*1000*1000) // 1/10^-9
+
+int SignalToSQI(const cChannel *Channel, int Signal, int Ber, int FeModulation, int FeCoderateH, int FeFec)
+{
+#if IGNORE_BER
+ Ber = BER_ERROR_FREE; // assume/pretend to be biterror free
+#endif
+ // Signal in 0.001dB (dB x 1000)
+ cDvbTransponderParameters dtp(Channel->Parameters());
+ int sqi = 0; // 0-100
+ int mod = (FeModulation >= 0) ? FeModulation : dtp.Modulation();
+ int cod = (FeCoderateH >= 0) ? FeCoderateH : dtp.CoderateH(); // DVB-T
+ int fec = (FeFec >= 0) ? FeFec : dtp.CoderateH();
+ if (Channel->IsTerr()) { // QEF: BER 10^-6
+ int cnref = 0;
+ // NorDig Unified Ver. 2.6 - 3.4.4.7 Page 45 ff.
+ // reference values for QEF (BER 10^-11 at MPEG2 demux input)
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ fec = cod; // adjustment for DVB-T
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // 0.1 dB Q4 Q16 Q64 (Hierarchy=None)
+ case FEC_1_2: cnref = REF_T1(51, 108, 165); break;
+ default:
+ case FEC_2_3: cnref = REF_T1(69, 131, 187); break;
+ case FEC_3_4: cnref = REF_T1(79, 146, 202); break;
+ case FEC_5_6: cnref = REF_T1(89, 156, 216); break;
+ case FEC_7_8: cnref = REF_T1(97, 160, 225); break;
+ }
+ }
+ else { // DVB-T2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // 0.1 dB Q4 Q16 Q64 Q256
+ case FEC_1_2: cnref = REF_T2(35, 87, 130, 170); break;
+ default:
+ case FEC_3_5: cnref = REF_T2(47, 101, 148, 194); break;
+ case FEC_2_3: cnref = REF_T2(56, 114, 162, 208); break;
+ case FEC_3_4: cnref = REF_T2(66, 125, 177, 229); break;
+ case FEC_4_5: cnref = REF_T2(72, 133, 187, 243); break;
+ case FEC_5_6: cnref = REF_T2(77, 138, 194, 251); break;
+ }
+ }
+ if (cnref) {
+ int cnrel = (Signal/100) - cnref; // 0.1 dB
+ int ber_sqi = 100; // 100%
+ int cnr_sqi = 0; // 0%
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-T
+ ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
+ (Ber >= 10000000) ? 100 : // <= 10^-7
+ (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
+ // scale: -7dB/+3dB to reference-value
+ cnr_sqi = (cnrel < -70) ? 0 :
+ (cnrel < +30) ? (100 + (cnrel - 30)) :
+ 100;
+ sqi = (cnr_sqi * ber_sqi) / 100;
+ // alternative: stretched scale: cnref-7dB = 0%, 30dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 30000);
+ }
+ else { // DVB-T2
+ ber_sqi = (Ber < 10000) ? 0 : // > 10^-4
+ (Ber >= 10000000) ? 100 * 100 / 6 : // <= 10^-7 : 16.67% -> SQI 0% .. 100%
+ (100 * 100 / 15); // 6.67% -> SQI 0% .. 40% || 100%
+ // scale: -3dB/+3dB to reference-value
+ sqi = (cnrel < -30) ? 0 :
+ (cnrel <= +30) ? (cnrel + 30) * ber_sqi / 1000 : // (0 .. 6) * 16,67 || 6.67
+ 100;
+ // alternative: stretched scale: cnref-3dB = 0%, 32dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-3000, 32000);
+ }
+#ifdef DEBUG_SIGNALQUALITY
+ fprintf(stderr, "SQI-T: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsCable()) { // ! COMPLETELY UNTESTED !
+ if (mod == QAM_AUTO) mod = QAM_64;
+ // 0.1 dB Q16 Q32 Q64 Q128 Q256
+ int cnref = REF_C1(200, 230, 260, 290, 320); // minimum for BER<10^-4
+ if (cnref) {
+ int cnrel = (Signal / 100) - cnref; // 0.1 dB
+ int ber_sqi = (Ber < 1000) ? 0 : // > 10^-3
+ (Ber >= 10000000) ? 100 : // <= 10^-7
+ (int)(20 * log10(Ber)) - 40; // 20*log10(1/BER)-40 -> 20% .. 100%
+ // scale: -7dB/+3dB to reference-value
+ int cnr_sqi = (cnrel < -70) ? 0 :
+ (cnrel < +30) ? (100 + (cnrel - 30)) :
+ 100;
+ sqi = (cnr_sqi * ber_sqi) / 100;
+ // alternative: stretched scale: cnref-7dB = 0%, 40dB = 100%
+ // sqi = dB1000toPercent(Signal, (100*cnref)-7000, 40000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-C: SIG:%d, BER:%d, CNref:%d, CNrel:%d, bersqi:%d, sqi:%d%%(sys:%d, mod:%d, fec:%d)\n", Signal, Ber, cnref, cnrel, ber_sqi, sqi, dtp.System(), mod, fec);
+#endif
+ }
+ }
+ else if (Channel->IsSat()) {
+ int cnref = 0;
+ if (dtp.System() == DVB_SYSTEM_1) { // DVB-S
+ if (mod == QAM_AUTO) mod = QPSK;
+ switch (fec) { // 0.1 dB: Q4 : 10^-7
+ case FEC_1_2: cnref = REF_S1(38); break;
+ default:
+ case FEC_2_3: cnref = REF_S1(56); break;
+ case FEC_3_4: cnref = REF_S1(67); break;
+ case FEC_5_6: cnref = REF_S1(77); break;
+ case FEC_7_8: cnref = REF_S1(84); break;
+ }
+ if (cnref) {
+ //cnrel = (Signal/100) - cnref; // 0.1 dB
+ // scale: cnref-4dB = 0%, 15dB = 100%
+ sqi = dB1000toPercent(Signal, (100*cnref)-4000, 15000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-S1: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
+#endif
+ }
+ }
+ else { // DVB-S2
+ if (mod == QAM_AUTO) mod = QAM_64;
+ switch (fec) { // 0.1 dB Q4 Q8 16A* 32A*
+ //case FEC_1_4: cnref = REF_S2(-14, 65, 90, 126); break;
+ //case FEC_1_3: cnref = REF_S2( -2, 65, 90, 126); break;
+ case FEC_2_5: cnref = REF_S2( 7, 65, 90, 126); break;
+ case FEC_1_2: cnref = REF_S2( 20, 65, 90, 126); break;
+ case FEC_3_5: cnref = REF_S2( 32, 65, 90, 126); break;
+ default:
+ case FEC_2_3: cnref = REF_S2( 41, 76, 90, 126); break;
+ case FEC_3_4: cnref = REF_S2( 50, 66, 102, 126); break;
+ case FEC_4_5: cnref = REF_S2( 57, 89, 110, 136); break;
+ case FEC_5_6: cnref = REF_S2( 62, 104, 116, 143); break;
+ case FEC_8_9: cnref = REF_S2( 72, 117, 129, 157); break;
+ case FEC_9_10: cnref = REF_S2( 74, 120, 131, 161); break;
+ }
+ if (cnref) {
+ // cnrel = (Signal/100) - cnref; // 0.1 dB
+ // scale: cnref-4dB = 0%, 20dB = 100%
+ sqi = dB1000toPercent(Signal, (100*cnref)-4000, 20000);
+#ifdef DEBUG_SIGNALQUALITY
+ dsyslog("SQI-S2: SIG:%d, BER:%d, CNref:%d, sqi:%d%%(mod:%d, fec:%d)\n", Signal, Ber, cnref, sqi, mod, fec);
+#endif
+ }
+ }
+ }
+ return sqi;
+}
+
int cDvbTuner::GetSignalStrength(void) const
{
ClearEventQueue();
@@ -710,14 +916,20 @@ int cDvbTuner::GetSignalStrength(void) const
memset(&CmdSeq, 0, sizeof(CmdSeq));
CmdSeq.props = Props;
SETCMD(DTV_STAT_SIGNAL_STRENGTH, 0);
+ SETCMD(DTV_MODULATION, 0);
+ SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
+ SETCMD(DTV_INNER_FEC, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
return -1;
}
+ int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
+ int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
+ int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
int Signal = 0;
if (Props[0].u.st.len > 0) {
switch (Props[0].u.st.stat[0].scale) {
- case FE_SCALE_DECIBEL: Signal = dB1000toPercent(Props[0].u.st.stat[0].svalue, -95000, -20000); // TODO use different values for DVB-S, -T, -C?
+ case FE_SCALE_DECIBEL: Signal = StrengthToSSI(&channel, Props[0].u.st.stat[0].svalue, FeMod, FeCod, FeFec);
break;
case FE_SCALE_RELATIVE: Signal = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
break;
@@ -768,14 +980,32 @@ int cDvbTuner::GetSignalQuality(void) const
memset(&CmdSeq, 0, sizeof(CmdSeq));
CmdSeq.props = Props;
SETCMD(DTV_STAT_CNR, 0);
+ SETCMD(DTV_MODULATION, 0);
+ SETCMD(DTV_CODE_RATE_HP, 0); // DVB-T only
+ SETCMD(DTV_INNER_FEC, 0);
+ SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0);
+ SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0);
if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) {
esyslog("ERROR: frontend %d/%d: %m", adapter, frontend);
return -1;
}
+ int FeMod = (Props[1].u.st.len > 0) ? (int)Props[1].u.data : -1;
+ int FeCod = (Props[2].u.st.len > 0) ? (int)Props[2].u.data : -1;
+ int FeFec = (Props[3].u.st.len > 0) ? (int)Props[3].u.data : -1;
+ int Ber = BER_ERROR_FREE; // 1/10^-9
+ if (Props[4].u.st.len > 0 && Props[4].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[5].u.st.len > 0 && Props[5].u.st.stat[0].scale == FE_SCALE_COUNTER) {
+ uint64_t ebc = Props[4].u.st.stat[0].uvalue; // error bit count
+ uint64_t tbc = Props[5].u.st.stat[0].uvalue; // total bit count
+ if (ebc > 0) {
+ uint64_t BerRev = tbc / ebc; // reversed, for integer arithmetic
+ if (BerRev < BER_ERROR_FREE)
+ Ber = (int)BerRev;
+ }
+ }
int Cnr = 0;
if (Props[0].u.st.len > 0) {
switch (Props[0].u.st.stat[0].scale) {
- case FE_SCALE_DECIBEL: Cnr = dB1000toPercent(Props[0].u.st.stat[0].svalue, 5000, 20000); // TODO use different values for DVB-S, -T, -C?
+ case FE_SCALE_DECIBEL: Cnr = SignalToSQI(&channel, Props[0].u.st.stat[0].svalue, Ber, FeMod, FeCod, FeFec);
break;
case FE_SCALE_RELATIVE: Cnr = 100 * Props[0].u.st.stat[0].uvalue / 0xFFFF;
break;
@@ -1116,13 +1346,13 @@ bool cDvbTuner::SetFrontend(void)
SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
if (frontendType == SYS_DVBT2) {
// DVB-T2
+ SETCMD(DTV_INNER_FEC, dtp.CoderateH());
if (DvbApiVersion >= 0x0508) {
SETCMD(DTV_STREAM_ID, dtp.StreamId());
}
else if (DvbApiVersion >= 0x0503)
SETCMD(DTV_DVBT2_PLP_ID_LEGACY, dtp.StreamId());
}
-
tuneTimeout = DVBT_TUNE_TIMEOUT;
lockTimeout = DVBT_LOCK_TIMEOUT;
}