ParameterDriver.java

  1. /* Copyright 2002-2025 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 convergence for orbit determination,
  58.  * the start, end date and driver periodicity must be wisely chosen </b>. There must be enough measurements
  59.  * on each interval or convergence won't reach or singular matrices 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.  * convergence for orbit determination, if the method is called several time, the start date must be wisely chosen </b>.
  64.  * There must be enough measurements on each interval or convergence won't reach or singular matrices 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 constant all along the span duration and will be the value at 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 blindly, 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 + 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.      * <p>
  220.      * Note that if the expunge policy of the names map is
  221.      * {@link TimeSpanMap#configureExpunge(int, double, ExpungePolicy) reconfigured},
  222.      * then the expunge policy of the {@link #getValueSpanMap() values map} should
  223.      * be reconfigured too with the same settings.
  224.      * </p>
  225.      * @return current name span map
  226.      * @since 12.0
  227.      */
  228.     public TimeSpanMap<String> getNamesSpanMap() {
  229.         return nameSpanMap;
  230.     }

  231.     /** Get value time span map for parameterDriver.
  232.      * <p>
  233.      * Note that if the expunge policy of the values map is
  234.      * {@link TimeSpanMap#configureExpunge(int, double, ExpungePolicy) reconfigured},
  235.      * then the expunge policy of the {@link #getNamesSpanMap()} names map} should
  236.      * be reconfigured too with the same settings.
  237.      * </p>
  238.      * @return value time span map
  239.      * @since 12.0
  240.      */
  241.     public TimeSpanMap<Double> getValueSpanMap() {
  242.         return valueSpanMap;
  243.     }

  244.     /** Set current parameter value span map to match another driver. In order to keep
  245.      * consistency, the validity period and name span map are updated.
  246.      * @param driver for which the value span map wants to be copied for the
  247.      * current driver
  248.      * @since 12.0
  249.      */
  250.     public void setValueSpanMap(final ParameterDriver driver) {
  251.         final TimeSpanMap<Double> previousValueSpanMap = driver.getValueSpanMap();
  252.         valueSpanMap   = driver.getValueSpanMap();
  253.         nameSpanMap    = driver.getNamesSpanMap();
  254.         for (final ParameterObserver observer : observers) {
  255.             observer.valueSpanMapChanged(previousValueSpanMap, this);
  256.         }
  257.     }

  258.     /** Get the number of values to estimate that is to say the number.
  259.      * of Span present in valueSpanMap
  260.      * @return int the number of values to estimate
  261.      * @since 12.0
  262.      */
  263.     public int getNbOfValues() {
  264.         return valueSpanMap.getSpansNumber();
  265.     }

  266.     /** Get the dates of the transitions {@link TimeSpanMap}.
  267.      * @return dates of the transitions {@link TimeSpanMap}
  268.      * @since 12.0
  269.      */
  270.     public AbsoluteDate[] getTransitionDates() {

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

  273.         // Extract all the transitions' dates
  274.         for (Transition<Double> transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) {
  275.             listDates.add(transition.getDate());
  276.         }
  277.         // Return the array of transition dates
  278.         return listDates.toArray(new AbsoluteDate[0]);
  279.     }

  280.     /** Get all values of the valueSpanMap in the chronological order.
  281.      * @return double[] containing values of the valueSpanMap in the chronological order
  282.      */
  283.     public double[] getValues() {
  284.         final double[] chronologicalValues = new double[getNbOfValues()];
  285.         Span<Double> currentSpan = valueSpanMap.getFirstSpan();
  286.         for (int i = 0; i < getNbOfValues() - 1; i++) {
  287.             chronologicalValues[i] = currentSpan.getData();
  288.             currentSpan = currentSpan.next();
  289.         }
  290.         chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData();
  291.         return chronologicalValues;
  292.     }


  293.     /** Add an observer for this driver.
  294.      * <p>
  295.      * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver)
  296.      * valueSpanMapChanged} method is called once automatically when the
  297.      * observer is added, and then called at each value change.
  298.      * </p>
  299.      * @param observer observer to add
  300.           * while being updated
  301.      */
  302.     public void addObserver(final ParameterObserver observer) {
  303.         observers.add(observer);
  304.         observer.valueSpanMapChanged(getValueSpanMap(), this);
  305.     }

  306.     /** Remove an observer.
  307.      * @param observer observer to remove
  308.      * @since 9.1
  309.      */
  310.     public void removeObserver(final ParameterObserver observer) {
  311.         for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
  312.             if (iterator.next() == observer) {
  313.                 iterator.remove();
  314.                 return;
  315.             }
  316.         }
  317.     }

  318.     /** Replace an observer.
  319.      * @param oldObserver observer to replace
  320.      * @param newObserver new observer to use
  321.      * @since 10.1
  322.      */
  323.     public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
  324.         for (int i = 0; i < observers.size(); ++i) {
  325.             if (observers.get(i) == oldObserver) {
  326.                 observers.set(i, newObserver);
  327.             }
  328.         }
  329.     }

  330.     /** Get the observers for this driver.
  331.      * @return an unmodifiable view of the observers for this driver
  332.      * @since 9.1
  333.      */
  334.     public List<ParameterObserver> getObservers() {
  335.         return Collections.unmodifiableList(observers);
  336.     }

  337.     /** Get parameter driver general name.
  338.      * @return name
  339.      */
  340.     public String getName() {
  341.         return name;
  342.     }

  343.     /** Get name of the parameter span for a specific date.
  344.      * @param date date at which the name of the span wants to be known
  345.      * @return name data of the name time span map at date
  346.      */
  347.     public String getNameSpan(final AbsoluteDate date) {
  348.         return nameSpanMap.get(date);
  349.     }

  350.     /** Change the general name of this parameter driver.
  351.      * @param name new name
  352.      */
  353.     public void setName(final String name) {
  354.         final String previousName = this.name;
  355.         this.name = name;
  356.         for (final ParameterObserver observer : observers) {
  357.             observer.nameChanged(previousName, this);
  358.         }
  359.         // the names time span map must also be updated with the new name
  360.         if (nameSpanMap.getSpansNumber() > 1) {
  361.             Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
  362.             nameSpanMap.addValidBefore(SPAN + name + 0, currentNameSpan.getEnd(), false);
  363.             for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) {
  364.                 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
  365.                 nameSpanMap.addValidAfter(SPAN + name + spanNumber, currentNameSpan.getStart(), false);
  366.             }
  367.         } else {
  368.             nameSpanMap = new TimeSpanMap<>(SPAN + name + 0);
  369.         }
  370.     }

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

  407.         // by convention 0 is when the parameter needs to be drived only on 1
  408.         // interval from -INF to +INF time period
  409.         if (getNbOfValues() != 1) {
  410.             // throw exception if called several time, must be called only once at the beginning of orbit
  411.             // determination, if the periods wants to be changed a new parameter must be created
  412.             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name);
  413.         } else {

  414.             int spanNumber = 1;
  415.             AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver);
  416.             //splitting the names and values span map accordingly with start and end of orbit determination
  417.             //and validity period. A security is added to avoid having to few measurements point for a span
  418.             //in order to assure orbit determination convergence
  419.             while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) {
  420.                 valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false);
  421.                 nameSpanMap.addValidAfter(SPAN + getName() + spanNumber++, currentDate, false);
  422.                 currentDate = currentDate.shiftedBy(validityPeriodForDriver);
  423.             }
  424.         }
  425.     }

  426.     /** Create a new span in values and names time span map given a start date.
  427.      * <b> One must be aware of the importance of choosing wise dates if this function is called
  428.      * several times to create several span at wanted times. Indeed, if orbit determination is performed
  429.      * it might not converge or find singular matrix if the spans are too short and contains to few measurements.
  430.      * Must be called before any computation (for example before
  431.      * orbit determination).</b>
  432.      * @param spanStartDate wanted start date for parameter value interval
  433.      * starts to be estimated.
  434.      * @since 12.0
  435.      */
  436.     public void addSpanAtDate(final AbsoluteDate spanStartDate) {

  437.         // Split value span map with new interval having for start date spanStartDate and end
  438.         // date next span start date of +INF if no span is present after
  439.         valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false);
  440.         nameSpanMap.addValidAfter(name, spanStartDate, false);
  441.         // Rename spans recursively
  442.         Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
  443.         nameSpanMap.addValidBefore(SPAN + name + 0, currentNameSpan.getEnd(), false);

  444.         for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) {
  445.             currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
  446.             nameSpanMap.addValidAfter(SPAN + name + spanNumber, currentNameSpan.getStart(), false);
  447.         }
  448.     }

  449.     /** Get reference parameter value.
  450.      * @return reference parameter value
  451.      */
  452.     public double getReferenceValue() {
  453.         return referenceValue;
  454.     }

  455.     /** Set reference parameter value.
  456.      * @since 9.3
  457.      * @param referenceValue the reference value to set.
  458.      */
  459.     public void setReferenceValue(final double referenceValue) {
  460.         final double previousReferenceValue = this.referenceValue;
  461.         this.referenceValue = referenceValue;
  462.         for (final ParameterObserver observer : observers) {
  463.             observer.referenceValueChanged(previousReferenceValue, this);
  464.         }
  465.     }

  466.     /** Get minimum parameter value.
  467.      * @return minimum parameter value
  468.      */
  469.     public double getMinValue() {
  470.         return minValue;
  471.     }

  472.     /** Set minimum parameter value.
  473.      * @since 9.3
  474.      * @param minValue the minimum value to set.
  475.      */
  476.     public void setMinValue(final double minValue) {
  477.         final double previousMinValue = this.minValue;
  478.         this.minValue = minValue;
  479.         for (final ParameterObserver observer : observers) {
  480.             observer.minValueChanged(previousMinValue, this);
  481.         }
  482.         // Check if all values are still not out of min/max range
  483.         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
  484.             setValue(getValue(span.getStart()), span.getStart());
  485.         }
  486.     }

  487.     /** Get maximum parameter value.
  488.      * @return maximum parameter value
  489.      */
  490.     public double getMaxValue() {
  491.         return maxValue;
  492.     }

  493.     /** Set maximum parameter value.
  494.      * @since 9.3
  495.      * @param maxValue the maximum value to set.
  496.      */
  497.     public void setMaxValue(final double maxValue) {
  498.         final double previousMaxValue = this.maxValue;
  499.         this.maxValue = maxValue;
  500.         for (final ParameterObserver observer : observers) {
  501.             observer.maxValueChanged(previousMaxValue, this);
  502.         }
  503.         // Check if all values are still not out of min/max range
  504.         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
  505.             setValue(getValue(span.getStart()), span.getStart());
  506.         }
  507.     }

  508.     /** Get scale.
  509.      * @return scale
  510.      */
  511.     public double getScale() {
  512.         return scale;
  513.     }

  514.     /** Set scale.
  515.      * @since 9.3
  516.      * @param scale the scale to set.
  517.      */
  518.     public void setScale(final double scale) {
  519.         final double previousScale = this.scale;
  520.         this.scale = scale;
  521.         for (final ParameterObserver observer : observers) {
  522.             observer.scaleChanged(previousScale, this);
  523.         }
  524.     }

  525.     /** Get normalized value at specific date.
  526.      * <p>
  527.      * The normalized value is a non-dimensional value
  528.      * suitable for use as part of a vector in an optimization
  529.      * process. It is computed as {@code (current - reference)/scale}.
  530.      * </p>
  531.      * @param date date for which the normalized value wants to be known
  532.      * @return normalized value
  533.      */
  534.     public double getNormalizedValue(final AbsoluteDate date) {
  535.         return (getValue(date) - getReferenceValue()) / scale;
  536.     }

  537.     /** Get normalized value. Only useable on ParameterDriver
  538.      * which have only 1 span on their TimeSpanMap value (that is
  539.      * to say for which the setPeriod method wasn't called) otherwise
  540.      * it will throw an exception.
  541.      * <p>
  542.      * The normalized value is a non-dimensional value
  543.      * suitable for use as part of a vector in an optimization
  544.      * process. It is computed as {@code (current - reference)/scale}.
  545.      * </p>
  546.      * @return normalized value
  547.      */
  548.     public double getNormalizedValue() {
  549.         return (getValue() - getReferenceValue()) / scale;
  550.     }

  551.     /** Set normalized value at specific date.
  552.      * <p>
  553.      * The normalized value is a non-dimensional value
  554.      * suitable for use as part of a vector in an optimization
  555.      * process. It is computed as {@code (current - reference)/scale}.
  556.      * </p>
  557.      * @param date date for which the normalized value wants to be set
  558.      * @param normalized value
  559.      */
  560.     public void setNormalizedValue(final double normalized, final AbsoluteDate date) {
  561.         setValue(getReferenceValue() + scale * normalized, date);
  562.     }

  563.     /** Set normalized value at specific date. Only useable on ParameterDriver
  564.      * which have only 1 span on their TimeSpanMap value (that is
  565.      * to say for which the setPeriod method wasn't called) otherwise
  566.      * it will throw an exception.
  567.      * <p>
  568.      * The normalized value is a non-dimensional value
  569.      * suitable for use as part of a vector in an optimization
  570.      * process. It is computed as {@code (current - reference)/scale}.
  571.      * </p>
  572.      * @param normalized value
  573.      */
  574.     public void setNormalizedValue(final double normalized) {
  575.         setValue(getReferenceValue() + scale * normalized);
  576.     }

  577.     /** Get current reference date.
  578.      * @return current reference date (null if it was never set)
  579.      * @since 9.0
  580.      */
  581.     public AbsoluteDate getReferenceDate() {
  582.         return referenceDate;
  583.     }

  584.     /** Set reference date.
  585.      * @param newReferenceDate new reference date
  586.      * @since 9.0
  587.      */
  588.     public void setReferenceDate(final AbsoluteDate newReferenceDate) {
  589.         final AbsoluteDate previousReferenceDate = getReferenceDate();
  590.         referenceDate = newReferenceDate;
  591.         for (final ParameterObserver observer : observers) {
  592.             observer.referenceDateChanged(previousReferenceDate, this);
  593.         }
  594.     }

  595.     /** Get current parameter value. Only usable on ParameterDriver
  596.      * which have only 1 span on their TimeSpanMap value (that is
  597.      * to say for which the setPeriod method wasn't called)
  598.      * @return current parameter value
  599.      */
  600.     public double getValue() {
  601.         if (getNbOfValues() > 1) {
  602.             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)");
  603.         }
  604.         // Attention voir si qlqchose est retournĂ© si une exception est levĂ©e
  605.         return valueSpanMap.getFirstSpan().getData();
  606.     }

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

  622.     /** Get current parameter value at specific date with step estimation.
  623.      * @param date date for which the value wants to be known. Only if
  624.      * parameter driver has 1 value estimated over the all orbit determination
  625.      * period (not validity period intervals for estimation), the date value can
  626.      * be <em>{@code null}</em> and then the only estimated value will be
  627.      * returned, in this case the date can also be whatever the value returned would
  628.      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
  629.      * @return current parameter value at date date, or for the all period if
  630.      * no validity period (= 1 value estimated over the all orbit determination
  631.      * period)
  632.      */
  633.     public double getValueStepEstimation(final AbsoluteDate date) {
  634.         return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date);
  635.     }

  636.     /** Get current parameter value at specific date with continuous estimation.
  637.      * @param date date for which the value wants to be known. Only if
  638.      * parameter driver has 1 value estimated over the all orbit determination
  639.      * period (not validity period intervals for estimation), the date value can
  640.      * be <em>{@code null}</em> and then the only estimated value will be
  641.      * returned, in this case the date can also be whatever the value returned would
  642.      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
  643.      * @return current parameter value at date date, or for the all period if
  644.      * no validity period (= 1 value estimated over the all orbit determination
  645.      * period)
  646.      * @since 12.0
  647.      */
  648.     public double getValueContinuousEstimation(final AbsoluteDate date) {
  649.         //TODO
  650.         throw new UnsupportedOperationException();
  651.     }

  652.     /** Get the value as a gradient at special date.
  653.      * @param freeParameters total number of free parameters in the gradient
  654.      * @param indices indices of the differentiation parameters in derivatives computations
  655.      * @return value with derivatives, will throw exception if called on a PDriver having
  656.      * several values driven
  657.      * @since 10.2
  658.      */
  659.     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
  660.         Integer index = null;
  661.         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
  662.             index = indices.get(span.getData());
  663.             if (index != null) {
  664.                 break;
  665.             }
  666.         }
  667.         return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue());
  668.     }

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

  691.     /** Set parameter value at specific date.
  692.      * <p>
  693.      * If {@code newValue} is below {@link #getMinValue()}, it will
  694.      * be silently set to {@link #getMinValue()}. If {@code newValue} is
  695.      * above {@link #getMaxValue()}, it will be silently set to {@link
  696.      * #getMaxValue()}.
  697.      * </p>
  698.      * @param date date for which the value wants to be set. Only if
  699.      * parameter driver has 1 value estimated over the all orbit determination
  700.      * period (not validity period intervals for estimation), the date value can
  701.      * be <em>{@code null}</em>
  702.      * @param newValue new value to set
  703.      */
  704.     public void setValue(final double newValue, final AbsoluteDate date) {

  705.         double previousValue = Double.NaN;
  706.         AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;

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

  712.         // if several value intervals
  713.         } else {
  714.             final Span<Double> valueSpan = valueSpanMap.getSpan(date);
  715.             previousValue = valueSpan.getData();
  716.             referenceDateSpan = valueSpan.getStart();
  717.             // if the Span considered is from past infinity to valueSpanEndDate it is
  718.             // impossible to addValidAfter past infinity because it is creating a new span that
  719.             // is why the below trick was set up
  720.             if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) {
  721.                 referenceDateSpan = valueSpan.getEnd();
  722.                 this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
  723.                                                  referenceDateSpan, false);
  724.             } else {
  725.                 this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
  726.                                                 referenceDateSpan, false);
  727.             }
  728.         }

  729.         for (final ParameterObserver observer : observers) {
  730.             observer.valueChanged(previousValue, this, date);
  731.         }
  732.     }


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

  756.     /** Configure a parameter selection status.
  757.      * <p>
  758.      * Selection is used for estimated parameters in orbit determination,
  759.      * or to compute the Jacobian matrix in partial derivatives computation.
  760.      * </p>
  761.      * @param selected if true the parameter is selected,
  762.      * otherwise it will be fixed
  763.      */
  764.     public void setSelected(final boolean selected) {
  765.         final boolean previousSelection = isSelected();
  766.         this.selected = selected;
  767.         for (final ParameterObserver observer : observers) {
  768.             observer.selectionChanged(previousSelection, this);
  769.         }
  770.     }

  771.     /** Check if parameter is selected.
  772.      * <p>
  773.      * Selection is used for estimated parameters in orbit determination,
  774.      * or to compute the Jacobian matrix in partial derivatives computation.
  775.      * </p>
  776.      * @return true if parameter is selected, false if it is not
  777.      */
  778.     public boolean isSelected() {
  779.         return selected;
  780.     }

  781.     /** Set parameter estimation to continuous, by default step estimation.
  782.      * <p> Continuous estimation : when a value wants to be known at date
  783.      * t, the value returned will be an interpolation between start value
  784.      * of the span corresponding to date t and end value (which corresponds
  785.      * to the start of the next span).
  786.      * </p>
  787.      * <p> Step estimation : when a value wants to be
  788.      * known at date t, the value returned will be the value of the beginning
  789.      * of span corresponding to date t, step estimation.
  790.      * </p>
  791.      * @param continuous if true the parameter will be estimated
  792.      * with continuous estimation, if false with step estimation.
  793.      */
  794.     public void setContinuousEstimation(final boolean continuous) {
  795.         final boolean previousEstimation = isContinuousEstimation();
  796.         this.isEstimationContinuous = continuous;
  797.         for (final ParameterObserver observer : observers) {
  798.             observer.estimationTypeChanged(previousEstimation, this);
  799.         }
  800.     }

  801.     /** Check if parameter estimation is continuous, that is to say when
  802.      * a value wants to be known at date t, the value returned
  803.      * will be an interpolation between start value on span corresponding
  804.      * for date t and end value (which corresponds to the start of the next
  805.      * span), continuous estimation. Or not continuous, that is to say when a value wants to be
  806.      * known at date t, the value returned will be the value of the start
  807.      * of span corresponding to date t, step estimation.
  808.      * @return true if continuous estimation/definition, false if step estimation/definition
  809.      * @since 12.0
  810.      */
  811.     public boolean isContinuousEstimation() {
  812.         return isEstimationContinuous;
  813.     }

  814.     /** Get a text representation of the parameter.
  815.      * @return text representation of the parameter, in the form name = value.
  816.      */
  817.     public String toString() {
  818.         return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH);
  819.     }

  820. }