Units
Air Force Research Laboratory (AFRL) Autonomous Capabilities Team (ACT3) Reinforcement Learning (RL) Core.
This is a US Government Work not subject to copyright protection in the US.
The use, dissemination or disclosure of data in this file is subject to limitation or restriction. See accompanying README and LICENSE for details.
Module containing unit dimensions and functions to convert between units
Acceleration (Enum)
¤
Acceleration
Source code in corl/libraries/units.py
class Acceleration(enum.Enum):
"""Acceleration
"""
knots_per_sec = (1.0, ["knots/s"])
meter_per_sec_2 = (0.51444563, ["m/s^2"])
feet_per_sec_2 = (1.68780986, ["ft/s^2"])
standard_gravity = (0.05245885496, ["g", "G", "gravity", "standard_gravity"])
DEFAULT = knots_per_sec
Angle (Enum)
¤
Angle dimension
Source code in corl/libraries/units.py
class Angle(enum.Enum):
"""Angle dimension
"""
Degree = (180.0, ["deg", "degree", "degrees"])
Rad = (math.pi, ["rad", "radian", "radians"])
DEFAULT = Rad
AngularSpeed (Enum)
¤
Angular speed dimension
Source code in corl/libraries/units.py
class AngularSpeed(enum.Enum):
"""Angular speed dimension
"""
radians_per_sec = (1, ["rad/s", "r/s"])
degrees_per_sec = (57.2958, ["deg/s"])
DEFAULT = radians_per_sec
CalibratedSpeed (Enum)
¤
Speed dimension for calibrated airspeed
Source code in corl/libraries/units.py
class CalibratedSpeed(enum.Enum):
"""Speed dimension for calibrated airspeed
"""
Meter_per_Sec = (Speed.Meter_per_Sec.value[0], ["mpscas"])
Knots = (Speed.Knots.value[0], ["kcas"])
Feet_per_Min = (Speed.Feet_per_Min.value[0], ["fpmcas"])
Feet_per_Sec = (Speed.Feet_per_Sec.value[0], ["fpscas"])
DEFAULT = Knots
Distance (Enum)
¤
Distance dimension
Source code in corl/libraries/units.py
class Distance(enum.Enum):
"""Distance dimension
"""
Meter = (1.0, ["m", "meter", "meters"])
Feet = (3.28084, ["ft", "feet"])
Nautical_Mile = (1 / 1852, ["nm"])
DEFAULT = Meter
Force (Enum)
¤
Force
Source code in corl/libraries/units.py
class Force(enum.Enum):
"""Force
"""
Newton = (1.0, ["N", "newton", "newtons"])
PoundForce = (0.22481, ["pound-force"])
DEFAULT = Newton
IndicatedSpeed (Enum)
¤
Speed dimension for indicated airspeed
Source code in corl/libraries/units.py
class IndicatedSpeed(enum.Enum):
"""Speed dimension for indicated airspeed
"""
Meter_per_Sec = (Speed.Meter_per_Sec.value[0], ["mpsias"])
Knots = (Speed.Knots.value[0], ["kias"])
Feet_per_Min = (Speed.Feet_per_Min.value[0], ["fpmias"])
Feet_per_Sec = (Speed.Feet_per_Sec.value[0], ["fpsias"])
DEFAULT = Knots
MachSpeed (Enum)
¤
Mach speed dimension
Source code in corl/libraries/units.py
class MachSpeed(enum.Enum):
"""Mach speed dimension
"""
Mach = (1.0, ["mach", "Ma"])
DEFAULT = Mach
NoneUnitType (Enum)
¤
None
Source code in corl/libraries/units.py
class NoneUnitType(enum.Enum):
"""None
"""
NoneUnit = (1.0, ["N/A", "None", "none"])
DEFAULT = NoneUnit
PartOfWhole (Enum)
¤
PartOfWhole
Source code in corl/libraries/units.py
class PartOfWhole(enum.Enum):
"""PartOfWhole
"""
Fraction = (1.0, ["fraction"])
Percent = (100.0, ["percent"])
DEFAULT = Fraction
Speed (Enum)
¤
Speed dimension
Source code in corl/libraries/units.py
class Speed(enum.Enum):
"""Speed dimension
"""
Meter_per_Sec = (1, ["m/s", "m_s"])
Knots = (1.94384, ["knot", "knots", "kts"])
Feet_per_Min = (196.8504, ["ft/min", "feet/min"])
Feet_per_Sec = (3.28084, ["ft/s", "feet/second", "feet/sec"])
DEFAULT = Knots
Time (Enum)
¤
Time dimension
Source code in corl/libraries/units.py
class Time(enum.Enum):
"""Time dimension
"""
Second = (1.0, ["s", "sec", "second", "seconds"])
Hour = (0.0002777778, ["hr", "hour"])
DEFAULT = Second
TrueSpeed (Enum)
¤
Speed dimension for true airspeed
Source code in corl/libraries/units.py
class TrueSpeed(enum.Enum):
"""Speed dimension for true airspeed
"""
Meter_per_Sec = (Speed.Meter_per_Sec.value[0], ["mpstas"])
Knots = (Speed.Knots.value[0], ["ktas"])
Feet_per_Min = (Speed.Feet_per_Min.value[0], ["fpmtas"])
Feet_per_Sec = (Speed.Feet_per_Sec.value[0], ["fpstas"])
DEFAULT = Knots
ValueWithUnits (BaseModel)
pydantic-model
¤
Wrap a value together with its units
Attributes¤
value : typing.Any The value units : typing.Union[None, enum.Enum] The units
Source code in corl/libraries/units.py
class ValueWithUnits(BaseModel):
"""Wrap a value together with its units
Attributes
----------
value : typing.Any
The value
units : typing.Union[None, enum.Enum]
The units
"""
value: typing.Union[StrictInt, StrictFloat, bool, str]
units: typing.Optional[enum.Enum]
@validator('value', pre=True)
def value_validator(cls, v):
"""Validate value"""
# automatically convert numpy types to native types when needed
# tolist will convert scalar or array to python native type
# tolist returns a single value (the scalar) when calling it on a single value
return getattr(v, "tolist", lambda: v)()
@validator('units', pre=True)
def convert_string_units(cls, v):
"""Build units out of string"""
if isinstance(v, str):
return GetUnitFromStr(v)
if v is None:
return NoneUnitType.NoneUnit
return v
@root_validator
def str_no_units(cls, values):
"""Confirm that string values have no units"""
if isinstance(values.get('value'), str):
assert values.get('units') is NoneUnitType.NoneUnit, 'String values must have unit None'
return values
def convert_to(self, to_unit: typing.Union[None, str, enum.Enum]) -> float:
"""Convert this value to another unit
Parameters
----------
to_unit : typing.Union[None, str, enum.Enum]
unit type to convert to
Returns
-------
numbers.Real
converted value
"""
return Convert(value=self.value, from_unit=self.units, to_unit=to_unit) # type: ignore
class PrincipalValueNormalization(enum.Enum):
"""Enumeration of the type of Principal Value normalization desired.
Enumeration Values
------------------
Positive
Normalize to be within the range [0, T], for periodicity T.
Centered
Normalize to be within the range [-T/2, T/2], for periodicity T.
"""
Positive = enum.auto()
Centered = enum.auto()
def as_units(self, units: typing.Union[None, str, enum.Enum]) -> typing.Any:
"""View the number in some other units
Parameters
----------
units : typing.Union[None, str, enum.Enum]
The desired units
Returns
-------
float
The value in the desired units
"""
if units is None and self.units is None:
return self.value
if units is None or self.units is None:
raise RuntimeError(f'Incompatible units involving None: {units} <> {self.units}')
if units == self.units:
return self.value
assert isinstance(self.value, (int, float))
return Convert(value=self.value, from_unit=self.units, to_unit=units)
def convert(self, units: typing.Union[None, str, enum.Enum]) -> 'ValueWithUnits':
"""Convert the internal representation to new units
Parameters
----------
units : typing.Union[None, str, enum.Enum]
The desired units
"""
if units is None and self.units is None:
return self
if units is None or self.units is None:
raise RuntimeError('Incompatible units involving None')
new_units = GetUnitFromStr(units) if isinstance(units, str) else units
self.value = self.as_units(new_units)
self.units = new_units
return self
def __add__(self, other: 'ValueWithUnits') -> 'ValueWithUnits':
"""Implement addition"""
raw_value = self.value + other.as_units(self.units)
return ValueWithUnits(value=raw_value, units=self.units)
def __sub__(self, other: 'ValueWithUnits') -> 'ValueWithUnits':
"""Implement subtraction"""
raw_value = self.value - other.as_units(self.units)
return ValueWithUnits(value=raw_value, units=self.units)
def normalize_to_principal_value(self, method) -> None:
"""Normalize an angular unit to its principal value.
Parameters
----------
method : PrincipalValueNormalization
The type of normalization desired.
"""
if not isinstance(self.units, Angle):
raise ValueError('Cannot normalize non-angular value')
if not isinstance(self.value, (int, float)):
raise TypeError('Can only normalize numeric values')
self.value = typing.cast(typing.Union[int, float], self.value)
if self.units == Angle.Degree:
period: float = 360
elif self.units == Angle.Rad:
period = 2 * math.pi
else:
raise ValueError(f'Fix normalize_to_principal_value for {self.units}')
# Normalize to [-period, period]
self.value = self.value % period
# Normalize to [0, period]
if self.value < 0:
self.value += period
if method == self.PrincipalValueNormalization.Positive:
return
if method == self.PrincipalValueNormalization.Centered:
if self.value > period / 2:
self.value -= period
return
raise ValueError(f'Fix normalize_to_principal_value for {method}')
def __str__(self):
if self.units is not None:
return f'{self.value} {self.units.value[1][0]}'
return str(self.value)
def __repr__(self):
return str(self)
PrincipalValueNormalization (Enum)
¤
Enumeration of the type of Principal Value normalization desired.
Enumeration Values¤
Positive Normalize to be within the range [0, T], for periodicity T. Centered Normalize to be within the range [-T/2, T/2], for periodicity T.
Source code in corl/libraries/units.py
class PrincipalValueNormalization(enum.Enum):
"""Enumeration of the type of Principal Value normalization desired.
Enumeration Values
------------------
Positive
Normalize to be within the range [0, T], for periodicity T.
Centered
Normalize to be within the range [-T/2, T/2], for periodicity T.
"""
Positive = enum.auto()
Centered = enum.auto()
__add__(self, other)
special
¤
Implement addition
Source code in corl/libraries/units.py
def __add__(self, other: 'ValueWithUnits') -> 'ValueWithUnits':
"""Implement addition"""
raw_value = self.value + other.as_units(self.units)
return ValueWithUnits(value=raw_value, units=self.units)
__repr__(self)
special
¤
Return repr(self).
Source code in corl/libraries/units.py
def __repr__(self):
return str(self)
__str__(self)
special
¤
Return str(self).
Source code in corl/libraries/units.py
def __str__(self):
if self.units is not None:
return f'{self.value} {self.units.value[1][0]}'
return str(self.value)
__sub__(self, other)
special
¤
Implement subtraction
Source code in corl/libraries/units.py
def __sub__(self, other: 'ValueWithUnits') -> 'ValueWithUnits':
"""Implement subtraction"""
raw_value = self.value - other.as_units(self.units)
return ValueWithUnits(value=raw_value, units=self.units)
as_units(self, units)
¤
View the number in some other units
Parameters¤
units : typing.Union[None, str, enum.Enum] The desired units
Returns¤
float The value in the desired units
Source code in corl/libraries/units.py
def as_units(self, units: typing.Union[None, str, enum.Enum]) -> typing.Any:
"""View the number in some other units
Parameters
----------
units : typing.Union[None, str, enum.Enum]
The desired units
Returns
-------
float
The value in the desired units
"""
if units is None and self.units is None:
return self.value
if units is None or self.units is None:
raise RuntimeError(f'Incompatible units involving None: {units} <> {self.units}')
if units == self.units:
return self.value
assert isinstance(self.value, (int, float))
return Convert(value=self.value, from_unit=self.units, to_unit=units)
convert(self, units)
¤
Convert the internal representation to new units
Parameters¤
units : typing.Union[None, str, enum.Enum] The desired units
Source code in corl/libraries/units.py
def convert(self, units: typing.Union[None, str, enum.Enum]) -> 'ValueWithUnits':
"""Convert the internal representation to new units
Parameters
----------
units : typing.Union[None, str, enum.Enum]
The desired units
"""
if units is None and self.units is None:
return self
if units is None or self.units is None:
raise RuntimeError('Incompatible units involving None')
new_units = GetUnitFromStr(units) if isinstance(units, str) else units
self.value = self.as_units(new_units)
self.units = new_units
return self
convert_string_units(v)
classmethod
¤
Build units out of string
Source code in corl/libraries/units.py
@validator('units', pre=True)
def convert_string_units(cls, v):
"""Build units out of string"""
if isinstance(v, str):
return GetUnitFromStr(v)
if v is None:
return NoneUnitType.NoneUnit
return v
convert_to(self, to_unit)
¤
Convert this value to another unit
Parameters¤
to_unit : typing.Union[None, str, enum.Enum] unit type to convert to
Returns¤
numbers.Real converted value
Source code in corl/libraries/units.py
def convert_to(self, to_unit: typing.Union[None, str, enum.Enum]) -> float:
"""Convert this value to another unit
Parameters
----------
to_unit : typing.Union[None, str, enum.Enum]
unit type to convert to
Returns
-------
numbers.Real
converted value
"""
return Convert(value=self.value, from_unit=self.units, to_unit=to_unit) # type: ignore
normalize_to_principal_value(self, method)
¤
Normalize an angular unit to its principal value.
Parameters¤
method : PrincipalValueNormalization The type of normalization desired.
Source code in corl/libraries/units.py
def normalize_to_principal_value(self, method) -> None:
"""Normalize an angular unit to its principal value.
Parameters
----------
method : PrincipalValueNormalization
The type of normalization desired.
"""
if not isinstance(self.units, Angle):
raise ValueError('Cannot normalize non-angular value')
if not isinstance(self.value, (int, float)):
raise TypeError('Can only normalize numeric values')
self.value = typing.cast(typing.Union[int, float], self.value)
if self.units == Angle.Degree:
period: float = 360
elif self.units == Angle.Rad:
period = 2 * math.pi
else:
raise ValueError(f'Fix normalize_to_principal_value for {self.units}')
# Normalize to [-period, period]
self.value = self.value % period
# Normalize to [0, period]
if self.value < 0:
self.value += period
if method == self.PrincipalValueNormalization.Positive:
return
if method == self.PrincipalValueNormalization.Centered:
if self.value > period / 2:
self.value -= period
return
raise ValueError(f'Fix normalize_to_principal_value for {method}')
str_no_units(values)
classmethod
¤
Confirm that string values have no units
Source code in corl/libraries/units.py
@root_validator
def str_no_units(cls, values):
"""Confirm that string values have no units"""
if isinstance(values.get('value'), str):
assert values.get('units') is NoneUnitType.NoneUnit, 'String values must have unit None'
return values
value_validator(v)
classmethod
¤
Validate value
Source code in corl/libraries/units.py
@validator('value', pre=True)
def value_validator(cls, v):
"""Validate value"""
# automatically convert numpy types to native types when needed
# tolist will convert scalar or array to python native type
# tolist returns a single value (the scalar) when calling it on a single value
return getattr(v, "tolist", lambda: v)()
Weight (Enum)
¤
Weight
Source code in corl/libraries/units.py
class Weight(enum.Enum):
"""Weight
"""
Kilogram = (1.0, ["kg", "kilogram"])
Pound = (2.20462, ["lb", "lbs", "pound", "pounds"])
DEFAULT = Kilogram
Convert(value, from_unit, to_unit)
¤
Convert a value from a unit to another unit
Exceptions:
Type | Description |
---|---|
RuntimeError |
Thrown if the unit's dimensions do not match |
Returns:
Type | Description |
---|---|
float |
float -- Converted value |
Source code in corl/libraries/units.py
def Convert(value: float, from_unit: typing.Union[str, enum.Enum], to_unit: typing.Union[str, enum.Enum]) -> float:
"""Convert a value from a unit to another unit
Arguments:
value {float} -- Value to convert
from_unit {typing.Union[str, enum.Enum]} -- Unit that provided value is in
to_unit {typing.Union[str, enum.Enum]} -- Desired Unit
Raises:
RuntimeError: Thrown if the unit's dimensions do not match
Returns:
float -- Converted value
"""
if isinstance(from_unit, str):
from_unit = GetUnitFromStr(from_unit)
if isinstance(to_unit, str):
to_unit = GetUnitFromStr(to_unit)
if isinstance(from_unit, type(to_unit)) is False:
raise RuntimeError(f"Dimensions do not match! {from_unit} -> {to_unit}")
return value * to_unit.value[0] / from_unit.value[0] # type: ignore
ConvertToDefault(value, from_unit)
¤
Convert a value from a unit to the default unit for its type
Returns:
Type | Description |
---|---|
float |
float -- Converted value |
Source code in corl/libraries/units.py
def ConvertToDefault(value: float, from_unit: typing.Union[None, str, enum.Enum]) -> float:
"""Convert a value from a unit to the default unit for its type
Arguments:
value {float} -- Value to convert
from_unit {typing.Union[str, enum.Enum]} -- Unit that provided value is in
Returns:
float -- Converted value
"""
if from_unit is None:
return value
if isinstance(from_unit, str):
from_unit = GetUnitFromStr(from_unit)
to_unit = type(from_unit)['DEFAULT']
return Convert(value=value, from_unit=from_unit, to_unit=to_unit)
GetStrFromUnit(unit)
¤
Given a unit determine the string that unit corresponds to.
Source code in corl/libraries/units.py
@lru_cache(maxsize=50)
def GetStrFromUnit(unit: enum.Enum) -> str:
"""Given a unit determine the string that unit corresponds to.
"""
return unit.value[1][0]
GetUnitFromStr(string)
¤
Given a string determine the unit that string corresponds to.
Source code in corl/libraries/units.py
@lru_cache(maxsize=50)
def GetUnitFromStr(string: str) -> enum.Enum:
"""Given a string determine the unit that string corresponds to.
"""
for Dimension in Dimensions:
for Unit in Dimension:
if string in Unit.value[1]:
return Unit
raise RuntimeError(f"{string} could not be matched to any known unit, please check act3/util/units.py for potential units")