From 6132a46768a1e7c16c5bf51ce7f2d831b83df67c Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Mon, 17 Apr 2017 15:02:44 +0200 Subject: [PATCH] Implemented getting and logging DVB API 5 signal statistics --- CONTRIBUTORS | 2 + HISTORY | 6 ++- device.c | 7 +++- device.h | 25 ++++++++++- dvbdevice.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++- dvbdevice.h | 3 +- 6 files changed, 154 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index c51ead69..580ec89b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -3388,6 +3388,8 @@ Dietmar Spingler for suggesting that the -V and -h options should list the plugins in alphabetical order for suggesting to implement the setup option "Recording/Record key handling" for suggesting to cache the channel/CAM relations in the file 'cam.data' + for suggesting to log signal statistics whenever the tuner of a cDvbDevice acquires + a lock, and DVB API 5 signal statistics are available Stefan Schallenberg for adding the functions IndexOf(), InsertUnique(), AppendUnique() and RemoveElement() diff --git a/HISTORY b/HISTORY index 15793e93..3c55a154 100644 --- a/HISTORY +++ b/HISTORY @@ -8925,7 +8925,7 @@ Video Disk Recorder Revision History - Now stopping any ongoing recordings before stopping the plugins, to avoid a crash when stopping VDR while recording. -2017-04-15: Version 2.3.4 +2017-04-17: Version 2.3.4 - The functionality of HandleRemoteModifications(), which synchronizes changes to timers between peer VDR machines, has been moved to timers.[ch] and renamed to @@ -8978,3 +8978,7 @@ Video Disk Recorder Revision History is called, but the actual TS packet returned (pointed to by Data) may well be (and typically is, unless the CAM copies the data) in the area of the buffer that would be deleted by Skip(). +- The new function cDevice::SignalStats() (if implemented by an actual device) returns + statistics about the currently received signal. +- If DVB API 5 signal statistics are available for a cDvbDevice, it now logs them + whenever the tuner acquires a lock (suggested by Dietmar Spingler). diff --git a/device.c b/device.c index 42cd21f9..25d16679 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 4.14 2017/04/15 09:44:50 kls Exp $ + * $Id: device.c 4.15 2017/04/17 14:47:42 kls Exp $ */ #include "device.h" @@ -749,6 +749,11 @@ const cPositioner *cDevice::Positioner(void) const return NULL; } +bool cDevice::SignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per) const +{ + return false; +} + int cDevice::SignalStrength(void) const { return -1; diff --git a/device.h b/device.h index 3f50e6b7..4796258b 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 4.7 2017/04/15 09:41:34 kls Exp $ + * $Id: device.h 4.8 2017/04/17 14:46:57 kls Exp $ */ #ifndef __DEVICE_H @@ -106,6 +106,13 @@ public: /// The cDevice class is the base from which actual devices can be derived. +#define DTV_STAT_VALID_NONE 0x0000 +#define DTV_STAT_VALID_STRENGTH 0x0001 +#define DTV_STAT_VALID_CNR 0x0002 +#define DTV_STAT_VALID_BERPRE 0x0004 +#define DTV_STAT_VALID_BERPOST 0x0008 +#define DTV_STAT_VALID_PER 0x0010 + class cDevice : public cThread { friend class cLiveSubtitle; friend class cDeviceHook; @@ -284,6 +291,22 @@ public: ///< move the satellite dish to the requested position (only applies to DVB-S ///< devices). If no positioner is involved, or this is not a DVB-S device, ///< NULL will be returned. + virtual bool SignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL) const; + ///< Returns statistics about the currently received signal (if available). + ///< Strength is the signal strength in dBm (typical range -100dBm...0dBm). + ///< Cnr is the carrier to noise ratio in dB (typical range 0dB...40dB). + ///< BerPre is the bit error rate before the forward error correction (FEC). + ///< BerPost is the bit error rate after the forward error correction (FEC). + ///< Per is the block error rate after the forward error correction (FEC). + ///< Typical range for BerPre, BerPost and Per is 0...1. + ///< If any of the given pointers is not NULL, the value of the respective signal + ///< statistic is returned in it. Upon return, Valid holds a combination of + ///< DTV_STAT_VALID_* flags, indicating which of the returned values are actually + ///< valid. If the flag for a particular parameter in Valid is 0, the returned + ///< value is undefined. It depends on the device which of these parameters + ///< (if any) are available. + ///< Returns true if any of the requested parameters is valid. + ///< If false is returned, the value in Valid is undefined. virtual int SignalStrength(void) const; ///< Returns the "strength" of the currently received signal. ///< This is a value in the range 0 (no signal at all) through diff --git a/dvbdevice.c b/dvbdevice.c index 7775465d..46a219b4 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.6 2017/04/14 10:05:15 kls Exp $ + * $Id: dvbdevice.c 4.7 2017/04/17 15:02:44 kls Exp $ */ #include "dvbdevice.h" @@ -346,6 +346,7 @@ public: void SetChannel(const cChannel *Channel); bool Locked(int TimeoutMs = 0); const cPositioner *Positioner(void) const { return positioner; } + bool GetSignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL) const; int GetSignalStrength(void) const; int GetSignalQuality(void) const; }; @@ -554,6 +555,7 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const return false; } +//#define DEBUG_SIGNALSTATS //#define DEBUG_SIGNALSTRENGTH //#define DEBUG_SIGNALQUALITY @@ -566,6 +568,101 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const }\ } +bool cDvbTuner::GetSignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per) const +{ + ClearEventQueue(); + dtv_property Props[MAXFRONTENDCMDS]; + dtv_properties CmdSeq; + memset(&Props, 0, sizeof(Props)); + memset(&CmdSeq, 0, sizeof(CmdSeq)); + CmdSeq.props = Props; + if (Strength) SETCMD(DTV_STAT_SIGNAL_STRENGTH, 0); + if (Cnr) SETCMD(DTV_STAT_CNR, 0); + if (BerPre) { SETCMD(DTV_STAT_PRE_ERROR_BIT_COUNT, 0); + SETCMD(DTV_STAT_PRE_TOTAL_BIT_COUNT, 0); } + if (BerPost) { SETCMD(DTV_STAT_POST_ERROR_BIT_COUNT, 0); + SETCMD(DTV_STAT_POST_TOTAL_BIT_COUNT, 0); } + if (Per) { SETCMD(DTV_STAT_ERROR_BLOCK_COUNT, 0); + SETCMD(DTV_STAT_TOTAL_BLOCK_COUNT, 0); } + if (ioctl(fd_frontend, FE_GET_PROPERTY, &CmdSeq) != 0) { + esyslog("ERROR: frontend %d/%d: %m", adapter, frontend); + return false; + } + Valid = DTV_STAT_VALID_NONE; + int i = 0; + if (Strength) { + if (Props[i].u.st.len > 0) { + switch (Props[i].u.st.stat[0].scale) { + case FE_SCALE_DECIBEL: *Strength = double(Props[i].u.st.stat[0].svalue) / 1000; + Valid |= DTV_STAT_VALID_STRENGTH; + break; + default: ; + } + } + i++; + } + if (Cnr) { + if (Props[i].u.st.len > 0) { + switch (Props[i].u.st.stat[0].scale) { + case FE_SCALE_DECIBEL: *Cnr = double(Props[i].u.st.stat[0].svalue) / 1000; + Valid |= DTV_STAT_VALID_CNR; + break; + default: ; + } + } + i++; + } + if (BerPre) { + if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) { + if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) { + uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error bit count + uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total bit count + if (tbc > 0) { + *BerPre = double(ebc) / tbc; + Valid |= DTV_STAT_VALID_BERPRE; + } + } + } + i += 2; + } + if (BerPost) { + if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) { + if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) { + uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error bit count + uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total bit count + if (tbc > 0) { + *BerPost = double(ebc) / tbc; + Valid |= DTV_STAT_VALID_BERPOST; + } + } + } + i += 2; + } + if (Per) { + if (Props[i].u.st.len > 0 && Props[i + 1].u.st.len > 0) { + if (Props[i].u.st.stat[0].scale == FE_SCALE_COUNTER && Props[i + 1].u.st.stat[0].scale == FE_SCALE_COUNTER) { + uint64_t ebc = Props[i].u.st.stat[0].uvalue; // error block count + uint64_t tbc = Props[i + 1].u.st.stat[0].uvalue; // total block count + if (tbc > 0) { + *Per = double(ebc) / tbc; + Valid |= DTV_STAT_VALID_PER; + } + } + } + i += 2; + } +#ifdef DEBUG_SIGNALSTATS + fprintf(stderr, "FE %d/%d: API5 %04X", adapter, frontend, Valid); + if ((Valid & DTV_STAT_VALID_STRENGTH) != 0) fprintf(stderr, " STR=%1.1fdBm", *Strength); + if ((Valid & DTV_STAT_VALID_CNR) != 0) fprintf(stderr, " CNR=%1.1fdB", *Cnr); + if ((Valid & DTV_STAT_VALID_BERPRE) != 0) fprintf(stderr, " BERPRE=%1.1e", *BerPre); + if ((Valid & DTV_STAT_VALID_BERPOST) != 0) fprintf(stderr, " BERPOST=%1.1e", *BerPost); + if ((Valid & DTV_STAT_VALID_PER) != 0) fprintf(stderr, " PER=%1.1e", *Per); + fprintf(stderr, "\n"); +#endif + return Valid != DTV_STAT_VALID_NONE; +} + int dB1000toPercent(int dB1000, int Low, int High) { // Convert the given value, which is in 1/1000 dBm, to a percentage in the @@ -1058,6 +1155,18 @@ void cDvbTuner::Action(void) isyslog("frontend %d/%d regained lock on channel %d (%s), tp %d", adapter, frontend, channel.Number(), channel.Name(), channel.Transponder()); LostLock = false; } + if (tunerStatus == tsTuned) { + if (SysLogLevel >= 3) { + int Valid; + double Strength, Cnr; + if (GetSignalStats(Valid, &Strength, &Cnr)) { + cString s = cString::sprintf("frontend %d/%d locked with signal", adapter, frontend); + if ((Valid & DTV_STAT_VALID_STRENGTH) != 0) s = cString::sprintf("%s STR=%1.1fdBm", *s, Strength); + if ((Valid & DTV_STAT_VALID_CNR) != 0) s = cString::sprintf("%s CNR=%1.1fdB", *s, Cnr); + dsyslog("%s", *s); + } + } + } tunerStatus = tsLocked; locked.Broadcast(); lastTimeoutReport = 0; @@ -1697,6 +1806,11 @@ const cPositioner *cDvbDevice::Positioner(void) const return dvbTuner ? dvbTuner->Positioner() : NULL; } +bool cDvbDevice::SignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per) const +{ + return dvbTuner ? dvbTuner->GetSignalStats(Valid, Strength, Cnr, BerPre, BerPost, Per) : false; +} + int cDvbDevice::SignalStrength(void) const { return dvbTuner ? dvbTuner->GetSignalStrength() : -1; diff --git a/dvbdevice.h b/dvbdevice.h index a799c0d2..edc94608 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.h 4.2 2017/04/14 09:31:29 kls Exp $ + * $Id: dvbdevice.h 4.3 2017/04/17 14:44:43 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -244,6 +244,7 @@ public: virtual bool ProvidesEIT(void) const; virtual int NumProvidedSystems(void) const; virtual const cPositioner *Positioner(void) const; + virtual bool SignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL) const; virtual int SignalStrength(void) const; virtual int SignalQuality(void) const; virtual const cChannel *GetCurrentlyTunedTransponder(void) const;