Skip to content

Commit

Permalink
TODO: fixme!
Browse files Browse the repository at this point in the history
Logarithmic units require `pow`/`root` functions which are not available
in no-std/no-libm builds. Excluding logarthmic units has proven
difficult (in-place filter foiled by macros unable to be used inside an
enum definition). Using a tt-muncher could possibly solve the issue, but
would likely hit the recursion limit very quickly and likely the
quadratic performance characteristics would make `uom`'s slow
compilation even slower.
  • Loading branch information
iliekturtles committed Jun 28, 2022
1 parent 9cda2d6 commit d93e17b
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 158 deletions.
124 changes: 93 additions & 31 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,7 @@ pub enum ConstantOp {
///
/// [units]: https://jcgm.bipm.org/vim/en/1.13.html
/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
pub trait Conversion<V> where
V: Conversion<V, T = Self::T> {
pub trait Conversion<V> {
/// Conversion factor type specific to the underlying storage type.
type T: ConversionFactor<V>;

Expand All @@ -448,26 +447,26 @@ pub trait Conversion<V> where
<Self::T as crate::num::Zero>::zero()
}

/// Base portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for converting
/// the given logarithmic unit to the base unit for the quantity: `base().pow((value *
/// coefficient() + constant()) / scale())`. Implementation should return zero
/// (`Self::T::zero()`) if no base exists.
#[cfg(any(feature = "std", feature = "libm"))]
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
#[allow(unused_variables)]
fn base() -> Self::T {
<Self::T as crate::num::One>::one()
<Self::T as crate::num::Zero>::zero()
}

/// Scale portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for converting
/// the given logarithmic unit to the base unit for the quantity: `base().pow((value *
/// coefficient() + constant()) / scale())`. Implementation should return zero
/// (`Self::T::zero()`) if no base exists.
#[cfg(any(feature = "std", feature = "libm"))]
#[must_use = "method returns a new number and does not mutate the original value"]
#[inline(always)]
#[allow(unused_variables)]
fn scale() -> Self::T {
<Self::T as crate::num::One>::one()
}

#[inline(always)]
fn into_linear(x: V) -> V {
x
}

#[inline(always)]
fn from_linear(x: V) -> V {
x
<Self::T as crate::num::Zero>::zero()
}

/// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html).
Expand Down Expand Up @@ -504,14 +503,14 @@ pub trait ConversionFactor<V>:
fn powi(self, e: i32) -> Self;

/// Raises a `ConversionFactor<V>` to a power.
fn pow(self, v: V) -> V {
unimplemented!()
}
#[cfg(any(feature = "std", feature = "libm"))]
#[must_use = "method returns a new number and does not mutate the original value"]
fn pow(self, e: Self) -> Self;

/// Takes the log_`ConversionFactor<V>` of a value.
fn log(self, v: V) -> V {
unimplemented!()
}
#[cfg(any(feature = "std", feature = "libm"))]
#[must_use = "method returns a new number and does not mutate the original value"]
fn log(self, base: Self) -> Self;

/// Converts a `ConversionFactor<V>` into its underlying storage type.
#[must_use = "method returns a new number and does not mutate the original value"]
Expand Down Expand Up @@ -578,18 +577,21 @@ storage_types! {
<V as crate::num::Float>::powi(self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn value(self) -> V {
self
fn pow(self, e: Self) -> Self {
<V as crate::num::Float>::powf(self, e)
}

fn pow(self, v: V) -> V {
<V as crate::num::Float>::powf(self, v)
#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, base: Self) -> Self {
<V as crate::num::Float>::log(self, base)
}

/// Takes the log_`ConversionFactor<V>` of a value.
fn log(self, v: V) -> V {
<V as crate::num::Float>::log(self, v)
#[inline(always)]
fn value(self) -> V {
self
}
}

Expand All @@ -613,7 +615,19 @@ storage_types! {
impl crate::ConversionFactor<V> for crate::num::rational::Ratio<V> {
#[inline(always)]
fn powi(self, e: i32) -> Self {
self.pow(e)
crate::num::rational::Ratio::<V>::pow(&self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, _e: Self) -> Self {
unimplemented!();
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, _base: Self) -> Self {
unimplemented!();
}

#[inline(always)]
Expand Down Expand Up @@ -649,6 +663,18 @@ storage_types! {
}
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, e: Self) -> Self {
crate::num::rational::Ratio::<V>::pow(&self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, base: Self) -> Self {
crate::num::rational::Ratio::<V>::log(&self, base)
}

#[inline(always)]
fn value(self) -> V {
self.to_integer()
Expand All @@ -671,7 +697,19 @@ storage_types! {
impl crate::ConversionFactor<V> for V {
#[inline(always)]
fn powi(self, e: i32) -> Self {
self.pow(e)
V::pow(&self, e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, _e: Self) -> Self {
unimplemented!();
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, _base: Self) -> Self {
unimplemented!();
}

#[inline(always)]
Expand Down Expand Up @@ -703,6 +741,18 @@ storage_types! {
}
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, _e: Self) -> Self {
unimplemented!();
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, _base: Self) -> Self {
unimplemented!();
}

#[inline(always)]
fn value(self) -> V {
self
Expand Down Expand Up @@ -737,6 +787,18 @@ storage_types! {
self.powi(e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn pow(self, e: Self) -> Self {
self.powf(e)
}

#[cfg(any(feature = "std", feature = "libm"))]
#[inline(always)]
fn log(self, base: Self) -> Self {
VV::log(self, base)
}

#[inline(always)]
fn value(self) -> V {
// Conversion by scaling (multiplication with only real number). Scaling a normalized
Expand Down
20 changes: 13 additions & 7 deletions src/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
/// * `$conversion`: Conversion (coefficient and constant factor) from the unit to the base unit of
/// the quantity (e.g. `3.048_E-1` to convert `foot` to `meter`. `1.0_E0, 273.15_E0` to convert
/// `celsius` to `kelvin`.). The coefficient is required and the constant factor is optional.
/// Note that using a unit with a non-zero constant factor is not currently supported as a base
/// unit.
/// Note that using a unit with a non-zero constant factor, base, or scale is not currently
/// supported as a base unit.
/// * `$abbreviation`: Unit abbreviation (e.g. `"m"`).
/// * `$singular`: Singular unit description (e.g. `"meter"`).
/// * `$plural`: Plural unit description (e.g. `"meters"`).
Expand Down Expand Up @@ -157,8 +157,11 @@ macro_rules! quantity {
#[allow(clippy::manual_non_exhaustive)]
#[derive(Debug, Clone, Copy)]
pub enum Units {
$(#[doc=$plural]
$unit($unit),)+
$(unit! {
@supported_attr $($conversion),+;
#[doc=$plural]
$unit($unit),
})+

#[doc(hidden)]
__nonexhaustive,
Expand All @@ -170,7 +173,10 @@ macro_rules! quantity {
#[allow(dead_code)]
pub fn abbreviation(&self) -> &'static str {
match self {
$(Units::$unit(_) => <$unit as __system::Unit>::abbreviation(),)+
$(unit! {
@supported_attr $($conversion),+;
Units::$unit(_) => <$unit as __system::Unit>::abbreviation(),
})+

Units::__nonexhaustive => "unknown",
}
Expand Down Expand Up @@ -227,7 +233,7 @@ macro_rules! quantity {
$quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: __system::to_base::<Dimension, U, V, N>(&<N>::into_linear(v)),
value: __system::to_base::<Dimension, U, V, N>(&v),
}
}

Expand All @@ -241,7 +247,7 @@ macro_rules! quantity {
where
N: Unit + $crate::Conversion<V, T = V::T>,
{
<N>::from_linear(__system::from_base::<Dimension, U, V, N>(&self.value))
__system::from_base::<Dimension, U, V, N>(&self.value)
}

/// Returns the largest integer less than or equal to a number in the given
Expand Down
8 changes: 5 additions & 3 deletions src/si/electric_potential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ quantity! {
@statvolt: 2.997_925_E2; "statV", "statvolt", "statvolts";

@decibel_volt: prefix!(none), 10.0, 20.0; "dBV", "decibel-volt", "decibel-volts";
@decibel_millivolt: prefix!(milli), 10.0, 20.0; "dBmV", "decibel-millivolt", "decibel-millivolts";
@decibel_microvolt: prefix!(micro), 10.0, 20.0; "dBµV", "decibel-microvolt", "decibel-microvolts";
@decibel_unit: 0.7746E0, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units";
@decibel_millivolt: prefix!(milli), 10.0, 20.0; "dBmV", "decibel-millivolt",
"decibel-millivolts";
@decibel_microvolt: prefix!(micro), 10.0, 20.0; "dBµV", "decibel-microvolt",
"decibel-microvolts";
@decibel_unit: 7.746_E-1, 10.0, 20.0; "dBu", "decibel-unit", "decibel-units";
}
}

Expand Down
18 changes: 4 additions & 14 deletions src/si/power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ quantity! {
@yoctowatt: prefix!(yocto); "yW", "yoctowatt", "yoctowatts";

@decibel_watt: prefix!(none), 10.0, 10.0; "dBW", "decibel-watt", "decibel-watts";
@decibel_milliwatt: prefix!(milli), 10.0, 10.0; "dBm", "decibel-milliwatt", "decibel-milliwatts";// dBm is more common than dBmW
@decibel_microwatt: prefix!(micro), 10.0, 10.0; "dBµW", "decibel-microwatt", "decibel-microwatts";
@decibel_milliwatt: prefix!(milli), 10.0, 10.0; "dBmW", "decibel-milliwatt",
"decibel-milliwatts";
@decibel_microwatt: prefix!(micro), 10.0, 10.0; "dBµW", "decibel-microwatt",
"decibel-microwatts";

@erg_per_second: 1.0_E-7; "erg/s", "erg per second", "ergs per second";
@foot_pound_per_hour: 3.766_161_111_111_111_E-4; "ft · lbf/h", "foot pound-force per hour",
Expand All @@ -62,18 +64,6 @@ quantity! {

#[cfg(test)]
mod tests {

#[test]
fn test_dbm() {
use crate::si::power as p;
use crate::si::quantities::*;
use crate::tests::Test;

let x = Power::new::<p::decibel_milliwatt>(0.0);
println!("{:?}", x.get::<p::watt>());
println!("{:?}", x.get::<p::decibel_watt>());
}

storage_types! {
use crate::num::One;
use crate::si::energy as e;
Expand Down
8 changes: 6 additions & 2 deletions src/si/ratio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ quantity! {
@part_per_trillion: 1.0_E-12; "ppt", "part per trillion", "parts per trillion";
@part_per_quadrillion: 1.0_E-15; "ppq", "part per quadrillion", "parts per quadrillion";

@decibel: 1.0, 10.0, 20.0; "dB", "decibel", "decibels";
@decibel: 1.0, 10.0, 10.0; "dB", "decibel", "decibels";
}
}

Expand Down Expand Up @@ -182,7 +182,7 @@ mod convert {
#[cfg(test)]
mod tests {
storage_types! {
use crate::num::{FromPrimitive, One};
use crate::num::{FromPrimitive, One, Zero};
use crate::si::quantities::*;
use crate::si::ratio as r;
use crate::tests::Test;
Expand Down Expand Up @@ -219,6 +219,10 @@ mod tests {
Test::assert_eq(&Ratio::new::<r::ratio>(V::one()
/ V::from_f64(1.0_E15).unwrap()),
&Ratio::new::<r::part_per_quadrillion>(V::one()));
Test::assert_eq(&Ratio::new::<r::ratio>(V::one()),
&Ratio::new::<r::decibel>(V::zero()));
Test::assert_eq(&Ratio::new::<r::ratio>(V::from_u8(10).unwrap()),
&Ratio::new::<r::decibel>(V::from_u8(10).unwrap()));
}
}

Expand Down
Loading

0 comments on commit d93e17b

Please sign in to comment.