1 /* Copyright 2002-2019 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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.DSFactory;
26 import org.hipparchus.analysis.differentiation.DerivativeStructure;
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
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
37 * algorithm (typically orbit determination or optimizer) and an
38 * internal parameter in a physical model that needs to be tuned,
39 * or a bridge between a finite differences algorithm and an
40 * internal parameter in a physical model that needs to be slightly
41 * offset. The physical model will expose to the algorithm a
42 * set of instances of this class so the algorithm can call the
43 * {@link #setValue(double)} method to update the
44 * parameter value. Each time the value is set, the physical model
45 * will be notified as it will register a {@link ParameterObserver
46 * ParameterObserver} for this purpose.
47 * </p>
48 * <p>
49 * This design has two major goals. First, it allows an external
50 * algorithm to drive internal parameters almost anonymously, as it only
51 * needs to get a list of instances of this class, without knowing
52 * what they really drive. Second, it allows the physical model to
53 * not expose directly setters methods for its parameters. In order
54 * to be able to modify the parameter value, the algorithm
55 * <em>must</em> retrieve a parameter driver.
56 * </p>
57 * @see ParameterObserver
58 * @author Luc Maisonobe
59 * @since 8.0
60 */
61 public class ParameterDriver {
62
63 /** Name of the parameter. */
64 private String name;
65
66 /** Reference value. */
67 private double referenceValue;
68
69 /** Scaling factor. */
70 private double scale;
71
72 /** Minimum value. */
73 private double minValue;
74
75 /** Maximum value. */
76 private double maxValue;
77
78 /** Reference date.
79 * @since 9.0
80 */
81 private AbsoluteDate referenceDate;
82
83 /** Current value. */
84 private double value;
85
86 /** Selection status.
87 * <p>
88 * Selection is used for estimated parameters in orbit determination,
89 * or to compute the Jacobian matrix in partial derivatives computation.
90 * </p>
91 */
92 private boolean selected;
93
94 /** Observers observing this driver. */
95 private final List<ParameterObserver> observers;
96
97 /** Simple constructor.
98 * <p>
99 * At construction, the parameter is configured as <em>not</em> selected,
100 * the reference date is set to {@code null} and the value is set to the
101 * {@code referenceValue}.
102 * </p>
103 * @param name name of the parameter
104 * @param referenceValue reference value of the parameter
105 * @param scale scaling factor to convert the parameters value to
106 * non-dimensional (typically set to the expected standard deviation of the
107 * parameter), it must be non-zero
108 * @param minValue minimum value
109 * @param maxValue maximum value
110 */
111 public ParameterDriver(final String name, final double referenceValue,
112 final double scale, final double minValue,
113 final double maxValue) {
114 if (FastMath.abs(scale) <= Precision.SAFE_MIN) {
115 throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER,
116 name, scale);
117 }
118 this.name = name;
119 this.referenceValue = referenceValue;
120 this.scale = scale;
121 this.minValue = minValue;
122 this.maxValue = maxValue;
123 this.referenceDate = null;
124 this.value = referenceValue;
125 this.selected = false;
126 this.observers = new ArrayList<>();
127 }
128
129
130 /** Add an observer for this driver.
131 * <p>
132 * The observer {@link ParameterObserver#valueChanged(double, ParameterDriver)
133 * valueChanged} method is called once automatically when the
134 * observer is added, and then called at each value change.
135 * </p>
136 * @param observer observer to add
137 * while being updated
138 */
139 public void addObserver(final ParameterObserver observer) {
140 observers.add(observer);
141 observer.valueChanged(getValue(), this);
142 }
143
144 /** Remove an observer.
145 * @param observer observer to remove
146 * @since 9.1
147 */
148 public void removeObserver(final ParameterObserver observer) {
149 for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) {
150 if (iterator.next() == observer) {
151 iterator.remove();
152 return;
153 }
154 }
155 }
156
157 /** Get the observers for this driver.
158 * @return an unmodifiable view of the observers for this driver
159 * @since 9.1
160 */
161 public List<ParameterObserver> getObservers() {
162 return Collections.unmodifiableList(observers);
163 }
164
165 /** Change the name of this parameter driver.
166 * @param name new name
167 */
168 public void setName(final String name) {
169 final String previousName = this.name;
170 this.name = name;
171 for (final ParameterObserver observer : observers) {
172 observer.nameChanged(previousName, this);
173 }
174 }
175
176 /** Get name.
177 * @return name
178 */
179 public String getName() {
180 return name;
181 }
182
183 /** Get reference parameter value.
184 * @return reference parameter value
185 */
186 public double getReferenceValue() {
187 return referenceValue;
188 }
189
190 /** Set reference parameter value.
191 * @since 9.3
192 * @param referenceValue the reference value to set.
193 */
194 public void setReferenceValue(final double referenceValue) {
195 final double previousReferenceValue = this.referenceValue;
196 this.referenceValue = referenceValue;
197 for (final ParameterObserver observer : observers) {
198 observer.referenceValueChanged(previousReferenceValue, this);
199 }
200 }
201
202 /** Get minimum parameter value.
203 * @return minimum parameter value
204 */
205 public double getMinValue() {
206 return minValue;
207 }
208
209 /** Set minimum parameter value.
210 * @since 9.3
211 * @param minValue the minimum value to set.
212 */
213 public void setMinValue(final double minValue) {
214 final double previousMinValue = this.minValue;
215 this.minValue = minValue;
216 for (final ParameterObserver observer : observers) {
217 observer.minValueChanged(previousMinValue, this);
218 }
219 // Check if current value is not out of min/max range
220 setValue(value);
221 }
222
223 /** Get maximum parameter value.
224 * @return maximum parameter value
225 */
226 public double getMaxValue() {
227 return maxValue;
228 }
229
230 /** Set maximum parameter value.
231 * @since 9.3
232 * @param maxValue the maximum value to set.
233 */
234 public void setMaxValue(final double maxValue) {
235 final double previousMaxValue = this.maxValue;
236 this.maxValue = maxValue;
237 for (final ParameterObserver observer : observers) {
238 observer.maxValueChanged(previousMaxValue, this);
239 }
240 // Check if current value is not out of min/max range
241 setValue(value);
242 }
243
244 /** Get scale.
245 * @return scale
246 */
247 public double getScale() {
248 return scale;
249 }
250
251 /** Set scale.
252 * @since 9.3
253 * @param scale the scale to set.
254 */
255 public void setScale(final double scale) {
256 final double previousScale = this.scale;
257 this.scale = scale;
258 for (final ParameterObserver observer : observers) {
259 observer.scaleChanged(previousScale, this);
260 }
261 }
262
263 /** Get normalized value.
264 * <p>
265 * The normalized value is a non-dimensional value
266 * suitable for use as part of a vector in an optimization
267 * process. It is computed as {@code (current - reference)/scale}.
268 * </p>
269 * @return normalized value
270 */
271 public double getNormalizedValue() {
272 return (value - referenceValue) / scale;
273 }
274
275 /** Set normalized value.
276 * <p>
277 * The normalized value is a non-dimensional value
278 * suitable for use as part of a vector in an optimization
279 * process. It is computed as {@code (current - reference)/scale}.
280 * </p>
281 * @param normalized value
282 */
283 public void setNormalizedValue(final double normalized) {
284 setValue(referenceValue + scale * normalized);
285 }
286
287 /** Get current reference date.
288 * @return current reference date (null if it was never set)
289 * @since 9.0
290 */
291 public AbsoluteDate getReferenceDate() {
292 return referenceDate;
293 }
294
295 /** Set reference date.
296 * @param newReferenceDate new reference date
297 * @since 9.0
298 */
299 public void setReferenceDate(final AbsoluteDate newReferenceDate) {
300 final AbsoluteDate previousReferenceDate = getReferenceDate();
301 referenceDate = newReferenceDate;
302 for (final ParameterObserver observer : observers) {
303 observer.referenceDateChanged(previousReferenceDate, this);
304 }
305 }
306
307 /** Get current parameter value.
308 * @return current parameter value
309 */
310 public double getValue() {
311 return value;
312 }
313
314 /** Get the value as a derivative structure.
315 * @param factory factory for the derivatives
316 * @param indices indices of the differentiation parameters in derivatives computations
317 * @return value with derivatives
318 * @since 9.3
319 */
320 public DerivativeStructure getValue(final DSFactory factory, final Map<String, Integer> indices) {
321 final Integer index = indices.get(name);
322 return (index == null) ? factory.constant(value) : factory.variable(index, value);
323 }
324
325 /** Set parameter value.
326 * <p>
327 * If {@code newValue} is below {@link #getMinValue()}, it will
328 * be silently set to {@link #getMinValue()}. If {@code newValue} is
329 * above {@link #getMaxValue()}, it will be silently set to {@link
330 * #getMaxValue()}.
331 * </p>
332 * @param newValue new value
333 */
334 public void setValue(final double newValue) {
335 final double previousValue = getValue();
336 value = FastMath.max(minValue, FastMath.min(maxValue, newValue));
337 for (final ParameterObserver observer : observers) {
338 observer.valueChanged(previousValue, this);
339 }
340 }
341
342 /** Configure a parameter selection status.
343 * <p>
344 * Selection is used for estimated parameters in orbit determination,
345 * or to compute the Jacobian matrix in partial derivatives computation.
346 * </p>
347 * @param selected if true the parameter is selected,
348 * otherwise it will be fixed
349 */
350 public void setSelected(final boolean selected) {
351 final boolean previousSelection = isSelected();
352 this.selected = selected;
353 for (final ParameterObserver observer : observers) {
354 observer.selectionChanged(previousSelection, this);
355 }
356 }
357
358 /** Check if parameter is selected.
359 * <p>
360 * Selection is used for estimated parameters in orbit determination,
361 * or to compute the Jacobian matrix in partial derivatives computation.
362 * </p>
363 * @return true if parameter is selected, false if it is not
364 */
365 public boolean isSelected() {
366 return selected;
367 }
368
369 /** Get a text representation of the parameter.
370 * @return text representation of the parameter, in the form name = value.
371 */
372 public String toString() {
373 return name + " = " + value;
374 }
375
376 }