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