Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed sensor/src/core/temperature.rs
Empty file.
122 changes: 122 additions & 0 deletions sensor/src/core/temperature/dynamic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use std::cmp::Ordering;

use crate::core::temperature::{Celsius, Fahrenheit, Kelvin, Temperature, TemperatureUnit};

// A temperatures with a unknown unit at compile time
//
// Can be either `Kelvin`, `Celsius` or `Fahrenheit`
//
// Try to use `Kelvin`, `Celsius` or `Fahrenheit` instead whenever possible.
#[derive(Debug, Clone, Copy)]
pub enum DynamicTemperature {
Kelvin(Kelvin),
Celsius(Celsius),
Fahrenheit(Fahrenheit),
}

impl From<Kelvin> for DynamicTemperature {
fn from(value: Kelvin) -> Self {
Self::Kelvin(value)
}
}
impl From<Celsius> for DynamicTemperature {
fn from(value: Celsius) -> Self {
Self::Celsius(value)
}
}
impl From<Fahrenheit> for DynamicTemperature {
fn from(value: Fahrenheit) -> Self {
Self::Fahrenheit(value)
}
}

impl<Unit> PartialEq<Temperature<Unit>> for DynamicTemperature
where
Unit: TemperatureUnit,
{
fn eq(&self, other: &Temperature<Unit>) -> bool {
match self {
Self::Kelvin(temperature) => temperature.eq(other),
Self::Celsius(temperature) => temperature.eq(other),
Self::Fahrenheit(temperature) => temperature.eq(other),
}
}
}

impl<Unit> PartialEq<DynamicTemperature> for Temperature<Unit>
where
Unit: TemperatureUnit,
{
fn eq(&self, other: &DynamicTemperature) -> bool {
other == self
}
}

impl PartialEq for DynamicTemperature {
fn eq(&self, other: &Self) -> bool {
match self {
Self::Kelvin(temperature) => temperature.eq(other),
Self::Celsius(temperature) => temperature.eq(other),
Self::Fahrenheit(temperature) => temperature.eq(other),
}
}
}

impl<Unit> PartialOrd<Temperature<Unit>> for DynamicTemperature
where
Unit: TemperatureUnit,
{
fn partial_cmp(&self, other: &Temperature<Unit>) -> Option<std::cmp::Ordering> {
match self {
Self::Kelvin(temperature) => temperature.partial_cmp(other),
Self::Celsius(temperature) => temperature.partial_cmp(other),
Self::Fahrenheit(temperature) => temperature.partial_cmp(other),
}
}
}

impl<Unit> PartialOrd<DynamicTemperature> for Temperature<Unit>
where
Unit: TemperatureUnit,
{
fn partial_cmp(&self, other: &DynamicTemperature) -> Option<std::cmp::Ordering> {
other.partial_cmp(self).map(Ordering::reverse)
}
}

impl PartialOrd for DynamicTemperature {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self {
Self::Kelvin(temperature) => temperature.partial_cmp(other),
Self::Celsius(temperature) => temperature.partial_cmp(other),
Self::Fahrenheit(temperature) => temperature.partial_cmp(other),
}
}
}

impl DynamicTemperature {
pub fn kelvin(value: f32) -> Self {
Self::Kelvin(Temperature::new(value))
}

pub fn celsius(value: f32) -> Self {
Self::Celsius(Temperature::new(value))
}

pub fn fahrenheit(value: f32) -> Self {
Self::Fahrenheit(Temperature::new(value))
}

pub fn convert<Unit: TemperatureUnit>(self) -> Temperature<Unit> {
match self {
Self::Kelvin(val) => val.convert(),
Self::Celsius(val) => val.convert(),
Self::Fahrenheit(val) => val.convert(),
}
}
}

#[cfg(test)]
mod test {
// TODO: implement test cases
}
75 changes: 75 additions & 0 deletions sensor/src/core/temperature/dynamic_range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::ops::Range;

use crate::core::temperature::{Celsius, Fahrenheit, Kelvin, Temperature, TemperatureUnit};

// A range of temperatures with a unknown unit at compile time
//
// Can be either `Kelvin`, `Celsius` or `Fahrenheit`
//
// Try to use `Range<Kelvin>`, `Range<Celsius>` or `Range<Fahrenheit>` instead whenever possible.
#[derive(Debug, Clone)]
pub enum DynamicRange {
Kelvin(Range<Kelvin>),
Celsius(Range<Celsius>),
Fahrenheit(Range<Fahrenheit>),
}
impl From<Range<Kelvin>> for DynamicRange {
fn from(value: Range<Kelvin>) -> Self {
Self::Kelvin(value)
}
}
impl From<Range<Celsius>> for DynamicRange {
fn from(value: Range<Celsius>) -> Self {
Self::Celsius(value)
}
}
impl From<Range<Fahrenheit>> for DynamicRange {
fn from(value: Range<Fahrenheit>) -> Self {
Self::Fahrenheit(value)
}
}

