CachedNormalizedSphericalHarmonicsProvider.java

  1. /* Copyright 2002-2015 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.forces.gravity.potential;

  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.List;

  23. import org.apache.commons.math3.analysis.interpolation.HermiteInterpolator;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.TimeStampedCacheException;
  26. import org.orekit.time.AbsoluteDate;
  27. import org.orekit.time.TimeStamped;
  28. import org.orekit.utils.GenericTimeStampedCache;
  29. import org.orekit.utils.TimeStampedCache;
  30. import org.orekit.utils.TimeStampedGenerator;

  31. /** Caching wrapper for {@link NormalizedSphericalHarmonicsProvider}.
  32.  * <p>
  33.  * This wrapper improves efficiency of {@link NormalizedSphericalHarmonicsProvider}
  34.  * by sampling the values at a user defined rate and using interpolation
  35.  * between samples. This is important with providers that have sub-daily
  36.  * frequencies and are computing intensive, such as tides fields.
  37.  * </p>
  38.  * @see NormalizedSphericalHarmonicsProvider
  39.  * @see org.orekit.forces.gravity.SolidTides
  40.  * @see TimeStampedCache
  41.  * @author Luc Maisonobe
  42.  * @since 6.1
  43.  */
  44. public class CachedNormalizedSphericalHarmonicsProvider implements NormalizedSphericalHarmonicsProvider {

  45.     /** Underlying raw provider. */
  46.     private final NormalizedSphericalHarmonicsProvider rawProvider;

  47.     /** Number of coefficients in C<sub>n, m</sub> and S<sub>n, m</sub> arrays (counted separately). */
  48.     private final int size;

  49.     /** Cache. */
  50.     private final TimeStampedCache<TimeStampedSphericalHarmonics> cache;

  51.     /** Simple constructor.
  52.      * @param rawProvider underlying raw provider
  53.      * @param step time step between sample points for interpolation
  54.      * @param nbPoints number of points to use for interpolation, must be at least 2
  55.      * @param maxSlots maximum number of independent cached time slots
  56.      * @param maxSpan maximum duration span in seconds of one slot
  57.      * (can be set to {@code Double.POSITIVE_INFINITY} if desired)
  58.      * @param newSlotInterval time interval above which a new slot is created
  59.      * instead of extending an existing one
  60.      */
  61.     public CachedNormalizedSphericalHarmonicsProvider(final NormalizedSphericalHarmonicsProvider rawProvider,
  62.                                                       final double step, final int nbPoints,
  63.                                                       final int maxSlots, final double maxSpan,
  64.                                                       final double newSlotInterval) {

  65.         this.rawProvider  = rawProvider;
  66.         final int k       = rawProvider.getMaxDegree() + 1;
  67.         this.size         = (k * (k + 1)) / 2;

  68.         cache = new GenericTimeStampedCache<TimeStampedSphericalHarmonics>(nbPoints, maxSlots, maxSpan,
  69.                                                                            newSlotInterval, new Generator(step),
  70.                                                                            TimeStampedSphericalHarmonics.class);
  71.     }

  72.     /** {@inheritDoc} */
  73.     @Override
  74.     public int getMaxDegree() {
  75.         return rawProvider.getMaxDegree();
  76.     }

  77.     /** {@inheritDoc} */
  78.     @Override
  79.     public int getMaxOrder() {
  80.         return rawProvider.getMaxOrder();
  81.     }

  82.     /** {@inheritDoc} */
  83.     @Override
  84.     public double getMu() {
  85.         return rawProvider.getMu();
  86.     }

  87.     /** {@inheritDoc} */
  88.     @Override
  89.     public double getAe() {
  90.         return rawProvider.getAe();
  91.     }

  92.     /** {@inheritDoc} */
  93.     @Override
  94.     public AbsoluteDate getReferenceDate() {
  95.         return rawProvider.getReferenceDate();
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     public double getOffset(final AbsoluteDate date) {
  100.         return rawProvider.getOffset(date);
  101.     }

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public TideSystem getTideSystem() {
  105.         return rawProvider.getTideSystem();
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) throws
  110.             TimeStampedCacheException {
  111.         return TimeStampedSphericalHarmonics.interpolate(date, cache.getNeighbors(date));
  112.     }

  113.     /** Generator for time-stamped spherical harmonics. */
  114.     private class Generator implements TimeStampedGenerator<TimeStampedSphericalHarmonics> {

  115.         /** Time step between generated sets. */
  116.         private final double step;

  117.         /** Simple constructor.
  118.          * @param step time step between generated sets
  119.          */
  120.         public Generator(final double step) {
  121.             this.step = step;
  122.         }

  123.         /** {@inheritDoc} */
  124.         @Override
  125.         public List<TimeStampedSphericalHarmonics> generate(final TimeStampedSphericalHarmonics existing,
  126.                                                             final AbsoluteDate date)
  127.             throws TimeStampedCacheException {
  128.             try {

  129.                 final List<TimeStampedSphericalHarmonics> generated =
  130.                         new ArrayList<TimeStampedSphericalHarmonics>();
  131.                 final double[] cnmsnm = new double[2 * size];

  132.                 if (existing == null) {

  133.                     // no prior existing transforms, just generate a first set
  134.                     for (int i = 0; i < cache.getNeighborsSize(); ++i) {
  135.                         final AbsoluteDate t = date.shiftedBy((i - cache.getNeighborsSize() / 2) * step);
  136.                         fillArray(rawProvider.onDate(t), cnmsnm);
  137.                         generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
  138.                     }

  139.                 } else {

  140.                     // some coefficients have already been generated
  141.                     // add the missing ones up to specified date

  142.                     AbsoluteDate t = existing.getDate();
  143.                     if (date.compareTo(t) > 0) {
  144.                         // forward generation
  145.                         do {
  146.                             t = t.shiftedBy(step);
  147.                             fillArray(rawProvider.onDate(t), cnmsnm);
  148.                             generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
  149.                         } while (t.compareTo(date) <= 0);
  150.                     } else {
  151.                         // backward generation
  152.                         do {
  153.                             t = t.shiftedBy(-step);
  154.                             fillArray(rawProvider.onDate(t), cnmsnm);
  155.                             generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
  156.                         } while (t.compareTo(date) >= 0);
  157.                         // ensure forward chronological order
  158.                         Collections.reverse(generated);
  159.                     }

  160.                 }

  161.                 // return the generated sample
  162.                 return generated;

  163.             } catch (OrekitException oe) {
  164.                 throw new TimeStampedCacheException(oe);
  165.             }
  166.         }

  167.         /** Fill coefficients array for one entry.
  168.          * @param raw the un-interpolated spherical harmonics
  169.          * @param cnmsnm arrays to fill in
  170.          * @exception OrekitException if coefficients cannot be computed at specified date
  171.          */
  172.         private void fillArray(final NormalizedSphericalHarmonics raw,
  173.                                final double[] cnmsnm)
  174.             throws OrekitException {
  175.             int index = 0;
  176.             for (int n = 0; n <= rawProvider.getMaxDegree(); ++n) {
  177.                 for (int m = 0; m <= n; ++m) {
  178.                     cnmsnm[index++] = raw.getNormalizedCnm(n, m);
  179.                 }
  180.             }
  181.             for (int n = 0; n <= rawProvider.getMaxDegree(); ++n) {
  182.                 for (int m = 0; m <= n; ++m) {
  183.                     cnmsnm[index++] = raw.getNormalizedSnm(n, m);
  184.                 }
  185.             }
  186.         }

  187.     }

  188.     /**
  189.      * Internal class for time-stamped spherical harmonics. Instances are created using
  190.      * {@link #interpolate(AbsoluteDate, Collection)}
  191.      */
  192.     private static class TimeStampedSphericalHarmonics
  193.             implements TimeStamped, Serializable, NormalizedSphericalHarmonics {

  194.         /** Serializable UID. */
  195.         private static final long serialVersionUID = 20131029l;

  196.         /** Current date. */
  197.         private final AbsoluteDate date;

  198.         /** number of C or S coefficients. */
  199.         private final int size;

  200.         /** Flattened array for C<sub>n,m</sub> and S<sub>n,m</sub> coefficients. */
  201.         private final double[] cnmsnm;

  202.         /** Simple constructor.
  203.          * @param date current date
  204.          * @param cnmsnm flattened array for C<sub>n,m</sub> and S<sub>n,m</sub>
  205.          *               coefficients. It is copied.
  206.          */
  207.         private TimeStampedSphericalHarmonics(final AbsoluteDate date,
  208.                                               final double[] cnmsnm) {
  209.             this.date   = date;
  210.             this.cnmsnm = cnmsnm.clone();
  211.             this.size   = cnmsnm.length / 2;
  212.         }

  213.         /** {@inheritDoc} */
  214.         @Override
  215.         public AbsoluteDate getDate() {
  216.             return date;
  217.         }

  218.         /** {@inheritDoc} */
  219.         @Override
  220.         public double getNormalizedCnm(final int n, final int m) throws OrekitException {
  221.             return cnmsnm[(n * (n + 1)) / 2 + m];
  222.         }

  223.         /** {@inheritDoc} */
  224.         @Override
  225.         public double getNormalizedSnm(final int n, final int m) throws OrekitException {
  226.             return cnmsnm[(n * (n + 1)) / 2 + m + size];
  227.         }

  228.         /** Interpolate spherical harmonics.
  229.          * <p>
  230.          * The interpolated instance is created by polynomial Hermite interpolation.
  231.          * </p>
  232.          * @param date interpolation date
  233.          * @param sample sample points on which interpolation should be done
  234.          * @return a new time-stamped spherical harmonics, interpolated at specified date
  235.          */
  236.         public static TimeStampedSphericalHarmonics interpolate(final AbsoluteDate date,
  237.                                                                 final Collection<TimeStampedSphericalHarmonics> sample) {

  238.             // set up an interpolator taking derivatives into account
  239.             final HermiteInterpolator interpolator = new HermiteInterpolator();

  240.             // add sample points
  241.             for (final TimeStampedSphericalHarmonics tssh : sample) {
  242.                 interpolator.addSamplePoint(tssh.date.durationFrom(date), tssh.cnmsnm);
  243.             }

  244.             // build a new interpolated instance
  245.             return new TimeStampedSphericalHarmonics(date, interpolator.value(0.0));

  246.         }

  247.     }

  248. }