View Javadoc
1   /*
2    * Copyright (C) 2019 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.indoor.position;
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.navigation.indoor.RadioSource;
23  import com.irurueta.navigation.indoor.RadioSourceLocated;
24  import com.irurueta.navigation.indoor.RangingAndRssiFingerprint;
25  import com.irurueta.navigation.indoor.RangingAndRssiReading;
26  import com.irurueta.navigation.indoor.RangingFingerprint;
27  import com.irurueta.navigation.indoor.RangingReading;
28  import com.irurueta.navigation.indoor.RssiFingerprint;
29  import com.irurueta.navigation.indoor.RssiReading;
30  import com.irurueta.numerical.robust.InliersData;
31  import com.irurueta.numerical.robust.RobustEstimatorException;
32  import com.irurueta.numerical.robust.RobustEstimatorMethod;
33  
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  /**
38   * Base class for robust position estimation, using RSSI readings first to obtain
39   * an initial coarse position estimation, and then ranging readings to refine such
40   * estimation.
41   *
42   * @param <P> a {@link Point} type.
43   */
44  public abstract class SequentialRobustRangingAndRssiPositionEstimator<P extends Point<?>> {
45  
46      /**
47       * Default robust estimator method for robust position estimation using ranging
48       * data when no robust method is provided.
49       */
50      public static final RobustEstimatorMethod DEFAULT_RANGING_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
51  
52      /**
53       * Default robust method for coarse robust position estimation using RSSI
54       * data when no robust method is provided.
55       */
56      public static final RobustEstimatorMethod DEFAULT_RSSI_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
57  
58      /**
59       * Indicates that by default located radio source position covariance is taken
60       * into account (if available) to determine distance standard deviation for ranging
61       * measurements.
62       */
63      public static final boolean DEFAULT_USE_RANGING_RADIO_SOURCE_POSITION_COVARIANCE = true;
64  
65      /**
66       * Indicates that by default located radio source position covariance is taken
67       * into account (if available) to determine distance standard deviation for RSSI
68       * measurements.
69       */
70      public static final boolean DEFAULT_USE_RSSI_RADIO_SOURCE_POSITION_COVARIANCE = true;
71  
72      /**
73       * Indicates that by default readings are distributed evenly among radio sources
74       * taking into account quality scores of both radio sources and ranging readings.
75       */
76      public static final boolean DEFAULT_EVENLY_DISTRIBUTE_RANGING_READINGS = true;
77  
78      /**
79       * Indicates that by default readings are distributed evenly among radio sources
80       * taking into account quality scores of both radio sources and RSSI readings.
81       */
82      public static final boolean DEFAULT_EVENLY_DISTRIBUTE_RSSI_READINGS = true;
83  
84      /**
85       * Distance standard deviation assumed for provided distances as a fallback when
86       * none can be determined.
87       */
88      public static final double FALLBACK_DISTANCE_STANDARD_DEVIATION =
89              RobustPositionEstimator.FALLBACK_DISTANCE_STANDARD_DEVIATION;
90  
91      /**
92       * Default amount of progress variation before notifying a change in estimation progress.
93       * By default, this is set to 5%.
94       */
95      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
96  
97      /**
98       * Minimum allowed value for progress delta.
99       */
100     public static final float MIN_PROGRESS_DELTA = 0.0f;
101 
102     /**
103      * Maximum allowed value for progress delta.
104      */
105     public static final float MAX_PROGRESS_DELTA = 1.0f;
106 
107     /**
108      * Constant defining default confidence of the estimated result, which is
109      * 99%. This means that with a probability of 99% estimation will be
110      * accurate because chosen sub-samples will be inliers.
111      */
112     public static final double DEFAULT_CONFIDENCE = 0.99;
113 
114     /**
115      * Default maximum allowed number of iterations.
116      */
117     public static final int DEFAULT_MAX_ITERATIONS = 5000;
118 
119     /**
120      * Minimum allowed confidence value.
121      */
122     public static final double MIN_CONFIDENCE = 0.0;
123 
124     /**
125      * Maximum allowed confidence value.
126      */
127     public static final double MAX_CONFIDENCE = 1.0;
128 
129     /**
130      * Minimum allowed number of iterations.
131      */
132     public static final int MIN_ITERATIONS = 1;
133 
134     /**
135      * Indicates that result is refined by default using all found inliers.
136      */
137     public static final boolean DEFAULT_REFINE_RESULT = true;
138 
139     /**
140      * Indicates that covariance is kept by default after refining result.
141      */
142     public static final boolean DEFAULT_KEEP_COVARIANCE = true;
143 
144     /**
145      * Indicates that by default a linear solver is used for preliminary solution
146      * estimation using ranging measurements.
147      * The result obtained on each preliminary solution might be later refined.
148      */
149     public static final boolean DEFAULT_USE_RANGING_LINEAR_SOLVER = true;
150 
151     /**
152      * Indicates that by default a linear solver is used for preliminary solution
153      * estimation using RSSI measurements.
154      * The result obtained on each preliminary solution might be later refined.
155      */
156     public static final boolean DEFAULT_USE_RSSI_LINEAR_SOLVER = true;
157 
158     /**
159      * Indicates that by default an homogeneous linear solver is used either to
160      * estimate preliminary solutions or an initial solution for preliminary solutions
161      * that will be later refined on the ranging fine estimation.
162      */
163     public static final boolean DEFAULT_USE_RANGING_HOMOGENEOUS_LINEAR_SOLVER = false;
164 
165     /**
166      * Indicates that by default an homogeneous linear solver is used either to
167      * estimate preliminary solutions or an initial solution for preliminary solutions
168      * that will be later refined on the RSSI coarse estimation.
169      */
170     public static final boolean DEFAULT_USE_RSSI_HOMOGENEOUS_LINEAR_SOLVER = false;
171 
172     /**
173      * Indicates that by default preliminary ranging solutions are refined.
174      */
175     public static final boolean DEFAULT_REFINE_RANGING_PRELIMINARY_SOLUTIONS = true;
176 
177     /**
178      * Indicates that by default preliminary RSSI solutions are refined.
179      */
180     public static final boolean DEFAULT_REFINE_RSSI_PRELIMINARY_SOLUTIONS = true;
181 
182     /**
183      * Internal robust estimator for position estimation using ranging readings.
184      */
185     protected RobustRangingPositionEstimator<P> rangingEstimator;
186 
187     /**
188      * Internal robust estimator for coarse position estimation using RSSI readings.
189      */
190     protected RobustRssiPositionEstimator<P> rssiEstimator;
191 
192     /**
193      * Robust method used for robust position estimation using ranging data.
194      */
195     protected RobustEstimatorMethod rangingRobustMethod = DEFAULT_RANGING_ROBUST_METHOD;
196 
197     /**
198      * Robust method used for coarse robust position estimation using RSSI data.
199      */
200     protected RobustEstimatorMethod rssiRobustMethod = DEFAULT_RSSI_ROBUST_METHOD;
201 
202     /**
203      * Size of subsets to be checked during robust estimation.
204      */
205     protected int rangingPreliminarySubsetSize;
206 
207     /**
208      * Size of subsets to be checked during RSSI robust estimation.
209      */
210     protected int rssiPreliminarySubsetSize;
211 
212     /**
213      * Indicates whether located radio source position covariance is taken into account
214      * (if available) to determine distance standard deviation for ranging measurements.
215      */
216     private boolean useRangingRadioSourcePositionCovariance = DEFAULT_USE_RANGING_RADIO_SOURCE_POSITION_COVARIANCE;
217 
218     /**
219      * Indicates whether located radio source position covariance is taken into account
220      * (if available) to determine distance standard deviation for RSSI measurements.
221      */
222     private boolean useRssiRadioSourcePositionCovariance = DEFAULT_USE_RSSI_RADIO_SOURCE_POSITION_COVARIANCE;
223 
224     /**
225      * Indicates whether ranging readings are evenly distributed among radio sources
226      * taking into account quality scores of both radio sources and ranging readings.
227      */
228     private boolean evenlyDistributeRangingReadings = DEFAULT_EVENLY_DISTRIBUTE_RANGING_READINGS;
229 
230     /**
231      * Indicates whether RSSI readings are evenly distributed among radio sources
232      * taking into account quality scores of both radio sources and RSSI readings.
233      */
234     private boolean evenlyDistributeRssiReadings = DEFAULT_EVENLY_DISTRIBUTE_RSSI_READINGS;
235 
236     /**
237      * Distance standard deviation fallback value to use when none can be determined
238      * from provided RSSI measurements.
239      */
240     private double rssiFallbackDistanceStandardDeviation = FALLBACK_DISTANCE_STANDARD_DEVIATION;
241 
242     /**
243      * Distance standard deviation fallback value to use when none can be determined
244      * from provided ranging measurements.
245      */
246     private double rangingFallbackDistanceStandardDeviation = FALLBACK_DISTANCE_STANDARD_DEVIATION;
247 
248     /**
249      * Amount of progress variation before notifying a progress change during
250      * estimation.
251      */
252     private float progressDelta = DEFAULT_PROGRESS_DELTA;
253 
254     /**
255      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
256      * equivalent to 100%) for robust position estimation on ranging data. The amount
257      * of confidence indicates the probability that the estimated result is correct.
258      * Usually this value will be close to 1.0, but not exactly 1.0.
259      */
260     private double rangingConfidence = DEFAULT_CONFIDENCE;
261 
262     /**
263      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
264      * equivalent to 100%) for robust position estimation on RSSI data. The amount
265      * of confidence indicates the probability that the estimated result is correct.
266      * Usually this value will be close to 1.0, but not exactly 1.0.
267      */
268     private double rssiConfidence = DEFAULT_CONFIDENCE;
269 
270     /**
271      * Maximum allowed number of iterations for robust ranging position estimation.
272      * When the maximum number of iterations is exceeded, an approximate result
273      * might be available for retrieval.
274      */
275     private int rangingMaxIterations = DEFAULT_MAX_ITERATIONS;
276 
277     /**
278      * Maximum allowed number of iterations for robust RSSI position estimation.
279      * When the maximum number of iterations is exceeded, an approximate result
280      * might be available for retrieval.
281      */
282     private int rssiMaxIterations = DEFAULT_MAX_ITERATIONS;
283 
284     /**
285      * Indicates whether result is refined using all found inliers.
286      */
287     private boolean refineResult = DEFAULT_REFINE_RESULT;
288 
289     /**
290      * Indicates that covariance is kept after refining result.
291      */
292     private boolean keepCovariance = DEFAULT_KEEP_COVARIANCE;
293 
294     /**
295      * Indicates that a linear solver is used for preliminary solution estimation
296      * using ranging measurements.
297      * The result obtained on each preliminary solution might be later refined.
298      */
299     private boolean useRangingLinearSolver = DEFAULT_USE_RANGING_LINEAR_SOLVER;
300 
301     /**
302      * Indicates that a linear solver is used for preliminary solution estimation
303      * using RSSI measurements.
304      * The result obtained on each preliminary solution might be later refined.
305      */
306     private boolean useRssiLinearSolver = DEFAULT_USE_RSSI_LINEAR_SOLVER;
307 
308     /**
309      * Indicates whether an homogeneous linear solver is used either to estimate
310      * preliminary solutions or an initial solution for preliminary solutions that
311      * will be later refined on the ranging fine estimation.
312      */
313     private boolean useRangingHomogeneousLinearSolver = DEFAULT_USE_RANGING_HOMOGENEOUS_LINEAR_SOLVER;
314 
315     /**
316      * Indicates whether an homogeneous linear solver is used either to estimate
317      * preliminary solutions or an initial solution for preliminary solutions that
318      * will be later refined on the RSSI coarse estimation.
319      */
320     private boolean useRssiHomogeneousLinearSolver = DEFAULT_USE_RSSI_HOMOGENEOUS_LINEAR_SOLVER;
321 
322     /**
323      * Indicates whether preliminary ranging solutions are refined.
324      */
325     private boolean refineRangingPreliminarySolutions = DEFAULT_REFINE_RANGING_PRELIMINARY_SOLUTIONS;
326 
327     /**
328      * Indicates whether preliminary RSSI solutions are refined.
329      */
330     private boolean refineRssiPreliminarySolutions = DEFAULT_REFINE_RSSI_PRELIMINARY_SOLUTIONS;
331 
332     /**
333      * Threshold to determine when samples are inliers or not used during ranging
334      * position estimation.
335      * If not defined, default threshold will be used.
336      */
337     private Double rangingThreshold;
338 
339     /**
340      * Threshold to determine when samples are inliers or not used during RSSI
341      * position estimation.
342      * If not defined, default threshold will be used.
343      */
344     private Double rssiThreshold;
345 
346     /**
347      * Listener in charge of handling events.
348      */
349     private SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener;
350 
351     /**
352      * Located radio sources used for lateration.
353      */
354     private List<? extends RadioSourceLocated<P>> sources;
355 
356     /**
357      * Fingerprint containing readings at an unknown location for provided located
358      * radio sources.
359      */
360     private RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
361             extends RadioSource>> fingerprint;
362 
363     /**
364      * Quality scores corresponding to each provided located radio source.
365      * The larger the score value the better the quality of the radio source.
366      */
367     private double[] sourceQualityScores;
368 
369     /**
370      * Quality scores corresponding to each reading within provided fingerprint.
371      * The larger the score value the better the quality of the reading.
372      */
373     private double[] fingerprintReadingsQualityScores;
374 
375     /**
376      * An initial position to start the estimation from. This can be useful if we only
377      * intend to refine a previously known estimation.
378      */
379     private P initialPosition;
380 
381     /**
382      * Indicates if this instance is locked because estimation is being executed.
383      */
384     private boolean locked;
385 
386     /**
387      * Constructor.
388      */
389     protected SequentialRobustRangingAndRssiPositionEstimator() {
390     }
391 
392     /**
393      * Constructor.
394      *
395      * @param sources located radio sources used for lateration.
396      * @throws IllegalArgumentException if provided sources is null or the number of
397      *                                  provided sources is less than the required minimum.
398      */
399     protected SequentialRobustRangingAndRssiPositionEstimator(final List<? extends RadioSourceLocated<P>> sources) {
400         internalSetSources(sources);
401     }
402 
403     /**
404      * Constructor.
405      *
406      * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
407      *                    location for provided located radio sources.
408      * @throws IllegalArgumentException if provided fingerprint is null.
409      */
410     protected SequentialRobustRangingAndRssiPositionEstimator(
411             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
412                     extends RadioSource>> fingerprint) {
413         internalSetFingerprint(fingerprint);
414     }
415 
416     /**
417      * Constructor.
418      *
419      * @param sources     located radio sources used for lateration.
420      * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
421      *                    location for provided located radio sources.
422      * @throws IllegalArgumentException if either provided sources or fingerprint is null
423      *                                  or the number of provided sources is less than the required minimum.
424      */
425     protected SequentialRobustRangingAndRssiPositionEstimator(
426             final List<? extends RadioSourceLocated<P>> sources,
427             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
428                     extends RadioSource>> fingerprint) {
429         internalSetSources(sources);
430         internalSetFingerprint(fingerprint);
431     }
432 
433     /**
434      * Constructor.
435      *
436      * @param listener listener in charge of handling events.
437      */
438     protected SequentialRobustRangingAndRssiPositionEstimator(
439             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
440         this.listener = listener;
441     }
442 
443     /**
444      * Constructor.
445      *
446      * @param sources  located radio sources used for lateration.
447      * @param listener listener in charge of handling events.
448      * @throws IllegalArgumentException if provided sources is null or the number of
449      *                                  provided sources is less than the required
450      *                                  minimum.
451      */
452     protected SequentialRobustRangingAndRssiPositionEstimator(
453             final List<? extends RadioSourceLocated<P>> sources,
454             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
455         this(sources);
456         this.listener = listener;
457     }
458 
459     /**
460      * Constructor.
461      *
462      * @param fingerprint fingerprint containing ranging+RSSI readings at an unknown
463      *                    location for provided located radio sources.
464      * @param listener    listener in charge of handling events.
465      * @throws IllegalArgumentException if provided fingerprint is null.
466      */
467     protected SequentialRobustRangingAndRssiPositionEstimator(
468             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
469                     extends RadioSource>> fingerprint,
470             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
471         this(fingerprint);
472         this.listener = listener;
473     }
474 
475     /**
476      * Constructor.
477      *
478      * @param sources     located radio sources used for lateration.
479      * @param fingerprint fingerprint containing ranging+RSSI readings at an
480      *                    unknown location for provided located radio sources.
481      * @param listener    listener in charge of handling events.
482      * @throws IllegalArgumentException if either provided sources or fingerprint is null
483      *                                  or the number of provided sources is less than the required minimum.
484      */
485     protected SequentialRobustRangingAndRssiPositionEstimator(
486             final List<? extends RadioSourceLocated<P>> sources,
487             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
488                     extends RadioSource>> fingerprint,
489             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
490         this(sources, fingerprint);
491         this.listener = listener;
492     }
493 
494     /**
495      * Constructor.
496      *
497      * @param sourceQualityScores             quality scores corresponding to
498      *                                        each provided located radio source.
499      *                                        The larger the score value the better
500      *                                        the quality of the radio source.
501      * @param fingerprintReadingQualityScores quality scores corresponding to readings
502      *                                        within provided fingerprint. The larger
503      *                                        the score the better the quality of the
504      *                                        reading.
505      */
506     protected SequentialRobustRangingAndRssiPositionEstimator(
507             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores) {
508         internalSetSourceQualityScores(sourceQualityScores);
509         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
510     }
511 
512     /**
513      * Constructor.
514      *
515      * @param sourceQualityScores             quality scores corresponding to
516      *                                        each provided located radio source.
517      *                                        The larger the score value the better
518      *                                        the quality of the radio source.
519      * @param fingerprintReadingQualityScores quality scores corresponding to readings
520      *                                        within provided fingerprint. The larger
521      *                                        the score the better the quality of the
522      *                                        reading.
523      * @param sources                         located radio sources used for
524      *                                        lateration.
525      * @throws IllegalArgumentException if provided sources is null or the number of
526      *                                  provided sources is less than the required
527      *                                  minimum.
528      */
529     protected SequentialRobustRangingAndRssiPositionEstimator(
530             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
531             final List<? extends RadioSourceLocated<P>> sources) {
532         this(sources);
533         internalSetSourceQualityScores(sourceQualityScores);
534         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
535     }
536 
537     /**
538      * Constructor.
539      *
540      * @param sourceQualityScores             quality scores corresponding to
541      *                                        each provided located radio source.
542      *                                        The larger the score value the better
543      *                                        the quality of the radio source.
544      * @param fingerprintReadingQualityScores quality scores corresponding to readings
545      *                                        within provided fingerprint. The larger
546      *                                        the score the better the quality of the
547      *                                        reading.
548      * @param fingerprint                     fingerprint containing ranging+RSSI
549      *                                        readings at an unknown location for
550      *                                        provided located radio sources.
551      * @throws IllegalArgumentException if provided fingerprint is null.
552      */
553     protected SequentialRobustRangingAndRssiPositionEstimator(
554             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
555             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
556                     extends RadioSource>> fingerprint) {
557         this(fingerprint);
558         internalSetSourceQualityScores(sourceQualityScores);
559         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
560     }
561 
562     /**
563      * Constructor.
564      *
565      * @param sourceQualityScores             quality scores corresponding to
566      *                                        each provided located radio source.
567      *                                        The larger the score value the better
568      *                                        the quality of the radio source.
569      * @param fingerprintReadingQualityScores quality scores corresponding to readings
570      *                                        within provided fingerprint. The larger
571      *                                        the score the better the quality of the
572      *                                        reading.
573      * @param sources                         located radio sources used for
574      *                                        lateration.
575      * @param fingerprint                     fingerprint containing ranging+RSSI
576      *                                        readings at an unknown location for
577      *                                        provided located radio sources.
578      * @throws IllegalArgumentException if either provided sources or fingerprint is null
579      *                                  or the number of provided sources is less than the required minimum.
580      */
581     protected SequentialRobustRangingAndRssiPositionEstimator(
582             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
583             final List<? extends RadioSourceLocated<P>> sources,
584             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
585                     extends RadioSource>> fingerprint) {
586         this(sources, fingerprint);
587         internalSetSourceQualityScores(sourceQualityScores);
588         internalSetFingerprintReadingsQualityScores(fingerprintReadingQualityScores);
589     }
590 
591     /**
592      * Constructor.
593      *
594      * @param sourceQualityScores             quality scores corresponding to
595      *                                        each provided located radio source.
596      *                                        The larger the score value the better
597      *                                        the quality of the radio source.
598      * @param fingerprintReadingQualityScores quality scores corresponding to readings
599      *                                        within provided fingerprint. The larger
600      *                                        the score the better the quality of the
601      *                                        reading.
602      * @param listener                        listener in charge of handling events.
603      */
604     protected SequentialRobustRangingAndRssiPositionEstimator(
605             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
606             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
607         this(sourceQualityScores, fingerprintReadingQualityScores);
608         this.listener = listener;
609     }
610 
611     /**
612      * Constructor.
613      *
614      * @param sourceQualityScores             quality scores corresponding to
615      *                                        each provided located radio source.
616      *                                        The larger the score value the better
617      *                                        the quality of the radio source.
618      * @param fingerprintReadingQualityScores quality scores corresponding to readings
619      *                                        within provided fingerprint. The larger
620      *                                        the score the better the quality of the
621      *                                        reading.
622      * @param sources                         located radio sources used for
623      *                                        lateration.
624      * @param listener                        listener in charge of handling events.
625      * @throws IllegalArgumentException if provided sources is null or the number of
626      *                                  provided sources is less than the required minimum.
627      */
628     protected SequentialRobustRangingAndRssiPositionEstimator(
629             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
630             final List<? extends RadioSourceLocated<P>> sources,
631             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
632         this(sourceQualityScores, fingerprintReadingQualityScores, sources);
633         this.listener = listener;
634     }
635 
636     /**
637      * Constructor.
638      *
639      * @param sourceQualityScores             quality scores corresponding to
640      *                                        each provided located radio source.
641      *                                        The larger the score value the better
642      *                                        the quality of the radio source.
643      * @param fingerprintReadingQualityScores quality scores corresponding to readings
644      *                                        within provided fingerprint. The larger
645      *                                        the score the better the quality of the
646      *                                        reading.
647      * @param fingerprint                     fingerprint containing ranging+RSSI
648      *                                        readings at an unknown location for
649      *                                        provided located radio sources.
650      * @param listener                        listener in charge of handling events.
651      * @throws IllegalArgumentException if provided fingerprint is null.
652      */
653     protected SequentialRobustRangingAndRssiPositionEstimator(
654             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
655             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
656                     extends RadioSource>> fingerprint,
657             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
658         this(sourceQualityScores, fingerprintReadingQualityScores, fingerprint);
659         this.listener = listener;
660     }
661 
662     /**
663      * Constructor.
664      *
665      * @param sourceQualityScores             quality scores corresponding to
666      *                                        each provided located radio source.
667      *                                        The larger the score value the better
668      *                                        the quality of the radio source.
669      * @param fingerprintReadingQualityScores quality scores corresponding to readings
670      *                                        within provided fingerprint. The larger
671      *                                        the score the better the quality of the
672      *                                        reading.
673      * @param sources                         located radio sources used for
674      *                                        lateration.
675      * @param fingerprint                     fingerprint containing ranging+RSSI
676      *                                        readings at an unknown location for
677      *                                        provided located radio sources.
678      * @param listener                        listener in charge of handling events.
679      * @throws IllegalArgumentException if either provided sources or fingerprint is null
680      *                                  or the number of provided sources is less than the required minimum.
681      */
682     protected SequentialRobustRangingAndRssiPositionEstimator(
683             final double[] sourceQualityScores, final double[] fingerprintReadingQualityScores,
684             final List<? extends RadioSourceLocated<P>> sources,
685             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
686                     extends RadioSource>> fingerprint,
687             final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener) {
688         this(sourceQualityScores, fingerprintReadingQualityScores, sources, fingerprint);
689         this.listener = listener;
690     }
691 
692     /**
693      * Gets robust method used for robust position estimation using ranging data.
694      *
695      * @return robust method used for robust position estimation using ranging data.
696      */
697     public RobustEstimatorMethod getRangingRobustMethod() {
698         return rangingRobustMethod;
699     }
700 
701     /**
702      * Sets robust method for robust position estimation using ranging data.
703      *
704      * @param rangingRobustMethod robust method used for robust position estimation
705      *                            using ranging data.
706      * @throws LockedException if this instance is locked.
707      */
708     public void setRangingRobustMethod(final RobustEstimatorMethod rangingRobustMethod) throws LockedException {
709         if (isLocked()) {
710             throw new LockedException();
711         }
712 
713         this.rangingRobustMethod = rangingRobustMethod;
714     }
715 
716     /**
717      * Gets robust method used for coarse robust position estimation using RSSI data.
718      *
719      * @return robust method used for coarse robust position estimation using RSSI data.
720      */
721     public RobustEstimatorMethod getRssiRobustMethod() {
722         return rssiRobustMethod;
723     }
724 
725     /**
726      * Sets robust method used for coarse robust position estimation using RSSI data.
727      *
728      * @param rssiRobustMethod robust method used for coarse robust position estimation
729      *                         using RSSI data.
730      * @throws LockedException if this instance is locked.
731      */
732     public void setRssiRobustMethod(final RobustEstimatorMethod rssiRobustMethod) throws LockedException {
733         if (isLocked()) {
734             throw new LockedException();
735         }
736 
737         this.rssiRobustMethod = rssiRobustMethod;
738     }
739 
740     /**
741      * Indicates whether located radio source position covariance is taken into account
742      * (if available) to determine distance standard deviation for ranging measurements.
743      *
744      * @return true to take into account radio source position covariance during
745      * ranging position estimation, false otherwise.
746      */
747     public boolean isRangingRadioSourcePositionCovarianceUsed() {
748         return useRangingRadioSourcePositionCovariance;
749     }
750 
751     /**
752      * Specifies whether located radio source position covariance is taken into account
753      * (if available) to determine distance standard deviation for ranging measurements.
754      *
755      * @param useRangingRadioSourcePositionCovariance true to take into account radio
756      *                                                source position covariance during
757      *                                                ranging position estimation, false
758      *                                                otherwise.
759      * @throws LockedException if this instance is locked.
760      */
761     public void setRangingRadioSourcePositionCovarianceUsed(final boolean useRangingRadioSourcePositionCovariance)
762             throws LockedException {
763         if (isLocked()) {
764             throw new LockedException();
765         }
766 
767         this.useRangingRadioSourcePositionCovariance = useRangingRadioSourcePositionCovariance;
768     }
769 
770     /**
771      * Indicates whether located radio source position covariance is taken into
772      * account (if available) to determine distance standard deviation for RSSI
773      * measurements.
774      *
775      * @return true to take into account radio source position covariance during
776      * RSSI position estimation, false otherwise.
777      */
778     public boolean isRssiRadioSourcePositionCovarianceUsed() {
779         return useRssiRadioSourcePositionCovariance;
780     }
781 
782     /**
783      * Specifies whether located radio source position covariance is taken into
784      * account (if available) to determine distance standard deviation for RSSI
785      * measurements.
786      *
787      * @param useRssiRadioSourcePositionCovariance true to take into account radio
788      *                                             source position covariance during
789      *                                             RSSI position estimation, false
790      *                                             otherwise.
791      * @throws LockedException if this instance is locked.
792      */
793     public void setRssiRadioSourcePositionCovarianceUsed(final boolean useRssiRadioSourcePositionCovariance)
794             throws LockedException {
795         if (isLocked()) {
796             throw new LockedException();
797         }
798 
799         this.useRssiRadioSourcePositionCovariance = useRssiRadioSourcePositionCovariance;
800     }
801 
802     /**
803      * Indicates whether ranging readings are evenly distributed among radio sources
804      * taking into account quality scores of both radio sources and ranging readings.
805      *
806      * @return true if ranging readings are evenly distributed among radio sources,
807      * false otherwise.
808      */
809     public boolean isRangingReadingsEvenlyDistributed() {
810         return evenlyDistributeRangingReadings;
811     }
812 
813     /**
814      * Specifies whether ranging readings are evenly distributed among radio sources
815      * taking into account quality scores of both radio sources and ranging readings.
816      *
817      * @param evenlyDistributeRangingReadings true if ranging readings are evenly
818      *                                        distributed among radio sources, false
819      *                                        otherwise.
820      * @throws LockedException if this instance is locked.
821      */
822     public void setRangingReadingsEvenlyDistributed(final boolean evenlyDistributeRangingReadings)
823             throws LockedException {
824         if (isLocked()) {
825             throw new LockedException();
826         }
827 
828         this.evenlyDistributeRangingReadings = evenlyDistributeRangingReadings;
829     }
830 
831     /**
832      * Gets distance standard deviation fallback value to use when none can be
833      * determined from provided RSSI measurements.
834      *
835      * @return distance standard deviation fallback value to use when none can be
836      * determined from provided RSSI measurements.
837      */
838     public double getRssiFallbackDistanceStandardDeviation() {
839         return rssiFallbackDistanceStandardDeviation;
840     }
841 
842     /**
843      * Sets distance standard deviation fallback value to use when none can be
844      * determined from provided RSSI measurements.
845      *
846      * @param rssiFallbackDistanceStandardDeviation distance standard deviation
847      *                                              fallback value to use when none can
848      *                                              be determined from provided RSSI
849      *                                              measurements.
850      * @throws LockedException if this instance is locked.
851      */
852     public void setRssiFallbackDistanceStandardDeviation(final double rssiFallbackDistanceStandardDeviation)
853             throws LockedException {
854         if (isLocked()) {
855             throw new LockedException();
856         }
857         this.rssiFallbackDistanceStandardDeviation = rssiFallbackDistanceStandardDeviation;
858     }
859 
860     /**
861      * Gets distance standard deviation fallback value to use when none can be
862      * determined from provided ranging measurements.
863      *
864      * @return distance standard deviation fallback value to use when none can be
865      * determined from provided ranging measurements.
866      */
867     public double getRangingFallbackDistanceStandardDeviation() {
868         return rangingFallbackDistanceStandardDeviation;
869     }
870 
871     /**
872      * Sets distance standard deviation fallback value to use when none can be
873      * determined from provided ranging measurements.
874      *
875      * @param rangingFallbackDistanceStandardDeviation distance standard deviation
876      *                                                 fallback value to use when none can
877      *                                                 be determined from provided ranging
878      *                                                 measurements.
879      * @throws LockedException if this instance is locked.
880      */
881     public void setRangingFallbackDistanceStandardDeviation(final double rangingFallbackDistanceStandardDeviation)
882             throws LockedException {
883         if (isLocked()) {
884             throw new LockedException();
885         }
886         this.rangingFallbackDistanceStandardDeviation = rangingFallbackDistanceStandardDeviation;
887     }
888 
889     /**
890      * Indicates whether RSSI readings are evenly distributed among radio sources
891      * taking into account quality scores of both radio sources and RSSI readings.
892      *
893      * @return true if RSSI readings are evenly distributed among radio sources,
894      * false otherwise.
895      */
896     public boolean isRssiReadingsEvenlyDistributed() {
897         return evenlyDistributeRssiReadings;
898     }
899 
900     /**
901      * Specifies whether RSSI readings are evenly distributed among radio sources
902      * taking into account quality scores of both radio sources and RSSI readings.
903      *
904      * @param evenlyDistributeRssiReadings true if RSSI readings are evenly distributed
905      *                                     among radio sources, false otherwise.
906      * @throws LockedException if this instance is locked.
907      */
908     public void setRssiReadingsEvenlyDistributed(final boolean evenlyDistributeRssiReadings) throws LockedException {
909         if (isLocked()) {
910             throw new LockedException();
911         }
912 
913         this.evenlyDistributeRssiReadings = evenlyDistributeRssiReadings;
914     }
915 
916     /**
917      * Gets amount of progress variation before notifying a progress change during
918      * estimation.
919      *
920      * @return amount of progress variation before notifying a progress change during
921      * estimation.
922      */
923     public float getProgressDelta() {
924         return progressDelta;
925     }
926 
927     /**
928      * Sets amount of progress variation before notifying a progress change during
929      * estimation.
930      *
931      * @param progressDelta amount of progress variation before notifying a progress
932      *                      change during estimation.
933      * @throws IllegalArgumentException if progress delta is less than zero or greater than 1.
934      * @throws LockedException          if this instance is locked.
935      */
936     public void setProgressDelta(final float progressDelta) throws LockedException {
937         if (isLocked()) {
938             throw new LockedException();
939         }
940         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
941             throw new IllegalArgumentException();
942         }
943         this.progressDelta = progressDelta;
944     }
945 
946     /**
947      * Returns amount of confidence expressed as a value between 0.0 and 1.0 (which is
948      * equivalent to 100%) for robust position estimation on ranging data. The amount
949      * of confidence indicates the probability that the estimated result is correct.
950      * Usually this value will be close to 1.0, but not exactly 1.0.
951      *
952      * @return amount of confidence for robust position estimation as a value between
953      * 0.0 and 1.0.
954      */
955     public double getRangingConfidence() {
956         return rangingConfidence;
957     }
958 
959     /**
960      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
961      * equivalent to 100%) for robust position estimation on ranging data. The amount
962      * of confidence indicates the probability that the estimated result is correct.
963      * Usually this value will be close to 1.0, but not exactly 1.0.
964      *
965      * @param rangingConfidence confidence to be set for robust position estimation
966      *                          as a value between 0.0 and 1.0.
967      * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
968      * @throws LockedException          if estimator is locked.
969      */
970     public void setRangingConfidence(final double rangingConfidence) throws LockedException {
971         if (isLocked()) {
972             throw new LockedException();
973         }
974         if (rangingConfidence < MIN_CONFIDENCE || rangingConfidence > MAX_CONFIDENCE) {
975             throw new IllegalArgumentException();
976         }
977         this.rangingConfidence = rangingConfidence;
978     }
979 
980     /**
981      * Returns amount of confidence expressed as a value between 0.0 and 1.0 (which is
982      * equivalent to 100%) for robust position estimation on RSSI data. The amount
983      * of confidence indicates the probability that the estimated result is correct.
984      * Usually this value will be close to 1.0, but not exactly 1.0.
985      *
986      * @return amount of confidence for robust position estimation as a value between
987      * 0.0 and 1.0.
988      */
989     public double getRssiConfidence() {
990         return rssiConfidence;
991     }
992 
993     /**
994      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which is
995      * equivalent to 100%) for robust position estimation on RSSI data. The amount
996      * of confidence indicates the probability that the estimated result is correct.
997      * Usually this value will be close to 1.0, but not exactly 1.0.
998      *
999      * @param rssiConfidence amount of confidence for robust position estimation as a
1000      *                       value between 0.0 and 1.0.
1001      * @throws IllegalArgumentException if provided value is not between 0.0 and 1.0.
1002      * @throws LockedException          if estimator is locked.
1003      */
1004     public void setRssiConfidence(final double rssiConfidence) throws LockedException {
1005         if (isLocked()) {
1006             throw new LockedException();
1007         }
1008         if (rssiConfidence < MIN_CONFIDENCE || rssiConfidence > MAX_CONFIDENCE) {
1009             throw new IllegalArgumentException();
1010         }
1011         this.rssiConfidence = rssiConfidence;
1012     }
1013 
1014     /**
1015      * Gets maximum allowed number of iterations for robust ranging position estimation.
1016      * When the maximum number of iterations is exceeded, an approximate result might
1017      * be available for retrieval.
1018      *
1019      * @return maximum allowed number of iterations for position estimation.
1020      */
1021     public int getRangingMaxIterations() {
1022         return rangingMaxIterations;
1023     }
1024 
1025     /**
1026      * Sets maximum allowed number of iterations for robust ranging position
1027      * estimation.
1028      * When the maximum number of iterations is exceeded, an approximate result might
1029      * be available for retrieval.
1030      *
1031      * @param rangingMaxIterations maximum allowed number of iterations to be set for
1032      *                             position estimation.
1033      * @throws IllegalArgumentException if provided value is less than 1.
1034      * @throws LockedException          if estimator is locked.
1035      */
1036     public void setRangingMaxIterations(final int rangingMaxIterations) throws LockedException {
1037         if (isLocked()) {
1038             throw new LockedException();
1039         }
1040         if (rangingMaxIterations < MIN_ITERATIONS) {
1041             throw new IllegalArgumentException();
1042         }
1043         this.rangingMaxIterations = rangingMaxIterations;
1044     }
1045 
1046     /**
1047      * Gets maximum allowed number of iterations for robust RSSI position estimation.
1048      * When the maximum number of iterations is exceeded, an approximate result might
1049      * be available for retrieval.
1050      *
1051      * @return maximum allowed number of iterations for position estimation.
1052      */
1053     public int getRssiMaxIterations() {
1054         return rssiMaxIterations;
1055     }
1056 
1057     /**
1058      * Sets maximum allowed number of iterations for robust RSSI position estimation.
1059      * When the maximum number of iterations is exceeded, an approximate result might
1060      * be available for retrieval.
1061      *
1062      * @param rssiMaxIterations maximum allowed number of iterations to be set for
1063      *                          position estimation.
1064      * @throws IllegalArgumentException if provided value is less than 1.
1065      * @throws LockedException          if estimator is locked.
1066      */
1067     public void setRssiMaxIterations(final int rssiMaxIterations) throws LockedException {
1068         if (isLocked()) {
1069             throw new LockedException();
1070         }
1071         if (rssiMaxIterations < MIN_ITERATIONS) {
1072             throw new IllegalArgumentException();
1073         }
1074         this.rssiMaxIterations = rssiMaxIterations;
1075     }
1076 
1077     /**
1078      * Indicates whether result is refined using all found inliers.
1079      *
1080      * @return true if result is refined, false otherwise.
1081      */
1082     public boolean isResultRefined() {
1083         return refineResult;
1084     }
1085 
1086     /**
1087      * Specifies whether result is refined using all found inliers.
1088      *
1089      * @param refineResult true if result is refined, false otherwise.
1090      * @throws LockedException if this instance is locked.
1091      */
1092     public void setResultRefined(final boolean refineResult) throws LockedException {
1093         if (isLocked()) {
1094             throw new LockedException();
1095         }
1096 
1097         this.refineResult = refineResult;
1098     }
1099 
1100     /**
1101      * Indicates whether covariance must be kept after refining result.
1102      * This setting is only taken into account if result is refined.
1103      *
1104      * @return true if covariance must be kept after refining result, false otherwise.
1105      */
1106     public boolean isCovarianceKept() {
1107         return keepCovariance;
1108     }
1109 
1110     /**
1111      * Specifies whether covariance must be kept after refining result.
1112      * This setting is only taken into account if result is refined.
1113      *
1114      * @param keepCovariance true if covariance must be kept after refining result,
1115      *                       false otherwise.
1116      * @throws LockedException if estimator is locked.
1117      */
1118     public void setCovarianceKept(final boolean keepCovariance) throws LockedException {
1119         if (isLocked()) {
1120             throw new LockedException();
1121         }
1122         this.keepCovariance = keepCovariance;
1123     }
1124 
1125     /**
1126      * Indicates whether a linear solver is used for preliminary solution estimation
1127      * using ranging measurements.
1128      * The result obtained on each preliminary solution might be later refined.
1129      *
1130      * @return true if a linear solver is used for preliminary solution estimation on
1131      * ranging readings.
1132      */
1133     public boolean isRangingLinearSolverUsed() {
1134         return useRangingLinearSolver;
1135     }
1136 
1137     /**
1138      * Specifies whether a linear solver is used for preliminary solution estimation
1139      * using ranging measurements.
1140      * The result obtained on each preliminary solution might be later refined.
1141      *
1142      * @param useRangingLinearSolver true if a linear solver is used for preliminary
1143      *                               solution estimation on ranging readings.
1144      * @throws LockedException if estimator is locked.
1145      */
1146     public void setRangingLinearSolverUsed(final boolean useRangingLinearSolver) throws LockedException {
1147         if (isLocked()) {
1148             throw new LockedException();
1149         }
1150         this.useRangingLinearSolver = useRangingLinearSolver;
1151     }
1152 
1153     /**
1154      * Indicates whether a linear solver is used for preliminary solution estimation
1155      * using RSSI measurements.
1156      * The result obtained on each preliminary solution might be later refined.
1157      *
1158      * @return true if a linear solver is used for preliminary solution estimation on
1159      * RSSI readings.
1160      */
1161     public boolean isRssiLinearSolverUsed() {
1162         return useRssiLinearSolver;
1163     }
1164 
1165     /**
1166      * Specifies whether a linear solver is used for preliminary solution estimation
1167      * using RSSI measurements.
1168      * The result obtained on each preliminary solution might be later refined.
1169      *
1170      * @param useRssiLinearSolver true if a linear solver is used for preliminary
1171      *                            solution estimation on RSSI readings.
1172      * @throws LockedException if estimator is locked.
1173      */
1174     public void setRssiLinearSolverUsed(final boolean useRssiLinearSolver) throws LockedException {
1175         if (isLocked()) {
1176             throw new LockedException();
1177         }
1178         this.useRssiLinearSolver = useRssiLinearSolver;
1179     }
1180 
1181     /**
1182      * Indicates whether an homogeneous linear solver is used either to estimate
1183      * preliminary solutions or an initial solution for preliminary solutions that
1184      * will be later refined on the ranging fine estimation.
1185      *
1186      * @return true to use an homogeneous linear solver for preliminary solutions
1187      * during ranging fine position estimation.
1188      */
1189     public boolean isRangingHomogeneousLinearSolverUsed() {
1190         return useRangingHomogeneousLinearSolver;
1191     }
1192 
1193     /**
1194      * Specifies whether an homogeneous linear solver is used either to estimate
1195      * preliminary solutions or an initial solution for preliminary solutions that
1196      * will be later refined on the ranging fine estimation.
1197      *
1198      * @param useRangingHomogeneousLinearSolver true to use an homogeneous linear
1199      *                                          solver for preliminary solutions during
1200      *                                          ranging fine position estimation.
1201      * @throws LockedException if estimator is locked.
1202      */
1203     public void setRangingHomogeneousLinearSolverUsed(final boolean useRangingHomogeneousLinearSolver)
1204             throws LockedException {
1205         if (isLocked()) {
1206             throw new LockedException();
1207         }
1208         this.useRangingHomogeneousLinearSolver = useRangingHomogeneousLinearSolver;
1209     }
1210 
1211     /**
1212      * Indicates whether an homogeneous linear solver is used either to estimate
1213      * preliminary solutions or an initial solution for preliminary solutions that
1214      * will be later refined on the RSSI coarse estimation.
1215      *
1216      * @return true to use an homogeneous linear solver for preliminary solutions
1217      * during RSSI coarse position estimation.
1218      */
1219     public boolean isRssiHomogeneousLinearSolverUsed() {
1220         return useRssiHomogeneousLinearSolver;
1221     }
1222 
1223     /**
1224      * Specifies whether an homogeneous linear solver is used either to estimate
1225      * preliminary solutions or an initial solution for preliminary solutions that
1226      * will be later refined on the RSSI coarse estimation.
1227      *
1228      * @param useRssiHomogeneousLinearSolver true to use an homogeneous linear solver
1229      *                                       for preliminary solutions during RSSI fine
1230      *                                       position estimation.
1231      * @throws LockedException if estimator is locked.
1232      */
1233     public void setRssiHomogeneousLinearSolverUsed(final boolean useRssiHomogeneousLinearSolver)
1234             throws LockedException {
1235         if (isLocked()) {
1236             throw new LockedException();
1237         }
1238         this.useRssiHomogeneousLinearSolver = useRssiHomogeneousLinearSolver;
1239     }
1240 
1241     /**
1242      * Indicates whether preliminary ranging solutions are refined after an initial
1243      * linear solution is found.
1244      * If no initial preliminary solution is found using a linear solver, a non-linear
1245      * solver will be used regardless of this value using an average solution
1246      * as the initial value to be refined.
1247      *
1248      * @return true if preliminary ranging solutions must be refined after an initial
1249      * linear solution, false otherwise.
1250      */
1251     public boolean isRangingPreliminarySolutionRefined() {
1252         return refineRangingPreliminarySolutions;
1253     }
1254 
1255     /**
1256      * Specifies whether preliminary ranging solutions are refined after an initial
1257      * linear solution is found.
1258      * If no initial preliminary solution is found using a linear solver, a non-linear
1259      * solver will be used regardless of this value using an average solution
1260      * as the initial value to be refined.
1261      *
1262      * @param refineRangingPreliminarySolutions true if preliminary ranging solutions
1263      *                                          must be refined after an initial linear
1264      *                                          solution, false otherwise.
1265      * @throws LockedException if estimator is locked.
1266      */
1267     public void setRangingPreliminarySolutionRefined(final boolean refineRangingPreliminarySolutions)
1268             throws LockedException {
1269         if (isLocked()) {
1270             throw new LockedException();
1271         }
1272         this.refineRangingPreliminarySolutions = refineRangingPreliminarySolutions;
1273     }
1274 
1275     /**
1276      * Indicates whether preliminary RSSI solutions are refined after an initial
1277      * linear solution is found.
1278      * If no initial preliminary solution is found using a linear solver, a non-linear
1279      * solver will be used regardless of this value using an average solution
1280      * as the initial value to be refined.
1281      *
1282      * @return true if preliminary RSSI solutions must be refined after an initial
1283      * linear solution, false otherwise.
1284      */
1285     public boolean isRssiPreliminarySolutionRefined() {
1286         return refineRssiPreliminarySolutions;
1287     }
1288 
1289     /**
1290      * Specifies whether preliminary RSSI solutions are refined after an initial
1291      * linear solution is found.
1292      * If no initial preliminary solution is found using a linear solver, a non-linear
1293      * solver will be used regardless of this value using an average solution
1294      * as the initial value to be refined.
1295      *
1296      * @param refineRssiPreliminarySolutions true if preliminary RSSI solutions must
1297      *                                       be refined after an initial linear
1298      *                                       solution, false otherwise.
1299      * @throws LockedException if estimator is locked.
1300      */
1301     public void setRssiPreliminarySolutionRefined(final boolean refineRssiPreliminarySolutions) throws LockedException {
1302         if (isLocked()) {
1303             throw new LockedException();
1304         }
1305         this.refineRssiPreliminarySolutions = refineRssiPreliminarySolutions;
1306     }
1307 
1308     /**
1309      * Gets size of subsets to be checked during ranging robust estimation.
1310      *
1311      * @return size of subsets to be checked during ranging robust estimation.
1312      */
1313     public int getRangingPreliminarySubsetSize() {
1314         return rangingPreliminarySubsetSize;
1315     }
1316 
1317     /**
1318      * Sets size of subsets to be checked during ranging robust estimation.
1319      *
1320      * @param rangingPreliminarySubsetSize size of subsets to be checked during
1321      *                                     ranging robust estimation.
1322      * @throws LockedException          if estimator is locked.
1323      * @throws IllegalArgumentException if provided value is less than {@link #getMinRequiredSources()}.
1324      */
1325     public void setRangingPreliminarySubsetSize(final int rangingPreliminarySubsetSize) throws LockedException {
1326         if (isLocked()) {
1327             throw new LockedException();
1328         }
1329         if (rangingPreliminarySubsetSize < getMinRequiredSources()) {
1330             throw new IllegalArgumentException();
1331         }
1332 
1333         this.rangingPreliminarySubsetSize = rangingPreliminarySubsetSize;
1334     }
1335 
1336     /**
1337      * Gets size of subsets to be checked during RSSI robust estimation.
1338      *
1339      * @return size of subsets to be checked during RSSI robust estimation.
1340      */
1341     public int getRssiPreliminarySubsetSize() {
1342         return rssiPreliminarySubsetSize;
1343     }
1344 
1345     /**
1346      * Sets size of subsets to be checked during RSSI robust estimation.
1347      *
1348      * @param rssiPreliminarySubsetSize size of subsets to be checked during
1349      *                                  RSSI robust estimation.
1350      * @throws LockedException          if estimator is locked.
1351      * @throws IllegalArgumentException if provided value is less than {@link #getMinRequiredSources()}.
1352      */
1353     public void setRssiPreliminarySubsetSize(final int rssiPreliminarySubsetSize) throws LockedException {
1354         if (isLocked()) {
1355             throw new LockedException();
1356         }
1357         if (rssiPreliminarySubsetSize < getMinRequiredSources()) {
1358             throw new IllegalArgumentException();
1359         }
1360 
1361         this.rssiPreliminarySubsetSize = rssiPreliminarySubsetSize;
1362     }
1363 
1364     /**
1365      * Gets threshold to determine when samples are inliers or not, used during robust
1366      * fine ranging position estimation.
1367      * If not defined, default threshold will be used.
1368      *
1369      * @return threshold for ranging estimation or null.
1370      */
1371     public Double getRangingThreshold() {
1372         return rangingThreshold;
1373     }
1374 
1375     /**
1376      * Sets threshold to determine when samples are inliers or not, used during robust
1377      * fine ranging position estimation.
1378      * If not defined, default threshold will be used.
1379      *
1380      * @param rangingThreshold threshold for ranging estimation or null.
1381      * @throws LockedException if estimator is locked.
1382      */
1383     public void setRangingThreshold(final Double rangingThreshold) throws LockedException {
1384         if (isLocked()) {
1385             throw new LockedException();
1386         }
1387         this.rangingThreshold = rangingThreshold;
1388     }
1389 
1390     /**
1391      * Gets threshold to determine when samples are inliers or not, used during robust
1392      * coarse RSSI position estimation.
1393      * If not defined, default threshold will be used.
1394      *
1395      * @return threshold for RSSI estimation or null.
1396      */
1397     public Double getRssiThreshold() {
1398         return rssiThreshold;
1399     }
1400 
1401     /**
1402      * Sets threshold to determine when samples are inliers or not, used during robust
1403      * coarse RSSI position estimation.
1404      * If not defined, default threshold will be used.
1405      *
1406      * @param rssiThreshold threshold for RSSI estimation or null.
1407      * @throws LockedException if estimator is locked.
1408      */
1409     public void setRssiThreshold(final Double rssiThreshold) throws LockedException {
1410         if (isLocked()) {
1411             throw new LockedException();
1412         }
1413         this.rssiThreshold = rssiThreshold;
1414     }
1415 
1416     /**
1417      * Gets located radio sources used for lateration.
1418      *
1419      * @return located radio sources used for lateration.
1420      */
1421     public List<RadioSourceLocated<P>> getSources() {
1422         //noinspection unchecked
1423         return (List<RadioSourceLocated<P>>) sources;
1424     }
1425 
1426     /**
1427      * Sets located radio sources used for lateration.
1428      *
1429      * @param sources located radio sources used for lateration.
1430      * @throws LockedException          if estimator is locked.
1431      * @throws IllegalArgumentException if provided value is null or the number of
1432      *                                  provided sources is less than the required
1433      *                                  minimum.
1434      */
1435     public void setSources(final List<? extends RadioSourceLocated<P>> sources) throws LockedException {
1436         if (isLocked()) {
1437             throw new LockedException();
1438         }
1439 
1440         internalSetSources(sources);
1441     }
1442 
1443     /**
1444      * Gets fingerprint containing ranging+RSSI readings at an unknown location for
1445      * provided located radio sources.
1446      *
1447      * @return fingerprint containing readings at an unknown location for provided
1448      * located radio sources.
1449      */
1450     public RangingAndRssiFingerprint<RadioSource, RangingAndRssiReading<RadioSource>> getFingerprint() {
1451         //noinspection unchecked
1452         return (RangingAndRssiFingerprint<RadioSource, RangingAndRssiReading<RadioSource>>) fingerprint;
1453     }
1454 
1455     /**
1456      * Sets fingerprint containing ranging+RSSI readings at an unknown location for
1457      * provided located radio sources.
1458      *
1459      * @param fingerprint fingerprint containing readings at an unknown location for
1460      *                    provided located radio sources.
1461      * @throws LockedException if estimator is locked.
1462      */
1463     public void setFingerprint(
1464             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
1465                     extends RadioSource>> fingerprint) throws LockedException {
1466         if (isLocked()) {
1467             throw new LockedException();
1468         }
1469 
1470         internalSetFingerprint(fingerprint);
1471     }
1472 
1473     /**
1474      * Returns quality scores corresponding to each radio source.
1475      * The larger the score value the better the quality of radio source.
1476      *
1477      * @return quality scores corresponding to each radio source.
1478      */
1479     public double[] getSourceQualityScores() {
1480         return sourceQualityScores;
1481     }
1482 
1483     /**
1484      * Sets quality scores corresponding to each radio source.
1485      * The larger the score value the better the quality of the radio source.
1486      *
1487      * @param sourceQualityScores quality scores corresponding to each radio source.
1488      * @throws LockedException          if this instance is locked.
1489      * @throws IllegalArgumentException if provided quality scores length is smaller
1490      *                                  than minimum required samples.
1491      */
1492     public void setSourceQualityScores(final double[] sourceQualityScores) throws LockedException {
1493         if (isLocked()) {
1494             throw new LockedException();
1495         }
1496         internalSetSourceQualityScores(sourceQualityScores);
1497     }
1498 
1499     /**
1500      * Gets quality scores corresponding to each reading within provided fingerprint.
1501      * The larger the score value the better the quality of the reading.
1502      *
1503      * @return quality scores corresponding to each reading within provided
1504      * fingerprint.
1505      */
1506     public double[] getFingerprintReadingsQualityScores() {
1507         return fingerprintReadingsQualityScores;
1508     }
1509 
1510     /**
1511      * Sets quality scores corresponding to each reading within provided fingerprint.
1512      * The larger the score value the better the quality of the reading.
1513      *
1514      * @param fingerprintReadingsQualityScores quality scores corresponding to each
1515      *                                         reading within provided fingerprint.
1516      * @throws LockedException          if this instance is locked.
1517      * @throws IllegalArgumentException if provided quality scores length is smaller
1518      *                                  than minimum required samples.
1519      */
1520     public void setFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores)
1521             throws LockedException {
1522         if (isLocked()) {
1523             throw new LockedException();
1524         }
1525         internalSetFingerprintReadingsQualityScores(fingerprintReadingsQualityScores);
1526     }
1527 
1528     /**
1529      * Gets listener to be notified of events raised by this instance.
1530      *
1531      * @return listener to be notified of events raised by this instance.
1532      */
1533     public SequentialRobustRangingAndRssiPositionEstimatorListener<P> getListener() {
1534         return listener;
1535     }
1536 
1537     /**
1538      * Sets listener to be notified of events raised by this instance.
1539      *
1540      * @param listener listener to be notified of events raised by this instance.
1541      * @throws LockedException if estimator is locked.
1542      */
1543     public void setListener(final SequentialRobustRangingAndRssiPositionEstimatorListener<P> listener)
1544             throws LockedException {
1545         if (isLocked()) {
1546             throw new LockedException();
1547         }
1548         this.listener = listener;
1549     }
1550 
1551     /**
1552      * Gets initial position to use as a starting point to find a new solution.
1553      * This is optional, but if provided, when no linear solvers are used, this is
1554      * taken into account. If linear solvers are used, this is ignored.
1555      *
1556      * @return an initial position.
1557      */
1558     public P getInitialPosition() {
1559         return initialPosition;
1560     }
1561 
1562     /**
1563      * Sets initial position to use as a starting point to find a new solution.
1564      * This is optional, but if provided, when no linear solvers are used, this is
1565      * taken into account. If linear solvers are used, this is ignored.
1566      *
1567      * @param initialPosition an initial position.
1568      * @throws LockedException if estimator is locked.
1569      */
1570     public void setInitialPosition(final P initialPosition) throws LockedException {
1571         if (isLocked()) {
1572             throw new LockedException();
1573         }
1574         this.initialPosition = initialPosition;
1575     }
1576 
1577     /**
1578      * Returns boolean indicating if estimator is locked because estimation is
1579      * under progress.
1580      *
1581      * @return true if estimator is locked, false otherwise.
1582      */
1583     public boolean isLocked() {
1584         return locked;
1585     }
1586 
1587     /**
1588      * Indicates whether this instance is ready to start the estimation.
1589      *
1590      * @return true if this instance is ready, false otherwise.
1591      */
1592     public boolean isReady() {
1593         final var numSources = sources != null ? sources.size() : 0;
1594         final var numReadings = fingerprint != null && fingerprint.getReadings() != null
1595                 ? fingerprint.getReadings().size() : 0;
1596 
1597         return numSources > getMinRequiredSources() && numReadings >= numSources;
1598     }
1599 
1600     /**
1601      * Estimates position based on provided located radio sources and readings of such
1602      * sources at an unknown location.
1603      *
1604      * @return estimated position.
1605      * @throws LockedException          if estimator is locked.
1606      * @throws NotReadyException        if estimator is not ready.
1607      * @throws RobustEstimatorException if estimation fails for some other reason.
1608      */
1609     public P estimate() throws LockedException, NotReadyException, RobustEstimatorException {
1610         if (isLocked()) {
1611             throw new LockedException();
1612         }
1613 
1614         // create inner estimators
1615         buildEstimators();
1616         setupEstimators();
1617 
1618         if (!isReady() || !rssiEstimator.isReady() || !rangingEstimator.isReady()) {
1619             throw new NotReadyException();
1620         }
1621 
1622         locked = true;
1623         if (listener != null) {
1624             listener.onEstimateStart(this);
1625         }
1626 
1627         P coarsePosition;
1628         try {
1629             // estimate coarse position using RSSI data
1630             coarsePosition = rssiEstimator.estimate();
1631         } catch (final RobustEstimatorException e) {
1632             coarsePosition = null;
1633         }
1634 
1635         // use coarse position as initial position for ranging estimation
1636         if (coarsePosition != null) {
1637             rangingEstimator.setInitialPosition(coarsePosition);
1638         }
1639 
1640         try {
1641             final var result = rangingEstimator.estimate();
1642 
1643             if (listener != null) {
1644                 listener.onEstimateEnd(this);
1645             }
1646 
1647             return result;
1648         } finally {
1649             locked = false;
1650         }
1651     }
1652 
1653     /**
1654      * Gets data related to inliers found after estimation.
1655      *
1656      * @return data related to inliers found after estimation.
1657      */
1658     public InliersData getInliersData() {
1659         return rangingEstimator != null ? rangingEstimator.getInliersData() : null;
1660     }
1661 
1662     /**
1663      * Gets known positions of radio sources used internally to solve lateration.
1664      *
1665      * @return known positions used internally.
1666      */
1667     public P[] getPositions() {
1668         return rangingEstimator != null ? rangingEstimator.getPositions() : null;
1669     }
1670 
1671     /**
1672      * Gets Euclidean distances from known located radio sources to the location of
1673      * provided readings in a fingerprint.
1674      * Distance values are used internally to solve lateration.
1675      *
1676      * @return Euclidean distances used internally.
1677      */
1678     public double[] getDistances() {
1679         return rangingEstimator != null ? rangingEstimator.getDistances() : null;
1680     }
1681 
1682     /**
1683      * Gets standard deviation distances from known located radio sources to the
1684      * location of provided readings in a fingerprint.
1685      * Distance standard deviations are used internally to solve lateration.
1686      *
1687      * @return standard deviations used internally.
1688      */
1689     public double[] getDistanceStandardDeviations() {
1690         return rangingEstimator != null ? rangingEstimator.getDistanceStandardDeviations() : null;
1691     }
1692 
1693     /**
1694      * Gets estimated covariance of estimated position if available.
1695      * This is only available when result has been refined and covariance is kept.
1696      *
1697      * @return estimated covariance or null.
1698      */
1699     public Matrix getCovariance() {
1700         return rangingEstimator != null ? rangingEstimator.getCovariance() : null;
1701     }
1702 
1703     /**
1704      * Gets estimated position.
1705      *
1706      * @return estimated position.
1707      */
1708     public P getEstimatedPosition() {
1709         return rangingEstimator != null ? rangingEstimator.getEstimatedPosition() : null;
1710     }
1711 
1712     /**
1713      * Gets number of dimensions of provided points.
1714      *
1715      * @return number of dimensions of provided points.
1716      */
1717     public abstract int getNumberOfDimensions();
1718 
1719     /**
1720      * Gets minimum required number of located radio sources to perform lateration.
1721      *
1722      * @return minimum required number of located radio sources to perform
1723      * lateration.
1724      */
1725     public abstract int getMinRequiredSources();
1726 
1727     /**
1728      * Builds ranging and RSSI internal estimators.
1729      */
1730     protected abstract void buildEstimators();
1731 
1732     /**
1733      * Setups ranging and RSSI internal estimators.
1734      *
1735      * @throws LockedException if estimator is locked.
1736      */
1737     @SuppressWarnings("DuplicatedCode")
1738     private void setupEstimators() throws LockedException {
1739         if (fingerprint != null) {
1740             // builds separated RSSI and ranging readings
1741             final var readings = fingerprint.getReadings();
1742 
1743             final var rangingReadings = new ArrayList<RangingReading<RadioSource>>();
1744             final var rssiReadings = new ArrayList<RssiReading<RadioSource>>();
1745 
1746             for (final var reading : readings) {
1747                 rangingReadings.add(createRangingReading(reading));
1748                 rssiReadings.add(createRssiReading(reading));
1749             }
1750 
1751             final var rssiFingerprint = new RssiFingerprint<>(rssiReadings);
1752             final var rangingFingerprint = new RangingFingerprint<>(rangingReadings);
1753 
1754             // set data and configuration on both internal estimators
1755             rssiEstimator.setSources(sources);
1756             rssiEstimator.setFingerprint(rssiFingerprint);
1757             rssiEstimator.setRadioSourcePositionCovarianceUsed(useRssiRadioSourcePositionCovariance);
1758             rssiEstimator.setEvenlyDistributeReadings(evenlyDistributeRssiReadings);
1759             rssiEstimator.setFallbackDistanceStandardDeviation(rssiFallbackDistanceStandardDeviation);
1760             rssiEstimator.setProgressDelta(2.0f * progressDelta);
1761             rssiEstimator.setConfidence(rssiConfidence);
1762             rssiEstimator.setMaxIterations(rssiMaxIterations);
1763             rssiEstimator.setResultRefined(refineResult);
1764             rssiEstimator.setCovarianceKept(keepCovariance);
1765             rssiEstimator.setInitialPosition(initialPosition);
1766             rssiEstimator.setLinearSolverUsed(useRssiLinearSolver);
1767             rssiEstimator.setHomogeneousLinearSolverUsed(useRssiHomogeneousLinearSolver);
1768             rssiEstimator.setPreliminarySolutionRefined(refineRssiPreliminarySolutions);
1769             rssiEstimator.setSourceQualityScores(sourceQualityScores);
1770             rssiEstimator.setFingerprintReadingsQualityScores(fingerprintReadingsQualityScores);
1771             rssiEstimator.setListener(new RobustRssiPositionEstimatorListener<>() {
1772                 @Override
1773                 public void onEstimateStart(final RobustRssiPositionEstimator<P> estimator) {
1774                     // not used
1775                 }
1776 
1777                 @Override
1778                 public void onEstimateEnd(final RobustRssiPositionEstimator<P> estimator) {
1779                     // not used
1780                 }
1781 
1782                 @Override
1783                 public void onEstimateNextIteration(
1784                         final RobustRssiPositionEstimator<P> estimator, final int iteration) {
1785                     // not used
1786                 }
1787 
1788                 @Override
1789                 public void onEstimateProgressChange(
1790                         final RobustRssiPositionEstimator<P> estimator, final float progress) {
1791                     if (listener != null) {
1792                         listener.onEstimateProgressChange(
1793                                 SequentialRobustRangingAndRssiPositionEstimator.this,
1794                                 0.5f * progress);
1795                     }
1796                 }
1797             });
1798 
1799             rssiEstimator.setPreliminarySubsetSize(
1800                     Math.max(rssiPreliminarySubsetSize, rssiEstimator.getMinRequiredSources()));
1801 
1802             rangingEstimator.setSources(sources);
1803             rangingEstimator.setFingerprint(rangingFingerprint);
1804             rangingEstimator.setRadioSourcePositionCovarianceUsed(useRangingRadioSourcePositionCovariance);
1805             rangingEstimator.setEvenlyDistributeReadings(evenlyDistributeRangingReadings);
1806             rangingEstimator.setFallbackDistanceStandardDeviation(rangingFallbackDistanceStandardDeviation);
1807             rangingEstimator.setProgressDelta(2.0f * progressDelta);
1808             rangingEstimator.setConfidence(rangingConfidence);
1809             rangingEstimator.setMaxIterations(rangingMaxIterations);
1810             rangingEstimator.setCovarianceKept(keepCovariance);
1811             rangingEstimator.setInitialPosition(initialPosition);
1812             rangingEstimator.setLinearSolverUsed(useRangingLinearSolver);
1813             rangingEstimator.setHomogeneousLinearSolverUsed(useRangingHomogeneousLinearSolver);
1814             rangingEstimator.setPreliminarySolutionRefined(refineRangingPreliminarySolutions);
1815             rangingEstimator.setSourceQualityScores(sourceQualityScores);
1816             rangingEstimator.setFingerprintReadingsQualityScores(fingerprintReadingsQualityScores);
1817             rangingEstimator.setListener(new RobustRangingPositionEstimatorListener<P>() {
1818                 @Override
1819                 public void onEstimateStart(final RobustRangingPositionEstimator<P> estimator) {
1820                     // not used
1821                 }
1822 
1823                 @Override
1824                 public void onEstimateEnd(final RobustRangingPositionEstimator<P> estimator) {
1825                     // not used
1826                 }
1827 
1828                 @Override
1829                 public void onEstimateNextIteration(
1830                         final RobustRangingPositionEstimator<P> estimator, final int iteration) {
1831                     // not used
1832                 }
1833 
1834                 @Override
1835                 public void onEstimateProgressChange(
1836                         final RobustRangingPositionEstimator<P> estimator, final float progress) {
1837                     if (listener != null) {
1838                         listener.onEstimateProgressChange(
1839                                 SequentialRobustRangingAndRssiPositionEstimator.this,
1840                                 0.5f + 0.5f * progress);
1841                     }
1842                 }
1843             });
1844 
1845             rangingEstimator.setPreliminarySubsetSize(
1846                     Math.max(rangingPreliminarySubsetSize, rangingEstimator.getMinRequiredSources()));
1847         }
1848     }
1849 
1850     /**
1851      * Internally sets located radio sources used for lateration.
1852      *
1853      * @param sources located radio sources used for lateration.
1854      * @throws IllegalArgumentException if provided value is null or the number of
1855      *                                  provided sources is less than the required minimum.
1856      */
1857     private void internalSetSources(final List<? extends RadioSourceLocated<P>> sources) {
1858         if (sources == null) {
1859             throw new IllegalArgumentException();
1860         }
1861 
1862         if (sources.size() < getMinRequiredSources()) {
1863             throw new IllegalArgumentException();
1864         }
1865 
1866         this.sources = sources;
1867     }
1868 
1869     /**
1870      * Internally sets fingerprint containing readings at an unknown location for
1871      * provided located radio sources.
1872      *
1873      * @param fingerprint fingerprint containing readings at an unknown location for
1874      *                    provided located radio sources.
1875      * @throws IllegalArgumentException if provided value is null.
1876      */
1877     private void internalSetFingerprint(
1878             final RangingAndRssiFingerprint<? extends RadioSource, ? extends RangingAndRssiReading<?
1879                     extends RadioSource>> fingerprint) {
1880         if (fingerprint == null) {
1881             throw new IllegalArgumentException();
1882         }
1883 
1884         this.fingerprint = fingerprint;
1885     }
1886 
1887     /**
1888      * Sets quality scores corresponding to each provided located radio source.
1889      * This method is used internally and does not check whether instance is
1890      * locked or not.
1891      *
1892      * @param sourceQualityScores quality scores to be set.
1893      * @throws IllegalArgumentException if provided quality scores length
1894      *                                  is smaller than 3 samples for 2D or 4 samples for 3D.
1895      */
1896     private void internalSetSourceQualityScores(final double[] sourceQualityScores) {
1897         if (sourceQualityScores == null || sourceQualityScores.length < getMinRequiredSources()) {
1898             throw new IllegalArgumentException();
1899         }
1900 
1901         this.sourceQualityScores = sourceQualityScores;
1902     }
1903 
1904     /**
1905      * Sets quality scores corresponding to each provided reading within provided
1906      * fingerprint.
1907      * This method is used internally and does not check whether instance is locked
1908      * or not.
1909      *
1910      * @param fingerprintReadingsQualityScores quality scores to be set.
1911      * @throws IllegalArgumentException if provided quality scores length is
1912      *                                  smaller than 3 samples for 2D or 4 samples for 3D.
1913      */
1914     private void internalSetFingerprintReadingsQualityScores(final double[] fingerprintReadingsQualityScores) {
1915         if (fingerprintReadingsQualityScores == null
1916                 || fingerprintReadingsQualityScores.length < getMinRequiredSources()) {
1917             throw new IllegalArgumentException();
1918         }
1919 
1920         this.fingerprintReadingsQualityScores = fingerprintReadingsQualityScores;
1921     }
1922 
1923     /**
1924      * Creates a ranging reading from a ranging and RSSI reading.
1925      *
1926      * @param reading input reading to convert from.
1927      * @return a ranging reading containing only the ranging data of input reading.
1928      */
1929     private RangingReading<RadioSource> createRangingReading(
1930             final RangingAndRssiReading<? extends RadioSource> reading) {
1931         return new RangingReading<>(reading.getSource(),
1932                 reading.getDistance(),
1933                 reading.getDistanceStandardDeviation(),
1934                 reading.getNumAttemptedMeasurements(),
1935                 reading.getNumSuccessfulMeasurements());
1936     }
1937 
1938     /**
1939      * Creates an RSSI reading from a ranging and RSSI reading.
1940      *
1941      * @param reading input reading to convert from.
1942      * @return an RSSI reading containing only the RSSI data of input reading.
1943      */
1944     private RssiReading<RadioSource> createRssiReading(final RangingAndRssiReading<? extends RadioSource> reading) {
1945         return new RssiReading<>(reading.getSource(), reading.getRssi(),
1946                 reading.getRssiStandardDeviation());
1947     }
1948 }