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 }