GNSSOrbitalElementsDriversProvider.java

/* Copyright 2022-2025 Luc Maisonobe
 * 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.propagation.analytical.gnss.data;

import org.hipparchus.util.FastMath;
import org.orekit.gnss.SatelliteSystem;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.GNSSDate;
import org.orekit.time.TimeScales;
import org.orekit.utils.ParameterDriver;
import org.orekit.utils.ParameterDriversProvider;
import org.orekit.utils.ParameterObserver;
import org.orekit.utils.TimeSpanMap;

import java.util.Arrays;
import java.util.List;

/** This class manages the non-keplerian parameter drivers for
 * {@link GNSSOrbitalElements} and {@link FieldGnssOrbitalElements}.
 * <p>
 * In both primitive double and field classes, only the non-Keplerian parameters
 * are returned in the {@link #getParametersDrivers()} method, the Keplerian orbital
 * parameters must be accessed independently. These groups ensure proper separate
 * computation of state transition matrix and Jacobian matrix by
 * {@link org.orekit.propagation.analytical.gnss.GNSSPropagator} and
 * {@link org.orekit.propagation.analytical.gnss.FieldGnssPropagator}.
 * </p>
 * @since 13.0
 * @author Luc Maisonobe
 */
public abstract class GNSSOrbitalElementsDriversProvider
    implements ParameterDriversProvider {

    /** Name for time parameter. */
    public static final String TIME = "GnssTime";

    /** Name for inclination rate parameter. */
    public static final String INCLINATION_RATE = "GnssInclinationRate";

    /** Name for longitude rate parameter. */
    public static final String LONGITUDE_RATE = "GnssLongitudeRate";

    /** Name for cosine of latitude argument harmonic parameter. */
    public static final String LATITUDE_COSINE = "GnssLatitudeCosine";

    /** Name for sine of latitude argument harmonic parameter. */
    public static final String LATITUDE_SINE = "GnssLatitudeSine";

    /** Name for cosine of orbit radius harmonic parameter. */
    public static final String RADIUS_COSINE = "GnssRadiusCosine";

    /** Name for sine of orbit radius harmonic parameter. */
    public static final String RADIUS_SINE = "GnssRadiusSine";

    /** Name for cosine of inclination harmonic parameter. */
    public static final String INCLINATION_COSINE = "GnssInclinationCosine";

    /** Name for sine of inclination harmonic parameter. */
    public static final String INCLINATION_SINE = "GnssInclinationSine";

    /** Index of time in the list returned by {@link #getParametersDrivers()}. */
    public static final int TIME_INDEX = 0;

    /** Index of inclination rate in the list returned by {@link #getParametersDrivers()}. */
    public static final int I_DOT_INDEX = TIME_INDEX + 1;

    /** Index of longitude rate in the list returned by {@link #getParametersDrivers()}. */
    public static final int OMEGA_DOT_INDEX = I_DOT_INDEX + 1;

    /** Index of cosine on latitude argument in the list returned by {@link #getParametersDrivers()}. */
    public static final int CUC_INDEX = OMEGA_DOT_INDEX + 1;

    /** Index of sine on latitude argument in the list returned by {@link #getParametersDrivers()}. */
    public static final int CUS_INDEX = CUC_INDEX + 1;

    /** Index of cosine on radius in the list returned by {@link #getParametersDrivers()}. */
    public static final int CRC_INDEX = CUS_INDEX + 1;

    /** Index of sine on radius in the list returned by {@link #getParametersDrivers()}. */
    public static final int CRS_INDEX = CRC_INDEX + 1;

    /** Index of cosine on inclination in the list returned by {@link #getParametersDrivers()}. */
    public static final int CIC_INDEX = CRS_INDEX + 1;

    /** Index of sine on inclination in the list returned by {@link #getParametersDrivers()}. */
    public static final int CIS_INDEX = CIC_INDEX + 1;

    /** Size of parameters array. */
    public static final int SIZE = CIS_INDEX + 1;

    /** Mean angular velocity of the Earth for the GNSS model. */
    private final double angularVelocity;

    /** Duration of the GNSS cycle in weeks. */
    private final int weeksInCycle;

    /** Duration of the GNSS cycle in seconds. */
    private final double cycleDuration;

    /** Satellite system to use for interpreting week number. */
    private final SatelliteSystem system;

    /** Known time scales. */
    private final TimeScales timeScales;

    /** PRN number of the satellite. */
    private int prn;

    /** Reference Week of the orbit. */
    private int week;

    /** Reference time. */
    private final ParameterDriver timeDriver;

    /** Inclination rate (rad/s). */
    private final ParameterDriver iDotDriver;

    /** Rate of right ascension (rad/s). */
    private final ParameterDriver domDriver;

    /** Amplitude of the cosine harmonic correction term to the argument of latitude. */
    private final ParameterDriver cucDriver;

    /** Amplitude of the sine harmonic correction term to the argument of latitude. */
    private final ParameterDriver cusDriver;

    /** Amplitude of the cosine harmonic correction term to the orbit radius. */
    private final ParameterDriver crcDriver;

    /** Amplitude of the sine harmonic correction term to the orbit radius. */
    private final ParameterDriver crsDriver;

    /** Amplitude of the cosine harmonic correction term to the inclination. */
    private final ParameterDriver cicDriver;

    /** Amplitude of the sine harmonic correction term to the inclination. */
    private final ParameterDriver cisDriver;

    /** Constructor.
     * @param angularVelocity mean angular velocity of the Earth for the GNSS model
     * @param weeksInCycle    number of weeks in the GNSS cycle
     * @param timeScales      known time scales
     * @param system          satellite system to consider for interpreting week number
     *                        (may be different from real system, for example in Rinex nav, weeks
     *                        are always according to GPS)
     */
    protected GNSSOrbitalElementsDriversProvider(final double angularVelocity, final int weeksInCycle,
                                                 final TimeScales timeScales, final SatelliteSystem system) {

        // immutable fields
        this.angularVelocity = angularVelocity;
        this.weeksInCycle    = weeksInCycle;
        this.cycleDuration   = GNSSConstants.GNSS_WEEK_IN_SECONDS * weeksInCycle;
        this.system          = system;
        this.timeScales      = timeScales;

        this.timeDriver = createDriver(TIME);
        this.iDotDriver = createDriver(INCLINATION_RATE);
        this.domDriver  = createDriver(LONGITUDE_RATE);
        this.cucDriver  = createDriver(LATITUDE_COSINE);
        this.cusDriver  = createDriver(LATITUDE_SINE);
        this.crcDriver  = createDriver(RADIUS_COSINE);
        this.crsDriver  = createDriver(RADIUS_SINE);
        this.cicDriver  = createDriver(INCLINATION_COSINE);
        this.cisDriver  = createDriver(INCLINATION_SINE);

        // automatically update date when time driver is updated
        timeDriver.addObserver(new ParameterObserver() {

            /** {@inheritDoc} */
            @Override
            public void valueChanged(final double previousValue, final ParameterDriver driver,
                                     final AbsoluteDate date) {
                setGnssDate(new GNSSDate(week, driver.getValue(), system, timeScales));
            }

            /** {@inheritDoc} */
            @Override
            public void valueSpanMapChanged(final TimeSpanMap<Double> previousValueSpanMap,
                                            final ParameterDriver driver) {
                // nothing to do
            }
        });

    }

    /** Copy drivers selection settings from another instance.
     * @param original original instance providing selection settings
     */
    protected void copySelectionSettings(final GNSSOrbitalElementsDriversProvider original) {
        timeDriver.setSelected(original.timeDriver.isSelected());
        iDotDriver.setSelected(original.iDotDriver.isSelected());
        domDriver.setSelected(original.domDriver.isSelected());
        cucDriver.setSelected(original.cucDriver.isSelected());
        cusDriver.setSelected(original.cusDriver.isSelected());
        crcDriver.setSelected(original.crcDriver.isSelected());
        crsDriver.setSelected(original.crsDriver.isSelected());
        cicDriver.setSelected(original.cicDriver.isSelected());
        cisDriver.setSelected(original.cisDriver.isSelected());
    }

    /** Set GNSS date.
     * @param gnssDate GNSS date
     */
    protected abstract void setGnssDate(GNSSDate gnssDate);

    /** Create parameter driver.
     * @param name name of the driver
     * @return build driver
     */
    protected static ParameterDriver createDriver(final String name) {
        return new ParameterDriver(name, 0, FastMath.scalb(1.0, -30),
                                   Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
    }

    /** Get satellite system.
     * @return satellite system
     */
    public SatelliteSystem getSystem() {
        return system;
    }

    /** Get known time scales.
     * @return known time scales
     */
    public TimeScales getTimeScales() {
        return timeScales;
    }

    /** {@inheritDoc}
     * <p>
     * Only the 9 non-Keplerian evolution parameters are listed here,
     * i.e. {@link #getTimeDriver()} (at index {@link #TIME_INDEX}),
     * {@link #getIDotDriver()} (at index {@link #I_DOT_INDEX}),
     * {@link #getOmegaDotDriver()} (at index {@link #OMEGA_DOT_INDEX}),
     * {@link #getCucDriver()} (at index {@link #CUC_INDEX}),
     * {@link #getCusDriver()} (at index {@link #CUS_INDEX}),
     * {@link #getCrcDriver()} (at index {@link #CRC_INDEX}),
     * {@link #getCrsDriver()} (at index {@link #CRS_INDEX}),
     * {@link #getCicDriver()} (at index {@link #CIC_INDEX}),
     * and {@link #getCisDriver()} (at index {@link #CIS_INDEX})
     * </p>
     * <p>
     * The Keplerian orbital parameters drivers are not included.
     * </p>
     */
    @Override
    public List<ParameterDriver> getParametersDrivers() {
        // ensure the parameters are really at the advertised indices
        final ParameterDriver[] array = new ParameterDriver[SIZE];
        array[TIME_INDEX]      = getTimeDriver();
        array[I_DOT_INDEX]     = getIDotDriver();
        array[OMEGA_DOT_INDEX] = getOmegaDotDriver();
        array[CUC_INDEX]       = getCucDriver();
        array[CUS_INDEX]       = getCusDriver();
        array[CRC_INDEX]       = getCrcDriver();
        array[CRS_INDEX]       = getCrsDriver();
        array[CIC_INDEX]       = getCicDriver();
        array[CIS_INDEX]       = getCisDriver();
        return Arrays.asList(array);
    }

    /** Get the mean angular velocity of the Earth of the GNSS model.
     * @return mean angular velocity of the Earth of the GNSS model
     */
    public double getAngularVelocity() {
        return angularVelocity;
    }

    /** Get for the duration of the GNSS cycle in weeks.
     * @return the duration of the GNSS cycle in weeks
     */
    public int getWeeksInCycle() {
        return weeksInCycle;
    }

    /** Get for the duration of the GNSS cycle in seconds.
     * @return the duration of the GNSS cycle in seconds
     */
    public double getCycleDuration() {
        return cycleDuration;
    }

    /** Get the PRN number of the satellite.
     * @return PRN number of the satellite
     */
    public int getPRN() {
        return prn;
    }

    /** Set the PRN number of the satellite.
     * @param number the prn number ot set
     */
    public void setPRN(final int number) {
        this.prn = number;
    }

    /** Get the reference week of the orbit.
     * @return reference week of the orbit
     */
    public int getWeek() {
        return week;
    }

    /** Set the reference week of the orbit.
     * @param week the week to set
     */
    public void setWeek(final int week) {
        this.week = week;
        setGnssDate(new GNSSDate(week, timeDriver.getValue(), system, timeScales));
    }

    /** Get the driver for reference time of the GNSS orbit as a duration from week start.
     * @return driver for the reference time of the GNSS orbit (s)
     */
    public ParameterDriver getTimeDriver() {
        return timeDriver;
    }

    /** Get reference time of the GNSS orbit as a duration from week start.
     * @return reference time of the GNSS orbit (s)
     */
    public double getTime() {
        return getTimeDriver().getValue();
    }

    /** Set reference time of the GNSS orbit as a duration from week start.
     * @param time reference time of the GNSS orbit (s)
     */
    public void setTime(final double time) {
        getTimeDriver().setValue(time);
    }

    /** Get the driver for the rate of inclination angle.
     * @return driver for the rate of inclination angle (rad/s)
     */
    public ParameterDriver getIDotDriver() {
        return iDotDriver;
    }

    /** Get rate of inclination angle.
     * @return rate of inclination angle (rad/s)
     */
    public double getIDot() {
        return getIDotDriver().getValue();
    }

    /** Set the driver for the rate of inclination angle.
     * @param iDot rate of inclination angle (rad/s)
     */
    public void setIDot(final double iDot) {
        getIDotDriver().setValue(iDot);
    }

    /** Get the driver for the rate of right ascension.
     * @return driver for the rate of right ascension (rad/s)
     */
    public ParameterDriver getOmegaDotDriver() {
        return domDriver;
    }

    /** Get rate of right ascension.
     * @return rate of right ascension (rad/s)
     */
    public double getOmegaDot() {
        return getOmegaDotDriver().getValue();
    }

    /** Set rate of right ascension.
     * @param dom rate of right ascension (rad/s)
     */
    public void setOmegaDot(final double dom) {
        getOmegaDotDriver().setValue(dom);
    }

    /** Get the driver for the amplitude of the cosine harmonic correction term to the argument of latitude.
     * @return driver for the amplitude of the cosine harmonic correction term to the argument of latitude (rad)
     */
    public ParameterDriver getCucDriver() {
        return cucDriver;
    }

    /** Get amplitude of the cosine harmonic correction term to the argument of latitude.
     * @return amplitude of the cosine harmonic correction term to the argument of latitude (rad)
     */
    public double getCuc() {
        return getCucDriver().getValue();
    }

    /** Set amplitude of the cosine harmonic correction term to the argument of latitude.
     * @param cuc amplitude of the cosine harmonic correction term to the argument of latitude (rad)
     */
    public void setCuc(final double cuc) {
        getCucDriver().setValue(cuc);
    }

    /** Get the driver for the amplitude of the sine harmonic correction term to the argument of latitude.
     * @return driver for the amplitude of the sine harmonic correction term to the argument of latitude (rad)
     */
    public ParameterDriver getCusDriver() {
        return cusDriver;
    }

    /** Get amplitude of the sine harmonic correction term to the argument of latitude.
     * @return amplitude of the sine harmonic correction term to the argument of latitude (rad)
     */
    public double getCus() {
        return getCusDriver().getValue();
    }

    /** Set amplitude of the sine harmonic correction term to the argument of latitude.
     * @param cus amplitude of the sine harmonic correction term to the argument of latitude (rad)
     */
    public void setCus(final double cus) {
        getCusDriver().setValue(cus);
    }

    /** Get the driver for the amplitude of the cosine harmonic correction term to the orbit radius.
     * @return driver for the amplitude of the cosine harmonic correction term to the orbit radius (m)
     */
    public ParameterDriver getCrcDriver() {
        return crcDriver;
    }

    /** Get amplitude of the cosine harmonic correction term to the orbit radius.
     * @return amplitude of the cosine harmonic correction term to the orbit radius (m)
     */
    public double getCrc() {
        return getCrcDriver().getValue();
    }

    /** Set amplitude of the cosine harmonic correction term to the orbit radius.
     * @param crc amplitude of the cosine harmonic correction term to the orbit radius (m)
     */
    public void setCrc(final double crc) {
        getCrcDriver().setValue(crc);
    }

    /** Get the driver for the amplitude of the sine harmonic correction term to the orbit radius.
     * @return driver for the amplitude of the sine harmonic correction term to the orbit radius (m)
     */
    public ParameterDriver getCrsDriver() {
        return crsDriver;
    }

    /** Get amplitude of the sine harmonic correction term to the orbit radius.
     * @return amplitude of the sine harmonic correction term to the orbit radius (m)
     */
    public double getCrs() {
        return getCrsDriver().getValue();
    }

    /** Set amplitude of the sine harmonic correction term to the orbit radius.
     * @param crs amplitude of the sine harmonic correction term to the orbit radius (m)
     */
    public void setCrs(final double crs) {
        getCrsDriver().setValue(crs);
    }

    /** Get the driver for the amplitude of the cosine harmonic correction term to the angle of inclination.
     * @return driver for the amplitude of the cosine harmonic correction term to the angle of inclination (rad)
     */
    public ParameterDriver getCicDriver() {
        return cicDriver;
    }

    /** Get amplitude of the cosine harmonic correction term to the angle of inclination.
     * @return amplitude of the cosine harmonic correction term to the angle of inclination (rad)
     */
    public double getCic() {
        return getCicDriver().getValue();
    }

    /** Set amplitude of the cosine harmonic correction term to the angle of inclination.
     * @param cic amplitude of the cosine harmonic correction term to the angle of inclination (rad)
     */
    public void setCic(final double cic) {
        getCicDriver().setValue(cic);
    }

    /** Get the driver for the amplitude of the sine harmonic correction term to the angle of inclination.
     * @return driver for the amplitude of the sine harmonic correction term to the angle of inclination (rad)
     */
    public ParameterDriver getCisDriver() {
        return cisDriver;
    }

    /** Get amplitude of the sine harmonic correction term to the angle of inclination.
     * @return amplitude of the sine harmonic correction term to the angle of inclination (rad)
     */
    public double getCis() {
        return getCisDriver().getValue();
    }

    /** Set amplitude of the sine harmonic correction term to the angle of inclination.
     * @param cis amplitude of the sine harmonic correction term to the angle of inclination (rad)
     */
    public void setCis(final double cis) {
        getCisDriver().setValue(cis);
    }

}