75 static constexpr const char*
symbol =
"dB";
79template<
typename Rep,
typename Precision = std::ratio<1>>
108 static constexpr const char*
symbol =
"dBm";
116 static constexpr const char*
symbol =
"dBW";
124 static constexpr const char*
symbol =
"dBV";
132 static constexpr const char*
symbol =
"dBmV";
140 static constexpr const char*
symbol =
"dBµV";
152template<
typename Reference>
154 typename Reference::unit;
155 typename Reference::precision;
156 { Reference::factor } -> std::convertible_to<int>;
157 { Reference::symbol } -> std::convertible_to<const char*>;
158} && (Reference::factor == 10 || Reference::factor == 20);
161template<
typename Reference>
164template<decibel_reference Reference>
168template<
typename Reference>
171template<decibel_reference Reference>
178template<
typename Reference,
typename Rep,
typename Precision = std::ratio<1>>
188template<
typename Reference,
typename Rep,
typename Precision>
211template<
typename Reference,
typename Rep,
typename Precision>
237 template<
typename Rep2>
238 requires std::convertible_to<const Rep2&, rep> && (treat_as_inexact_v<rep> || !treat_as_inexact_v<Rep2>)
239 constexpr explicit level(
const Rep2& r)
240 : _o(
static_cast<rep>(r)) {}
251 template<
typename Rep2,
typename Precision2>
252 requires std::convertible_to<gain<Rep2, Precision2>,
offset>
257 template<
typename Rep2,
typename Precision2>
258 requires(!std::is_same_v<level, level<Reference, Rep2, Precision2>>) &&
260 std::constructible_from<offset, gain<Rep2, Precision2>>
312template<
typename ToLevel,
typename Reference,
typename Rep,
typename Precision>
313 requires(!std::is_same_v<typename ToLevel::reference, Reference>) ||
314 detail::_quantity_cast_requirements<gain<Rep, Precision>,
typename ToLevel::offset>
318 std::is_same_v<typename ToLevel::reference, Reference>,
"level_cast cannot convert between different references"
328template<
typename ToLevel,
typename Reference,
typename Rep,
typename Precision>
330 static_assert(std::is_same_v<typename ToLevel::reference, Reference>,
"floor cannot convert between references");
339template<
typename ToLevel,
typename Reference,
typename Rep,
typename Precision>
341 static_assert(std::is_same_v<typename ToLevel::reference, Reference>,
"ceil cannot convert between references");
346template<
typename ToLevel,
typename Reference,
typename Rep,
typename Precision>
348 static_assert(std::is_same_v<typename ToLevel::reference, Reference>,
"trunc cannot convert between references");
357template<
typename ToLevel,
typename Reference,
typename Rep,
typename Precision>
359 static_assert(std::is_same_v<typename ToLevel::reference, Reference>,
"round cannot convert between references");
368template<
typename R1,
typename P1,
typename R2,
typename P2>
369using _common_gain = std::common_type_t<gain<R1, P1>, gain<R2, P2>>;
373template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
376 using cg = _common_gain<Rep1, Precision1, Rep2, Precision2>;
382template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
389template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
392 using cg = _common_gain<Rep1, Precision1, Rep2, Precision2>;
404template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
409template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
414template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
415 requires std::three_way_comparable<std::common_type_t<Rep1, Rep2>>
425template<
typename Offset>
426constexpr double _db_value(
const Offset& o) {
427 return quantity_cast<gain<double>>(o).count();
433template<
typename Offset>
434inline Offset _db_offset(
double db) {
435 if constexpr (treat_as_inexact_v<typename Offset::rep>) {
436 return quantity_cast<Offset>(gain<double>(db));
438 using p =
typename Offset::precision;
439 const double ticks = db *
static_cast<double>(p::den) /
static_cast<double>(p::num);
440 double rounded = std::floor(ticks);
441 const double frac = ticks - rounded;
442 if (frac > 0.5 || (frac == 0.5 && std::fmod(rounded, 2.0) != 0.0)) {
449 using rep =
typename Offset::rep;
450 if (rounded >=
static_cast<double>(std::numeric_limits<rep>::max())) {
451 return Offset(std::numeric_limits<rep>::max());
453 if (rounded <=
static_cast<double>(std::numeric_limits<rep>::min())) {
454 return Offset(std::numeric_limits<rep>::min());
456 return Offset(
static_cast<rep
>(rounded));
475template<
typename Reference,
typename Rep,
typename Precision>
507template<
typename ToLevel,
typename Unit,
typename Rep,
typename Precision>
509 using ref =
typename ToLevel::reference;
511 std::is_same_v<typename ref::unit, Unit>,
"to_level: quantity unit does not match the level reference"
515 const double ratio = quantity_cast<linear>(q).
count();
516 if (!(ratio > 0.0)) {
517 if constexpr (std::floating_point<typename ToLevel::rep>) {
518 if (std::isnan(ratio)) {
519 return ToLevel(std::numeric_limits<typename ToLevel::rep>::quiet_NaN());
521 assert(ratio >= 0.0 &&
"to_level: quantity must be non-negative and not NaN");
522 return ToLevel(-std::numeric_limits<typename ToLevel::rep>::infinity());
524 assert(ratio >= 0.0 &&
"to_level: quantity must be non-negative and not NaN");
525 return ToLevel::min();
528 return ToLevel(_db_offset<typename ToLevel::offset>(ref::factor * std::log10(ratio)));
540template<
typename Rep,
typename Precision>
542 return std::pow(10.0, _db_value(g) / 10.0);
554template<
typename Rep,
typename Precision>
556 return std::pow(10.0, _db_value(g) / 20.0);
561 assert(ratio > 0.0 &&
"power_gain: ratio must be positive");
567 assert(ratio > 0.0 &&
"amplitude_gain: ratio must be positive");
587template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
590 using cg = _common_gain<Rep1, Precision1, Rep2, Precision2>;
605template<
typename Rep,
typename Precision = std::ratio<1>>
609template<
typename Rep,
typename Precision = std::ratio<1>>
613template<
typename Rep,
typename Precision = std::ratio<1>>
617template<
typename Rep,
typename Precision = std::ratio<1>>
621template<
typename Rep,
typename Precision = std::ratio<1>>
660template<
typename Precision>
666consteval int _decimal_places(intmax_t num, intmax_t den) {
671 while (den % 10 == 0) {
675 return den == 1 ? places : -1;
678template<
typename Precision>
679inline constexpr bool _is_decimal_precision = _decimal_places(Precision::num, Precision::den) >= 0;
682template<
typename Precision,
typename Rep>
683constexpr double _db_double(
const Rep& count) {
684 return static_cast<double>(count) * Precision::num / Precision::den;
691template<
typename Precision,
typename Rep>
692inline std::string _format_db(
const Rep& count) {
693 static_assert(_is_decimal_precision<Precision>,
"precision cannot be rendered as a fixed-point decimal");
694 if constexpr (std::floating_point<Rep>) {
696 if constexpr (Precision::den != 1) {
697 value /=
static_cast<Rep
>(Precision::den);
699 return _fixed_string(value);
700 }
else if constexpr (treat_as_inexact_v<Rep>) {
701 return _fixed_string(_db_double<Precision>(count));
703 constexpr int places = _decimal_places(Precision::num, Precision::den);
704 if constexpr (places == 0) {
705 return std::to_string(count);
707 const bool negative = count < Rep(0);
710 const unsigned long long magnitude =
711 negative ? 0ULL -
static_cast<unsigned long long>(count) : static_cast<unsigned long long>(count);
713 const unsigned long long whole = magnitude / Precision::den;
714 const unsigned long long frac = magnitude % Precision::den;
716 std::string frac_digits = std::to_string(frac);
717 frac_digits.insert(0,
static_cast<size_t>(places) - frac_digits.size(),
'0');
719 return (negative ?
"-" :
"") +
std::
to_string(whole) +
"." + frac_digits;
731template<
typename Rep,
typename Precision>
732 requires _is_decimal_precision<typename Precision::type>
744template<
typename Rep,
typename Precision>
745 requires _is_decimal_precision<typename Precision::type>
756template<
typename Rep,
typename Precision>
757 requires _is_decimal_precision<typename Precision::type>
765template<
typename Reference,
typename Rep,
typename Precision>
766 requires _is_decimal_precision<typename Precision::type>
768 return _format_db<typename Precision::type>(l.
count()) + Reference::symbol;
778template<
typename Reference,
typename Rep,
typename Precision>
779 requires _is_decimal_precision<typename Precision::type>
781 return _fixed_string(_db_double<typename Precision::type>(l.
count())) + Reference::symbol;
790template<
typename Reference,
typename Rep,
typename Precision>
791 requires _is_decimal_precision<typename Precision::type>
800template<
typename Reference,
typename Rep1,
typename Precision1,
typename Rep2,
typename Precision2>
802 quanta::electro::level<Reference, Rep1, Precision1>,
805 using common_offset =
814#if CONFIG_QUANTA_STD_FORMAT
826template<
typename Precision>
827struct _db_formatter {
829 std::formatter<double> _num;
830 bool _has_spec =
false;
831 bool _marker =
false;
834 constexpr auto parse(std::format_parse_context& ctx) {
835 _marker = detail::_parse_unit_marker(ctx,
"bs") != detail::_unit_form::own;
836 auto it = ctx.begin();
837 if (it == ctx.end() || *it ==
'}') {
841 return _num.parse(ctx);
844 template<
typename Rep,
typename FormatContext>
845 auto format(
const Rep& count,
const char* symbol, FormatContext& ctx)
const {
846 auto out = ctx.out();
848 out = _num.format(_db_double<Precision>(count), ctx);
849 }
else if (_marker) {
850 out = std::format_to(out,
"{}", _fixed_string(_db_double<Precision>(count)));
852 out = std::format_to(out,
"{}", _format_db<Precision>(count));
854 return _format_append(out, symbol);
864template<
typename Rep,
typename Precision>
865 requires quanta::electro::_is_decimal_precision<typename Precision::type>
866struct formatter<
quanta::electro::quantity<quanta::electro::decibel_unit, Rep, Precision>, char>
867 : quanta::electro::_db_formatter<typename Precision::type> {
868 template<
typename FormatContext>
873 return quanta::electro::_db_formatter<typename Precision::type>::format(
881template<
typename Reference,
typename Rep,
typename Precision>
882 requires quanta::electro::_is_decimal_precision<typename Precision::type>
883struct formatter<
quanta::electro::level<Reference, Rep, Precision>, char>
884 : quanta::electro::_db_formatter<typename Precision::type> {
885 template<
typename FormatContext>
887 return quanta::electro::_db_formatter<typename Precision::type>::format(l.
count(), Reference::symbol, ctx);
895inline namespace literals {
896inline namespace electro_literals {
946#if QUANTA_HAS_UTF8_LITERALS
958using namespace literals::electro_literals;
An absolute point on a logarithmic scale, relative to a reference.
constexpr level(const level< Reference, Rep2, Precision2 > &l)
Converts from a level with the same reference.
constexpr level operator+() const
constexpr level(const level< Reference, Rep2, Precision2 > &l)
Explicit constructor for lossy precision conversions.
constexpr level(const offset &o)
Constructs from an offset relative to the reference.
constexpr level & operator-=(const offset &o)
level & operator=(const level &)=default
constexpr rep count() const
Returns the raw offset count, in units of Precision.
constexpr level operator-() const
Reflects the level about its reference point.
gain< Rep, Precision > offset
The gain type measuring the offset from the reference.
constexpr level(const Rep2 &r)
Constructs from an offset count in dB.
Rep rep
The representation type.
static constexpr level max() noexcept
Returns the maximum representable level.
constexpr level()=default
Constructs a level at the reference point (0 dB offset).
level(const level &)=default
static constexpr level min() noexcept
Returns the minimum representable level.
constexpr offset offset_from_reference() const
Returns the offset from the reference point.
static constexpr level reference_level() noexcept
Returns the reference point itself (a 0 dB offset).
Reference reference
The reference tag type.
typename Precision::type precision
The offset precision, in dB, as a std::ratio.
constexpr level & operator+=(const offset &o)
A quantity of an electrical unit with a representation and precision.
constexpr rep count() const
Returns the tick count.
Concept for decibel reference tags.
Electrical unit types and arithmetic, modeled after std::chrono.
dbm_level< int64_t, std::centi > centi_dbm
Power level in dBm, with 0.01 dB precision.
dbv_level< int64_t > dbv
Voltage level in dBV, with 1 dB precision.
level< microvolt_reference, Rep, Precision > dbuv_level
A level referenced to 1 µV.
dbm_level< int64_t > dbm
Power level in dBm, with 1 dB precision.
level< millivolt_reference, Rep, Precision > dbmv_level
A level referenced to 1 mV.
level< milliwatt_reference, Rep, Precision > dbm_level
A level referenced to 1 mW.
dbuv_level< int64_t > dbuv
Voltage level in dBµV, with 1 dB precision.
dbuv_level< int64_t, std::centi > centi_dbuv
Voltage level in dBµV, with 0.01 dB precision.
level< volt_reference, Rep, Precision > dbv_level
A level referenced to 1 V.
level< watt_reference, Rep, Precision > dbw_level
A level referenced to 1 W.
dbmv_level< int64_t > dbmv
Voltage level in dBmV, with 1 dB precision.
dbw_level< int64_t > dbw
Power level in dBW, with 1 dB precision.
Electrical unit types and utilities.
constexpr ToLevel ceil(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward positive infinity.
gain< double > power_gain(double ratio)
The gain corresponding to a linear power ratio (10*log10(ratio)).
constexpr bool is_power_reference_v< Reference >
gain< int64_t > decibels
Gain with 1 dB precision.
gain< int64_t, std::centi > centidecibels
Gain with 0.01 dB precision.
constexpr auto operator-(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a loss to a level.
auto add_powers(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
Combines the powers of two uncorrelated signals.
constexpr bool is_power_reference_v
True if the reference measures a power quantity (10*log10).
constexpr ToLevel level_cast(const level< Reference, Rep, Precision > &l)
Converts a level to a different precision or representation.
quantity< decibel_unit, Rep, Precision > gain
A logarithmic ratio with configurable representation and precision.
ToLevel to_level(const quantity< Unit, Rep, Precision > &q)
Converts a linear quantity to a level.
constexpr ToLevel round(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding to nearest (ties to even).
double amplitude_ratio(const quantity< decibel_unit, Rep, Precision > &g)
The linear amplitude ratio a gain represents (10^(dB/20)).
constexpr ToLevel trunc(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward zero (as level_cast does).
constexpr bool is_field_reference_v
True if the reference measures a field quantity (20*log10).
constexpr bool is_level_v
std::string to_string(const quantity< decibel_unit, Rep, Precision > &g)
Converts a gain to a string, e.g.
constexpr bool is_field_reference_v< Reference >
double power_ratio(const quantity< decibel_unit, Rep, Precision > &g)
The linear power ratio a gain represents (10^(dB/10)).
constexpr auto operator<=>(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
auto to_linear(const level< Reference, Rep, Precision > &l)
Converts a level to the equivalent linear quantity.
gain< int64_t, std::milli > millidecibels
Gain with 0.001 dB precision.
gain< double > amplitude_gain(double ratio)
The gain corresponding to a linear amplitude ratio (20*log10(ratio)).
constexpr bool operator==(const level< Reference, Rep1, Precision1 > &a, const level< Reference, Rep2, Precision2 > &b)
constexpr auto operator+(const level< Reference, Rep1, Precision1 > &l, const quantity< decibel_unit, Rep2, Precision2 > &g)
Applies a gain to a level.
constexpr ToLevel floor(const level< Reference, Rep, Precision > &l)
Converts a level to the target type, rounding toward negative infinity.
Type-safe units modeled after std::chrono.
constexpr base_unit_t base_unit
Selects the base-unit form in to_string().
constexpr bool treat_as_inexact_v
Tag type selecting the base-unit form, see base_unit.
A dimensionless logarithmic ratio, measured in decibels.
static constexpr const char * symbol
Trait to detect level specializations.
Reference of 1 µV, for dBµV.
static constexpr int factor
static constexpr const char * symbol
Reference of 1 mV, for dBmV.
static constexpr const char * symbol
static constexpr int factor
Reference of 1 mW, for dBm.
static constexpr const char * symbol
static constexpr int factor
Trait giving the display suffix for a (unit, precision) pair.
Reference of 1 V, for dBV.
static constexpr const char * symbol
std::ratio< 1 > precision
static constexpr int factor
Voltage, measured in volts.
Reference of 1 W, for dBW.
static constexpr const char * symbol
static constexpr int factor
std::ratio< 1 > precision
Power, measured in watts.
Tag type selecting the scaled form, see scaled.