impl DynamicRange {
pub fn kelvin(value: Range<f32>) -> Self {
Self::Kelvin(Range {
start: Temperature::new(value.start),
end: Temperature::new(value.end),
})
}

pub fn celsius(value: Range<f32>) -> Self {
Self::Celsius(Range {
start: Temperature::new(value.start),
end: Temperature::new(value.end),
})
}

pub fn fahrenheit(value: Range<f32>) -> Self {
Self::Fahrenheit(Range {
start: Temperature::new(value.start),
end: Temperature::new(value.end),
})
}

pub fn convert<Unit: TemperatureUnit>(self) -> Range<Temperature<Unit>> {
match self {
Self::Kelvin(val) => Range {
start: val.start.convert(),
end: val.end.convert(),
},
Self::Celsius(val) => Range {
start: val.start.convert(),
end: val.end.convert(),
},
Self::Fahrenheit(val) => Range {
start: val.start.convert(),
end: val.end.convert(),
},
}
}
}

#[cfg(test)]
mod test {
// TODO: implement test cases
}
83 changes: 83 additions & 0 deletions sensor/src/core/temperature/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
mod dynamic;
pub use dynamic::*;

pub mod unit;
pub use unit::TemperatureUnit;
pub type Celsius = Temperature<unit::Celsius>;
pub type Kelvin = Temperature<unit::Kelvin>;
pub type Fahrenheit = Temperature<unit::Fahrenheit>;

pub mod dynamic_range;
pub use dynamic_range::*;

use std::{cmp::Ordering, marker::PhantomData};

#[derive(Debug)]
pub struct Temperature<Unit: TemperatureUnit>(f32, PhantomData<Unit>);

impl<Unit: TemperatureUnit> Clone for Temperature<Unit> {
fn clone(&self) -> Self {
*self
}
}

impl<Unit: TemperatureUnit> Copy for Temperature<Unit> {}

impl<Unit1, Unit2> PartialEq<Temperature<Unit2>> for Temperature<Unit1>
where
Unit1: TemperatureUnit,
Unit2: TemperatureUnit,
{
fn eq(&self, other: &Temperature<Unit2>) -> bool {
// avoid precision loss, although partialeq on floats is probably a pretty bad idea anyways
if const { Unit2::SCALING_FACTOR > Unit1::SCALING_FACTOR } {
other.convert::<Unit1>().0 == self.0
} else {
self.convert::<Unit2>().0 == other.0
}
}
}

impl<Unit1, Unit2> PartialOrd<Temperature<Unit2>> for Temperature<Unit1>
where
Unit1: TemperatureUnit,
Unit2: TemperatureUnit,
{
fn partial_cmp(&self, other: &Temperature<Unit2>) -> Option<std::cmp::Ordering> {
// avoid precision loss
if const { Unit2::SCALING_FACTOR > Unit1::SCALING_FACTOR } {
other
.convert::<Unit1>()
.0
.partial_cmp(&self.0)
.map(Ordering::reverse)
} else {
self.convert::<Unit2>().0.partial_cmp(&other.0)
}
}
}

impl<Unit> Temperature<Unit>
where
Unit: TemperatureUnit,
{
pub fn new(value: f32) -> Self {
Self(value, PhantomData)
}

pub fn convert<TargetUnit: TemperatureUnit>(self) -> Temperature<TargetUnit> {
// this is essentially a f(g^-1(x)) composition in normal form. The factors get calculated at compile time, eliminating overhead.
let result = self.0 * const { TargetUnit::SCALING_FACTOR / Unit::SCALING_FACTOR }
+ const {
TargetUnit::ZERO_OFFSET
- Unit::ZERO_OFFSET * (TargetUnit::SCALING_FACTOR / Unit::SCALING_FACTOR)
};

Temperature(result, PhantomData)
}
}

#[cfg(test)]
mod test {
// TODO: implement test cases
}
25 changes: 25 additions & 0 deletions sensor/src/core/temperature/unit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/// A temperature unit, defined by its zero offset and scaling factor relative to the absolute temperature (Kelvin).
pub trait TemperatureUnit {
const ZERO_OFFSET: f32;
const SCALING_FACTOR: f32;
}

#[derive(Debug)]
pub struct Kelvin;
impl TemperatureUnit for Kelvin {
const ZERO_OFFSET: f32 = 0.0;
const SCALING_FACTOR: f32 = 1.0;
}

#[derive(Debug)]
pub struct Celsius;
impl TemperatureUnit for Celsius {
const ZERO_OFFSET: f32 = -273.15;
const SCALING_FACTOR: f32 = 1.0;
}
#[derive(Debug)]
pub struct Fahrenheit;
impl TemperatureUnit for Fahrenheit {
const ZERO_OFFSET: f32 = -459.67;
const SCALING_FACTOR: f32 = 1.8;
}
1 change: 1 addition & 0 deletions sensor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod core;
pub mod error;
pub mod ezo;
pub mod i2c_bus;
Expand Down
Loading