diff --git a/CONTRIBUTORS b/CONTRIBUTORS index bc5b0b47..d128222b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1104,6 +1104,7 @@ Rolf Ahrenberg 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 for reporting a bug in cutting a recording if there is only a single editing mark diff --git a/HISTORY b/HISTORY index 3dbbb38c..4b091020 100644 --- a/HISTORY +++ b/HISTORY @@ -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. diff --git a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c index cad7d311..73e55bac 100644 --- a/PLUGINS/src/dvbsddevice/dvbsdffdevice.c +++ b/PLUGINS/src/dvbsddevice/dvbsdffdevice.c @@ -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"); diff --git a/device.c b/device.c index 854ee677..07599936 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 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; diff --git a/device.h b/device.h index 14036c61..d937e5f0 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 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 diff --git a/dvbdevice.c b/dvbdevice.c index 0d882b15..7ce31ce2 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 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; +} diff --git a/dvbdevice.h b/dvbdevice.h index ff606fd4..0962548c 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 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. diff --git a/skinsttng.c b/skinsttng.c index 9bd1b15c..5039a6c2 100644 --- a/skinsttng.c +++ b/skinsttng.c @@ -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(); }