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 */ 015public abstract class Unit { 016 private final UnaryFunction m_toBaseConverter; 017 private final UnaryFunction m_fromBaseConverter; 018 019 private final Unit m_baseUnit; 020 021 private final String m_name; 022 private final String m_symbol; 023 024 private final Measure<?> m_zero; 025 private final Measure<?> m_one; 026 027 /** 028 * Creates a new unit defined by its relationship to some base unit. 029 * 030 * @param baseUnit the base unit, e.g. Meters for distances. Set this to {@code null} if the unit 031 * being constructed is its own base unit 032 * @param toBaseConverter a function for converting units of this type to the base unit 033 * @param fromBaseConverter a function for converting units of the base unit to this one 034 * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters") 035 * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds 036 */ 037 @SuppressWarnings("this-escape") 038 protected Unit( 039 Unit baseUnit, 040 UnaryFunction toBaseConverter, 041 UnaryFunction fromBaseConverter, 042 String name, 043 String symbol) { 044 m_baseUnit = baseUnit == null ? this : baseUnit; 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 m_zero = of(0); 051 m_one = of(1); 052 } 053 054 /** 055 * Creates a new unit with the given name and multiplier to the base unit. 056 * 057 * @param baseUnit the base unit, e.g. Meters for distances 058 * @param baseUnitEquivalent the multiplier to convert this unit to the base unit of this type. 059 * For example, meters has a multiplier of 1, mm has a multiplier of 1e3, and km has 060 * multiplier of 1e-3. 061 * @param name the name of the unit. This should be a singular noun (so "Meter", not "Meters") 062 * @param symbol the short symbol for the unit, such as "m" for meters or "lb." for pounds 063 */ 064 protected Unit(Unit baseUnit, double baseUnitEquivalent, String name, String symbol) { 065 this(baseUnit, x -> x * baseUnitEquivalent, x -> x / baseUnitEquivalent, name, symbol); 066 } 067 068 /** 069 * Creates a new immutable measurement of the given magnitude in terms of this unit. 070 * Implementations are <strong>strongly</strong> recommended to sharpen the return type to a 071 * unit-specific measurement implementation. 072 * 073 * @param magnitude the magnitude of the measurement. 074 * @return the measurement object 075 */ 076 public abstract Measure<?> of(double magnitude); 077 078 /** 079 * Creates a new immutable measurement of the given magnitude in terms of this unit's base unit. 080 * Implementations are <strong>strongly</strong> recommended to sharpen the return type to a 081 * unit-specific measurement implementation. 082 * 083 * @param baseUnitMagnitude the magnitude in terms of the base unit 084 * @return the measurement object 085 */ 086 public abstract Measure<?> ofBaseUnits(double baseUnitMagnitude); 087 088 /** 089 * Gets a measure of zero magnitude in terms of this unit. The returned object is guaranteed to be 090 * of the same type returned by {@link #of(double)}. Subclasses are encouraged to override this 091 * method to sharpen the return type. 092 * 093 * @return a zero-magnitude measure of this unit 094 */ 095 public Measure<?> zero() { 096 return m_zero; 097 } 098 099 /** 100 * Gets a measure with a magnitude of 1.0 in terms of this unit. The returned object is guaranteed 101 * to be of the same type returned by {@link #of(double)}. Subclasses are encouraged to override 102 * this method to sharpen the return type. 103 * 104 * @return a measure of magnitude 1.0 in terms of this unit 105 */ 106 public Measure<?> one() { 107 return m_one; 108 } 109 110 /** 111 * Combines this unit with a unit of time. This often - but not always - results in a velocity. 112 * Subclasses should sharpen the return type to be unit-specific. 113 * 114 * @param time the unit of time 115 * @return the combined unit 116 */ 117 public abstract Unit per(TimeUnit time); 118 119 /** 120 * Gets the base unit of measurement that this unit is derived from. If the unit is the base unit, 121 * the unit will be returned. 122 * 123 * <p><strong>NOTE:</strong> Subclasses <strong>must</strong> override this method to provide the 124 * correct return type. Failing to do say will make unit combinations that use it break at 125 * runtime! 126 * 127 * <pre><code> 128 * Unit baseUnit = new Unit(null, ...); 129 * baseUnit.getBaseUnit(); // returns baseUnit 130 * 131 * Unit derivedUnit = new Unit(baseUnit, ...); 132 * derivedUnit.getBaseUnit(); // returns baseUnit 133 * </code></pre> 134 * 135 * @return the base unit 136 */ 137 public Unit getBaseUnit() { 138 return m_baseUnit; 139 } 140 141 /** 142 * Checks if this unit is the base unit for its own system of measurement. 143 * 144 * @return true if this is the base unit, false if not 145 */ 146 public boolean isBaseUnit() { 147 return this.equals(m_baseUnit); 148 } 149 150 /** 151 * Converts a value in terms of base units to a value in terms of this unit. 152 * 153 * @param valueInBaseUnits the value in base units to convert 154 * @return the equivalent value in terms of this unit 155 */ 156 public double fromBaseUnits(double valueInBaseUnits) { 157 return m_fromBaseConverter.apply(valueInBaseUnits); 158 } 159 160 /** 161 * Converts a value in terms of this unit to a value in terms of the base unit. 162 * 163 * @param valueInNativeUnits the value in terms of this unit to convert 164 * @return the equivalent value in terms of the base unit 165 */ 166 public double toBaseUnits(double valueInNativeUnits) { 167 return m_toBaseConverter.apply(valueInNativeUnits); 168 } 169 170 /** 171 * Gets the conversion function used to convert values to base unit terms. This generally 172 * shouldn't need to be used directly; prefer {@link #toBaseUnits(double)} instead. 173 * 174 * @return the conversion function 175 */ 176 public UnaryFunction getConverterToBase() { 177 return m_toBaseConverter; 178 } 179 180 /** 181 * Gets the conversion function used to convert values to terms of this unit. This generally 182 * shouldn't need to be used directly; prefer {@link #fromBaseUnits(double)} instead. 183 * 184 * @return the conversion function 185 */ 186 public UnaryFunction getConverterFromBase() { 187 return m_fromBaseConverter; 188 } 189 190 /** 191 * Checks if this unit is equivalent to another one. Equivalence is determined by both units 192 * having the same base type and treat the same base unit magnitude as the same magnitude in their 193 * own units, to within {@link Measure#EQUIVALENCE_THRESHOLD}. 194 * 195 * @param other the unit to compare to. 196 * @return true if both units are equivalent, false if not 197 */ 198 public boolean equivalent(Unit other) { 199 if (!getClass().equals(other.getClass())) { 200 // different unit types, not compatible 201 return false; 202 } 203 204 double arbitrary = 16_777.214; // 2^24 / 1e3 205 206 return Math.abs( 207 this.m_fromBaseConverter.apply(arbitrary) 208 - other.m_fromBaseConverter.apply(arbitrary)) 209 <= Measure.EQUIVALENCE_THRESHOLD 210 && Math.abs( 211 this.m_toBaseConverter.apply(arbitrary) - other.m_toBaseConverter.apply(arbitrary)) 212 <= Measure.EQUIVALENCE_THRESHOLD; 213 } 214 215 @Override 216 public boolean equals(Object o) { 217 return this == o 218 || o instanceof Unit that 219 && m_name.equals(that.m_name) 220 && m_symbol.equals(that.m_symbol) 221 && this.equivalent(that); 222 } 223 224 @Override 225 public int hashCode() { 226 return Objects.hash(m_toBaseConverter, m_fromBaseConverter, m_name, m_symbol); 227 } 228 229 /** 230 * Gets the name of this unit. 231 * 232 * @return the unit's name 233 */ 234 public String name() { 235 return m_name; 236 } 237 238 /** 239 * Gets the symbol of this unit. 240 * 241 * @return the unit's symbol 242 */ 243 public String symbol() { 244 return m_symbol; 245 } 246 247 @Override 248 public String toString() { 249 return name(); 250 } 251}