diff --git a/src/htsp_server.c b/src/htsp_server.c index ea2a625c..95580905 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2798,9 +2798,9 @@ htsp_subscription_signal_status(htsp_subscription_t *hs, signal_status_t *sig) htsmsg_add_str(m, "method", "signalStatus"); htsmsg_add_u32(m, "subscriptionId", hs->hs_sid); htsmsg_add_str(m, "feStatus", sig->status_text); - if(sig->snr != -2) + if((sig->snr != -2) && (sig->snr_scale == SIGNAL_STATUS_SCALE_RELATIVE)) htsmsg_add_u32(m, "feSNR", sig->snr); - if(sig->signal != -2) + if((sig->signal != -2) && (sig->signal_scale == SIGNAL_STATUS_SCALE_RELATIVE)) htsmsg_add_u32(m, "feSignal", sig->signal); if(sig->ber != -2) htsmsg_add_u32(m, "feBER", sig->ber); diff --git a/src/input.c b/src/input.c index 5f1f3310..fdfa36c2 100644 --- a/src/input.c +++ b/src/input.c @@ -80,12 +80,18 @@ tvh_input_stream_create_msg htsmsg_add_u32(m, "subs", st->subs_count); htsmsg_add_u32(m, "weight", st->max_weight); htsmsg_add_u32(m, "signal", st->stats.signal); + htsmsg_add_u32(m, "signal_scale", st->stats.signal_scale); htsmsg_add_u32(m, "ber", st->stats.ber); htsmsg_add_u32(m, "snr", st->stats.snr); + htsmsg_add_u32(m, "snr_scale", st->stats.snr_scale); htsmsg_add_u32(m, "unc", st->stats.unc); htsmsg_add_u32(m, "bps", st->stats.bps); htsmsg_add_u32(m, "te", st->stats.te); htsmsg_add_u32(m, "cc", st->stats.cc); + htsmsg_add_u32(m, "ec_bit", st->stats.ec_bit); + htsmsg_add_u32(m, "tc_bit", st->stats.tc_bit); + htsmsg_add_u32(m, "ec_block", st->stats.ec_block); + htsmsg_add_u32(m, "tc_block", st->stats.tc_block); return m; } diff --git a/src/input.h b/src/input.h index 946b21f3..2ee5cb9d 100644 --- a/src/input.h +++ b/src/input.h @@ -34,18 +34,42 @@ typedef LIST_HEAD(,tvh_hardware) tvh_hardware_list_t; typedef LIST_HEAD(,tvh_input) tvh_input_list_t; typedef LIST_HEAD(,tvh_input_stream) tvh_input_stream_list_t; +/* + * Scales for input stream statistics values + */ +typedef enum { + INPUT_STREAM_STATS_SCALE_UNKNOWN = 0, + INPUT_STREAM_STATS_SCALE_RELATIVE, // value is unsigned, where 0 means 0% and 65535 means 100% + INPUT_STREAM_STATS_SCALE_DECIBEL // value is measured in dB +} tvh_input_stream_stats_scale_t; + /* * Input stream structure - used for getting statistics about active streams */ struct tvh_input_stream_stats { - int signal; ///< Signal level (0-100) - int ber; ///< Bit error rate (0-100?) - int unc; ///< Uncorrectable errors - int snr; ///< Signal 2 Noise (dB) - int bps; ///< Bandwidth (bps) - int cc; ///< Continuity errors - int te; ///< Transport errors + int signal; ///< signal strength, value depending on signal_scale value: + ///< - SCALE_RELATIVE : 0...65535 (which means 0%...100%) + ///< - SCALE DECIBEL : 0.0001 dBm units. This value is generally negative. + int snr; ///< signal to noise ratio, value depending on snr_scale value: + ///< - SCALE_RELATIVE : 0...65535 (which means 0%...100%) + ///< - SCALE DECIBEL : 0.0001 dB units. + int ber; ///< bit error rate (driver/vendor specific value!) + int unc; ///< number of uncorrected blocks + int bps; ///< bandwidth (bps) + int cc; ///< number of continuity errors + int te; ///< number of transport errors + + tvh_input_stream_stats_scale_t signal_scale; + tvh_input_stream_stats_scale_t snr_scale; + + /* Note: if tc_bit > 0, BER = ec_bit / tc_bit (0...1) else BER = ber (driver specific value) */ + int ec_bit; ///< ERROR_BIT_COUNT (same as unc?) + int tc_bit; ///< TOTAL_BIT_COUNT + + /* Note: PER = ec_block / tc_block (0...1) */ + int ec_block; ///< ERROR_BLOCK_COUNT + int tc_block; ///< TOTAL_BLOCK_COUNT }; struct tvh_input_stream { diff --git a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c index 28a9885c..a793bfec 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c @@ -432,6 +432,7 @@ linuxdvb_frontend_monitor ( void *aux ) #if DVB_VER_ATLEAST(5,10) struct dtv_property fe_properties[6]; struct dtv_properties dtv_prop; + int gotprop; #endif lfe->mi_display_name((mpegts_input_t*)lfe, buf, sizeof(buf)); @@ -536,82 +537,194 @@ linuxdvb_frontend_monitor ( void *aux ) /* Statistics - New API */ #if DVB_VER_ATLEAST(5,10) memset(&fe_properties, 0, sizeof(fe_properties)); + + /* Signal strength */ fe_properties[0].cmd = DTV_STAT_SIGNAL_STRENGTH; /* BER */ fe_properties[1].cmd = DTV_STAT_PRE_ERROR_BIT_COUNT; fe_properties[2].cmd = DTV_STAT_PRE_TOTAL_BIT_COUNT; + + /* SNR */ fe_properties[3].cmd = DTV_STAT_CNR; - /* PER */ + /* PER / UNC */ fe_properties[4].cmd = DTV_STAT_ERROR_BLOCK_COUNT; fe_properties[5].cmd = DTV_STAT_TOTAL_BLOCK_COUNT; dtv_prop.num = 6; dtv_prop.props = fe_properties; if(!ioctl(lfe->lfe_fe_fd, FE_GET_PROPERTY, &dtv_prop)) { + /* Signal strength */ + gotprop = 0; if(fe_properties[0].u.st.len > 0) { - if(fe_properties[0].u.st.stat[0].scale == FE_SCALE_RELATIVE) - mmi->mmi_stats.signal = (fe_properties[0].u.st.stat[0].uvalue * 100) / 0xffff; - /* TODO: handle other scales */ + if(fe_properties[0].u.st.stat[0].scale == FE_SCALE_RELATIVE) { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; + mmi->mmi_stats.signal = fe_properties[0].u.st.stat[0].uvalue; + gotprop = 1; + } + else if(fe_properties[0].u.st.stat[0].scale == FE_SCALE_DECIBEL) { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_DECIBEL; + mmi->mmi_stats.signal = fe_properties[0].u.st.stat[0].svalue; + gotprop = 1; + } + else { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled signal scale: %d", + fe_properties[0].u.st.stat[0].scale); + } + } + if(!gotprop) { + /* try old API */ + if (!ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16)) { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; + mmi->mmi_stats.signal = u16; + } + else { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide signal strength value."); + } } - /* Calculate BER from PRE_ERROR and TOTAL_BIT_COUNT */ + /* ERROR_BIT_COUNT */ + gotprop = 0; if(fe_properties[1].u.st.len > 0) { - if(fe_properties[1].u.st.stat[0].scale == FE_SCALE_COUNTER) - u16 = fe_properties[1].u.st.stat[0].uvalue; - } - if(fe_properties[2].u.st.len > 0) { - if(fe_properties[2].u.st.stat[0].scale == FE_SCALE_COUNTER) { - if(fe_properties[2].u.st.stat[0].uvalue > 0 ) - mmi->mmi_stats.ber = u16 / fe_properties[2].u.st.stat[0].uvalue; - else - mmi->mmi_stats.ber = 0; + if(fe_properties[1].u.st.stat[0].scale == FE_SCALE_COUNTER) { + mmi->mmi_stats.ec_bit = fe_properties[1].u.st.stat[0].uvalue; + gotprop = 1; } + else + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled ERROR_BIT_COUNT scale: %d", + fe_properties[1].u.st.stat[0].scale); + } + /* TOTAL_BIT_COUNT */ + if(gotprop && (fe_properties[2].u.st.len > 0)) { + gotprop = 0; + if(fe_properties[2].u.st.stat[0].scale == FE_SCALE_COUNTER) { + mmi->mmi_stats.tc_bit = fe_properties[2].u.st.stat[0].uvalue; + gotprop = 1; + } + else { + mmi->mmi_stats.ec_bit = 0; /* both values or none */ + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled TOTAL_BIT_COUNT scale: %d", + fe_properties[2].u.st.stat[0].scale); + } + } + if(!gotprop) { + /* try old API */ + if (!ioctl(lfe->lfe_fe_fd, FE_READ_BER, &u32)) + mmi->mmi_stats.ber = u32; + else + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide BER value."); } /* SNR */ + gotprop = 0; if(fe_properties[3].u.st.len > 0) { - /* note that decibel scale means 1 = 0.0001 dB units here */ - if(fe_properties[3].u.st.stat[0].scale == FE_SCALE_DECIBEL) - mmi->mmi_stats.snr = fe_properties[3].u.st.stat[0].svalue * 0.0001; - /* TODO: handle other scales */ - } - - /* Calculate PER from PRE_ERROR and TOTAL_BIT_COUNT */ - if(fe_properties[4].u.st.len > 0) { - if(fe_properties[4].u.st.stat[0].scale == FE_SCALE_COUNTER) - u16 = fe_properties[4].u.st.stat[0].uvalue; - } - if(fe_properties[5].u.st.len > 0) { - if(fe_properties[5].u.st.stat[0].scale == FE_SCALE_COUNTER) { - if(fe_properties[5].u.st.stat[0].uvalue > 0 ) - mmi->mmi_stats.unc = u16 / fe_properties[5].u.st.stat[0].uvalue; - else - mmi->mmi_stats.unc = 0; + if(fe_properties[3].u.st.stat[0].scale == FE_SCALE_RELATIVE) { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; + mmi->mmi_stats.snr = fe_properties[3].u.st.stat[0].uvalue; + gotprop = 1; + } + else if(fe_properties[3].u.st.stat[0].scale == FE_SCALE_DECIBEL) { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_DECIBEL; + mmi->mmi_stats.snr = fe_properties[3].u.st.stat[0].svalue; + gotprop = 1; + } + else { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled SNR scale: %d", + fe_properties[3].u.st.stat[0].scale); } } - + if(!gotprop) { + /* try old API */ + if (!ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16)) { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; + mmi->mmi_stats.snr = u16; + } + else { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide SNR value."); + } + } + + /* ERROR_BLOCK_COUNT == Uncorrected blocks (UNC) */ + gotprop = 0; + if(fe_properties[4].u.st.len > 0) { + if(fe_properties[4].u.st.stat[0].scale == FE_SCALE_COUNTER) { + mmi->mmi_stats.unc = mmi->mmi_stats.ec_block = fe_properties[4].u.st.stat[0].uvalue; + gotprop = 1; + } + else + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled ERROR_BLOCK_COUNT scale: %d", + fe_properties[4].u.st.stat[0].scale); + } + + /* TOTAL_BLOCK_COUNT */ + if(gotprop && (fe_properties[5].u.st.len > 0)) { + gotprop = 0; + if(fe_properties[5].u.st.stat[0].scale == FE_SCALE_COUNTER) { + mmi->mmi_stats.tc_block = fe_properties[5].u.st.stat[0].uvalue; + gotprop = 1; + } + else { + mmi->mmi_stats.ec_block = mmi->mmi_stats.unc = 0; /* both values or none */ + tvhlog(LOG_WARNING, "linuxdvb", "Unhandled TOTAL_BLOCK_COUNT scale: %d", + fe_properties[5].u.st.stat[0].scale); + } + } + if(!gotprop) { + /* try old API */ + if (!ioctl(lfe->lfe_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &u32)) { + mmi->mmi_stats.unc = u32; + gotprop = 1; + } + else + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide UNC value."); + } /* Older API */ } else #endif { - if (!ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16)) + if (!ioctl(lfe->lfe_fe_fd, FE_READ_SIGNAL_STRENGTH, &u16)) { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; mmi->mmi_stats.signal = u16; + } + else { + mmi->mmi_stats.signal_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide signal strength value."); + } if (!ioctl(lfe->lfe_fe_fd, FE_READ_BER, &u32)) mmi->mmi_stats.ber = u32; - if (!ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16)) + else + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide BER value."); + if (!ioctl(lfe->lfe_fe_fd, FE_READ_SNR, &u16)) { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_RELATIVE; mmi->mmi_stats.snr = u16; + } + else { + mmi->mmi_stats.snr_scale = INPUT_STREAM_STATS_SCALE_UNKNOWN; + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide SNR value."); + } if (!ioctl(lfe->lfe_fe_fd, FE_READ_UNCORRECTED_BLOCKS, &u32)) mmi->mmi_stats.unc = u32; + else + tvhlog(LOG_WARNING, "linuxdvb", "Unable to provide UNC value."); } /* Send message */ - sigstat.status_text = signal2str(status); - sigstat.snr = mmi->mmi_stats.snr; - sigstat.signal = mmi->mmi_stats.signal; - sigstat.ber = mmi->mmi_stats.ber; - sigstat.unc = mmi->mmi_stats.unc; + sigstat.status_text = signal2str(status); + sigstat.snr = mmi->mmi_stats.snr; + sigstat.signal = mmi->mmi_stats.signal; + sigstat.ber = mmi->mmi_stats.ber; + sigstat.unc = mmi->mmi_stats.unc; + sigstat.signal_scale = mmi->mmi_stats.signal_scale; + sigstat.snr_scale = mmi->mmi_stats.snr_scale; + sigstat.ec_bit = mmi->mmi_stats.ec_bit; + sigstat.tc_bit = mmi->mmi_stats.tc_bit; + sigstat.ec_block = mmi->mmi_stats.ec_block; + sigstat.tc_block = mmi->mmi_stats.tc_block; sm.sm_type = SMT_SIGNAL_STATUS; sm.sm_data = &sigstat; LIST_FOREACH(s, &lfe->mi_transports, s_active_link) { diff --git a/src/input/mpegts/satip/satip_frontend.c b/src/input/mpegts/satip/satip_frontend.c index c342ad62..18e9cbfc 100644 --- a/src/input/mpegts/satip/satip_frontend.c +++ b/src/input/mpegts/satip/satip_frontend.c @@ -712,15 +712,19 @@ satip_frontend_decode_rtcp( satip_frontend_t *lfe, const char *name, if (atoi(argv[0]) != lfe->sf_number) return; mmi->mmi_stats.signal = - (atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale; + ((atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale) * 65535 / 100; + mmi->mmi_stats.signal_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; if (atoi(argv[2]) > 0) status = SIGNAL_GOOD; - mmi->mmi_stats.snr = atoi(argv[3]); + mmi->mmi_stats.snr = atoi(argv[3]) * 65535 / 100; + mmi->mmi_stats.snr_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; if (status == SIGNAL_GOOD && mmi->mmi_stats.signal == 0 && mmi->mmi_stats.snr == 0) { /* some values that we're tuned */ - mmi->mmi_stats.signal = 50; - mmi->mmi_stats.snr = 12; + mmi->mmi_stats.signal = 50 * 65535 / 100; + mmi->mmi_stats.snr = 12 * 65535 / 100; } goto ok; } else if (strncmp(s, "ver=1.0;", 8) == 0) { @@ -733,10 +737,14 @@ satip_frontend_decode_rtcp( satip_frontend_t *lfe, const char *name, if (atoi(argv[0]) != lfe->sf_number) return; mmi->mmi_stats.signal = - (atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale; + ((atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale) * 65535 / 100; + mmi->mmi_stats.signal_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; if (atoi(argv[2]) > 0) status = SIGNAL_GOOD; - mmi->mmi_stats.snr = atoi(argv[3]); + mmi->mmi_stats.snr = atoi(argv[3]) * 65535 / 100; + mmi->mmi_stats.snr_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; goto ok; } else if (strncmp(s, "ver=1.1;tuner=", 14) == 0) { n = http_tokenize(s + 14, argv, 4, ','); @@ -745,10 +753,14 @@ satip_frontend_decode_rtcp( satip_frontend_t *lfe, const char *name, if (atoi(argv[0]) != lfe->sf_number) return; mmi->mmi_stats.signal = - (atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale; + ((atoi(argv[1]) * 100) / lfe->sf_device->sd_sig_scale) * 65535 / 100; + mmi->mmi_stats.signal_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; if (atoi(argv[2]) > 0) status = SIGNAL_GOOD; - mmi->mmi_stats.snr = atoi(argv[3]); + mmi->mmi_stats.snr = atoi(argv[3]) * 65535 / 100; + mmi->mmi_stats.snr_scale = + INPUT_STREAM_STATS_SCALE_RELATIVE; goto ok; } } @@ -1260,6 +1272,12 @@ satip_frontend_signal_cb( void *aux ) sigstat.signal = mmi->mmi_stats.signal; sigstat.ber = mmi->mmi_stats.ber; sigstat.unc = mmi->mmi_stats.unc; + sigstat.signal_scale = mmi->mmi_stats.signal_scale; + sigstat.snr_scale = mmi->mmi_stats.snr_scale; + sigstat.ec_bit = mmi->mmi_stats.ec_bit; + sigstat.tc_bit = mmi->mmi_stats.tc_bit; + sigstat.ec_block = mmi->mmi_stats.ec_block; + sigstat.tc_block = mmi->mmi_stats.tc_block; sm.sm_type = SMT_SIGNAL_STATUS; sm.sm_data = &sigstat; LIST_FOREACH(svc, &lfe->mi_transports, s_active_link) { diff --git a/src/tvheadend.h b/src/tvheadend.h index 2aee1fa6..a6bbaae0 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -237,15 +237,30 @@ typedef enum { #define SCT_ISSUBTITLE(t) ((t) == SCT_TEXTSUB || (t) == SCT_DVBSUB) +/* + * Scales for signal status values + */ +typedef enum { + SIGNAL_STATUS_SCALE_UNKNOWN = 0, + SIGNAL_STATUS_SCALE_RELATIVE, // value is unsigned, where 0 means 0% and 65535 means 100% + SIGNAL_STATUS_SCALE_DECIBEL // value is measured in dB +} signal_status_scale_t; + /** * The signal status of a tuner */ typedef struct signal_status { const char *status_text; /* adapter status text */ int snr; /* signal/noise ratio */ + signal_status_scale_t snr_scale; int signal; /* signal strength */ + signal_status_scale_t signal_scale; int ber; /* bit error rate */ int unc; /* uncorrected blocks */ + int ec_bit; /* error bit count */ + int tc_bit; /* total bit count */ + int ec_block; /* error block count */ + int tc_block; /* total block count */ } signal_status_t; /** diff --git a/src/webui/static/app/extensions.js b/src/webui/static/app/extensions.js index 3ac7579f..cd6583dc 100644 --- a/src/webui/static/app/extensions.js +++ b/src/webui/static/app/extensions.js @@ -345,6 +345,9 @@ Ext.reg("multiselect", Ext.ux.Multiselect); * License details: http://www.gnu.org/licenses/lgpl.html */ +/** + * 22/07/2014: ceiling support backported from version 1.2, by Kai Sommerfeld + */ Ext.namespace('Ext.ux.grid'); Ext.ux.grid.ProgressColumn = function(config) { @@ -355,6 +358,10 @@ Ext.ux.grid.ProgressColumn = function(config) { }; Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, { + /** + * @cfg {Integer} upper limit for full progress indicator (defaults to 100) + */ + ceiling : 100, /** * @cfg {String} colored determines whether use special progression coloring * or the standard Ext.ProgressBar coloring for the bar (defaults to @@ -397,27 +404,30 @@ Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, { }, renderer: function(v, p, record) { var style = ''; - var textClass = (v < 55) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : ''); + var textClass = (v < (this.ceiling / 1.818)) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : ''); + + var value = v / this.ceiling * 100; + value = value.toFixed(0); //ugly hack to deal with IE6 issue var text = String.format('