Thresholds¶
Exact at compile time¶
db!(x) is the stored value nearest to the exact gain \(10^{x/20}\) (or \(10^{x/10}\)). It is worked out in integers, with an error bound, at compile time:
- The result is the correctly rounded stored value.
- A level too close to halfway between two stored values to decide doesn't compile, rather than being rounded the wrong way.
- A level whose gain doesn't fit the storage doesn't compile.
use exact_units::{Fixed, db, prefix::Milli, units::{Per, Volt}};
// 100 V/V is 100 000 mV/V: too big for a u16
let _: Fixed<Per<Volt, Volt>, Milli, u16> = db!(40);
Fixed::from_db_const is the same as a const fn, for a level that isn't a literal:
use exact_units::{Fixed, decibel::Decibels, prefix::Milli, units::{Per, Volt}};
type Gain = Fixed<Per<Volt, Volt>, Milli, u16>;
const PLUS_20: Gain = Gain::from_db_const(Decibels::new(20, 0)); // 20.0 dB
assert_eq!(PLUS_20.get(), 10_000);
How fine a threshold is¶
A threshold can only sit on a stored value. Two neighbouring stored gains \(n\) and \(n+1\) are
apart: coarse at low levels, fine near full scale. For a Fixed<Per<Volt, Volt>, Milli, u16>:
to_db. At −40 dB a step is almost 1 dB; near 0 dB it is under 0.01 dB.When a threshold at −50 dB must be set to 0.1 dB, a finer scale (Micro) or a wider storage is the answer, not a float.
dBFS¶
A level against full scale is a level of a gain against 1. A sample stored as a fraction of the converter's full-scale voltage is a Per<Volt, Volt>, so dBFS needs nothing new:
use exact_units::{Q15, db, units::{Per, Volt}};
type Sample = Q15<Per<Volt, Volt>>; // a fraction of full scale
const MINUS_6_DBFS: Sample = db!(-6);
assert_eq!(MINUS_6_DBFS.get(), 16423); // 0.501 · 2¹⁵
Power of a complex value¶
For an IQ sample or a reflection coefficient the level is of \(|z|^2\), and the square root is never needed: db!(norm_sqr x) is the threshold on \(|z|^2\).
use exact_units::{CQ15, Complex, Fixed, db, prefix::{Pow2, Product}, units::{Per, Times, Volt}};
type FullScale = Per<Volt, Volt>;
type PowerLevel = Fixed<Times<FullScale, FullScale>, Product<Pow2<-15>, Pow2<-15>>, u32>;
const BUSY: PowerLevel = db!(norm_sqr -10); // −10 dBFS as |z|² in Q30
let x: CQ15<FullScale> = Complex::of(16384, 0); // 0.5: −6 dBFS
assert!(x.norm_sqr() >= BUSY);
The firmware compares two integers. See IQ samples for a block's mean power.