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.forces.gravity.potential;
18  
19  import org.hipparchus.util.FastMath;
20  import org.hipparchus.util.SinCos;
21  import org.orekit.time.AbsoluteDate;
22  import org.orekit.utils.TimeSpanMap;
23  
24  /** Piecewise gravity fields with time-dependent models in each interval.
25   * @author Luc Maisonobe
26   * @since 11.1
27   */
28  class PiecewiseSphericalHarmonics implements RawSphericalHarmonicsProvider {
29  
30      /** Constant part of the field. */
31      private final ConstantSphericalHarmonics constant;
32  
33      /** Reference dates. */
34      private final AbsoluteDate[] references;
35  
36      /** Pulsations (rad/s). */
37      private final double[] pulsations;
38  
39      /** Piecewise parts. */
40      private final TimeSpanMap<PiecewisePart> pieces;
41  
42      /** Maximum supported degree. */
43      private final int maxDegree;
44  
45      /** Maximum supported order. */
46      private final int maxOrder;
47  
48      /** Simple constructor.
49       * @param constant constant part of the field
50       * @param references references dates
51       * @param pulsations pulsations (rad/s)
52       * @param pieces piecewise parts
53       */
54      PiecewiseSphericalHarmonics(final ConstantSphericalHarmonics constant,
55                                  final AbsoluteDate[] references, final double[] pulsations,
56                                  final TimeSpanMap<PiecewisePart> pieces) {
57          this.constant   = constant;
58          this.references = references.clone();
59          this.pulsations = pulsations.clone();
60          this.pieces     = pieces;
61  
62          // get limits
63          int d = constant.getMaxDegree();
64          int o = constant.getMaxOrder();
65          for (TimeSpanMap.Span<PiecewisePart> span = pieces.getFirstSpan(); span != null; span = span.next()) {
66              final PiecewisePart piece = span.getData();
67              if (piece != null) {
68                  d = FastMath.max(d, piece.getMaxDegree());
69                  o = FastMath.max(o, piece.getMaxOrder());
70              }
71          }
72          this.maxDegree = d;
73          this.maxOrder  = o;
74  
75      }
76  
77      /** Get the constant part of the field.
78       * @return constant part of the field
79       */
80      public ConstantSphericalHarmonics getConstant() {
81          return constant;
82      }
83  
84      /** {@inheritDoc} */
85      public int getMaxDegree() {
86          return maxDegree;
87      }
88  
89      /** {@inheritDoc} */
90      public int getMaxOrder() {
91          return maxOrder;
92      }
93  
94      /** {@inheritDoc} */
95      public double getMu() {
96          return constant.getMu();
97      }
98  
99      /** {@inheritDoc} */
100     public double getAe() {
101         return constant.getAe();
102     }
103 
104     /** {@inheritDoc} */
105     public AbsoluteDate getReferenceDate() {
106         AbsoluteDate last = AbsoluteDate.PAST_INFINITY;
107         for (final AbsoluteDate date : references) {
108             if (date.isAfter(last)) {
109                 last = date;
110             }
111         }
112         return last;
113     }
114 
115     /** {@inheritDoc} */
116     public TideSystem getTideSystem() {
117         return constant.getTideSystem();
118     }
119 
120     /** Get the raw spherical harmonic coefficients on a specific date.
121      * @param date to evaluate the spherical harmonics
122      * @return the raw spherical harmonics on {@code date}.
123      */
124     public RawSphericalHarmonics onDate(final AbsoluteDate date) {
125 
126         // raw (constant) harmonics
127         final RawSphericalHarmonics raw = constant.onDate(date);
128 
129         // select part of the piecewise model that is active at specified date
130         final PiecewisePart piece = pieces.get(date);
131 
132         // pre-compute canonical functions
133         final double[]   offsets = new double[references.length];
134         final SinCos[][] sinCos  = new SinCos[references.length][pulsations.length];
135         for (int i = 0; i < references.length; ++i) {
136             final double offset = date.durationFrom(references[i]);
137             offsets[i] = offset;
138             for (int j = 0; j < pulsations.length; ++j) {
139                 sinCos[i][j] = FastMath.sinCos(offset * pulsations[j]);
140             }
141         }
142 
143         return new RawSphericalHarmonics() {
144 
145             @Override
146             public AbsoluteDate getDate() {
147                 return date;
148             }
149 
150             /** {@inheritDoc} */
151             public double getRawCnm(final int n, final int m) {
152                 return raw.getRawCnm(n, m) + piece.computeCnm(n, m, offsets, sinCos);
153             }
154 
155             /** {@inheritDoc} */
156             public double getRawSnm(final int n, final int m) {
157                 return raw.getRawSnm(n, m) + piece.computeSnm(n, m, offsets, sinCos);
158             }
159 
160         };
161 
162     }
163 
164 }