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
21 import org.hipparchus.exception.LocalizedCoreFormats;
22 import org.orekit.errors.OrekitException;
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.generation.Generator;
27 import org.orekit.propagation.Propagator;
28 import org.orekit.propagation.SpacecraftState;
29 import org.orekit.propagation.sampling.OrekitFixedStepHandler;
30 import org.orekit.time.AbsoluteDate;
31
32 /**
33 * A writer for AEM files.
34 *
35 * <p> Each instance corresponds to a single AEM file.
36 *
37 * <p> This class can be used as a step handler for a {@link Propagator}.
38 *
39 * <pre>{@code
40 * Propagator propagator = ...; // pre-configured propagator
41 * AEMWriter aemWriter = ...; // pre-configured writer
42 * try (Generator out = ...; // set-up output stream
43 * StreamingAemWriter sw = new StreamingAemWriter(out, aemWriter)) { // set-up streaming writer
44 *
45 * // write segment 1
46 * propagator.getMultiplexer().add(step, sw.newSegment());
47 * propagator.propagate(startDate1, stopDate1);
48 *
49 * ...
50 *
51 * // write segment n
52 * propagator.getMultiplexer().clear();
53 * propagator.getMultiplexer().add(step, sw.newSegment());
54 * propagator.propagate(startDateN, stopDateN);
55 *
56 * }
57 * }</pre>
58 * @author Bryan Cazabonne
59 * @author Luc Maisonobe
60 * @see <a href="https://public.ccsds.org/Pubs/504x0b1c1.pdf">CCSDS 504.0-B-1 Attitude Data Messages</a>
61 * @see AemWriter
62 * @since 10.2
63 */
64 public class StreamingAemWriter implements AutoCloseable {
65
66 /** Generator for AEM output. */
67 private final Generator generator;
68
69 /** Writer for the AEM message format. */
70 private final AemWriter writer;
71
72 /** Header. */
73 private final Header header;
74
75 /** Current metadata. */
76 private final AemMetadata metadata;
77
78 /** Indicator for writing header. */
79 private boolean headerWritePending;
80
81 /** Simple constructor.
82 * @param generator generator for AEM output
83 * @param writer writer for the AEM message format
84 * @param header file header (may be null)
85 * @param template template for metadata
86 * @since 11.0
87 */
88 public StreamingAemWriter(final Generator generator, final AemWriter writer,
89 final Header header, final AemMetadata template) {
90 this.generator = generator;
91 this.writer = writer;
92 this.header = header;
93 this.metadata = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
94 this.headerWritePending = true;
95 }
96
97 /**
98 * Create a writer for a new AEM attitude ephemeris segment.
99 * <p> The returned writer can only write a single attitude ephemeris segment in an AEM.
100 * This method must be called to create a writer for each attitude ephemeris segment.
101 * @return a new AEM segment writer, ready for use.
102 */
103 public SegmentWriter newSegment() {
104 return new SegmentWriter();
105 }
106
107 /** {@inheritDoc} */
108 @Override
109 public void close() throws IOException {
110 writer.writeFooter(generator);
111 }
112
113 /** A writer for a segment of an AEM. */
114 public class SegmentWriter implements OrekitFixedStepHandler {
115
116 /**
117 * {@inheritDoc}
118 *
119 * <p> Sets the {@link AemMetadataKey#START_TIME} and {@link AemMetadataKey#STOP_TIME} in this
120 * segment's metadata if not already set by the user. Then calls {@link AemWriter#writeHeader(Generator, Header)
121 * writeHeader} if it is the first segment) and {@link AemWriter#writeMetadata(Generator, AemMetadata)} to start the segment.
122 */
123 @Override
124 public void init(final SpacecraftState s0, final AbsoluteDate t, final double step) {
125 try {
126 final AbsoluteDate date = s0.getDate();
127 if (t.isBefore(date)) {
128 throw new OrekitException(OrekitMessages.NON_CHRONOLOGICALLY_SORTED_ENTRIES,
129 date, t, date.durationFrom(t));
130 }
131
132 if (headerWritePending) {
133 // we write the header only for the first segment
134 writer.writeHeader(generator, header);
135 headerWritePending = false;
136 }
137
138 metadata.setStartTime(date);
139 metadata.setUseableStartTime(null);
140 metadata.setUseableStopTime(null);
141 metadata.setStopTime(t);
142 if (metadata.getEndpoints().getFrameA() == null ||
143 metadata.getEndpoints().getFrameA().asSpacecraftBodyFrame() == null) {
144 // the external frame must be frame A
145 metadata.getEndpoints().setFrameA(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
146 } else {
147 // the external frame must be frame B
148 metadata.getEndpoints().setFrameB(FrameFacade.map(s0.getAttitude().getReferenceFrame()));
149 }
150 writer.writeMetadata(generator, metadata);
151 writer.startAttitudeBlock(generator);
152 } catch (IOException e) {
153 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
154 }
155 }
156
157 /** {@inheritDoc}. */
158 @Override
159 public void handleStep(final SpacecraftState currentState) {
160 try {
161 writer.writeAttitudeEphemerisLine(generator, metadata, currentState.getAttitude().getOrientation());
162 } catch (IOException e) {
163 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
164 }
165 }
166
167 /** {@inheritDoc}. */
168 @Override
169 public void finish(final SpacecraftState finalState) {
170 try {
171 writer.endAttitudeBlock(generator);
172 } catch (IOException e) {
173 throw new OrekitException(e, LocalizedCoreFormats.SIMPLE_MESSAGE, e.getLocalizedMessage());
174 }
175 }
176
177 }
178
179 }