Implemented signal strength and quality handling

This commit is contained in:
Klaus Schmidinger 2011-06-02 13:28:42 +02:00
parent 40278ec121
commit e572cbda47
8 changed files with 235 additions and 39 deletions

View File

@ -1104,6 +1104,7 @@ Rolf Ahrenberg <rahrenbe@cc.hut.fi>
for adding support for "registration descriptor" to 'libsi' and using it in pat.c
for adding an include of VDR's 'Make.global' to libsi's Makefile
for adding handling of "ANSI/SCTE 57" descriptors
for some input on how to use BER and UNC values to generate a "quality" value
Ralf Klueber <ralf.klueber@vodafone.com>
for reporting a bug in cutting a recording if there is only a single editing mark

10
HISTORY
View File

@ -6607,7 +6607,7 @@ Video Disk Recorder Revision History
- Avoiding an unecessary call to Recordings.ResetResume() (thanks to Reinhard
Nissl).
2011-05-22: Version 1.7.19
2011-06-02: Version 1.7.19
- Fixed cString's operator=(const char *String) in case the given string is the
same as the existing one (thanks to Dirk Leber).
@ -6624,3 +6624,11 @@ Video Disk Recorder Revision History
- Fixed a possible race condition in cDiseqc::Execute() (reported by Marco Göbenich).
The return value of cDiseqcs::Get() is now const, so plugin authors may need to
adjust their code if they use this function.
- The new functions cDevice::SignalStrength() and cDevice::SignalQuality() can be
used to determine the signal strength and quality of a given device (thanks to
Rolf Ahrenberg for some input on how to use BER and UNC values to generate a
"quality" value).
- The 'sttng' skin now displays two colored bars at the bottom of the channel display,
indicating the strength (upper bar) and quality (lower bar) of the received signal.
The number to the left of these bars indicates the actual device the current
channel is being received with.

View File

