ParameterDriver.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.utils;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.Map;

  23. import org.hipparchus.analysis.differentiation.Gradient;
  24. import org.hipparchus.util.FastMath;
  25. import org.hipparchus.util.Precision;
  26. import org.orekit.errors.OrekitException;
  27. import org.orekit.errors.OrekitIllegalStateException;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.propagation.events.ParameterDrivenDateIntervalDetector;
  30. import org.orekit.time.AbsoluteDate;
  31. import org.orekit.utils.TimeSpanMap.Span;
  32. import org.orekit.utils.TimeSpanMap.Transition;


  33. /** Class allowing to drive the value of a parameter.
  34.  * <p>
  35.  * This class is typically used as a bridge between an estimation
  36.  * algorithm (typically orbit determination or optimizer) and an
  37.  * internal parameter in a physical model that needs to be tuned,
  38.  * or a bridge between a finite differences algorithm and an
  39.  * internal parameter in a physical model that needs to be slightly
  40.  * offset. The physical model will expose to the algorithm a
  41.  * set of instances of this class so the algorithm can call the
  42.  * {@link #setValue(double, AbsoluteDate)} method to update the
  43.  * parameter value at a given date. Some parameters driver only have 1 value estimated/driven
  44.  * over the all period (constructor by default). Some others have several
  45.  * values estimated/driven on several periods/intervals. For example if the time period is 3 days
  46.  * for a drag parameter estimated all days then 3 values would be estimated, one for
  47.  * each time period. In order to allow several values to be estimated, the PDriver has
  48.  * a name and a value {@link TimeSpanMap} as attribute. In order,
  49.  * to cut the time span map there are 2 options :
  50.  * </p>
  51.  * <ul>
  52.  * <li>Passive cut calling the {@link #addSpans(AbsoluteDate, AbsoluteDate, double)} method.
  53.  * Given a start date, an end date and and a validity period (in sec)
  54.  * for the driver, the {@link #addSpans} method will cut the interval of name and value time span map
  55.  * from start date to date end in several interval of validity period duration. This method should not
  56.  * be called on orbital drivers and must be called only once at beginning of the process (for example
  57.  * beginning of orbit determination). <b>WARNING : In order to ensure converge for orbit determination,
  58.  * the start, end date and driver periodicity must be wisely choosen </b>. There must be enough measurements
  59.  * on each interval or convergence won't reach or singular matrixes will appear.  </li>
  60.  * <li> Active cut calling the {@link #addSpanAtDate(AbsoluteDate)} method.
  61.  * Given a date, the method will cut the value and name time span name, in order to have a new span starting at
  62.  * the given date. Can be called several time to cut the time map as wished. <b>WARNING : In order to ensure
  63.  * converge for orbit determination, if the method is called several time, the start date must be wisely choosen </b>.
  64.  * There must be enough measurements on each interval or convergence won't reach or singular matrixes will appear.  </li>
  65.  * </ul>
  66.  * <p>
  67.  * Several ways exist in order to get a ParameterDriver value at a certain
  68.  * date for parameters having several values on several intervals.
  69.  * </p>
  70.  * <ul>
  71.  * <li>First of all, the step estimation, that is to say, if a value wants
  72.  * to be known at a certain date, the value returned is the one of span
  73.  * beginning corresponding to the date. With this definition a value
  74.  * will be kept all along the span duration and will be the value of the span
  75.  * start.</li>
  76.  * <li> The continuous estimation, that is to say, when a value wants be to
  77.  * known at a date t, the value returned would be a linear interpolation between
  78.  * the value at the beginning of the span corresponding to date t and end this span
  79.  * (which is also the beginning of next span). NOT IMPLEMENTED FOR NOW
  80.  * </li>
  81.  * </ul>
  82.  * Each time the value is set, the physical model
  83.  * will be notified as it will register a {@link ParameterObserver
  84.  * ParameterObserver} for this purpose.
  85.  * <p>
  86.  * This design has two major goals. First, it allows an external
  87.  * algorithm to drive internal parameters almost anonymously, as it only
  88.  * needs to get a list of instances of this class, without knowing
  89.  * what they really drive. Second, it allows the physical model to
  90.  * not expose directly setters methods for its parameters. In order
  91.  * to be able to modify the parameter value, the algorithm
  92.  * <em>must</em> retrieve a parameter driver.
  93.  * </p>
  94.  * @see ParameterObserver
  95.  * @author Luc Maisonobe
  96.  * @author Melina Vanel
  97.  * @since 8.0
  98.  */
  99. public class ParameterDriver {

  100.     /** Name of the parameter.*/
  101.     public static final String SPAN = "Span";

  102.     /** Name of the parameter. */
  103.     private String name;

  104.     /** TimeSpan for period names.
  105.      * @since 12.0
  106.      */
  107.     private TimeSpanMap<String> nameSpanMap;

  108.     /** Reference value. */
  109.     private double referenceValue;

  110.     /** Scaling factor. */
  111.     private double scale;

  112.     /** Minimum value. */
  113.     private double minValue;

  114.     /** Maximum value. */
  115.     private double maxValue;

  116.     /** Reference date.
  117.      * @since 9.0
  118.      */
  119.     private AbsoluteDate referenceDate;

  120.     /** Flag to choose estimation method. If estimationContinuous
  121.      * is true then when a value wants to be known an interpolation
  122.      * is performed between given date span start and end (start of
  123.      * next span) otherwise the value returned is the value of span start
  124.      * @since 12.0
  125.      */
  126.     private boolean isEstimationContinuous;

  127.     /** Value time span map.
  128.      * @since 12.0
  129.      */
  130.     private TimeSpanMap<Double> valueSpanMap;

  131.     /** Selection status.
  132.      * <p>
  133.      * Selection is used for estimated parameters in orbit determination,
  134.      * or to compute the Jacobian matrix in partial derivatives computation.
  135.      * </p>
  136.      */
  137.     private boolean selected;

  138.     /** Observers observing this driver. */
  139.     private final List<ParameterObserver> observers;

  140.     /** Create a new instance from another parameterDriver informations
  141.      * for example (useful for {@link ParameterDriversList.DelegatingDriver}))
  142.      * At construction, the parameter new is configured as <em>not</em> selected,
  143.      * the reference date is set to {@code null}. validityPeriod, namesSpanMap and
  144.      * valueSpanMap.
  145.      * @param name general name of the parameter
  146.      * @param namesSpanMap name time span map. WARNING, number of Span must be coherent with
  147.      * validityPeriod and valueSpanMap (same number of Span with same transitions
  148.      * dates)
  149.      * @param valuesSpanMap values time span map
  150.      * @param referenceValue reference value of the parameter
  151.      * @param scale scaling factor to convert the parameters value to
  152.      * non-dimensional (typically set to the expected standard deviation of the
  153.      * parameter), it must be non-zero
  154.      * @param minValue minimum value allowed
  155.      * @param maxValue maximum value allowed
  156.      * @since 12.0
  157.      */
  158.     public ParameterDriver(final String name, final TimeSpanMap<String> namesSpanMap,
  159.                            final TimeSpanMap<Double> valuesSpanMap, final double referenceValue,
  160.                            final double scale, final double minValue, final double maxValue) {
  161.         if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
  162.             throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
  163.                                       name, scale);
  164.         }
  165.         this.name                   = name;
  166.         this.nameSpanMap            = namesSpanMap;
  167.         this.referenceValue         = referenceValue;
  168.         this.scale                  = scale;
  169.         this.minValue               = minValue;
  170.         this.maxValue               = maxValue;
  171.         this.referenceDate          = null;
  172.         this.valueSpanMap           = valuesSpanMap;
  173.         this.selected               = false;
  174.         this.observers              = new ArrayList<>();
  175.         this.isEstimationContinuous = false;
  176.     }

  177.     /** Simple constructor.
  178.      * <p>
  179.      * At construction, the parameter is configured as <em>not</em> selected,
  180.      * the reference date is set to {@code null}, the value is set to the
  181.      * {@code referenceValue}, the validity period is set to 0 so by default
  182.      * the parameterDriver will be estimated on only 1 interval from -INF to
  183.      * +INF. To change the validity period the
  184.      * {@link ParameterDriver#addSpans(AbsoluteDate, AbsoluteDate, double)}
  185.      * method must be called.
  186.      * </p>
  187.      * @param name name of the parameter
  188.      * @param referenceValue reference value of the parameter
  189.      * @param scale scaling factor to convert the parameters value to
  190.      * non-dimensional (typically set to the expected standard deviation of the
  191.      * parameter), it must be non-zero
  192.      * @param minValue minimum value allowed
  193.      * @param maxValue maximum value allowed
  194.      */
  195.     public ParameterDriver(final String name,
  196.                            final double referenceValue, final double scale,
  197.                            final double minValue, final double maxValue) {
  198.         if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
  199.             throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
  200.                                       name, scale);
  201.         }
  202.         this.name                   = name;
  203.         this.nameSpanMap            = new TimeSpanMap<>(SPAN + name + Integer.toString(0));
  204.         this.referenceValue         = referenceValue;
  205.         this.scale                  = scale;
  206.         this.minValue               = minValue;
  207.         this.maxValue               = maxValue;
  208.         this.referenceDate          = null;
  209.         // at construction the parameter driver
  210.         // will be consider with only 1 estimated value over the all orbit
  211.         // determination
  212.         this.valueSpanMap           = new TimeSpanMap<>(referenceValue);
  213.         this.selected               = false;
  214.         this.observers              = new ArrayList<>();
  215.         this.isEstimationContinuous = false;
  216.     }

  217.     /** Get current name span map of the parameterDriver, cut in interval
  218.      * in accordance with value span map and validity period.
  219.      * @return current name span map
  220.      * @since 12.0
  221.      */
  222.     public TimeSpanMap<String> getNamesSpanMap() {
  223.         return nameSpanMap;
  224.     }

  225.     /** Get value time span map for parameterDriver.
  226.      * @return value time span map
  227.      * @since 12.0
  228.      */
  229.     public TimeSpanMap<Double> getValueSpanMap() {
  230.         return valueSpanMap;
  231.     }

  232.     /** Set current parameter value span map to match another driver. In order to keep
  233.      * consistency, the validity period and name span map are updated.
  234.      * @param driver for which the value span map wants to be copied for the
  235.      * current driver
  236.      * @since 12.0
  237.      */
  238.     public void setValueSpanMap(final ParameterDriver driver) {
  239.         final TimeSpanMap<Double> previousValueSpanMap = driver.getValueSpanMap();
  240.         valueSpanMap   = driver.getValueSpanMap();
  241.         nameSpanMap    = driver.getNamesSpanMap();
  242.         for (final ParameterObserver observer : observers) {
  243.             observer.valueSpanMapChanged(previousValueSpanMap, this);
  244.         }
  245.     }

  246.     /** Get the number of values to estimate that is to say the number.
  247.      * of Span present in valueSpanMap
  248.      * @return int the number of values to estimate
  249.      * @since 12.0
  250.      */
  251.     public int getNbOfValues() {
  252.         return valueSpanMap.getSpansNumber();
  253.     }

  254.     /** Get the dates of the transitions for the drag sensitive models {@link TimeSpanMap}.
  255.      * @return dates of the transitions for the drag sensitive models {@link TimeSpanMap}
  256.      * @since 12.0
  257.      */
  258.     public AbsoluteDate[] getTransitionDates() {

  259.         // Get all transitions
  260.         final List<AbsoluteDate> listDates = new ArrayList<>();

  261.         // Extract all the transitions' dates
  262.         for (Transition<Double> transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) {
  263.             listDates.add(transition.getDate());
  264.         }
  265.         // Return the array of transition dates
  266.         return listDates.toArray(new AbsoluteDate[0]);
  267.     }

  268.     /** Get all values of the valueSpanMap in the chronological order.
  269.      * @return double[] containing values of the valueSpanMap in the chronological order
  270.      */
  271.     public double[] getValues() {
  272.         final double[] chronologicalValues = new double[getNbOfValues()];
  273.         Span<Double> currentSpan = valueSpanMap.getFirstSpan();
  274.         for (int i = 0; i < getNbOfValues() - 1; i++) {
  275.             chronologicalValues[i] = currentSpan.getData();
  276.             currentSpan = currentSpan.next();
  277.         }
  278.         chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData();
  279.         return chronologicalValues;
  280.     }


  281.     /** Add an observer for this driver.
  282.      * <p>
  283.      * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver)
  284.      * valueSpanMapChanged} method is called once automatically when the
  285.      * observer is added, and then called at each value change.
  286.      * </p>
  287.      * @param observer observer to add
  288.           * while being updated
  289.      */
  290.     public void addObserver(final ParameterObserver observer) {
  291.         observers.add(observer);
  292.         observer.valueSpanMapChanged(getValueSpanMap(), this);
  293.     }

  294.     /** Remove an observer.
  295.      * @param observer observer to remove
  296.      * @since 9.1
  297.      */
  298.     public void removeObserver(final ParameterObserver observer) {
  299.         for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
  300.             if (iterator.next() == observer) {
  301.                 iterator.remove();
  302.                 return;
  303.             }
  304.         }
  305.     }

  306.     /** Replace an observer.
  307.      * @param oldObserver observer to replace
  308.      * @param newObserver new observer to use
  309.      * @since 10.1
  310.      */
  311.     public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
  312.         for (int i = 0; i < observers.size(); ++i) {
  313.             if (observers.get(i) == oldObserver) {
  314.                 observers.set(i, newObserver);
  315.             }
  316.         }
  317.     }

  318.     /** Get the observers for this driver.
  319.      * @return an unmodifiable view of the observers for this driver
  320.      * @since 9.1
  321.      */
  322.     public List<ParameterObserver> getObservers() {
  323.         return Collections.unmodifiableList(observers);
  324.     }

  325.     /** Get parameter driver general name.
  326.      * @return name
  327.      */
  328.     public String getName() {
  329.         return name;
  330.     }

  331.     /** Get name of the parameter span for a specific date.
  332.      * @param date date at which the name of the span wants to be known
  333.      * @return name data of the name time span map at date
  334.      */
  335.     public String getNameSpan(final AbsoluteDate date) {
  336.         return nameSpanMap.get(date);
  337.     }

  338.     /** Change the general name of this parameter driver.
  339.      * @param name new name
  340.      */
  341.     public void setName(final String name) {
  342.         final String previousName = this.name;
  343.         this.name = name;
  344.         for (final ParameterObserver observer : observers) {
  345.             observer.nameChanged(previousName, this);
  346.         }
  347.         // the names time span map must also be updated with the new name
  348.         if (nameSpanMap.getSpansNumber() > 1) {
  349.             Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
  350.             nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false);
  351.             for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) {
  352.                 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
  353.                 nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false);
  354.             }
  355.         } else {
  356.             nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0));
  357.         }
  358.     }

  359.     /** Cut values and names time span map given orbit determination start and end and driver
  360.      * periodicity.
  361.      * <p>
  362.      * For example for a drag coefficient the validity period would be
  363.      * 1 days = 86400sec. To be called after constructor to cut the temporal axis with
  364.      * the wanted parameter driver temporality for estimations on the wanted interval.
  365.      * </p>
  366.      * <p>
  367.      * Must be called only once at the beginning of orbit
  368.      * determination for example. If called several times, will throw exception. If parameter
  369.      * estimations intervals must be changed then a new ParameterDriver must be created or the
  370.      * function {@link #addSpanAtDate} should be used.
  371.      * </p>
  372.      * <p>
  373.      * This function should not be called on {@link DateDriver} and
  374.      * any of {@link ParameterDrivenDateIntervalDetector} attribute, because there is no sense to
  375.      * estimate several values for dateDriver.
  376.      * </p>
  377.      * <p>
  378.      * The choice of {@code orbitDeterminationStartDate}, {@code orbitDeterminationEndDate} and
  379.      * {@code validityPeriodForDriver} in a case of orbit determination must be done carefully,
  380.      * indeed, enough measurement should be available for each time interval or
  381.      * the orbit determination won't converge.
  382.      * </p>
  383.      * @param orbitDeterminationStartDate start date for which the parameter driver
  384.      * starts to be estimated.
  385.      * @param orbitDeterminationEndDate end date for which the parameter driver
  386.      * stops to be estimated.
  387.      * @param validityPeriodForDriver validity period for which the parameter value
  388.      * is effective (for example 1 day for drag coefficient). Warning, validityPeriod
  389.      * should not be too short or the orbit determination won't converge.
  390.      * @since 12.0
  391.      */
  392.     public void addSpans(final AbsoluteDate orbitDeterminationStartDate,
  393.                          final AbsoluteDate orbitDeterminationEndDate,
  394.                          final double validityPeriodForDriver) {

  395.         // by convention 0 is when the parameter needs to be drived only on 1
  396.         // interval from -INF to +INF time period
  397.         if (getNbOfValues() != 1) {
  398.             // throw exception if called several time, must be called only once at the beginning of orbit
  399.             // determination, if the periods wants to be changed a new parameter must be created
  400.             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name);
  401.         } else {

  402.             int spanNumber = 1;
  403.             AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver);
  404.             //splitting the names and values span map accordingly with start and end of orbit determination
  405.             //and validity period. A security is added to avoid having to few measurements point for a span
  406.             //in order to assure orbit determination convergence
  407.             while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) {
  408.                 valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false);
  409.                 nameSpanMap.addValidAfter(SPAN + getName() + Integer.toString(spanNumber++), currentDate, false);
  410.                 currentDate = currentDate.shiftedBy(validityPeriodForDriver);
  411.             }
  412.         }
  413.     }

  414.     /** Create a new span in values and names time span map given a start date.
  415.      * <b> One must be aware of the importance of choosing wise dates if this function is called
  416.      * several times to create several span at wanted times. Indeed, if orbit determination is performed
  417.      * it might not converge or find singular matrix if the spans are too short and contains to few measurements.
  418.      * Must be called before any computation (for example before
  419.      * orbit determination).</b>
  420.      * @param spanStartDate wanted start date for parameter value interval
  421.      * starts to be estimated.
  422.      * @since 12.0
  423.      */
  424.     public void addSpanAtDate(final AbsoluteDate spanStartDate) {

  425.         // Split value span map with new interval having for start date spanStartDate and end
  426.         // date next span start date of +INF if no span is present after
  427.         valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false);
  428.         nameSpanMap.addValidAfter(name, spanStartDate, false);
  429.         // Rename spans recursively
  430.         Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
  431.         nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false);

  432.         for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) {
  433.             currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
  434.             nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false);
  435.         }
  436.     }

  437.     /** Get reference parameter value.
  438.      * @return reference parameter value
  439.      */
  440.     public double getReferenceValue() {
  441.         return referenceValue;
  442.     }

  443.     /** Set reference parameter value.
  444.      * @since 9.3
  445.      * @param referenceValue the reference value to set.
  446.      */
  447.     public void setReferenceValue(final double referenceValue) {
  448.         final double previousReferenceValue = this.referenceValue;
  449.         this.referenceValue = referenceValue;
  450.         for (final ParameterObserver observer : observers) {
  451.             observer.referenceValueChanged(previousReferenceValue, this);
  452.         }
  453.     }

  454.     /** Get minimum parameter value.
  455.      * @return minimum parameter value
  456.      */
  457.     public double getMinValue() {
  458.         return minValue;
  459.     }

  460.     /** Set minimum parameter value.
  461.      * @since 9.3
  462.      * @param minValue the minimum value to set.
  463.      */
  464.     public void setMinValue(final double minValue) {
  465.         final double previousMinValue = this.minValue;
  466.         this.minValue = minValue;
  467.         for (final ParameterObserver observer : observers) {
  468.             observer.minValueChanged(previousMinValue, this);
  469.         }
  470.         // Check if all values are still not out of min/max range
  471.         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
  472.             setValue(getValue(span.getStart()), span.getStart());
  473.         }
  474.     }

  475.     /** Get maximum parameter value.
  476.      * @return maximum parameter value
  477.      */
  478.     public double getMaxValue() {
  479.         return maxValue;
  480.     }

  481.     /** Set maximum parameter value.
  482.      * @since 9.3
  483.      * @param maxValue the maximum value to set.
  484.      */
  485.     public void setMaxValue(final double maxValue) {
  486.         final double previousMaxValue = this.maxValue;
  487.         this.maxValue = maxValue;
  488.         for (final ParameterObserver observer : observers) {
  489.             observer.maxValueChanged(previousMaxValue, this);
  490.         }
  491.         // Check if all values are still not out of min/max range
  492.         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
  493.             setValue(getValue(span.getStart()), span.getStart());
  494.         }
  495.     }

  496.     /** Get scale.
  497.      * @return scale
  498.      */
  499.     public double getScale() {
  500.         return scale;
  501.     }

  502.     /** Set scale.
  503.      * @since 9.3
  504.      * @param scale the scale to set.
  505.      */
  506.     public void setScale(final double scale) {
  507.         final double previousScale = this.scale;
  508.         this.scale = scale;
  509.         for (final ParameterObserver observer : observers) {
  510.             observer.scaleChanged(previousScale, this);
  511.         }
  512.     }

  513.     /** Get normalized value at specific date.
  514.      * <p>
  515.      * The normalized value is a non-dimensional value
  516.      * suitable for use as part of a vector in an optimization
  517.      * process. It is computed as {@code (current - reference)/scale}.
  518.      * </p>
  519.      * @param date date for which the normalized value wants to be known
  520.      * @return normalized value
  521.      */
  522.     public double getNormalizedValue(final AbsoluteDate date) {
  523.         return (getValue(date) - getReferenceValue()) / scale;
  524.     }

  525.     /** Get normalized value. Only useable on ParameterDriver
  526.      * which have only 1 span on their TimeSpanMap value (that is
  527.      * to say for which the setPeriod method wasn't called) otherwise
  528.      * it will throw an exception.
  529.      * <p>
  530.      * The normalized value is a non-dimensional value
  531.      * suitable for use as part of a vector in an optimization
  532.      * process. It is computed as {@code (current - reference)/scale}.
  533.      * </p>
  534.      * @return normalized value
  535.      */
  536.     public double getNormalizedValue() {
  537.         return (getValue() - getReferenceValue()) / scale;
  538.     }

  539.     /** Set normalized value at specific date.
  540.      * <p>
  541.      * The normalized value is a non-dimensional value
  542.      * suitable for use as part of a vector in an optimization
  543.      * process. It is computed as {@code (current - reference)/scale}.
  544.      * </p>
  545.      * @param date date for which the normalized value wants to be set
  546.      * @param normalized value
  547.      */
  548.     public void setNormalizedValue(final double normalized, final AbsoluteDate date) {
  549.         setValue(getReferenceValue() + scale * normalized, date);
  550.     }

  551.     /** Set normalized value at specific date. Only useable on ParameterDriver
  552.      * which have only 1 span on their TimeSpanMap value (that is
  553.      * to say for which the setPeriod method wasn't called) otherwise
  554.      * it will throw an exception.
  555.      * <p>
  556.      * The normalized value is a non-dimensional value
  557.      * suitable for use as part of a vector in an optimization
  558.      * process. It is computed as {@code (current - reference)/scale}.
  559.      * </p>
  560.      * @param normalized value
  561.      */
  562.     public void setNormalizedValue(final double normalized) {
  563.         setValue(getReferenceValue() + scale * normalized);
  564.     }

  565.     /** Get current reference date.
  566.      * @return current reference date (null if it was never set)
  567.      * @since 9.0
  568.      */
  569.     public AbsoluteDate getReferenceDate() {
  570.         return referenceDate;
  571.     }

  572.     /** Set reference date.
  573.      * @param newReferenceDate new reference date
  574.      * @since 9.0
  575.      */
  576.     public void setReferenceDate(final AbsoluteDate newReferenceDate) {
  577.         final AbsoluteDate previousReferenceDate = getReferenceDate();
  578.         referenceDate = newReferenceDate;
  579.         for (final ParameterObserver observer : observers) {
  580.             observer.referenceDateChanged(previousReferenceDate, this);
  581.         }
  582.     }

  583.     /** Get current parameter value. Only usable on ParameterDriver
  584.      * which have only 1 span on their TimeSpanMap value (that is
  585.      * to say for which the setPeriod method wasn't called)
  586.      * @return current parameter value
  587.      */
  588.     public double getValue() {
  589.         if (getNbOfValues() > 1) {
  590.             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)");
  591.         }
  592.         // Attention voir si qlqchose est retournĂ© si une exception est levĂ©e
  593.         return valueSpanMap.getFirstSpan().getData();
  594.     }

  595.     /** Get current parameter value at specific date, depending on isContinuousEstimation
  596.      * value, the value returned will be obtained by step estimation or continuous estimation.
  597.      * @param date date for which the value wants to be known. Only if
  598.      * parameter driver has 1 value estimated over the all orbit determination
  599.      * period (not validity period intervals for estimation), the date value can
  600.      * be <em>{@code null}</em> and then the only estimated value will be
  601.      * returned, in this case the date can also be whatever the value returned would
  602.      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
  603.      * @return current parameter value at date date, or for the all period if
  604.      * no validity period (= 1 value estimated over the all orbit determination
  605.      * period)
  606.      */
  607.     public double getValue(final AbsoluteDate date) {
  608.         return isEstimationContinuous ? getValueContinuousEstimation(date) : getValueStepEstimation(date);
  609.     }

  610.     /** Get current parameter value at specific date with step estimation.
  611.      * @param date date for which the value wants to be known. Only if
  612.      * parameter driver has 1 value estimated over the all orbit determination
  613.      * period (not validity period intervals for estimation), the date value can
  614.      * be <em>{@code null}</em> and then the only estimated value will be
  615.      * returned, in this case the date can also be whatever the value returned would
  616.      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
  617.      * @return current parameter value at date date, or for the all period if
  618.      * no validity period (= 1 value estimated over the all orbit determination
  619.      * period)
  620.      */
  621.     public double getValueStepEstimation(final AbsoluteDate date) {
  622.         return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date);
  623.     }

  624.     /** Get current parameter value at specific date with continuous estimation.
  625.      * @param date date for which the value wants to be known. Only if
  626.      * parameter driver has 1 value estimated over the all orbit determination
  627.      * period (not validity period intervals for estimation), the date value can
  628.      * be <em>{@code null}</em> and then the only estimated value will be
  629.      * returned, in this case the date can also be whatever the value returned would
  630.      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
  631.      * @return current parameter value at date date, or for the all period if
  632.      * no validity period (= 1 value estimated over the all orbit determination
  633.      * period)
  634.      * @since 12.0
  635.      */
  636.     public double getValueContinuousEstimation(final AbsoluteDate date) {
  637.         //TODO
  638.         throw new UnsupportedOperationException();
  639.     }

  640.     /** Get the value as a gradient at special date.
  641.      * @param freeParameters total number of free parameters in the gradient
  642.      * @param indices indices of the differentiation parameters in derivatives computations
  643.      * @return value with derivatives, will throw exception if called on a PDriver having
  644.      * several values driven
  645.      * @since 10.2
  646.      */
  647.     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
  648.         Integer index = null;
  649.         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  650.             index = indices.get(span.getData());
  651.             if (index != null) {
  652.                 break;
  653.             }
  654.         }
  655.         return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue());
  656.     }

  657.     /** Get the value as a gradient at special date.
  658.      * @param freeParameters total number of free parameters in the gradient
  659.      * @param indices indices of the differentiation parameters in derivatives computations,
  660.      * must be span name and not driver name
  661.      * @param date date for which the value wants to be known. Only if
  662.      * parameter driver has 1 value estimated over the all orbit determination
  663.      * period (not validity period intervals for estimation), the date value can
  664.      * be <em>{@code null}</em> and then the only estimated value will be
  665.      * returned
  666.      * @return value with derivatives
  667.      * @since 10.2
  668.      */
  669.     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices, final AbsoluteDate date) {
  670.         Integer index = null;
  671.         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  672.             index = indices.get(span.getData());
  673.             if (index != null) {
  674.                 break;
  675.             }
  676.         }
  677.         return (index == null) ? Gradient.constant(freeParameters, getValue(date)) : Gradient.variable(freeParameters, index, getValue(date));
  678.     }

  679.     /** Set parameter value at specific date.
  680.      * <p>
  681.      * If {@code newValue} is below {@link #getMinValue()}, it will
  682.      * be silently set to {@link #getMinValue()}. If {@code newValue} is
  683.      * above {@link #getMaxValue()}, it will be silently set to {@link
  684.      * #getMaxValue()}.
  685.      * </p>
  686.      * @param date date for which the value wants to be set. Only if
  687.      * parameter driver has 1 value estimated over the all orbit determination
  688.      * period (not validity period intervals for estimation), the date value can
  689.      * be <em>{@code null}</em>
  690.      * @param newValue new value to set
  691.      */
  692.     public void setValue(final double newValue, final AbsoluteDate date) {

  693.         double previousValue = Double.NaN;
  694.         AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;

  695.         // if valid for infinity (only 1 value estimation for the orbit determination )
  696.         if (getNbOfValues() == 1) {
  697.             previousValue = this.getValue(referenceDateSpan);
  698.             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
  699.         // if needs to be estimated per time range / validity period

  700.         // if several value intervals
  701.         } else {
  702.             final Span<Double> valueSpan = valueSpanMap.getSpan(date);
  703.             previousValue = valueSpan.getData();
  704.             referenceDateSpan = valueSpan.getStart();
  705.             // if the Span considered is from past infinity to valueSpanEndDate it is
  706.             // impossible to addValidAfter past infinity because it is creating a new span that
  707.             // is why the below trick was set up
  708.             if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) {
  709.                 referenceDateSpan = valueSpan.getEnd();
  710.                 this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
  711.                                                  referenceDateSpan, false);
  712.             } else {
  713.                 this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
  714.                                                 referenceDateSpan, false);
  715.             }
  716.         }

  717.         for (final ParameterObserver observer : observers) {
  718.             observer.valueChanged(previousValue, this, date);
  719.         }
  720.     }


  721.     /** Set parameter value. Only usable on ParameterDriver
  722.      * which have only 1 span on their TimeSpanMap value (that is
  723.      * to say for which the setPeriod method wasn't called)
  724.      * <p>
  725.      * If {@code newValue} is below {@link #getMinValue()}, it will
  726.      * be silently set to {@link #getMinValue()}. If {@code newValue} is
  727.      * above {@link #getMaxValue()}, it will be silently set to {@link
  728.      * #getMaxValue()}.
  729.      * </p>
  730.      * @param newValue new value to set
  731.      */
  732.     public void setValue(final double newValue) {
  733.         if (getNbOfValues() == 1) {
  734.             final AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;
  735.             final double previousValue = this.getValue(referenceDateSpan);
  736.             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
  737.             for (final ParameterObserver observer : observers) {
  738.                 observer.valueChanged(previousValue, this, referenceDateSpan);
  739.             }
  740.         } else {
  741.             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "setValue(date)");
  742.         }
  743.     }

  744.     /** Configure a parameter selection status.
  745.      * <p>
  746.      * Selection is used for estimated parameters in orbit determination,
  747.      * or to compute the Jacobian matrix in partial derivatives computation.
  748.      * </p>
  749.      * @param selected if true the parameter is selected,
  750.      * otherwise it will be fixed
  751.      */
  752.     public void setSelected(final boolean selected) {
  753.         final boolean previousSelection = isSelected();
  754.         this.selected = selected;
  755.         for (final ParameterObserver observer : observers) {
  756.             observer.selectionChanged(previousSelection, this);
  757.         }
  758.     }

  759.     /** Check if parameter is selected.
  760.      * <p>
  761.      * Selection is used for estimated parameters in orbit determination,
  762.      * or to compute the Jacobian matrix in partial derivatives computation.
  763.      * </p>
  764.      * @return true if parameter is selected, false if it is not
  765.      */
  766.     public boolean isSelected() {
  767.         return selected;
  768.     }

  769.     /** Set parameter estimation to continuous, by default step estimation.
  770.      * <p> Continuous estimation : when a value wants to be known at date
  771.      * t, the value returned will be an interpolation between start value
  772.      * of the span corresponding to date t and end value (which corresponds
  773.      * to the start of the next span).
  774.      * </p>
  775.      * <p> Step estimation : when a value wants to be
  776.      * known at date t, the value returned will be the value of the beginning
  777.      * of span corresponding to date t, step estimation.
  778.      * </p>
  779.      * @param continuous if true the parameter will be estimated
  780.      * with continuous estimation, if false with step estimation.
  781.      */
  782.     public void setContinuousEstimation(final boolean continuous) {
  783.         final boolean previousEstimation = isContinuousEstimation();
  784.         this.isEstimationContinuous = continuous;
  785.         for (final ParameterObserver observer : observers) {
  786.             observer.estimationTypeChanged(previousEstimation, this);
  787.         }
  788.     }

  789.     /** Check if parameter estimation is continuous, that is to say when
  790.      * a value wants to be known at date t, the value returned
  791.      * will be an interpolation between start value on span corresponding
  792.      * for date t and end value (which corresponds to the start of the next
  793.      * span), continuous estimation. Or not continuous, that is to say when a value wants to be
  794.      * known at date t, the value returned will be the value of the start
  795.      * of span corresponding to date t, step estimation.
  796.      * @return true if continuous estimation/definition, false if step estimation/definition
  797.      * @since 12.0
  798.      */
  799.     public boolean isContinuousEstimation() {
  800.         return isEstimationContinuous;
  801.     }

  802.     /** Get a text representation of the parameter.
  803.      * @return text representation of the parameter, in the form name = value.
  804.      */
  805.     public String toString() {
  806.         return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH);
  807.     }

  808. }