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  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.hipparchus.analysis.differentiation.Gradient;
26  import org.hipparchus.util.FastMath;
27  import org.hipparchus.util.Precision;
28  import org.orekit.errors.OrekitException;
29  import org.orekit.errors.OrekitIllegalStateException;
30  import org.orekit.errors.OrekitMessages;
31  import org.orekit.propagation.events.ParameterDrivenDateIntervalDetector;
32  import org.orekit.time.AbsoluteDate;
33  import org.orekit.utils.TimeSpanMap.Span;
34  import org.orekit.utils.TimeSpanMap.Transition;
35  
36  
37  /** Class allowing to drive the value of a parameter.
38   * <p>
39   * This class is typically used as a bridge between an estimation
40   * algorithm (typically orbit determination or optimizer) and an
41   * internal parameter in a physical model that needs to be tuned,
42   * or a bridge between a finite differences algorithm and an
43   * internal parameter in a physical model that needs to be slightly
44   * offset. The physical model will expose to the algorithm a
45   * set of instances of this class so the algorithm can call the
46   * {@link #setValue(double, AbsoluteDate)} method to update the
47   * parameter value at a given date. Some parameters driver only have 1 value estimated/driven
48   * over the all period (constructor by default). Some others have several
49   * values estimated/driven on several periods/intervals. For example if the time period is 3 days
50   * for a drag parameter estimated all days then 3 values would be estimated, one for
51   * each time period. In order to allow several values to be estimated, the PDriver has
52   * a name and a value {@link TimeSpanMap} as attribute. In order,
53   * to cut the time span map there are 2 options :
54   * </p>
55   * <ul>
56   * <li>Passive cut calling the {@link #addSpans(AbsoluteDate, AbsoluteDate, double)} method.
57   * Given a start date, an end date and and a validity period (in sec)
58   * for the driver, the {@link #addSpans} method will cut the interval of name and value time span map
59   * from start date to date end in several interval of validity period duration. This method should not
60   * be called on orbital drivers and must be called only once at beginning of the process (for example
61   * beginning of orbit determination). <b>WARNING : In order to ensure converge for orbit determination,
62   * the start, end date and driver periodicity must be wisely choosen </b>. There must be enough measurements
63   * on each interval or convergence won't reach or singular matrixes will appear.  </li>
64   * <li> Active cut calling the {@link #addSpanAtDate(AbsoluteDate)} method.
65   * Given a date, the method will cut the value and name time span name, in order to have a new span starting at
66   * the given date. Can be called several time to cut the time map as wished. <b>WARNING : In order to ensure
67   * converge for orbit determination, if the method is called several time, the start date must be wisely choosen </b>.
68   * There must be enough measurements on each interval or convergence won't reach or singular matrixes will appear.  </li>
69   * </ul>
70   * <p>
71   * Several ways exist in order to get a ParameterDriver value at a certain
72   * date for parameters having several values on several intervals.
73   * </p>
74   * <ul>
75   * <li>First of all, the step estimation, that is to say, if a value wants
76   * to be known at a certain date, the value returned is the one of span
77   * beginning corresponding to the date. With this definition a value
78   * will be kept all along the span duration and will be the value of the span
79   * start.</li>
80   * <li> The continuous estimation, that is to say, when a value wants be to
81   * known at a date t, the value returned would be a linear interpolation between
82   * the value at the beginning of the span corresponding to date t and end this span
83   * (which is also the beginning of next span). NOT IMPLEMENTED FOR NOW
84   * </li>
85   * </ul>
86   * Each time the value is set, the physical model
87   * will be notified as it will register a {@link ParameterObserver
88   * ParameterObserver} for this purpose.
89   * <p>
90   * This design has two major goals. First, it allows an external
91   * algorithm to drive internal parameters almost anonymously, as it only
92   * needs to get a list of instances of this class, without knowing
93   * what they really drive. Second, it allows the physical model to
94   * not expose directly setters methods for its parameters. In order
95   * to be able to modify the parameter value, the algorithm
96   * <em>must</em> retrieve a parameter driver.
97   * </p>
98   * @see ParameterObserver
99   * @author Luc Maisonobe
100  * @author Melina Vanel
101  * @since 8.0
102  */
103 public class ParameterDriver {
104 
105     /** Name of the parameter.*/
106     public static final String SPAN = "Span";
107 
108     /** Name of the parameter. */
109     private String name;
110 
111     /** TimeSpan for period names.
112      * @since 12.0
113      */
114     private TimeSpanMap<String> nameSpanMap;
115 
116     /** Reference value. */
117     private double referenceValue;
118 
119     /** Scaling factor. */
120     private double scale;
121 
122     /** Minimum value. */
123     private double minValue;
124 
125     /** Maximum value. */
126     private double maxValue;
127 
128     /** Reference date.
129      * @since 9.0
130      */
131     private AbsoluteDate referenceDate;
132 
133     /** Flag to choose estimation method. If estimationContinuous
134      * is true then when a value wants to be known an interpolation
135      * is performed between given date span start and end (start of
136      * next span) otherwise the value returned is the value of span start
137      * @since 12.0
138      */
139     private boolean isEstimationContinuous;
140 
141     /** Value time span map.
142      * @since 12.0
143      */
144     private TimeSpanMap<Double> valueSpanMap;
145 
146     /** Selection status.
147      * <p>
148      * Selection is used for estimated parameters in orbit determination,
149      * or to compute the Jacobian matrix in partial derivatives computation.
150      * </p>
151      */
152     private boolean selected;
153 
154     /** Observers observing this driver. */
155     private final List<ParameterObserver> observers;
156 
157     /** Create a new instance from another parameterDriver informations
158      * for example (useful for {@link ParameterDriversList.DelegatingDriver}))
159      * At construction, the parameter new is configured as <em>not</em> selected,
160      * the reference date is set to {@code null}. validityPeriod, namesSpanMap and
161      * valueSpanMap.
162      * @param name general name of the parameter
163      * @param namesSpanMap name time span map. WARNING, number of Span must be coherent with
164      * validityPeriod and valueSpanMap (same number of Span with same transitions
165      * dates)
166      * @param valuesSpanMap values time span map
167      * @param referenceValue reference value of the parameter
168      * @param scale scaling factor to convert the parameters value to
169      * non-dimensional (typically set to the expected standard deviation of the
170      * parameter), it must be non-zero
171      * @param minValue minimum value allowed
172      * @param maxValue maximum value allowed
173      * @since 12.0
174      */
175     public ParameterDriver(final String name, final TimeSpanMap<String> namesSpanMap,
176                            final TimeSpanMap<Double> valuesSpanMap, final double referenceValue,
177                            final double scale, final double minValue, final double maxValue) {
178         if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
179             throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
180                                       name, scale);
181         }
182         this.name                   = name;
183         this.nameSpanMap            = namesSpanMap;
184         this.referenceValue         = referenceValue;
185         this.scale                  = scale;
186         this.minValue               = minValue;
187         this.maxValue               = maxValue;
188         this.referenceDate          = null;
189         this.valueSpanMap           = valuesSpanMap;
190         this.selected               = false;
191         this.observers              = new ArrayList<>();
192         this.isEstimationContinuous = false;
193     }
194 
195     /** Simple constructor.
196      * <p>
197      * At construction, the parameter is configured as <em>not</em> selected,
198      * the reference date is set to {@code null}, the value is set to the
199      * {@code referenceValue}, the validity period is set to 0 so by default
200      * the parameterDriver will be estimated on only 1 interval from -INF to
201      * +INF. To change the validity period the
202      * {@link ParameterDriver#addSpans(AbsoluteDate, AbsoluteDate, double)}
203      * method must be called.
204      * </p>
205      * @param name name of the parameter
206      * @param referenceValue reference value of the parameter
207      * @param scale scaling factor to convert the parameters value to
208      * non-dimensional (typically set to the expected standard deviation of the
209      * parameter), it must be non-zero
210      * @param minValue minimum value allowed
211      * @param maxValue maximum value allowed
212      */
213     public ParameterDriver(final String name,
214                            final double referenceValue, final double scale,
215                            final double minValue, final double maxValue) {
216         if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
217             throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
218                                       name, scale);
219         }
220         this.name                   = name;
221         this.nameSpanMap            = new TimeSpanMap<>(SPAN + name + Integer.toString(0));
222         this.referenceValue         = referenceValue;
223         this.scale                  = scale;
224         this.minValue               = minValue;
225         this.maxValue               = maxValue;
226         this.referenceDate          = null;
227         // at construction the parameter driver
228         // will be consider with only 1 estimated value over the all orbit
229         // determination
230         this.valueSpanMap           = new TimeSpanMap<>(referenceValue);
231         this.selected               = false;
232         this.observers              = new ArrayList<>();
233         this.isEstimationContinuous = false;
234     }
235 
236     /** Get current name span map of the parameterDriver, cut in interval
237      * in accordance with value span map and validity period.
238      * @return current name span map
239      * @since 12.0
240      */
241     public TimeSpanMap<String> getNamesSpanMap() {
242         return nameSpanMap;
243     }
244 
245     /** Get value time span map for parameterDriver.
246      * @return value time span map
247      * @since 12.0
248      */
249     public TimeSpanMap<Double> getValueSpanMap() {
250         return valueSpanMap;
251     }
252 
253     /** Set current parameter value span map to match another driver. In order to keep
254      * consistency, the validity period and name span map are updated.
255      * @param driver for which the value span map wants to be copied for the
256      * current driver
257      * @since 12.0
258      */
259     public void setValueSpanMap(final ParameterDriver driver) {
260         final TimeSpanMap<Double> previousValueSpanMap = driver.getValueSpanMap();
261         valueSpanMap   = driver.getValueSpanMap();
262         nameSpanMap    = driver.getNamesSpanMap();
263         for (final ParameterObserver observer : observers) {
264             observer.valueSpanMapChanged(previousValueSpanMap, this);
265         }
266     }
267 
268     /** Get the number of values to estimate that is to say the number.
269      * of Span present in valueSpanMap
270      * @return int the number of values to estimate
271      * @since 12.0
272      */
273     public int getNbOfValues() {
274         return valueSpanMap.getSpansNumber();
275     }
276 
277     /** Get the dates of the transitions for the drag sensitive models {@link TimeSpanMap}.
278      * @return dates of the transitions for the drag sensitive models {@link TimeSpanMap}
279      * @since 12.0
280      */
281     public AbsoluteDate[] getTransitionDates() {
282 
283         // Get all transitions
284         final List<AbsoluteDate> listDates = new ArrayList<>();
285 
286         // Extract all the transitions' dates
287         for (Transition<Double> transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) {
288             listDates.add(transition.getDate());
289         }
290         // Return the array of transition dates
291         return listDates.toArray(new AbsoluteDate[0]);
292     }
293 
294     /** Get all values of the valueSpanMap in the chronological order.
295      * @return double[] containing values of the valueSpanMap in the chronological order
296      */
297     public double[] getValues() {
298         final double[] chronologicalValues = new double[getNbOfValues()];
299         Span<Double> currentSpan = valueSpanMap.getFirstSpan();
300         for (int i = 0; i < getNbOfValues() - 1; i++) {
301             chronologicalValues[i] = currentSpan.getData();
302             currentSpan = currentSpan.next();
303         }
304         chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData();
305         return chronologicalValues;
306     }
307 
308 
309     /** Add an observer for this driver.
310      * <p>
311      * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver)
312      * valueSpanMapChanged} method is called once automatically when the
313      * observer is added, and then called at each value change.
314      * </p>
315      * @param observer observer to add
316           * while being updated
317      */
318     public void addObserver(final ParameterObserver observer) {
319         observers.add(observer);
320         observer.valueSpanMapChanged(getValueSpanMap(), this);
321     }
322 
323     /** Remove an observer.
324      * @param observer observer to remove
325      * @since 9.1
326      */
327     public void removeObserver(final ParameterObserver observer) {
328         for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
329             if (iterator.next() == observer) {
330                 iterator.remove();
331                 return;
332             }
333         }
334     }
335 
336     /** Replace an observer.
337      * @param oldObserver observer to replace
338      * @param newObserver new observer to use
339      * @since 10.1
340      */
341     public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
342         for (int i = 0; i < observers.size(); ++i) {
343             if (observers.get(i) == oldObserver) {
344                 observers.set(i, newObserver);
345             }
346         }
347     }
348 
349     /** Get the observers for this driver.
350      * @return an unmodifiable view of the observers for this driver
351      * @since 9.1
352      */
353     public List<ParameterObserver> getObservers() {
354         return Collections.unmodifiableList(observers);
355     }
356 
357     /** Get parameter driver general name.
358      * @return name
359      */
360     public String getName() {
361         return name;
362     }
363 
364     /** Get name of the parameter span for a specific date.
365      * @param date date at which the name of the span wants to be known
366      * @return name data of the name time span map at date
367      */
368     public String getNameSpan(final AbsoluteDate date) {
369         return nameSpanMap.get(date);
370     }
371 
372     /** Change the general name of this parameter driver.
373      * @param name new name
374      */
375     public void setName(final String name) {
376         final String previousName = this.name;
377         this.name = name;
378         for (final ParameterObserver observer : observers) {
379             observer.nameChanged(previousName, this);
380         }
381         // the names time span map must also be updated with the new name
382         if (nameSpanMap.getSpansNumber() > 1) {
383             Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
384             nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false);
385             for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) {
386                 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
387                 nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false);
388             }
389         } else {
390             nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0));
391         }
392     }
393 
394     /** Cut values and names time span map given orbit determination start and end and driver
395      * periodicity.
396      * <p>
397      * For example for a drag coefficient the validity period would be
398      * 1 days = 86400sec. To be called after constructor to cut the temporal axis with
399      * the wanted parameter driver temporality for estimations on the wanted interval.
400      * </p>
401      * <p>
402      * Must be called only once at the beginning of orbit
403      * determination for example. If called several times, will throw exception. If parameter
404      * estimations intervals must be changed then a new ParameterDriver must be created or the
405      * function {@link #addSpanAtDate} should be used.
406      * </p>
407      * <p>
408      * This function should not be called on {@link DateDriver} and
409      * any of {@link ParameterDrivenDateIntervalDetector} attribute, because there is no sense to
410      * estimate several values for dateDriver.
411      * </p>
412      * <p>
413      * The choice of {@code orbitDeterminationStartDate}, {@code orbitDeterminationEndDate} and
414      * {@code validityPeriodForDriver} in a case of orbit determination must be done carefully,
415      * indeed, enough measurement should be available for each time interval or
416      * the orbit determination won't converge.
417      * </p>
418      * @param orbitDeterminationStartDate start date for which the parameter driver
419      * starts to be estimated.
420      * @param orbitDeterminationEndDate end date for which the parameter driver
421      * stops to be estimated.
422      * @param validityPeriodForDriver validity period for which the parameter value
423      * is effective (for example 1 day for drag coefficient). Warning, validityPeriod
424      * should not be too short or the orbit determination won't converge.
425      * @since 12.0
426      */
427     public void addSpans(final AbsoluteDate orbitDeterminationStartDate,
428                          final AbsoluteDate orbitDeterminationEndDate,
429                          final double validityPeriodForDriver) {
430 
431         // by convention 0 is when the parameter needs to be drived only on 1
432         // interval from -INF to +INF time period
433         if (getNbOfValues() != 1) {
434             // throw exception if called several time, must be called only once at the beginning of orbit
435             // determination, if the periods wants to be changed a new parameter must be created
436             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name);
437         } else {
438 
439             int spanNumber = 1;
440             AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver);
441             //splitting the names and values span map accordingly with start and end of orbit determination
442             //and validity period. A security is added to avoid having to few measurements point for a span
443             //in order to assure orbit determination convergence
444             while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) {
445                 valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false);
446                 nameSpanMap.addValidAfter(SPAN + getName() + Integer.toString(spanNumber++), currentDate, false);
447                 currentDate = currentDate.shiftedBy(validityPeriodForDriver);
448             }
449         }
450     }
451 
452     /** Create a new span in values and names time span map given a start date.
453      * <b> One must be aware of the importance of choosing wise dates if this function is called
454      * several times to create several span at wanted times. Indeed, if orbit determination is performed
455      * it might not converge or find singular matrix if the spans are too short and contains to few measurements.
456      * Must be called before any computation (for example before
457      * orbit determination).</b>
458      * @param spanStartDate wanted start date for parameter value interval
459      * starts to be estimated.
460      * @since 12.0
461      */
462     public void addSpanAtDate(final AbsoluteDate spanStartDate) {
463 
464         // Split value span map with new interval having for start date spanStartDate and end
465         // date next span start date of +INF if no span is present after
466         valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false);
467         nameSpanMap.addValidAfter(name, spanStartDate, false);
468         // Rename spans recursively
469         Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
470         nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false);
471 
472         for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) {
473             currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
474             nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false);
475         }
476     }
477 
478     /** Get reference parameter value.
479      * @return reference parameter value
480      */
481     public double getReferenceValue() {
482         return referenceValue;
483     }
484 
485     /** Set reference parameter value.
486      * @since 9.3
487      * @param referenceValue the reference value to set.
488      */
489     public void setReferenceValue(final double referenceValue) {
490         final double previousReferenceValue = this.referenceValue;
491         this.referenceValue = referenceValue;
492         for (final ParameterObserver observer : observers) {
493             observer.referenceValueChanged(previousReferenceValue, this);
494         }
495     }
496 
497     /** Get minimum parameter value.
498      * @return minimum parameter value
499      */
500     public double getMinValue() {
501         return minValue;
502     }
503 
504     /** Set minimum parameter value.
505      * @since 9.3
506      * @param minValue the minimum value to set.
507      */
508     public void setMinValue(final double minValue) {
509         final double previousMinValue = this.minValue;
510         this.minValue = minValue;
511         for (final ParameterObserver observer : observers) {
512             observer.minValueChanged(previousMinValue, this);
513         }
514         // Check if all values are still not out of min/max range
515         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
516             setValue(getValue(span.getStart()), span.getStart());
517         }
518     }
519 
520     /** Get maximum parameter value.
521      * @return maximum parameter value
522      */
523     public double getMaxValue() {
524         return maxValue;
525     }
526 
527     /** Set maximum parameter value.
528      * @since 9.3
529      * @param maxValue the maximum value to set.
530      */
531     public void setMaxValue(final double maxValue) {
532         final double previousMaxValue = this.maxValue;
533         this.maxValue = maxValue;
534         for (final ParameterObserver observer : observers) {
535             observer.maxValueChanged(previousMaxValue, this);
536         }
537         // Check if all values are still not out of min/max range
538         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
539             setValue(getValue(span.getStart()), span.getStart());
540         }
541     }
542 
543     /** Get scale.
544      * @return scale
545      */
546     public double getScale() {
547         return scale;
548     }
549 
550     /** Set scale.
551      * @since 9.3
552      * @param scale the scale to set.
553      */
554     public void setScale(final double scale) {
555         final double previousScale = this.scale;
556         this.scale = scale;
557         for (final ParameterObserver observer : observers) {
558             observer.scaleChanged(previousScale, this);
559         }
560     }
561 
562     /** Get normalized value at specific date.
563      * <p>
564      * The normalized value is a non-dimensional value
565      * suitable for use as part of a vector in an optimization
566      * process. It is computed as {@code (current - reference)/scale}.
567      * </p>
568      * @param date date for which the normalized value wants to be known
569      * @return normalized value
570      */
571     public double getNormalizedValue(final AbsoluteDate date) {
572         return (getValue(date) - getReferenceValue()) / scale;
573     }
574 
575     /** Get normalized value. Only useable on ParameterDriver
576      * which have only 1 span on their TimeSpanMap value (that is
577      * to say for which the setPeriod method wasn't called) otherwise
578      * it will throw an exception.
579      * <p>
580      * The normalized value is a non-dimensional value
581      * suitable for use as part of a vector in an optimization
582      * process. It is computed as {@code (current - reference)/scale}.
583      * </p>
584      * @return normalized value
585      */
586     public double getNormalizedValue() {
587         return (getValue() - getReferenceValue()) / scale;
588     }
589 
590     /** Set normalized value at specific date.
591      * <p>
592      * The normalized value is a non-dimensional value
593      * suitable for use as part of a vector in an optimization
594      * process. It is computed as {@code (current - reference)/scale}.
595      * </p>
596      * @param date date for which the normalized value wants to be set
597      * @param normalized value
598      */
599     public void setNormalizedValue(final double normalized, final AbsoluteDate date) {
600         setValue(getReferenceValue() + scale * normalized, date);
601     }
602 
603     /** Set normalized value at specific date. Only useable on ParameterDriver
604      * which have only 1 span on their TimeSpanMap value (that is
605      * to say for which the setPeriod method wasn't called) otherwise
606      * it will throw an exception.
607      * <p>
608      * The normalized value is a non-dimensional value
609      * suitable for use as part of a vector in an optimization
610      * process. It is computed as {@code (current - reference)/scale}.
611      * </p>
612      * @param normalized value
613      */
614     public void setNormalizedValue(final double normalized) {
615         setValue(getReferenceValue() + scale * normalized);
616     }
617 
618     /** Get current reference date.
619      * @return current reference date (null if it was never set)
620      * @since 9.0
621      */
622     public AbsoluteDate getReferenceDate() {
623         return referenceDate;
624     }
625 
626     /** Set reference date.
627      * @param newReferenceDate new reference date
628      * @since 9.0
629      */
630     public void setReferenceDate(final AbsoluteDate newReferenceDate) {
631         final AbsoluteDate previousReferenceDate = getReferenceDate();
632         referenceDate = newReferenceDate;
633         for (final ParameterObserver observer : observers) {
634             observer.referenceDateChanged(previousReferenceDate, this);
635         }
636     }
637 
638     /** Get current parameter value. Only usable on ParameterDriver
639      * which have only 1 span on their TimeSpanMap value (that is
640      * to say for which the setPeriod method wasn't called)
641      * @return current parameter value
642      */
643     public double getValue() {
644         if (getNbOfValues() > 1) {
645             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)");
646         }
647         // Attention voir si qlqchose est retourné si une exception est levée
648         return valueSpanMap.getFirstSpan().getData();
649     }
650 
651     /** Get current parameter value at specific date, depending on isContinuousEstimation
652      * value, the value returned will be obtained by step estimation or continuous estimation.
653      * @param date date for which the value wants to be known. Only if
654      * parameter driver has 1 value estimated over the all orbit determination
655      * period (not validity period intervals for estimation), the date value can
656      * be <em>{@code null}</em> and then the only estimated value will be
657      * returned, in this case the date can also be whatever the value returned would
658      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
659      * @return current parameter value at date date, or for the all period if
660      * no validity period (= 1 value estimated over the all orbit determination
661      * period)
662      */
663     public double getValue(final AbsoluteDate date) {
664         return isEstimationContinuous ? getValueContinuousEstimation(date) : getValueStepEstimation(date);
665     }
666 
667     /** Get current parameter value at specific date with step estimation.
668      * @param date date for which the value wants to be known. Only if
669      * parameter driver has 1 value estimated over the all orbit determination
670      * period (not validity period intervals for estimation), the date value can
671      * be <em>{@code null}</em> and then the only estimated value will be
672      * returned, in this case the date can also be whatever the value returned would
673      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
674      * @return current parameter value at date date, or for the all period if
675      * no validity period (= 1 value estimated over the all orbit determination
676      * period)
677      */
678     public double getValueStepEstimation(final AbsoluteDate date) {
679         return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date);
680     }
681 
682     /** Get current parameter value at specific date with continuous estimation.
683      * @param date date for which the value wants to be known. Only if
684      * parameter driver has 1 value estimated over the all orbit determination
685      * period (not validity period intervals for estimation), the date value can
686      * be <em>{@code null}</em> and then the only estimated value will be
687      * returned, in this case the date can also be whatever the value returned would
688      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
689      * @return current parameter value at date date, or for the all period if
690      * no validity period (= 1 value estimated over the all orbit determination
691      * period)
692      * @since 12.0
693      */
694     public double getValueContinuousEstimation(final AbsoluteDate date) {
695         //TODO
696         throw new UnsupportedOperationException();
697     }
698 
699     /** Get the value as a gradient at special date.
700      * @param freeParameters total number of free parameters in the gradient
701      * @param indices indices of the differentiation parameters in derivatives computations
702      * @return value with derivatives, will throw exception if called on a PDriver having
703      * several values driven
704      * @since 10.2
705      */
706     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
707         Integer index = null;
708         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
709             index = indices.get(span.getData());
710             if (index != null) {
711                 break;
712             }
713         }
714         return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue());
715     }
716 
717     /** Get the value as a gradient at special date.
718      * @param freeParameters total number of free parameters in the gradient
719      * @param indices indices of the differentiation parameters in derivatives computations,
720      * must be span name and not driver name
721      * @param date date for which the value wants to be known. Only if
722      * parameter driver has 1 value estimated over the all orbit determination
723      * period (not validity period intervals for estimation), the date value can
724      * be <em>{@code null}</em> and then the only estimated value will be
725      * returned
726      * @return value with derivatives
727      * @since 10.2
728      */
729     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices, final AbsoluteDate date) {
730         Integer index = null;
731         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
732             index = indices.get(span.getData());
733             if (index != null) {
734                 break;
735             }
736         }
737         return (index == null) ? Gradient.constant(freeParameters, getValue(date)) : Gradient.variable(freeParameters, index, getValue(date));
738     }
739 
740     /** Set parameter value at specific date.
741      * <p>
742      * If {@code newValue} is below {@link #getMinValue()}, it will
743      * be silently set to {@link #getMinValue()}. If {@code newValue} is
744      * above {@link #getMaxValue()}, it will be silently set to {@link
745      * #getMaxValue()}.
746      * </p>
747      * @param date date for which the value wants to be set. Only if
748      * parameter driver has 1 value estimated over the all orbit determination
749      * period (not validity period intervals for estimation), the date value can
750      * be <em>{@code null}</em>
751      * @param newValue new value to set
752      */
753     public void setValue(final double newValue, final AbsoluteDate date) {
754 
755         double previousValue = Double.NaN;
756         AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;
757 
758         // if valid for infinity (only 1 value estimation for the orbit determination )
759         if (getNbOfValues() == 1) {
760             previousValue = this.getValue(referenceDateSpan);
761             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
762         // if needs to be estimated per time range / validity period
763 
764         // if several value intervals
765         } else {
766             final Span<Double> valueSpan = valueSpanMap.getSpan(date);
767             previousValue = valueSpan.getData();
768             referenceDateSpan = valueSpan.getStart();
769             // if the Span considered is from past infinity to valueSpanEndDate it is
770             // impossible to addValidAfter past infinity because it is creating a new span that
771             // is why the below trick was set up
772             if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) {
773                 referenceDateSpan = valueSpan.getEnd();
774                 this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
775                                                  referenceDateSpan, false);
776             } else {
777                 this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
778                                                 referenceDateSpan, false);
779             }
780         }
781 
782         for (final ParameterObserver observer : observers) {
783             observer.valueChanged(previousValue, this, date);
784         }
785     }
786 
787 
788     /** Set parameter value. Only usable on ParameterDriver
789      * which have only 1 span on their TimeSpanMap value (that is
790      * to say for which the setPeriod method wasn't called)
791      * <p>
792      * If {@code newValue} is below {@link #getMinValue()}, it will
793      * be silently set to {@link #getMinValue()}. If {@code newValue} is
794      * above {@link #getMaxValue()}, it will be silently set to {@link
795      * #getMaxValue()}.
796      * </p>
797      * @param newValue new value to set
798      */
799     public void setValue(final double newValue) {
800         if (getNbOfValues() == 1) {
801             final AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;
802             final double previousValue = this.getValue(referenceDateSpan);
803             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
804             for (final ParameterObserver observer : observers) {
805                 observer.valueChanged(previousValue, this, referenceDateSpan);
806             }
807         } else {
808             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "setValue(date)");
809         }
810     }
811 
812     /** Configure a parameter selection status.
813      * <p>
814      * Selection is used for estimated parameters in orbit determination,
815      * or to compute the Jacobian matrix in partial derivatives computation.
816      * </p>
817      * @param selected if true the parameter is selected,
818      * otherwise it will be fixed
819      */
820     public void setSelected(final boolean selected) {
821         final boolean previousSelection = isSelected();
822         this.selected = selected;
823         for (final ParameterObserver observer : observers) {
824             observer.selectionChanged(previousSelection, this);
825         }
826     }
827 
828     /** Check if parameter is selected.
829      * <p>
830      * Selection is used for estimated parameters in orbit determination,
831      * or to compute the Jacobian matrix in partial derivatives computation.
832      * </p>
833      * @return true if parameter is selected, false if it is not
834      */
835     public boolean isSelected() {
836         return selected;
837     }
838 
839     /** Set parameter estimation to continuous, by default step estimation.
840      * <p> Continuous estimation : when a value wants to be known at date
841      * t, the value returned will be an interpolation between start value
842      * of the span corresponding to date t and end value (which corresponds
843      * to the start of the next span).
844      * </p>
845      * <p> Step estimation : when a value wants to be
846      * known at date t, the value returned will be the value of the beginning
847      * of span corresponding to date t, step estimation.
848      * </p>
849      * @param continuous if true the parameter will be estimated
850      * with continuous estimation, if false with step estimation.
851      */
852     public void setContinuousEstimation(final boolean continuous) {
853         final boolean previousEstimation = isContinuousEstimation();
854         this.isEstimationContinuous = continuous;
855         for (final ParameterObserver observer : observers) {
856             observer.estimationTypeChanged(previousEstimation, this);
857         }
858     }
859 
860     /** Check if parameter estimation is continuous, that is to say when
861      * a value wants to be known at date t, the value returned
862      * will be an interpolation between start value on span corresponding
863      * for date t and end value (which corresponds to the start of the next
864      * span), continuous estimation. Or not continuous, that is to say when a value wants to be
865      * known at date t, the value returned will be the value of the start
866      * of span corresponding to date t, step estimation.
867      * @return true if continuous estimation/definition, false if step estimation/definition
868      * @since 12.0
869      */
870     public boolean isContinuousEstimation() {
871         return isEstimationContinuous;
872     }
873 
874     /** Get a text representation of the parameter.
875      * @return text representation of the parameter, in the form name = value.
876      */
877     public String toString() {
878         return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH);
879     }
880 
881 }