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 }