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

org.xtce.math.PolynomialCalibration 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.HashMap;
import java.util.List;
import org.omg.space.xtce.PolynomialType;
import org.xtce.toolkit.XTCEDatabaseException;
import org.xtce.toolkit.XTCEFunctions;
import org.xtce.toolkit.XTCETypedObject.RawType;
import org.xtce.toolkit.XTCEValidRange;

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

public class PolynomialCalibration implements Calibration {

    /** Constructs a Calibration object based on the PolynomialType
     * element in the XTCE document.
     *
     * @param polynomialElement PolynomialType containing the data.
     *
     * @param validRange XTCEValidRange object associated with the typed object
     * so that a "best" uncalibrated value can be deduced during the usage of
     * the uncalibrate() method.
     *
     * @param rawSizeInBits int containing the raw encoding size in bits to
     * help deduce a "best" uncalibrated value when using the uncalibrate()
     * method.
     *
     * @param rawTypeName RawType enumeration containing the raw encoding
     * type to help deduce a "best" uncalibrated value when using the
     * uncalibrate() method.
     *
     */

    public PolynomialCalibration( final PolynomialType polynomialElement,
                                  final XTCEValidRange validRange,
                                  final int            rawSizeInBits,
                                  final RawType        rawTypeName ) {

        polynomialElement_ = polynomialElement;
        validRange_        = validRange;
        rawSizeInBits_     = rawSizeInBits;
        rawTypeName_       = rawTypeName;

    }

    /** Apply this Polynomial Calibrator to an uncalibrated value.
     *
     * This method doesn't care which order the coefficient/exponent terms
     * appears because of the commutative property of addition, so any
     * sequence of terms may be specified in XTCE and this function will apply
     * them as they are specified.
     *
     * This function is not concerned with the encoding type (integer or float)
     * when performing the calculation.  The calculation is always done in the
     * floating point space and if the engineering type is integer, then it
     * will be later rounded back.  This could be a point of controversy.
     *
     * @param xValue Number containing the uncalibrated value.
     *
     * @return Number containing the calibrated value after the calibration.
     *
     * @throws XTCEDatabaseException in the event that the PolynomialCalibrator
     * does not have any terms, which would also be invalid in the XTCE
     * document when the schema is checked against the document.
     *
     */

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

        final List terms = polynomialElement_.getTerm();

        if ( terms.isEmpty() == true ) {
            throw new XTCEDatabaseException( XTCEFunctions.getText( "error_encdec_nopolyterms" ) ); // NOI18N
        }

        double yValue = 0.0;

        for ( PolynomialType.Term term : terms ) {

            final double coeff    = term.getCoefficient();
            final double exponent = term.getExponent().doubleValue();
            final double powTerm  = Math.pow( xValue.doubleValue(), exponent );

            yValue += coeff * powTerm;

        }

