1   /* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.files.iirv.terms;
18  
19  
20  import org.orekit.errors.OrekitIllegalArgumentException;
21  import org.orekit.errors.OrekitMessages;
22  import org.orekit.files.iirv.IIRVVector;
23  import org.orekit.files.iirv.terms.base.IIRVVectorTerm;
24  import org.orekit.files.iirv.terms.base.LongValuedIIRVTerm;
25  
26  /**
27   * Three-character checksum to validate message.
28   * <p>
29   * Calculated by summing the decimal equivalent of the preceding characters in the line, counting spaces as 0 and
30   * negative signs as 1:
31   * <ul>
32   * <li> 0 through 9 = face value
33   * <li> Minus (-)   = 1
34   * <li> ASCII Space = 0
35   * </ul>
36   * <p>
37   * Valid Values: 000-999
38   *
39   * @author Nick LaFarge
40   * @since 13.0
41   */
42  public class CheckSumTerm extends LongValuedIIRVTerm {
43  
44      /** The length of the IIRV term within the message. */
45      public static final int CHECK_SUM_TERM_LENGTH = 3;
46  
47      /** Regular expression that ensures the validity of string values for this term. */
48      public static final String CHECK_SUM_TERM_PATTERN = "\\d{3}";
49  
50      /**
51       * Constructor.
52       * <p>
53       * See {@link LongValuedIIRVTerm#LongValuedIIRVTerm(String, String, int, boolean)}
54       *
55       * @param value value of the coordinate system term
56       */
57      public CheckSumTerm(final String value) {
58          super(CHECK_SUM_TERM_PATTERN, value.replace(IIRVVector.LINE_SEPARATOR, ""), CHECK_SUM_TERM_LENGTH, false);
59      }
60  
61      /**
62       * Constructor.
63       * <p>
64       * See {@link LongValuedIIRVTerm#LongValuedIIRVTerm(String, long, int, boolean)}
65       *
66       * @param value value of the coordinate system term
67       */
68      public CheckSumTerm(final long value) {
69          super(CHECK_SUM_TERM_PATTERN, value, CHECK_SUM_TERM_LENGTH, false);
70      }
71  
72      /**
73       * Constructs an IIRV checksum from a series of IIRVTerm instances.
74       *
75       * @param terms IIRVTerms to compute checksum
76       * @return newly created CheckSum instance
77       */
78      public static CheckSumTerm fromIIRVTerms(final IIRVVectorTerm<?>... terms) {
79          final String lineString = IIRVTermUtils.iirvTermsToLineString(terms);
80          return new CheckSumTerm(computeChecksum(lineString));
81      }
82  
83      /**
84       * Computes the sum of the decimal equivalent of characters in the line, counting spaces as 0 and
85       * negative signs as 1.
86       *
87       * @param input input string to compute checksum from
88       * @return computed checksum integer value
89       */
90      public static int computeChecksum(final String input) {
91          // Compute the sum based on the characters
92          int sum = 0;
93          for (int i = 0; i < input.length(); i++) {
94              final char c = input.charAt(i);
95              final int valueToAdd;
96              if (Character.isDigit(c)) {
97                  valueToAdd = Character.getNumericValue(c);  // Convert the digit character to its numeric value
98              } else if (c == ' ') {
99                  valueToAdd = 0;  // Space counts as 0
100             } else if (c == '-') {
101                 valueToAdd = 1;  // Sign character counts as 1
102             } else {
103                 throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_TERM_VALUE, c);
104             }
105             sum += valueToAdd;  // Increment the sum
106         }
107         return sum;
108     }
109 
110     /**
111      * Validate a line's embedded checksum value.
112      *
113      * @param line string line of an IIRV message (including checksum as the final three characters)
114      * @return true if the derived and embedded checksum values are equal
115      */
116     public static boolean validateLineCheckSum(final String line) {
117         // Don't include carriage/line returns in checksum
118         final String strippedLine = line.replace(IIRVVector.LINE_SEPARATOR, "");
119 
120         // Separate message from checksum
121         final String message = strippedLine.substring(0, strippedLine.length() - 3);
122         final String checkSum = strippedLine.substring(strippedLine.length() - 3);
123 
124         return CheckSumTerm.computeChecksum(message) == Integer.parseInt(checkSum);
125     }
126 
127     /**
128      * Validate the checksum from a line based on the object's checksum integer value.
129      *
130      * @param line string line of an IIRV message (including checksum as the final three characters)
131      * @return true if the extracted checksum value matches this object's integer value
132      */
133     public boolean validateAgainstLineString(final String line) {
134         final String strippedLine = line.replace(IIRVVector.LINE_SEPARATOR, "");
135         final String message = strippedLine.substring(0, strippedLine.length() - 3);
136         final int computedChecksum = computeChecksum(message);
137         return value() == computedChecksum;
138     }
139 
140 }