@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
* $Id: dvbsdffdevice.c 2.28 2011/05/21 13:24:35 kls Exp $
* $Id: dvbsdffdevice.c 2.29 2011/05/22 15:22:14 kls Exp $
*/
#include "dvbsdffdevice.h"
@ -777,22 +777,7 @@ bool cDvbSdFfDeviceProbe::Probe(int Adapter, int Frontend)
0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
0x00000000
};
cString FileName;
cReadLine ReadLine;
FILE *f = NULL;
uint32_t SubsystemId = 0;
FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor", Adapter, Frontend);
if ((f = fopen(FileName, "r")) != NULL) {
if (char *s = ReadLine.Read(f))
SubsystemId = strtoul(s, NULL, 0) << 16;
fclose(f);
}
FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_device", Adapter, Frontend);
if ((f = fopen(FileName, "r")) != NULL) {
if (char *s = ReadLine.Read(f))
SubsystemId |= strtoul(s, NULL, 0);
fclose(f);
}
uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend);
for (uint32_t *sid = SubsystemIds; *sid; sid++) {
if (*sid == SubsystemId) {
dsyslog("creating cDvbSdFfDevice");

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 2.40 2011/05/22 09:42:57 kls Exp $
* $Id: device.c 2.41 2011/06/02 13:14:16 kls Exp $
*/
#include "device.h"
@ -618,6 +618,16 @@ int cDevice::NumProvidedSystems(void) const
return 0;
}
int cDevice::SignalStrength(void) const
{
return -1;
}
int cDevice::SignalQuality(void) const
{
return -1;
}
const cChannel *cDevice::GetCurrentlyTunedTransponder(void) const
{
return NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 2.25 2011/05/21 12:54:43 kls Exp $
* $Id: device.h 2.26 2011/06/02 13:15:31 kls Exp $
*/
#ifndef __DEVICE_H
@ -253,6 +253,16 @@ public:
///< actually provide channels must implement this function.
///< The result of this function is used when selecting a device, in order
///< to avoid devices that provide more than one system.
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
///< 100 (best possible signal). A value of -1 indicates that this
///< device has no concept of a "signal strength".
virtual int SignalQuality(void) const;
///< Returns the "quality" of the currently received signal.
///< This is a value in the range 0 (worst quality) through
///< 100 (best possible quality). A value of -1 indicates that this
///< device has no concept of a "signal quality".
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
///< Returns a pointer to the currently tuned transponder.
///< This is not one of the channels in the global cChannels list, but rather

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 2.39 2011/05/22 10:34:49 kls Exp $
* $Id: dvbdevice.c 2.40 2011/06/02 13:28:42 kls Exp $
*/
#include "dvbdevice.h"
@ -253,12 +253,15 @@ bool cDvbTransponderParameters::Parse(const char *s)
// --- cDvbTuner -------------------------------------------------------------
#define TUNER_POLL_TIMEOUT 10 // ms
class cDvbTuner : public cThread {
private:
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int device;
int fd_frontend;
int adapter, frontend;
uint32_t subsystemId;
int tuneTimeout;
int lockTimeout;
time_t lastTimeoutReport;
@ -269,16 +272,20 @@ private:
cMutex mutex;
cCondVar locked;
cCondVar newSet;
bool GetFrontendStatus(fe_status_t &Status, int TimeoutMs = 0);
void ClearEventQueue(void) const;
bool GetFrontendStatus(fe_status_t &Status) const;
bool SetFrontend(void);
virtual void Action(void);
public:
cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType);
virtual ~cDvbTuner();
const cChannel *GetTransponder(void) const { return &channel; }
uint32_t SubsystemId(void) const { return subsystemId; }
bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel);
bool Locked(int TimeoutMs = 0);
int GetSignalStrength(void) const;
int GetSignalQuality(void) const;
};
cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType)
@ -288,6 +295,7 @@ cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_
adapter = Adapter;
frontend = Frontend;
frontendType = FrontendType;
subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend);
tuneTimeout = 0;
lockTimeout = 0;
lastTimeoutReport = 0;
@ -339,16 +347,19 @@ bool cDvbTuner::Locked(int TimeoutMs)
return tunerStatus >= tsLocked;
}
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
void cDvbTuner::ClearEventQueue(void) const
{
if (TimeoutMs) {
cPoller Poller(fd_frontend);
if (Poller.Poll(TimeoutMs)) {
dvb_frontend_event Event;
while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
; // just to clear the event queue - we'll read the actual status below
}
cPoller Poller(fd_frontend);
if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
dvb_frontend_event Event;
while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
; // just to clear the event queue - we'll read the actual status below
}
}
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
{
ClearEventQueue();
while (1) {
if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
return true;
@ -358,6 +369,104 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
return false;
}
//#define DEBUG_SIGNALSTRENGTH
//#define DEBUG_SIGNALQUALITY
int cDvbTuner::GetSignalStrength(void) const
{
ClearEventQueue();
uint16_t Signal;
while (1) {
if (ioctl(fd_frontend, FE_READ_SIGNAL_STRENGTH, &Signal) != -1)
break;
if (errno != EINTR)
return -1;
}
uint16_t MaxSignal = 0xFFFF; // Let's assume the default is using the entire range.
// Use the subsystemId to identify individual devices in case they need
// special treatment to map their Signal value into the range 0...0xFFFF.
int s = int(Signal) * 100 / MaxSignal;
if (s > 100)
s = 100;
#ifdef DEBUG_SIGNALSTRENGTH
fprintf(stderr, "FE %d/%d: %08X S = %04X %04X %3d%%\n", adapter, frontend, subsystemId, MaxSignal, Signal, s);
#endif
return s;
}
#define LOCK_THRESHOLD 10
int cDvbTuner::GetSignalQuality(void) const
{
fe_status_t Status;
if (GetFrontendStatus(Status)) {
if ((Status & FE_HAS_SIGNAL) == 0)
return 0;
if ((Status & FE_HAS_CARRIER) == 0)
return LOCK_THRESHOLD / 4;
if ((Status & FE_HAS_VITERBI) == 0)
return LOCK_THRESHOLD / 3;
if ((Status & FE_HAS_SYNC) == 0)
return LOCK_THRESHOLD / 2;
if ((Status & FE_HAS_LOCK) == 0)
return LOCK_THRESHOLD;
bool HasSnr = true;
uint16_t Snr;
while (1) {
if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
break;
if (errno == EOPNOTSUPP) {
Snr = 0xFFFF;
HasSnr = false;
break;
}
if (errno != EINTR)
return -1;
}
bool HasBer = true;
uint32_t Ber;
while (1) {
if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
break;
if (errno == EOPNOTSUPP) {
Ber = 0;
HasBer = false;
break;
}
if (errno != EINTR)
return -1;
}
bool HasUnc = true;
uint32_t Unc;
while (1) {
if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
break;
if (errno == EOPNOTSUPP) {
Unc = 0;
HasUnc = false;
break;
}
if (errno != EINTR)
return -1;
}
uint16_t MaxSnr = 0xFFFF; // Let's assume the default is using the entire range.
// Use the subsystemId to identify individual devices in case they need
// special treatment to map their Snr value into the range 0...0xFFFF.
int a = int(Snr) * 100 / MaxSnr;
int b = 100 - (Unc * 10 + (Ber / 256) * 5);
if (b < 0)
b = 0;
int q = LOCK_THRESHOLD + a * b * (100 - LOCK_THRESHOLD) / 100 / 100;
if (q > 100)
q = 100;
#ifdef DEBUG_SIGNALQUALITY
fprintf(stderr, "FE %d/%d: %08X Q = %04X %04X %5d %5d %3d%%\n", adapter, frontend, subsystemId, MaxSnr, Snr, HasBer ? int(Ber) : -1, HasUnc ? int(Unc) : -1, q);
#endif
return q;
}
return -1;
}
static unsigned int FrequencyToHz(unsigned int f)
{
while (f && f < 1000000)
@ -504,9 +613,9 @@ bool cDvbTuner::SetFrontend(void)
SETCMD(DTV_FREQUENCY, FrequencyToHz(channel.Frequency()));
SETCMD(DTV_INVERSION, dtp.Inversion());
SETCMD(DTV_MODULATION, dtp.Modulation());
tuneTimeout = ATSC_TUNE_TIMEOUT;
lockTimeout = ATSC_LOCK_TIMEOUT;
lockTimeout = ATSC_LOCK_TIMEOUT;
}
else {
esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
@ -527,7 +636,7 @@ void cDvbTuner::Action(void)
fe_status_t Status = (fe_status_t)0;
while (Running()) {
fe_status_t NewStatus;
if (GetFrontendStatus(NewStatus, 10))
if (GetFrontendStatus(NewStatus))
Status = NewStatus;
cMutexLock MutexLock(&mutex);
switch (tunerStatus) {
@ -936,7 +1045,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false;
if (ProvidesTransponder(Channel)) {
if (dvbTuner && ProvidesTransponder(Channel)) {
result = hasPriority;
if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) {
@ -969,19 +1078,30 @@ int cDvbDevice::NumProvidedSystems(void) const
return numProvidedSystems;
}
int cDvbDevice::SignalStrength(void) const
{
return dvbTuner ? dvbTuner->GetSignalStrength() : -1;
}
int cDvbDevice::SignalQuality(void) const
{
return dvbTuner ? dvbTuner->GetSignalQuality() : -1;
}
const cChannel *cDvbDevice::GetCurrentlyTunedTransponder(void) const
{
return dvbTuner->GetTransponder();
return dvbTuner ? dvbTuner->GetTransponder() : NULL;
}
bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
{
return dvbTuner->IsTunedTo(Channel);
return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
}
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{
dvbTuner->Set(Channel);
if (dvbTuner)
dvbTuner->Set(Channel);
return true;
}
@ -1036,3 +1156,24 @@ cDvbDeviceProbe::~cDvbDeviceProbe()
{
DvbDeviceProbes.Del(this, false);
}
uint32_t cDvbDeviceProbe::GetSubsystemId(int Adapter, int Frontend)
{
cString FileName;
cReadLine ReadLine;
FILE *f = NULL;
uint32_t SubsystemId = 0;
FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_vendor", Adapter, Frontend);
if ((f = fopen(FileName, "r")) != NULL) {
if (char *s = ReadLine.Read(f))
SubsystemId = strtoul(s, NULL, 0) << 16;
fclose(f);
}
FileName = cString::sprintf("/sys/class/dvb/dvb%d.frontend%d/device/subsystem_device", Adapter, Frontend);
if ((f = fopen(FileName, "r")) != NULL) {
if (char *s = ReadLine.Read(f))
SubsystemId |= strtoul(s, NULL, 0);
fclose(f);
}
return SubsystemId;
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 2.14 2010/04/11 10:29:37 kls Exp $
* $Id: dvbdevice.h 2.15 2011/06/02 13:20:05 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -141,6 +141,8 @@ public:
virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
virtual int NumProvidedSystems(void) const;
virtual int SignalStrength(void) const;
virtual int SignalQuality(void) const;
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
virtual bool IsTunedToTransponder(const cChannel *Channel);
protected:
@ -196,6 +198,7 @@ class cDvbDeviceProbe : public cListObject {
public:
cDvbDeviceProbe(void);
virtual ~cDvbDeviceProbe();
static uint32_t GetSubsystemId(int Adapter, int Frontend);
virtual bool Probe(int Adapter, int Frontend) = 0;
///< Probes for a DVB device at the given Adapter and creates the appropriate
///< object derived from cDvbDevice if applicable.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: skinsttng.c 2.7 2011/02/20 13:02:49 kls Exp $
* $Id: skinsttng.c 2.8 2011/06/02 12:54:43 kls Exp $
*/
// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
@ -92,6 +92,8 @@ THEME_CLR(Theme, clrChannelEpgTitle, clrCyan);
THEME_CLR(Theme, clrChannelEpgShortText, clrYellow);
THEME_CLR(Theme, clrChannelTimebarSeen, clrYellow);
THEME_CLR(Theme, clrChannelTimebarRest, clrGray50);
THEME_CLR(Theme, clrChannelSignalValue, clrGreen);
THEME_CLR(Theme, clrChannelSignalRest, clrRed);
THEME_CLR(Theme, clrMenuFrame, clrYellow);
THEME_CLR(Theme, clrMenuTitle, clrBlack);
THEME_CLR(Theme, clrMenuDate, clrBlack);
@ -134,6 +136,9 @@ private:
const cEvent *present;
cString lastDate;
int lastSeen;
int lastDeviceNumber;
int lastSignalStrength;
int lastSignalQuality;
tTrackId lastTrackId;
static cBitmap bmTeletext, bmRadio, bmAudio, bmDolbyDigital, bmEncrypted, bmRecording;
public:
@ -156,6 +161,9 @@ cSkinSTTNGDisplayChannel::cSkinSTTNGDisplayChannel(bool WithInfo)
{
present = NULL;
lastSeen = -1;
lastDeviceNumber = -1;
lastSignalStrength = -1;
lastSignalQuality = -1;
memset(&lastTrackId, 0, sizeof(lastTrackId));
const cFont *font = cFont::GetFont(fontOsd);
withInfo = WithInfo;
@ -344,6 +352,36 @@ void cSkinSTTNGDisplayChannel::Flush(void)
osd->DrawRectangle(x1 + Gap, y3, x1 + Gap + ScrollWidth - 1, y3 + seen, Theme.Color(clrChannelTimebarSeen));
lastSeen = seen;
}
int DeviceNumber = cDevice::ActualDevice()->DeviceNumber() + 1;
int SignalStrength = cDevice::ActualDevice()->SignalStrength();
int SignalQuality = cDevice::ActualDevice()->SignalQuality();
if (DeviceNumber != lastDeviceNumber || SignalStrength != lastSignalStrength || SignalQuality != lastSignalQuality) {
int d = 3;
int h = ((y7 - y6 + 1) - 3 * d) / 2;
int w = (x4 - x3) / 5;
int x = (x3 + x4) / 2 - w / 2;
if (SignalStrength >= 0) {
int s = SignalStrength * w / 100;
osd->DrawRectangle(x, y6 + d, x + s - 1, y6 + d + h - 1, Theme.Color(clrChannelSignalValue));
osd->DrawRectangle(x + s, y6 + d, x + w - 1, y6 + d + h - 1, Theme.Color(clrChannelSignalRest));
}
else if (DeviceNumber != lastDeviceNumber)
osd->DrawRectangle(x, y6 + d, x + w - 1, y6 + d + h - 1, Theme.Color(clrChannelFrame));
if (SignalQuality >= 0) {
int q = SignalQuality * w / 100;
osd->DrawRectangle(x, y7 - d - h + 1, x + q - 1, y7 - d, Theme.Color(clrChannelSignalValue));
osd->DrawRectangle(x + q, y7 - d - h + 1, x + w - 1, y7 - d, Theme.Color(clrChannelSignalRest));
}
else if (DeviceNumber != lastDeviceNumber)
osd->DrawRectangle(x, y7 - d - h + 1, x + w - 1, y7 - d, Theme.Color(clrChannelFrame));
cString dn = cString::sprintf(" %d ", DeviceNumber);
const cFont *font = cFont::GetFont(fontSml);
int dw = font->Width(dn);
osd->DrawText(x - 2 * d - dw, y6, dn, Theme.Color(clrChannelDate), frameColor, font, dw);
lastDeviceNumber = DeviceNumber;
lastSignalStrength = SignalStrength;
lastSignalQuality = SignalQuality;
}
}
osd->Flush();
}