static u32 calculate_snr(u32 mse, u32 c) { if (mse == 0) /* No signal */ return 0; mse = 2*intlog10(mse); if (mse > c) { /* Negative SNR, which is possible, but realisticly the demod will lose lock before the signal gets this bad. The API only allows for unsigned values, so just return 0 */ return 0; } return 10*(c - mse); }
static int mtvhd_fe_read_snr(struct dvb_frontend *fe, u16 *snr) { int ret; u32 val, x; deb_info("FE read snr: "); *snr = 0; ret = mtvhd_demod_reg_read(fe, 0x8B); if (ret < 0) { goto done; } val = ret << 16; ret = mtvhd_demod_reg_read(fe, 0x8C); if (ret < 0) { goto done; } val |= ret << 8; ret = mtvhd_demod_reg_read(fe, 0x8D); if (ret < 0) { goto done; } val |= ret; ret = 0; deb_info("%06x -> ", val); if (val == 0 || val > 0x540000) { goto done; } x = 20 * intlog10(0x540000 / val); *snr = (u16)(x >> 16); done: deb_info("%04x (%d.%d dB)\n", *snr, *snr >> 8, ((*snr & 0xFF) * 10) >> 8); return ret; }
static int m88ds3103_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct m88ds3103_dev *dev = fe->demodulator_priv; struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, itmp; unsigned int utmp; u8 buf[3]; *status = 0; if (!dev->warm) { ret = -EAGAIN; goto err; } switch (c->delivery_system) { case SYS_DVBS: ret = regmap_read(dev->regmap, 0xd1, &utmp); if (ret) goto err; if ((utmp & 0x07) == 0x07) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; break; case SYS_DVBS2: ret = regmap_read(dev->regmap, 0x0d, &utmp); if (ret) goto err; if ((utmp & 0x8f) == 0x8f) *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; break; default: dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto err; } dev->fe_status = *status; dev_dbg(&client->dev, "lock=%02x status=%02x\n", utmp, *status); /* CNR */ if (dev->fe_status & FE_HAS_VITERBI) { unsigned int cnr, noise, signal, noise_tot, signal_tot; cnr = 0; /* more iterations for more accurate estimation */ #define M88DS3103_SNR_ITERATIONS 3 switch (c->delivery_system) { case SYS_DVBS: itmp = 0; for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { ret = regmap_read(dev->regmap, 0xff, &utmp); if (ret) goto err; itmp += utmp; } /* use of single register limits max value to 15 dB */ /* SNR(X) dB = 10 * ln(X) / ln(10) dB */ itmp = DIV_ROUND_CLOSEST(itmp, 8 * M88DS3103_SNR_ITERATIONS); if (itmp) cnr = div_u64((u64) 10000 * intlog2(itmp), intlog2(10)); break; case SYS_DVBS2: noise_tot = 0; signal_tot = 0; for (i = 0; i < M88DS3103_SNR_ITERATIONS; i++) { ret = regmap_bulk_read(dev->regmap, 0x8c, buf, 3); if (ret) goto err; noise = buf[1] << 6; /* [13:6] */ noise |= buf[0] & 0x3f; /* [5:0] */ noise >>= 2; signal = buf[2] * buf[2]; signal >>= 1; noise_tot += noise; signal_tot += signal; } noise = noise_tot / M88DS3103_SNR_ITERATIONS; signal = signal_tot / M88DS3103_SNR_ITERATIONS; /* SNR(X) dB = 10 * log10(X) dB */ if (signal > noise) { itmp = signal / noise; cnr = div_u64((u64) 10000 * intlog10(itmp), (1 << 24)); } break; default: dev_dbg(&client->dev, "invalid delivery_system\n"); ret = -EINVAL; goto err; } if (cnr) { c->cnr.stat[0].scale = FE_SCALE_DECIBEL; c->cnr.stat[0].svalue = cnr; } else { c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } } else {