AbstractTimeInterpolator.java

  1. /* Copyright 2002-2025 CS GROUP
  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.time;

  18. import org.hipparchus.util.FastMath;
  19. import org.orekit.errors.OrekitIllegalArgumentException;
  20. import org.orekit.errors.OrekitInternalError;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.utils.ImmutableTimeStampedCache;

  23. import java.util.ArrayList;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Optional;
  28. import java.util.stream.Collectors;
  29. import java.util.stream.Stream;

  30. /**
  31.  * Abstract class for time interpolator.
  32.  *
  33.  * @param <T> interpolated time stamped type
  34.  *
  35.  * @author Vincent Cucchietti
  36.  */
  37. public abstract class AbstractTimeInterpolator<T extends TimeStamped> implements TimeInterpolator<T> {

  38.     /** Default extrapolation time threshold: 1ms. */
  39.     public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3;

  40.     /** Default number of interpolation points. */
  41.     public static final int DEFAULT_INTERPOLATION_POINTS = 2;

  42.     /** The extrapolation threshold beyond which the propagation will fail. */
  43.     private final double extrapolationThreshold;

  44.     /** Neighbor size. */
  45.     private final int interpolationPoints;

  46.     /**
  47.      * Constructor.
  48.      *
  49.      * @param interpolationPoints number of interpolation points
  50.      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
  51.      */
  52.     public AbstractTimeInterpolator(final int interpolationPoints, final double extrapolationThreshold) {
  53.         this.interpolationPoints    = interpolationPoints;
  54.         this.extrapolationThreshold = extrapolationThreshold;
  55.     }

  56.     /**
  57.      * Method checking if given interpolator is compatible with given sample size.
  58.      *
  59.      * @param interpolator interpolator
  60.      * @param sampleSize sample size
  61.      */
  62.     public static void checkInterpolatorCompatibilityWithSampleSize(
  63.             final TimeInterpolator<? extends TimeStamped> interpolator,
  64.             final int sampleSize) {

  65.         // Retrieve all sub-interpolators (or a singleton list with given interpolator if there are no sub-interpolators)
  66.         final List<TimeInterpolator<? extends TimeStamped>> subInterpolators = interpolator.getSubInterpolators();
  67.         for (final TimeInterpolator<? extends TimeStamped> subInterpolator : subInterpolators) {
  68.             if (sampleSize < subInterpolator.getNbInterpolationPoints()) {
  69.                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sampleSize);
  70.             }
  71.         }
  72.     }

  73.     /** {@inheritDoc} */
  74.     @Override
  75.     public T interpolate(final AbsoluteDate interpolationDate, final Stream<T> sample) {
  76.         return interpolate(interpolationDate, sample.collect(Collectors.toList()));
  77.     }

  78.     /** {@inheritDoc}. */
  79.     @Override
  80.     public T interpolate(final AbsoluteDate interpolationDate, final Collection<T> sample) {
  81.         final InterpolationData interpolationData = new InterpolationData(interpolationDate, sample);
  82.         return interpolate(interpolationData);
  83.     }

  84.     /**
  85.      * Get the central date to use to find neighbors while taking into account extrapolation threshold.
  86.      *
  87.      * @param date interpolation date
  88.      * @param cachedSamples cached samples
  89.      * @param threshold extrapolation threshold
  90.      * @param <T> type of element
  91.      *
  92.      * @return central date to use to find neighbors
  93.      * @since 12.0.1
  94.      */
  95.     public static <T extends TimeStamped> AbsoluteDate getCentralDate(final AbsoluteDate date,
  96.                                                                       final ImmutableTimeStampedCache<T> cachedSamples,
  97.                                                                       final double threshold) {
  98.         final AbsoluteDate minDate = cachedSamples.getEarliest().getDate();
  99.         final AbsoluteDate maxDate = cachedSamples.getLatest().getDate();
  100.         return getCentralDate(date, minDate, maxDate, threshold);
  101.     }

  102.     /**
  103.      * Get the central date to use to find neighbors while taking into account extrapolation threshold.
  104.      *
  105.      * @param date interpolation date
  106.      * @param minDate earliest date in the sample.
  107.      * @param maxDate latest date in the sample.
  108.      * @param threshold extrapolation threshold
  109.      *
  110.      * @return central date to use to find neighbors
  111.      * @since 12.0.1
  112.      */
  113.     public static AbsoluteDate getCentralDate(final AbsoluteDate date,
  114.                                               final AbsoluteDate minDate,
  115.                                               final AbsoluteDate maxDate,
  116.                                               final double threshold) {
  117.         final AbsoluteDate central;

  118.         if (date.compareTo(minDate) < 0 && FastMath.abs(date.durationFrom(minDate)) <= threshold) {
  119.             // avoid TimeStampedCacheException as we are still within the tolerance before minDate
  120.             central = minDate;
  121.         } else if (date.compareTo(maxDate) > 0 && FastMath.abs(date.durationFrom(maxDate)) <= threshold) {
  122.             // avoid TimeStampedCacheException as we are still within the tolerance after maxDate
  123.             central = maxDate;
  124.         } else {
  125.             central = date;
  126.         }

  127.         return central;
  128.     }

  129.     /** {@inheritDoc} */
  130.     public List<TimeInterpolator<? extends TimeStamped>> getSubInterpolators() {
  131.         return Collections.singletonList(this);
  132.     }

  133.     /** {@inheritDoc} */
  134.     public int getNbInterpolationPoints() {
  135.         final List<TimeInterpolator<? extends TimeStamped>> subInterpolators = getSubInterpolators();
  136.         // In case the interpolator does not have sub interpolators
  137.         if (subInterpolators.size() == 1) {
  138.             return interpolationPoints;
  139.         }
  140.         // Otherwise find maximum number of interpolation points among sub interpolators
  141.         else {
  142.             final Optional<Integer> optionalMaxNbInterpolationPoints =
  143.                     subInterpolators.stream().map(TimeInterpolator::getNbInterpolationPoints).max(Integer::compareTo);
  144.             if (optionalMaxNbInterpolationPoints.isPresent()) {
  145.                 return optionalMaxNbInterpolationPoints.get();
  146.             } else {
  147.                 // This should never happen
  148.                 throw new OrekitInternalError(null);
  149.             }
  150.         }
  151.     }

  152.     /** {@inheritDoc} */
  153.     public double getExtrapolationThreshold() {
  154.         return extrapolationThreshold;
  155.     }

  156.     /**
  157.      * Add all lowest level sub interpolators to the sub interpolator list.
  158.      *
  159.      * @param subInterpolator optional sub interpolator to add
  160.      * @param subInterpolators list of sub interpolators
  161.      */
  162.     protected void addOptionalSubInterpolatorIfDefined(final TimeInterpolator<? extends TimeStamped> subInterpolator,
  163.                                                        final List<TimeInterpolator<? extends TimeStamped>> subInterpolators) {
  164.         // Add all lowest level sub interpolators
  165.         if (subInterpolator != null) {
  166.             subInterpolators.addAll(subInterpolator.getSubInterpolators());
  167.         }
  168.     }

  169.     /**
  170.      * Interpolate instance from given interpolation data.
  171.      *
  172.      * @param interpolationData interpolation data
  173.      *
  174.      * @return interpolated instance from given interpolation data.
  175.      */
  176.     protected abstract T interpolate(InterpolationData interpolationData);

  177.     /**
  178.      * Get the time parameter which lies between [0:1] by normalizing the difference between interpolating time and previous
  179.      * date by the Δt between tabulated values.
  180.      *
  181.      * @param interpolatingTime time at which we want to interpolate a value (between previous and next tabulated dates)
  182.      * @param previousDate previous tabulated value date
  183.      * @param nextDate next tabulated value date
  184.      *
  185.      * @return time parameter which lies between [0:1]
  186.      */
  187.     protected double getTimeParameter(final AbsoluteDate interpolatingTime,
  188.                                       final AbsoluteDate previousDate,
  189.                                       final AbsoluteDate nextDate) {
  190.         return interpolatingTime.durationFrom(previousDate) / nextDate.getDate().durationFrom(previousDate);
  191.     }

  192.     /**
  193.      * Nested class used to store interpolation data.
  194.      * <p>
  195.      * It makes the interpolator thread safe.
  196.      */
  197.     public class InterpolationData {

  198.         /** Interpolation date. */
  199.         private final AbsoluteDate interpolationDate;

  200.         /** Neighbor list around interpolation date. */
  201.         private final List<T> neighborList;

  202.         /**
  203.          * Constructor.
  204.          *
  205.          * @param interpolationDate interpolation date
  206.          * @param sample time stamped sample
  207.          */
  208.         protected InterpolationData(final AbsoluteDate interpolationDate, final Collection<T> sample) {
  209.             // Handle specific case that is not handled by the immutable time stamped cache constructor
  210.             if (sample.isEmpty()) {
  211.                 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0);
  212.             }

  213.             // TODO performance: create neighborsList without copying sample.
  214.             if (sample.size() == interpolationPoints) {
  215.                 // shortcut for simple case
  216.                 // copy list to make neighborList immutable
  217.                 this.neighborList = Collections.unmodifiableList(new ArrayList<>(sample));
  218.             } else {
  219.                 // else, select sample.

  220.                 // Create immutable time stamped cache
  221.                 final ImmutableTimeStampedCache<T> cachedSamples =
  222.                         new ImmutableTimeStampedCache<>(interpolationPoints, sample);

  223.                 // Find neighbors
  224.                 final AbsoluteDate central = AbstractTimeInterpolator.getCentralDate(
  225.                         interpolationDate,
  226.                         cachedSamples,
  227.                         extrapolationThreshold);
  228.                 final Stream<T> neighborsStream = cachedSamples.getNeighbors(central);

  229.                 // Convert to unmodifiable list
  230.                 this.neighborList = Collections.unmodifiableList(neighborsStream.collect(Collectors.toList()));
  231.             }

  232.             // Store interpolation date
  233.             this.interpolationDate = interpolationDate;
  234.         }

  235.         /** Get interpolation date.
  236.          * @return interpolation date
  237.          */
  238.         public AbsoluteDate getInterpolationDate() {
  239.             return interpolationDate;
  240.         }

  241.         /** Get neighbor list.
  242.          * @return neighbor list
  243.          */
  244.         public List<T> getNeighborList() {
  245.             return neighborList;
  246.         }

  247.     }
  248. }