001/*
002 *  SI Units
003 *  Copyright (c) 2005-2016, Jean-Marie Dautelle, Werner Keil, V2COM.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
008 *
009 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
010 *
011 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
012 *
013 * 3. Neither the name of JSR-363 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
014 *
015 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
016 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
017 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
018 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
019 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
020 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
021 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
022 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
023 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
024 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
025 */
026package si.uom;
027
028import javax.measure.Quantity;
029import javax.measure.Unit;
030import javax.measure.quantity.Acceleration;
031import javax.measure.quantity.Angle;
032import javax.measure.quantity.Area;
033import javax.measure.quantity.Dimensionless;
034import javax.measure.quantity.Energy;
035import javax.measure.quantity.Length;
036import javax.measure.quantity.Mass;
037
038import si.uom.quantity.Action;
039import si.uom.quantity.DynamicViscosity;
040import si.uom.quantity.ElectricPermittivity;
041import si.uom.quantity.IonizingRadiation;
042import si.uom.quantity.KinematicViscosity;
043import si.uom.quantity.Luminance;
044import si.uom.quantity.MagneticFieldStrength;
045import si.uom.quantity.MagneticPermeability;
046import si.uom.quantity.MagnetomotiveForce;
047import si.uom.quantity.WaveNumber;
048import tec.units.ri.AbstractSystemOfUnits;
049import tec.units.ri.AbstractUnit;
050import tec.units.ri.format.SimpleUnitFormat;
051import tec.units.ri.function.LogConverter;
052import tec.units.ri.function.MultiplyConverter;
053import tec.units.ri.function.PiMultiplierConverter;
054import tec.units.ri.function.RationalConverter;
055import tec.units.ri.unit.AlternateUnit;
056import tec.units.ri.unit.ProductUnit;
057import tec.units.ri.unit.TransformedUnit;
058import tec.units.ri.unit.Units;
059
060/**
061 * <p> This class defines all SI (Système International d'Unités) base units and
062 *     derived units as well as units that are accepted for use with the
063 *     SI units.</p>
064 *
065 * @see <a href="http://en.wikipedia.org/wiki/International_System_of_Units">Wikipedia: International System of Units</a>
066 * @see <a href="http://physics.nist.gov/cuu/Units/outside.html>Units outside the SI that are accepted for use with the SI</a>
067 * @see <a href="http://www.bipm.org/utils/common/pdf/si_brochure_8.pdf>SI 2006 - Official Specification</a>
068 * @see MetricPrefix
069 * 
070 * @noextend This class is not intended to be extended by clients.
071 *
072 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
073 * @author  <a href="mailto:units@catmedia.us">Werner Keil</a>
074 * @version 1.0, October 7, 2016
075*/
076public final class SI extends Units {
077
078    /**
079     * The singleton instance.
080     */
081    private static final SI INSTANCE = new SI();
082
083    /**
084     * Default constructor (prevents this class from being instantiated).
085     */
086    private SI() {
087    }
088
089    /**
090     * Returns the singleton instance of this class.
091     *
092     * @return the metric system instance.
093     */
094    public static SI getInstance() {
095        return INSTANCE;
096    }
097 
098    ////////////////////////////////
099    // SI DERIVED ALTERNATE UNITS //
100    ////////////////////////////////
101
102    /**
103     * The SI unit for magnetomotive force (standard name <code>At</code>).
104     */
105    public static final AlternateUnit<MagnetomotiveForce> AMPERE_TURN
106            = addUnit(new AlternateUnit<MagnetomotiveForce>(SI.AMPERE, "At"), 
107                        MagnetomotiveForce.class);
108    
109    //////////////////////////////
110    // SI DERIVED PRODUCT UNITS //
111    //////////////////////////////
112
113    /**
114     * The SI unit for acceleration quantities (standard name <code>m/s2</code>).
115     */
116    public static final Unit<Acceleration> METRES_PER_SQUARE_SECOND
117            = addUnit(new ProductUnit<Acceleration>(
118            METRES_PER_SECOND.divide(SECOND)), Acceleration.class);
119
120    /**
121     * The SI unit for action quantities (standard name <code>j.s</code>).
122     */
123    public static final Unit<Action> JOULE_SECOND
124            = addUnit(new ProductUnit<Action>(
125            JOULE.multiply(SECOND)), Action.class);
126
127    /**
128     * The SI unit for electric permittivity (standard name <code>ε</code>, <code>F/m </code> or <code>F·m−1</code>).
129     * In electromagnetism, absolute permittivity is the measure of resistance 
130     * that is encountered when forming an electric field in a medium.
131     */
132    public static final Unit<ElectricPermittivity> FARADS_PER_METRE
133            = addUnit(new AlternateUnit<ElectricPermittivity>(
134             FARAD.divide(METRE), "ε"), ElectricPermittivity.class);
135  
136    /**
137     * The SI unit for magnetic permeability quantities (standard name <code>N/A2</code>).
138     */
139    public static final Unit<MagneticPermeability> NEWTONS_PER_SQUARE_AMPERE
140            = addUnit(new ProductUnit<MagneticPermeability>(
141            NEWTON.divide(AMPERE.pow(2))), MagneticPermeability.class);
142
143    /**
144     * The SI unit for wave number quantities (standard name <code>1/m</code>).
145     */
146    public static final Unit<WaveNumber> RECIPROCAL_METRE
147            = addUnit(new ProductUnit<WaveNumber>(
148            METRE.pow(-1)), WaveNumber.class);
149
150    /**
151     * The SI unit for dynamic viscosity quantities (standard name <code>Pa.s</code>).
152     */
153    public static final Unit<DynamicViscosity> PASCAL_SECOND
154            = addUnit(new ProductUnit<DynamicViscosity>(
155            PASCAL.multiply(SECOND)), DynamicViscosity.class);
156
157    /**
158     * Luminance is a photometric measure of the luminous intensity per unit area of light travelling in a given
159     * direction. It describes the amount of light that passes through, is emitted or reflected from a particular 
160     * area, and falls within a given solid angle. The SI unit for luminance is candela per square metre (<code>cd/m2</code>).
161     * @see <a href="https://en.wikipedia.org/wiki/Luminance">
162     *      Wikipedia: Luminance</a>
163     */
164    public static final Unit<Luminance> CANDELA_PER_SQUARE_METRE
165            = addUnit(new ProductUnit<Luminance>(
166            CANDELA.divide(SQUARE_METRE)), Luminance.class);
167
168    /**
169     * The SI unit for kinematic viscosity quantities (standard name <code>m2/s"</code>).
170     */
171    public static final Unit<KinematicViscosity> SQUARE_METRES_PER_SECOND
172            = addUnit(new ProductUnit<KinematicViscosity>(
173            SQUARE_METRE.divide(SECOND)), KinematicViscosity.class);
174
175    /**
176     * The SI unit for magnetic field strength quantities (standard name <code>A/m"</code>).
177     */
178    public static final Unit<MagneticFieldStrength> AMPERES_PER_METRE
179            = addUnit(new ProductUnit<MagneticFieldStrength>(
180            AMPERE.divide(METRE)), MagneticFieldStrength.class);
181
182    /**
183     * The SI unit for ionizing radiation quantities (standard name <code>C/kg"</code>).
184     */
185    public static final Unit<IonizingRadiation> COULOMBS_PER_KILOGRAM
186            = addUnit(new ProductUnit<IonizingRadiation>(
187            COULOMB.divide(KILOGRAM)), IonizingRadiation.class);
188
189    /////////////////////////////////////////////////////////////////
190    // Units outside the SI that are accepted for use with the SI. //
191    /////////////////////////////////////////////////////////////////
192
193    /**
194     * An angle unit accepted for use with SI units (standard name <code>deg/code>).
195     */
196    public static final Unit<Angle> DEGREE_ANGLE
197        = new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(1, 180)));
198
199    /**
200     * An angle unit accepted for use with SI units (standard name <code>'/code>).
201     */
202    public static final Unit<Angle> MINUTE_ANGLE
203        = new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(1, 180 * 60)));
204
205    /**
206     * An angle unit accepted for use with SI units (standard name <code>''</code>).
207     */
208    public static final Unit<Angle> SECOND_ANGLE
209        = new TransformedUnit<Angle>(RADIAN,  new PiMultiplierConverter().concatenate(new RationalConverter(1, 180 * 60 * 60)));
210
211    /**
212     * A mass unit accepted for use with SI units (standard name <code>t</code>).
213     */
214    public static final Unit<Mass> TONNE
215        = AbstractSystemOfUnits.Helper.addUnit(INSTANCE.units, 
216                        new TransformedUnit<Mass>(KILOGRAM, new RationalConverter(1000, 1)),
217                                        "Tonne", "t");
218    /**
219     * A dimensionless unit accepted for use with SI units (standard name <code>Np</code>).
220     * Although the neper is coherent with SI units and is accepted by the CIPM,
221     * it has not been adopted by the General Conference on Weights and Measures
222     * (CGPM, Conférence Générale des Poids et Mesures) and is thus not an SI unit.
223     */
224    public static final Unit<Dimensionless> NEPER
225        = new TransformedUnit<Dimensionless>(AbstractUnit.ONE, new LogConverter(E).inverse());
226
227    /**
228     * A dimensionless unit accepted for use with SI units (standard name <code>B</code>).
229     * The bel is most commonly used with the SI prefix deci: 1 dB = 0.1 B
230     */
231    public static final Unit<Dimensionless> BEL
232        = addUnit(new TransformedUnit<Dimensionless>(AbstractUnit.ONE, new LogConverter(10).inverse()));
233
234    /**
235     * An energy unit accepted for use with SI units (standard name <code>eV</code>).
236     * The electronvolt is the kinetic energy acquired by an electron passing
237     * through a potential difference of 1 V in vacuum. 
238     * The value must be obtained by experiment, and is therefore not known exactly.
239     */
240    public static final Unit<Energy> ELECTRON_VOLT
241        = new TransformedUnit<Energy>(JOULE, new MultiplyConverter(1.602176487E-19));
242        // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
243            
244    /**
245     * A mass unit accepted for use with SI units (standard name <code>u</code>).
246     *  The unified atomic mass unit is equal to 1/12 of the mass of an unbound
247     * atom of the nuclide 12C, at rest and in its ground state. The value must
248     * be obtained by experiment, and is therefore not known exactly.
249     */
250    public static final Unit<Mass> UNIFIED_ATOMIC_MASS
251        = new TransformedUnit<Mass>(KILOGRAM, new MultiplyConverter(1.660538782E-27));
252        // CODATA 2006 - http://physics.nist.gov/cuu/Constants/codata.pdf
253
254    /**
255     * A length unit accepted for use with SI units (standard name <code>UA</code>).
256     * The astronomical unit is a unit of length. Its value is such that,
257     * when used to describe the motion of bodies in the solar system,
258     * the heliocentric gravitation constant is (0.017 202 098 95)2 ua3·d-2.
259     * The value must be obtained by experiment, and is therefore not known exactly.
260     */
261    public static final Unit<Length> ASTRONOMICAL_UNIT
262        = addUnit(new TransformedUnit<Length>(METRE, new MultiplyConverter(149597871000.0)));
263        // Best estimate source: http://maia.usno.navy.mil/NSFA/CBE.html
264    
265    /**
266     *  An angle unit accepted for use with SI units (standard name <code>rev</code>).
267     */
268    public static final Unit<Angle> REVOLUTION
269            = new TransformedUnit<Angle>(RADIAN, new PiMultiplierConverter().concatenate(new RationalConverter(2, 1)));
270
271    /**
272     *  An angle unit accepted for use with SI units (standard name <code>ha</code>).
273     */
274    public static final Unit<Area> HECTARE
275            = new TransformedUnit<Area>(SQUARE_METRE, new RationalConverter(10000, 1));
276
277    /////////////////////
278    // Collection View //
279    /////////////////////
280
281    @Override
282    public String getName() {
283        return "SI";
284    }
285    
286    /**
287     * Adds a new unit not mapped to any specified quantity type.
288     *
289     * @param  unit the unit being added.
290     * @return <code>unit</code>.
291     */
292    private static <U extends Unit<?>>  U addUnit(U unit) {
293        INSTANCE.units.add(unit);
294        return unit;
295    }
296
297    /**
298     * Adds a new unit not mapped to any specified quantity type and puts a text
299     * as symbol or label.
300     *
301     * @param unit
302     *            the unit being added.
303     * @param name
304     *            the string to use as name
305     * @param text
306     *            the string to use as label or symbol
307     * @param isLabel
308     *            if the string should be used as a label or not
309     * @return <code>unit</code>.
310     */
311    private static <U extends Unit<?>> U addUnit(U unit, String name,
312            String text, boolean isLabel) {
313        if (isLabel) {
314            SimpleUnitFormat.getInstance().label(unit, text);
315        }
316        if (name != null && unit instanceof AbstractUnit) {
317            return Helper.addUnit(INSTANCE.units, unit, name);
318        } else {
319            INSTANCE.units.add(unit);
320        }
321        return unit;
322    }
323
324    /**
325     * Adds a new unit not mapped to any specified quantity type and puts a text
326     * as symbol or label.
327     *
328     * @param unit
329     *            the unit being added.
330     * @param text
331     *            the string to use as label or symbol
332     * @param isLabel
333     *            if the string should be used as a label or not
334     * @return <code>unit</code>.
335     */
336    @SuppressWarnings("unused")
337        private static <U extends Unit<?>> U addUnit(U unit, String text,
338            boolean isLabel) {
339        return addUnit(unit, null, text, isLabel);
340    }
341    
342    /**
343     * Adds a new unit and maps it to the specified quantity type.
344     *
345     * @param  unit the unit being added.
346     * @param type the quantity type.
347     * @return <code>unit</code>.
348     */
349    private static <U extends AbstractUnit<?>>  U addUnit(U unit, Class<? extends Quantity<?>> type) {
350        INSTANCE.units.add(unit);
351        INSTANCE.quantityToUnit.put(type, unit);
352        return unit;
353    }
354}