AttitudeWriter.java

  1. /* Copyright 2002-2022 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. import java.io.IOException;
  19. import java.util.List;

  20. import org.orekit.errors.OrekitIllegalArgumentException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.files.ccsds.definitions.FrameFacade;
  23. import org.orekit.files.ccsds.section.Header;
  24. import org.orekit.files.ccsds.utils.FileFormat;
  25. import org.orekit.files.ccsds.utils.generation.Generator;
  26. import org.orekit.files.ccsds.utils.generation.KvnGenerator;
  27. import org.orekit.files.ccsds.utils.generation.XmlGenerator;
  28. import org.orekit.files.general.AttitudeEphemerisFile;
  29. import org.orekit.files.general.AttitudeEphemerisFile.SatelliteAttitudeEphemeris;
  30. import org.orekit.files.general.AttitudeEphemerisFileWriter;
  31. import org.orekit.utils.TimeStampedAngularCoordinates;

  32. /** An {@link AttitudeEphemerisFileWriter} generating {@link Aem AEM} files.
  33.  * @author Bryan Cazabonne
  34.  * @since 11.0
  35.  */
  36. public class AttitudeWriter implements AttitudeEphemerisFileWriter {

  37.     /** Underlying writer. */
  38.     private final AemWriter writer;

  39.     /** Header. */
  40.     private final Header header;

  41.     /** Current metadata. */
  42.     private final AemMetadata metadata;

  43.     /** File format to use. */
  44.     private final FileFormat fileFormat;

  45.     /** Output name for error messages. */
  46.     private final String outputName;

  47.     /** Column number for aligning units. */
  48.     private final int unitsColumn;

  49.     /**
  50.      * Constructor used to create a new AEM writer configured with the necessary parameters
  51.      * to successfully fill in all required fields that aren't part of a standard object.
  52.      * <p>
  53.      * If the mandatory header entries are not present (or if header is null),
  54.      * built-in defaults will be used
  55.      * </p>
  56.      * <p>
  57.      * The writer is built from the complete header and partial metadata. The template
  58.      * metadata is used to initialize and independent local copy, that will be updated
  59.      * as new segments are written (with at least the segment start and stop will change,
  60.      * but some other parts may change too). The {@code template} argument itself is not
  61.      * changed.
  62.      * </p>
  63.      * <p>
  64.      * Calling this constructor directly is not recommended. Users should rather use
  65.      * {@link org.orekit.files.ccsds.ndm.WriterBuilder#buildAemWriter()}.
  66.      * </p>
  67.      * @param writer underlying writer
  68.      * @param header file header (may be null)
  69.      * @param template template for metadata
  70.      * @param fileFormat file format to use
  71.      * @param outputName output name for error messages
  72.      * @param unitsColumn columns number for aligning units (if negative or zero, units are not output)
  73.      * @since 11.0
  74.      */
  75.     public AttitudeWriter(final AemWriter writer,
  76.                           final Header header, final AemMetadata template,
  77.                           final FileFormat fileFormat, final String outputName,
  78.                           final int unitsColumn) {
  79.         this.writer      = writer;
  80.         this.header      = header;
  81.         this.metadata    = template.copy(header == null ? writer.getDefaultVersion() : header.getFormatVersion());
  82.         this.fileFormat  = fileFormat;
  83.         this.outputName  = outputName;
  84.         this.unitsColumn = unitsColumn;
  85.     }

  86.     /** {@inheritDoc}
  87.      * <p>
  88.      * As {@code AttitudeEphemerisFile.SatelliteAttitudeEphemeris} does not have all the entries
  89.      * from {@link AemMetadata}, the only values that will be extracted from the
  90.      * {@code ephemerisFile} will be the start time, stop time, reference frame, interpolation
  91.      * method and interpolation degree. The missing values (like object name, local spacecraft
  92.      * body frame, attitude type...) will be inherited from the template  metadata set at writer
  93.      * {@link #AttitudeWriter(AemWriter, Header, AemMetadata, FileFormat, String, int) construction}.
  94.      * </p>
  95.      */
  96.     @Override
  97.     public <C extends TimeStampedAngularCoordinates, S extends AttitudeEphemerisFile.AttitudeEphemerisSegment<C>>
  98.         void write(final Appendable appendable, final AttitudeEphemerisFile<C, S> ephemerisFile)
  99.         throws IOException {

  100.         if (appendable == null) {
  101.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
  102.         }

  103.         if (ephemerisFile == null) {
  104.             return;
  105.         }

  106.         final SatelliteAttitudeEphemeris<C, S> satEphem =
  107.                         ephemerisFile.getSatellites().get(metadata.getObjectID());
  108.         if (satEphem == null) {
  109.             throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND,
  110.                                                      metadata.getObjectID(), "ephemerisFile");
  111.         }

  112.         // Get attitude ephemeris segments to output.
  113.         final List<S> segments = satEphem.getSegments();
  114.         if (segments.isEmpty()) {
  115.             // No data -> No output
  116.             return;
  117.         }

  118.         try (Generator generator = fileFormat == FileFormat.KVN ?
  119.                                    new KvnGenerator(appendable, AemWriter.KVN_PADDING_WIDTH, outputName, unitsColumn) :
  120.                                    new XmlGenerator(appendable, XmlGenerator.DEFAULT_INDENT, outputName, unitsColumn > 0)) {

  121.             writer.writeHeader(generator, header);

  122.             // Loop on segments
  123.             for (final S segment : segments) {
  124.                 writeSegment(generator, segment);
  125.             }

  126.             writer.writeFooter(generator);

  127.         }

  128.     }

  129.     /** Write one segment.
  130.      * @param generator generator to use for producing output
  131.      * @param segment segment to write
  132.      * @param <C> type of the angular coordinates
  133.      * @param <S> type of the segment
  134.      * @throws IOException if any buffer writing operations fails
  135.      */
  136.     private <C extends TimeStampedAngularCoordinates, S extends AttitudeEphemerisFile.AttitudeEphemerisSegment<C>>
  137.         void writeSegment(final Generator generator, final S segment) throws IOException {

  138.         // override template metadata with segment values
  139.         metadata.setStartTime(segment.getStart());
  140.         metadata.setStopTime(segment.getStop());
  141.         if (metadata.getEndpoints().getFrameA() == null ||
  142.             metadata.getEndpoints().getFrameA().asSpacecraftBodyFrame() == null) {
  143.             // the external frame must be frame A
  144.             metadata.getEndpoints().setFrameA(FrameFacade.map(segment.getReferenceFrame()));
  145.         } else {
  146.             // the external frame must be frame B
  147.             metadata.getEndpoints().setFrameB(FrameFacade.map(segment.getReferenceFrame()));
  148.         }
  149.         metadata.setInterpolationMethod(segment.getInterpolationMethod());
  150.         metadata.setInterpolationDegree(segment.getInterpolationSamples() - 1);
  151.         writer.writeMetadata(generator, metadata);

  152.         // Loop on attitude data
  153.         writer.startAttitudeBlock(generator);
  154.         if (segment instanceof AemSegment) {
  155.             generator.writeComments(((AemSegment) segment).getData().getComments());
  156.         }
  157.         for (final TimeStampedAngularCoordinates coordinates : segment.getAngularCoordinates()) {
  158.             writer.writeAttitudeEphemerisLine(generator, metadata, coordinates);
  159.         }
  160.         writer.endAttitudeBlock(generator);

  161.     }

  162. }