1   /* Copyright 2002-2026 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.Optional;
20  
21  import org.orekit.annotation.Nullable;
22  import org.orekit.errors.OrekitException;
23  import org.orekit.errors.OrekitMessages;
24  import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
25  import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints;
26  import org.orekit.files.ccsds.section.CommentsContainer;
27  import org.orekit.frames.Frame;
28  
29  /**
30   * Container for Attitude Parameter Message data lines.
31   * <p>
32   * Beware that the Orekit getters and setters all rely on SI units. The parsers
33   * and writers take care of converting these SI units into CCSDS mandatory units.
34   * The {@link org.orekit.utils.units.Unit Unit} class provides useful
35   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
36   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} methods in case the callers
37   * already use CCSDS units instead of the API SI units. The general-purpose
38   * {@link org.orekit.utils.units.Unit Unit} class (without an 's') and the
39   * CCSDS-specific {@link org.orekit.files.ccsds.definitions.Units Units} class
40   * (with an 's') also provide some predefined units. These predefined units and the
41   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
42   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} conversion methods are indeed
43   * what the parsers and writers use for the conversions.
44   * </p>
45   * @author Bryan Cazabonne
46   * @since 10.2
47   */
48  public class SpinStabilized extends CommentsContainer {
49  
50      /** Endpoints (i.e. frames A, B and their relationship). */
51      private final AttitudeEndpoints endpoints;
52  
53      /** Right ascension of spin axis vector (rad). */
54      private double spinAlpha;
55  
56      /** Declination of the spin axis vector (rad). */
57      private double spinDelta;
58  
59      /** Phase of the satellite about the spin axis (rad). */
60      private double spinAngle;
61  
62      /** Angular velocity of satellite around spin axis (rad/s). */
63      private double spinAngleVel;
64  
65      /** Nutation angle of spin axis (rad). */
66      @Nullable
67      private Double nutation;
68  
69      /** Body nutation period of the spin axis (s). */
70      @Nullable
71      private Double nutationPer;
72  
73      /** Inertial nutation phase (rad). */
74      @Nullable
75      private Double nutationPhase;
76  
77      /** Right ascension of angular momentum vector (rad).
78       * @since 12.0
79       */
80      @Nullable
81      private Double momentumAlpha;
82  
83      /** Declination of the angular momentum vector (rad).
84       * @since 12.0
85       */
86      @Nullable
87      private Double momentumDelta;
88  
89      /** Angular velocity of spin vector around the angular momentum vector (rad/s).
90       * @since 12.0
91       */
92      @Nullable
93      private Double nutationVel;
94  
95      /**
96       * Simple constructor.
97       *
98       * @param frameMapper for creating a {@link Frame}.
99       * @since 13.1.5
100      */
101     public SpinStabilized(final CcsdsFrameMapper frameMapper) {
102         endpoints      = new AttitudeEndpoints(frameMapper);
103         spinAlpha      = Double.NaN;
104         spinDelta      = Double.NaN;
105         spinAngle      = Double.NaN;
106         spinAngleVel   = Double.NaN;
107     }
108 
109     /** {@inheritDoc} */
110     @Override
111     public void validate(final double version) {
112         super.validate(version);
113         endpoints.checkMandatoryEntriesExceptExternalFrame(version,
114                                                            SpinStabilizedKey.SPIN_FRAME_A,
115                                                            SpinStabilizedKey.SPIN_FRAME_B,
116                                                            SpinStabilizedKey.SPIN_DIR);
117         endpoints.checkExternalFrame(SpinStabilizedKey.SPIN_FRAME_A, SpinStabilizedKey.SPIN_FRAME_B);
118         checkNotNaN(spinAlpha,    SpinStabilizedKey.SPIN_ALPHA.name());
119         checkNotNaN(spinDelta,    SpinStabilizedKey.SPIN_DELTA.name());
120         checkNotNaN(spinAngle,    SpinStabilizedKey.SPIN_ANGLE.name());
121         checkNotNaN(spinAngleVel, SpinStabilizedKey.SPIN_ANGLE_VEL.name());
122         if (getNutation().isEmpty() || getNutationPeriod().isEmpty() || getNutationPhase().isEmpty()) {
123             // if at least one is empty, all must be empty
124             if (!(getNutation().isEmpty() && getNutationPeriod().isEmpty() && getNutationPhase().isEmpty())) {
125                 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "NUTATION*");
126             }
127         }
128         if (getMomentumAlpha().isEmpty() || getMomentumDelta().isEmpty() || getNutationVel().isEmpty()) {
129             // if at least one is empty, all must be empty
130             if (!(getMomentumAlpha().isEmpty() && getMomentumDelta().isEmpty() && getNutationVel().isEmpty())) {
131                 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "MOMENTUM*/NUTATION_VEL");
132             }
133         }
134     }
135 
136     /** Get the endpoints (i.e. frames A, B and their relationship).
137      * @return endpoints
138      */
139     public AttitudeEndpoints getEndpoints() {
140         return endpoints;
141     }
142 
143     /**
144      * Get the right ascension of spin axis vector (rad).
145      * @return the right ascension of spin axis vector
146      */
147     public double getSpinAlpha() {
148         return spinAlpha;
149     }
150 
151     /**
152      * Set the right ascension of spin axis vector (rad).
153      * @param spinAlpha value to be set
154      */
155     public void setSpinAlpha(final double spinAlpha) {
156         refuseFurtherComments();
157         this.spinAlpha = spinAlpha;
158     }
159 
160     /**
161      * Get the declination of the spin axis vector (rad).
162      * @return the declination of the spin axis vector (rad).
163      */
164     public double getSpinDelta() {
165         return spinDelta;
166     }
167 
168     /**
169      * Set the declination of the spin axis vector (rad).
170      * @param spinDelta value to be set
171      */
172     public void setSpinDelta(final double spinDelta) {
173         refuseFurtherComments();
174         this.spinDelta = spinDelta;
175     }
176 
177     /**
178      * Get the phase of the satellite about the spin axis (rad).
179      * @return the phase of the satellite about the spin axis
180      */
181     public double getSpinAngle() {
182         return spinAngle;
183     }
184 
185     /**
186      * Set the phase of the satellite about the spin axis (rad).
187      * @param spinAngle value to be set
188      */
189     public void setSpinAngle(final double spinAngle) {
190         refuseFurtherComments();
191         this.spinAngle = spinAngle;
192     }
193 
194     /**
195      * Get the angular velocity of satellite around spin axis (rad/s).
196      * @return the angular velocity of satellite around spin axis
197      */
198     public double getSpinAngleVel() {
199         return spinAngleVel;
200     }
201 
202     /**
203      * Set the angular velocity of satellite around spin axis (rad/s).
204      * @param spinAngleVel value to be set
205      */
206     public void setSpinAngleVel(final double spinAngleVel) {
207         refuseFurtherComments();
208         this.spinAngleVel = spinAngleVel;
209     }
210 
211     /**
212      * Get the nutation angle of spin axis (rad).
213      * @return the nutation angle of spin axis
214      */
215     public Optional<Double> getNutation() {
216         return Optional.ofNullable(nutation);
217     }
218 
219     /**
220      * Set the nutation angle of spin axis (rad).
221      * @param nutation the nutation angle to be set
222      */
223     public void setNutation(final double nutation) {
224         refuseFurtherComments();
225         this.nutation = nutation;
226     }
227 
228     /**
229      * Get the body nutation period of the spin axis (s).
230      * @return the body nutation period of the spin axis
231      */
232     public Optional<Double> getNutationPeriod() {
233         return Optional.ofNullable(nutationPer);
234     }
235 
236     /**
237      * Set the body nutation period of the spin axis (s).
238      * @param period the nutation period to be set
239      */
240     public void setNutationPeriod(final double period) {
241         refuseFurtherComments();
242         this.nutationPer = period;
243     }
244 
245     /**
246      * Get the inertial nutation phase (rad).
247      * @return the inertial nutation phase
248      */
249     public Optional<Double> getNutationPhase() {
250         return Optional.ofNullable(nutationPhase);
251     }
252 
253     /**
254      * Set the inertial nutation phase (rad).
255      * @param nutationPhase the nutation phase to be set
256      */
257     public void setNutationPhase(final double nutationPhase) {
258         refuseFurtherComments();
259         this.nutationPhase = nutationPhase;
260     }
261 
262     /**
263      * Get the right ascension of angular momentum vector (rad).
264      * @return the right ascension of angular momentum vector
265      * @since 12.0
266      */
267     public Optional<Double> getMomentumAlpha() {
268         return Optional.ofNullable(momentumAlpha);
269     }
270 
271     /**
272      * Set the right ascension of angular momentum vector (rad).
273      * @param momentumAlpha value to be set
274      * @since 12.0
275      */
276     public void setMomentumAlpha(final double momentumAlpha) {
277         refuseFurtherComments();
278         this.momentumAlpha = momentumAlpha;
279     }
280 
281     /**
282      * Get the declination of the angular momentum vector (rad).
283      * @return the declination of the angular momentum vector (rad).
284      * @since 12.0
285      */
286     public Optional<Double> getMomentumDelta() {
287         return Optional.ofNullable(momentumDelta);
288     }
289 
290     /**
291      * Set the declination of the angular momentum vector (rad).
292      * @param momentumDelta value to be set
293      * @since 12.0
294      */
295     public void setMomentumDelta(final double momentumDelta) {
296         refuseFurtherComments();
297         this.momentumDelta = momentumDelta;
298     }
299 
300     /**
301      * Get the angular velocity of spin vector around angular momentum vector.
302      * @return angular velocity of spin vector around angular momentum vector (rad/s)
303      * @since 12.0
304      */
305     public Optional<Double> getNutationVel() {
306         return Optional.ofNullable(nutationVel);
307     }
308 
309     /**
310      * Set the angular velocity of spin vector around angular momentum vector.
311      * @param nutationVel angular velocity of spin vector around angular momentum vector (rad/s)
312      * @since 12.0
313      */
314     public void setNutationVel(final double nutationVel) {
315         refuseFurtherComments();
316         this.nutationVel = nutationVel;
317     }
318 
319     /** Check if the logical block includes nutation.
320      * @return true if logical block includes nutation
321      * @since 12.0
322      */
323     public boolean hasNutation() {
324         return getNutation().isPresent() && getNutationPeriod().isPresent() && getNutationPhase().isPresent();
325     }
326 
327     /** Check if the logical block includes momentum.
328      * @return true if logical block includes momentum
329      * @since 12.0
330      */
331     public boolean hasMomentum() {
332         return getMomentumAlpha().isPresent() && getMomentumDelta().isPresent() && getNutationVel().isPresent();
333     }
334 
335 }