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  
19  import java.io.ByteArrayInputStream;
20  import java.nio.charset.StandardCharsets;
21  import java.util.Collections;
22  import java.util.List;
23  
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.junit.Assert;
27  import org.junit.Before;
28  import org.junit.Test;
29  import org.orekit.Utils;
30  import org.orekit.attitudes.AttitudeProvider;
31  import org.orekit.attitudes.InertialProvider;
32  import org.orekit.data.DataSource;
33  import org.orekit.files.ccsds.definitions.FrameFacade;
34  import org.orekit.files.ccsds.definitions.TimeSystem;
35  import org.orekit.files.ccsds.ndm.ParserBuilder;
36  import org.orekit.files.ccsds.ndm.WriterBuilder;
37  import org.orekit.files.ccsds.ndm.adm.AttitudeType;
38  import org.orekit.files.ccsds.section.Header;
39  import org.orekit.files.ccsds.utils.generation.KvnGenerator;
40  import org.orekit.frames.FramesFactory;
41  import org.orekit.orbits.CartesianOrbit;
42  import org.orekit.propagation.analytical.KeplerianPropagator;
43  import org.orekit.time.AbsoluteDate;
44  import org.orekit.utils.PVCoordinates;
45  import org.orekit.utils.TimeStampedAngularCoordinates;
46  
47  
48  public class StreamingAemWriterTest {
49  
50      private static final double QUATERNION_PRECISION = 1e-5;
51      private static final double DATE_PRECISION = 1e-3;
52  
53      /** Set Orekit data. */
54      @Before
55      public void setUp() {
56          Utils.setDataRoot("regular-data");
57      }
58  
59      /**
60       * Check reading and writing an AEM both with and without using the step handler
61       * methods.
62       */
63      @Test
64      public void testWriteAemStepHandler() throws Exception {
65  
66          // Create a list of files
67          List<String> files = Collections.singletonList("/ccsds/adm/aem/AEMExample07.txt");
68          for (final String ex : files) {
69  
70              // Reference AEM file
71              final DataSource source0 = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
72              AemParser parser = new ParserBuilder().buildAemParser();
73              Aem aem  = parser.parseMessage(source0);
74  
75              // Satellite attitude ephemeris as read from the reference file
76              AemSegment ephemerisBlock = aem.getSegments().get(0);
77  
78              // Meta data are extracted from the reference file
79              String            originator   = aem.getHeader().getOriginator();
80              String            objectName   = ephemerisBlock.getMetadata().getObjectName();
81              String            objectID     = ephemerisBlock.getMetadata().getObjectID();
82              String            headerCmt    = aem.getHeader().getComments().get(0);
83              FrameFacade       frameA       = ephemerisBlock.getMetadata().getEndpoints().getFrameA();
84              FrameFacade       frameB       = ephemerisBlock.getMetadata().getEndpoints().getFrameB();
85              boolean           a2b          = ephemerisBlock.getMetadata().getEndpoints().isA2b();
86              AttitudeType   attitudeType = ephemerisBlock.getMetadata().getAttitudeType();
87              boolean           isFirst      = ephemerisBlock.getMetadata().isFirst();
88  
89              // Initialize the header and metadata
90              // Here, we use only one data segment.
91              Header header = new Header(3.0);
92              header.setOriginator(originator);
93              header.addComment(headerCmt);
94  
95              AemMetadata metadata = new AemMetadata(1);
96              metadata.setTimeSystem(TimeSystem.UTC);
97              metadata.setObjectID(objectID);
98              metadata.setObjectName(objectName);
99              metadata.setAttitudeType(attitudeType);
100             metadata.setIsFirst(isFirst);
101             metadata.getEndpoints().setFrameA(frameA);
102             metadata.getEndpoints().setFrameB(frameB);
103             metadata.getEndpoints().setA2b(a2b);
104             metadata.setStartTime(AbsoluteDate.PAST_INFINITY);  // will be overwritten at propagation start
105             metadata.setStopTime(AbsoluteDate.FUTURE_INFINITY); // will be overwritten at propagation start
106 
107             StringBuilder buffer = new StringBuilder();
108             StreamingAemWriter writer =
109                             new StreamingAemWriter(new KvnGenerator(buffer, AemWriter.KVN_PADDING_WIDTH, ex + "-new", 60),
110                                                    new WriterBuilder(). buildAemWriter(),
111                                                    header, metadata);
112 
113             // Initialize a Keplerian propagator with an Inertial attitude provider
114             // It is expected that all attitude data lines will have the same value
115             StreamingAemWriter.SegmentWriter segment = writer.newSegment();
116             KeplerianPropagator propagator =
117                             createPropagator(ephemerisBlock.getStart(),
118                                              new InertialProvider(ephemerisBlock.getAngularCoordinates().get(0).getRotation(),
119                                                                   FramesFactory.getEME2000()));
120 
121             // We propagate 60 seconds after the start date with a step equals to 10.0 seconds
122             // It is expected to have an attitude data block containing 7 data lines
123             double step = 10.0;
124             propagator.setStepHandler(step, segment);
125             propagator.propagate(ephemerisBlock.getStart().shiftedBy(60.0));
126             writer.close();
127 
128             // Generated AEM file
129             final DataSource source1 = new DataSource("buffer",
130                                                    () -> new ByteArrayInputStream(buffer.toString().getBytes(StandardCharsets.UTF_8)));
131             Aem generatedAem = parser.parseMessage(source1);
132 
133             // There is only one attitude ephemeris block
134             Assert.assertEquals(1, generatedAem.getSegments().size());
135             AemSegment attitudeBlocks = generatedAem.getSegments().get(0);
136             // There are 7 data lines in the attitude ephemeris block
137             List<? extends TimeStampedAngularCoordinates> ac  = attitudeBlocks.getAngularCoordinates();
138             Assert.assertEquals(7, ac.size());
139 
140             // Verify
141             for (int i = 0; i < 7; i++) {
142                 Assert.assertEquals(step * i, ac.get(i).getDate().durationFrom(ephemerisBlock.getStart()), DATE_PRECISION);
143                 Rotation rot = ac.get(i).getRotation();
144                 Assert.assertEquals(0.68427, rot.getQ0(), QUATERNION_PRECISION);
145                 Assert.assertEquals(0.56748, rot.getQ1(), QUATERNION_PRECISION);
146                 Assert.assertEquals(0.03146, rot.getQ2(), QUATERNION_PRECISION);
147                 Assert.assertEquals(0.45689, rot.getQ3(), QUATERNION_PRECISION);
148             }
149 
150         }
151 
152     }
153 
154     /**
155      * Create a Keplerian propagator.
156      * @param date reference date
157      * @param attitudeProv attitude provider
158      * @return a Keplerian propagator
159      */
160     private KeplerianPropagator createPropagator(AbsoluteDate date,
161                                                  AttitudeProvider attitudeProv) {
162         Vector3D position = new Vector3D(-29536113.0, 30329259.0, -100125.0);
163         Vector3D velocity = new Vector3D(-2194.0, -2141.0, -8.0);
164         PVCoordinates pvCoordinates = new PVCoordinates( position, velocity);
165         double mu = 3.9860047e14;
166 
167         CartesianOrbit p = new CartesianOrbit(pvCoordinates, FramesFactory.getEME2000(), date, mu);
168 
169         return new KeplerianPropagator(p, attitudeProv);
170     }
171 
172 }