1   /* Copyright 2013-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.rugged.adjustment;
18  
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum;
27  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
28  import org.orekit.rugged.adjustment.measurements.Observables;
29  import org.orekit.rugged.api.Rugged;
30  import org.orekit.rugged.errors.RuggedException;
31  import org.orekit.rugged.errors.RuggedMessages;
32  import org.orekit.rugged.linesensor.LineSensor;
33  
34  /** Create adjustment context for viewing model refining.
35   * @author Lucie LabatAllee
36   * @author Jonathan Guinet
37   * @author Luc Maisonobe
38   * @author Guylaine Prat
39   * @since 2.0
40   */
41  public class AdjustmentContext {
42  
43      /** List of Rugged instances to optimize. */
44      private final Map<String, Rugged> viewingModel;
45  
46      /** Set of measurements. */
47      private final Observables measurements;
48  
49      /** Least square optimizer choice.*/
50      private OptimizerId optimizerID;
51  
52  
53      /** Build a new instance.
54       * The default optimizer is Gauss Newton with QR decomposition.
55       * @param viewingModel viewing model
56       * @param measurements control and tie points
57       */
58      public AdjustmentContext(final Collection<Rugged> viewingModel, final Observables measurements) {
59  
60          this.viewingModel = new HashMap<String, Rugged>();
61          for (final Rugged r : viewingModel) {
62              this.viewingModel.put(r.getName(), r);
63          }
64          this.measurements = measurements;
65          this.optimizerID = OptimizerId.GAUSS_NEWTON_QR;
66      }
67  
68      /** Setter for optimizer algorithm.
69       * @param optimizerId the chosen algorithm
70       */
71      public void setOptimizer(final OptimizerId optimizerId)
72      {
73          this.optimizerID = optimizerId;
74      }
75  
76      /**
77       * Estimate the free parameters in viewing model to match specified sensor
78       * to ground mappings.
79       * <p>
80       * This method is typically used for calibration of on-board sensor
81       * parameters, like rotation angles polynomial coefficients.
82       * </p>
83       * <p>
84       * Before using this method, the {@link org.orekit.utils.ParameterDriver viewing model
85       * parameters} retrieved by calling the
86       * {@link LineSensor#getParametersDrivers() getParametersDrivers()} method
87       * on the desired sensors must be configured. The parameters that should be
88       * estimated must have their {@link org.orekit.utils.ParameterDriver#setSelected(boolean)
89       * selection status} set to {@code true} whereas the parameters that should
90       * retain their current value must have their
91       * {@link org.orekit.utils.ParameterDriver#setSelected(boolean) selection status} set to
92       * {@code false}. If needed, the {@link org.orekit.utils.ParameterDriver#setValue(double)
93       * value} of the estimated/selected parameters can also be changed before
94       * calling the method, as this value will serve as the initial value in the
95       * estimation process.
96       * </p>
97       * <p>
98       * The method solves a least-squares problem to minimize the residuals
99       * between test locations and the reference mappings by adjusting the
100      * selected viewing models parameters.
101      * </p>
102      * <p>
103      * The estimated parameters can be retrieved after the method completes by
104      * calling again the {@link LineSensor#getParametersDrivers()
105      * getParametersDrivers()} method on the desired sensors and checking the
106      * updated values of the parameters. In fact, as the values of the
107      * parameters are already updated by this method, if users want to use the
108      * updated values immediately to perform new direct/inverse locations, they
109      * can do so without looking at the parameters: the viewing models are
110      * already aware of the updated parameters.
111      * </p>
112      *
113      * @param ruggedNameList list of rugged to refine
114      * @param maxEvaluations maximum number of evaluations
115      * @param parametersConvergenceThreshold convergence threshold on normalized
116      *                                       parameters (dimensionless, related to parameters scales)
117      * @return optimum of the least squares problem
118      */
119     public Optimum estimateFreeParameters(final Collection<String> ruggedNameList, final int maxEvaluations,
120                                           final double parametersConvergenceThreshold) {
121 
122         final List<Rugged> ruggedList = new ArrayList<Rugged>();
123         final List<LineSensor> selectedSensors = new ArrayList<LineSensor>();
124         for (String ruggedName : ruggedNameList) {
125             final Rugged rugged = this.viewingModel.get(ruggedName);
126             if (rugged == null) {
127                 throw new RuggedException(RuggedMessages.INVALID_RUGGED_NAME);
128             }
129 
130             ruggedList.add(rugged);
131             selectedSensors.addAll(rugged.getLineSensors());
132         }
133 
134         final LeastSquareAdjusteruster.html#LeastSquareAdjuster">LeastSquareAdjuster adjuster = new LeastSquareAdjuster(this.optimizerID);
135         LeastSquaresProblem theProblem = null;
136 
137         // builder
138         switch (ruggedList.size()) {
139             case 1:
140                 final Rugged rugged = ruggedList.get(0);
141                 final GroundOptimizationProblemBuilderOptimizationProblemBuilder">GroundOptimizationProblemBuilder groundOptimizationProblem = new GroundOptimizationProblemBuilder(selectedSensors, measurements, rugged);
142                 theProblem = groundOptimizationProblem.build(maxEvaluations, parametersConvergenceThreshold);
143                 break;
144             case 2:
145                 final InterSensorsOptimizationProblemBuilderOptimizationProblemBuilder">InterSensorsOptimizationProblemBuilder interSensorsOptimizationProblem = new InterSensorsOptimizationProblemBuilder(selectedSensors, measurements, ruggedList);
146                 theProblem = interSensorsOptimizationProblem.build(maxEvaluations, parametersConvergenceThreshold);
147                 break;
148             default :
149                 throw new RuggedException(RuggedMessages.UNSUPPORTED_REFINING_CONTEXT, ruggedList.size());
150         }
151 
152         return adjuster.optimize(theProblem);
153     }
154 }