DoubleValuedIIRVTerm.java
- /* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You 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.orekit.files.iirv.terms.base;
- import org.hipparchus.util.ArithmeticUtils;
- import org.hipparchus.util.FastMath;
- import org.orekit.errors.OrekitIllegalArgumentException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.files.iirv.terms.IIRVTermUtils;
- import java.util.Locale;
- import java.util.regex.Pattern;
- /**
- * Term in an IIRV Vector representing a double value.
- *
- * @author Nick LaFarge
- * @since 13.0
- */
- public class DoubleValuedIIRVTerm extends IIRVVectorTerm<Double> {
- /** Space pattern. */
- private static final Pattern SPACE_PATTERN = Pattern.compile(" ");
- /**
- * Number of characters before the end of the encoded String the decimal place is assumed to occur.
- * <p>
- * For example, for {@code nCharsAfterDecimalPlace=2} and {@code length=4}, then 12.34
- * is encoded to "1234".
- */
- private final int nCharsAfterDecimalPlace;
- /** True if negative values are permitted, false if the value is positive. */
- private final boolean isSigned;
- /**
- * Constructs an IIRV Vector Term represented by a double. This representation is used for any numeric terms
- * in the IIRV Vector that contain a decimal point.
- *
- * @param pattern Regular expression pattern that validates the term
- * @param value Value of the term, expressed as a double
- * @param length Length of the term, measured in number of characters in the String representation
- * @param nCharsAfterDecimalPlace Number of characters before the end of the encoded String the decimal place is
- * assumed to occur.
- * @param isSigned True if negative values are permitted, false if the value is positive
- */
- public DoubleValuedIIRVTerm(final String pattern, final double value, final int length, final int nCharsAfterDecimalPlace, final boolean isSigned) {
- super(pattern, value, length);
- this.nCharsAfterDecimalPlace = nCharsAfterDecimalPlace;
- this.isSigned = isSigned;
- // Validate input data
- validateString(toEncodedString(value));
- validateOverflow(this.value());
- }
- /**
- * Constructs an IIRV Vector Term represented by a double from a given String. This representation is used for any
- * numeric terms in the IIRV Vector that contain a decimal point.
- *
- * @param pattern Regular expression pattern that validates the term
- * @param value Value of the term, expressed as a String
- * @param length Length of the term, measured in number of characters in the String representation
- * @param nCharsAfterDecimalPlace Number of characters before the end of {@code value} the decimal place is
- * assumed to occur.
- * @param isSigned True if negative values are permitted, false if the value is positive
- */
- public DoubleValuedIIRVTerm(final String pattern, final String value, final int length, final int nCharsAfterDecimalPlace, final boolean isSigned) {
- super(pattern, DoubleValuedIIRVTerm.computeValueFromString(value, nCharsAfterDecimalPlace), length);
- this.nCharsAfterDecimalPlace = nCharsAfterDecimalPlace;
- this.isSigned = isSigned;
- // Validate input data
- validateString(value);
- validateOverflow(this.value());
- }
- /**
- * Compute the double value of the term from a given String.
- *
- * @param value String value to convert to a double
- * @param nCharsAfterDecimalPlace Number of characters before the end of {@code value} the decimal place is
- * assumed to occur.
- * @return Double value corresponding to the {@code value} String argument
- */
- public static double computeValueFromString(final String value, final int nCharsAfterDecimalPlace) {
- try {
- String intStr = value;
- // Remove spaces (for positive values)
- intStr = SPACE_PATTERN.matcher(intStr).replaceAll("");
- // Return if there are no characters after the decimal place
- if (nCharsAfterDecimalPlace == 0) {
- return Integer.parseInt(intStr);
- }
- // Get the sign: negative if the first character is '-'
- final int sign = intStr.charAt(0) == '-' ? -1 : 1;
- // Get value before/after the decimal place
- final int beforeDecimalPlace = Integer.parseInt(intStr.substring(0, intStr.length() - nCharsAfterDecimalPlace));
- final int afterDecimalPlace = Integer.parseInt(intStr.substring(intStr.length() - nCharsAfterDecimalPlace));
- // Turn into a double by dividing the n numbers that appear after the decimal places by 10^n
- final double unsignedValue = FastMath.abs(beforeDecimalPlace) +
- afterDecimalPlace / (double) ArithmeticUtils.pow(10, nCharsAfterDecimalPlace);
- // Return the resulting double with the correct sign
- return sign * unsignedValue;
- } catch (NumberFormatException e) {
- throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_TERM_VALUE, value);
- }
- }
- /** {@inheritDoc} */
- @Override
- public String toEncodedString(final Double termValue) {
- // Reserve one character for the sign (if applicable)
- final int signAdjustedStringLength = isSigned ? length() - 1 : length();
- // Round the number to the specified number of decimal places
- final double p = ArithmeticUtils.pow(10, nCharsAfterDecimalPlace); // beware, this *must* be a double
- final double roundedNum = FastMath.round(termValue * p) / p;
- // Format the absolute value of the rounded number with specified integer and decimal lengths
- String formattedStr = String.format(Locale.US,
- "%0" + signAdjustedStringLength + "." + nCharsAfterDecimalPlace + "f",
- FastMath.abs(roundedNum));
- // Remove the decimal point
- formattedStr = formattedStr.replace(".", "");
- // Add leading zeros for cases where the number has less than the max number of integer digits
- if (formattedStr.length() < signAdjustedStringLength) {
- formattedStr = IIRVTermUtils.addPadding(formattedStr, '0', signAdjustedStringLength, true);
- }
- // If the resulting String is all zero, then always use a positive sign to avoid encoding negative zero
- final int numNonzeroCharacters = formattedStr.replace("0", "").length();
- // Sign character ("" for unsigned number)
- String signCharacter = "";
- if (isSigned) {
- if (termValue >= 0 || numNonzeroCharacters == 0) {
- signCharacter = " ";
- } else {
- signCharacter = "-";
- }
- }
- return signCharacter + formattedStr;
- }
- /**
- * Validate that there is a sufficient number of characters available in the encoded String representation to
- * represent the value of the given double value.
- *
- * @param value Double value to check against the String encoding parameters
- */
- void validateOverflow(final double value) {
- // Compute the number of characters, excluding the sign character and all characters after the decimal place
- int n = length() - nCharsAfterDecimalPlace;
- if (isSigned) {
- n--;
- }
- // If the value is greater than the maximum possible value, throw an error
- final double maxPossibleValue = ArithmeticUtils.pow(10, n);
- if (FastMath.abs(value) >= maxPossibleValue) {
- throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_VALUE_TOO_LARGE, FastMath.abs(value), maxPossibleValue);
- }
- }
- }