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 * @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 {@link TimeSpanMap}.
278 * @return dates of the transitions {@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 + 0, currentNameSpan.getEnd(), false);
385 for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) {
386 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd());
387 nameSpanMap.addValidAfter(SPAN + name + spanNumber, currentNameSpan.getStart(), false);
388 }
389 } else {
390 nameSpanMap = new TimeSpanMap<>(SPAN + name + 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() + 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 + 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 + 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 }