1 /* Copyright 2002-2026 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.drivers;
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.exception.LocalizedCoreFormats;
27 import org.hipparchus.util.FastMath;
28 import org.hipparchus.util.Precision;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.errors.OrekitMessages;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.TimeInterval;
33
34 /** Class allowing to drive the value of a parameter.
35 * <p>
36 * This class is typically used as a bridge between an estimation algorithm
37 * (typically orbit determination or optimizer) and an internal parameter in
38 * a physical model that needs to be tuned. The physical model will expose to
39 * the algorithm a set of instances of this class so the algorithm can call the
40 * {@link #setValue(double)} method to update the parameter value.
41 * </p>
42 * <p>
43 * Any object can be notified when any of value, name, selection status… are changed.
44 * This is done by {@link #addObserver(ParameterObserver)} registering} a
45 * {@link ParameterObserver ParameterObserver} to the parameter driver.
46 * <p>
47 * This design has two major goals. First, it allows an external algorithm to drive
48 * internal parameters blindly, as it only needs to get a list of instances of this
49 * class, without knowing what they really drive. Second, it allows the physical
50 * model to not expose directly setters methods for its parameters. In order to be
51 * able to modify the parameter value, the algorithm <em>must</em> retrieve a
52 * parameter driver.
53 * </p>
54 * <p>
55 * As of versions 12.X and 13.X, it was possible to set up time-dependent values
56 * within a single {@code ParameterDriver}. This feature has been replaced by
57 * {@link ParameterDriversSequence} as of 14.0, which is simpler and also allows
58 * finer selection, making it possible to select only a subset of the parameters
59 * along a timeline. Starting with version 14.0, {@code ParameterDriver} instances
60 * only hold one value, which can have a restricted validity range.
61 * </p>
62 * @see ParameterObserver
63 * @author Luc Maisonobe
64 * @author Melina Vanel
65 * @since 8.0
66 */
67 public class ParameterDriver {
68
69 /** Name of the parameter. */
70 private String name;
71
72 /** Reference value. */
73 private double referenceValue;
74
75 /** Scaling factor. */
76 private double scale;
77
78 /** Current value.
79 * @since 14.0
80 */
81 private double value;
82
83 /** Minimum value. */
84 private double minValue;
85
86 /** Maximum value. */
87 private double maxValue;
88
89 /** Reference date.
90 * @since 9.0
91 */
92 private AbsoluteDate referenceDate;
93
94 /** Validity interval.
95 * @since 14.0
96 */
97 private TimeInterval validity;
98
99 /** Selection status.
100 * <p>
101 * Selection is used for estimated parameters in orbit determination,
102 * or to compute the Jacobian matrix in partial derivatives computation.
103 * </p>
104 */
105 private boolean selected;
106
107 /** Observers observing this driver. */
108 private final List<ParameterObserver> observers;
109
110 /**
111 * Simple constructor.
112 * <p>
113 * At construction, the parameter is configured as <em>not</em> selected, the reference date is set to {@code null},
114 * the value is set to the {@code referenceValue}.
115 * </p>
116 * @param name name of the parameter
117 * @param referenceValue reference value of the parameter
118 * @param scale scaling factor to convert the parameters value to non-dimensional (typically set to the
119 * expected standard deviation of the parameter), it must be non-zero
120 * @param minValue minimum value allowed
121 * @param maxValue maximum value allowed
122 * @param validity validity interval
123 */
124 public ParameterDriver(final String name,
125 final double referenceValue, final double scale,
126 final double minValue, final double maxValue,
127 final TimeInterval validity) {
128
129 if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
130 throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER, name, scale);
131 }
132
133 this.name = name;
134 this.referenceValue = referenceValue;
135 this.scale = scale;
136 this.value = referenceValue;
137 this.minValue = minValue;
138 this.maxValue = maxValue;
139 this.validity = validity;
140 this.referenceDate = null;
141 this.selected = false;
142 this.observers = new ArrayList<>();
143
144 }
145
146 /** Add an observer for this driver.
147 * <p>
148 * The observer {@link ParameterObserver#valueChanged(double, ParameterDriver)
149 * valueChanged} method is called once automatically when the
150 * observer is added, and then called at each value change.
151 * </p>
152 * @param observer observer to add
153 */
154 public void addObserver(final ParameterObserver observer) {
155 observers.add(observer);
156 }
157
158 /** Remove an observer.
159 * @param observer observer to remove
160 * @since 9.1
161 */
162 public void removeObserver(final ParameterObserver observer) {
163 for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
164 if (iterator.next() == observer) {
165 iterator.remove();
166 return;
167 }
168 }
169 }
170
171 /** Replace an observer.
172 * @param oldObserver observer to replace
173 * @param newObserver new observer to use
174 * @since 10.1
175 */
176 public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) {
177 for (int i = 0; i < observers.size(); ++i) {
178 if (observers.get(i) == oldObserver) {
179 observers.set(i, newObserver);
180 }
181 }
182 }
183
184 /** Get the observers for this driver.
185 * @return an unmodifiable view of the observers for this driver
186 * @since 9.1
187 */
188 public List<ParameterObserver> getObservers() {
189 return Collections.unmodifiableList(observers);
190 }
191
192 /** Get parameter driver general name.
193 * @return name
194 */
195 public String getName() {
196 return name;
197 }
198
199 /** Change the general name of this parameter driver.
200 * @param name new name
201 */
202 public void setName(final String name) {
203 final String previousName = this.name;
204 this.name = name;
205 for (final ParameterObserver observer : observers) {
206 observer.nameChanged(previousName, this);
207 }
208 }
209
210 /** Get reference parameter value.
211 * @return reference parameter value
212 */
213 public double getReferenceValue() {
214 return referenceValue;
215 }
216
217 /** Set reference parameter value.
218 * @since 9.3
219 * @param referenceValue the reference value to set.
220 */
221 public void setReferenceValue(final double referenceValue) {
222 final double previousReferenceValue = this.referenceValue;
223 this.referenceValue = referenceValue;
224 for (final ParameterObserver observer : observers) {
225 observer.referenceValueChanged(previousReferenceValue, this);
226 }
227 }
228
229 /** Get minimum parameter value.
230 * @return minimum parameter value
231 */
232 public double getMinValue() {
233 return minValue;
234 }
235
236 /** Set minimum parameter value.
237 * @since 9.3
238 * @param minValue the minimum value to set.
239 */
240 public void setMinValue(final double minValue) {
241
242 // safety check
243 if (minValue > maxValue) {
244 throw new OrekitException(LocalizedCoreFormats.NUMBER_TOO_LARGE, minValue, maxValue);
245 }
246
247 if (value < minValue) {
248 // clip value to minimum
249 setValue(minValue);
250 }
251
252 final double previousMinValue = this.minValue;
253 this.minValue = minValue;
254 for (final ParameterObserver observer : observers) {
255 observer.minValueChanged(previousMinValue, this);
256 }
257
258 }
259
260 /** Get maximum parameter value.
261 * @return maximum parameter value
262 */
263 public double getMaxValue() {
264 return maxValue;
265 }
266
267 /** Set maximum parameter value.
268 * @since 9.3
269 * @param maxValue the maximum value to set.
270 */
271 public void setMaxValue(final double maxValue) {
272
273 // safety check
274 if (maxValue < minValue) {
275 throw new OrekitException(LocalizedCoreFormats.NUMBER_TOO_SMALL, maxValue, minValue);
276 }
277
278 if (value > maxValue) {
279 // clip value to maximum
280 setValue(maxValue);
281 }
282
283 final double previousMaxValue = this.maxValue;
284 this.maxValue = maxValue;
285 for (final ParameterObserver observer : observers) {
286 observer.maxValueChanged(previousMaxValue, this);
287 }
288
289 }
290
291 /** Get scale.
292 * @return scale
293 */
294 public double getScale() {
295 return scale;
296 }
297
298 /** Set scale.
299 * @since 9.3
300 * @param scale the scale to set.
301 */
302 public void setScale(final double scale) {
303 final double previousScale = this.scale;
304 this.scale = scale;
305 for (final ParameterObserver observer : observers) {
306 observer.scaleChanged(previousScale, this);
307 }
308 }
309
310 /** Get normalized value.
311 * <p>
312 * The normalized value is a non-dimensional value
313 * suitable for use as part of a vector in an optimization
314 * process. It is computed as {@code (current - reference)/scale}.
315 * </p>
316 * @return normalized value
317 */
318 public double getNormalizedValue() {
319 return (value - referenceValue) / scale;
320 }
321
322 /** Set normalized value.
323 * <p>
324 * The normalized value is a non-dimensional value
325 * suitable for use as part of a vector in an optimization
326 * process. It is computed as {@code (current - reference)/scale}.
327 * </p>
328 * @param normalized value
329 */
330 public void setNormalizedValue(final double normalized) {
331 setValue(referenceValue + scale * normalized);
332 }
333
334 /** Get current reference date.
335 * @return current reference date (null if it was never set)
336 * @since 9.0
337 */
338 public AbsoluteDate getReferenceDate() {
339 return referenceDate;
340 }
341
342 /** Set reference date.
343 * @param newReferenceDate new reference date
344 * @since 9.0
345 */
346 public void setReferenceDate(final AbsoluteDate newReferenceDate) {
347 final AbsoluteDate previousReferenceDate = getReferenceDate();
348 referenceDate = newReferenceDate;
349 for (final ParameterObserver observer : observers) {
350 observer.referenceDateChanged(previousReferenceDate, this);
351 }
352 }
353
354 /** Get current parameter value.
355 * @return current parameter value
356 */
357 public double getValue() {
358 return value;
359 }
360
361 /** Get the value as a gradient.
362 * @param freeParameters total number of free parameters in the gradient
363 * @param indices indices of the differentiation parameters in derivatives computations
364 * @return value with derivatives
365 * @since 10.2
366 */
367 public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) {
368 final Integer index = indices.get(getName());
369 return (index == null) ?
370 Gradient.constant(freeParameters, getValue()) :
371 Gradient.variable(freeParameters, index, getValue());
372 }
373
374 /** Set parameter value.
375 * <p>
376 * If {@code newValue} is below {@link #getMinValue()}, it will
377 * be silently set to {@link #getMinValue()}. If {@code newValue} is
378 * above {@link #getMaxValue()}, it will be silently set to {@link
379 * #getMaxValue()}.
380 * </p>
381 * @param newValue new value to set
382 */
383 public void setValue(final double newValue) {
384 final double previousValue = value;
385 value = FastMath.max(minValue, FastMath.min(maxValue, newValue));
386 for (final ParameterObserver observer : observers) {
387 observer.valueChanged(previousValue, this);
388 }
389 }
390
391 /** Get the validity interval.
392 * @return validity interval
393 * @since 14.0
394 */
395 public TimeInterval getValidity() {
396 return validity;
397 }
398
399 /** Set the validity interval.
400 * @param validity validity interval
401 * @since 14.0
402 */
403 public void setValidity(final TimeInterval validity) {
404 final TimeInterval previousValidity = getValidity();
405 this.validity = validity;
406 for (final ParameterObserver observer : observers) {
407 observer.validityChanged(previousValidity, this);
408 }
409 }
410
411 /** Configure a parameter selection status.
412 * <p>
413 * Selection is used for estimated parameters in orbit determination,
414 * or to compute the Jacobian matrix in partial derivatives computation.
415 * </p>
416 * @param selected if true the parameter is selected,
417 * otherwise it will be fixed
418 */
419 public void setSelected(final boolean selected) {
420 final boolean previousSelection = isSelected();
421 this.selected = selected;
422 for (final ParameterObserver observer : observers) {
423 observer.selectionChanged(previousSelection, this);
424 }
425 }
426
427 /** Check if parameter is selected.
428 * <p>
429 * Selection is used for estimated parameters in orbit determination,
430 * or to compute the Jacobian matrix in partial derivatives computation.
431 * </p>
432 * @return true if parameter is selected, false if it is not
433 */
434 public boolean isSelected() {
435 return selected;
436 }
437
438 /** Get a text representation of the parameter.
439 * @return text representation of the parameter, in the form name = value.
440 */
441 public String toString() {
442 return name + " = " + value;
443 }
444
445 }