GroundStation.java

  1. /* Copyright 2002-2018 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.estimation.measurements;

  18. import java.util.Map;

  19. import org.hipparchus.Field;
  20. import org.hipparchus.analysis.differentiation.DSFactory;
  21. import org.hipparchus.analysis.differentiation.DerivativeStructure;
  22. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  23. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  24. import org.hipparchus.geometry.euclidean.threed.Rotation;
  25. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  26. import org.hipparchus.util.FastMath;
  27. import org.orekit.bodies.BodyShape;
  28. import org.orekit.bodies.FieldGeodeticPoint;
  29. import org.orekit.bodies.GeodeticPoint;
  30. import org.orekit.bodies.OneAxisEllipsoid;
  31. import org.orekit.data.BodiesElements;
  32. import org.orekit.data.FundamentalNutationArguments;
  33. import org.orekit.errors.OrekitException;
  34. import org.orekit.errors.OrekitExceptionWrapper;
  35. import org.orekit.errors.OrekitMessages;
  36. import org.orekit.frames.EOPHistory;
  37. import org.orekit.frames.FieldTransform;
  38. import org.orekit.frames.Frame;
  39. import org.orekit.frames.FramesFactory;
  40. import org.orekit.frames.TopocentricFrame;
  41. import org.orekit.frames.Transform;
  42. import org.orekit.models.earth.displacement.StationDisplacement;
  43. import org.orekit.time.AbsoluteDate;
  44. import org.orekit.time.FieldAbsoluteDate;
  45. import org.orekit.time.TimeScalesFactory;
  46. import org.orekit.time.UT1Scale;
  47. import org.orekit.utils.ParameterDriver;

  48. /** Class modeling a ground station that can perform some measurements.
  49.  * <p>
  50.  * This class adds a position offset parameter to a base {@link TopocentricFrame
  51.  * topocentric frame}.
  52.  * </p>
  53.  * <p>
  54.  * Since 9.0, this class also adds parameters for an additional polar motion
  55.  * and an additional prime meridian orientation. Since these parameters will
  56.  * have the same name for all ground stations, they will be managed consistently
  57.  * and allow to estimate Earth orientation precisely (this is needed for precise
  58.  * orbit determination). The polar motion and prime meridian orientation will
  59.  * be applied <em>after</em> regular Earth orientation parameters, so the value
  60.  * of the estimated parameters will be correction to EOP, they will not be the
  61.  * complete EOP values by themselves. Basically, this means that for Earth, the
  62.  * following transforms are applied in order, between inertial frame and ground
  63.  * station frame (for non-Earth based ground stations, different precession nutation
  64.  * models and associated planet oritentation parameters would be applied, if available):
  65.  * </p>
  66.  * <ol>
  67.  *   <li>precession/nutation, as theoretical model plus celestial pole EOP parameters</li>
  68.  *   <li>body rotation, as theoretical model plus prime meridian EOP parameters</li>
  69.  *   <li>polar motion, which is only from EOP parameters (no theoretical models)</li>
  70.  *   <li>additional body rotation, controlled by {@link #getPrimeMeridianOffsetDriver()} and {@link #getPrimeMeridianDriftDriver()}</li>
  71.  *   <li>additional polar motion, controlled by {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()},
  72.  *   {@link #getPolarOffsetYDriver()} and {@link #getPolarDriftYDriver()}</li>
  73.  *   <li>station position offset, controlled by {@link #getEastOffsetDriver()},
  74.  *   {@link #getNorthOffsetDriver()} and {@link #getZenithOffsetDriver()}</li>
  75.  * </ol>
  76.  * @author Luc Maisonobe
  77.  * @since 8.0
  78.  */
  79. public class GroundStation {

  80.     /** Suffix for ground station position offset parameter name. */
  81.     public static final String OFFSET_SUFFIX = "-offset";

  82.     /** Suffix for ground station intermediate frame name. */
  83.     public static final String INTERMEDIATE_SUFFIX = "-intermediate";

  84.     /** Offsets scaling factor.
  85.      * <p>
  86.      * We use a power of 2 (in fact really 1.0 here) to avoid numeric noise introduction
  87.      * in the multiplications/divisions sequences.
  88.      * </p>
  89.      */
  90.     private static final double OFFSET_SCALE = FastMath.scalb(1.0, 0);

  91.     /** Provider for Earth frame whose EOP parameters can be estimated. */
  92.     private final EstimatedEarthFrameProvider estimatedEarthFrameProvider;

  93.     /** Earth frame whose EOP parameters can be estimated. */
  94.     private final Frame estimatedEarthFrame;

  95.     /** Base frame associated with the station. */
  96.     private final TopocentricFrame baseFrame;

  97.     /** Fundamental nutation arguments. */
  98.     private final FundamentalNutationArguments arguments;

  99.     /** Displacement models. */
  100.     private final StationDisplacement[] displacements;

  101.     /** Driver for position offset along the East axis. */
  102.     private final ParameterDriver eastOffsetDriver;

  103.     /** Driver for position offset along the North axis. */
  104.     private final ParameterDriver northOffsetDriver;

  105.     /** Driver for position offset along the zenith axis. */
  106.     private final ParameterDriver zenithOffsetDriver;

  107.     /** Build a ground station ignoring {@link StationDisplacement station displacements}.
  108.      * <p>
  109.      * The initial values for the pole and prime meridian parametric linear models
  110.      * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()},
  111.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()},
  112.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}) are set to 0.
  113.      * The initial values for the station offset model ({@link #getEastOffsetDriver()},
  114.      * {@link #getNorthOffsetDriver()}, {@link #getZenithOffsetDriver()}) are set to 0.
  115.      * This implies that as long as these values are not changed, the offset frame is
  116.      * the same as the {@link #getBaseFrame() base frame}. As soon as some of these models
  117.      * are changed, the offset frame moves away from the {@link #getBaseFrame() base frame}.
  118.      * </p>
  119.      * @param baseFrame base frame associated with the station, without *any* parametric
  120.      * model (no station offset, no polar motion, no meridian shift)
  121.      * @exception OrekitException if some frame transforms cannot be computed
  122.      * or if Earth Orientation Parameters cannot be retrieved from the base frame.
  123.      * @see #GroundStation(TopocentricFrame, EOPHistory, StationDisplacement...)
  124.      */
  125.     public GroundStation(final TopocentricFrame baseFrame)
  126.         throws OrekitException {
  127.         this(baseFrame, FramesFactory.findEOP(baseFrame), new StationDisplacement[0]);
  128.     }

  129.     /** Simple constructor.
  130.      * <p>
  131.      * The initial values for the pole and prime meridian parametric linear models
  132.      * ({@link #getPrimeMeridianOffsetDriver()}, {@link #getPrimeMeridianDriftDriver()},
  133.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()},
  134.      * {@link #getPolarOffsetXDriver()}, {@link #getPolarDriftXDriver()}) are set to 0.
  135.      * The initial values for the station offset model ({@link #getEastOffsetDriver()},
  136.      * {@link #getNorthOffsetDriver()}, {@link #getZenithOffsetDriver()}) are set to 0.
  137.      * This implies that as long as these values are not changed, the offset frame is
  138.      * the same as the {@link #getBaseFrame() base frame}. As soon as some of these models
  139.      * are changed, the offset frame moves away from the {@link #getBaseFrame() base frame}.
  140.      * </p>
  141.      * @param baseFrame base frame associated with the station, without *any* parametric
  142.      * model (no station offset, no polar motion, no meridian shift)
  143.      * @param eopHistory EOP history associated with Earth frames
  144.      * @param displacements ground station displacement model (tides, ocean loading,
  145.      * atmospheric loading, thermal effects...)
  146.      * @exception OrekitException if some frame transforms cannot be computed
  147.      * or if the ground station is not defined on a {@link OneAxisEllipsoid ellipsoid}.
  148.      * @since 9.1
  149.      */
  150.     public GroundStation(final TopocentricFrame baseFrame, final EOPHistory eopHistory,
  151.                          final StationDisplacement... displacements)
  152.         throws OrekitException {

  153.         this.baseFrame = baseFrame;

  154.         if (eopHistory == null) {
  155.             throw new OrekitException(OrekitMessages.NO_EARTH_ORIENTATION_PARAMETERS);
  156.         }

  157.         final UT1Scale baseUT1 = TimeScalesFactory.getUT1(eopHistory);
  158.         this.estimatedEarthFrameProvider = new EstimatedEarthFrameProvider(baseUT1);
  159.         this.estimatedEarthFrame = new Frame(baseFrame.getParent(), estimatedEarthFrameProvider,
  160.                                              baseFrame.getParent() + "-estimated");

  161.         if (displacements.length == 0) {
  162.             arguments = null;
  163.         } else {
  164.             arguments = eopHistory.getConventions().getNutationArguments(estimatedEarthFrameProvider.getEstimatedUT1());
  165.         }

  166.         this.displacements = displacements.clone();

  167.         this.eastOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-East",
  168.                                                     0.0, OFFSET_SCALE,
  169.                                                     Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  170.         this.northOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-North",
  171.                                                      0.0, OFFSET_SCALE,
  172.                                                      Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  173.         this.zenithOffsetDriver = new ParameterDriver(baseFrame.getName() + OFFSET_SUFFIX + "-Zenith",
  174.                                                       0.0, OFFSET_SCALE,
  175.                                                       Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);

  176.     }

  177.     /** Get the displacement models.
  178.      * @return displacement models (empty if no model has been set up)
  179.      * @since 9.1
  180.      */
  181.     public StationDisplacement[] getDisplacements() {
  182.         return displacements.clone();
  183.     }

  184.     /** Get a driver allowing to change station position along East axis.
  185.      * @return driver for station position offset along East axis
  186.      */
  187.     public ParameterDriver getEastOffsetDriver() {
  188.         return eastOffsetDriver;
  189.     }

  190.     /** Get a driver allowing to change station position along North axis.
  191.      * @return driver for station position offset along North axis
  192.      */
  193.     public ParameterDriver getNorthOffsetDriver() {
  194.         return northOffsetDriver;
  195.     }

  196.     /** Get a driver allowing to change station position along Zenith axis.
  197.      * @return driver for station position offset along Zenith axis
  198.      */
  199.     public ParameterDriver getZenithOffsetDriver() {
  200.         return zenithOffsetDriver;
  201.     }

  202.     /** Get a driver allowing to add a prime meridian rotation.
  203.      * <p>
  204.      * The parameter is an angle in radians. In order to convert this
  205.      * value to a DUT1 in seconds, the value must be divided by
  206.      * {@code ave = 7.292115146706979e-5} (which is the nominal Angular Velocity
  207.      * of Earth from the TIRF model).
  208.      * </p>
  209.      * @return driver for prime meridian rotation
  210.      */
  211.     public ParameterDriver getPrimeMeridianOffsetDriver() {
  212.         return estimatedEarthFrameProvider.getPrimeMeridianOffsetDriver();
  213.     }

  214.     /** Get a driver allowing to add a prime meridian rotation rate.
  215.      * <p>
  216.      * The parameter is an angle rate in radians per second. In order to convert this
  217.      * value to a LOD in seconds, the value must be multiplied by -86400 and divided by
  218.      * {@code ave = 7.292115146706979e-5} (which is the nominal Angular Velocity
  219.      * of Earth from the TIRF model).
  220.      * </p>
  221.      * @return driver for prime meridian rotation rate
  222.      */
  223.     public ParameterDriver getPrimeMeridianDriftDriver() {
  224.         return estimatedEarthFrameProvider.getPrimeMeridianDriftDriver();
  225.     }

  226.     /** Get a driver allowing to add a polar offset along X.
  227.      * <p>
  228.      * The parameter is an angle in radians
  229.      * </p>
  230.      * @return driver for polar offset along X
  231.      */
  232.     public ParameterDriver getPolarOffsetXDriver() {
  233.         return estimatedEarthFrameProvider.getPolarOffsetXDriver();
  234.     }

  235.     /** Get a driver allowing to add a polar drift along X.
  236.      * <p>
  237.      * The parameter is an angle rate in radians per second
  238.      * </p>
  239.      * @return driver for polar drift along X
  240.      */
  241.     public ParameterDriver getPolarDriftXDriver() {
  242.         return estimatedEarthFrameProvider.getPolarDriftXDriver();
  243.     }

  244.     /** Get a driver allowing to add a polar offset along Y.
  245.      * <p>
  246.      * The parameter is an angle in radians
  247.      * </p>
  248.      * @return driver for polar offset along Y
  249.      */
  250.     public ParameterDriver getPolarOffsetYDriver() {
  251.         return estimatedEarthFrameProvider.getPolarOffsetYDriver();
  252.     }

  253.     /** Get a driver allowing to add a polar drift along Y.
  254.      * <p>
  255.      * The parameter is an angle rate in radians per second
  256.      * </p>
  257.      * @return driver for polar drift along Y
  258.      */
  259.     public ParameterDriver getPolarDriftYDriver() {
  260.         return estimatedEarthFrameProvider.getPolarDriftYDriver();
  261.     }

  262.     /** Get the base frame associated with the station.
  263.      * <p>
  264.      * The base frame corresponds to a null position offset, null
  265.      * polar motion, null meridian shift
  266.      * </p>
  267.      * @return base frame associated with the station
  268.      */
  269.     public TopocentricFrame getBaseFrame() {
  270.         return baseFrame;
  271.     }

  272.     /** Get the estimated Earth frame, including the estimated linear models for pole and prime meridian.
  273.      * <p>
  274.      * This frame is bound to the {@link #getPrimeMeridianOffsetDriver() driver for prime meridian offset},
  275.      * {@link #getPrimeMeridianDriftDriver() driver prime meridian drift},
  276.      * {@link #getPolarOffsetXDriver() driver for polar offset along X},
  277.      * {@link #getPolarDriftXDriver() driver for polar drift along X},
  278.      * {@link #getPolarOffsetYDriver() driver for polar offset along Y},
  279.      * {@link #getPolarDriftYDriver() driver for polar drift along Y}, so its orientation changes when
  280.      * the {@link ParameterDriver#setValue(double) setValue} methods of the drivers are called.
  281.      * </p>
  282.      * @return estimated Earth frame
  283.      * @since 9.1
  284.      */
  285.     public Frame getEstimatedEarthFrame() {
  286.         return estimatedEarthFrame;
  287.     }

  288.     /** Get the estimated UT1 scale, including the estimated linear models for prime meridian.
  289.      * <p>
  290.      * This time scale is bound to the {@link #getPrimeMeridianOffsetDriver() driver for prime meridian offset},
  291.      * and {@link #getPrimeMeridianDriftDriver() driver prime meridian drift}, so its offset from UTC changes when
  292.      * the {@link ParameterDriver#setValue(double) setValue} methods of the drivers are called.
  293.      * </p>
  294.      * @return estimated Earth frame
  295.      * @since 9.1
  296.      */
  297.     public UT1Scale getEstimatedUT1() {
  298.         return estimatedEarthFrameProvider.getEstimatedUT1();
  299.     }

  300.     /** Get the geodetic point at the center of the offset frame.
  301.      * @return geodetic point at the center of the offset frame
  302.      * @exception OrekitException if frames transforms cannot be computed
  303.      * @deprecated as of 9.1, replaced by {@link #getOffsetGeodeticPoint(AbsoluteDate)}
  304.      */
  305.     @Deprecated
  306.     public GeodeticPoint getOffsetGeodeticPoint()
  307.         throws OrekitException {
  308.         return getOffsetGeodeticPoint(null);
  309.     }

  310.     /** Get the station displacement.
  311.      * @param date current date
  312.      * @param position raw position of the station in Earth frame
  313.      * before displacement is applied
  314.      * @return station displacement
  315.      * @exception OrekitException if displacement cannot be computed
  316.      * @since 9.1
  317.      */
  318.     private Vector3D computeDisplacement(final AbsoluteDate date, final Vector3D position)
  319.         throws OrekitException {
  320.         try {
  321.             Vector3D displacement = Vector3D.ZERO;
  322.             if (arguments != null) {
  323.                 final BodiesElements elements = arguments.evaluateAll(date);
  324.                 for (final StationDisplacement sd : displacements) {
  325.                     // we consider all displacements apply to the same initial position,
  326.                     // i.e. they apply simultaneously, not according to some order
  327.                     displacement = displacement.add(sd.displacement(elements, estimatedEarthFrame, position));
  328.                 }
  329.             }
  330.             return displacement;
  331.         } catch (OrekitExceptionWrapper oew) {
  332.             throw oew.getException();
  333.         }
  334.     }

  335.     /** Get the geodetic point at the center of the offset frame.
  336.      * @param date current date (may be null if displacements are ignored)
  337.      * @return geodetic point at the center of the offset frame
  338.      * @exception OrekitException if frames transforms cannot be computed
  339.      * @since 9.1
  340.      */
  341.     public GeodeticPoint getOffsetGeodeticPoint(final AbsoluteDate date)
  342.         throws OrekitException {

  343.         // take station offset into account
  344.         final double    x          = parametricModel(eastOffsetDriver);
  345.         final double    y          = parametricModel(northOffsetDriver);
  346.         final double    z          = parametricModel(zenithOffsetDriver);
  347.         final BodyShape baseShape  = baseFrame.getParentShape();
  348.         final Transform baseToBody = baseFrame.getTransformTo(baseShape.getBodyFrame(), date);
  349.         Vector3D        origin     = baseToBody.transformPosition(new Vector3D(x, y, z));

  350.         if (date != null) {
  351.             origin = origin.add(computeDisplacement(date, origin));
  352.         }

  353.         return baseShape.transform(origin, baseShape.getBodyFrame(), null);

  354.     }

  355.     /** Get the transform between offset frame and inertial frame.
  356.      * <p>
  357.      * The offset frame takes the <em>current</em> position offset,
  358.      * polar motion and the meridian shift into account. The frame
  359.      * returned is disconnected from later changes in the parameters.
  360.      * When the {@link ParameterDriver parameters} managing these
  361.      * offsets are changed, the method must be called again to retrieve
  362.      * a new offset frame.
  363.      * </p>
  364.      * @param inertial inertial frame to transform to
  365.      * @param date date of the transform
  366.      * @return offset frame defining vectors
  367.      * @exception OrekitException if offset frame cannot be computed for current offset values
  368.      */
  369.     public Transform getOffsetToInertial(final Frame inertial, final AbsoluteDate date)
  370.         throws OrekitException {

  371.         // take Earth offsets into account
  372.         final Transform intermediateToBody = estimatedEarthFrameProvider.getTransform(date).getInverse();

  373.         // take station offset into account
  374.         final double    x          = parametricModel(eastOffsetDriver);
  375.         final double    y          = parametricModel(northOffsetDriver);
  376.         final double    z          = parametricModel(zenithOffsetDriver);
  377.         final BodyShape baseShape  = baseFrame.getParentShape();
  378.         final Transform baseToBody = baseFrame.getTransformTo(baseShape.getBodyFrame(), date);
  379.         Vector3D        origin     = baseToBody.transformPosition(new Vector3D(x, y, z));
  380.         origin = origin.add(computeDisplacement(date, origin));

  381.         final GeodeticPoint originGP = baseShape.transform(origin, baseShape.getBodyFrame(), date);
  382.         final Transform offsetToIntermediate =
  383.                         new Transform(date,
  384.                                       new Transform(date,
  385.                                                     new Rotation(Vector3D.PLUS_I, Vector3D.PLUS_K,
  386.                                                                  originGP.getEast(), originGP.getZenith()),
  387.                                                     Vector3D.ZERO),
  388.                                       new Transform(date, origin));

  389.         // combine all transforms together
  390.         final Transform bodyToInert        = baseFrame.getParent().getTransformTo(inertial, date);

  391.         return new Transform(date, offsetToIntermediate, new Transform(date, intermediateToBody, bodyToInert));

  392.     }

  393.     /** Get the transform between offset frame and inertial frame with derivatives.
  394.      * <p>
  395.      * As the East and North vector are not well defined at pole, the derivatives
  396.      * of these two vectors diverge to infinity as we get closer to the pole.
  397.      * So this method should not be used for stations less than 0.0001 degree from
  398.      * either poles.
  399.      * </p>
  400.      * @param inertial inertial frame to transform to
  401.      * @param date date of the transform
  402.      * @param factory factory for the derivatives
  403.      * @param indices indices of the estimated parameters in derivatives computations
  404.      * @return offset frame defining vectors with derivatives
  405.      * @exception OrekitException if some frame transforms cannot be computed
  406.      * @since 9.0
  407.      */
  408.     public FieldTransform<DerivativeStructure> getOffsetToInertial(final Frame inertial,
  409.                                                                    final FieldAbsoluteDate<DerivativeStructure> date,
  410.                                                                    final DSFactory factory,
  411.                                                                    final Map<String, Integer> indices)
  412.         throws OrekitException {

  413.         final Field<DerivativeStructure>         field = date.getField();
  414.         final FieldVector3D<DerivativeStructure> zero  = FieldVector3D.getZero(field);
  415.         final FieldVector3D<DerivativeStructure> plusI = FieldVector3D.getPlusI(field);
  416.         final FieldVector3D<DerivativeStructure> plusK = FieldVector3D.getPlusK(field);

  417.         // take Earth offsets into account
  418.         final FieldTransform<DerivativeStructure> intermediateToBody =
  419.                         estimatedEarthFrameProvider.getTransform(date, factory, indices).getInverse();

  420.         // take station offset into account
  421.         final DerivativeStructure  x          = parametricModel(factory, eastOffsetDriver,   indices);
  422.         final DerivativeStructure  y          = parametricModel(factory, northOffsetDriver,  indices);
  423.         final DerivativeStructure  z          = parametricModel(factory, zenithOffsetDriver, indices);
  424.         final BodyShape            baseShape  = baseFrame.getParentShape();
  425.         final Transform            baseToBody = baseFrame.getTransformTo(baseShape.getBodyFrame(), (AbsoluteDate) null);

  426.         FieldVector3D<DerivativeStructure>            origin   = baseToBody.transformPosition(new FieldVector3D<>(x, y, z));
  427.         origin = origin.add(computeDisplacement(date.toAbsoluteDate(), origin.toVector3D()));
  428.         final FieldGeodeticPoint<DerivativeStructure> originGP = baseShape.transform(origin, baseShape.getBodyFrame(), date);
  429.         final FieldTransform<DerivativeStructure> offsetToIntermediate =
  430.                         new FieldTransform<>(date,
  431.                                              new FieldTransform<>(date,
  432.                                                                   new FieldRotation<>(plusI, plusK,
  433.                                                                                       originGP.getEast(), originGP.getZenith()),
  434.                                                                   zero),
  435.                                              new FieldTransform<>(date, origin));

  436.         // combine all transforms together
  437.         final FieldTransform<DerivativeStructure> bodyToInert        = baseFrame.getParent().getTransformTo(inertial, date);

  438.         return new FieldTransform<>(date,
  439.                                     offsetToIntermediate,
  440.                                     new FieldTransform<>(date, intermediateToBody, bodyToInert));

  441.     }

  442.     /** Evaluate a parametric model.
  443.      * @param driver driver managing the parameter
  444.      * @return value of the parametric model
  445.      */
  446.     private double parametricModel(final ParameterDriver driver) {
  447.         return driver.getValue();
  448.     }

  449.     /** Evaluate a parametric model.
  450.      * @param factory factory for the derivatives
  451.      * @param driver driver managing the parameter
  452.      * @param indices indices of the estimated parameters in derivatives computations
  453.      * @return value of the parametric model
  454.      */
  455.     private DerivativeStructure parametricModel(final DSFactory factory, final ParameterDriver driver,
  456.                                                 final Map<String, Integer> indices) {
  457.         final Integer index = indices.get(driver.getName());
  458.         return (index == null) ?
  459.              factory.constant(driver.getValue()) :
  460.              factory.variable(index, driver.getValue());
  461.     }

  462. }