|
electro 0.3.0
Type-safe electrical units library modeled after std::chrono
|
A type-safe, header-only C++20 library for electrical units, modeled after std::chrono::duration.
I * R = V), power (V * I = P), and friendsstd::chrono interop**: mA x hours = mAh, W x hours = Wh, Ω x F = RC time constant (as a std::chrono::duration)dB gains and dBm/dBW/dBµV levels, related as duration is to time_point, so that dBm + dBm is a compile error3300_mV, 10_kΩ, 2000_mAh, -73_dBm)std::format support** (when available)std::log10/std::pow)/utf-8 flag is required (the source contains unit symbols such as Ω)Copy the include/electro/ directory to your project.
Quantities of different kinds combine according to circuit theory, producing results in the correct unit with a precision derived from the operands:
Products are exact for integer representations. Quotients truncate like integer division, so choose operand precisions accordingly: 5_V / 3_Ohm is 1 A, while 5000_mV / 3_Ohm is 1666 mA. Floating-point representations (e.g. voltage<double>) avoid truncation entirely.
Decibels live in a separate, opt-in header:
They come in two kinds, and conflating them is the classic source of bugs. This library keeps them apart using the same distinction std::chrono draws between a duration and a time_point:
| Closed under | ||
|---|---|---|
gain (dB) | a dimensionless ratio — the duration analog | +, -, * scalar |
level (dBm, dBW, dBµV) | an absolute point on a log scale — the time_point analog | level ± gain, level - level → gain |
That last group matters: a plain dBm unit tag would accept all three and silently compute nonsense (10 dBm + 10 dBm is 13.01 dBm, not 20 dBm). Combining the powers of two uncorrelated signals is a named function, never operator+:
Conversions are explicit, named functions rather than constructors: they are non-constexpr, nonlinear, and have domain errors a constructor cannot report. A level converts at its own reference precision, so dBm yields milliwatts:
A zero quantity has no finite logarithm and maps to dbm::min(); a negative one asserts. Values are rounded to nearest, not truncated.
A reference records whether its linear quantity is a power (10·log10) or a field such as voltage (20·log10), so callers never have to remember which factor applies:
For the same reason, a bare dB value cannot be turned into a linear ratio unambiguously — 3 dB is a factor of 2 in power but ~1.41 in amplitude — so the two conversions are separately named:
add_powers is available only for power references; for a field reference, summing linear amplitudes would model coherent addition instead, which is a different operation with a different answer.
Integer decibels at 1 dB resolution are coarse. Use a finer precision or a floating-point representation:
All standard aliases use int64_t representation. Each quantity also has an alias template for custom representations and precisions, e.g. voltage<double> or current<float, std::milli>.
| Quantity | Alias template | Standard aliases |
|---|---|---|
| Voltage (V) | voltage<Rep, Precision> | microvolts, millivolts, volts, kilovolts |
| Current (A) | current<Rep, Precision> | microamperes/microamps, milliamperes/milliamps, amperes/amps |
| Resistance (Ω) | resistance<Rep, Precision> | milliohms, ohms, kiloohms, megaohms, gigaohms |
| Power (W) | power<Rep, Precision> | microwatts, milliwatts, watts, kilowatts, megawatts, gigawatts |
| Charge (C) | charge<Rep, Precision> | microcoulombs, millicoulombs, coulombs, milliampere_hours, ampere_hours |
| Energy (J) | energy<Rep, Precision> | millijoules, joules, kilojoules, megajoules, watt_hours, kilowatt_hours |
| Capacitance (F) | capacitance<Rep, Precision> | picofarads, nanofarads, microfarads, millifarads, farads |
| Inductance (H) | inductance<Rep, Precision> | nanohenries, microhenries, millihenries, henries |
From <electro/decibel>. Levels are level<Reference, Rep, Precision>; gains are ordinary quantities of decibel_unit.
| Kind | Alias template | Standard aliases |
|---|---|---|
| Gain (dB) | gain<Rep, Precision> | decibels, centidecibels, millidecibels |
| Level vs 1 mW | dbm_level<Rep, Precision> | dbm, centi_dbm |
| Level vs 1 W | dbw_level<Rep, Precision> | dbw |
| Level vs 1 V | dbv_level<Rep, Precision> | dbv |
| Level vs 1 mV | dbmv_level<Rep, Precision> | dbmv |
| Level vs 1 µV | dbuv_level<Rep, Precision> | dbuv, centi_dbuv |
| Quantity | Literals |
|---|---|
| Voltage | _uV, _mV, _V, _kV |
| Current | _uA, _mA, _A |
| Resistance | _mOhm, _Ohm, _kOhm, _MOhm (also _mΩ, _Ω, _kΩ, _MΩ) |
| Power | _uW, _mW, _W, _kW, _MW |
| Charge | _C, _mAh, _Ah |
| Energy | _mJ, _J, _kJ, _Wh, _kWh |
| Capacitance | _pF, _nF, _uF, _F |
| Inductance | _nH, _uH, _mH, _H |
| Gain | _dB, _cdB (0.01 dB steps) |
| Level | _dBm, _cdBm, _dBW, _dBV, _dBmV, _dBuV (also _dBµV) |
Literals are integral, so fractional values use the centi variants (1301_cdBm is 13.01 dBm) or an explicit floating-point type. Negative levels such as -73_dBm work because level provides a unary operator-, which reflects the level about its reference.
Following std::chrono semantics:
quantity_cast) are required when the conversion may lose precisionfloor, ceil and round provide explicit rounding controlLevels follow the same rules, via level_cast and the same floor, ceil and round overloads:
The library can be used as an ESP-IDF component. Add it to your project's idf_component.yml:
std::format support is controlled via CONFIG_ELECTRO_STD_FORMAT in menuconfig (under "Component config" → "Electro Library").
MIT License - see [LICENSE](LICENSE) for details.