1   /* Copyright 2002-2016 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.List;
21  
22  import org.hipparchus.util.FastMath;
23  import org.orekit.errors.OrekitException;
24  
25  
26  /** Class allowing to drive the value of a parameter.
27   * <p>
28   * This class is typically used as a bridge between an estimation
29   * algorithm (typically orbit determination or optimizer) and an
30   * internal parameter in a physical model that needs to be tuned,
31   * or a bridge between a finite differences algorithm and an
32   * internal parameter in a physical model that needs to be slightly
33   * offset. The physical model will expose to the algorithm a
34   * set of instances of this class so the algorithm can call the
35   * {@link #setValue(double)} method to update the
36   * parameter value. Each time the value is set, the physical model
37   * will be notified as it will register a {@link ParameterObserver
38   * ParameterObserver} for this purpose.
39   * </p>
40   * <p>
41   * This design has two major goals. First, it allows an external
42   * algorithm to drive internal parameters almost anonymously, as it only
43   * needs to get a list of instances of this class, without knowing
44   * what they really drive. Second, it allows the physical model to
45   * not expose directly setters methods for its parameters. In order
46   * to be able to modify the parameter value, the algorithm
47   * <em>must</em> retrieve a parameter driver.
48   * </p>
49   * @see ParameterObserver
50   * @author Luc Maisonobe
51   * @since 8.0
52   */
53  public class ParameterDriver {
54  
55      /** Name of the parameter. */
56      private final String name;
57  
58      /** Reference value. */
59      private final double referenceValue;
60  
61      /** Scaling factor. */
62      private final double scale;
63  
64      /** Minimum value. */
65      private final double minValue;
66  
67      /** Maximum value. */
68      private final double maxValue;
69  
70      /** Current value. */
71      private double value;
72  
73      /** Selection status.
74       * <p>
75       * Selection is used for estimated parameters in orbit determination,
76       * or to compute the Jacobian matrix in partial derivatives computation.
77       * </p>
78       */
79      private boolean selected;
80  
81      /** Observers observing this driver. */
82      private final List<ParameterObserver> observers;
83  
84      /** Simple constructor.
85       * <p>
86       * At construction, the parameter is configured as <em>not</em> selected,
87       * and the value is set to the {@code referenceValue}.
88       * </p>
89       * @param name name of the parameter
90       * @param referenceValue reference value of the parameter
91       * @param scale scaling factor to convert the parameters value to
92       * non-dimensional (typically set to the expected standard deviation of the
93       * parameter)
94       * @param minValue minimum value
95       * @param maxValue maximum value
96       */
97      public ParameterDriver(final String name, final double referenceValue,
98                             final double scale, final double minValue,
99                             final double maxValue) {
100         this.name           = name;
101         this.referenceValue = referenceValue;
102         this.scale          = scale;
103         this.minValue       = minValue;
104         this.maxValue       = maxValue;
105         this.value          = referenceValue;
106         this.selected       = false;
107         this.observers      = new ArrayList<ParameterObserver>();
108     }
109 
110 
111     /** Add an observer for this driver.
112      * <p>
113      * The observer {@link ParameterObserver#valueChanged(double, ParameterDriver)
114      * valueChanged} method is called once automatically when the
115      * observer is added, and then called at each value change.
116      * </p>
117      * @param observer observer to add
118      * @exception OrekitException if the observer triggers one
119      * while being updated
120      */
121     public void addObserver(final ParameterObserver observer)
122         throws OrekitException {
123         observers.add(observer);
124         observer.valueChanged(getValue(), this);
125     }
126 
127     /** Get name.
128      * @return name
129      */
130     public String getName() {
131         return name;
132     }
133 
134     /** Get reference parameter value.
135      * @return reference parameter value
136      */
137     public double getReferenceValue() {
138         return referenceValue;
139     }
140 
141     /** Get minimum parameter value.
142      * @return minimum parameter value
143      */
144     public double getMinValue() {
145         return minValue;
146     }
147 
148     /** Get maximum parameter value.
149      * @return maximum parameter value
150      */
151     public double getMaxValue() {
152         return maxValue;
153     }
154 
155     /** Get scale.
156      * @return scale
157      */
158     public double getScale() {
159         return scale;
160     }
161 
162     /** Get normalized value.
163      * <p>
164      * The normalized value is a non-dimensional value
165      * suitable for use as part of a vector in an optimization
166      * process. It is computed as {@code (current - reference)/scale}.
167      * </p>
168      * @return normalized value
169      */
170     public double getNormalizedValue() {
171         return (value - referenceValue) / scale;
172     }
173 
174     /** Set normalized value.
175      * <p>
176      * The normalized value is a non-dimensional value
177      * suitable for use as part of a vector in an optimization
178      * process. It is computed as {@code (current - reference)/scale}.
179      * </p>
180      * @param normalized value
181      * @exception OrekitException if an observer throws one
182      */
183     public void setNormalizedValue(final double normalized) throws OrekitException {
184         setValue(referenceValue + scale * normalized);
185     }
186 
187     /** Get current parameter value.
188      * @return current parameter value
189      */
190     public double getValue() {
191         return value;
192     }
193 
194     /** Set parameter value.
195      * <p>
196      * If {@code newValue} is below {@link #getMinValue()}, it will
197      * be silently to {@link #getMinValue()}. If {@code newValue} is
198      * above {@link #getMaxValue()}, it will be silently to {@link
199      * #getMaxValue()}.
200      * </p>
201      * @param newValue new value
202      * @exception OrekitException if an observer throws one
203      */
204     public void setValue(final double newValue) throws OrekitException {
205         final double previousValue = getValue();
206         value = FastMath.max(minValue, FastMath.min(maxValue, newValue));
207         for (final ParameterObserver observer : observers) {
208             observer.valueChanged(previousValue, this);
209         }
210     }
211 
212     /** Configure a parameter selection status.
213      * <p>
214      * Selection is used for estimated parameters in orbit determination,
215      * or to compute the Jacobian matrix in partial derivatives computation.
216      * </p>
217      * @param selected if true the parameter is selected,
218      * otherwise it will be fixed
219      */
220     public void setSelected(final boolean selected) {
221         this.selected = selected;
222     }
223 
224     /** Check if parameter is selected.
225      * <p>
226      * Selection is used for estimated parameters in orbit determination,
227      * or to compute the Jacobian matrix in partial derivatives computation.
228      * </p>
229      * @return true if parameter is selected, false if it is not
230      */
231     public boolean isSelected() {
232         return selected;
233     }
234 
235     /** Get a text representation of the parameter.
236      * @return text representation of the parameter, in the form name = value.
237      */
238     public String toString() {
239         return name + " = " + value;
240     }
241 
242 }