View Javadoc
1   /*
2    * Copyright (C) 2018 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.navigation.lateration;
17  
18  import com.irurueta.algebra.Matrix;
19  import com.irurueta.geometry.Point;
20  import com.irurueta.navigation.LockedException;
21  import com.irurueta.navigation.NotReadyException;
22  import com.irurueta.numerical.robust.InliersData;
23  import com.irurueta.numerical.robust.RobustEstimatorException;
24  import com.irurueta.numerical.robust.RobustEstimatorMethod;
25  
26  /**
27   * This is an abstract class to robustly solve the lateration problem by finding the best
28   * pairs of positions and distances among the provided ones.
29   * Implementations of this class should be able to detect and discard outliers in order to find
30   * the best solution.
31   *
32   * @param <P> a {@link Point} type.
33   */
34  public abstract class RobustLaterationSolver<P extends Point<?>> {
35  
36      /**
37       * Indicates that by default a linear solver is used for preliminary solution estimation.
38       * The result obtained on each preliminary solution might be later refined.
39       */
40      private static final boolean DEFAULT_USE_LINEAR_SOLVER = true;
41  
42      /**
43       * Indicates that by default an homogeneous linear solver is used either to estimate preliminary
44       * solutions or an initial solution for preliminary solutions that will be later refined.
45       */
46      public static final boolean DEFAULT_USE_HOMOGENEOUS_LINEAR_SOLVER = false;
47  
48      /**
49       * Indicates that by default preliminary solutions are refined.
50       */
51      public static final boolean DEFAULT_REFINE_PRELIMINARY_SOLUTIONS = true;
52  
53      /**
54       * Default robust estimator method when none is provided.
55       */
56      public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
57  
58      /**
59       * Indicates that result is refined by default using a non-linear lateration solver
60       * (which uses Levenberg-Marquardt fitter).
61       */
62      public static final boolean DEFAULT_REFINE_RESULT = true;
63  
64      /**
65       * Indicates that covariance is kept by default after refining result.
66       */
67      public static final boolean DEFAULT_KEEP_COVARIANCE = true;
68  
69      /**
70       * Default amount of progress variation before notifying a change in estimation progress.
71       * By default, this is set to 5%.
72       */
73      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
74  
75      /**
76       * Minimum allowed value for progress delta.
77       */
78      public static final float MIN_PROGRESS_DELTA = 0.0f;
79  
80      /**
81       * Maximum allowed value for progress delta.
82       */
83      public static final float MAX_PROGRESS_DELTA = 1.0f;
84  
85      /**
86       * Constant defining default confidence of the estimated result, which is
87       * 99%. This means that with a probability of 99% estimation will be
88       * accurate because chosen sub-samples will be inliers.
89       */
90      public static final double DEFAULT_CONFIDENCE = 0.99;
91  
92      /**
93       * Default maximum allowed number of iterations.
94       */
95      public static final int DEFAULT_MAX_ITERATIONS = 5000;
96  
97      /**
98       * Minimum allowed confidence value.
99       */
100     public static final double MIN_CONFIDENCE = 0.0;
101 
102     /**
103      * Maximum allowed confidence value.
104      */
105     public static final double MAX_CONFIDENCE = 1.0;
106 
107     /**
108      * Minimum allowed number of iterations.
109      */
110     public static final int MIN_ITERATIONS = 1;
111 
112     /**
113      * Minimum allowed distance for a given circle or sphere.
114      */
115     public static final double EPSILON = 1e-7;
116 
117     /**
118      * Known positions of static nodes.
119      */
120     protected P[] positions;
121 
122     /**
123      * Euclidean distances from static nodes to mobile node.
124      */
125     protected double[] distances;
126 
127     /**
128      * Listener to be notified of events such as when solving starts, ends or its
129      * progress significantly changes.
130      */
131     protected RobustLaterationSolverListener<P> listener;
132 
133     /**
134      * Initial position to use as a starting point to find a new solution.
135      * This is optional, but if provided, when no linear solvers are used, this is
136      * taken into account. If linear solvers are used, this is ignored.
137      */
138     protected P initialPosition;
139 
140     /**
141      * Indicates whether a linear solver is used or not (either homogeneous or inhomogeneous)
142      * for preliminary solutions.
143      */
144     protected boolean useLinearSolver = DEFAULT_USE_LINEAR_SOLVER;
145 
146     /**
147      * Indicates whether an homogeneous linear solver is used either to estimate preliminary solutions
148      * or an initial solution for preliminary solutions that will be later refined.
149      */
150     protected boolean useHomogeneousLinearSolver = DEFAULT_USE_HOMOGENEOUS_LINEAR_SOLVER;
151 
152     /**
153      * Indicates whether preliminary solutions must be refined after an initial linear solution is found.
154      */
155     protected boolean refinePreliminarySolutions = DEFAULT_REFINE_PRELIMINARY_SOLUTIONS;
156 
157     /**
158      * Estimated position.
159      */
160     protected P estimatedPosition;
161 
162     /**
163      * Indicates if this instance is locked because lateration is being solved.
164      */
165     protected boolean locked;
166 
167     /**
168      * Amount of progress variation before notifying a progress change during estimation.
169      */
170     protected float progressDelta = DEFAULT_PROGRESS_DELTA;
171 
172     /**
173      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is equivalent
174      * to 100%). The amount of confidence indicates the probability that the estimated
175      * result is correct. Usually this value will be close to 1.0, but not exactly 1.0.
176      */
177     protected double confidence = DEFAULT_CONFIDENCE;
178 
179     /**
180      * Maximum allowed number of iterations. When the maximum number of iterations is
181      * exceeded, result will not be available, however an approximate result will be
182      * available for retrieval.
183      */
184     protected int maxIterations = DEFAULT_MAX_ITERATIONS;
185 
186     /**
187      * Data related to inliers found after estimation.
188      */
189     protected InliersData inliersData;
190 
191     /**
192      * Indicates whether result must be refined using a non-linear lateration solver over
193      * found inliers.
194      * If true, inliers will be computed and kept in any implementation regardless of the
195      * settings.
196      */
197     protected boolean refineResult = DEFAULT_REFINE_RESULT;
198 
199     /**
200      * Indicates whether covariance must be kept after refining result.
201      * This setting is only taken into account if result is refined.
202      */
203     protected boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
204 
205     /**
206      * Estimated covariance of estimated position.
207      * This is only available when result has been refined and covariance is kept.
208      */
209     protected Matrix covariance;
210 
211     /**
212      * Standard deviations of provided distances.
213      */
214     protected double[] distanceStandardDeviations;
215 
216     /**
217      * Size of subsets to be checked during robust estimation.
218      */
219     protected int preliminarySubsetSize;
220 
221     /**
222      * Constructor.
223      */
224     protected RobustLaterationSolver() {
225     }
226 
227     /**
228      * Constructor.
229      *
230      * @param listener listener to be notified of events such as when estimation
231      *                 starts, ends or its progress significantly changes.
232      */
233     protected RobustLaterationSolver(final RobustLaterationSolverListener<P> listener) {
234         this.listener = listener;
235     }
236 
237     /**
238      * Constructor.
239      *
240      * @param positions known positions of static nodes.
241      * @param distances euclidean distances from static nodes to mobile node.
242      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
243      *                                  length is smaller than required (3 for 2D points or 4 for 3D points).
244      */
245     protected RobustLaterationSolver(final P[] positions, final double[] distances) {
246         internalSetPositionsAndDistances(positions, distances);
247     }
248 
249     /**
250      * Constructor.
251      *
252      * @param positions                  known positions of static nodes.
253      * @param distances                  euclidean distances from static nodes to mobile node.
254      * @param distanceStandardDeviations standard deviations of provided measured distances.
255      * @throws IllegalArgumentException if either positions, distances or standard deviations
256      *                                  are null, don't have the same length or their length is smaller than required
257      *                                  (3 for 2D points or 4 for 3D points).
258      */
259     protected RobustLaterationSolver(
260             final P[] positions, final double[] distances, final double[] distanceStandardDeviations) {
261         internalSetPositionsDistancesAndStandardDeviations(positions, distances, distanceStandardDeviations);
262     }
263 
264 
265     /**
266      * Constructor.
267      *
268      * @param positions known positions of static nodes.
269      * @param distances euclidean distances from static nodes to mobile node.
270      * @param listener  listener to be notified of events such as when estimation starts, ends or its progress
271      *                  significantly changes.
272      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
273      *                                  length is smaller than required (3 for 2D points or 4 for 3D points).
274      */
275     protected RobustLaterationSolver(
276             final P[] positions, final double[] distances, final RobustLaterationSolverListener<P> listener) {
277         internalSetPositionsAndDistances(positions, distances);
278         this.listener = listener;
279     }
280 
281     /**
282      * Constructor.
283      *
284      * @param positions                  known positions of static nodes.
285      * @param distances                  euclidean distances from static nodes to mobile node.
286      * @param distanceStandardDeviations standard deviations of provided measured distances.
287      * @param listener                   listener to be notified of events raised by this instance.
288      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
289      *                                  length is smaller than required (3 for 2D points or 4 for 3D points).
290      */
291     protected RobustLaterationSolver(
292             final P[] positions, final double[] distances, final double[] distanceStandardDeviations,
293             final RobustLaterationSolverListener<P> listener) {
294         internalSetPositionsDistancesAndStandardDeviations(positions, distances, distanceStandardDeviations);
295         this.listener = listener;
296     }
297 
298     /**
299      * Gets listener to be notified of events raised by this instance.
300      *
301      * @return listener to be notified of events raised by this instance.
302      */
303     public RobustLaterationSolverListener<P> getListener() {
304         return listener;
305     }
306 
307     /**
308      * Sets listener to be notified of events raised by this instance.
309      *
310      * @param listener listener to be notified of events raised by this instance.
311      * @throws LockedException if instance is busy solving the lateration problem.
312      */
313     public void setListener(final RobustLaterationSolverListener<P> listener) throws LockedException {
314         if (isLocked()) {
315             throw new LockedException();
316         }
317         this.listener = listener;
318     }
319 
320     /**
321      * Gets initial position to use as a starting point to find a new solution.
322      * This is optional, but if provided, when no linear solvers are used, this is
323      * taken into account. If linear solvers are used, this is ignored.
324      *
325      * @return an initial position.
326      */
327     public P getInitialPosition() {
328         return initialPosition;
329     }
330 
331     /**
332      * Sets initial position to use as a starting point to find a new solution.
333      * This is optional, but if provided, when no linear solvers are used, this is
334      * taken into account. If linear solvers are used, this is ignored.
335      *
336      * @param initialPosition an initial position.
337      * @throws LockedException if instance is busy solving the lateration problem.
338      */
339     public void setInitialPosition(final P initialPosition) throws LockedException {
340         if (isLocked()) {
341             throw new LockedException();
342         }
343 
344         this.initialPosition = initialPosition;
345     }
346 
347     /**
348      * Indicates whether a linear solver is used or not (either homogeneous or inhomogeneous)
349      * for preliminary solutions.
350      *
351      * @return true if a linear solver is used, false otherwise.
352      */
353     public boolean isLinearSolverUsed() {
354         return useLinearSolver;
355     }
356 
357     /**
358      * Specifies whether a linear solver is used or not (either homogeneous or inhomogeneous)
359      * for preliminary solutions.
360      *
361      * @param linearSolverUsed true if a linear solver is used, false otherwise.
362      * @throws LockedException if instance is busy solving the lateration problem.
363      */
364     public void setLinearSolverUsed(final boolean linearSolverUsed) throws LockedException {
365         if (isLocked()) {
366             throw new LockedException();
367         }
368 
369         useLinearSolver = linearSolverUsed;
370     }
371 
372     /**
373      * Indicates whether an homogeneous linear solver is used either to estimate preliminary solutions
374      * or an initial solution for preliminary solutions that will be later refined.
375      *
376      * @return true if homogeneous linear solver is used, false otherwise.
377      */
378     public boolean isHomogeneousLinearSolverUsed() {
379         return useHomogeneousLinearSolver;
380     }
381 
382     /**
383      * Specifies whether an homogeneous linear solver is used either to estimate preliminary solutions
384      * or an initial solution for preliminary solutions that will be later refined.
385      *
386      * @param useHomogeneousLinearSolver true if homogeneous linear solver is used, false
387      *                                   otherwise.
388      * @throws LockedException if instance is busy solving the lateration problem.
389      */
390     public void setHomogeneousLinearSolverUsed(final boolean useHomogeneousLinearSolver) throws LockedException {
391         if (isLocked()) {
392             throw new LockedException();
393         }
394 
395         this.useHomogeneousLinearSolver = useHomogeneousLinearSolver;
396     }
397 
398     /**
399      * Indicates whether preliminary solutions must be refined after an initial linear solution is found.
400      * If no initial solution is found using a linear solver, a non-linear solver will be
401      * used regardless of this value using an average solution as the initial value to be
402      * refined.
403      *
404      * @return true if preliminary solutions must be refined after an initial linear solution, false
405      * otherwise.
406      */
407     public boolean isPreliminarySolutionRefined() {
408         return refinePreliminarySolutions;
409     }
410 
411     /**
412      * Specifies whether preliminary solutions must be refined after an initial linear solution is found.
413      * If no initial solution is found using a linear solver, a non-linear solver will be
414      * used regardless of this value using an average solution as the initial value to be
415      * refined.
416      *
417      * @param preliminarySolutionRefined true if preliminary solutions must be refined after an
418      *                                   initial linear solution, false otherwise.
419      * @throws LockedException if instance is busy solving the lateration problem.
420      */
421     public void setPreliminarySolutionRefined(final boolean preliminarySolutionRefined) throws LockedException {
422         if (isLocked()) {
423             throw new LockedException();
424         }
425 
426         refinePreliminarySolutions = preliminarySolutionRefined;
427     }
428 
429     /**
430      * Returns boolean indicating if solver is locked because estimation is under
431      * progress.
432      *
433      * @return true if solver is locked, false otherwise.
434      */
435     public boolean isLocked() {
436         return locked;
437     }
438 
439     /**
440      * Returns amount of progress variation before notifying a progress change during
441      * estimation.
442      *
443      * @return amount of progress variation before notifying a progress change during
444      * estimation.
445      */
446     public float getProgressDelta() {
447         return progressDelta;
448     }
449 
450     /**
451      * Sets amount of progress variation before notifying a progress change during
452      * estimation.
453      *
454      * @param progressDelta amount of progress variation before notifying a progress
455      *                      change during estimation.
456      * @throws IllegalArgumentException if progress delta is less than zero or greater than 1.
457      * @throws LockedException          if this solver is locked because an estimation is being computed.
458      */
459     public void setProgressDelta(final float progressDelta) throws LockedException {
460         if (isLocked()) {
461             throw new LockedException();
462         }
463         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
464             throw new IllegalArgumentException();
465         }
466         this.progressDelta = progressDelta;
467     }
468 
469     /**
470      * Returns amount of confidence expressed as a value between 0.0 and 1.0
471      * (which is equivalent to 100%). The amount of confidence indicates the probability
472      * that the estimated result is correct. Usually this value will be close to 1.0, but
473      * not exactly 1.0.
474      *
475      * @return amount of confidence as a value between 0.0 and 1.0.
476      */
477     public double getConfidence() {
478         return confidence;
479     }
480 
481     /**
482      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
483      * equivalent to 100%). The amount of confidence indicates the probability that
484      * the estimated result is correct. Usually this value will be close to 1.0, but
485      * not exactly 1.0.
486      *
487      * @param confidence confidence to be set as a value between 0.0 and 1.0.
488      * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
489      * @throws LockedException          if solver is locked because an estimation is being computed.
490      */
491     public void setConfidence(final double confidence) throws LockedException {
492         if (isLocked()) {
493             throw new LockedException();
494         }
495         if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
496             throw new IllegalArgumentException();
497         }
498         this.confidence = confidence;
499     }
500 
501     /**
502      * Returns maximum allowed number of iterations. If maximum allowed number of
503      * iterations is achieved without converging to a result when calling solve(),
504      * a RobustEstimatorException will be raised.
505      *
506      * @return maximum allowed number of iterations.
507      */
508     public int getMaxIterations() {
509         return maxIterations;
510     }
511 
512     /**
513      * Sets maximum allowed number of iterations. When the maximum number of iterations
514      * is exceeded, result will not be available, however an approximate result will be
515      * available for retrieval.
516      *
517      * @param maxIterations maximum allowed number of iterations to be set.
518      * @throws IllegalArgumentException if provided value is less than 1.
519      * @throws LockedException          if this solver is locked because an estimation is being
520      *                                  computed.
521      */
522     public void setMaxIterations(final int maxIterations) throws LockedException {
523         if (isLocked()) {
524             throw new LockedException();
525         }
526         if (maxIterations < MIN_ITERATIONS) {
527             throw new IllegalArgumentException();
528         }
529         this.maxIterations = maxIterations;
530     }
531 
532     /**
533      * Gets data related to inliers found after estimation.
534      *
535      * @return data related to inliers found after estimation.
536      */
537     public InliersData getInliersData() {
538         return inliersData;
539     }
540 
541     /**
542      * Indicates whether result must be refined using a non-linear solver over found inliers.
543      *
544      * @return true to refine result, false to simply use result found by robust estimator
545      * without further refining.
546      */
547     public boolean isResultRefined() {
548         return refineResult;
549     }
550 
551     /**
552      * Specifies whether result must be refined using a non-linear solver over found inliers.
553      *
554      * @param refineResult true to refine result, false to simply use result found by robust
555      *                     estimator without further refining.
556      * @throws LockedException if solver is locked.
557      */
558     public void setResultRefined(final boolean refineResult) throws LockedException {
559         if (isLocked()) {
560             throw new LockedException();
561         }
562         this.refineResult = refineResult;
563     }
564 
565     /**
566      * Indicates whether covariance must be kept after refining result.
567      * This setting is only taken into account if result is refined.
568      *
569      * @return true if covariance must be kept after refining result, false otherwise.
570      */
571     public boolean isCovarianceKept() {
572         return keepCovariance;
573     }
574 
575     /**
576      * Specifies whether covariance must be kept after refining result.
577      * This setting is only taken into account if result is refined.
578      *
579      * @param keepCovariance true if covariance must be kept after refining result,
580      *                       false otherwise.
581      * @throws LockedException if estimator is locked.
582      */
583     public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
584         if (isLocked()) {
585             throw new LockedException();
586         }
587         this.keepCovariance = keepCovariance;
588     }
589 
590     /**
591      * Gets known positions of static nodes.
592      *
593      * @return known positions of static nodes.
594      */
595     public P[] getPositions() {
596         return positions;
597     }
598 
599     /**
600      * Gets euclidean distances from static nodes to mobile node.
601      *
602      * @return euclidean distances from static nodes to mobile node.
603      */
604     public double[] getDistances() {
605         return distances;
606     }
607 
608     /**
609      * Gets standard deviations of provided distances.
610      * This is used during refinement.
611      *
612      * @return standard deviations of provided distances.
613      */
614     public double[] getDistanceStandardDeviations() {
615         return distanceStandardDeviations;
616     }
617 
618     /**
619      * Indicates whether solver is ready to find a solution.
620      *
621      * @return true if solver is ready, false otherwise.
622      */
623     public boolean isReady() {
624         return positions != null && distances != null && positions.length >= getMinRequiredPositionsAndDistances();
625     }
626 
627     /**
628      * Returns quality scores corresponding to each pair of
629      * positions and distances (i.e. sample).
630      * The larger the score value the better the quality of the sample.
631      * This implementation always returns null.
632      * Subclasses using quality scores must implement proper behavior.
633      *
634      * @return quality scores corresponding to each sample.
635      */
636     public double[] getQualityScores() {
637         return null;
638     }
639 
640     /**
641      * Sets quality scores corresponding to each pair of positions and
642      * distances (i.e. sample).
643      * The larger the score value the better the quality of the sample.
644      * This implementation makes no action.
645      * Subclasses using quality scores must implement proper behaviour.
646      *
647      * @param qualityScores quality scores corresponding to each pair of
648      *                      matched points.
649      * @throws IllegalArgumentException if provided quality scores length
650      *                                  is smaller than minimum required samples.
651      * @throws LockedException          if robust solver is locked because an
652      *                                  estimation is already in progress.
653      */
654     public void setQualityScores(final double[] qualityScores) throws LockedException {
655     }
656 
657     /**
658      * Gets estimated covariance of estimated position if available.
659      * This is only available when result has been refined and covariance
660      * is kept.
661      *
662      * @return estimated covariance or null.
663      */
664     public Matrix getCovariance() {
665         return covariance;
666     }
667 
668     /**
669      * Sets known positions and Euclidean distances.
670      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
671      *
672      * @param positions known positions of static nodes.
673      * @param distances euclidean distances from static nodes to mobile node.
674      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
675      *                                  length is smaller than required (2 points).
676      * @throws LockedException          if instance is busy solving the lateration problem.
677      */
678     public void setPositionsAndDistances(final P[] positions, final double[] distances) throws LockedException {
679         if (isLocked()) {
680             throw new LockedException();
681         }
682         internalSetPositionsAndDistances(positions, distances);
683     }
684 
685     /**
686      * Sets known positions, Euclidean distances and the respective standard deviations of
687      * measured distances.
688      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
689      *
690      * @param positions                  known positions of static nodes.
691      * @param distances                  euclidean distances from static nodes to mobile node.
692      * @param distanceStandardDeviations standard deviations of provided measured distances.
693      * @throws IllegalArgumentException if either positions, distances or standard deviations
694      *                                  are null, don't have the same length of their length is smaller than required
695      *                                 (2 points).
696      * @throws LockedException          if instance is busy solving the lateration problem.
697      */
698     public void setPositionsDistancesAndStandardDeviations(
699             final P[] positions, final double[] distances, final double[] distanceStandardDeviations)
700             throws LockedException {
701         if (isLocked()) {
702             throw new LockedException();
703         }
704         internalSetPositionsDistancesAndStandardDeviations(positions, distances, distanceStandardDeviations);
705     }
706 
707     /**
708      * Gets estimated position.
709      *
710      * @return estimated position.
711      */
712     public P getEstimatedPosition() {
713         return estimatedPosition;
714     }
715 
716     /**
717      * Gets number of dimensions of provided points.
718      *
719      * @return number of dimensions of provided points.
720      */
721     public abstract int getNumberOfDimensions();
722 
723     /**
724      * Minimum required number of positions and distances.
725      * This value will depend on actual implementation and whether we are solving a 2D or 3D problem.
726      *
727      * @return minimum required number of positions and distances.
728      */
729     public abstract int getMinRequiredPositionsAndDistances();
730 
731     /**
732      * Gets size of subsets to be checked during robust estimation.
733      * This has to be at least {@link #getMinRequiredPositionsAndDistances()}.
734      *
735      * @return size of subsets to be checked during robust estimation.
736      */
737     public int getPreliminarySubsetSize() {
738         return preliminarySubsetSize;
739     }
740 
741     /**
742      * Sets size of subsets to be checked during robust estimation.
743      * This has to be at least {@link #getMinRequiredPositionsAndDistances()}.
744      *
745      * @param preliminarySubsetSize size of subsets to be checked during robust estimation.
746      * @throws LockedException          if instance is busy solving the lateration problem.
747      * @throws IllegalArgumentException if provided value is less than {@link #getMinRequiredPositionsAndDistances()}.
748      */
749     public void setPreliminarySubsetSize(final int preliminarySubsetSize) throws LockedException {
750         if (isLocked()) {
751             throw new LockedException();
752         }
753         if (preliminarySubsetSize < getMinRequiredPositionsAndDistances()) {
754             throw new IllegalArgumentException();
755         }
756 
757         this.preliminarySubsetSize = preliminarySubsetSize;
758     }
759 
760     /**
761      * Solves the lateration problem.
762      *
763      * @return estimated position.
764      * @throws LockedException          if instance is busy solving the lateration problem.
765      * @throws NotReadyException        if solver is not ready.
766      * @throws RobustEstimatorException if estimation fails for any reason
767      *                                  (i.e. numerical instability, no solution available, etc).
768      */
769     public abstract P solve() throws LockedException, NotReadyException, RobustEstimatorException;
770 
771     /**
772      * Returns method being used for robust estimation.
773      *
774      * @return method being used for robust estimation.
775      */
776     public abstract RobustEstimatorMethod getMethod();
777 
778     /**
779      * Internally sets known positions and Euclidean distances.
780      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
781      *
782      * @param positions known positions of static nodes.
783      * @param distances euclidean distances from static nodes to mobile node.
784      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
785      *                                  length is smaller than required (2 points).
786      */
787     protected void internalSetPositionsAndDistances(final P[] positions, final double[] distances) {
788         if (positions == null || distances == null) {
789             throw new IllegalArgumentException();
790         }
791 
792         if (positions.length < getMinRequiredPositionsAndDistances()) {
793             throw new IllegalArgumentException();
794         }
795 
796         if (positions.length != distances.length) {
797             throw new IllegalArgumentException();
798         }
799 
800         this.positions = positions;
801         this.distances = distances;
802 
803         // fix distances if needed
804         for (var i = 0; i < this.distances.length; i++) {
805             if (this.distances[i] < EPSILON) {
806                 this.distances[i] = EPSILON;
807             }
808         }
809     }
810 
811     /**
812      * Internally sets known positions, Euclidean distances and the respective standard
813      * deviations of measured distances.
814      * If any distance value is zero or negative, it will be fixed assuming an EPSILON value.
815      *
816      * @param positions                  known positions of static nodes.
817      * @param distances                  euclidean distances from static nodes to mobile node.
818      * @param distanceStandardDeviations standard deviations of provided measured distances.
819      * @throws IllegalArgumentException if either positions or distances are null, don't have the same length or their
820      *                                  length is smaller than required (2 points).
821      */
822     protected void internalSetPositionsDistancesAndStandardDeviations(
823             final P[] positions, final double[] distances, final double[] distanceStandardDeviations) {
824         if (distanceStandardDeviations == null || distances == null) {
825             throw new IllegalArgumentException();
826         }
827         if (distances.length != distanceStandardDeviations.length) {
828             throw new IllegalArgumentException();
829         }
830         internalSetPositionsAndDistances(positions, distances);
831         this.distanceStandardDeviations = distanceStandardDeviations;
832     }
833 }