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 }