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