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  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.hipparchus.analysis.differentiation.DerivativeStructure;
28  import org.hipparchus.linear.Array2DRowRealMatrix;
29  import org.hipparchus.linear.ArrayRealVector;
30  import org.hipparchus.linear.RealMatrix;
31  import org.hipparchus.linear.RealVector;
32  import org.hipparchus.optim.ConvergenceChecker;
33  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
34  import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
35  import org.hipparchus.optim.nonlinear.vector.leastsquares.MultivariateJacobianFunction;
36  import org.hipparchus.optim.nonlinear.vector.leastsquares.ParameterValidator;
37  import org.hipparchus.util.Pair;
38  import org.orekit.rugged.adjustment.measurements.Observables;
39  import org.orekit.rugged.adjustment.measurements.SensorToSensorMapping;
40  import org.orekit.rugged.api.Rugged;
41  import org.orekit.rugged.errors.RuggedException;
42  import org.orekit.rugged.errors.RuggedMessages;
43  import org.orekit.rugged.linesensor.LineSensor;
44  import org.orekit.rugged.linesensor.SensorPixel;
45  import org.orekit.rugged.utils.SpacecraftToObservedBody;
46  import org.orekit.time.AbsoluteDate;
47  import org.orekit.utils.ParameterDriver;
48  
49  /** Constructs the optimization problem for a list of tie points.
50   * @author Guylaine Prat
51   * @author Lucie Labat Allee
52   * @author Jonathan Guinet
53   * @author Luc Maisonobe
54   * @since 2.0
55   */
56  public class InterSensorsOptimizationProblemBuilder extends OptimizationProblemBuilder {
57  
58      /** Key for target. */
59      private static final String TARGET = "Target";
60  
61      /** Key for weight. */
62      private static final String WEIGHT = "Weight";
63  
64      /** List of rugged instance to refine.*/
65      private Map<String, Rugged> ruggedMap;
66  
67      /** Sensor to ground mapping to generate target tab for optimization.*/
68      private List<SensorToSensorMapping> sensorToSensorMappings;
69  
70      /** Targets and weights of optimization problem. */
71      private HashMap<String, double[] > targetAndWeight;
72  
73      /** Constructor.
74       * @param sensors list of sensors to refine
75       * @param measurements set of observables
76       * @param ruggedList names of rugged to refine
77       */
78      public InterSensorsOptimizationProblemBuilder(final List<LineSensor> sensors,
79                                                    final Observables measurements, final Collection<Rugged> ruggedList) {
80  
81          super(sensors, measurements);
82          this.ruggedMap = new LinkedHashMap<String, Rugged>();
83          for (final Rugged rugged : ruggedList) {
84              this.ruggedMap.put(rugged.getName(), rugged);
85          }
86          this.initMapping();
87      }
88  
89      /** {@inheritDoc} */
90      @Override
91      protected void initMapping() {
92  
93          this.sensorToSensorMappings = new ArrayList<SensorToSensorMapping>();
94  
95          for (final String ruggedNameA : this.ruggedMap.keySet()) {
96              for (final String ruggedNameB : this.ruggedMap.keySet()) {
97  
98                  for (final LineSensor sensorA : this.getSensors()) {
99                      for (final LineSensor sensorB : this.getSensors()) {
100 
101                         final String sensorNameA = sensorA.getName();
102                         final String sensorNameB = sensorB.getName();
103                         final SensorToSensorMapping mapping = this.getMeasurements().getInterMapping(ruggedNameA, sensorNameA, ruggedNameB, sensorNameB);
104 
105                         if (mapping != null) {
106                             this.sensorToSensorMappings.add(mapping);
107                         }
108                     }
109                 }
110             }
111         }
112     }
113 
114     /** {@inheritDoc} */
115     @Override
116     protected void createTargetAndWeight() {
117 
118         int n = 0;
119         for (final SensorToSensorMapping reference : this.sensorToSensorMappings) {
120             n += reference.getMapping().size();
121         }
122         if (n == 0) {
123             throw new RuggedException(RuggedMessages.NO_REFERENCE_MAPPINGS);
124         }
125 
126         n = 2 * n;
127 
128         final double[] target = new double[n];
129         final double[] weight = new double[n];
130 
131         int k = 0;
132         for (final SensorToSensorMapping reference : this.sensorToSensorMappings) {
133 
134             // Get central body constraint weight
135             final double bodyConstraintWeight = reference.getBodyConstraintWeight();
136 
137             int i = 0;
138             for (Iterator<Map.Entry<SensorPixel, SensorPixel>> gtIt = reference.getMapping().iterator(); gtIt.hasNext(); i++) {
139 
140                 if (i == reference.getMapping().size()) break;
141 
142                 // Get LOS distance
143                 final Double losDistance  = reference.getLosDistance(i);
144 
145                 weight[k] = 1.0 - bodyConstraintWeight;
146                 target[k++] = losDistance.doubleValue();
147 
148                 // Get central body distance (constraint)
149                 final Double bodyDistance  = reference.getBodyDistance(i);
150                 weight[k] = bodyConstraintWeight;
151                 target[k++] = bodyDistance.doubleValue();
152             }
153         }
154 
155         this.targetAndWeight = new HashMap<String, double[]>();
156         this.targetAndWeight.put(TARGET, target);
157         this.targetAndWeight.put(WEIGHT, weight);
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     protected MultivariateJacobianFunction createFunction() {
163 
164         // model function
165         final MultivariateJacobianFunction model = point -> {
166 
167             // set the current parameters values
168             int i = 0;
169             for (final ParameterDriver driver : this.getDrivers()) {
170                 driver.setNormalizedValue(point.getEntry(i++));
171             }
172 
173             final double[] target = this.targetAndWeight.get(TARGET);
174 
175             // compute distance and its partial derivatives
176             final RealVector value = new ArrayRealVector(target.length);
177             final RealMatrix jacobian = new Array2DRowRealMatrix(target.length, this.getNbParams());
178 
179             int l = 0;
180             for (final SensorToSensorMapping reference : this.sensorToSensorMappings) {
181 
182                 final String ruggedNameA = reference.getRuggedNameA();
183                 final String ruggedNameB = reference.getRuggedNameB();
184                 final Rugged ruggedA = this.ruggedMap.get(ruggedNameA);
185                 if (ruggedA == null) {
186                     throw new RuggedException(RuggedMessages.INVALID_RUGGED_NAME);
187                 }
188 
189                 final Rugged ruggedB = this.ruggedMap.get(ruggedNameB);
190                 if (ruggedB == null) {
191                     throw new RuggedException(RuggedMessages.INVALID_RUGGED_NAME);
192                 }
193 
194                 for (final Map.Entry<SensorPixel, SensorPixel> mapping : reference.getMapping()) {
195 
196                     final SensorPixel spA = mapping.getKey();
197                     final SensorPixel spB = mapping.getValue();
198 
199                     final LineSensor lineSensorB = ruggedB.getLineSensor(reference.getSensorNameB());
200                     final LineSensor lineSensorA = ruggedA.getLineSensor(reference.getSensorNameA());
201 
202                     final AbsoluteDate dateA = lineSensorA.getDate(spA.getLineNumber());
203                     final AbsoluteDate dateB = lineSensorB.getDate(spB.getLineNumber());
204 
205                     final double pixelA = spA.getPixelNumber();
206                     final double pixelB = spB.getPixelNumber();
207 
208                     final SpacecraftToObservedBody scToBodyA = ruggedA.getScToBody();
209 
210                     final DerivativeStructure[] ilResult =
211                             ruggedB.distanceBetweenLOSderivatives(lineSensorA, dateA, pixelA, scToBodyA,
212                                     lineSensorB, dateB, pixelB, this.getGenerator());
213 
214                     // extract the value
215                     value.setEntry(l, ilResult[0].getValue());
216                     value.setEntry(l + 1, ilResult[1].getValue());
217 
218                     // extract the Jacobian
219                     final int[] orders = new int[this.getNbParams()];
220                     int m = 0;
221 
222                     for (final ParameterDriver driver : this.getDrivers()) {
223                         final double scale = driver.getScale();
224                         orders[m] = 1;
225                         jacobian.setEntry(l, m, ilResult[0].getPartialDerivative(orders) * scale);
226                         jacobian.setEntry(l + 1, m, ilResult[1].getPartialDerivative(orders) * scale);
227                         orders[m] = 0;
228                         m++;
229                     }
230 
231                     l += 2; // pass to the next evaluation
232                 }
233             }
234 
235             // distance result with Jacobian for all reference points
236             return new Pair<RealVector, RealMatrix>(value, jacobian);
237         };
238 
239         return model;
240     }
241 
242 
243     /** Least square problem builder.
244      * @param maxEvaluations maxIterations and evaluations
245      * @param convergenceThreshold parameter convergence threshold
246      * @return the least square problem
247      */
248     @Override
249     public final LeastSquaresProblem build(final int maxEvaluations, final double convergenceThreshold) {
250 
251         this.createTargetAndWeight();
252         final double[] target = this.targetAndWeight.get(TARGET);
253         final double[] start = this.createStartTab();
254         final ParameterValidator validator = this.createParameterValidator();
255         final ConvergenceChecker<LeastSquaresProblem.Evaluation> checker = this.createChecker(convergenceThreshold);
256         final MultivariateJacobianFunction model = this.createFunction();
257         return new LeastSquaresBuilder()
258                         .lazyEvaluation(false).maxIterations(maxEvaluations)
259                         .maxEvaluations(maxEvaluations).weight(null).start(start)
260                         .target(target).parameterValidator(validator).checker(checker)
261                         .model(model).build();
262     }
263 }