All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xtce.math.SplineCalibration Maven / Gradle / Ivy

Go to download

This project contains software to support the Object Management Group (OMG) Space Domain Task Force (SDTF) maintained XML Telemetry and Command Exchange (XTCE) specification.

There is a newer version: 1.1.6
Show newest version
/* Copyright 2017 David Overeem ([email protected])
 * 
 * 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.xtce.math;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.omg.space.xtce.CalibratorType.SplineCalibrator;
import org.omg.space.xtce.SplinePointType;
import org.xtce.toolkit.XTCEDatabaseException;
import org.xtce.toolkit.XTCEFunctions;

/** Class to apply a calibration specified in an XTCE document in the form of
 * a Spline Calibrator.
 *
 * @author David Overeem
 *
 */

public class SplineCalibration implements Calibration {

    /** Constructs a Calibration object based on the SplineCalibrator
     * element in the XTCE document.
     *
     * @param splineElement SplineCalibrator containing the data.
     *
     */

    public SplineCalibration( final SplineCalibrator splineElement ) {

        splineElement_ = splineElement;

    }

    /** Apply this Spline Calibrator, otherwise known as a
     * "piecewise function", to an uncalibrated value.
     *
     * The Spline Point pairs in XTCE are expected to be in sequential order
     * from lowest to highest raw value.  The first two points are mandatory
     * and subsequent points are made by adding one new point and dropping the
     * previous low point.  This assures that the evaluation is continuous.  It
     * is also assumed that they are in order from lowest raw value to highest
     * raw value.
     *
     * @param xValue Number containing the uncalibrated value.
     *
     * @return Number containing the calibrated value after the calibration.
     *
     * @throws XTCEDatabaseException when the Spline Calibrator has unsupported
     * features, the calibrated value is unbounded, or has infinite slope
     * between points.
     *
     */

    @Override
    public Number calibrate( final Number xValue ) throws XTCEDatabaseException {

        final BigInteger            order       = splineElement_.getOrder();
        final boolean               extrapolate = splineElement_.isExtrapolate();
        final List points      = splineElement_.getSplinePoint();

        // TODO: Support quadratics because I did it on the other side

        if ( ( order.intValue() > 1 ) || ( order.intValue() < 0 ) ) {
            throw new XTCEDatabaseException(
                XTCEFunctions.getText( "error_encdec_orderapprox" ) + // NOI18N
                " '" + // NOI18N
                order.toString() +
                "', " + // NOI18N
                XTCEFunctions.getText( "error_encdec_onlylinear" ) ); // NOI18N
        }

        if ( points.size() < 2 ) {
            throw new XTCEDatabaseException( XTCEFunctions.getText( "error_encdec_minpoints" ) ); // NOI18N
        }

        double rawLow  = points.get( 0 ).getRaw();
        double calLow  = points.get( 0 ).getCalibrated();
        double rawHigh = rawLow;
        double calHigh = calLow;

        for ( int iii = 1; iii < points.size(); ++iii ) {

            rawHigh = points.get( iii ).getRaw();
            calHigh = points.get( iii ).getCalibrated();

            if ( ( xValue.doubleValue() >= rawLow  ) &&
                 ( xValue.doubleValue() <= rawHigh ) ) {

                if ( order.intValue() == 0 ) {

                    // if it equals rawHigh, then take the next one as there is
                    // a discontinuity and this is how I handled it
                    if ( xValue.doubleValue() < rawHigh ) {
                        return calLow;
                    }

                } else if ( order.intValue() == 1 ) {

                    if ( rawHigh - rawLow == 0.0 ) {
                        throw new XTCEDatabaseException(
                              XTCEFunctions.getText( "error_encdec_infslope" ) +
                              " " +
                              Double.toString( calLow ) +
                              " " +
                              XTCEFunctions.getText( "general_and" ) +
                              " " +
                              Double.toString( calHigh ) );
                    }

                    double slope = ( calHigh - calLow ) / ( rawHigh - rawLow );
                    double intercept = calLow - ( slope * rawLow );
                    //double slope = ( rawHigh - rawLow ) / ( calHigh - calLow );
                    //System.out.println( "xvalue = " + new Double( xValue ).toString() +
                    //    " slope = " + new Double( slope ).toString() +
                    //    " calLow = " + new Double( calLow ).toString() +
                    //    " calHigh = " + new Double( calHigh ).toString() );
                    return ( slope * xValue.doubleValue() ) + intercept;

                }

            }

            // prepare for next iteration of spline point loop

            rawLow = rawHigh;
            calLow = calHigh;

        } // end of spline point loop

        if ( extrapolate == true ) {
            throw new XTCEDatabaseException( XTCEFunctions.getText( "error_encdec_noextrapolate" ) ); // NOI18N
        }

        // TODO out of bounds case, add extrapolate support

        return calHigh;

    }

    /** Apply this Spline Calibrator to an already calibrated value.
     *
     * The Spline Point pairs in XTCE are expected to be in sequential order
     * from lowest to highest raw value.  The first two points are mandatory
     * and subsequent points are made by adding one new point and dropping the
     * previous low point.  This assures that the evaluation is continuous.  It
     * is also assumed that they are in order from lowest raw value to highest
     * raw value.
     *
     * @param yValue Number containing a calibrated value.
     *
     * @return Number containing the uncalibrated value after the reverse
     * calibration operation.
     *
     * @throws XTCEDatabaseException when the Spline Calibrator has unsupported
     * features, the calibrated value is unbounded, or has infinite slope
     * between points.
     *
     */

