RuggedBuilder.java

  1. /* Copyright 2013-2022 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.rugged.api;

  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.ObjectInputStream;
  21. import java.io.ObjectOutputStream;
  22. import java.io.OutputStream;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.List;

  26. import org.hipparchus.exception.LocalizedCoreFormats;
  27. import org.hipparchus.geometry.euclidean.threed.Rotation;
  28. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  29. import org.orekit.bodies.OneAxisEllipsoid;
  30. import org.orekit.frames.Frame;
  31. import org.orekit.frames.FramesFactory;
  32. import org.orekit.propagation.Propagator;
  33. import org.orekit.rugged.errors.RuggedException;
  34. import org.orekit.rugged.errors.RuggedInternalError;
  35. import org.orekit.rugged.errors.RuggedMessages;
  36. import org.orekit.rugged.intersection.BasicScanAlgorithm;
  37. import org.orekit.rugged.intersection.ConstantElevationAlgorithm;
  38. import org.orekit.rugged.intersection.IgnoreDEMAlgorithm;
  39. import org.orekit.rugged.intersection.IntersectionAlgorithm;
  40. import org.orekit.rugged.intersection.duvenhage.DuvenhageAlgorithm;
  41. import org.orekit.rugged.linesensor.LineSensor;
  42. import org.orekit.rugged.raster.TileUpdater;
  43. import org.orekit.rugged.refraction.AtmosphericRefraction;
  44. import org.orekit.rugged.utils.ExtendedEllipsoid;
  45. import org.orekit.rugged.utils.SpacecraftToObservedBody;
  46. import org.orekit.time.AbsoluteDate;
  47. import org.orekit.utils.AngularDerivativesFilter;
  48. import org.orekit.utils.CartesianDerivativesFilter;
  49. import org.orekit.utils.Constants;
  50. import org.orekit.utils.IERSConventions;
  51. import org.orekit.utils.PVCoordinates;
  52. import org.orekit.utils.TimeStampedAngularCoordinates;
  53. import org.orekit.utils.TimeStampedPVCoordinates;

  54. /** Builder for {@link Rugged} instances.
  55.  * <p>
  56.  * This class implements the <em>builder pattern</em> to create {@link Rugged} instances.
  57.  * It does so by using a <em>fluent API</em> in order to clarify reading and allow
  58.  * later extensions with new configuration parameters.
  59.  * </p>
  60.  * <p>
  61.  * A typical use would be:
  62.  * </p>
  63.  * <pre>
  64.  *   Rugged rugged = new RuggedBuilder().
  65.  *                   setEllipsoid(EllipsoidId.WGS84, BodyRotatingFrameId.ITRF).
  66.  *                   setAlgorithmID(AlgorithmId.Duvenhage).
  67.  *                   setDigitalElevationModel(tileUpdater, maxCachedTiles).
  68.  *                   setTimeSpan(minDate, maxDate, tStep, overshootTolerance).
  69.  *                   setTrajectory(positionsVelocities, pvInterpolationNumber, pvFilter,
  70.  *                                 quaternions, aInterpolationNumber, aFilter).
  71.  *                   addLineSensor(sensor1).
  72.  *                   addLineSensor(sensor2).
  73.  *                   addLineSensor(sensor3).
  74.  *                   build();
  75.  * </pre>
  76.  * <p>
  77.  * If a configuration parameter has not been set prior to the call to {]link #build()}, then
  78.  * an exception will be triggered with an explicit error message.
  79.  * </p>
  80.  * @see <a href="https://en.wikipedia.org/wiki/Builder_pattern">Builder pattern (wikipedia)</a>
  81.  * @see <a href="https://en.wikipedia.org/wiki/Fluent_interface">Fluent interface (wikipedia)</a>
  82.  * @author Luc Maisonobe
  83.  * @author Guylaine Prat
  84.  */
  85. public class RuggedBuilder {

  86.     /** Reference ellipsoid. */
  87.     private ExtendedEllipsoid ellipsoid;

  88.     /** DEM intersection algorithm. */
  89.     private AlgorithmId algorithmID;

  90.     /** Updater used to load Digital Elevation Model tiles. */
  91.     private TileUpdater tileUpdater;

  92.     /** Constant elevation over ellipsoid (m).
  93.      * used only with {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID. */
  94.     private double constantElevation;

  95.     /** Maximum number of tiles stored in the cache. */
  96.     private int maxCachedTiles;

  97.     /** Start of search time span. */
  98.     private AbsoluteDate minDate;

  99.     /** End of search time span. */
  100.     private AbsoluteDate maxDate;

  101.     /** Step to use for inertial frame to body frame transforms cache computations (s). */
  102.     private double tStep;

  103.     /** OvershootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting (s). */
  104.     private double overshootTolerance;

  105.     /** Inertial frame for position/velocity/attitude. */
  106.     private Frame inertial;

  107.     /** Satellite position and velocity (m and m/s in inertial frame). */
  108.     private List<TimeStampedPVCoordinates> pvSample;

  109.     /** Number of points to use for position/velocity interpolation. */
  110.     private int pvNeighborsSize;

  111.     /** Filter for derivatives from the sample to use in position/velocity interpolation. */
  112.     private CartesianDerivativesFilter pvDerivatives;

  113.     /** Satellite quaternions with respect to inertial frame. */
  114.     private List<TimeStampedAngularCoordinates> aSample;

  115.     /** Number of points to use for attitude interpolation. */
  116.     private int aNeighborsSize;

  117.     /** Filter for derivatives from the sample to use in attitude interpolation. */
  118.     private AngularDerivativesFilter aDerivatives;

  119.     /** Propagator for position/velocity/attitude. */
  120.     private Propagator pvaPropagator;

  121.     /** Step to use for inertial/Earth/spacecraft transforms interpolations (s). */
  122.     private double iStep;

  123.     /** Number of points to use for inertial/Earth/spacecraft transforms interpolations. */
  124.     private int iN;

  125.     /** Converter between spacecraft and body. */
  126.     private SpacecraftToObservedBody scToBody;

  127.     /** Flag for light time correction. */
  128.     private boolean lightTimeCorrection;

  129.     /** Flag for aberration of light correction. */
  130.     private boolean aberrationOfLightCorrection;

  131.     /** Atmospheric refraction to use for line of sight correction. */
  132.     private AtmosphericRefraction atmosphericRefraction;

  133.     /** Sensors. */
  134.     private final List<LineSensor> sensors;

  135.     /** Rugged name. */
  136.     private String name;

  137.     /** Create a non-configured builder.
  138.      * <p>
  139.      * The builder <em>must</em> be configured before calling the
  140.      * {@link #build} method, otherwise an exception will be triggered
  141.      * at build time.
  142.      * </p>
  143.      */
  144.     public RuggedBuilder() {
  145.         sensors                     = new ArrayList<>();
  146.         constantElevation           = Double.NaN;
  147.         lightTimeCorrection         = true;
  148.         aberrationOfLightCorrection = true;
  149.         name                        = "Rugged";
  150.     }

  151.     /** Set the reference ellipsoid.
  152.      * @param ellipsoidID reference ellipsoid
  153.      * @param bodyRotatingFrameID body rotating frame identifier
  154.      * from an earlier run and frames mismatch
  155.      * @return the builder instance
  156.      * @see #setEllipsoid(OneAxisEllipsoid)
  157.      * @see #getEllipsoid()
  158.      */
  159.     public RuggedBuilder setEllipsoid(final EllipsoidId ellipsoidID, final BodyRotatingFrameId bodyRotatingFrameID) {
  160.         return setEllipsoid(selectEllipsoid(ellipsoidID, selectBodyRotatingFrame(bodyRotatingFrameID)));
  161.     }

  162.     /** Set the reference ellipsoid.
  163.      * @param newEllipsoid reference ellipsoid
  164.      * @return the builder instance
  165.      * @see #setEllipsoid(EllipsoidId, BodyRotatingFrameId)
  166.      * @see #getEllipsoid()
  167.      */
  168.     public RuggedBuilder setEllipsoid(final OneAxisEllipsoid newEllipsoid) {
  169.         this.ellipsoid = new ExtendedEllipsoid(newEllipsoid.getEquatorialRadius(),
  170.                                                newEllipsoid.getFlattening(),
  171.                                                newEllipsoid.getBodyFrame());
  172.         checkFramesConsistency();
  173.         return this;
  174.     }

  175.     /** Get the ellipsoid.
  176.      * @return the ellipsoid
  177.      * @see #setEllipsoid(EllipsoidId, BodyRotatingFrameId)
  178.      * @see #setEllipsoid(OneAxisEllipsoid)
  179.      */
  180.     public ExtendedEllipsoid getEllipsoid() {
  181.         return ellipsoid;
  182.     }

  183.     /** Get the Rugged name.
  184.      * @return the Rugged name
  185.      * @since 2.0
  186.      */
  187.     public String getName() {
  188.         return name;
  189.     }

  190.     /** Set the Rugged name.
  191.      * @param name the Rugged name
  192.      * @since 2.0
  193.      */
  194.     public void setName(final String name) {
  195.         this.name = name;
  196.     }

  197.     /** Set the algorithm to use for Digital Elevation Model intersection.
  198.      * <p>
  199.      * Note that some algorithms require specific other methods to be called too:
  200.      * <ul>
  201.      *   <li>{@link AlgorithmId#DUVENHAGE DUVENHAGE},
  202.      *   {@link AlgorithmId#DUVENHAGE_FLAT_BODY DUVENHAGE_FLAT_BODY}
  203.      *   and {@link AlgorithmId#BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY
  204.      *   BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY} all
  205.      *   require {@link #setDigitalElevationModel(TileUpdater, int) setDigitalElevationModel}
  206.      *   to be called,</li>
  207.      *   <li>{@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID
  208.      *   CONSTANT_ELEVATION_OVER_ELLIPSOID} requires
  209.      *   {@link #setConstantElevation(double) setConstantElevation} to be called,</li>
  210.      *   <li>{@link AlgorithmId#IGNORE_DEM_USE_ELLIPSOID
  211.      *   IGNORE_DEM_USE_ELLIPSOID} does not require
  212.      *   any methods tobe called.</li>
  213.      * </ul>
  214.      *
  215.      * @param newAlgorithmId identifier of algorithm to use for Digital Elevation Model intersection
  216.      * @return the builder instance
  217.      * @see #setDigitalElevationModel(TileUpdater, int)
  218.      * @see #getAlgorithm()
  219.      */
  220.     public RuggedBuilder setAlgorithm(final AlgorithmId newAlgorithmId) {
  221.         this.algorithmID = newAlgorithmId;
  222.         return this;
  223.     }

  224.     /** Get the algorithm to use for Digital Elevation Model intersection.
  225.      * @return algorithm to use for Digital Elevation Model intersection
  226.      * @see #setAlgorithm(AlgorithmId)
  227.      */
  228.     public AlgorithmId getAlgorithm() {
  229.         return algorithmID;
  230.     }

  231.     /** Set the user-provided {@link TileUpdater tile updater}.
  232.      * <p>
  233.      * Note that when the algorithm specified in {@link #setAlgorithm(AlgorithmId)}
  234.      * is either {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID
  235.      * CONSTANT_ELEVATION_OVER_ELLIPSOID} or {@link
  236.      * AlgorithmId#IGNORE_DEM_USE_ELLIPSOID IGNORE_DEM_USE_ELLIPSOID},
  237.      * then this method becomes irrelevant and can either be not called at all,
  238.      * or it can be called with an updater set to {@code null}. For all other
  239.      * algorithms, the updater must be properly configured.
  240.      * </p>
  241.      * @param newTileUpdater updater used to load Digital Elevation Model tiles
  242.      * @param newMaxCachedTiles maximum number of tiles stored in the cache
  243.      * @return the builder instance
  244.      * @see #setAlgorithm(AlgorithmId)
  245.      * @see #getTileUpdater()
  246.      * @see #getMaxCachedTiles()
  247.      */
  248.     public RuggedBuilder setDigitalElevationModel(final TileUpdater newTileUpdater, final int newMaxCachedTiles) {
  249.         this.tileUpdater    = newTileUpdater;
  250.         this.maxCachedTiles = newMaxCachedTiles;
  251.         return this;
  252.     }

  253.     /** Get the updater used to load Digital Elevation Model tiles.
  254.      * @return updater used to load Digital Elevation Model tiles
  255.      * @see #setDigitalElevationModel(TileUpdater, int)
  256.      * @see #getMaxCachedTiles()
  257.      */
  258.     public TileUpdater getTileUpdater() {
  259.         return tileUpdater;
  260.     }

  261.     /** Set the user-provided constant elevation model.
  262.      * <p>
  263.      * Note that this method is relevant <em>only</em> if the algorithm specified
  264.      * in {@link #setAlgorithm(AlgorithmId)} is {@link
  265.      * AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID CONSTANT_ELEVATION_OVER_ELLIPSOID}.
  266.      * If it is called for another algorithm, the elevation set here will be ignored.
  267.      * </p>
  268.      * @param newConstantElevation constant elevation to use (m)
  269.      * @return the builder instance
  270.      * @see #setAlgorithm(AlgorithmId)
  271.      * @see #getConstantElevation()
  272.      */
  273.     public RuggedBuilder setConstantElevation(final double newConstantElevation) {
  274.         this.constantElevation = newConstantElevation;
  275.         return this;
  276.     }

  277.     /** Get the constant elevation over ellipsoid to use with {@link AlgorithmId#CONSTANT_ELEVATION_OVER_ELLIPSOID}.
  278.      * @return updater used to load Digital Elevation Model tiles
  279.      * @see #setConstantElevation(double)
  280.      */
  281.     public double getConstantElevation() {
  282.         return constantElevation;
  283.     }

  284.     /** Get the maximum number of tiles stored in the cache.
  285.      * @return maximum number of tiles stored in the cache
  286.      * @see #setDigitalElevationModel(TileUpdater, int)
  287.      * @see #getTileUpdater()
  288.      */
  289.     public int getMaxCachedTiles() {
  290.         return maxCachedTiles;
  291.     }

  292.     /** Set the time span to be covered for direct and inverse location calls.
  293.      * <p>
  294.      * This method set only the time span and not the trajectory, therefore it
  295.      * <em>must</em> be used together with either
  296.      * {@link #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)},
  297.      * {@link #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)},
  298.      * or {@link #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)}
  299.      * but should <em>not</em> be mixed with {@link #setTrajectoryAndTimeSpan(InputStream)}.
  300.      * </p>
  301.      * @param newMinDate start of search time span
  302.      * @param newMaxDate end of search time span
  303.      * @param newTstep step to use for inertial frame to body frame transforms cache computations (s)
  304.      * @param newOvershootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting (s)
  305.      * @return the builder instance
  306.      * @see #setTrajectoryAndTimeSpan(InputStream)
  307.      * @see #getMinDate()
  308.      * @see #getMaxDate()
  309.      * @see #getTStep()
  310.      * @see #getOvershootTolerance()
  311.      */
  312.     public RuggedBuilder setTimeSpan(final AbsoluteDate newMinDate, final AbsoluteDate newMaxDate,
  313.                                      final double newTstep, final double newOvershootTolerance) {
  314.         this.minDate            = newMinDate;
  315.         this.maxDate            = newMaxDate;
  316.         this.tStep              = newTstep;
  317.         this.overshootTolerance = newOvershootTolerance;
  318.         this.scToBody           = null;
  319.         return this;
  320.     }

  321.     /** Get the start of search time span.
  322.      * @return start of search time span
  323.      * @see #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)
  324.      */
  325.     public AbsoluteDate getMinDate() {
  326.         return minDate;
  327.     }

  328.     /** Get the end of search time span.
  329.      * @return end of search time span
  330.      * @see #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)
  331.      */
  332.     public AbsoluteDate getMaxDate() {
  333.         return maxDate;
  334.     }

  335.     /** Get the step to use for inertial frame to body frame transforms cache computations.
  336.      * @return step to use for inertial frame to body frame transforms cache computations
  337.      * @see #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)
  338.      */
  339.     public double getTStep() {
  340.         return tStep;
  341.     }

  342.     /** Get the tolerance in seconds allowed for {@link #getMinDate()} and {@link #getMaxDate()} overshooting.
  343.      * @return tolerance in seconds allowed for {@link #getMinDate()} and {@link #getMaxDate()} overshooting
  344.      * @see #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)
  345.      */
  346.     public double getOvershootTolerance() {
  347.         return overshootTolerance;
  348.     }

  349.     /** Set the spacecraft trajectory.
  350.      * <p>
  351.      * This method set only the trajectory and not the time span, therefore it
  352.      * <em>must</em> be used together with the {@link #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)}
  353.      * but should <em>not</em> be mixed with {@link #setTrajectoryAndTimeSpan(InputStream)}.
  354.      * </p>
  355.      * @param inertialFrameId inertial frame Id used for spacecraft positions/velocities/quaternions
  356.      * @param positionsVelocities satellite position and velocity (m and m/s in inertial frame)
  357.      * @param pvInterpolationNumber number of points to use for position/velocity interpolation
  358.      * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation
  359.      * @param quaternions satellite quaternions with respect to inertial frame
  360.      * @param aInterpolationNumber number of points to use for attitude interpolation
  361.      * @param aFilter filter for derivatives from the sample to use in attitude interpolation
  362.      * @return the builder instance
  363.      * @see #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  364.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  365.      * @see #setTrajectoryAndTimeSpan(InputStream)
  366.      * @see #getInertialFrame()
  367.      * @see #getPositionsVelocities()
  368.      * @see #getPVInterpolationNumber()
  369.      * @see #getPVInterpolationNumber()
  370.      * @see #getQuaternions()
  371.      * @see #getAInterpolationNumber()
  372.      * @see #getAFilter()
  373.      */
  374.     public RuggedBuilder setTrajectory(final InertialFrameId inertialFrameId,
  375.                                        final List<TimeStampedPVCoordinates> positionsVelocities, final int pvInterpolationNumber,
  376.                                        final CartesianDerivativesFilter pvFilter,
  377.                                        final List<TimeStampedAngularCoordinates> quaternions, final int aInterpolationNumber,
  378.                                        final AngularDerivativesFilter aFilter) {

  379.         return setTrajectory(selectInertialFrame(inertialFrameId),
  380.                              positionsVelocities, pvInterpolationNumber, pvFilter,
  381.                              quaternions, aInterpolationNumber, aFilter);
  382.     }

  383.     /** Set the spacecraft trajectory.
  384.      * <p>
  385.      * This method set only the trajectory and not the time span, therefore it
  386.      * <em>must</em> be used together with the {@link #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)}
  387.      * but should <em>not</em> be mixed with {@link #setTrajectoryAndTimeSpan(InputStream)}.
  388.      * </p>
  389.      * @param inertialFrame inertial frame used for spacecraft positions/velocities/quaternions
  390.      * @param positionsVelocities satellite position and velocity (m and m/s in inertial frame)
  391.      * @param pvInterpolationNumber number of points to use for position/velocity interpolation
  392.      * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation
  393.      * @param quaternions satellite quaternions with respect to inertial frame
  394.      * @param aInterpolationNumber number of points to use for attitude interpolation
  395.      * @param aFilter filter for derivatives from the sample to use in attitude interpolation
  396.      * @return the builder instance
  397.      * @see #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  398.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  399.      * @see #setTrajectoryAndTimeSpan(InputStream)
  400.      * @see #getPositionsVelocities()
  401.      * @see #getPVInterpolationNumber()
  402.      * @see #getPVInterpolationNumber()
  403.      * @see #getQuaternions()
  404.      * @see #getAInterpolationNumber()
  405.      * @see #getAFilter()
  406.      */
  407.     public RuggedBuilder setTrajectory(final Frame inertialFrame,
  408.                                        final List<TimeStampedPVCoordinates> positionsVelocities, final int pvInterpolationNumber,
  409.                                        final CartesianDerivativesFilter pvFilter,
  410.                                        final List<TimeStampedAngularCoordinates> quaternions, final int aInterpolationNumber,
  411.                                        final AngularDerivativesFilter aFilter) {

  412.         this.inertial        = inertialFrame;
  413.         this.pvSample        = positionsVelocities;
  414.         this.pvNeighborsSize = pvInterpolationNumber;
  415.         this.pvDerivatives   = pvFilter;
  416.         this.aSample         = quaternions;
  417.         this.aNeighborsSize  = aInterpolationNumber;
  418.         this.aDerivatives    = aFilter;
  419.         this.pvaPropagator   = null;
  420.         this.iStep           = Double.NaN;
  421.         this.iN              = -1;
  422.         this.scToBody        = null;
  423.         return this;
  424.     }

  425.     /** Set the spacecraft trajectory.
  426.      * <p>
  427.      * This method set only the trajectory and not the time span, therefore it
  428.      * <em>must</em> be used together with the {@link #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)}
  429.      * but should <em>not</em> be mixed with {@link #setTrajectoryAndTimeSpan(InputStream)}.
  430.      * </p>
  431.      * @param interpolationStep step to use for inertial/Earth/spacecraft transforms interpolations (s)
  432.      * @param interpolationNumber number of points to use for inertial/Earth/spacecraft transforms interpolations
  433.      * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation
  434.      * @param aFilter filter for derivatives from the sample to use in attitude interpolation
  435.      * @param propagator global propagator
  436.      * @return the builder instance
  437.      * @see #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  438.      * @see #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  439.      * @see #setTrajectoryAndTimeSpan(InputStream)
  440.      */
  441.     public RuggedBuilder setTrajectory(final double interpolationStep, final int interpolationNumber,
  442.                                        final CartesianDerivativesFilter pvFilter, final AngularDerivativesFilter aFilter,
  443.                                        final Propagator propagator) {
  444.         this.inertial        = propagator.getFrame();
  445.         this.pvSample        = null;
  446.         this.pvNeighborsSize = -1;
  447.         this.pvDerivatives   = pvFilter;
  448.         this.aSample         = null;
  449.         this.aNeighborsSize  = -1;
  450.         this.aDerivatives    = aFilter;
  451.         this.pvaPropagator   = propagator;
  452.         this.iStep           = interpolationStep;
  453.         this.iN              = interpolationNumber;
  454.         this.scToBody        = null;
  455.         return this;
  456.     }

  457.     /** Get the inertial frame.
  458.      * @return inertial frame
  459.      */
  460.     public Frame getInertialFrame() {
  461.         return inertial;
  462.     }

  463.     /** Get the satellite position and velocity (m and m/s in inertial frame).
  464.      * @return satellite position and velocity (m and m/s in inertial frame)
  465.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  466.      */
  467.     public List<TimeStampedPVCoordinates> getPositionsVelocities() {
  468.         return pvSample;
  469.     }

  470.     /** Get the number of points to use for position/velocity interpolation.
  471.      * @return number of points to use for position/velocity interpolation
  472.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  473.      */
  474.     public int getPVInterpolationNumber() {
  475.         return pvNeighborsSize;
  476.     }

  477.     /** Get the filter for derivatives from the sample to use in position/velocity interpolation.
  478.      * @return filter for derivatives from the sample to use in position/velocity interpolation
  479.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  480.      */
  481.     public CartesianDerivativesFilter getPVFilter() {
  482.         return pvDerivatives;
  483.     }

  484.     /** Get the satellite quaternions with respect to inertial frame.
  485.      * @return satellite quaternions with respect to inertial frame
  486.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  487.      */
  488.     public List<TimeStampedAngularCoordinates> getQuaternions() {
  489.         return aSample;
  490.     }

  491.     /** Get the number of points to use for attitude interpolation.
  492.      * @return number of points to use for attitude interpolation
  493.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  494.      */
  495.     public int getAInterpolationNumber() {
  496.         return aNeighborsSize;
  497.     }

  498.     /** Get the filter for derivatives from the sample to use in attitude interpolation.
  499.      * @return filter for derivatives from the sample to use in attitude interpolation
  500.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  501.      */
  502.     public AngularDerivativesFilter getAFilter() {
  503.         return aDerivatives;
  504.     }

  505.     /** Set both the spacecraft trajectory and the time span.
  506.      * <p>
  507.      * This method set both the trajectory and the time span in a tightly coupled
  508.      * way, therefore it should <em>not</em> be mixed with the individual methods
  509.      * {@link #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)},
  510.      * {@link #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)},
  511.      * {@link #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)},
  512.      * or {@link #setTimeSpan(AbsoluteDate, AbsoluteDate, double, double)}.
  513.      * </p>
  514.      * @param storageStream stream from where to read previous instance {@link #storeInterpolator(OutputStream)
  515.      * stored interpolator} (caller opened it and remains responsible for closing it)
  516.      * @return the builder instance
  517.      * @see #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  518.      * @see #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  519.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  520.      * @see #storeInterpolator(OutputStream)
  521.      */
  522.     public RuggedBuilder setTrajectoryAndTimeSpan(final InputStream storageStream) {

  523.         try {
  524.             this.inertial           = null;
  525.             this.pvSample           = null;
  526.             this.pvNeighborsSize    = -1;
  527.             this.pvDerivatives      = null;
  528.             this.aSample            = null;
  529.             this.aNeighborsSize     = -1;
  530.             this.aDerivatives       = null;
  531.             this.pvaPropagator      = null;
  532.             this.iStep              = Double.NaN;
  533.             this.iN                 = -1;
  534.             this.scToBody           = (SpacecraftToObservedBody) new ObjectInputStream(storageStream).readObject();
  535.             this.minDate            = scToBody.getMinDate();
  536.             this.maxDate            = scToBody.getMaxDate();
  537.             this.tStep              = scToBody.getTStep();
  538.             this.overshootTolerance = scToBody.getOvershootTolerance();
  539.             checkFramesConsistency();
  540.             return this;

  541.         } catch (ClassNotFoundException cnfe) {
  542.             throw new RuggedException(cnfe, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA);
  543.         } catch (ClassCastException cce) {
  544.             throw new RuggedException(cce, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA);
  545.         } catch (IOException ioe) {
  546.             throw new RuggedException(ioe, RuggedMessages.NOT_INTERPOLATOR_DUMP_DATA);
  547.         }
  548.     }

  549.     /** Store frames transform interpolator.
  550.      * <p>
  551.      * This method allows to reuse the interpolator built in one instance, to build
  552.      * another instance by calling {@link #setTrajectoryAndTimeSpan(InputStream)}.
  553.      * This reduces the builder initialization time as setting up the interpolator can be long, it is
  554.      * mainly intended to be used when several runs are done (for example in an image processing chain)
  555.      * with the same configuration.
  556.      * </p>
  557.      * <p>
  558.      * This method must be called <em>after</em> both the ellipsoid and trajectory have been set.
  559.      * </p>
  560.      * @param storageStream stream where to store the interpolator
  561.      * (caller opened it and remains responsible for closing it)
  562.      * @see #setEllipsoid(EllipsoidId, BodyRotatingFrameId)
  563.      * @see #setEllipsoid(OneAxisEllipsoid)
  564.      * @see #setTrajectory(InertialFrameId, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  565.      * @see #setTrajectory(Frame, List, int, CartesianDerivativesFilter, List, int, AngularDerivativesFilter)
  566.      * @see #setTrajectory(double, int, CartesianDerivativesFilter, AngularDerivativesFilter, Propagator)
  567.      * @see #setTrajectoryAndTimeSpan(InputStream)
  568.      */
  569.     public void storeInterpolator(final OutputStream storageStream) {
  570.         try {
  571.             createInterpolatorIfNeeded();
  572.             new ObjectOutputStream(storageStream).writeObject(scToBody);
  573.         } catch (IOException ioe) {
  574.             throw new RuggedException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getMessage());
  575.         }
  576.     }

  577.     /** Check frames consistency.
  578.      */
  579.     private void checkFramesConsistency() {
  580.         if (ellipsoid != null && scToBody != null &&
  581.             !ellipsoid.getBodyFrame().getName().equals(scToBody.getBodyFrame().getName())) {
  582.             // if frames have been set both by direct calls and by deserializing an interpolator dump and a mismatch occurs
  583.             throw new RuggedException(RuggedMessages.FRAMES_MISMATCH_WITH_INTERPOLATOR_DUMP,
  584.                                       ellipsoid.getBodyFrame().getName(), scToBody.getBodyFrame().getName());
  585.         }
  586.     }

  587.     /** Create a transform interpolator if needed.
  588.      */
  589.     private void createInterpolatorIfNeeded() {

  590.         if (ellipsoid == null) {
  591.             throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setEllipsoid()");
  592.         }

  593.         if (scToBody == null) {
  594.             if (pvSample != null) {
  595.                 scToBody = createInterpolator(inertial, ellipsoid.getBodyFrame(),
  596.                                               minDate, maxDate, tStep, overshootTolerance,
  597.                                               pvSample, pvNeighborsSize, pvDerivatives,
  598.                                               aSample, aNeighborsSize, aDerivatives);
  599.             } else if (pvaPropagator != null) {
  600.                 scToBody = createInterpolator(inertial, ellipsoid.getBodyFrame(),
  601.                                               minDate, maxDate, tStep, overshootTolerance,
  602.                                               iStep, iN, pvDerivatives, aDerivatives, pvaPropagator);
  603.             } else {
  604.                 throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setTrajectory()");
  605.             }
  606.         }
  607.     }

  608.     /** Create a transform interpolator from positions and quaternions lists.
  609.      * @param inertialFrame inertial frame
  610.      * @param bodyFrame observed body frame
  611.      * @param minDate start of search time span
  612.      * @param maxDate end of search time span
  613.      * @param tStep step to use for inertial frame to body frame transforms cache computations
  614.      * @param overshootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting
  615.      * @param positionsVelocities satellite position and velocity
  616.      * @param pvInterpolationNumber number of points to use for position/velocity interpolation
  617.      * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation
  618.      * @param quaternions satellite quaternions
  619.      * @param aInterpolationNumber number of points to use for attitude interpolation
  620.      * @param aFilter filter for derivatives from the sample to use in attitude interpolation
  621.      * @return transforms interpolator
  622.      */
  623.     private static SpacecraftToObservedBody createInterpolator(final Frame inertialFrame, final Frame bodyFrame,
  624.                                                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
  625.                                                                final double tStep, final double overshootTolerance,
  626.                                                                final List<TimeStampedPVCoordinates> positionsVelocities,
  627.                                                                final int pvInterpolationNumber,
  628.                                                                final CartesianDerivativesFilter pvFilter,
  629.                                                                final List<TimeStampedAngularCoordinates> quaternions,
  630.                                                                final int aInterpolationNumber,
  631.                                                                final AngularDerivativesFilter aFilter) {

  632.         return new SpacecraftToObservedBody(inertialFrame, bodyFrame,
  633.                                             minDate, maxDate, tStep, overshootTolerance,
  634.                                             positionsVelocities, pvInterpolationNumber,
  635.                                             pvFilter, quaternions, aInterpolationNumber,
  636.                                             aFilter);
  637.     }

  638.     /** Create a transform interpolator from a propagator.
  639.      * @param inertialFrame inertial frame
  640.      * @param bodyFrame observed body frame
  641.      * @param minDate start of search time span
  642.      * @param maxDate end of search time span
  643.      * @param tStep step to use for inertial frame to body frame transforms cache computations
  644.      * @param overshootTolerance tolerance in seconds allowed for {@code minDate} and {@code maxDate} overshooting
  645.      * @param interpolationStep step to use for inertial/Earth/spacecraft transforms interpolations
  646.      * @param interpolationNumber number of points of to use for inertial/Earth/spacecraft transforms interpolations
  647.      * @param pvFilter filter for derivatives from the sample to use in position/velocity interpolation
  648.      * @param aFilter filter for derivatives from the sample to use in attitude interpolation
  649.      * @param propagator global propagator
  650.      * @return transforms interpolator
  651.      */
  652.     private static SpacecraftToObservedBody createInterpolator(final Frame inertialFrame, final Frame bodyFrame,
  653.                                                                final AbsoluteDate minDate, final AbsoluteDate maxDate,
  654.                                                                final double tStep, final double overshootTolerance,
  655.                                                                final double interpolationStep, final int interpolationNumber,
  656.                                                                final CartesianDerivativesFilter pvFilter,
  657.                                                                final AngularDerivativesFilter aFilter,
  658.                                                                final Propagator propagator) {

  659.         // extract position/attitude samples from propagator
  660.         final List<TimeStampedPVCoordinates> positionsVelocities =
  661.                 new ArrayList<>();
  662.         final List<TimeStampedAngularCoordinates> quaternions =
  663.                 new ArrayList<>();
  664.         propagator.getMultiplexer().add(interpolationStep,
  665.             currentState -> {
  666.                 final AbsoluteDate  date = currentState.getDate();
  667.                 final PVCoordinates pv   = currentState.getPVCoordinates(inertialFrame);
  668.                 final Rotation      q    = currentState.getAttitude().getRotation();
  669.                 positionsVelocities.add(new TimeStampedPVCoordinates(date, pv.getPosition(), pv.getVelocity(), Vector3D.ZERO));
  670.                 quaternions.add(new TimeStampedAngularCoordinates(date, q, Vector3D.ZERO, Vector3D.ZERO));
  671.             });
  672.         propagator.propagate(minDate.shiftedBy(-interpolationStep), maxDate.shiftedBy(interpolationStep));

  673.         // orbit/attitude to body converter
  674.         return createInterpolator(inertialFrame, bodyFrame,
  675.                 minDate, maxDate, tStep, overshootTolerance,
  676.                 positionsVelocities, interpolationNumber,
  677.                 pvFilter, quaternions, interpolationNumber,
  678.                 aFilter);
  679.     }

  680.     /** Set flag for light time correction.
  681.      * <p>
  682.      * This methods set the flag for compensating or not light time between
  683.      * ground and spacecraft. Compensating this delay improves location
  684.      * accuracy and is <em>enabled</em> by default (i.e. not calling this
  685.      * method before building is therefore equivalent to calling it with
  686.      * a parameter set to {@code true}). Not compensating it is mainly useful
  687.      * for validation purposes against system that do not compensate it.
  688.      * </p>
  689.      * @param newLightTimeCorrection if true, the light travel time between ground
  690.      * and spacecraft is compensated for more accurate location
  691.      * @return the builder instance
  692.      * @see #setAberrationOfLightCorrection(boolean)
  693.      * @see #getLightTimeCorrection()
  694.      */
  695.     public RuggedBuilder setLightTimeCorrection(final boolean newLightTimeCorrection) {
  696.         this.lightTimeCorrection = newLightTimeCorrection;
  697.         return this;
  698.     }

  699.     /** Get the light time correction flag.
  700.      * @return light time correction flag
  701.      * @see #setLightTimeCorrection(boolean)
  702.      */
  703.     public boolean getLightTimeCorrection() {
  704.         return lightTimeCorrection;
  705.     }

  706.     /** Set flag for aberration of light correction.
  707.      * <p>
  708.      * This methods set the flag for compensating or not aberration of light,
  709.      * which is velocity composition between light and spacecraft when the
  710.      * light from ground points reaches the sensor.
  711.      * Compensating this velocity composition improves location
  712.      * accuracy and is <em>enabled</em> by default (i.e. not calling this
  713.      * method before building is therefore equivalent to calling it with
  714.      * a parameter set to {@code true}). Not compensating it is useful
  715.      * in two cases: for validation purposes against system that do not
  716.      * compensate it or when the pixels line of sight already include the
  717.      * correction.
  718.      * </p>
  719.      * @param newAberrationOfLightCorrection if true, the aberration of light
  720.      * is corrected for more accurate location
  721.      * @return the builder instance
  722.      * @see #setLightTimeCorrection(boolean)
  723.      * @see #getAberrationOfLightCorrection()
  724.      */
  725.     public RuggedBuilder setAberrationOfLightCorrection(final boolean newAberrationOfLightCorrection) {
  726.         this.aberrationOfLightCorrection = newAberrationOfLightCorrection;
  727.         return this;
  728.     }

  729.     /** Get the aberration of light correction flag.
  730.      * @return aberration of light correction flag
  731.      * @see #setAberrationOfLightCorrection(boolean)
  732.      */
  733.     public boolean getAberrationOfLightCorrection() {
  734.         return aberrationOfLightCorrection;
  735.     }

  736.     /** Set atmospheric refraction for line of sight correction.
  737.      * <p>
  738.      * This method sets an atmospheric refraction model to be used between
  739.      * spacecraft and ground for the correction of intersected points on ground.
  740.      * Compensating for the effect of atmospheric refraction improves location
  741.      * accuracy.
  742.      * </p>
  743.      * @param newAtmosphericRefraction the atmospheric refraction model to be used for more accurate location
  744.      * @return the builder instance
  745.      * @see #getRefractionCorrection()
  746.      */
  747.     public RuggedBuilder setRefractionCorrection(final AtmosphericRefraction newAtmosphericRefraction) {
  748.         this.atmosphericRefraction = newAtmosphericRefraction;
  749.         return this;
  750.     }

  751.     /** Get the atmospheric refraction model.
  752.      * @return atmospheric refraction model
  753.      * @see #setRefractionCorrection(AtmosphericRefraction)
  754.      */
  755.     public AtmosphericRefraction getRefractionCorrection() {
  756.         return atmosphericRefraction;
  757.     }

  758.     /** Set up line sensor model.
  759.      * @param lineSensor line sensor model
  760.      * @return the builder instance
  761.      */
  762.     public RuggedBuilder addLineSensor(final LineSensor lineSensor) {
  763.         sensors.add(lineSensor);
  764.         return this;
  765.     }

  766.     /** Remove all line sensors.
  767.      * @return the builder instance
  768.      */
  769.     public RuggedBuilder clearLineSensors() {
  770.         sensors.clear();
  771.         return this;
  772.     }

  773.     /** Get all line sensors.
  774.      * @return all line sensors (in an unmodifiable list)
  775.      */
  776.     public List<LineSensor> getLineSensors() {
  777.         return Collections.unmodifiableList(sensors);
  778.     }

  779.     /** Select inertial frame.
  780.      * @param inertialFrameId inertial frame identifier
  781.      * @return inertial frame
  782.      */
  783.     private static Frame selectInertialFrame(final InertialFrameId inertialFrameId) {

  784.         // set up the inertial frame
  785.         switch (inertialFrameId) {
  786.             case GCRF :
  787.                 return FramesFactory.getGCRF();
  788.             case EME2000 :
  789.                 return FramesFactory.getEME2000();
  790.             case MOD :
  791.                 return FramesFactory.getMOD(IERSConventions.IERS_1996);
  792.             case TOD :
  793.                 return FramesFactory.getTOD(IERSConventions.IERS_1996, true);
  794.             case VEIS1950 :
  795.                 return FramesFactory.getVeis1950();
  796.             default :
  797.                 // this should never happen
  798.                 throw new RuggedInternalError(null);
  799.         }
  800.     }

  801.     /** Select body rotating frame.
  802.      * @param bodyRotatingFrame body rotating frame identifier
  803.      * @return body rotating frame
  804.      */
  805.     private static Frame selectBodyRotatingFrame(final BodyRotatingFrameId bodyRotatingFrame) {

  806.         // set up the rotating frame
  807.         switch (bodyRotatingFrame) {
  808.             case ITRF :
  809.                 return FramesFactory.getITRF(IERSConventions.IERS_2010, true);
  810.             case ITRF_EQUINOX :
  811.                 return FramesFactory.getITRFEquinox(IERSConventions.IERS_1996, true);
  812.             case GTOD :
  813.                 return FramesFactory.getGTOD(IERSConventions.IERS_1996, true);
  814.             default :
  815.                 // this should never happen
  816.                 throw new RuggedInternalError(null);
  817.         }
  818.     }

  819.     /** Select ellipsoid.
  820.      * @param ellipsoidID reference ellipsoid identifier
  821.      * @param bodyFrame body rotating frame
  822.      * @return selected ellipsoid
  823.      */
  824.     private static OneAxisEllipsoid selectEllipsoid(final EllipsoidId ellipsoidID, final Frame bodyFrame) {

  825.         // set up the ellipsoid
  826.         switch (ellipsoidID) {
  827.             case GRS80 :
  828.                 return new OneAxisEllipsoid(Constants.GRS80_EARTH_EQUATORIAL_RADIUS,
  829.                                             Constants.GRS80_EARTH_FLATTENING,
  830.                                             bodyFrame);
  831.             case WGS84 :
  832.                 return new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
  833.                                             Constants.WGS84_EARTH_FLATTENING,
  834.                                             bodyFrame);
  835.             case IERS96 :
  836.                 return new OneAxisEllipsoid(Constants.IERS96_EARTH_EQUATORIAL_RADIUS,
  837.                                             Constants.IERS96_EARTH_FLATTENING,
  838.                                             bodyFrame);
  839.             case IERS2003 :
  840.                 return new OneAxisEllipsoid(Constants.IERS2003_EARTH_EQUATORIAL_RADIUS,
  841.                                             Constants.IERS2003_EARTH_FLATTENING,
  842.                                             bodyFrame);
  843.             default :
  844.                 // this should never happen
  845.                 throw new RuggedInternalError(null);
  846.         }

  847.     }

  848.     /** Create DEM intersection algorithm.
  849.      * @param algorithmID intersection algorithm identifier
  850.      * @param updater updater used to load Digital Elevation Model tiles
  851.      * @param maxCachedTiles maximum number of tiles stored in the cache
  852.      * @param constantElevation constant elevation over ellipsoid
  853.      * @return selected algorithm
  854.      */
  855.     private static IntersectionAlgorithm createAlgorithm(final AlgorithmId algorithmID,
  856.                                                          final TileUpdater updater, final int maxCachedTiles,
  857.                                                          final double constantElevation) {

  858.         // set up the algorithm
  859.         switch (algorithmID) {
  860.             case DUVENHAGE :
  861.                 return new DuvenhageAlgorithm(updater, maxCachedTiles, false);
  862.             case DUVENHAGE_FLAT_BODY :
  863.                 return new DuvenhageAlgorithm(updater, maxCachedTiles, true);
  864.             case BASIC_SLOW_EXHAUSTIVE_SCAN_FOR_TESTS_ONLY :
  865.                 return new BasicScanAlgorithm(updater, maxCachedTiles);
  866.             case CONSTANT_ELEVATION_OVER_ELLIPSOID :
  867.                 return new ConstantElevationAlgorithm(constantElevation);
  868.             case IGNORE_DEM_USE_ELLIPSOID :
  869.                 return new IgnoreDEMAlgorithm();
  870.             default :
  871.                 // this should never happen
  872.                 throw new RuggedInternalError(null);
  873.         }
  874.     }

  875.     /** Build a {@link Rugged} instance.
  876.      * @return built instance
  877.      */
  878.     public Rugged build() {

  879.         if (algorithmID == null) {
  880.             throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setAlgorithmID()");
  881.         }
  882.         if (algorithmID == AlgorithmId.CONSTANT_ELEVATION_OVER_ELLIPSOID) {
  883.             if (Double.isNaN(constantElevation)) {
  884.                 throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setConstantElevation()");
  885.             }
  886.         } else if (algorithmID != AlgorithmId.IGNORE_DEM_USE_ELLIPSOID) {
  887.             if (tileUpdater == null) {
  888.                 throw new RuggedException(RuggedMessages.UNINITIALIZED_CONTEXT, "RuggedBuilder.setDigitalElevationModel()");
  889.             }
  890.         }
  891.         createInterpolatorIfNeeded();
  892.         return new Rugged(createAlgorithm(algorithmID, tileUpdater, maxCachedTiles, constantElevation), ellipsoid,
  893.                           lightTimeCorrection, aberrationOfLightCorrection, atmosphericRefraction, scToBody, sensors, name);
  894.     }
  895. }