FourierTimeSeries.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.util.FastMath;
import org.hipparchus.util.SinCos;
import org.orekit.time.DateTimeComponents;

/**
 * Fourier time series for the NeQuick model.
 * @see NeQuickModel#computeFourierTimeSeries(DateTimeComponents, double)
 * @author Luc Maisonobe
 * @since 13.0.1
 */
public class FourierTimeSeries {

    /** Date. */
    private final DateTimeComponents dateTime;

    /** Effective ionisation level. */
    private final double az;

    /** Effective sunspot number (Eq. 19). */
    private final double azr;

    /** Fourier time series for foF2. */
    private final double[] cf2;

    /** Fourier time series for M(3000)F2. */
    private final double[] 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)
     */
    FourierTimeSeries(final DateTimeComponents dateTime, final double az,
                      final double[] flattenF2, final double[] flattenFm3) {

        this.dateTime = dateTime;
        this.az       = az;

        // Effective sunspot number (Eq. 19)
        this.azr = FastMath.sqrt(167273.0 + (az - 63.7) * 1123.6) - 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 double[] scT = sinCos(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 double getAz() {
        return az;
    }

    /** Get effective sunspot number.
     * @return effective sunspot number
     */
    public double 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)
     */
    double[] 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)
     */
    double[] 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 double[] computeCF2(final double[] flattenF2, final double[] scT) {

        // interpolation coefficients for effective spot number
        final double azr01   = azr * 0.01;
        final double omazr01 = 1 - azr01;

        // Eq. 44 and Eq. 50 merged into one loop
        final double[] array   = new double[76];
        int            index = 0;
        for (int i = 0; i < array.length; i++) {
            array[i] =
                    omazr01 * flattenF2[index] + azr01 * flattenF2[index + 1] +
                   (omazr01 * flattenF2[index + 2] + azr01 * flattenF2[index + 3]) * scT[0] +
                   (omazr01 * flattenF2[index + 4] + azr01 * flattenF2[index + 5]) * scT[1] +
                   (omazr01 * flattenF2[index + 6] + azr01 * flattenF2[index + 7]) * scT[2] +
                   (omazr01 * flattenF2[index + 8] + azr01 * flattenF2[index + 9]) * scT[3] +
                   (omazr01 * flattenF2[index + 10] + azr01 * flattenF2[index + 11]) * scT[4] +
                   (omazr01 * flattenF2[index + 12] + azr01 * flattenF2[index + 13]) * scT[5] +
                   (omazr01 * flattenF2[index + 14] + azr01 * flattenF2[index + 15]) * scT[6] +
                   (omazr01 * flattenF2[index + 16] + azr01 * flattenF2[index + 17]) * scT[7] +
                   (omazr01 * flattenF2[index + 18] + azr01 * flattenF2[index + 19]) * scT[8] +
                   (omazr01 * flattenF2[index + 20] + azr01 * flattenF2[index + 21]) * scT[9] +
                   (omazr01 * flattenF2[index + 22] + azr01 * flattenF2[index + 23]) * scT[10] +
                   (omazr01 * flattenF2[index + 24] + azr01 * flattenF2[index + 25]) * scT[11];
            index += 26;
        }
        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 double[] computeCm3(final double[] flattenFm3, final double[] scT) {

        // interpolation coefficients for effective spot number
        final double azr01   = azr * 0.01;
        final double omazr01 = 1 - azr01;

        // Eq. 44 and Eq. 51 merged into one loop
        final double[] array   = new double[49];
        int            index = 0;
        for (int i = 0; i < array.length; i++) {
            array[i] =
                    omazr01 * flattenFm3[index] + azr01 * flattenFm3[index + 1] +
                   (omazr01 * flattenFm3[index + 2] + azr01 * flattenFm3[index + 3]) * scT[0] +
                   (omazr01 * flattenFm3[index + 4] + azr01 * flattenFm3[index + 5]) * scT[1] +
                   (omazr01 * flattenFm3[index + 6] + azr01 * flattenFm3[index + 7]) * scT[2] +
                   (omazr01 * flattenFm3[index + 8] + azr01 * flattenFm3[index + 9]) * scT[3] +
                   (omazr01 * flattenFm3[index + 10] + azr01 * flattenFm3[index + 11]) * scT[4] +
                   (omazr01 * flattenFm3[index + 12] + azr01 * flattenFm3[index + 13]) * scT[5] +
                   (omazr01 * flattenFm3[index + 14] + azr01 * flattenFm3[index + 15]) * scT[6] +
                   (omazr01 * flattenFm3[index + 16] + azr01 * flattenFm3[index + 17]) * scT[7];
            index += 18;
        }
        return array;
    }

    /** Compute sines and cosines.
     * @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 double[] sinCos(final double a, final int n) {

        final SinCos sc0 = FastMath.sinCos(a);
        SinCos sci = sc0;
        final double[] sc = new double[2 * n];
        int isc = 0;
        sc[isc++] = sci.sin();
        sc[isc++] = sci.cos();
        for (int i = 1; i < n; i++) {
            sci = SinCos.sum(sc0, sci);
            sc[isc++] = sci.sin();
            sc[isc++] = sci.cos();
        }

        return sc;

    }

}