001    package util.wavelet;
002    import java.util.List;
003    import java.util.Collections;
004    
005    /** A wrapper for an array of signal values and their coordinate.
006     * @author John Talbot
007     */
008    public class SignalAndCoordinate extends Signal {
009    
010        /** Coordinates stored as a list permits the use of {@link Collections#binarySearch}. */
011        java.util.List<Float> coordinates;
012        
013        /** 
014         * Construct using the given signal array, noise array and coordinate positions.
015         *
016         * @param   value            signal value array
017         * @param   noise            the noise array
018         * @param   noiseMultiplier  noise multiplier (when sigmas = 3.0 confidence = 99.9 percent)
019         * @param   coordinates      x positions
020         */
021        public SignalAndCoordinate(float[] value, float[] noise, float noiseMultiplier, java.util.List<Float> coordinates) {
022            super(value, noise, noiseMultiplier);
023            this.coordinates = coordinates;
024        }
025    
026        /** 
027         * Get the x coordinate for the given position. The coordinate is read-only.
028         *
029         * @param   position   the position for which the coordinate is sought
030         * @return  the x coordinate at the given position
031         */
032        public float getCoordinate(int position) {
033            return coordinates.get(position);
034        }
035        
036        /* 
037         * Get the interpolated or extrapolated signal value at the given coordinate.
038         *
039         * @param coordinate the coordinate (must be within range)
040         * @return the interpolated value at coordinate
041         */
042        public float getValueAtCoordinate(float coordinate) {
043            int position = Collections.binarySearch(coordinates, coordinate);
044            if (position >= 0 && position < size()) {
045                // TODO: second order approximation using float coordinate0 = getCoordinate(position-1);
046                float coordinate1 = getCoordinate(position);
047                float coordinate2 = getCoordinate(position + 1);  
048                float value1 = getValue(position);
049                float value2 = getValue(position + 1);
050                if (coordinate1 == coordinate2) return value1;   // pre-condition failure (should not happen) TODO: throw exception here
051                else return (value2 - value1) / (coordinate2 - coordinate1) / (coordinate - coordinate1) + value1;
052            } else { // out of range coordinate
053                if (position == -1)
054                    return getValue(0);            // TODO: extrapolation
055                else if (position == size())
056                    return getValue(size() - 1);   // TODO: extrapolation
057                else
058                    return 0f;  // should never happen
059            }
060        }
061    }