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.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Hashtable;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Set;
27  import java.util.regex.Pattern;
28  
29  /**
30   * Container for Consolidated laser ranging Data Format (CDR) configuration records.
31   * @author Bryan Cazabonne
32   * @author Rongwang Li
33   * @since 10.3
34   */
35  public class CRDConfiguration {
36  
37      /** Dict of configuration record. **/
38      private final Map<String, BaseConfiguration> mapConfigurationRecords;
39  
40      /** List of system configuration. **/
41      private final List<SystemConfiguration> systemConfigurationRecords;
42  
43      /**
44       * Constructor.
45       */
46      public CRDConfiguration() {
47          systemConfigurationRecords = new ArrayList<>();
48          mapConfigurationRecords = new Hashtable<>();
49      }
50  
51      /**
52       * Get the system configuration record.
53       * @return the system configuration record
54       */
55      public SystemConfiguration getSystemRecord() {
56          return systemConfigurationRecords.isEmpty() ? null : systemConfigurationRecords.get(0);
57      }
58  
59      /**
60       * Get the system configuration record.
61       * @return the system configuration record
62       */
63      public SystemConfiguration getLastSystemRecord() {
64          return systemConfigurationRecords.isEmpty() ? null : systemConfigurationRecords.get(systemConfigurationRecords.size() - 1);
65      }
66  
67      /**
68       * Get the laser configuration record.
69       * @return the laser configuration record
70       */
71      public LaserConfiguration getLaserRecord() {
72          return getLaserRecord(getSystemRecord());
73      }
74  
75      /**
76       * Get the detector configuration record.
77       * @return the detector configuration record
78       */
79      public DetectorConfiguration getDetectorRecord() {
80          return getDetectorRecord(getSystemRecord());
81      }
82  
83      /**
84       * Get the timing system configuration record.
85       * @return the timing system configuration record
86       */
87      public TimingSystemConfiguration getTimingRecord() {
88          return getTimingRecord(getSystemRecord());
89      }
90  
91      /**
92       * Get the transponder configuration record.
93       * @return the transponder configuration record
94       */
95      public TransponderConfiguration getTransponderRecord() {
96          return getTransponderRecord(getSystemRecord());
97      }
98  
99  
100     /**
101      * Get the software configuration record.
102      * @return the software configuration record
103      */
104     public SoftwareConfiguration getSoftwareRecord() {
105         return getSoftwareRecord(getSystemRecord());
106     }
107 
108     /**
109      * Get the meteorological record.
110      * @return the meteorological record
111      */
112     public MeteorologicalConfiguration getMeteorologicalRecord() {
113         return getMeteorologicalRecord(getSystemRecord());
114     }
115 
116     /**
117      * Add a configuration record, such as SystemConfiguation, LaserConfiguration, DetectorConfiguration, etc.
118      * @param config the configuration record
119      * @since 12.0
120      */
121     public void addConfigurationRecord(final BaseConfiguration config) {
122         if (config == null) {
123             return;
124         }
125 
126         mapConfigurationRecords.put(config.getConfigurationId(), config);
127 
128         if (config instanceof SystemConfiguration) {
129             // Add to the list systemConfigurationRecords if it is a SystemConfiguration
130             systemConfigurationRecords.add((SystemConfiguration) config);
131         }
132     }
133 
134     /**
135      * Get the configuration records map.
136      * @return the configuration records map
137      * @since 12.0
138      */
139     public Map<String, BaseConfiguration> getConfigurationRecordMap() {
140         return Collections.unmodifiableMap(mapConfigurationRecords);
141     }
142 
143     /**
144      * Get configuration record corresponding to the configId.
145      * @param configId the id of configuration
146      * @return the configuration with configId, or null
147      * @since 12.0
148      */
149     public BaseConfiguration getConfigurationRecord(final String configId) {
150         return mapConfigurationRecords.get(configId);
151     }
152 
153     /**
154      * Get a set of configuration ids.
155      * @return an unmodifiable set of configuration ids
156      * @since 12.0
157      */
158     public Set<String> getSystemConfigurationIds() {
159         return Collections.unmodifiableSet(mapConfigurationRecords.keySet());
160     }
161 
162     /**
163      * Get a list of system configurations.
164      * @return an unmodifiable list of system configurations
165      * @since 12.0
166      */
167     public List<SystemConfiguration> getSystemConfigurationRecords() {
168         return Collections.unmodifiableList(systemConfigurationRecords);
169     }
170 
171     /**
172      * Get configuration record related to systemRecord and the given class.
173      * @param systemRecord system configuration record
174      * @param c the class, such as LaserConfiguration, DetectorConfiguration, TimingSystemConfiguration, etc
175      * @return the configuration record
176      * @since 12.0
177      */
178     private BaseConfiguration getRecord(final SystemConfiguration systemRecord,
179             final Class<? extends BaseConfiguration> c) {
180         BaseConfiguration config;
181         for (final String configId : systemRecord.getComponents()) {
182             config = getConfigurationRecord(configId);
183             if (config != null && config.getClass() == c) {
184                 return config;
185             }
186         }
187 
188         return null;
189     }
190 
191     /**
192      * Get system configuration record. If configId is null, the default(first system configuration record) is returned.
193      * @param configId system configuration id, it can be null.
194      * @return the system configuration record
195      * @since 12.0
196      */
197     public SystemConfiguration getSystemRecord(final String configId) {
198         if (configId == null) {
199             // default
200             return getSystemRecord();
201         }
202 
203         final BaseConfiguration config = mapConfigurationRecords.get(configId);
204         return config == null ? null : (SystemConfiguration) config;
205     }
206 
207     /**
208      * Get laser configuration record related to the systemRecord.
209      * @param systemRecord the system configuration
210      * @return the laser configuration record related the the systemRecord
211      * @since 12.0
212      */
213     public LaserConfiguration getLaserRecord(final SystemConfiguration systemRecord) {
214         final BaseConfiguration config = getRecord(systemRecord, LaserConfiguration.class);
215         return config == null ? null : (LaserConfiguration) config;
216     }
217 
218     /**
219      * Get detector configuration record related to the systemRecord.
220      * @param systemRecord the system configuration
221      * @return the detector configuration record related the the systemRecord
222      * @since 12.0
223      */
224     public DetectorConfiguration getDetectorRecord(final SystemConfiguration systemRecord) {
225         final BaseConfiguration config = getRecord(systemRecord, DetectorConfiguration.class);
226         return config == null ? null : (DetectorConfiguration) config;
227     }
228 
229     /**
230      * Get timing system configuration record related to the systemRecord.
231      * @param systemRecord the system configuration
232      * @return the timing system configuration record related the the systemRecord
233      * @since 12.0
234      */
235     public TimingSystemConfiguration getTimingRecord(final SystemConfiguration systemRecord) {
236         final BaseConfiguration config = getRecord(systemRecord, TimingSystemConfiguration.class);
237         return config == null ? null : (TimingSystemConfiguration) config;
238     }
239 
240     /**
241      * Get transponder configuration record related to the systemRecord.
242      * @param systemRecord the system configuration
243      * @return the transponder configuration record related the the systemRecord
244      * @since 12.0
245      */
246     public TransponderConfiguration getTransponderRecord(final SystemConfiguration systemRecord) {
247         final BaseConfiguration config = getRecord(systemRecord, TransponderConfiguration.class);
248         return config == null ? null : (TransponderConfiguration) config;
249     }
250 
251     /**
252      * Get software configuration record related to the systemRecord.
253      * @param systemRecord the system configuration
254      * @return the software configuration record related the the systemRecord
255      * @since 12.0
256      */
257     public SoftwareConfiguration getSoftwareRecord(final SystemConfiguration systemRecord) {
258         final BaseConfiguration config = getRecord(systemRecord, SoftwareConfiguration.class);
259         return config == null ? null : (SoftwareConfiguration) config;
260     }
261 
262     /**
263      * Get meteorological configuration record related to the systemRecord.
264      * @param systemRecord the system configuration
265      * @return the meteorological configuration record related the the systemRecord
266      * @since 12.0
267      */
268     public MeteorologicalConfiguration getMeteorologicalRecord(final SystemConfiguration systemRecord) {
269         final BaseConfiguration config = getRecord(systemRecord, MeteorologicalConfiguration.class);
270         return config == null ? null : (MeteorologicalConfiguration) config;
271     }
272 
273     /**
274      * Get calibration target configuration record related to the systemRecord.
275      * @param systemRecord the system configuration
276      * @return the calibration target configuration record related the the systemRecord
277      * @since 12.0
278      */
279     public CalibrationTargetConfiguration getCalibrationTargetRecord(final SystemConfiguration systemRecord) {
280         final BaseConfiguration config = getRecord(systemRecord, CalibrationTargetConfiguration.class);
281         return config == null ? null : (CalibrationTargetConfiguration) config;
282     }
283 
284     /**
285      * Get the calibration target configuration record.
286      * @return the calibration target configuration record
287      * @since 12.0
288      */
289     public CalibrationTargetConfiguration getCalibrationTargetRecord() {
290         return getCalibrationTargetRecord(getSystemRecord());
291     }
292 
293     /**
294      * Base class for configuration record.
295      * @since 12.0
296      */
297     public abstract static class BaseConfiguration {
298 
299         /** Configuration ID. */
300         private String configurationId;
301 
302         /** Empty constructor.
303          * <p>
304          * This constructor is not strictly necessary, but it prevents spurious
305          * javadoc warnings with JDK 18 and later.
306          * </p>
307          * @since 12.0
308          */
309         public BaseConfiguration() {
310             // nothing to do
311         }
312 
313         /**
314          * Get the configuration ID.
315          * @return the configuration ID
316          */
317         public String getConfigurationId() {
318             return configurationId;
319         }
320 
321         /**
322          * Set the configuration ID.
323          * @param configurationId the configuration ID to set
324          */
325         public void setConfigurationId(final String configurationId) {
326             this.configurationId = configurationId;
327         }
328 
329         @Override
330         public int hashCode() {
331             return toString().hashCode();
332         }
333 
334         @Override
335         public boolean equals(final Object record) {
336             if (record == null) {
337                 return false;
338             }
339 
340             if (record == this) {
341                 return true;
342             }
343 
344             return toString().equals(record.toString());
345 
346         }
347 
348         /**
349          * Get a string representation of the instance in the CRD format.
350          * @return a string representation of the instance, in the CRD format.
351          * @since 12.0
352          */
353         public abstract String toCrdString();
354     }
355 
356     /** Container for system configuration record. */
357     public static class SystemConfiguration extends BaseConfiguration {
358 
359         /** Transmit Wavelength [m]. */
360         private double wavelength;
361 
362         /** List of components. **/
363         private List<String> components;
364 
365         /**
366          * Constructor.
367          */
368         public SystemConfiguration() {
369             this.components = new ArrayList<>();
370         }
371 
372         /**
373          * Get the transmit wavelength.
374          * @return the transmit wavelength in meters
375          */
376         public double getWavelength() {
377             return wavelength;
378         }
379 
380         /**
381          * Set the transmit wavelength.
382          * @param wavelength the wavelength to set
383          */
384         public void setWavelength(final double wavelength) {
385             this.wavelength = wavelength;
386         }
387 
388         /**
389          * Get the system configuration ID.
390          * @return the system configuration ID
391          */
392         public String getSystemId() {
393             return getConfigurationId();
394         }
395 
396         /**
397          * Set the system configuration ID.
398          * @param systemId the system configuration ID to set
399          */
400         public void setSystemId(final String systemId) {
401             setConfigurationId(systemId);
402         }
403 
404         /**
405          * Get the components (config ids) for system configuration.
406          * @return an unmodifiable list of components
407          * @since 12.0
408          */
409         public List<String> getComponents() {
410             return Collections.unmodifiableList(components);
411         }
412 
413         /**
414          * Set the components (config ids) for system configuration.
415          * @param components the components (config ids)
416          * @since 12.0
417          */
418         public void setComponents(final String[] components) {
419             this.components = Arrays.asList(components);
420         }
421 
422         /** {@inheritDoc} */
423         @Override
424         public String toCrdString() {
425             return String.format(Locale.US, "C0 0 %s", toString());
426         }
427 
428         @Override
429         public String toString() {
430             // CRD suggested format, excluding the record type
431             final StringBuilder sb = new StringBuilder();
432             sb.append(String.format(Locale.US, "%10.3f %s", wavelength * 1e9, getConfigurationId()));
433             for (final String comp : components) {
434                 sb.append(String.format(Locale.US, " %s", comp));
435             }
436             return sb.toString().replace(',', '.');
437         }
438     }
439 
440     /** Container for laser configuration record. */
441     public static class LaserConfiguration extends BaseConfiguration {
442 
443         /** Laser Type. */
444         private String laserType;
445 
446         /** Primary wavelength [m]. */
447         private double primaryWavelength;
448 
449         /** Nominal Fire Rate [Hz]. */
450         private double nominalFireRate;
451 
452         /** Pulse Energy [mJ]. */
453         private double pulseEnergy;
454 
455         /** Pulse Width [ps]. */
456         private double pulseWidth;
457 
458         /** Bean divergence [arcsec]. */
459         private double beamDivergence;
460 
461         /** Number of pulses in outgoing semi-train. */
462         private int pulseInOutgoingSemiTrain;
463 
464         /** Empty constructor.
465          * <p>
466          * This constructor is not strictly necessary, but it prevents spurious
467          * javadoc warnings with JDK 18 and later.
468          * </p>
469          * @since 12.0
470          */
471         public LaserConfiguration() {
472             // nothing to do
473         }
474 
475         /**
476          * Get the laser configuration ID.
477          * @return the laser configuration ID
478          */
479         public String getLaserId() {
480             return getConfigurationId();
481         }
482 
483         /**
484          * Set the laser configuration ID.
485          * @param laserId the laser configuration ID to set
486          */
487         public void setLaserId(final String laserId) {
488             setConfigurationId(laserId);
489         }
490 
491         /**
492          * Get the laser type.
493          * @return the laser type
494          */
495         public String getLaserType() {
496             return laserType;
497         }
498 
499         /**
500          * Set the laser type.
501          * @param laserType the laser type to set
502          */
503         public void setLaserType(final String laserType) {
504             this.laserType = laserType;
505         }
506 
507         /**
508          * Get the primary wavelength.
509          * @return the primary wavelength in meters
510          */
511         public double getPrimaryWavelength() {
512             return primaryWavelength;
513         }
514 
515         /**
516          * Set the primary wavelength.
517          * @param primaryWavelength the primary wavelength to set in meters
518          */
519         public void setPrimaryWavelength(final double primaryWavelength) {
520             this.primaryWavelength = primaryWavelength;
521         }
522 
523         /**
524          * Get the nominal fire rate.
525          * @return the nominal fire rate in Hz.
526          */
527         public double getNominalFireRate() {
528             return nominalFireRate;
529         }
530 
531         /**
532          * Set the nominal fire rate.
533          * @param nominalFireRate the nominal fire rate to set in Hz
534          */
535         public void setNominalFireRate(final double nominalFireRate) {
536             this.nominalFireRate = nominalFireRate;
537         }
538 
539         /**
540          * Get the pulse energy.
541          * @return the pulse energy in mJ
542          */
543         public double getPulseEnergy() {
544             return pulseEnergy;
545         }
546 
547         /**
548          * Set the pulse energy.
549          * @param pulseEnergy the pulse energy to set in mJ
550          */
551         public void setPulseEnergy(final double pulseEnergy) {
552             this.pulseEnergy = pulseEnergy;
553         }
554 
555         /**
556          * Get the pulse width (FWHM in ps).
557          * @return the pulse width
558          */
559         public double getPulseWidth() {
560             return pulseWidth;
561         }
562 
563         /**
564          * Set the pulse width.
565          * @param pulseWidth the pulse width to set, ps
566          */
567         public void setPulseWidth(final double pulseWidth) {
568             this.pulseWidth = pulseWidth;
569         }
570 
571         /**
572          * Get the beam divergence.
573          * @return the beam divergence in arcsec
574          */
575         public double getBeamDivergence() {
576             return beamDivergence;
577         }
578 
579         /**
580          * Set the beam divergence.
581          * @param beamDivergence the beam divergence to set in arcsec
582          */
583         public void setBeamDivergence(final double beamDivergence) {
584             this.beamDivergence = beamDivergence;
585         }
586 
587         /**
588          * Get the number of pulses in outgoing semi-train.
589          * @return the number of pulses in outgoing semi-train
590          */
591         public int getPulseInOutgoingSemiTrain() {
592             return pulseInOutgoingSemiTrain;
593         }
594 
595         /**
596          * Set the number of pulses in outgoing semi-train.
597          * @param pulseInOutgoingSemiTrain the number of pulses in outgoing semi-train to set
598          */
599         public void setPulseInOutgoingSemiTrain(final int pulseInOutgoingSemiTrain) {
600             this.pulseInOutgoingSemiTrain = pulseInOutgoingSemiTrain;
601         }
602 
603         /** {@inheritDoc} */
604         @Override
605         public String toCrdString() {
606             return String.format(Locale.US, "C1 0 %s", toString());
607         }
608 
609         @Override
610         public String toString() {
611             // CRD suggested format, excluding the record type
612             // primaryWavelength: m --> nm
613             final String str = String.format(Locale.US,
614                     "%s %s %.2f %.2f %.2f %.1f %.2f %d", getConfigurationId(),
615                     laserType, primaryWavelength * 1e9, nominalFireRate,
616                     pulseEnergy, pulseWidth, beamDivergence,
617                     pulseInOutgoingSemiTrain);
618             return CRD.handleNaN(str).replace(',', '.');
619         }
620 
621     }
622 
623     /** Container for detector configuration record. */
624     public static class DetectorConfiguration extends BaseConfiguration {
625 
626         /** Detector Type. */
627         private String detectorType;
628 
629         /** Applicable wavelength. */
630         private double applicableWavelength;
631 
632         /** Quantum efficiency at applicable wavelength [%]. */
633         private double quantumEfficiency;
634 
635         /** Applied voltage [V]. */
636         private double appliedVoltage;
637 
638         /** Dark Count [Hz]. */
639         private double darkCount;
640 
641         /** Output pulse type. */
642         private String outputPulseType;
643 
644         /** Output pulse width [ps]. */
645         private double outputPulseWidth;
646 
647         /** Spectral Filter [m]. */
648         private double spectralFilter;
649 
650         /** % Transmission of Spectral Filter. */
651         private double transmissionOfSpectralFilter;
652 
653         /** Spatial Filter [arcsec]. */
654         private double spatialFilter;
655 
656         /** External Signal processing. */
657         private String externalSignalProcessing;
658 
659         /** Amplifier Gain. */
660         private double amplifierGain;
661 
662         /** Amplifier Bandwidth [Hz]. */
663         private double amplifierBandwidth;
664 
665         /** Amplifier In Use. */
666         private String amplifierInUse;
667 
668         /** Empty constructor.
669          * <p>
670          * This constructor is not strictly necessary, but it prevents spurious
671          * javadoc warnings with JDK 18 and later.
672          * </p>
673          * @since 12.0
674          */
675         public DetectorConfiguration() {
676             // nothing to do
677         }
678 
679         /**
680          * Get the detector configuration ID.
681          * @return the detector configuration ID
682          */
683         public String getDetectorId() {
684             return getConfigurationId();
685         }
686 
687         /**
688          * Set the detector configuration ID.
689          * @param detectorId the detector configuration ID to set
690          */
691         public void setDetectorId(final String detectorId) {
692             setConfigurationId(detectorId);
693         }
694 
695         /**
696          * Get the detector type.
697          * @return the detector type
698          */
699         public String getDetectorType() {
700             return detectorType;
701         }
702 
703         /**
704          * Set the detector type.
705          * @param detectorType the detector type to set
706          */
707         public void setDetectorType(final String detectorType) {
708             this.detectorType = detectorType;
709         }
710 
711         /**
712          * Get the applicable wavelength.
713          * @return pplicable wavelength in meters
714          */
715         public double getApplicableWavelength() {
716             return applicableWavelength;
717         }
718 
719         /**
720          * Set the applicable wavelength.
721          * @param applicableWavelength the applicable wavelength to set in meters
722          */
723         public void setApplicableWavelength(final double applicableWavelength) {
724             this.applicableWavelength = applicableWavelength;
725         }
726 
727         /**
728          * Get the quantum efficiency at applicable wavelength.
729          * @return the quantum efficiency at applicable wavelength in percents
730          */
731         public double getQuantumEfficiency() {
732             return quantumEfficiency;
733         }
734 
735         /**
736          * Set the quantum efficiency at applicable wavelength.
737          * @param quantumEfficiency the efficiency to set in percents
738          */
739         public void setQuantumEfficiency(final double quantumEfficiency) {
740             // NOTE: The quantumEfficiency may be -1.0, which means 'not available'
741             if (quantumEfficiency == -1.0) {
742                 this.quantumEfficiency = Double.NaN;
743             } else {
744                 this.quantumEfficiency = quantumEfficiency;
745             }
746         }
747 
748         /**
749          * Get the applied voltage.
750          * @return the applied voltage in Volts
751          */
752         public double getAppliedVoltage() {
753             return appliedVoltage;
754         }
755 
756         /**
757          * Set the applied voltage.
758          * @param appliedVoltage the applied voltage to set in Volts
759          */
760         public void setAppliedVoltage(final double appliedVoltage) {
761             // NOTE: The quantumEfficiency may be -1.0, which means 'not available' or 'not applicable'
762             if (appliedVoltage == -1.0) {
763                 this.appliedVoltage = Double.NaN;
764             } else {
765                 this.appliedVoltage = appliedVoltage;
766             }
767         }
768 
769         /**
770          * Get the dark count.
771          * @return the dark count in Hz
772          */
773         public double getDarkCount() {
774             return darkCount;
775         }
776 
777         /**
778          * Set the dark count.
779          * @param darkCount the dark count to set in Hz
780          */
781         public void setDarkCount(final double darkCount) {
782          // NOTE: The quantumEfficiency may be -1.0, which means 'not available'
783             if (darkCount == -1.0e3) { // -1=na, kHz --> Hz
784                 this.darkCount = Double.NaN;
785             } else {
786                 this.darkCount = darkCount;
787             }
788         }
789 
790         /**
791          * Get the output pulse type.
792          * @return the output pulse type
793          */
794         public String getOutputPulseType() {
795             return outputPulseType;
796         }
797 
798         /**
799          * Set the output pulse type.
800          * @param outputPulseType the output pulse type to set
801          */
802         public void setOutputPulseType(final String outputPulseType) {
803             this.outputPulseType = outputPulseType;
804         }
805 
806         /**
807          * Get the output pulse width.
808          * @return the output pulse width in ps
809          */
810         public double getOutputPulseWidth() {
811             return outputPulseWidth;
812         }
813 
814         /**
815          * Set the output pulse width.
816          * @param outputPulseWidth the output pulse width to set in ps
817          */
818         public void setOutputPulseWidth(final double outputPulseWidth) {
819             // NOTE: The quantumEfficiency may be -1.0, which means 'not available' or 'not applicable'
820             if (outputPulseWidth == -1.0) {
821                 this.outputPulseWidth = Double.NaN;
822             } else {
823                 this.outputPulseWidth = outputPulseWidth;
824             }
825         }
826 
827         /**
828          * Get the spectral filter.
829          * @return the spectral filter in meters
830          */
831         public double getSpectralFilter() {
832             return spectralFilter;
833         }
834 
835         /**
836          * Set the spectral filter.
837          * @param spectralFilter  the spectral filter to set in meters
838          */
839         public void setSpectralFilter(final double spectralFilter) {
840             this.spectralFilter = spectralFilter;
841         }
842 
843         /**
844          * Get the percentage of transmission of spectral filter.
845          * @return the percentage of transmission of spectral filter
846          */
847         public double getTransmissionOfSpectralFilter() {
848             return transmissionOfSpectralFilter;
849         }
850 
851         /**
852          * Set the percentage of transmission of spectral filter.
853          * @param transmissionOfSpectralFilter the percentage to set
854          */
855         public void setTransmissionOfSpectralFilter(final double transmissionOfSpectralFilter) {
856             this.transmissionOfSpectralFilter = transmissionOfSpectralFilter;
857         }
858 
859         /**
860          * Get the spatial filter.
861          * @return the spatial filter in arcsec
862          */
863         public double getSpatialFilter() {
864             return spatialFilter;
865         }
866 
867         /**
868          * Set the spatial filter.
869          * @param spatialFilter the spatial filter to set in arcsec
870          */
871         public void setSpatialFilter(final double spatialFilter) {
872             this.spatialFilter = spatialFilter;
873         }
874 
875         /**
876          * Get the external signal processing.
877          * @return the external signal processing
878          */
879         public String getExternalSignalProcessing() {
880             return externalSignalProcessing;
881         }
882 
883         /**
884          * Set the external signal processing.
885          * @param externalSignalProcessing the external signal processing to set
886          */
887         public void setExternalSignalProcessing(final String externalSignalProcessing) {
888             this.externalSignalProcessing = externalSignalProcessing;
889         }
890 
891         /**
892          * Get the amplifier gain.
893          * @return the amplifier gain
894          */
895         public double getAmplifierGain() {
896             return amplifierGain;
897         }
898 
899         /**
900          * Set the amplifier gain.
901          * @param amplifierGain the amplifier gain to set
902          */
903         public void setAmplifierGain(final double amplifierGain) {
904             this.amplifierGain = amplifierGain;
905         }
906 
907         /**
908          * Get the amplifier bandwidth.
909          * @return the amplifier bandwidth in Hz
910          */
911         public double getAmplifierBandwidth() {
912             return amplifierBandwidth;
913         }
914 
915         /**
916          * Set the amplifier bandwidth.
917          * @param amplifierBandwidth the amplifier bandwidth to set in Hz
918          */
919         public void setAmplifierBandwidth(final double amplifierBandwidth) {
920             this.amplifierBandwidth = amplifierBandwidth;
921         }
922 
923         /**
924          * Get the amplifier in use.
925          * @return the amplifier in use
926          */
927         public String getAmplifierInUse() {
928             return amplifierInUse;
929         }
930 
931         /**
932          * Set the amplifier in use.
933          * @param amplifierInUse the amplifier in use to set
934          */
935         public void setAmplifierInUse(final String amplifierInUse) {
936             this.amplifierInUse = amplifierInUse;
937         }
938 
939         /** {@inheritDoc} */
940         @Override
941         public String toCrdString() {
942             return String.format(Locale.US, "C2 0 %s", toString());
943         }
944 
945         @Override
946         public String toString() {
947             // CRD suggested format, excluding the record type
948             // applicableWavelength, spectralFilter: m --> nm
949             // darkCount, amplifierBandwidth: Hz --> kHz
950             final String str = String.format(Locale.US,
951                     "%s %s %.3f %.2f %.1f %.1f %s %.1f %.2f %.1f %.1f %s %.1f %.1f %s",
952                     getConfigurationId(), detectorType,
953                     applicableWavelength * 1e9, quantumEfficiency,
954                     appliedVoltage, darkCount * 1e-3, outputPulseType,
955                     outputPulseWidth, spectralFilter * 1e9,
956                     transmissionOfSpectralFilter, spatialFilter,
957                     externalSignalProcessing, amplifierGain,
958                     amplifierBandwidth * 1e-3, amplifierInUse);
959             return CRD.handleNaN(str).replace(',', '.');
960         }
961     }
962 
963     /** Container for timing system configuration record. */
964     public static class TimingSystemConfiguration extends BaseConfiguration {
965 
966         /** Time Source. */
967         private String timeSource;
968 
969         /** Frequency Source. */
970         private String frequencySource;
971 
972         /** Timer. */
973         private String timer;
974 
975         /** Timer Serial Number. */
976         private String timerSerialNumber;
977 
978         /** Epoch delay correction [s]. */
979         private double epochDelayCorrection;
980 
981         /** Empty constructor.
982          * <p>
983          * This constructor is not strictly necessary, but it prevents spurious
984          * javadoc warnings with JDK 18 and later.
985          * </p>
986          * @since 12.0
987          */
988         public TimingSystemConfiguration() {
989             // nothing to do
990         }
991 
992         /**
993          * Get the time source.
994          * @return the time source
995          */
996         public String getTimeSource() {
997             return timeSource;
998         }
999 
1000         /**
1001          * Get the local timing system configuration ID.
1002          * @return the local timing system configuration ID
1003          */
1004         public String getLocalTimingId() {
1005             return getConfigurationId();
1006         }
1007 
1008         /**
1009          * Set the local timing system configuration ID.
1010          * @param localTimingId the local timing system configuration ID to set
1011          */
1012         public void setLocalTimingId(final String localTimingId) {
1013             setConfigurationId(localTimingId);
1014         }
1015 
1016         /**
1017          * Set the time source.
1018          * @param timeSource the time source to set
1019          */
1020         public void setTimeSource(final String timeSource) {
1021             this.timeSource = timeSource;
1022         }
1023 
1024         /**
1025          * Get the frequency source.
1026          * @return the frequency source
1027          */
1028         public String getFrequencySource() {
1029             return frequencySource;
1030         }
1031 
1032         /**
1033          * Set the frequency source.
1034          * @param frequencySource the frequency source to set
1035          */
1036         public void setFrequencySource(final String frequencySource) {
1037             this.frequencySource = frequencySource;
1038         }
1039 
1040         /**
1041          * Get the timer name.
1042          * @return the timer name
1043          */
1044         public String getTimer() {
1045             return timer;
1046         }
1047 
1048         /**
1049          * Set the timer name.
1050          * @param timer the timer name to set
1051          */
1052         public void setTimer(final String timer) {
1053             this.timer = timer;
1054         }
1055 
1056         /**
1057          * Get the timer serial number.
1058          * @return the timer serial number
1059          */
1060         public String getTimerSerialNumber() {
1061             return timerSerialNumber;
1062         }
1063 
1064         /**
1065          * Set the timer serial number.
1066          * @param timerSerialNumber the timer serial number to set
1067          */
1068         public void setTimerSerialNumber(final String timerSerialNumber) {
1069             this.timerSerialNumber = timerSerialNumber;
1070         }
1071 
1072         /**
1073          * Get the epoch delay correction.
1074          * @return the epoch delay correction in seconds
1075          */
1076         public double getEpochDelayCorrection() {
1077             return epochDelayCorrection;
1078         }
1079 
1080         /**
1081          * Set the epoch delay correction.
1082          * @param epochDelayCorrection the epoch delay correction to set in seconds
1083          */
1084         public void setEpochDelayCorrection(final double epochDelayCorrection) {
1085             this.epochDelayCorrection = epochDelayCorrection;
1086         }
1087 
1088         /** {@inheritDoc} */
1089         @Override
1090         public String toCrdString() {
1091             return String.format(Locale.US, "C3 0 %s", toString());
1092         }
1093 
1094         @Override
1095         public String toString() {
1096             // CRD suggested format, excluding the record type
1097             // epochDelayCorrection: s --> us
1098             final String str = String.format(Locale.US, "%s %s %s %s %s %.1f",
1099                     getConfigurationId(), timeSource, frequencySource, timer,
1100                     timerSerialNumber, epochDelayCorrection * 1e6);
1101             return CRD.handleNaN(str).replace(',', '.');
1102         }
1103     }
1104 
1105     /** Container for transponder configuration record. */
1106     public static class TransponderConfiguration extends BaseConfiguration {
1107 
1108         /** Estimated Station UTC offset [s]. */
1109         private double stationUTCOffset;
1110 
1111         /** Estimated Station Oscillator Drift in parts in 10^15. */
1112         private double stationOscDrift;
1113 
1114         /** Estimated Transponder UTC offset [s]. */
1115         private double transpUTCOffset;
1116 
1117         /** Estimated Transponder Oscillator Drift in parts in 10^15. */
1118         private double transpOscDrift;
1119 
1120         /** Transponder Clock Reference Time. */
1121         private double transpClkRefTime;
1122 
1123         /** Station clock offset and drift applied indicator. */
1124         private int stationClockAndDriftApplied;
1125 
1126         /** Spacecraft clock offset and drift applied indicator . */
1127         private int spacecraftClockAndDriftApplied;
1128 
1129         /** Spacecraft time simplified flag. */
1130         private boolean isSpacecraftTimeSimplified;
1131 
1132         /** Empty constructor.
1133          * <p>
1134          * This constructor is not strictly necessary, but it prevents spurious
1135          * javadoc warnings with JDK 18 and later.
1136          * </p>
1137          * @since 12.0
1138          */
1139         public TransponderConfiguration() {
1140             // nothing to do
1141         }
1142 
1143         /**
1144          * Get the transponder configuration ID.
1145          * @return the transponder configuration ID
1146          */
1147         public String getTransponderId() {
1148             return getConfigurationId();
1149         }
1150 
1151         /**
1152          * Set the transponder configuration ID.
1153          * @param transponderId the transponder configuration ID to set
1154          */
1155         public void setTransponderId(final String transponderId) {
1156             setConfigurationId(transponderId);
1157         }
1158 
1159         /**
1160          * Get the estimated station UTC offset.
1161          * @return the estimated station UTC offset in seconds
1162          */
1163         public double getStationUTCOffset() {
1164             return stationUTCOffset;
1165         }
1166 
1167         /**
1168          * Set the estimated station UTC offset.
1169          * @param stationUTCOffset the estimated station UTC offset to set in seconds
1170          */
1171         public void setStationUTCOffset(final double stationUTCOffset) {
1172             this.stationUTCOffset = stationUTCOffset;
1173         }
1174 
1175         /**
1176          * Get the estimated station oscillator drift in parts in 10¹⁵.
1177          * @return the station oscillator drift
1178          */
1179         public double getStationOscDrift() {
1180             return stationOscDrift;
1181         }
1182 
1183         /**
1184          * Set the estimated station oscillator drift in parts in 10¹⁵.
1185          * @param stationOscDrift the station oscillator drift to set
1186          */
1187         public void setStationOscDrift(final double stationOscDrift) {
1188             this.stationOscDrift = stationOscDrift;
1189         }
1190 
1191         /**
1192          * Get the estimated transponder UTC offset.
1193          * @return the estimated transponder UTC offset in seconds
1194          */
1195         public double getTranspUTCOffset() {
1196             return transpUTCOffset;
1197         }
1198 
1199         /**
1200          * Set the estimated transponder UTC offset.
1201          * @param transpUTCOffset the estimated transponder UTC offset to set in seconds
1202          */
1203         public void setTranspUTCOffset(final double transpUTCOffset) {
1204             this.transpUTCOffset = transpUTCOffset;
1205         }
1206 
1207         /**
1208          * Get the estimated transponder oscillator drift in parts in 10¹⁵.
1209          * @return the estimated transponder oscillator drift
1210          */
1211         public double getTranspOscDrift() {
1212             return transpOscDrift;
1213         }
1214 
1215         /**
1216          * Set the estimated transponder oscillator drift in parts in 10¹⁵.
1217          * @param transpOscDrift the estimated transponder oscillator drift to set
1218          */
1219         public void setTranspOscDrift(final double transpOscDrift) {
1220             this.transpOscDrift = transpOscDrift;
1221         }
1222 
1223         /**
1224          * Get the transponder clock reference time.
1225          * @return the transponder clock reference time
1226          */
1227         public double getTranspClkRefTime() {
1228             return transpClkRefTime;
1229         }
1230 
1231         /**
1232          * Set the transponder clock reference time.
1233          * @param transpClkRefTime the transponder clock reference time to set
1234          */
1235         public void setTranspClkRefTime(final double transpClkRefTime) {
1236             this.transpClkRefTime = transpClkRefTime;
1237         }
1238 
1239         /**
1240          * Get the station clock offset and drift applied indicator.
1241          * @return the station clock offset and drift applied indicator
1242          */
1243         public int getStationClockAndDriftApplied() {
1244             return stationClockAndDriftApplied;
1245         }
1246 
1247         /**
1248          * Set the station clock offset and drift applied indicator.
1249          * @param stationClockAndDriftApplied the indicator to set
1250          */
1251         public void setStationClockAndDriftApplied(final int stationClockAndDriftApplied) {
1252             this.stationClockAndDriftApplied = stationClockAndDriftApplied;
1253         }
1254 
1255         /**
1256          * Get the spacecraft clock offset and drift applied indicator.
1257          * @return the spacecraft clock offset and drift applied indicator
1258          */
1259         public int getSpacecraftClockAndDriftApplied() {
1260             return spacecraftClockAndDriftApplied;
1261         }
1262 
1263         /**
1264          * Set the spacecraft clock offset and drift applied indicator.
1265          * @param spacecraftClockAndDriftApplied the indicator to set
1266          */
1267         public void setSpacecraftClockAndDriftApplied(final int spacecraftClockAndDriftApplied) {
1268             this.spacecraftClockAndDriftApplied = spacecraftClockAndDriftApplied;
1269         }
1270 
1271         /**
1272          * Get the spacecraft time simplified flag.
1273          * @return true if spacecraft time is simplified
1274          */
1275         public boolean isSpacecraftTimeSimplified() {
1276             return isSpacecraftTimeSimplified;
1277         }
1278 
1279         /**
1280          * Set the spacecraft time simplified flag.
1281          * @param isSpacecraftTimeSimplified true if spacecraft time is simplified
1282          */
1283         public void setIsSpacecraftTimeSimplified(final boolean isSpacecraftTimeSimplified) {
1284             this.isSpacecraftTimeSimplified = isSpacecraftTimeSimplified;
1285         }
1286 
1287         /** {@inheritDoc} */
1288         @Override
1289         public String toCrdString() {
1290             return String.format(Locale.US, "C4 0 %s", toString());
1291         }
1292 
1293         @Override
1294         public String toString() {
1295             // CRD suggested format, excluding the record type
1296             // stationUTCOffset, transpUTCOffset: s --> ns
1297             final String str = String.format(
1298                     "%s %.3f %.2f %.3f %.2f %.12f %d %d %d",
1299                     getConfigurationId(),
1300                     stationUTCOffset * 1e9, stationOscDrift,
1301                     transpUTCOffset * 1e9, transpOscDrift,
1302                     transpClkRefTime,
1303                     stationClockAndDriftApplied, spacecraftClockAndDriftApplied,
1304                     isSpacecraftTimeSimplified ? 1 : 0);
1305             return CRD.handleNaN(str).replace(',', '.');
1306         }
1307 
1308     }
1309 
1310     /** Container for software configuration record. */
1311     public static class SoftwareConfiguration extends BaseConfiguration {
1312 
1313         /** Pattern of "[\\s+\\[\\]]". */
1314         private static final Pattern PATTERN_WHITESPACE_OR_SQUAREBRACKET = Pattern.compile("[\\s+\\[\\]]");
1315 
1316         /** Tracking software in measurement path. */
1317         private String[] trackingSoftwares;
1318 
1319         /** Tracking software version(s). */
1320         private String[] trackingSoftwareVersions;
1321 
1322         /** Processing software in measurement path. */
1323         private String[] processingSoftwares;
1324 
1325         /** Processing software version(s). */
1326         private String[] processingSoftwareVersions;
1327 
1328         /** Empty constructor.
1329          * <p>
1330          * This constructor is not strictly necessary, but it prevents spurious
1331          * javadoc warnings with JDK 18 and later.
1332          * </p>
1333          * @since 12.0
1334          */
1335         public SoftwareConfiguration() {
1336             // nothing to do
1337         }
1338 
1339         /**
1340          * Get the software configuration ID.
1341          * @return the software configuration ID.
1342          */
1343         public String getSoftwareId() {
1344             return getConfigurationId();
1345         }
1346 
1347         /**
1348          * Set the software configuration ID.
1349          * @param softwareId the software configuration ID
1350          */
1351         public void setSoftwareId(final String softwareId) {
1352             setConfigurationId(softwareId);
1353         }
1354 
1355         /**
1356          * Get the tracking softwares.
1357          * @return the tracking softwares
1358          */
1359         public String[] getTrackingSoftwares() {
1360             return trackingSoftwares.clone();
1361         }
1362 
1363         /**
1364          * Set the tracking softwares.
1365          * @param trackingSoftwares the tracking softwares to set
1366          */
1367         public void setTrackingSoftwares(final String[] trackingSoftwares) {
1368             this.trackingSoftwares = trackingSoftwares.clone();
1369         }
1370 
1371         /**
1372          * Get the tracking software versions.
1373          * @return the tracking software versions
1374          */
1375         public String[] getTrackingSoftwareVersions() {
1376             return trackingSoftwareVersions.clone();
1377         }
1378 
1379         /**
1380          * Set the tracking software versions.
1381          * @param trackingSoftwareVersions the tracking software versions to set
1382          */
1383         public void setTrackingSoftwareVersions(final String[] trackingSoftwareVersions) {
1384             this.trackingSoftwareVersions = trackingSoftwareVersions.clone();
1385         }
1386 
1387         /**
1388          * Get the processing softwares.
1389          * @return the processing softwares
1390          */
1391         public String[] getProcessingSoftwares() {
1392             return processingSoftwares.clone();
1393         }
1394 
1395         /**
1396          * Set the processing softwares.
1397          * @param processingSoftwares the processing softwares to set
1398          */
1399         public void setProcessingSoftwares(final String[] processingSoftwares) {
1400             this.processingSoftwares = processingSoftwares.clone();
1401         }
1402 
1403         /**
1404          * Get the processing software versions.
1405          * @return the processing software versions
1406          */
1407         public String[] getProcessingSoftwareVersions() {
1408             return processingSoftwareVersions.clone();
1409         }
1410 
1411         /**
1412          * Set the processing software versions.
1413          * @param processingSoftwareVersions the processing software versions to set
1414          */
1415         public void setProcessingSoftwareVersions(final String[] processingSoftwareVersions) {
1416             this.processingSoftwareVersions = processingSoftwareVersions.clone();
1417         }
1418 
1419         private static String formatArray(final String[] arr) {
1420             // comma delimited
1421             // "[Monitor, Sattrk]" ==> "Monitor,Sattrk"
1422             // "[conpro, crd_cal, PoissonCRD, gnp]" ==> "conpro,crd_cal,PoissonCRD,gnp"
1423             final String s = Arrays.toString(arr);
1424             return PATTERN_WHITESPACE_OR_SQUAREBRACKET.matcher(s).replaceAll("");
1425         }
1426 
1427         /** {@inheritDoc} */
1428         @Override
1429         public String toCrdString() {
1430             return String.format(Locale.US, "C5 0 %s", toString());
1431         }
1432 
1433         @Override
1434         public String toString() {
1435             // CRD suggested format, excluding the record type
1436             return String.format(Locale.US, "%s %s %s %s %s", getConfigurationId(),
1437                     formatArray(trackingSoftwares),
1438                     formatArray(trackingSoftwareVersions),
1439                     formatArray(processingSoftwares),
1440                     formatArray(processingSoftwareVersions));
1441         }
1442 
1443     }
1444 
1445     /** Container for meteorological configuration record. */
1446     public static class MeteorologicalConfiguration extends BaseConfiguration {
1447 
1448         /** Pressure Sensor Manufacturer. */
1449         private String pressSensorManufacturer;
1450 
1451         /** Pressure Sensor Model. */
1452         private String pressSensorModel;
1453 
1454         /** Pressure Sensor Serial Number. */
1455         private String pressSensorSerialNumber;
1456 
1457         /** Temperature Sensor Manufacturer. */
1458         private String tempSensorManufacturer;
1459 
1460         /** Temperature Sensor Model. */
1461         private String tempSensorModel;
1462 
1463         /** Temperature Sensor Serial Number. */
1464         private String tempSensorSerialNumber;
1465 
1466         /** Humidity Sensor Manufacturer. */
1467         private String humiSensorManufacturer;
1468 
1469         /** Humidity Sensor Model. */
1470         private String humiSensorModel;
1471 
1472         /** Humidity Sensor Serial Number. */
1473         private String humiSensorSerialNumber;
1474 
1475         /** Empty constructor.
1476          * <p>
1477          * This constructor is not strictly necessary, but it prevents spurious
1478          * javadoc warnings with JDK 18 and later.
1479          * </p>
1480          * @since 12.0
1481          */
1482         public MeteorologicalConfiguration() {
1483             // nothing to do
1484         }
1485 
1486         /**
1487          * Get the meteorological configuration ID.
1488          * @return the meteorological configuration ID
1489          */
1490         public String getMeteorologicalId() {
1491             return getConfigurationId();
1492         }
1493 
1494         /**
1495          * Set the meteorological configuration ID.
1496          * @param meteorologicalId the meteorological configuration ID to set
1497          */
1498         public void setMeteorologicalId(final String meteorologicalId) {
1499             setConfigurationId(meteorologicalId);
1500         }
1501 
1502         /**
1503          * Get the pressure sensor manufacturer.
1504          * @return the pressure sensor manufacturer
1505          */
1506         public String getPressSensorManufacturer() {
1507             return pressSensorManufacturer;
1508         }
1509 
1510         /**
1511          * Set the pressure sensor manufacturer.
1512          * @param pressSensorManufacturer the manufacturer to set
1513          */
1514         public void setPressSensorManufacturer(final String pressSensorManufacturer) {
1515             this.pressSensorManufacturer = pressSensorManufacturer;
1516         }
1517 
1518         /**
1519          * Get the pressure sensor model.
1520          * @return the pressure sensor model
1521          */
1522         public String getPressSensorModel() {
1523             return pressSensorModel;
1524         }
1525 
1526         /**
1527          * Set the pressure sensor model.
1528          * @param pressSensorModel the model to set
1529          */
1530         public void setPressSensorModel(final String pressSensorModel) {
1531             this.pressSensorModel = pressSensorModel;
1532         }
1533 
1534         /**
1535          * Get the pressure sensor serial number.
1536          * @return the pressure sensor serial number
1537          */
1538         public String getPressSensorSerialNumber() {
1539             return pressSensorSerialNumber;
1540         }
1541 
1542         /**
1543          * Set the pressure sensor serial number.
1544          * @param pressSensorSerialNumber the serial number to set
1545          */
1546         public void setPressSensorSerialNumber(final String pressSensorSerialNumber) {
1547             this.pressSensorSerialNumber = pressSensorSerialNumber;
1548         }
1549 
1550         /**
1551          * Get the temperature sensor manufacturer.
1552          * @return the temperature sensor manufacturer
1553          */
1554         public String getTempSensorManufacturer() {
1555             return tempSensorManufacturer;
1556         }
1557 
1558         /**
1559          * Set the temperature sensor manufacturer.
1560          * @param tempSensorManufacturer the temperature sensor manufacturer
1561          */
1562         public void setTempSensorManufacturer(final String tempSensorManufacturer) {
1563             this.tempSensorManufacturer = tempSensorManufacturer;
1564         }
1565 
1566         /**
1567          * Get the temperature sensor model.
1568          * @return the temperature sensor model
1569          */
1570         public String getTempSensorModel() {
1571             return tempSensorModel;
1572         }
1573 
1574         /**
1575          * Set the temperature sensor model.
1576          * @param tempSensorModel the model to set
1577          */
1578         public void setTempSensorModel(final String tempSensorModel) {
1579             this.tempSensorModel = tempSensorModel;
1580         }
1581 
1582         /**
1583          * Get the temperature sensor serial number.
1584          * @return the temperature sensor serial number
1585          */
1586         public String getTempSensorSerialNumber() {
1587             return tempSensorSerialNumber;
1588         }
1589 
1590         /**
1591          * Set the temperature sensor serial number.
1592          * @param tempSensorSerialNumber the serial number to set
1593          */
1594         public void setTempSensorSerialNumber(final String tempSensorSerialNumber) {
1595             this.tempSensorSerialNumber = tempSensorSerialNumber;
1596         }
1597 
1598         /**
1599          * Get the humidity sensor manufacturer.
1600          * @return the humidity sensor manufacturer
1601          */
1602         public String getHumiSensorManufacturer() {
1603             return humiSensorManufacturer;
1604         }
1605 
1606         /**
1607          * Set the humidity sensor manufacturer.
1608          * @param humiSensorManufacturer the manufacturer to set
1609          */
1610         public void setHumiSensorManufacturer(final String humiSensorManufacturer) {
1611             this.humiSensorManufacturer = humiSensorManufacturer;
1612         }
1613 
1614         /**
1615          * Get the humidity sensor model.
1616          * @return the humidity sensor model
1617          */
1618         public String getHumiSensorModel() {
1619             return humiSensorModel;
1620         }
1621 
1622         /**
1623          * Set the humidity sensor model.
1624          * @param humiSensorModel the model to set
1625          */
1626         public void setHumiSensorModel(final String humiSensorModel) {
1627             this.humiSensorModel = humiSensorModel;
1628         }
1629 
1630         /**
1631          * Get the humidity sensor serial number.
1632          * @return the humidity sensor serial number
1633          */
1634         public String getHumiSensorSerialNumber() {
1635             return humiSensorSerialNumber;
1636         }
1637 
1638         /**
1639          * Set the humidity sensor serial number.
1640          * @param humiSensorSerialNumber the serial number to set
1641          */
1642         public void setHumiSensorSerialNumber(final String humiSensorSerialNumber) {
1643             this.humiSensorSerialNumber = humiSensorSerialNumber;
1644         }
1645 
1646         /** {@inheritDoc} */
1647         @Override
1648         public String toCrdString() {
1649             return String.format("C6 0 %s", toString());
1650         }
1651 
1652         @Override
1653         public String toString() {
1654             // CRD suggested format, excluding the record type
1655             return String.format("%s %s %s %s %s %s %s %s %s %s",
1656                     getConfigurationId(), pressSensorManufacturer,
1657                     pressSensorModel, pressSensorSerialNumber,
1658                     tempSensorManufacturer, tempSensorModel,
1659                     tempSensorSerialNumber, humiSensorManufacturer,
1660                     humiSensorModel, humiSensorSerialNumber);
1661         }
1662     }
1663 
1664     /**
1665      * Container for calibration target configuration record.
1666      * @since 12.0
1667      */
1668     public static class CalibrationTargetConfiguration extends BaseConfiguration {
1669 
1670         /** Target name or ID. */
1671         private String targetName;
1672 
1673         /** Surveyed target distance. */
1674         private double surveyedTargetDistance;
1675 
1676         /** Survey error. */
1677         private double surveyError;
1678 
1679         /** Sum of all constant delays (m, one way). */
1680         private double sumOfAllConstantDelays;
1681 
1682         /** Pulse Energy [mJ]. */
1683         private double pulseEnergy;
1684 
1685         /** Processing software name. */
1686         private String processingSoftwareName;
1687 
1688         /** Processing software version. */
1689         private String processingSoftwareVersion;
1690 
1691         /** Empty constructor.
1692          * <p>
1693          * This constructor is not strictly necessary, but it prevents spurious
1694          * javadoc warnings with JDK 18 and later.
1695          * </p>
1696          * @since 12.0
1697          */
1698         public CalibrationTargetConfiguration() {
1699             // nothing to do
1700         }
1701 
1702         /**
1703          * Get the target name or ID.
1704          * @return the target name or ID
1705          */
1706         public String getTargetName() {
1707             return targetName;
1708         }
1709 
1710         /**
1711          * Set the target name or ID.
1712          * @param targetName target name or ID to set
1713          */
1714         public void setTargetName(final String targetName) {
1715             this.targetName = targetName;
1716         }
1717 
1718         /**
1719          * Get the surveyed target distance.
1720          * @return the surveyed target distance in meters
1721          */
1722         public double getSurveyedTargetDistance() {
1723             return surveyedTargetDistance;
1724         }
1725 
1726         /**
1727          * Set the surveyed target distance.
1728          * @param surveyedTargetDistance the surveyed target distance to set, in meters
1729          */
1730         public void setSurveyedTargetDistance(final double surveyedTargetDistance) {
1731             this.surveyedTargetDistance = surveyedTargetDistance;
1732         }
1733 
1734         /**
1735          * Get the survey error.
1736          * @return the survey error in meters
1737          */
1738         public double getSurveyError() {
1739             return surveyError;
1740         }
1741 
1742         /**
1743          * Set the survey error.
1744          * @param surveyError the survey error to set, in meters
1745          */
1746         public void setSurveyError(final double surveyError) {
1747             this.surveyError = surveyError;
1748         }
1749 
1750         /**
1751          * Get the sum of all constant delays (electronic, geometric, optical) that
1752          * are not included in the time of flight measurements or time- variant
1753          * or point angle-variant delays in the “42” record below (m, one way).
1754          * @return the sum of all constant delays
1755          */
1756         public double getSumOfAllConstantDelays() {
1757             return sumOfAllConstantDelays;
1758         }
1759 
1760         /**
1761          * Set the sum of all constant delays (electronic, geometric, optical) that
1762          * are not included in the time of flight measurements or time- variant
1763          * or point angle-variant delays in the “42” record below (m, one way).
1764          * @param sumOfAllConstantDelays the sum of all constant delays
1765          */
1766         public void setSumOfAllConstantDelays(final double sumOfAllConstantDelays) {
1767             this.sumOfAllConstantDelays = sumOfAllConstantDelays;
1768         }
1769 
1770         /**
1771          * Get the pulse energy.
1772          * @return the pulse energy in mJ
1773          */
1774         public double getPulseEnergy() {
1775             return pulseEnergy;
1776         }
1777 
1778         /**
1779          * Set the pulse energy.
1780          * @param pulseEnergy the pulse energy to set, in mJ
1781          */
1782         public void setPulseEnergy(final double pulseEnergy) {
1783             this.pulseEnergy = pulseEnergy;
1784         }
1785 
1786         /**
1787          * Get the processing software name.
1788          * @return the processing software name
1789          */
1790         public String getProcessingSoftwareName() {
1791             return processingSoftwareName;
1792         }
1793 
1794         /**
1795          * Set the processing software name.
1796          * @param processingSoftwareName the processing software name to set
1797          */
1798         public void setProcessingSoftwareName(final String processingSoftwareName) {
1799             this.processingSoftwareName = processingSoftwareName;
1800         }
1801 
1802         /**
1803          * Get the processing software version.
1804          * @return the processing software version
1805          */
1806         public String getProcessingSoftwareVersion() {
1807             return processingSoftwareVersion;
1808         }
1809 
1810         /**
1811          * Set the processing software version.
1812          * @param processingSoftwareVersion the processing software version to set
1813          */
1814         public void setProcessingSoftwareVersion(final String processingSoftwareVersion) {
1815             this.processingSoftwareVersion = processingSoftwareVersion;
1816         }
1817 
1818         /** {@inheritDoc} */
1819         @Override
1820         public String toCrdString() {
1821             return String.format("C7 0 %s", toString());
1822         }
1823 
1824         @Override
1825         public String toString() {
1826             // CRD suggested format, excluding the record type
1827             // surveyError: m --> mm
1828             final String str = String.format("%s %s %.5f %.2f %.4f %.2f %s %s",
1829                     getConfigurationId(), targetName, surveyedTargetDistance,
1830                     surveyError * 1e3, sumOfAllConstantDelays, pulseEnergy,
1831                     processingSoftwareName, processingSoftwareVersion);
1832             return CRD.handleNaN(str).replace(',', '.');
1833         }
1834 
1835     }
1836 }