1 /* Copyright 2002-2021 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.ccsds.ndm.adm.aem;
18
19 import java.io.IOException;
20 import java.util.List;
21
22 import org.orekit.errors.OrekitIllegalArgumentException;
23 import org.orekit.errors.OrekitMessages;
24 import org.orekit.files.ccsds.definitions.FrameFacade;
25 import org.orekit.files.ccsds.section.Header;
26 import org.orekit.files.ccsds.utils.FileFormat;
27 import org.orekit.files.ccsds.utils.generation.Generator;
28 import org.orekit.files.ccsds.utils.generation.KvnGenerator;
29 import org.orekit.files.ccsds.utils.generation.XmlGenerator;
30 import org.orekit.files.general.AttitudeEphemerisFile;
31 import org.orekit.files.general.AttitudeEphemerisFile.SatelliteAttitudeEphemeris;
32 import org.orekit.files.general.AttitudeEphemerisFileWriter;
33 import org.orekit.utils.TimeStampedAngularCoordinates;
34
35 /** An {@link AttitudeEphemerisFileWriter} generating {@link Aem AEM} files.
36 * @author Bryan Cazabonne
37 * @since 11.0
38 */
39 public class AttitudeWriter implements AttitudeEphemerisFileWriter {
40
41 /** Underlying writer. */
42 private final AemWriter writer;
43
44 /** Header. */
45 private final Header header;
46
47 /** Current metadata. */
48 private final AemMetadata metadata;
49
50 /** File format to use. */
51 private final FileFormat fileFormat;
52
53 /** Output name for error messages. */
54 private final String outputName;
55
56 /** Column number for aligning units. */
57 private final int unitsColumn;
58
59 /**
60 * Constructor used to create a new AEM writer configured with the necessary parameters
61 * to successfully fill in all required fields that aren't part of a standard object.
62 * <p>
63 * If the mandatory header entries are not present (or if header is null),
64 * built-in defaults will be used
65 * </p>
66 * <p>
67 * The writer is built from the complete header and partial metadata. The template
68 * metadata is used to initialize and independent local copy, that will be updated
69 * as new segments are written (with at least the segment start and stop will change,
70 * but some other parts may change too). The {@code template} argument itself is not
71 * changed.
72 * </p>
73 * <p>
74 * Calling this constructor directly is not recommended. Users should rather use
75 * {@link org.orekit.files.ccsds.ndm.WriterBuilder#buildAemWriter()}.
76 * </p>
77 * @param writer underlying writer
78 * @param header file header (may be null)
79 * @param template template for metadata
80 * @param fileFormat file format to use
81 * @param outputName output name for error messages
82 * @param unitsColumn columns number for aligning units (if negative or zero, units are not output)
83 * @since 11.0
84 */
85 public AttitudeWriter(final AemWriter writer,
86 final Header header, final AemMetadata template,
87 final FileFormat fileFormat, final String outputName,
88 final int unitsColumn) {
89 this.writer = writer;
90 this.header = header;
91 this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
92 this.fileFormat = fileFormat;
93 this.outputName = outputName;
94 this.unitsColumn = unitsColumn;
95 }
96
97 /** {@inheritDoc}
98 * <p>
99 * As {@code AttitudeEphemerisFile.SatelliteAttitudeEphemeris} does not have all the entries
100 * from {@link AemMetadata}, the only values that will be extracted from the
101 * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation
102 * method and interpolation degree. The missing values (like object name, local spacecraft
103 * body frame, attitude type...) will be inherited from the template metadata set at writer
104 * {@link #AttitudeWriter(AemWriter, Header, AemMetadata, FileFormat, String, int) construction}.
105 * </p>
106 */
107 @Override
108 public <C extends TimeStampedAngularCoordinates, S extends AttitudeEphemerisFile.AttitudeEphemerisSegment<C>>
109 void write(final Appendable appendable, final AttitudeEphemerisFile<C, S> ephemerisFile)
110 throws IOException {
111
112 if (appendable == null) {
113 throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
114 }
115
116 if (ephemerisFile == null) {
117 return;
118 }
119
120 final SatelliteAttitudeEphemeris<C, S> satEphem =
121 ephemerisFile.getSatellites().get(metadata.getObjectID());
122 if (satEphem == null) {
123 throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND,
124 metadata.getObjectID(), "ephemerisFile");
125 }
126
127 // Get attitude ephemeris segments to output.
128 final List<S> segments = satEphem.getSegments();
129 if (segments.isEmpty()) {
130 // No data -> No output
131 return;
132 }
133
134 try (Generator generator = fileFormat == FileFormat.KVN ?
135 new KvnGenerator(appendable, AemWriter.KVN_PADDING_WIDTH, outputName, unitsColumn) :
136 new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, unitsColumn > 0)) {
137
138 writer.writeHeader(generator, header);
139
140 // Loop on segments
141 for (final S segment : segments) {
142 writeSegment(generator, segment);
143 }
144
145 writer.writeFooter(generator);
146
147 }
148
149 }
150
151 /** Write one segment.
152 * @param generator generator to use for producing output
153 * @param segment segment to write
154 * @param <C> type of the angular coordinates
155 * @param <S> type of the segment
156 * @throws IOException if any buffer writing operations fails
157 */
158 private <C extends TimeStampedAngularCoordinates, S extends AttitudeEphemerisFile.AttitudeEphemerisSegment<C>>
159 void writeSegment(final Generator generator, final S segment) throws IOException {
160
161 // override template metadata with segment values
162 metadata.setStartTime(segment.getStart());
163 metadata.setStopTime(segment.getStop());
164 if (metadata.getEndpoints().getFrameA() == null ||
165 metadata.getEndpoints().getFrameA().asSpacecraftBodyFrame() == null) {
166 // the external frame must be frame A
167 metadata.getEndpoints().setFrameA(FrameFacade.map(segment.getReferenceFrame()));
168 } else {
169 // the external frame must be frame B
170 metadata.getEndpoints().setFrameB(FrameFacade.map(segment.getReferenceFrame()));
171 }
172 metadata.setInterpolationMethod(segment.getInterpolationMethod());
173 metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1);
174 writer.writeMetadata(generator, metadata);
175
176 // Loop on attitude data
177 writer.startAttitudeBlock(generator);
178 if (segment instanceof AemSegment) {
179 generator.writeComments(((AemSegment) segment).getData().getComments());
180 }
181 for (final TimeStampedAngularCoordinates coordinates : segment.getAngularCoordinates()) {
182 writer.writeAttitudeEphemerisLine(generator, metadata, coordinates);
183 }
184 writer.endAttitudeBlock(generator);
185
186 }
187
188 }