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;
18  
19  import org.hipparchus.geometry.euclidean.threed.Vector3D;
20  import org.orekit.annotation.DefaultDataContext;
21  import org.orekit.data.DataContext;
22  import org.orekit.errors.OrekitIllegalArgumentException;
23  import org.orekit.errors.OrekitInternalError;
24  import org.orekit.errors.OrekitMessages;
25  import org.orekit.files.iirv.terms.CheckSumTerm;
26  import org.orekit.files.iirv.terms.CoordinateSystemTerm;
27  import org.orekit.files.iirv.terms.CrossSectionalAreaTerm;
28  import org.orekit.files.iirv.terms.DataSourceTerm;
29  import org.orekit.files.iirv.terms.DayOfYearTerm;
30  import org.orekit.files.iirv.terms.DragCoefficientTerm;
31  import org.orekit.files.iirv.terms.IIRVTermUtils;
32  import org.orekit.files.iirv.terms.MassTerm;
33  import org.orekit.files.iirv.terms.MessageClassTerm;
34  import org.orekit.files.iirv.terms.MessageEndConstantTerm;
35  import org.orekit.files.iirv.terms.MessageIDTerm;
36  import org.orekit.files.iirv.terms.MessageSourceTerm;
37  import org.orekit.files.iirv.terms.MessageStartConstantTerm;
38  import org.orekit.files.iirv.terms.MessageTypeTerm;
39  import org.orekit.files.iirv.terms.OriginIdentificationTerm;
40  import org.orekit.files.iirv.terms.OriginatorRoutingIndicatorTerm;
41  import org.orekit.files.iirv.terms.PositionVectorComponentTerm;
42  import org.orekit.files.iirv.terms.RoutingIndicatorTerm;
43  import org.orekit.files.iirv.terms.SequenceNumberTerm;
44  import org.orekit.files.iirv.terms.SolarReflectivityCoefficientTerm;
45  import org.orekit.files.iirv.terms.SpareConstantTerm;
46  import org.orekit.files.iirv.terms.SupportIdCodeTerm;
47  import org.orekit.files.iirv.terms.TransferTypeConstantTerm;
48  import org.orekit.files.iirv.terms.VectorEpochTerm;
49  import org.orekit.files.iirv.terms.VectorTypeTerm;
50  import org.orekit.files.iirv.terms.VehicleIdCodeTerm;
51  import org.orekit.files.iirv.terms.VelocityVectorComponentTerm;
52  import org.orekit.frames.Frame;
53  import org.orekit.time.AbsoluteDate;
54  import org.orekit.time.UTCScale;
55  import org.orekit.utils.PVCoordinates;
56  import org.orekit.utils.TimeStampedPVCoordinates;
57  
58  import java.util.ArrayList;
59  import java.util.Arrays;
60  import java.util.List;
61  import java.util.Objects;
62  import java.util.regex.Pattern;
63  
64  /**
65   * A vector containing ephemeris state, epoch, and metadata information that, when taken as a series,
66   * comprises an {@link IIRVMessage object}.
67   * <p>
68   * See {@link IIRVMessage} for documentation on the structure of a single IIRV vector within an IIRV message body.
69   *
70   * @author Nick LaFarge
71   * @see IIRVMessage
72   * @see IIRVEphemerisFile.IIRVEphemeris
73   * @since 13.0
74   */
75  public class IIRVVector implements Comparable<IIRVVector> {
76  
77      /** Line separator: two ASCII carriage returns and two ASCII line feeds. */
78      public static final String LINE_SEPARATOR = "\r\r\n\n";
79  
80  
81      /** For creating from a list of Strings, OK to omit the line separators. */
82      private static final String LINE_SEPARATOR_PATTERN = "(" + LINE_SEPARATOR + ")?";
83  
84      /**
85       * Regular expression for validating for line 1 when message metadata is included.
86       * <p>
87       * Message metadata fields refer to the first four terms defined for an IIRV vector:
88       * <ul>
89       *     <li>{@link MessageTypeTerm}</li>
90       *     <li>{@link MessageIDTerm}</li>
91       *     <li>{@link MessageSourceTerm}</li>
92       *     <li>{@link MessageClassTerm}</li>
93       * </ul>
94       */
95      public static final Pattern LINE_1_PATTERN_METADATA_INCLUDED = Pattern.compile("^" + MessageTypeTerm.MESSAGE_TYPE_TERM_PATTERN + MessageIDTerm.MESSAGE_ID_TERM_PATTERN +
96          MessageSourceTerm.MESSAGE_SOURCE_TERM_PATTERN + MessageClassTerm.MESSAGE_CLASS_TERM_PATTERN + MessageStartConstantTerm.MESSAGE_START_TERM_STRING +
97          OriginIdentificationTerm.ORIGIN_IDENTIFICATION_TERM_PATTERN + RoutingIndicatorTerm.ROUTING_INDICATOR_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
98  
99      /**
100      * Regular expression for validating for line 1 when message metadata is omitted.
101      * <p>
102      * Message metadata fields refer to the first four terms defined for an IIRV vector:
103      * <ul>
104      *     <li>{@link MessageTypeTerm}</li>
105      *     <li>{@link MessageIDTerm}</li>
106      *     <li>{@link MessageSourceTerm}</li>
107      *     <li>{@link MessageClassTerm}</li>
108      * </ul>
109      */
110     public static final Pattern LINE_1_PATTERN_METADATA_OMITTED = Pattern.compile("^" + MessageStartConstantTerm.MESSAGE_START_TERM_STRING + OriginIdentificationTerm.ORIGIN_IDENTIFICATION_TERM_PATTERN + RoutingIndicatorTerm.ROUTING_INDICATOR_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
111 
112     /** Regular expression for validating for line 2. */
113     public static final Pattern LINE_2_PATTERN = Pattern.compile("^" + VectorTypeTerm.VECTOR_TYPE_TERM_PATTERN + DataSourceTerm.DATA_SOURCE_TERM_PATTERN + TransferTypeConstantTerm.TRANSFER_TYPE_TERM_STRING + CoordinateSystemTerm.COORDINATE_SYSTEM_TERM_PATTERN + SupportIdCodeTerm.SUPPORT_ID_TERM_PATTERN + VehicleIdCodeTerm.VEHICLE_ID_TERM_PATTERN + SequenceNumberTerm.SEQUENCE_NUMBER_TERM_PATTERN + DayOfYearTerm.DAY_OF_YEAR_PATTERN + VectorEpochTerm.VECTOR_EPOCH_TERM_PATTERN + CheckSumTerm.CHECK_SUM_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
114 
115     /** Regular expression for validating for line 3. */
116     public static final Pattern LINE_3_PATTERN = Pattern.compile("^" + PositionVectorComponentTerm.POSITION_VECTOR_COMPONENT_TERM_PATTERN + PositionVectorComponentTerm.POSITION_VECTOR_COMPONENT_TERM_PATTERN + PositionVectorComponentTerm.POSITION_VECTOR_COMPONENT_TERM_PATTERN + CheckSumTerm.CHECK_SUM_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
117 
118     /** Regular expression for validating for line 4. */
119     public static final Pattern LINE_4_PATTERN = Pattern.compile("^" + VelocityVectorComponentTerm.VELOCITY_VECTOR_COMPONENT_TERM_PATTERN + VelocityVectorComponentTerm.VELOCITY_VECTOR_COMPONENT_TERM_PATTERN + VelocityVectorComponentTerm.VELOCITY_VECTOR_COMPONENT_TERM_PATTERN + CheckSumTerm.CHECK_SUM_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
120 
121     /** Regular expression for validating for line 5. */
122     public static final Pattern LINE_5_PATTERN = Pattern.compile("^" + MassTerm.MASS_TERM_PATTERN + CrossSectionalAreaTerm.CROSS_SECTIONAL_AREA_TERM_PATTERN + DragCoefficientTerm.DRAG_COEFFICIENT_TERM_PATTERN + SolarReflectivityCoefficientTerm.SOLAR_REFLECTIVITY_COEFFICIENT_TERM_PATTERN + CheckSumTerm.CHECK_SUM_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
123 
124     /** Regular expression for validating for line 6. */
125     public static final Pattern LINE_6_PATTERN = Pattern.compile("^" + MessageEndConstantTerm.MESSAGE_END_TERM_STRING + SpareConstantTerm.SPARE_TERM_STRING + OriginatorRoutingIndicatorTerm.ORIGINATOR_ROUTING_INDICATOR_TERM_PATTERN + LINE_SEPARATOR_PATTERN);
126 
127     /** Line 1: Message Type. */
128     private final MessageTypeTerm messageType;
129 
130     /** Line 1: Message Identification. */
131     private final MessageIDTerm messageID;
132 
133     /** Line 1: Message Source. */
134     private final MessageSourceTerm messageSource;
135 
136     /** Line 1: Message class. */
137     private final MessageClassTerm messageClass;
138 
139     /** Line 1: Message Start. */
140     private final MessageStartConstantTerm messageStart;
141 
142     /** Line 1: Origin identification. */
143     private final OriginIdentificationTerm originIdentification;
144 
145     /** Line 1: Destination routing indicator. */
146     private final RoutingIndicatorTerm routingIndicator;
147 
148     /** Line 2: Vector type. */
149     private final VectorTypeTerm vectorType;
150 
151     /** Line 2: Source of data. */
152     private final DataSourceTerm dataSource;
153 
154     /** Line 2: Transfer Type. */
155     private final TransferTypeConstantTerm transferType;
156 
157     /** Line 2: Transfer Type. */
158     private final CoordinateSystemTerm coordinateSystem;
159 
160     /** Line 2: Support ID Code. */
161     private final SupportIdCodeTerm supportIdCode;
162 
163     /** Line 2: Vehicle ID Code. */
164     private final VehicleIdCodeTerm vehicleIdCode;
165 
166     /** Line 2: Sequence number. */
167     private final SequenceNumberTerm sequenceNumber;
168 
169     /** Line 2: Day of year (1-366). */
170     private final DayOfYearTerm dayOfYear;
171 
172     /** Line 2: Transfer Type. */
173     private final VectorEpochTerm vectorEpoch;
174 
175     /** Line 2: Check Sum. */
176     private final CheckSumTerm line2CheckSum;
177 
178     /** Line 3: x component of the position vector [m]. */
179     private final PositionVectorComponentTerm xPosition;
180 
181     /** Line 3: y component of the position vector [m]. */
182     private final PositionVectorComponentTerm yPosition;
183 
184     /** Line 3: z component of the position vector [m]. */
185     private final PositionVectorComponentTerm zPosition;
186 
187     /** Line 3: Check Sum. */
188     private final CheckSumTerm line3CheckSum;
189 
190     /** Line 3: x component of the velocity vector [m/s]. */
191     private final VelocityVectorComponentTerm xVelocity;
192 
193     /** Line 3: y component of the position vector [m/s]. */
194     private final VelocityVectorComponentTerm yVelocity;
195 
196     /** Line 3: z component of the position vector [m/s]. */
197     private final VelocityVectorComponentTerm zVelocity;
198 
199     /** Line 4: Check Sum. */
200     private final CheckSumTerm line4CheckSum;
201 
202     /** Line 5: Satellite mass [kg]. */
203     private final MassTerm mass;
204 
205     /** Line 5: Average satellite cross-sectional area [m^2]. */
206     private final CrossSectionalAreaTerm crossSectionalArea;
207 
208     /** Line 5: Drag coefficient [dimensionless]. */
209     private final DragCoefficientTerm dragCoefficient;
210 
211     /** Line 5: Solar reflectivity coefficient [dimensionless]. */
212     private final SolarReflectivityCoefficientTerm solarReflectivityCoefficient;
213 
214     /** Line 5: Check Sum. */
215     private final CheckSumTerm line5CheckSum;
216 
217     /** Line 6: End of message (always ITERM). */
218     private final MessageEndConstantTerm endOfMessage;
219 
220     /** Line 6: Spare term (always ASCII space). */
221     private final SpareConstantTerm spareTerm;
222 
223     /** Line 6: originator of message (GCQU or GAQD). */
224     private final OriginatorRoutingIndicatorTerm originatorRoutingIndicatorTerm;
225 
226     /** UTC time scale. */
227     private final UTCScale utc;
228 
229     /**
230      * Constructs an IIRV message from its composite terms.
231      *
232      * @param messageType                    Message type
233      * @param messageID                      Message identification
234      * @param messageSource                  Message source
235      * @param messageClass                   Message class
236      * @param originIdentification           Origin identification
237      * @param routingIndicator               Destination routing indicator
238      * @param vectorType                     Vector type
239      * @param dataSource                     Data source
240      * @param coordinateSystem               Coordinate system
241      * @param supportIdCode                  Support identification code
242      * @param vehicleIdCode                  Vehicle identification code
243      * @param sequenceNumber                 Sequence number
244      * @param dayOfYear                      Day of year
245      * @param vectorEpoch                    Vector epoch
246      * @param xPosition                      X component of the position vector [m]
247      * @param yPosition                      Y component of the position vector [m]
248      * @param zPosition                      Z component of the position vector [m]
249      * @param xVelocity                      X component of the velocity vector [m/s]
250      * @param yVelocity                      Y component of the velocity vector [m/s]
251      * @param zVelocity                      Z component of the velocity vector [m/s]
252      * @param mass                           Satellite mass (kg)
253      * @param crossSectionalArea             Average satellite cross-sectional area [m^2]
254      * @param dragCoefficient                Drag coefficient [dimensionless]
255      * @param solarReflectivityCoefficient   Solar reflectivity coefficient [dimensionless]
256      * @param originatorRoutingIndicatorTerm Originator of message (GCQU or GAQD)
257      * @param utc                            UTC time scale
258      */
259     public IIRVVector(final MessageTypeTerm messageType, final MessageIDTerm messageID, final MessageSourceTerm messageSource, final MessageClassTerm messageClass, final OriginIdentificationTerm originIdentification, final RoutingIndicatorTerm routingIndicator, final VectorTypeTerm vectorType, final DataSourceTerm dataSource, final CoordinateSystemTerm coordinateSystem, final SupportIdCodeTerm supportIdCode, final VehicleIdCodeTerm vehicleIdCode, final SequenceNumberTerm sequenceNumber, final DayOfYearTerm dayOfYear, final VectorEpochTerm vectorEpoch, final PositionVectorComponentTerm xPosition, final PositionVectorComponentTerm yPosition, final PositionVectorComponentTerm zPosition, final VelocityVectorComponentTerm xVelocity, final VelocityVectorComponentTerm yVelocity, final VelocityVectorComponentTerm zVelocity, final MassTerm mass, final CrossSectionalAreaTerm crossSectionalArea, final DragCoefficientTerm dragCoefficient, final SolarReflectivityCoefficientTerm solarReflectivityCoefficient, final OriginatorRoutingIndicatorTerm originatorRoutingIndicatorTerm, final UTCScale utc) {
260 
261         // Line 1
262         this.messageType = messageType;
263         this.messageID = messageID;
264         this.messageSource = messageSource;
265         this.messageClass = messageClass;
266         this.messageStart = new MessageStartConstantTerm();
267         this.originIdentification = originIdentification;
268         this.routingIndicator = routingIndicator;
269 
270         // Line 2
271         this.vectorType = vectorType;
272         this.dataSource = dataSource;
273         this.transferType = new TransferTypeConstantTerm();
274         this.coordinateSystem = coordinateSystem;
275         this.supportIdCode = supportIdCode;
276         this.vehicleIdCode = vehicleIdCode;
277         this.sequenceNumber = sequenceNumber;
278         this.dayOfYear = dayOfYear;
279         this.vectorEpoch = vectorEpoch;
280         this.line2CheckSum = CheckSumTerm.fromIIRVTerms(this.vectorType, this.dataSource, this.transferType, this.coordinateSystem, this.supportIdCode, this.vehicleIdCode, this.sequenceNumber, this.dayOfYear, this.vectorEpoch);
281 
282         // Line 3
283         this.xPosition = xPosition;
284         this.yPosition = yPosition;
285         this.zPosition = zPosition;
286         this.line3CheckSum = CheckSumTerm.fromIIRVTerms(this.xPosition, this.yPosition, this.zPosition);
287 
288         // Line 4
289         this.xVelocity = xVelocity;
290         this.yVelocity = yVelocity;
291         this.zVelocity = zVelocity;
292         this.line4CheckSum = CheckSumTerm.fromIIRVTerms(this.xVelocity, this.yVelocity, this.zVelocity);
293 
294         // Line 5
295         this.mass = mass;
296         this.crossSectionalArea = crossSectionalArea;
297         this.dragCoefficient = dragCoefficient;
298         this.solarReflectivityCoefficient = solarReflectivityCoefficient;
299         this.line5CheckSum = CheckSumTerm.fromIIRVTerms(this.mass, this.crossSectionalArea, this.dragCoefficient, this.solarReflectivityCoefficient);
300 
301         // Line 6
302         this.endOfMessage = new MessageEndConstantTerm();
303         this.spareTerm = new SpareConstantTerm();
304         this.originatorRoutingIndicatorTerm = originatorRoutingIndicatorTerm;
305 
306 
307         // UTC time scale
308         this.utc = utc;
309     }
310 
311     /**
312      * Constructs an IIRV message object given a list of 6 lines of message body (omitting blank line feeds).
313      *
314      * @param lines Six-element list of lines in an IIRV message
315      * @param utc   UTC time scale
316      */
317     public IIRVVector(final List<String> lines, final UTCScale utc) {
318         this(lines.get(0), lines.get(1), lines.get(2), lines.get(3), lines.get(4), lines.get(5), utc);
319     }
320 
321     /**
322      * Constructs an IIRV message object given 6 lines of message body (omitting blank line feeds).
323      *
324      * @param line1 Line 1 of IIRV message body
325      * @param line2 Line 2 of IIRV message body
326      * @param line3 Line 3 of IIRV message body
327      * @param line4 Line 4 of IIRV message body
328      * @param line5 Line 5 of IIRV message body
329      * @param line6 Line 6 of IIRV message body
330      * @param utc   UTC time scale
331      */
332     public IIRVVector(final String line1, final String line2, final String line3, final String line4, final String line5, final String line6, final UTCScale utc) {
333         this.utc = utc;
334         final ArrayList<String> iirvLines = new ArrayList<>(Arrays.asList(line1, line2, line3, line4, line5, line6));
335 
336         if (!IIRVVector.isFormatOK(iirvLines)) {
337             // trigger line-specific error
338             validateLines(iirvLines, true);
339             validateLines(iirvLines, false);
340 
341             // this should never happen; error should have been triggered by one of the calls above
342             throw new OrekitInternalError(null);
343         }
344 
345         // Parse Line 1
346         messageType = new MessageTypeTerm(line1.substring(0, 2));
347         messageStart = new MessageStartConstantTerm();
348         if (LINE_1_PATTERN_METADATA_INCLUDED.matcher(line1).matches()) {
349             messageID = new MessageIDTerm(line1.substring(2, 9));
350             messageSource = new MessageSourceTerm(line1.substring(9, 10));
351             messageClass = new MessageClassTerm(line1.substring(10, 12));
352             originIdentification = new OriginIdentificationTerm(line1.substring(17, 18));
353             routingIndicator = new RoutingIndicatorTerm(line1.substring(18, 22));
354         } else {
355             messageID = new MessageIDTerm(0);     // Default to 0
356             messageSource = MessageSourceTerm.DEFAULT;  // Default to "0"
357             messageClass = MessageClassTerm.NOMINAL;    // Default to "10" (nominal)
358             originIdentification = new OriginIdentificationTerm(line1.substring(5, 6));
359             routingIndicator = new RoutingIndicatorTerm(line1.substring(6, 10));
360         }
361 
362         // Parse Line 2
363         vectorType = new VectorTypeTerm(line2.substring(0, 1));
364         dataSource = new DataSourceTerm(line2.substring(1, 2));
365         transferType = new TransferTypeConstantTerm();
366         coordinateSystem = new CoordinateSystemTerm(line2.substring(3, 4));
367         supportIdCode = new SupportIdCodeTerm(line2.substring(4, 8));
368         vehicleIdCode = new VehicleIdCodeTerm(line2.substring(8, 10));
369         sequenceNumber = new SequenceNumberTerm(line2.substring(10, 13));
370         dayOfYear = new DayOfYearTerm(line2.substring(13, 16));
371         vectorEpoch = new VectorEpochTerm(line2.substring(16, 25));
372         line2CheckSum = new CheckSumTerm(line2.substring(25, 28));
373 
374         // Parse Line 3 (position coordinates in meters)
375         xPosition = new PositionVectorComponentTerm(line3.substring(0, 13));
376         yPosition = new PositionVectorComponentTerm(line3.substring(13, 26));
377         zPosition = new PositionVectorComponentTerm(line3.substring(26, 39));
378         line3CheckSum = new CheckSumTerm(line3.substring(39));
379 
380         // Parse Line 4 (velocity coordinates in m/s)
381         xVelocity = new VelocityVectorComponentTerm(line4.substring(0, 13));
382         yVelocity = new VelocityVectorComponentTerm(line4.substring(13, 26));
383         zVelocity = new VelocityVectorComponentTerm(line4.substring(26, 39));
384         line4CheckSum = new CheckSumTerm(line4.substring(39));
385 
386         // Parse Line 5
387         mass = new MassTerm(line5.substring(0, 8));
388         crossSectionalArea = new CrossSectionalAreaTerm(line5.substring(8, 13));
389         dragCoefficient = new DragCoefficientTerm(line5.substring(13, 17));
390         solarReflectivityCoefficient = new SolarReflectivityCoefficientTerm(line5.substring(17, 25));
391         line5CheckSum = new CheckSumTerm(line5.substring(25));
392 
393         // Parse Line 6
394         endOfMessage = new MessageEndConstantTerm();
395         spareTerm = new SpareConstantTerm();
396         originatorRoutingIndicatorTerm = new OriginatorRoutingIndicatorTerm(line6.substring(6, 10));
397     }
398 
399     /**
400      * Copy constructor.
401      *
402      * @param other Other IIRVVector to create from.
403      */
404     public IIRVVector(final IIRVVector other) {
405         this(other.toIIRVStrings(true), other.utc);
406     }
407 
408     /**
409      * Checks the format validity for each line using regular expressions.
410      *
411      * @param lines the six-element list of lines
412      * @return true if format is valid, false otherwise
413      */
414     public static boolean isFormatOK(final List<String> lines) {
415         if (lines == null) {
416             return false;
417         }
418         if (lines.size() != 6) {
419             return false;
420         }
421         return isFormatOK(lines.get(0), lines.get(1), lines.get(2), lines.get(3), lines.get(4), lines.get(5));
422     }
423 
424     /**
425      * Checks the format validity for each line using regular expressions.
426      *
427      * @param line1 Line 1 of IIRV message body
428      * @param line2 Line 2 of IIRV message body
429      * @param line3 Line 3 of IIRV message body
430      * @param line4 Line 4 of IIRV message body
431      * @param line5 Line 5 of IIRV message body
432      * @param line6 Line 6 of IIRV message body
433      * @return true if format is valid, false otherwise
434      */
435     public static boolean isFormatOK(final String line1, final String line2, final String line3, final String line4, final String line5, final String line6) {
436         // Attempt to validate the input lines: with metadata
437         try {
438             validateLines(line1, line2, line3, line4, line5, line6, true);
439             return true;
440         } catch (OrekitIllegalArgumentException oe) {
441             // Continue to other check
442         }
443 
444         // Attempt to validate the input lines: without metadata
445         try {
446             validateLines(line1, line2, line3, line4, line5, line6, false);
447             return true;
448         } catch (OrekitIllegalArgumentException oe) {
449             return false;  // both validation checks failed
450         }
451     }
452 
453     /**
454      * Check the format validity for each line using regular expressions.
455      *
456      * @param lines                            the six-element list of lines
457      * @param firstLineIncludesMessageMetadata true if message metadata terms are included in the first line
458      *                                         of the vector
459      */
460     public static void validateLines(final List<String> lines, final boolean firstLineIncludesMessageMetadata) {
461         if (lines == null) {
462             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "lines");
463         }
464         if (lines.size() != 6) {
465             throw new OrekitIllegalArgumentException(OrekitMessages.INCONSISTENT_NUMBER_OF_ELEMENTS, 6, lines.size());
466         }
467 
468         validateLines(lines.get(0), lines.get(1), lines.get(2), lines.get(3), lines.get(4), lines.get(5), firstLineIncludesMessageMetadata);
469     }
470 
471 
472     /**
473      * Check the format validity for each line using regular expressions.
474      *
475      * @param line1                            Line 1 of IIRV message body
476      * @param line2                            Line 2 of IIRV message body
477      * @param line3                            Line 3 of IIRV message body
478      * @param line4                            Line 4 of IIRV message body
479      * @param line5                            Line 5 of IIRV message body
480      * @param line6                            Line 6 of IIRV message body
481      * @param firstLineIncludesMessageMetadata true if message metadata terms are included in the first line
482      *                                         of the vector
483      */
484     public static void validateLines(final String line1, final String line2, final String line3, final String line4, final String line5, final String line6, final boolean firstLineIncludesMessageMetadata) {
485         // Validate line1 separately based on if metadata is included
486         if (line1 == null) {
487             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "line1");
488         }
489         final Pattern line1Pattern = firstLineIncludesMessageMetadata ? LINE_1_PATTERN_METADATA_INCLUDED : LINE_1_PATTERN_METADATA_OMITTED;
490         final boolean lineOneOkay = line1Pattern.matcher(line1).matches();
491 
492         // Validate remaining lines
493         final boolean lineTwoOkay = validateLine(1, line2);
494         final boolean lineThreeOkay = validateLine(2, line3);
495         final boolean lineFourOkay = validateLine(3, line4);
496         final boolean lineFiveOkay = validateLine(4, line5);
497         final boolean lineSixOkay = validateLine(5, line6);
498 
499         if (!lineTwoOkay) {
500             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 2, line2);
501         } else if (!lineThreeOkay) {
502             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 3, line3);
503         } else if (!lineFourOkay) {
504             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 4, line4);
505         } else if (!lineFiveOkay) {
506             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 5, line5);
507         } else if (!lineSixOkay) {
508             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 6, line6);
509         } else if (!lineOneOkay) {
510             // Do line 1 last since this is the one that can change format
511             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 1, line1);
512         }
513     }
514 
515     /**
516      * Check an input string against the specified line's regular expression, and validate the checksum for lines 2-5.
517      *
518      * @param lineIndex Line index to validate against (0-5)
519      * @param line      String to validate
520      * @return true if the inputted line string is valid, false otherwise.
521      */
522     public static boolean validateLine(final int lineIndex, final String line) {
523         if (line == null) {
524             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "line");
525         } else if (line.length() < 3) {
526             throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, line.length());
527         }
528 
529         // Validate against regular expressions
530         final boolean regexMatches;
531         switch (lineIndex) {
532             case 0:
533                 final boolean line1Metadata = LINE_1_PATTERN_METADATA_INCLUDED.matcher(line).matches();
534                 final boolean line1NoMetadata = LINE_1_PATTERN_METADATA_OMITTED.matcher(line).matches();
535                 regexMatches = line1Metadata || line1NoMetadata;
536                 break;
537             case 1:
538                 regexMatches = LINE_2_PATTERN.matcher(line).matches();
539                 break;
540             case 2:
541                 regexMatches = LINE_3_PATTERN.matcher(line).matches();
542                 break;
543             case 3:
544                 regexMatches = LINE_4_PATTERN.matcher(line).matches();
545                 break;
546             case 4:
547                 regexMatches = LINE_5_PATTERN.matcher(line).matches();
548                 break;
549             case 5:
550                 regexMatches = LINE_6_PATTERN.matcher(line).matches();
551                 break;
552             default:
553                 throw new OrekitIllegalArgumentException(OrekitMessages.INVALID_PARAMETER_RANGE, "lineIndex", lineIndex, 0, 5);
554         }
555 
556         // Validate checksum. Value is always "true" for lines that omit checksum (1, 6))
557         final boolean checksumValid;
558         if (lineIndex == 0 || lineIndex == 5) {
559             checksumValid = true;
560         } else {
561             try {
562                 checksumValid = CheckSumTerm.validateLineCheckSum(line);
563             } catch (OrekitIllegalArgumentException e) {
564                 return false;
565             }
566         }
567 
568         return regexMatches && checksumValid;
569     }
570 
571     /**
572      * Compares two IIRV vectors for equality based on their string representations, include message
573      * metadata information.
574      */
575     @Override
576     public int compareTo(final IIRVVector o) {
577         final String thisIIRV = toIIRVString(true);
578         final String otherIIRV = o.toIIRVString(true);
579         return thisIIRV.compareTo(otherIIRV);
580     }
581 
582     /**
583      * Compares two IIRV vectors for equality based on their string representations, include message
584      * metadata information.
585      */
586     @Override
587     public boolean equals(final Object o) {
588         if (this == o) {
589             return true;
590         }
591         if (o == null || getClass() != o.getClass()) {
592             return false;
593         }
594         final IIRVVector vector = (IIRVVector) o;
595         return vector.toIIRVString(true).equals(toIIRVString(true));
596     }
597 
598     @Override
599     public String toString() {
600         return toIIRVString(true);
601     }
602 
603     @Override
604     public int hashCode() {
605         return Objects.hash(toString());
606     }
607 
608     /**
609      * Combine each line to build a String containing the entire IIRV message (including carriage returns and line
610      * feeds).
611      *
612      * @param includeMessageMetadata If true, include message metadata terms
613      *                               ({@link MessageTypeTerm},
614      *                               {@link MessageIDTerm},
615      *                               {@link MessageSourceTerm},
616      *                               {@link MessageClassTerm})
617      *                               at the beginning of the first line.
618      * @return Full IIRV vector in String format
619      */
620     public String toIIRVString(final boolean includeMessageMetadata) {
621         final List<String> lines = toIIRVStrings(includeMessageMetadata);
622         final StringBuilder iirvVectorString = new StringBuilder();
623         for (String x : lines) {
624             iirvVectorString.append(x);
625             iirvVectorString.append(LINE_SEPARATOR);
626         }
627 
628         return iirvVectorString.toString();
629     }
630 
631     /**
632      * Computes each IIRV lines as Strings, validate it, and return it in a list.
633      *
634      * @param includeMessageMetadata If true, include message metadata terms
635      *                               ({@link MessageTypeTerm},
636      *                               {@link MessageIDTerm},
637      *                               {@link MessageSourceTerm},
638      *                               {@link MessageClassTerm})
639      *                               at the beginning of the first line.
640      * @return List of IIRV lines as Strings
641      */
642     public List<String> toIIRVStrings(final boolean includeMessageMetadata) {
643         final String line1 = buildLine1(includeMessageMetadata);
644         final String line2 = buildLine2();
645         final String line3 = buildLine3();
646         final String line4 = buildLine4();
647         final String line5 = buildLine5();
648         final String line6 = buildLine6();
649 
650         // Validate each line and return
651         validateLines(line1, line2, line3, line4, line5, line6, includeMessageMetadata);
652         return new ArrayList<>(Arrays.asList(line1, line2, line3, line4, line5, line6));
653     }
654 
655     /**
656      * Computes each IIRV lines as Strings, with each IIRV vector term separated by a forward slash '/'.
657      * <p>
658      * This method is intended to display more human-readable IIRV vectors, and should never be used
659      * for writing an actual IIRV message (see {@link #toIIRVStrings(boolean)}).
660      *
661      * @return List of six human-readable IIRV lines, with each IIRV vector term separated by a forward slash '/'
662      */
663     public List<String> toHumanReadableLines() {
664         final String deliminator = "/";
665         return new ArrayList<>(Arrays.asList(buildLine1SplitByTerm(deliminator, true), buildLine2SplitByTerm(deliminator), buildLine3SplitByTerm(deliminator), buildLine4SplitByTerm(deliminator), buildLine5SplitByTerm(deliminator), buildLine6SplitByTerm(deliminator)));
666     }
667 
668     /**
669      * Builds the first line of the IIRV vector, not including ASCII carriage returns and line feeds.
670      * <br>
671      * Only includes message type, id, source, and class if this is the first IIRV in the sequence, i.e.,
672      * sequenceNumber=0
673      *
674      * @param includeMessageMetadata If true, include message metadata terms
675      *                               ({@link MessageTypeTerm},
676      *                               {@link MessageIDTerm},
677      *                               {@link MessageSourceTerm},
678      *                               {@link MessageClassTerm}) at the beginning of the line.
679      * @return Line 1 of IIRV vector
680      */
681     public String buildLine1(final boolean includeMessageMetadata) {
682         return buildLine1SplitByTerm("", includeMessageMetadata);
683     }
684 
685     /**
686      * Builds the second line of the IIRV vector, not including ASCII carriage returns and line feeds.
687      *
688      * @return Line 2 of IIRV vector
689      */
690     public String buildLine2() {
691         return buildLine2SplitByTerm("");
692     }
693 
694     /**
695      * Builds the third line of the IIRV vector, not including ASCII carriage returns and line feeds.
696      *
697      * @return Line 3 of IIRV vector
698      */
699     public String buildLine3() {
700         return buildLine3SplitByTerm("");
701     }
702 
703     /**
704      * Builds the fourth line of the IIRV vector, not including ASCII carriage returns and line feeds.
705      *
706      * @return Line 4 of IIRV vector
707      */
708     public String buildLine4() {
709         return buildLine4SplitByTerm("");
710     }
711 
712     /**
713      * Builds the fifth line of the IIRV vector, not including ASCII carriage returns and line feeds.
714      *
715      * @return Line 5 of IIRV vector
716      */
717     public String buildLine5() {
718         return buildLine5SplitByTerm("");
719     }
720 
721     /**
722      * Builds the sixth line of the IIRV vector, not including ASCII carriage returns and line feeds.
723      *
724      * @return Line 6 of IIRV vector
725      */
726     public String buildLine6() {
727         return buildLine6SplitByTerm("");
728     }
729 
730     /**
731      * Creates an {@link AbsoluteDate} instance (in UTC), with time components given by the {@link VectorEpochTerm},
732      * and date components from the {@link DayOfYearTerm} / inputted year.
733      *
734      * @param year Year associated with the created
735      * @return created {@link AbsoluteDate} instance
736      */
737     public AbsoluteDate getAbsoluteDate(final int year) {
738         return new AbsoluteDate(dayOfYear.getDateComponents(year), vectorEpoch.value(), utc);
739     }
740 
741     /**
742      * Gets a position vector [x y z], represented as a {@link Vector3D} instance.
743      * <p>
744      * Data supplied by the {@link PositionVectorComponentTerm} values; See {@link #getXPosition()},
745      * {@link #getYPosition()}, {@link #getZPosition()}.
746      *
747      * @return Vector containing x,y,z position components
748      */
749     public Vector3D getPositionVector() {
750         return new Vector3D(xPosition.value(), yPosition.value(), zPosition.value());
751     }
752 
753     /**
754      * Gets a velocity vector [vx vy vz], represented as a {@link Vector3D} instance.
755      * <p>
756      * Data supplied by the {@link VelocityVectorComponentTerm} values; See {@link #getXVelocity()},
757      * {@link #getYVelocity()}, {@link #getZVelocity()}.
758      *
759      * @return Vector containing x,y,z velocity components
760      */
761     public Vector3D getVelocityVector() {
762         return new Vector3D(xVelocity.value(), yVelocity.value(), zVelocity.value());
763     }
764 
765     /**
766      * Gets the state vector and time data as a {@link TimeStampedPVCoordinates} instance.
767      * <p>
768      * Position and velocity data supplied by the x,y,z position components ({@link PositionVectorComponentTerm}) and
769      * vx, vy, vz velocity components ({@link VelocityVectorComponentTerm}). Epoch data given by {@link DayOfYearTerm}
770      * {@link VectorEpochTerm}.
771      *
772      * @param year The year of the corresponding coordinates (IIRV vector does not contain year information)
773      * @return Newly created {@link TimeStampedPVCoordinates} instance populated with data from the IIRV vector terms.
774      */
775     public TimeStampedPVCoordinates getTimeStampedPVCoordinates(final int year) {
776         return new TimeStampedPVCoordinates(getAbsoluteDate(year), getPositionVector(), getVelocityVector());
777     }
778 
779     /**
780      * Gets the state vector as a {@link org.orekit.utils.PVCoordinates} instance.
781      * <p>
782      * Position and velocity data supplied by the x,y,z position components ({@link PositionVectorComponentTerm}) and
783      * vx, vy, vz velocity components ({@link VelocityVectorComponentTerm}).
784      *
785      * @return Newly created {@link PVCoordinates} instance populated with data from the IIRV vector terms.
786      */
787     public PVCoordinates getPVCoordinates() {
788         return new PVCoordinates(getPositionVector(), getVelocityVector());
789     }
790 
791     /**
792      * Returns the {@link Frame} associated with the IIRV vector based on its {@link CoordinateSystemTerm}.
793      *
794      * @param context data context used to retrieve frames
795      * @return coordinate system
796      * @see CoordinateSystemTerm#getFrame
797      */
798     public Frame getFrame(final DataContext context) {
799         return coordinateSystem.getFrame(context);
800     }
801 
802     /**
803      * Returns the {@link Frame} associated with the IIRV vector based on its {@link CoordinateSystemTerm}.
804      *
805      * @return coordinate system
806      * @see CoordinateSystemTerm#getFrame
807      */
808     @DefaultDataContext
809     public Frame getFrame() {
810         return coordinateSystem.getFrame();
811     }
812 
813     /**
814      * Gets the message type term.
815      *
816      * @return the message type term
817      */
818     public MessageTypeTerm getMessageType() {
819         return messageType;
820     }
821 
822     /**
823      * Gets the message ID term.
824      *
825      * @return the message ID term
826      */
827     public MessageIDTerm getMessageID() {
828         return messageID;
829     }
830 
831     /**
832      * Gets the message source term.
833      *
834      * @return the message source term
835      */
836     public MessageSourceTerm getMessageSource() {
837         return messageSource;
838     }
839 
840     /**
841      * Gets the message class term.
842      *
843      * @return the message class term
844      */
845     public MessageClassTerm getMessageClass() {
846         return messageClass;
847     }
848 
849     /**
850      * Gets the message start term.
851      *
852      * @return the message start term
853      */
854     public MessageStartConstantTerm getMessageStart() {
855         return messageStart;
856     }
857 
858     /**
859      * Gets the origin identification term.
860      *
861      * @return the origin identification term
862      */
863     public OriginIdentificationTerm getOriginIdentification() {
864         return originIdentification;
865     }
866 
867     /**
868      * Gets the routing indicator term.
869      *
870      * @return the routing indicator term
871      */
872     public RoutingIndicatorTerm getRoutingIndicator() {
873         return routingIndicator;
874     }
875 
876     /**
877      * Gets the vector type term.
878      *
879      * @return the vector type term
880      */
881     public VectorTypeTerm getVectorType() {
882         return vectorType;
883     }
884 
885     /**
886      * Gets the data source term.
887      *
888      * @return the data source term
889      */
890     public DataSourceTerm getDataSource() {
891         return dataSource;
892     }
893 
894     /**
895      * Gets the transfer type term.
896      *
897      * @return the transfer type term
898      */
899     public TransferTypeConstantTerm getTransferType() {
900         return transferType;
901     }
902 
903     /**
904      * Gets the coordinate system term.
905      *
906      * @return the coordinate system term
907      */
908     public CoordinateSystemTerm getCoordinateSystem() {
909         return coordinateSystem;
910     }
911 
912     /**
913      * Gets the support ID code term.
914      *
915      * @return the support ID code term
916      */
917     public SupportIdCodeTerm getSupportIdCode() {
918         return supportIdCode;
919     }
920 
921     /**
922      * Gets the vehicle ID code term.
923      *
924      * @return the vehicle ID code term
925      */
926     public VehicleIdCodeTerm getVehicleIdCode() {
927         return vehicleIdCode;
928     }
929 
930     /**
931      * Gets the sequence number term.
932      *
933      * @return the sequence number term
934      */
935     public SequenceNumberTerm getSequenceNumber() {
936         return sequenceNumber;
937     }
938 
939     /**
940      * Gets the day of year term.
941      *
942      * @return the day of year term
943      */
944     public DayOfYearTerm getDayOfYear() {
945         return dayOfYear;
946     }
947 
948     /**
949      * Gets the vector epoch term.
950      *
951      * @return the vector epoch term
952      */
953     public VectorEpochTerm getVectorEpoch() {
954         return vectorEpoch;
955     }
956 
957     /**
958      * Gets the checksum value for line 2.
959      *
960      * @return the checksum value for line 2
961      */
962     public CheckSumTerm getLine2CheckSum() {
963         return line2CheckSum;
964     }
965 
966     /**
967      * Gets the x component of position.
968      *
969      * @return the x component of position
970      */
971     public PositionVectorComponentTerm getXPosition() {
972         return xPosition;
973     }
974 
975     /**
976      * Gets the y component of position.
977      *
978      * @return the y component of position
979      */
980     public PositionVectorComponentTerm getYPosition() {
981         return yPosition;
982     }
983 
984     /**
985      * Gets the z component of position.
986      *
987      * @return the z component of position
988      */
989     public PositionVectorComponentTerm getZPosition() {
990         return zPosition;
991     }
992 
993     /**
994      * Gets the checksum term for line 3.
995      *
996      * @return the checksum term for line 3
997      */
998     public CheckSumTerm getLine3CheckSum() {
999         return line3CheckSum;
1000     }
1001 
1002     /**
1003      * Gets the x component of velocity.
1004      *
1005      * @return the x component of velocity
1006      */
1007     public VelocityVectorComponentTerm getXVelocity() {
1008         return xVelocity;
1009     }
1010 
1011     /**
1012      * Gets the y component of velocity.
1013      *
1014      * @return the y component of velocity
1015      */
1016     public VelocityVectorComponentTerm getYVelocity() {
1017         return yVelocity;
1018     }
1019 
1020     /**
1021      * Gets the z component of velocity.
1022      *
1023      * @return the z component of velocity
1024      */
1025     public VelocityVectorComponentTerm getZVelocity() {
1026         return zVelocity;
1027     }
1028 
1029     /**
1030      * Gets the checksum value for line 4 term.
1031      *
1032      * @return the checksum value for line 4 term
1033      */
1034     public CheckSumTerm getLine4CheckSum() {
1035         return line4CheckSum;
1036     }
1037 
1038     /**
1039      * Gets the mass term.
1040      *
1041      * @return the mass term
1042      */
1043     public MassTerm getMass() {
1044         return mass;
1045     }
1046 
1047     /**
1048      * Gets the cross-sectional area term.
1049      *
1050      * @return the cross-sectional area term
1051      */
1052     public CrossSectionalAreaTerm getCrossSectionalArea() {
1053         return crossSectionalArea;
1054     }
1055 
1056     /**
1057      * Gets the drag coefficient term.
1058      *
1059      * @return the drag coefficient term
1060      */
1061     public DragCoefficientTerm getDragCoefficient() {
1062         return dragCoefficient;
1063     }
1064 
1065     /**
1066      * Gets the solar reflectivity coefficient term.
1067      *
1068      * @return the solar reflectivity coefficient term
1069      */
1070     public SolarReflectivityCoefficientTerm getSolarReflectivityCoefficient() {
1071         return solarReflectivityCoefficient;
1072     }
1073 
1074     /**
1075      * Gets the checksum term for line 5.
1076      *
1077      * @return the checksum term for line 5
1078      */
1079     public CheckSumTerm getLine5CheckSum() {
1080         return line5CheckSum;
1081     }
1082 
1083     /**
1084      * Gets the "ITERM" message end term.
1085      *
1086      * @return the "ITERM" message end term
1087      */
1088     public MessageEndConstantTerm getMessageEnd() {
1089         return endOfMessage;
1090     }
1091 
1092     /**
1093      * Gets the spare character term (ASCII space).
1094      *
1095      * @return the spare character term (ASCII space)
1096      */
1097     public SpareConstantTerm getSpareTerm() {
1098         return spareTerm;
1099     }
1100 
1101     /**
1102      * Gets the originator routing indicator term.
1103      *
1104      * @return the originator routing indicator term
1105      */
1106     public OriginatorRoutingIndicatorTerm getOriginatorRoutingIndicator() {
1107         return originatorRoutingIndicatorTerm;
1108     }
1109 
1110     /**
1111      * Builds the first line of the IIRV, not including ASCII carriage returns and line feeds.
1112      * <br>
1113      * <p>
1114      * Only includes message type, id, source, and class if this is the first IIRV in the sequence, i.e.,
1115      * sequenceNumber=0.
1116      *
1117      * @param delimiter              Delimiter to split the terms in the message. Should always be "", except when printing
1118      *                               for human readability.
1119      * @param includeMessageMetadata If true, include message metadata terms
1120      *                               ({@link MessageTypeTerm},
1121      *                               {@link MessageIDTerm},
1122      *                               {@link MessageSourceTerm},
1123      *                               {@link MessageClassTerm}) at the beginning of the line.
1124      * @return Line 1 of IIRV message
1125      */
1126     private String buildLine1SplitByTerm(final String delimiter, final boolean includeMessageMetadata) {
1127         final String splitLine;
1128         if (includeMessageMetadata) {
1129             splitLine = IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, messageType, messageID, messageSource, messageClass, messageStart, originIdentification, routingIndicator);
1130 
1131         } else {
1132             splitLine = IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, messageStart, originIdentification, routingIndicator);
1133         }
1134         return splitLine;
1135     }
1136 
1137     /**
1138      * Builds the second line of the IIRV message, not including ASCII carriage returns and line feeds.
1139      *
1140      * @param delimiter Delimiter to split the terms in the message. Should always be "", except when printing
1141      *                  for human readability.
1142      * @return Line 2 of IIRV message
1143      */
1144     private String buildLine2SplitByTerm(final String delimiter) {
1145         return IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, vectorType, dataSource, transferType, coordinateSystem, supportIdCode, vehicleIdCode, sequenceNumber, dayOfYear, vectorEpoch, line2CheckSum);
1146     }
1147 
1148     /**
1149      * Builds the third line of the IIRV message, not including ASCII carriage returns and line feeds.
1150      *
1151      * @param delimiter Delimiter to split the terms in the message. Should always be "", except when printing
1152      *                  for human readability.
1153      * @return Line 3 of IIRV message
1154      */
1155     private String buildLine3SplitByTerm(final String delimiter) {
1156         return IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, xPosition, yPosition, zPosition, line3CheckSum);
1157     }
1158 
1159     /**
1160      * Builds the fourth line of the IIRV message, not including ASCII carriage returns and line feeds.
1161      *
1162      * @param delimiter Delimiter to split the terms in the message. Should always be "", except when printing
1163      *                  for human readability.
1164      * @return Line f of IIRV message
1165      */
1166     private String buildLine4SplitByTerm(final String delimiter) {
1167         return IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, xVelocity, yVelocity, zVelocity, line4CheckSum);
1168     }
1169 
1170     /**
1171      * Builds the fifth line of the IIRV message, not including ASCII carriage returns and line feeds.
1172      *
1173      * @param delimiter Delimiter to split the terms in the message. Should always be "", except when printing
1174      *                  for human readability.
1175      * @return Line 5 of IIRV message
1176      */
1177     private String buildLine5SplitByTerm(final String delimiter) {
1178         return IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, mass, crossSectionalArea, dragCoefficient, solarReflectivityCoefficient, line5CheckSum);
1179     }
1180 
1181     /**
1182      * Builds the sixth line of the IIRV message, not including ASCII carriage returns and line feeds.
1183      *
1184      * @param delimiter Delimiter to split the terms in the message. Should always be "", except when printing
1185      *                  for human readability.
1186      * @return Line 6 of IIRV message
1187      */
1188     private String buildLine6SplitByTerm(final String delimiter) {
1189         return IIRVTermUtils.iirvTermsToLineStringSplitByTerm(delimiter, endOfMessage, spareTerm, originatorRoutingIndicatorTerm);
1190     }
1191 
1192 }