AEMWriter.java

  1. /* Copyright 2002-2020 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;

  18. import java.io.BufferedWriter;
  19. import java.io.IOException;
  20. import java.nio.charset.StandardCharsets;
  21. import java.nio.file.Files;
  22. import java.nio.file.Paths;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;

  26. import org.orekit.errors.OrekitIllegalArgumentException;
  27. import org.orekit.errors.OrekitMessages;
  28. import org.orekit.files.ccsds.StreamingAemWriter.AEMSegment;
  29. import org.orekit.files.general.AttitudeEphemerisFile;
  30. import org.orekit.files.general.AttitudeEphemerisFile.SatelliteAttitudeEphemeris;
  31. import org.orekit.files.general.AttitudeEphemerisFile.AttitudeEphemerisSegment;
  32. import org.orekit.files.general.AttitudeEphemerisFileWriter;
  33. import org.orekit.time.TimeScale;
  34. import org.orekit.utils.TimeStampedAngularCoordinates;

  35. /**
  36.  * A writer for Attitude Ephemeris Messsage (AEM) files.
  37.  * @author Bryan Cazabonne
  38.  * @since 10.2
  39.  */
  40. public class AEMWriter implements AttitudeEphemerisFileWriter {

  41.     /** Originator name, usually the organization and/or country. **/
  42.     private final String originator;

  43.     /** Space object ID, usually an official international designator such as "1998-067A". */
  44.     private final String spaceObjectId;

  45.     /** Space object name, usually a common name for an object like "ISS". **/
  46.     private final String spaceObjectName;

  47.     /** Format for attitude ephemeris data output. */
  48.     private final String attitudeFormat;

  49.     /**
  50.      * Standard default constructor that creates a writer with default configurations
  51.      * including {@link StreamingAemWriter#DEFAULT_ATTITUDE_FORMAT Default formatting}.
  52.      */
  53.     public AEMWriter() {
  54.         this(StreamingAemWriter.DEFAULT_ORIGINATOR, null, null,
  55.              StreamingAemWriter.DEFAULT_ATTITUDE_FORMAT);
  56.     }

  57.     /**
  58.      * Constructor used to create a new AEM writer configured with the necessary parameters
  59.      * to successfully fill in all required fields that aren't part of a standard object
  60.      * and using {@link StreamingAemWriter#DEFAULT_ATTITUDE_FORMAT default formatting}
  61.      * for attitude ephemeris data output.
  62.      *
  63.      * @param originator the originator field string
  64.      * @param spaceObjectId the spacecraft ID
  65.      * @param spaceObjectName the space object common name
  66.      */
  67.     public AEMWriter(final String originator, final String spaceObjectId,
  68.                      final String spaceObjectName) {
  69.         this(originator, spaceObjectId, spaceObjectName,
  70.              StreamingAemWriter.DEFAULT_ATTITUDE_FORMAT);
  71.     }

  72.     /**
  73.      * Constructor used to create a new AEM writer configured with the necessary
  74.      * parameters to successfully fill in all required fields that aren't part
  75.      * of a standard object and user-defined attitude ephemeris data output format.
  76.      *
  77.      * @param originator the originator field string
  78.      * @param spaceObjectId the spacecraft ID
  79.      * @param spaceObjectName the space object common name
  80.      * @param attitudeFormat {@link java.util.Formatter format parameters} for
  81.      *                       attitude ephemeris data output
  82.      * @since 10.3
  83.      */
  84.     public AEMWriter(final String originator, final String spaceObjectId,
  85.                      final String spaceObjectName, final String attitudeFormat) {
  86.         this.originator          = originator;
  87.         this.spaceObjectId       = spaceObjectId;
  88.         this.spaceObjectName     = spaceObjectName;
  89.         this.attitudeFormat      = attitudeFormat;
  90.     }

  91.     /** {@inheritDoc} */
  92.     @Override
  93.     public void write(final Appendable writer, final AttitudeEphemerisFile ephemerisFile)
  94.         throws IOException {

  95.         if (writer == null) {
  96.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "writer");
  97.         }

  98.         if (ephemerisFile == null) {
  99.             return;
  100.         }

  101.         final String idToProcess;
  102.         if (spaceObjectId != null) {
  103.             if (ephemerisFile.getSatellites().containsKey(spaceObjectId)) {
  104.                 idToProcess = spaceObjectId;
  105.             } else {
  106.                 throw new OrekitIllegalArgumentException(OrekitMessages.VALUE_NOT_FOUND, spaceObjectId, "ephemerisFile");
  107.             }
  108.         } else if (ephemerisFile.getSatellites().keySet().size() == 1) {
  109.             idToProcess = ephemerisFile.getSatellites().keySet().iterator().next();
  110.         } else {
  111.             throw new OrekitIllegalArgumentException(OrekitMessages.EPHEMERIS_FILE_NO_MULTI_SUPPORT);
  112.         }

  113.         // Get satellite and attitude ephemeris segments to output.
  114.         final SatelliteAttitudeEphemeris satEphem = ephemerisFile.getSatellites().get(idToProcess);
  115.         final List<? extends AttitudeEphemerisSegment> segments = satEphem.getSegments();
  116.         if (segments.isEmpty()) {
  117.             // No data -> No output
  118.             return;
  119.         }
  120.         // First segment
  121.         final AttitudeEphemerisSegment firstSegment = segments.get(0);

  122.         final String objectName = this.spaceObjectName == null ? idToProcess : this.spaceObjectName;
  123.         // Only one time scale per AEM file, see Section 4.2.5.4.2
  124.         final TimeScale timeScale = firstSegment.getTimeScale();
  125.         // Metadata that is constant for the whole AEM file
  126.         final Map<Keyword, String> metadata = new LinkedHashMap<>();
  127.         metadata.put(Keyword.TIME_SYSTEM, firstSegment.getTimeScaleString());
  128.         metadata.put(Keyword.ORIGINATOR,  this.originator);
  129.         // Only one object in an AEM file, see Section 2.3.1
  130.         metadata.put(Keyword.OBJECT_NAME,   objectName);
  131.         metadata.put(Keyword.OBJECT_ID,     idToProcess);

  132.         // Header comments. If header comments are presents, they are assembled together in a single line
  133.         if (ephemerisFile instanceof AEMFile) {
  134.             // Cast to OEMFile
  135.             final AEMFile aemFile = (AEMFile) ephemerisFile;
  136.             if (!aemFile.getHeaderComment().isEmpty()) {
  137.                 // Loop on comments
  138.                 final StringBuffer buffer = new StringBuffer();
  139.                 for (String comment : aemFile.getHeaderComment()) {
  140.                     buffer.append(comment);
  141.                 }
  142.                 // Update metadata
  143.                 metadata.put(Keyword.COMMENT, buffer.toString());
  144.             }
  145.         }

  146.         // Writer for AEM files
  147.         final StreamingAemWriter aemWriter =
  148.                         new StreamingAemWriter(writer, timeScale, metadata, attitudeFormat);
  149.         aemWriter.writeHeader();

  150.         // Loop on segments
  151.         for (final AttitudeEphemerisSegment segment : segments) {
  152.             // Segment specific metadata
  153.             metadata.clear();
  154.             metadata.put(Keyword.CENTER_NAME,          segment.getFrameCenterString());
  155.             metadata.put(Keyword.REF_FRAME_A,          segment.getRefFrameAString());
  156.             metadata.put(Keyword.REF_FRAME_B,          segment.getRefFrameBString());
  157.             metadata.put(Keyword.ATTITUDE_DIR,         segment.getAttitudeDirection());
  158.             metadata.put(Keyword.START_TIME,           segment.getStart().toString(timeScale));
  159.             metadata.put(Keyword.STOP_TIME,            segment.getStop().toString(timeScale));
  160.             metadata.put(Keyword.ATTITUDE_TYPE,        segment.getAttitudeType());
  161.             metadata.put(Keyword.INTERPOLATION_METHOD, segment.getInterpolationMethod());
  162.             metadata.put(Keyword.INTERPOLATION_DEGREE,
  163.                          String.valueOf(segment.getInterpolationSamples() - 1));

  164.             final AEMSegment segmentWriter = aemWriter.newSegment(metadata);
  165.             segmentWriter.writeMetadata();
  166.             segmentWriter.startAttitudeBlock();
  167.             // Loop on attitude data
  168.             for (final TimeStampedAngularCoordinates coordinates : segment.getAngularCoordinates()) {
  169.                 segmentWriter.writeAttitudeEphemerisLine(coordinates, segment.isFirst(),
  170.                                                          segment.getAttitudeType(), segment.getRotationOrder());
  171.             }
  172.             segmentWriter.endAttitudeBlock();
  173.         }

  174.     }

  175.     /**
  176.      * Write the passed in {@link AEMFile} using the passed in {@link Appendable}.
  177.      * @param writer a configured Appendable to feed with text
  178.      * @param aemFile a populated aem file to serialize into the buffer
  179.      * @throws IOException if any buffer writing operations fail or if the underlying
  180.      *         format doesn't support a configuration in the EphemerisFile
  181.      *         for example having multiple satellites in one file, having
  182.      *         the origin at an unspecified celestial body, etc.)
  183.      */
  184.     public void write(final Appendable writer, final AEMFile aemFile) throws IOException {
  185.         write(writer, (AttitudeEphemerisFile) aemFile);
  186.     }

  187.     /**
  188.      * Write the passed in {@link AEMFile} to a file at the output path specified.
  189.      * @param outputFilePath a file path that the corresponding file will be written to
  190.      * @param aemFile a populated aem file to serialize into the buffer
  191.      * @throws IOException if any file writing operations fail or if the underlying
  192.      *         format doesn't support a configuration in the EphemerisFile
  193.      *         (for example having multiple satellites in one file, having
  194.      *         the origin at an unspecified celestial body, etc.)
  195.      */
  196.     public void write(final String outputFilePath, final AEMFile aemFile)
  197.         throws IOException {
  198.         try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputFilePath), StandardCharsets.UTF_8)) {
  199.             write(writer, (AttitudeEphemerisFile) aemFile);
  200.         }
  201.     }

  202. }