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  
18  package org.orekit.files.ccsds.ndm.odm.ocm;
19  
20  import java.io.IOException;
21  import java.util.List;
22  
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.orekit.files.ccsds.definitions.DutyCycleType;
25  import org.orekit.files.ccsds.definitions.TimeConverter;
26  import org.orekit.files.ccsds.section.AbstractWriter;
27  import org.orekit.files.ccsds.utils.FileFormat;
28  import org.orekit.files.ccsds.utils.generation.Generator;
29  import org.orekit.utils.Formatter;
30  import org.orekit.utils.units.Unit;
31  
32  /** Writer for maneuvers history data.
33   * @author Luc Maisonobe
34   * @since 11.0
35   */
36  class OrbitManeuverHistoryWriter extends AbstractWriter {
37  
38      /** Maneuvers history block. */
39      private final OrbitManeuverHistory history;
40  
41      /** Converter for dates. */
42      private final TimeConverter timeConverter;
43  
44      /** Create a writer.
45       * @param maneuverHistory maneuvers history to write
46       * @param timeConverter converter for dates
47       */
48      OrbitManeuverHistoryWriter(final OrbitManeuverHistory maneuverHistory,
49                            final TimeConverter timeConverter) {
50          super(OcmDataSubStructureKey.man.name(), OcmDataSubStructureKey.MAN.name());
51          this.history       = maneuverHistory;
52          this.timeConverter = timeConverter;
53      }
54  
55      /** {@inheritDoc} */
56      @Override
57      protected void writeContent(final Generator generator) throws IOException {
58  
59          // maneuvers history block
60          final OrbitManeuverHistoryMetadata metadata = history.getMetadata();
61          generator.writeComments(metadata.getComments());
62  
63          // identifiers
64          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_ID.name(),        metadata.getManID(),       null, false);
65          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PREV_ID.name(),   metadata.getManPrevID(),   null, false);
66          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_NEXT_ID.name(),   metadata.getManNextID(),   null, false);
67          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_BASIS.name(),     metadata.getManBasis(),          false);
68          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_BASIS_ID.name(),  metadata.getManBasisID(),  null, false);
69          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_DEVICE_ID.name(), metadata.getManDeviceID(), null, false);
70  
71          // time
72          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PREV_EPOCH.name(), timeConverter, metadata.getManPrevEpoch(), true, false);
73          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_NEXT_EPOCH.name(), timeConverter, metadata.getManNextEpoch(), true, false);
74  
75          // references
76          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PURPOSE.name(),      metadata.getManPurpose(),                          false);
77          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_PRED_SOURCE.name(),  metadata.getManPredSource(),                 null, false);
78          generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_REF_FRAME.name(),    metadata.getManReferenceFrame().getName(),   null, false);
79          if (!metadata.getManFrameEpoch().equals(timeConverter.getReferenceDate()) &&
80              metadata.getManReferenceFrame().asOrbitRelativeFrame() == null &&
81              metadata.getManReferenceFrame().asSpacecraftBodyFrame() == null) {
82              generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_FRAME_EPOCH.name(),  timeConverter, metadata.getManFrameEpoch(),  true, false);
83          }
84          if (metadata.getGravitationalAssist() != null) {
85              generator.writeEntry(OrbitManeuverHistoryMetadataKey.GRAV_ASSIST_NAME.name(), metadata.getGravitationalAssist().getName(), null, false);
86          }
87  
88          // duty cycle
89          final boolean notContinuous = metadata.getDcType() != DutyCycleType.CONTINUOUS;
90          final boolean timeAndAngle  = metadata.getDcType() == DutyCycleType.TIME_AND_ANGLE;
91          generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TYPE.name(), metadata.getDcType(), false);
92          generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_WIN_OPEN.name(),  timeConverter, metadata.getDcWindowOpen(),  false, notContinuous);
93          generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_WIN_CLOSE.name(), timeConverter, metadata.getDcWindowClose(), false, notContinuous);
94          if (metadata.getDcMinCycles() >= 0) {
95              generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_MIN_CYCLES.name(), metadata.getDcMinCycles(), false);
96          }
97          if (metadata.getDcMaxCycles() >= 0) {
98              generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_MAX_CYCLES.name(), metadata.getDcMaxCycles(), false);
99          }
100         generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_EXEC_START.name(),          timeConverter, metadata.getDcExecStart(), false, notContinuous);
101         generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_EXEC_STOP.name(),           timeConverter, metadata.getDcExecStop(),  false, notContinuous);
102         generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_REF_TIME.name(),            timeConverter, metadata.getDcRefTime(),   false, notContinuous);
103         generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_DURATION.name(), metadata.getDcTimePulseDuration(), Unit.SECOND,  notContinuous);
104         generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_TIME_PULSE_PERIOD.name(),   metadata.getDcTimePulsePeriod(),   Unit.SECOND,  notContinuous);
105         if (timeAndAngle) {
106             generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_REF_DIR.name(), toString(metadata.getDcRefDir(), generator.getFormatter()), null, timeAndAngle);
107             generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_BODY_FRAME.name(),
108                                  metadata.getDcBodyFrame().toString().replace(' ', '_'),
109                                  null, timeAndAngle);
110             generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_BODY_TRIGGER.name(),   toString(metadata.getDcBodyTrigger(), generator.getFormatter()), null,   timeAndAngle);
111             generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_PA_START_ANGLE.name(), metadata.getDcPhaseStartAngle(), Unit.DEGREE,  timeAndAngle);
112             generator.writeEntry(OrbitManeuverHistoryMetadataKey.DC_PA_STOP_ANGLE.name(),  metadata.getDcPhaseStopAngle(),  Unit.DEGREE,  timeAndAngle);
113         }
114 
115         // elements
116         final List<ManeuverFieldType> types       = metadata.getManComposition();
117         final StringBuilder           composition = new StringBuilder();
118         for (int i = 0; i < types.size(); ++i) {
119             if (i > 0) {
120                 composition.append(',');
121             }
122             composition.append(types.get(i).name());
123         }
124         generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_COMPOSITION.name(), composition.toString(),                        null, false);
125         generator.writeEntry(OrbitManeuverHistoryMetadataKey.MAN_UNITS.name(), generator.unitsListToString(metadata.getManUnits()), null, false);
126 
127         // data
128         for (final OrbitManeuver maneuver : history.getManeuvers()) {
129             final StringBuilder line = new StringBuilder();
130             for (int i = 0; i < types.size(); ++i) {
131                 if (i > 0) {
132                     line.append(' ');
133                 }
134                 line.append(types.get(i).outputField(timeConverter, maneuver, generator.getFormatter()));
135             }
136             if (generator.getFormat() == FileFormat.XML) {
137                 generator.writeEntry(Ocm.MAN_LINE, line.toString(), null, true);
138             } else {
139                 generator.writeRawData(line);
140                 generator.newLine();
141             }
142         }
143     }
144 
145     /** Convert a vector to a space separated string.
146      * @param vector vector to convert
147      * @param formatter to use for double and date to string
148      * @return corresponding string
149      */
150     private String toString(final Vector3D vector, final Formatter formatter) {
151         final StringBuilder builder = new StringBuilder();
152         builder.append(formatter.toString(Unit.ONE.fromSI(vector.getX())));
153         builder.append(' ');
154         builder.append(formatter.toString(Unit.ONE.fromSI(vector.getY())));
155         builder.append(' ');
156         builder.append(formatter.toString(Unit.ONE.fromSI(vector.getZ())));
157         return builder.toString();
158     }
159 
160 }