QuadraticClockModel.java

  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.estimation.measurements;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.analysis.differentiation.Gradient;
  20. import org.hipparchus.util.FastMath;
  21. import org.orekit.errors.OrekitException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.time.AbsoluteDate;
  24. import org.orekit.time.ClockModel;
  25. import org.orekit.time.ClockOffset;
  26. import org.orekit.time.FieldAbsoluteDate;
  27. import org.orekit.time.FieldClockOffset;
  28. import org.orekit.utils.ParameterDriver;

  29. import java.util.Map;

  30. /** Quadratic clock model.
  31.  *
  32.  * @author Luc Maisonobe
  33.  * @since 12.1
  34.  *
  35.  */
  36. public class QuadraticClockModel implements ClockModel {

  37.     /** Clock offset scaling factor.
  38.      * <p>
  39.      * We use a power of 2 to avoid numeric noise introduction
  40.      * in the multiplications/divisions sequences.
  41.      * </p>
  42.      */
  43.     private static final double CLOCK_OFFSET_SCALE = FastMath.scalb(1.0, -10);

  44.     /** Constant term. */
  45.     private final ParameterDriver a0;

  46.     /** Linear term. */
  47.     private final ParameterDriver a1;

  48.     /** Quadratic term. */
  49.     private final ParameterDriver a2;

  50.     /** Simple constructor.
  51.      * @param referenceDate reference date
  52.      * @param a0 constant term
  53.      * @param a1 linear term
  54.      * @param a2 quadratic term
  55.      */
  56.     public QuadraticClockModel(final AbsoluteDate referenceDate,
  57.                                final double a0, final double a1, final double a2) {
  58.         this(new ParameterDriver("a0",
  59.                                  0.0, CLOCK_OFFSET_SCALE,
  60.                                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY),
  61.              new ParameterDriver("a1",
  62.                                  0.0, CLOCK_OFFSET_SCALE,
  63.                                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY),
  64.              new ParameterDriver("a2",
  65.                                  0.0, CLOCK_OFFSET_SCALE,
  66.                                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY));
  67.         this.a0.setValue(a0);
  68.         this.a0.setReferenceDate(referenceDate);
  69.         this.a1.setValue(a1);
  70.         this.a1.setReferenceDate(referenceDate);
  71.         this.a2.setValue(a2);
  72.         this.a2.setReferenceDate(referenceDate);
  73.     }

  74.     /** Simple constructor.
  75.      * @param a0 constant term
  76.      * @param a1 linear term
  77.      * @param a2 quadratic term
  78.      */
  79.     public QuadraticClockModel(final ParameterDriver a0, final ParameterDriver a1, final ParameterDriver a2) {
  80.         this.a0 = a0;
  81.         this.a1 = a1;
  82.         this.a2 = a2;
  83.     }

  84.     /** {@inheritDoc} */
  85.     @Override
  86.     public AbsoluteDate getValidityStart() {
  87.         return AbsoluteDate.PAST_INFINITY;
  88.     }

  89.     /** {@inheritDoc} */
  90.     @Override
  91.     public AbsoluteDate getValidityEnd() {
  92.         return AbsoluteDate.FUTURE_INFINITY;
  93.     }

  94.     /** {@inheritDoc} */
  95.     @Override
  96.     public ClockOffset getOffset(final AbsoluteDate date) {
  97.         final double dt = date.durationFrom(getSafeReference(date));
  98.         final double c0 = a0.getValue(date);
  99.         final double c1 = a1.getValue(date);
  100.         final double c2 = a2.getValue(date);
  101.         return new ClockOffset(date,
  102.                                (c2 * dt + c1) * dt + c0,
  103.                                2 * c2 * dt + c1,
  104.                                2 * c2);
  105.     }

  106.     /** {@inheritDoc} */
  107.     @Override
  108.     public <T extends CalculusFieldElement<T>> FieldClockOffset<T> getOffset(final FieldAbsoluteDate<T> date) {
  109.         final AbsoluteDate aDate = date.toAbsoluteDate();
  110.         final T dt = date.durationFrom(getSafeReference(aDate));
  111.         final double c0 = a0.getValue(aDate);
  112.         final double c1 = a1.getValue(aDate);
  113.         final double c2 = a2.getValue(aDate);
  114.         return new FieldClockOffset<>(date,
  115.                                       dt.multiply(dt.multiply(c2).add(c1)).add(c0),
  116.                                       dt.multiply(2 * c2).add(c1),
  117.                                       dt.newInstance(2 * c2));
  118.     }

  119.     /** Get a safe reference date.
  120.      * <p>
  121.      * This method deals with parameters drivers for which no reference
  122.      * date has been set, which is acceptable if the model is not
  123.      * time-dependent.
  124.      * </p>
  125.      * @param date date at which values are requested
  126.      * @return safe reference date
  127.      */
  128.     private AbsoluteDate getSafeReference(final AbsoluteDate date) {
  129.         if (a0.getReferenceDate() == null) {
  130.             if (a1.getValue(date) == 0 && a2.getValue(date) == 0) {
  131.                 // it is OK to not have a reference date is clock offset is constant
  132.                 return date;
  133.             } else {
  134.                 throw new OrekitException(OrekitMessages.NO_REFERENCE_DATE_FOR_PARAMETER,
  135.                                           a0.getName());
  136.             }
  137.         } else {
  138.             return a0.getReferenceDate();
  139.         }
  140.     }

  141.     /** Convert to gradient model.
  142.      * @param freeParameters total number of free parameters in the gradient
  143.      * @param indices indices of the differentiation parameters in derivatives computations,
  144.      * must be span name and not driver name
  145.      * @param date date at which model must be valid
  146.      * @return converted clock model
  147.      */
  148.     public QuadraticFieldClockModel<Gradient> toGradientModel(final int freeParameters,
  149.                                                               final Map<String, Integer> indices,
  150.                                                               final AbsoluteDate date) {
  151.         final Gradient g0 = a0.getValue(freeParameters, indices, date);
  152.         final Gradient g1 = a1.getValue(freeParameters, indices, date);
  153.         final Gradient g2 = a2.getValue(freeParameters, indices, date);
  154.         final FieldAbsoluteDate<Gradient> referenceDate =
  155.             new FieldAbsoluteDate<>(g0.getField(), getSafeReference(date));
  156.         return new QuadraticFieldClockModel<>(referenceDate, g0, g1, g2);
  157.     }

  158. }