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.apm;
18  
19  import java.util.Arrays;
20  
21  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
22  import org.hipparchus.complex.Quaternion;
23  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
24  import org.hipparchus.geometry.euclidean.threed.Rotation;
25  import org.hipparchus.geometry.euclidean.threed.Vector3D;
26  import org.orekit.attitudes.Attitude;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitMessages;
29  import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints;
30  import org.orekit.files.ccsds.section.Section;
31  import org.orekit.frames.Frame;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.utils.PVCoordinatesProvider;
34  import org.orekit.utils.TimeStampedAngularCoordinates;
35  
36  /**
37   * Container for Attitude Parameter Message quaternion logical block.
38   * @author Bryan Cazabonne
39   * @since 10.2
40   */
41  public class ApmQuaternion implements Section {
42  
43      /** Epoch of the data. */
44      private AbsoluteDate epoch;
45  
46      /** Endpoints (i.e. frames A, B and their relationship). */
47      private final AttitudeEndoints endpoints;
48  
49      /** Quaternion. */
50      private double[] q;
51  
52      /** Quaternion derivative. */
53      private double[] qDot;
54  
55      /** Simple constructor.
56       */
57      public ApmQuaternion() {
58          endpoints = new AttitudeEndoints();
59          q         = new double[4];
60          qDot      = new double[4];
61          Arrays.fill(q,    Double.NaN);
62          Arrays.fill(qDot, Double.NaN);
63      }
64  
65      /** {@inheritDoc} */
66      @Override
67      public void validate(final double version) {
68          endpoints.checkMandatoryEntriesExceptExternalFrame(ApmQuaternionKey.Q_FRAME_A,
69                                                             ApmQuaternionKey.Q_FRAME_B,
70                                                             ApmQuaternionKey.Q_DIR);
71          endpoints.checkExternalFrame(ApmQuaternionKey.Q_FRAME_A, ApmQuaternionKey.Q_FRAME_B);
72          if (Double.isNaN(q[0] + q[1] + q[2] + q[3])) {
73              throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "Q{C|1|2|3}");
74          }
75      }
76  
77      /**
78       * Get the epoch of the data.
79       * @return epoch the epoch
80       */
81      public AbsoluteDate getEpoch() {
82          return epoch;
83      }
84  
85      /**
86       * Set the epoch of the data.
87       * @param epoch the epoch to be set
88       */
89      public void setEpoch(final AbsoluteDate epoch) {
90          this.epoch = epoch;
91      }
92  
93      /** Get the endpoints (i.e. frames A, B and their relationship).
94       * @return endpoints
95       */
96      public AttitudeEndoints getEndpoints() {
97          return endpoints;
98      }
99  
100     /**
101      * Get the quaternion.
102      * @return quaternion
103      */
104     public Quaternion getQuaternion() {
105         return new Quaternion(q[0], q[1], q[2], q[3]);
106     }
107 
108     /**
109      * Set quaternion component.
110      * @param index component index (0 is scalar part)
111      * @param value quaternion component
112      */
113     public void setQ(final int index, final double value) {
114         this.q[index] = value;
115     }
116 
117     /**
118      * Get the quaternion derivative.
119      * @return quaternion derivative
120      */
121     public Quaternion getQuaternionDot() {
122         return new Quaternion(qDot[0], qDot[1], qDot[2], qDot[3]);
123     }
124 
125     /**
126      * Set quaternion derivative component.
127      * @param index component index (0 is scalar part)
128      * @param derivative quaternion derivative component
129      */
130     public void setQDot(final int index, final double derivative) {
131         this.qDot[index] = derivative;
132     }
133 
134     /** Check if the logical block includes rates.
135      * @return true if logical block includes rates
136      */
137     public boolean hasRates() {
138         return !Double.isNaN(qDot[0] + qDot[1] + qDot[2] + qDot[3]);
139     }
140 
141     /** Get the attitude.
142      * @param frame reference frame with respect to which attitude must be defined
143      * (may be null if attitude is <em>not</em> orbit-relative and one wants
144      * attitude in the same frame as used in the attitude message)
145      * @param pvProvider provider for spacecraft position and velocity
146      * (may be null if attitude is <em>not</em> orbit-relative)
147      * @return attitude
148      */
149     public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) {
150 
151         if (Double.isNaN(qDot[0])) {
152             // rotation as it is stored in the APM
153             final Rotation r = new Rotation(q[0], q[1], q[2], q[3], true);
154 
155             // attitude without rotation rate
156             return endpoints.build(frame, pvProvider,
157                                    new TimeStampedAngularCoordinates(epoch, r, Vector3D.ZERO, Vector3D.ZERO));
158 
159         } else {
160             // attitude as it is stored in the APM
161             final UnivariateDerivative1                q0 = new UnivariateDerivative1(q[0], qDot[0]);
162             final UnivariateDerivative1                q1 = new UnivariateDerivative1(q[1], qDot[1]);
163             final UnivariateDerivative1                q2 = new UnivariateDerivative1(q[2], qDot[2]);
164             final UnivariateDerivative1                q3 = new UnivariateDerivative1(q[3], qDot[3]);
165             final FieldRotation<UnivariateDerivative1> rd = new FieldRotation<>(q0, q1, q2, q3, true);
166 
167             // attitude converted to Orekit conventions
168             return endpoints.build(frame, pvProvider, new TimeStampedAngularCoordinates(epoch, rd));
169         }
170 
171     }
172 
173 }