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  
18  package org.orekit.files.ccsds.ndm.odm.omm;
19  
20  import java.util.Optional;
21  
22  import org.orekit.annotation.Nullable;
23  import org.orekit.files.ccsds.section.CommentsContainer;
24  
25  /** Container for TLE data.
26   * <p>
27   * Beware that the Orekit getters and setters all rely on SI units. The parsers
28   * and writers take care of converting these SI units into CCSDS mandatory units.
29   * The {@link org.orekit.utils.units.Unit Unit} class provides useful
30   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
31   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} methods in case the callers
32   * already use CCSDS units instead of the API SI units. The general-purpose
33   * {@link org.orekit.utils.units.Unit Unit} class (without an 's') and the
34   * CCSDS-specific {@link org.orekit.files.ccsds.definitions.Units Units} class
35   * (with an 's') also provide some predefined units. These predefined units and the
36   * {@link org.orekit.utils.units.Unit#fromSI(double) fromSi} and
37   * {@link org.orekit.utils.units.Unit#toSI(double) toSI} conversion methods are indeed
38   * what the parsers and writers use for the conversions.
39   * </p>
40   * @author sports
41   * @since 6.1
42   */
43  public class OmmTle extends CommentsContainer {
44  
45      /** Constant for EPHEMERIS_TYPE SGP.
46       * @since 12.0
47       */
48      public static final int EPHEMERIS_TYPE_SGP = 0;
49  
50      /** Constant for EPHEMERIS_TYPE SGP4.
51       * @since 12.0
52       */
53      public static final int EPHEMERIS_TYPE_SGP4 = 2;
54  
55      /** Constant for EPHEMERIS_TYPE PPT3.
56       * @since 12.0
57       */
58      public static final int EPHEMERIS_TYPE_PPT3 = 3;
59  
60      /** Constant for EPHEMERIS_TYPE SGP4-XP.
61       * @since 12.0
62       */
63      public static final int EPHEMERIS_TYPE_SGP4_XP = 4;
64  
65      /** Constant for EPHEMERIS_TYPE Special Perturbations.
66       * @since 12.0
67       */
68      public static final int EPHEMERIS_TYPE_SPECIAL_PERTURBATIONS = 6;
69  
70      /** Ephemeris Type, only required if MEAN_ELEMENT_THEORY = SGP/SGP4. Some sources suggest the coding for
71       * the EPHEMERIS_TYPE keyword: 0 = SGP, 2 = SGP4, 3 = PPT3, 4 = SGP4-XP, 6 = Special Perturbations. Default value = 0.
72       */
73      private int ephemerisType;
74  
75      /** Classification Type, only required if MEAN_ELEMENT_THEORY = SGP/SGP4. Some sources suggest the
76       *  following coding for the CLASSIFICATION_TYPE keyword: U = unclassified, S = secret. Default value = U.
77       */
78      private char classificationType;
79  
80      /** NORAD Catalog Number ("Satellite Number"), an integer of up to nine digits. */
81      private int noradID;
82  
83      /** Element set number for this satellite, only required if MEAN_ELEMENT_THEORY = SGP/SGP4.
84       * Normally incremented sequentially, but may be out of sync if it is generated from a backup source.
85       * Used to distinguish different TLEs, and therefore only meaningful if TLE based data is being exchanged. */
86      private int elementSetNo;
87  
88      /** Revolution Number, only required if MEAN_ELEMENT_THEORY = SGP/SGP4. */
89      private int revAtEpoch;
90  
91      /** SGP/SGP4 drag-like coefficient (in units 1/[Earth radii]), only required if MEAN_ELEMENT_THEORY = SGP/SGP4. */
92      @Nullable
93      private Double bStar;
94  
95      /** SGP4-XP drag-like coefficient (in m²/kg), only required if MEAN_ELEMENT_THEORY = SGP4-XP.
96       * @since 12.0
97       */
98      @Nullable
99      private Double bTerm;
100 
101     /** First Time Derivative of the Mean Motion, only required if MEAN_ELEMENT_THEORY = SGP. */
102     @Nullable
103     private Double meanMotionDot;
104 
105     /** Second Time Derivative of Mean Motion, only required if MEAN_ELEMENT_THEORY = SGP. */
106     @Nullable
107     private Double meanMotionDotDot;
108 
109     /** SGP4-XP solar radiation pressure-like coefficient Aγ/m (in m²/kg), only required if MEAN_ELEMENT_THEORY = SGP4-XP.
110      * @since 12.0
111      */
112     @Nullable
113     private Double agOm;
114 
115     /** Create an empty data set.
116      */
117     public OmmTle() {
118         ephemerisType      = EPHEMERIS_TYPE_SGP;
119         classificationType = 'U';
120         noradID            = -1;
121         elementSetNo       = -1;
122         revAtEpoch         = -1;
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public void validate(final double version) {
128         super.validate(version);
129 
130         checkNotNegative(noradID,      OmmTleKey.NORAD_CAT_ID.name());
131         checkNotNegative(elementSetNo, OmmTleKey.ELEMENT_SET_NO.name());
132         checkNotNegative(revAtEpoch,   OmmTleKey.REV_AT_EPOCH.name());
133 
134         if (ephemerisType == EPHEMERIS_TYPE_SGP4) {
135             checkNotNaN(getBStar().orElse(Double.NaN), OmmTleKey.BSTAR.name());
136         } else if (ephemerisType == EPHEMERIS_TYPE_SGP4_XP) {
137             checkNotNaN(getBTerm().orElse(Double.NaN), OmmTleKey.BTERM.name());
138         }
139 
140         if (ephemerisType == EPHEMERIS_TYPE_SGP  || ephemerisType == EPHEMERIS_TYPE_PPT3) {
141             checkNotNaN(getMeanMotionDot().orElse(Double.NaN), OmmTleKey.MEAN_MOTION_DOT.name());
142         }
143 
144         if (ephemerisType == EPHEMERIS_TYPE_SGP  || ephemerisType == EPHEMERIS_TYPE_PPT3) {
145             checkNotNaN(getMeanMotionDotDot().orElse(Double.NaN), OmmTleKey.MEAN_MOTION_DDOT.name());
146         } else if (ephemerisType == EPHEMERIS_TYPE_SGP4_XP) {
147             checkNotNaN(getAGoM().orElse(Double.NaN), OmmTleKey.AGOM.name());
148         }
149 
150     }
151 
152     /** Get the ephemeris type.
153      * @return the ephemerisType
154      */
155     public int getEphemerisType() {
156         return ephemerisType;
157     }
158 
159     /** Set the ephemeris type.
160      * @param ephemerisType the ephemeris type to be set
161      */
162     public void setEphemerisType(final int ephemerisType) {
163         refuseFurtherComments();
164         this.ephemerisType = ephemerisType;
165     }
166 
167     /** Get the classification type.
168      * @return the classificationType
169      */
170     public char getClassificationType() {
171         return classificationType;
172     }
173 
174     /** Set the classification type.
175      * @param classificationType the classification type to be set
176      */
177     public void setClassificationType(final char classificationType) {
178         refuseFurtherComments();
179         this.classificationType = classificationType;
180     }
181 
182     /** Get the NORAD Catalog Number ("Satellite Number").
183      * @return the NORAD Catalog Number
184      */
185     public int getNoradID() {
186         return noradID;
187     }
188 
189     /** Set the NORAD Catalog Number ("Satellite Number").
190      * @param noradID the element set number to be set
191      */
192     public void setNoradID(final int noradID) {
193         refuseFurtherComments();
194         this.noradID = noradID;
195     }
196 
197     /** Get the element set number for this satellite.
198      * @return the element set number for this satellite
199      */
200     public int getElementSetNumber() {
201         return elementSetNo;
202     }
203 
204     /** Set the element set number for this satellite.
205      * @param elementSetNo the element set number to be set
206      */
207     public void setElementSetNo(final int elementSetNo) {
208         refuseFurtherComments();
209         this.elementSetNo = elementSetNo;
210     }
211 
212     /** Get the revolution rumber.
213      * @return the revolution rumber
214      */
215     public int getRevAtEpoch() {
216         return revAtEpoch;
217     }
218 
219     /** Set the revolution rumber.
220      * @param revAtEpoch the Revolution Number to be set
221      */
222     public void setRevAtEpoch(final int revAtEpoch) {
223         refuseFurtherComments();
224         this.revAtEpoch = revAtEpoch;
225     }
226 
227     /** Get the SGP/SGP4 drag-like coefficient.
228      * @return the SGP/SGP4 drag-like coefficient
229      */
230     public Optional<Double> getBStar() {
231         return Optional.ofNullable(bStar);
232     }
233 
234     /** Set the SGP/SGP4 drag-like coefficient.
235      * @param bstar the SGP/SGP4 drag-like coefficient to be set
236      */
237     public void setBStar(final double bstar) {
238         refuseFurtherComments();
239         this.bStar = bstar;
240     }
241 
242     /** Get the SGP4-XP drag-like coefficient.
243      * @return the SGP4-XP drag-like coefficient
244      * @since 12.0
245      */
246     public Optional<Double> getBTerm() {
247         return Optional.ofNullable(bTerm);
248     }
249 
250     /** Set the SGP4-XP drag-like coefficient.
251      * @param bterm the SGP4-XP drag-like coefficient to be set
252      * @since 12.0
253      */
254     public void setBTerm(final double bterm) {
255         refuseFurtherComments();
256         this.bTerm = bterm;
257     }
258 
259     /** Get the first time derivative of the mean motion.
260      * @return the first time derivative of the mean motion
261      */
262     public Optional<Double> getMeanMotionDot() {
263         return Optional.ofNullable(meanMotionDot);
264     }
265 
266     /** Set the first time derivative of the mean motion.
267      * @param meanMotionDot the first time derivative of the mean motion to be set
268      */
269     public void setMeanMotionDot(final double meanMotionDot) {
270         refuseFurtherComments();
271         this.meanMotionDot = meanMotionDot;
272     }
273 
274     /** Get the second time derivative of the mean motion.
275      * @return the second time derivative of the mean motion
276      */
277     public Optional<Double> getMeanMotionDotDot() {
278         return Optional.ofNullable(meanMotionDotDot);
279     }
280 
281     /** Set the second time derivative of the mean motion.
282      * @param meanMotionDotDot the second time derivative of the mean motion to be set
283      */
284     public void setMeanMotionDotDot(final double meanMotionDotDot) {
285         refuseFurtherComments();
286         this.meanMotionDotDot = meanMotionDotDot;
287     }
288 
289     /** Get the SGP4-XP solar radiation pressure-like coefficient Aγ/m.
290      * @return the SGP4-XP solar radiation pressure-like coefficient Aγ/m
291      * @since 12.0
292      */
293     public Optional<Double> getAGoM() {
294         return Optional.ofNullable(agOm);
295     }
296 
297     /** Set the SGP4-XP solar radiation pressure-like coefficient Aγ/m.
298      * @param agom the SGP4-XP solar radiation pressure-like coefficient Aγ/m to be set
299      * @since 12.0
300      */
301     public void setAGoM(final double agom) {
302         refuseFurtherComments();
303         this.agOm = agom;
304     }
305 
306 }