1   /* Copyright 2002-2025 CS GROUP
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.ilrs;
18  
19  import java.util.HashMap;
20  import java.util.Locale;
21  import java.util.Map;
22  import java.util.regex.Pattern;
23  
24  import org.orekit.annotation.DefaultDataContext;
25  import org.orekit.errors.OrekitException;
26  import org.orekit.errors.OrekitMessages;
27  import org.orekit.time.DateComponents;
28  import org.orekit.time.TimeScale;
29  import org.orekit.time.TimeScalesFactory;
30  
31  /**
32   * Container for Consolidated laser ranging Data Format (CDR) header.
33   * @author Bryan Cazabonne
34   * @author Rongwang Li
35   * @since 10.3
36   */
37  public class CRDHeader extends ILRSHeader {
38  
39      /** String delimiter regex of datetime. */
40      private static final String DATETIME_DELIMITER_REGEX = "[-:T]";
41  
42      /** Space. */
43      private static final String SPACE = " ";
44  
45      /** Pattern of delimiter of datetime. */
46      public static final Pattern PATTERN_DATETIME_DELIMITER_REGEX = Pattern.compile(DATETIME_DELIMITER_REGEX);
47  
48      /** Station name from official list. */
49      private String stationName;
50  
51      /** System identifier: Crustal Dynamics Project (CDP) Pad Identifier for SLR. */
52      private int systemIdentifier;
53  
54      /** System number: Crustal Dynamics Project (CDP) 2-digit system number for SLR. */
55      private int systemNumber;
56  
57      /** System occupancy: Crustal Dynamics Project (CDP) 2-digit occupancy sequence number for SLR. */
58      private int systemOccupancy;
59  
60      /** Station Epoch Time Scale. */
61      private int epochIdentifier;
62  
63      /** Station network. */
64      private String stationNetword;
65  
66      /** Spacecraft Epoch Time Scale (transponders only). */
67      private int spacecraftEpochTimeScale;
68  
69      /** Data type. */
70      private int dataType;
71  
72      /** A flag to indicate the data release. */
73      private int dataReleaseFlag;
74  
75      /** Tropospheric refraction correction applied indicator. */
76      private boolean isTroposphericRefractionApplied;
77  
78      /** Center of mass correction applied indicator. */
79      private boolean isCenterOfMassCorrectionApplied;
80  
81      /** Receive amplitude correction applied indicator. */
82      private boolean isReceiveAmplitudeCorrectionApplied;
83  
84      /** Station system delay applied indicator. */
85      private boolean isStationSystemDelayApplied;
86  
87      /** Spacecraft system delay applied (transponders) indicator. */
88      private boolean isTransponderDelayApplied;
89  
90      /** Range type. */
91      private RangeType rangeType;
92  
93      /** Data quality indicator. */
94      private int qualityIndicator;
95  
96      /** Prediction type (CPF or TLE). */
97      private int predictionType;
98  
99      /** Year of century from CPF or TLE. */
100     private int yearOfCentury;
101 
102     /**
103      * Date and time.
104      * CPF starting date and hour (MMDDHH) from CPF H2 record or
105      * TLE epoch day/fractional day.
106      */
107     private String dateAndTime;
108 
109     /** Prediction provider (CPF provider in H1 record or TLE source). */
110     private String predictionProvider;
111 
112     /** Empty constructor.
113      * <p>
114      * This constructor is not strictly necessary, but it prevents spurious
115      * javadoc warnings with JDK 18 and later.
116      * </p>
117      * @since 12.0
118      */
119     public CRDHeader() {
120         // nothing to do
121     }
122 
123     /**
124      * Get the station name from official list.
125      * @return the station name from official list
126      */
127     public String getStationName() {
128         return stationName;
129     }
130 
131     /**
132      * Set the station name from official list.
133      * @param stationName the station name to set
134      */
135     public void setStationName(final String stationName) {
136         this.stationName = stationName;
137     }
138 
139     /**
140      * Get the system identifier.
141      * @return the system identifier
142      */
143     public int getSystemIdentifier() {
144         return systemIdentifier;
145     }
146 
147     /**
148      * Set the system identifier.
149      * @param systemIdentifier the system identifier to set
150      */
151     public void setSystemIdentifier(final int systemIdentifier) {
152         this.systemIdentifier = systemIdentifier;
153     }
154 
155     /**
156      * Get the system number.
157      * @return the system number
158      */
159     public int getSystemNumber() {
160         return systemNumber;
161     }
162 
163     /**
164      * Set the system number.
165      * @param systemNumber the system number to set
166      */
167     public void setSystemNumber(final int systemNumber) {
168         this.systemNumber = systemNumber;
169     }
170 
171     /**
172      * Get the system occupancy.
173      * @return the system occupancy
174      */
175     public int getSystemOccupancy() {
176         return systemOccupancy;
177     }
178 
179     /**
180      * Set the system occupancy.
181      * @param systemOccupancy the system occupancy to set
182      */
183     public void setSystemOccupancy(final int systemOccupancy) {
184         this.systemOccupancy = systemOccupancy;
185     }
186 
187     /**
188      * Get the epoch identifier.
189      * <p>
190      * 3 = UTC (UNSO) ; 4 = UTC (GPS) ; 7 = UTC (BIPM) ; 10 = UTC (Station Time Scale)
191      * </p>
192      * @return the epoch identifier
193      */
194     public int getEpochIdentifier() {
195         return epochIdentifier;
196     }
197 
198     /**
199      * Set the epoch identifier.
200      * @param epochIdentifier the epoch identifier to set
201      */
202     public void setEpochIdentifier(final int epochIdentifier) {
203         this.epochIdentifier = epochIdentifier;
204     }
205 
206     /**
207      * Get the station network.
208      * @return the station network
209      */
210     public String getStationNetword() {
211         return stationNetword;
212     }
213 
214     /**
215      * Set the station network.
216      * @param stationNetword the station network to set
217      */
218     public void setStationNetword(final String stationNetword) {
219         this.stationNetword = stationNetword;
220     }
221 
222     /**
223      * Get the spacecraft epoch time scale.
224      * @return the spacecraft epoch time scale
225      */
226     public int getSpacecraftEpochTimeScale() {
227         return spacecraftEpochTimeScale;
228     }
229 
230     /**
231      * Set the spacecraft epoch time scale.
232      * @param spacecraftEpochTimeScale the spacecraft epoch time scale to set
233      */
234     public void setSpacecraftEpochTimeScale(final int spacecraftEpochTimeScale) {
235         this.spacecraftEpochTimeScale = spacecraftEpochTimeScale;
236     }
237 
238     /**
239      * Get the data type.
240      * <p>
241      * 0 = full rate ; 1 = normal point ; 2 = sampled engineering
242      * </p>
243      * @return the data type
244      */
245     public int getDataType() {
246         return dataType;
247     }
248 
249     /**
250      * Set the data type.
251      * @param dataType the data type to set
252      */
253     public void setDataType(final int dataType) {
254         this.dataType = dataType;
255     }
256 
257     /**
258      * Get the flag indicating the data release.
259      * @return the flag indicating the data release
260      */
261     public int getDataReleaseFlag() {
262         return dataReleaseFlag;
263     }
264 
265     /**
266      * Set the flag indicating the data release.
267      * @param dataReleaseFlag the flag to set
268      */
269     public void setDataReleaseFlag(final int dataReleaseFlag) {
270         this.dataReleaseFlag = dataReleaseFlag;
271     }
272 
273     /**
274      * Get the tropospheric refraction correction applied indicator.
275      * @return true if tropospheric refraction correction is applied
276      */
277     public boolean isTroposphericRefractionApplied() {
278         return isTroposphericRefractionApplied;
279     }
280 
281     /**
282      * Set the tropospheric refraction correction applied indicator.
283      * @param isTroposphericRefractionApplied true if tropospheric refraction correction is applied
284      */
285     public void setIsTroposphericRefractionApplied(final boolean isTroposphericRefractionApplied) {
286         this.isTroposphericRefractionApplied = isTroposphericRefractionApplied;
287     }
288 
289     /**
290      * Get the center of mass correction applied indicator.
291      * @return true if center of mass correction is applied
292      */
293     public boolean isCenterOfMassCorrectionApplied() {
294         return isCenterOfMassCorrectionApplied;
295     }
296 
297     /**
298      * Set the center of mass correction applied indicator.
299      * @param isCenterOfMassCorrectionApplied true if center of mass correction is applied
300      */
301     public void setIsCenterOfMassCorrectionApplied(final boolean isCenterOfMassCorrectionApplied) {
302         this.isCenterOfMassCorrectionApplied = isCenterOfMassCorrectionApplied;
303     }
304 
305     /**
306      * Get the receive amplitude correction applied indicator.
307      * @return true if receive amplitude correction is applied
308      */
309     public boolean isReceiveAmplitudeCorrectionApplied() {
310         return isReceiveAmplitudeCorrectionApplied;
311     }
312 
313     /**
314      * Set the receive amplitude correction applied indicator.
315      * @param isReceiveAmplitudeCorrectionApplied true if receive amplitude correction is applied
316      */
317     public void setIsReceiveAmplitudeCorrectionApplied(final boolean isReceiveAmplitudeCorrectionApplied) {
318         this.isReceiveAmplitudeCorrectionApplied = isReceiveAmplitudeCorrectionApplied;
319     }
320 
321     /**
322      * Get the station system delay applied indicator.
323      * @return true if station system delay is applied
324      */
325     public boolean isStationSystemDelayApplied() {
326         return isStationSystemDelayApplied;
327     }
328 
329     /**
330      * Set the station system delay applied indicator.
331      * @param isStationSystemDelayApplied true if station system delay is applied
332      */
333     public void setIsStationSystemDelayApplied(final boolean isStationSystemDelayApplied) {
334         this.isStationSystemDelayApplied = isStationSystemDelayApplied;
335     }
336 
337     /**
338      * Get the spacecraft system delay applied (transponders) indicator.
339      * @return true if transponder delay is applied
340      */
341     public boolean isTransponderDelayApplied() {
342         return isTransponderDelayApplied;
343     }
344 
345     /**
346      * Set the spacecraft system delay applied (transponders) indicator.
347      * @param isTransponderDelayApplied true if transponder delay is applied
348      */
349     public void setIsTransponderDelayApplied(final boolean isTransponderDelayApplied) {
350         this.isTransponderDelayApplied = isTransponderDelayApplied;
351     }
352 
353     /**
354      * Get the range type.
355      * @return the range type
356      */
357     public RangeType getRangeType() {
358         return rangeType;
359     }
360 
361     /**
362      * Set the range type indicator.
363      * @param indicator range type indicator
364      */
365     public void setRangeType(final int indicator) {
366         this.rangeType = RangeType.getRangeType(indicator);
367     }
368 
369     /**
370      * Get the data quality indicator.
371      * @return the data quality indicator
372      */
373     public int getQualityIndicator() {
374         return qualityIndicator;
375     }
376 
377     /**
378      * Set the data quality indicator.
379      * @param qualityIndicator the indicator to set
380      */
381     public void setQualityIndicator(final int qualityIndicator) {
382         this.qualityIndicator = qualityIndicator;
383     }
384 
385     /**
386      * Get the prediction type (CPF or TLE).
387      * @return the prediction type
388      */
389     public int getPredictionType() {
390         return predictionType;
391     }
392 
393     /**
394      * Set the prediction type.
395      * @param predictionType the prediction type to set
396      */
397     public void setPredictionType(final int predictionType) {
398         this.predictionType = predictionType;
399     }
400 
401     /**
402      * Get the year of century from CPF or TLE.
403      * @return the year of century from CPF or TLE
404      */
405     public int getYearOfCentury() {
406         return yearOfCentury;
407     }
408 
409     /**
410      * Set the year of century from CPF or TLE.
411      * @param yearOfCentury the year of century to set
412      */
413     public void setYearOfCentury(final int yearOfCentury) {
414         this.yearOfCentury = yearOfCentury;
415     }
416 
417 
418     /**
419      * Get the date and time as the string value.
420      * <p>
421      * Depending the prediction type, this value can represent the
422      * CPF starting date and hour (MMDDHH) from CPF H2 record or
423      * TLE epoch day/fractional day
424      * </p>
425      * @return the date and time as the string value
426      */
427     public String getDateAndTime() {
428         return dateAndTime;
429     }
430 
431     /**
432      * Set the string value of date and time.
433      * @param dateAndTime the date and time to set
434      */
435     public void setDateAndTime(final String dateAndTime) {
436         this.dateAndTime = dateAndTime;
437     }
438 
439     /**
440      * Get the prediction provider.
441      * @return the preditction provider
442      */
443     public String getPredictionProvider() {
444         return predictionProvider;
445     }
446 
447     /**
448      * Set the prediction provider.
449      * @param predictionProvider the prediction provider to set
450      */
451     public void setPredictionProvider(final String predictionProvider) {
452         this.predictionProvider = predictionProvider;
453     }
454 
455     /**
456      * Get a string representation of the H1 in the CRD format.
457      * @return a string representation of the H1, in the CRD format.
458      * @since 12.0
459      */
460     public String getH1CrdString() {
461         final DateComponents dc = getProductionEpoch();
462         return String.format(Locale.US, "H1 %3s %2d %04d %02d %02d %02d", getFormat(),
463                              getVersion(), dc.getYear(), dc.getMonth(), dc.getDay(),
464                              getProductionHour());
465     }
466 
467     /**
468      * Get a string representation of the H2 in the CRD format.
469      * @return a string representation of the H2, in the CRD format.
470      * @since 12.0
471      */
472     public String getH2CrdString() {
473         return String.format(Locale.US, "H2 %s %4d %02d %02d %2d %s", stationName,
474                 systemIdentifier, systemNumber, systemOccupancy,
475                 epochIdentifier, stationNetword);
476     }
477 
478     /**
479      * Get a string representation of the H3 in the CRD format.
480      * @return a string representation of the H3, in the CRD format.
481      * @since 12.0
482      */
483     public String getH3CrdString() {
484         final int targetLocation = getTargetLocation();
485         return String.format(Locale.US, "H3 %s %7s %4s %5s %1d %1d %2s", getName(),
486                 getIlrsSatelliteId(), getSic(), getNoradId(),
487                 getSpacecraftEpochTimeScale(), getTargetClass(),
488                 CRD.formatIntegerOrNaN(targetLocation, -1));
489     }
490 
491     /**
492      * Get a string representation of the H4 in the CRD format.
493      * @return a string representation of the H4, in the CRD format.
494      * @since 12.0
495      */
496     @DefaultDataContext
497     public String getH4CrdString() {
498         // "2006-11-13T15:23:52" -- > "2006 11 13 15 23 52"
499         final TimeScale utc = TimeScalesFactory.getUTC();
500         final String startEpoch = getStartEpoch().toStringWithoutUtcOffset(utc, 0);
501         final String endEpoch = getEndEpoch().toStringWithoutUtcOffset(utc, 0);
502         return String.format(Locale.US, "H4 %2d %s %s %d %d %d %d %d %d %d %d", getDataType(),
503                 PATTERN_DATETIME_DELIMITER_REGEX.matcher(startEpoch).replaceAll(SPACE),
504                 PATTERN_DATETIME_DELIMITER_REGEX.matcher(endEpoch).replaceAll(SPACE),
505                 dataReleaseFlag, isTroposphericRefractionApplied ? 1 : 0,
506                 isCenterOfMassCorrectionApplied ? 1 : 0,
507                 isReceiveAmplitudeCorrectionApplied ? 1 : 0,
508                 isStationSystemDelayApplied ? 1 : 0,
509                 isTransponderDelayApplied ? 1 : 0, rangeType.getIndicator(),
510                 qualityIndicator);
511     }
512 
513     /**
514      * Get a string representation of the H5 in the CRD format.
515      * @return a string representation of the H5, in the CRD format.
516      * @since 12.0
517      */
518     public String getH5CrdString() {
519         return String.format(Locale.US, "H5 %2d %02d %s %3s %5d", getPredictionType(), getYearOfCentury(),
520                 getDateAndTime(), getPredictionProvider(), getSequenceNumber());
521     }
522 
523     /** Range type for SLR data. */
524     public enum RangeType {
525 
526         /** No ranges (i.e. transmit time only). */
527         NO_RANGES(0),
528 
529         /** One-way ranging. */
530         ONE_WAY(1),
531 
532         /** Two-way ranging. */
533         TWO_WAY(2),
534 
535         /** Received times only. */
536         RECEIVED_ONLY(3),
537 
538         /** Mixed. */
539         MIXED(4);
540 
541         /** Codes map. */
542         private static final Map<Integer, RangeType> CODES_MAP = new HashMap<>();
543         static {
544             for (final RangeType type : values()) {
545                 CODES_MAP.put(type.getIndicator(), type);
546             }
547         }
548 
549         /** range type indicator. */
550         private final int indicator;
551 
552         /**
553          * Constructor.
554          * @param indicator range type indicator
555          */
556         RangeType(final int indicator) {
557             this.indicator = indicator;
558         }
559 
560         /**
561          * Get the range type indicator.
562          * @return the range type indicator
563          */
564         public int getIndicator() {
565             return indicator;
566         }
567 
568         /**
569          * Get the range type for the given indicator.
570          * @param id indicator
571          * @return the range type corresponding to the indicator
572          */
573         public static RangeType getRangeType(final int id) {
574             final RangeType type = CODES_MAP.get(id);
575             if (type == null) {
576                // Invalid value. An exception is thrown
577                 throw new OrekitException(OrekitMessages.INVALID_RANGE_INDICATOR_IN_CRD_FILE, id);
578             }
579             return type;
580         }
581 
582     }
583 
584     /** Data type for CRD data.
585      * @since 12.0
586      */
587     public enum DataType {
588 
589         /** Full rate. */
590         FULL_RATE(0),
591 
592         /** Normal point. */
593         NORMAL_POINT(1),
594 
595         /** Sampled engineering. */
596         SAMPLED_ENGIEERING(2);
597 
598         /** Codes map. */
599         private static final Map<Integer, DataType> CODES_MAP = new HashMap<>();
600         static {
601             for (final DataType type : values()) {
602                 CODES_MAP.put(type.getIndicator(), type);
603             }
604         }
605 
606         /** data type indicator. */
607         private final int indicator;
608 
609         /**
610          * Constructor.
611          * @param indicator data type indicator
612          */
613         DataType(final int indicator) {
614             this.indicator = indicator;
615         }
616 
617         /**
618          * Get the data type indicator.
619          * @return the data type indicator
620          */
621         public int getIndicator() {
622             return indicator;
623         }
624 
625         /**
626          * Get the data type for the given indicator.
627          * @param id indicator
628          * @return the data type corresponding to the indicator
629          */
630         public static DataType getDataType(final int id) {
631             final DataType type = CODES_MAP.get(id);
632             if (type == null) {
633                // Invalid value. An exception is thrown
634                 throw new RuntimeException(String.format(Locale.US,
635                                                          "Invalid data type indicator {0} in CRD file header", id));
636             }
637             return type;
638         }
639 
640     }
641 
642 }