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;
18  
19  import java.util.Arrays;
20  import java.util.Optional;
21  
22  import org.hipparchus.complex.Quaternion;
23  import org.orekit.annotation.Nullable;
24  import org.orekit.files.ccsds.definitions.CcsdsFrameMapper;
25  import org.orekit.files.ccsds.definitions.FrameFacade;
26  import org.orekit.files.ccsds.ndm.cdm.AdditionalParameters;
27  import org.orekit.files.ccsds.ndm.odm.ocm.OrbitPhysicalProperties;
28  import org.orekit.files.ccsds.section.CommentsContainer;
29  import org.orekit.frames.Frame;
30  import org.orekit.time.AbsoluteDate;
31  
32  /** Container for common physical properties for both {@link OrbitPhysicalProperties} and {@link AdditionalParameters}.
33   * <p>
34   * Beware that the Orekit getters and setters all rely on SI units. The parsers
35   * and writers take care of converting these SI units into CCSDS mandatory units.
36   * The {@link org.orekit.utils.units.Unit Unit} class provides useful
37   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
38   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} methods in case the callers
39   * already use CCSDS units instead of the API SI units. The general-purpose
40   * {@link org.orekit.utils.units.Unit Unit} class (without an 's') and the
41   * CCSDS-specific {@link org.orekit.files.ccsds.definitions.Units Units} class
42   * (with an 's') also provide some predefined units. These predefined units and the
43   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
44   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} conversion methods are indeed
45   * what the parsers and writers use for the conversions.
46   * </p>
47   * @author Maxime Journot
48   * @since 11.3
49   */
50  public class CommonPhysicalProperties extends CommentsContainer {
51  
52      /** For creating a {@link Frame}. */
53      private final CcsdsFrameMapper frameMapper;
54  
55      /** Optimally Enclosing Box parent reference frame. */
56      private FrameFacade oebParentFrame;
57  
58      /** Optimally Enclosing Box parent reference frame epoch. */
59      @Nullable
60      private AbsoluteDate oebParentFrameEpoch;
61  
62      /** Quaternion defining Optimally Enclosing Box. */
63      @Nullable
64      private double[] oebQ;
65  
66      /** Maximum physical dimension of Optimally Enclosing Box. */
67      @Nullable
68      private Double oebMax;
69  
70      /** Intermediate physical dimension of Optimally Enclosing Box. */
71      @Nullable
72      private Double oebIntermediate;
73  
74      /** Minimum physical dimension of Optimally Enclosing Box. */
75      @Nullable
76      private Double oebMin;
77  
78      /** Cross-sectional area of Optimally Enclosing Box when viewed along the maximum OEB direction. */
79      @Nullable
80      private Double oebAreaAlongMax;
81  
82      /** Cross-sectional area of Optimally Enclosing Box when viewed along the intermediate OEB direction. */
83      @Nullable
84      private Double oebAreaAlongIntermediate;
85  
86      /** Cross-sectional area of Optimally Enclosing Box when viewed along the minimum OEB direction. */
87      @Nullable
88      private Double oebAreaAlongMin;
89  
90      /** Typical (50th percentile) radar cross-section. */
91      @Nullable
92      private Double rcs;
93  
94      /** Minimum radar cross-section. */
95      @Nullable
96      private Double minRcs;
97  
98      /** Maximum radar cross-section. */
99      @Nullable
100     private Double maxRcs;
101 
102     /** Typical (50th percentile) visual magnitude. */
103     @Nullable
104     private Double vmAbsolute;
105 
106     /** Minimum apparent visual magnitude. */
107     @Nullable
108     private Double vmApparentMin;
109 
110     /** Typical (50th percentile) apparent visual magnitude. */
111     @Nullable
112     private Double vmApparent;
113 
114     /** Maximum apparent visual magnitude. */
115     @Nullable
116     private Double vmApparentMax;
117 
118     /** Typical (50th percentile) coefficient of reflectivity. */
119     @Nullable
120     private Double reflectance;
121 
122     /**
123      * Simple constructor.
124      *
125      * @param epochT0     T0 epoch from file metadata
126      * @param frameMapper for creating a {@link Frame}.
127      * @since 13.1.5
128      */
129     public CommonPhysicalProperties(final AbsoluteDate epochT0, final CcsdsFrameMapper frameMapper) {
130         // 502.0-B-3 (page 6-23) says the default is RSW_ROTATING, but also says,
131         // "This keyword shall be provided if OEB_Q1,2,3,4 are specified".
132         // Which means it must be specified in the file any time it would be used,
133         // which leaves the default without any effect.
134         this.frameMapper    = frameMapper;
135         oebParentFrame      = new FrameFacade(null, null, null, null, null);
136         // 502.0-B-3 (page 6-23) says the default is EPOCH_TZERO from the OCM metadata.
137         oebParentFrameEpoch = epochT0;
138     }
139 
140     /** Get the Optimally Enclosing Box parent reference frame.
141      * @return Optimally Enclosing Box parent reference frame
142      */
143     public FrameFacade getOebParentFrame() {
144         return oebParentFrame;
145     }
146 
147     /** Set the Optimally Enclosing Box parent reference frame.
148      * @param oebParentFrame Optimally Enclosing Box parent reference frame
149      */
150     public void setOebParentFrame(final FrameFacade oebParentFrame) {
151         refuseFurtherComments();
152         this.oebParentFrame = oebParentFrame;
153     }
154 
155     /** Get the Optimally Enclosing Box parent reference frame epoch.
156      * @return Optimally Enclosing Box parent reference frame epoch
157      */
158     public Optional<AbsoluteDate> getOebParentFrameEpoch() {
159         return Optional.ofNullable(oebParentFrameEpoch);
160     }
161 
162     /** Set the Optimally Enclosing Box parent reference frame epoch.
163      * @param oebParentFrameEpoch Optimally Enclosing Box parent reference frame epoch
164      */
165     public void setOebParentFrameEpoch(final AbsoluteDate oebParentFrameEpoch) {
166         refuseFurtherComments();
167         this.oebParentFrameEpoch = oebParentFrameEpoch;
168     }
169 
170     /**
171      * Get the mapping between a CCSDS frame and a {@link Frame}.
172      *
173      * @return the frame mapper.
174      * @since 13.1.5
175      */
176     public CcsdsFrameMapper getFrameMapper() {
177         return frameMapper;
178     }
179 
180     /**
181      * Get the frame OEB parent frame. Note that only the orientation of the
182      * returned frame is significant, the position of the returned frame is
183      * irrelevant and should be ignored.
184      *
185      * @return Orekit frame for this covariance history.
186      * @see #getOebParentFrame()
187      * @see #getOebParentFrameEpoch()
188      * @see #getFrameMapper()
189      * @since 13.1.5
190      */
191     public Frame getOebParent() {
192         return getFrameMapper().buildCcsdsFrame(oebParentFrame, oebParentFrameEpoch);
193     }
194 
195     /** Get the quaternion defining Optimally Enclosing Box.
196      * @return quaternion defining Optimally Enclosing Box
197      */
198     public Optional<Quaternion> getOebQ() {
199         if (oebQ == null) {
200             return Optional.empty();
201         }
202         return Optional.of(new Quaternion(oebQ[0], oebQ[1], oebQ[2], oebQ[3]));
203     }
204 
205     /** set the component of quaternion defining Optimally Enclosing Box.
206      * @param i index of the component
207      * @param qI component of quaternion defining Optimally Enclosing Box
208      */
209     public void setOebQ(final int i, final double qI) {
210         refuseFurtherComments();
211         if (oebQ == null) {
212             oebQ = new double[4];
213             Arrays.fill(oebQ, Double.NaN);
214         }
215         oebQ[i] = qI;
216     }
217 
218     /** Get the maximum physical dimension of the OEB.
219      * @return maximum physical dimension of the OEB.
220      */
221     public Optional<Double> getOebMax() {
222         return Optional.ofNullable(oebMax);
223     }
224 
225     /** Set the maximum physical dimension of the OEB.
226      * @param oebMax maximum physical dimension of the OEB.
227      */
228     public void setOebMax(final double oebMax) {
229         refuseFurtherComments();
230         this.oebMax = oebMax;
231     }
232 
233     /** Get the intermediate physical dimension of the OEB.
234      * @return intermediate physical dimension of the OEB.
235      */
236     public Optional<Double> getOebIntermediate() {
237         return Optional.ofNullable(oebIntermediate);
238     }
239 
240     /** Set the intermediate physical dimension of the OEB.
241      * @param oebIntermediate intermediate physical dimension of the OEB.
242      */
243     public void setOebIntermediate(final double oebIntermediate) {
244         refuseFurtherComments();
245         this.oebIntermediate = oebIntermediate;
246     }
247 
248     /** Get the minimum physical dimension of the OEB.
249      * @return dimensions the minimum physical dimension of the OEB.
250      */
251     public Optional<Double> getOebMin() {
252         return Optional.ofNullable(oebMin);
253     }
254 
255     /** Set the minimum physical dimension of the OEB.
256      * @param oebMin the minimum physical dimension of the OEB.
257      */
258     public void setOebMin(final double oebMin) {
259         refuseFurtherComments();
260         this.oebMin = oebMin;
261     }
262 
263     /** Get the cross-sectional area of Optimally Enclosing Box when viewed along the maximum OEB direction.
264      * @return cross-sectional area of Optimally Enclosing Box when viewed along the maximum OEB direction.
265      */
266     public Optional<Double> getOebAreaAlongMax() {
267         return Optional.ofNullable(oebAreaAlongMax);
268     }
269 
270     /** Set the cross-sectional area of Optimally Enclosing Box when viewed along the maximum OEB direction.
271      * @param oebAreaAlongMax cross-sectional area of Optimally Enclosing Box when viewed along the maximum OEB direction.
272      */
273     public void setOebAreaAlongMax(final double oebAreaAlongMax) {
274         refuseFurtherComments();
275         this.oebAreaAlongMax = oebAreaAlongMax;
276     }
277 
278     /** Get the cross-sectional area of Optimally Enclosing Box when viewed along the intermediate OEB direction.
279      * @return cross-sectional area of Optimally Enclosing Box when viewed along the intermediate OEB direction.
280      */
281     public Optional<Double> getOebAreaAlongIntermediate() {
282         return Optional.ofNullable(oebAreaAlongIntermediate);
283     }
284 
285     /** Set the cross-sectional area of Optimally Enclosing Box when viewed along the intermediate OEB direction.
286      * @param oebAreaAlongIntermediate cross-sectional area of Optimally Enclosing Box when viewed along the intermediate OEB direction.
287      */
288     public void setOebAreaAlongIntermediate(final double oebAreaAlongIntermediate) {
289         refuseFurtherComments();
290         this.oebAreaAlongIntermediate = oebAreaAlongIntermediate;
291     }
292 
293     /** Get the cross-sectional area of Optimally Enclosing Box when viewed along the minimum OEB direction.
294      * @return cross-sectional area of Optimally Enclosing Box when viewed along the minimum OEB direction.
295      */
296     public Optional<Double> getOebAreaAlongMin() {
297         return Optional.ofNullable(oebAreaAlongMin);
298     }
299 
300     /** Set the cross-sectional area of Optimally Enclosing Box when viewed along the minimum OEB direction.
301      * @param oebAreaAlongMin cross-sectional area of Optimally Enclosing Box when viewed along the minimum OEB direction.
302      */
303     public void setOebAreaAlongMin(final double oebAreaAlongMin) {
304         refuseFurtherComments();
305         this.oebAreaAlongMin = oebAreaAlongMin;
306     }
307 
308 
309     /** Get the typical (50th percentile) radar cross-section.
310      * @return typical (50th percentile) radar cross-section
311      */
312     public Optional<Double> getRcs() {
313         return Optional.ofNullable(rcs);
314     }
315 
316     /** Set the typical (50th percentile) radar cross-section.
317      * @param rcs typical (50th percentile) radar cross-section
318      */
319     public void setRcs(final double rcs) {
320         refuseFurtherComments();
321         this.rcs = rcs;
322     }
323 
324     /** Get the minimum radar cross-section.
325      * @return minimum radar cross-section
326      */
327     public Optional<Double> getMinRcs() {
328         return Optional.ofNullable(minRcs);
329     }
330 
331     /** Set the minimum radar cross-section.
332      * @param minRcs minimum radar cross-section
333      */
334     public void setMinRcs(final double minRcs) {
335         refuseFurtherComments();
336         this.minRcs = minRcs;
337     }
338 
339     /** Get the maximum radar cross-section.
340      * @return maximum radar cross-section
341      */
342     public Optional<Double> getMaxRcs() {
343         return Optional.ofNullable(maxRcs);
344     }
345 
346     /** Set the maximum radar cross-section.
347      * @param maxRcs maximum radar cross-section
348      */
349     public void setMaxRcs(final double maxRcs) {
350         refuseFurtherComments();
351         this.maxRcs = maxRcs;
352     }
353 
354     /** Get the typical (50th percentile) visual magnitude.
355      * @return typical (50th percentile) visual magnitude
356      */
357     public Optional<Double> getVmAbsolute() {
358         return Optional.ofNullable(vmAbsolute);
359     }
360 
361     /** Set the typical (50th percentile) visual magnitude.
362      * @param vmAbsolute typical (50th percentile) visual magnitude
363      */
364     public void setVmAbsolute(final double vmAbsolute) {
365         refuseFurtherComments();
366         this.vmAbsolute = vmAbsolute;
367     }
368 
369     /** Get the minimum apparent visual magnitude.
370      * @return minimum apparent visual magnitude
371      */
372     public Optional<Double> getVmApparentMin() {
373         return Optional.ofNullable(vmApparentMin);
374     }
375 
376     /** Set the minimum apparent visual magnitude.
377      * @param vmApparentMin minimum apparent visual magnitude
378      */
379     public void setVmApparentMin(final double vmApparentMin) {
380         refuseFurtherComments();
381         this.vmApparentMin = vmApparentMin;
382     }
383 
384     /** Get the typical (50th percentile) apparent visual magnitude.
385      * @return typical (50th percentile) apparent visual magnitude
386      */
387     public Optional<Double> getVmApparent() {
388         return Optional.ofNullable(vmApparent);
389     }
390 
391     /** Set the typical (50th percentile) apparent visual magnitude.
392      * @param vmApparent typical (50th percentile) apparent visual magnitude
393      */
394     public void setVmApparent(final double vmApparent) {
395         refuseFurtherComments();
396         this.vmApparent = vmApparent;
397     }
398 
399     /** Get the maximum apparent visual magnitude.
400      * @return maximum apparent visual magnitude
401      */
402     public Optional<Double> getVmApparentMax() {
403         return Optional.ofNullable(vmApparentMax);
404     }
405 
406     /** Set the maximum apparent visual magnitude.
407      * @param vmApparentMax maximum apparent visual magnitude
408      */
409     public void setVmApparentMax(final double vmApparentMax) {
410         refuseFurtherComments();
411         this.vmApparentMax = vmApparentMax;
412     }
413 
414     /** Get the typical (50th percentile) coefficient of reflectance.
415      * @return typical (50th percentile) coefficient of reflectance
416      */
417     public Optional<Double> getReflectance() {
418         return Optional.ofNullable(reflectance);
419     }
420 
421     /** Set the typical (50th percentile) coefficient of reflectance.
422      * @param reflectance typical (50th percentile) coefficient of reflectance
423      */
424     public void setReflectance(final double reflectance) {
425         refuseFurtherComments();
426         this.reflectance = reflectance;
427     }
428 }