1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

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 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 an include of VDR's 'Make.global' to libsi's Makefile
for adding handling of "ANSI/SCTE 57" descriptors 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> Ralf Klueber <ralf.klueber@vodafone.com>
for reporting a bug in cutting a recording if there is only a single editing mark 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 - Avoiding an unecessary call to Recordings.ResetResume() (thanks to Reinhard
Nissl). 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 - Fixed cString's operator=(const char *String) in case the given string is the
same as the existing one (thanks to Dirk Leber). 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). - 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 The return value of cDiseqcs::Get() is now const, so plugin authors may need to
adjust their code if they use this function. 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. * 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" #include "dvbsdffdevice.h"
@ -777,22 +777,7 @@ bool cDvbSdFfDeviceProbe::Probe(int Adapter, int Frontend)
0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE 0x13C21002, // Technotrend/Hauppauge WinTV DVB-S rev1.3 SE
0x00000000 0x00000000
}; };
cString FileName; uint32_t SubsystemId = GetSubsystemId(Adapter, Frontend);
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);
}
for (uint32_t *sid = SubsystemIds; *sid; sid++) { for (uint32_t *sid = SubsystemIds; *sid; sid++) {
if (*sid == SubsystemId) { if (*sid == SubsystemId) {
dsyslog("creating cDvbSdFfDevice"); dsyslog("creating cDvbSdFfDevice");

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "device.h"
@ -618,6 +618,16 @@ int cDevice::NumProvidedSystems(void) const
return 0; return 0;
} }
int cDevice::SignalStrength(void) const
{
return -1;
}
int cDevice::SignalQuality(void) const
{
return -1;
}
const cChannel *cDevice::GetCurrentlyTunedTransponder(void) const const cChannel *cDevice::GetCurrentlyTunedTransponder(void) const
{ {
return NULL; return NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DEVICE_H
@ -253,6 +253,16 @@ public:
///< actually provide channels must implement this function. ///< actually provide channels must implement this function.
///< The result of this function is used when selecting a device, in order ///< The result of this function is used when selecting a device, in order
///< to avoid devices that provide more than one system. ///< 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; virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
///< Returns a pointer to the currently tuned transponder. ///< Returns a pointer to the currently tuned transponder.
///< This is not one of the channels in the global cChannels list, but rather ///< 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "dvbdevice.h"
@ -253,12 +253,15 @@ bool cDvbTransponderParameters::Parse(const char *s)
// --- cDvbTuner ------------------------------------------------------------- // --- cDvbTuner -------------------------------------------------------------
#define TUNER_POLL_TIMEOUT 10 // ms
class cDvbTuner : public cThread { class cDvbTuner : public cThread {
private: private:
enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked }; enum eTunerStatus { tsIdle, tsSet, tsTuned, tsLocked };
int device; int device;
int fd_frontend; int fd_frontend;
int adapter, frontend; int adapter, frontend;
uint32_t subsystemId;
int tuneTimeout; int tuneTimeout;
int lockTimeout; int lockTimeout;
time_t lastTimeoutReport; time_t lastTimeoutReport;
@ -269,16 +272,20 @@ private:
cMutex mutex; cMutex mutex;
cCondVar locked; cCondVar locked;
cCondVar newSet; 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); bool SetFrontend(void);
virtual void Action(void); virtual void Action(void);
public: public:
cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType); cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType);
virtual ~cDvbTuner(); virtual ~cDvbTuner();
const cChannel *GetTransponder(void) const { return &channel; } const cChannel *GetTransponder(void) const { return &channel; }
uint32_t SubsystemId(void) const { return subsystemId; }
bool IsTunedTo(const cChannel *Channel) const; bool IsTunedTo(const cChannel *Channel) const;
void Set(const cChannel *Channel); void Set(const cChannel *Channel);
bool Locked(int TimeoutMs = 0); 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) 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; adapter = Adapter;
frontend = Frontend; frontend = Frontend;
frontendType = FrontendType; frontendType = FrontendType;
subsystemId = cDvbDeviceProbe::GetSubsystemId(adapter, frontend);
tuneTimeout = 0; tuneTimeout = 0;
lockTimeout = 0; lockTimeout = 0;
lastTimeoutReport = 0; lastTimeoutReport = 0;
@ -339,16 +347,19 @@ bool cDvbTuner::Locked(int TimeoutMs)
return tunerStatus >= tsLocked; return tunerStatus >= tsLocked;
} }
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs) void cDvbTuner::ClearEventQueue(void) const
{ {
if (TimeoutMs) {
cPoller Poller(fd_frontend); cPoller Poller(fd_frontend);
if (Poller.Poll(TimeoutMs)) { if (Poller.Poll(TUNER_POLL_TIMEOUT)) {
dvb_frontend_event Event; dvb_frontend_event Event;
while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0) while (ioctl(fd_frontend, FE_GET_EVENT, &Event) == 0)
; // just to clear the event queue - we'll read the actual status below ; // just to clear the event queue - we'll read the actual status below
} }
} }
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
{
ClearEventQueue();
while (1) { while (1) {
if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1) if (ioctl(fd_frontend, FE_READ_STATUS, &Status) != -1)
return true; return true;
@ -358,6 +369,104 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status, int TimeoutMs)
return false; 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) static unsigned int FrequencyToHz(unsigned int f)
{ {
while (f && f < 1000000) while (f && f < 1000000)
@ -527,7 +636,7 @@ void cDvbTuner::Action(void)
fe_status_t Status = (fe_status_t)0; fe_status_t Status = (fe_status_t)0;
while (Running()) { while (Running()) {
fe_status_t NewStatus; fe_status_t NewStatus;
if (GetFrontendStatus(NewStatus, 10)) if (GetFrontendStatus(NewStatus))
Status = NewStatus; Status = NewStatus;
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
switch (tunerStatus) { switch (tunerStatus) {
@ -936,7 +1045,7 @@ bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *Ne
bool hasPriority = Priority < 0 || Priority > this->Priority(); bool hasPriority = Priority < 0 || Priority > this->Priority();
bool needsDetachReceivers = false; bool needsDetachReceivers = false;
if (ProvidesTransponder(Channel)) { if (dvbTuner && ProvidesTransponder(Channel)) {
result = hasPriority; result = hasPriority;
if (Priority >= 0 && Receiving(true)) { if (Priority >= 0 && Receiving(true)) {
if (dvbTuner->IsTunedTo(Channel)) { if (dvbTuner->IsTunedTo(Channel)) {
@ -969,18 +1078,29 @@ int cDvbDevice::NumProvidedSystems(void) const
return numProvidedSystems; 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 const cChannel *cDvbDevice::GetCurrentlyTunedTransponder(void) const
{ {
return dvbTuner->GetTransponder(); return dvbTuner ? dvbTuner->GetTransponder() : NULL;
} }
bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel) bool cDvbDevice::IsTunedToTransponder(const cChannel *Channel)
{ {
return dvbTuner->IsTunedTo(Channel); return dvbTuner ? dvbTuner->IsTunedTo(Channel) : false;
} }
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
{ {
if (dvbTuner)
dvbTuner->Set(Channel); dvbTuner->Set(Channel);
return true; return true;
} }
@ -1036,3 +1156,24 @@ cDvbDeviceProbe::~cDvbDeviceProbe()
{ {
DvbDeviceProbes.Del(this, false); 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 * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __DVBDEVICE_H
@ -141,6 +141,8 @@ public:
virtual bool ProvidesTransponder(const cChannel *Channel) const; virtual bool ProvidesTransponder(const cChannel *Channel) const;
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const; virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
virtual int NumProvidedSystems(void) const; virtual int NumProvidedSystems(void) const;
virtual int SignalStrength(void) const;
virtual int SignalQuality(void) const;
virtual const cChannel *GetCurrentlyTunedTransponder(void) const; virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
virtual bool IsTunedToTransponder(const cChannel *Channel); virtual bool IsTunedToTransponder(const cChannel *Channel);
protected: protected:
@ -196,6 +198,7 @@ class cDvbDeviceProbe : public cListObject {
public: public:
cDvbDeviceProbe(void); cDvbDeviceProbe(void);
virtual ~cDvbDeviceProbe(); virtual ~cDvbDeviceProbe();
static uint32_t GetSubsystemId(int Adapter, int Frontend);
virtual bool Probe(int Adapter, int Frontend) = 0; virtual bool Probe(int Adapter, int Frontend) = 0;
///< Probes for a DVB device at the given Adapter and creates the appropriate ///< Probes for a DVB device at the given Adapter and creates the appropriate
///< object derived from cDvbDevice if applicable. ///< object derived from cDvbDevice if applicable.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 // 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, clrChannelEpgShortText, clrYellow);
THEME_CLR(Theme, clrChannelTimebarSeen, clrYellow); THEME_CLR(Theme, clrChannelTimebarSeen, clrYellow);
THEME_CLR(Theme, clrChannelTimebarRest, clrGray50); THEME_CLR(Theme, clrChannelTimebarRest, clrGray50);
THEME_CLR(Theme, clrChannelSignalValue, clrGreen);
THEME_CLR(Theme, clrChannelSignalRest, clrRed);
THEME_CLR(Theme, clrMenuFrame, clrYellow); THEME_CLR(Theme, clrMenuFrame, clrYellow);
THEME_CLR(Theme, clrMenuTitle, clrBlack); THEME_CLR(Theme, clrMenuTitle, clrBlack);
THEME_CLR(Theme, clrMenuDate, clrBlack); THEME_CLR(Theme, clrMenuDate, clrBlack);
@ -134,6 +136,9 @@ private:
const cEvent *present; const cEvent *present;
cString lastDate; cString lastDate;
int lastSeen; int lastSeen;
int lastDeviceNumber;
int lastSignalStrength;
int lastSignalQuality;
tTrackId lastTrackId; tTrackId lastTrackId;
static cBitmap bmTeletext, bmRadio, bmAudio, bmDolbyDigital, bmEncrypted, bmRecording; static cBitmap bmTeletext, bmRadio, bmAudio, bmDolbyDigital, bmEncrypted, bmRecording;
public: public:
@ -156,6 +161,9 @@ cSkinSTTNGDisplayChannel::cSkinSTTNGDisplayChannel(bool WithInfo)
{ {
present = NULL; present = NULL;
lastSeen = -1; lastSeen = -1;
lastDeviceNumber = -1;
lastSignalStrength = -1;
lastSignalQuality = -1;
memset(&lastTrackId, 0, sizeof(lastTrackId)); memset(&lastTrackId, 0, sizeof(lastTrackId));
const cFont *font = cFont::GetFont(fontOsd); const cFont *font = cFont::GetFont(fontOsd);
withInfo = WithInfo; withInfo = WithInfo;
@ -344,6 +352,36 @@ void cSkinSTTNGDisplayChannel::Flush(void)
osd->DrawRectangle(x1 + Gap, y3, x1 + Gap + ScrollWidth - 1, y3 + seen, Theme.Color(clrChannelTimebarSeen)); osd->DrawRectangle(x1 + Gap, y3, x1 + Gap + ScrollWidth - 1, y3 + seen, Theme.Color(clrChannelTimebarSeen));
lastSeen = seen; 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(); osd->Flush();
} }