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  
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 convergence for orbit determination,
62   * the start, end date and driver periodicity must be wisely chosen </b>. There must be enough measurements
63   * on each interval or convergence won't reach or singular matrices 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   * convergence for orbit determination, if the method is called several time, the start date must be wisely chosen </b>.
68   * There must be enough measurements on each interval or convergence won't reach or singular matrices 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 constant all along the span duration and will be the value at 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 blindly, 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 + 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      * <p>
239      * Note that if the expunge policy of the names map is
240      * {@link TimeSpanMap#configureExpunge(int, double, ExpungePolicy) reconfigured},
241      * then the expunge policy of the {@link #getValueSpanMap() values map} should
242      * be reconfigured too with the same settings.
243      * </p>
244      * @return current name span map
245      * @since 12.0
246      */
247     public TimeSpanMap<String> getNamesSpanMap() {
248         return nameSpanMap;
249     }
250 
251     /** Get value time span map for parameterDriver.
252      * <p>
253      * Note that if the expunge policy of the values map is
254      * {@link TimeSpanMap#configureExpunge(int, double, ExpungePolicy) reconfigured},
255      * then the expunge policy of the {@link #getNamesSpanMap()} names map} should
256      * be reconfigured too with the same settings.
257      * </p>
258      * @return value time span map
259      * @since 12.0
260      */
261     public TimeSpanMap<Double> getValueSpanMap() {
262         return valueSpanMap;
263     }
264 
265     /** Set current parameter value span map to match another driver. In order to keep
266      * consistency, the validity period and name span map are updated.
267      * @param driver for which the value span map wants to be copied for the
268      * current driver
269      * @since 12.0
270      */
271     public void setValueSpanMap(final ParameterDriver driver) {
272         final TimeSpanMap<Double> previousValueSpanMap = driver.getValueSpanMap();
273         valueSpanMap   = driver.getValueSpanMap();
274         nameSpanMap    = driver.getNamesSpanMap();
275         for (final ParameterObserver observer : observers) {
276             observer.valueSpanMapChanged(previousValueSpanMap, this);
277         }
278     }
279 
280     /** Get the number of values to estimate that is to say the number.
281      * of Span present in valueSpanMap
282      * @return int the number of values to estimate
283      * @since 12.0
284      */
285     public int getNbOfValues() {
286         return valueSpanMap.getSpansNumber();
287     }
288 
289     /** Get the dates of the transitions {@link TimeSpanMap}.
290      * @return dates of the transitions {@link TimeSpanMap}
291      * @since 12.0
292      */
293     public AbsoluteDate[] getTransitionDates() {
294 
295         // Get all transitions
296         final List<AbsoluteDate> listDates = new ArrayList<>();
297 
298         // Extract all the transitions' dates
299         for (Transition<Double> transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) {
300             listDates.add(transition.getDate());
301         }
302         // Return the array of transition dates
303         return listDates.toArray(new AbsoluteDate[0]);
304     }
305 
306     /** Get all values of the valueSpanMap in the chronological order.
307      * @return double[] containing values of the valueSpanMap in the chronological order
308      */
309     public double[] getValues() {
310         final double[] chronologicalValues = new double[getNbOfValues()];
311         Span<Double> currentSpan = valueSpanMap.getFirstSpan();
312         for (int i = 0; i < getNbOfValues() - 1; i++) {
313             chronologicalValues[i] = currentSpan.getData();
314             currentSpan = currentSpan.next();
315         }
316         chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData();
317         return chronologicalValues;
318     }
319 
320 
321     /** Add an observer for this driver.
322      * <p>
323      * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver)
324      * valueSpanMapChanged} method is called once automatically when the
325      * observer is added, and then called at each value change.
326      * </p>
327      * @param observer observer to add
328           * while being updated
329      */
330     public void addObserver(final ParameterObserver observer) {
331         observers.add(observer);
332         observer.valueSpanMapChanged(getValueSpanMap(), this);
333     }
334 
335     /** Remove an observer.
336      * @param observer observer to remove
337      * @since 9.1
338      */
339     public void removeObserver(final ParameterObserver observer) {
340         for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
341             if (iterator.next() == observer) {
342                 iterator.remove();
343                 return;
344             }
345         }
346     }
347 
348     /** Replace an observer.
349      * @param oldObserver observer to replace
350      * @param newObserver new observer to use
351      * @since 10.1
352      */
353     public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
354         for (int i = 0; i < observers.size(); ++i) {
355             if (observers.get(i) == oldObserver) {
356                 observers.set(i, newObserver);
357             }
358         }
359     }
360 
361     /** Get the observers for this driver.
362      * @return an unmodifiable view of the observers for this driver
363      * @since 9.1
364      */
365     public List<ParameterObserver> getObservers() {
366         return Collections.unmodifiableList(observers);
367     }
368 
369     /** Get parameter driver general name.
370      * @return name
371      */
372     public String getName() {
373         return name;
374     }
375 
376     /** Get name of the parameter span for a specific date.
377      * @param date date at which the name of the span wants to be known
378      * @return name data of the name time span map at date
379      */
380     public String getNameSpan(final AbsoluteDate date) {
381         return nameSpanMap.get(date);
382     }
383 
384     /** Change the general name of this parameter driver.
385      * @param name new name
386      */
387     public void setName(final String name) {
388         final String previousName = this.name;
389         this.name = name;
390         for (final ParameterObserver observer : observers) {
391             observer.nameChanged(previousName, this);
392         }
393         // the names time span map must also be updated with the new name
394         if (nameSpanMap.getSpansNumber() > 1) {
395             Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
396             nameSpanMap.addValidBefore(SPAN + name + 0, currentNameSpan.getEnd(), false);
397             for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) {
398                 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
399                 nameSpanMap.addValidAfter(SPAN + name + spanNumber, currentNameSpan.getStart(), false);
400             }
401         } else {
402             nameSpanMap = new TimeSpanMap<>(SPAN + name + 0);
403         }
404     }
405 
406     /** Cut values and names time span map given orbit determination start and end and driver
407      * periodicity.
408      * <p>
409      * For example for a drag coefficient the validity period would be
410      * 1 days = 86400sec. To be called after constructor to cut the temporal axis with
411      * the wanted parameter driver temporality for estimations on the wanted interval.
412      * </p>
413      * <p>
414      * Must be called only once at the beginning of orbit
415      * determination for example. If called several times, will throw exception. If parameter
416      * estimations intervals must be changed then a new ParameterDriver must be created or the
417      * function {@link #addSpanAtDate} should be used.
418      * </p>
419      * <p>
420      * This function should not be called on {@link DateDriver} and
421      * any of {@link ParameterDrivenDateIntervalDetector} attribute, because there is no sense to
422      * estimate several values for dateDriver.
423      * </p>
424      * <p>
425      * The choice of {@code orbitDeterminationStartDate}, {@code orbitDeterminationEndDate} and
426      * {@code validityPeriodForDriver} in a case of orbit determination must be done carefully,
427      * indeed, enough measurement should be available for each time interval or
428      * the orbit determination won't converge.
429      * </p>
430      * @param orbitDeterminationStartDate start date for which the parameter driver
431      * starts to be estimated.
432      * @param orbitDeterminationEndDate end date for which the parameter driver
433      * stops to be estimated.
434      * @param validityPeriodForDriver validity period for which the parameter value
435      * is effective (for example 1 day for drag coefficient). Warning, validityPeriod
436      * should not be too short or the orbit determination won't converge.
437      * @since 12.0
438      */
439     public void addSpans(final AbsoluteDate orbitDeterminationStartDate,
440                          final AbsoluteDate orbitDeterminationEndDate,
441                          final double validityPeriodForDriver) {
442 
443         // by convention 0 is when the parameter needs to be drived only on 1
444         // interval from -INF to +INF time period
445         if (getNbOfValues() != 1) {
446             // throw exception if called several time, must be called only once at the beginning of orbit
447             // determination, if the periods wants to be changed a new parameter must be created
448             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name);
449         } else {
450 
451             int spanNumber = 1;
452             AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver);
453             //splitting the names and values span map accordingly with start and end of orbit determination
454             //and validity period. A security is added to avoid having to few measurements point for a span
455             //in order to assure orbit determination convergence
456             while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) {
457                 valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false);
458                 nameSpanMap.addValidAfter(SPAN + getName() + spanNumber++, currentDate, false);
459                 currentDate = currentDate.shiftedBy(validityPeriodForDriver);
460             }
461         }
462     }
463 
464     /** Create a new span in values and names time span map given a start date.
465      * <b> One must be aware of the importance of choosing wise dates if this function is called
466      * several times to create several span at wanted times. Indeed, if orbit determination is performed
467      * it might not converge or find singular matrix if the spans are too short and contains to few measurements.
468      * Must be called before any computation (for example before
469      * orbit determination).</b>
470      * @param spanStartDate wanted start date for parameter value interval
471      * starts to be estimated.
472      * @since 12.0
473      */
474     public void addSpanAtDate(final AbsoluteDate spanStartDate) {
475 
476         // Split value span map with new interval having for start date spanStartDate and end
477         // date next span start date of +INF if no span is present after
478         valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false);
479         nameSpanMap.addValidAfter(name, spanStartDate, false);
480         // Rename spans recursively
481         Span<String> currentNameSpan = nameSpanMap.getFirstSpan();
482         nameSpanMap.addValidBefore(SPAN + name + 0, currentNameSpan.getEnd(), false);
483 
484         for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) {
485             currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
486             nameSpanMap.addValidAfter(SPAN + name + spanNumber, currentNameSpan.getStart(), false);
487         }
488     }
489 
490     /** Get reference parameter value.
491      * @return reference parameter value
492      */
493     public double getReferenceValue() {
494         return referenceValue;
495     }
496 
497     /** Set reference parameter value.
498      * @since 9.3
499      * @param referenceValue the reference value to set.
500      */
501     public void setReferenceValue(final double referenceValue) {
502         final double previousReferenceValue = this.referenceValue;
503         this.referenceValue = referenceValue;
504         for (final ParameterObserver observer : observers) {
505             observer.referenceValueChanged(previousReferenceValue, this);
506         }
507     }
508 
509     /** Get minimum parameter value.
510      * @return minimum parameter value
511      */
512     public double getMinValue() {
513         return minValue;
514     }
515 
516     /** Set minimum parameter value.
517      * @since 9.3
518      * @param minValue the minimum value to set.
519      */
520     public void setMinValue(final double minValue) {
521         final double previousMinValue = this.minValue;
522         this.minValue = minValue;
523         for (final ParameterObserver observer : observers) {
524             observer.minValueChanged(previousMinValue, this);
525         }
526         // Check if all values are still not out of min/max range
527         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
528             setValue(getValue(span.getStart()), span.getStart());
529         }
530     }
531 
532     /** Get maximum parameter value.
533      * @return maximum parameter value
534      */
535     public double getMaxValue() {
536         return maxValue;
537     }
538 
539     /** Set maximum parameter value.
540      * @since 9.3
541      * @param maxValue the maximum value to set.
542      */
543     public void setMaxValue(final double maxValue) {
544         final double previousMaxValue = this.maxValue;
545         this.maxValue = maxValue;
546         for (final ParameterObserver observer : observers) {
547             observer.maxValueChanged(previousMaxValue, this);
548         }
549         // Check if all values are still not out of min/max range
550         for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) {
551             setValue(getValue(span.getStart()), span.getStart());
552         }
553     }
554 
555     /** Get scale.
556      * @return scale
557      */
558     public double getScale() {
559         return scale;
560     }
561 
562     /** Set scale.
563      * @since 9.3
564      * @param scale the scale to set.
565      */
566     public void setScale(final double scale) {
567         final double previousScale = this.scale;
568         this.scale = scale;
569         for (final ParameterObserver observer : observers) {
570             observer.scaleChanged(previousScale, this);
571         }
572     }
573 
574     /** Get normalized value at specific date.
575      * <p>
576      * The normalized value is a non-dimensional value
577      * suitable for use as part of a vector in an optimization
578      * process. It is computed as {@code (current - reference)/scale}.
579      * </p>
580      * @param date date for which the normalized value wants to be known
581      * @return normalized value
582      */
583     public double getNormalizedValue(final AbsoluteDate date) {
584         return (getValue(date) - getReferenceValue()) / scale;
585     }
586 
587     /** Get normalized value. Only useable on ParameterDriver
588      * which have only 1 span on their TimeSpanMap value (that is
589      * to say for which the setPeriod method wasn't called) otherwise
590      * it will throw an exception.
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      * @return normalized value
597      */
598     public double getNormalizedValue() {
599         return (getValue() - getReferenceValue()) / scale;
600     }
601 
602     /** Set normalized value at specific date.
603      * <p>
604      * The normalized value is a non-dimensional value
605      * suitable for use as part of a vector in an optimization
606      * process. It is computed as {@code (current - reference)/scale}.
607      * </p>
608      * @param date date for which the normalized value wants to be set
609      * @param normalized value
610      */
611     public void setNormalizedValue(final double normalized, final AbsoluteDate date) {
612         setValue(getReferenceValue() + scale * normalized, date);
613     }
614 
615     /** Set normalized value at specific date. Only useable on ParameterDriver
616      * which have only 1 span on their TimeSpanMap value (that is
617      * to say for which the setPeriod method wasn't called) otherwise
618      * it will throw an exception.
619      * <p>
620      * The normalized value is a non-dimensional value
621      * suitable for use as part of a vector in an optimization
622      * process. It is computed as {@code (current - reference)/scale}.
623      * </p>
624      * @param normalized value
625      */
626     public void setNormalizedValue(final double normalized) {
627         setValue(getReferenceValue() + scale * normalized);
628     }
629 
630     /** Get current reference date.
631      * @return current reference date (null if it was never set)
632      * @since 9.0
633      */
634     public AbsoluteDate getReferenceDate() {
635         return referenceDate;
636     }
637 
638     /** Set reference date.
639      * @param newReferenceDate new reference date
640      * @since 9.0
641      */
642     public void setReferenceDate(final AbsoluteDate newReferenceDate) {
643         final AbsoluteDate previousReferenceDate = getReferenceDate();
644         referenceDate = newReferenceDate;
645         for (final ParameterObserver observer : observers) {
646             observer.referenceDateChanged(previousReferenceDate, this);
647         }
648     }
649 
650     /** Get current parameter value. Only usable on ParameterDriver
651      * which have only 1 span on their TimeSpanMap value (that is
652      * to say for which the setPeriod method wasn't called)
653      * @return current parameter value
654      */
655     public double getValue() {
656         if (getNbOfValues() > 1) {
657             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)");
658         }
659         // Attention voir si qlqchose est retourné si une exception est levée
660         return valueSpanMap.getFirstSpan().getData();
661     }
662 
663     /** Get current parameter value at specific date, depending on isContinuousEstimation
664      * value, the value returned will be obtained by step estimation or continuous estimation.
665      * @param date date for which the value wants to be known. Only if
666      * parameter driver has 1 value estimated over the all orbit determination
667      * period (not validity period intervals for estimation), the date value can
668      * be <em>{@code null}</em> and then the only estimated value will be
669      * returned, in this case the date can also be whatever the value returned would
670      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
671      * @return current parameter value at date date, or for the all period if
672      * no validity period (= 1 value estimated over the all orbit determination
673      * period)
674      */
675     public double getValue(final AbsoluteDate date) {
676         return isEstimationContinuous ? getValueContinuousEstimation(date) : getValueStepEstimation(date);
677     }
678 
679     /** Get current parameter value at specific date with step estimation.
680      * @param date date for which the value wants to be known. Only if
681      * parameter driver has 1 value estimated over the all orbit determination
682      * period (not validity period intervals for estimation), the date value can
683      * be <em>{@code null}</em> and then the only estimated value will be
684      * returned, in this case the date can also be whatever the value returned would
685      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
686      * @return current parameter value at date date, or for the all period if
687      * no validity period (= 1 value estimated over the all orbit determination
688      * period)
689      */
690     public double getValueStepEstimation(final AbsoluteDate date) {
691         return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date);
692     }
693 
694     /** Get current parameter value at specific date with continuous estimation.
695      * @param date date for which the value wants to be known. Only if
696      * parameter driver has 1 value estimated over the all orbit determination
697      * period (not validity period intervals for estimation), the date value can
698      * be <em>{@code null}</em> and then the only estimated value will be
699      * returned, in this case the date can also be whatever the value returned would
700      * be the same. Moreover in this particular case one can also call the {@link #getValue()}.
701      * @return current parameter value at date date, or for the all period if
702      * no validity period (= 1 value estimated over the all orbit determination
703      * period)
704      * @since 12.0
705      */
706     public double getValueContinuousEstimation(final AbsoluteDate date) {
707         //TODO
708         throw new UnsupportedOperationException();
709     }
710 
711     /** Get the value as a gradient at special date.
712      * @param freeParameters total number of free parameters in the gradient
713      * @param indices indices of the differentiation parameters in derivatives computations
714      * @return value with derivatives, will throw exception if called on a PDriver having
715      * several values driven
716      * @since 10.2
717      */
718     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
719         Integer index = null;
720         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
721             index = indices.get(span.getData());
722             if (index != null) {
723                 break;
724             }
725         }
726         return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue());
727     }
728 
729     /** Get the value as a gradient at special date.
730      * @param freeParameters total number of free parameters in the gradient
731      * @param indices indices of the differentiation parameters in derivatives computations,
732      * must be span name and not driver name
733      * @param date date for which the value wants to be known. Only if
734      * parameter driver has 1 value estimated over the all orbit determination
735      * period (not validity period intervals for estimation), the date value can
736      * be <em>{@code null}</em> and then the only estimated value will be
737      * returned
738      * @return value with derivatives
739      * @since 10.2
740      */
741     public Gradient getValue(final int freeParameters, final Map<String, Integer> indices, final AbsoluteDate date) {
742         Integer index = null;
743         for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) {
744             index = indices.get(span.getData());
745             if (index != null) {
746                 break;
747             }
748         }
749         return (index == null) ? Gradient.constant(freeParameters, getValue(date)) : Gradient.variable(freeParameters, index, getValue(date));
750     }
751 
752     /** Set parameter value at specific date.
753      * <p>
754      * If {@code newValue} is below {@link #getMinValue()}, it will
755      * be silently set to {@link #getMinValue()}. If {@code newValue} is
756      * above {@link #getMaxValue()}, it will be silently set to {@link
757      * #getMaxValue()}.
758      * </p>
759      * @param date date for which the value wants to be set. Only if
760      * parameter driver has 1 value estimated over the all orbit determination
761      * period (not validity period intervals for estimation), the date value can
762      * be <em>{@code null}</em>
763      * @param newValue new value to set
764      */
765     public void setValue(final double newValue, final AbsoluteDate date) {
766 
767         double previousValue = Double.NaN;
768         AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;
769 
770         // if valid for infinity (only 1 value estimation for the orbit determination )
771         if (getNbOfValues() == 1) {
772             previousValue = this.getValue(referenceDateSpan);
773             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
774         // if needs to be estimated per time range / validity period
775 
776         // if several value intervals
777         } else {
778             final Span<Double> valueSpan = valueSpanMap.getSpan(date);
779             previousValue = valueSpan.getData();
780             referenceDateSpan = valueSpan.getStart();
781             // if the Span considered is from past infinity to valueSpanEndDate it is
782             // impossible to addValidAfter past infinity because it is creating a new span that
783             // is why the below trick was set up
784             if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) {
785                 referenceDateSpan = valueSpan.getEnd();
786                 this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
787                                                  referenceDateSpan, false);
788             } else {
789                 this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)),
790                                                 referenceDateSpan, false);
791             }
792         }
793 
794         for (final ParameterObserver observer : observers) {
795             observer.valueChanged(previousValue, this, date);
796         }
797     }
798 
799 
800     /** Set parameter value. Only usable on ParameterDriver
801      * which have only 1 span on their TimeSpanMap value (that is
802      * to say for which the setPeriod method wasn't called)
803      * <p>
804      * If {@code newValue} is below {@link #getMinValue()}, it will
805      * be silently set to {@link #getMinValue()}. If {@code newValue} is
806      * above {@link #getMaxValue()}, it will be silently set to {@link
807      * #getMaxValue()}.
808      * </p>
809      * @param newValue new value to set
810      */
811     public void setValue(final double newValue) {
812         if (getNbOfValues() == 1) {
813             final AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH;
814             final double previousValue = this.getValue(referenceDateSpan);
815             this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue)));
816             for (final ParameterObserver observer : observers) {
817                 observer.valueChanged(previousValue, this, referenceDateSpan);
818             }
819         } else {
820             throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "setValue(date)");
821         }
822     }
823 
824     /** Configure a parameter selection status.
825      * <p>
826      * Selection is used for estimated parameters in orbit determination,
827      * or to compute the Jacobian matrix in partial derivatives computation.
828      * </p>
829      * @param selected if true the parameter is selected,
830      * otherwise it will be fixed
831      */
832     public void setSelected(final boolean selected) {
833         final boolean previousSelection = isSelected();
834         this.selected = selected;
835         for (final ParameterObserver observer : observers) {
836             observer.selectionChanged(previousSelection, this);
837         }
838     }
839 
840     /** Check if parameter is selected.
841      * <p>
842      * Selection is used for estimated parameters in orbit determination,
843      * or to compute the Jacobian matrix in partial derivatives computation.
844      * </p>
845      * @return true if parameter is selected, false if it is not
846      */
847     public boolean isSelected() {
848         return selected;
849     }
850 
851     /** Set parameter estimation to continuous, by default step estimation.
852      * <p> Continuous estimation : when a value wants to be known at date
853      * t, the value returned will be an interpolation between start value
854      * of the span corresponding to date t and end value (which corresponds
855      * to the start of the next span).
856      * </p>
857      * <p> Step estimation : when a value wants to be
858      * known at date t, the value returned will be the value of the beginning
859      * of span corresponding to date t, step estimation.
860      * </p>
861      * @param continuous if true the parameter will be estimated
862      * with continuous estimation, if false with step estimation.
863      */
864     public void setContinuousEstimation(final boolean continuous) {
865         final boolean previousEstimation = isContinuousEstimation();
866         this.isEstimationContinuous = continuous;
867         for (final ParameterObserver observer : observers) {
868             observer.estimationTypeChanged(previousEstimation, this);
869         }
870     }
871 
872     /** Check if parameter estimation is continuous, that is to say when
873      * a value wants to be known at date t, the value returned
874      * will be an interpolation between start value on span corresponding
875      * for date t and end value (which corresponds to the start of the next
876      * span), continuous estimation. Or not continuous, that is to say when a value wants to be
877      * known at date t, the value returned will be the value of the start
878      * of span corresponding to date t, step estimation.
879      * @return true if continuous estimation/definition, false if step estimation/definition
880      * @since 12.0
881      */
882     public boolean isContinuousEstimation() {
883         return isEstimationContinuous;
884     }
885 
886     /** Get a text representation of the parameter.
887      * @return text representation of the parameter, in the form name = value.
888      */
889     public String toString() {
890         return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH);
891     }
892 
893 }