mirror of
https://github.com/danog/libdvb.git
synced 2024-11-26 20:04:39 +01:00
fe status reading with fallback to DVBv3 API
This commit is contained in:
parent
8198ccca69
commit
8a558d7470
@ -23,7 +23,7 @@ fn check_frontend(path: &Path) -> Result<()> {
|
||||
|
||||
let mut status = FeStatus::default();
|
||||
status.read(&fe)?;
|
||||
println!("{}", &status);
|
||||
println!("Status: {}", &status);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -315,7 +315,6 @@ impl FeDevice {
|
||||
pub fn get_event(&self, event: &mut FeEvent) -> Result<()> {
|
||||
// FE_GET_EVENT
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 78, FeEvent);
|
||||
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), event as *mut _)
|
||||
}.context("FE: get event")?;
|
||||
@ -323,6 +322,79 @@ impl FeDevice {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns frontend status
|
||||
/// - [`FE_NONE`]
|
||||
/// - [`FE_HAS_SIGNAL`]
|
||||
/// - [`FE_HAS_CARRIER`]
|
||||
/// - [`FE_HAS_VITERBI`]
|
||||
/// - [`FE_HAS_SYNC`]
|
||||
/// - [`FE_HAS_LOCK`]
|
||||
/// - [`FE_TIMEDOUT`]
|
||||
/// - [`FE_REINIT`]
|
||||
pub fn read_status(&self) -> Result<u32> {
|
||||
let mut result: u32 = FE_NONE;
|
||||
|
||||
// FE_READ_STATUS
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 69, u32);
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), &mut result as *mut _)
|
||||
}.context("FE: read status")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Reads and returns a signal strength relative value (DVBv3 API)
|
||||
pub fn read_signal_strength(&self) -> Result<u16> {
|
||||
let mut result: u16 = 0;
|
||||
|
||||
// FE_READ_SIGNAL_STRENGTH
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 71, u16);
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), &mut result as *mut _)
|
||||
}.context("FE: read signal strength")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Reads and returns a signal-to-noise ratio, relative value (DVBv3 API)
|
||||
pub fn read_snr(&self) -> Result<u16> {
|
||||
let mut result: u16 = 0;
|
||||
|
||||
// FE_READ_SNR
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 72, u16);
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), &mut result as *mut _)
|
||||
}.context("FE: read snr")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Reads and returns a bit error counter (DVBv3 API)
|
||||
pub fn read_ber(&self) -> Result<u32> {
|
||||
let mut result: u32 = 0;
|
||||
|
||||
// FE_READ_BER
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 70, u32);
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), &mut result as *mut _)
|
||||
}.context("FE: read ber")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Reads and returns an uncorrected blocks counter (DVBv3 API)
|
||||
pub fn read_unc(&self) -> Result<u32> {
|
||||
let mut result: u32 = 0;
|
||||
|
||||
// FE_READ_UNCORRECTED_BLOCKS
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 73, u32);
|
||||
unsafe {
|
||||
ioctl_call(self.as_raw_fd(), &mut result as *mut _)
|
||||
}.context("FE: read uncorrected blocks")?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Turns on/off generation of the continuous 22kHz tone
|
||||
///
|
||||
/// allowed `value`'s:
|
||||
|
352
src/fe/status.rs
352
src/fe/status.rs
@ -1,17 +1,9 @@
|
||||
use {
|
||||
std::{
|
||||
fmt,
|
||||
os::unix::io::AsRawFd,
|
||||
},
|
||||
|
||||
anyhow::{
|
||||
Context,
|
||||
Result,
|
||||
},
|
||||
|
||||
nix::{
|
||||
ioctl_read,
|
||||
},
|
||||
anyhow::Result,
|
||||
|
||||
super::{
|
||||
FeDevice,
|
||||
@ -27,7 +19,7 @@ pub struct FeStatus {
|
||||
status: u32,
|
||||
|
||||
/// properties
|
||||
props: [DtvProperty; 4],
|
||||
props: [DtvProperty; 6],
|
||||
}
|
||||
|
||||
|
||||
@ -36,14 +28,18 @@ impl Default for FeStatus {
|
||||
FeStatus {
|
||||
status: 0,
|
||||
props: [
|
||||
// 0: signal level
|
||||
// 0: delivery system
|
||||
DtvProperty::new(DTV_DELIVERY_SYSTEM, FE_NONE),
|
||||
// 1: signal level
|
||||
DtvProperty::new(DTV_STAT_SIGNAL_STRENGTH, 0),
|
||||
// 1: signal-to-noise ratio
|
||||
// 2: signal-to-noise ratio
|
||||
DtvProperty::new(DTV_STAT_CNR, 0),
|
||||
// 2: ber - number of bit errors
|
||||
// 3: ber - number of bit errors
|
||||
DtvProperty::new(DTV_STAT_PRE_ERROR_BIT_COUNT, 0),
|
||||
// 3: unc - number of block errors
|
||||
// 4: unc - number of block errors
|
||||
DtvProperty::new(DTV_STAT_ERROR_BLOCK_COUNT, 0),
|
||||
// 5: modulation
|
||||
DtvProperty::new(DTV_MODULATION, QPSK),
|
||||
],
|
||||
}
|
||||
}
|
||||
@ -52,69 +48,80 @@ impl Default for FeStatus {
|
||||
|
||||
/// Returns an object that implements `Display` for different verbosity levels
|
||||
///
|
||||
/// Tuner is turned off:
|
||||
///
|
||||
/// ```text
|
||||
/// Status:SCVYL S:-38.56dBm (59%) Q:14.57dB (70%) BER:0 UNC:0
|
||||
/// OFF
|
||||
/// ```
|
||||
///
|
||||
/// Status:
|
||||
/// - S - Signal
|
||||
/// - C - Carrier
|
||||
/// - V - FEC
|
||||
/// - Y - Sync
|
||||
/// - L - Lock
|
||||
/// Tuner acquiring signal but has no lock:
|
||||
///
|
||||
/// ```text
|
||||
/// NO-LOCK 0x01 | Signal -38.56dBm (59%)
|
||||
/// NO-LOCK 0x03 | Signal -38.56dBm (59%) | Quality 5.32dB (25%)
|
||||
/// ```
|
||||
///
|
||||
/// Hex number after `NO-LOCK` this is tuner status bit flags:
|
||||
/// - 0x01 - has signal
|
||||
/// - 0x02 - has carrier
|
||||
/// - 0x04 - has viterbi
|
||||
/// - 0x08 - has sync
|
||||
/// - 0x10 - has lock
|
||||
/// - 0x20 - timed-out
|
||||
/// - 0x40 - re-init
|
||||
///
|
||||
/// Tuner has lock
|
||||
///
|
||||
/// ```text
|
||||
/// LOCK dvb-s2 | Signal -38.56dBm (59%) | Quality 14.57dB (70%) | BER:0 | UNC:0
|
||||
/// ```
|
||||
impl fmt::Display for FeStatus {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Status:")?;
|
||||
|
||||
if self.status == FE_NONE {
|
||||
write!(f, "OFF")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
const STATUS_MAP: &[char] = &['S', 'C', 'V', 'Y', 'L'];
|
||||
for (i, s) in STATUS_MAP.iter().enumerate() {
|
||||
let c = if self.status & (1 << i) != 0 { *s } else { '_' };
|
||||
write!(f, "{}", c)?;
|
||||
if self.status & FE_HAS_LOCK != 0 {
|
||||
write!(f, "LOCK {}", DeliverySystemDisplay(self.get_delivery_system()))?;
|
||||
} else {
|
||||
write!(f, "NO-LOCK 0x{:02X}", self.status)?;
|
||||
}
|
||||
|
||||
if self.status & FE_HAS_SIGNAL == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write!(f, " Signal:")?;
|
||||
if let Some(s) = self.get_signal_level() {
|
||||
// TODO: config for lo/hi
|
||||
let lo: f64 = -85.0;
|
||||
let hi: f64 = -6.0;
|
||||
let relative = 100.0 - (s - hi) * 100.0 / (lo - hi);
|
||||
write!(f, "{:.02}dBm ({:.0}%)", s, relative)?;
|
||||
} else {
|
||||
write!(f, "-")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
" | Signal {:.02}dBm ({}%)",
|
||||
self.get_signal_strength_decibel().unwrap_or(0.0),
|
||||
self.get_signal_strength().unwrap_or(0)
|
||||
)?;
|
||||
|
||||
if self.status & FE_HAS_CARRIER == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write!(f, " Quality:")?;
|
||||
if let Some(q) = self.get_signal_noise_ratio() {
|
||||
write!(f, "{:.02}dB", q)?;
|
||||
} else {
|
||||
write!(f, "-")?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
" | Quality {:.02}dB ({}%)",
|
||||
self.get_snr_decibel().unwrap_or(0.0),
|
||||
self.get_snr().unwrap_or(0)
|
||||
)?;
|
||||
|
||||
if self.status & FE_HAS_LOCK == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
write!(f, " BER:")?;
|
||||
write!(f, " | BER:")?;
|
||||
if let Some(ber) = self.get_ber() {
|
||||
write!(f, "{}", ber)?;
|
||||
} else {
|
||||
write!(f, "-")?;
|
||||
}
|
||||
|
||||
write!(f, " UNC:")?;
|
||||
write!(f, " | UNC:")?;
|
||||
if let Some(unc) = self.get_unc() {
|
||||
write!(f, "{}", unc)?;
|
||||
} else {
|
||||
@ -127,64 +134,241 @@ impl fmt::Display for FeStatus {
|
||||
|
||||
|
||||
impl FeStatus {
|
||||
fn get_stats_decibel(&self, u: usize) -> Option<f64> {
|
||||
let stats = self.props.get(u)?;
|
||||
let stats = unsafe { &stats.u.st };
|
||||
/// Returns current delivery system
|
||||
#[inline]
|
||||
pub fn get_delivery_system(&self) -> u32 { unsafe { self.props[0].u.data } }
|
||||
|
||||
let len = ::std::cmp::min(stats.len as usize, stats.stat.len());
|
||||
for s in stats.stat[.. len].iter() {
|
||||
if s.scale == FE_SCALE_DECIBEL {
|
||||
return Some((s.value as f64) / 1000.0);
|
||||
}
|
||||
/// Returns current modulation
|
||||
#[inline]
|
||||
pub fn get_modulation(&self) -> u32 { unsafe { self.props[5].u.data } }
|
||||
|
||||
/// Returns Signal Strength in dBm
|
||||
pub fn get_signal_strength_decibel(&self) -> Option<f64> {
|
||||
let stat = unsafe { &self.props[1].u.st.stat[0] };
|
||||
if stat.scale == FE_SCALE_DECIBEL {
|
||||
Some((stat.value as f64) / 1000.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns Signal Level in dBm
|
||||
#[inline]
|
||||
pub fn get_signal_level(&self) -> Option<f64> { self.get_stats_decibel(0) }
|
||||
/// Returns Signal Strength in percentage
|
||||
pub fn get_signal_strength(&self) -> Option<u32> {
|
||||
let stat = unsafe { &self.props[1].u.st.stat[1] };
|
||||
if stat.scale == FE_SCALE_RELATIVE {
|
||||
Some(((stat.value & 0xFFFF) * 100 / 65535) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Signal to noise ratio in dB
|
||||
#[inline]
|
||||
pub fn get_signal_noise_ratio(&self) -> Option<f64> { self.get_stats_decibel(1) }
|
||||
|
||||
fn get_stats_counter(&self, u: usize) -> Option<u32> {
|
||||
let stats = self.props.get(u)?;
|
||||
let stats = unsafe { &stats.u.st };
|
||||
if stats.len > 0 {
|
||||
let s = &stats.stat[0];
|
||||
if s.scale == FE_SCALE_COUNTER {
|
||||
return Some((s.value & 0xFFFF) as u32);
|
||||
}
|
||||
pub fn get_snr_decibel(&self) -> Option<f64> {
|
||||
let stat = unsafe { &self.props[2].u.st.stat[0] };
|
||||
if stat.scale == FE_SCALE_DECIBEL {
|
||||
Some((stat.value as f64) / 1000.0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns Signal Strength in percentage
|
||||
pub fn get_snr(&self) -> Option<u32> {
|
||||
let stat = unsafe { &self.props[2].u.st.stat[1] };
|
||||
if stat.scale == FE_SCALE_RELATIVE {
|
||||
Some(((stat.value & 0xFFFF) * 100 / 65535) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns BER value if available
|
||||
#[inline]
|
||||
pub fn get_ber(&self) -> Option<u32> { self.get_stats_counter(2) }
|
||||
pub fn get_ber(&self) -> Option<u32> {
|
||||
let stat = unsafe { &self.props[3].u.st.stat[0] };
|
||||
if stat.scale == FE_SCALE_COUNTER {
|
||||
Some((stat.value & 0xFFFF) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns UNC value if available
|
||||
#[inline]
|
||||
pub fn get_unc(&self) -> Option<u32> { self.get_stats_counter(3) }
|
||||
pub fn get_unc(&self) -> Option<u32> {
|
||||
let stat = unsafe { &self.props[4].u.st.stat[0] };
|
||||
if stat.scale == FE_SCALE_COUNTER {
|
||||
Some((stat.value & 0xFFFF) as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads frontend status
|
||||
fn normalize_signal_strength(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
let mut stats = unsafe { &mut self.props[1].u.st };
|
||||
|
||||
for i in usize::from(stats.len) .. 2 {
|
||||
stats.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
stats.stat[i].value = 0;
|
||||
}
|
||||
|
||||
stats.len = 2;
|
||||
|
||||
if stats.stat[0].scale == FE_SCALE_RELATIVE {
|
||||
stats.stat.swap(0, 1);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if stats.stat[1].scale == FE_SCALE_RELATIVE || (self.status & FE_HAS_SIGNAL) == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// fallback to DVBv3 API
|
||||
|
||||
if let Ok(value) = fe.read_signal_strength() {
|
||||
stats.stat[1].scale = FE_SCALE_RELATIVE;
|
||||
stats.stat[1].value = i64::from(value);
|
||||
} else if stats.stat[0].scale == FE_SCALE_DECIBEL {
|
||||
// TODO: check delivery_system
|
||||
|
||||
let lo: i64 = -85000;
|
||||
let hi: i64 = -6000;
|
||||
|
||||
stats.stat[1].scale = FE_SCALE_RELATIVE;
|
||||
stats.stat[1].value = {
|
||||
if stats.stat[0].value > hi {
|
||||
65545
|
||||
} else if stats.stat[0].value < lo {
|
||||
0
|
||||
} else {
|
||||
65545 * (lo - stats.stat[0].value) / (lo - hi)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_snr(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
let delivery_system = self.get_delivery_system();
|
||||
let modulation = self.get_modulation();
|
||||
|
||||
let mut stats = unsafe { &mut self.props[2].u.st };
|
||||
|
||||
for i in usize::from(stats.len) .. 2 {
|
||||
stats.stat[i].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
stats.stat[i].value = 0;
|
||||
}
|
||||
|
||||
stats.len = 2;
|
||||
|
||||
if stats.stat[0].scale == FE_SCALE_RELATIVE {
|
||||
stats.stat.swap(0, 1);
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if stats.stat[1].scale == FE_SCALE_RELATIVE || (self.status & FE_HAS_CARRIER) == 0 {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// fallback to DVBv3 API
|
||||
|
||||
if let Ok(value) = fe.read_snr() {
|
||||
stats.stat[1].scale = FE_SCALE_RELATIVE;
|
||||
stats.stat[1].value = i64::from(value);
|
||||
} else if stats.stat[0].scale == FE_SCALE_DECIBEL {
|
||||
let maxdb = match delivery_system {
|
||||
SYS_DVBS |
|
||||
SYS_DVBS2 => 15000,
|
||||
|
||||
SYS_DVBC_ANNEX_A |
|
||||
SYS_DVBC_ANNEX_B |
|
||||
SYS_DVBC_ANNEX_C |
|
||||
SYS_DVBC2 => 28000,
|
||||
|
||||
SYS_DVBT |
|
||||
SYS_DVBT2 => 19000,
|
||||
|
||||
SYS_ATSC => {
|
||||
match modulation {
|
||||
VSB_8 | VSB_16 => 19000,
|
||||
_ => 28000,
|
||||
}
|
||||
}
|
||||
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if maxdb != 0 {
|
||||
stats.stat[1].scale = FE_SCALE_RELATIVE;
|
||||
stats.stat[1].value = {
|
||||
if stats.stat[0].value >= maxdb {
|
||||
65535
|
||||
} else if stats.stat[0].value <= 0 {
|
||||
0
|
||||
} else {
|
||||
65535 * stats.stat[0].value / maxdb
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_ber(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
let mut stats = unsafe { &mut self.props[3].u.st };
|
||||
|
||||
if stats.len == 0 || stats.stat[0].scale != FE_SCALE_COUNTER {
|
||||
if let Ok(value) = fe.read_ber() {
|
||||
stats.stat[0].scale = FE_SCALE_COUNTER;
|
||||
stats.stat[0].value = i64::from(value);
|
||||
} else {
|
||||
stats.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
stats.stat[0].value = 0;
|
||||
}
|
||||
|
||||
stats.len = 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn normalize_unc(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
let mut stats = unsafe { &mut self.props[4].u.st };
|
||||
|
||||
if stats.len == 0 || stats.stat[0].scale != FE_SCALE_COUNTER {
|
||||
if let Ok(value) = fe.read_unc() {
|
||||
stats.stat[0].scale = FE_SCALE_COUNTER;
|
||||
stats.stat[0].value = i64::from(value);
|
||||
} else {
|
||||
stats.stat[0].scale = FE_SCALE_NOT_AVAILABLE;
|
||||
stats.stat[0].value = 0;
|
||||
}
|
||||
|
||||
stats.len = 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// set decibel to `stat[0]` and relative to `stat[1]` and fallback to DVBv3 API
|
||||
fn normalize_props(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
self.normalize_signal_strength(fe)?;
|
||||
self.normalize_snr(fe)?;
|
||||
self.normalize_ber(fe)?;
|
||||
self.normalize_unc(fe)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads frontend status with fallback to DVBv3 API
|
||||
pub fn read(&mut self, fe: &FeDevice) -> Result<()> {
|
||||
self.status = FE_NONE;
|
||||
|
||||
// FE_READ_STATUS
|
||||
ioctl_read!(#[inline] ioctl_call, b'o', 69, u32);
|
||||
unsafe {
|
||||
ioctl_call(fe.as_raw_fd(), &mut self.status as *mut _)
|
||||
}.context("FE: read status")?;
|
||||
self.status = fe.read_status()?;
|
||||
|
||||
if self.status == FE_NONE {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fe.get_properties(&mut self.props)?;
|
||||
|
||||
Ok(())
|
||||
self.normalize_props(fe)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user