        return yValue;

    }

    /** Apply this Polynomial Calibrator to an already calibrated value.
     *
     * This method doesn't care which order the coefficient/exponent terms
     * appears because of the commutative property of addition, so any
     * sequence of terms may be specified in XTCE and this function will apply
     * them as they are specified.
     *
     * This function is not concerned with the encoding type (integer or float)
     * when performing the calculation.  The calculation is always done in the
     * floating point space and if the engineering type is integer, then it
     * will be later rounded back.  This could be a point of controversy.
     *
     * @param yValue Number containing a calibrated value.
     *
     * @return Number containing the uncalibrated value after the reverse
     * calibration operation.
     *
     * @throws XTCEDatabaseException in the event that the PolynomialCalibrator
     * does not have any terms, which would also be invalid in the XTCE
     * document when the schema is checked against the document.  The exception
     * can also be thrown in the event that there are no real roots for a
     * quadratic uncalibrate or the polynomial has a higher order than a
     * quadratic, which is not currently supported by this toolkit.
     *
     */

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

        final HashMap terms = new HashMap<>();

        final List xtceTerms = polynomialElement_.getTerm();

        if ( xtceTerms.isEmpty() == true ) {
            throw new XTCEDatabaseException( XTCEFunctions.getText( "error_encdec_nopolyterms" ) ); // NOI18N
        }

        long maxExponent = 0;

        for ( PolynomialType.Term term : xtceTerms ) {
            if ( term.getCoefficient() != 0.0 ) {
                terms.put( term.getExponent(), new BigDecimal( term.getCoefficient() ) );
                if ( term.getExponent().longValue() > maxExponent ) {
                    maxExponent = term.getExponent().longValue();
                }
            }
        }

        if ( maxExponent <= 1 ) {

            double value = yValue.doubleValue();

            if ( terms.containsKey( BigInteger.ZERO ) == true ) {
                value -= terms.get( BigInteger.ZERO ).doubleValue();
            }

            if ( terms.containsKey( BigInteger.ONE ) == true ) {
                value /= terms.get( BigInteger.ONE ).doubleValue();
            }

            return value;

        } else if ( maxExponent == 2 ) {

            final BigInteger inttwo = new BigInteger( "2" ); // NOI18N
            // evaluate b^2 -4ac to determine if roots exist
            double aaa = 0.0;
            double bbb = 0.0;
            double ccc = -1.0 * yValue.doubleValue();

            if ( terms.containsKey( BigInteger.ZERO ) == true ) {
                ccc += terms.get( BigInteger.ZERO ).doubleValue();
            }

            if ( terms.containsKey( BigInteger.ONE ) == true ) {
                bbb = terms.get( BigInteger.ONE ).doubleValue();
            }

            if ( terms.containsKey( inttwo ) == true ) {
                aaa = terms.get( inttwo ).doubleValue();
            }

            double discriminant = Math.pow( bbb, 2 ) - ( 4.0 * aaa * ccc );

            if ( discriminant < 0 ) {

                throw new XTCEDatabaseException(
                    XTCEFunctions.getText( "error_encdec_norealroots" ) + // NOI18N
                    " '" + // NOI18N
                    yValue.toString() +
                    "'" ); // NOI18N

            } else {

                double posroot = Math.sqrt( discriminant );
                double root1   = ( ( bbb * -1.0 ) - posroot ) / ( 2.0 * aaa );
                double root2   = ( ( bbb * -1.0 ) + posroot ) / ( 2.0 * aaa );
                //System.out.println( "Root1 = " + Double.toString( root1 ) + " Root2 = " + Double.toString( root2 ) );

                return findBestRoot( root1, root2 );

            }

        } else {

            throw new XTCEDatabaseException(
                XTCEFunctions.getText( "error_encdec_maxexponent" ) + // NOI18N
                " (" + // NOI18N
                Long.toString( maxExponent ) +
                ")" ); // NOI18N

        }

    }

    private double findBestRoot( double root1, double root2 )
        throws XTCEDatabaseException {

        // function is never called if BOTH are invalid since we checked the
        // discriminant earlier.

        if ( ( true  == Double.isNaN( root1 )    ) ||
             ( root1 == Double.POSITIVE_INFINITY ) ||
             ( root1 == Double.NEGATIVE_INFINITY ) ) {
            root1 = root2;
        }

        if ( ( true  == Double.isNaN( root2 )    ) ||
             ( root2 == Double.POSITIVE_INFINITY ) ||
             ( root2 == Double.NEGATIVE_INFINITY ) ) {
            root2 = root1;
        }

        if ( rawTypeName_ == RawType.unsigned ) {
            // TODO worry about inclusive versus exclusive
            long minValue = 0;
            long maxValue = (long)Math.pow( 2, rawSizeInBits_  ) - 1;
            if ( ( validRange_.isValidRangeApplied()  == true  ) &&
                 ( validRange_.isLowValueCalibrated() == false ) ) {
                minValue = Math.round( Double.parseDouble( validRange_.getLowValue() ) );
            }
            if ( ( validRange_.isValidRangeApplied()   == true  ) &&
                 ( validRange_.isHighValueCalibrated() == false ) ) {
                maxValue = Math.round( Double.parseDouble( validRange_.getHighValue() ) );
            }
            if ( ( root1 >= minValue ) && ( root1 <= maxValue ) ) {
                return root1;
            } else if ( ( root2 >= minValue ) && ( root2 <= maxValue ) ) {
                return root2;
            } else {
                throw new XTCEDatabaseException(
                      XTCEFunctions.getText( "error_encdec_polyroots" ) + // NOI18N
                      Double.toString( root1 ) +
                      " " + // NOI18N
                      XTCEFunctions.getText( "general_and" ) + // NOI18N
                      " " + // NOI18N
                      Double.toString( root2 ) +
                      ", " + // NOI18N
                      XTCEFunctions.getText( "error_encdec_notinrange" ) + // NOI18N
                      " ( " + // NOI18N
                      Long.toString( minValue ) +
                      ", " + // NOI18N
                      Long.toString( maxValue ) +
                      " ) " + // NOI18N
                      XTCEFunctions.getText( "error_encdec_forrawtype" ) ); // NOI18N
            }
        } else if ( ( rawTypeName_ == RawType.signMagnitude  ) ||
                    ( rawTypeName_ == RawType.twosComplement ) ||
                    ( rawTypeName_ == RawType.onesComplement ) ) {
            // TODO worry about inclusive versus exclusive
            long minValue = -1 * (long)Math.pow( 2, ( rawSizeInBits_ - 1 ) );
            long maxValue = (long)Math.pow( 2, ( rawSizeInBits_ - 1 ) ) - 1;
            if ( ( validRange_.isValidRangeApplied()  == true  ) &&
                 ( validRange_.isLowValueCalibrated() == false ) ) {
                minValue = Math.round( Double.parseDouble( validRange_.getLowValue() ) );
            }
            if ( ( validRange_.isValidRangeApplied()   == true  ) &&
                 ( validRange_.isHighValueCalibrated() == false ) ) {
                maxValue = Math.round( Double.parseDouble( validRange_.getHighValue() ) );
            }
            if ( ( root1 >= minValue ) && ( root1 <= maxValue ) ) {
                return root1;
            } else if ( ( root2 >= minValue ) && ( root2 <= maxValue ) ) {
                return root2;
            } else {
                throw new XTCEDatabaseException(
                      XTCEFunctions.getText( "error_encdec_polyroots" ) + // NOI18N
                      Double.toString( root1 ) +
                      " " + // NOI18N
                      XTCEFunctions.getText( "general_and" ) + // NOI18N
                      " " + // NOI18N
                      Double.toString( root2 ) +
                      ", " + // NOI18N
                      XTCEFunctions.getText( "error_encdec_notinrange" ) + // NOI18N
                      " ( " + // NOI18N
                      Long.toString( minValue ) +
                      ", " + // NOI18N
                      Long.toString( maxValue ) +
                      " ) " + // NOI18N
                      XTCEFunctions.getText( "error_encdec_forrawtype" ) ); // NOI18N
            }
        }

        return root1;

    }

    // Private Data Members

    private final PolynomialType polynomialElement_;
    private final XTCEValidRange validRange_;
    private final int            rawSizeInBits_;
    private final RawType        rawTypeName_;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy