FieldFourierTimeSeries.java
/* Copyright 2022-2025 Thales Alenia Space
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.models.earth.ionosphere.nequick;
import org.hipparchus.CalculusFieldElement;
import org.hipparchus.util.FastMath;
import org.hipparchus.util.FieldSinCos;
import org.hipparchus.util.MathArrays;
import org.orekit.time.DateTimeComponents;
/**
* Fourier time series for the NeQuick model.
* @see NeQuickModel#computeFourierTimeSeries(DateTimeComponents, double)
* @author Luc Maisonobe
* @since 13.0.1
* @param <T> type of the field elements
*/
public class FieldFourierTimeSeries<T extends CalculusFieldElement<T>> {
/** Date. */
private final DateTimeComponents dateTime;
/** Effective ionisation level. */
private final T az;
/** Effective sunspot number (Eq. 19). */
private final T azr;
/** Fourier time series for foF2. */
private final T[] cf2;
/** Fourier time series for M(3000)F2. */
private final T[] cm3;
/**
* Simple constructor.
* @param dateTime current date time components
* @param az effective ionisation level
* @param flattenF2 F2 coefficients used by the F2 layer (flatten array)
* @param flattenFm3 Fm3 coefficients used by the M(3000)F2 layer (flatten array)
*/
FieldFourierTimeSeries(final DateTimeComponents dateTime, final T az,
final double[] flattenF2, final double[] flattenFm3) {
this.dateTime = dateTime;
this.az = az;
// Effective sunspot number (Eq. 19)
this.azr = FastMath.sqrt(az.subtract(63.7).multiply(1123.6).add(167273.0)).subtract(408.99);
// Hours
final double hours = dateTime.getTime().getSecondsInUTCDay() / 3600.0;
// Time argument (Eq. 49)
final double t = FastMath.toRadians(15 * hours) - FastMath.PI;
// Compute Fourier time series for foF2 and M(3000)F2
final T[] scT = sinCos(az.newInstance(t), 6);
this.cf2 = computeCF2(flattenF2, scT);
this.cm3 = computeCm3(flattenFm3, scT);
}
/** Get date time components.
* @return date time components
*/
public DateTimeComponents getDateTime() {
return dateTime;
}
/** Get effective ionisation level.
* @return effective ionisation level
*/
public T getAz() {
return az;
}
/** Get effective sunspot number.
* @return effective sunspot number
*/
public T getAzr() {
return azr;
}
/** Get Fourier time series for foF2.
* <p>
* Beware that for efficiency purposes, this method returns
* a reference to an internal array; this is the reason why
* this method visibility is limited to package level.
* </p>
* @return Fourier time series for foF2 (reference to an internal array)
*/
T[] getCf2Reference() {
return cf2;
}
/** Get Fourier time series for M(3000)F2.
* <p>
* Beware that for efficiency purposes, this method returns
* a reference to an internal array; this is the reason why
* this method visibility is limited to package level.
* </p>
* @return Fourier time series for M(3000)F2 (reference to an internal array)
*/
T[] getCm3Reference() {
return cm3;
}
/** Computes cf2 coefficients.
* @param flattenF2 F2 coefficients used by the F2 layer (flatten array)
* @param scT sines/cosines array of time argument
* @return the cf2 coefficients array
*/
private T[] computeCF2(final double[] flattenF2, final T[] scT) {
// interpolation coefficients for effective spot number
final T azr01 = azr.multiply(0.01);
final T omazr01 = azr01.negate().add(1);
// Eq. 44 and Eq. 50 merged into one loop
final T[] array = MathArrays.buildArray(azr.getField(), 76);
int index = 0;
for (int i = 0; i < array.length; i++) {
// CHECKSTYLE: stop Indentation check
array[i] = omazr01.multiply(flattenF2[index ]).add(azr01.multiply(flattenF2[index + 1])).
add(omazr01.multiply(flattenF2[index + 2]).add(azr01.multiply(flattenF2[index + 3])).multiply(scT[ 0])).
add(omazr01.multiply(flattenF2[index + 4]).add(azr01.multiply(flattenF2[index + 5])).multiply(scT[ 1])).
add(omazr01.multiply(flattenF2[index + 6]).add(azr01.multiply(flattenF2[index + 7])).multiply(scT[ 2])).
add(omazr01.multiply(flattenF2[index + 8]).add(azr01.multiply(flattenF2[index + 9])).multiply(scT[ 3])).
add(omazr01.multiply(flattenF2[index + 10]).add(azr01.multiply(flattenF2[index + 11])).multiply(scT[ 4])).
add(omazr01.multiply(flattenF2[index + 12]).add(azr01.multiply(flattenF2[index + 13])).multiply(scT[ 5])).
add(omazr01.multiply(flattenF2[index + 14]).add(azr01.multiply(flattenF2[index + 15])).multiply(scT[ 6])).
add(omazr01.multiply(flattenF2[index + 16]).add(azr01.multiply(flattenF2[index + 17])).multiply(scT[ 7])).
add(omazr01.multiply(flattenF2[index + 18]).add(azr01.multiply(flattenF2[index + 19])).multiply(scT[ 8])).
add(omazr01.multiply(flattenF2[index + 20]).add(azr01.multiply(flattenF2[index + 21])).multiply(scT[ 9])).
add(omazr01.multiply(flattenF2[index + 22]).add(azr01.multiply(flattenF2[index + 23])).multiply(scT[10])).
add(omazr01.multiply(flattenF2[index + 24]).add(azr01.multiply(flattenF2[index + 25])).multiply(scT[11]));
index += 26;
// CHECKSTYLE: resume Indentation check
}
return array;
}
/** Computes Cm3 coefficients.
* @param flattenFm3 Fm3 coefficients used by the M(3000)F2 layer (flatten array)
* @param scT sines/cosines array of time argument
* @return the Cm3 coefficients array
*/
private T[] computeCm3(final double[] flattenFm3, final T[] scT) {
// interpolation coefficients for effective spot number
final T azr01 = azr.multiply(0.01);
final T omazr01 = azr01.negate().add(1);
// Eq. 44 and Eq. 51 merged into one loop
final T[] array = MathArrays.buildArray(azr.getField(), 49);
int index = 0;
for (int i = 0; i < array.length; i++) {
array[i] = omazr01.multiply(flattenFm3[index ]).add(azr01.multiply(flattenFm3[index + 1])).
add(omazr01.multiply(flattenFm3[index + 2]).add(azr01.multiply(flattenFm3[index + 3])).multiply(scT[ 0])).
add(omazr01.multiply(flattenFm3[index + 4]).add(azr01.multiply(flattenFm3[index + 5])).multiply(scT[ 1])).
add(omazr01.multiply(flattenFm3[index + 6]).add(azr01.multiply(flattenFm3[index + 7])).multiply(scT[ 2])).
add(omazr01.multiply(flattenFm3[index + 8]).add(azr01.multiply(flattenFm3[index + 9])).multiply(scT[ 3])).
add(omazr01.multiply(flattenFm3[index + 10]).add(azr01.multiply(flattenFm3[index + 11])).multiply(scT[ 4])).
add(omazr01.multiply(flattenFm3[index + 12]).add(azr01.multiply(flattenFm3[index + 13])).multiply(scT[ 5])).
add(omazr01.multiply(flattenFm3[index + 14]).add(azr01.multiply(flattenFm3[index + 15])).multiply(scT[ 6])).
add(omazr01.multiply(flattenFm3[index + 16]).add(azr01.multiply(flattenFm3[index + 17])).multiply(scT[ 7]));
index += 18;
}
return array;
}
/** Compute sines and cosines.
* @param <T> type of the field elements
* @param a argument
* @param n number of terms
* @return sin(a), cos(a), sin(2a), cos(2a) … sin(n a), cos(n a) array
*/
static <T extends CalculusFieldElement<T>> T[] sinCos(final T a, final int n) {
final FieldSinCos<T> sc0 = FastMath.sinCos(a);
FieldSinCos<T> sci = sc0;
final T[] sc = MathArrays.buildArray(a.getField(), 2 * n);
int isc = 0;
sc[isc++] = sci.sin();
sc[isc++] = sci.cos();
for (int i = 1; i < n; i++) {
sci = FieldSinCos.sum(sc0, sci);
sc[isc++] = sci.sin();
sc[isc++] = sci.cos();
}
return sc;
}
}