Skip to content
Advanced

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

\[ \Delta L = 20 \log_{10}\frac{n+1}{n}\ \mathrm{dB} \]

apart: coarse at low levels, fine near full scale. For a Fixed<Per<Volt, Volt>, Milli, u16>:

−60−40−2002040 dB0.0010.010.1110 dBlevel of the stored gainstep to the next stored gain (log scale)
The step between neighbouring stored gains, computed with the crate's 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\).

\[ |z| \le 10^{-15/20} \iff |z|^2 \le 10^{-15/10} \]
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.