001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.units; 006 007import java.util.Objects; 008 009/** 010 * Unit of measurement that defines a quantity, such as grams, meters, or seconds. 011 * 012 * <p>This is the base class for units. Actual units (such as {@link Units#Grams} and {@link 013 * Units#Meters}) can be found in the {@link Units} class. 014 * 015 * @param <U> the self type, e.g. {@code class SomeUnit extends Unit<SomeUnit>} 016 */ 017public class Unit<U extends Unit<U>> { 018 private final UnaryFunction m_toBaseConverter; 019 private final UnaryFunction m_fromBaseConverter; 020 021 final Class<? extends U> m_baseType; // package-private for the builder 022 023 private Measure<U> m_zero; 024 private Measure<U> m_one; 025 026 private final String m_name; 027 private final String m_symbol; 028 029 /** 030 * Creates a new unit defined by its relationship to some base unit. 031 * 032 * @param baseType the base type of the unit, e.g. Distance.class for the distance unit 033 * @param toBaseConverter a function for converting units of this type to the base unit 034 * @param fromBaseConverter a function for converting units of the base unit to this one 035 * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters") 036 * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds 037 */ 038 protected Unit( 039 Class<? extends U> baseType, 040 UnaryFunction toBaseConverter, 041 UnaryFunction fromBaseConverter, 042 String name, 043 String symbol) { 044 m_baseType = Objects.requireNonNull(baseType); 045 m_toBaseConverter = Objects.requireNonNull(toBaseConverter); 046 m_fromBaseConverter = Objects.requireNonNull(fromBaseConverter); 047 m_name = Objects.requireNonNull(name); 048 m_symbol = Objects.requireNonNull(symbol); 049 } 050 051 /** 052 * Creates a new unit with the given name and multiplier to the base unit. 053 * 054 * @param baseType the base type of the unit, e.g. Distance.class for the distance unit 055 * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type. 056 * For example, meters has a multiplier of 1, mm has a multiplier of 1e3, and km has 057 * multiplier of 1e-3. 058 * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters") 059 * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds 060 */ 061 protected Unit( 062 Class<? extends U> baseType, double baseUnitEquivalent, String name, String symbol) { 063 this(baseType, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol); 064 } 065 066 /** 067 * Converts a value in terms of base units to a value in terms of this unit. 068 * 069 * @param valueInBaseUnits the value in base units to convert 070 * @return the equivalent value in terms of this unit 071 */ 072 public double fromBaseUnits(double valueInBaseUnits) { 073 return m_fromBaseConverter.apply(valueInBaseUnits); 074 } 075 076 /** 077 * Converts a value in terms of this unit to a value in terms of the base unit. 078 * 079 * @param valueInNativeUnits the value in terms of this unit to convert 080 * @return the equivalent value in terms of the base unit 081 */ 082 public double toBaseUnits(double valueInNativeUnits) { 083 return m_toBaseConverter.apply(valueInNativeUnits); 084 } 085 086 /** 087 * Converts a magnitude in terms of another unit of the same dimension to a magnitude in terms of 088 * this unit. 089 * 090 * <pre> 091 * Inches.convertFrom(12, Feet) // 144.0 092 * Kilograms.convertFrom(2.2, Pounds) // 0.9979024 093 * </pre> 094 * 095 * @param magnitude a magnitude measured in another unit 096 * @param otherUnit the unit to convert the magnitude to 097 * @return the corresponding value in terms of this unit. 098 */ 099 public double convertFrom(double magnitude, Unit<U> otherUnit) { 100 if (this.equivalent(otherUnit)) { 101 // same unit, don't bother converting 102 return magnitude; 103 } 104 return this.fromBaseUnits(otherUnit.toBaseUnits(magnitude)); 105 } 106 107 /** 108 * Gets the conversion function used to convert values to base unit terms. This generally 109 * shouldn't need to be used directly; prefer {@link #toBaseUnits(double)} instead. 110 * 111 * @return the conversion function 112 */ 113 public UnaryFunction getConverterToBase() { 114 return m_toBaseConverter; 115 } 116 117 /** 118 * Gets the conversion function used to convert values to terms of this unit. This generally 119 * shouldn't need to be used directly; prefer {@link #fromBaseUnits(double)} instead. 120 * 121 * @return the conversion function 122 */ 123 public UnaryFunction getConverterFromBase() { 124 return m_fromBaseConverter; 125 } 126 127 /** 128 * Creates a new measure of this unit with the given value. The resulting measure is 129 * <i>immutable</i> and cannot have its value modified. 130 * 131 * @param magnitude the magnitude of the measure to create 132 * @return the measure 133 */ 134 public Measure<U> of(double magnitude) { 135 if (magnitude == 0) { 136 // reuse static object 137 return zero(); 138 } 139 if (magnitude == 1) { 140 // reuse static object 141 return one(); 142 } 143 return ImmutableMeasure.ofRelativeUnits(magnitude, this); 144 } 145 146 /** 147 * Creates a new measure with a magnitude equal to the given base unit magnitude, converted to be 148 * in terms of this unit. 149 * 150 * @param baseUnitMagnitude the magnitude of the measure in terms of the base unit 151 * @return the measure 152 */ 153 public Measure<U> ofBaseUnits(double baseUnitMagnitude) { 154 return ImmutableMeasure.ofBaseUnits(baseUnitMagnitude, this); 155 } 156 157 /** 158 * Gets a measure with a magnitude of 0 in terms of this unit. 159 * 160 * @return the zero-valued measure 161 */ 162 public Measure<U> zero() { 163 // lazy init because 'this' is null in object initialization 164 if (m_zero == null) { 165 m_zero = ImmutableMeasure.ofRelativeUnits(0, this); 166 } 167 return m_zero; 168 } 169 170 /** 171 * Gets a measure with a magnitude of 1 in terms of this unit. 172 * 173 * @return the 1-valued measure 174 */ 175 public Measure<U> one() { 176 // lazy init because 'this' is null in object initialization 177 if (m_one == null) { 178 m_one = ImmutableMeasure.ofRelativeUnits(1, this); 179 } 180 return m_one; 181 } 182 183 /** 184 * Creates a velocity unit derived from this one. Can be chained to denote velocity, acceleration, 185 * jerk, etc. 186 * 187 * <pre> 188 * Meters.per(Second) // linear velocity 189 * Kilograms.per(Second) // mass flow 190 * Feet.per(Second).per(Second).of(32) // roughly 1G of acceleration 191 * </pre> 192 * 193 * @param period the time period of the velocity, such as seconds or milliseconds 194 * @return a velocity unit corresponding to the rate of change of this unit over time 195 */ 196 public Velocity<U> per(Time period) { 197 return Velocity.combine(this, period); 198 } 199 200 /** 201 * Takes this unit and creates a new proportional unit where this unit is the numerator and the 202 * given denominator is the denominator. 203 * 204 * <pre> 205 * Volts.per(Meter) // V/m 206 * </pre> 207 * 208 * @param <D> the type of the denominator units 209 * @param denominator the denominator of the proportional unit 210 * @return a combined proportional unit 211 */ 212 @SuppressWarnings("unchecked") 213 public <D extends Unit<D>> Per<U, D> per(D denominator) { 214 return Per.combine((U) this, denominator); 215 } 216 217 /** 218 * Takes this unit and creates a new combinatory unit equivalent to this unit multiplied by 219 * another. 220 * 221 * <pre> 222 * Volts.mult(Meter) // V*m 223 * </pre> 224 * 225 * @param <U2> the type of the unit to multiply by 226 * @param other the unit to multiply by 227 * @return a combined unit equivalent to this unit multiplied by the other 228 */ 229 @SuppressWarnings("unchecked") 230 public <U2 extends Unit<U2>> Mult<U, U2> mult(U2 other) { 231 return Mult.combine((U) this, other); 232 } 233 234 /** 235 * Checks if this unit is equivalent to another one. Equivalence is determined by both units 236 * having the same base type and treat the same base unit magnitude as the same magnitude in their 237 * own units, to within {@link Measure#EQUIVALENCE_THRESHOLD}. 238 * 239 * @param other the unit to compare to. 240 * @return true if both units are equivalent, false if not 241 */ 242 public boolean equivalent(Unit<?> other) { 243 if (this.m_baseType != other.m_baseType) { 244 // different unit types, not compatible 245 return false; 246 } 247 248 double arbitrary = 16_777.214; // 2^24 / 1e3 249 250 return Math.abs( 251 this.m_fromBaseConverter.apply(arbitrary) 252 - other.m_fromBaseConverter.apply(arbitrary)) 253 <= Measure.EQUIVALENCE_THRESHOLD 254 && Math.abs( 255 this.m_toBaseConverter.apply(arbitrary) - other.m_toBaseConverter.apply(arbitrary)) 256 <= Measure.EQUIVALENCE_THRESHOLD; 257 } 258 259 @Override 260 public boolean equals(Object o) { 261 if (this == o) { 262 return true; 263 } 264 if (!(o instanceof Unit)) { 265 return false; 266 } 267 Unit<?> that = (Unit<?>) o; 268 return m_baseType.equals(that.m_baseType) 269 && m_name.equals(that.m_name) 270 && m_symbol.equals(that.m_symbol) 271 && this.equivalent(that); 272 } 273 274 @Override 275 public int hashCode() { 276 return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_baseType, m_name, m_symbol); 277 } 278 279 /** 280 * Gets the name of this unit. 281 * 282 * @return the unit's name 283 */ 284 public String name() { 285 return m_name; 286 } 287 288 /** 289 * Gets the symbol of this unit. 290 * 291 * @return the unit's symbol 292 */ 293 public String symbol() { 294 return m_symbol; 295 } 296 297 @Override 298 public String toString() { 299 return name(); 300 } 301}