1 /* Copyright 2002-2021 CS GROUP
2 * Licensed to CS GROUP (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.estimation.leastsquares;
18
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.Comparator;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.hipparchus.exception.LocalizedCoreFormats;
27 import org.hipparchus.exception.MathIllegalArgumentException;
28 import org.hipparchus.exception.MathRuntimeException;
29 import org.hipparchus.linear.RealMatrix;
30 import org.hipparchus.linear.RealVector;
31 import org.hipparchus.optim.ConvergenceChecker;
32 import org.hipparchus.optim.nonlinear.vector.leastsquares.EvaluationRmsChecker;
33 import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresBuilder;
34 import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer;
35 import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresOptimizer.Optimum;
36 import org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem;
37 import org.hipparchus.optim.nonlinear.vector.leastsquares.ParameterValidator;
38 import org.hipparchus.util.Incrementor;
39 import org.orekit.errors.OrekitException;
40 import org.orekit.estimation.measurements.EstimatedMeasurement;
41 import org.orekit.estimation.measurements.EstimationsProvider;
42 import org.orekit.estimation.measurements.ObservedMeasurement;
43 import org.orekit.orbits.Orbit;
44 import org.orekit.propagation.Propagator;
45 import org.orekit.propagation.conversion.AbstractPropagatorBuilder;
46 import org.orekit.propagation.conversion.OrbitDeterminationPropagatorBuilder;
47 import org.orekit.propagation.conversion.PropagatorBuilder;
48 import org.orekit.propagation.numerical.NumericalPropagator;
49 import org.orekit.propagation.semianalytical.dsst.DSSTPropagator;
50 import org.orekit.utils.ParameterDriver;
51 import org.orekit.utils.ParameterDriversList;
52 import org.orekit.utils.ParameterDriversList.DelegatingDriver;
53
54
55 /** Least squares estimator for orbit determination.
56 * <p>
57 * Since 10.0, the least squares estimator can be used with both
58 * {@link NumericalPropagator numerical} and {@link DSSTPropagator DSST}
59 * orbit propagators.
60 * </p>
61 * @author Luc Maisonobe
62 * @since 8.0
63 */
64 public class BatchLSEstimator {
65
66 /** Builders for propagator. */
67 private final OrbitDeterminationPropagatorBuilder[] builders;
68
69 /** Measurements. */
70 private final List<ObservedMeasurement<?>> measurements;
71
72 /** Solver for least squares problem. */
73 private final LeastSquaresOptimizer optimizer;
74
75 /** Convergence checker. */
76 private ConvergenceChecker<LeastSquaresProblem.Evaluation> convergenceChecker;
77
78 /** Builder for the least squares problem. */
79 private final LeastSquaresBuilder lsBuilder;
80
81 /** Observer for iterations. */
82 private BatchLSObserver observer;
83
84 /** Last estimations. */
85 private Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> estimations;
86
87 /** Last orbits. */
88 private Orbit[] orbits;
89
90 /** Optimum found. */
91 private Optimum optimum;
92
93 /** Counter for the evaluations. */
94 private Incrementor evaluationsCounter;
95
96 /** Counter for the iterations. */
97 private Incrementor iterationsCounter;
98
99 /** Simple constructor.
100 * <p>
101 * If multiple {@link PropagatorBuilder propagator builders} are set up,
102 * the orbits of several spacecrafts will be used simultaneously.
103 * This is useful if the propagators share some model or measurements
104 * parameters (typically pole motion, prime meridian correction or
105 * ground stations positions).
106 * </p>
107 * <p>
108 * Setting up multiple {@link PropagatorBuilder propagator builders} is
109 * also useful when inter-satellite measurements are used, even if only one
110 * of the orbit is estimated and the other ones are fixed. This is typically
111 * used when very high accuracy GNSS measurements are needed and the
112 * navigation bulletins are not considered accurate enough and the navigation
113 * constellation must be propagated numerically.
114 * </p>
115 * @param optimizer solver for least squares problem
116 * @param propagatorBuilder builders to use for propagation
117 */
118 public BatchLSEstimator(final LeastSquaresOptimizer optimizer,
119 final OrbitDeterminationPropagatorBuilder... propagatorBuilder) {
120
121 this.builders = propagatorBuilder;
122 this.measurements = new ArrayList<ObservedMeasurement<?>>();
123 this.optimizer = optimizer;
124 this.lsBuilder = new LeastSquaresBuilder();
125 this.observer = null;
126 this.estimations = null;
127 this.orbits = new Orbit[builders.length];
128
129 setParametersConvergenceThreshold(Double.NaN);
130
131 // our model computes value and Jacobian in one call,
132 // so we don't use the lazy evaluation feature
133 lsBuilder.lazyEvaluation(false);
134
135 // we manage weight by ourselves, as we change them during
136 // iterations (setting to 0 the identified outliers measurements)
137 // so the least squares problem should not see our weights
138 lsBuilder.weight(null);
139
140 }
141
142 /** Set an observer for iterations.
143 * @param observer observer to be notified at the end of each iteration
144 */
145 public void setObserver(final BatchLSObserver observer) {
146 this.observer = observer;
147 }
148
149 /** Add a measurement.
150 * @param measurement measurement to add
151 */
152 public void addMeasurement(final ObservedMeasurement<?> measurement) {
153 measurements.add(measurement);
154 }
155
156 /** Set the maximum number of iterations.
157 * <p>
158 * The iterations correspond to the top level iterations of
159 * the {@link LeastSquaresOptimizer least squares optimizer}.
160 * </p>
161 * @param maxIterations maxIterations maximum number of iterations
162 * @see #setMaxEvaluations(int)
163 * @see #getIterationsCount()
164 */
165 public void setMaxIterations(final int maxIterations) {
166 lsBuilder.maxIterations(maxIterations);
167 }
168
169 /** Set the maximum number of model evaluations.
170 * <p>
171 * The evaluations correspond to the orbit propagations and
172 * measurements estimations performed with a set of estimated
173 * parameters.
174 * </p>
175 * <p>
176 * For {@link org.hipparchus.optim.nonlinear.vector.leastsquares.GaussNewtonOptimizer
177 * Gauss-Newton optimizer} there is one evaluation at each iteration,
178 * so the maximum numbers may be set to the same value. For {@link
179 * org.hipparchus.optim.nonlinear.vector.leastsquares.LevenbergMarquardtOptimizer
180 * Levenberg-Marquardt optimizer}, there can be several evaluations at
181 * some iterations (typically for the first couple of iterations), so the
182 * maximum number of evaluations may be set to a higher value than the
183 * maximum number of iterations.
184 * </p>
185 * @param maxEvaluations maximum number of model evaluations
186 * @see #setMaxIterations(int)
187 * @see #getEvaluationsCount()
188 */
189 public void setMaxEvaluations(final int maxEvaluations) {
190 lsBuilder.maxEvaluations(maxEvaluations);
191 }
192
193 /** Get the orbital parameters supported by this estimator.
194 * <p>
195 * If there are more than one propagator builder, then the names
196 * of the drivers have an index marker in square brackets appended
197 * to them in order to distinguish the various orbits. So for example
198 * with one builder generating Keplerian orbits the names would be
199 * simply "a", "e", "i"... but if there are several builders the
200 * names would be "a[0]", "e[0]", "i[0]"..."a[1]", "e[1]", "i[1]"...
201 * </p>
202 * @param estimatedOnly if true, only estimated parameters are returned
203 * @return orbital parameters supported by this estimator
204 */
205 public ParameterDriversList getOrbitalParametersDrivers(final boolean estimatedOnly) {
206
207 final ParameterDriversList estimated = new ParameterDriversList();
208 for (int i = 0; i < builders.length; ++i) {
209 final String suffix = builders.length > 1 ? "[" + i + "]" : null;
210 for (final DelegatingDriver delegating : builders[i].getOrbitalParametersDrivers().getDrivers()) {
211 if (delegating.isSelected() || !estimatedOnly) {
212 for (final ParameterDriver driver : delegating.getRawDrivers()) {
213 if (suffix != null && !driver.getName().endsWith(suffix)) {
214 // we add suffix only conditionally because the method may already have been called
215 // and suffixes may have already been appended
216 driver.setName(driver.getName() + suffix);
217 }
218 estimated.add(driver);
219 }
220 }
221 }
222 }
223 return estimated;
224
225 }
226
227 /** Get the propagator parameters supported by this estimator.
228 * @param estimatedOnly if true, only estimated parameters are returned
229 * @return propagator parameters supported by this estimator
230 */
231 public ParameterDriversList getPropagatorParametersDrivers(final boolean estimatedOnly) {
232
233 final ParameterDriversList estimated = new ParameterDriversList();
234 for (PropagatorBuilder builder : builders) {
235 for (final DelegatingDriver delegating : builder.getPropagationParametersDrivers().getDrivers()) {
236 if (delegating.isSelected() || !estimatedOnly) {
237 for (final ParameterDriver driver : delegating.getRawDrivers()) {
238 estimated.add(driver);
239 }
240 }
241 }
242 }
243 return estimated;
244
245 }
246
247 /** Get the measurements parameters supported by this estimator (including measurements and modifiers).
248 * @param estimatedOnly if true, only estimated parameters are returned
249 * @return measurements parameters supported by this estimator
250 */
251 public ParameterDriversList getMeasurementsParametersDrivers(final boolean estimatedOnly) {
252
253 final ParameterDriversList parameters = new ParameterDriversList();
254 for (final ObservedMeasurement<?> measurement : measurements) {
255 for (final ParameterDriver driver : measurement.getParametersDrivers()) {
256 if (!estimatedOnly || driver.isSelected()) {
257 parameters.add(driver);
258 }
259 }
260 }
261
262 parameters.sort();
263
264 return parameters;
265
266 }
267
268 /**
269 * Set convergence threshold.
270 * <p>
271 * The convergence used for estimation is based on the estimated
272 * parameters {@link ParameterDriver#getNormalizedValue() normalized values}.
273 * Convergence is considered to have been reached when the difference
274 * between previous and current normalized value is less than the
275 * convergence threshold for all parameters. The same value is used
276 * for all parameters since they are normalized and hence dimensionless.
277 * </p>
278 * <p>
279 * Normalized values are computed as {@code (current - reference)/scale},
280 * so convergence is reached when the following condition holds for
281 * all estimated parameters:
282 * {@code |current[i] - previous[i]| <= threshold * scale[i]}
283 * </p>
284 * <p>
285 * So the convergence threshold specified here can be considered as
286 * a multiplication factor applied to scale. Since for all parameters
287 * the scale is often small (typically about 1 m for orbital positions
288 * for example), then the threshold should not be too small. A value
289 * of 10⁻³ is often quite accurate.
290 * </p>
291 * <p>
292 * Calling this method overrides any checker that could have been set
293 * beforehand by calling {@link #setConvergenceChecker(ConvergenceChecker)}.
294 * Both methods are mutually exclusive.
295 * </p>
296 *
297 * @param parametersConvergenceThreshold convergence threshold on
298 * normalized parameters (dimensionless, related to parameters scales)
299 * @see #setConvergenceChecker(ConvergenceChecker)
300 * @see EvaluationRmsChecker
301 */
302 public void setParametersConvergenceThreshold(final double parametersConvergenceThreshold) {
303 setConvergenceChecker((iteration, previous, current) ->
304 current.getPoint().getLInfDistance(previous.getPoint()) <= parametersConvergenceThreshold);
305 }
306
307 /** Set a custom convergence checker.
308 * <p>
309 * Calling this method overrides any checker that could have been set
310 * beforehand by calling {@link #setParametersConvergenceThreshold(double)}.
311 * Both methods are mutually exclusive.
312 * </p>
313 * @param convergenceChecker convergence checker to set
314 * @see #setParametersConvergenceThreshold(double)
315 * @since 10.1
316 */
317 public void setConvergenceChecker(final ConvergenceChecker<LeastSquaresProblem.Evaluation> convergenceChecker) {
318 this.convergenceChecker = convergenceChecker;
319 }
320
321 /** Estimate the orbital, propagation and measurements parameters.
322 * <p>
323 * The initial guess for all parameters must have been set before calling this method
324 * using {@link #getOrbitalParametersDrivers(boolean)}, {@link #getPropagatorParametersDrivers(boolean)},
325 * and {@link #getMeasurementsParametersDrivers(boolean)} and then {@link ParameterDriver#setValue(double)
326 * setting the values} of the parameters.
327 * </p>
328 * <p>
329 * For parameters whose reference date has not been set to a non-null date beforehand (i.e.
330 * the parameters for which {@link ParameterDriver#getReferenceDate()} returns {@code null},
331 * a default reference date will be set automatically at the start of the estimation to the
332 * {@link AbstractPropagatorBuilder#getInitialOrbitDate() initial orbit date} of the first
333 * propagator builder. For parameters whose reference date has been set to a non-null date,
334 * this reference date is untouched.
335 * </p>
336 * <p>
337 * After this method returns, the estimated parameters can be retrieved using
338 * {@link #getOrbitalParametersDrivers(boolean)}, {@link #getPropagatorParametersDrivers(boolean)},
339 * and {@link #getMeasurementsParametersDrivers(boolean)} and then {@link ParameterDriver#getValue()
340 * getting the values} of the parameters.
341 * </p>
342 * <p>
343 * As a convenience, the method also returns a fully configured and ready to use
344 * propagator set up with all the estimated values.
345 * </p>
346 * <p>
347 * For even more in-depth information, the {@link #getOptimum()} method provides detailed
348 * elements (covariance matrix, estimated parameters standard deviation, weighted Jacobian, RMS,
349 * χ², residuals and more).
350 * </p>
351 * @return propagators configured with estimated orbits as initial states, and all
352 * propagators estimated parameters also set
353 */
354 public Propagator[] estimate() {
355
356 // set reference date for all parameters that lack one (including the not estimated parameters)
357 for (final ParameterDriver driver : getOrbitalParametersDrivers(false).getDrivers()) {
358 if (driver.getReferenceDate() == null) {
359 driver.setReferenceDate(builders[0].getInitialOrbitDate());
360 }
361 }
362 for (final ParameterDriver driver : getPropagatorParametersDrivers(false).getDrivers()) {
363 if (driver.getReferenceDate() == null) {
364 driver.setReferenceDate(builders[0].getInitialOrbitDate());
365 }
366 }
367 for (final ParameterDriver driver : getMeasurementsParametersDrivers(false).getDrivers()) {
368 if (driver.getReferenceDate() == null) {
369 driver.setReferenceDate(builders[0].getInitialOrbitDate());
370 }
371 }
372
373 // get all estimated parameters
374 final ParameterDriversList estimatedOrbitalParameters = getOrbitalParametersDrivers(true);
375 final ParameterDriversList estimatedPropagatorParameters = getPropagatorParametersDrivers(true);
376 final ParameterDriversList estimatedMeasurementsParameters = getMeasurementsParametersDrivers(true);
377
378 // create start point
379 final double[] start = new double[estimatedOrbitalParameters.getNbParams() +
380 estimatedPropagatorParameters.getNbParams() +
381 estimatedMeasurementsParameters.getNbParams()];
382 int iStart = 0;
383 for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
384 start[iStart++] = driver.getNormalizedValue();
385 }
386 for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) {
387 start[iStart++] = driver.getNormalizedValue();
388 }
389 for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
390 start[iStart++] = driver.getNormalizedValue();
391 }
392 lsBuilder.start(start);
393
394 // create target (which is an array set to 0, as we compute weighted residuals ourselves)
395 int p = 0;
396 for (final ObservedMeasurement<?> measurement : measurements) {
397 if (measurement.isEnabled()) {
398 p += measurement.getDimension();
399 }
400 }
401 final double[] target = new double[p];
402 lsBuilder.target(target);
403
404 // set up the model
405 final ModelObserver modelObserver = new ModelObserver() {
406 /** {@inheritDoc} */
407 @Override
408 public void modelCalled(final Orbit[] newOrbits,
409 final Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> newEstimations) {
410 BatchLSEstimator.this.orbits = newOrbits;
411 BatchLSEstimator.this.estimations = newEstimations;
412 }
413 };
414 final AbstractBatchLSModel model = builders[0].buildLSModel(builders, measurements, estimatedMeasurementsParameters, modelObserver);
415
416 lsBuilder.model(model);
417
418 // add a validator for orbital parameters
419 lsBuilder.parameterValidator(new Validator(estimatedOrbitalParameters,
420 estimatedPropagatorParameters,
421 estimatedMeasurementsParameters));
422
423 lsBuilder.checker(convergenceChecker);
424
425 // set up the problem to solve
426 final LeastSquaresProblem problem = new TappedLSProblem(lsBuilder.build(),
427 model,
428 estimatedOrbitalParameters,
429 estimatedPropagatorParameters,
430 estimatedMeasurementsParameters);
431
432 try {
433
434 // solve the problem
435 optimum = optimizer.optimize(problem);
436
437 // create a new configured propagator with all estimated parameters
438 return model.createPropagators(optimum.getPoint());
439
440 } catch (MathRuntimeException mrte) {
441 throw new OrekitException(mrte);
442 }
443 }
444
445 /** Get the last estimations performed.
446 * @return last estimations performed
447 */
448 public Map<ObservedMeasurement<?>, EstimatedMeasurement<?>> getLastEstimations() {
449 return Collections.unmodifiableMap(estimations);
450 }
451
452 /** Get the optimum found.
453 * <p>
454 * The {@link Optimum} object contains detailed elements (covariance matrix, estimated
455 * parameters standard deviation, weighted Jacobian, RMS, χ², residuals and more).
456 * </p>
457 * <p>
458 * Beware that the returned object is the raw view from the underlying mathematical
459 * library. At this raw level, parameters have {@link ParameterDriver#getNormalizedValue()
460 * normalized values} whereas the space flight parameters have {@link ParameterDriver#getValue()
461 * physical values} with their units. So there are {@link ParameterDriver#getScale() scaling
462 * factors} to apply when using these elements.
463 * </p>
464 * @return optimum found after last call to {@link #estimate()}
465 */
466 public Optimum getOptimum() {
467 return optimum;
468 }
469
470 /** Get the covariances matrix in space flight dynamics physical units.
471 * <p>
472 * This method retrieve the {@link
473 * org.hipparchus.optim.nonlinear.vector.leastsquares.LeastSquaresProblem.Evaluation#getCovariances(double)
474 * covariances} from the [@link {@link #getOptimum() optimum} and applies the scaling factors
475 * to it in order to convert it from raw normalized values back to physical values.
476 * </p>
477 * @param threshold threshold to identify matrix singularity
478 * @return covariances matrix in space flight dynamics physical units
479 * @since 9.1
480 */
481 public RealMatrix getPhysicalCovariances(final double threshold) {
482 final RealMatrix covariances;
483 try {
484 // get the normalized matrix
485 covariances = optimum.getCovariances(threshold).copy();
486 } catch (MathIllegalArgumentException miae) {
487 // the problem is singular
488 throw new OrekitException(miae);
489 }
490
491 // retrieve the scaling factors
492 final double[] scale = new double[covariances.getRowDimension()];
493 int index = 0;
494 for (final ParameterDriver driver : getOrbitalParametersDrivers(true).getDrivers()) {
495 scale[index++] = driver.getScale();
496 }
497 for (final ParameterDriver driver : getPropagatorParametersDrivers(true).getDrivers()) {
498 scale[index++] = driver.getScale();
499 }
500 for (final ParameterDriver driver : getMeasurementsParametersDrivers(true).getDrivers()) {
501 scale[index++] = driver.getScale();
502 }
503
504 // unnormalize the matrix, to retrieve physical covariances
505 for (int i = 0; i < covariances.getRowDimension(); ++i) {
506 for (int j = 0; j < covariances.getColumnDimension(); ++j) {
507 covariances.setEntry(i, j, scale[i] * scale[j] * covariances.getEntry(i, j));
508 }
509 }
510
511 return covariances;
512
513 }
514
515 /** Get the number of iterations used for last estimation.
516 * @return number of iterations used for last estimation
517 * @see #setMaxIterations(int)
518 */
519 public int getIterationsCount() {
520 return iterationsCounter.getCount();
521 }
522
523 /** Get the number of evaluations used for last estimation.
524 * @return number of evaluations used for last estimation
525 * @see #setMaxEvaluations(int)
526 */
527 public int getEvaluationsCount() {
528 return evaluationsCounter.getCount();
529 }
530
531 /** Wrapper used to tap the various counters. */
532 private class TappedLSProblem implements LeastSquaresProblem {
533
534 /** Underlying problem. */
535 private final LeastSquaresProblem problem;
536
537 /** Multivariate function model. */
538 private final AbstractBatchLSModel model;
539
540 /** Estimated orbital parameters. */
541 private final ParameterDriversList estimatedOrbitalParameters;
542
543 /** Estimated propagator parameters. */
544 private final ParameterDriversList estimatedPropagatorParameters;
545
546 /** Estimated measurements parameters. */
547 private final ParameterDriversList estimatedMeasurementsParameters;
548
549 /** Simple constructor.
550 * @param problem underlying problem
551 * @param model multivariate function model
552 * @param estimatedOrbitalParameters estimated orbital parameters
553 * @param estimatedPropagatorParameters estimated propagator parameters
554 * @param estimatedMeasurementsParameters estimated measurements parameters
555 */
556 TappedLSProblem(final LeastSquaresProblem problem,
557 final AbstractBatchLSModel model,
558 final ParameterDriversList estimatedOrbitalParameters,
559 final ParameterDriversList estimatedPropagatorParameters,
560 final ParameterDriversList estimatedMeasurementsParameters) {
561 this.problem = problem;
562 this.model = model;
563 this.estimatedOrbitalParameters = estimatedOrbitalParameters;
564 this.estimatedPropagatorParameters = estimatedPropagatorParameters;
565 this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
566 }
567
568 /** {@inheritDoc} */
569 @Override
570 public Incrementor getEvaluationCounter() {
571 // tap the evaluations counter
572 BatchLSEstimator.this.evaluationsCounter = problem.getEvaluationCounter();
573 model.setEvaluationsCounter(BatchLSEstimator.this.evaluationsCounter);
574 return BatchLSEstimator.this.evaluationsCounter;
575 }
576
577 /** {@inheritDoc} */
578 @Override
579 public Incrementor getIterationCounter() {
580 // tap the iterations counter
581 BatchLSEstimator.this.iterationsCounter = problem.getIterationCounter();
582 model.setIterationsCounter(BatchLSEstimator.this.iterationsCounter);
583 return BatchLSEstimator.this.iterationsCounter;
584 }
585
586 /** {@inheritDoc} */
587 @Override
588 public ConvergenceChecker<Evaluation> getConvergenceChecker() {
589 return problem.getConvergenceChecker();
590 }
591
592 /** {@inheritDoc} */
593 @Override
594 public RealVector getStart() {
595 return problem.getStart();
596 }
597
598 /** {@inheritDoc} */
599 @Override
600 public int getObservationSize() {
601 return problem.getObservationSize();
602 }
603
604 /** {@inheritDoc} */
605 @Override
606 public int getParameterSize() {
607 return problem.getParameterSize();
608 }
609
610 /** {@inheritDoc} */
611 @Override
612 public Evaluation evaluate(final RealVector point) {
613
614 // perform the evaluation
615 final Evaluation evaluation = problem.evaluate(point);
616
617 // notify the observer
618 if (observer != null) {
619 observer.evaluationPerformed(iterationsCounter.getCount(),
620 evaluationsCounter.getCount(),
621 orbits,
622 estimatedOrbitalParameters,
623 estimatedPropagatorParameters,
624 estimatedMeasurementsParameters,
625 new Provider(),
626 evaluation);
627 }
628
629 return evaluation;
630
631 }
632
633 }
634
635 /** Provider for evaluations. */
636 private class Provider implements EstimationsProvider {
637
638 /** Sorted estimations. */
639 private EstimatedMeasurement<?>[] sortedEstimations;
640
641 /** {@inheritDoc} */
642 @Override
643 public int getNumber() {
644 return estimations.size();
645 }
646
647 /** {@inheritDoc} */
648 @Override
649 public EstimatedMeasurement<?> getEstimatedMeasurement(final int index) {
650
651 // safety checks
652 if (index < 0 || index >= estimations.size()) {
653 throw new OrekitException(LocalizedCoreFormats.OUT_OF_RANGE_SIMPLE,
654 index, 0, estimations.size());
655 }
656
657 if (sortedEstimations == null) {
658
659 // lazy evaluation of the sorted array
660 sortedEstimations = new EstimatedMeasurement<?>[estimations.size()];
661 int i = 0;
662 for (final Map.Entry<ObservedMeasurement<?>, EstimatedMeasurement<?>> entry : estimations.entrySet()) {
663 sortedEstimations[i++] = entry.getValue();
664 }
665
666 // sort the array, primarily chronologically
667 Arrays.sort(sortedEstimations, 0, sortedEstimations.length, Comparator.naturalOrder());
668
669 }
670
671 return sortedEstimations[index];
672
673 }
674
675 }
676
677 /** Validator for estimated parameters. */
678 private static class Validator implements ParameterValidator {
679
680 /** Estimated orbital parameters. */
681 private final ParameterDriversList estimatedOrbitalParameters;
682
683 /** Estimated propagator parameters. */
684 private final ParameterDriversList estimatedPropagatorParameters;
685
686 /** Estimated measurements parameters. */
687 private final ParameterDriversList estimatedMeasurementsParameters;
688
689 /** Simple constructor.
690 * @param estimatedOrbitalParameters estimated orbital parameters
691 * @param estimatedPropagatorParameters estimated propagator parameters
692 * @param estimatedMeasurementsParameters estimated measurements parameters
693 */
694 Validator(final ParameterDriversList estimatedOrbitalParameters,
695 final ParameterDriversList estimatedPropagatorParameters,
696 final ParameterDriversList estimatedMeasurementsParameters) {
697 this.estimatedOrbitalParameters = estimatedOrbitalParameters;
698 this.estimatedPropagatorParameters = estimatedPropagatorParameters;
699 this.estimatedMeasurementsParameters = estimatedMeasurementsParameters;
700 }
701
702 /** {@inheritDoc} */
703 @Override
704 public RealVector validate(final RealVector params) {
705
706 int i = 0;
707 for (final ParameterDriver driver : estimatedOrbitalParameters.getDrivers()) {
708 // let the parameter handle min/max clipping
709 driver.setNormalizedValue(params.getEntry(i));
710 params.setEntry(i++, driver.getNormalizedValue());
711 }
712 for (final ParameterDriver driver : estimatedPropagatorParameters.getDrivers()) {
713 // let the parameter handle min/max clipping
714 driver.setNormalizedValue(params.getEntry(i));
715 params.setEntry(i++, driver.getNormalizedValue());
716 }
717 for (final ParameterDriver driver : estimatedMeasurementsParameters.getDrivers()) {
718 // let the parameter handle min/max clipping
719 driver.setNormalizedValue(params.getEntry(i));
720 params.setEntry(i++, driver.getNormalizedValue());
721 }
722
723 return params;
724 }
725 }
726
727 }