    @Override
    public Number uncalibrate( final Number yValue ) throws XTCEDatabaseException {

        final long    interpolateOrder     = splineElement_.getOrder().longValue();
        final boolean extrapolate          = splineElement_.isExtrapolate();
        final List points = splineElement_.getSplinePoint();

        if ( points.size() < 2 ) {
            throw new XTCEDatabaseException( XTCEFunctions.getText( "error_encdec_minpoints" ) ); // NOI18N
        }

        final List calList = new ArrayList<>();
        final List rawList = new ArrayList<>();

        for ( SplinePointType point : points ) {
            calList.add( new BigDecimal( point.getCalibrated() ) );
            rawList.add( new BigDecimal( point.getRaw() ) );
        }

        BigDecimal minCalValue = calList.get( 0 );
        BigDecimal maxCalValue = calList.get( calList.size() - 1 );

        for ( BigDecimal cal : calList ) {
            if ( cal.min( minCalValue ) == cal ) {
                minCalValue = cal;
            }
            if ( cal.max( maxCalValue ) == cal ) {
                maxCalValue = cal;
            }
        }

        BigDecimal calValue = new BigDecimal( yValue.toString() );

        if ( extrapolate == false ) {

            if ( ( calValue.compareTo( minCalValue ) < 0 ) ||
                 ( calValue.compareTo( maxCalValue ) > 0 ) ) {
                throw new XTCEDatabaseException(
                    XTCEFunctions.getText( "error_encdec_noboundval" ) +
                    " '" +
                    yValue.toString() +
                    "'" );
            }

        }

        // shema requires two spline points minimum, so this should never
        // hit a null case where a spline point is not found.
        BigDecimal rawValue1 = null;
        BigDecimal rawValue2 = null;
        BigDecimal calValue1 = null;
        BigDecimal calValue2 = null;

        final Iterator calitr = calList.iterator();
        final Iterator rawitr = rawList.iterator();

        if ( calitr.hasNext() == true ) {
            calValue1 = calitr.next();
            rawValue1 = rawitr.next();
        }

        while ( calitr.hasNext() == true ) {

            if ( calValue2 != null ) {
                calValue1 = calValue2;
                rawValue1 = rawValue2;
            }

            calValue2 = calitr.next();
            rawValue2 = rawitr.next();
            //System.out.println( "Cals: cal1 = " + calValue1.toString() +
            //                    " cal2 = " + calValue2.toString() );

            if ( ( calValue1.compareTo( calValue ) <= 0 ) &&
                 ( calValue2.compareTo( calValue ) >= 0 ) ) {
                if ( yValue.equals( calValue1 ) == true ) {
                    return rawValue1;
                } else if ( yValue.equals( calValue2 ) == true ) {
                    return rawValue2;
                }
                break;
            }
        }

        if ( rawValue1 == null || rawValue2 == null ) {
            throw new XTCEDatabaseException(
                XTCEFunctions.getText( "error_encdec_noextrapolate" ) +
                ", " +
                XTCEFunctions.getText( "error_encdec_neededforval" ) +
                " " +
                yValue.toString() );
        }

        //System.out.println( calValue.toString() +
        //                    " Order = " + Long.toString( interpolateOrder ) +
        //                    " y2 = " + calValue2.toString() +
        //                    " y1 = " + calValue1.toString() +
        //                    " x2 = " + rawValue2.toString() +
        //                    " x1 = " + rawValue2.toString() );

        final double y2 = calValue2.doubleValue();
        final double y1 = calValue1.doubleValue();
        final double x2 = rawValue2.doubleValue();
        final double x1 = rawValue1.doubleValue();

        if ( interpolateOrder == 0 ) {

            return new BigDecimal( ( x1 + x2 ) / 2.0 );

        } else if ( interpolateOrder == 1 ) {

            if ( x2 - x1 == 0.0 ) {
                throw new XTCEDatabaseException(
                      XTCEFunctions.getText( "error_encdec_infslope" ) +
                      " " +
                      Double.toString( y1 ) +
                      " " +
                      XTCEFunctions.getText( "general_and" ) +
                      " " +
                      Double.toString( y2 ) );
            }
            double slope = ( y2 - y1 ) / ( x2 - x1 );
            //System.out.println( "Slope = " + Double.toString( slope ) );
            if ( slope == 0.0 ) {
                // does not matter which since slope is 0
                return new BigDecimal( x1 );
            }
            double rawValue = ( calValue.doubleValue() - y1 ) / slope + x1;
            //System.out.println( "Raw = " + Double.toString( rawValue ) );
            return new BigDecimal( rawValue );

        } else { // interpolation order is not 0 or 1

            throw new XTCEDatabaseException(
                XTCEFunctions.getText( "error_encdec_orderapprox" ) + // NOI18N
                " '" + // NOI18N
                Long.toString( interpolateOrder ) +
                "', " + // NOI18N
                XTCEFunctions.getText( "error_encdec_onlylinear" ) ); // NOI18N

        }

    }

    // Private Data Members

    private final SplineCalibrator splineElement_;

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy