1   /* Copyright 2022-2025 Thales Alenia Space
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.models.earth.ionosphere.nequick;
18  
19  import org.hipparchus.CalculusFieldElement;
20  import org.hipparchus.util.FastMath;
21  import org.hipparchus.util.FieldSinCos;
22  import org.hipparchus.util.MathArrays;
23  import org.orekit.time.DateTimeComponents;
24  
25  /**
26   * Fourier time series for the NeQuick model.
27   * @see NeQuickModel#computeFourierTimeSeries(DateTimeComponents, double)
28   * @author Luc Maisonobe
29   * @since 13.0.1
30   * @param <T> type of the field elements
31   */
32  public class FieldFourierTimeSeries<T extends CalculusFieldElement<T>> {
33  
34      /** Date. */
35      private final DateTimeComponents dateTime;
36  
37      /** Effective ionisation level. */
38      private final T az;
39  
40      /** Effective sunspot number (Eq. 19). */
41      private final T azr;
42  
43      /** Fourier time series for foF2. */
44      private final T[] cf2;
45  
46      /** Fourier time series for M(3000)F2. */
47      private final T[] cm3;
48  
49      /**
50       * Simple constructor.
51       * @param dateTime   current date time components
52       * @param az         effective ionisation level
53       * @param flattenF2  F2 coefficients used by the F2 layer (flatten array)
54       * @param flattenFm3 Fm3 coefficients used by the M(3000)F2 layer (flatten array)
55       */
56      FieldFourierTimeSeries(final DateTimeComponents dateTime, final T az,
57                             final double[] flattenF2, final double[] flattenFm3) {
58  
59          this.dateTime = dateTime;
60          this.az       = az;
61  
62          // Effective sunspot number (Eq. 19)
63          this.azr = FastMath.sqrt(az.subtract(63.7).multiply(1123.6).add(167273.0)).subtract(408.99);
64  
65          // Hours
66          final double hours = dateTime.getTime().getSecondsInUTCDay() / 3600.0;
67  
68          // Time argument (Eq. 49)
69          final double t = FastMath.toRadians(15 * hours) - FastMath.PI;
70  
71          // Compute Fourier time series for foF2 and M(3000)F2
72          final T[] scT = sinCos(az.newInstance(t), 6);
73          this.cf2 = computeCF2(flattenF2, scT);
74          this.cm3 = computeCm3(flattenFm3, scT);
75  
76      }
77  
78      /** Get date time components.
79       * @return date time components
80       */
81      public DateTimeComponents getDateTime() {
82          return dateTime;
83      }
84  
85      /** Get effective ionisation level.
86       * @return effective ionisation level
87       */
88      public T getAz() {
89          return az;
90      }
91  
92      /** Get effective sunspot number.
93       * @return effective sunspot number
94       */
95      public T getAzr() {
96          return azr;
97      }
98  
99      /** Get Fourier time series for foF2.
100      * <p>
101      * Beware that for efficiency purposes, this method returns
102      * a reference to an internal array; this is the reason why
103      * this method visibility is limited to package level.
104      * </p>
105      * @return Fourier time series for foF2 (reference to an internal array)
106      */
107     T[] getCf2Reference() {
108         return cf2;
109     }
110 
111     /** Get Fourier time series for M(3000)F2.
112      * <p>
113      * Beware that for efficiency purposes, this method returns
114      * a reference to an internal array; this is the reason why
115      * this method visibility is limited to package level.
116      * </p>
117      * @return Fourier time series for M(3000)F2 (reference to an internal array)
118      */
119     T[] getCm3Reference() {
120         return cm3;
121     }
122 
123     /** Computes cf2 coefficients.
124      * @param flattenF2 F2 coefficients used by the F2 layer (flatten array)
125      * @param scT       sines/cosines array of time argument
126      * @return the cf2 coefficients array
127      */
128     private T[] computeCF2(final double[] flattenF2, final T[] scT) {
129 
130         // interpolation coefficients for effective spot number
131         final T azr01 = azr.multiply(0.01);
132         final T omazr01 = azr01.negate().add(1);
133 
134         // Eq. 44 and Eq. 50 merged into one loop
135         final T[] array = MathArrays.buildArray(azr.getField(), 76);
136         int index = 0;
137         for (int i = 0; i < array.length; i++) {
138             // CHECKSTYLE: stop Indentation check
139             array[i] = omazr01.multiply(flattenF2[index     ]).add(azr01.multiply(flattenF2[index +  1])).
140                    add(omazr01.multiply(flattenF2[index +  2]).add(azr01.multiply(flattenF2[index +  3])).multiply(scT[ 0])).
141                    add(omazr01.multiply(flattenF2[index +  4]).add(azr01.multiply(flattenF2[index +  5])).multiply(scT[ 1])).
142                    add(omazr01.multiply(flattenF2[index +  6]).add(azr01.multiply(flattenF2[index +  7])).multiply(scT[ 2])).
143                    add(omazr01.multiply(flattenF2[index +  8]).add(azr01.multiply(flattenF2[index +  9])).multiply(scT[ 3])).
144                    add(omazr01.multiply(flattenF2[index + 10]).add(azr01.multiply(flattenF2[index + 11])).multiply(scT[ 4])).
145                    add(omazr01.multiply(flattenF2[index + 12]).add(azr01.multiply(flattenF2[index + 13])).multiply(scT[ 5])).
146                    add(omazr01.multiply(flattenF2[index + 14]).add(azr01.multiply(flattenF2[index + 15])).multiply(scT[ 6])).
147                    add(omazr01.multiply(flattenF2[index + 16]).add(azr01.multiply(flattenF2[index + 17])).multiply(scT[ 7])).
148                    add(omazr01.multiply(flattenF2[index + 18]).add(azr01.multiply(flattenF2[index + 19])).multiply(scT[ 8])).
149                    add(omazr01.multiply(flattenF2[index + 20]).add(azr01.multiply(flattenF2[index + 21])).multiply(scT[ 9])).
150                    add(omazr01.multiply(flattenF2[index + 22]).add(azr01.multiply(flattenF2[index + 23])).multiply(scT[10])).
151                    add(omazr01.multiply(flattenF2[index + 24]).add(azr01.multiply(flattenF2[index + 25])).multiply(scT[11]));
152             index += 26;
153             // CHECKSTYLE: resume Indentation check
154         }
155         return array;
156     }
157 
158     /** Computes Cm3 coefficients.
159      * @param flattenFm3 Fm3 coefficients used by the M(3000)F2 layer (flatten array)
160      * @param scT        sines/cosines array of time argument
161      * @return the Cm3 coefficients array
162      */
163     private T[] computeCm3(final double[] flattenFm3, final T[] scT) {
164 
165         // interpolation coefficients for effective spot number
166         final T azr01 = azr.multiply(0.01);
167         final T omazr01 = azr01.negate().add(1);
168 
169         // Eq. 44 and Eq. 51 merged into one loop
170         final T[] array = MathArrays.buildArray(azr.getField(), 49);
171         int index = 0;
172         for (int i = 0; i < array.length; i++) {
173             array[i] = omazr01.multiply(flattenFm3[index     ]).add(azr01.multiply(flattenFm3[index +  1])).
174                    add(omazr01.multiply(flattenFm3[index +  2]).add(azr01.multiply(flattenFm3[index +  3])).multiply(scT[ 0])).
175                    add(omazr01.multiply(flattenFm3[index +  4]).add(azr01.multiply(flattenFm3[index +  5])).multiply(scT[ 1])).
176                    add(omazr01.multiply(flattenFm3[index +  6]).add(azr01.multiply(flattenFm3[index +  7])).multiply(scT[ 2])).
177                    add(omazr01.multiply(flattenFm3[index +  8]).add(azr01.multiply(flattenFm3[index +  9])).multiply(scT[ 3])).
178                    add(omazr01.multiply(flattenFm3[index + 10]).add(azr01.multiply(flattenFm3[index + 11])).multiply(scT[ 4])).
179                    add(omazr01.multiply(flattenFm3[index + 12]).add(azr01.multiply(flattenFm3[index + 13])).multiply(scT[ 5])).
180                    add(omazr01.multiply(flattenFm3[index + 14]).add(azr01.multiply(flattenFm3[index + 15])).multiply(scT[ 6])).
181                    add(omazr01.multiply(flattenFm3[index + 16]).add(azr01.multiply(flattenFm3[index + 17])).multiply(scT[ 7]));
182             index += 18;
183         }
184         return array;
185     }
186 
187     /** Compute sines and cosines.
188      * @param <T> type of the field elements
189      * @param a argument
190      * @param n number of terms
191      * @return sin(a), cos(a), sin(2a), cos(2a) … sin(n a), cos(n a) array
192      */
193     static <T extends CalculusFieldElement<T>> T[] sinCos(final T a, final int n) {
194 
195         final FieldSinCos<T> sc0 = FastMath.sinCos(a);
196         FieldSinCos<T>       sci = sc0;
197         final T[] sc = MathArrays.buildArray(a.getField(), 2 * n);
198         int isc = 0;
199         sc[isc++] = sci.sin();
200         sc[isc++] = sci.cos();
201         for (int i = 1; i < n; i++) {
202             sci = FieldSinCos.sum(sc0, sci);
203             sc[isc++] = sci.sin();
204             sc[isc++] = sci.cos();
205         }
206 
207         return sc;
208 
209     }
210 
211 }