1   /* Copyright 2002-2025 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.odm.ocm;
18  
19  import java.util.regex.Matcher;
20  import java.util.regex.Pattern;
21  
22  import org.orekit.files.ccsds.definitions.Units;
23  import org.orekit.files.ccsds.utils.ContextBinding;
24  import org.orekit.files.ccsds.utils.lexical.ParseToken;
25  import org.orekit.files.ccsds.utils.lexical.TokenType;
26  import org.orekit.utils.units.Unit;
27  
28  
29  /** Keys for {@link Perturbations perturbations data} entries.
30   * @author Luc Maisonobe
31   * @since 11.0
32   */
33  public enum PerturbationsKey {
34  
35      /** Comment entry. */
36      COMMENT((token, context, container) ->
37              token.getType() == TokenType.ENTRY ? container.addComment(token.getContentAsNormalizedString()) : true),
38  
39      /** Name of atmospheric model. */
40      ATMOSPHERIC_MODEL((token, context, container) -> token.processAsFreeTextString(container::setAtmosphericModel)),
41  
42      /** Gravity model. */
43      GRAVITY_MODEL(new GravityProcessor()),
44  
45      /** Oblate spheroid equatorial radius of central body. */
46      EQUATORIAL_RADIUS((token, context, container) -> token.processAsDouble(Unit.KILOMETRE, context.getParsedUnitsBehavior(),
47                                                                             container::setEquatorialRadius)),
48  
49      /** Central body oblate spheroid oblateness. */
50      GM((token, context, container) -> token.processAsDouble(Units.KM3_PER_S2, context.getParsedUnitsBehavior(),
51                                                              container::setGm)),
52  
53      /** N-body perturbation bodies. */
54      N_BODY_PERTURBATIONS((token, context, container) -> token.processAsCenterList(container::setNBodyPerturbations,
55                                                                                    context.getDataContext().getCelestialBodies())),
56  
57      /** Central body angular rotation rate. */
58      CENTRAL_BODY_ROTATION((token, context, container) -> token.processAsDouble(Units.DEG_PER_S, context.getParsedUnitsBehavior(),
59                                                                                 container::setCentralBodyRotation)),
60  
61      /** Central body oblate spheroid oblateness. */
62      OBLATE_FLATTENING((token, context, container) -> token.processAsDouble(Unit.ONE, context.getParsedUnitsBehavior(),
63                                                                             container::setOblateFlattening)),
64  
65      /** Ocean tides model. */
66      OCEAN_TIDES_MODEL((token, context, container) -> token.processAsFreeTextString(container::setOceanTidesModel)),
67  
68      /** Solid tides model. */
69      SOLID_TIDES_MODEL((token, context, container) -> token.processAsFreeTextString(container::setSolidTidesModel)),
70  
71      /** Reduction theory used for precession and nutation modeling. */
72      REDUCTION_THEORY((token, context, container) -> token.processAsFreeTextString(container::setReductionTheory)),
73  
74      /** Albedo model. */
75      ALBEDO_MODEL((token, context, container) -> token.processAsFreeTextString(container::setAlbedoModel)),
76  
77      /** Albedo grid size. */
78      ALBEDO_GRID_SIZE((token, context, container) -> token.processAsInteger(container::setAlbedoGridSize)),
79  
80      /** Shadow model used for solar radiation pressure. */
81      SHADOW_MODEL((token, context, container) -> token.processAsEnum(ShadowModel.class, container::setShadowModel)),
82  
83      /** Names of shadow bodies. */
84      SHADOW_BODIES((token, context, container) -> token.processAsCenterList(container::setShadowBodies,
85                                                                             context.getDataContext().getCelestialBodies())),
86  
87      /** Solar Radiation Pressure model. */
88      SRP_MODEL((token, context, container) -> token.processAsFreeTextString(container::setSrpModel)),
89  
90      /** Space Weather data source. */
91      SW_DATA_SOURCE((token, context, container) -> token.processAsFreeTextString(container::setSpaceWeatherSource)),
92  
93      /** Epoch of the Space Weather data. */
94      SW_DATA_EPOCH((token, context, container) -> token.processAsDate(container::setSpaceWeatherEpoch, context)),
95  
96      /** Interpolation method for Space Weather data. */
97      SW_INTERP_METHOD((token, context, container) -> token.processAsFreeTextString(container::setInterpMethodSW)),
98  
99      /** Fixed (time invariant) value of the planetary 3-hour-range geomagnetic index Kₚ. */
100     FIXED_GEOMAG_KP((token, context, container) -> token.processAsDouble(Units.NANO_TESLA, context.getParsedUnitsBehavior(),
101                                                                          container::setFixedGeomagneticKp)),
102 
103     /** Fixed (time invariant) value of the planetary 3-hour-range geomagnetic index aₚ. */
104     FIXED_GEOMAG_AP((token, context, container) -> token.processAsDouble(Units.NANO_TESLA, context.getParsedUnitsBehavior(),
105                                                                          container::setFixedGeomagneticAp)),
106 
107     /** Fixed (time invariant) value of the planetary 1-hour-range geomagnetic index Dst. */
108     FIXED_GEOMAG_DST((token, context, container) -> token.processAsDouble(Units.NANO_TESLA, context.getParsedUnitsBehavior(),
109                                                                           container::setFixedGeomagneticDst)),
110 
111     /** Fixed (time invariant) value of the Solar Flux Unit daily proxy F10.7. */
112     FIXED_F10P7((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
113                                                                      container::setFixedF10P7)),
114 
115     /** Fixed (time invariant) value of the Solar Flux Unit 81-day running center-average proxy F10.7. */
116     FIXED_F10P7_MEAN((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
117                                                                           container::setFixedF10P7Mean)),
118 
119     /** Fixed (time invariant) value of the Solar Flux daily proxy M10.7. */
120     FIXED_M10P7((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
121                                                                      container::setFixedM10P7)),
122 
123     /** Fixed (time invariant) value of the Solar Flux 81-day running center-average proxy M10.7. */
124     FIXED_M10P7_MEAN((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
125                                                                           container::setFixedM10P7Mean)),
126 
127     /** Fixed (time invariant) value of the Solar Flux daily proxy S10.7. */
128     FIXED_S10P7((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
129                                                                      container::setFixedS10P7)),
130 
131     /** Fixed (time invariant) value of the Solar Flux 81-day running center-average proxy S10.7. */
132     FIXED_S10P7_MEAN((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
133                                                                           container::setFixedS10P7Mean)),
134 
135     /** Fixed (time invariant) value of the Solar Flux daily proxy Y10.7. */
136     FIXED_Y10P7((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
137                                                                      container::setFixedY10P7)),
138 
139     /** Fixed (time invariant) value of the Solar Flux 81-day running center-average proxy Y10.7. */
140     FIXED_Y10P7_MEAN((token, context, container) -> token.processAsDouble(Unit.SOLAR_FLUX_UNIT, context.getParsedUnitsBehavior(),
141                                                                           container::setFixedY10P7Mean));
142 
143     /** Processing method. */
144     private final transient TokenProcessor processor;
145 
146     /** Simple constructor.
147      * @param processor processing method
148      */
149     PerturbationsKey(final TokenProcessor processor) {
150         this.processor = processor;
151     }
152 
153     /** Process an token.
154      * @param token token to process
155      * @param context context binding
156      * @param container container to fill
157      * @return true of token was accepted
158      */
159     public boolean process(final ParseToken token, final ContextBinding context, final Perturbations container) {
160         return processor.process(token, context, container);
161     }
162 
163     /** Interface for processing one token. */
164     interface TokenProcessor {
165         /** Process one token.
166          * @param token token to process
167          * @param context context binding
168          * @param container container to fill
169          * @return true of token was accepted
170          */
171         boolean process(ParseToken token, ContextBinding context, Perturbations container);
172     }
173 
174     /** Dedicated processor for gravity field. */
175     private static class GravityProcessor implements TokenProcessor {
176 
177         /** Pattern for splitting gravity specification. */
178         private static final Pattern GRAVITY_PATTERN =
179                         Pattern.compile("^\\p{Blank}*([-_A-Za-z0-9]+)\\p{Blank}*:" +
180                                         "\\p{Blank}*([0-9]+)D" +
181                                         "\\p{Blank}*([0-9]+)O" +
182                                         "\\p{Blank}*$");
183 
184         /** {@inheritDoc} */
185         @Override
186         public boolean process(final ParseToken token, final ContextBinding context, final Perturbations container) {
187             if (token.getType() == TokenType.ENTRY) {
188                 final Matcher matcher = GRAVITY_PATTERN.matcher(token.getRawContent());
189                 if (!matcher.matches()) {
190                     throw token.generateException(null);
191                 }
192                 container.setGravityModel(matcher.group(1),
193                                           Integer.parseInt(matcher.group(2)),
194                                           Integer.parseInt(matcher.group(3)));
195             }
196             return true;
197         }
198     }
199 
200 }