AbstractTimeInterpolator.java

  1. /* Copyright 2002-2024 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.      * @param <T> type of element
  110.      *
  111.      * @return central date to use to find neighbors
  112.      * @since 12.0.1
  113.      */
  114.     public static <T extends TimeStamped> AbsoluteDate getCentralDate(final AbsoluteDate date,
  115.                                                                       final AbsoluteDate minDate,
  116.                                                                       final AbsoluteDate maxDate,
  117.                                                                       final double threshold) {
  118.         final AbsoluteDate central;

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

  128.         return central;
  129.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  236.         /**
  237.          * Get the central date to use to find neighbors while taking into account extrapolation threshold.
  238.          *
  239.          * @param date interpolation date
  240.          *
  241.          * @return central date to use to find neighbors
  242.          *
  243.          * @deprecated This method appears to be unused and may be removed in Orekit 13.0.
  244.          * Please Comment on forum.orekit.org if you have a use case for this method.
  245.          */
  246.         @Deprecated
  247.         protected AbsoluteDate getCentralDate(final AbsoluteDate date) {
  248.             return AbstractTimeInterpolator.getCentralDate(date,
  249.                     neighborList.get(0).getDate(),
  250.                     neighborList.get(neighborList.size() - 1).getDate(),
  251.                     extrapolationThreshold);
  252.         }

  253.         /** Get interpolation date.
  254.          * @return interpolation date
  255.          */
  256.         public AbsoluteDate getInterpolationDate() {
  257.             return interpolationDate;
  258.         }

  259.         /** Get cached samples.
  260.          * @return cached samples
  261.          *
  262.          * @deprecated This method appears to be unused and may be removed in Orekit 13.0.
  263.          * Please Comment on forum.orekit.org if you have a use case for this method.
  264.          */
  265.         @Deprecated
  266.         public ImmutableTimeStampedCache<T> getCachedSamples() {
  267.             return new ImmutableTimeStampedCache<>(interpolationPoints, neighborList);
  268.         }

  269.         /** Get neighbor list.
  270.          * @return neighbor list
  271.          */
  272.         public List<T> getNeighborList() {
  273.             return neighborList;
  274.         }

  275.     }
  276. }