/* * Copyright (c) OSGi Alliance (2002, 2009). All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.osgi.util.measurement; /** * Represents a value with an error, a unit and a time-stamp. * * <p> * A <tt>Measurement</tt> object is used for maintaining the tuple of value, * error, unit and time-stamp. The value and error are represented as doubles * and the time is measured in milliseconds since midnight, January 1, 1970 UTC. * * <p> * Mathematic methods are provided that correctly calculate taking the error * into account. A runtime error will occur when two measurements are used in an * incompatible way. E.g., when a speed (m/s) is added to a distance (m). The * measurement class will correctly track changes in unit during multiplication * and division, always coercing the result to the most simple form. See * {@link Unit} for more information on the supported units. * * <p> * Errors in the measurement class are absolute errors. Measurement errors * should use the P95 rule. Actual values must fall in the range value +/- error * 95% or more of the time. * * <p> * A <tt>Measurement</tt> object is immutable in order to be easily shared. * * <p> * Note: This class has a natural ordering that is inconsistent with equals. See * {@link #compareTo}. * * @version $Revision: 1.1 $ * @author Open Services Gateway Initiative */ public class Measurement implements Comparable { /* package private so it can be accessed by Unit */ final double value; final double error; final long time; final Unit unit; private String name; /** * Create a new <tt>Measurement</tt> object. * * @param value * The value of the <tt>Measurement</tt>. * @param error * The error of the <tt>Measurement</tt>. * @param unit * The <tt>Unit</tt> object in which the value is measured. If * this argument is <tt>null</tt>, then the unit will be set * to {@link Unit#unity}. * @param time * The time measured in milliseconds since midnight, January 1, * 1970 UTC. */ public Measurement(double value, double error, Unit unit, long time) { this.value = value; this.error = Math.abs(error); this.unit = (unit != null) ? unit : Unit.unity; this.time = time; } /** * Create a new <tt>Measurement</tt> object with a time of zero. * * @param value * The value of the <tt>Measurement</tt>. * @param error * The error of the <tt>Measurement</tt>. * @param unit * The <tt>Unit</tt> object in which the value is measured. If * this argument is <tt>null</tt>, then the unit will be set * to {@link Unit#unity}. */ public Measurement(double value, double error, Unit unit) { this(value, error, unit, 0); } /** * Create a new <tt>Measurement</tt> object with an error of 0.0 and a * time of zero. * * @param value * The value of the <tt>Measurement</tt>. * @param unit * The <tt>Unit</tt> in which the value is measured. If this * argument is <tt>null</tt>, then the unit will be set to * {@link Unit#unity}. */ public Measurement(double value, Unit unit) { this(value, 0.0d, unit, 0); } /** * Create a new <tt>Measurement</tt> object with an error of 0.0, a unit * of {@link Unit#unity} and a time of zero. * * @param value * The value of the <tt>Measurement</tt>. */ public Measurement(double value) { this(value, 0.0d, null, 0); } /** * Returns the value of this <tt>Measurement</tt> object. * * @return The value of this <tt>Measurement</tt> object as a double. */ public final double getValue() { return value; } /** * Returns the error of this <tt>Measurement</tt> object. The error is * always a positive value. * * @return The error of this <tt>Measurement</tt> as a double. */ public final double getError() { return error; } /** * Returns the <tt>Unit</tt> object of this <tt>Measurement</tt> object. * * @return The <tt>Unit</tt> object of this <tt>Measurement</tt> object. * * @see Unit */ public final Unit getUnit() { return unit; } /** * Returns the time at which this <tt>Measurement</tt> object was taken. * The time is measured in milliseconds since midnight, January 1, 1970 UTC, * or zero when not defined. * * @return The time at which this <tt>Measurement</tt> object was taken or * zero. */ public final long getTime() { return time; } /** * Returns a new <tt>Measurement</tt> object that is the product of this * object multiplied by the specified object. * * @param m * The <tt>Measurement</tt> object that will be multiplied with * this object. * @return A new <tt>Measurement</tt> that is the product of this object * multiplied by the specified object. The error and unit of the new * object are computed. The time of the new object is set to the * time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be multiplied. * @see Unit */ public Measurement mul(Measurement m) { double mvalue = m.value; return new Measurement(value * mvalue, Math.abs(value) * m.error + error * Math.abs(mvalue), unit.mul(m.unit), time); } /** * Returns a new <tt>Measurement</tt> object that is the product of this * object multiplied by the specified value. * * @param d * The value that will be multiplied with this object. * @param u * The <tt>Unit</tt> of the specified value. * @return A new <tt>Measurement</tt> object that is the product of this * object multiplied by the specified value. The error and unit of * the new object are computed. The time of the new object is set to * the time of this object. * @throws ArithmeticException * If the units of this object and the specified value cannot be * multiplied. * @see Unit */ public Measurement mul(double d, Unit u) { return new Measurement(value * d, error * Math.abs(d), unit.mul(u), time); } /** * Returns a new <tt>Measurement</tt> object that is the product of this * object multiplied by the specified value. * * @param d * The value that will be multiplied with this object. * @return A new <tt>Measurement</tt> object that is the product of this * object multiplied by the specified value. The error of the new * object is computed. The unit and time of the new object is set to * the unit and time of this object. */ public Measurement mul(double d) { return new Measurement(value * d, error * Math.abs(d), unit, time); } /** * Returns a new <tt>Measurement</tt> object that is the quotient of this * object divided by the specified object. * * @param m * The <tt>Measurement</tt> object that will be the divisor of * this object. * @return A new <tt>Measurement</tt> object that is the quotient of this * object divided by the specified object. The error and unit of the * new object are computed. The time of the new object is set to the * time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be divided. * @see Unit */ public Measurement div(Measurement m) { double mvalue = m.value; return new Measurement(value / mvalue, (Math.abs(value) * m.error + error * Math.abs(mvalue)) / (mvalue * mvalue), unit.div(m.unit), time); } /** * Returns a new <tt>Measurement</tt> object that is the quotient of this * object divided by the specified value. * * @param d * The value that will be the divisor of this object. * @param u * The <tt>Unit</tt> object of the specified value. * @return A new <tt>Measurement</tt> that is the quotient of this object * divided by the specified value. The error and unit of the new * object are computed. The time of the new object is set to the * time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be divided. * @see Unit */ public Measurement div(double d, Unit u) { return new Measurement(value / d, error / Math.abs(d), unit.div(u), time); } /** * Returns a new <tt>Measurement</tt> object that is the quotient of this * object divided by the specified value. * * @param d * The value that will be the divisor of this object. * @return A new <tt>Measurement</tt> object that is the quotient of this * object divided by the specified value. The error of the new * object is computed. The unit and time of the new object is set to * the <tt>Unit</tt> and time of this object. */ public Measurement div(double d) { return new Measurement(value / d, error / Math.abs(d), unit, time); } /** * Returns a new <tt>Measurement</tt> object that is the sum of this * object added to the specified object. * * The error and unit of the new object are computed. The time of the new * object is set to the time of this object. * * @param m * The <tt>Measurement</tt> object that will be added with this * object. * @return A new <tt>Measurement</tt> object that is the sum of this and * m. * @see Unit * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be added. */ public Measurement add(Measurement m) { return new Measurement(value + m.value, error + m.error, unit.add(m.unit), time); } /** * Returns a new <tt>Measurement</tt> object that is the sum of this * object added to the specified value. * * @param d * The value that will be added with this object. * @param u * The <tt>Unit</tt> object of the specified value. * @return A new <tt>Measurement</tt> object that is the sum of this * object added to the specified value. The unit of the new object * is computed. The error and time of the new object is set to the * error and time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified value cannot be added. * @see Unit */ public Measurement add(double d, Unit u) { return new Measurement(value + d, error, unit.add(u), time); } /** * Returns a new <tt>Measurement</tt> object that is the sum of this * object added to the specified value. * * @param d * The value that will be added with this object. * @return A new <tt>Measurement</tt> object that is the sum of this * object added to the specified value. The error, unit, and time of * the new object is set to the error, <tt>Unit</tt> and time of * this object. */ public Measurement add(double d) { return new Measurement(value + d, error, unit, time); } /** * Returns a new <tt>Measurement</tt> object that is the subtraction of * the specified object from this object. * * @param m * The <tt>Measurement</tt> object that will be subtracted from * this object. * @return A new <tt>Measurement</tt> object that is the subtraction of * the specified object from this object. The error and unit of the * new object are computed. The time of the new object is set to the * time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be subtracted. * @see Unit */ public Measurement sub(Measurement m) { return new Measurement(value - m.value, error + m.error, unit.sub(m.unit), time); } /** * Returns a new <tt>Measurement</tt> object that is the subtraction of * the specified value from this object. * * @param d * The value that will be subtracted from this object. * @param u * The <tt>Unit</tt> object of the specified value. * @return A new <tt>Measurement</tt> object that is the subtraction of * the specified value from this object. The unit of the new object * is computed. The error and time of the new object is set to the * error and time of this object. * @throws ArithmeticException * If the <tt>Unit</tt> objects of this object and the * specified object cannot be subtracted. * @see Unit */ public Measurement sub(double d, Unit u) { return new Measurement(value - d, error, unit.sub(u), time); } /** * Returns a new <tt>Measurement</tt> object that is the subtraction of * the specified value from this object. * * @param d * The value that will be subtracted from this object. * @return A new <tt>Measurement</tt> object that is the subtraction of * the specified value from this object. The error, unit and time of * the new object is set to the error, <tt>Unit</tt> object and * time of this object. */ public Measurement sub(double d) { return new Measurement(value - d, error, unit, time); } /** * Returns a <tt>String</tt> object representing this <tt>Measurement</tt> * object. * * @return a <tt>String</tt> object representing this <tt>Measurement</tt> * object. */ public String toString() { if (name == null) { StringBuffer sb = new StringBuffer(); sb.append(value); if (error != 0.0d) { sb.append(" +/- "); sb.append(error); } String u = unit.toString(); if (u.length() > 0) { sb.append(" "); sb.append(u); } name = sb.toString(); } return name; } /** * Compares this object with the specified object for order. Returns a * negative integer, zero, or a positive integer if this object is less * than, equal to, or greater than the specified object. * * <p> * Note: This class has a natural ordering that is inconsistent with equals. * For this method, another <tt>Measurement</tt> object is considered * equal if there is some <tt>x</tt> such that * * <pre> * getValue() - getError() <= x <= getValue() + getError() * </pre> * * for both <tt>Measurement</tt> objects being compared. * * @param obj * The object to be compared. * @return A negative integer, zero, or a positive integer if this object is * less than, equal to, or greater than the specified object. * * @throws ClassCastException * If the specified object is not of type <tt>Measurement</tt>. * @throws ArithmeticException * If the unit of the specified <tt>Measurement</tt> object is * not equal to the <tt>Unit</tt> object of this object. */ public int compareTo(Object obj) { if (this == obj) { return 0; } Measurement that = (Measurement) obj; if (!unit.equals(that.unit)) { throw new ArithmeticException("Cannot compare " + this + " and " + that); } if (value == that.value) { return 0; } if (value < that.value) { if ((value + error) >= (that.value - that.error)) { return 0; } else { return -1; } } else { if ((value - error) <= (that.value + that.error)) { return 0; } else { return 1; } } } /** * Returns a hash code value for this object. * * @return A hash code value for this object. */ public int hashCode() { long bits = Double.doubleToLongBits(value + error); return ((int) (bits ^ (bits >>> 32))) ^ unit.hashCode(); } /** * Returns whether the specified object is equal to this object. Two * <tt>Measurement</tt> objects are equal if they have same value, error * and <tt>Unit</tt>. * * <p> * Note: This class has a natural ordering that is inconsistent with equals. * See {@link #compareTo}. * * @param obj * The object to compare with this object. * @return <tt>true</tt> if this object is equal to the specified object; * <tt>false</tt> otherwise. */ public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Measurement)) { return false; } Measurement that = (Measurement) obj; return (value == that.value) && (error == that.error) && unit.equals(that.unit); } }