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.apm;
18
19 import java.util.List;
20
21 import org.hipparchus.complex.Quaternion;
22 import org.orekit.attitudes.Attitude;
23 import org.orekit.data.DataContext;
24 import org.orekit.errors.OrekitException;
25 import org.orekit.errors.OrekitMessages;
26 import org.orekit.files.ccsds.ndm.NdmConstituent;
27 import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
28 import org.orekit.files.ccsds.ndm.adm.AttitudeType;
29 import org.orekit.files.ccsds.section.Header;
30 import org.orekit.files.ccsds.section.Segment;
31 import org.orekit.frames.Frame;
32 import org.orekit.utils.IERSConventions;
33 import org.orekit.utils.PVCoordinatesProvider;
34 import org.orekit.utils.TimeStampedAngularCoordinates;
35
36 /**
37 * This class stores all the information of the Attitude Parameter Message (APM) File parsed
38 * by APMParser. It contains the header and the metadata and a the data lines.
39 * @author Bryan Cazabonne
40 * @since 10.2
41 */
42 public class Apm extends NdmConstituent<Header, Segment<AdmMetadata, ApmData>> {
43
44 /** Root element for XML files. */
45 public static final String ROOT = "apm";
46
47 /** Key for format version. */
48 public static final String FORMAT_VERSION_KEY = "CCSDS_APM_VERS";
49
50 /** Simple constructor.
51 * @param header file header
52 * @param segments file segments
53 * @param conventions IERS conventions
54 * @param dataContext used for creating frames, time scales, etc.
55 */
56 public Apm(final Header header, final List<Segment<AdmMetadata, ApmData>> segments,
57 final IERSConventions conventions, final DataContext dataContext) {
58 super(header, segments, conventions, dataContext);
59 }
60
61 /** Get the file metadata.
62 * @return file metadata
63 */
64 public AdmMetadata getMetadata() {
65 return getSegments().get(0).getMetadata();
66 }
67
68 /** Get the file data.
69 * @return file data
70 */
71 public ApmData getData() {
72 return getSegments().get(0).getData();
73 }
74
75 /** Get the attitude.
76 * <p>
77 * The orientation part of the attitude is always extracted from the file mandatory
78 * {@link ApmQuaternion quaternion logical block}. The rotation rate part of
79 * the attitude is extracted from the {@link ApmQuaternion quaternion logical block}
80 * if rate is available there, or from the {@link Euler Euler logical block} if rate
81 * is missing from quaternion logical block but available in Euler logical block.
82 * </p>
83 * @param frame reference frame with respect to which attitude must be defined,
84 * (may be null if attitude is <em>not</em> orbit-relative and one wants
85 * attitude in the same frame as used in the attitude message)
86 * @param pvProvider provider for spacecraft position and velocity
87 * (may be null if attitude is <em>not</em> orbit-relative)
88 * @return attitude
89 */
90 public Attitude getAttitude(final Frame frame, final PVCoordinatesProvider pvProvider) {
91
92 final ApmData data = getSegments().get(0).getData();
93 final ApmQuaternion qBlock = data.getQuaternionBlock();
94 final Euler eBlock = data.getEulerBlock();
95
96 final TimeStampedAngularCoordinates tac;
97 if (qBlock.hasRates()) {
98 // quaternion logical block includes everything we need
99 final Quaternion q = qBlock.getQuaternion();
100 final Quaternion qDot = qBlock.getQuaternionDot();
101 tac = AttitudeType.QUATERNION_DERIVATIVE.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(),
102 null, true, qBlock.getEpoch(),
103 q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(),
104 qDot.getQ0(), qDot.getQ1(), qDot.getQ2(), qDot.getQ3());
105 } else if (eBlock != null && eBlock.hasRates()) {
106 // we have to rely on the Euler logical block to take rates into account
107
108 if (!qBlock.getEndpoints().isCompatibleWith(eBlock.getEndpoints())) {
109 // nothing really prevents having two logical blocks with different settings
110 // but it is a nightmare to process and probably not worse the trouble.
111 // For now, we just throw an exception, we may reconsider this if some use case arises
112 throw new OrekitException(OrekitMessages.INCOMPATIBLE_FRAMES,
113 qBlock.getEndpoints().toString(),
114 eBlock.getEndpoints().toString());
115 }
116
117 final Quaternion q = qBlock.getQuaternion();
118 final double[] rates = eBlock.getRotationRates();
119 tac = AttitudeType.QUATERNION_RATE.build(true,
120 qBlock.getEndpoints().isExternal2SpacecraftBody(),
121 eBlock.getEulerRotSeq(), eBlock.isSpacecraftBodyRate(), qBlock.getEpoch(),
122 q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3(),
123 rates[0], rates[1], rates[2]);
124
125 } else {
126 // we rely only on the quaternion logical block, despite it doesn't include rates
127 final Quaternion q = qBlock.getQuaternion();
128 tac = AttitudeType.QUATERNION.build(true, qBlock.getEndpoints().isExternal2SpacecraftBody(),
129 null, true, qBlock.getEpoch(),
130 q.getQ0(), q.getQ1(), q.getQ2(), q.getQ3());
131 }
132
133 // build the attitude
134 return qBlock.getEndpoints().build(frame, pvProvider, tac);
135
136 }
137
138 }